[{"data":1,"prerenderedAt":247585},["ShallowReactive",2],{"navigation":3,"\u002Fservices\u002F":927,"service-catalog-pages":1052},[4,40,114,446,474,498,596,903],{"title":5,"path":6,"stem":7,"children":8},"Architecture","\u002Farchitecture","architecture",[9,14,28,32,36],{"title":10,"path":6,"stem":11,"access":12,"icon":13},"Overview","architecture\u002Findex","public","i-lucide-network",{"title":10,"path":15,"stem":16,"children":17,"access":12,"icon":19},"\u002Farchitecture\u002Fkernel","architecture\u002F1.kernel\u002Findex",[18,20,24],{"title":10,"path":15,"stem":16,"access":12,"icon":19},"i-lucide-box",{"title":21,"path":22,"stem":23,"access":12},"Enclave","\u002Farchitecture\u002Fkernel\u002Fenclave","architecture\u002F1.kernel\u002Fenclave",{"title":25,"path":26,"stem":27,"access":12},"Forking the enclave","\u002Farchitecture\u002Fkernel\u002Fforking-the-enclave","architecture\u002F1.kernel\u002Fforking-the-enclave",{"title":29,"path":30,"stem":31,"access":12},"Ports","\u002Farchitecture\u002Fports","architecture\u002F2.ports",{"title":33,"path":34,"stem":35,"access":12},"Service layers","\u002Farchitecture\u002Fservice-layers","architecture\u002F3.service-layers",{"title":37,"path":38,"stem":39,"access":12},"Families","\u002Farchitecture\u002Ffamilies","architecture\u002F4.families",{"title":41,"path":42,"stem":43,"children":44},"Core","\u002Fcore","core",[45,47,50,53,56,88,97],{"title":10,"path":42,"stem":46,"access":12,"icon":19},"core\u002Findex",{"title":10,"path":48,"stem":49,"access":12},"\u002Fcore\u002F01-overview","core\u002F01-overview",{"title":10,"path":51,"stem":52,"access":12},"\u002Fcore\u002F05-stores","core\u002F05-stores",{"title":10,"path":54,"stem":55,"access":12},"\u002Fcore\u002F06-permissions-and-keyring","core\u002F06-permissions-and-keyring",{"title":10,"path":57,"stem":58,"children":59,"access":12},"\u002Fcore\u002Fnoydb","core\u002F2.noydb\u002Findex",[60,61,65,68,72,76,80,84],{"title":10,"path":57,"stem":58,"access":12},{"title":62,"path":63,"stem":64,"access":12},"Noydb","\u002Fcore\u002Fnoydb\u002Fmethods","core\u002F2.noydb\u002F1.methods",{"title":21,"path":66,"stem":67,"access":12},"\u002Fcore\u002Fnoydb\u002Fenclave","core\u002F2.noydb\u002F2.enclave",{"title":69,"path":70,"stem":71,"access":12},"Secret & keyring","\u002Fcore\u002Fnoydb\u002Fkeyring","core\u002F2.noydb\u002F3.keyring",{"title":73,"path":74,"stem":75,"access":12},"Authentication","\u002Fcore\u002Fnoydb\u002Fauthentication","core\u002F2.noydb\u002F4.authentication",{"title":77,"path":78,"stem":79,"access":12},"Auth recovery","\u002Fcore\u002Fnoydb\u002Frecovery","core\u002F2.noydb\u002F5.recovery",{"title":81,"path":82,"stem":83,"access":12},"Configuration","\u002Fcore\u002Fnoydb\u002Fconfiguration","core\u002F2.noydb\u002F6.configuration",{"title":85,"path":86,"stem":87,"access":12},"Strategies","\u002Fcore\u002Fnoydb\u002Fstrategies","core\u002F2.noydb\u002F7.strategies",{"title":10,"path":89,"stem":90,"children":91,"access":12},"\u002Fcore\u002Fvault","core\u002F3.vault\u002Findex",[92,93],{"title":10,"path":89,"stem":90,"access":12},{"title":94,"path":95,"stem":96,"access":12},"Vault","\u002Fcore\u002Fvault\u002Fmethods","core\u002F3.vault\u002F1.methods",{"title":10,"path":98,"stem":99,"children":100,"access":12},"\u002Fcore\u002Fcollection","core\u002F4.collection\u002Findex",[101,102,106,110],{"title":10,"path":98,"stem":99,"access":12},{"title":103,"path":104,"stem":105,"access":12},"Collection","\u002Fcore\u002Fcollection\u002Fmethods","core\u002F4.collection\u002F1.methods",{"title":107,"path":108,"stem":109,"access":12},"Querying","\u002Fcore\u002Fcollection\u002Fquerying","core\u002F4.collection\u002F2.querying",{"title":111,"path":112,"stem":113,"access":12},"Schema","\u002Fcore\u002Fcollection\u002Fschema","core\u002F4.collection\u002F3.schema",{"title":37,"path":115,"stem":116,"children":117},"\u002Ffamilies","families",[118,121,168,194,208,212,216,294,340],{"title":10,"path":115,"stem":119,"access":12,"icon":120},"families\u002Findex","i-lucide-plug",{"title":122,"path":123,"stem":124,"children":125,"access":12,"icon":127},"as-Exports","\u002Ffamilies\u002Fas","families\u002Fas\u002Findex",[126,128,132,136,140,144,148,152,156,160,164],{"title":122,"path":123,"stem":124,"access":12,"icon":127},"i-lucide-book-open",{"title":129,"path":130,"stem":131,"access":12},"aws-s3","\u002Ffamilies\u002Fas\u002Faws-s3","families\u002Fas\u002Faws-s3",{"title":133,"path":134,"stem":135,"access":12},"blob","\u002Ffamilies\u002Fas\u002Fblob","families\u002Fas\u002Fblob",{"title":137,"path":138,"stem":139,"access":12},"csv","\u002Ffamilies\u002Fas\u002Fcsv","families\u002Fas\u002Fcsv",{"title":141,"path":142,"stem":143,"access":12},"json","\u002Ffamilies\u002Fas\u002Fjson","families\u002Fas\u002Fjson",{"title":145,"path":146,"stem":147,"access":12},"ndjson","\u002Ffamilies\u002Fas\u002Fndjson","families\u002Fas\u002Fndjson",{"title":149,"path":150,"stem":151,"access":12},"noydb","\u002Ffamilies\u002Fas\u002Fnoydb","families\u002Fas\u002Fnoydb",{"title":153,"path":154,"stem":155,"access":12},"sql","\u002Ffamilies\u002Fas\u002Fsql","families\u002Fas\u002Fsql",{"title":157,"path":158,"stem":159,"access":12},"xlsx","\u002Ffamilies\u002Fas\u002Fxlsx","families\u002Fas\u002Fxlsx",{"title":161,"path":162,"stem":163,"access":12},"xml","\u002Ffamilies\u002Fas\u002Fxml","families\u002Fas\u002Fxml",{"title":165,"path":166,"stem":167,"access":12},"zip","\u002Ffamilies\u002Fas\u002Fzip","families\u002Fas\u002Fzip",{"title":169,"path":170,"stem":171,"children":172,"access":12,"icon":127},"at-Seals","\u002Ffamilies\u002Fat","families\u002Fat\u002Findex",[173,174,178,182,186,190],{"title":169,"path":170,"stem":171,"access":12,"icon":127},{"title":175,"path":176,"stem":177,"access":12},"aws-kms","\u002Ffamilies\u002Fat\u002Faws-kms","families\u002Fat\u002Faws-kms",{"title":179,"path":180,"stem":181,"access":12},"azure-keyvault","\u002Ffamilies\u002Fat\u002Fazure-keyvault","families\u002Fat\u002Fazure-keyvault",{"title":183,"path":184,"stem":185,"access":12},"env","\u002Ffamilies\u002Fat\u002Fenv","families\u002Fat\u002Fenv",{"title":187,"path":188,"stem":189,"access":12},"gcp-kms","\u002Ffamilies\u002Fat\u002Fgcp-kms","families\u002Fat\u002Fgcp-kms",{"title":191,"path":192,"stem":193,"access":12},"macos-keychain","\u002Ffamilies\u002Fat\u002Fmacos-keychain","families\u002Fat\u002Fmacos-keychain",{"title":195,"path":196,"stem":197,"children":198,"access":12,"icon":127},"by-Sessions","\u002Ffamilies\u002Fby","families\u002Fby\u002Findex",[199,200,204],{"title":195,"path":196,"stem":197,"access":12,"icon":127},{"title":201,"path":202,"stem":203,"access":12},"peer","\u002Ffamilies\u002Fby\u002Fpeer","families\u002Fby\u002Fpeer",{"title":205,"path":206,"stem":207,"access":12},"tabs","\u002Ffamilies\u002Fby\u002Ftabs","families\u002Fby\u002Ftabs",{"title":209,"path":210,"stem":211,"access":12},"Classifying a package","\u002Ffamilies\u002Fclassification","families\u002Fclassification",{"title":213,"path":214,"stem":215,"access":12},"Command-line tools","\u002Ffamilies\u002Fcli","families\u002Fcli",{"title":217,"path":218,"stem":219,"children":220,"access":12,"icon":127},"in-Frameworks","\u002Ffamilies\u002Fin","families\u002Fin\u002Findex",[221,222,226,230,234,238,242,246,250,254,258,262,266,270,274,278,282,286,290],{"title":217,"path":218,"stem":219,"access":12,"icon":127},{"title":223,"path":224,"stem":225,"access":12},"ai","\u002Ffamilies\u002Fin\u002Fai","families\u002Fin\u002Fai",{"title":227,"path":228,"stem":229,"access":12},"devtools","\u002Ffamilies\u002Fin\u002Fdevtools","families\u002Fin\u002Fdevtools",{"title":231,"path":232,"stem":233,"access":12},"devtools-tui","\u002Ffamilies\u002Fin\u002Fdevtools-tui","families\u002Fin\u002Fdevtools-tui",{"title":235,"path":236,"stem":237,"access":12},"liff","\u002Ffamilies\u002Fin\u002Fliff","families\u002Fin\u002Fliff",{"title":239,"path":240,"stem":241,"access":12},"nextjs","\u002Ffamilies\u002Fin\u002Fnextjs","families\u002Fin\u002Fnextjs",{"title":243,"path":244,"stem":245,"access":12},"nuxt","\u002Ffamilies\u002Fin\u002Fnuxt","families\u002Fin\u002Fnuxt",{"title":247,"path":248,"stem":249,"access":12},"pinia","\u002Ffamilies\u002Fin\u002Fpinia","families\u002Fin\u002Fpinia",{"title":251,"path":252,"stem":253,"access":12},"pwa","\u002Ffamilies\u002Fin\u002Fpwa","families\u002Fin\u002Fpwa",{"title":255,"path":256,"stem":257,"access":12},"react","\u002Ffamilies\u002Fin\u002Freact","families\u002Fin\u002Freact",{"title":259,"path":260,"stem":261,"access":12},"relay","\u002Ffamilies\u002Fin\u002Frelay","families\u002Fin\u002Frelay",{"title":263,"path":264,"stem":265,"access":12},"rest","\u002Ffamilies\u002Fin\u002Frest","families\u002Fin\u002Frest",{"title":267,"path":268,"stem":269,"access":12},"solid","\u002Ffamilies\u002Fin\u002Fsolid","families\u002Fin\u002Fsolid",{"title":271,"path":272,"stem":273,"access":12},"svelte","\u002Ffamilies\u002Fin\u002Fsvelte","families\u002Fin\u002Fsvelte",{"title":275,"path":276,"stem":277,"access":12},"tanstack-query","\u002Ffamilies\u002Fin\u002Ftanstack-query","families\u002Fin\u002Ftanstack-query",{"title":279,"path":280,"stem":281,"access":12},"tanstack-table","\u002Ffamilies\u002Fin\u002Ftanstack-table","families\u002Fin\u002Ftanstack-table",{"title":283,"path":284,"stem":285,"access":12},"vue","\u002Ffamilies\u002Fin\u002Fvue","families\u002Fin\u002Fvue",{"title":287,"path":288,"stem":289,"access":12},"yjs","\u002Ffamilies\u002Fin\u002Fyjs","families\u002Fin\u002Fyjs",{"title":291,"path":292,"stem":293,"access":12},"zustand","\u002Ffamilies\u002Fin\u002Fzustand","families\u002Fin\u002Fzustand",{"title":295,"path":296,"stem":297,"children":298,"access":12,"icon":127},"on-Auth","\u002Ffamilies\u002Fon","families\u002Fon\u002Findex",[299,300,304,308,312,316,320,324,328,332,336],{"title":295,"path":296,"stem":297,"access":12,"icon":127},{"title":301,"path":302,"stem":303,"access":12},"email-otp","\u002Ffamilies\u002Fon\u002Femail-otp","families\u002Fon\u002Femail-otp",{"title":305,"path":306,"stem":307,"access":12},"magic-link","\u002Ffamilies\u002Fon\u002Fmagic-link","families\u002Fon\u002Fmagic-link",{"title":309,"path":310,"stem":311,"access":12},"oidc","\u002Ffamilies\u002Fon\u002Foidc","families\u002Fon\u002Foidc",{"title":313,"path":314,"stem":315,"access":12},"password","\u002Ffamilies\u002Fon\u002Fpassword","families\u002Fon\u002Fpassword",{"title":317,"path":318,"stem":319,"access":12},"pin","\u002Ffamilies\u002Fon\u002Fpin","families\u002Fon\u002Fpin",{"title":321,"path":322,"stem":323,"access":12},"recovery","\u002Ffamilies\u002Fon\u002Frecovery","families\u002Fon\u002Frecovery",{"title":325,"path":326,"stem":327,"access":12},"shamir","\u002Ffamilies\u002Fon\u002Fshamir","families\u002Fon\u002Fshamir",{"title":329,"path":330,"stem":331,"access":12},"threat","\u002Ffamilies\u002Fon\u002Fthreat","families\u002Fon\u002Fthreat",{"title":333,"path":334,"stem":335,"access":12},"totp","\u002Ffamilies\u002Fon\u002Ftotp","families\u002Fon\u002Ftotp",{"title":337,"path":338,"stem":339,"access":12},"webauthn","\u002Ffamilies\u002Fon\u002Fwebauthn","families\u002Fon\u002Fwebauthn",{"title":341,"path":342,"stem":343,"children":344,"access":12,"icon":127},"to-Stores","\u002Ffamilies\u002Fto","families\u002Fto\u002Findex",[345,346,350,353,357,361,365,370,374,378,382,386,390,394,398,402,406,410,414,417,421,425,429,434,438,442],{"title":341,"path":342,"stem":343,"access":12,"icon":127},{"title":347,"path":348,"stem":349,"access":12},"aws-dynamo","\u002Ffamilies\u002Fto\u002Faws-dynamo","families\u002Fto\u002Faws-dynamo",{"title":129,"path":351,"stem":352,"access":12},"\u002Ffamilies\u002Fto\u002Faws-s3","families\u002Fto\u002Faws-s3",{"title":354,"path":355,"stem":356,"access":12},"browser-fs","\u002Ffamilies\u002Fto\u002Fbrowser-fs","families\u002Fto\u002Fbrowser-fs",{"title":358,"path":359,"stem":360,"access":12},"browser-idb","\u002Ffamilies\u002Fto\u002Fbrowser-idb","families\u002Fto\u002Fbrowser-idb",{"title":362,"path":363,"stem":364,"access":12},"browser-local","\u002Ffamilies\u002Fto\u002Fbrowser-local","families\u002Fto\u002Fbrowser-local",{"title":366,"path":367,"stem":368,"access":12,"icon":369},"Choosing a store","\u002Ffamilies\u002Fto\u002Fchoosing-a-store","families\u002Fto\u002Fchoosing-a-store","i-lucide-split",{"title":371,"path":372,"stem":373,"access":12},"cloudflare-d1","\u002Ffamilies\u002Fto\u002Fcloudflare-d1","families\u002Fto\u002Fcloudflare-d1",{"title":375,"path":376,"stem":377,"access":12},"cloudflare-r2","\u002Ffamilies\u002Fto\u002Fcloudflare-r2","families\u002Fto\u002Fcloudflare-r2",{"title":379,"path":380,"stem":381,"access":12},"drive","\u002Ffamilies\u002Fto\u002Fdrive","families\u002Fto\u002Fdrive",{"title":383,"path":384,"stem":385,"access":12},"file","\u002Ffamilies\u002Fto\u002Ffile","families\u002Fto\u002Ffile",{"title":387,"path":388,"stem":389,"access":12},"icloud","\u002Ffamilies\u002Fto\u002Ficloud","families\u002Fto\u002Ficloud",{"title":391,"path":392,"stem":393,"access":12},"memory","\u002Ffamilies\u002Fto\u002Fmemory","families\u002Fto\u002Fmemory",{"title":395,"path":396,"stem":397,"access":12},"meter","\u002Ffamilies\u002Fto\u002Fmeter","families\u002Fto\u002Fmeter",{"title":399,"path":400,"stem":401,"access":12},"mysql","\u002Ffamilies\u002Fto\u002Fmysql","families\u002Fto\u002Fmysql",{"title":403,"path":404,"stem":405,"access":12},"nfs","\u002Ffamilies\u002Fto\u002Fnfs","families\u002Fto\u002Fnfs",{"title":407,"path":408,"stem":409,"access":12},"postgres","\u002Ffamilies\u002Fto\u002Fpostgres","families\u002Fto\u002Fpostgres",{"title":411,"path":412,"stem":413,"access":12},"probe","\u002Ffamilies\u002Fto\u002Fprobe","families\u002Fto\u002Fprobe",{"title":263,"path":415,"stem":416,"access":12},"\u002Ffamilies\u002Fto\u002Frest","families\u002Fto\u002Frest",{"title":418,"path":419,"stem":420,"access":12},"smb","\u002Ffamilies\u002Fto\u002Fsmb","families\u002Fto\u002Fsmb",{"title":422,"path":423,"stem":424,"access":12},"sqlite","\u002Ffamilies\u002Fto\u002Fsqlite","families\u002Fto\u002Fsqlite",{"title":426,"path":427,"stem":428,"access":12},"ssh","\u002Ffamilies\u002Fto\u002Fssh","families\u002Fto\u002Fssh",{"title":430,"path":431,"stem":432,"access":12,"icon":433},"Store-locator descriptors","\u002Ffamilies\u002Fto\u002Fstore-locator","families\u002Fto\u002Fstore-locator","i-lucide-map-pin",{"title":435,"path":436,"stem":437,"access":12},"supabase","\u002Ffamilies\u002Fto\u002Fsupabase","families\u002Fto\u002Fsupabase",{"title":439,"path":440,"stem":441,"access":12},"turso","\u002Ffamilies\u002Fto\u002Fturso","families\u002Fto\u002Fturso",{"title":443,"path":444,"stem":445,"access":12},"webdav","\u002Ffamilies\u002Fto\u002Fwebdav","families\u002Fto\u002Fwebdav",{"title":447,"path":448,"stem":449,"children":450},"Faq","\u002Ffaq","faq",[451,454,458,462,466,470],{"title":10,"path":448,"stem":452,"access":12,"icon":453},"faq\u002Findex","i-lucide-circle-help",{"title":455,"path":456,"stem":457,"access":12},"Where can noy-db run?","\u002Ffaq\u002Fwhere-can-noy-db-run","faq\u002F1.where-can-noy-db-run",{"title":459,"path":460,"stem":461,"access":12},"Native mobile & PWA","\u002Ffaq\u002Fmobile-and-pwa","faq\u002F2.mobile-and-pwa",{"title":463,"path":464,"stem":465,"access":12},"Threat model","\u002Ffaq\u002Fthreat-model","faq\u002F3.threat-model",{"title":467,"path":468,"stem":469,"access":12},"Community & ecosystem","\u002Ffaq\u002Fcommunity-and-ecosystem","faq\u002F4.community-and-ecosystem",{"title":471,"path":472,"stem":473,"access":12},"Licensing","\u002Ffaq\u002Flicensing","faq\u002F5.licensing",{"title":475,"path":476,"stem":477,"children":478},"Glossary","\u002Fglossary","glossary",[479,482,486,490,494],{"title":10,"path":476,"stem":480,"access":12,"icon":481},"glossary\u002Findex","i-lucide-book-a",{"title":483,"path":484,"stem":485,"access":12},"Core vocabulary","\u002Fglossary\u002Fcore-vocabulary","glossary\u002Fcore-vocabulary",{"title":487,"path":488,"stem":489,"access":12},"Deletion vocabulary","\u002Fglossary\u002Fdeletion","glossary\u002Fdeletion",{"title":491,"path":492,"stem":493,"access":12},"Satellite collection vocabulary","\u002Fglossary\u002Fsatellite-collections","glossary\u002Fsatellite-collections",{"title":495,"path":496,"stem":497,"access":12},"Schema migration vocabulary","\u002Fglossary\u002Fschema-migration","glossary\u002Fschema-migration",{"title":499,"path":500,"stem":501,"children":502},"Recipes","\u002Frecipes","recipes",[503,506,510,514,518,522,526,530,534,538,542,546,550,554,558,562,567,571,575,580,584,588,592],{"title":10,"path":500,"stem":504,"access":12,"icon":505},"recipes\u002Findex","i-lucide-play",{"title":507,"path":508,"stem":509,"access":12},"Accounting app","\u002Frecipes\u002Faccounting-app","recipes\u002Faccounting-app",{"title":511,"path":512,"stem":513,"access":12},"Analytics app","\u002Frecipes\u002Fanalytics-app","recipes\u002Fanalytics-app",{"title":515,"path":516,"stem":517,"access":12},"Attestation verifier","\u002Frecipes\u002Fattestation-verifier","recipes\u002Fattestation-verifier",{"title":519,"path":520,"stem":521,"access":12},"AWS-KMS attestation","\u002Frecipes\u002Faws-kms-pdf-attestation","recipes\u002Faws-kms-pdf-attestation",{"title":523,"path":524,"stem":525,"access":12},"Browser durability","\u002Frecipes\u002Fbrowser-durability","recipes\u002Fbrowser-durability",{"title":527,"path":528,"stem":529,"access":12},"Production cloud store","\u002Frecipes\u002Fcloud-store","recipes\u002Fcloud-store",{"title":531,"path":532,"stem":533,"access":12},"Compose your own setup","\u002Frecipes\u002Fcompose-your-own","recipes\u002Fcompose-your-own",{"title":535,"path":536,"stem":537,"access":12},"Confidential HR records","\u002Frecipes\u002Fconfidential-hr","recipes\u002Fconfidential-hr",{"title":539,"path":540,"stem":541,"access":12},"Composing an echo secret","\u002Frecipes\u002Fecho-secret-enrollment","recipes\u002Fecho-secret-enrollment",{"title":543,"path":544,"stem":545,"access":12},"Email archive","\u002Frecipes\u002Femail-archive","recipes\u002Femail-archive",{"title":547,"path":548,"stem":549,"access":12},"Email archive — threads & revisions","\u002Frecipes\u002Femail-archive-threads-revisions","recipes\u002Femail-archive-threads-revisions",{"title":551,"path":552,"stem":553,"access":12},"Export & portability","\u002Frecipes\u002Fexport-portability","recipes\u002Fexport-portability",{"title":555,"path":556,"stem":557,"access":12},"Passwordless OIDC (AWS Cognito)","\u002Frecipes\u002Fpasswordless-oidc","recipes\u002Fpasswordless-oidc",{"title":559,"path":560,"stem":561,"access":12},"Personal notebook","\u002Frecipes\u002Fpersonal-notebook","recipes\u002Fpersonal-notebook",{"title":563,"path":564,"stem":565,"access":12,"icon":566},"Playgrounds","\u002Frecipes\u002Fplaygrounds","recipes\u002Fplaygrounds","i-lucide-gamepad-2",{"title":568,"path":569,"stem":570,"access":12},"React invoice app","\u002Frecipes\u002Freact-invoices","recipes\u002Freact-invoices",{"title":572,"path":573,"stem":574,"access":12},"Collaborative app","\u002Frecipes\u002Frealtime-crdt-app","recipes\u002Frealtime-crdt-app",{"title":576,"path":577,"stem":578,"access":12,"icon":579},"Run the showcases","\u002Frecipes\u002Frun-the-showcases","recipes\u002Frun-the-showcases","i-lucide-flask-conical",{"title":581,"path":582,"stem":583,"access":12},"Search & RAG","\u002Frecipes\u002Fsearch-rag","recipes\u002Fsearch-rag",{"title":585,"path":586,"stem":587,"access":12},"Team vault topology","\u002Frecipes\u002Fteam-vault-topology","recipes\u002Fteam-vault-topology",{"title":589,"path":590,"stem":591,"access":12},"Unlock ceremonies","\u002Frecipes\u002Funlock-ceremonies","recipes\u002Funlock-ceremonies",{"title":593,"path":594,"stem":595,"access":12},"User preferences","\u002Frecipes\u002Fuser-preferences","recipes\u002Fuser-preferences",{"title":597,"path":598,"stem":599,"children":600},"Services","\u002Fservices","services",[601,604,608,612,616,620,624,628,632,636,640,644,648,652,656,660,664,668,672,676,680,684,688,692,696,700,704,710,714,718,722,726,730,734,738,742,746,750,755,759,763,767,771,775,779,783,787,791,795,799,803,807,811,815,819,823,827,831,835,839,843,847,851,855,859,863,867,871,875,879,883,887,891,895,899],{"title":10,"path":598,"stem":602,"access":12,"icon":603},"services\u002Findex","i-lucide-layers",{"title":605,"path":606,"stem":607,"access":12},"archive","\u002Fservices\u002Farchive","services\u002Farchive",{"title":609,"path":610,"stem":611,"access":12},"attestation","\u002Fservices\u002Fattestation","services\u002Fattestation",{"title":613,"path":614,"stem":615,"access":12},"auth-introspection","\u002Fservices\u002Fauth-introspection","services\u002Fauth-introspection",{"title":617,"path":618,"stem":619,"access":12},"auth-landscape","\u002Fservices\u002Fauth-landscape","services\u002Fauth-landscape",{"title":621,"path":622,"stem":623,"access":12},"blobs","\u002Fservices\u002Fblobs","services\u002Fblobs",{"title":625,"path":626,"stem":627,"access":12},"broker","\u002Fservices\u002Fbroker","services\u002Fbroker",{"title":629,"path":630,"stem":631,"access":12},"bundle","\u002Fservices\u002Fbundle","services\u002Fbundle",{"title":633,"path":634,"stem":635,"access":12},"classified","\u002Fservices\u002Fclassified","services\u002Fclassified",{"title":637,"path":638,"stem":639,"access":12},"computed","\u002Fservices\u002Fcomputed","services\u002Fcomputed",{"title":641,"path":642,"stem":643,"access":12},"consent","\u002Fservices\u002Fconsent","services\u002Fconsent",{"title":645,"path":646,"stem":647,"access":12},"cover","\u002Fservices\u002Fcover","services\u002Fcover",{"title":649,"path":650,"stem":651,"access":12},"coverage","\u002Fservices\u002Fcoverage","services\u002Fcoverage",{"title":653,"path":654,"stem":655,"access":12},"crdt","\u002Fservices\u002Fcrdt","services\u002Fcrdt",{"title":657,"path":658,"stem":659,"access":12},"cross-join","\u002Fservices\u002Fcross-join","services\u002Fcross-join",{"title":661,"path":662,"stem":663,"access":12},"custody","\u002Fservices\u002Fcustody","services\u002Fcustody",{"title":665,"path":666,"stem":667,"access":12},"derivations","\u002Fservices\u002Fderivations","services\u002Fderivations",{"title":669,"path":670,"stem":671,"access":12},"directory","\u002Fservices\u002Fdirectory","services\u002Fdirectory",{"title":673,"path":674,"stem":675,"access":12},"search — AI","\u002Fservices\u002Fembeddings","services\u002Fembeddings",{"title":677,"path":678,"stem":679,"access":12},"Field metadata","\u002Fservices\u002Ffield-metadata","services\u002Ffield-metadata",{"title":681,"path":682,"stem":683,"access":12},"Foreign refs","\u002Fservices\u002Fforeign-refs","services\u002Fforeign-refs",{"title":685,"path":686,"stem":687,"access":12},"forget","\u002Fservices\u002Fforget","services\u002Fforget",{"title":689,"path":690,"stem":691,"access":12},"governance","\u002Fservices\u002Fgovernance","services\u002Fgovernance",{"title":693,"path":694,"stem":695,"access":12},"guards","\u002Fservices\u002Fguards","services\u002Fguards",{"title":697,"path":698,"stem":699,"access":12},"history","\u002Fservices\u002Fhistory","services\u002Fhistory",{"title":701,"path":702,"stem":703,"access":12},"i18n","\u002Fservices\u002Fi18n","services\u002Fi18n",{"title":705,"path":706,"stem":707,"children":708,"access":12},"indexing","\u002Fservices\u002Findexing","services\u002Findexing",[709],{"title":705,"path":706,"stem":707,"access":12},{"title":711,"path":712,"stem":713,"access":12},"introspection","\u002Fservices\u002Fintrospection","services\u002Fintrospection",{"title":715,"path":716,"stem":717,"access":12},"joins","\u002Fservices\u002Fjoins","services\u002Fjoins",{"title":719,"path":720,"stem":721,"access":12},"lazy","\u002Fservices\u002Flazy","services\u002Flazy",{"title":723,"path":724,"stem":725,"access":12},"links","\u002Fservices\u002Flinks","services\u002Flinks",{"title":727,"path":728,"stem":729,"access":12},"live","\u002Fservices\u002Flive","services\u002Flive",{"title":731,"path":732,"stem":733,"access":12},"lookup","\u002Fservices\u002Flookup","services\u002Flookup",{"title":735,"path":736,"stem":737,"access":12},"manifest","\u002Fservices\u002Fmanifest","services\u002Fmanifest",{"title":739,"path":740,"stem":741,"access":12},"materialized-views","\u002Fservices\u002Fmaterialized-views","services\u002Fmaterialized-views",{"title":743,"path":744,"stem":745,"access":12},"Metadata ladder","\u002Fservices\u002Fmetadata-ladder","services\u002Fmetadata-ladder",{"title":747,"path":748,"stem":749,"access":12},"Metadata riders","\u002Fservices\u002Fmetadata-riders","services\u002Fmetadata-riders",{"title":751,"path":752,"stem":753,"access":12,"badge":754},"metrics","\u002Fservices\u002Fmetrics","services\u002Fmetrics","Planned",{"title":756,"path":757,"stem":758,"access":12,"badge":754},"migrations","\u002Fservices\u002Fmigrations","services\u002Fmigrations",{"title":760,"path":761,"stem":762,"access":12},"money","\u002Fservices\u002Fmoney","services\u002Fmoney",{"title":764,"path":765,"stem":766,"access":12},"numbering","\u002Fservices\u002Fnumbering","services\u002Fnumbering",{"title":768,"path":769,"stem":770,"access":12},"overlay-views","\u002Fservices\u002Foverlay-views","services\u002Foverlay-views",{"title":772,"path":773,"stem":774,"access":12,"badge":754},"partitioning","\u002Fservices\u002Fpartitioning","services\u002Fpartitioning",{"title":776,"path":777,"stem":778,"access":12},"periods","\u002Fservices\u002Fperiods","services\u002Fperiods",{"title":780,"path":781,"stem":782,"access":12},"persisted-schemas","\u002Fservices\u002Fpersisted-schemas","services\u002Fpersisted-schemas",{"title":784,"path":785,"stem":786,"access":12},"plaintext-bypass","\u002Fservices\u002Fplaintext-bypass","services\u002Fplaintext-bypass",{"title":788,"path":789,"stem":790,"access":12},"pod-redirect","\u002Fservices\u002Fpod-redirect","services\u002Fpod-redirect",{"title":792,"path":793,"stem":794,"access":12},"pod-signature","\u002Fservices\u002Fpod-signature","services\u002Fpod-signature",{"title":796,"path":797,"stem":798,"access":12},"policy","\u002Fservices\u002Fpolicy","services\u002Fpolicy",{"title":800,"path":801,"stem":802,"access":12},"portability","\u002Fservices\u002Fportability","services\u002Fportability",{"title":804,"path":805,"stem":806,"access":12},"reduce","\u002Fservices\u002Freduce","services\u002Freduce",{"title":808,"path":809,"stem":810,"access":12},"routing","\u002Fservices\u002Frouting","services\u002Frouting",{"title":812,"path":813,"stem":814,"access":12},"satellites","\u002Fservices\u002Fsatellites","services\u002Fsatellites",{"title":816,"path":817,"stem":818,"access":12},"schema-identifiers","\u002Fservices\u002Fschema-identifiers","services\u002Fschema-identifiers",{"title":820,"path":821,"stem":822,"access":12},"schema-update","\u002Fservices\u002Fschema-update","services\u002Fschema-update",{"title":824,"path":825,"stem":826,"access":12},"sealed-record","\u002Fservices\u002Fsealed-record","services\u002Fsealed-record",{"title":828,"path":829,"stem":830,"access":12},"sealing provider id","\u002Fservices\u002Fsealing-pid-stability","services\u002Fsealing-pid-stability",{"title":832,"path":833,"stem":834,"access":12},"search — full-text","\u002Fservices\u002Fsearch","services\u002Fsearch",{"title":836,"path":837,"stem":838,"access":12},"sequence","\u002Fservices\u002Fsequence","services\u002Fsequence",{"title":840,"path":841,"stem":842,"access":12},"session","\u002Fservices\u002Fsession","services\u002Fsession",{"title":844,"path":845,"stem":846,"access":12},"session-tiers","\u002Fservices\u002Fsession-tiers","services\u002Fsession-tiers",{"title":848,"path":849,"stem":850,"access":12},"shadow","\u002Fservices\u002Fshadow","services\u002Fshadow",{"title":852,"path":853,"stem":854,"access":12},"share-link","\u002Fservices\u002Fshare-link","services\u002Fshare-link",{"title":856,"path":857,"stem":858,"access":12},"snapshots","\u002Fservices\u002Fsnapshots","services\u002Fsnapshots",{"title":860,"path":861,"stem":862,"access":12},"store capabilities","\u002Fservices\u002Fstorage-capability-matrix","services\u002Fstorage-capability-matrix",{"title":864,"path":865,"stem":866,"access":12},"sync","\u002Fservices\u002Fsync","services\u002Fsync",{"title":868,"path":869,"stem":870,"access":12},"team","\u002Fservices\u002Fteam","services\u002Fteam",{"title":872,"path":873,"stem":874,"access":12},"tiers","\u002Fservices\u002Ftiers","services\u002Ftiers",{"title":876,"path":877,"stem":878,"access":12},"transactions","\u002Fservices\u002Ftransactions","services\u002Ftransactions",{"title":880,"path":881,"stem":882,"access":12},"partitions","\u002Fservices\u002Ftransferable-partitions","services\u002Ftransferable-partitions",{"title":884,"path":885,"stem":886,"access":12},"user-envelope","\u002Fservices\u002Fuser-envelope","services\u002Fuser-envelope",{"title":888,"path":889,"stem":890,"access":12,"badge":754},"validation","\u002Fservices\u002Fvalidation","services\u002Fvalidation",{"title":892,"path":893,"stem":894,"access":12},"vault-head","\u002Fservices\u002Fvault-head","services\u002Fvault-head",{"title":896,"path":897,"stem":898,"access":12},"via","\u002Fservices\u002Fvia","services\u002Fvia",{"title":900,"path":901,"stem":902,"access":12},"withdrawal","\u002Fservices\u002Fwithdrawal","services\u002Fwithdrawal",{"title":904,"path":905,"stem":906,"children":907},"Start","\u002Fstart","start",[908,911,915,919,923],{"title":10,"path":905,"stem":909,"access":12,"icon":910},"start\u002Findex","i-lucide-flag",{"title":912,"path":913,"stem":914,"access":12},"Install","\u002Fstart\u002Finstall","start\u002F1.install",{"title":916,"path":917,"stem":918,"access":12},"Your first vault","\u002Fstart\u002Ffirst-vault","start\u002F2.first-vault",{"title":920,"path":921,"stem":922,"access":12},"Your first service","\u002Fstart\u002Ffirst-service","start\u002F3.first-service",{"title":924,"path":925,"stem":926,"access":12},"See the ciphertext","\u002Fstart\u002Fsee-the-ciphertext","start\u002F4.see-the-ciphertext",{"id":928,"title":597,"access":12,"apiToc":929,"body":930,"category":1042,"description":1043,"extension":1044,"meta":1045,"navigation":1046,"nest":1047,"path":598,"seo":1048,"sinceVersion":1042,"status":1049,"stem":602,"subtitle":1042,"tier":1050,"tocExternal":1042,"__hash__":1051},"docs\u002Fservices\u002Findex.md",false,{"type":931,"value":932,"toc":1036},"minimark",[933,936,958,1013,1021,1026,1033],[934,935,597],"h1",{"id":599},[937,938,939,940,944,945,948,949,953,954,957],"p",{},"The opt-in ",[941,942,943],"code",{},"with*()"," catalog — capabilities you compose on top of the core. Each capability is its own tree-shakeable service behind a ",[941,946,947],{},"with\u003CService>()"," strategy seam: when you don't import the factory, none of that service's code reaches your bundle. You always opt in ",[950,951,952],"strong",{},"per service"," (",[941,955,956],{},"someStrategy: withSomething()",").",[937,959,960,961,964,965,968,969,968,972,968,975,968,978,968,981,968,984,968,987,968,990,993,994,997,998,1001,1002,1005,1006,1012],{},"The ",[950,962,963],{},"service layers"," — ",[941,966,967],{},"with-lookup",", ",[941,970,971],{},"with-commit",[941,973,974],{},"with-formula",[941,976,977],{},"with-shape",[941,979,980],{},"with-audit",[941,982,983],{},"with-party",[941,985,986],{},"with-sync",[941,988,989],{},"with-fork",[941,991,992],{},"with-store",", plus the single-purpose ",[941,995,996],{},"with-pod"," and ",[941,999,1000],{},"with-cargo"," — are simply how the service source is grouped into folders. They're a way to browse the catalog by intent, ",[950,1003,1004],{},"not"," something you import or configure: there is no layer factory or layer export, only per-service ones. The ",[1007,1008,1009],"a",{"href":897},[941,1010,1011],{},"via\u002F"," field features are a separate declaration surface (per field, not per vault).",[937,1014,1015,1016,1020],{},"Browse the pages in the sidebar, or jump into the ",[1007,1017,1019],{"href":1018},"\u002Forientation","Orientation map"," to pick capabilities and land on the exact pages you need.",[1022,1023,1025],"h2",{"id":1024},"the-catalog","The catalog",[937,1027,1028,1029,1032],{},"Every service page, grouped by its ",[941,1030,1031],{},"with-*"," source layer (and the Via field features) — generated live from the pages that exist.",[1034,1035],"service-catalog",{},{"title":1037,"searchDepth":1038,"depth":1038,"links":1039},"",3,[1040],{"id":1024,"depth":1041,"text":1025},2,null,"The opt-in with*() catalog, grouped by service layer.","md",{},{"title":10,"icon":603},"service",{"title":597,"description":1043},"stable","important","oh5MBOE147Z7mLvhwh4JR7JFqrEKHzpc_3HwkHob4-g",[1053,1475,2668,3266,4015,4811,5250,6221,6924,9841,11597,13385,16144,16954,19578,20133,21093,22194,22306,23841,23906,25749,28247,28913,29008,29835,30162,30458,30743,31364,31604,31953,32178,32383,32716,32938,33280,33458,33642,33822,33997,34282,34463,34641,34996,35290,36035,36748,37026,37246,37350,38188,38552,38840,39166,39457,40124,40545,41071,41317,41561,41681,41900,42065,42668,42926,43129,43615,43883,44306,44566,44892,45130,45329,45737,45992,46241,46570,46851,47081,47320,47958,48030,48219,48893,49109,49490,49697,49894,50198,51122,51212,51345,51532,51839,52051,52239,52876,53078,53293,53500,54519,54724,54927,55107,55471,55702,56148,56387,56494,56623,56789,57029,57235,57870,58206,61764,65329,68315,70013,71531,72352,73720,76502,77886,80338,83103,84210,84594,86389,87895,88081,89347,92314,94703,95630,96446,97689,99796,101616,103421,104582,107429,111613,114862,117123,120796,124854,126594,128423,129051,132445,135189,137849,141219,142677,146718,150367,151694,153985,154218,158386,160857,164028,164093,165457,168258,170698,172077,173753,174812,178326,178830,181322,181838,183014,183054,183090,185934,186935,188833,188888,191872,194525,195490,196133,196602,198552,199368,202447,205136,207220,207565,210548,212291,212807,215462,216472,218348,225188,226476,226877,228862,229590,232819,233743,235063,236719,238326,240458,240495,241134,242545,244562,244901,245344,245941,246587,246657],{"id":1054,"title":21,"access":12,"apiToc":929,"body":1055,"category":1042,"description":1470,"extension":1044,"meta":1471,"navigation":1042,"nest":1059,"path":22,"seo":1472,"sinceVersion":1042,"status":1049,"stem":23,"subtitle":1042,"tier":1473,"tocExternal":1042,"__hash__":1474},"docs\u002Farchitecture\u002F1.kernel\u002Fenclave.md",{"type":931,"value":1056,"toc":1463},[1057,1060,1081,1100,1104,1110,1186,1190,1207,1226,1233,1294,1297,1301,1333,1337,1428,1434,1452,1456],[934,1058,21],{"id":1059},"enclave",[1061,1062,1063],"blockquote",{},[937,1064,1065,1066,1069,1070,1073,1074,1076,1077,1080],{},"Mirrors ",[941,1067,1068],{},"packages\u002Fhub\u002Fsrc\u002Fcapsule\u002Fenclave-aes\u002F"," in the ",[941,1071,1072],{},"noy-db"," repo. Companion:\n",[1007,1075,37],{"href":38}," and the\n",[1007,1078,1079],{"href":6},"nested-cores frame"," this page's arrow-flip belongs to.",[937,1082,1083,1084,1087,1088,1090,1091,1095,1096,1099],{},"noy-db is an encrypted, offline-first document store whose trust boundary is the\n",[941,1085,1086],{},"@noy-db\u002Fhub"," package: all encryption happens inside the hub before data reaches any\nstorage backend. The ",[950,1089,1059],{}," is where that encryption actually lives — a minimal,\nself-contained folder ",[1092,1093,1094],"em",{},"inside"," the hub's kernel that owns the open↔close boundary: the\ncrypto functions, the keys, and the encrypted data format (codec). It depends on no other\nkernel part and exposes exactly one ",[950,1097,1098],{},"frozen interface"," (its barrel file), which makes it\nthe one part of the core that can be replaced wholesale. noy-db ships one enclave — full\nAES-256-GCM, zero-knowledge.",[1022,1101,1103],{"id":1102},"whats-inside-the-folder","What's inside the folder",[937,1105,1106,1109],{},[941,1107,1108],{},"capsule\u002Fenclave-aes\u002F"," maps to the source tree like this:",[1111,1112,1113,1127,1153,1164],"ul",{},[1114,1115,1116,1119,1120,953,1123,1126],"li",{},[941,1117,1118],{},"crypto.ts"," — the cryptographic primitives, thin wrappers over the Web Crypto API with\n",[950,1121,1122],{},"zero npm crypto dependencies",[941,1124,1125],{},"globalThis.crypto.subtle"," only): AES-256-GCM record\nand binary encryption, PBKDF2-SHA256 key derivation (600,000 iterations), AES-KW key\nwrapping, SHA-256 \u002F HMAC-SHA-256 hashing.",[1114,1128,1129,1132,1133,1136,1137,1140,1141,968,1144,968,1147,968,1150,957],{},[941,1130,1131],{},"record-keys\u002F"," — the engines built on those primitives: the per-record codec, ",[950,1134,1135],{},"sealing","\n(granting an ",[941,1138,1139],{},"at-*"," host you control decrypt access to a scoped slice), deterministic\nencryption (blind-equality lookup), the tombstone (erased-record residue), and the\nenvelope-body access helpers — the sanctioned door onto an envelope's protected fields\n(",[941,1142,1143],{},"_iv",[941,1145,1146],{},"_data",[941,1148,1149],{},"_cek",[941,1151,1152],{},"_sealed",[1114,1154,1155,1158,1159,1163],{},[941,1156,1157],{},"classify\u002F"," — blind-index, digest, and k-of-n verify-without-reveal primitives consumed\nby the ",[1007,1160,1161],{"href":634},[941,1162,633],{}," service.",[1114,1165,1166,1169,1170,1173,1174,1177,1178,1181,1182,1185],{},[941,1167,1168],{},"index.ts"," — the ",[950,1171,1172],{},"barrel",": the only file the rest of the hub is allowed to import from\nthis folder. The ",[941,1175,1176],{},"enclave-barrel-only"," check in ",[941,1179,1180],{},"scripts\u002Fcheck-architecture.mjs"," enforces\nthat mechanically, and the export list is frozen by a golden test\n(",[941,1183,1184],{},"packages\u002Fhub\u002F__tests__\u002Fenclave-surface-golden.test.ts",") — additive changes only.",[1022,1187,1189],{"id":1188},"the-level-0-arrow-flip","The level-0 arrow-flip",[937,1191,960,1192,1194,1195,1198,1199,1202,1203,1206],{},[1007,1193,1079],{"href":6}," composes the whole product as rings: kernel inside\nhub inside noy-db inside the product family. At every ring, the periphery binds the core's\nfrozen contract and the core never imports outward. At level 0 — the innermost ring — the\narrow flips: the kernel ",[1092,1196,1197],{},"spine"," (the always-on orchestration code) consumes the ",[1092,1200,1201],{},"enclave's","\nfrozen barrel. Because the crypto is the innermost core, the one-way law protects it from\neverything outside it, ",[950,1204,1205],{},"including the kernel spine itself",", which may only call through\nthe barrel, never reach past it.",[937,1208,1209,1210,1213,1214,1217,1218,1221,1222,1225],{},"That flip is the security payoff. Application code binds the hub's public API.\n",[950,1211,1212],{},"klum-db"," — the sister product that orchestrates a ",[1092,1215,1216],{},"fleet"," of vaults (federation,\ncustody, interchange) — binds the ",[941,1219,1220],{},"@noy-db\u002Fhub\u002Fcargo"," orchestration subpath. Neither has\nany code path to the enclave, so noy-db's promise that ",[950,1223,1224],{},"encryption cannot be disabled","\nstays absolute for both: there is nothing an app or a fleet orchestrator can call, wrap, or\nconfigure that would reach the keys or the crypto functions directly.",[937,1227,1228,1229,1232],{},"Each ring outward also ",[950,1230,1231],{},"sheds capability",", monotonically — the trust gradient:",[1234,1235,1236,1241,1247,1254,1278,1284],"ol",{},[1114,1237,1238,1240],{},[950,1239,21],{}," — raw keys.",[1114,1242,1243,1246],{},[950,1244,1245],{},"Kernel"," — DEK handles and plaintext, never raw key bytes.",[1114,1248,1249,953,1251,1253],{},[950,1250,597],{},[941,1252,1031],{}," layers) — plaintext, but opt-in and scoped.",[1114,1255,1256,953,1258,1261,1262,1261,1265,1261,1268,1261,1271,1261,1274,1277],{},[950,1257,37],{},[941,1259,1260],{},"to-"," \u002F ",[941,1263,1264],{},"on-",[941,1266,1267],{},"at-",[941,1269,1270],{},"in-",[941,1272,1273],{},"by-",[941,1275,1276],{},"as-"," packages) — ciphertext only,\nor post-decrypt data as an ordinary authorized consumer.",[1114,1279,1280,1283],{},[950,1281,1282],{},"Sister repos"," (klum-db, noy-db-to, noy-db-ui) — the published surface only.",[1114,1285,1286,1289,1290,1293],{},[950,1287,1288],{},"Docs"," — published ",[1092,1291,1292],{},"versions"," only.",[937,1295,1296],{},"Nothing at ring N can reach ring N−1's capabilities except through the seam between them,\nand no seam lets a caller climb back inward.",[1022,1298,1300],{"id":1299},"why-this-holds","Why this holds",[937,1302,1303,1304,1307,1308,1310,1311,1313,1314,1317,1318,1321,1322,1324,1325,1328,1329,1332],{},"The enclave is a ",[950,1305,1306],{},"minimal, self-contained"," unit inside the kernel — it has no dependency\non other kernel parts or on any ",[941,1309,1031],{}," service, and it exposes a ",[950,1312,1098],{},"\n(the enclave barrel, ",[941,1315,1316],{},"capsule\u002Fenclave-aes\u002Findex.ts",") that is the ",[1092,1319,1320],{},"only"," door the rest of the hub\nis allowed to use, mechanically enforced by the ",[941,1323,1176],{}," ",[941,1326,1327],{},"check:architecture","\nguard — see ",[1007,1330,1331],{"href":30},"Ports → Imports point inward only",". Nothing outside that\nfolder can reach past the barrel to touch keys, crypto functions, or the data format\ndirectly — there is no code path for an app, klum-db, or a service layer to bypass the\nenclave, only to call through it.",[1022,1334,1336],{"id":1335},"who-can-and-cant-swap-it","Who can (and can't) swap it",[1338,1339,1340,1356],"table",{},[1341,1342,1343],"thead",{},[1344,1345,1346,1350,1353],"tr",{},[1347,1348,1349],"th",{},"Consumer",[1347,1351,1352],{},"Binds",[1347,1354,1355],{},"Reaches",[1357,1358,1359,1385,1411],"tbody",{},[1344,1360,1361,1372,1382],{},[1362,1363,1364,1365,1368,1369],"td",{},"apps · ",[950,1366,1367],{},"noy-db-ui"," · ",[950,1370,1371],{},"noy-db-docs",[1362,1373,1374,1375,1377,1378,1381],{},"the hub's public API (+ chosen ",[950,1376,1047],{}," layers; ui also ",[941,1379,1380],{},"collection.describe()",")",[1362,1383,1384],{},"full zero-knowledge; cannot touch the enclave",[1344,1386,1387,1391,1404],{},[1362,1388,1389],{},[950,1390,1212],{},[1362,1392,1393,1395,1396,1399,1400,1403],{},[941,1394,1220],{}," (+ ",[941,1397,1398],{},"\u002Fpod",", the ",[941,1401,1402],{},".noydb"," container format)",[1362,1405,1406,1407,1410],{},"full zero-knowledge — orchestrates vaults ",[1092,1408,1409],{},"through"," the fixed enclave; never swaps it",[1344,1412,1413,1419,1425],{},[1362,1414,1415,1416],{},"an ",[950,1417,1418],{},"enclave fork",[1362,1420,1421,1422,1424],{},"the frozen enclave barrel, by replacing the ",[941,1423,1108],{}," folder in its own build",[1362,1426,1427],{},"supplies its own crypto engine, key story, and protected-body format",[937,1429,1430,1431,1433],{},"klum-db orchestrates serialized vaults across a fleet, but it does so ",[950,1432,1409],{}," the fixed\nenclave like any other consumer — cross-vault orchestration never implies crypto access.",[937,1435,1436,1437,1440,1441,1443,1444,1447,1448,1451],{},"Swapping the enclave is a ",[950,1438,1439],{},"source-level fork seam",", not something a package can opt into\nat runtime: a sister project replaces the ",[941,1442,1108],{}," folder wholesale in its own\nbuild and honors the frozen barrel (verified by the golden surface test and the\n",[941,1445,1446],{},"test-harnesses\u002Fenclave-conformance"," kit). The seam exists so that a sister project\nre-implementing the hub foundation against noy-db's published surface can make a different,\n",[1092,1449,1450],{},"explicit"," protection tradeoff for its own use case — an unencrypted envelope, say. Any such\ntradeoff is opt-in and scoped to the fork alone: it never weakens the invariant for noy-db,\nklum-db, or any other consumer, because they never load a fork's enclave.",[1022,1453,1455],{"id":1454},"next","Next",[937,1457,1458,1462],{},[1007,1459,1461],{"href":1460},".\u002Fforking-the-enclave","Forking the enclave →"," — the contract a fork implements to\nexercise this seam: the frozen barrel surface, refusal semantics, and the conformance kit.",{"title":1037,"searchDepth":1038,"depth":1038,"links":1464},[1465,1466,1467,1468,1469],{"id":1102,"depth":1041,"text":1103},{"id":1188,"depth":1041,"text":1189},{"id":1299,"depth":1041,"text":1300},{"id":1335,"depth":1041,"text":1336},{"id":1454,"depth":1041,"text":1455},"The kernel's crypto interior behind one frozen door — why encryption cannot be disabled, and why the enclave is the one swappable core.",{},{"title":21,"description":1470},"advanced","mo44eQ8jl1syq2vpjE41UidIxne63PYlNlZ3pqgBah0",{"id":1476,"title":25,"access":12,"apiToc":929,"body":1477,"category":1042,"description":2664,"extension":1044,"meta":2665,"navigation":1042,"nest":1059,"path":26,"seo":2666,"sinceVersion":1042,"status":1049,"stem":27,"subtitle":1042,"tier":1473,"tocExternal":1042,"__hash__":2667},"docs\u002Farchitecture\u002F1.kernel\u002Fforking-the-enclave.md",{"type":931,"value":1478,"toc":2648},[1479,1482,1494,1521,1525,1534,1590,1599,1612,1647,1661,1678,1682,1688,1803,1807,1823,2072,2079,2121,2163,2166,2189,2202,2206,2216,2363,2370,2451,2482,2489,2493,2496,2562,2569,2590,2596,2618,2622,2644],[934,1480,25],{"id":1481},"forking-the-enclave",[937,1483,1484,1485,1487,1488,1490,1491,1493],{},"noy-db is an encrypted, offline-first document store: every record is encrypted inside the\n",[941,1486,1086],{}," package before it reaches any storage backend. The ",[950,1489,1059],{}," is the one\nfolder inside the hub's kernel where that crypto actually lives — see\n",[1007,1492,21],{"href":22}," for why it is the one swappable core. This page is\nthe contract for actually swapping it: what a fork replaces, what it must keep, and how it\nproves conformance.",[1061,1495,1496,1502],{},[937,1497,1498,1501],{},[950,1499,1500],{},"Audience:"," a sister project that wants noy-db's spine (the always-on\nkernel orchestration code), services, and wire protocol, but its own crypto engine,\nin-memory protection policy, or key story.",[937,1503,1504,1324,1507,1509,1510,1513,1514,1517,1518,1520],{},[950,1505,1506],{},"Read first:",[1007,1508,21],{"href":22}," — the security invariant this\ncontract protects, and the ",[1007,1511,1512],{"href":6},"nested-cores"," level-0 arrow-flip it exercises\n(the spine consumes the enclave's frozen barrel, never the reverse). The reference\nenclave's own behavior is documented in ",[1007,1515,1516],{"href":66},"Core 02 — Enclave",";\nthe family-level frame is\n",[1007,1519,37],{"href":38},".",[1022,1522,1524],{"id":1523},"what-a-fork-replaces","What a fork replaces",[937,1526,1527,1533],{},[950,1528,1529,1530,1532],{},"Exactly one folder: ",[941,1531,1068],{}," — nothing else.","\nThat folder is noy-db's crypto interior:",[1111,1535,1536,1541,1555,1564,1576],{},[1114,1537,1538,1540],{},[941,1539,1118],{}," — the Web Crypto primitives (AES-256-GCM, PBKDF2-SHA256,\nAES-KW, HKDF, SHA-256\u002FHMAC-SHA-256).",[1114,1542,1543,1546,1547,1550,1551,1554],{},[941,1544,1545],{},"primitives.ts"," — those primitives assembled into the\n",[941,1548,1549],{},"CapsulePrimitives"," surface that ",[941,1552,1553],{},"makeCapsule()"," consumes (below).",[1114,1556,1557,1559,1560,1163],{},[941,1558,1157],{}," — the verify-without-reveal primitives (value digests,\nblind-index tags, constant-time compare, k-of-n evaluation) consumed by\nthe ",[1007,1561,1562],{"href":634},[941,1563,633],{},[1114,1565,1566,968,1569,968,1572,1575],{},[941,1567,1568],{},"broker\u002F",[941,1570,1571],{},"bound.ts",[941,1573,1574],{},"capabilities.ts"," — the capsule's bound form and\nthe capability set it declares.",[1114,1577,1578,1580,1581,1583,1584,1586,1587,1589],{},[941,1579,1168],{}," — the barrel: the ",[950,1582,1320],{}," file the rest of the hub is allowed\nto import from this folder (",[941,1585,1180],{},"'s\n",[941,1588,1176],{}," check enforces that mechanically).",[1591,1592,1594,1595,1598],"h3",{"id":1593},"the-envelope-format-is-not-in-that-folder-capsuleplumbing-is-shared","⭐ The envelope format is NOT in that folder — ",[941,1596,1597],{},"capsule\u002Fplumbing"," is shared",[937,1600,1601,1602,1607,1608,1611],{},"A fork is smaller than it looks, and smaller than this page used to say. The\nper-record codec, record-identity AAD, tombstones, sealed slots, rekeying,\nsigning and deterministic-slot handling — roughly three quarters of what used\nto sit inside the enclave — live in ",[950,1603,1604],{},[941,1605,1606],{},"capsule\u002Fplumbing\u002F",", because they\nencode ",[950,1609,1610],{},"hub's envelope format, not anyone's cipher",", and are therefore\nidentical for every capsule.",[937,1613,1614,1615,1617,1618,1621,1622,1625,1626,1629,1630,968,1633,1625,1636,1639,1640,1643,1644,1646],{},"So a fork does not reimplement them. It supplies ",[941,1616,1549],{},"\n(",[941,1619,1620],{},"capsule\u002Fcontract.ts",") — ",[941,1623,1624],{},"encrypt","\u002F",[941,1627,1628],{},"decrypt",", the deterministic and\nsealed-field key derivations, ",[941,1631,1632],{},"generateDEK",[941,1634,1635],{},"wrapCek",[941,1637,1638],{},"unwrapCek",", and the\n",[941,1641,1642],{},"classify"," mint\u002Fopen pair — and ",[941,1645,1553],{}," returns the whole surface\nbuilt on top.",[937,1648,1649,1650,1653,1654,1656,1657,1660],{},"⚠️ ",[950,1651,1652],{},"That is a boundary worth reading before you plan a fork."," Replacing the\ncipher is a ",[941,1655,1549],{}," implementation; changing the ",[1092,1658,1659],{},"envelope"," is a\nchange to hub's format, which is a family-wide seam and not a fork-local\ndecision at all.",[937,1662,1663,1664,1667,1668,1670,1671,1673,1674,1677],{},"A fork deletes this folder and drops in its own implementation — a\ndifferent KDF, a hardware-backed keystore, a post-quantum wrap algorithm, a\nkeyless HSM-backed design — as long as the replacement re-exports the same\nbarrel (below). Nothing outside ",[941,1665,1666],{},"capsule\u002Fenclave-aes\u002F**"," needs to change, and\n",[941,1669,1666],{}," itself imports nothing from ",[941,1672,1031],{}," services — the\n",[950,1675,1676],{},"self-containment guarantee",", enforced by the same architecture check in\nthe reverse direction — so there's no tangle to unwind on the way out.",[1022,1679,1681],{"id":1680},"what-a-fork-keeps","What a fork keeps",[937,1683,1684,1685,1687],{},"Everything outside ",[941,1686,1666],{}," is family-owned and untouched by a\nfork:",[1111,1689,1690,1739,1759,1772],{},[1114,1691,1692,964,1695,968,1698,968,1701,968,1704,1707,1708,968,1711,968,1714,968,1717,1720,1721,953,1724,968,1726,968,1728,968,1730,968,1733,968,1735,1738],{},[950,1693,1694],{},"The envelope protocol header",[941,1696,1697],{},"_noydb",[941,1699,1700],{},"_v",[941,1702,1703],{},"_ts",[941,1705,1706],{},"_by",",\n",[941,1709,1710],{},"_source",[941,1712,1713],{},"_sourceTs",[941,1715,1716],{},"_tier",[941,1718,1719],{},"_elevatedBy",". This is wire protocol, not\nprotection: stores, the sync engine, history, and klum's cross-vault\norchestration all read these fields directly, on every enclave. Only the\n",[950,1722,1723],{},"protected body",[941,1725,1143],{},[941,1727,1146],{},[941,1729,1149],{},[941,1731,1732],{},"_det",[941,1734,1152],{},[941,1736,1737],{},"_debug",")\nis enclave territory (see \"The body-access ratchet\" below) — a fork's\nenclave owns those fields' meaning, the header's meaning is fixed by the\nfamily.",[1114,1740,1741,964,1744,1746,1747,1746,1750,1746,1752,1755,1756,1758],{},[950,1742,1743],{},"The spine",[941,1745,62],{}," → ",[941,1748,1749],{},"openVault()",[941,1751,94],{},[941,1753,1754],{},"vault.collection\u003CT>()","\n→ ",[941,1757,103],{},", the owner keyring, schema\u002Frefs, basic query.",[1114,1760,1761,1764,1765,1767,1768,1771],{},[950,1762,1763],{},"The services"," — every ",[941,1766,1031],{}," subsystem (history, blobs, sync,\naggregate, crdt, team, …) and its ",[941,1769,1770],{},"with\u003CName>()"," strategy seam.",[1114,1773,1774,964,1777,1780,1781,1784,1785,1788,1789,1792,1793,1796,1797,1799,1800,1802],{},[950,1775,1776],{},"The ports",[941,1778,1779],{},"to-*"," storage destinations, ",[941,1782,1783],{},"in-*"," framework bindings,\n",[941,1786,1787],{},"on-*"," unlock primitives, ",[941,1790,1791],{},"as-*"," export formats, ",[941,1794,1795],{},"by-*"," transports,\n",[941,1798,1139],{}," sealing-key providers. None of these see plaintext or the\nprotected body directly; they call through the collection\u002Fvault API or\n(for ",[941,1801,1779],{},") receive ciphertext envelopes verbatim.",[1022,1804,1806],{"id":1805},"the-contract-surface","The contract surface",[937,1808,1809,1811,1812,1814,1815,1818,1819,1822],{},[941,1810,1316],{}," is the frozen barrel — every symbol below must\nexist in any fork's replacement folder, with the same name and shape.\nFrozen by ",[941,1813,1184],{}," against\n",[941,1816,1817],{},"enclave-surface.golden.json"," (49 value exports + 5 type exports today).\n",[950,1820,1821],{},"Additive changes only"," — the family can add a new export in a later\nrelease; removing or renaming one is breaking for every fork.",[1111,1824,1825,1844,1869,1919,1932,1959,1973,1985,2022],{},[1114,1826,1827,964,1830,953,1833,1836,1837,1840,1841,1843],{},[950,1828,1829],{},"Key type",[941,1831,1832],{},"EnclaveKey",[941,1834,1835],{},"= CryptoKey"," in the reference enclave). The\nopaque handle every barrel-facing signature traffics in. A fork\nredefines the alias to its own representation (",[941,1838,1839],{},"type EnclaveKey = null","\nfor a keyless design) and the spine type-checks against that keyless\nworld at fork-compile time — outside the enclave, ",[941,1842,1832],{}," is never\nconstructed, inspected, or serialized, only passed between barrel calls.",[1114,1845,1846,964,1849,1625,1851,1395,1853,968,1856,1707,1859,1862,1863,968,1866,1520],{},[950,1847,1848],{},"Crypto ops",[941,1850,1624],{},[941,1852,1628],{},[941,1854,1855],{},"Bytes",[941,1857,1858],{},"BytesWithAAD",[941,1860,1861],{},"Deterministic"," variants), ",[941,1864,1865],{},"sha256Hex",[941,1867,1868],{},"hmacSha256Hex",[1114,1870,1871,964,1874,1625,1877,1880,1881,1884,1885,1707,1887,1625,1890,968,1893,1625,1895,1625,1897,1707,1900,968,1903,1707,1906,1625,1909,1707,1912,1625,1915,1918],{},[950,1872,1873],{},"Key lifecycle",[941,1875,1876],{},"deriveKey",[941,1878,1879],{},"deriveSecretKey"," (PBKDF2-SHA256 →\nAES-KW \u002F AES-GCM; usage typed by ",[941,1882,1883],{},"SecretKeyUsage","), ",[941,1886,1632],{},[941,1888,1889],{},"wrapKey",[941,1891,1892],{},"unwrapKey",[941,1894,1635],{},[941,1896,1638],{},[941,1898,1899],{},"importCek",[941,1901,1902],{},"derivePresenceKey",[941,1904,1905],{},"deriveDeterministicKey",[941,1907,1908],{},"deriveSealedFieldKey",[941,1910,1911],{},"deriveSealedFieldKeyFromCek",[941,1913,1914],{},"resolveStableCek",[941,1916,1917],{},"rewrapBodyToDek",", base64 helpers, IV\u002Fsalt generators.",[1114,1920,1921,964,1924,1927,1928,1931],{},[950,1922,1923],{},"Record codec",[941,1925,1926],{},"RecordCodec",", the per-record encode\u002Fdecode engine\n(+ the ",[941,1929,1930],{},"SealedShredSlot"," type).",[1114,1933,1934,964,1937,1625,1940,1625,1943,1707,1946,968,1949,1952,1953,1955,1956,1958],{},[950,1935,1936],{},"Sealing",[941,1938,1939],{},"sealRecordToHost",[941,1941,1942],{},"revokeSealedRecord",[941,1944,1945],{},"rotateRecordCek",[941,1947,1948],{},"SealingContext",[941,1950,1951],{},"SEALED_CEK_NS",". ",[1092,1954,1936],{}," grants an ",[941,1957,1139],{}," host you\ncontrol decrypt access to a scoped slice of a record.",[1114,1960,1961,964,1963,1625,1966,968,1969,1972],{},[950,1962,1861],{},[941,1964,1965],{},"findByDet",[941,1967,1968],{},"queryByDet",[941,1970,1971],{},"DeterministicContext"," —\nblind-equality lookup over deterministically-encrypted fields.",[1114,1974,1975,964,1978,1625,1981,1984],{},[950,1976,1977],{},"Tombstone",[941,1979,1980],{},"isTombstone",[941,1982,1983],{},"buildTombstone"," — the residue an erased\nrecord leaves behind.",[1114,1986,1987,964,1990,968,1993,968,1996,1707,1999,2002,2003,1707,2006,968,2009,968,2012,2015,2016,2018,2019,2021],{},[950,1988,1989],{},"Classify",[941,1991,1992],{},"deriveVdigSlotKey",[941,1994,1995],{},"pbkdf2VerifyDigest",[941,1997,1998],{},"ctEqualTags",[941,2000,2001],{},"evaluateKofN"," (verify-digest primitives) and ",[941,2004,2005],{},"deriveClassifyIndexKey",[941,2007,2008],{},"deriveClassifyIndexSalt",[941,2010,2011],{},"mintBidxTag",[941,2013,2014],{},"computeBidxTarget","\n(blind-index primitives). The verify\u002Fmatch orchestration around them\nlives in the ",[941,2017,633],{}," service and is ",[1092,2020,1004],{}," part of the fork\ncontract — only these eight primitives are.",[1114,2023,2024,1169,2027,2030,2031,1625,2033,1625,2035,1625,2037,2039,2040],{},[950,2025,2026],{},"Envelope body (the protected-body access contract)",[950,2028,2029],{},"4 body\nhelpers",", the sanctioned door onto ",[941,2032,1143],{},[941,2034,1146],{},[941,2036,1149],{},[941,2038,1152],{}," for\neveryone outside the folder:\n",[1111,2041,2042,2048,2054,2066],{},[1114,2043,2044,2047],{},[941,2045,2046],{},"openEnvelopeJson(env, key, opts?): Promise\u003Cstring>"," — decrypt (or pass\nthrough, in plaintext mode) the body to its JSON text.",[1114,2049,2050,2053],{},[941,2051,2052],{},"writeEnvelopeBody(json, key, opts?): Promise\u003CPick\u003CEncryptedEnvelope, '_iv' | '_data' | '_cek'>>","\n— produce the protected-body fields for callers assembling envelopes.",[1114,2055,2056,964,2059,2062,2063,2065],{},[941,2057,2058],{},"hasPerRecordKey(env): boolean",[941,2060,2061],{},"_cek !== undefined",", without the\ncaller touching ",[941,2064,1149],{}," itself.",[1114,2067,2068,2071],{},[941,2069,2070],{},"envelopeBodyForHash(env): string"," — canonical body-bytes accessor for\nthe ledger hash chain.",[1591,2073,2075,2076],{"id":2074},"refusal-semantics-capsulenotsupportederror","Refusal semantics — ",[941,2077,2078],{},"CapsuleNotSupportedError",[937,2080,2081,2082,2085,2086,1707,2088,968,2091,953,2094,1625,2096,2098,2099,2101,2102,2104,2105,2108,2109,2112,2113,2116,2117,2120],{},"The barrel is frozen, but four groups are ",[950,2083,2084],{},"optional",": ",[941,2087,1135],{},[941,2089,2090],{},"deterministic",[941,2092,2093],{},"per-record-keys",[941,2095,1635],{},[941,2097,1638],{},"), and ",[941,2100,1642],{},".\nA fork whose enclave doesn't implement one of these refuses it by throwing\n",[941,2103,2078],{}," (code ",[941,2106,2107],{},"'CAPSULE_NOT_SUPPORTED'",", exported from\n",[941,2110,2111],{},"@noy-db\u002Fhub\u002Fcapsule",", carrying the refused ",[941,2114,2115],{},"group",") from ",[950,2118,2119],{},"every"," function\nin that group.",[937,2122,2123,2124,2127,2128,2131,2132,2135,2136,2139,2140,1625,2143,2146,2147,2104,2150,2153,2154,2156,2157,2160,2161,1520],{},"Since hub ",[941,2125,2126],{},"0.10.0-pre.2"," this is the canonical refusal, and hub itself throws it:\na capsule that lacks a group is refused ",[950,2129,2130],{},"at startup"," — by ",[941,2133,2134],{},"createNoydb()"," for\nan enabled service, by ",[941,2137,2138],{},"vault.collection()"," for a declared field form, and for\nthe ",[941,2141,2142],{},"authenticate",[941,2144,2145],{},"seal"," posture groups — before any keyring is read or written.\nThe older ",[941,2148,2149],{},"EnclaveNotSupportedError",[941,2151,2152],{},"'ENCLAVE_NOT_SUPPORTED'",", root\n",[941,2155,1086],{},") is ",[950,2158,2159],{},"deprecated"," and goes at a later breaking cut; the\nconformance kit accepts both codes, so a fork that still throws it keeps\npassing, but new code throws ",[941,2162,2078],{},[937,2164,2165],{},"Two hard rules:",[1234,2167,2168,2180],{},[1114,2169,2170,2173,2174,2176,2177,2179],{},[950,2171,2172],{},"Never a mix."," A group either fully works or fully refuses. A fork\nwhose ",[941,2175,1908],{}," refuses but whose ",[941,2178,1911],{},"\nsilently succeeds is non-conformant — see the conformance kit below,\nwhich exists specifically to catch this.",[1114,2181,2182,2185,2186,2188],{},[950,2183,2184],{},"The core groups must never throw it."," Crypto ops, key lifecycle, the\nrecord codec, the envelope-body helpers, and tombstone semantics are\nunconditional — every fork must implement them for real.\n",[941,2187,2078],{}," is a lever for the four optional groups only,\nnever a way to skip the core contract.",[937,2190,2191,2192,2194,2195,2197,2198,2201],{},"A service that receives ",[941,2193,2078],{}," (or the deprecated\n",[941,2196,2149],{},") must ",[950,2199,2200],{},"fail closed"," —\ntreat it as \"this operation is refused,\" never as \"no protection was\nnecessary.\" Silently proceeding as if the optional group succeeded would\nbe a data-protection regression, not a graceful degradation.",[1022,2203,2205],{"id":2204},"running-the-conformance-kit","Running the conformance kit",[937,2207,2208,2211,2212,2215],{},[941,2209,2210],{},"test-harnesses\u002Fenclave-conformance\u002F"," in the noy-db source tree is the\ncontract as an executable spec — the enclave equivalent of\n",[941,2213,2214],{},"test-harnesses\u002Fadapter-conformance",". It ships with the source a fork\nstarts from (it is not a published npm package), and it shape-checks the\nenclave module structurally — a fork never has to import noy-db's types.\nPoint it at any enclave module (the reference one, or a fork's):",[2217,2218,2222],"pre",{"className":2219,"code":2220,"language":2221,"meta":1037,"style":1037},"language-ts shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","import { runEnclaveConformance } from '@noy-db\u002Fports\u002Fcapsule'\nimport * as enclave from '.\u002Fmy-fork\u002Fcapsule\u002Fenclave-aes\u002Findex.js'\n\nrunEnclaveConformance(enclave, {\n  supports: { sealing: true, deterministic: true, perRecordKeys: false, classify: false },\n})\n","ts",[941,2223,2224,2257,2280,2286,2302,2354],{"__ignoreMap":1037},[2225,2226,2229,2233,2237,2241,2244,2247,2250,2254],"span",{"class":2227,"line":2228},"line",1,[2225,2230,2232],{"class":2231},"s7zQu","import",[2225,2234,2236],{"class":2235},"sMK4o"," {",[2225,2238,2240],{"class":2239},"sTEyZ"," runEnclaveConformance",[2225,2242,2243],{"class":2235}," }",[2225,2245,2246],{"class":2231}," from",[2225,2248,2249],{"class":2235}," '",[2225,2251,2253],{"class":2252},"sfazB","@noy-db\u002Fports\u002Fcapsule",[2225,2255,2256],{"class":2235},"'\n",[2225,2258,2259,2261,2264,2267,2270,2273,2275,2278],{"class":2227,"line":1041},[2225,2260,2232],{"class":2231},[2225,2262,2263],{"class":2235}," *",[2225,2265,2266],{"class":2231}," as",[2225,2268,2269],{"class":2239}," enclave ",[2225,2271,2272],{"class":2231},"from",[2225,2274,2249],{"class":2235},[2225,2276,2277],{"class":2252},".\u002Fmy-fork\u002Fcapsule\u002Fenclave-aes\u002Findex.js",[2225,2279,2256],{"class":2235},[2225,2281,2282],{"class":2227,"line":1038},[2225,2283,2285],{"emptyLinePlaceholder":2284},true,"\n",[2225,2287,2289,2293,2296,2299],{"class":2227,"line":2288},4,[2225,2290,2292],{"class":2291},"s2Zo4","runEnclaveConformance",[2225,2294,2295],{"class":2239},"(enclave",[2225,2297,2298],{"class":2235},",",[2225,2300,2301],{"class":2235}," {\n",[2225,2303,2305,2309,2312,2314,2317,2319,2323,2325,2328,2330,2332,2334,2337,2339,2342,2344,2347,2349,2351],{"class":2227,"line":2304},5,[2225,2306,2308],{"class":2307},"swJcz","  supports",[2225,2310,2311],{"class":2235},":",[2225,2313,2236],{"class":2235},[2225,2315,2316],{"class":2307}," sealing",[2225,2318,2311],{"class":2235},[2225,2320,2322],{"class":2321},"sfNiH"," true",[2225,2324,2298],{"class":2235},[2225,2326,2327],{"class":2307}," deterministic",[2225,2329,2311],{"class":2235},[2225,2331,2322],{"class":2321},[2225,2333,2298],{"class":2235},[2225,2335,2336],{"class":2307}," perRecordKeys",[2225,2338,2311],{"class":2235},[2225,2340,2341],{"class":2321}," false",[2225,2343,2298],{"class":2235},[2225,2345,2346],{"class":2307}," classify",[2225,2348,2311],{"class":2235},[2225,2350,2341],{"class":2321},[2225,2352,2353],{"class":2235}," },\n",[2225,2355,2357,2360],{"class":2227,"line":2356},6,[2225,2358,2359],{"class":2235},"}",[2225,2361,2362],{"class":2239},")\n",[937,2364,2365,2366,2369],{},"Set each ",[941,2367,2368],{},"supports.*"," flag to whether your enclave implements that optional\ngroup. The kit then verifies, in one pass:",[1111,2371,2372,2375,2389,2397,2407,2428,2435,2438,2441],{},[1114,2373,2374],{},"envelope body round-trip, in both encrypted and plaintext modes (write →\nopen → identical JSON; protocol header untouched),",[1114,2376,2377,2378,2381,2382,2384,2385,2388],{},"body-helper discriminants (",[941,2379,2380],{},"hasPerRecordKey"," reflects ",[941,2383,1149],{}," presence;\n",[941,2386,2387],{},"envelopeBodyForHash"," produces the canonical, key-order-independent body\nstring),",[1114,2390,2391,2392,1746,2394,2396],{},"tombstone semantics (",[941,2393,1983],{},[941,2395,1980],{}," true on an\nencrypted collection; live envelopes false; always false in plaintext\nmode),",[1114,2398,2399,2400,1746,2402,1746,2404,2406],{},"key lifecycle coherence (",[941,2401,1876],{},[941,2403,1889],{},[941,2405,1892],{}," round\ntrip recovers a usable key),",[1114,2408,2409,2412,2413,2415,2416,2419,2420,2423,2424,2427],{},[950,2410,2411],{},"group-consistency"," for each optional group — every function in the\ngroup works, or every function refuses via the\n",[941,2414,2152],{}," code; a mixed group fails the suite (see\n",[941,2417,2418],{},"assertGroupRefuses"," in ",[941,2421,2422],{},"test-harnesses\u002Fenclave-conformance\u002Fsrc\u002Findex.ts",",\nand ",[941,2425,2426],{},"self-test.test.ts",", which proves the checker itself catches a\ndeliberately mixed stub),",[1114,2429,2430,2431,2434],{},"determinism where claimed (",[941,2432,2433],{},"encryptDeterministic"," is stable for the same\ninput),",[1114,2436,2437],{},"the classify primitives where claimed (digest determinism and salt\nseparation, constant-time tag comparison, k-of-n bounds, AAD splice\nrejection, per-field\u002Fper-collection blind-index separation),",[1114,2439,2440],{},"no protected-body-field leaks in helper outputs, and",[1114,2442,2443,2444,2447,2448,2450],{},"known-answer test vectors decrypt correctly (plus, when\n",[941,2445,2446],{},"supports.sealing",", the sealed-slot vector decrypts under\n",[941,2449,1908],{},") — useful if your fork claims wire-compatibility\nwith noy-db's own codec.",[937,2452,2453,1324,2456,968,2458,1707,2460,2462,2463,2465,2466,1261,2469,2471,2472,1625,2474,1707,2476,1625,2478,2481],{},[950,2454,2455],{},"4 documented coverage omissions:",[941,2457,1914],{},[941,2459,1917],{},[941,2461,1965],{},", and ",[941,2464,1968],{}," take a collection-shaped context object\n(",[941,2467,2468],{},"StableCekDeps",[941,2470,1971],{},") rather than the plain key\u002Fenvelope\narguments a generic kit can construct portably. The kit exercises their\nlower-level primitives instead (",[941,2473,1635],{},[941,2475,1638],{},[941,2477,2433],{},[941,2479,2480],{},"decryptDeterministic",") — a fork's implementation of\nthose four functions is not independently conformance-checked today.",[937,2483,2484,2485,2488],{},"noy-db runs the kit in CI against its own reference enclave\n(",[941,2486,2487],{},"test-harnesses\u002Fenclave-conformance\u002Fsrc\u002Fconformance.test.ts","), proving the\nkit against a known-good implementation before any fork relies on it.",[1022,2490,2492],{"id":2491},"security-expectations","Security expectations",[937,2494,2495],{},"A fork's enclave inherits these expectations from the reference\nimplementation — they are the contract's security floor, not\nimplementation details a fork is free to relax:",[1111,2497,2498,2519,2536],{},[1114,2499,2500,2503,2504,2507,2508,2510,2511,2514,2515,2518],{},[950,2501,2502],{},"Fail closed."," A refusal on an optional group must stop the operation,\nnot silently downgrade to \"unprotected.\" Zero npm crypto dependencies\n(",[941,2505,2506],{},"crypto.subtle"," only) and no Node-only imports (",[941,2509,1666],{},"\nmust stay portable to browser\u002FWorker\u002FDeno\u002FBun) are both mechanically\nenforced for the reference enclave (the ",[941,2512,2513],{},"no-crypto-deps"," and\n",[941,2516,2517],{},"hub-portable"," architecture checks) and are strongly recommended\ndefaults for a fork.",[1114,2520,2521,2524,2525,2528,2529,1625,2532,2535],{},[950,2522,2523],{},"Sealed non-residency."," Sealed fields (",[941,2526,2527],{},"_sealed[field]",") must never be\nmaterialized into the plain working set by a barrel helper — no helper\nreturns key material, and ",[941,2530,2531],{},"openEnvelopeJson",[941,2533,2534],{},"writeEnvelopeBody"," operate\nonly on the fields they're contracted to touch.",[1114,2537,2538,1324,2544,2547,2548],{},[950,2539,2540,2543],{},[941,2541,2542],{},"forget()"," RAM-scrub.",[941,2545,2546],{},"vault.forget()"," (GDPR crypto-shred) drops the\nwrapped keys that make a subject's data recoverable; live in-memory\ncaches (DEKs, vault instance, active tier) are scrubbed as part of the\nsame call. A fork's key-lifecycle functions must preserve this: once a\nkey is dropped, nothing in the enclave should be able to reconstruct it\nfrom residual state.\n",[1092,2549,2550,2551,2553,2554,2557,2558,2561],{},"(Scope note: on an encrypted vault, ",[941,2552,2542],{}," is idempotent — a second\ncall for the same subject shreds nothing and does not throw. On an\nunencrypted collection, ",[941,2555,2556],{},"isTombstone()"," returns ",[941,2559,2560],{},"false"," by design —\nnothing is ever recognized as a tombstone — so the shred-once\nshort-circuit only exists in encrypted mode; plaintext mode is not\nzero-knowledge to begin with. A fork's tombstone predicate must preserve\nthe encrypted-mode contract.)",[1022,2563,2565,2566],{"id":2564},"the-body-access-ratchet-enclave-body-only","The body-access ratchet — ",[941,2567,2568],{},"enclave-body-only",[937,2570,2571,2572,2574,2575,2578,2579,2582,2583,2586,2587,2589],{},"The envelope splits into the protocol header (above) and the protected\nbody, but when the ",[941,2573,2568],{}," architecture check was introduced,\nits scan found ",[950,2576,2577],{},"337 direct body-field accesses across 53 non-enclave\nfiles"," already in the tree — too many to migrate in one pass. The check\nis therefore a ",[950,2580,2581],{},"ratchet, not a hard ban",": each offending file is\ngrandfathered in the ",[941,2584,2585],{},"PRE_EXISTING_BODY_ACCESS"," map in\n",[941,2588,1180],{}," at its scanned count. The rule is exact\nequality — a file's actual count must always match its grandfathered\nentry; raising it fails CI, lowering it without updating the map also\nfails CI (the map has to be edited to bank a real reduction). The map\nreaching empty is the migration's finish line.",[937,2591,2592,2595],{},[950,2593,2594],{},"Current remaining count: 268 direct accesses across 52 files"," (down\nfrom the original 337 across 53) — the counts shrink as grandfathered call\nsites move onto the 4 body helpers.",[937,2597,2598,2599,2601,2602,1261,2604,1261,2606,2608,2609,1625,2611,1625,2613,1625,2615,2617],{},"This ratchet is a fork's friend, not its problem: every grandfathered site\nthat shrinks is a call site that stopped assuming the envelope body's\nliteral shape and started going through ",[941,2600,2531],{}," \u002F\n",[941,2603,2534],{},[941,2605,2380],{},[941,2607,2387],{}," instead —\none fewer place a fork's own enclave has to structurally match the\nreference implementation's ",[941,2610,1143],{},[941,2612,1146],{},[941,2614,1149],{},[941,2616,1152],{}," layout. A fork\ndoesn't need to wait for the ratchet to reach zero to fork today (the\nbarrel contract is what's frozen, not the migration's completion), but a\nsmaller grandfather map means less code implicitly assumes noy-db's\nspecific envelope shape.",[1022,2619,2621],{"id":2620},"see-also","See also",[1111,2623,2624,2630,2637],{},[1114,2625,2626,2629],{},[1007,2627,1059],{"href":2628},".\u002Fenclave"," — the encryption core a fork must structurally match",[1114,2631,2632,2636],{},[1007,2633,2635],{"href":2634},".\u002F","kernel"," — the always-on foundation a fork reimplements",[1114,2638,2639,2643],{},[1007,2640,2642],{"href":2641},"..\u002Fports","ports"," — the published seams a fork binds instead of hub internals",[2645,2646,2647],"style",{},"html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .sfNiH, html code.shiki .sfNiH{--shiki-light:#FF5370;--shiki-default:#FF9CAC;--shiki-dark:#FF9CAC}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":1037,"searchDepth":1038,"depth":1038,"links":2649},[2650,2654,2655,2659,2660,2661,2663],{"id":1523,"depth":1041,"text":1524,"children":2651},[2652],{"id":1593,"depth":1038,"text":2653},"⭐ The envelope format is NOT in that folder — capsule\u002Fplumbing is shared",{"id":1680,"depth":1041,"text":1681},{"id":1805,"depth":1041,"text":1806,"children":2656},[2657],{"id":2074,"depth":1038,"text":2658},"Refusal semantics — CapsuleNotSupportedError",{"id":2204,"depth":1041,"text":2205},{"id":2491,"depth":1041,"text":2492},{"id":2564,"depth":1041,"text":2662},"The body-access ratchet — enclave-body-only",{"id":2620,"depth":1041,"text":2621},"The frozen-barrel contract a sister project implements to replace noy-db's crypto interior — surface, refusal semantics, conformance kit, body-access ratchet.",{},{"title":25,"description":2664},"jMAdwJZUqRodsWm-gCjZ-nUbSP0PWBmaHBI0PUH1pRA",{"id":2669,"title":1245,"access":12,"apiToc":929,"body":2670,"category":1042,"description":3261,"extension":1044,"meta":3262,"navigation":3263,"nest":2635,"path":15,"seo":3264,"sinceVersion":1042,"status":1049,"stem":16,"subtitle":1042,"tier":1473,"tocExternal":1042,"__hash__":3265},"docs\u002Farchitecture\u002F1.kernel\u002Findex.md",{"type":931,"value":2671,"toc":3255},[2672,2674,2684,2728,2731,2865,2869,2948,2952,2959,3073,3077,3088,3243,3245,3252],[934,2673,1245],{"id":2635},[1061,2675,2676],{},[937,2677,1065,2678,1069,2681,2683],{},[941,2679,2680],{},"packages\u002Fhub\u002Fsrc\u002Fkernel\u002F",[941,2682,1072],{}," repo.",[937,2685,960,2686,2688,2689,2691,2692,2695,2696,2699,2700,1399,2703,2706,2707,2710,2711,2716,2717,2720,2721,2724,2725,2727],{},[950,2687,2635],{}," is the always-on, mandatory core of ",[941,2690,1086],{}," — the code every consumer\nships no matter how the database is configured. It provides the ",[950,2693,2694],{},"vault"," (an encrypted\ndatabase instance), ",[950,2697,2698],{},"collections"," (the typed record sets inside a vault), the ",[950,2701,2702],{},"query\nengine",[950,2704,2705],{},"keyring"," (the per-vault structure that wraps each collection's data keys),\nand the ",[950,2708,2709],{},"service bus"," — an internal, per-instance event bus on which the kernel dispatches\nwrite-lifecycle events so that opt-in ",[1007,2712,2713,2715],{"href":34},[941,2714,1031],{}," services"," can\n",[1092,2718,2719],{},"observe"," completed writes (audit, devtools, sync dirty-marking) or ",[1092,2722,2723],{},"gate"," pending ones\n(write guards, closed accounting periods) without the kernel ever naming a service. Nothing\nin the kernel is tree-shaken; that is what distinguishes it from a ",[941,2726,1031],{}," service layer.",[937,2729,2730],{},"Even the smallest possible setup ships the whole kernel:",[2217,2732,2734],{"className":2219,"code":2733,"language":2221,"meta":1037,"style":1037},"import { createNoydb } from '@noy-db\u002Fhub'\n\n\u002F\u002F No store configured, no with-* services opted in — the kernel alone.\n\u002F\u002F `store` defaults to the built-in in-memory store (non-persistent).\nconst db = await createNoydb({ user: 'ada', secret: 'a-long-secret' })\n\nconst vault = await db.openVault('notes')\n\u002F\u002F → Vault — encrypted end to end, held in the built-in memory store\n",[941,2735,2736,2755,2759,2765,2770,2824,2828,2859],{"__ignoreMap":1037},[2225,2737,2738,2740,2742,2745,2747,2749,2751,2753],{"class":2227,"line":2228},[2225,2739,2232],{"class":2231},[2225,2741,2236],{"class":2235},[2225,2743,2744],{"class":2239}," createNoydb",[2225,2746,2243],{"class":2235},[2225,2748,2246],{"class":2231},[2225,2750,2249],{"class":2235},[2225,2752,1086],{"class":2252},[2225,2754,2256],{"class":2235},[2225,2756,2757],{"class":2227,"line":1041},[2225,2758,2285],{"emptyLinePlaceholder":2284},[2225,2760,2761],{"class":2227,"line":1038},[2225,2762,2764],{"class":2763},"sHwdD","\u002F\u002F No store configured, no with-* services opted in — the kernel alone.\n",[2225,2766,2767],{"class":2227,"line":2288},[2225,2768,2769],{"class":2763},"\u002F\u002F `store` defaults to the built-in in-memory store (non-persistent).\n",[2225,2771,2772,2776,2779,2782,2785,2787,2790,2793,2796,2798,2800,2803,2806,2808,2811,2813,2815,2818,2820,2822],{"class":2227,"line":2304},[2225,2773,2775],{"class":2774},"spNyl","const",[2225,2777,2778],{"class":2239}," db ",[2225,2780,2781],{"class":2235},"=",[2225,2783,2784],{"class":2231}," await",[2225,2786,2744],{"class":2291},[2225,2788,2789],{"class":2239},"(",[2225,2791,2792],{"class":2235},"{",[2225,2794,2795],{"class":2307}," user",[2225,2797,2311],{"class":2235},[2225,2799,2249],{"class":2235},[2225,2801,2802],{"class":2252},"ada",[2225,2804,2805],{"class":2235},"'",[2225,2807,2298],{"class":2235},[2225,2809,2810],{"class":2307}," secret",[2225,2812,2311],{"class":2235},[2225,2814,2249],{"class":2235},[2225,2816,2817],{"class":2252},"a-long-secret",[2225,2819,2805],{"class":2235},[2225,2821,2243],{"class":2235},[2225,2823,2362],{"class":2239},[2225,2825,2826],{"class":2227,"line":2356},[2225,2827,2285],{"emptyLinePlaceholder":2284},[2225,2829,2831,2833,2836,2838,2840,2843,2845,2848,2850,2852,2855,2857],{"class":2227,"line":2830},7,[2225,2832,2775],{"class":2774},[2225,2834,2835],{"class":2239}," vault ",[2225,2837,2781],{"class":2235},[2225,2839,2784],{"class":2231},[2225,2841,2842],{"class":2239}," db",[2225,2844,1520],{"class":2235},[2225,2846,2847],{"class":2291},"openVault",[2225,2849,2789],{"class":2239},[2225,2851,2805],{"class":2235},[2225,2853,2854],{"class":2252},"notes",[2225,2856,2805],{"class":2235},[2225,2858,2362],{"class":2239},[2225,2860,2862],{"class":2227,"line":2861},8,[2225,2863,2864],{"class":2763},"\u002F\u002F → Vault — encrypted end to end, held in the built-in memory store\n",[1022,2866,2868],{"id":2867},"spine-files","Spine files",[937,2870,2871,2872,2874,2875,2878,2879,2881,2882,953,2885,1746,2887,1884,2889,1617,2892,1746,2894,1884,2896,968,2899,968,2902,968,2905,1707,2908,968,2911,968,2914,968,2917,968,2920,1707,2923,968,2926,968,2929,2932,2933,2423,2936,2939,2940,2943,2944,2947],{},"The kernel folder is mostly flat files, not sub-packages — the ",[950,2873,1197],{},":\n",[941,2876,2877],{},"noydb.ts"," (the ",[941,2880,62],{}," entry point), ",[941,2883,2884],{},"vault.ts",[941,2886,1749],{},[941,2888,94],{},[941,2890,2891],{},"collection.ts",[941,2893,1754],{},[941,2895,103],{},[941,2897,2898],{},"types.ts",[941,2900,2901],{},"errors.ts",[941,2903,2904],{},"schema.ts",[941,2906,2907],{},"refs.ts",[941,2909,2910],{},"validation.ts",[941,2912,2913],{},"collection-config.ts",[941,2915,2916],{},"write-queue.ts",[941,2918,2919],{},"constants.ts",[941,2921,2922],{},"events.ts",[941,2924,2925],{},"debug.ts",[941,2927,2928],{},"env-check.ts",[941,2930,2931],{},"memory-store.ts"," (the built-in default store), ",[941,2934,2935],{},"sync-policy.ts",[941,2937,2938],{},"money-runtime.ts",". Individual ",[941,2941,2942],{},".ts"," files are illustrative and churn over time —\n",[950,2945,2946],{},"folders are the architecture",", and it is the folder level that this documentation tracks.",[1022,2949,2951],{"id":2950},"the-five-sibling-folders","The five sibling folders",[937,2953,2954,2955,2958],{},"Beside the spine files, ",[941,2956,2957],{},"kernel\u002F"," holds five sub-folders:",[1338,2960,2961,2971],{},[1341,2962,2963],{},[1344,2964,2965,2968],{},[1347,2966,2967],{},"Folder",[1347,2969,2970],{},"Role",[1357,2972,2973,3004,3028,3050,3060],{},[1344,2974,2975,2980],{},[1362,2976,2977],{},[941,2978,2979],{},"kernel\u002Fcache",[1362,2981,2982,2983,2986,2987,2989,2990,2993,2994,2997,2998,2993,3000,3003],{},"the kernel's internal record-level cache (LRU + eviction policy) — ",[950,2984,2985],{},"plaintext",", per-collection, inside the trust boundary, not pluggable. Not to be confused with a ",[941,2988,1779],{}," store: ",[941,2991,2992],{},"to-memory"," is a ",[1092,2995,2996],{},"ciphertext persistence backend"," (a storage destination that happens to be RAM); ",[941,2999,2979],{},[1092,3001,3002],{},"read-acceleration tier",". They coexist because they answer different questions — where encrypted data lives vs how fast decrypted reads come back.",[1344,3005,3006,3011],{},[1362,3007,3008],{},[941,3009,3010],{},"kernel\u002Fquery",[1362,3012,3013,3014,1261,3017,1261,3020,1261,3023,1261,3025,1520],{},"the query engine: the builder, the join engine (indexed nested-loop \u002F hash strategies), live-query subscriptions, and predicate matching that back ",[941,3015,3016],{},"where",[941,3018,3019],{},"orderBy",[941,3021,3022],{},"limit",[941,3024,715],{},[941,3026,3027],{},".live()",[1344,3029,3030,3035],{},[1362,3031,3032],{},[941,3033,3034],{},"kernel\u002Fvia",[1362,3036,3037,3038,3041,3042,3044,3045,3047,3048,1520],{},"the field-feature spine: the phased ",[941,3039,3040],{},"via()"," pipeline (compose, wiring, dispatch, reconcile) that runs every field feature — money, i18n, lookup, computed, classified, blob — plus the per-vault dependency graph and taint-binding that fold a derived field's security posture from its sources. The feature families themselves live in the top-level ",[941,3043,1011],{}," folder; ",[941,3046,3034],{}," is the machinery that runs them. See ",[1007,3049,896],{"href":897},[1344,3051,3052,3057],{},[1362,3053,3054],{},[941,3055,3056],{},"kernel\u002Futil",[1362,3058,3059],{},"small stateless helpers shared across the spine — discriminant typing and filename sanitization.",[1344,3061,3062,3067],{},[1362,3063,3064],{},[941,3065,3066],{},"capsule\u002Fenclave-aes",[1362,3068,3069,3070,1520],{},"the crypto interior — self-contained, frozen-interface, swappable. See ",[1007,3071,3072],{"href":22},"Enclave →",[1022,3074,3076],{"id":3075},"the-core-areas-c1c6","The core areas (C1–C6)",[937,3078,3079,3080,3083,3084,3087],{},"The kernel is what noy-db ",[950,3081,3082],{},"is",", not what it ",[950,3085,3086],{},"does",". Six areas are always loaded, totalling\nroughly 6,500 LOC out of the hub's ~28,000:",[1338,3089,3090,3103],{},[1341,3091,3092],{},[1344,3093,3094,3097,3100],{},[1347,3095,3096],{},"#",[1347,3098,3099],{},"Core area",[1347,3101,3102],{},"What it covers",[1357,3104,3105,3128,3141,3181,3198,3209],{},[1344,3106,3107,3110,3113],{},[1362,3108,3109],{},"C1",[1362,3111,3112],{},"Vault & Collection model",[1362,3114,3115,968,3117,968,3119,3122,3123,968,3125],{},[941,3116,62],{},[941,3118,94],{},[941,3120,3121],{},"Collection\u003CT>",", lifecycle, ",[941,3124,2847],{},[941,3126,3127],{},"listVaults",[1344,3129,3130,3133,3136],{},[1362,3131,3132],{},"C2",[1362,3134,3135],{},"Encryption",[1362,3137,3138,3139],{},"AES-256-GCM, PBKDF2-SHA256 (600K), AES-KW, KEK→DEK, envelope format — lives in ",[941,3140,3066],{},[1344,3142,3143,3146,3149],{},[1362,3144,3145],{},"C3",[1362,3147,3148],{},"Store contract",[1362,3150,3151,3152,3155,3156,1625,3159,1625,3162,1625,3165,1625,3168,1625,3171,3174,3175,3178,3179],{},"The 6-method ",[941,3153,3154],{},"NoydbStore"," interface (",[941,3157,3158],{},"get",[941,3160,3161],{},"put",[941,3163,3164],{},"delete",[941,3166,3167],{},"list",[941,3169,3170],{},"loadAll",[941,3172,3173],{},"saveAll",") — the contract door is ",[941,3176,3177],{},"port\u002Fto",", see ",[1007,3180,29],{"href":30},[1344,3182,3183,3186,3189],{},[1362,3184,3185],{},"C4",[1362,3187,3188],{},"Keyring & Permissions",[1362,3190,3191,3192,3194,3195,1381],{},"Owner-role keyring, DEK wrapping, single-user permission check (multi-user grant\u002Frevoke\u002Frotate lives in the ",[941,3193,868],{}," service, gated behind ",[941,3196,3197],{},"withTeam()",[1344,3199,3200,3203,3206],{},[1362,3201,3202],{},"C5",[1362,3204,3205],{},"Schema & Refs",[1362,3207,3208],{},"Typed records, foreign-key references, ref-mode dispatch (strict \u002F warn \u002F cascade)",[1344,3210,3211,3214,3217],{},[1362,3212,3213],{},"C6",[1362,3215,3216],{},"Query basics",[1362,3218,3219,1261,3221,1261,3223,1261,3225,1261,3228,1261,3231,1261,3234,1261,3237,3240,3241],{},[941,3220,3016],{},[941,3222,3019],{},[941,3224,3022],{},[941,3226,3227],{},"offset",[941,3229,3230],{},"toArray",[941,3232,3233],{},"first",[941,3235,3236],{},"count",[941,3238,3239],{},"scan"," — implemented in ",[941,3242,3010],{},[1022,3244,1455],{"id":1454},[937,3246,3247,964,3249,3251],{},[1007,3248,3072],{"href":22},[941,3250,3066],{},", the one swappable core.",[2645,3253,3254],{},"html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":1037,"searchDepth":1038,"depth":1038,"links":3256},[3257,3258,3259,3260],{"id":2867,"depth":1041,"text":2868},{"id":2950,"depth":1041,"text":2951},{"id":3075,"depth":1041,"text":3076},{"id":1454,"depth":1041,"text":1455},"The always-on core of @noy-db\u002Fhub — vault, collection, query, keyring, and the service bus. Mirrors packages\u002Fhub\u002Fsrc\u002Fkernel\u002F.",{},{"title":10,"icon":19},{"title":1245,"description":3261},"ZzZLQWKe6gBpcbgmgRxvj8dO_AbBheh4iEUgWj05Kqk",{"id":3267,"title":29,"access":12,"apiToc":929,"body":3268,"category":1042,"description":4011,"extension":1044,"meta":4012,"navigation":1042,"nest":2635,"path":30,"seo":4013,"sinceVersion":1042,"status":1049,"stem":31,"subtitle":1042,"tier":1473,"tocExternal":1042,"__hash__":4014},"docs\u002Farchitecture\u002F2.ports.md",{"type":931,"value":3269,"toc":4003},[3270,3272,3287,3321,3383,3420,3424,3438,3613,3617,3638,3645,3655,3693,3701,3735,3738,3786,3790,3797,3816,3896,3900,3907,3946,3956,3975,3986,3988,4000],[934,3271,29],{"id":2642},[1061,3273,3274],{},[937,3275,1065,3276,1069,3279,3281,3282,3284,3285,1520],{},[941,3277,3278],{},"packages\u002Fhub\u002Fsrc\u002Fport\u002F",[941,3280,1072],{}," repo. The one-way law it\nenforces is an invariant of the ",[1007,3283,1079],{"href":6},"; the family-level view\nis ",[1007,3286,37],{"href":38},[937,3288,3289,3290,3293,3294,3296,3297,3300,3301,3304,3305,3307,3308,3310,3311,3313,3314,3316,3317,3320],{},"A ",[950,3291,3292],{},"port"," is a named import subpath of ",[941,3295,1086],{}," — today exactly one, ",[941,3298,3299],{},"@noy-db\u002Fhub\u002Fto"," —\nthat a satellite ",[950,3302,3303],{},"family"," binds instead of reaching into hub internals. A ",[1092,3306,3303],{}," is one of\nthe preposition-named groups of external packages (",[941,3309,1779],{}," storage backends, ",[941,3312,1787],{}," unlock\nmethods, …; see ",[1007,3315,37],{"href":38},"). A port's export surface is\n",[950,3318,3319],{},"golden-frozen",": a checked-in JSON snapshot that a test compares against the real exports, so\nthe contract can only change deliberately — never by accident.",[937,3322,3323,3330,3331,968,3334,968,3337,968,3340,968,3343,968,3346,968,3349,3352,3353,3356,3357,3359,3360,3363,3364,3366,3367,3373,3374,3376,3377,3379,3380,3382],{},[950,3324,3325,3326,3329],{},"Only ",[941,3327,3328],{},"\u002Fto"," is still a family port."," The model once gave seven more families a subpath of\ntheir own (",[941,3332,3333],{},"\u002Fon",[941,3335,3336],{},"\u002Fat",[941,3338,3339],{},"\u002Fas",[941,3341,3342],{},"\u002Fby",[941,3344,3345],{},"\u002Fin",[941,3347,3348],{},"\u002Fwith",[941,3350,3351],{},"\u002Fui","). A consumer census found all seven\nhad ",[950,3354,3355],{},"zero importers"," — against 252 for the root ",[941,3358,1086],{}," barrel, 29 for ",[941,3361,3362],{},"\u002Fteam"," and 9 for\n",[941,3365,3328],{}," — so they were pruned in ",[1007,3368,3372],{"href":3369,"rel":3370},"https:\u002F\u002Fgithub.com\u002Fnoy-db\u002Fcore\u002Fpull\u002F866",[3371],"nofollow","noy-db#866"," after\n0.4.0-pre.5, taking the hub from 41 published subpaths to 34. Stores keep ",[941,3375,3328],{}," because they do\nbind a narrow ciphertext contract, one ",[941,3378,1327],{}," enforces; every other family depends\non the root ",[941,3381,1086],{}," package instead.",[2217,3384,3386],{"className":2219,"code":3385,"language":2221,"meta":1037,"style":1037},"\u002F\u002F A to-* store binds the \u002Fto port — never a hub-internal path:\nimport type { NoydbStore, EncryptedEnvelope } from '@noy-db\u002Fhub\u002Fto'\n",[941,3387,3388,3393],{"__ignoreMap":1037},[2225,3389,3390],{"class":2227,"line":2228},[2225,3391,3392],{"class":2763},"\u002F\u002F A to-* store binds the \u002Fto port — never a hub-internal path:\n",[2225,3394,3395,3397,3400,3402,3405,3407,3410,3412,3414,3416,3418],{"class":2227,"line":1041},[2225,3396,2232],{"class":2231},[2225,3398,3399],{"class":2231}," type",[2225,3401,2236],{"class":2235},[2225,3403,3404],{"class":2239}," NoydbStore",[2225,3406,2298],{"class":2235},[2225,3408,3409],{"class":2239}," EncryptedEnvelope",[2225,3411,2243],{"class":2235},[2225,3413,2246],{"class":2231},[2225,3415,2249],{"class":2235},[2225,3417,3299],{"class":2252},[2225,3419,2256],{"class":2235},[1022,3421,3423],{"id":3422},"the-port-table","The port table",[937,3425,3426,3427,3429,3430,3433,3434,3437],{},"Physically, ",[941,3428,3278],{}," holds ",[950,3431,3432],{},"six"," sibling folders. Five are published\nfamily ports, each frozen by a golden and backed by a conformance kit; ",[941,3435,3436],{},"with\u002F"," is internal.",[1338,3439,3440,3459],{},[1341,3441,3442],{},[1344,3443,3444,3447,3450,3453,3456],{},[1347,3445,3446],{},"Subpath",[1347,3448,3449],{},"Physical home",[1347,3451,3452],{},"Bound by",[1347,3454,3455],{},"Frozen by (golden)",[1347,3457,3458],{},"Conformance kit",[1357,3460,3461,3487,3513,3539,3565,3591],{},[1344,3462,3463,3467,3472,3477,3482],{},[1362,3464,3465],{},[941,3466,3328],{},[1362,3468,3469],{},[941,3470,3471],{},"port\u002Fto\u002F",[1362,3473,3474,3476],{},[941,3475,1779],{}," stores",[1362,3478,3479],{},[941,3480,3481],{},"to-surface.golden.json",[1362,3483,3484],{},[941,3485,3486],{},"@noy-db\u002Fports\u002Fto",[1344,3488,3489,3493,3498,3503,3508],{},[1362,3490,3491],{},[941,3492,3336],{},[1362,3494,3495],{},[941,3496,3497],{},"port\u002Fat\u002F",[1362,3499,3500,3502],{},[941,3501,1139],{}," sealing-key providers",[1362,3504,3505],{},[941,3506,3507],{},"at-surface.golden.json",[1362,3509,3510],{},[941,3511,3512],{},"@noy-db\u002Fports\u002Fat",[1344,3514,3515,3519,3524,3529,3534],{},[1362,3516,3517],{},[941,3518,3342],{},[1362,3520,3521],{},[941,3522,3523],{},"port\u002Fby\u002F",[1362,3525,3526,3528],{},[941,3527,1795],{}," session-share transports",[1362,3530,3531],{},[941,3532,3533],{},"by-surface.golden.json",[1362,3535,3536],{},[941,3537,3538],{},"@noy-db\u002Fports\u002Fby",[1344,3540,3541,3545,3550,3555,3560],{},[1362,3542,3543],{},[941,3544,3333],{},[1362,3546,3547],{},[941,3548,3549],{},"port\u002Fon\u002F",[1362,3551,3552,3554],{},[941,3553,1787],{}," unlock primitives",[1362,3556,3557],{},[941,3558,3559],{},"on-surface.golden.json",[1362,3561,3562],{},[941,3563,3564],{},"@noy-db\u002Fports\u002Fon",[1344,3566,3567,3571,3576,3581,3586],{},[1362,3568,3569],{},[941,3570,3339],{},[1362,3572,3573],{},[941,3574,3575],{},"port\u002Fas\u002F",[1362,3577,3578,3580],{},[941,3579,1791],{}," export\u002Fimport formats",[1362,3582,3583],{},[941,3584,3585],{},"as-surface.golden.json",[1362,3587,3588],{},[941,3589,3590],{},"@noy-db\u002Fports\u002Fas",[1344,3592,3593,3598,3603,3608,3611],{},[1362,3594,3595],{},[1092,3596,3597],{},"(unpublished)",[1362,3599,3600],{},[941,3601,3602],{},"port\u002Fwith\u002F",[1362,3604,3605,3606,2715],{},"the kernel spine's hook seam into the ",[941,3607,1031],{},[1362,3609,3610],{},"—",[1362,3612,3610],{},[1591,3614,3616],{"id":3615},"these-four-came-back-and-why-that-is-not-a-reversal","These four came back, and why that is not a reversal",[937,3618,3619,3620,968,3622,968,3624,968,3626,997,3628,3630,3631,3634,3635,3637],{},"An earlier revision of this page recorded the 0.4 prune: ",[941,3621,3339],{},[941,3623,3336],{},[941,3625,3345],{},[941,3627,3333],{},[941,3629,3351],{},"\nwere ",[950,3632,3633],{},"deleted outright"," because no family bound them, and ",[941,3636,3342],{}," was demoted to internal for\nthe same reason. That was correct — a subpath with zero importers is a promise nobody is\nkeeping.",[937,3639,3640,3641,3644],{},"Four of them returned in the 0.7 pre line, and the difference is the bar they had to clear:\n",[950,3642,3643],{},"a contract, a published conformance kit, and real binders."," Every one of those kits found a\ndefect in a binding it was written against, which is the argument for writing the kit before\nshipping the seam rather than after.",[937,3646,3647,997,3649,3651,3652,3654],{},[941,3648,3345],{},[941,3650,3351],{}," did ",[950,3653,1004],{}," return, and for different reasons:",[1111,3656,3657,3667],{},[1114,3658,3659,3663,3664,3666],{},[950,3660,3661],{},[941,3662,3345],{}," was never re-proposed; ",[941,3665,1783],{}," framework bindings consume the root barrel.",[1114,3668,3669,3673,3674,3677,3678,3681,3682,3687,3688,1069,3691,2683],{},[950,3670,3671],{},[941,3672,3351],{}," will not exist. A UI is a ",[1092,3675,3676],{},"driving"," adapter — it calls hub, and hub never invokes a\nUI — so a locator or factory would have no caller. ",[941,3679,3680],{},"ui-*"," binds\n",[1007,3683,3684],{"href":30},[941,3685,3686],{},"\u002Fintrospection",", which is golden-frozen like a port. See\n",[941,3689,3690],{},"docs\u002Fadr\u002F0005-no-ui-port.md",[941,3692,1072],{},[1061,3694,3695],{},[937,3696,3697,3700],{},[950,3698,3699],{},"The rule the sequence teaches: the seam follows the port, never precedes it."," Extract the\ncontract from two working consumers, publish a kit, then ship the subpath.",[937,3702,3703,3704,3707,3708,3711,3712,1617,3715,3718,3719,953,3721,3724,3725,968,3728,3731,3732,3734],{},"Two more ports are physical ",[1092,3705,3706],{},"layer"," ports rather than ",[941,3709,3710],{},"port\u002F"," siblings — ",[941,3713,3714],{},"\u002Fcargo",[941,3716,3717],{},"with-cargo\u002F",", klum-db lobby) and ",[941,3720,1398],{},[941,3722,3723],{},"with-pod\u002F",", klum-db + backup consumers), frozen\nthe same way (",[941,3726,3727],{},"cargo-surface.golden.json",[941,3729,3730],{},"pod-surface.golden.json",") — see\n",[1007,3733,33],{"href":34}," for those two folders.",[937,3736,3737],{},"Three contracts that aren't family ports are golden-frozen by the same mechanism:",[1111,3739,3740,3754,3770],{},[1114,3741,3742,3743,953,3746,3749,3750,3753],{},"the ",[950,3744,3745],{},"root barrel",[941,3747,3748],{},"src\u002Findex.ts",", the flat app-developer API) —\n",[941,3751,3752],{},"root-barrel-surface.golden.json",", 472 values \u002F 427 types;",[1114,3755,3742,3756,953,3759,3761,3762,3764,3765,1621,3767,3769],{},[950,3757,3758],{},"enclave barrel",[941,3760,1316],{},", the fork-swap contract a sister project\nreplaces wholesale; the ",[1092,3763,1059],{}," is the hub's self-contained crypto interior,\nsee ",[1007,3766,21],{"href":22},[941,3768,1817],{},";",[1114,3771,3742,3772,2878,3775,1625,3777,1625,3779,3781,3782,3785],{},[950,3773,3774],{},"kernel API manifest",[941,3776,62],{},[941,3778,94],{},[941,3780,103],{}," public prototype surface) —\n",[941,3783,3784],{},"kernel-api.golden.json",": Noydb 53 \u002F Vault 83 \u002F Collection 86 methods.",[1022,3787,3789],{"id":3788},"imports-point-inward-only","Imports point inward only",[937,3791,3792],{},[950,3793,3794,1520],{},[941,3795,3796],{},"family → port → service layer → kernel spine → enclave",[937,3798,3799,3800,1324,3802,3805,3806,3809,3810,3812,3813,3815],{},"This is the ",[1007,3801,1512],{"href":6},[950,3803,3804],{},"one-way law",": at every seam the\nperiphery binds the core's frozen contract and the core never imports outward — with exactly\n",[950,3807,3808],{},"one deliberate arrow-flip"," at the innermost seam. The kernel ",[1092,3811,1197],{}," consumes the\n",[1092,3814,1201],{}," frozen barrel (the last hop above), because the crypto is the innermost core,\nprotected from everything outside it — including the rest of the kernel.",[1111,3817,3818,3864,3876],{},[1114,3819,960,3820,3823,3824,3826,3827,968,3829,968,3831,968,3833,1707,3835,3837,3838,968,3841,2462,3844,3847,3848,3850,3851,3853,3854,3856,3857,3859,3860,3863],{},[950,3821,3822],{},"kernel spine"," — everything under ",[941,3825,2680],{}," outside the enclave:\nthe flat orchestration files (",[941,3828,2877],{},[941,3830,2884],{},[941,3832,2891],{},[941,3834,2898],{},[941,3836,2901],{},", …) plus the ",[941,3839,3840],{},"query\u002F",[941,3842,3843],{},"cache\u002F",[941,3845,3846],{},"util\u002F"," sub-folders (see\n",[1007,3849,1245],{"href":15}," for the full list) — is restricted: it may import\n",[941,3852,3602],{}," (the hook seam) and ",[941,3855,1108],{}," (only via its barrel), but never\nanother port folder and never a ",[941,3858,1031],{}," service directly (a dynamic ",[941,3861,3862],{},"import()"," is the\nsanctioned escape hatch).",[1114,3865,3866,3869,3870,3872,3873,3875],{},[950,3867,3868],{},"Ports are views, not new code paths."," A port re-exports a contract whose implementation\nlives in the kernel or a service — importing the port ",[1092,3871,3082],{}," the opt-in, so this costs nothing\nin tree-shaking. Port folders may not import each other; ",[941,3874,3602],{}," is the one exception,\nalways an allowed target.",[1114,3877,3878,3879,3881,3882,3887,3888,3892,3893,3895],{},"Enforced mechanically by two ",[941,3880,1327],{}," checks: ",[950,3883,3884],{},[941,3885,3886],{},"port-layering"," (the\nspine\u002Fport import-direction rules above) and ",[950,3889,3890],{},[941,3891,1176],{}," (nothing outside\n",[941,3894,1666],{}," reaches past its barrel).",[1022,3897,3899],{"id":3898},"removed-and-deprecated-aliases","Removed and deprecated aliases",[937,3901,3902,3903,3906],{},"Three old subpaths were ",[950,3904,3905],{},"removed in 0.3.0-pre.11"," — import the canonical seams instead:",[1111,3908,3909,3917,3938],{},[1114,3910,3911,1746,3914,3916],{},[941,3912,3913],{},"\u002Fadapter",[941,3915,3328],{}," (the store contract).",[1114,3918,3919,1746,3922,2878,3924,3927,3928,3930,3931,3933,3934,3937],{},[941,3920,3921],{},"\u002Fdescribe",[941,3923,3351],{},[941,3925,3926],{},"describe()"," contract) — but ",[941,3929,3351],{}," has since been pruned in turn, so\nthe current home is the root ",[941,3932,1086],{}," barrel, which is what ",[941,3935,3936],{},"@noy-db\u002Fui"," binds.",[1114,3939,3940,1746,3943,3945],{},[941,3941,3942],{},"\u002Fkernel",[941,3944,3714],{},", which carries the same surface plus the orchestration delta.",[937,3947,3948,3949,953,3952,3955],{},"One deprecated alias remains, kept working out of ",[941,3950,3951],{},"src\u002Flegacy\u002F",[941,3953,3954],{},"@deprecated"," JSDoc naming the\nreplacement, removed only on a coordinated version bump once klum-db migrates off it):",[1111,3957,3958],{},[1114,3959,3960,953,3963,3966,3967,3969,3970,3972,3973,1520],{},[941,3961,3962],{},"\u002Fbundle",[941,3964,3965],{},"legacy\u002Fbundle.ts",") → split in two: the ",[941,3968,1402],{}," artifact format lives at ",[941,3971,1398],{},",\nthe partition-transfer ops at ",[941,3974,3714],{},[937,3976,3977,3978,3981,3982,3985],{},"There is no ",[941,3979,3980],{},"\u002Fstore"," subpath: store plumbing (routing, middleware, lazy loading) lives in\n",[941,3983,3984],{},"with-store\u002F"," and is exported only through the root barrel.",[1022,3987,1455],{"id":1454},[937,3989,3990,3993,3994,3996,3997,3999],{},[1007,3991,3992],{"href":34},"Service layers →"," — the 10 ",[941,3995,1031],{}," folders each port's\n",[941,3998,3348],{}," hook seam gates into.",[2645,4001,4002],{},"html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":1037,"searchDepth":1038,"depth":1038,"links":4004},[4005,4008,4009,4010],{"id":3422,"depth":1041,"text":3423,"children":4006},[4007],{"id":3615,"depth":1038,"text":3616},{"id":3788,"depth":1041,"text":3789},{"id":3898,"depth":1041,"text":3899},{"id":1454,"depth":1041,"text":1455},"The golden-frozen contract subpath the to-* stores bind, and the two internal port folders beside it — mirrors packages\u002Fhub\u002Fsrc\u002Fport\u002F.",{},{"title":29,"description":4011},"yIQNrjTiZQPzJh7PtQCpqOLA9jxM5yET7AYeYaN16Fw",{"id":4016,"title":33,"access":12,"apiToc":929,"body":4017,"category":1042,"description":4807,"extension":1044,"meta":4808,"navigation":1042,"nest":1047,"path":34,"seo":4809,"sinceVersion":1042,"status":1049,"stem":35,"subtitle":1042,"tier":1473,"tocExternal":1042,"__hash__":4810},"docs\u002Farchitecture\u002F3.service-layers.md",{"type":931,"value":4018,"toc":4800},[4019,4022,4040,4064,4068,4086,4111,4263,4267,4287,4593,4639,4696,4703,4772,4774,4779,4798],[934,4020,33],{"id":4021},"service-layers",[1061,4023,4024],{},[937,4025,4026,4027,4030,4031,4034,4035,4037,4038,1520],{},"Mirrors the eleven ",[941,4028,4029],{},"with-*\u002F"," folders in ",[941,4032,4033],{},"packages\u002Fhub\u002Fsrc\u002F",". The full opt-in\ncatalog — every service with its subpath and ",[941,4036,1770],{}," factory — is\n",[1007,4039,597],{"href":598},[937,4041,4042,4044,4045,4047,4048,4050,4051,4053,4054,4057,4058,4060,4061,4063],{},[941,4043,1086],{}," is a minimal always-on core plus opt-in ",[950,4046,599],{},": tree-shakeable\ncapabilities (history, indexing, blobs, sync, …) that you enable one at a time by passing a\n",[941,4049,947],{}," factory to ",[941,4052,2134],{},". In the source tree those services are grouped\ninto eleven ",[941,4055,4056],{},"with-\u003Cname>\u002F"," folders — the ",[950,4059,963],{},". This page explains what a layer\nfolder is, and — just as important — what it is not. The developer-facing view of the same\nservices is the ",[1007,4062,597],{"href":598}," catalog.",[1022,4065,4067],{"id":4066},"layers-are-a-folder-grouping-not-a-runtime-seam","Layers are a folder grouping, not a runtime seam",[937,4069,3289,4070,953,4072,4074,4075,4077,4078,997,4080,4082,4083,4085],{},[950,4071,3706],{},[941,4073,4056],{},") is the organizational grouping of the ",[941,4076,1031],{}," units — the\nservice folders, plus the two single-purpose port folders ",[941,4079,1000],{},[941,4081,996],{},". It is\na soft bracket today; it may later harden into a real plug-in seam, but it is ",[950,4084,1004],{}," one now.",[937,4087,4088,4089,4092,4093,4096,4097,4100,4101,4104,4105,4107,4108,4110],{},"Concretely, that means ",[950,4090,4091],{},"there is nothing in the runtime or code at the layer level",": no\nlayer factory, no layer subpath export, no layer API, no ",[941,4094,4095],{},"withLayer()",". A layer is purely the\nparent folder its service files sit in. Every capability is a ",[950,4098,4099],{},"per-service"," unit: each\nservice keeps its own ",[941,4102,4103],{},"@noy-db\u002Fhub\u002F\u003Cservice>"," subpath export and its own ",[941,4106,947],{},"\nfactory registered at ",[941,4109,2134],{},". You never import or configure a layer — only the\nservices inside it:",[2217,4112,4114],{"className":2219,"code":4113,"language":2221,"meta":1037,"style":1037},"import { createNoydb } from '@noy-db\u002Fhub'\nimport { withHistory } from '@noy-db\u002Fhub\u002Fhistory' \u002F\u002F lives in with-commit\u002F\nimport { withIndexing } from '@noy-db\u002Fhub\u002Findexing' \u002F\u002F lives in with-lookup\u002F\n\nconst db = await createNoydb({\n  user: 'me',\n  historyStrategy: withHistory(),\n  indexingStrategy: withIndexing(),\n})\n\u002F\u002F → two services from two different layers, wired identically per-service;\n\u002F\u002F   \"with-commit\" and \"with-lookup\" never appear anywhere in application code\n",[941,4115,4116,4134,4157,4180,4184,4201,4217,4231,4244,4251,4257],{"__ignoreMap":1037},[2225,4117,4118,4120,4122,4124,4126,4128,4130,4132],{"class":2227,"line":2228},[2225,4119,2232],{"class":2231},[2225,4121,2236],{"class":2235},[2225,4123,2744],{"class":2239},[2225,4125,2243],{"class":2235},[2225,4127,2246],{"class":2231},[2225,4129,2249],{"class":2235},[2225,4131,1086],{"class":2252},[2225,4133,2256],{"class":2235},[2225,4135,4136,4138,4140,4143,4145,4147,4149,4152,4154],{"class":2227,"line":1041},[2225,4137,2232],{"class":2231},[2225,4139,2236],{"class":2235},[2225,4141,4142],{"class":2239}," withHistory",[2225,4144,2243],{"class":2235},[2225,4146,2246],{"class":2231},[2225,4148,2249],{"class":2235},[2225,4150,4151],{"class":2252},"@noy-db\u002Fhub\u002Fhistory",[2225,4153,2805],{"class":2235},[2225,4155,4156],{"class":2763}," \u002F\u002F lives in with-commit\u002F\n",[2225,4158,4159,4161,4163,4166,4168,4170,4172,4175,4177],{"class":2227,"line":1038},[2225,4160,2232],{"class":2231},[2225,4162,2236],{"class":2235},[2225,4164,4165],{"class":2239}," withIndexing",[2225,4167,2243],{"class":2235},[2225,4169,2246],{"class":2231},[2225,4171,2249],{"class":2235},[2225,4173,4174],{"class":2252},"@noy-db\u002Fhub\u002Findexing",[2225,4176,2805],{"class":2235},[2225,4178,4179],{"class":2763}," \u002F\u002F lives in with-lookup\u002F\n",[2225,4181,4182],{"class":2227,"line":2288},[2225,4183,2285],{"emptyLinePlaceholder":2284},[2225,4185,4186,4188,4190,4192,4194,4196,4198],{"class":2227,"line":2304},[2225,4187,2775],{"class":2774},[2225,4189,2778],{"class":2239},[2225,4191,2781],{"class":2235},[2225,4193,2784],{"class":2231},[2225,4195,2744],{"class":2291},[2225,4197,2789],{"class":2239},[2225,4199,4200],{"class":2235},"{\n",[2225,4202,4203,4206,4208,4210,4213,4215],{"class":2227,"line":2356},[2225,4204,4205],{"class":2307},"  user",[2225,4207,2311],{"class":2235},[2225,4209,2249],{"class":2235},[2225,4211,4212],{"class":2252},"me",[2225,4214,2805],{"class":2235},[2225,4216,1707],{"class":2235},[2225,4218,4219,4222,4224,4226,4229],{"class":2227,"line":2830},[2225,4220,4221],{"class":2307},"  historyStrategy",[2225,4223,2311],{"class":2235},[2225,4225,4142],{"class":2291},[2225,4227,4228],{"class":2239},"()",[2225,4230,1707],{"class":2235},[2225,4232,4233,4236,4238,4240,4242],{"class":2227,"line":2861},[2225,4234,4235],{"class":2307},"  indexingStrategy",[2225,4237,2311],{"class":2235},[2225,4239,4165],{"class":2291},[2225,4241,4228],{"class":2239},[2225,4243,1707],{"class":2235},[2225,4245,4247,4249],{"class":2227,"line":4246},9,[2225,4248,2359],{"class":2235},[2225,4250,2362],{"class":2239},[2225,4252,4254],{"class":2227,"line":4253},10,[2225,4255,4256],{"class":2763},"\u002F\u002F → two services from two different layers, wired identically per-service;\n",[2225,4258,4260],{"class":2227,"line":4259},11,[2225,4261,4262],{"class":2763},"\u002F\u002F   \"with-commit\" and \"with-lookup\" never appear anywhere in application code\n",[1022,4264,4266],{"id":4265},"the-eleven-layers","The eleven layers",[937,4268,4269,4270,4273,4274,4276,4277,1707,4279,4281,4282,4284,4285,1520],{},"Each layer folder groups services with a common intent (see the\n",[1007,4271,4272],{"href":598},"Services catalog"," for every service with its subpath,\n",[941,4275,1770],{}," factory, and cost). Two layers (",[941,4278,1000],{},[941,4280,996],{},") are single-purpose ",[950,4283,2642],{}," — contract subpaths an external consumer binds —\nrather than service groupings; see ",[1007,4286,29],{"href":30},[1338,4288,4289,4305],{},[1341,4290,4291],{},[1344,4292,4293,4296,4299],{},[1347,4294,4295],{},"Layer",[1347,4297,4298],{},"Groups",[1347,4300,4301,4304],{},[941,4302,4303],{},"\u003Cservice>\u002F"," subfolders",[1357,4306,4307,4334,4378,4400,4417,4436,4458,4483,4514,4537,4557],{},[1344,4308,4309,4313,4316],{},[1362,4310,4311],{},[941,4312,980],{},[1362,4314,4315],{},"Time, audit & compliance — attestation, consent logging, erasure, write guards, accounting periods, data portability",[1362,4317,4318,968,4320,968,4322,968,4324,968,4326,968,4328,968,4330,968,4332],{},[941,4319,609],{},[941,4321,641],{},[941,4323,685],{},[941,4325,693],{},[941,4327,776],{},[941,4329,800],{},[941,4331,824],{},[941,4333,872],{},[1344,4335,4336,4340,4350],{},[1362,4337,4338],{},[941,4339,1000],{},[1362,4341,960,4342,4344,4345,4349],{},[941,4343,3714],{}," port — the multi-vault orchestration seam bound by ",[1007,4346,1212],{"href":4347,"rel":4348},"https:\u002F\u002Fgithub.com\u002Fnoy-db\u002Flobby",[3371],", the control plane that manages a fleet of vaults",[1362,4351,4352,4353,968,4356,968,4359,968,4362,968,4365,968,4368,968,4371,968,4374,4377],{},"flat files (",[941,4354,4355],{},"active.ts",[941,4357,4358],{},"adopt-partition.ts",[941,4360,4361],{},"decrypt-partition.ts",[941,4363,4364],{},"describe-extraction.ts",[941,4366,4367],{},"extract-partition.ts",[941,4369,4370],{},"strategy.ts",[941,4372,4373],{},"vault-diff.ts",[941,4375,4376],{},"walk-closure.ts",") — no service subfolders",[1344,4379,4380,4384,4387],{},[1362,4381,4382],{},[941,4383,971],{},[1362,4385,4386],{},"Write & mutate — transactions, versioning\u002Fhistory, CRDT merge, sequences",[1362,4388,4389,968,4391,968,4393,968,4395,968,4397],{},[941,4390,653],{},[941,4392,697],{},[941,4394,764],{},[941,4396,836],{},[941,4398,4399],{},"tx",[1344,4401,4402,4406,4409],{},[1362,4403,4404],{},[941,4405,989],{},[1362,4407,4408],{},"Snapshot & portability — checkpoints, archives, read-only frames",[1362,4410,4411,968,4413,968,4415],{},[941,4412,605],{},[941,4414,848],{},[941,4416,856],{},[1344,4418,4419,4423,4426],{},[1362,4420,4421],{},[941,4422,974],{},[1362,4424,4425],{},"Derived data — computed fields, derivations, materialized views, overlay views",[1362,4427,4428,968,4430,968,4432,968,4434],{},[941,4429,637],{},[941,4431,665],{},[941,4433,739],{},[941,4435,768],{},[1344,4437,4438,4442,4445],{},[1362,4439,4440],{},[941,4441,967],{},[1362,4443,4444],{},"Read & query — persisted indexes, search, aggregation, embeddings",[1362,4446,4447,968,4450,968,4453,968,4455],{},[941,4448,4449],{},"aggregate",[941,4451,4452],{},"embeddings",[941,4454,705],{},[941,4456,4457],{},"search",[1344,4459,4460,4464,4467],{},[1362,4461,4462],{},[941,4463,983],{},[1362,4465,4466],{},"Principals — who you are, what you may do, how you prove it: teams, sessions, policy, custody, the credential broker",[1362,4468,4469,968,4471,968,4473,968,4475,968,4477,968,4479,968,4481],{},[941,4470,613],{},[941,4472,625],{},[941,4474,661],{},[941,4476,669],{},[941,4478,796],{},[941,4480,840],{},[941,4482,868],{},[1344,4484,4485,4489,4497],{},[1362,4486,4487],{},[941,4488,996],{},[1362,4490,960,4491,4493,4494,4496],{},[941,4492,1398],{}," port — serialize\u002Fsave a vault as the ",[941,4495,1402],{}," artifact (the encrypted container format used for backup and transport)",[1362,4498,4352,4499,968,4502,968,4505,968,4508,968,4511,4377],{},[941,4500,4501],{},"backup.ts",[941,4503,4504],{},"bundle.ts",[941,4506,4507],{},"format.ts",[941,4509,4510],{},"pod-store.ts",[941,4512,4513],{},"ulid.ts",[1344,4515,4516,4520,4523],{},[1362,4517,4518],{},[941,4519,977],{},[1362,4521,4522],{},"Data shape — binary attachments, links, schema introspection & updates, satellites, persisted schemas",[1362,4524,4525,968,4527,968,4529,968,4531,968,4533,968,4535],{},[941,4526,621],{},[941,4528,711],{},[941,4530,723],{},[941,4532,780],{},[941,4534,812],{},[941,4536,820],{},[1344,4538,4539,4543,4546],{},[1362,4540,4541],{},[941,4542,992],{},[1362,4544,4545],{},"Store operations — combinators over the store contract, not a store adapter itself",[1362,4547,4548,4550,4551,968,4554,1381],{},[941,4549,719],{}," (+ flat ",[941,4552,4553],{},"route-store.ts",[941,4555,4556],{},"store-middleware.ts",[1344,4558,4559,4563,4566],{},[1362,4560,4561],{},[941,4562,986],{},[1362,4564,4565],{},"Replication — moving state between stores and between browser contexts: the sync engine, strategy, presence, sync transactions, tab coordination",[1362,4567,4352,4568,968,4570,968,4573,968,4576,968,4579,968,4582,968,4584,968,4587,968,4590,4377],{},[941,4569,4355],{},[941,4571,4572],{},"credentials.ts",[941,4574,4575],{},"engine.ts",[941,4577,4578],{},"period-scope.ts",[941,4580,4581],{},"presence.ts",[941,4583,4370],{},[941,4585,4586],{},"tab-coordination.ts",[941,4588,4589],{},"tab-write-relay.ts",[941,4591,4592],{},"transaction.ts",[937,4594,4595,4597,4598,4600,4601,1617,4603,4605,4606,4609,4610,4612,4613,2993,4615,964,4618,4621,4622,968,4625,1707,4628,4631,4632,4635,4636,4638],{},[941,4596,992],{}," is worth a specific callout since it's easy to confuse with the ",[941,4599,3328],{}," port: ",[941,4602,3328],{},[941,4604,3471],{},") is the ",[950,4607,4608],{},"contract door"," — a behavior-free re-export barrel that a ",[941,4611,1779],{}," storage\npackage binds. ",[941,4614,992],{},[950,4616,4617],{},"service of combinators over that contract",[941,4619,4620],{},"routeStore","\n(multiplex by collection\u002Fsize\u002Fage) and store middleware (",[941,4623,4624],{},"withRetry",[941,4626,4627],{},"withCache",[941,4629,4630],{},"withCircuitBreaker",", …), each mapping ",[941,4633,4634],{},"NoydbStore → NoydbStore",". Its audience is application\nauthors composing stores; a store author never touches it. See\n",[1007,4637,1331],{"href":30}," for the door-stays-behavior-free rule\nthis split enforces.",[937,4640,4641,4643,4644,2085,4647,4649,4650,4653,4654,4657,4658,4661,4662,4665,4666,4669,4670,4673,4674,4677,4678,4681,4682,968,4685,4688,4689,4692,4693,4695],{},[941,4642,986],{}," is the newest layer, and the reason it exists is the cleanest illustration of what a\nlayer is ",[1092,4645,4646],{},"for",[941,4648,983],{}," describes ",[950,4651,4652],{},"principals",", while sync replicates ",[950,4655,4656],{},"state"," — two\ndifferent concerns that had been filed under one bracket. Since 0.4.0-pre the sync engine,\nstrategy, presence and sync transactions live in ",[941,4659,4660],{},"with-sync\u002F",", along with tab coordination and the\ntab write relay (which coordinate browser contexts of the ",[1092,4663,4664],{},"same"," user, so they were never a\n",[941,4667,4668],{},"party"," concern either). Nothing about how you use sync changed: the subpath is still\n",[941,4671,4672],{},"@noy-db\u002Fhub\u002Fsync"," and the factory is still ",[941,4675,4676],{},"withSync()",". One narrow import path did change —\n",[941,4679,4680],{},"@noy-db\u002Fhub\u002Fteam"," no longer re-exports ",[941,4683,4684],{},"SyncEngine",[941,4686,4687],{},"SyncTransaction"," or ",[941,4690,4691],{},"PresenceHandle",";\nimport those from ",[941,4694,4672],{},", which is where they ship.",[1022,4697,4699,4700,4702],{"id":4698},"field-features-live-in-the-top-level-via-folder","Field features live in the top-level ",[941,4701,1011],{}," folder",[937,4704,960,4705,4708,4709,968,4711,968,4713,968,4715,968,4717,2462,4719,4721,4722,4724,4725,4727,4728,4730,4731,4734,4735,4738,4739,4742,4743,4746,4747,4749,4750,4754,4755,4757,4758,4760,4761,968,4763,4765,4766,4768,4769,4771],{},[950,4706,4707],{},"field-feature"," families — ",[941,4710,760],{},[941,4712,701],{},[941,4714,731],{},[941,4716,633],{},[941,4718,133],{},[941,4720,637],{}," —\nare ",[950,4723,1004],{}," a ",[941,4726,1031],{}," service layer. Since 0.3.0-pre.11 they sit in their own top-level ",[941,4729,1011],{},"\nfolder (",[941,4732,4733],{},"packages\u002Fhub\u002Fsrc\u002Fvia\u002F",", renamed from the former ",[941,4736,4737],{},"shape\u002F","), one subfolder per feature. A\nfield feature is declared ",[950,4740,4741],{},"per field"," through ",[941,4744,4745],{},"viaFields: { field: via(...) }",", not opted into\nper collection like a service — so it earns its own preposition rather than a ",[941,4748,1031],{}," bracket. The\nkernel machinery that runs the phased pipeline and the dependency graph lives in\n",[1007,4751,4752],{"href":15},[941,4753,3034],{},"; the developer-facing surface is the ",[1007,4756,896],{"href":897}," page.\nThis is why the ",[941,4759,977],{}," row above no longer lists ",[941,4762,633],{},[941,4764,701],{},", or ",[941,4767,760],{}," — those\nmoved into ",[941,4770,1011],{}," when field features gained their own folder.",[1022,4773,1455],{"id":1454},[937,4775,960,4776,4778],{},[1007,4777,597],{"href":598}," catalog is the developer-facing view of the same folders — one page\nper service, organized by layer, with subpath, factory, LOC cost, and opt-in example. This\npage is the structural mirror of the source tree; that page is where you go to actually use one.",[937,4780,4781,4784,4785,968,4787,1707,4789,968,4791,968,4793,968,4795,4797],{},[1007,4782,4783],{"href":38},"Families →"," — the satellite packages these layers' ports (",[941,4786,3328],{},[941,4788,3333],{},[941,4790,3336],{},[941,4792,3345],{},[941,4794,3342],{},[941,4796,3339],{},") are bound by.",[2645,4799,3254],{},{"title":1037,"searchDepth":1038,"depth":1038,"links":4801},[4802,4803,4804,4806],{"id":4066,"depth":1041,"text":4067},{"id":4265,"depth":1041,"text":4266},{"id":4698,"depth":1041,"text":4805},"Field features live in the top-level via\u002F folder",{"id":1454,"depth":1041,"text":1455},"The eleven with-* folders that group services in the hub source tree — a folder bracket, not a runtime seam.",{},{"title":33,"description":4807},"T4tx7OGsd-GkLetgYOprqcJyIREdsQMRjk9g_SMcI20",{"id":4812,"title":37,"access":12,"apiToc":929,"body":4813,"category":1042,"description":5246,"extension":1044,"meta":5247,"navigation":1042,"nest":3303,"path":38,"seo":5248,"sinceVersion":1042,"status":1049,"stem":39,"subtitle":1042,"tier":1473,"tocExternal":1042,"__hash__":5249},"docs\u002Farchitecture\u002F4.families.md",{"type":931,"value":4814,"toc":5240},[4815,4817,4825,4874,4901,4916,4920,5091,5118,5143,5147,5169,5173,5180,5225,5232,5234],[934,4816,37],{"id":116},[1061,4818,4819],{},[937,4820,4821,4822,1520],{},"Structural view of the package catalog. For the usage-first intro and the adapter catalog\nper family, see ",[1007,4823,4824],{"href":115},"Families & ports",[937,4826,4827,4828,4830,4831,4833,4834,4837,4838,968,4840,968,4842,968,4844,968,4846,968,4848,4850,4851,4853,4854,4857,4858,4860,4861,968,4863,968,4865,968,4867,968,4869,968,4871,4873],{},"noy-db is an encrypted document store: your data lives in a ",[950,4829,2694],{},", and everything in it is\nencrypted inside the core package, ",[941,4832,1086],{},", before it touches any other code. Everything\noutside the hub ships as a ",[950,4835,4836],{},"satellite"," — a separate npm package whose name starts with a\npreposition: ",[941,4839,1260],{},[941,4841,1264],{},[941,4843,1267],{},[941,4845,1270],{},[941,4847,1273],{},[941,4849,1276],{},". That prefix is the package's ",[950,4852,3303],{},",\nand it is not a naming flourish: a family classifies its members by their ",[950,4855,4856],{},"relationship to the\ntrust boundary"," — whether the code may ever see decrypted data, hold key material, or write\nvault state. Each family binds exactly one frozen ",[1007,4859,3292],{"href":30}," subpath\n(",[941,4862,3299],{},[941,4864,3333],{},[941,4866,3336],{},[941,4868,3345],{},[941,4870,3342],{},[941,4872,3339],{},") and nothing else — never hub internals.",[937,4875,4876,4877,4880,4881,4884,4885,4888,4889,4892,4893,4896,4897,4900],{},"The preposition reads from the vault owner's perspective: data goes ",[950,4878,4879],{},"to"," a backend, the owner\ngets ",[950,4882,4883],{},"on"," via an unlock method, a scoped slice is sealed ",[950,4886,4887],{},"at"," a host they control, the hub\nruns ",[950,4890,4891],{},"in"," a host runtime or protocol, sessions sync ",[950,4894,4895],{},"by"," a transport, and exports go out\n",[950,4898,4899],{},"as"," a format.",[937,4902,4903,4904,4907,4908,4911,4912,4915],{},"On disk this is flat: the satellites sit directly in the repository's ",[941,4905,4906],{},"packages\u002F"," directory\nalongside the core, so folder listing clusters them by family only alphabetically. A\nfamily-folder nesting (",[941,4909,4910],{},"packages\u002Fin\u002Fin-react","-style) is planned but not in place today — the\nclassification lives in the package ",[1092,4913,4914],{},"name",", not the path.",[1022,4917,4919],{"id":4918},"the-family-table","The family table",[1338,4921,4922,4943],{},[1341,4923,4924],{},[1344,4925,4926,4929,4932,4935,4938,4941],{},[1347,4927,4928],{},"Prefix",[1347,4930,4931],{},"Reads as",[1347,4933,4934],{},"Sees plaintext?",[1347,4936,4937],{},"May hold keys?",[1347,4939,4940],{},"May write vault state?",[1347,4942,1352],{},[1357,4944,4945,4970,4993,5017,5041,5063],{},[1344,4946,4947,4951,4957,4960,4963,4966],{},[1362,4948,4949],{},[941,4950,1260],{},[1362,4952,4953,4954,4956],{},"data goes ",[950,4955,4879],{}," a backend",[1362,4958,4959],{},"No — ciphertext only",[1362,4961,4962],{},"No",[1362,4964,4965],{},"Envelopes only, as told",[1362,4967,4968],{},[941,4969,3328],{},[1344,4971,4972,4976,4982,4984,4987,4989],{},[1362,4973,4974],{},[941,4975,1264],{},[1362,4977,4978,4979,4981],{},"get ",[950,4980,4883],{}," via this method",[1362,4983,4962],{},[1362,4985,4986],{},"Handles unlock material, never data-encryption keys",[1362,4988,4962],{},[1362,4990,4991],{},[941,4992,3333],{},[1344,4994,4995,4999,5005,5008,5011,5013],{},[1362,4996,4997],{},[941,4998,1267],{},[1362,5000,5001,5002,5004],{},"sealed ",[950,5003,4887],{}," a trusted host",[1362,5006,5007],{},"Scoped slice",[1362,5009,5010],{},"Scoped, on your infra",[1362,5012,4962],{},[1362,5014,5015],{},[941,5016,3336],{},[1344,5018,5019,5023,5029,5032,5034,5037],{},[1362,5020,5021],{},[941,5022,1270],{},[1362,5024,5025,5026,5028],{},"runs ",[950,5027,4891],{}," a host",[1362,5030,5031],{},"As an authorized consumer (like app code)",[1362,5033,4962],{},[1362,5035,5036],{},"Via public API only",[1362,5038,5039],{},[941,5040,3345],{},[1344,5042,5043,5047,5053,5055,5057,5059],{},[1362,5044,5045],{},[941,5046,1273],{},[1362,5048,5049,5050,5052],{},"sync ",[950,5051,4895],{}," way of",[1362,5054,4962],{},[1362,5056,4962],{},[1362,5058,4962],{},[1362,5060,5061],{},[941,5062,3342],{},[1344,5064,5065,5069,5075,5078,5083,5087],{},[1362,5066,5067],{},[941,5068,1276],{},[1362,5070,5071,5072,5074],{},"export ",[950,5073,4899],{}," a format",[1362,5076,5077],{},"Plaintext (post-decrypt, by design)",[1362,5079,5080],{},[950,5081,5082],{},"Never",[1362,5084,5085],{},[950,5086,5082],{},[1362,5088,5089],{},[941,5090,3339],{},[937,5092,5093,5094,5096,5097,5099,5100,5102,5103,5106,5107,968,5109,968,5111,5113,5114,968,5116,957],{},"An ",[950,5095,1659],{}," is the encrypted wrapper a record is stored and shipped as — the only shape a\n",[941,5098,1779],{}," backend ever handles. ",[941,5101,1267],{}," is the one non-zero-knowledge family, by design: a host you\ncontrol ",[1092,5104,5105],{},"can"," decrypt the scoped slice it unseals. Every other family is zero-knowledge —\neither it never sees decrypted data at all (",[941,5108,1260],{},[941,5110,1264],{},[941,5112,1273],{},"), or it sees it only the way\nyour own application code does, after you unlock the vault (",[941,5115,1270],{},[941,5117,1276],{},[937,5119,960,5120,5122,5123,5126,5127,5130,5131,5133,5134,5136,5137,5142],{},[941,5121,1260],{}," family also spans two repositories: the three essential stores (file, browser\nIndexedDB, and the metering store ",[941,5124,5125],{},"to-meter",", which also carries the one-shot store\nprobes) ship with noy-db itself, while the nineteen extended\ncloud, SQL, server, and remote-filesystem adapters ship separately under the same ",[941,5128,5129],{},"@noy-db\u002F*","\nscope and bind the same ",[941,5132,3328],{}," port. ",[941,5135,2992],{}," moved from the first group to the second on\n2026-09-17 — the split is about which repo publishes a store, not about what it does, and a\npackage keeps its name and its contract across such a move. See the ",[1007,5138,5139,5141],{"href":115},[941,5140,1260],{}," catalog","\nfor the full list.",[1022,5144,5146],{"id":5145},"a-package-answers-a-different-question-than-a-service","A package answers a different question than a service",[937,5148,5149,5150,5153,5154,5156,5157,5160,5161,5163,5164,5166,5167,1520],{},"A package family answers ",[1092,5151,5152],{},"\"who is this code to the vault?\""," — a service (see\n",[1007,5155,33],{"href":34},") answers ",[1092,5158,5159],{},"\"what can the vault itself do?\"",".\nNothing is ever both: the two axes are independent and easy to conflate. ",[941,5162,2992],{}," (a family\npackage) and the kernel's record cache both hold data in RAM, but ",[941,5165,2992],{}," is a ciphertext\npersistence backend outside the trust boundary while the kernel cache is a plaintext\nread-acceleration tier inside it — see ",[1007,5168,1245],{"href":15},[1022,5170,5172],{"id":5171},"prefix-less-packages","Prefix-less packages",[937,5174,5175,5176,5179],{},"No prefix means the package ",[950,5177,5178],{},"holds no vault and serves someone other than (or larger than) a\nsingle vault owner",". This is a meaningful third tier, not a junk drawer. There are four such\npackages:",[1111,5181,5182,5190,5203,5214],{},[1114,5183,5184,5189],{},[950,5185,5186],{},[941,5187,5188],{},"hub"," — the core: the trust boundary itself, plus every opt-in service.",[1114,5191,5192,5199,5200,5202],{},[950,5193,5194],{},[1007,5195,5196],{"href":214},[941,5197,5198],{},"cli"," — command-line tooling: inspect ",[941,5201,1402],{}," bundles without decrypting, verify\nintegrity, validate config, monitor a live vault's store metrics.",[1114,5204,5205,5210,5211,957],{},[950,5206,5207],{},[941,5208,5209],{},"create-noy-db"," — the project scaffolding wizard (",[941,5212,5213],{},"npm create noy-db",[1114,5215,5216,5220,5221,5224],{},[950,5217,5218],{},[941,5219,609],{}," — a pure, zero-dependency verifier for document attestations. Its user is a\n",[1092,5222,5223],{},"counterparty"," — someone verifying a credential you issued — who runs it offline without\ninstalling the hub or holding any key material.",[937,5226,5227,5228,5231],{},"Fleet-level orchestration — managing many sovereign vaults at once — is deliberately not a\nnoy-db package: it lives in the separate ",[941,5229,5230],{},"@klum-db\u002Flobby"," product, which binds noy-db only\nthrough published subpaths — the same one-way law the families follow.",[1022,5233,1455],{"id":1454},[937,5235,5236,5239],{},[1007,5237,5238],{"href":115},"Families & ports →"," — the usage-first intro and full adapter catalog for each\nfamily, organized by category and compared in capability tables.",{"title":1037,"searchDepth":1038,"depth":1038,"links":5241},[5242,5243,5244,5245],{"id":4918,"depth":1041,"text":4919},{"id":5145,"depth":1041,"text":5146},{"id":5171,"depth":1041,"text":5172},{"id":1454,"depth":1041,"text":1455},"The six satellite package families — to\u002Fon\u002Fat\u002Fin\u002Fby\u002Fas — classified by their relationship to the vault's trust boundary.",{},{"title":37,"description":5246},"vOSwZZpthtieqX063Cee58yHAhkqnuI-fueht0AhdHE",{"id":5251,"title":5,"access":12,"apiToc":929,"body":5252,"category":1042,"description":6216,"extension":1044,"meta":6217,"navigation":6218,"nest":2635,"path":6,"seo":6219,"sinceVersion":1042,"status":1049,"stem":11,"subtitle":1042,"tier":1473,"tocExternal":1042,"__hash__":6220},"docs\u002Farchitecture\u002Findex.md",{"type":931,"value":5253,"toc":6208},[5254,5256,5265,5269,5321,5331,5337,5378,5539,5547,5595,5598,5650,5657,5661,5667,5675,5699,5703,5711,5937,5941,6120,6124,6192,6194,6202],[934,5255,5],{"id":7},[937,5257,5258,5259,5261,5262,5264],{},"noy-db is a zero-knowledge, offline-first, encrypted document store: one hub package\n(",[941,5260,1086],{},") that does all encryption and querying, surrounded by satellite packages\nfor storage, unlock, export, and framework integration. This section is the reference\nlayer for how that system is put together — for readers who need the shape of the system\nitself, not just its API. It distills the noy-db repository's own architecture notes (the\nnested-cores frame and the classification guide), and its pages mirror the real\n",[941,5263,4033],{}," tree, level by level — start here for the frame, then follow the tree\ndown.",[1022,5266,5268],{"id":5267},"the-containing-architecture-nested-cores","The containing architecture — nested cores",[937,5270,5271,5272,5274,5275,5277,5278,5280,5281,5283,5284,5287,5288,5290,5291,5293,5294,5296,5297,5299,5300,5302,5303,5305,5306,5308,5309,5311,5312,5314,5315,5317,5318,5320],{},"Four words carry everything below. The ",[950,5273,1059],{}," is the self-contained crypto interior:\nthe encryption functions, the keys, and the data format, behind a frozen interface. The\n",[950,5276,2635],{}," is the always-on core that holds the enclave — a ",[1007,5279,2694],{"href":48},"\n(an encrypted database instance), its ",[1007,5282,2698],{"href":48}," (the\ntyped record sets inside it), the ",[1007,5285,5286],{"href":108},"query"," engine, the\n",[1007,5289,2705],{"href":54}," (the structure that wraps each collection's data\nkeys), and the ",[1007,5292,2709],{"href":15}," (the internal event bus opt-in services\nlisten on). The ",[950,5295,5188],{}," is the ",[941,5298,1086],{}," package: the kernel plus opt-in service\nlayers. The ",[950,5301,116],{}," are the satellite packages, named by preposition — ",[941,5304,1779],{}," storage\nbackends, ",[941,5307,1787],{}," unlock methods, ",[941,5310,1139],{}," sealing hosts, ",[941,5313,1783],{}," framework bindings, ",[941,5316,1795],{},"\nsync transports, ",[941,5319,1791],{}," exporters.",[937,5322,5323,5324,5327,5328,5330],{},"The whole product is ",[950,5325,5326],{},"one composition rule applied at four scales",": a minimal core, an\nopt-in periphery, and a frozen seam between them, with a mechanical guard holding the seam.\nEach level ",[1092,5329,3082],{}," the next level's core.",[937,5332,5333,5334,5336],{},"The widest scale, level 3, zooms out past the ",[941,5335,1072],{}," package itself to the separate,\nindependently-versioned repositories built around it, each binding one of noy-db's frozen\nseams rather than reaching into its internals:",[1111,5338,5339,5347,5366,5373],{},[1114,5340,5341,5343,5344,5346],{},[950,5342,1212],{}," is a control-plane repository that orchestrates a fleet of noy-db vaults —\nfederation, cross-vault query, and custody — by binding the ",[941,5345,3714],{}," subpath.",[1114,5348,5349,5352,5353,968,5356,968,5359,5362,5363,5365],{},[950,5350,5351],{},"noy-db-to"," is a repository of extended storage adapters: the cloud, SQL, and\nremote-filesystem backends (",[941,5354,5355],{},"to-postgres",[941,5357,5358],{},"to-aws-s3",[941,5360,5361],{},"to-sqlite",", …) that don't ship in\nthe core, bound to ",[941,5364,3328],{}," at a pinned version range.",[1114,5367,5368,5370,5371,1520],{},[950,5369,1367],{}," is a UI repository — a framework-agnostic, schema-driven list\u002Fsearch engine\nplus a Nuxt binding — that reads a collection's shape through ",[941,5372,1380],{},[1114,5374,5375,5377],{},[950,5376,1371],{}," is this documentation site: it consumes published versions of all of the\nabove and publishes nothing itself.",[1338,5379,5380,5396],{},[1341,5381,5382],{},[1344,5383,5384,5387,5390,5393],{},[1347,5385,5386],{},"Level",[1347,5388,5389],{},"Equation",[1347,5391,5392],{},"Seam the periphery binds",[1347,5394,5395],{},"Mechanical guard",[1357,5397,5398,5433,5466,5508],{},[1344,5399,5400,5403,5419,5422],{},[1362,5401,5402],{},"0",[1362,5404,5405],{},[950,5406,5407,5408,5411,5412,1625,5414,1625,5416,5418],{},"microkernel (",[1007,5409,1197],{"href":5410},"\u002Farchitecture\u002Fkernel#spine-files"," — the kernel's flat, always-on files: the ",[941,5413,62],{},[941,5415,94],{},[941,5417,103],{}," entry points, validation, the write queue, and the sync policy that governs push\u002Fpull timing) + enclave = kernel",[1362,5420,5421],{},"the enclave barrel — the frozen crypto contract the spine consumes",[1362,5423,5424,1261,5426,5428,5429,5432],{},[941,5425,1176],{},[941,5427,2568],{}," ratchets; ",[941,5430,5431],{},"kernel-surface"," line ceilings",[1344,5434,5435,5438,5446,5458],{},[1362,5436,5437],{},"1",[1362,5439,5440],{},[950,5441,5442,5443,5445],{},"kernel + ",[941,5444,1031],{}," service layers = hub",[1362,5447,5448,5450,5451,5453,5454,5457],{},[941,5449,943],{}," strategy factories via the ",[941,5452,3348],{}," port; ",[941,5455,5456],{},"NO_X"," stubs when unused",[1362,5459,5460,5463,5464],{},[941,5461,5462],{},"strategy-opt-in","; bundle-size gate; ",[941,5465,3886],{},[1344,5467,5468,5471,5489,5497],{},[1362,5469,5470],{},"2",[1362,5472,5473],{},[950,5474,5475,5476,1324,5478,1324,5480,1324,5482,1324,5484,1324,5486,5488],{},"hub + families (",[941,5477,4879],{},[941,5479,4883],{},[941,5481,4887],{},[941,5483,4891],{},[941,5485,4895],{},[941,5487,4899],{},") = noy-db",[1362,5490,5491,5492,968,5494,5496],{},"the golden-frozen port subpaths (",[941,5493,3299],{},[941,5495,3333],{},", …)",[1362,5498,5499,5502,5503,5502,5506],{},[941,5500,5501],{},"peer-deps","; ",[941,5504,5505],{},"stores-ciphertext-only",[941,5507,2513],{},[1344,5509,5510,5513,5518,5529],{},[1362,5511,5512],{},"3",[1362,5514,5515],{},[950,5516,5517],{},"noy-db + klum-db + noy-db-to + noy-db-ui + noy-db-docs = the product family",[1362,5519,5520,5522,5523,5525,5526,5528],{},[941,5521,3714],{}," (klum), ",[941,5524,3328],{}," at a version range (extended stores), ",[941,5527,1380],{}," (ui), published versions (docs)",[1362,5530,5531,5534,5535,5538],{},[941,5532,5533],{},"no-outbound-klum-import","; each sister repo's own ",[941,5536,5537],{},"check-architecture","; version-pinned doc sync",[937,5540,5541,5542,1069,5544,5546],{},"The guard column names real CI checks (",[941,5543,1180],{},[941,5545,1072],{},"\nrepo). Four of its terms deserve a definition:",[1111,5548,5549,5564,5570,5576],{},[1114,5550,3289,5551,5553,5554,5556,5557,953,5559,5561,5562,957],{},[950,5552,1172],{}," is a folder's single ",[941,5555,1168],{}," re-export file — the folder's entire public\nsurface in one module. The ",[950,5558,3758],{},[941,5560,1316],{},") is the frozen\ncontract the rest of the kernel consumes, and the seam a sister project replaces\nwholesale (see ",[1007,5563,21],{"href":22},[1114,5565,3289,5566,5569],{},[950,5567,5568],{},"ratchet"," is a check with a checked-in per-file violation count: the count may never\nrise, and any genuine reduction must be recorded in the baseline — so pre-existing\nviolations can only shrink toward zero, never silently grow.",[1114,5571,3289,5572,5575],{},[950,5573,5574],{},"line ceiling"," is a per-file line budget on the always-on kernel files: when a file\nwould grow past its ceiling, the new capability must move into an opt-in service instead\nof enlarging the core.",[1114,5577,5578,5581,5582,5584,5585,5587,5588,968,5591,5594],{},[950,5579,5580],{},"Golden-frozen"," subpaths are import paths whose export surface is snapshotted in a\nchecked-in JSON file that a test compares against the real exports — the contract changes\nonly deliberately, never by accident (see ",[1007,5583,29],{"href":30},"). ",[941,5586,5456],{}," stubs are\nthe inert placeholder implementations (",[941,5589,5590],{},"NO_HISTORY",[941,5592,5593],{},"NO_BLOBS",", …) substituted when a\nservice is not opted into, letting the real code tree-shake out of the bundle.",[937,5596,5597],{},"Three invariants make the recursion more than a diagram:",[1111,5599,5600,5619,5641],{},[1114,5601,5602,5605,5606,5609,5610,5612,5613,5615,5616,1520],{},[950,5603,5604],{},"The one-way law, with one deliberate arrow-flip."," At levels 1–3 the periphery binds the\ncore's frozen contract and the core never imports outward. At ",[950,5607,5608],{},"level 0 the arrow flips",":\nthe ",[1092,5611,1197],{}," consumes the ",[1092,5614,1201],{}," frozen barrel — because the crypto is the innermost\ncore, protected from everything, ",[950,5617,5618],{},"including the kernel itself",[1114,5620,5621,5624,5625,5628,5629],{},[950,5622,5623],{},"The trust gradient."," Every seam crossing sheds capability, monotonically: enclave (raw\nkeys) → kernel (",[1007,5626,5627],{"href":66},"DEK"," — the collection's data-encryption key — handles",[1111,5630,5631],{},[1114,5632,5633,5634,5636,5637,5640],{},"plaintext) → services (plaintext, opt-in, scoped) → families (ciphertext-only, or\npost-decrypt as a consumer) → sister repos (published surface only) → docs (published\n",[1092,5635,1292],{}," only). ",[950,5638,5639],{},"Docs are the terminus of both gradients"," — this site documents pinned,\npublished versions of each upstream package, never unreleased code.",[1114,5642,5643,5646,5647,5649],{},[950,5644,5645],{},"Isolation hardens outward."," The same seam idea, implemented with progressively stronger\nmechanisms: tree-shaking + ",[941,5648,5456],{}," stubs (level 1) → package boundary + golden-frozen\nsubpaths (level 2) → repo boundary + npm peer ranges (level 3) → version-pinned sync\nstate for the ring that holds no code at all.",[937,5651,5652,5653,5656],{},"At every level the economics are identical: ",[950,5654,5655],{},"pay only for the rings you opt into"," — an\nunused service tree-shakes out, an unused family is a package you never install, a\nmulti-vault control plane you don't need is a repo you never touch.",[1022,5658,5660],{"id":5659},"the-model-the-containment-view","The model — the containment view",[937,5662,5663,5664,1520],{},"The four equations above, read spatially, are a nest of containment: ",[950,5665,5666],{},"enclave ⊂ kernel ⊂\nhub ⊂ noy-db ⊂ product family",[2217,5668,5673],{"className":5669,"code":5671,"language":5672},[5670],"language-text","family — the satellites: to- in- on- as- by- at-   (separate packages)\n\nhub  (@noy-db\u002Fhub)\n├─ kernel — the always-on, mandatory core: vault · collection · query · keyring · bus\n│    └─ enclave — self-contained, frozen-interface, swappable:\n│                 encryption functions + keys + data format\n└─ layers — optional, tree-shaken; each a  with-\u003Cname>\u002F  folder + a  with\u003CName>()  factory\n     · service — an in-vault capability (history, blobs, aggregate, crdt, …)\n     · cargo   — the layer that manages pods  (used by klum)\n     · pod     — serialize a vault ⇄ a pod  (the saved \u002F at-rest \u002F transportable artifact)\n","text",[941,5674,5671],{"__ignoreMap":1037},[937,5676,5677,5678,5502,5681,5684,5685,5687,5688,5691,5692,5695,5696,1520],{},"Containment: ",[950,5679,5680],{},"enclave ⊂ kernel ⊂ hub",[950,5682,5683],{},"layers"," sit beside the kernel inside the hub;\n",[950,5686,3303],{}," is outside the hub. ",[941,5689,5690],{},"[layers]"," is an ",[950,5693,5694],{},"organizational"," grouping today — it is\nthe seam where services may later harden into a real runtime ",[950,5697,5698],{},"plug-in interface",[1022,5700,5702],{"id":5701},"the-real-source-tree","The real source tree",[937,5704,5705,5706,1069,5708,5710],{},"The pages in this section mirror ",[941,5707,4033],{},[941,5709,1072],{}," repo, one page per level:",[1338,5712,5713,5725],{},[1341,5714,5715],{},[1344,5716,5717,5719,5722],{},[1347,5718,2967],{},[1347,5720,5721],{},"Page",[1347,5723,5724],{},"What lives there",[1357,5726,5727,5749,5762,5778,5789,5812,5855,5886,5912],{},[1344,5728,5729,5742,5746],{},[1362,5730,5731,5733,5734,968,5736,968,5738,968,5740,1381],{},[941,5732,2957],{}," (spine files + ",[941,5735,2979],{},[941,5737,3010],{},[941,5739,3034],{},[941,5741,3056],{},[1362,5743,5744],{},[1007,5745,1245],{"href":15},[1362,5747,5748],{},"the always-on, mandatory core",[1344,5750,5751,5755,5759],{},[1362,5752,5753],{},[941,5754,1108],{},[1362,5756,5757],{},[1007,5758,21],{"href":22},[1362,5760,5761],{},"the swappable crypto interior",[1344,5763,5764,5768,5772],{},[1362,5765,5766],{},[941,5767,1606],{},[1362,5769,5770],{},[1007,5771,25],{"href":26},[1362,5773,5774,5775,5777],{},"hub's envelope format, shared by every capsule — ",[941,5776,1553],{}," builds it from a capsule's primitives",[1344,5779,5780,5782,5786],{},[1362,5781,3610],{},[1362,5783,5784],{},[1007,5785,25],{"href":26},[1362,5787,5788],{},"the fork contract a sister project implements",[1344,5790,5791,5802,5806],{},[1362,5792,5793,953,5795,1324,5797,1324,5799,1381],{},[941,5794,3710],{},[941,5796,4879],{},[941,5798,4895],{},[941,5800,5801],{},"with",[1362,5803,5804],{},[1007,5805,29],{"href":30},[1362,5807,5808,5809,5811],{},"the golden doors — ",[941,5810,3328],{}," is the one still published to a family",[1344,5813,5814,5845,5849],{},[1362,5815,5816,1324,5819,1324,5821,1324,5824,1324,5827,1324,5830,1324,5833,1324,5836,1324,5838,1324,5841,1324,5843],{},[941,5817,5818],{},"with-audit\u002F",[941,5820,3717],{},[941,5822,5823],{},"with-commit\u002F",[941,5825,5826],{},"with-fork\u002F",[941,5828,5829],{},"with-formula\u002F",[941,5831,5832],{},"with-lookup\u002F",[941,5834,5835],{},"with-party\u002F",[941,5837,3723],{},[941,5839,5840],{},"with-shape\u002F",[941,5842,3984],{},[941,5844,4660],{},[1362,5846,5847],{},[1007,5848,33],{"href":34},[1362,5850,5851,5852,5854],{},"the 11 opt-in ",[941,5853,1031],{}," folders and their services",[1344,5856,5857,5873,5880],{},[1362,5858,5859,953,5861,1324,5863,1324,5865,1324,5867,1324,5869,1324,5871,1381],{},[941,5860,1011],{},[941,5862,760],{},[941,5864,701],{},[941,5866,731],{},[941,5868,633],{},[941,5870,133],{},[941,5872,637],{},[1362,5874,5875,1368,5878],{},[1007,5876,5877],{"href":34},"Service layers → Field features",[1007,5879,896],{"href":897},[1362,5881,5882,5883],{},"the field-feature families, declared per-field via ",[941,5884,5885],{},"viaFields",[1344,5887,5888,5905,5909],{},[1362,5889,5890,5892,5893,1324,5895,1324,5897,1324,5899,1324,5901,1324,5903,1381],{},[941,5891,4906],{}," by prefix (",[941,5894,1260],{},[941,5896,1264],{},[941,5898,1267],{},[941,5900,1270],{},[941,5902,1273],{},[941,5904,1276],{},[1362,5906,5907],{},[1007,5908,37],{"href":38},[1362,5910,5911],{},"the satellite packages, structurally",[1344,5913,5914,5919,5925],{},[1362,5915,5916],{},[941,5917,5918],{},"legacy\u002F",[1362,5920,5921,5922],{},"see ",[1007,5923,5924],{"href":30},"Ports → Removed & deprecated aliases",[1362,5926,5927,968,5929,968,5931,5933,5934,5936],{},[941,5928,3913],{},[941,5930,3942],{},[941,5932,3921],{}," retired in 0.3.0-pre.11; only ",[941,5935,3962],{}," remains",[1022,5938,5940],{"id":5939},"definitions","Definitions",[1338,5942,5943,5953],{},[1341,5944,5945],{},[1344,5946,5947,5950],{},[1347,5948,5949],{},"Term",[1347,5951,5952],{},"Definition",[1357,5954,5955,5966,5975,6011,6034,6053,6078,6097,6108],{},[1344,5956,5957,5961],{},[1362,5958,5959],{},[950,5960,5188],{},[1362,5962,3742,5963,5965],{},[941,5964,1086],{}," package — the whole machine.",[1344,5967,5968,5972],{},[1362,5969,5970],{},[950,5971,2635],{},[1362,5973,5974],{},"the always-on, mandatory core (vault · collection · query · keyring · the service bus). One center word; \"core\" is a description of it, not a separate level. Holds the enclave.",[1344,5976,5977,5981],{},[1362,5978,5979],{},[950,5980,1059],{},[1362,5982,5983,5984,5986,5987,5989,5990,5992,5993,5996,5997,6000,6001,6004,6005,6008,6009,957],{},"a ",[950,5985,1306],{}," unit ",[1092,5988,1094],{}," the kernel (no dependency on other kernel parts) with a ",[950,5991,1098],{}," to the kernel — therefore ",[950,5994,5995],{},"replaceable",". It owns the open↔close boundary: the ",[950,5998,5999],{},"encryption functions + keys"," and the ",[950,6002,6003],{},"data format"," (codec). noy-db ships one enclave — full AES-256-GCM, zero-knowledge. A trusted wrapper can supply ",[1092,6006,6007],{},"its own"," enclave (see ",[1007,6010,21],{"href":22},[1344,6012,6013,6017],{},[1362,6014,6015],{},[950,6016,1047],{},[1362,6018,6019,6020,6023,6024,6027,6028,6030,6031,6033],{},"a tree-shakeable capability that ships ",[950,6021,6022],{},"by default"," and is ",[950,6025,6026],{},"removed at build"," when unused (the microkernel-with-services model — not a \"plugin\" you add). Each is a ",[941,6029,4056],{}," + ",[941,6032,1770],{}," layer.",[1344,6035,6036,6040],{},[1362,6037,6038],{},[950,6039,3706],{},[1362,6041,6042,6043,6045,6046,997,6049,6052],{},"the organizational grouping of the ",[941,6044,1031],{}," units (services, plus ",[941,6047,6048],{},"cargo",[941,6050,6051],{},"pod","). A soft bracket now; a future plug-in seam.",[1344,6054,6055,6059],{},[1362,6056,6057],{},[950,6058,6048],{},[1362,6060,3742,6061,6063,6064,6067,6068,6071,6072,6074,6075,6077],{},[950,6062,3706],{}," of services + interfaces required to ",[950,6065,6066],{},"manage pods"," — the multi-vault management plane ",[950,6069,6070],{},"klum"," binds: custody, deed, diff, distributed query, addressing. Bound via the ",[941,6073,3714],{}," subpath (formerly ",[941,6076,3942],{},", which was retired in 0.3.0-pre.11).",[1344,6079,6080,6084],{},[1362,6081,6082],{},[950,6083,6051],{},[1362,6085,6086,6087,1169,6090,6093,6094,6096],{},"a vault ",[950,6088,6089],{},"serialized and saved",[950,6091,6092],{},"at-rest",", self-contained, transportable artifact (the ",[941,6095,1402],{}," file). Formerly \"bundle\".",[1344,6098,6099,6103],{},[1362,6100,6101],{},[950,6102,2694],{},[1362,6104,6105,6106,1520],{},"a live tenant namespace with its own keyrings — a kernel concept; the ",[1092,6107,1094],{},[1344,6109,6110,6114],{},[1362,6111,6112],{},[950,6113,3303],{},[1362,6115,6116,6117,1520],{},"the satellite packages, by prefix: ",[941,6118,6119],{},"to- in- on- as- by- at-",[1022,6121,6123],{"id":6122},"who-binds-what","Who binds what",[1338,6125,6126,6136],{},[1341,6127,6128],{},[1344,6129,6130,6132,6134],{},[1347,6131,1349],{},[1347,6133,1352],{},[1347,6135,1355],{},[1357,6137,6138,6157,6175],{},[1344,6139,6140,6146,6154],{},[1362,6141,1364,6142,1368,6144],{},[950,6143,1367],{},[950,6145,1371],{},[1362,6147,6148,6149,1377,6151,1381],{},"the kernel's public API (+ chosen ",[950,6150,1047],{},[941,6152,6153],{},"describe",[1362,6155,6156],{},"full ZK, cannot touch the enclave",[1344,6158,6159,6163,6170],{},[1362,6160,6161],{},[950,6162,1212],{},[1362,6164,3742,6165,6167,6168,1381],{},[950,6166,6048],{}," layer (+ ",[950,6169,6051],{},[1362,6171,6172,6173,1410],{},"full ZK — orchestrates pods ",[1092,6174,1409],{},[1344,6176,6177,6183,6189],{},[1362,6178,6179,6182],{},[950,6180,6181],{},"an enclave fork"," (a separate, clean-room re-implementation of the hub foundation, built against noy-db's published surface)",[1362,6184,6185,6186,6188],{},"its own ",[950,6187,1059],{}," via the enclave barrel (the fork contract)",[1362,6190,6191],{},"full control of the crypto interior — may relax encryption and supply its own data format",[1022,6193,1455],{"id":1454},[937,6195,6196,6199,6200,1520],{},[1007,6197,6198],{"href":15},"Kernel →"," — the always-on core, mirroring ",[941,6201,2957],{},[937,6203,6204,6205,6207],{},"Or jump into the ",[1007,6206,1019],{"href":1018}," to pick capabilities and land on the exact\npages you need.",{"title":1037,"searchDepth":1038,"depth":1038,"links":6209},[6210,6211,6212,6213,6214,6215],{"id":5267,"depth":1041,"text":5268},{"id":5659,"depth":1041,"text":5660},{"id":5701,"depth":1041,"text":5702},{"id":5939,"depth":1041,"text":5940},{"id":6122,"depth":1041,"text":6123},{"id":1454,"depth":1041,"text":1455},"The nested-cores frame, the real source tree, and the three invariants.",{},{"title":10,"icon":13},{"title":5,"description":6216},"D7kQjWJOpeDhUURJw12PJt8tTm7lsIPhdDhQzHpZMpM",{"id":6222,"title":10,"access":12,"apiToc":929,"body":6223,"category":1042,"description":6918,"extension":1044,"meta":6919,"navigation":6920,"nest":2635,"path":48,"seo":6921,"sinceVersion":1042,"status":1049,"stem":49,"subtitle":1042,"tier":6922,"tocExternal":1042,"__hash__":6923},"docs\u002Fcore\u002F01-overview.md",{"type":931,"value":6224,"toc":6910},[6225,6228,6239,6243,6256,6262,6294,6301,6305,6308,6602,6644,6657,6661,6664,6693,6697,6703,6784,6822,6826,6877,6879,6907],[934,6226,3112],{"id":6227},"vault-collection-model",[1061,6229,6230],{},[937,6231,6232,6235,6236],{},[950,6233,6234],{},"Always-on. Cannot be opted out.","\nSource of truth: ",[941,6237,6238],{},"packages\u002Fhub\u002Fsrc\u002Fkernel\u002F{noydb,vault,collection}.ts",[1022,6240,6242],{"id":6241},"what-it-is","What it is",[937,6244,6245,6246,6248,6249,6251,6252,6255],{},"noy-db is an encrypted, offline-first document store: your application holds the keys, and the storage backend only ever sees ciphertext. Before you read or write anything, you need to know where a record lives — that is what this page defines. Everything in noy-db hangs off a three-layer data model, implemented in the ",[950,6247,2635],{}," (the always-on core of ",[941,6250,1086],{}," that every install ships, as opposed to the opt-in ",[1007,6253,599],{"href":6254},"..\u002Fservices\u002F","):",[2217,6257,6260],{"className":6258,"code":6259,"language":5672},[5670],"Noydb              ← top-level instance from createNoydb()\n  └── Vault        ← tenant \u002F company namespace (\"acme\", \"firm-2026\")\n        └── Collection\u003CT>   ← typed record set (\"invoices\", \"clients\")\n",[941,6261,6259],{"__ignoreMap":1037},[1111,6263,6264,6271,6278],{},[1114,6265,6266,6270],{},[950,6267,6268],{},[941,6269,62],{}," is one unlocked session. It holds the encryption keys in memory while you work and hands them back to nobody.",[1114,6272,6273,6277],{},[950,6274,6275],{},[941,6276,94],{}," is an isolated namespace — one tenant, one company, one user. Each vault has its own keyring, so access to one vault says nothing about another.",[1114,6279,6280,6284,6285,1261,6287,1261,6289,1261,6291,6293],{},[950,6281,6282],{},[941,6283,3121],{}," is a typed set of records inside a vault. The TypeScript generic carries through ",[941,6286,3158],{},[941,6288,3161],{},[941,6290,5286],{},[941,6292,3239],{},", so records come back typed.",[937,6295,6296,6297,6300],{},"Every record is addressed by a ",[941,6298,6299],{},"(vault, collection, id)"," triple.",[1022,6302,6304],{"id":6303},"lifecycle","Lifecycle",[937,6306,6307],{},"The full round trip — create a session, open a vault, write and read a record, close:",[2217,6309,6311],{"className":2219,"code":6310,"language":2221,"meta":1037,"style":1037},"import { createNoydb } from '@noy-db\u002Fhub'\n\ntype Invoice = { amount: number; status: string }\n\n\u002F\u002F Without `store`, noy-db uses its built-in in-memory store — nothing\n\u002F\u002F persists past the session. Pass `store: …` to persist (see Stores below).\nconst db = await createNoydb({ user: 'alice', secret: 'a-strong-secret' })\nconst vault = await db.openVault('acme')\nconst invoices = vault.collection\u003CInvoice>('invoices')\n\nawait invoices.put('inv-1', { amount: 100, status: 'paid' })\nconst inv = await invoices.get('inv-1')\n\u002F\u002F → { amount: 100, status: 'paid' }\n\ndb.close()  \u002F\u002F wipes the in-memory encryption keys (KEK + per-collection DEKs)\n",[941,6312,6313,6331,6335,6370,6374,6379,6384,6428,6455,6492,6496,6546,6574,6580,6585],{"__ignoreMap":1037},[2225,6314,6315,6317,6319,6321,6323,6325,6327,6329],{"class":2227,"line":2228},[2225,6316,2232],{"class":2231},[2225,6318,2236],{"class":2235},[2225,6320,2744],{"class":2239},[2225,6322,2243],{"class":2235},[2225,6324,2246],{"class":2231},[2225,6326,2249],{"class":2235},[2225,6328,1086],{"class":2252},[2225,6330,2256],{"class":2235},[2225,6332,6333],{"class":2227,"line":1041},[2225,6334,2285],{"emptyLinePlaceholder":2284},[2225,6336,6337,6340,6344,6347,6349,6352,6354,6357,6359,6362,6364,6367],{"class":2227,"line":1038},[2225,6338,6339],{"class":2774},"type",[2225,6341,6343],{"class":6342},"sBMFI"," Invoice",[2225,6345,6346],{"class":2235}," =",[2225,6348,2236],{"class":2235},[2225,6350,6351],{"class":2307}," amount",[2225,6353,2311],{"class":2235},[2225,6355,6356],{"class":6342}," number",[2225,6358,3769],{"class":2235},[2225,6360,6361],{"class":2307}," status",[2225,6363,2311],{"class":2235},[2225,6365,6366],{"class":6342}," string",[2225,6368,6369],{"class":2235}," }\n",[2225,6371,6372],{"class":2227,"line":2288},[2225,6373,2285],{"emptyLinePlaceholder":2284},[2225,6375,6376],{"class":2227,"line":2304},[2225,6377,6378],{"class":2763},"\u002F\u002F Without `store`, noy-db uses its built-in in-memory store — nothing\n",[2225,6380,6381],{"class":2227,"line":2356},[2225,6382,6383],{"class":2763},"\u002F\u002F persists past the session. Pass `store: …` to persist (see Stores below).\n",[2225,6385,6386,6388,6390,6392,6394,6396,6398,6400,6402,6404,6406,6409,6411,6413,6415,6417,6419,6422,6424,6426],{"class":2227,"line":2830},[2225,6387,2775],{"class":2774},[2225,6389,2778],{"class":2239},[2225,6391,2781],{"class":2235},[2225,6393,2784],{"class":2231},[2225,6395,2744],{"class":2291},[2225,6397,2789],{"class":2239},[2225,6399,2792],{"class":2235},[2225,6401,2795],{"class":2307},[2225,6403,2311],{"class":2235},[2225,6405,2249],{"class":2235},[2225,6407,6408],{"class":2252},"alice",[2225,6410,2805],{"class":2235},[2225,6412,2298],{"class":2235},[2225,6414,2810],{"class":2307},[2225,6416,2311],{"class":2235},[2225,6418,2249],{"class":2235},[2225,6420,6421],{"class":2252},"a-strong-secret",[2225,6423,2805],{"class":2235},[2225,6425,2243],{"class":2235},[2225,6427,2362],{"class":2239},[2225,6429,6430,6432,6434,6436,6438,6440,6442,6444,6446,6448,6451,6453],{"class":2227,"line":2861},[2225,6431,2775],{"class":2774},[2225,6433,2835],{"class":2239},[2225,6435,2781],{"class":2235},[2225,6437,2784],{"class":2231},[2225,6439,2842],{"class":2239},[2225,6441,1520],{"class":2235},[2225,6443,2847],{"class":2291},[2225,6445,2789],{"class":2239},[2225,6447,2805],{"class":2235},[2225,6449,6450],{"class":2252},"acme",[2225,6452,2805],{"class":2235},[2225,6454,2362],{"class":2239},[2225,6456,6457,6459,6462,6464,6467,6469,6472,6475,6478,6481,6483,6485,6488,6490],{"class":2227,"line":4246},[2225,6458,2775],{"class":2774},[2225,6460,6461],{"class":2239}," invoices ",[2225,6463,2781],{"class":2235},[2225,6465,6466],{"class":2239}," vault",[2225,6468,1520],{"class":2235},[2225,6470,6471],{"class":2291},"collection",[2225,6473,6474],{"class":2235},"\u003C",[2225,6476,6477],{"class":6342},"Invoice",[2225,6479,6480],{"class":2235},">",[2225,6482,2789],{"class":2239},[2225,6484,2805],{"class":2235},[2225,6486,6487],{"class":2252},"invoices",[2225,6489,2805],{"class":2235},[2225,6491,2362],{"class":2239},[2225,6493,6494],{"class":2227,"line":4253},[2225,6495,2285],{"emptyLinePlaceholder":2284},[2225,6497,6498,6501,6504,6506,6508,6510,6512,6515,6517,6519,6521,6523,6525,6529,6531,6533,6535,6537,6540,6542,6544],{"class":2227,"line":4259},[2225,6499,6500],{"class":2231},"await",[2225,6502,6503],{"class":2239}," invoices",[2225,6505,1520],{"class":2235},[2225,6507,3161],{"class":2291},[2225,6509,2789],{"class":2239},[2225,6511,2805],{"class":2235},[2225,6513,6514],{"class":2252},"inv-1",[2225,6516,2805],{"class":2235},[2225,6518,2298],{"class":2235},[2225,6520,2236],{"class":2235},[2225,6522,6351],{"class":2307},[2225,6524,2311],{"class":2235},[2225,6526,6528],{"class":6527},"sbssI"," 100",[2225,6530,2298],{"class":2235},[2225,6532,6361],{"class":2307},[2225,6534,2311],{"class":2235},[2225,6536,2249],{"class":2235},[2225,6538,6539],{"class":2252},"paid",[2225,6541,2805],{"class":2235},[2225,6543,2243],{"class":2235},[2225,6545,2362],{"class":2239},[2225,6547,6549,6551,6554,6556,6558,6560,6562,6564,6566,6568,6570,6572],{"class":2227,"line":6548},12,[2225,6550,2775],{"class":2774},[2225,6552,6553],{"class":2239}," inv ",[2225,6555,2781],{"class":2235},[2225,6557,2784],{"class":2231},[2225,6559,6503],{"class":2239},[2225,6561,1520],{"class":2235},[2225,6563,3158],{"class":2291},[2225,6565,2789],{"class":2239},[2225,6567,2805],{"class":2235},[2225,6569,6514],{"class":2252},[2225,6571,2805],{"class":2235},[2225,6573,2362],{"class":2239},[2225,6575,6577],{"class":2227,"line":6576},13,[2225,6578,6579],{"class":2763},"\u002F\u002F → { amount: 100, status: 'paid' }\n",[2225,6581,6583],{"class":2227,"line":6582},14,[2225,6584,2285],{"emptyLinePlaceholder":2284},[2225,6586,6588,6591,6593,6596,6599],{"class":2227,"line":6587},15,[2225,6589,6590],{"class":2239},"db",[2225,6592,1520],{"class":2235},[2225,6594,6595],{"class":2291},"close",[2225,6597,6598],{"class":2239},"()  ",[2225,6600,6601],{"class":2763},"\u002F\u002F wipes the in-memory encryption keys (KEK + per-collection DEKs)\n",[2217,6603,6606],{"className":6604,"code":6605,"language":141,"meta":1037,"style":1037},"language-json shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","{ \"amount\": 100, \"status\": \"paid\" }\n",[941,6607,6608],{"__ignoreMap":1037},[2225,6609,6610,6612,6615,6618,6621,6623,6625,6627,6629,6632,6634,6636,6638,6640,6642],{"class":2227,"line":2228},[2225,6611,2792],{"class":2235},[2225,6613,6614],{"class":2235}," \"",[2225,6616,6617],{"class":2774},"amount",[2225,6619,6620],{"class":2235},"\"",[2225,6622,2311],{"class":2235},[2225,6624,6528],{"class":6527},[2225,6626,2298],{"class":2235},[2225,6628,6614],{"class":2235},[2225,6630,6631],{"class":2774},"status",[2225,6633,6620],{"class":2235},[2225,6635,2311],{"class":2235},[2225,6637,6614],{"class":2235},[2225,6639,6539],{"class":2252},[2225,6641,6620],{"class":2235},[2225,6643,6369],{"class":2235},[937,6645,6646,6648,6649,6652,6653,6656],{},[941,6647,3158],{}," returns the decrypted record, or ",[941,6650,6651],{},"null"," if the id does not exist. The KEK (key-encryption key, derived from your secret) and the per-collection DEKs (data-encryption keys) never leave memory — ",[1007,6654,21],{"href":6655},".\u002Fnoydb\u002Fenclave"," covers the key hierarchy.",[1022,6658,6660],{"id":6659},"handles","Handles",[937,6662,6663],{},"Each handle has its own page with its main methods:",[1111,6665,6666,6677,6685],{},[1114,6667,6668,1169,6674,6676],{},[950,6669,6670],{},[1007,6671,6673],{"href":6672},".\u002Fnoydb\u002F","Client",[941,6675,62],{}," session",[1114,6678,6679,6684],{},[950,6680,6681],{},[1007,6682,94],{"href":6683},".\u002Fvault\u002F"," — an isolated namespace",[1114,6686,6687,6692],{},[950,6688,6689],{},[1007,6690,103],{"href":6691},".\u002Fcollection\u002F"," — a typed set of records",[1022,6694,6696],{"id":6695},"reserved-collection-names","Reserved collection names",[937,6698,6699,6700,2311],{},"noy-db keeps its own bookkeeping in internal collections whose names start with ",[941,6701,6702],{},"_",[1111,6704,6705,6711,6720,6731,6740,6749,6758,6767,6776],{},[1114,6706,6707,6710],{},[941,6708,6709],{},"_keyring"," — wrapped DEKs per user",[1114,6712,6713,6716,6717,1381],{},[941,6714,6715],{},"_history"," — version snapshots (with ",[941,6718,6719],{},"withHistory()",[1114,6721,6722,1261,6725,6728,6729,1381],{},[941,6723,6724],{},"_ledger",[941,6726,6727],{},"_ledger_deltas"," — audit ledger (with ",[941,6730,6719],{},[1114,6732,6733,6736,6737,1381],{},[941,6734,6735],{},"_dict_\u003Cname>"," — shared dictionaries (with ",[941,6738,6739],{},"withI18n()",[1114,6741,6742,6745,6746,1381],{},[941,6743,6744],{},"_consent_audit"," — consent log (with ",[941,6747,6748],{},"withConsent()",[1114,6750,6751,6754,6755,1381],{},[941,6752,6753],{},"_periods"," — period anchors (with ",[941,6756,6757],{},"withPeriods()",[1114,6759,6760,6763,6764,1381],{},[941,6761,6762],{},"_idx\u002F\u003Cfield>\u002F\u003Cid>"," — persisted index side-cars (with ",[941,6765,6766],{},"withIndexing()",[1114,6768,6769,6772,6773,1381],{},[941,6770,6771],{},"_blob_*"," — blob slot metadata + chunks (with ",[941,6774,6775],{},"withBlobs()",[1114,6777,6778,6781,6782,1381],{},[941,6779,6780],{},"_presence_\u003Ccollection>"," — presence storage-poll fallback (with ",[941,6783,4676],{},[937,6785,6786,6787,6789,6790,6793,6794,968,6797,968,6800,6803,6804,6807,6808,968,6811,968,6814,6817,6818,6821],{},"Reserved names cannot be opened through the generic handle: ",[941,6788,2138],{}," throws ",[941,6791,6792],{},"ReservedCollectionNameError"," for ",[941,6795,6796],{},"_dict_*",[941,6798,6799],{},"_sequences",[941,6801,6802],{},"_links_*",", and secret-bearing internals such as ",[941,6805,6806],{},"_sync_credentials"," — use their dedicated APIs instead (",[941,6809,6810],{},"vault.dictionary()",[941,6812,6813],{},"vault.sequence()",[941,6815,6816],{},"vault.link()","). Plaintext exports skip all underscore-prefixed collections, so internal bookkeeping never leaks into an export; ",[941,6819,6820],{},"vault.dump()"," includes them, because a full backup needs them.",[1022,6823,6825],{"id":6824},"edge-cases-limits","Edge cases & limits",[1111,6827,6828,6849,6866],{},[1114,6829,6830,6833,6834,6837,6838,6841,6842,2462,6845,6848],{},[950,6831,6832],{},"Memory-first",": an eager collection (the default) decrypts its full record set into memory on first access. The target scale is 1K–50K records per collection; beyond that, open heavy collections in lazy mode (",[941,6835,6836],{},"prefetch: false"," plus a ",[941,6839,6840],{},"cache"," bound) so reads go through a size-bounded LRU cache — see ",[1007,6843,719],{"href":6844},"..\u002Fservices\u002Flazy",[1007,6846,705],{"href":6847},"..\u002Fservices\u002Findexing"," for querying lazy collections.",[1114,6850,6851,6854,6855,6857,6858,6861,6862,6865],{},[950,6852,6853],{},"Concurrent writers",": every stored envelope carries a version counter ",[941,6856,1700],{},", and the store contract supports compare-and-swap on it — a write that loses a version race fails with ",[941,6859,6860],{},"ConflictError",". Multi-record ",[941,6863,6864],{},"transaction()"," batches pre-flight these version checks and commit all-or-nothing.",[1114,6867,6868,6871,6872,6876],{},[950,6869,6870],{},"Heavy fields",": a record is one monolithic encrypted envelope — reading any field decrypts the whole record. For a collection mixing small hot fields with a large rarely-read one, ",[1007,6873,6875],{"href":6874},"..\u002Fservices\u002Fsatellites","satellite collections"," (experimental) offer an off-row modeling option.",[1022,6878,2621],{"id":2620},[1111,6880,6881,6886,6893,6900],{},[1114,6882,6883,6885],{},[1007,6884,21],{"href":6655}," — the key hierarchy behind KEK \u002F DEK",[1114,6887,6888,6892],{},[1007,6889,6891],{"href":6890},".\u002F05-stores","Stores"," — the pluggable ciphertext storage layer",[1114,6894,6895,6899],{},[1007,6896,6898],{"href":6897},".\u002F06-permissions-and-keyring","Permissions & Keyring"," — multi-user access",[1114,6901,6902,6904,6905],{},[1007,6903,4272],{"href":6254}," — everything the kernel gates behind ",[941,6906,943],{},[2645,6908,6909],{},"html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":1037,"searchDepth":1038,"depth":1038,"links":6911},[6912,6913,6914,6915,6916,6917],{"id":6241,"depth":1041,"text":6242},{"id":6303,"depth":1041,"text":6304},{"id":6659,"depth":1041,"text":6660},{"id":6695,"depth":1041,"text":6696},{"id":6824,"depth":1041,"text":6825},{"id":2620,"depth":1041,"text":2621},"Where a record lives: the Noydb → Vault → Collection\u003CT> hierarchy, the session lifecycle, and how the three handles map to their pages.",{},{"title":10},{"title":10,"description":6918},"essential","11f5IQ8uwGEm-vGVL9SPctsabedWYZrDI7j8ZLWLSOg",{"id":6925,"title":6891,"access":12,"apiToc":929,"body":6926,"category":1042,"description":9836,"extension":1044,"meta":9837,"navigation":9838,"nest":2635,"path":51,"seo":9839,"sinceVersion":1042,"status":1049,"stem":52,"subtitle":1042,"tier":6922,"tocExternal":1042,"__hash__":9840},"docs\u002Fcore\u002F05-stores.md",{"type":931,"value":6927,"toc":9825},[6928,6931,6944,6946,6960,6968,6972,6985,7352,7361,7370,7466,7477,7522,7645,7696,7700,7802,7812,7816,7823,7993,7997,8011,9349,9355,9454,9477,9496,9500,9522,9617,9650,9657,9781,9790,9794,9800,9802,9822],[934,6929,6891],{"id":6930},"stores",[1061,6932,6933],{},[937,6934,6935,6235,6938,953,6941,6943],{},[950,6936,6937],{},"Always-on. The pluggable storage layer.",[941,6939,6940],{},"packages\u002Fhub\u002Fsrc\u002Fkernel\u002Ftypes.ts",[941,6942,3154],{}," interface)",[1022,6945,6242],{"id":6241},[937,6947,6948,6949,6952,6953,6956,6957,6959],{},"noy-db encrypts every record inside the hub — the core library — before anything is written out. A ",[950,6950,6951],{},"store"," is the pluggable backend that persists the result: memory, a directory of JSON files, IndexedDB, S3, Postgres, and so on. The hub hands the store an ",[1092,6954,6955],{},"encrypted envelope"," (the ciphertext wrapper described in ",[1007,6958,21],{"href":6655},") and gets ciphertext back. The store never sees plaintext and never runs a query — the query DSL runs inside the hub, after decryption, so the backend stays a dumb, untrusted ciphertext shelf.",[937,6961,6962,6963,6967],{},"Three essential backends ship with noy-db itself, on top of the in-memory store built into the hub; the extended cloud \u002F SQL \u002F remote-FS stores are published from ",[1007,6964,5351],{"href":6965,"rel":6966},"https:\u002F\u002Fgithub.com\u002Fnoy-db\u002Fto",[3371],". You can write your own in ~150 lines.",[1022,6969,6971],{"id":6970},"the-contract","The contract",[937,6973,6974,6975,6977,6978,6980,6981,6984],{},"Six required methods; everything else is optional. ",[941,6976,2694],{}," is the compartment name, ",[941,6979,6471],{}," the record group inside it, ",[941,6982,6983],{},"id"," the record key.",[2217,6986,6988],{"className":2219,"code":6987,"language":2221,"meta":1037,"style":1037},"interface NoydbStore {\n  \u002F** Optional human-readable name for diagnostics ('memory', 'file', 'dynamo', …). *\u002F\n  name?: string\n\n  \u002F** Optional declared capabilities — see StoreCapabilities below. *\u002F\n  capabilities?: StoreCapabilities\n\n  \u002F** Get a single record. Returns null if not found. *\u002F\n  get(vault: string, collection: string, id: string): Promise\u003CEncryptedEnvelope | null>\n\n  \u002F** Put a record. Throws ConflictError if expectedVersion doesn't match the current _v,\n   *  where an absent id counts as version 0 (since hub 0.10.0-pre.2). *\u002F\n  put(vault: string, collection: string, id: string,\n      envelope: EncryptedEnvelope, expectedVersion?: number): Promise\u003Cvoid>\n\n  \u002F** Delete a record. *\u002F\n  delete(vault: string, collection: string, id: string): Promise\u003Cvoid>\n\n  \u002F** List all record IDs in a collection. *\u002F\n  list(vault: string, collection: string): Promise\u003Cstring[]>\n\n  \u002F** Load all records for a vault (initial hydration). *\u002F\n  loadAll(vault: string): Promise\u003CVaultSnapshot>\n\n  \u002F** Save all records for a vault (restore). REPLACES since hub 0.10.0-pre.2 — see below. *\u002F\n  saveAll(vault: string, data: VaultSnapshot): Promise\u003Cvoid>\n}\n",[941,6989,6990,6999,7004,7015,7019,7024,7034,7038,7043,7094,7098,7103,7108,7139,7168,7172,7178,7218,7223,7229,7265,7270,7276,7301,7306,7312,7346],{"__ignoreMap":1037},[2225,6991,6992,6995,6997],{"class":2227,"line":2228},[2225,6993,6994],{"class":2774},"interface",[2225,6996,3404],{"class":6342},[2225,6998,2301],{"class":2235},[2225,7000,7001],{"class":2227,"line":1041},[2225,7002,7003],{"class":2763},"  \u002F** Optional human-readable name for diagnostics ('memory', 'file', 'dynamo', …). *\u002F\n",[2225,7005,7006,7009,7012],{"class":2227,"line":1038},[2225,7007,7008],{"class":2307},"  name",[2225,7010,7011],{"class":2235},"?:",[2225,7013,7014],{"class":6342}," string\n",[2225,7016,7017],{"class":2227,"line":2288},[2225,7018,2285],{"emptyLinePlaceholder":2284},[2225,7020,7021],{"class":2227,"line":2304},[2225,7022,7023],{"class":2763},"  \u002F** Optional declared capabilities — see StoreCapabilities below. *\u002F\n",[2225,7025,7026,7029,7031],{"class":2227,"line":2356},[2225,7027,7028],{"class":2307},"  capabilities",[2225,7030,7011],{"class":2235},[2225,7032,7033],{"class":6342}," StoreCapabilities\n",[2225,7035,7036],{"class":2227,"line":2830},[2225,7037,2285],{"emptyLinePlaceholder":2284},[2225,7039,7040],{"class":2227,"line":2861},[2225,7041,7042],{"class":2763},"  \u002F** Get a single record. Returns null if not found. *\u002F\n",[2225,7044,7045,7048,7050,7053,7055,7057,7059,7062,7064,7066,7068,7071,7073,7075,7077,7080,7082,7085,7088,7091],{"class":2227,"line":4246},[2225,7046,7047],{"class":2307},"  get",[2225,7049,2789],{"class":2235},[2225,7051,2694],{"class":7052},"sHdIc",[2225,7054,2311],{"class":2235},[2225,7056,6366],{"class":6342},[2225,7058,2298],{"class":2235},[2225,7060,7061],{"class":7052}," collection",[2225,7063,2311],{"class":2235},[2225,7065,6366],{"class":6342},[2225,7067,2298],{"class":2235},[2225,7069,7070],{"class":7052}," id",[2225,7072,2311],{"class":2235},[2225,7074,6366],{"class":6342},[2225,7076,6255],{"class":2235},[2225,7078,7079],{"class":6342}," Promise",[2225,7081,6474],{"class":2235},[2225,7083,7084],{"class":6342},"EncryptedEnvelope",[2225,7086,7087],{"class":2235}," |",[2225,7089,7090],{"class":6342}," null",[2225,7092,7093],{"class":2235},">\n",[2225,7095,7096],{"class":2227,"line":4253},[2225,7097,2285],{"emptyLinePlaceholder":2284},[2225,7099,7100],{"class":2227,"line":4259},[2225,7101,7102],{"class":2763},"  \u002F** Put a record. Throws ConflictError if expectedVersion doesn't match the current _v,\n",[2225,7104,7105],{"class":2227,"line":6548},[2225,7106,7107],{"class":2763},"   *  where an absent id counts as version 0 (since hub 0.10.0-pre.2). *\u002F\n",[2225,7109,7110,7113,7115,7117,7119,7121,7123,7125,7127,7129,7131,7133,7135,7137],{"class":2227,"line":6576},[2225,7111,7112],{"class":2307},"  put",[2225,7114,2789],{"class":2235},[2225,7116,2694],{"class":7052},[2225,7118,2311],{"class":2235},[2225,7120,6366],{"class":6342},[2225,7122,2298],{"class":2235},[2225,7124,7061],{"class":7052},[2225,7126,2311],{"class":2235},[2225,7128,6366],{"class":6342},[2225,7130,2298],{"class":2235},[2225,7132,7070],{"class":7052},[2225,7134,2311],{"class":2235},[2225,7136,6366],{"class":6342},[2225,7138,1707],{"class":2235},[2225,7140,7141,7144,7146,7148,7150,7153,7155,7157,7159,7161,7163,7166],{"class":2227,"line":6582},[2225,7142,7143],{"class":7052},"      envelope",[2225,7145,2311],{"class":2235},[2225,7147,3409],{"class":6342},[2225,7149,2298],{"class":2235},[2225,7151,7152],{"class":7052}," expectedVersion",[2225,7154,7011],{"class":2235},[2225,7156,6356],{"class":6342},[2225,7158,6255],{"class":2235},[2225,7160,7079],{"class":6342},[2225,7162,6474],{"class":2235},[2225,7164,7165],{"class":6342},"void",[2225,7167,7093],{"class":2235},[2225,7169,7170],{"class":2227,"line":6587},[2225,7171,2285],{"emptyLinePlaceholder":2284},[2225,7173,7175],{"class":2227,"line":7174},16,[2225,7176,7177],{"class":2763},"  \u002F** Delete a record. *\u002F\n",[2225,7179,7181,7184,7186,7188,7190,7192,7194,7196,7198,7200,7202,7204,7206,7208,7210,7212,7214,7216],{"class":2227,"line":7180},17,[2225,7182,7183],{"class":2307},"  delete",[2225,7185,2789],{"class":2235},[2225,7187,2694],{"class":7052},[2225,7189,2311],{"class":2235},[2225,7191,6366],{"class":6342},[2225,7193,2298],{"class":2235},[2225,7195,7061],{"class":7052},[2225,7197,2311],{"class":2235},[2225,7199,6366],{"class":6342},[2225,7201,2298],{"class":2235},[2225,7203,7070],{"class":7052},[2225,7205,2311],{"class":2235},[2225,7207,6366],{"class":6342},[2225,7209,6255],{"class":2235},[2225,7211,7079],{"class":6342},[2225,7213,6474],{"class":2235},[2225,7215,7165],{"class":6342},[2225,7217,7093],{"class":2235},[2225,7219,7221],{"class":2227,"line":7220},18,[2225,7222,2285],{"emptyLinePlaceholder":2284},[2225,7224,7226],{"class":2227,"line":7225},19,[2225,7227,7228],{"class":2763},"  \u002F** List all record IDs in a collection. *\u002F\n",[2225,7230,7232,7235,7237,7239,7241,7243,7245,7247,7249,7251,7253,7255,7257,7260,7263],{"class":2227,"line":7231},20,[2225,7233,7234],{"class":2307},"  list",[2225,7236,2789],{"class":2235},[2225,7238,2694],{"class":7052},[2225,7240,2311],{"class":2235},[2225,7242,6366],{"class":6342},[2225,7244,2298],{"class":2235},[2225,7246,7061],{"class":7052},[2225,7248,2311],{"class":2235},[2225,7250,6366],{"class":6342},[2225,7252,6255],{"class":2235},[2225,7254,7079],{"class":6342},[2225,7256,6474],{"class":2235},[2225,7258,7259],{"class":6342},"string",[2225,7261,7262],{"class":2239},"[]",[2225,7264,7093],{"class":2235},[2225,7266,7268],{"class":2227,"line":7267},21,[2225,7269,2285],{"emptyLinePlaceholder":2284},[2225,7271,7273],{"class":2227,"line":7272},22,[2225,7274,7275],{"class":2763},"  \u002F** Load all records for a vault (initial hydration). *\u002F\n",[2225,7277,7279,7282,7284,7286,7288,7290,7292,7294,7296,7299],{"class":2227,"line":7278},23,[2225,7280,7281],{"class":2307},"  loadAll",[2225,7283,2789],{"class":2235},[2225,7285,2694],{"class":7052},[2225,7287,2311],{"class":2235},[2225,7289,6366],{"class":6342},[2225,7291,6255],{"class":2235},[2225,7293,7079],{"class":6342},[2225,7295,6474],{"class":2235},[2225,7297,7298],{"class":6342},"VaultSnapshot",[2225,7300,7093],{"class":2235},[2225,7302,7304],{"class":2227,"line":7303},24,[2225,7305,2285],{"emptyLinePlaceholder":2284},[2225,7307,7309],{"class":2227,"line":7308},25,[2225,7310,7311],{"class":2763},"  \u002F** Save all records for a vault (restore). REPLACES since hub 0.10.0-pre.2 — see below. *\u002F\n",[2225,7313,7315,7318,7320,7322,7324,7326,7328,7331,7333,7336,7338,7340,7342,7344],{"class":2227,"line":7314},26,[2225,7316,7317],{"class":2307},"  saveAll",[2225,7319,2789],{"class":2235},[2225,7321,2694],{"class":7052},[2225,7323,2311],{"class":2235},[2225,7325,6366],{"class":6342},[2225,7327,2298],{"class":2235},[2225,7329,7330],{"class":7052}," data",[2225,7332,2311],{"class":2235},[2225,7334,7335],{"class":6342}," VaultSnapshot",[2225,7337,6255],{"class":2235},[2225,7339,7079],{"class":6342},[2225,7341,6474],{"class":2235},[2225,7343,7165],{"class":6342},[2225,7345,7093],{"class":2235},[2225,7347,7349],{"class":2227,"line":7348},27,[2225,7350,7351],{"class":2235},"}\n",[937,7353,7354,7356,7357,7360],{},[941,7355,7298],{}," is ",[941,7358,7359],{},"Record\u003Cstring, Record\u003Cstring, EncryptedEnvelope>>"," — collection → id → envelope.",[937,7362,7363,7364,7366,7367,7369],{},"Two rules a store must get exactly right, both tightened in hub ",[941,7365,2126],{}," and asserted by the\n",[941,7368,3486],{}," conformance kit:",[1111,7371,7372,7401,7421],{},[1114,7373,7374,7377,7378,7381,7382,7385,7386,7389,7390,7392,7393,7396,7397,7400],{},[950,7375,7376],{},"An absent record is at version 0."," Compare ",[941,7379,7380],{},"(existing?._v ?? 0) !== expectedVersion",", never\n",[941,7383,7384],{},"existing && existing._v !== expectedVersion",". So ",[941,7387,7388],{},"expectedVersion: 0"," is an exact \"write only if\nabsent\", and a nonzero expectation against a missing id throws ",[941,7391,6860],{}," (carrying\n",[941,7394,7395],{},"version: 0",") instead of upserting — the shorter guard silently upserted, which older stores\nwritten against hub ",[941,7398,7399],{},"0.10.0-pre.1"," and earlier may still do. Don't implement absence by throwing\neither: the first create must succeed.",[1114,7402,7403,7408,7409,7411,7412,7414,7415,7417,7418,7420],{},[950,7404,7405,7407],{},[941,7406,3173],{}," replaces, it does not merge."," Every non-",[941,7410,6702],{}," collection is dropped and the snapshot\nwritten over the top, so a collection absent from the snapshot is gone and an empty record set\nempties its collection. Reserved ",[941,7413,6702],{},"-prefixed collections are never dropped, but are overwritten\nwhen the snapshot carries them — so a snapshot carrying ",[941,7416,6709],{}," replaces the live keyring\nwithout passing through any keyring code path. Treat a ",[941,7419,3173],{}," payload as exactly as trusted as\nthe vault itself.",[1114,7422,7423,7426,7427,7429,7430,7433,7434,968,7436,2514,7438,7440,7441,997,7444,7447,7448,4688,7450,7453,7454,1324,7457,5502,7459,997,7462,7465],{},[950,7424,7425],{},"Ids are opaque, within limits."," An id may contain ",[941,7428,1625],{}," and a NUL byte (hub emits both — a\npartitioned sequence key joins its parts with ",[941,7431,7432],{},"\\x00","), and must round-trip through ",[941,7435,3161],{},[941,7437,3158],{},[941,7439,3167],{},". Ids are case-distinct: ",[941,7442,7443],{},"put('Abc')",[941,7445,7446],{},"put('abc')"," are two records, or the store refuses\nthe second write. Hub refuses a non-single-segment vault or collection name, and an id with an\nempty, ",[941,7449,1520],{},[941,7451,7452],{},".."," component, before any store sees it. The kit asks all of these since\n",[941,7455,7456],{},"@noy-db\u002Fports",[941,7458,2126],{},[941,7460,7461],{},"to-file",[941,7463,7464],{},"to-mysql"," each failed one when it was added.",[937,7467,7468,7469,7471,7472,7474,7475,1520],{},"Every one of these types (plus the store-facing error classes such as ",[941,7470,6860],{},") is importable from ",[941,7473,3299],{},", the ciphertext-facing subpath that standalone store packages bind. They are also re-exported from the root ",[941,7476,1086],{},[937,7478,7479,7480,7483,7484,7487,7488,7491,1324,7494,7497,7498,1617,7500,7505,7506,7509,7510,7512,7513,7515,7516,7518,7519,7521],{},"To ",[1092,7481,7482],{},"match"," a conflict a store may have thrown, use the ",[941,7485,7486],{},"isConflictError"," predicate (",[941,7489,7490],{},"instanceof",[950,7492,7493],{},"or",[941,7495,7496],{},"name === 'ConflictError'",") rather than a bare ",[941,7499,7490],{},[1007,7501,7504],{"href":7502,"rel":7503},"https:\u002F\u002Fgithub.com\u002Fnoy-db\u002Fcore\u002Fissues\u002F935",[3371],"noy-db#935","). It is what every hub-internal catch\nsite that handles a store-thrown conflict — the sync engine's push channels, the CAS retry loops —\nuses, and it exists for a failure the class check cannot survive: when npm fails to dedupe hub\nwithin the peer range, a store bound to a ",[950,7507,7508],{},"second copy"," of ",[941,7511,3299],{}," throws a\n",[941,7514,6860],{}," with a foreign class identity. ",[941,7517,7490],{}," silently returns ",[941,7520,2560],{},", so a retry\nloop rethrows instead of retrying and the sync engine misfiles the conflict with no resolution run.",[2217,7523,7525],{"className":2219,"code":7524,"language":2221,"meta":1037,"style":1037},"import { isConflictError } from '@noy-db\u002Fhub\u002Fto'\n\ntry {\n  await store.put(vault, collection, id, envelope, expectedVersion)\n} catch (err) {\n  if (isConflictError(err)) {\n    \u002F\u002F re-read, re-apply, retry — matches across duplicate hub copies\n  }\n  throw err\n}\n",[941,7526,7527,7546,7550,7557,7592,7604,7623,7628,7633,7641],{"__ignoreMap":1037},[2225,7528,7529,7531,7533,7536,7538,7540,7542,7544],{"class":2227,"line":2228},[2225,7530,2232],{"class":2231},[2225,7532,2236],{"class":2235},[2225,7534,7535],{"class":2239}," isConflictError",[2225,7537,2243],{"class":2235},[2225,7539,2246],{"class":2231},[2225,7541,2249],{"class":2235},[2225,7543,3299],{"class":2252},[2225,7545,2256],{"class":2235},[2225,7547,7548],{"class":2227,"line":1041},[2225,7549,2285],{"emptyLinePlaceholder":2284},[2225,7551,7552,7555],{"class":2227,"line":1038},[2225,7553,7554],{"class":2231},"try",[2225,7556,2301],{"class":2235},[2225,7558,7559,7562,7565,7567,7569,7571,7573,7575,7577,7579,7581,7583,7586,7588,7590],{"class":2227,"line":2288},[2225,7560,7561],{"class":2231},"  await",[2225,7563,7564],{"class":2239}," store",[2225,7566,1520],{"class":2235},[2225,7568,3161],{"class":2291},[2225,7570,2789],{"class":2307},[2225,7572,2694],{"class":2239},[2225,7574,2298],{"class":2235},[2225,7576,7061],{"class":2239},[2225,7578,2298],{"class":2235},[2225,7580,7070],{"class":2239},[2225,7582,2298],{"class":2235},[2225,7584,7585],{"class":2239}," envelope",[2225,7587,2298],{"class":2235},[2225,7589,7152],{"class":2239},[2225,7591,2362],{"class":2307},[2225,7593,7594,7596,7599,7602],{"class":2227,"line":2304},[2225,7595,2359],{"class":2235},[2225,7597,7598],{"class":2231}," catch",[2225,7600,7601],{"class":2239}," (err) ",[2225,7603,4200],{"class":2235},[2225,7605,7606,7609,7611,7613,7615,7618,7621],{"class":2227,"line":2356},[2225,7607,7608],{"class":2231},"  if",[2225,7610,953],{"class":2307},[2225,7612,7486],{"class":2291},[2225,7614,2789],{"class":2307},[2225,7616,7617],{"class":2239},"err",[2225,7619,7620],{"class":2307},")) ",[2225,7622,4200],{"class":2235},[2225,7624,7625],{"class":2227,"line":2830},[2225,7626,7627],{"class":2763},"    \u002F\u002F re-read, re-apply, retry — matches across duplicate hub copies\n",[2225,7629,7630],{"class":2227,"line":2861},[2225,7631,7632],{"class":2235},"  }\n",[2225,7634,7635,7638],{"class":2227,"line":4246},[2225,7636,7637],{"class":2231},"  throw",[2225,7639,7640],{"class":2239}," err\n",[2225,7642,7643],{"class":2227,"line":4253},[2225,7644,7351],{"class":2235},[7646,7647,7650],"callout",{"color":7648,"icon":7649},"neutral","i-lucide-package-check",[937,7651,7652,7658,7659,7662,7663,7665,7666,1324,7668,7671,7672,7674,7675,7678,7679,7681,7682,7684,7685,953,7687,7692,7693,7695],{},[950,7653,7654,7655,7657],{},"Import it from ",[941,7656,3299],{}," if you are writing a store."," Until ",[941,7660,7661],{},"0.7.0-pre.6"," the predicate\nwas exported only from the root barrel while ",[941,7664,3328],{}," exported the ",[941,7667,6860],{},[950,7669,7670],{},"class"," — the\nwrong way round for the one seam that needs it, since a standalone store package binds ",[941,7673,3328],{}," and\nnothing else. Store authors were pointed at a function they could not import, and the obvious\nfallback, ",[941,7676,7677],{},"instanceof ConflictError"," off ",[941,7680,3328],{},", is precisely the defect the predicate prevents.\n",[941,7683,7661],{}," adds it to ",[941,7686,3328],{},[1007,7688,7691],{"href":7689,"rel":7690},"https:\u002F\u002Fgithub.com\u002Fnoy-db\u002Fcore\u002Fissues\u002F1224",[3371],"noy-db#1224",");\nthe root export is unchanged, so in-app code that already imports it from ",[941,7694,1086],{}," keeps\nworking.",[1591,7697,7699],{"id":7698},"declared-capabilities","Declared capabilities",[2217,7701,7703],{"className":2219,"code":7702,"language":2221,"meta":1037,"style":1037},"interface StoreCapabilities {\n  casAtomic: boolean        \u002F\u002F is the expectedVersion check + write atomic at the storage layer?\n  auth: StoreAuth           \u002F\u002F how the backend authenticates: kind ('none' | 'filesystem' |\n                            \u002F\u002F   'api-key' | 'iam' | 'oauth' | 'kerberos' | 'browser-origin'),\n                            \u002F\u002F   required, and flow\n  serverWriteTime?: boolean \u002F\u002F store exposes an authoritative clock via getStoreTime()\n  region?: string           \u002F\u002F advisory geographic region ('eu', 'us') for data-residency checks\n  txAtomic?: boolean        \u002F\u002F store implements tx() with storage-layer atomicity\n  maxBlobBytes?: number     \u002F\u002F max raw bytes per blob chunk; undefined = no limit\n}\n",[941,7704,7705,7714,7727,7740,7745,7750,7762,7774,7786,7798],{"__ignoreMap":1037},[2225,7706,7707,7709,7712],{"class":2227,"line":2228},[2225,7708,6994],{"class":2774},[2225,7710,7711],{"class":6342}," StoreCapabilities",[2225,7713,2301],{"class":2235},[2225,7715,7716,7719,7721,7724],{"class":2227,"line":1041},[2225,7717,7718],{"class":2307},"  casAtomic",[2225,7720,2311],{"class":2235},[2225,7722,7723],{"class":6342}," boolean",[2225,7725,7726],{"class":2763},"        \u002F\u002F is the expectedVersion check + write atomic at the storage layer?\n",[2225,7728,7729,7732,7734,7737],{"class":2227,"line":1038},[2225,7730,7731],{"class":2307},"  auth",[2225,7733,2311],{"class":2235},[2225,7735,7736],{"class":6342}," StoreAuth",[2225,7738,7739],{"class":2763},"           \u002F\u002F how the backend authenticates: kind ('none' | 'filesystem' |\n",[2225,7741,7742],{"class":2227,"line":2288},[2225,7743,7744],{"class":2763},"                            \u002F\u002F   'api-key' | 'iam' | 'oauth' | 'kerberos' | 'browser-origin'),\n",[2225,7746,7747],{"class":2227,"line":2304},[2225,7748,7749],{"class":2763},"                            \u002F\u002F   required, and flow\n",[2225,7751,7752,7755,7757,7759],{"class":2227,"line":2356},[2225,7753,7754],{"class":2307},"  serverWriteTime",[2225,7756,7011],{"class":2235},[2225,7758,7723],{"class":6342},[2225,7760,7761],{"class":2763}," \u002F\u002F store exposes an authoritative clock via getStoreTime()\n",[2225,7763,7764,7767,7769,7771],{"class":2227,"line":2830},[2225,7765,7766],{"class":2307},"  region",[2225,7768,7011],{"class":2235},[2225,7770,6366],{"class":6342},[2225,7772,7773],{"class":2763},"           \u002F\u002F advisory geographic region ('eu', 'us') for data-residency checks\n",[2225,7775,7776,7779,7781,7783],{"class":2227,"line":2861},[2225,7777,7778],{"class":2307},"  txAtomic",[2225,7780,7011],{"class":2235},[2225,7782,7723],{"class":6342},[2225,7784,7785],{"class":2763},"        \u002F\u002F store implements tx() with storage-layer atomicity\n",[2225,7787,7788,7791,7793,7795],{"class":2227,"line":4246},[2225,7789,7790],{"class":2307},"  maxBlobBytes",[2225,7792,7011],{"class":2235},[2225,7794,6356],{"class":6342},[2225,7796,7797],{"class":2763},"     \u002F\u002F max raw bytes per blob chunk; undefined = no limit\n",[2225,7799,7800],{"class":2227,"line":4253},[2225,7801,7351],{"class":2235},[937,7803,7804,7805,7808,7809,1520],{},"Consumers that need a capability read it here — for example ",[941,7806,7807],{},"vault.sequence().next()"," requires ",[941,7810,7811],{},"casAtomic: true",[1591,7813,7815],{"id":7814},"optional-extension-methods","Optional extension methods",[937,7817,7818,7819,7822],{},"Stores may implement any of these; the hub feature-detects them (e.g. ",[941,7820,7821],{},"'listPage' in store",") and falls back gracefully when absent:",[1338,7824,7825,7838],{},[1341,7826,7827],{},[1344,7828,7829,7832,7835],{},[1347,7830,7831],{},"Method",[1347,7833,7834],{},"Purpose",[1347,7836,7837],{},"Fallback when absent",[1357,7839,7840,7852,7871,7894,7913,7929,7948,7960,7977],{},[1344,7841,7842,7847,7850],{},[1362,7843,7844],{},[941,7845,7846],{},"ping()",[1362,7848,7849],{},"Connectivity check for the sync engine",[1362,7851,3610],{},[1344,7853,7854,7859,7869],{},[1362,7855,7856],{},[941,7857,7858],{},"getStoreTime()",[1362,7860,7861,7862,7865,7866],{},"Authoritative store clock as a ",[941,7863,7864],{},"{ earliest, latest }"," interval; present iff ",[941,7867,7868],{},"capabilities.serverWriteTime",[1362,7870,3610],{},[1344,7872,7873,7878,7888],{},[1362,7874,7875],{},[941,7876,7877],{},"listSince(vault, collection, since)",[1362,7879,7880,7881,7884,7885,1381],{},"IDs modified after ",[941,7882,7883],{},"since",", for partial sync (",[941,7886,7887],{},"pull({ modifiedSince })",[1362,7889,7890,7891,7893],{},"Full ",[941,7892,3170],{}," + client-side timestamp filter",[1344,7895,7896,7901,7908],{},[1362,7897,7898],{},[941,7899,7900],{},"listPage(vault, collection, cursor?, limit?)",[1362,7902,7903,7904,7907],{},"Cursor-based pages of envelopes; the streaming ",[941,7905,7906],{},"scan()"," fast path",[1362,7909,7890,7910,7912],{},[941,7911,3170],{}," + slice (one-time console warning)",[1344,7914,7915,7923,7926],{},[1362,7916,7917,1261,7920],{},[941,7918,7919],{},"presencePublish(channel, payload)",[941,7921,7922],{},"presenceSubscribe(channel, cb)",[1362,7924,7925],{},"Real-time presence pub\u002Fsub",[1362,7927,7928],{},"Storage-based polling",[1344,7930,7931,7936,7942],{},[1362,7932,7933],{},[941,7934,7935],{},"listVaults()",[1362,7937,7938,7939],{},"Enumerate every vault the store holds, for ",[941,7940,7941],{},"listAccessibleVaults()",[1362,7943,7944,7947],{},[941,7945,7946],{},"StoreCapabilityError"," with guidance",[1344,7949,7950,7955,7958],{},[1362,7951,7952],{},[941,7953,7954],{},"presignUrl(vault, collection, id, expiresInSeconds?)",[1362,7956,7957],{},"Time-limited direct-download URL (object stores); the URL serves ciphertext",[1362,7959,3610],{},[1344,7961,7962,7967,7975],{},[1362,7963,7964],{},[941,7965,7966],{},"estimateUsage()",[1362,7968,7969,4688,7972,7974],{},[941,7970,7971],{},"{ usedBytes, quotaBytes }",[941,7973,6651],{},", for quota-aware routing",[1362,7976,3610],{},[1344,7978,7979,7984,7990],{},[1362,7980,7981],{},[941,7982,7983],{},"tx(ops)",[1362,7985,7986,7987],{},"Native multi-record atomic write backing ",[941,7988,7989],{},"db.transaction(fn)",[1362,7991,7992],{},"Per-record optimistic concurrency with best-effort unwind",[1022,7994,7996],{"id":7995},"authoring-a-store","Authoring a store",[937,7998,7999,8000,8003,8004,1952,8007,8010],{},"A complete, working store — a flat ",[941,8001,8002],{},"Map"," keyed by ",[941,8005,8006],{},"vault\u002Fcollection\u002Fid",[941,8008,8009],{},"createStore"," is a type-safe identity helper for store factories:",[2217,8012,8014],{"className":2219,"code":8013,"language":2221,"meta":1037,"style":1037},"import { createNoydb, createStore, ConflictError } from '@noy-db\u002Fhub'\nimport type { NoydbStore, EncryptedEnvelope, VaultSnapshot } from '@noy-db\u002Fhub'\n\nexport const mapStore = createStore((opts: { name?: string }): NoydbStore => {\n  const data = new Map\u003Cstring, EncryptedEnvelope>()\n  const key = (v: string, c: string, id: string) => `${v}\u002F${c}\u002F${id}`\n\n  return {\n    name: opts.name ?? 'map',\n    capabilities: {\n      \u002F\u002F Synchronous Map ops: the version check and the write cannot interleave.\n      casAtomic: true,\n      auth: { kind: 'none', required: false, flow: 'static' },\n    },\n    async get(v, c, id) {\n      return data.get(key(v, c, id)) ?? null\n    },\n    async put(v, c, id, envelope, expectedVersion) {\n      const current = data.get(key(v, c, id))?._v ?? 0   \u002F\u002F absent = version 0\n      if (expectedVersion !== undefined && current !== expectedVersion) {\n        throw new ConflictError(current)\n      }\n      data.set(key(v, c, id), envelope)\n    },\n    async delete(v, c, id) {\n      data.delete(key(v, c, id))\n    },\n    async list(v, c) {\n      const prefix = `${v}\u002F${c}\u002F`\n      return [...data.keys()].filter((k) => k.startsWith(prefix)).map((k) => k.slice(prefix.length))\n    },\n    async loadAll(v) {\n      const snapshot: VaultSnapshot = {}\n      for (const [k, envelope] of data) {\n        const [vault, collection, id] = k.split('\u002F')\n        if (vault !== v) continue\n        ;(snapshot[collection] ??= {})[id] = envelope\n      }\n      return snapshot\n    },\n    async saveAll(v, snapshot) {\n      \u002F\u002F Replace, not merge: drop every non-`_` collection of this vault first.\n      for (const k of [...data.keys()]) {\n        const [vault, collection] = k.split('\u002F')\n        if (vault === v && !collection.startsWith('_')) data.delete(k)\n      }\n      for (const [collection, records] of Object.entries(snapshot)) {\n        for (const [id, envelope] of Object.entries(records)) {\n          data.set(key(v, collection, id), envelope)\n        }\n      }\n    },\n  }\n})\n\nconst db = await createNoydb({ user: 'alice', secret: 's3cret', store: mapStore({}) })\nconst vault = await db.openVault('acme')\nawait vault.collection('contacts').put('c-1', { name: 'Ada' })\n",[941,8015,8016,8044,8072,8076,8119,8147,8212,8216,8223,8249,8258,8263,8274,8320,8325,8349,8385,8389,8420,8468,8498,8514,8519,8553,8557,8580,8607,8611,8631,8660,8745,8750,8766,8783,8813,8852,8872,8909,8914,8922,8927,8947,8953,8981,9014,9061,9066,9104,9141,9175,9181,9186,9191,9196,9203,9208,9268,9295],{"__ignoreMap":1037},[2225,8017,8018,8020,8022,8024,8026,8029,8031,8034,8036,8038,8040,8042],{"class":2227,"line":2228},[2225,8019,2232],{"class":2231},[2225,8021,2236],{"class":2235},[2225,8023,2744],{"class":2239},[2225,8025,2298],{"class":2235},[2225,8027,8028],{"class":2239}," createStore",[2225,8030,2298],{"class":2235},[2225,8032,8033],{"class":2239}," ConflictError",[2225,8035,2243],{"class":2235},[2225,8037,2246],{"class":2231},[2225,8039,2249],{"class":2235},[2225,8041,1086],{"class":2252},[2225,8043,2256],{"class":2235},[2225,8045,8046,8048,8050,8052,8054,8056,8058,8060,8062,8064,8066,8068,8070],{"class":2227,"line":1041},[2225,8047,2232],{"class":2231},[2225,8049,3399],{"class":2231},[2225,8051,2236],{"class":2235},[2225,8053,3404],{"class":2239},[2225,8055,2298],{"class":2235},[2225,8057,3409],{"class":2239},[2225,8059,2298],{"class":2235},[2225,8061,7335],{"class":2239},[2225,8063,2243],{"class":2235},[2225,8065,2246],{"class":2231},[2225,8067,2249],{"class":2235},[2225,8069,1086],{"class":2252},[2225,8071,2256],{"class":2235},[2225,8073,8074],{"class":2227,"line":1038},[2225,8075,2285],{"emptyLinePlaceholder":2284},[2225,8077,8078,8081,8084,8087,8089,8091,8093,8095,8098,8100,8102,8105,8107,8109,8112,8114,8117],{"class":2227,"line":2288},[2225,8079,8080],{"class":2231},"export",[2225,8082,8083],{"class":2774}," const",[2225,8085,8086],{"class":2239}," mapStore ",[2225,8088,2781],{"class":2235},[2225,8090,8028],{"class":2291},[2225,8092,2789],{"class":2239},[2225,8094,2789],{"class":2235},[2225,8096,8097],{"class":7052},"opts",[2225,8099,2311],{"class":2235},[2225,8101,2236],{"class":2235},[2225,8103,8104],{"class":2307}," name",[2225,8106,7011],{"class":2235},[2225,8108,6366],{"class":6342},[2225,8110,8111],{"class":2235}," }):",[2225,8113,3404],{"class":6342},[2225,8115,8116],{"class":2774}," =>",[2225,8118,2301],{"class":2235},[2225,8120,8121,8124,8126,8128,8131,8134,8136,8138,8140,8142,8144],{"class":2227,"line":2304},[2225,8122,8123],{"class":2774},"  const",[2225,8125,7330],{"class":2239},[2225,8127,6346],{"class":2235},[2225,8129,8130],{"class":2235}," new",[2225,8132,8133],{"class":2291}," Map",[2225,8135,6474],{"class":2235},[2225,8137,7259],{"class":6342},[2225,8139,2298],{"class":2235},[2225,8141,3409],{"class":6342},[2225,8143,6480],{"class":2235},[2225,8145,8146],{"class":2307},"()\n",[2225,8148,8149,8151,8154,8156,8158,8161,8163,8165,8167,8170,8172,8174,8176,8178,8180,8182,8184,8186,8189,8191,8193,8195,8198,8201,8203,8205,8207,8209],{"class":2227,"line":2356},[2225,8150,8123],{"class":2774},[2225,8152,8153],{"class":2239}," key",[2225,8155,6346],{"class":2235},[2225,8157,953],{"class":2235},[2225,8159,8160],{"class":7052},"v",[2225,8162,2311],{"class":2235},[2225,8164,6366],{"class":6342},[2225,8166,2298],{"class":2235},[2225,8168,8169],{"class":7052}," c",[2225,8171,2311],{"class":2235},[2225,8173,6366],{"class":6342},[2225,8175,2298],{"class":2235},[2225,8177,7070],{"class":7052},[2225,8179,2311],{"class":2235},[2225,8181,6366],{"class":6342},[2225,8183,1381],{"class":2235},[2225,8185,8116],{"class":2774},[2225,8187,8188],{"class":2235}," `${",[2225,8190,8160],{"class":2239},[2225,8192,2359],{"class":2235},[2225,8194,1625],{"class":2252},[2225,8196,8197],{"class":2235},"${",[2225,8199,8200],{"class":2239},"c",[2225,8202,2359],{"class":2235},[2225,8204,1625],{"class":2252},[2225,8206,8197],{"class":2235},[2225,8208,6983],{"class":2239},[2225,8210,8211],{"class":2235},"}`\n",[2225,8213,8214],{"class":2227,"line":2830},[2225,8215,2285],{"emptyLinePlaceholder":2284},[2225,8217,8218,8221],{"class":2227,"line":2861},[2225,8219,8220],{"class":2231},"  return",[2225,8222,2301],{"class":2235},[2225,8224,8225,8228,8230,8233,8235,8237,8240,8242,8245,8247],{"class":2227,"line":4246},[2225,8226,8227],{"class":2307},"    name",[2225,8229,2311],{"class":2235},[2225,8231,8232],{"class":2239}," opts",[2225,8234,1520],{"class":2235},[2225,8236,4914],{"class":2239},[2225,8238,8239],{"class":2235}," ??",[2225,8241,2249],{"class":2235},[2225,8243,8244],{"class":2252},"map",[2225,8246,2805],{"class":2235},[2225,8248,1707],{"class":2235},[2225,8250,8251,8254,8256],{"class":2227,"line":4253},[2225,8252,8253],{"class":2307},"    capabilities",[2225,8255,2311],{"class":2235},[2225,8257,2301],{"class":2235},[2225,8259,8260],{"class":2227,"line":4259},[2225,8261,8262],{"class":2763},"      \u002F\u002F Synchronous Map ops: the version check and the write cannot interleave.\n",[2225,8264,8265,8268,8270,8272],{"class":2227,"line":6548},[2225,8266,8267],{"class":2307},"      casAtomic",[2225,8269,2311],{"class":2235},[2225,8271,2322],{"class":2321},[2225,8273,1707],{"class":2235},[2225,8275,8276,8279,8281,8283,8286,8288,8290,8293,8295,8297,8300,8302,8304,8306,8309,8311,8313,8316,8318],{"class":2227,"line":6576},[2225,8277,8278],{"class":2307},"      auth",[2225,8280,2311],{"class":2235},[2225,8282,2236],{"class":2235},[2225,8284,8285],{"class":2307}," kind",[2225,8287,2311],{"class":2235},[2225,8289,2249],{"class":2235},[2225,8291,8292],{"class":2252},"none",[2225,8294,2805],{"class":2235},[2225,8296,2298],{"class":2235},[2225,8298,8299],{"class":2307}," required",[2225,8301,2311],{"class":2235},[2225,8303,2341],{"class":2321},[2225,8305,2298],{"class":2235},[2225,8307,8308],{"class":2307}," flow",[2225,8310,2311],{"class":2235},[2225,8312,2249],{"class":2235},[2225,8314,8315],{"class":2252},"static",[2225,8317,2805],{"class":2235},[2225,8319,2353],{"class":2235},[2225,8321,8322],{"class":2227,"line":6582},[2225,8323,8324],{"class":2235},"    },\n",[2225,8326,8327,8330,8333,8335,8337,8339,8341,8343,8345,8347],{"class":2227,"line":6587},[2225,8328,8329],{"class":2774},"    async",[2225,8331,8332],{"class":2307}," get",[2225,8334,2789],{"class":2235},[2225,8336,8160],{"class":7052},[2225,8338,2298],{"class":2235},[2225,8340,8169],{"class":7052},[2225,8342,2298],{"class":2235},[2225,8344,7070],{"class":7052},[2225,8346,1381],{"class":2235},[2225,8348,2301],{"class":2235},[2225,8350,8351,8354,8356,8358,8360,8362,8365,8367,8369,8371,8373,8375,8377,8379,8382],{"class":2227,"line":7174},[2225,8352,8353],{"class":2231},"      return",[2225,8355,7330],{"class":2239},[2225,8357,1520],{"class":2235},[2225,8359,3158],{"class":2291},[2225,8361,2789],{"class":2307},[2225,8363,8364],{"class":2291},"key",[2225,8366,2789],{"class":2307},[2225,8368,8160],{"class":2239},[2225,8370,2298],{"class":2235},[2225,8372,8169],{"class":2239},[2225,8374,2298],{"class":2235},[2225,8376,7070],{"class":2239},[2225,8378,7620],{"class":2307},[2225,8380,8381],{"class":2235},"??",[2225,8383,8384],{"class":2235}," null\n",[2225,8386,8387],{"class":2227,"line":7180},[2225,8388,8324],{"class":2235},[2225,8390,8391,8393,8396,8398,8400,8402,8404,8406,8408,8410,8412,8414,8416,8418],{"class":2227,"line":7220},[2225,8392,8329],{"class":2774},[2225,8394,8395],{"class":2307}," put",[2225,8397,2789],{"class":2235},[2225,8399,8160],{"class":7052},[2225,8401,2298],{"class":2235},[2225,8403,8169],{"class":7052},[2225,8405,2298],{"class":2235},[2225,8407,7070],{"class":7052},[2225,8409,2298],{"class":2235},[2225,8411,7585],{"class":7052},[2225,8413,2298],{"class":2235},[2225,8415,7152],{"class":7052},[2225,8417,1381],{"class":2235},[2225,8419,2301],{"class":2235},[2225,8421,8422,8425,8428,8430,8432,8434,8436,8438,8440,8442,8444,8446,8448,8450,8452,8455,8458,8460,8462,8465],{"class":2227,"line":7225},[2225,8423,8424],{"class":2774},"      const",[2225,8426,8427],{"class":2239}," current",[2225,8429,6346],{"class":2235},[2225,8431,7330],{"class":2239},[2225,8433,1520],{"class":2235},[2225,8435,3158],{"class":2291},[2225,8437,2789],{"class":2307},[2225,8439,8364],{"class":2291},[2225,8441,2789],{"class":2307},[2225,8443,8160],{"class":2239},[2225,8445,2298],{"class":2235},[2225,8447,8169],{"class":2239},[2225,8449,2298],{"class":2235},[2225,8451,7070],{"class":2239},[2225,8453,8454],{"class":2307},"))",[2225,8456,8457],{"class":2235},"?.",[2225,8459,1700],{"class":2239},[2225,8461,8239],{"class":2235},[2225,8463,8464],{"class":6527}," 0",[2225,8466,8467],{"class":2763},"   \u002F\u002F absent = version 0\n",[2225,8469,8470,8473,8475,8478,8481,8484,8487,8489,8491,8493,8496],{"class":2227,"line":7231},[2225,8471,8472],{"class":2231},"      if",[2225,8474,953],{"class":2307},[2225,8476,8477],{"class":2239},"expectedVersion",[2225,8479,8480],{"class":2235}," !==",[2225,8482,8483],{"class":2235}," undefined",[2225,8485,8486],{"class":2235}," &&",[2225,8488,8427],{"class":2239},[2225,8490,8480],{"class":2235},[2225,8492,7152],{"class":2239},[2225,8494,8495],{"class":2307},") ",[2225,8497,4200],{"class":2235},[2225,8499,8500,8503,8505,8507,8509,8512],{"class":2227,"line":7267},[2225,8501,8502],{"class":2231},"        throw",[2225,8504,8130],{"class":2235},[2225,8506,8033],{"class":2291},[2225,8508,2789],{"class":2307},[2225,8510,8511],{"class":2239},"current",[2225,8513,2362],{"class":2307},[2225,8515,8516],{"class":2227,"line":7272},[2225,8517,8518],{"class":2235},"      }\n",[2225,8520,8521,8524,8526,8529,8531,8533,8535,8537,8539,8541,8543,8545,8547,8549,8551],{"class":2227,"line":7278},[2225,8522,8523],{"class":2239},"      data",[2225,8525,1520],{"class":2235},[2225,8527,8528],{"class":2291},"set",[2225,8530,2789],{"class":2307},[2225,8532,8364],{"class":2291},[2225,8534,2789],{"class":2307},[2225,8536,8160],{"class":2239},[2225,8538,2298],{"class":2235},[2225,8540,8169],{"class":2239},[2225,8542,2298],{"class":2235},[2225,8544,7070],{"class":2239},[2225,8546,1381],{"class":2307},[2225,8548,2298],{"class":2235},[2225,8550,7585],{"class":2239},[2225,8552,2362],{"class":2307},[2225,8554,8555],{"class":2227,"line":7303},[2225,8556,8324],{"class":2235},[2225,8558,8559,8561,8564,8566,8568,8570,8572,8574,8576,8578],{"class":2227,"line":7308},[2225,8560,8329],{"class":2774},[2225,8562,8563],{"class":2307}," delete",[2225,8565,2789],{"class":2235},[2225,8567,8160],{"class":7052},[2225,8569,2298],{"class":2235},[2225,8571,8169],{"class":7052},[2225,8573,2298],{"class":2235},[2225,8575,7070],{"class":7052},[2225,8577,1381],{"class":2235},[2225,8579,2301],{"class":2235},[2225,8581,8582,8584,8586,8588,8590,8592,8594,8596,8598,8600,8602,8604],{"class":2227,"line":7314},[2225,8583,8523],{"class":2239},[2225,8585,1520],{"class":2235},[2225,8587,3164],{"class":2291},[2225,8589,2789],{"class":2307},[2225,8591,8364],{"class":2291},[2225,8593,2789],{"class":2307},[2225,8595,8160],{"class":2239},[2225,8597,2298],{"class":2235},[2225,8599,8169],{"class":2239},[2225,8601,2298],{"class":2235},[2225,8603,7070],{"class":2239},[2225,8605,8606],{"class":2307},"))\n",[2225,8608,8609],{"class":2227,"line":7348},[2225,8610,8324],{"class":2235},[2225,8612,8614,8616,8619,8621,8623,8625,8627,8629],{"class":2227,"line":8613},28,[2225,8615,8329],{"class":2774},[2225,8617,8618],{"class":2307}," list",[2225,8620,2789],{"class":2235},[2225,8622,8160],{"class":7052},[2225,8624,2298],{"class":2235},[2225,8626,8169],{"class":7052},[2225,8628,1381],{"class":2235},[2225,8630,2301],{"class":2235},[2225,8632,8634,8636,8639,8641,8643,8645,8647,8649,8651,8653,8655,8657],{"class":2227,"line":8633},29,[2225,8635,8424],{"class":2774},[2225,8637,8638],{"class":2239}," prefix",[2225,8640,6346],{"class":2235},[2225,8642,8188],{"class":2235},[2225,8644,8160],{"class":2239},[2225,8646,2359],{"class":2235},[2225,8648,1625],{"class":2252},[2225,8650,8197],{"class":2235},[2225,8652,8200],{"class":2239},[2225,8654,2359],{"class":2235},[2225,8656,1625],{"class":2252},[2225,8658,8659],{"class":2235},"`\n",[2225,8661,8663,8665,8668,8671,8674,8676,8679,8682,8684,8687,8689,8691,8694,8696,8698,8701,8703,8706,8708,8711,8713,8715,8717,8719,8721,8723,8725,8727,8729,8731,8734,8736,8738,8740,8743],{"class":2227,"line":8662},30,[2225,8664,8353],{"class":2231},[2225,8666,8667],{"class":2307}," [",[2225,8669,8670],{"class":2235},"...",[2225,8672,8673],{"class":2239},"data",[2225,8675,1520],{"class":2235},[2225,8677,8678],{"class":2291},"keys",[2225,8680,8681],{"class":2307},"()]",[2225,8683,1520],{"class":2235},[2225,8685,8686],{"class":2291},"filter",[2225,8688,2789],{"class":2307},[2225,8690,2789],{"class":2235},[2225,8692,8693],{"class":7052},"k",[2225,8695,1381],{"class":2235},[2225,8697,8116],{"class":2774},[2225,8699,8700],{"class":2239}," k",[2225,8702,1520],{"class":2235},[2225,8704,8705],{"class":2291},"startsWith",[2225,8707,2789],{"class":2307},[2225,8709,8710],{"class":2239},"prefix",[2225,8712,8454],{"class":2307},[2225,8714,1520],{"class":2235},[2225,8716,8244],{"class":2291},[2225,8718,2789],{"class":2307},[2225,8720,2789],{"class":2235},[2225,8722,8693],{"class":7052},[2225,8724,1381],{"class":2235},[2225,8726,8116],{"class":2774},[2225,8728,8700],{"class":2239},[2225,8730,1520],{"class":2235},[2225,8732,8733],{"class":2291},"slice",[2225,8735,2789],{"class":2307},[2225,8737,8710],{"class":2239},[2225,8739,1520],{"class":2235},[2225,8741,8742],{"class":2239},"length",[2225,8744,8606],{"class":2307},[2225,8746,8748],{"class":2227,"line":8747},31,[2225,8749,8324],{"class":2235},[2225,8751,8753,8755,8758,8760,8762,8764],{"class":2227,"line":8752},32,[2225,8754,8329],{"class":2774},[2225,8756,8757],{"class":2307}," loadAll",[2225,8759,2789],{"class":2235},[2225,8761,8160],{"class":7052},[2225,8763,1381],{"class":2235},[2225,8765,2301],{"class":2235},[2225,8767,8769,8771,8774,8776,8778,8780],{"class":2227,"line":8768},33,[2225,8770,8424],{"class":2774},[2225,8772,8773],{"class":2239}," snapshot",[2225,8775,2311],{"class":2235},[2225,8777,7335],{"class":6342},[2225,8779,6346],{"class":2235},[2225,8781,8782],{"class":2235}," {}\n",[2225,8784,8786,8789,8791,8793,8795,8797,8799,8801,8804,8807,8809,8811],{"class":2227,"line":8785},34,[2225,8787,8788],{"class":2231},"      for",[2225,8790,953],{"class":2307},[2225,8792,2775],{"class":2774},[2225,8794,8667],{"class":2235},[2225,8796,8693],{"class":2239},[2225,8798,2298],{"class":2235},[2225,8800,7585],{"class":2239},[2225,8802,8803],{"class":2235},"]",[2225,8805,8806],{"class":2235}," of",[2225,8808,7330],{"class":2239},[2225,8810,8495],{"class":2307},[2225,8812,4200],{"class":2235},[2225,8814,8816,8819,8821,8823,8825,8827,8829,8831,8833,8835,8837,8839,8842,8844,8846,8848,8850],{"class":2227,"line":8815},35,[2225,8817,8818],{"class":2774},"        const",[2225,8820,8667],{"class":2235},[2225,8822,2694],{"class":2239},[2225,8824,2298],{"class":2235},[2225,8826,7061],{"class":2239},[2225,8828,2298],{"class":2235},[2225,8830,7070],{"class":2239},[2225,8832,8803],{"class":2235},[2225,8834,6346],{"class":2235},[2225,8836,8700],{"class":2239},[2225,8838,1520],{"class":2235},[2225,8840,8841],{"class":2291},"split",[2225,8843,2789],{"class":2307},[2225,8845,2805],{"class":2235},[2225,8847,1625],{"class":2252},[2225,8849,2805],{"class":2235},[2225,8851,2362],{"class":2307},[2225,8853,8855,8858,8860,8862,8864,8867,8869],{"class":2227,"line":8854},36,[2225,8856,8857],{"class":2231},"        if",[2225,8859,953],{"class":2307},[2225,8861,2694],{"class":2239},[2225,8863,8480],{"class":2235},[2225,8865,8866],{"class":2239}," v",[2225,8868,8495],{"class":2307},[2225,8870,8871],{"class":2231},"continue\n",[2225,8873,8875,8878,8880,8883,8886,8888,8891,8894,8897,8900,8902,8904,8906],{"class":2227,"line":8874},37,[2225,8876,8877],{"class":2235},"        ;",[2225,8879,2789],{"class":2307},[2225,8881,8882],{"class":2239},"snapshot",[2225,8884,8885],{"class":2307},"[",[2225,8887,6471],{"class":2239},[2225,8889,8890],{"class":2307},"] ",[2225,8892,8893],{"class":2235},"??=",[2225,8895,8896],{"class":2235}," {}",[2225,8898,8899],{"class":2307},")[",[2225,8901,6983],{"class":2239},[2225,8903,8890],{"class":2307},[2225,8905,2781],{"class":2235},[2225,8907,8908],{"class":2239}," envelope\n",[2225,8910,8912],{"class":2227,"line":8911},38,[2225,8913,8518],{"class":2235},[2225,8915,8917,8919],{"class":2227,"line":8916},39,[2225,8918,8353],{"class":2231},[2225,8920,8921],{"class":2239}," snapshot\n",[2225,8923,8925],{"class":2227,"line":8924},40,[2225,8926,8324],{"class":2235},[2225,8928,8930,8932,8935,8937,8939,8941,8943,8945],{"class":2227,"line":8929},41,[2225,8931,8329],{"class":2774},[2225,8933,8934],{"class":2307}," saveAll",[2225,8936,2789],{"class":2235},[2225,8938,8160],{"class":7052},[2225,8940,2298],{"class":2235},[2225,8942,8773],{"class":7052},[2225,8944,1381],{"class":2235},[2225,8946,2301],{"class":2235},[2225,8948,8950],{"class":2227,"line":8949},42,[2225,8951,8952],{"class":2763},"      \u002F\u002F Replace, not merge: drop every non-`_` collection of this vault first.\n",[2225,8954,8956,8958,8960,8962,8964,8966,8968,8970,8972,8974,8976,8979],{"class":2227,"line":8955},43,[2225,8957,8788],{"class":2231},[2225,8959,953],{"class":2307},[2225,8961,2775],{"class":2774},[2225,8963,8700],{"class":2239},[2225,8965,8806],{"class":2235},[2225,8967,8667],{"class":2307},[2225,8969,8670],{"class":2235},[2225,8971,8673],{"class":2239},[2225,8973,1520],{"class":2235},[2225,8975,8678],{"class":2291},[2225,8977,8978],{"class":2307},"()]) ",[2225,8980,4200],{"class":2235},[2225,8982,8984,8986,8988,8990,8992,8994,8996,8998,9000,9002,9004,9006,9008,9010,9012],{"class":2227,"line":8983},44,[2225,8985,8818],{"class":2774},[2225,8987,8667],{"class":2235},[2225,8989,2694],{"class":2239},[2225,8991,2298],{"class":2235},[2225,8993,7061],{"class":2239},[2225,8995,8803],{"class":2235},[2225,8997,6346],{"class":2235},[2225,8999,8700],{"class":2239},[2225,9001,1520],{"class":2235},[2225,9003,8841],{"class":2291},[2225,9005,2789],{"class":2307},[2225,9007,2805],{"class":2235},[2225,9009,1625],{"class":2252},[2225,9011,2805],{"class":2235},[2225,9013,2362],{"class":2307},[2225,9015,9017,9019,9021,9023,9026,9028,9030,9033,9035,9037,9039,9041,9043,9045,9047,9049,9051,9053,9055,9057,9059],{"class":2227,"line":9016},45,[2225,9018,8857],{"class":2231},[2225,9020,953],{"class":2307},[2225,9022,2694],{"class":2239},[2225,9024,9025],{"class":2235}," ===",[2225,9027,8866],{"class":2239},[2225,9029,8486],{"class":2235},[2225,9031,9032],{"class":2235}," !",[2225,9034,6471],{"class":2239},[2225,9036,1520],{"class":2235},[2225,9038,8705],{"class":2291},[2225,9040,2789],{"class":2307},[2225,9042,2805],{"class":2235},[2225,9044,6702],{"class":2252},[2225,9046,2805],{"class":2235},[2225,9048,7620],{"class":2307},[2225,9050,8673],{"class":2239},[2225,9052,1520],{"class":2235},[2225,9054,3164],{"class":2291},[2225,9056,2789],{"class":2307},[2225,9058,8693],{"class":2239},[2225,9060,2362],{"class":2307},[2225,9062,9064],{"class":2227,"line":9063},46,[2225,9065,8518],{"class":2235},[2225,9067,9069,9071,9073,9075,9077,9079,9081,9084,9086,9088,9091,9093,9096,9098,9100,9102],{"class":2227,"line":9068},47,[2225,9070,8788],{"class":2231},[2225,9072,953],{"class":2307},[2225,9074,2775],{"class":2774},[2225,9076,8667],{"class":2235},[2225,9078,6471],{"class":2239},[2225,9080,2298],{"class":2235},[2225,9082,9083],{"class":2239}," records",[2225,9085,8803],{"class":2235},[2225,9087,8806],{"class":2235},[2225,9089,9090],{"class":2239}," Object",[2225,9092,1520],{"class":2235},[2225,9094,9095],{"class":2291},"entries",[2225,9097,2789],{"class":2307},[2225,9099,8882],{"class":2239},[2225,9101,7620],{"class":2307},[2225,9103,4200],{"class":2235},[2225,9105,9107,9110,9112,9114,9116,9118,9120,9122,9124,9126,9128,9130,9132,9134,9137,9139],{"class":2227,"line":9106},48,[2225,9108,9109],{"class":2231},"        for",[2225,9111,953],{"class":2307},[2225,9113,2775],{"class":2774},[2225,9115,8667],{"class":2235},[2225,9117,6983],{"class":2239},[2225,9119,2298],{"class":2235},[2225,9121,7585],{"class":2239},[2225,9123,8803],{"class":2235},[2225,9125,8806],{"class":2235},[2225,9127,9090],{"class":2239},[2225,9129,1520],{"class":2235},[2225,9131,9095],{"class":2291},[2225,9133,2789],{"class":2307},[2225,9135,9136],{"class":2239},"records",[2225,9138,7620],{"class":2307},[2225,9140,4200],{"class":2235},[2225,9142,9144,9147,9149,9151,9153,9155,9157,9159,9161,9163,9165,9167,9169,9171,9173],{"class":2227,"line":9143},49,[2225,9145,9146],{"class":2239},"          data",[2225,9148,1520],{"class":2235},[2225,9150,8528],{"class":2291},[2225,9152,2789],{"class":2307},[2225,9154,8364],{"class":2291},[2225,9156,2789],{"class":2307},[2225,9158,8160],{"class":2239},[2225,9160,2298],{"class":2235},[2225,9162,7061],{"class":2239},[2225,9164,2298],{"class":2235},[2225,9166,7070],{"class":2239},[2225,9168,1381],{"class":2307},[2225,9170,2298],{"class":2235},[2225,9172,7585],{"class":2239},[2225,9174,2362],{"class":2307},[2225,9176,9178],{"class":2227,"line":9177},50,[2225,9179,9180],{"class":2235},"        }\n",[2225,9182,9184],{"class":2227,"line":9183},51,[2225,9185,8518],{"class":2235},[2225,9187,9189],{"class":2227,"line":9188},52,[2225,9190,8324],{"class":2235},[2225,9192,9194],{"class":2227,"line":9193},53,[2225,9195,7632],{"class":2235},[2225,9197,9199,9201],{"class":2227,"line":9198},54,[2225,9200,2359],{"class":2235},[2225,9202,2362],{"class":2239},[2225,9204,9206],{"class":2227,"line":9205},55,[2225,9207,2285],{"emptyLinePlaceholder":2284},[2225,9209,9211,9213,9215,9217,9219,9221,9223,9225,9227,9229,9231,9233,9235,9237,9239,9241,9243,9246,9248,9250,9252,9254,9257,9259,9262,9264,9266],{"class":2227,"line":9210},56,[2225,9212,2775],{"class":2774},[2225,9214,2778],{"class":2239},[2225,9216,2781],{"class":2235},[2225,9218,2784],{"class":2231},[2225,9220,2744],{"class":2291},[2225,9222,2789],{"class":2239},[2225,9224,2792],{"class":2235},[2225,9226,2795],{"class":2307},[2225,9228,2311],{"class":2235},[2225,9230,2249],{"class":2235},[2225,9232,6408],{"class":2252},[2225,9234,2805],{"class":2235},[2225,9236,2298],{"class":2235},[2225,9238,2810],{"class":2307},[2225,9240,2311],{"class":2235},[2225,9242,2249],{"class":2235},[2225,9244,9245],{"class":2252},"s3cret",[2225,9247,2805],{"class":2235},[2225,9249,2298],{"class":2235},[2225,9251,7564],{"class":2307},[2225,9253,2311],{"class":2235},[2225,9255,9256],{"class":2291}," mapStore",[2225,9258,2789],{"class":2239},[2225,9260,9261],{"class":2235},"{}",[2225,9263,8495],{"class":2239},[2225,9265,2359],{"class":2235},[2225,9267,2362],{"class":2239},[2225,9269,9271,9273,9275,9277,9279,9281,9283,9285,9287,9289,9291,9293],{"class":2227,"line":9270},57,[2225,9272,2775],{"class":2774},[2225,9274,2835],{"class":2239},[2225,9276,2781],{"class":2235},[2225,9278,2784],{"class":2231},[2225,9280,2842],{"class":2239},[2225,9282,1520],{"class":2235},[2225,9284,2847],{"class":2291},[2225,9286,2789],{"class":2239},[2225,9288,2805],{"class":2235},[2225,9290,6450],{"class":2252},[2225,9292,2805],{"class":2235},[2225,9294,2362],{"class":2239},[2225,9296,9298,9300,9302,9304,9306,9308,9310,9313,9315,9317,9319,9321,9323,9325,9328,9330,9332,9334,9336,9338,9340,9343,9345,9347],{"class":2227,"line":9297},58,[2225,9299,6500],{"class":2231},[2225,9301,6466],{"class":2239},[2225,9303,1520],{"class":2235},[2225,9305,6471],{"class":2291},[2225,9307,2789],{"class":2239},[2225,9309,2805],{"class":2235},[2225,9311,9312],{"class":2252},"contacts",[2225,9314,2805],{"class":2235},[2225,9316,1381],{"class":2239},[2225,9318,1520],{"class":2235},[2225,9320,3161],{"class":2291},[2225,9322,2789],{"class":2239},[2225,9324,2805],{"class":2235},[2225,9326,9327],{"class":2252},"c-1",[2225,9329,2805],{"class":2235},[2225,9331,2298],{"class":2235},[2225,9333,2236],{"class":2235},[2225,9335,8104],{"class":2307},[2225,9337,2311],{"class":2235},[2225,9339,2249],{"class":2235},[2225,9341,9342],{"class":2252},"Ada",[2225,9344,2805],{"class":2235},[2225,9346,2243],{"class":2235},[2225,9348,2362],{"class":2239},[937,9350,9351,9352,9354],{},"What the store actually received in ",[941,9353,3161],{}," is an envelope — metadata plus opaque ciphertext, never the record:",[2217,9356,9358],{"className":6604,"code":9357,"language":141,"meta":1037,"style":1037},"{\n  \"_noydb\": 1,\n  \"_v\": 1,\n  \"_ts\": \"2026-07-08T09:30:00.000Z\",\n  \"_iv\": \"…base64…\",\n  \"_data\": \"…base64 ciphertext…\"\n}\n",[941,9359,9360,9364,9380,9394,9413,9432,9450],{"__ignoreMap":1037},[2225,9361,9362],{"class":2227,"line":2228},[2225,9363,4200],{"class":2235},[2225,9365,9366,9369,9371,9373,9375,9378],{"class":2227,"line":1041},[2225,9367,9368],{"class":2235},"  \"",[2225,9370,1697],{"class":2774},[2225,9372,6620],{"class":2235},[2225,9374,2311],{"class":2235},[2225,9376,9377],{"class":6527}," 1",[2225,9379,1707],{"class":2235},[2225,9381,9382,9384,9386,9388,9390,9392],{"class":2227,"line":1038},[2225,9383,9368],{"class":2235},[2225,9385,1700],{"class":2774},[2225,9387,6620],{"class":2235},[2225,9389,2311],{"class":2235},[2225,9391,9377],{"class":6527},[2225,9393,1707],{"class":2235},[2225,9395,9396,9398,9400,9402,9404,9406,9409,9411],{"class":2227,"line":2288},[2225,9397,9368],{"class":2235},[2225,9399,1703],{"class":2774},[2225,9401,6620],{"class":2235},[2225,9403,2311],{"class":2235},[2225,9405,6614],{"class":2235},[2225,9407,9408],{"class":2252},"2026-07-08T09:30:00.000Z",[2225,9410,6620],{"class":2235},[2225,9412,1707],{"class":2235},[2225,9414,9415,9417,9419,9421,9423,9425,9428,9430],{"class":2227,"line":2304},[2225,9416,9368],{"class":2235},[2225,9418,1143],{"class":2774},[2225,9420,6620],{"class":2235},[2225,9422,2311],{"class":2235},[2225,9424,6614],{"class":2235},[2225,9426,9427],{"class":2252},"…base64…",[2225,9429,6620],{"class":2235},[2225,9431,1707],{"class":2235},[2225,9433,9434,9436,9438,9440,9442,9444,9447],{"class":2227,"line":2356},[2225,9435,9368],{"class":2235},[2225,9437,1146],{"class":2774},[2225,9439,6620],{"class":2235},[2225,9441,2311],{"class":2235},[2225,9443,6614],{"class":2235},[2225,9445,9446],{"class":2252},"…base64 ciphertext…",[2225,9448,9449],{"class":2235},"\"\n",[2225,9451,9452],{"class":2227,"line":2830},[2225,9453,7351],{"class":2235},[937,9455,9456,9457,9460,9461,9464,9465,9468,9469,9472,9473,1520],{},"One optional metadata field a custom store must round-trip untouched: ",[941,9458,9459],{},"_del: true"," marks a\n",[950,9462,9463],{},"delete marker"," — the envelope a synced collection's ",[941,9466,9467],{},"delete()"," writes instead of a physical\nremoval (empty body, version-ordered, so the delete converges across peers; since ",[941,9470,9471],{},"0.3.0-pre.7","). The hub treats it as \"record absent\" on read; the store just persists it\nlike any other envelope. See ",[1007,9474,9476],{"href":9475},"..\u002Fservices\u002Fsync#deletes-converge-as-delete-markers","sync → deletes converge as delete\nmarkers",[937,9478,9479,9480,9483,9484,9486,9487,9489,9490,9492,9493,9495],{},"Every official store is held to the contract by a shared conformance suite: ",[941,9481,9482],{},"runStoreConformanceTests()"," (from ",[941,9485,3486],{},") exercises the six methods and the ",[941,9488,6860],{}," semantics in each ",[941,9491,1779],{}," package's CI — against mocks, no real cloud service. When writing your own store, mirror the behaviors documented in the contract above; the version-conflict semantics of ",[941,9494,3161],{}," are the part most implementations get wrong.",[1022,9497,9499],{"id":9498},"capabilities-by-essential-store","Capabilities by essential store",[937,9501,9502,9503,997,9505,9508,9509,9511,9512,9515,9516,9518,9519,9521],{},"Three essential packages. ",[941,9504,7461],{},[941,9506,9507],{},"to-browser-idb"," publish from noy-db's core repo;\n",[941,9510,5125],{}," — a wrapper, not a backend — publishes from ",[941,9513,9514],{},"noy-db\u002Fin"," since ",[941,9517,7399],{},".\nAll three share the ",[941,9520,5129],{}," scope and the same install line:",[1338,9523,9524,9543],{},[1341,9525,9526],{},[1344,9527,9528,9531,9534,9540],{},[1347,9529,9530],{},"Package",[1347,9532,9533],{},"Factory",[1347,9535,9537],{"align":9536},"center",[941,9538,9539],{},"casAtomic",[1347,9541,9542],{},"Notes",[1357,9544,9545,9567,9589],{},[1344,9546,9547,9552,9557,9560],{},[1362,9548,9549],{},[941,9550,9551],{},"@noy-db\u002Fto-file",[1362,9553,9554],{},[941,9555,9556],{},"toFile({ dir })",[1362,9558,9559],{"align":9536},"✗",[1362,9561,9562,9563,9566],{},"One JSON file per record at ",[941,9564,9565],{},"dir\u002Fvault\u002Fcollection\u002Fid.json","; version check is read-then-write",[1344,9568,9569,9574,9579,9582],{},[1362,9570,9571],{},[941,9572,9573],{},"@noy-db\u002Fto-browser-idb",[1362,9575,9576],{},[941,9577,9578],{},"toBrowserIdb()",[1362,9580,9581],{"align":9536},"✓",[1362,9583,9584,9585,9588],{},"IndexedDB; check + write in a single ",[941,9586,9587],{},"readwrite"," transaction",[1344,9590,9591,9596,9601,9604],{},[1362,9592,9593],{},[941,9594,9595],{},"@noy-db\u002Fto-meter",[1362,9597,9598],{},[941,9599,9600],{},"toMeter(inner)",[1362,9602,9603],{"align":9536},"inherited",[1362,9605,9606,9607,1261,9610,9613,9614],{},"Wraps any store; times and counts every call, zero semantic changes. Also ships the store diagnostics ",[941,9608,9609],{},"runStoreProbe(store)",[941,9611,9612],{},"probeTopology(…)",", absorbed from the retired ",[941,9615,9616],{},"@noy-db\u002Fto-probe",[937,9618,9619,9620,9622,9623,9625,9626,9635,9636,9639,9640,9643,9644,9646,9647,9649],{},"No store at all is also valid: ",[941,9621,2134],{}," with no ",[941,9624,6951],{}," falls back to\n",[950,9627,9628,9631,9632,9634],{},[941,9629,9630],{},"memoryStore()",", which is built into ",[941,9633,1086],{}," itself"," — not the\n",[941,9637,9638],{},"@noy-db\u002Fto-memory"," package. The two are capability-equivalent and easy to\nconflate, but hub ships with ",[950,9641,9642],{},"zero dependencies",", so the default can never be\nthe package. Reach for ",[941,9645,9638],{}," when you want the fuller store as an\nexplicit, swappable choice; the built-in default is there so ",[941,9648,2134],{},"\nworks with no wiring at all.",[937,9651,9652,9653,9656],{},"Extended stores (in ",[1007,9654,5351],{"href":6965,"rel":9655},[3371],") include:",[1338,9658,9659,9672],{},[1341,9660,9661],{},[1344,9662,9663,9666,9670],{},[1347,9664,9665],{},"Store",[1347,9667,9668],{"align":9536},[941,9669,9539],{},[1347,9671,9542],{},[1357,9673,9674,9692,9704,9719,9736,9754,9766],{},[1344,9675,9676,9680,9682],{},[1362,9677,9678],{},[941,9679,2992],{},[1362,9681,9581],{"align":9536},[1362,9683,9684,9685,9688,9689,9691],{},"Map-of-maps; the fuller in-memory store. ",[950,9686,9687],{},"Relocated from noy-db to noy-db-to on 2026-09-17"," — same ",[941,9690,9638],{}," name, same contract, so an existing install line keeps working; only the publishing repo and version line changed",[1344,9693,9694,9699,9701],{},[1362,9695,9696],{},[941,9697,9698],{},"to-browser-local",[1362,9700,9559],{"align":9536},[1362,9702,9703],{},"localStorage; atomic within one tab, no cross-tab serialization",[1344,9705,9706,9711,9713],{},[1362,9707,9708],{},[941,9709,9710],{},"to-aws-dynamo",[1362,9712,9581],{"align":9536},[1362,9714,9715,9718],{},[941,9716,9717],{},"ConditionExpression"," on the envelope version",[1344,9720,9721,9725,9727],{},[1362,9722,9723],{},[941,9724,5358],{},[1362,9726,9581],{"align":9536},[1362,9728,9729,9730,1261,9733,1381],{},"S3 conditional writes (",[941,9731,9732],{},"IfMatch",[941,9734,9735],{},"IfNoneMatch",[1344,9737,9738,9742,9744],{},[1362,9739,9740],{},[941,9741,5355],{},[1362,9743,9581],{"align":9536},[1362,9745,9746,9747,9750,9751],{},"Version check + upsert; native ",[941,9748,9749],{},"tx()"," via ",[941,9752,9753],{},"BEGIN … COMMIT",[1344,9755,9756,9761,9763],{},[1362,9757,9758],{},[941,9759,9760],{},"to-cloudflare-r2",[1362,9762,9581],{"align":9536},[1362,9764,9765],{},"S3-compatible conditional writes",[1344,9767,9768,9773,9775],{},[1362,9769,9770],{},[941,9771,9772],{},"to-cloudflare-d1",[1362,9774,9581],{"align":9536},[1362,9776,9777,9780],{},[941,9778,9779],{},"UPDATE … WHERE v = ?"," inside a D1 batch",[937,9782,9783,9784,1520],{},"Full list: ",[1007,9785,3742,9787,9789],{"href":9786},"..\u002Ffamilies\u002Fto\u002F",[941,9788,1779],{}," family",[1022,9791,9793],{"id":9792},"store-routing-middleware","Store routing & middleware",[937,9795,9796,9797,1163],{},"Multi-backend routing, retry \u002F circuit-breaker \u002F metrics middleware, lazy-mode caching, and bundle-store wrapping live in the ",[1007,9798,808],{"href":9799},"..\u002Fservices\u002Frouting",[1022,9801,2621],{"id":2620},[1111,9803,9804,9809,9816],{},[1114,9805,9806,9808],{},[1007,9807,21],{"href":6655}," — what is inside the envelope the store persists",[1114,9810,9811,9815],{},[1007,9812,960,9813,9789],{"href":9786},[941,9814,1779],{}," — every official storage backend",[1114,9817,9818,9821],{},[1007,9819,9820],{"href":9799},"Routing"," — composing multiple stores",[2645,9823,9824],{},"html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .sHdIc, html code.shiki .sHdIc{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#EEFFFF;--shiki-default-font-style:italic;--shiki-dark:#BABED8;--shiki-dark-font-style:italic}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .sfNiH, html code.shiki .sfNiH{--shiki-light:#FF5370;--shiki-default:#FF9CAC;--shiki-dark:#FF9CAC}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}",{"title":1037,"searchDepth":1038,"depth":1038,"links":9826},[9827,9828,9832,9833,9834,9835],{"id":6241,"depth":1041,"text":6242},{"id":6970,"depth":1041,"text":6971,"children":9829},[9830,9831],{"id":7698,"depth":1038,"text":7699},{"id":7814,"depth":1038,"text":7815},{"id":7995,"depth":1041,"text":7996},{"id":9498,"depth":1041,"text":9499},{"id":9792,"depth":1041,"text":9793},{"id":2620,"depth":1041,"text":2621},"The pluggable ciphertext backend: the NoydbStore contract, the three stores that ship with noy-db, and the extended cloud\u002FSQL\u002Fremote-FS family.",{},{"title":10},{"title":6891,"description":9836},"lv2i3so94G00igmgYQ5vJRcOADD8WZhNC9b0saQqV6g",{"id":9842,"title":6898,"access":12,"apiToc":929,"body":9843,"category":1042,"description":11592,"extension":1044,"meta":11593,"navigation":11594,"nest":2635,"path":54,"seo":11595,"sinceVersion":1042,"status":1049,"stem":55,"subtitle":1042,"tier":6922,"tocExternal":1042,"__hash__":11596},"docs\u002Fcore\u002F06-permissions-and-keyring.md",{"type":931,"value":9844,"toc":11577},[9845,9848,9858,9860,9884,9891,9940,9955,9959,10055,10124,10128,10134,10285,10296,10300,10342,10681,10685,10688,10714,10720,10722,10740,10763,10770,10783,10787,10811,10814,10823,10833,10977,10987,10991,11002,11019,11024,11096,11178,11260,11276,11305,11322,11339,11343,11439,11443,11479,11483,11548,11550,11575],[934,9846,6898],{"id":9847},"permissions-keyring",[1061,9849,9850],{},[937,9851,9852,6235,9855],{},[950,9853,9854],{},"Always-on. Every vault has a keyring — a single-owner keyring is the minimum.",[941,9856,9857],{},"packages\u002Fhub\u002Fsrc\u002Fwith-party\u002Fteam\u002Fkeyring.ts",[1022,9859,6242],{"id":6241},[937,9861,9862,9863,9865,9866,9868,9869,9872,9873,9876,9877,9880,9881,9883],{},"noy-db encrypts each collection with its own ",[950,9864,5627],{}," (data-encryption key). Your copy of\nthose DEKs lives in your ",[950,9867,2705],{},": a per-user record stored at ",[941,9870,9871],{},"_keyring\u002F\u003CuserId>","\ninside the vault. The DEKs in the keyring are ",[1092,9874,9875],{},"wrapped"," — encrypted with AES-KW — under\nyour ",[950,9878,9879],{},"KEK"," (key-encryption key), which is derived from your secret and never written\nto disk. The full key hierarchy is covered in ",[1007,9882,1516],{"href":6655},";\nthis page assumes it.",[937,9885,9886,9887,9890],{},"That design makes the keyring both the access-control list and the cryptographic gate:\n",[950,9888,9889],{},"what you can read is exactly what your keyring can unwrap",". The keyring record holds:",[1111,9892,9893,9914,9921,9924,9927,9933],{},[1114,9894,9895,9896,1261,9899,1261,9902,1261,9905,1261,9908,1261,9911,1381],{},"Your role (",[941,9897,9898],{},"owner",[941,9900,9901],{},"admin",[941,9903,9904],{},"custodian",[941,9906,9907],{},"operator",[941,9909,9910],{},"viewer",[941,9912,9913],{},"client",[1114,9915,9916,9917,9920],{},"Per-collection permissions (",[941,9918,9919],{},"{ collectionName: 'rw' | 'ro' }",") for the explicit-grant roles",[1114,9922,9923],{},"Wrapped DEKs — one per collection you can decrypt",[1114,9925,9926],{},"The salt used to derive your KEK from your secret",[1114,9928,3289,9929,9932],{},[1092,9930,9931],{},"canary"," — a fixed value wrapped under your KEK, so the hub can tell a wrong\nsecret apart from on-disk corruption",[1114,9934,9935,9936,9939],{},"Optional export\u002Fimport capability grants for the ",[941,9937,9938],{},"@noy-db\u002Fas-*"," packages",[937,9941,9942,9943,9946,9947,9950,9951,9954],{},"A vault opens only for a user whose keyring unwraps. Secret unlock is the default;\npasskey, OIDC, magic-link, and other unlock methods come from the ",[941,9944,9945],{},"@noy-db\u002Fon-*"," packages\n(see ",[1007,9948,617],{"href":9949},"..\u002Fservices\u002Fauth-landscape",") via the ",[941,9952,9953],{},"getKeyring"," option.",[1022,9956,9958],{"id":9957},"roles","Roles",[1338,9960,9961,9973],{},[1341,9962,9963],{},[1344,9964,9965,9967,9970],{},[1347,9966,2970],{},[1347,9968,9969],{},"Collection access",[1347,9971,9972],{},"Can grant \u002F revoke",[1357,9974,9975,9987,10005,10015,10033,10044],{},[1344,9976,9977,9981,9984],{},[1362,9978,9979],{},[941,9980,9898],{},[1362,9982,9983],{},"all, read-write",[1362,9985,9986],{},"Yes — any role",[1344,9988,9989,9993,9995],{},[1362,9990,9991],{},[941,9992,9901],{},[1362,9994,9983],{},[1362,9996,9997,1261,9999,1261,10001,1261,10003],{},[941,9998,9901],{},[941,10000,9907],{},[941,10002,9910],{},[941,10004,9913],{},[1344,10006,10007,10011,10013],{},[1362,10008,10009],{},[941,10010,9904],{},[1362,10012,9983],{},[1362,10014,4962],{},[1344,10016,10017,10021,10031],{},[1362,10018,10019],{},[941,10020,9907],{},[1362,10022,10023,10024,4688,10027,10030],{},"explicit list (",[941,10025,10026],{},"rw",[941,10028,10029],{},"ro"," per collection)",[1362,10032,4962],{},[1344,10034,10035,10039,10042],{},[1362,10036,10037],{},[941,10038,9910],{},[1362,10040,10041],{},"all, read-only",[1362,10043,4962],{},[1344,10045,10046,10050,10053],{},[1362,10047,10048],{},[941,10049,9913],{},[1362,10051,10052],{},"explicit list, read-only",[1362,10054,4962],{},[1111,10056,10057,10065,10078,10100],{},[1114,10058,10059,10064],{},[950,10060,10061,10063],{},[941,10062,9898],{}," cannot be revoked"," — by anyone, ever.",[1114,10066,10067,10072,10073,10077],{},[950,10068,10069,10071],{},[941,10070,9901],{},"s delegate laterally",": an admin can grant another admin, but never an owner or\na custodian. When an admin is revoked, the admins they granted are revoked too (see\n",[1007,10074,10076],{"href":10075},"#revoke","Revoke"," below).",[1114,10079,10080,10084,10085,2601,10088,10091,10092,10095,10096,10099],{},[950,10081,10082],{},[941,10083,9904],{}," is the sovereign-custody role: operationally admin-rank (reads and\nwrites every collection) but provably non-owning — it can never grant, revoke, or\nrotate keys. Only the owner can mint or remove one, via ",[941,10086,10087],{},"db.grantCustodian()",[941,10089,10090],{},"db.revokeCustodian()"," (opt-in: ",[941,10093,10094],{},"custodyStrategy: withCustody()"," — see the\n",[1007,10097,661],{"href":10098},"..\u002Fservices\u002Fcustody"," service).",[1114,10101,10102,10105,10106,10109,10110,10113,10114,10117,10118,10120,10121,10123],{},[950,10103,10104],{},"Privilege rank"," (used by ",[941,10107,10108],{},"db.listAccessibleVaults({ minRole })","):\n",[941,10111,10112],{},"client (1) \u003C viewer (2) \u003C operator (3) \u003C custodian = admin (4) \u003C owner (5)",". The order\nreflects ",[1092,10115,10116],{},"how much a principal can see",", not how much it can modify — which is why\n",[941,10119,9910],{}," (read-all) outranks ",[941,10122,9913],{}," (explicit-read).",[1022,10125,10127],{"id":10126},"single-owner-flow","Single-owner flow",[937,10129,10130,10131,10133],{},"Single-user vaults need nothing beyond the core. The first ",[941,10132,1749],{}," for a brand-new\nvault creates the owner keyring automatically:",[2217,10135,10137],{"className":2219,"code":10136,"language":2221,"meta":1037,"style":1037},"import { createNoydb } from '@noy-db\u002Fhub'\n\n\u002F\u002F No store → the kernel's built-in in-memory store. Add `store: …` to persist.\nconst db = await createNoydb({ user: 'me', secret: 'correct-horse-battery-staple' })\n\n\u002F\u002F First open of a new vault: a random salt is generated, the KEK is derived\n\u002F\u002F from the secret, and an owner keyring is persisted to _keyring\u002Fme.\nconst vault = await db.openVault('personal')\n\nawait db.listUsers('personal')\n\u002F\u002F → [{ userId: 'me', displayName: 'me', role: 'owner', permissions: {},\n\u002F\u002F      createdAt: '2026-07-08T10:00:00.000Z', grantedBy: 'me' }]\n",[941,10138,10139,10157,10161,10166,10209,10213,10218,10223,10250,10254,10275,10280],{"__ignoreMap":1037},[2225,10140,10141,10143,10145,10147,10149,10151,10153,10155],{"class":2227,"line":2228},[2225,10142,2232],{"class":2231},[2225,10144,2236],{"class":2235},[2225,10146,2744],{"class":2239},[2225,10148,2243],{"class":2235},[2225,10150,2246],{"class":2231},[2225,10152,2249],{"class":2235},[2225,10154,1086],{"class":2252},[2225,10156,2256],{"class":2235},[2225,10158,10159],{"class":2227,"line":1041},[2225,10160,2285],{"emptyLinePlaceholder":2284},[2225,10162,10163],{"class":2227,"line":1038},[2225,10164,10165],{"class":2763},"\u002F\u002F No store → the kernel's built-in in-memory store. Add `store: …` to persist.\n",[2225,10167,10168,10170,10172,10174,10176,10178,10180,10182,10184,10186,10188,10190,10192,10194,10196,10198,10200,10203,10205,10207],{"class":2227,"line":2288},[2225,10169,2775],{"class":2774},[2225,10171,2778],{"class":2239},[2225,10173,2781],{"class":2235},[2225,10175,2784],{"class":2231},[2225,10177,2744],{"class":2291},[2225,10179,2789],{"class":2239},[2225,10181,2792],{"class":2235},[2225,10183,2795],{"class":2307},[2225,10185,2311],{"class":2235},[2225,10187,2249],{"class":2235},[2225,10189,4212],{"class":2252},[2225,10191,2805],{"class":2235},[2225,10193,2298],{"class":2235},[2225,10195,2810],{"class":2307},[2225,10197,2311],{"class":2235},[2225,10199,2249],{"class":2235},[2225,10201,10202],{"class":2252},"correct-horse-battery-staple",[2225,10204,2805],{"class":2235},[2225,10206,2243],{"class":2235},[2225,10208,2362],{"class":2239},[2225,10210,10211],{"class":2227,"line":2304},[2225,10212,2285],{"emptyLinePlaceholder":2284},[2225,10214,10215],{"class":2227,"line":2356},[2225,10216,10217],{"class":2763},"\u002F\u002F First open of a new vault: a random salt is generated, the KEK is derived\n",[2225,10219,10220],{"class":2227,"line":2830},[2225,10221,10222],{"class":2763},"\u002F\u002F from the secret, and an owner keyring is persisted to _keyring\u002Fme.\n",[2225,10224,10225,10227,10229,10231,10233,10235,10237,10239,10241,10243,10246,10248],{"class":2227,"line":2861},[2225,10226,2775],{"class":2774},[2225,10228,2835],{"class":2239},[2225,10230,2781],{"class":2235},[2225,10232,2784],{"class":2231},[2225,10234,2842],{"class":2239},[2225,10236,1520],{"class":2235},[2225,10238,2847],{"class":2291},[2225,10240,2789],{"class":2239},[2225,10242,2805],{"class":2235},[2225,10244,10245],{"class":2252},"personal",[2225,10247,2805],{"class":2235},[2225,10249,2362],{"class":2239},[2225,10251,10252],{"class":2227,"line":4246},[2225,10253,2285],{"emptyLinePlaceholder":2284},[2225,10255,10256,10258,10260,10262,10265,10267,10269,10271,10273],{"class":2227,"line":4253},[2225,10257,6500],{"class":2231},[2225,10259,2842],{"class":2239},[2225,10261,1520],{"class":2235},[2225,10263,10264],{"class":2291},"listUsers",[2225,10266,2789],{"class":2239},[2225,10268,2805],{"class":2235},[2225,10270,10245],{"class":2252},[2225,10272,2805],{"class":2235},[2225,10274,2362],{"class":2239},[2225,10276,10277],{"class":2227,"line":4259},[2225,10278,10279],{"class":2763},"\u002F\u002F → [{ userId: 'me', displayName: 'me', role: 'owner', permissions: {},\n",[2225,10281,10282],{"class":2227,"line":6548},[2225,10283,10284],{"class":2763},"\u002F\u002F      createdAt: '2026-07-08T10:00:00.000Z', grantedBy: 'me' }]\n",[937,10286,10287,10288,10291,10292,10295],{},"Collection DEKs are minted lazily: the first time a collection is touched, the hub\ngenerates a fresh DEK, wraps it under your KEK, and re-persists the keyring. On every\nlater open, the hub re-derives the KEK from your secret plus the stored salt and\nunwraps the DEKs — a wrong secret throws ",[941,10289,10290],{},"InvalidKeyError","; a keyring whose bytes\nwere damaged throws ",[941,10293,10294],{},"KeyringCorruptError"," (the canary is what distinguishes the two).",[1022,10297,10299],{"id":10298},"multi-user-grant-and-revoke","Multi-user: grant and revoke",[937,10301,10302,10303,10305,10306,10309,10310,10313,10314,10316,10317,1261,10320,2601,10323,10326,10327,10330,10331,10334,10335,10338,10339,1520],{},"Multi-user membership is the ",[950,10304,868],{}," capability, opt-in since ",[941,10307,10308],{},"0.3",": pass\n",[941,10311,10312],{},"teamStrategy: withTeam()"," to ",[941,10315,2134],{},", otherwise ",[941,10318,10319],{},"db.grant",[941,10321,10322],{},"db.revoke",[941,10324,10325],{},"db.rotate"," throw ",[941,10328,10329],{},"TeamNotEnabledError",". Magic-link invites live with the\n",[1007,10332,868],{"href":10333},"..\u002Fservices\u002Fteam"," service; cross-tier delegation moved to\n",[1007,10336,872],{"href":10337},"..\u002Fservices\u002Ftiers",", and the session-authentication ladder has its own page at\n",[1007,10340,844],{"href":10341},"..\u002Fservices\u002Fsession-tiers",[2217,10343,10345],{"className":2219,"code":10344,"language":2221,"meta":1037,"style":1037},"import { createNoydb } from '@noy-db\u002Fhub'\nimport { withTeam } from '@noy-db\u002Fhub\u002Fteam'\n\nconst db = await createNoydb({\n  user: 'owner@example.com',\n  secret: 'owner-secret-goes-here',\n  teamStrategy: withTeam(),\n})\nawait db.openVault('acme')\n\n\u002F\u002F Owner grants Alice read-write on invoices\nawait db.grant('acme', {\n  userId: 'alice@example.com',\n  displayName: 'Alice',\n  role: 'operator',\n  secret: 'alice-initial-secret',\n  permissions: { invoices: 'rw' },\n})\n\nawait db.listUsers('acme')\n\u002F\u002F → [ { userId: 'owner@example.com', role: 'owner',    … },\n\u002F\u002F     { userId: 'alice@example.com', role: 'operator', permissions: { invoices: 'rw' },\n\u002F\u002F       grantedBy: 'owner@example.com', … } ]\n\n\u002F\u002F Owner revokes Alice; the DEKs of her collections are rotated\nawait db.revoke('acme', { userId: 'alice@example.com' })\n",[941,10346,10347,10365,10384,10388,10404,10419,10435,10448,10454,10474,10478,10483,10506,10522,10538,10553,10568,10589,10595,10599,10619,10624,10629,10634,10638,10643],{"__ignoreMap":1037},[2225,10348,10349,10351,10353,10355,10357,10359,10361,10363],{"class":2227,"line":2228},[2225,10350,2232],{"class":2231},[2225,10352,2236],{"class":2235},[2225,10354,2744],{"class":2239},[2225,10356,2243],{"class":2235},[2225,10358,2246],{"class":2231},[2225,10360,2249],{"class":2235},[2225,10362,1086],{"class":2252},[2225,10364,2256],{"class":2235},[2225,10366,10367,10369,10371,10374,10376,10378,10380,10382],{"class":2227,"line":1041},[2225,10368,2232],{"class":2231},[2225,10370,2236],{"class":2235},[2225,10372,10373],{"class":2239}," withTeam",[2225,10375,2243],{"class":2235},[2225,10377,2246],{"class":2231},[2225,10379,2249],{"class":2235},[2225,10381,4680],{"class":2252},[2225,10383,2256],{"class":2235},[2225,10385,10386],{"class":2227,"line":1038},[2225,10387,2285],{"emptyLinePlaceholder":2284},[2225,10389,10390,10392,10394,10396,10398,10400,10402],{"class":2227,"line":2288},[2225,10391,2775],{"class":2774},[2225,10393,2778],{"class":2239},[2225,10395,2781],{"class":2235},[2225,10397,2784],{"class":2231},[2225,10399,2744],{"class":2291},[2225,10401,2789],{"class":2239},[2225,10403,4200],{"class":2235},[2225,10405,10406,10408,10410,10412,10415,10417],{"class":2227,"line":2304},[2225,10407,4205],{"class":2307},[2225,10409,2311],{"class":2235},[2225,10411,2249],{"class":2235},[2225,10413,10414],{"class":2252},"owner@example.com",[2225,10416,2805],{"class":2235},[2225,10418,1707],{"class":2235},[2225,10420,10421,10424,10426,10428,10431,10433],{"class":2227,"line":2356},[2225,10422,10423],{"class":2307},"  secret",[2225,10425,2311],{"class":2235},[2225,10427,2249],{"class":2235},[2225,10429,10430],{"class":2252},"owner-secret-goes-here",[2225,10432,2805],{"class":2235},[2225,10434,1707],{"class":2235},[2225,10436,10437,10440,10442,10444,10446],{"class":2227,"line":2830},[2225,10438,10439],{"class":2307},"  teamStrategy",[2225,10441,2311],{"class":2235},[2225,10443,10373],{"class":2291},[2225,10445,4228],{"class":2239},[2225,10447,1707],{"class":2235},[2225,10449,10450,10452],{"class":2227,"line":2861},[2225,10451,2359],{"class":2235},[2225,10453,2362],{"class":2239},[2225,10455,10456,10458,10460,10462,10464,10466,10468,10470,10472],{"class":2227,"line":4246},[2225,10457,6500],{"class":2231},[2225,10459,2842],{"class":2239},[2225,10461,1520],{"class":2235},[2225,10463,2847],{"class":2291},[2225,10465,2789],{"class":2239},[2225,10467,2805],{"class":2235},[2225,10469,6450],{"class":2252},[2225,10471,2805],{"class":2235},[2225,10473,2362],{"class":2239},[2225,10475,10476],{"class":2227,"line":4253},[2225,10477,2285],{"emptyLinePlaceholder":2284},[2225,10479,10480],{"class":2227,"line":4259},[2225,10481,10482],{"class":2763},"\u002F\u002F Owner grants Alice read-write on invoices\n",[2225,10484,10485,10487,10489,10491,10494,10496,10498,10500,10502,10504],{"class":2227,"line":6548},[2225,10486,6500],{"class":2231},[2225,10488,2842],{"class":2239},[2225,10490,1520],{"class":2235},[2225,10492,10493],{"class":2291},"grant",[2225,10495,2789],{"class":2239},[2225,10497,2805],{"class":2235},[2225,10499,6450],{"class":2252},[2225,10501,2805],{"class":2235},[2225,10503,2298],{"class":2235},[2225,10505,2301],{"class":2235},[2225,10507,10508,10511,10513,10515,10518,10520],{"class":2227,"line":6576},[2225,10509,10510],{"class":2307},"  userId",[2225,10512,2311],{"class":2235},[2225,10514,2249],{"class":2235},[2225,10516,10517],{"class":2252},"alice@example.com",[2225,10519,2805],{"class":2235},[2225,10521,1707],{"class":2235},[2225,10523,10524,10527,10529,10531,10534,10536],{"class":2227,"line":6582},[2225,10525,10526],{"class":2307},"  displayName",[2225,10528,2311],{"class":2235},[2225,10530,2249],{"class":2235},[2225,10532,10533],{"class":2252},"Alice",[2225,10535,2805],{"class":2235},[2225,10537,1707],{"class":2235},[2225,10539,10540,10543,10545,10547,10549,10551],{"class":2227,"line":6587},[2225,10541,10542],{"class":2307},"  role",[2225,10544,2311],{"class":2235},[2225,10546,2249],{"class":2235},[2225,10548,9907],{"class":2252},[2225,10550,2805],{"class":2235},[2225,10552,1707],{"class":2235},[2225,10554,10555,10557,10559,10561,10564,10566],{"class":2227,"line":7174},[2225,10556,10423],{"class":2307},[2225,10558,2311],{"class":2235},[2225,10560,2249],{"class":2235},[2225,10562,10563],{"class":2252},"alice-initial-secret",[2225,10565,2805],{"class":2235},[2225,10567,1707],{"class":2235},[2225,10569,10570,10573,10575,10577,10579,10581,10583,10585,10587],{"class":2227,"line":7180},[2225,10571,10572],{"class":2307},"  permissions",[2225,10574,2311],{"class":2235},[2225,10576,2236],{"class":2235},[2225,10578,6503],{"class":2307},[2225,10580,2311],{"class":2235},[2225,10582,2249],{"class":2235},[2225,10584,10026],{"class":2252},[2225,10586,2805],{"class":2235},[2225,10588,2353],{"class":2235},[2225,10590,10591,10593],{"class":2227,"line":7220},[2225,10592,2359],{"class":2235},[2225,10594,2362],{"class":2239},[2225,10596,10597],{"class":2227,"line":7225},[2225,10598,2285],{"emptyLinePlaceholder":2284},[2225,10600,10601,10603,10605,10607,10609,10611,10613,10615,10617],{"class":2227,"line":7231},[2225,10602,6500],{"class":2231},[2225,10604,2842],{"class":2239},[2225,10606,1520],{"class":2235},[2225,10608,10264],{"class":2291},[2225,10610,2789],{"class":2239},[2225,10612,2805],{"class":2235},[2225,10614,6450],{"class":2252},[2225,10616,2805],{"class":2235},[2225,10618,2362],{"class":2239},[2225,10620,10621],{"class":2227,"line":7267},[2225,10622,10623],{"class":2763},"\u002F\u002F → [ { userId: 'owner@example.com', role: 'owner',    … },\n",[2225,10625,10626],{"class":2227,"line":7272},[2225,10627,10628],{"class":2763},"\u002F\u002F     { userId: 'alice@example.com', role: 'operator', permissions: { invoices: 'rw' },\n",[2225,10630,10631],{"class":2227,"line":7278},[2225,10632,10633],{"class":2763},"\u002F\u002F       grantedBy: 'owner@example.com', … } ]\n",[2225,10635,10636],{"class":2227,"line":7303},[2225,10637,2285],{"emptyLinePlaceholder":2284},[2225,10639,10640],{"class":2227,"line":7308},[2225,10641,10642],{"class":2763},"\u002F\u002F Owner revokes Alice; the DEKs of her collections are rotated\n",[2225,10644,10645,10647,10649,10651,10654,10656,10658,10660,10662,10664,10666,10669,10671,10673,10675,10677,10679],{"class":2227,"line":7314},[2225,10646,6500],{"class":2231},[2225,10648,2842],{"class":2239},[2225,10650,1520],{"class":2235},[2225,10652,10653],{"class":2291},"revoke",[2225,10655,2789],{"class":2239},[2225,10657,2805],{"class":2235},[2225,10659,6450],{"class":2252},[2225,10661,2805],{"class":2235},[2225,10663,2298],{"class":2235},[2225,10665,2236],{"class":2235},[2225,10667,10668],{"class":2307}," userId",[2225,10670,2311],{"class":2235},[2225,10672,2249],{"class":2235},[2225,10674,10517],{"class":2252},[2225,10676,2805],{"class":2235},[2225,10678,2243],{"class":2235},[2225,10680,2362],{"class":2239},[1591,10682,10684],{"id":10683},"what-a-grant-does","What a grant does",[937,10686,10687],{},"A grant is a key ceremony, not a row in an ACL table:",[1234,10689,10690,10697,10704],{},[1114,10691,10692,10693,10696],{},"A fresh salt is generated for the grantee, and their KEK is derived from the\n",[941,10694,10695],{},"secret"," in the grant options.",[1114,10698,10699,10700,10703],{},"The DEKs of the granted collections — which the ",[1092,10701,10702],{},"grantor's"," unlocked keyring already\nholds in memory — are wrapped under the grantee's new KEK.",[1114,10705,10706,10707,10709,10710,10713],{},"A keyring record for the grantee is written to ",[941,10708,9871],{},", stamped with\n",[941,10711,10712],{},"grantedBy"," for the delegation trail.",[937,10715,10716,10717,1520],{},"The records themselves are never re-encrypted, and no plaintext is touched. A grantor can\nonly hand out DEKs it holds itself — a grant that would widen access beyond the grantor's\nown keyring throws ",[941,10718,10719],{},"PrivilegeEscalationError",[1591,10721,10076],{"id":10653},[937,10723,10724,10727,10728,10731,10732,10735,10736,10739],{},[941,10725,10726],{},"db.revoke(vault, { userId })"," deletes the target's keyring and ",[950,10729,10730],{},"rotates the DEKs"," of\nevery collection that keyring could decrypt — each affected record is re-encrypted under a\nfresh DEK. That rotation is the same operation as ",[941,10733,10734],{},"db.rotate()"," below, so it has the same\nconsequence: ",[950,10737,10738],{},"the remaining members also lose their keys"," for those collections and must\nbe re-granted.",[937,10741,10742,10743,10746,10747,10750,10751,10754,10755,10758,10759,10762],{},"Rotation is unconditional — there is no option to skip it. ",[941,10744,10745],{},"RevokeOptions"," carries ",[941,10748,10749],{},"userId","\nand ",[941,10752,10753],{},"cascade",", and nothing else. An earlier ",[941,10756,10757],{},"rotateKeys: false"," escape hatch was removed\nbecause it made revocation a no-op: leaving the DEKs in place means the revoked member's\ncopied key material keeps opening the collection, including records written ",[1092,10760,10761],{},"after"," the\nrevocation. Losing the other members' keys is the cost of revocation actually revoking.",[937,10764,10765,10766,10769],{},"When the revoked user is an admin, the revoke\ncascades through every admin they (transitively) granted; pass ",[941,10767,10768],{},"cascade: 'warn'"," to leave\nthose descendants in place and get a console warning instead.",[937,10771,10772,10773,10776,10777,10780,10781,1520],{},"To rotate without removing anyone (e.g. after a suspected key leak), use\n",[941,10774,10775],{},"db.rotate(vault, ['invoices', …])"," — but read the next section first: \"without removing\nanyone\" does not mean \"without taking anything away.\" To change a role, display name, or permission map\nwithout touching any keys, use ",[941,10778,10779],{},"db.updateUser(vault, { userId, role?, displayName?, permissions? })"," — a header-only rewrite that leaves DEK wrappings and enrolled\nauthenticators intact, guarded by the same role hierarchy as ",[941,10782,10493],{},[1591,10784,10786],{"id":10785},"rotation-drops-every-other-members-access","Rotation drops every other member's access",[7646,10788,10791],{"color":10789,"icon":10790},"warning","i-lucide-shield-alert",[937,10792,10793,10796,10797,10800,10801,10803,10804,10806,10807,10810],{},[941,10794,10795],{},"db.rotate(vault, collections)"," keeps everyone's ",[950,10798,10799],{},"membership"," but removes their ",[950,10802,8678],{}," for the\nrotated collections. Every other member is locked out of those collections until you re-grant them.\n",[941,10805,10726],{}," runs the same rotation by default, so ",[950,10808,10809],{},"revoking one member locks\nout the rest"," of the team on that member's collections.",[937,10812,10813],{},"This is the zero-knowledge property working as designed rather than a gap. A member's DEKs are\nwrapped under that member's KEK, and a KEK derives only from that member's secret — so the caller\ncannot derive anyone else's KEK, and therefore cannot re-wrap a fresh DEK for them. There is no\nkey material in the caller's hands that would let rotation preserve someone else's access.",[937,10815,10816,10817,10819,10820,10822],{},"What rotation does ",[1092,10818,1004],{}," do is remove anyone from the vault: members keep their membership, their\nrole, and their keys for every collection you did not rotate. That is the only sense in which this\nis the \"just rotate\" path — \"revoke and rotate\" is ",[941,10821,10726],{},", which\nalways rotates.",[937,10824,10825,10826],{},"So the recovery step is always the same: ",[950,10827,10828,10829,10832],{},"re-run ",[941,10830,10831],{},"grant()"," for each (member, collection) pair you\nrotated.",[2217,10834,10836],{"className":2219,"code":10835,"language":2221,"meta":1037,"style":1037},"const { needsRegrant } = await db.rotate('acme', ['invoices'])\n\u002F\u002F → [{ userId: 'bob', collection: 'invoices' }]\n\nfor (const { userId, collection } of needsRegrant) {\n  await db.grant('acme', { userId, permissions: { [collection]: 'rw' }, \u002F* … *\u002F })\n}\n",[941,10837,10838,10881,10886,10890,10916,10973],{"__ignoreMap":1037},[2225,10839,10840,10842,10844,10847,10849,10851,10853,10855,10857,10860,10862,10864,10866,10868,10870,10872,10874,10876,10878],{"class":2227,"line":2228},[2225,10841,2775],{"class":2774},[2225,10843,2236],{"class":2235},[2225,10845,10846],{"class":2239}," needsRegrant ",[2225,10848,2359],{"class":2235},[2225,10850,6346],{"class":2235},[2225,10852,2784],{"class":2231},[2225,10854,2842],{"class":2239},[2225,10856,1520],{"class":2235},[2225,10858,10859],{"class":2291},"rotate",[2225,10861,2789],{"class":2239},[2225,10863,2805],{"class":2235},[2225,10865,6450],{"class":2252},[2225,10867,2805],{"class":2235},[2225,10869,2298],{"class":2235},[2225,10871,8667],{"class":2239},[2225,10873,2805],{"class":2235},[2225,10875,6487],{"class":2252},[2225,10877,2805],{"class":2235},[2225,10879,10880],{"class":2239},"])\n",[2225,10882,10883],{"class":2227,"line":1041},[2225,10884,10885],{"class":2763},"\u002F\u002F → [{ userId: 'bob', collection: 'invoices' }]\n",[2225,10887,10888],{"class":2227,"line":1038},[2225,10889,2285],{"emptyLinePlaceholder":2284},[2225,10891,10892,10894,10896,10898,10900,10902,10904,10907,10909,10911,10914],{"class":2227,"line":2288},[2225,10893,4646],{"class":2231},[2225,10895,953],{"class":2239},[2225,10897,2775],{"class":2774},[2225,10899,2236],{"class":2235},[2225,10901,10668],{"class":2239},[2225,10903,2298],{"class":2235},[2225,10905,10906],{"class":2239}," collection ",[2225,10908,2359],{"class":2235},[2225,10910,8806],{"class":2235},[2225,10912,10913],{"class":2239}," needsRegrant) ",[2225,10915,4200],{"class":2235},[2225,10917,10918,10920,10922,10924,10926,10928,10930,10932,10934,10936,10938,10940,10942,10945,10947,10949,10951,10953,10955,10957,10959,10961,10963,10966,10969,10971],{"class":2227,"line":2304},[2225,10919,7561],{"class":2231},[2225,10921,2842],{"class":2239},[2225,10923,1520],{"class":2235},[2225,10925,10493],{"class":2291},[2225,10927,2789],{"class":2307},[2225,10929,2805],{"class":2235},[2225,10931,6450],{"class":2252},[2225,10933,2805],{"class":2235},[2225,10935,2298],{"class":2235},[2225,10937,2236],{"class":2235},[2225,10939,10668],{"class":2239},[2225,10941,2298],{"class":2235},[2225,10943,10944],{"class":2307}," permissions",[2225,10946,2311],{"class":2235},[2225,10948,2236],{"class":2235},[2225,10950,8667],{"class":2307},[2225,10952,6471],{"class":2239},[2225,10954,8803],{"class":2307},[2225,10956,2311],{"class":2235},[2225,10958,2249],{"class":2235},[2225,10960,10026],{"class":2252},[2225,10962,2805],{"class":2235},[2225,10964,10965],{"class":2235}," },",[2225,10967,10968],{"class":2763}," \u002F* … *\u002F",[2225,10970,2243],{"class":2235},[2225,10972,2362],{"class":2307},[2225,10974,10975],{"class":2227,"line":2356},[2225,10976,7351],{"class":2235},[937,10978,10979,10982,10983,10986],{},[941,10980,10981],{},"rotate()"," returns that list as ",[941,10984,10985],{},"needsRegrant"," (noy-db#854), so you do not have to track the\naffected pairs yourself before rotating.",[1591,10988,10990],{"id":10989},"the-roster-epoch-refusing-a-replayed-roster","The roster epoch — refusing a replayed roster",[937,10992,10993,10994,10997,10998,11001],{},"Since ",[941,10995,10996],{},"0.7.0-pre.9"," a keyring carries ",[941,10999,11000],{},"roster_epoch",", bumped on every roster write and bound into\nthe roster's canonical form so a store can neither edit it nor strip it (noy-db#1097).",[937,11003,11004,11005,11008,11009,11011,11012,11014,11015,11018],{},"It exists because a roster tag authenticates the roster's ",[950,11006,11007],{},"contents"," and says nothing about it\nbeing ",[950,11010,8511],{}," — and the gap is reachable without forging anything. There is no role-change API,\nso narrowing someone's standing means calling ",[941,11013,10493],{}," again with a lower role, which overwrites in\nplace. The previous, broader file was legitimately minted by the vault, so a store that kept a copy\ncan re-serve it: the KEK unwraps, the canary checks out, the tag verifies, and the replay ",[950,11016,11017],{},"restores\nthe higher role",", usably. Rotation does not close this — it converts live access into stale access,\nand role gates capabilities rather than keys.",[937,11020,11021,11022,2311],{},"What ships in ",[941,11023,10996],{},[1111,11025,11026,11038,11064,11089],{},[1114,11027,11028,11033,11034,11037],{},[950,11029,11030],{},[941,11031,11032],{},"KeyringFile.roster_epoch?: number"," — bumped on every roster write and bound into\n",[941,11035,11036],{},"rosterCanonical",", so a store can neither edit nor strip it.",[1114,11039,11040,964,11047,11050,11051,11054,11055,11060,11061,1520],{},[950,11041,11042,11043,11046],{},"Two ",[941,11044,11045],{},"KeyringTamperedError"," reasons",[941,11048,11049],{},"roster-epoch-rewound"," (the file is older than the floor\nyou supplied) and ",[941,11052,11053],{},"roster-epoch-absent"," (it carries no epoch at all). Both arrive at\n",[950,11056,11057],{},[941,11058,11059],{},"err.details.reason",", not ",[941,11062,11063],{},"err.reason",[1114,11065,11066,997,11071,11076,11077,11080,11081,11084,11085,11088],{},[950,11067,11068],{},[941,11069,11070],{},"assertRosterEpochCurrent(found, expected, userId)",[950,11072,11073],{},[941,11074,11075],{},"nextRosterEpoch(current)",", on the\nroot barrel since ",[941,11078,11079],{},"0.7.0-pre.10",". (They shipped in ",[941,11082,11083],{},"pre.9"," reachable from no entry point at all;\n",[941,11086,11087],{},"pre.10"," exports them.)",[1114,11090,11091,11092,11095],{},"All thirteen roster-mint sites stamp the epoch before the tag is minted, and ",[941,11093,11094],{},"writeKeyringFile","\nrefuses to write a keyring without one — an output-domain guard, so a future mint site cannot ship\nthe field empty.",[2217,11097,11099],{"className":2219,"code":11098,"language":2221,"meta":1037,"style":1037},"import { assertRosterEpochCurrent } from '@noy-db\u002Fhub'\n\n\u002F\u002F `expected` is your floor, and it must reach you by a channel the store does not carry.\ntry {\n  assertRosterEpochCurrent(keyring.roster_epoch, expected, userId)\n} catch (err) {\n  \u002F\u002F err.details.reason === 'roster-epoch-rewound' | 'roster-epoch-absent'\n}\n",[941,11100,11101,11120,11124,11129,11135,11159,11169,11174],{"__ignoreMap":1037},[2225,11102,11103,11105,11107,11110,11112,11114,11116,11118],{"class":2227,"line":2228},[2225,11104,2232],{"class":2231},[2225,11106,2236],{"class":2235},[2225,11108,11109],{"class":2239}," assertRosterEpochCurrent",[2225,11111,2243],{"class":2235},[2225,11113,2246],{"class":2231},[2225,11115,2249],{"class":2235},[2225,11117,1086],{"class":2252},[2225,11119,2256],{"class":2235},[2225,11121,11122],{"class":2227,"line":1041},[2225,11123,2285],{"emptyLinePlaceholder":2284},[2225,11125,11126],{"class":2227,"line":1038},[2225,11127,11128],{"class":2763},"\u002F\u002F `expected` is your floor, and it must reach you by a channel the store does not carry.\n",[2225,11130,11131,11133],{"class":2227,"line":2288},[2225,11132,7554],{"class":2231},[2225,11134,2301],{"class":2235},[2225,11136,11137,11140,11142,11144,11146,11148,11150,11153,11155,11157],{"class":2227,"line":2304},[2225,11138,11139],{"class":2291},"  assertRosterEpochCurrent",[2225,11141,2789],{"class":2307},[2225,11143,2705],{"class":2239},[2225,11145,1520],{"class":2235},[2225,11147,11000],{"class":2239},[2225,11149,2298],{"class":2235},[2225,11151,11152],{"class":2239}," expected",[2225,11154,2298],{"class":2235},[2225,11156,10668],{"class":2239},[2225,11158,2362],{"class":2307},[2225,11160,11161,11163,11165,11167],{"class":2227,"line":2356},[2225,11162,2359],{"class":2235},[2225,11164,7598],{"class":2231},[2225,11166,7601],{"class":2239},[2225,11168,4200],{"class":2235},[2225,11170,11171],{"class":2227,"line":2830},[2225,11172,11173],{"class":2763},"  \u002F\u002F err.details.reason === 'roster-epoch-rewound' | 'roster-epoch-absent'\n",[2225,11175,11176],{"class":2227,"line":2861},[2225,11177,7351],{"class":2235},[1338,11179,11180,11196],{},[1341,11181,11182],{},[1344,11183,11184,11193],{},[1347,11185,11186,11189,11190],{},[941,11187,11188],{},"found"," vs ",[941,11191,11192],{},"expected",[1347,11194,11195],{},"result",[1357,11197,11198,11206,11218,11234,11250],{},[1344,11199,11200,11203],{},[1362,11201,11202],{},"equal",[1362,11204,11205],{},"accepted",[1344,11207,11208,11216],{},[1362,11209,11210,1324,11212,11215],{},[941,11211,11188],{},[950,11213,11214],{},"greater"," — the roster moved on since your floor was minted",[1362,11217,11205],{},[1344,11219,11220,11228],{},[1362,11221,11222,1324,11224,11227],{},[941,11223,11188],{},[950,11225,11226],{},"lower"," — a rewind",[1362,11229,11230,968,11232],{},[941,11231,11045],{},[941,11233,11049],{},[1344,11235,11236,11244],{},[1362,11237,11238,11240,11241,11243],{},[941,11239,11188],{}," absent, ",[941,11242,11192],{}," supplied",[1362,11245,11246,968,11248],{},[941,11247,11045],{},[941,11249,11053],{},[1344,11251,11252,11257],{},[1362,11253,11254,11256],{},[941,11255,11192],{}," absent — you supplied no floor",[1362,11258,11259],{},"accepted; the check is opt-in",[937,11261,11262,2557,11265,11060,11269,11271,11272,11275],{},[941,11263,11264],{},"nextRosterEpoch(undefined)",[950,11266,11267],{},[941,11268,5437],{},[941,11270,5402],{},", so ",[1092,11273,11274],{},"\"has an epoch\""," is never confusable with\nfalsy. It is the only sanctioned way to produce the next value: a write that reused or lowered an\nepoch would mint a file the comparison above cannot tell from a replay.",[7646,11277,11280,11290],{"color":11278,"icon":11279},"info","i-lucide-radio",[937,11281,11282,11285,11286,11289],{},[950,11283,11284],{},"The anchor has to arrive out of band, and that part is yours."," An epoch stored beside the roster\nis not an anchor — a store that rewinds the roster rewinds the epoch with it. It becomes one only\nwhen compared against a value that reached the reader by a channel the store does not carry. The hub\nowns the epoch and the comparison; ",[950,11287,11288],{},"which channel a deployment trusts is deliberately the\nconsumer's problem",", because the hub cannot know.",[937,11291,11292,11293,11296,11297,11300,11301,11304],{},"Two alternatives were considered and rejected, recorded here so they are not re-derived: ",[950,11294,11295],{},"time","\n(defeats long rewinds, useless against fresh ones — and a narrowing replay is entirely the fresh\ncase) and ",[950,11298,11299],{},"the vault head"," (circular — head buckets are read with a keyring-issued DEK, and a\nrewound roster renders as the benign ",[1092,11302,11303],{},"no-expectations"," verdict).",[937,11306,11307,11310,11311,11314,11315,11317,11318,11321],{},[950,11308,11309],{},"Opt-in, and absence is never zero."," With no expected epoch, nothing changes for a caller. When\none is supplied, a file carrying ",[950,11312,11313],{},"no"," epoch is refused as ",[941,11316,11053],{}," rather than read as\nepoch 0 — treating absence as zero would accept every pre-epoch file, which is exactly the replay\nthe mechanism refuses. ",[941,11319,11320],{},"found > expected"," is accepted: the anchor is a floor, not an equality, since\na roster may legitimately have moved on since the floor was issued.",[7646,11323,11325],{"color":7648,"icon":11324},"i-lucide-check-circle-2",[937,11326,11327,11330,11331,11334,11335,11338],{},[950,11328,11329],{},"Not a format break — no migration, no re-seed."," The epoch is bound ",[950,11332,11333],{},"conditionally",", so a\nkeyring written before it existed canonicalises byte-identically and its existing tag still\nverifies. (Binding it the way every other optional field is bound would have failed every\npre-existing tag and rendered every existing vault unopenable.) This is unrelated to the\n",[941,11336,11337],{},"0.6.0"," on-disk format breaks — nothing stored needs to change.",[1022,11340,11342],{"id":11341},"permission-checks-on-every-operation","Permission checks on every operation",[1111,11344,11345,11381,11387,11396],{},[1114,11346,11347,1764,11350,1261,11352,11354,11355,11358,11359,11362,11363,1261,11365,1261,11367,11369,11370,1261,11372,11374,11375,11377,11378,1520],{},[950,11348,11349],{},"Writes",[941,11351,3161],{},[941,11353,3164],{}," checks write permission ",[1092,11356,11357],{},"before"," any encryption or\nstore I\u002FO; a denied write throws ",[941,11360,11361],{},"ReadOnlyError"," and the store never sees the request.\nThe rule: ",[941,11364,9898],{},[941,11366,9901],{},[941,11368,9904],{}," always write; ",[941,11371,9910],{},[941,11373,9913],{}," never do;\n",[941,11376,9907],{}," writes where its permission entry is ",[941,11379,11380],{},"'rw'",[1114,11382,11383,11386],{},[950,11384,11385],{},"Reads"," — vault-wide enumeration silently skips collections the keyring has no access\nto. The floor is cryptographic, not advisory: without the wrapped DEK for a collection,\nthere is nothing to decrypt.",[1114,11388,11389,11392,11393,1520],{},[950,11390,11391],{},"Grant \u002F revoke \u002F role changes"," — an operation the caller's role does not permit\nthrows ",[941,11394,11395],{},"PermissionDeniedError",[1114,11397,11398,1169,11401,11403,11404,11407,11408,11411,11412,11415,11416,11419,11420,11423,11424,11427,11428,1261,11430,11432,11433,11435,11436,1520],{},[950,11399,11400],{},"Exports",[941,11402,9938],{}," packages authorize through\n",[941,11405,11406],{},"vault.assertCanExport(tier, format?)",", backed by the public\n",[941,11409,11410],{},"hasExportCapability(keyring, tier, format?)"," check. Two tiers:\n",[941,11413,11414],{},"assertCanExport('plaintext', 'xlsx')"," for plaintext formats — the default is ",[950,11417,11418],{},"empty\nfor every role",", so plaintext export must be positively granted via the\n",[941,11421,11422],{},"exportCapability"," grant option — and ",[941,11425,11426],{},"assertCanExport('bundle')"," for encrypted\nbundles, which defaults on for ",[941,11429,9898],{},[941,11431,9901],{}," and off for everyone else. Imports are\ndefault-closed for ",[1092,11434,2119],{}," role, including owner, until granted via ",[941,11437,11438],{},"importCapability",[1022,11440,11442],{"id":11441},"critical-invariants","Critical invariants",[1111,11444,11445,11455,11464,11470],{},[1114,11446,11447,11450,11451,11454],{},[950,11448,11449],{},"No vault without a keyring."," Even ",[941,11452,11453],{},"encrypt: false"," mode synthesizes an in-memory\nowner keyring, so the permission code path is uniform.",[1114,11456,11457,11460,11461,11463],{},[950,11458,11459],{},"Wrapped DEKs are persisted; raw DEKs and the KEK never are."," The keyring record's\nmetadata (role, permissions, salt) is readable on the store, but an attacker reading\n",[941,11462,9871],{}," cannot decrypt anything without the user's secret.",[1114,11465,11466,11469],{},[950,11467,11468],{},"Owner is irrevocable",", and no role below owner can create or destroy an\nowner-shaped keyring.",[1114,11471,11472,11475,11476,11478],{},[950,11473,11474],{},"A grant can never widen access",": every DEK wrapped into a new keyring must come from\nthe grantor's own unlocked DEK set (",[941,11477,10719],{}," otherwise).",[1022,11480,11482],{"id":11481},"public-surface","Public surface",[1111,11484,11485,11494,11503,11512,11518,11524,11537],{},[1114,11486,11487,11490,11491,1381],{},[941,11488,11489],{},"db.team.getKeyring(vault)"," — defensive copy of the unlocked keyring (",[941,11492,11493],{},"UnlockedKeyring",[1114,11495,11496,11499,11500],{},[941,11497,11498],{},"db.listUsers(vault)"," — every member's ",[941,11501,11502],{},"{ userId, displayName, role, permissions, createdAt, grantedBy }",[1114,11504,11505,964,11508,11511],{},[941,11506,11507],{},"db.listAccessibleVaults({ minRole? })",[941,11509,11510],{},"{ id, role }"," per vault the current user can unwrap",[1114,11513,11514,11517],{},[941,11515,11516],{},"db.changeSecret(vault, newSecret, opts?)"," — re-wrap your own keyring under a new secret",[1114,11519,11520,11523],{},[941,11521,11522],{},"db.updateUser(vault, opts)"," — header-only role \u002F name \u002F permissions change",[1114,11525,11526,11527,2085,11529,968,11532,968,11535],{},"With ",[941,11528,3197],{},[941,11530,11531],{},"db.grant(vault, opts)",[941,11533,11534],{},"db.revoke(vault, opts)",[941,11536,10795],{},[1114,11538,11526,11539,2085,11542,968,11545],{},[941,11540,11541],{},"withCustody()",[941,11543,11544],{},"db.grantCustodian(vault, opts)",[941,11546,11547],{},"db.revokeCustodian(vault, opts)",[1022,11549,2621],{"id":2620},[1111,11551,11552,11557,11563,11570],{},[1114,11553,11554,11556],{},[1007,11555,868],{"href":10333}," — multi-user grant\u002Frevoke\u002Frotate, magic-link, delegation",[1114,11558,11559,11562],{},[1007,11560,840],{"href":11561},"..\u002Fservices\u002Fsession"," — token sessions on top of keyrings",[1114,11564,11565,1169,11567,11569],{},[1007,11566,617],{"href":9949},[941,11568,9945],{}," unlock methods (secret, WebAuthn, OIDC, magic-link, …)",[1114,11571,11572,11574],{},[1007,11573,1516],{"href":6655}," — the key hierarchy this page builds on",[2645,11576,3254],{},{"title":1037,"searchDepth":1038,"depth":1038,"links":11578},[11579,11580,11581,11582,11588,11589,11590,11591],{"id":6241,"depth":1041,"text":6242},{"id":9957,"depth":1041,"text":9958},{"id":10126,"depth":1041,"text":10127},{"id":10298,"depth":1041,"text":10299,"children":11583},[11584,11585,11586,11587],{"id":10683,"depth":1038,"text":10684},{"id":10653,"depth":1038,"text":10076},{"id":10785,"depth":1038,"text":10786},{"id":10989,"depth":1038,"text":10990},{"id":11341,"depth":1041,"text":11342},{"id":11441,"depth":1041,"text":11442},{"id":11481,"depth":1041,"text":11482},{"id":2620,"depth":1041,"text":2621},"How a vault admits users: the per-user keyring file, roles, and per-collection rw\u002Fro\u002Fnone permissions.",{},{"title":10},{"title":6898,"description":11592},"7XpIhN7wqk8y0q4zo8JanK6PKs6mm7x5adjBAHkLBlI",{"id":11598,"title":62,"access":12,"apiToc":2284,"body":11599,"category":1042,"description":13380,"extension":1044,"meta":13381,"navigation":1042,"nest":2635,"path":63,"seo":13382,"sinceVersion":1042,"status":1049,"stem":64,"subtitle":1042,"tier":6922,"tocExternal":13383,"__hash__":13384},"docs\u002Fcore\u002F2.noydb\u002F1.methods.md",{"type":931,"value":11600,"toc":13344},[11601,11605,11623,11647,11654,11658,11667,11672,11747,11763,11819,11824,11836,11844,11851,11856,11904,11917,11934,11949,11954,12050,12053,12088,12092,12095,12105,12150,12174,12231,12236,12296,12301,12318,12324,12377,12383,12398,12403,12417,12420,12509,12515,12527,12530,12544,12550,12561,12564,12573,12607,12672,12685,12690,12721,12724,12734,12738,12748,12754,12768,12786,12793,12798,12812,12815,12819,12846,12852,12873,12883,12890,12896,12938,12948,12967,12973,13017,13027,13053,13057,13074,13078,13083,13090,13096,13137,13140,13150,13156,13170,13173,13179,13192,13195,13201,13231,13234,13241,13247,13285,13288,13295,13299,13304,13310,13323,13341],[934,11602,11604],{"id":11603},"noydb-the-object-reference","Noydb — the object reference",[937,11606,3289,11607,11609,11610,11612,11613,11615,11616,1746,11618,1746,11620,11622],{},[941,11608,62],{}," is the object ",[941,11611,2134],{}," returns — one unlocked session, conventionally bound to ",[941,11614,6590],{},". It holds the encryption keys in memory while you work and hands them back to nobody, and it sits at the top of the ",[941,11617,62],{},[941,11619,94],{},[941,11621,3121],{}," hierarchy: it opens vaults, owns the keyring, and runs the client-side crypto.",[937,11624,11625,11626,10313,11629,11631,11632,11635,11636,997,11639,11642,11643,11646],{},"Every method on this page is on that instance and works when you pass ",[950,11627,11628],{},"zero strategies",[941,11630,2134],{}," — the strategy-free kernel surface. Keyring and secret primitives live on ",[1007,11633,69],{"href":11634},".\u002Fkeyring",", the session-auth ladder on ",[1007,11637,73],{"href":11638},".\u002Fauthentication",[1007,11640,77],{"href":11641},".\u002Frecovery","; anything that needs an opt-in strategy is listed in the ",[1007,11644,85],{"href":11645},".\u002Fstrategies"," directory.",[937,11648,11649,11650,11653],{},"The reference is organized into ",[950,11651,11652],{},"groups"," — opening and reaching vaults, events and the write queue, session lifecycle, and policy and gates — each opening with a short note on what unites its methods, then the individual methods. Two groups that lean on session infrastructure (multi-tab coordination, translator audit) sit at the end, marked for deeper coverage in a later pass.",[1022,11655,11657],{"id":11656},"opening-and-reaching-vaults","Opening and reaching vaults",[937,11659,11660,11661,11663,11664,11666],{},"These methods turn vault names into live ",[941,11662,94],{}," handles and let a session discover or fan out across the vaults it can reach. Reach for them first — everything else you do with ",[941,11665,6590],{}," happens through a vault you opened here.",[1591,11668,11670],{"id":11669},"openvault",[941,11671,1749],{},[2217,11673,11675],{"className":2219,"code":11674,"language":2221,"meta":1037,"style":1037},"openVault(\n  name: string,\n  opts?: {\n    locale?: string\n    create?: boolean\n    meta?: VaultMeta\n  }\n): Promise\u003CVault>\n",[941,11676,11677,11684,11691,11700,11709,11719,11729,11733],{"__ignoreMap":1037},[2225,11678,11679,11681],{"class":2227,"line":2228},[2225,11680,2847],{"class":2291},[2225,11682,11683],{"class":2239},"(\n",[2225,11685,11686,11689],{"class":2227,"line":1041},[2225,11687,11688],{"class":2239},"  name: string",[2225,11690,1707],{"class":2235},[2225,11692,11693,11696,11698],{"class":2227,"line":1038},[2225,11694,11695],{"class":2239},"  opts",[2225,11697,7011],{"class":2235},[2225,11699,2301],{"class":2235},[2225,11701,11702,11705,11707],{"class":2227,"line":2288},[2225,11703,11704],{"class":2239},"    locale?",[2225,11706,2311],{"class":2235},[2225,11708,7014],{"class":2239},[2225,11710,11711,11714,11716],{"class":2227,"line":2304},[2225,11712,11713],{"class":2239},"    create",[2225,11715,7011],{"class":2235},[2225,11717,11718],{"class":2239}," boolean\n",[2225,11720,11721,11724,11726],{"class":2227,"line":2356},[2225,11722,11723],{"class":2239},"    meta",[2225,11725,7011],{"class":2235},[2225,11727,11728],{"class":2239}," VaultMeta\n",[2225,11730,11731],{"class":2227,"line":2830},[2225,11732,7632],{"class":2235},[2225,11734,11735,11738,11741,11743,11745],{"class":2227,"line":2861},[2225,11736,11737],{"class":2239},"): ",[2225,11739,11740],{"class":6342},"Promise",[2225,11742,6474],{"class":2235},[2225,11744,94],{"class":2239},[2225,11746,7093],{"class":2235},[937,11748,11749,11750,11753,11754,1324,11757,11759,11760,1520],{},"Open a vault by name, creating it on first use, and cache it. The returned handle is a ",[1007,11751,94],{"href":11752},"..\u002Fvault\u002F",". Concurrent opens of the same name are coalesced onto ",[950,11755,11756],{},"one",[941,11758,94],{}," instance — without that convergence two callers racing past the cache miss would each build a vault with independent DEKs for the same store slice, and a record written through one would fail decryption through the other with a spurious ",[941,11761,11762],{},"TamperedError",[1111,11764,11765,11773,11783,11810],{},[1114,11766,11767,11769,11770,957],{},[941,11768,4914],{}," — the vault name; created on first use (see ",[941,11771,11772],{},"opts.create",[1114,11774,11775,11778,11779,11782],{},[941,11776,11777],{},"opts.locale"," — default locale for i18n \u002F dictKey field resolution, so you don't pass ",[941,11780,11781],{},"{ locale }"," on every read. Passing it against an already-open vault re-sets the cached vault's locale.",[1114,11784,11785,11787,11788,11791,11792,11795,11796,11798,11799,11802,11803,11806,11807,11809],{},[941,11786,11772],{}," — whether to self-provision on open. Default ",[941,11789,11790],{},"true",": opening a genuinely-new vault (no ",[941,11793,11794],{},"_keyring\u002F*"," yet) mints an owner keyring for the calling user; set ",[941,11797,2560],{}," to open existing-only, which throws ",[941,11800,11801],{},"NoAccessError"," when the caller has no keyring for the vault. Either way, opening a vault already held by ",[1092,11804,11805],{},"other"," principals throws ",[941,11808,11801],{}," — noy-db never self-provisions into someone else's vault.",[1114,11811,11812,964,11815,11818],{},[941,11813,11814],{},"opts.meta",[941,11816,11817],{},"VaultMeta"," (label, description); applied first-wins on the first open and ignored afterwards.",[1591,11820,11821],{"id":2694},[941,11822,11823],{},"vault()",[2217,11825,11827],{"className":2219,"code":11826,"language":2221,"meta":1037,"style":1037},"vault(name: string): Vault\n",[941,11828,11829],{"__ignoreMap":1037},[2225,11830,11831,11833],{"class":2227,"line":2228},[2225,11832,2694],{"class":2291},[2225,11834,11835],{"class":2239},"(name: string): Vault\n",[937,11837,11838,11839,11841,11842,1520],{},"Synchronous access to a vault. Call ",[941,11840,1749],{}," first; if the vault isn't cached this auto-opens it. Use it when you've already opened the vault and want the handle without ",[941,11843,6500],{},[1111,11845,11846],{},[1114,11847,11848,11850],{},[941,11849,4914],{}," — the vault to return; auto-opens it if not already cached.",[1591,11852,11854],{"id":11853},"listaccessiblevaults",[941,11855,7941],{},[2217,11857,11859],{"className":2219,"code":11858,"language":2221,"meta":1037,"style":1037},"listAccessibleVaults(\n  options?: {\n    minRole?: Role\n  }\n): Promise\u003CAccessibleVault[]>\n",[941,11860,11861,11868,11877,11887,11891],{"__ignoreMap":1037},[2225,11862,11863,11866],{"class":2227,"line":2228},[2225,11864,11865],{"class":2291},"listAccessibleVaults",[2225,11867,11683],{"class":2239},[2225,11869,11870,11873,11875],{"class":2227,"line":1041},[2225,11871,11872],{"class":2239},"  options",[2225,11874,7011],{"class":2235},[2225,11876,2301],{"class":2235},[2225,11878,11879,11882,11884],{"class":2227,"line":1038},[2225,11880,11881],{"class":2239},"    minRole?",[2225,11883,2311],{"class":2235},[2225,11885,11886],{"class":2239}," Role\n",[2225,11888,11889],{"class":2227,"line":2288},[2225,11890,7632],{"class":2235},[2225,11892,11893,11895,11897,11899,11902],{"class":2227,"line":2304},[2225,11894,11737],{"class":2239},[2225,11896,11740],{"class":6342},[2225,11898,6474],{"class":2235},[2225,11900,11901],{"class":2239},"AccessibleVault[]",[2225,11903,7093],{"class":2235},[937,11905,11906,11907,11909,11910,11912,11913,11916],{},"Enumerate every vault the calling principal can actually unwrap, optionally filtered by a minimum role. The walk is two steps: ask the store for the universe of vaults it holds, then for each one attempt to load the caller's keyring with the in-memory secret — vaults where no keyring exists (",[941,11908,11801],{},") or the secret doesn't unwrap (",[941,11911,10290],{},") are silently dropped, so the ",[950,11914,11915],{},"return value never confirms the existence"," of a vault the caller cannot open. That is the existence-leak boundary: the store sees the enumeration (it owns the storage), but downstream consumers of this method see only the filtered list.",[937,11918,11919,11920,11922,11923,11925,11926,11929,11930,11933],{},"Requires the store's optional ",[941,11921,7935],{}," capability; against a store without it (e.g. DynamoDB, S3, browser-local, browser-idb) it throws ",[941,11924,7946],{},", and you should pass an explicit candidate list to ",[941,11927,11928],{},"queryAcross()"," or keep a vault index out of band. Cost is O(vaults × keyring-load), so cache the result for large universes and refresh on grant\u002Frevoke. One honest edge case: a vault whose keyring has an empty wrapped-DEK map (access granted before any collection existed) passes the load probe under ",[1092,11931,11932],{},"any"," secret, so an unrelated principal who knows the user-id and vault name can surface as having access to that empty vault — a metadata leak (vault name + user-id), never a content leak, since their DEK set is empty.",[1111,11935,11936],{},[1114,11937,11938,11941,11942,11945,11946,11948],{},[941,11939,11940],{},"options.minRole"," — include only vaults where the caller holds at least this role (ordering ",[941,11943,11944],{},"client \u003C viewer \u003C operator \u003C admin \u003C owner","); defaults to ",[941,11947,9913],{},", i.e. every vault the caller can unwrap.",[1591,11950,11952],{"id":11951},"queryacross",[941,11953,11928],{},[2217,11955,11957],{"className":2219,"code":11956,"language":2221,"meta":1037,"style":1037},"queryAcross\u003CT>(\n  vaultIds: string[],\n  fn: (vault: Vault) => Promise\u003CT>,\n  options?: {\n    concurrency?: number\n  }\n): Promise\u003CQueryAcrossResult\u003CT>[]>\n",[941,11958,11959,11973,11980,12007,12015,12025,12029],{"__ignoreMap":1037},[2225,11960,11961,11964,11966,11969,11971],{"class":2227,"line":2228},[2225,11962,11963],{"class":2291},"queryAcross",[2225,11965,6474],{"class":2235},[2225,11967,11968],{"class":6342},"T",[2225,11970,6480],{"class":2235},[2225,11972,11683],{"class":2239},[2225,11974,11975,11978],{"class":2227,"line":1041},[2225,11976,11977],{"class":2239},"  vaultIds: string[]",[2225,11979,1707],{"class":2235},[2225,11981,11982,11985,11987,11989,11991,11994,11996,11998,12000,12002,12004],{"class":2227,"line":1038},[2225,11983,11984],{"class":2239},"  fn: ",[2225,11986,2789],{"class":2235},[2225,11988,2694],{"class":7052},[2225,11990,2311],{"class":2235},[2225,11992,11993],{"class":6342}," Vault",[2225,11995,1381],{"class":2235},[2225,11997,8116],{"class":2774},[2225,11999,7079],{"class":6342},[2225,12001,6474],{"class":2235},[2225,12003,11968],{"class":2239},[2225,12005,12006],{"class":2235},">,\n",[2225,12008,12009,12011,12013],{"class":2227,"line":2288},[2225,12010,11872],{"class":2239},[2225,12012,7011],{"class":2235},[2225,12014,2301],{"class":2235},[2225,12016,12017,12020,12022],{"class":2227,"line":2304},[2225,12018,12019],{"class":2239},"    concurrency?",[2225,12021,2311],{"class":2235},[2225,12023,12024],{"class":2239}," number\n",[2225,12026,12027],{"class":2227,"line":2356},[2225,12028,7632],{"class":2235},[2225,12030,12031,12033,12035,12037,12040,12042,12044,12046,12048],{"class":2227,"line":2830},[2225,12032,11737],{"class":2239},[2225,12034,11740],{"class":6342},[2225,12036,6474],{"class":2235},[2225,12038,12039],{"class":2239},"QueryAcrossResult",[2225,12041,6474],{"class":2235},[2225,12043,11968],{"class":2239},[2225,12045,6480],{"class":2235},[2225,12047,7262],{"class":2239},[2225,12049,7093],{"class":2235},[937,12051,12052],{},"Run a per-vault callback against a list of vaults and collect the results — pure orchestration, no new crypto, sync, or authorization. A per-vault failure never aborts the fan-out, so one tenant's outage can't hide the others.",[1111,12054,12055,12066,12079],{},[1114,12056,12057,12060,12061,12063,12064,957],{},[941,12058,12059],{},"vaultIds"," — the vaults to fan out across; each is opened via the normal ",[941,12062,1749],{}," path (honoring any cache primed by ",[941,12065,11865],{},[1114,12067,12068,12071,12072,12074,12075,12078],{},[941,12069,12070],{},"fn"," — the per-vault callback, run against the resulting ",[941,12073,94],{},"; its return value ",[950,12076,12077],{},"or the error it throws"," is captured into that vault's result slot.",[1114,12080,12081,12084,12085,12087],{},[941,12082,12083],{},"options.concurrency"," — how many vaults to process in parallel; default ",[941,12086,5437],{}," (sequential). Bump it to 4–8 for cloud-backed stores where parallelism is the point, but beware exhausting adapter connection limits.",[1022,12089,12091],{"id":12090},"events-and-the-write-queue","Events and the write queue",[937,12093,12094],{},"This group is how you observe and hook a session's write activity: subscribe to typed session events, watch the in-flight write queue, run before\u002Fafter hooks, or reconcile a cross-tab write conflict. Reach for it when the UI needs to react to writes — a save indicator, an unload guard, or a conflict prompt.",[937,12096,12097,12098,12101,12102,2311],{},"The strategy-free session emits these ",[950,12099,12100],{},"core events"," over ",[941,12103,12104],{},"NoydbEventMap",[1111,12106,12107,12119,12128,12141],{},[1114,12108,12109,964,12112,953,12115,12118],{},[941,12110,12111],{},"change",[941,12113,12114],{},"ChangeEvent",[941,12116,12117],{},"{ vault, collection, id, action: 'put' | 'delete' }","). Fires on a completed record write in a vault.",[1114,12120,12121,964,12124,12127],{},[941,12122,12123],{},"error",[941,12125,12126],{},"Error",". A session-level error signal.",[1114,12129,12130,964,12133,12136,12137,12140],{},[941,12131,12132],{},"write:conflict",[941,12134,12135],{},"WriteConflict",". Fires when another tab overwrote a document this tab had written from an older base (the same event ",[941,12138,12139],{},"onWriteConflict()"," wraps); delivered decrypted.",[1114,12142,12143,964,12146,12149],{},[941,12144,12145],{},"schema:fence-changed",[941,12147,12148],{},"{ vault, currentSchemaVersion, fenceState: 'normal' | 'draining' | 'migrating' | 'complete' }",". A same-instance signal that a vault's schema-fence state changed, for UI integration; cross-client coordination goes through the store, not this event.",[937,12151,12152,12153,12156,12157,12160,12161,968,12164,1261,12167,2462,12170,12173],{},"Opt-in subsystems extend the map with their own families — ",[941,12154,12155],{},"sync:*"," (with sync targets), ",[941,12158,12159],{},"history:*"," (with history), ",[941,12162,12163],{},"index:*",[941,12165,12166],{},"derivation:*",[941,12168,12169],{},"lookup:*",[941,12171,12172],{},"i18n:script-violation"," — each documented on its own service page; they don't fire on the strategy-free surface.",[937,12175,12176,12179,12180,2085,12186,12189,12190,12193,12194,12196,12197,12199,12200,12206,12207,12213,12214,12216,12217,1261,12219,1952,12221,953,12224,12226,12227,12230],{},[950,12177,12178],{},"How they interact."," A record write is tracked on the ",[1007,12181,12183],{"href":12182},"#writequeue",[941,12184,12185],{},"writeQueue",[941,12187,12188],{},"writeQueue.depth"," increments as the write begins (so ",[941,12191,12192],{},"pending"," becomes ",[941,12195,11790],{},") and decrements when it settles, on success ",[1092,12198,7493],{}," failure. Around the write, ",[1007,12201,12203],{"href":12202},"#onbeforewrite",[941,12204,12205],{},"onBeforeWrite"," hooks run first — awaited in registration order, and a throw aborts the write so nothing commits; after the write commits, ",[1007,12208,12210],{"href":12209},"#onafterwrite",[941,12211,12212],{},"onAfterWrite"," hooks run — awaited in order, with a handler error warned but never rolled back. Writes issued from inside a hook are suppressed by a re-entrancy guard so a writing handler can't loop. A ",[941,12215,12111],{}," event signals a completed ",[941,12218,3161],{},[941,12220,3164],{},[941,12222,12223],{},"onWriteConflict",[941,12225,12132],{},") sits ",[1092,12228,12229],{},"outside"," this single-write sequence: it is the same-device cross-tab reconciliation signal, not a stage of one write's before\u002Fcommit\u002Fafter path.",[1591,12232,12233],{"id":4883},[941,12234,12235],{},"on()",[2217,12237,12239],{"className":2219,"code":12238,"language":2221,"meta":1037,"style":1037},"on\u003CK extends keyof NoydbEventMap>(\n  event: K,\n  handler: (data: NoydbEventMap[K]) => void\n): void\n",[941,12240,12241,12254,12261,12289],{"__ignoreMap":1037},[2225,12242,12243,12245,12247,12250,12252],{"class":2227,"line":2228},[2225,12244,4883],{"class":2239},[2225,12246,6474],{"class":2235},[2225,12248,12249],{"class":2239},"K extends keyof NoydbEventMap",[2225,12251,6480],{"class":2235},[2225,12253,11683],{"class":2239},[2225,12255,12256,12259],{"class":2227,"line":1041},[2225,12257,12258],{"class":2239},"  event: K",[2225,12260,1707],{"class":2235},[2225,12262,12263,12266,12268,12270,12272,12275,12277,12280,12282,12284,12286],{"class":2227,"line":1038},[2225,12264,12265],{"class":2239},"  handler: ",[2225,12267,2789],{"class":2235},[2225,12269,8673],{"class":7052},[2225,12271,2311],{"class":2235},[2225,12273,12274],{"class":6342}," NoydbEventMap",[2225,12276,8885],{"class":2239},[2225,12278,12279],{"class":6342},"K",[2225,12281,8803],{"class":2239},[2225,12283,1381],{"class":2235},[2225,12285,8116],{"class":2774},[2225,12287,12288],{"class":2235}," void\n",[2225,12290,12291,12293],{"class":2227,"line":2288},[2225,12292,11737],{"class":2239},[2225,12294,12295],{"class":2235},"void\n",[937,12297,12298,12299,1520],{},"Subscribe to session events over the typed ",[941,12300,12104],{},[1111,12302,12303,12312],{},[1114,12304,12305,12308,12309,12311],{},[941,12306,12307],{},"event"," — a key of ",[941,12310,12104],{}," (see the core-event catalog above).",[1114,12313,12314,12317],{},[941,12315,12316],{},"handler"," — called with that event's typed payload each time the event fires.",[1591,12319,12321],{"id":12320},"off",[941,12322,12323],{},"off()",[2217,12325,12327],{"className":2219,"code":12326,"language":2221,"meta":1037,"style":1037},"off\u003CK extends keyof NoydbEventMap>(\n  event: K,\n  handler: (data: NoydbEventMap[K]) => void\n): void\n",[941,12328,12329,12341,12347,12371],{"__ignoreMap":1037},[2225,12330,12331,12333,12335,12337,12339],{"class":2227,"line":2228},[2225,12332,12320],{"class":2239},[2225,12334,6474],{"class":2235},[2225,12336,12249],{"class":2239},[2225,12338,6480],{"class":2235},[2225,12340,11683],{"class":2239},[2225,12342,12343,12345],{"class":2227,"line":1041},[2225,12344,12258],{"class":2239},[2225,12346,1707],{"class":2235},[2225,12348,12349,12351,12353,12355,12357,12359,12361,12363,12365,12367,12369],{"class":2227,"line":1038},[2225,12350,12265],{"class":2239},[2225,12352,2789],{"class":2235},[2225,12354,8673],{"class":7052},[2225,12356,2311],{"class":2235},[2225,12358,12274],{"class":6342},[2225,12360,8885],{"class":2239},[2225,12362,12279],{"class":6342},[2225,12364,8803],{"class":2239},[2225,12366,1381],{"class":2235},[2225,12368,8116],{"class":2774},[2225,12370,12288],{"class":2235},[2225,12372,12373,12375],{"class":2227,"line":2288},[2225,12374,11737],{"class":2239},[2225,12376,12295],{"class":2235},[937,12378,12379,12380,12382],{},"Unsubscribe a handler previously registered with ",[941,12381,12235],{}," for the same event.",[1111,12384,12385,12390],{},[1114,12386,12387,12389],{},[941,12388,12307],{}," — the event key the handler was registered for.",[1114,12391,12392,12394,12395,12397],{},[941,12393,12316],{}," — the exact function reference passed to ",[941,12396,12235],{},"; identity-matched, so pass the same reference you subscribed with.",[1591,12399,12401],{"id":12400},"writequeue",[941,12402,12185],{},[2217,12404,12406],{"className":2219,"code":12405,"language":2221,"meta":1037,"style":1037},"get writeQueue(): WriteQueue\n",[941,12407,12408],{"__ignoreMap":1037},[2225,12409,12410,12412,12414],{"class":2227,"line":2228},[2225,12411,4978],{"class":2239},[2225,12413,12185],{"class":2291},[2225,12415,12416],{"class":2239},"(): WriteQueue\n",[937,12418,12419],{},"An observable write-queue reflecting outstanding in-flight writes across every collection. The canonical use is guarding an unload:",[2217,12421,12423],{"className":2219,"code":12422,"language":2221,"meta":1037,"style":1037},"window.addEventListener('beforeunload', (e) => {\n  if (db.writeQueue.pending) { e.preventDefault(); e.returnValue = '' }\n})\n",[941,12424,12425,12457,12503],{"__ignoreMap":1037},[2225,12426,12427,12430,12432,12435,12437,12439,12442,12444,12446,12448,12451,12453,12455],{"class":2227,"line":2228},[2225,12428,12429],{"class":2239},"window",[2225,12431,1520],{"class":2235},[2225,12433,12434],{"class":2291},"addEventListener",[2225,12436,2789],{"class":2239},[2225,12438,2805],{"class":2235},[2225,12440,12441],{"class":2252},"beforeunload",[2225,12443,2805],{"class":2235},[2225,12445,2298],{"class":2235},[2225,12447,953],{"class":2235},[2225,12449,12450],{"class":7052},"e",[2225,12452,1381],{"class":2235},[2225,12454,8116],{"class":2774},[2225,12456,2301],{"class":2235},[2225,12458,12459,12461,12463,12465,12467,12469,12471,12473,12475,12477,12480,12482,12485,12487,12489,12491,12493,12496,12498,12501],{"class":2227,"line":1041},[2225,12460,7608],{"class":2231},[2225,12462,953],{"class":2307},[2225,12464,6590],{"class":2239},[2225,12466,1520],{"class":2235},[2225,12468,12185],{"class":2239},[2225,12470,1520],{"class":2235},[2225,12472,12192],{"class":2239},[2225,12474,8495],{"class":2307},[2225,12476,2792],{"class":2235},[2225,12478,12479],{"class":2239}," e",[2225,12481,1520],{"class":2235},[2225,12483,12484],{"class":2291},"preventDefault",[2225,12486,4228],{"class":2307},[2225,12488,3769],{"class":2235},[2225,12490,12479],{"class":2239},[2225,12492,1520],{"class":2235},[2225,12494,12495],{"class":2239},"returnValue",[2225,12497,6346],{"class":2235},[2225,12499,12500],{"class":2235}," ''",[2225,12502,6369],{"class":2235},[2225,12504,12505,12507],{"class":2227,"line":1038},[2225,12506,2359],{"class":2235},[2225,12508,2362],{"class":2239},[1591,12510,12512],{"id":12511},"onbeforewrite",[941,12513,12514],{},"onBeforeWrite()",[2217,12516,12518],{"className":2219,"code":12517,"language":2221,"meta":1037,"style":1037},"onBeforeWrite(handler: WriteHook): Unsubscribe\n",[941,12519,12520],{"__ignoreMap":1037},[2225,12521,12522,12524],{"class":2227,"line":2228},[2225,12523,12205],{"class":2291},[2225,12525,12526],{"class":2239},"(handler: WriteHook): Unsubscribe\n",[937,12528,12529],{},"Register a pre-write hook. Returns an unsubscribe function.",[1111,12531,12532],{},[1114,12533,12534,12536,12537,12540,12541,1520],{},[941,12535,12316],{}," — a ",[941,12538,12539],{},"WriteHook",", awaited before each write; ",[950,12542,12543],{},"a throw aborts the write",[1591,12545,12547],{"id":12546},"onafterwrite",[941,12548,12549],{},"onAfterWrite()",[2217,12551,12553],{"className":2219,"code":12552,"language":2221,"meta":1037,"style":1037},"onAfterWrite(handler: WriteHook): Unsubscribe\n",[941,12554,12555],{"__ignoreMap":1037},[2225,12556,12557,12559],{"class":2227,"line":2228},[2225,12558,12212],{"class":2291},[2225,12560,12526],{"class":2239},[937,12562,12563],{},"Register a post-write hook. Returns an unsubscribe function.",[1111,12565,12566],{},[1114,12567,12568,12536,12570,12572],{},[941,12569,12316],{},[941,12571,12539],{},", awaited after each committed write; a handler error is only warned — never rolled back.",[937,12574,12575,12576,964,12579,12582,12583,1625,12585,12587,12588,7356,12590,12592,12593,12595,12596,12599,12600,12603,12604,12606],{},"Both hooks receive a ",[941,12577,12578],{},"WriteEvent",[941,12580,12581],{},"{ op, vault, collection, docId, before, after, baseVersion, version, userId, timestamp, txId }",", with ",[941,12584,11357],{},[941,12586,10761],{}," delivered decrypted (",[941,12589,11357],{},[941,12591,6651],{}," on a create, ",[941,12594,10761],{}," on a delete). From ",[941,12597,12598],{},"0.6.0-pre.4"," it is exported ",[950,12601,12602],{},"by name"," from both the root barrel and ",[941,12605,1220],{},", so a named handler no longer has to derive it structurally:",[2217,12608,12610],{"className":2219,"code":12609,"language":2221,"meta":1037,"style":1037},"import type { WriteEvent } from '@noy-db\u002Fhub'\n\nfunction audit(event: WriteEvent) { \u002F* … *\u002F }\ndb.onAfterWrite(audit)\n",[941,12611,12612,12633,12637,12661],{"__ignoreMap":1037},[2225,12613,12614,12616,12618,12620,12623,12625,12627,12629,12631],{"class":2227,"line":2228},[2225,12615,2232],{"class":2231},[2225,12617,3399],{"class":2231},[2225,12619,2236],{"class":2235},[2225,12621,12622],{"class":2239}," WriteEvent",[2225,12624,2243],{"class":2235},[2225,12626,2246],{"class":2231},[2225,12628,2249],{"class":2235},[2225,12630,1086],{"class":2252},[2225,12632,2256],{"class":2235},[2225,12634,12635],{"class":2227,"line":1041},[2225,12636,2285],{"emptyLinePlaceholder":2284},[2225,12638,12639,12642,12645,12647,12649,12651,12653,12655,12657,12659],{"class":2227,"line":1038},[2225,12640,12641],{"class":2774},"function",[2225,12643,12644],{"class":2291}," audit",[2225,12646,2789],{"class":2235},[2225,12648,12307],{"class":7052},[2225,12650,2311],{"class":2235},[2225,12652,12622],{"class":6342},[2225,12654,1381],{"class":2235},[2225,12656,2236],{"class":2235},[2225,12658,10968],{"class":2763},[2225,12660,6369],{"class":2235},[2225,12662,12663,12665,12667,12669],{"class":2227,"line":2288},[2225,12664,6590],{"class":2239},[2225,12666,1520],{"class":2235},[2225,12668,12212],{"class":2291},[2225,12670,12671],{"class":2239},"(audit)\n",[937,12673,12674,12675,12677,12678,12680,12681,12684],{},"Before that release ",[941,12676,3714],{}," published ",[941,12679,12539],{}," but not the event it carries, so the only ways to name it were ",[941,12682,12683],{},"Parameters\u003CWriteHook>[0]"," or a hub-internal import path.",[1591,12686,12688],{"id":12687},"onwriteconflict",[941,12689,12139],{},[2217,12691,12693],{"className":2219,"code":12692,"language":2221,"meta":1037,"style":1037},"onWriteConflict(fn: (c: WriteConflict) => void): Unsubscribe\n",[941,12694,12695],{"__ignoreMap":1037},[2225,12696,12697,12699,12702,12704,12706,12708,12711,12713,12715,12718],{"class":2227,"line":2228},[2225,12698,12223],{"class":2291},[2225,12700,12701],{"class":2239},"(fn: ",[2225,12703,2789],{"class":2235},[2225,12705,8200],{"class":7052},[2225,12707,2311],{"class":2235},[2225,12709,12710],{"class":6342}," WriteConflict",[2225,12712,1381],{"class":2235},[2225,12714,8116],{"class":2774},[2225,12716,12717],{"class":2235}," void",[2225,12719,12720],{"class":2239},"): Unsubscribe\n",[937,12722,12723],{},"Subscribe to same-device cross-tab write conflicts (another tab overwrote a document this tab wrote, having diverged from an older base). Returns an unsubscribe.",[1111,12725,12726],{},[1114,12727,12728,12730,12731,12733],{},[941,12729,12070],{}," — called with each ",[941,12732,12135],{},", delivered decrypted so the handler can reconcile in plaintext.",[1022,12735,12737],{"id":12736},"session-lifecycle","Session lifecycle",[937,12739,12740,12741,12744,12745,12747],{},"These two methods suspend or end the unlocked session. Reach for ",[941,12742,12743],{},"lockVault"," to build a lock screen that scrubs a vault's keys but keeps the instance re-unlockable, and ",[941,12746,6595],{}," to tear the whole session down.",[1591,12749,12751],{"id":12750},"lockvault",[941,12752,12753],{},"lockVault()",[2217,12755,12757],{"className":2219,"code":12756,"language":2221,"meta":1037,"style":1037},"lockVault(vault: string): void\n",[941,12758,12759],{"__ignoreMap":1037},[2225,12760,12761,12763,12766],{"class":2227,"line":2228},[2225,12762,12743],{"class":2291},[2225,12764,12765],{"class":2239},"(vault: string): ",[2225,12767,12295],{"class":2235},[937,12769,12770,12771,12774,12775,12777,12778,12781,12782,12785],{},"Soft-lock a single vault: clear its in-memory keyring, DEKs, vault instance, sync engine, policy enforcer, and active-tier entry ",[950,12772,12773],{},"without"," destroying the ",[941,12776,62],{}," instance. Built for a \"lock screen\" — DEKs are scrubbed immediately, but the same instance can be re-unlocked (via a tier-2 authenticator or tier-3 PIN) without re-running ",[941,12779,12780],{},"createNoydb",". Quick-unlock state and the cached ",[941,12783,12784],{},"_meta\u002Fpolicy"," document are deliberately preserved.",[1111,12787,12788],{},[1114,12789,12790,12792],{},[941,12791,2694],{}," — the vault to soft-lock; a no-op when it isn't currently cached.",[1591,12794,12795],{"id":6595},[941,12796,12797],{},"close()",[2217,12799,12801],{"className":2219,"code":12800,"language":2221,"meta":1037,"style":1037},"close(): void\n",[941,12802,12803],{"__ignoreMap":1037},[2225,12804,12805,12807,12810],{"class":2227,"line":2228},[2225,12806,6595],{"class":2291},[2225,12808,12809],{"class":2239},"(): ",[2225,12811,12295],{"class":2235},[937,12813,12814],{},"End the session: clear the KEK, all DEKs, and the vault caches, and stop sync engines and timers. The in-process translation cache and the translator audit log are cleared here too.",[1022,12816,12818],{"id":12817},"policy-and-gates","Policy and gates",[937,12820,3289,12821,12823,12824,12827,12828,12831,12832,12835,12836,12838,12839,997,12842,12845],{},[950,12822,796],{}," is a vault's rulebook of named ",[950,12825,12826],{},"gates"," — one rule per sensitive operation (rotating the secret, exporting plaintext, enrolling an authenticator, granting a user) declaring the minimum session tier and any extra factor proofs a caller must present before that operation is allowed to run. Every vault has a policy from the moment it is created; if you pass none, noy-db persists a sensible default (",[941,12829,12830],{},"PERSONAL_POLICY","), while ",[941,12833,12834],{},"STRICT_POLICY"," is the hardened preset for regulated deployments and shared workstations. The policy is per-vault, persisted once at ",[941,12837,12784],{},", and the same gate-check enforcement backs the session, tiers, and team services. The methods below read and replace that policy and let you pre-flight a gate yourself; see ",[1007,12840,796],{"href":12841},"..\u002F..\u002Fservices\u002Fpolicy",[1007,12843,844],{"href":12844},"..\u002F..\u002Fservices\u002Fsession-tiers"," for the full gate DSL.",[1591,12847,12849],{"id":12848},"getpolicy",[941,12850,12851],{},"getPolicy()",[2217,12853,12855],{"className":2219,"code":12854,"language":2221,"meta":1037,"style":1037},"getPolicy(vault: string): Promise\u003CVaultPolicy>\n",[941,12856,12857],{"__ignoreMap":1037},[2225,12858,12859,12862,12864,12866,12868,12871],{"class":2227,"line":2228},[2225,12860,12861],{"class":2291},"getPolicy",[2225,12863,12765],{"class":2239},[2225,12865,11740],{"class":6342},[2225,12867,6474],{"class":2235},[2225,12869,12870],{"class":2239},"VaultPolicy",[2225,12872,7093],{"class":2235},[937,12874,12875,12876,12878,12879,12882],{},"Read the active policy for a vault — loaded from the ",[941,12877,12784],{}," document on first call, then served from an in-memory cache. Throws ",[941,12880,12881],{},"ValidationError"," if the vault has not been opened.",[1111,12884,12885],{},[1114,12886,12887,12889],{},[941,12888,2694],{}," — the vault whose active policy to read; must already be opened.",[1591,12891,12893],{"id":12892},"updatepolicy",[941,12894,12895],{},"updatePolicy()",[2217,12897,12899],{"className":2219,"code":12898,"language":2221,"meta":1037,"style":1037},"updatePolicy(\n  vault: string,\n  override: Partial\u003CVaultPolicy>\n): Promise\u003CVaultPolicy>\n",[941,12900,12901,12908,12915,12926],{"__ignoreMap":1037},[2225,12902,12903,12906],{"class":2227,"line":2228},[2225,12904,12905],{"class":2291},"updatePolicy",[2225,12907,11683],{"class":2239},[2225,12909,12910,12913],{"class":2227,"line":1041},[2225,12911,12912],{"class":2239},"  vault: string",[2225,12914,1707],{"class":2235},[2225,12916,12917,12920,12922,12924],{"class":2227,"line":1038},[2225,12918,12919],{"class":2239},"  override: Partial",[2225,12921,6474],{"class":2235},[2225,12923,12870],{"class":2239},[2225,12925,7093],{"class":2235},[2225,12927,12928,12930,12932,12934,12936],{"class":2227,"line":2288},[2225,12929,11737],{"class":2239},[2225,12931,11740],{"class":6342},[2225,12933,6474],{"class":2235},[2225,12935,12870],{"class":2239},[2225,12937,7093],{"class":2235},[937,12939,12940,12941,12943,12944,12947],{},"Replace the persisted ",[941,12942,12784],{}," document and refresh the cache. Because changing a policy is itself a privilege-management action, it is gated by the ",[941,12945,12946],{},"enroll-user"," policy.",[1111,12949,12950,12958],{},[1114,12951,12952,12954,12955,12957],{},[941,12953,2694],{}," — the vault whose ",[941,12956,12784],{}," document to replace.",[1114,12959,12960,12536,12963,12966],{},[941,12961,12962],{},"override",[941,12964,12965],{},"Partial\u003CVaultPolicy>"," that becomes the new persisted policy.",[1591,12968,12970],{"id":12969},"checkgate",[941,12971,12972],{},"checkGate()",[2217,12974,12976],{"className":2219,"code":12975,"language":2221,"meta":1037,"style":1037},"checkGate(\n  vault: string,\n  gate: GateName,\n  factors?: FactorProofBundle\n): Promise\u003Cvoid>\n",[941,12977,12978,12985,12991,12998,13008],{"__ignoreMap":1037},[2225,12979,12980,12983],{"class":2227,"line":2228},[2225,12981,12982],{"class":2291},"checkGate",[2225,12984,11683],{"class":2239},[2225,12986,12987,12989],{"class":2227,"line":1041},[2225,12988,12912],{"class":2239},[2225,12990,1707],{"class":2235},[2225,12992,12993,12996],{"class":2227,"line":1038},[2225,12994,12995],{"class":2239},"  gate: GateName",[2225,12997,1707],{"class":2235},[2225,12999,13000,13003,13005],{"class":2227,"line":2288},[2225,13001,13002],{"class":2239},"  factors",[2225,13004,7011],{"class":2235},[2225,13006,13007],{"class":2239}," FactorProofBundle\n",[2225,13009,13010,13012,13014],{"class":2227,"line":2304},[2225,13011,11737],{"class":2239},[2225,13013,11740],{"class":6342},[2225,13015,13016],{"class":2235},"\u003Cvoid>\n",[937,13018,13019,13020,13022,13023,13026],{},"Evaluate a policy gate against the active session tier and the presented factor proofs. Resolves with ",[941,13021,7165],{}," on success; throws ",[941,13024,13025],{},"PolicyDeniedError"," on denial.",[1111,13028,13029,13034,13047],{},[1114,13030,13031,13033],{},[941,13032,2694],{}," — the vault whose policy applies.",[1114,13035,13036,13038,13039,13042,13043,13046],{},[941,13037,2723],{}," — the gate to evaluate: a built-in name (e.g. ",[941,13040,13041],{},"'rotate-secret'",") or an app-defined ",[941,13044,13045],{},"app:*"," gate.",[1114,13048,13049,13052],{},[941,13050,13051],{},"factors"," — the caller-supplied factor proofs presented for the gate.",[1022,13054,13056],{"id":13055},"public-envelope","Public envelope",[937,13058,13059,1261,13062,13065,13066,13070,13071,13073],{},[941,13060,13061],{},"getCover",[941,13063,13064],{},"setCover"," — the owner-curated, deliberately-plaintext vault descriptor — are documented on the ",[1007,13067,13069],{"href":13068},"..\u002F..\u002Fservices\u002Fcover","cover service page",", alongside the ",[941,13072,645],{}," config opt-in.",[1022,13075,13077],{"id":13076},"multi-tab-coordination","Multi-tab coordination",[937,13079,13080],{},[1092,13081,13082],{},"Deferred for deeper coverage — multi-tab coordination is session infrastructure that will move out of the kernel reference in a future pass.",[937,13084,13085,13086,13089],{},"Browser-only; a graceful no-op (role ",[941,13087,13088],{},"'unknown'",") when Web Locks \u002F BroadcastChannel are unavailable and nothing is injected.",[1591,13091,13093],{"id":13092},"enabletabcoordination",[941,13094,13095],{},"enableTabCoordination()",[2217,13097,13099],{"className":2219,"code":13098,"language":2221,"meta":1037,"style":1037},"enableTabCoordination(\n  opts?: TabCoordinationOptions\n): { dispose: () => void }\n",[941,13100,13101,13108,13117],{"__ignoreMap":1037},[2225,13102,13103,13106],{"class":2227,"line":2228},[2225,13104,13105],{"class":2291},"enableTabCoordination",[2225,13107,11683],{"class":2239},[2225,13109,13110,13112,13114],{"class":2227,"line":1041},[2225,13111,11695],{"class":2239},[2225,13113,7011],{"class":2235},[2225,13115,13116],{"class":2239}," TabCoordinationOptions\n",[2225,13118,13119,13121,13123,13126,13128,13131,13133,13135],{"class":2227,"line":1038},[2225,13120,11737],{"class":2239},[2225,13122,2792],{"class":2235},[2225,13124,13125],{"class":2291}," dispose",[2225,13127,2311],{"class":2235},[2225,13129,13130],{"class":2235}," ()",[2225,13132,8116],{"class":2774},[2225,13134,12717],{"class":2235},[2225,13136,6369],{"class":2235},[937,13138,13139],{},"Enable same-device multi-tab coordination — primary \u002F secondary election plus presence. Idempotent; returns a disposer that turns it back off.",[1111,13141,13142],{},[1114,13143,13144,964,13146,13149],{},[941,13145,8097],{},[941,13147,13148],{},"TabCoordinationOptions"," tuning election \u002F presence behavior.",[1591,13151,13153],{"id":13152},"tabrole",[941,13154,13155],{},"tabRole",[2217,13157,13159],{"className":2219,"code":13158,"language":2221,"meta":1037,"style":1037},"get tabRole(): TabRole\n",[941,13160,13161],{"__ignoreMap":1037},[2225,13162,13163,13165,13167],{"class":2227,"line":2228},[2225,13164,4978],{"class":2239},[2225,13166,13155],{"class":2291},[2225,13168,13169],{"class":2239},"(): TabRole\n",[937,13171,13172],{},"Read this tab's current coordination role.",[1591,13174,13176],{"id":13175},"activetabs",[941,13177,13178],{},"activeTabs()",[2217,13180,13182],{"className":2219,"code":13181,"language":2221,"meta":1037,"style":1037},"activeTabs(): TabPresence[]\n",[941,13183,13184],{"__ignoreMap":1037},[2225,13185,13186,13189],{"class":2227,"line":2228},[2225,13187,13188],{"class":2291},"activeTabs",[2225,13190,13191],{"class":2239},"(): TabPresence[]\n",[937,13193,13194],{},"List the presently-active tabs on this device.",[1591,13196,13198],{"id":13197},"ontabrolechange",[941,13199,13200],{},"onTabRoleChange()",[2217,13202,13204],{"className":2219,"code":13203,"language":2221,"meta":1037,"style":1037},"onTabRoleChange(fn: (r: TabRole) => void): Unsubscribe\n",[941,13205,13206],{"__ignoreMap":1037},[2225,13207,13208,13211,13213,13215,13218,13220,13223,13225,13227,13229],{"class":2227,"line":2228},[2225,13209,13210],{"class":2291},"onTabRoleChange",[2225,13212,12701],{"class":2239},[2225,13214,2789],{"class":2235},[2225,13216,13217],{"class":7052},"r",[2225,13219,2311],{"class":2235},[2225,13221,13222],{"class":6342}," TabRole",[2225,13224,1381],{"class":2235},[2225,13226,8116],{"class":2774},[2225,13228,12717],{"class":2235},[2225,13230,12720],{"class":2239},[937,13232,13233],{},"Subscribe to changes of this tab's role. Returns an unsubscribe.",[1111,13235,13236],{},[1114,13237,13238,13240],{},[941,13239,12070],{}," — called with this tab's new role on every change.",[1591,13242,13244],{"id":13243},"onactivetabschange",[941,13245,13246],{},"onActiveTabsChange()",[2217,13248,13250],{"className":2219,"code":13249,"language":2221,"meta":1037,"style":1037},"onActiveTabsChange(\n  fn: (t: TabPresence[]) => void\n): Unsubscribe\n",[941,13251,13252,13259,13281],{"__ignoreMap":1037},[2225,13253,13254,13257],{"class":2227,"line":2228},[2225,13255,13256],{"class":2291},"onActiveTabsChange",[2225,13258,11683],{"class":2239},[2225,13260,13261,13263,13265,13268,13270,13273,13275,13277,13279],{"class":2227,"line":1041},[2225,13262,11984],{"class":2239},[2225,13264,2789],{"class":2235},[2225,13266,13267],{"class":7052},"t",[2225,13269,2311],{"class":2235},[2225,13271,13272],{"class":6342}," TabPresence",[2225,13274,7262],{"class":2239},[2225,13276,1381],{"class":2235},[2225,13278,8116],{"class":2774},[2225,13280,12288],{"class":2235},[2225,13282,13283],{"class":2227,"line":1038},[2225,13284,12720],{"class":2239},[937,13286,13287],{},"Subscribe to changes in the active-tab presence list. Returns an unsubscribe.",[1111,13289,13290],{},[1114,13291,13292,13294],{},[941,13293,12070],{}," — called with the updated active-tab presence list on every change.",[1022,13296,13298],{"id":13297},"translator-audit","Translator audit",[937,13300,13301],{},[1092,13302,13303],{},"Deferred for deeper coverage — the translator audit log is plaintext-bypass instrumentation that will move out of the kernel reference in a future pass.",[1591,13305,13307],{"id":13306},"translatorauditlog",[941,13308,13309],{},"translatorAuditLog()",[2217,13311,13313],{"className":2219,"code":13312,"language":2221,"meta":1037,"style":1037},"translatorAuditLog(): readonly TranslatorAuditEntry[]\n",[941,13314,13315],{"__ignoreMap":1037},[2225,13316,13317,13320],{"class":2227,"line":2228},[2225,13318,13319],{"class":2291},"translatorAuditLog",[2225,13321,13322],{"class":2239},"(): readonly TranslatorAuditEntry[]\n",[937,13324,13325,13326,13328,13329,13332,13333,13336,13337,13340],{},"Return a snapshot of every translator invocation since the last ",[941,13327,12797],{}," — useful for testing and compliance auditing. The log is in-memory only and is cleared on ",[941,13330,13331],{},"db.close()",", and entries deliberately omit content hashes. It works with no strategy: with no ",[941,13334,13335],{},"plaintextTranslator"," configured (see ",[1007,13338,81],{"href":13339},".\u002Fconfiguration",") there are simply no entries. Configuring that translator is the one path by which plaintext can leave the zero-knowledge boundary, so this log exists to record when it did.",[2645,13342,13343],{},"html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sHdIc, html code.shiki .sHdIc{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#EEFFFF;--shiki-default-font-style:italic;--shiki-dark:#BABED8;--shiki-dark-font-style:italic}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}",{"title":1037,"searchDepth":1038,"depth":1038,"links":13345},[13346,13352,13360,13364,13369,13370,13377],{"id":11656,"depth":1041,"text":11657,"children":13347},[13348,13349,13350,13351],{"id":11669,"depth":1038,"text":1749},{"id":2694,"depth":1038,"text":11823},{"id":11853,"depth":1038,"text":7941},{"id":11951,"depth":1038,"text":11928},{"id":12090,"depth":1041,"text":12091,"children":13353},[13354,13355,13356,13357,13358,13359],{"id":4883,"depth":1038,"text":12235},{"id":12320,"depth":1038,"text":12323},{"id":12400,"depth":1038,"text":12185},{"id":12511,"depth":1038,"text":12514},{"id":12546,"depth":1038,"text":12549},{"id":12687,"depth":1038,"text":12139},{"id":12736,"depth":1041,"text":12737,"children":13361},[13362,13363],{"id":12750,"depth":1038,"text":12753},{"id":6595,"depth":1038,"text":12797},{"id":12817,"depth":1041,"text":12818,"children":13365},[13366,13367,13368],{"id":12848,"depth":1038,"text":12851},{"id":12892,"depth":1038,"text":12895},{"id":12969,"depth":1038,"text":12972},{"id":13055,"depth":1041,"text":13056},{"id":13076,"depth":1041,"text":13077,"children":13371},[13372,13373,13374,13375,13376],{"id":13092,"depth":1038,"text":13095},{"id":13152,"depth":1038,"text":13155},{"id":13175,"depth":1038,"text":13178},{"id":13197,"depth":1038,"text":13200},{"id":13243,"depth":1038,"text":13246},{"id":13297,"depth":1041,"text":13298,"children":13378},[13379],{"id":13306,"depth":1038,"text":13309},"The Noydb object reference: one unlocked session that opens vaults, hooks the write queue, runs policy gates, and manages the session lifecycle.",{},{"title":62,"description":13380},[645],"2j2bfnUofI_aQCxYsbqVMVDUb7xF-Zd-zsgv0b9cWRc",{"id":13386,"title":21,"access":12,"apiToc":929,"body":13387,"category":1042,"description":16140,"extension":1044,"meta":16141,"navigation":1042,"nest":1059,"path":66,"seo":16142,"sinceVersion":1042,"status":1049,"stem":67,"subtitle":1042,"tier":6922,"tocExternal":1042,"__hash__":16143},"docs\u002Fcore\u002F2.noydb\u002F2.enclave.md",{"type":931,"value":13388,"toc":16109},[13389,13391,13401,13423,13431,13435,13463,13466,13472,13503,13529,13546,13552,13598,13609,13662,13666,13680,13687,13690,13783,13786,13809,13813,13817,13829,13832,13878,13897,13901,13904,13999,14002,14080,14083,14144,14146,14151,14249,14259,14263,14284,14332,14335,14368,14389,14412,14415,14427,14536,14650,14661,14667,14697,14700,14735,14742,14746,14752,15101,15127,15131,15134,15360,15364,15367,15500,15504,15507,15513,15522,15528,15542,15678,15685,15722,15733,15837,15840,15867,15873,15877,15880,16049,16067,16069,16106],[934,13390,21],{"id":1059},[1061,13392,13393],{},[937,13394,13395,6235,13398],{},[950,13396,13397],{},"Always-on. The non-negotiable trust boundary.",[941,13399,13400],{},"packages\u002Fhub\u002Fsrc\u002Fcapsule\u002Fenclave-aes\u002Fcrypto.ts",[937,13402,5093,13403,13405,13406,13408,13409,13412,13413,13416,13417,13419,13420,13422],{},[950,13404,1059],{}," — the term Apple and others use for a restricted, trusted area of a system where privacy and security are structurally ensured — is a place where secrets may exist because nothing outside can reach in. noy-db has one, in software: the region inside ",[941,13407,1086],{}," where keys and plaintext exist ",[1092,13410,13411],{},"at all",". noy-db encrypts every record ",[950,13414,13415],{},"inside the client library, before it reaches any storage backend"," — the store (a file, a browser database, a cloud bucket) only ever sees ciphertext. The secret-derived KEK, every unwrapped DEK and CEK, and the cleartext of your records are materialized ",[1092,13418,1320],{}," in the hub's process memory, and only while a session is unlocked — ",[941,13421,13331],{}," scrubs them. The code that does this lives in a self-contained folder inside the hub's kernel that owns the keys, the crypto functions, and the encrypted data format.",[937,13424,13425,13426,13430],{},"This page walks the enclave from the outside in: the boundary and its two designed openings, the primitives, how a record is actually encrypted, the three extension layers (users, sealing, per-record keys), the envelope-slot reference and the key-rotation map, the invariants — and, at the very bottom, the dev-only ",[1007,13427,13429],{"href":13428},"#plaintext-modes","plaintext modes"," that switch the enclave off for local inspection.",[1022,13432,13434],{"id":13433},"the-enclave","The enclave",[937,13436,13437,13438,13440,13441,13443,13444,13446,13447,13449,13450,13452,13453,13455,13456,13458,13459,13462],{},"The far side of the boundary is everything the hub hands data to. The ",[950,13439,6951],{}," receives only the ciphertext envelope; it never sees plaintext, so it can never run your query — the query DSL lives inside the hub and runs ",[1092,13442,10761],{}," decryption. The same holds for every bridge package the family ships: the ",[941,13445,1779],{}," storage destinations, the ",[941,13448,1783],{}," framework bindings, the ",[941,13451,1787],{}," unlock methods, the ",[941,13454,1791],{}," exporters, and the ",[941,13457,1795],{}," session-share transports ",[950,13460,13461],{},"never see plaintext"," — the encryption happens in the core before data reaches any of them. Encrypt-before-store is not a policy the store cooperates with; it is structural, and the store stays a dumb, untrusted ciphertext shelf.",[937,13464,13465],{},"Everything is encrypted, then. But a vault nothing could ever leave would be a safe, not a database — so the boundary has exactly two designed openings, and both are deliberate.",[1591,13467,13469,13471],{"id":13468},"at-delegated-decryption",[941,13470,1139],{}," — delegated decryption",[937,13473,13474,13475,13477,13478,968,13482,968,13486,968,13490,968,13494,13498,13499,13502],{},"Sometimes the party that needs to read is not a human with a secret but a machine: a server-side job, a scheduled function, a serverless handler that must process a user's data while the user is asleep. The ",[941,13476,1139],{}," sealing-key hosts (",[1007,13479,13481],{"href":13480},"..\u002F..\u002Ffamilies\u002Fat\u002Fenv","at-env",[1007,13483,13485],{"href":13484},"..\u002F..\u002Ffamilies\u002Fat\u002Faws-kms","at-aws-kms",[1007,13487,13489],{"href":13488},"..\u002F..\u002Ffamilies\u002Fat\u002Fgcp-kms","at-gcp-kms",[1007,13491,13493],{"href":13492},"..\u002F..\u002Ffamilies\u002Fat\u002Fazure-keyvault","at-azure-keyvault",[1007,13495,13497],{"href":13496},"..\u002F..\u002Ffamilies\u002Fat\u002Fmacos-keychain","at-macos-keychain",") exist for exactly this case: a host ",[950,13500,13501],{},"you"," control holds a sealing key, and that key unseals what was explicitly sealed to it — with no human present to type a secret.",[937,13504,13505,13506,13509,13510,6030,13513,13516,13517,13509,13520,13523,13524,13528],{},"Be precise about what is unsealed, because there are two grants with very different scopes. A ",[950,13507,13508],{},"vault-scoped"," grant (",[941,13511,13512],{},"secretMode: 'managed'",[941,13514,13515],{},"sealingKey",") seals a machine-generated vault secret to the host, so the host can open — in full — exactly the vaults sealed under its key, and nothing else. A ",[950,13518,13519],{},"record-scoped",[1007,13521,824],{"href":13522},"..\u002F..\u002Fservices\u002Fsealed-record",") seals a single record's per-record key to the host, which can then decrypt exactly that one record, until an expiry, and nothing more. The mechanics of both live in ",[1007,13525,13527],{"href":13526},"#the-sealing-key","The sealing key"," below.",[937,13530,13531,13532,13535,13536,13538,13539,13541,13542,13545],{},"Either way, this is the family's ",[950,13533,13534],{},"single non-zero-knowledge seam"," — an ",[941,13537,1139],{}," host ",[1092,13540,5105],{}," decrypt what it unseals, running on infrastructure you own, by design. The safeguard is ",[1092,13543,13544],{},"scope",", not secrecy.",[1591,13547,13549,13551],{"id":13548},"as-the-designed-output-channel",[941,13550,1791],{}," — the designed output channel",[937,13553,13554,13555,13558,13559,13565,13566,13568,13569,13571,13572,13575,13576,13578,13579,13582,13583,1169,13586,13588,13589,13593,13594,13597],{},"Data also flows ",[1092,13556,13557],{},"out"," — through the ",[1007,13560,13562,13564],{"href":13561},"..\u002F..\u002Ffamilies\u002Fas\u002F",[941,13563,1791],{}," exporters",", the deliberate, user-invoked exit. Every ",[941,13567,1791],{}," artefact sits in one of two tiers, and both pass a capability check on the caller's ",[1007,13570,2705],{"href":11634},". The ",[950,13573,13574],{},"plaintext tier"," (CSV, XLSX, JSON, NDJSON, XML, SQL, blob, ZIP) runs ",[1092,13577,10761],{}," the hub decrypts and necessarily produces plaintext artefacts — that is the point of an export: an explicit act by a keyholder inside the enclave, gated by the keyring's ",[941,13580,13581],{},"exportCapability.plaintext"," grant (a per-format allow-list), not a leak. The ",[950,13584,13585],{},"bundle tier",[941,13587,1402],{}," container (",[1007,13590,13592],{"href":13591},"..\u002F..\u002Ffamilies\u002Fas\u002Fnoydb","as-noydb",") — never leaves ciphertext: record bodies stay encrypted under the vault's own DEKs end-to-end, and recipient access travels as rebuilt keyring slots that wrap the DEK set under a key derived from the recipient's secret (PBKDF2-SHA256, 600,000 iterations), gated by the keyring's ",[941,13595,13596],{},"exportCapability.bundle"," grant.",[937,13599,13600,13601,13603,13604,13608],{},"The enclave is also the one part of the kernel that is swappable: a sister project can replace the crypto interior wholesale by implementing the frozen enclave contract — the envelope-in \u002F plaintext-out surface, its refusal semantics, and the wire protocol between the kernel and its crypto core are specified in ",[1007,13602,25],{"href":26},", and the architecture of the interior itself in ",[1007,13605,13607],{"href":13606},"..\u002F..\u002Farchitecture\u002Fkernel\u002Fenclave","Enclave (architecture)",". Everything on this page describes the enclave noy-db ships by default.",[7646,13610,13612,13618,13656],{"color":7648,"icon":13611},"i-lucide-eye",[937,13613,13614,13617],{},[950,13615,13616],{},"What the store still learns."," Ciphertext is not invisibility. An observer with store access still sees:",[1111,13619,13620,13629,13639,13645],{},[1114,13621,13622,13623,13625,13626,13628],{},"the plaintext envelope metadata — ",[941,13624,1700],{}," (version counter) and ",[941,13627,1703],{}," (timestamp) — which the sync engine needs without keys;",[1114,13630,13631,13632,13635,13636,3769],{},"record counts, ids, sizes, and write timing — ",[1092,13633,13634],{},"that"," you write and roughly how much, never ",[1092,13637,13638],{},"what",[1114,13640,13641,13642,13644],{},"with opt-in deterministic fields, the ",[941,13643,1732],{}," slots' equality classes — which records share a value, acknowledged per collection;",[1114,13646,13647,13648,13651,13652,13655],{},"with opt-in classified ",[941,13649,13650],{},"equatable"," fields, the ",[941,13653,13654],{},"_bidx"," tags — the same equality leak, behind its own acknowledgement gate.",[937,13657,13658,13659,1520],{},"The full walk by attacker position is the ",[1007,13660,13661],{"href":464},"threat model",[1022,13663,13665],{"id":13664},"zero-crypto-dependencies","Zero crypto dependencies",[937,13667,13668,13669,13671,13672,13675,13676,13679],{},"All cryptography uses the Web Crypto API — every operation goes through ",[941,13670,1125],{},", and random material comes from ",[941,13673,13674],{},"crypto.getRandomValues",". No ",[941,13677,13678],{},"npm"," crypto packages are or ever will be installed.",[937,13681,13682,13683,13686],{},"That is a security feature, not a footnote. Cryptographic primitives are the worst possible place for a supply-chain compromise, and the way to shrink that attack surface is to have ",[950,13684,13685],{},"zero third-party crypto code",": nothing to audit beyond the runtime itself, no transitive crypto dependency to pin, patch, or watch for CVEs. The primitives are implemented and maintained by the runtime vendor — the same code path the platform itself trusts — and are hardware-accelerated where the platform provides it. It also forces discipline: the Web Crypto API only offers standard, reviewed constructions, so there is no homegrown cipher anywhere in noy-db.",[937,13688,13689],{},"The full primitive set the enclave uses:",[1338,13691,13692,13705],{},[1341,13693,13694],{},[1344,13695,13696,13699,13702],{},[1347,13697,13698],{},"Primitive",[1347,13700,13701],{},"Parameters",[1347,13703,13704],{},"Used for",[1357,13706,13707,13718,13729,13740,13751,13762,13772],{},[1344,13708,13709,13712,13715],{},[1362,13710,13711],{},"PBKDF2-SHA256",[1362,13713,13714],{},"600,000 iterations, 32-byte salt",[1362,13716,13717],{},"secret → KEK derivation",[1344,13719,13720,13723,13726],{},[1362,13721,13722],{},"AES-KW (RFC 3394)",[1362,13724,13725],{},"256-bit keys",[1362,13727,13728],{},"wrapping DEKs under the KEK (and CEKs under an HKDF-derived wrap key)",[1344,13730,13731,13734,13737],{},[1362,13732,13733],{},"AES-256-GCM",[1362,13735,13736],{},"fresh random 12-byte IV per operation",[1362,13738,13739],{},"record bodies, binary blob chunks",[1344,13741,13742,13745,13748],{},[1362,13743,13744],{},"HKDF-SHA256",[1362,13746,13747],{},"domain-separated salt + info per channel",[1362,13749,13750],{},"deriving subkeys from a DEK: the CEK wrap key, presence key, sealed-field keys, deterministic-index key",[1344,13752,13753,13756,13759],{},[1362,13754,13755],{},"HMAC-SHA256",[1362,13757,13758],{},"keyed by a DEK",[1362,13760,13761],{},"blob eTags, presence integrity",[1344,13763,13764,13767,13769],{},[1362,13765,13766],{},"SHA-256",[1362,13768,3610],{},[1362,13770,13771],{},"content hashes for ledger and bundle integrity",[1344,13773,13774,13778,13780],{},[1362,13775,13776],{},[941,13777,13674],{},[1362,13779,3610],{},[1362,13781,13782],{},"IVs, salts, minted keys and secrets",[937,13784,13785],{},"Available in:",[1111,13787,13788,13791,13794,13797,13800,13803,13806],{},[1114,13789,13790],{},"Node.js 18+",[1114,13792,13793],{},"Bun",[1114,13795,13796],{},"Deno",[1114,13798,13799],{},"Modern browsers",[1114,13801,13802],{},"Cloudflare Workers",[1114,13804,13805],{},"Electron \u002F NW.js",[1114,13807,13808],{},"Mobile WebViews",[1022,13810,13812],{"id":13811},"how-a-record-is-encrypted","How a record is encrypted",[1591,13814,13816],{"id":13815},"first-the-ideas-in-plain-words","First, the ideas — in plain words",[937,13818,13819,13820,13823,13824,1520],{},"noy-db is built on ",[950,13821,13822],{},"zero trust"," — the idea that you never assume anyone, even your own server, is safe to show secrets to. The term was popularized around 2010 by John Kindervag at Forrester Research, building on earlier \"de-perimeterization\" work — see ",[1007,13825,13828],{"href":13826,"rel":13827},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FZero_trust_architecture",[3371],"Zero trust architecture",[937,13830,13831],{},"The rest of this page uses a small cast of characters. Each one, in plain words:",[1111,13833,13834,13839,13844,13853,13861,13867,13872],{},[1114,13835,3289,13836,13838],{},[950,13837,8364],{}," is a big secret number that scrambles a box shut — and unscrambles it again.",[1114,13840,960,13841,13843],{},[950,13842,5627],{}," (data-encryption key) is the key that locks your actual documents — one per drawer (a collection).",[1114,13845,960,13846,13848,13849,13852],{},[950,13847,9879],{}," (key-encryption key) is a key whose only job is to lock ",[1092,13850,13851],{},"other keys",". It is rebuilt from your secret secret every time you unlock, and it is never written down anywhere.",[1114,13854,960,13855,13857,13858,13860],{},[950,13856,2705],{}," is the little box of locked-up keys that ",[1092,13859,3082],{}," safe to store — because without the KEK it is just noise.",[1114,13862,960,13863,13866],{},[950,13864,13865],{},"IV"," is a fresh random sprinkle added to every scramble, so the same document never scrambles the same way twice.",[1114,13868,960,13869,13871],{},[950,13870,1659],{}," is the sealed package the storage actually receives — the scrambled document plus its sprinkle, nothing readable.",[1114,13873,3289,13874,13877],{},[950,13875,13876],{},"CEK"," (content-encryption key) is an optional extra: every single document gets its own tiny key, so one document can be destroyed — or lent out — on its own.",[937,13879,13880,13881,13884,13885,13888,13889,997,13892,13896],{},"One honest note before the details: everything above is ",[950,13882,13883],{},"symmetric"," encryption — the same key locks and unlocks. The other kind, ",[950,13886,13887],{},"asymmetric",", is a two-part lock: anyone can snap it shut with the public part, but only the private part opens it. noy-db uses asymmetric crypto only in special places — ",[1007,13890,13891],{"href":13526},"handing one record's key to a machine",[1007,13893,13895],{"href":13894},"#encryption-elsewhere-in-the-family","signing attestations"," — the record path below is symmetric throughout.",[1591,13898,13900],{"id":13899},"the-chain-precisely","The chain, precisely",[937,13902,13903],{},"This is the basic, single-secret flow — one user, one secret, no per-record keys. (The extensions — more users, a sealing key, per-record keys — each add one layer on top and are covered in the next three sections.)",[13905,13906,13908],"div",{"style":13907},"border:1px solid var(--ui-border); border-radius:0.75rem; background:var(--ui-bg-elevated); padding:1.25rem 1rem; margin:1.5rem 0; overflow-x:auto;",[13905,13909,13911,13922,13925,13929,13931,13935,13951,13954,13960,13962,13964,13975,13977,13980,13982,13984],{"style":13910},"display:flex; flex-direction:column; align-items:center; min-width:21rem; text-align:center; line-height:1.35;",[13905,13912,13914,13918],{"style":13913},"border:1px solid var(--ui-border); border-radius:0.5rem; background:var(--ui-bg); padding:0.5rem 1.5rem;",[13905,13915,13917],{"style":13916},"font-weight:600; color:var(--ui-text-highlighted);","Secret",[13905,13919,13921],{"style":13920},"font-size:0.75rem; color:var(--ui-text-muted);","the secret — supplied at unlock, never stored",[13905,13923],{"style":13924},"width:1px; height:0.5rem; background:var(--ui-border);",[13905,13926,13928],{"style":13927},"font-size:0.75rem; color:var(--ui-text-muted); border:1px dashed var(--ui-border); border-radius:999px; padding:0.15rem 0.9rem; margin:0.25rem 0;","PBKDF2-SHA256 · 600,000 iterations · 32-byte salt from the keyring",[13905,13930],{"style":13924},[13905,13932,13934],{"style":13933},"color:var(--ui-text-muted); line-height:1; font-size:0.7rem;","▼",[13905,13936,13938,13945],{"style":13937},"border:1px solid var(--ui-primary); border-radius:0.5rem; background:var(--ui-bg); padding:0.5rem 1.5rem; margin-top:0.25rem;",[13905,13939,13940,13941],{"style":13916},"KEK ",[2225,13942,13944],{"style":13943},"font-weight:400; color:var(--ui-text-muted);","— key-encryption key",[13905,13946,13947,13948],{"style":13920},"memory only · wiped on ",[2225,13949,12797],{"style":13950},"font-family:ui-monospace,monospace;",[13905,13952],{"style":13953},"width:1px; height:0.5rem; background:var(--ui-border); margin-top:0.25rem;",[13905,13955,13956,13957],{"style":13927},"AES-KW (RFC 3394) unwrap · wrapped DEK read from ",[2225,13958,13959],{"style":13950},"_keyring\u002F\u003Cuser>",[13905,13961],{"style":13924},[13905,13963,13934],{"style":13933},[13905,13965,13966,13972],{"style":13937},[13905,13967,13968,13969],{"style":13916},"DEK ",[2225,13970,13971],{"style":13943},"— data-encryption key, one per collection",[13905,13973,13974],{"style":13920},"memory only while the session is unlocked",[13905,13976],{"style":13953},[13905,13978,13979],{"style":13927},"AES-256-GCM · fresh random 12-byte IV per write",[13905,13981],{"style":13924},[13905,13983,13934],{"style":13933},[13905,13985,13987,13990],{"style":13986},"border:1px solid var(--ui-border); border-radius:0.5rem; background:var(--ui-bg); padding:0.5rem 1.5rem; margin-top:0.25rem;",[13905,13988,13989],{"style":13916},"Envelope",[13905,13991,13992,13993,13995,13996,13998],{"style":13920},"ciphertext in ",[2225,13994,1146],{"style":13950}," + its ",[2225,13997,1143],{"style":13950}," — the only thing a store ever receives",[937,14000,14001],{},"Reading top to bottom, each term in the chart is one link in the chain:",[1111,14003,14004,14024,14038,14051,14066],{},[1114,14005,14006,14009,14010,10313,14012,14014,14015,14017,14018,14020,14021,14023],{},[950,14007,14008],{},"Secret → KEK."," The secret you pass as ",[941,14011,10695],{},[941,14013,2134],{}," is fed through ",[950,14016,13711],{}," at 600,000 iterations, salted with a random 32-byte salt, to derive the ",[950,14019,9879],{}," (key-encryption key). PBKDF2 is a deliberately slow, brute-force-resistant key-derivation function — the iteration count is the cost an attacker pays per password guess. The KEK is never persisted; it lives only in memory during an active session, and ",[941,14022,13331],{}," wipes it.",[1114,14025,14026,14029,14030,14032,14033,14035,14036,1520],{},[950,14027,14028],{},"The keyring."," The salt — and everything else the vault must remember about your access — lives in the ",[950,14031,2705],{},", the persisted record at ",[941,14034,13959],{}," inside the vault. It holds your wrapped keys, never usable ones: see ",[1007,14037,69],{"href":11634},[1114,14039,14040,14043,14044,14046,14047,14050],{},[950,14041,14042],{},"DEK."," Each collection gets its own ",[950,14045,5627],{}," (data-encryption key), a randomly generated AES-256-GCM key. Your copy of each DEK is stored in the keyring ",[950,14048,14049],{},"AES-KW-wrapped"," under your KEK — AES-KW (RFC 3394) is the standard construction for encrypting one key under another. Unlocking a session unwraps the DEKs into memory; they are never persisted unwrapped.",[1114,14052,14053,14056,14057,13535,14059,14062,14063,14065],{},[950,14054,14055],{},"Envelope."," A record body is serialized to JSON and encrypted with ",[950,14058,13733],{},[1092,14060,14061],{},"authenticated"," cipher: it produces both ciphertext and an integrity tag, so tampering is detected at decrypt time. Every encrypt call uses a fresh random 12-byte ",[950,14064,13865],{}," (initialization vector); an IV is never reused.",[1114,14067,14068,14071,14072,14075,14076,13528],{},[950,14069,14070],{},"CEK."," A collection can additionally opt into per-record ",[950,14073,14074],{},"content-encryption keys"," — one key per record, enabling per-record erasure and sealing — introduced in ",[1007,14077,14079],{"href":14078},"#per-record-keys-cek","Per-record keys (CEK)",[937,14081,14082],{},"The two directions are the same chain walked in opposite orders:",[1234,14084,14085,14108],{},[1114,14086,14087,964,14090,14093,14094,14097,14098,14100,14101,14104,14105,14107],{},[950,14088,14089],{},"Encrypting a write",[1092,14091,14092],{},"derive"," the KEK from the secret (once per unlock), ",[1092,14095,14096],{},"unwrap"," the collection's DEK from the keyring, ",[1092,14099,1624],{}," the record body under the DEK with a fresh IV, ",[1092,14102,14103],{},"wrap"," the result in an envelope, and ",[1092,14106,6951],{}," it. The store receives ciphertext, nothing else.",[1114,14109,14110,14113,14114,14116,14117,1324,14120,14122,14123,14125,14126,14129,14130,14132,14133,14136,14137,14140,14141,14143],{},[950,14111,14112],{},"Decrypting a read"," — fetch the ",[1092,14115,1659],{}," from the store, ",[1092,14118,14119],{},"authenticated-decrypt",[941,14121,1146],{}," under the DEK using the stored ",[941,14124,1143],{}," — the GCM tag is verified as part of decryption — over the body ",[1092,14127,14128],{},"and"," the record's bound identity and version — so an envelope that does not authenticate throws ",[941,14131,11762],{}," and the read ",[950,14134,14135],{},"fails closed",": there is no partial-decryption fallback (a body sealed before that binding also fails, and reports ",[941,14138,14139],{},"reason: 'unbound-legacy-format'",") — then parse the ",[1092,14142,2985],{}," JSON body.",[1591,14145,13989],{"id":1659},[937,14147,960,14148,14150],{},[1092,14149,1659],{}," is the wrapper a store receives instead of your record:",[2217,14152,14154],{"className":6604,"code":14153,"language":141,"meta":1037,"style":1037},"{\n  \"_noydb\": 1,\n  \"_v\":     3,\n  \"_ts\":    \"2026-04-04T10:00:00.000Z\",\n  \"_iv\":    \"\u003Cbase64>\",\n  \"_data\":  \"\u003Cbase64 ciphertext>\"\n}\n",[941,14155,14156,14160,14174,14189,14209,14228,14245],{"__ignoreMap":1037},[2225,14157,14158],{"class":2227,"line":2228},[2225,14159,4200],{"class":2235},[2225,14161,14162,14164,14166,14168,14170,14172],{"class":2227,"line":1041},[2225,14163,9368],{"class":2235},[2225,14165,1697],{"class":2774},[2225,14167,6620],{"class":2235},[2225,14169,2311],{"class":2235},[2225,14171,9377],{"class":6527},[2225,14173,1707],{"class":2235},[2225,14175,14176,14178,14180,14182,14184,14187],{"class":2227,"line":1038},[2225,14177,9368],{"class":2235},[2225,14179,1700],{"class":2774},[2225,14181,6620],{"class":2235},[2225,14183,2311],{"class":2235},[2225,14185,14186],{"class":6527},"     3",[2225,14188,1707],{"class":2235},[2225,14190,14191,14193,14195,14197,14199,14202,14205,14207],{"class":2227,"line":2288},[2225,14192,9368],{"class":2235},[2225,14194,1703],{"class":2774},[2225,14196,6620],{"class":2235},[2225,14198,2311],{"class":2235},[2225,14200,14201],{"class":2235},"    \"",[2225,14203,14204],{"class":2252},"2026-04-04T10:00:00.000Z",[2225,14206,6620],{"class":2235},[2225,14208,1707],{"class":2235},[2225,14210,14211,14213,14215,14217,14219,14221,14224,14226],{"class":2227,"line":2304},[2225,14212,9368],{"class":2235},[2225,14214,1143],{"class":2774},[2225,14216,6620],{"class":2235},[2225,14218,2311],{"class":2235},[2225,14220,14201],{"class":2235},[2225,14222,14223],{"class":2252},"\u003Cbase64>",[2225,14225,6620],{"class":2235},[2225,14227,1707],{"class":2235},[2225,14229,14230,14232,14234,14236,14238,14240,14243],{"class":2227,"line":2356},[2225,14231,9368],{"class":2235},[2225,14233,1146],{"class":2774},[2225,14235,6620],{"class":2235},[2225,14237,2311],{"class":2235},[2225,14239,9368],{"class":2235},[2225,14241,14242],{"class":2252},"\u003Cbase64 ciphertext>",[2225,14244,9449],{"class":2235},[2225,14246,14247],{"class":2227,"line":2830},[2225,14248,7351],{"class":2235},[937,14250,14251,13625,14253,14255,14256,14258],{},[941,14252,1700],{},[941,14254,1703],{}," (timestamp) are NOT encrypted — the sync engine needs them without keys. Everything inside ",[941,14257,1146],{}," IS encrypted.",[1022,14260,14262],{"id":14261},"users-the-keyring","Users & the keyring",[937,14264,14265,14266,14269,14270,14272,14273,14275,14276,14280,14281,1163],{},"The multi-user extension changes nothing about the chain above — it multiplies one link. The collection DEKs stay the same keys; what a member holds is their ",[950,14267,14268],{},"own wrapped copy"," of them, in their own ",[941,14271,13959],{}," record, wrapped under their own KEK (derived from their own secret and salt). Granting a user wraps the DEKs for the new member; revoking one re-wraps the DEKs for the remaining members, so the revoked keyring can no longer unwrap anything current. The keyring is thus both the access-control list and the cryptographic gate: what you can read is exactly what your keyring can unwrap. The single-user primitives live on ",[1007,14274,69],{"href":11634},", the roles and grant\u002Frevoke ceremony on ",[1007,14277,14279],{"href":14278},"..\u002F06-permissions-and-keyring","Permissions & keyring",", and the multi-user ceremony itself is the opt-in ",[1007,14282,868],{"href":14283},"..\u002F..\u002Fservices\u002Fteam",[13905,14285,14286],{"style":13907},[13905,14287,14288,14297,14300,14303],{"style":13910},[13905,14289,14291,14294],{"style":14290},"border:1px solid var(--ui-primary); border-radius:0.5rem; background:var(--ui-bg); padding:0.5rem 1.5rem;",[13905,14292,14293],{"style":13916},"Collection DEK",[13905,14295,14296],{"style":13920},"one key — every member reads the same ciphertext",[13905,14298],{"style":14299},"width:1px; height:0.6rem; background:var(--ui-border);",[13905,14301,14302],{"style":13927},"AES-KW wrap · once per member, under each member’s KEK",[13905,14304,14306,14316,14323],{"style":14305},"display:flex; gap:0.75rem; margin-top:0.5rem; flex-wrap:wrap; justify-content:center;",[13905,14307,14309,14313],{"style":14308},"border:1px solid var(--ui-border); border-radius:0.5rem; background:var(--ui-bg); padding:0.4rem 1rem;",[13905,14310,14312],{"style":14311},"font-size:0.8125rem; font-family:ui-monospace,monospace; color:var(--ui-text-highlighted);","_keyring\u002Falice",[13905,14314,14315],{"style":13920},"wrapped copy",[13905,14317,14318,14321],{"style":14308},[13905,14319,14320],{"style":14311},"_keyring\u002Fbob",[13905,14322,14315],{"style":13920},[13905,14324,14325,14329],{"style":14308},[13905,14326,14328],{"style":14327},"font-size:0.8125rem; font-family:ui-monospace,monospace; color:var(--ui-text-muted);","_keyring\u002F…",[13905,14330,14331],{"style":13920},"grant = wrap · revoke = re-wrap the rest",[1022,14333,13527],{"id":14334},"the-sealing-key",[937,14336,960,14337,14339,14340,14343,14344,14347,14348,14351,14352,968,14354,968,14357,14360,14361,14367],{},[941,14338,1139],{}," extension replaces the ",[1092,14341,14342],{},"human"," at the top of the chart with a ",[1092,14345,14346],{},"host",". A sealing-key provider is a tiny contract — ",[941,14349,14350],{},"NoydbSealer",", three members: ",[941,14353,6983],{},[941,14355,14356],{},"seal(bytes)",[941,14358,14359],{},"unseal(bytes)"," — implemented by each ",[1007,14362,14364,14366],{"href":14363},"..\u002F..\u002Ffamilies\u002Fat\u002F",[941,14365,1139],{}," package"," over whatever the host trusts: an env var, the OS keychain, a cloud KMS.",[937,14369,14370,14371,953,14374,14377,14378,14381,14382,14385,14386,14388],{},"In ",[950,14372,14373],{},"managed-secret mode",[941,14375,14376],{},"createNoydb({ secretMode: 'managed', sealingKey })",") the hub generates a random 256-bit secret at first open, seals it under the provider, and persists the sealed blob per vault at ",[941,14379,14380],{},"_meta\u002Fsealed-secret","; on reopen it checks the stored provider id, unseals, and feeds the secret into the normal PBKDF2 → KEK chain. The user never sees or types it. The scope of this grant is the ",[950,14383,14384],{},"whole vault",": a host holding the sealing key can open exactly the vaults sealed under its key — nothing else, but nothing less. (Managed mode is also the rubber-hose-resistant posture — see ",[1007,14387,844],{"href":12844},".)",[937,14390,960,14391,14393,14394,14396,14397,14400,14401,14404,14405,14407,14408,957],{},[950,14392,13519],{}," variant is ",[1007,14395,824],{"href":13522},": the grantor unwraps one record's CEK, binds it into ",[941,14398,14399],{},"{collection, id, cek, expiresAt}",", seals that binding via the host's pluggable sealer (RSA-OAEP-SHA256 over a fresh AES-GCM transport key in the shipped providers), and writes the delivery envelope at ",[941,14402,14403],{},"_sealed_cek\u002F\u003Ccollection>\u002F\u003Cid>\u002F\u003Cpid>",". The host-side opener verifies the expiry and that the binding names exactly the record it is being used on, then decrypts that one record — it never touches a DEK, and a CEK sealed for record A is rejected against record B. One provider ",[941,14406,6983],{}," string identifies each host across every sealed artefact — frozen once shipped (",[1007,14409,14411],{"href":14410},"..\u002F..\u002Fservices\u002Fsealing-pid-stability","sealing-pid-stability",[1022,14413,14079],{"id":14414},"per-record-keys-cek",[937,14416,14417,14418,14421,14422,14424,14425,2311],{},"By default one per-collection DEK encrypts every record body. Opt a collection into per-record content-encryption keys with ",[941,14419,14420],{},"vault.collection(name, { perRecordKeys: true })"," and each record gets its own AES-256-GCM ",[950,14423,13876],{},", AES-KW-wrapped under the collection DEK and stored on the envelope's ",[941,14426,1149],{},[2217,14428,14430],{"className":6604,"code":14429,"language":141,"meta":1037,"style":1037},"{\n  \"_noydb\": 1, \"_v\": 3, \"_ts\": \"…\",\n  \"_iv\": \"\u003Cbase64>\", \"_data\": \"\u003Cbase64 ciphertext, encrypted under the CEK>\",\n  \"_cek\": \"\u003Cbase64 AES-KW-wrapped CEK, wrapped under the collection\u002Ftier DEK>\"\n}\n",[941,14431,14432,14436,14480,14515,14532],{"__ignoreMap":1037},[2225,14433,14434],{"class":2227,"line":2228},[2225,14435,4200],{"class":2235},[2225,14437,14438,14440,14442,14444,14446,14448,14450,14452,14454,14456,14458,14461,14463,14465,14467,14469,14471,14473,14476,14478],{"class":2227,"line":1041},[2225,14439,9368],{"class":2235},[2225,14441,1697],{"class":2774},[2225,14443,6620],{"class":2235},[2225,14445,2311],{"class":2235},[2225,14447,9377],{"class":6527},[2225,14449,2298],{"class":2235},[2225,14451,6614],{"class":2235},[2225,14453,1700],{"class":2774},[2225,14455,6620],{"class":2235},[2225,14457,2311],{"class":2235},[2225,14459,14460],{"class":6527}," 3",[2225,14462,2298],{"class":2235},[2225,14464,6614],{"class":2235},[2225,14466,1703],{"class":2774},[2225,14468,6620],{"class":2235},[2225,14470,2311],{"class":2235},[2225,14472,6614],{"class":2235},[2225,14474,14475],{"class":2252},"…",[2225,14477,6620],{"class":2235},[2225,14479,1707],{"class":2235},[2225,14481,14482,14484,14486,14488,14490,14492,14494,14496,14498,14500,14502,14504,14506,14508,14511,14513],{"class":2227,"line":1038},[2225,14483,9368],{"class":2235},[2225,14485,1143],{"class":2774},[2225,14487,6620],{"class":2235},[2225,14489,2311],{"class":2235},[2225,14491,6614],{"class":2235},[2225,14493,14223],{"class":2252},[2225,14495,6620],{"class":2235},[2225,14497,2298],{"class":2235},[2225,14499,6614],{"class":2235},[2225,14501,1146],{"class":2774},[2225,14503,6620],{"class":2235},[2225,14505,2311],{"class":2235},[2225,14507,6614],{"class":2235},[2225,14509,14510],{"class":2252},"\u003Cbase64 ciphertext, encrypted under the CEK>",[2225,14512,6620],{"class":2235},[2225,14514,1707],{"class":2235},[2225,14516,14517,14519,14521,14523,14525,14527,14530],{"class":2227,"line":2288},[2225,14518,9368],{"class":2235},[2225,14520,1149],{"class":2774},[2225,14522,6620],{"class":2235},[2225,14524,2311],{"class":2235},[2225,14526,6614],{"class":2235},[2225,14528,14529],{"class":2252},"\u003Cbase64 AES-KW-wrapped CEK, wrapped under the collection\u002Ftier DEK>",[2225,14531,9449],{"class":2235},[2225,14533,14534],{"class":2227,"line":2304},[2225,14535,7351],{"class":2235},[1111,14537,14538,14554,14572,14598,14617,14631],{},[1114,14539,14540,14546,14547,14549,14550,14553],{},[950,14541,14542,14543,14545],{},"Discriminant is ",[941,14544,1149],{}," presence, not the flag."," A record with ",[941,14548,1149],{}," is decrypted by unwrapping the CEK under the collection DEK then decrypting the body under the CEK; a record without it takes the legacy path (body directly under the collection DEK). So a collection without ",[941,14551,14552],{},"perRecordKeys"," is byte-identical to before, and a mixed vault — or a recipient that never set the flag — reads both kinds.",[1114,14555,14556,14559,14560,14562,14563,14565,14566,14568,14569,1520],{},[950,14557,14558],{},"Stable across versions."," Insert mints the CEK; updates and history snapshots reuse it, so every ",[941,14561,6715],{}," envelope for a record carries the same ",[941,14564,1149],{},". (The wrapping key is HKDF-derived from the DEK and the wrap is deterministic, which is what makes the reused ",[941,14567,1149],{}," byte-identical.) A session-scoped LRU caches the unwrapped CEK per ",[941,14570,14571],{},"(collection, id)",[1114,14573,14574,14577,14578,953,14581,14584,14585,14588,14589,14591,14592,14594,14595,14597],{},[950,14575,14576],{},"Deterministic index slots stay collection-keyed."," A collection can opt specific fields into ",[1092,14579,14580],{},"deterministic encryption",[941,14582,14583],{},"deterministicFields",", acknowledged with ",[941,14586,14587],{},"acknowledgeDeterministicRisk: true","): the field value is additionally stored in the envelope's ",[941,14590,1732],{}," map, encrypted so that equal plaintext produces equal ciphertext — enabling blind equality search (",[941,14593,1965],{},") without decrypting every record, at the cost of leaking which records share a value. ",[941,14596,1732],{}," slots are keyed off a key derived from the collection DEK, never the per-record CEK — cross-record equality only works if all records share the key.",[1114,14599,14600,14603,14604,1625,14607,14610,14611,14613,14614,1520],{},[950,14601,14602],{},"Tiers compose."," Moving a record between hierarchical access tiers (",[941,14605,14606],{},"elevate",[941,14608,14609],{},"demote",") re-wraps the ",[1092,14612,4664],{}," CEK from the source tier DEK to the target tier DEK; the body key is unchanged — the elevation API that drives this re-wrap lives in ",[1007,14615,872],{"href":14616},"..\u002F..\u002Fservices\u002Ftiers",[1114,14618,14619,14622,14623,14627,14628,14630],{},[950,14620,14621],{},"Bundles re-wrap."," When records leave the vault as a portable bundle (",[1007,14624,14626],{"href":14625},"..\u002F..\u002Fservices\u002Ftransferable-partitions","transferable partitions","), the re-key step unwraps each ",[941,14629,1149],{}," under the source DEK and re-wraps it under the fresh destination DEK, so adopted records stay decryptable.",[1114,14632,14633,14636,14637,14640,14641,1625,14643,2557,14645,14647,14648,1520],{},[950,14634,14635],{},"Tombstone-tolerant read."," A ",[941,14638,14639],{},"get()"," on an envelope with no ",[941,14642,1146],{},[941,14644,1149],{},[941,14646,6651],{}," rather than throwing ",[941,14649,11762],{},[937,14651,14652,14653,14657,14658,14660],{},"Per-record keys are the foundation that makes per-record erasure (crypto-shred — see the ",[1007,14654,14656],{"href":14655},"..\u002F..\u002Fservices\u002Fforget","forget service",") and record-scoped sealing (",[1007,14659,824],{"href":13522},") possible: destroy one record's wrapped CEK and only that record becomes undecryptable.",[1591,14662,14664,14665,1381],{"id":14663},"sealed-fields-_sealed","Sealed fields (",[941,14666,1152],{},[937,14668,14669,14670,14673,14674,14678,14679,14682,14683,14685,14686,14688,14689,14692,14693,14696],{},"A collection can additionally pull individual sensitive fields out of the record body: declare them in the collection's ",[941,14671,14672],{},"sensitive: [...]"," option and each declared field is encrypted into its own slot in the envelope's ",[950,14675,14676],{},[941,14677,1152],{}," map — an ",[941,14680,14681],{},"iv:data"," blob per field — instead of travelling inside ",[941,14684,1146],{},". A reader who can open ",[941,14687,1146],{}," still cannot see a sealed field without deriving that field's key: each field gets a ",[950,14690,14691],{},"distinct"," AES-256-GCM key, HKDF-derived with an injective ",[941,14694,14695],{},"[domain, collection, field]"," context, so one field's ciphertext does not authenticate under another field's key — sealed fields are cryptographically isolated from one another, and from the same field name in any other collection.",[937,14698,14699],{},"The field key's parent follows the record's key model:",[1111,14701,14702,14718],{},[1114,14703,14704,14705,14707,14708,14710,14711,14714,14715,14717],{},"On a ",[941,14706,14552],{}," collection the field key derives from the record's ",[950,14709,13876],{}," (HKDF domain ",[941,14712,14713],{},"noydb-sealed-cek",") — which extends the crypto-shred guarantee to sealed fields: destroying the record's wrapped ",[941,14716,1149],{}," makes every sealed field unrecoverable along with the body.",[1114,14719,14720,14721,14723,14724,14727,14728,14730,14731,14734],{},"Without per-record keys it derives from the collection ",[950,14722,5627],{}," (domain ",[941,14725,14726],{},"noydb-sealed",") — and ",[1007,14729,685],{"href":14655}," then reports those slots as ",[1092,14732,14733],{},"residue"," rather than shredded: a synced or backed-up copy stays decryptable as long as the collection DEK survives.",[937,14736,14737,14738,14741],{},"Reads dual-read — the CEK-derived key first, then the DEK-derived key as the legacy fallback — so records sealed before a collection adopted per-record keys stay readable. The ergonomic layer on top is the ",[1007,14739,633],{"href":14740},"..\u002F..\u002Fservices\u002Fclassified"," service: declare a field once with a preset and it routes to sealed (recoverable) or digest-only storage, with safe projections and audited reveal.",[1022,14743,14745],{"id":14744},"envelope-slots-the-reference","Envelope slots — the reference",[937,14747,14748,14749,14751],{},"Every layer above writes into the same wrapper. This is the complete set of ",[941,14750,6702],{},"-slots a record envelope can carry — five slots every envelope has, and the rest present only when a feature writes them:",[1338,14753,14754,14773],{},[1341,14755,14756],{},[1344,14757,14758,14761,14764,14767,14770],{},[1347,14759,14760],{},"Slot",[1347,14762,14763],{},"What it holds",[1347,14765,14766],{},"Encrypted?",[1347,14768,14769],{},"Key",[1347,14771,14772],{},"Introduced by",[1357,14774,14775,14791,14807,14823,14839,14861,14877,14901,14919,14938,14962,14985,15008,15036,15058,15081],{},[1344,14776,14777,14781,14784,14786,14788],{},[1362,14778,14779],{},[941,14780,1697],{},[1362,14782,14783],{},"format version marker",[1362,14785,11313],{},[1362,14787,3610],{},[1362,14789,14790],{},"core — every envelope",[1344,14792,14793,14797,14800,14803,14805],{},[1362,14794,14795],{},[941,14796,1700],{},[1362,14798,14799],{},"version counter",[1362,14801,14802],{},"no — sync orders by it without keys",[1362,14804,3610],{},[1362,14806,14790],{},[1344,14808,14809,14813,14816,14819,14821],{},[1362,14810,14811],{},[941,14812,1703],{},[1362,14814,14815],{},"write timestamp (ISO 8601)",[1362,14817,14818],{},"no — sync needs it without keys",[1362,14820,3610],{},[1362,14822,14790],{},[1344,14824,14825,14829,14832,14835,14837],{},[1362,14826,14827],{},[941,14828,1143],{},[1362,14830,14831],{},"the body's fresh random IV",[1362,14833,14834],{},"no — an IV is not secret",[1362,14836,3610],{},[1362,14838,14790],{},[1344,14840,14841,14845,14848,14853,14859],{},[1362,14842,14843],{},[941,14844,1146],{},[1362,14846,14847],{},"the record body, ciphertext",[1362,14849,14850],{},[950,14851,14852],{},"yes",[1362,14854,14855,14856,14858],{},"collection\u002Ftier DEK — or the record's CEK when ",[941,14857,1149],{}," is present",[1362,14860,14790],{},[1344,14862,14863,14867,14870,14872,14874],{},[1362,14864,14865],{},[941,14866,1706],{},[1362,14868,14869],{},"actor id who wrote this version",[1362,14871,11313],{},[1362,14873,3610],{},[1362,14875,14876],{},"core — stamped when the writer is known",[1344,14878,14879,14885,14888,14890,14892],{},[1362,14880,14881,1261,14883],{},[941,14882,1710],{},[941,14884,1713],{},[1362,14886,14887],{},"provenance source id + timestamp",[1362,14889,11313],{},[1362,14891,3610],{},[1362,14893,14894,964,14897],{},[941,14895,14896],{},"provenance: true",[1007,14898,14900],{"href":14899},"..\u002F..\u002Fservices\u002Ffield-metadata","field-metadata",[1344,14902,14903,14907,14910,14913,14915],{},[1362,14904,14905],{},[941,14906,1716],{},[1362,14908,14909],{},"the record's access tier",[1362,14911,14912],{},"no — reads route to the right tier DEK without try-decrypting",[1362,14914,3610],{},[1362,14916,14917],{},[1007,14918,872],{"href":14616},[1344,14920,14921,14925,14930,14932,14934],{},[1362,14922,14923],{},[941,14924,1719],{},[1362,14926,14927,14928,1381],{},"who elevated the record (gates ",[941,14929,14609],{},[1362,14931,11313],{},[1362,14933,3610],{},[1362,14935,14936],{},[1007,14937,872],{"href":14616},[1344,14939,14940,14944,14947,14952,14955],{},[1362,14941,14942],{},[941,14943,1149],{},[1362,14945,14946],{},"the record's own key, AES-KW-wrapped",[1362,14948,14949,14951],{},[950,14950,14852],{}," — a wrapped key",[1362,14953,14954],{},"wrapped under the collection\u002Ftier DEK",[1362,14956,14957,964,14959],{},[941,14958,14552],{},[1007,14960,14961],{"href":14078},"above",[1344,14963,14964,14968,14971,14976,14979],{},[1362,14965,14966],{},[941,14967,1732],{},[1362,14969,14970],{},"deterministic equality index, per field",[1362,14972,14973,14975],{},[950,14974,14852],{}," — deterministic: equal values → equal ciphertext (acknowledged leak)",[1362,14977,14978],{},"HKDF(DEK) deterministic-index key",[1362,14980,14981,964,14983],{},[941,14982,14583],{},[1007,14984,14961],{"href":14078},[1344,14986,14987,14991,14994,14998,15001],{},[1362,14988,14989],{},[941,14990,1152],{},[1362,14992,14993],{},"per-field sealed ciphertext",[1362,14995,14996],{},[950,14997,14852],{},[1362,14999,15000],{},"HKDF per-field key from the CEK (or the DEK for legacy records)",[1362,15002,15003,964,15005],{},[941,15004,14672],{},[1007,15006,14961],{"href":15007},"#sealed-fields-_sealed",[1344,15009,15010,15015,15018,15026,15031],{},[1362,15011,15012],{},[941,15013,15014],{},"_vdig",[1362,15016,15017],{},"classified verify-digest blobs (digest + reuse ring)",[1362,15019,15020,15022,15023],{},[950,15021,14852],{}," — AAD-bound to ",[941,15024,15025],{},"[collection, record, field]",[1362,15027,15028,15029],{},"HKDF(CEK) vdig slot key — CEK-only, dies with ",[941,15030,1149],{},[1362,15032,15033,15034],{},"classified digest-only fields — ",[1007,15035,633],{"href":14740},[1344,15037,15038,15042,15045,15048,15051],{},[1362,15039,15040],{},[941,15041,13654],{},[1362,15043,15044],{},"classified equality tag (cost byte ‖ keyed MAC)",[1362,15046,15047],{},"not reversible ciphertext — a store-visible keyed MAC",[1362,15049,15050],{},"HMAC key HKDF-derived from the DEK",[1362,15052,15053,15054,964,15056],{},"classified ",[941,15055,13650],{},[1007,15057,633],{"href":14740},[1344,15059,15060,15064,15067,15070,15072],{},[1362,15061,15062],{},[941,15063,1737],{},[1362,15065,15066],{},"debug-plaintext marker; fields inlined beside it",[1362,15068,15069],{},"no — the whole envelope is plaintext",[1362,15071,3610],{},[1362,15073,15074,964,15077],{},[941,15075,15076],{},"debugPlaintext",[1007,15078,15080],{"href":15079},"#debug-plaintext-layout-debugplaintext","below",[1344,15082,15083,15088,15091,15093,15095],{},[1362,15084,15085],{},[941,15086,15087],{},"_del",[1362,15089,15090],{},"delete marker (empty body, version-ordered)",[1362,15092,11313],{},[1362,15094,3610],{},[1362,15096,15097,15098],{},"deletes under ",[1007,15099,864],{"href":15100},"..\u002F..\u002Fservices\u002Fsync",[937,15102,15103,15104,15106,15107,15109,15110,15113,15114,1625,15116,15119,15120,15123,15124,957],{},"Two neighbours of the record envelope keep their own slot sets: ",[941,15105,6715],{}," envelopes are ordinary envelopes (same slots — and the same ",[941,15108,1149],{},", which is what lets per-record erasure cover history), and blob ",[1092,15111,15112],{},"index"," objects carry their own ",[941,15115,1149],{},[941,15117,15118],{},"_cekPending"," wrapped content keys under the ",[941,15121,15122],{},"_blob"," DEK (",[1007,15125,621],{"href":15126},"..\u002F..\u002Fservices\u002Fblobs",[1022,15128,15130],{"id":15129},"key-lifecycle-rotation","Key lifecycle & rotation",[937,15132,15133],{},"Every key above has a rotation story. This is the whole map — the trigger, what actually gets re-encrypted or re-wrapped, and where each ceremony is documented:",[1338,15135,15136,15152],{},[1341,15137,15138],{},[1344,15139,15140,15143,15146,15149],{},[1347,15141,15142],{},"What rotates",[1347,15144,15145],{},"Trigger \u002F API",[1347,15147,15148],{},"What is re-wrapped or re-encrypted",[1347,15150,15151],{},"Where documented",[1357,15153,15154,15178,15201,15225,15254,15274,15301,15321,15341],{},[1344,15155,15156,15161,15171,15174],{},[1362,15157,15158],{},[950,15159,15160],{},"Secret → KEK",[1362,15162,15163,15166,15167,15170],{},[941,15164,15165],{},"db.team.rotateSecret(vault, input)"," — user remembers the old one; behind the ",[941,15168,15169],{},"rotate-secret"," policy gate",[1362,15172,15173],{},"a fresh salt and KEK are derived; the keyring's DEKs are unwrapped under the old KEK and re-wrapped under the new one — the DEKs themselves do not change",[1362,15175,15176],{},[1007,15177,69],{"href":11634},[1344,15179,15180,15185,15191,15194],{},[1362,15181,15182],{},[950,15183,15184],{},"Secret, forgotten",[1362,15186,15187,15190],{},[941,15188,15189],{},"db.team.recoverSecret(vault, input)"," — a recovery proof stands in for the old secret",[1362,15192,15193],{},"the DEK set is recovered via the enrolled profile and re-wrapped under the new secret's KEK",[1362,15195,15196,1368,15198],{},[1007,15197,69],{"href":11634},[1007,15199,15200],{"href":11641},"Recovery",[1344,15202,15203,15208,15218,15221],{},[1362,15204,15205],{},[950,15206,15207],{},"Managed secret",[1362,15209,15210,15213,15214,15217],{},[941,15211,15212],{},"db.team.recoverManagedSecret(vault, options)"," — in managed mode ",[941,15215,15216],{},"rotateSecret"," is unavailable; recovery is the sanctioned reset",[1362,15219,15220],{},"the machine-minted secret is replaced and re-sealed under the sealing-key provider (the provider itself is unchanged)",[1362,15222,15223],{},[1007,15224,15200],{"href":11641},[1344,15226,15227,15232,15243,15250],{},[1362,15228,15229],{},[950,15230,15231],{},"Collection DEKs",[1362,15233,15234,964,15236,15239,15240,15242],{},[941,15235,10795],{},[950,15237,15238],{},"other members lose access to the rotated collections","; or ",[941,15241,10726],{},", which always rotates when a member is removed",[1362,15244,15245,15246,15249],{},"fresh DEKs are minted, ",[950,15247,15248],{},"every record in each collection is re-encrypted",", and only the caller's keyring gets the new DEKs — every other member's entries for those collections are dropped and must be re-granted",[1362,15251,15252],{},[1007,15253,14279],{"href":14278},[1344,15255,15256,15261,15264,15267],{},[1362,15257,15258],{},[950,15259,15260],{},"HKDF-derived subkeys",[1362,15262,15263],{},"automatic — the presence key, the deterministic-index key, and the DEK-derived sealed-field keys are HKDF children of the collection DEK",[1362,15265,15266],{},"they rotate whenever their parent DEK rotates; a revoked user cannot derive the new presence key",[1362,15268,15269,15270],{},"this page — ",[1007,15271,15273],{"href":15272},"#zero-crypto-dependencies","primitives",[1344,15275,15276,15281,15290,15297],{},[1362,15277,15278],{},[950,15279,15280],{},"A record's CEK",[1362,15282,15283,15286,15287],{},[941,15284,15285],{},"vault.rotateRecordCek(collection, id)"," — directly, or via ",[941,15288,15289],{},"revokeSealedRecord(…, { hard: true })",[1362,15291,15292,15293,15296],{},"the live body is re-encrypted under a fresh CEK and every ",[941,15294,15295],{},"_sealed_cek"," delivery envelope for the record is deleted; history versions keep their original CEK",[1362,15298,15299],{},[1007,15300,824],{"href":13522},[1344,15302,15303,15308,15314,15317],{},[1362,15304,15305],{},[950,15306,15307],{},"Recovery material",[1362,15309,15310,15313],{},[941,15311,15312],{},"db.team.rotateRecovery(vault, options)"," — deliberate paper\u002FShamir regeneration",[1362,15315,15316],{},"the enrolled recovery entry is replaced — a reissued paper sheet or a re-shared Shamir entry",[1362,15318,15319],{},[1007,15320,15200],{"href":11641},[1344,15322,15323,15328,15333,15336],{},[1362,15324,15325],{},[950,15326,15327],{},"Broker seed",[1362,15329,15330],{},[941,15331,15332],{},"vault.broker().rotate()",[1362,15334,15335],{},"a new seed is registered before the old is dropped, with a grace window for in-flight proofs",[1362,15337,15338],{},[1007,15339,625],{"href":15340},"..\u002F..\u002Fservices\u002Fbroker",[1344,15342,15343,15348,15350,15356],{},[1362,15344,15345],{},[950,15346,15347],{},"Sealing provider id — never",[1362,15349,3610],{},[1362,15351,15352,15353,15355],{},"a provider's ",[941,15354,6983],{}," string is frozen once shipped; every sealed artefact names it forever",[1362,15357,15358],{},[1007,15359,14411],{"href":14410},[1022,15361,15363],{"id":15362},"invariants","Invariants",[937,15365,15366],{},"These are the bright lines, ordered from the outermost boundary inward. Any change to one of them is a security review.",[1338,15368,15369,15379],{},[1341,15370,15371],{},[1344,15372,15373,15376],{},[1347,15374,15375],{},"Invariant",[1347,15377,15378],{},"Enforcement",[1357,15380,15381,15397,15423,15446,15465,15475,15487],{},[1344,15382,15383,15388],{},[1362,15384,15385],{},[950,15386,15387],{},"Stores see only ciphertext",[1362,15389,15390,15391,1324,15394,15396],{},"Encryption happens in ",[941,15392,15393],{},"Collection.put",[1092,15395,11357],{}," the adapter call. No code path puts plaintext into a store.",[1344,15398,15399,15404],{},[1362,15400,15401],{},[950,15402,15403],{},"Authenticated decryption fails closed",[1362,15405,15406,15407,15409,15410,15415,15416,15418,15419,15422],{},"An envelope that does not authenticate throws ",[941,15408,11762],{},"; no partial-decryption fallback. ",[950,15411,15412,15414],{},[941,15413,11762],{}," is not by itself proof of an attack"," — read ",[941,15417,11063],{},", which distinguishes a genuine tamper from a record written before the ",[941,15420,15421],{},"0.6.0-pre.18"," format change.",[1344,15424,15425,15430],{},[1362,15426,15427],{},[950,15428,15429],{},"Per-record CEK is stable + DEK-scoped",[1362,15431,15432,15433,15435,15436,1625,15438,2419,15440,5584,15443,15445],{},"Under ",[941,15434,14552],{},", a record's CEK is reused across all its versions\u002Fhistory and is only ever AES-KW-wrapped under the collection\u002Ftier DEK (",[941,15437,1635],{},[941,15439,1638],{},[941,15441,15442],{},"capsule\u002Fenclave-aes\u002Fcrypto.ts",[941,15444,1732],{}," slots are never CEK-keyed.",[1344,15447,15448,15453],{},[1362,15449,15450],{},[950,15451,15452],{},"AES-256-GCM with random 12-byte IV per op",[1362,15454,15455,15457,15458,15461,15462,15464],{},[941,15456,15442],{}," generates IVs via ",[941,15459,15460],{},"crypto.getRandomValues(new Uint8Array(12))",". Never deterministic (except opt-in ",[941,15463,1732],{}," slots, whose equality leak is acknowledged per collection).",[1344,15466,15467,15472],{},[1362,15468,15469],{},[950,15470,15471],{},"AES-KW for DEK wrapping",[1362,15473,15474],{},"Standard RFC 3394. No custom KDF.",[1344,15476,15477,15482],{},[1362,15478,15479],{},[950,15480,15481],{},"KEK never on disk",[1362,15483,3325,15484,15486],{},[941,15485,13959],{}," records persist (which carry wrapped DEKs, not the KEK).",[1344,15488,15489,15494],{},[1362,15490,15491],{},[950,15492,15493],{},"PBKDF2-SHA256 600,000 iterations",[1362,15495,15496,15499],{},[941,15497,15498],{},"deriveKey()"," constant. Lowering this is a breaking security change.",[1022,15501,15503],{"id":15502},"plaintext-modes","Plaintext modes",[937,15505,15506],{},"Two dev-only escape hatches switch the enclave off. Both are explicit, loud, and never for production or client data.",[1591,15508,15510,15511,1381],{"id":15509},"plaintext-mode-encrypt-false","Plaintext mode (",[941,15512,11453],{},[937,15514,15515,15518,15519,15521],{},[941,15516,15517],{},"createNoydb({ encrypt: false })"," skips the crypto path entirely. Records are stored as raw JSON in ",[941,15520,1146],{},". Use only for testing \u002F debugging — no privacy guarantees.",[1591,15523,15525,15526,1381],{"id":15524},"debug-plaintext-layout-debugplaintext","Debug-plaintext layout (",[941,15527,15076],{},[937,15529,15530,15531,15534,15535,1324,15537,1324,15539,2311],{},"Zero-knowledge encryption is the default. During ",[950,15532,15533],{},"local development",", though, you sometimes want to point your store's native tools straight at the data. Open the vault with ",[941,15536,11453],{},[950,15538,14128],{},[941,15540,15541],{},"debugPlaintext: true",[2217,15543,15545],{"className":2219,"code":15544,"language":2221,"meta":1037,"style":1037},"import { createNoydb } from '@noy-db\u002Fhub'\nimport { toFile } from '@noy-db\u002Fto-file'\n\nconst db = await createNoydb({\n  store: toFile({ dir: '.\u002Fdata' }),\n  user: 'dev',\n  encrypt: false,\n  debugPlaintext: true,\n})\n",[941,15546,15547,15565,15584,15588,15604,15635,15650,15661,15672],{"__ignoreMap":1037},[2225,15548,15549,15551,15553,15555,15557,15559,15561,15563],{"class":2227,"line":2228},[2225,15550,2232],{"class":2231},[2225,15552,2236],{"class":2235},[2225,15554,2744],{"class":2239},[2225,15556,2243],{"class":2235},[2225,15558,2246],{"class":2231},[2225,15560,2249],{"class":2235},[2225,15562,1086],{"class":2252},[2225,15564,2256],{"class":2235},[2225,15566,15567,15569,15571,15574,15576,15578,15580,15582],{"class":2227,"line":1041},[2225,15568,2232],{"class":2231},[2225,15570,2236],{"class":2235},[2225,15572,15573],{"class":2239}," toFile",[2225,15575,2243],{"class":2235},[2225,15577,2246],{"class":2231},[2225,15579,2249],{"class":2235},[2225,15581,9551],{"class":2252},[2225,15583,2256],{"class":2235},[2225,15585,15586],{"class":2227,"line":1038},[2225,15587,2285],{"emptyLinePlaceholder":2284},[2225,15589,15590,15592,15594,15596,15598,15600,15602],{"class":2227,"line":2288},[2225,15591,2775],{"class":2774},[2225,15593,2778],{"class":2239},[2225,15595,2781],{"class":2235},[2225,15597,2784],{"class":2231},[2225,15599,2744],{"class":2291},[2225,15601,2789],{"class":2239},[2225,15603,4200],{"class":2235},[2225,15605,15606,15609,15611,15613,15615,15617,15620,15622,15624,15627,15629,15631,15633],{"class":2227,"line":2304},[2225,15607,15608],{"class":2307},"  store",[2225,15610,2311],{"class":2235},[2225,15612,15573],{"class":2291},[2225,15614,2789],{"class":2239},[2225,15616,2792],{"class":2235},[2225,15618,15619],{"class":2307}," dir",[2225,15621,2311],{"class":2235},[2225,15623,2249],{"class":2235},[2225,15625,15626],{"class":2252},".\u002Fdata",[2225,15628,2805],{"class":2235},[2225,15630,2243],{"class":2235},[2225,15632,1381],{"class":2239},[2225,15634,1707],{"class":2235},[2225,15636,15637,15639,15641,15643,15646,15648],{"class":2227,"line":2356},[2225,15638,4205],{"class":2307},[2225,15640,2311],{"class":2235},[2225,15642,2249],{"class":2235},[2225,15644,15645],{"class":2252},"dev",[2225,15647,2805],{"class":2235},[2225,15649,1707],{"class":2235},[2225,15651,15652,15655,15657,15659],{"class":2227,"line":2830},[2225,15653,15654],{"class":2307},"  encrypt",[2225,15656,2311],{"class":2235},[2225,15658,2341],{"class":2321},[2225,15660,1707],{"class":2235},[2225,15662,15663,15666,15668,15670],{"class":2227,"line":2861},[2225,15664,15665],{"class":2307},"  debugPlaintext",[2225,15667,2311],{"class":2235},[2225,15669,2322],{"class":2321},[2225,15671,1707],{"class":2235},[2225,15673,15674,15676],{"class":2227,"line":4246},[2225,15675,2359],{"class":2235},[2225,15677,2362],{"class":2239},[937,15679,15680,15681,15684],{},"This changes the on-store ",[1092,15682,15683],{},"layout"," so it's directly legible:",[1111,15686,15687,15710],{},[1114,15688,15689,15692,15693,15696,15697,15699,15700,15702,15703,15706,15707,15709],{},[950,15690,15691],{},"Records"," are written with their fields inlined beside the envelope metadata (",[941,15694,15695],{},"_debug: 1",", empty ",[941,15698,1146],{},") instead of stuffed into ",[941,15701,1146],{}," as a JSON string — so ",[941,15704,15705],{},"jq '.total'"," over a ",[941,15708,7461],{}," object resolves directly.",[1114,15711,15712,15715,15716,15718,15719,957],{},[950,15713,15714],{},"Blobs"," are written as a single un-gzipped object — the chunk's base64 ",[941,15717,1146],{}," decodes straight to the original bytes (",[941,15720,15721],{},"base64 -d",[937,15723,15724,15725,15728,15729,15732],{},"To unwrap an envelope programmatically (the core of a ",[941,15726,15727],{},"noydb cat"," script), use ",[941,15730,15731],{},"readPlaintextRecord(envelope)"," — it handles both the inlined and the classic plaintext layouts, and throws on an encrypted envelope:",[2217,15734,15736],{"className":2219,"code":15735,"language":2221,"meta":1037,"style":1037},"import { readFileSync } from 'node:fs'\nimport { readPlaintextRecord } from '@noy-db\u002Fhub\u002Fdebug'\n\nconst env = JSON.parse(readFileSync('data\u002Facme\u002Finvoices\u002Finv-1.json', 'utf8'))\nreadPlaintextRecord(env)\n\u002F\u002F → { id: 'inv-1', total: '120.00', … }\n",[941,15737,15738,15758,15778,15782,15824,15832],{"__ignoreMap":1037},[2225,15739,15740,15742,15744,15747,15749,15751,15753,15756],{"class":2227,"line":2228},[2225,15741,2232],{"class":2231},[2225,15743,2236],{"class":2235},[2225,15745,15746],{"class":2239}," readFileSync",[2225,15748,2243],{"class":2235},[2225,15750,2246],{"class":2231},[2225,15752,2249],{"class":2235},[2225,15754,15755],{"class":2252},"node:fs",[2225,15757,2256],{"class":2235},[2225,15759,15760,15762,15764,15767,15769,15771,15773,15776],{"class":2227,"line":1041},[2225,15761,2232],{"class":2231},[2225,15763,2236],{"class":2235},[2225,15765,15766],{"class":2239}," readPlaintextRecord",[2225,15768,2243],{"class":2235},[2225,15770,2246],{"class":2231},[2225,15772,2249],{"class":2235},[2225,15774,15775],{"class":2252},"@noy-db\u002Fhub\u002Fdebug",[2225,15777,2256],{"class":2235},[2225,15779,15780],{"class":2227,"line":1038},[2225,15781,2285],{"emptyLinePlaceholder":2284},[2225,15783,15784,15786,15789,15791,15794,15796,15799,15801,15804,15806,15808,15811,15813,15815,15817,15820,15822],{"class":2227,"line":2288},[2225,15785,2775],{"class":2774},[2225,15787,15788],{"class":2239}," env ",[2225,15790,2781],{"class":2235},[2225,15792,15793],{"class":2239}," JSON",[2225,15795,1520],{"class":2235},[2225,15797,15798],{"class":2291},"parse",[2225,15800,2789],{"class":2239},[2225,15802,15803],{"class":2291},"readFileSync",[2225,15805,2789],{"class":2239},[2225,15807,2805],{"class":2235},[2225,15809,15810],{"class":2252},"data\u002Facme\u002Finvoices\u002Finv-1.json",[2225,15812,2805],{"class":2235},[2225,15814,2298],{"class":2235},[2225,15816,2249],{"class":2235},[2225,15818,15819],{"class":2252},"utf8",[2225,15821,2805],{"class":2235},[2225,15823,8606],{"class":2239},[2225,15825,15826,15829],{"class":2227,"line":2304},[2225,15827,15828],{"class":2291},"readPlaintextRecord",[2225,15830,15831],{"class":2239},"(env)\n",[2225,15833,15834],{"class":2227,"line":2356},[2225,15835,15836],{"class":2763},"\u002F\u002F → { id: 'inv-1', total: '120.00', … }\n",[937,15838,15839],{},"Guardrails:",[1111,15841,15842,15851,15857],{},[1114,15843,15844,15846,15847,15850],{},[941,15845,15541],{}," combined with encryption throws ",[941,15848,15849],{},"DebugPlaintextError"," at construction — it is an unencrypted-only inspection mode.",[1114,15852,15853,15854,1520],{},"Opening a vault in this mode prints a loud ",[941,15855,15856],{},"console.warn",[1114,15858,15859,15860,15863,15864,15866],{},"A CI architecture check (",[941,15861,15862],{},"no-debug-plaintext-in-source",") forbids hardcoding ",[941,15865,15541],{}," in shipped library source.",[937,15868,15869,15872],{},[950,15870,15871],{},"Never use this for production or client data."," It stores everything in cleartext and is not crypto-shreddable.",[1022,15874,15876],{"id":15875},"encryption-elsewhere-in-the-family","Encryption elsewhere in the family",[937,15878,15879],{},"The record path above is not the only place the family does real cryptography. The same zero-dependency Web Crypto discipline covers every one of these:",[1111,15881,15882,15901,15915,15928,15986,15996,16005,16023,16036],{},[1114,15883,15884,15887,15888,15890,15891,15894,15895,15898,15899,1520],{},[950,15885,15886],{},"Blobs."," Binary attachments are chunked and each chunk encrypted with AES-256-GCM under a dedicated ",[941,15889,15122],{}," collection DEK, with the chunk's parent blob and position bound in as additional authenticated data (",[941,15892,15893],{},"{eTag}:{chunkIndex}:{chunkCount}",") — so chunks cannot be reordered, substituted, or truncated undetected. The dedup address is ",[941,15896,15897],{},"eTag = HMAC-SHA256(blob DEK, plaintext)",", keyed so an observer with store access cannot pre-compute eTags for known files. See ",[1007,15900,621],{"href":15126},[1114,15902,15903,1324,15906,15910,15911,15914],{},[950,15904,15905],{},"Magic links.",[1007,15907,15909],{"href":15908},"..\u002F..\u002Ffamilies\u002Fon\u002Fmagic-link","on-magic-link"," grants read-only access from a single-use URL that carries only an opaque token — no key material. Server and client each derive the ",[1092,15912,15913],{},"same viewer KEK"," via HKDF-SHA256 from a server-held secret salted with the token's hash; that KEK unlocks a purpose-built viewer keyring directly (bypassing PBKDF2), so it covers only the DEKs granted to the viewer.",[1114,15916,15917,15920,15921,15923,15924,15927],{},[950,15918,15919],{},"Credential broker."," The ",[1007,15922,625],{"href":15340}," authenticates a client to a remote credential-minting host without the host ever holding a secret: a 32-byte seed is stored encrypted in the reserved ",[941,15925,15926],{},"_broker"," collection under its own DEK, an HKDF-SHA256-derived proof key HMAC-SHA256-signs the host's challenge, and the host verifies the signature (constant-time) against a short-TTL challenge.",[1114,15929,15930,15933,15934,15940,15941,15945,15946,15949,15950,15954,15955,15959,15960,15962,15963,15965,15966,15970,15971,15975,15976,15980,15981,15985],{},[950,15931,15932],{},"Unlock factors."," Each ",[1007,15935,15937,15939],{"href":15936},"..\u002F..\u002Fservices\u002Fauth-landscape",[941,15938,1787],{}," method"," wraps or proves with real primitives of its own: ",[1007,15942,15944],{"href":15943},"..\u002F..\u002Ffamilies\u002Fon\u002Fwebauthn","on-webauthn"," derives an AES-GCM wrapping key via HKDF-SHA256 from the passkey's PRF output (falling back to the credential's ",[941,15947,15948],{},"rawId"," on authenticators without PRF); ",[1007,15951,15953],{"href":15952},"..\u002F..\u002Ffamilies\u002Fon\u002Fpassword","on-password"," wraps the DEK set under PBKDF2-SHA256 (600,000 iterations); ",[1007,15956,15958],{"href":15957},"..\u002F..\u002Ffamilies\u002Fon\u002Fpin","on-pin"," quick-unlock wraps ",[1092,15961,1320],{}," the session's cached DEK map (never the KEK) under a PIN-derived AES-GCM key (PBKDF2, 100,000 iterations, attempt-bounded + TTL — see ",[1007,15964,844],{"href":12844},"); ",[1007,15967,15969],{"href":15968},"..\u002F..\u002Ffamilies\u002Fon\u002Foidc","on-oidc"," XOR-splits the key payload into a device half (HKDF-derived from a per-device random secret that never leaves the device) and a server half encrypted under a key derived from the ID token; ",[1007,15972,15974],{"href":15973},"..\u002F..\u002Ffamilies\u002Fon\u002Ftotp","on-totp"," is RFC 6238 HMAC; ",[1007,15977,15979],{"href":15978},"..\u002F..\u002Ffamilies\u002Fon\u002Femail-otp","on-email-otp"," stores only salted SHA-256 digests of codes; ",[1007,15982,15984],{"href":15983},"..\u002F..\u002Ffamilies\u002Fon\u002Fthreat","on-threat"," detects duress secrets via a PBKDF2 hash compared in constant time.",[1114,15987,15988,1324,15991,15995],{},[950,15989,15990],{},"Shamir secret sharing.",[1007,15992,15994],{"href":15993},"..\u002F..\u002Ffamilies\u002Fon\u002Fshamir","on-shamir"," splits the KEK into k-of-n shares with genuine Shamir secret sharing over GF(2⁸) — any k shares reconstruct it by Lagrange interpolation; fewer than k leak zero bits.",[1114,15997,15998,15920,16001,16004],{},[950,15999,16000],{},"Attestations.",[1007,16002,609],{"href":16003},"..\u002F..\u002Fservices\u002Fattestation"," package signs selective-disclosure attestations with Ed25519 over a canonical core of salted SHA-256 field hashes — a verifier checks that a record contained given field values without seeing the others.",[1114,16006,16007,1324,16010,16012,16013,16015,16016,16018,16019,16022],{},[950,16008,16009],{},"Classified blind index.",[1007,16011,633],{"href":14740}," fields marked ",[941,16014,13650],{}," get an HMAC-SHA256 equality tag under an HKDF-derived index key — searchable without decryption. (The related deterministic ",[941,16017,1732],{}," slots are covered under ",[1007,16020,16021],{"href":14078},"Per-record keys"," above.)",[1114,16024,16025,16028,16029,16032,16033,16035],{},[950,16026,16027],{},"Integrity chains."," The audit ",[1007,16030,776],{"href":16031},"..\u002F..\u002Fservices\u002Fperiods"," ledger is a tamper-evident SHA-256 hash chain — integrity, not secrecy — and the ",[1007,16034,685],{"href":14655}," subject index pseudonymizes subject ids as HMAC-SHA256 under a dedicated index DEK.",[1114,16037,16038,1324,16041,16045,16046,16048],{},[950,16039,16040],{},"Encrypted ZIP export.",[1007,16042,16044],{"href":16043},"..\u002F..\u002Ffamilies\u002Fas\u002Fzip","as-zip"," can optionally encrypt the archive it produces in the standard WinZip-AES-256 format under a user-supplied password — protection for the exported ",[1092,16047,383],{},", orthogonal to the vault's own keys.",[937,16050,960,16051,16053,16054,968,16058,16062,16063,16066],{},[941,16052,1795],{}," session-share transports (",[1007,16055,16057],{"href":16056},"..\u002F..\u002Ffamilies\u002Fby\u002Fpeer","by-peer",[1007,16059,16061],{"href":16060},"..\u002F..\u002Ffamilies\u002Fby\u002Ftabs","by-tabs",") deliberately have ",[950,16064,16065],{},"no crypto of their own"," — they carry the already-encrypted envelopes and rely on the transport layer (DTLS, same-origin) for the wire, exactly as the enclave boundary intends.",[1022,16068,2621],{"id":2620},[1111,16070,16071,16078,16083,16088,16093,16099],{},[1114,16072,16073,16077],{},[1007,16074,16076],{"href":16075},"..\u002F05-stores","Core 05 — Stores"," — the contract that holds these invariants in place",[1114,16079,16080,16082],{},[1007,16081,69],{"href":11634}," — the secret, the single-user keyring, and its primitives",[1114,16084,16085,16087],{},[1007,16086,14279],{"href":14278}," — how wrapped DEKs are granted and revoked",[1114,16089,16090,16092],{},[1007,16091,13607],{"href":13606}," — the architecture of the crypto interior",[1114,16094,16095,16098],{},[1007,16096,25],{"href":16097},"..\u002F..\u002Farchitecture\u002Fkernel\u002Fforking-the-enclave"," — the frozen contract a replacement enclave must satisfy",[1114,16100,16101,16105],{},[1007,16102,16104],{"href":16103},"\u002Ffaq\u002Fthreat-model#reporting-a-vulnerability","Reporting a vulnerability"," — how to disclose privately",[2645,16107,16108],{},"html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .sfNiH, html code.shiki .sfNiH{--shiki-light:#FF5370;--shiki-default:#FF9CAC;--shiki-dark:#FF9CAC}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}",{"title":1037,"searchDepth":1038,"depth":1038,"links":16110},[16111,16117,16118,16123,16124,16125,16129,16130,16131,16132,16138,16139],{"id":13433,"depth":1041,"text":13434,"children":16112},[16113,16115],{"id":13468,"depth":1038,"text":16114},"at-* — delegated decryption",{"id":13548,"depth":1038,"text":16116},"as-* — the designed output channel",{"id":13664,"depth":1041,"text":13665},{"id":13811,"depth":1041,"text":13812,"children":16119},[16120,16121,16122],{"id":13815,"depth":1038,"text":13816},{"id":13899,"depth":1038,"text":13900},{"id":1659,"depth":1038,"text":13989},{"id":14261,"depth":1041,"text":14262},{"id":14334,"depth":1041,"text":13527},{"id":14414,"depth":1041,"text":14079,"children":16126},[16127],{"id":14663,"depth":1038,"text":16128},"Sealed fields (_sealed)",{"id":14744,"depth":1041,"text":14745},{"id":15129,"depth":1041,"text":15130},{"id":15362,"depth":1041,"text":15363},{"id":15502,"depth":1041,"text":15503,"children":16133},[16134,16136],{"id":15509,"depth":1038,"text":16135},"Plaintext mode (encrypt: false)",{"id":15524,"depth":1038,"text":16137},"Debug-plaintext layout (debugPlaintext)",{"id":15875,"depth":1041,"text":15876},{"id":2620,"depth":1041,"text":2621},"The enclave — the trusted region inside the hub where keys and plaintext exist at all: the KEK\u002FDEK\u002FCEK hierarchy, the envelope, sealing, and invariants.",{},{"title":21,"description":16140},"cGOSC0aFIA2ifJ2spObMhzGMVr1QdN1ywcEw8Usf0Dc",{"id":16145,"title":69,"access":12,"apiToc":2284,"body":16146,"category":1042,"description":16950,"extension":1044,"meta":16951,"navigation":1042,"nest":2635,"path":70,"seo":16952,"sinceVersion":1042,"status":1049,"stem":71,"subtitle":1042,"tier":6922,"tocExternal":1042,"__hash__":16953},"docs\u002Fcore\u002F2.noydb\u002F3.keyring.md",{"type":931,"value":16147,"toc":16936},[16148,16151,16156,16203,16217,16244,16267,16270,16276,16282,16342,16345,16387,16393,16433,16439,16479,16485,16531,16550,16588,16594,16613,16620,16627,16631,16649,16655,16675,16678,16685,16691,16731,16759,16808,16820,16829,16843,16890,16913,16933],[934,16149,69],{"id":16150},"secret-keyring",[937,16152,16153,16154,1520],{},"Every vault is unlocked by one ",[950,16155,10695],{},[7646,16157,16159,16168,16191],{"color":7648,"icon":16158},"i-lucide-key-round",[937,16160,16161,16164,16165,16167],{},[950,16162,16163],{},"What counts as a secret."," noy-db never inspects ",[1092,16166,13638],{}," your secret is — only that it can derive\na key from it. It may be a passphrase a person types, a generated code, a certificate, or anything\nelse. Two properties are what matter:",[1111,16169,16170,16176],{},[1114,16171,16172,16175],{},[950,16173,16174],{},"Hard to guess."," The secret is the whole of the vault's security; there is no server-side\naccount to fall back on, and no one to appeal to if it is guessed.",[1114,16177,16178,16181,16182,16187,16188,16190],{},[950,16179,16180],{},"Held in the owner's memory where possible"," — the one store nobody else can read, copy, or\nsubpoena. Where memory isn't practical (machine access, long random values), the\n",[1007,16183,16184],{"href":170},[941,16185,16186],{},"@noy-db\u002Fat-*"," hosts seal the secret to infrastructure you control, and\n",[1007,16189,321],{"href":11641}," covers the case where it is lost.",[937,16192,16193,16194,16196,16197,16199,16200,16202],{},"The docs say ",[1092,16195,10695],{}," throughout because that is the API's name for it (",[941,16198,10695],{}," on\n",[941,16201,2134],{},") and because \"passphrase\" would narrow it to one of the forms it can take.",[937,16204,16205,16206,10313,16208,16210,16211,16213,16214,16216],{},"In standard mode that is the secret you pass as ",[941,16207,10695],{},[941,16209,2134],{},"; the hub derives a key-encryption key (KEK) from it in memory via PBKDF2 and never writes it to disk, so storage backends only ever see ciphertext (see ",[1007,16212,21],{"href":2628},"). WebAuthn, OIDC and Shamir supply the unlocked keyring through the ",[941,16215,9953],{}," option instead, so no secret is derived at all.",[937,16218,16219,16220,1169,16223,16226,16227,16229,16230,16232,16233,16235,16236,16238,16239,16241,16242,1520],{},"A vault's minimum is a ",[950,16221,16222],{},"single-owner keyring",[941,16224,16225],{},"user"," you name at ",[941,16228,2134],{}," becomes the owner on the first open of a new vault. ",[941,16231,16225],{}," is required configuration, but a personal vault needs nothing more: single-user is the default, and multi-user membership is opt-in team territory. Grant \u002F revoke \u002F rotate route through the team service and throw ",[941,16234,10329],{}," until you pass ",[941,16237,3197],{}," (see ",[1007,16240,85],{"href":11645},"); every primitive on this page works with ",[950,16243,11628],{},[937,16245,960,16246,16248,16249,16251,16252,1261,16254,1261,16256,1261,16258,1261,16260,1261,16262,16264,16265,1520],{},[950,16247,2705],{}," is the per-user record at ",[941,16250,9871],{}," that is both the access-control list and the cryptographic gate — what you can read is exactly what your keyring can unwrap. It holds your role (",[941,16253,9898],{},[941,16255,9901],{},[941,16257,9904],{},[941,16259,9907],{},[941,16261,9910],{},[941,16263,9913],{},"), your per-collection permissions, the wrapped DEKs (one AES-KW-wrapped data-encryption key per collection you can decrypt), and the salt used to derive your KEK from the secret. The full grant\u002Frevoke key ceremony and role model live on ",[1007,16266,14279],{"href":14278},[1022,16268,69],{"id":16269},"secret-keyring-1",[937,16271,16272,16273,16275],{},"Change, rotate, recover, and read the single-user keyring. Each is a single-user primitive — always available, no strategy required; the sensitive ones sit behind a policy gate, not a ",[941,16274,943],{}," opt-in.",[1591,16277,16279],{"id":16278},"changesecret",[941,16280,16281],{},"changeSecret()",[2217,16283,16285],{"className":2219,"code":16284,"language":2221,"meta":1037,"style":1037},"changeSecret(\n  vault: string,\n  newSecret: string,\n  options?: SecretPolicy & {\n    allowWeakSecret?: boolean\n  }\n): Promise\u003Cvoid>\n",[941,16286,16287,16294,16300,16307,16321,16330,16334],{"__ignoreMap":1037},[2225,16288,16289,16292],{"class":2227,"line":2228},[2225,16290,16291],{"class":2291},"changeSecret",[2225,16293,11683],{"class":2239},[2225,16295,16296,16298],{"class":2227,"line":1041},[2225,16297,12912],{"class":2239},[2225,16299,1707],{"class":2235},[2225,16301,16302,16305],{"class":2227,"line":1038},[2225,16303,16304],{"class":2239},"  newSecret: string",[2225,16306,1707],{"class":2235},[2225,16308,16309,16311,16313,16316,16319],{"class":2227,"line":2288},[2225,16310,11872],{"class":2239},[2225,16312,7011],{"class":2235},[2225,16314,16315],{"class":2239}," SecretPolicy ",[2225,16317,16318],{"class":2235},"&",[2225,16320,2301],{"class":2235},[2225,16322,16323,16326,16328],{"class":2227,"line":2304},[2225,16324,16325],{"class":2239},"    allowWeakSecret?",[2225,16327,2311],{"class":2235},[2225,16329,11718],{"class":2239},[2225,16331,16332],{"class":2227,"line":2356},[2225,16333,7632],{"class":2235},[2225,16335,16336,16338,16340],{"class":2227,"line":2830},[2225,16337,11737],{"class":2239},[2225,16339,11740],{"class":6342},[2225,16341,13016],{"class":2235},[937,16343,16344],{},"Change the current user's secret for a vault. The new secret is validated against the strength rules unless you opt out.",[1111,16346,16347,16352,16358],{},[1114,16348,16349,16351],{},[941,16350,2694],{}," — the vault whose secret to change.",[1114,16353,16354,16357],{},[941,16355,16356],{},"newSecret"," — the replacement secret, validated against the strength rules.",[1114,16359,16360,12536,16363,16366,16367,16370,16371],{},[941,16361,16362],{},"options",[941,16364,16365],{},"SecretPolicy"," (plus ",[941,16368,16369],{},"allowWeakSecret",") tuning validation:\n",[1111,16372,16373,16378],{},[1114,16374,16375,16377],{},[941,16376,16369],{}," — skip strength validation entirely (typically only for fixtures and migrations).",[1114,16379,16380,1261,16383,16386],{},[941,16381,16382],{},"pattern",[941,16384,16385],{},"customValidator"," — override the default character-class rule, or replace validation wholesale with a custom function.",[1591,16388,16390],{"id":16389},"rotatesecret",[941,16391,16392],{},"rotateSecret()",[2217,16394,16396],{"className":2219,"code":16395,"language":2221,"meta":1037,"style":1037},"rotateSecret(\n  vault: string,\n  input: RotateSecretInput,\n  factors?: FactorProofBundle\n): Promise\u003Cvoid>\n",[941,16397,16398,16404,16410,16417,16425],{"__ignoreMap":1037},[2225,16399,16400,16402],{"class":2227,"line":2228},[2225,16401,15216],{"class":2291},[2225,16403,11683],{"class":2239},[2225,16405,16406,16408],{"class":2227,"line":1041},[2225,16407,12912],{"class":2239},[2225,16409,1707],{"class":2235},[2225,16411,16412,16415],{"class":2227,"line":1038},[2225,16413,16414],{"class":2239},"  input: RotateSecretInput",[2225,16416,1707],{"class":2235},[2225,16418,16419,16421,16423],{"class":2227,"line":2288},[2225,16420,13002],{"class":2239},[2225,16422,7011],{"class":2235},[2225,16424,13007],{"class":2239},[2225,16426,16427,16429,16431],{"class":2227,"line":2304},[2225,16428,11737],{"class":2239},[2225,16430,11740],{"class":6342},[2225,16432,13016],{"class":2235},[937,16434,16435,16436,16438],{},"Rotate the secret when the user still remembers the old one. A single-user primitive — it stays available with no strategy. Sensitive enough to sit behind the ",[941,16437,15169],{}," policy gate, so a tightened policy can demand fresh factor proofs.",[1111,16440,16441,16446,16471],{},[1114,16442,16443,16445],{},[941,16444,2694],{}," — the vault whose secret to rotate.",[1114,16447,16448,12536,16451,16454,16455,997,16458,16460,16461,1261,16464,16466,16467,16470],{},[941,16449,16450],{},"input",[941,16452,16453],{},"RotateSecretInput"," carrying the ",[941,16456,16457],{},"oldSecret",[941,16459,16356],{}," (plus optional ",[941,16462,16463],{},"secretPolicy",[941,16465,16369],{}," and per-slot ",[941,16468,16469],{},"slotCeremonies"," to preserve tier-2 authenticators across the rotation).",[1114,16472,16473,16475,16476,16478],{},[941,16474,13051],{}," — factor proofs presented to satisfy the ",[941,16477,15169],{}," gate when a tightened policy demands them.",[1591,16480,16482],{"id":16481},"recoversecret",[941,16483,16484],{},"recoverSecret()",[2217,16486,16488],{"className":2219,"code":16487,"language":2221,"meta":1037,"style":1037},"recoverSecret(\n  vault: string,\n  input: RecoverSecretInput,\n  factors?: FactorProofBundle\n): Promise\u003CRecoverSecretResult>\n",[941,16489,16490,16497,16503,16510,16518],{"__ignoreMap":1037},[2225,16491,16492,16495],{"class":2227,"line":2228},[2225,16493,16494],{"class":2291},"recoverSecret",[2225,16496,11683],{"class":2239},[2225,16498,16499,16501],{"class":2227,"line":1041},[2225,16500,12912],{"class":2239},[2225,16502,1707],{"class":2235},[2225,16504,16505,16508],{"class":2227,"line":1038},[2225,16506,16507],{"class":2239},"  input: RecoverSecretInput",[2225,16509,1707],{"class":2235},[2225,16511,16512,16514,16516],{"class":2227,"line":2288},[2225,16513,13002],{"class":2239},[2225,16515,7011],{"class":2235},[2225,16517,13007],{"class":2239},[2225,16519,16520,16522,16524,16526,16529],{"class":2227,"line":2304},[2225,16521,11737],{"class":2239},[2225,16523,11740],{"class":6342},[2225,16525,6474],{"class":2235},[2225,16527,16528],{"class":2239},"RecoverSecretResult",[2225,16530,7093],{"class":2235},[937,16532,16533,16534,16537,16538,16541,16542,16544,16545,16547,16548,1520],{},"Reset the secret using a recovery proof rather than the old secret. Also a single-user primitive. The ",[941,16535,16536],{},"'paper'"," recovery profile is supported end-to-end; calling it against a profile that only ships its API shape throws ",[941,16539,16540],{},"RecoveryProfileNotImplementedError",". Enrolling recovery profiles is a ",[941,16543,321],{}," config concern (see ",[1007,16546,81],{"href":13339},"), and the recovery-profile lifecycle and flows are covered on ",[1007,16549,77],{"href":11641},[1111,16551,16552,16557,16580],{},[1114,16553,16554,16556],{},[941,16555,2694],{}," — the vault whose secret to reset.",[1114,16558,16559,12536,16561,16454,16564,997,16567,16460,16569,1261,16571,2462,16573,16576,16577,16579],{},[941,16560,16450],{},[941,16562,16563],{},"RecoverSecretInput",[941,16565,16566],{},"recoveryProof",[941,16568,16356],{},[941,16570,16463],{},[941,16572,16369],{},[941,16574,16575],{},"rotateRemainingCodes"," — default ",[941,16578,11790],{}," — to re-mint the remaining recovery codes after a successful paper recovery).",[1114,16581,16582,16475,16584,16587],{},[941,16583,13051],{},[941,16585,16586],{},"recover-secret"," gate when the policy demands them.",[1591,16589,16591],{"id":16590},"getkeyring",[941,16592,16593],{},"getKeyring()",[2217,16595,16597],{"className":2219,"code":16596,"language":2221,"meta":1037,"style":1037},"getKeyring(vault: string): Promise\u003CUnlockedKeyring>\n",[941,16598,16599],{"__ignoreMap":1037},[2225,16600,16601,16603,16605,16607,16609,16611],{"class":2227,"line":2228},[2225,16602,9953],{"class":2291},[2225,16604,12765],{"class":2239},[2225,16606,11740],{"class":6342},[2225,16608,6474],{"class":2235},[2225,16610,11493],{"class":2239},[2225,16612,7093],{"class":2235},[937,16614,16615,16616,16619],{},"Return a ",[950,16617,16618],{},"defensive copy"," of the unlocked keyring for a vault (roles, permissions, wrapped DEKs). Reading the owner keyring is a single-user primitive and stays ungated.",[1111,16621,16622],{},[1114,16623,16624,16626],{},[941,16625,2694],{}," — the vault whose unlocked keyring to copy.",[1022,16628,16630],{"id":16629},"users","Users",[937,16632,16633,16634,997,16636,16639,16640,16643,16644,16646,16647,957],{},"The users home for a vault: enumerate members and rewrite a member's post-grant identity fields. ",[941,16635,10264],{},[941,16637,16638],{},"updateUser"," are always-on primitives; the multi-user ",[1092,16641,16642],{},"grant \u002F revoke \u002F rotate"," ceremony is the opt-in ",[1007,16645,868],{"href":14283}," service (see ",[1007,16648,85],{"href":11645},[1591,16650,16652],{"id":16651},"listusers",[941,16653,16654],{},"listUsers()",[2217,16656,16658],{"className":2219,"code":16657,"language":2221,"meta":1037,"style":1037},"listUsers(vault: string): Promise\u003CUserInfo[]>\n",[941,16659,16660],{"__ignoreMap":1037},[2225,16661,16662,16664,16666,16668,16670,16673],{"class":2227,"line":2228},[2225,16663,10264],{"class":2291},[2225,16665,12765],{"class":2239},[2225,16667,11740],{"class":6342},[2225,16669,6474],{"class":2235},[2225,16671,16672],{"class":2239},"UserInfo[]",[2225,16674,7093],{"class":2235},[937,16676,16677],{},"List every user with access to a vault.",[1111,16679,16680],{},[1114,16681,16682,16684],{},[941,16683,2694],{}," — the vault whose users to list.",[1591,16686,16688],{"id":16687},"updateuser",[941,16689,16690],{},"updateUser()",[2217,16692,16694],{"className":2219,"code":16693,"language":2221,"meta":1037,"style":1037},"updateUser(\n  vault: string,\n  options: UpdateUserOptions,\n  factors?: FactorProofBundle\n): Promise\u003Cvoid>\n",[941,16695,16696,16702,16708,16715,16723],{"__ignoreMap":1037},[2225,16697,16698,16700],{"class":2227,"line":2228},[2225,16699,16638],{"class":2291},[2225,16701,11683],{"class":2239},[2225,16703,16704,16706],{"class":2227,"line":1041},[2225,16705,12912],{"class":2239},[2225,16707,1707],{"class":2235},[2225,16709,16710,16713],{"class":2227,"line":1038},[2225,16711,16712],{"class":2239},"  options: UpdateUserOptions",[2225,16714,1707],{"class":2235},[2225,16716,16717,16719,16721],{"class":2227,"line":2288},[2225,16718,13002],{"class":2239},[2225,16720,7011],{"class":2235},[2225,16722,13007],{"class":2239},[2225,16724,16725,16727,16729],{"class":2227,"line":2304},[2225,16726,11737],{"class":2239},[2225,16728,11740],{"class":6342},[2225,16730,13016],{"class":2235},[937,16732,16733,16734,6030,16736,16738,16739,16741,16742,16745,16746,16748,16749,16752,16753,16755,16756,16758],{},"Mutate post-grant identity fields on an existing keyring. It's a pure plaintext-header rewrite: no DEK re-wrap, no KEK needed, no authenticator or tier-2 slots touched, and — unlike ",[941,16735,10322],{},[941,16737,10319],{}," — the same ",[941,16740,10749],{},", DEK wrappings, and ",[941,16743,16744],{},"granted_by"," survive; only the specified header fields move (last-write-wins). Demoting a role does ",[950,16747,1004],{}," cascade (that's a revoke concern). A role-elevation guard runs regardless of policy: owner can do anything, an admin manages admin\u002Foperator\u002Fviewer\u002Fclient laterally but can neither promote to owner nor demote from owner. Gated by the ",[941,16750,16751],{},"update-user"," policy (a ",[941,16754,12834],{}," demands a TOTP \u002F email-OTP proof; ",[941,16757,12830],{}," accepts a tier-1 unlock).",[1111,16760,16761,16766,16802],{},[1114,16762,16763,16765],{},[941,16764,2694],{}," — the vault whose keyring to mutate.",[1114,16767,16768,13535,16770,16773,16774],{},[941,16769,16362],{},[941,16771,16772],{},"UpdateUserOptions"," naming the target and the fields to move:\n",[1111,16775,16776,16781,16787,16796],{},[1114,16777,16778,16780],{},[941,16779,10749],{}," — the principal whose keyring to update (required).",[1114,16782,16783,16786],{},[941,16784,16785],{},"role"," — the new role, subject to the elevation guard above.",[1114,16788,16789,16792,16793,16795],{},[941,16790,16791],{},"displayName"," — the new display name; ",[941,16794,6651],{}," clears it.",[1114,16797,16798,16801],{},[941,16799,16800],{},"permissions"," — the replacement permission map (full replacement at the map level).",[1114,16803,16804,16475,16806,13046],{},[941,16805,13051],{},[941,16807,16751],{},[937,16809,16810,16811,16813,16814,16816,16817,16819],{},"Throws ",[941,16812,11801],{}," when no keyring exists for the target, ",[941,16815,11395],{}," when the hierarchy rejects, and ",[941,16818,12881],{}," when no field is provided.",[1591,16821,16823,1368,16826],{"id":16822},"getdirectoryenabled-setdirectoryenabled",[941,16824,16825],{},"getDirectoryEnabled()",[941,16827,16828],{},"setDirectoryEnabled()",[937,16830,16831,16832,16835,16836,997,16839,16842],{},"The vault-level ",[950,16833,16834],{},"user directory"," toggle decides whether this vault's members show up when you enumerate its team. It sits alongside the per-user hide flag and the user-envelope records those listings read — see ",[1007,16837,669],{"href":16838},"..\u002F..\u002Fservices\u002Fdirectory",[1007,16840,884],{"href":16841},"..\u002F..\u002Fservices\u002Fuser-envelope"," for the full visibility model.",[2217,16844,16846],{"className":2219,"code":16845,"language":2221,"meta":1037,"style":1037},"getDirectoryEnabled(vault: string): Promise\u003Cboolean>\nsetDirectoryEnabled(\n  vault: string,\n  enabled: boolean\n): Promise\u003Cvoid>\n",[941,16847,16848,16864,16871,16877,16882],{"__ignoreMap":1037},[2225,16849,16850,16853,16855,16857,16859,16862],{"class":2227,"line":2228},[2225,16851,16852],{"class":2291},"getDirectoryEnabled",[2225,16854,12765],{"class":2239},[2225,16856,11740],{"class":6342},[2225,16858,6474],{"class":2235},[2225,16860,16861],{"class":2239},"boolean",[2225,16863,7093],{"class":2235},[2225,16865,16866,16869],{"class":2227,"line":1041},[2225,16867,16868],{"class":2291},"setDirectoryEnabled",[2225,16870,11683],{"class":2239},[2225,16872,16873,16875],{"class":2227,"line":1038},[2225,16874,12912],{"class":2239},[2225,16876,1707],{"class":2235},[2225,16878,16879],{"class":2227,"line":2288},[2225,16880,16881],{"class":2239},"  enabled: boolean\n",[2225,16883,16884,16886,16888],{"class":2227,"line":2304},[2225,16885,11737],{"class":2239},[2225,16887,11740],{"class":6342},[2225,16889,13016],{"class":2235},[937,16891,16892,16894,16895,16897,16898,16901,16902,16904,16905,16908,16909,16912],{},[941,16893,16852],{}," reads the vault-level user-directory listing toggle, defaulting to on (",[941,16896,11790],{},") when no ",[941,16899,16900],{},"_meta\u002Fdirectory"," document exists yet — no role gate, anyone who can open the vault may read it. ",[941,16903,16868],{}," flips it and is owner-only; when disabled, the hub-level user enumeration throws ",[941,16906,16907],{},"DirectoryDisabledError"," for callers who are neither owner nor admin. Honest caveat: this is a UX flag, ",[950,16910,16911],{},"not a privacy guarantee"," — the keyring and user-envelope records stay observable to anyone with direct store read access; only the hub-level enumeration is gated.",[1111,16914,16915,16925],{},[1114,16916,16917,16919,16920,16922,16923,957],{},[941,16918,2694],{}," — the vault whose directory toggle to read (",[941,16921,16852],{},") or flip (",[941,16924,16868],{},[1114,16926,16927,16930,16931,1520],{},[941,16928,16929],{},"enabled"," — the new toggle value for ",[941,16932,16868],{},[2645,16934,16935],{},"html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":1037,"searchDepth":1038,"depth":1038,"links":16937},[16938,16944],{"id":16269,"depth":1041,"text":69,"children":16939},[16940,16941,16942,16943],{"id":16278,"depth":1038,"text":16281},{"id":16389,"depth":1038,"text":16392},{"id":16481,"depth":1038,"text":16484},{"id":16590,"depth":1038,"text":16593},{"id":16629,"depth":1041,"text":16630,"children":16945},[16946,16947,16948],{"id":16651,"depth":1038,"text":16654},{"id":16687,"depth":1038,"text":16690},{"id":16822,"depth":1038,"text":16949},"getDirectoryEnabled() · setDirectoryEnabled()","The vault secret and single-user keyring: change \u002F rotate \u002F recover the secret, read the keyring, and the users home — list, update, directory toggle.",{},{"title":69,"description":16950},"hSBKE4ZEGwVfr31YRPLSxo1CM7nfdiuahjoqpa8rSoo",{"id":16955,"title":73,"access":12,"apiToc":2284,"body":16956,"category":1042,"description":19574,"extension":1044,"meta":19575,"navigation":1042,"nest":2635,"path":74,"seo":19576,"sinceVersion":1042,"status":1049,"stem":75,"subtitle":1042,"tier":6922,"tocExternal":1042,"__hash__":19577},"docs\u002Fcore\u002F2.noydb\u002F4.authentication.md",{"type":931,"value":16957,"toc":19541},[16958,16961,16990,16994,17019,17022,17064,17068,17075,17296,17306,17310,17316,17319,17323,17336,17407,17455,17459,17488,17492,17506,17512,17544,17555,17594,17600,17629,17635,17656,17662,17689,17702,17709,17715,17748,17766,17796,17802,17862,17884,17913,17919,17965,17980,17987,17993,18046,18059,18082,18089,18095,18118,18140,18168,18171,18182,18188,18217,18228,18252,18258,18311,18328,18344,18350,18364,18367,18374,18380,18393,18397,18413,18419,18425,18465,18468,18514,18523,18546,18561,18565,18573,18579,18804,18820,19192,19203,19286,19313,19316,19326,19332,19352,19355,19362,19368,19388,19395,19402,19408,19441,19447,19465,19471,19517,19525,19538],[934,16959,73],{"id":16960},"authentication",[937,16962,16963,16964,16967,16968,1261,16970,1261,16972,16974,16975,16977,16978,16980,16981,16984,16985,16987,16988,1520],{},"The session-authentication ladder is ",[950,16965,16966],{},"strategy-free",". Unlike multi-user management (",[941,16969,10493],{},[941,16971,10653],{},[941,16973,10859],{},", which route through the opt-in team strategy and throw ",[941,16976,10329],{}," without it), every method below is always available on a plain ",[941,16979,2134],{}," session; the ones that touch a sensitive action are guarded by a ",[950,16982,16983],{},"policy gate",", not by a ",[941,16986,943],{}," opt-in. Recovery — regaining access when factors are lost — has moved to its own page: ",[1007,16989,77],{"href":11641},[1022,16991,16993],{"id":16992},"the-three-tiers","The three tiers",[937,16995,16996,16997,17000,17001,17004,17005,17009,17010,17014,17015,1520],{},"Every vault is unlocked with a ",[950,16998,16999],{},"tier-1"," secret; typing it on every read is not realistic, so noy-db layers two lighter-weight unlock methods on top of it and calls the resulting stack the ",[950,17002,17003],{},"three-tier privilege ladder",". The ladder concept, factor packages, policy-gate DSL, and worked flows live on ",[950,17006,17007],{},[1007,17008,844],{"href":12844}," — this page is the kernel-API index for the methods that enroll and use each tier, plus a dedicated look at ",[1007,17011,17013],{"href":17012},"#tier-1-the-root-secret","how tier 1 is supplied"," and at ",[1007,17016,17018],{"href":17017},"#can-a-method-serve-a-different-tier","why a method cannot serve a different tier",[937,17020,17021],{},"Three concrete scenarios show the ladder in practice:",[1111,17023,17024,17037,17051],{},[1114,17025,17026,17029,17030,953,17033,17036],{},[950,17027,17028],{},"Daily password."," A SaaS user signs in each morning with a routine ",[950,17031,17032],{},"tier-2 password",[941,17034,17035],{},"@noy-db\u002Fon-password",") — a second secret, distinct from the rarely-typed tier-1 phrase — which unwraps the DEK set directly.",[1114,17038,17039,17042,17043,17046,17047,17050],{},[950,17040,17041],{},"Biometric passkey."," A laptop user unlocks with a ",[950,17044,17045],{},"tier-2 passkey"," — Touch ID \u002F Face ID \u002F Windows Hello via ",[941,17048,17049],{},"@noy-db\u002Fon-webauthn"," — with no secret typed at all.",[1114,17052,17053,17056,17057,953,17060,17063],{},[950,17054,17055],{},"PIN resume."," After a short idle window, the app resumes from a ",[950,17058,17059],{},"tier-3 PIN",[941,17061,17062],{},"@noy-db\u002Fon-pin",") over the DEKs still cached from the last unlock — it never re-derives the secret and cannot open a cold vault.",[1022,17065,17067],{"id":17066},"the-three-tier-capability-matrix","The three-tier capability matrix",[937,17069,17070,17071,17074],{},"Each tier is a distinct cryptographic capability, not an interchangeable label. The full picture — use ",[950,17072,17073],{},"Expand"," for a large-screen view:",[17076,17077,17079],"wide-table",{"title":17078},"The three-tier ladder",[1338,17080,17081,17097],{},[1341,17082,17083],{},[1344,17084,17085,17088,17091,17094],{},[1347,17086,17087],{},"Capability \u002F property",[1347,17089,17090],{},"Tier 1 — root secret",[1347,17092,17093],{},"Tier 2 — authenticate",[1347,17095,17096],{},"Tier 3 — quick unlock",[1357,17098,17099,17114,17141,17164,17179,17197,17217,17233,17259,17280],{},[1344,17100,17101,17106,17108,17111],{},[1362,17102,17103],{},[950,17104,17105],{},"Opens a cold vault",[1362,17107,14852],{},[1362,17109,17110],{},"yes — any one enrolled slot",[1362,17112,17113],{},"no — resume only",[1344,17115,17116,17121,17124,17136],{},[1362,17117,17118],{},[950,17119,17120],{},"Session holds the KEK",[1362,17122,17123],{},"yes — derived from the secret (PBKDF2, 600k)",[1362,17125,17126,17127,17130,17131,1261,17133,17135],{},"no — the unlock yields the DEK set, ",[941,17128,17129],{},"kek: null"," (even ",[941,17132,337],{},[941,17134,309],{}," wrap the DEKs, not the KEK)",[1362,17137,17138,17139],{},"no — ",[941,17140,17129],{},[1344,17142,17143,17149,17151,17162],{},[1362,17144,17145,17148],{},[950,17146,17147],{},"Can grant \u002F rotate \u002F enroll \u002F open another vault"," (KEK-requiring)",[1362,17150,14852],{},[1362,17152,17153,17154,17157,17158,17161],{},"no — blocked twice: no KEK, and ",[941,17155,17156],{},"activeTier"," 2 fails a ",[941,17159,17160],{},"minTier: 1"," gate",[1362,17163,11313],{},[1344,17165,17166,17171,17173,17176],{},[1362,17167,17168],{},[950,17169,17170],{},"Can read \u002F write records",[1362,17172,14852],{},[1362,17174,17175],{},"yes — it holds the unwrapped DEK set",[1362,17177,17178],{},"yes — over the cached DEKs",[1344,17180,17181,17186,17189,17194],{},[1362,17182,17183],{},[950,17184,17185],{},"What its unlock yields",[1362,17187,17188],{},"the KEK, which unwraps the DEKs",[1362,17190,17191,17192,1381],{},"the DEK set directly (",[941,17193,17129],{},[1362,17195,17196],{},"the already-cached DEK map only",[1344,17198,17199,17204,17209,17214],{},[1362,17200,17201],{},[950,17202,17203],{},"Where the factor lives",[1362,17205,17206,17207],{},"user memory, or a managed ",[941,17208,14350],{},[1362,17210,17211,17213],{},[941,17212,1787],{}," slots inside the keyring file",[1362,17215,17216],{},"in-memory state, TTL-bounded",[1344,17218,17219,17224,17227,17230],{},[1362,17220,17221],{},[950,17222,17223],{},"Typical lifetime",[1362,17225,17226],{},"enrollment, recovery, tier-1-gated actions",[1362,17228,17229],{},"days–weeks (app-defined)",[1362,17231,17232],{},"minutes (app-defined)",[1344,17234,17235,17239,17247,17255],{},[1362,17236,17237],{},[950,17238,9530],{},[1362,17240,17241,17242,17244,17245,14366],{},"core ",[941,17243,1086],{}," — no ",[941,17246,1264],{},[1362,17248,17249,1261,17251,1261,17253],{},[941,17250,15953],{},[941,17252,15944],{},[941,17254,15969],{},[1362,17256,17257],{},[941,17258,15958],{},[1344,17260,17261,17266,17272,17277],{},[1362,17262,17263],{},[950,17264,17265],{},"Survives secret rotation",[1362,17267,17268,17269,17271],{},"it ",[1092,17270,3082],{}," the secret",[1362,17273,17274,17275],{},"dropped unless preserved via ",[941,17276,16469],{},[1362,17278,17279],{},"n\u002Fa — memory-only, expires on its TTL",[1344,17281,17282,17287,17290,17293],{},[1362,17283,17284],{},[950,17285,17286],{},"When it can't unlock, fall back to",[1362,17288,17289],{},"recovery (paper \u002F Shamir)",[1362,17291,17292],{},"another slot, or tier 1",[1362,17294,17295],{},"re-present tier 2",[937,17297,17298,17299,2993,17302,17305],{},"Higher tier outranks lower: only a higher tier can reset a lower one, and a policy gate's ",[941,17300,17301],{},"minTier",[1092,17303,17304],{},"floor"," — a tier-1 session can always do what a tier-2 gate allows, never the reverse.",[1022,17307,17309],{"id":17308},"the-on-method-family","The on-* method family",[937,17311,17312,17313,17315],{},"Every factor beyond the tier-1 secret ships as its own ",[941,17314,9945],{}," package; the tier-1 secret itself is the one kernel-built credential. The whole catalog, by tier and integration level:",[17317,17318],"on-methods-table",{},[1022,17320,17322],{"id":17321},"tier-1-the-root-secret","Tier 1 — the root secret",[937,17324,17325,17326,17329,17330,17332,17333,17335],{},"Tier 1 is the only credential that can open a ",[950,17327,17328],{},"cold"," vault — a fresh process with nothing cached. It establishes the session every lighter factor then re-opens, and it is built into ",[941,17331,1086],{}," core (there is no separate ",[941,17334,1787],{}," package for it). There are three ways to supply it:",[1111,17337,17338,17352,17385],{},[1114,17339,17340,17343,17344,10313,17346,17348,17349,17351],{},[950,17341,17342],{},"Owner secret (standard)."," Pass ",[941,17345,10695],{},[941,17347,2134],{},"; the hub derives the KEK from it via PBKDF2-SHA256 (600,000 iterations) and unwraps the collection DEKs (see ",[1007,17350,21],{"href":2628},"). The secret lives only in the user's memory — the hub never stores it.",[1114,17353,17354,17357,17358,17360,17361,17363,17364,17367,17368,17374,17375,17377,17378,17380,17381,1520],{},[950,17355,17356],{},"Self-generated sealed secret (managed mode)."," Set ",[941,17359,13512],{}," and supply a ",[941,17362,14350],{},": the hub mints a random 256-bit secret at first open and ",[1092,17365,17366],{},"seals"," it under a key your own infrastructure holds — an ",[1007,17369,17371,17373],{"href":17370},".\u002Fenclave#the-sealing-key",[941,17372,1139],{}," host"," such as an OS keychain or a cloud KMS — persisting only the sealed blob at ",[941,17376,14380],{},". The user never types or sees a secret, which makes the vault coercion-resistant: there is nothing to reveal. Managed mode requires a strong (Shamir) recovery profile and disables ",[941,17379,15216],{}," (the secret is replaced only through recovery under a fresh sealing key). Full flow: ",[1007,17382,17384],{"href":17383},"..\u002F..\u002Fservices\u002Fsession-tiers#managed-secret-mode","session-tiers → managed mode",[1114,17386,17387,17392,17393,17395,17396,17399,17400,997,17402,17404,17405,14388],{},[950,17388,17389,17390,957],{},"Externally-supplied keyring (",[941,17391,9953],{}," Skip the typed-secret model entirely: pass a ",[941,17394,9953],{}," callback that returns an already-unlocked keyring produced by WebAuthn, OIDC, or Shamir. This is how a ",[950,17397,17398],{},"passwordless app puts a tier-2 factor at the front door"," — the factor supplies the keyring at cold start, so no secret is ever typed. (",[941,17401,10695],{},[941,17403,9953],{}," are mutually exclusive; bootstrapping a brand-new vault still needs a secret or recovery code once, to enroll the first factor, after which you switch to ",[941,17406,9953],{},[937,17408,17409,15920,17412,17415,17416,17418,17419,17422,17423,17426,17427,997,17429,17431,17432,17435,17436,17439,17440,17442,17443,997,17445,17447,17448,17450,17451,1520],{},[950,17410,17411],{},"Secret strength is your choice; the tier is not.",[1092,17413,17414],{},"tier"," of a factor is fixed by its cryptographic role (see ",[1007,17417,15080],{"href":17017},"); the ",[1092,17420,17421],{},"strength"," of the tier-1 secret is not enforced by default. ",[941,17424,17425],{},"validateSecret"," is opt-in at ",[941,17428,2134],{},[941,17430,10831],{}," (default off), so ",[941,17433,17434],{},"secret: '1234'"," will open a vault and derive a real — if low-entropy — KEK. Pass ",[941,17437,17438],{},"validateSecret: true"," (or attach a ",[941,17441,16365],{},") to enforce the phrase-format floor. ",[941,17444,15216],{},[941,17446,16494],{}," are the exception: they validate the new phrase by default, so opt out with ",[941,17449,16369],{}," if a weak one is deliberate. Format rules and worked examples: ",[1007,17452,17454],{"href":17453},"..\u002F..\u002Fservices\u002Fsession-tiers#strength-validation","session-tiers → strength validation",[1022,17456,17458],{"id":17457},"multi-owner-tier-1","Multi-owner tier 1",[937,17460,17461,17462,17464,17465,17467,17468,17471,17472,17474,17475,17478,17479,17481,17482,17485,17486,1520],{},"A vault is not limited to a single root secret. Each user has their own ",[941,17463,13959],{}," record holding the ",[1092,17466,4664],{}," collection DEKs wrapped under ",[1092,17469,17470],{},"their own"," KEK — so several people can each hold an independent tier-1 secret that unlocks the same vault, and more than one can hold the ",[941,17473,9898],{}," role (",[941,17476,17477],{},"grant(vault, { role: 'owner', … })","; owners cannot be revoked). This is the standard remedy for single-owner bus-factor risk: ",[941,17480,10831],{}," re-wraps the collection DEKs for the new user under a KEK derived from ",[1092,17483,17484],{},"their"," secret — see ",[1007,17487,14279],{"href":14278},[1022,17489,17491],{"id":17490},"tier-2-authenticators","Tier 2 — authenticators",[937,17493,17494,17495,17498,17499,17501,17502,1520],{},"Tier 2 is routine login — it replaces retyping the secret. It is ",[950,17496,17497],{},"multi-slot",": a user enrolls one or more authenticator slots, each independently wrapping the same KEK (or, for ",[941,17500,15953],{},", the DEK set directly), and any one enrolled slot unlocks the vault. Adding or removing a slot is a constant-time keyring write; it never re-keys the DEKs. Enroll \u002F remove \u002F update each check the corresponding policy gate. Full walkthrough: ",[1007,17503,17505],{"href":17504},"..\u002F..\u002Fservices\u002Fsession-tiers#tier-2-authenticate-multi-slot","session-tiers → Tier 2",[1591,17507,17509],{"id":17508},"enrollauthenticator",[941,17510,17511],{},"enrollAuthenticator()",[2217,17513,17515],{"className":2219,"code":17514,"language":2221,"meta":1037,"style":1037},"enrollAuthenticator(vault: string, options: EnrollAuthenticatorOptions, factors?: FactorProofBundle): Promise\u003Cvoid>\n",[941,17516,17517],{"__ignoreMap":1037},[2225,17518,17519,17522,17525,17527,17530,17532,17535,17537,17540,17542],{"class":2227,"line":2228},[2225,17520,17521],{"class":2291},"enrollAuthenticator",[2225,17523,17524],{"class":2239},"(vault: string",[2225,17526,2298],{"class":2235},[2225,17528,17529],{"class":2239}," options: EnrollAuthenticatorOptions",[2225,17531,2298],{"class":2235},[2225,17533,17534],{"class":2239}," factors",[2225,17536,7011],{"class":2235},[2225,17538,17539],{"class":2239}," FactorProofBundle): ",[2225,17541,11740],{"class":6342},[2225,17543,13016],{"class":2235},[937,17545,17546,17547,17550,17551,17554],{},"Add a tier-2 authenticator slot to a vault's keyring. Takes the ",[950,17548,17549],{},"vault name",", not a keyring — the hub checks the ",[941,17552,17553],{},"enroll-authenticator"," gate and fetches the active keyring internally.",[1111,17556,17557,17562,17588],{},[1114,17558,17559,17561],{},[941,17560,2694],{}," — the vault whose keyring gains the slot.",[1114,17563,17564,13535,17566,17569,17570,968,17572,953,17575,1261,17577,1261,17579,17581,17582,17584,17585,1520],{},[941,17565,16362],{},[941,17567,17568],{},"EnrollAuthenticatorOptions"," describing the slot: its caller-chosen ",[941,17571,6983],{},[941,17573,17574],{},"method",[941,17576,337],{},[941,17578,309],{},[941,17580,313],{},"), the wrap material produced by the matching ",[941,17583,9945],{}," package, and method-specific ",[941,17586,17587],{},"meta",[1114,17589,17590,16475,17592,16478],{},[941,17591,13051],{},[941,17593,17553],{},[1591,17595,17597],{"id":17596},"removeauthenticator",[941,17598,17599],{},"removeAuthenticator()",[2217,17601,17603],{"className":2219,"code":17602,"language":2221,"meta":1037,"style":1037},"removeAuthenticator(vault: string, slotId: string, factors?: FactorProofBundle): Promise\u003Cvoid>\n",[941,17604,17605],{"__ignoreMap":1037},[2225,17606,17607,17610,17612,17614,17617,17619,17621,17623,17625,17627],{"class":2227,"line":2228},[2225,17608,17609],{"class":2291},"removeAuthenticator",[2225,17611,17524],{"class":2239},[2225,17613,2298],{"class":2235},[2225,17615,17616],{"class":2239}," slotId: string",[2225,17618,2298],{"class":2235},[2225,17620,17534],{"class":2239},[2225,17622,7011],{"class":2235},[2225,17624,17539],{"class":2239},[2225,17626,11740],{"class":6342},[2225,17628,13016],{"class":2235},[937,17630,17631,17632,1520],{},"Remove a tier-2 slot by id. Idempotent — removing a missing slot is a no-op. Gated by ",[941,17633,17634],{},"remove-authenticator",[1111,17636,17637,17642,17650],{},[1114,17638,17639,17641],{},[941,17640,2694],{}," — the vault whose keyring loses the slot.",[1114,17643,17644,1169,17647,17649],{},[941,17645,17646],{},"slotId",[941,17648,6983],{}," of the slot to remove.",[1114,17651,17652,16475,17654,13046],{},[941,17653,13051],{},[941,17655,17634],{},[1591,17657,17659],{"id":17658},"listauthenticators",[941,17660,17661],{},"listAuthenticators()",[2217,17663,17665],{"className":2219,"code":17664,"language":2221,"meta":1037,"style":1037},"listAuthenticators(vault: string): Promise\u003CReadonlyArray\u003CKeyringAuthenticator>>\n",[941,17666,17667],{"__ignoreMap":1037},[2225,17668,17669,17672,17674,17676,17678,17681,17683,17686],{"class":2227,"line":2228},[2225,17670,17671],{"class":2291},"listAuthenticators",[2225,17673,12765],{"class":2239},[2225,17675,11740],{"class":6342},[2225,17677,6474],{"class":2235},[2225,17679,17680],{"class":2239},"ReadonlyArray",[2225,17682,6474],{"class":2235},[2225,17684,17685],{"class":2239},"KeyringAuthenticator",[2225,17687,17688],{"class":2235},">>\n",[937,17690,17691,17692,17694,17695,968,17697,17699,17700,957],{},"List the enrolled tier-2 slots on a vault's keyring. Each entry is a ",[941,17693,17685],{}," — a discriminated union of wrap-KEK slots (",[941,17696,337],{},[941,17698,309],{},") and wrap-DEKs slots (",[941,17701,313],{},[1111,17703,17704],{},[1114,17705,17706,17708],{},[941,17707,2694],{}," — the vault whose slots to list.",[1591,17710,17712],{"id":17711},"updateauthenticator",[941,17713,17714],{},"updateAuthenticator()",[2217,17716,17718],{"className":2219,"code":17717,"language":2221,"meta":1037,"style":1037},"updateAuthenticator(vault: string, slotId: string, options: UpdateAuthenticatorOptions, factors?: FactorProofBundle): Promise\u003Cvoid>\n",[941,17719,17720],{"__ignoreMap":1037},[2225,17721,17722,17725,17727,17729,17731,17733,17736,17738,17740,17742,17744,17746],{"class":2227,"line":2228},[2225,17723,17724],{"class":2291},"updateAuthenticator",[2225,17726,17524],{"class":2239},[2225,17728,2298],{"class":2235},[2225,17730,17616],{"class":2239},[2225,17732,2298],{"class":2235},[2225,17734,17735],{"class":2239}," options: UpdateAuthenticatorOptions",[2225,17737,2298],{"class":2235},[2225,17739,17534],{"class":2239},[2225,17741,7011],{"class":2235},[2225,17743,17539],{"class":2239},[2225,17745,11740],{"class":6342},[2225,17747,13016],{"class":2235},[937,17749,17750,17751,17753,17754,968,17756,17758,17759,17762,17763,17765],{},"Rename or re-label a slot's ",[941,17752,17587],{}," — its ",[941,17755,6983],{},[941,17757,17574],{},", and wrap material are immutable. Gated by ",[941,17760,17761],{},"update-authenticator","; at least one ",[941,17764,17587],{}," field must be provided.",[1111,17767,17768,17773,17780,17790],{},[1114,17769,17770,17772],{},[941,17771,2694],{}," — the vault whose slot to update.",[1114,17774,17775,1169,17777,17779],{},[941,17776,17646],{},[941,17778,6983],{}," of the slot to relabel.",[1114,17781,17782,13535,17784,17787,17788,1520],{},[941,17783,16362],{},[941,17785,17786],{},"UpdateAuthenticatorOptions"," carrying the replacement ",[941,17789,17587],{},[1114,17791,17792,16475,17794,13046],{},[941,17793,13051],{},[941,17795,17761],{},[1591,17797,17799],{"id":17798},"enrollwebauthn",[941,17800,17801],{},"enrollWebAuthn()",[2217,17803,17805],{"className":2219,"code":17804,"language":2221,"meta":1037,"style":1037},"enrollWebAuthn(vault: string, ceremony: (keyring: UnlockedKeyring) => Promise\u003CEnrollAuthenticatorOptions>, factors?: FactorProofBundle): Promise\u003C{ credentialId: string }>\n",[941,17806,17807],{"__ignoreMap":1037},[2225,17808,17809,17812,17814,17816,17819,17821,17823,17825,17828,17830,17832,17834,17836,17838,17841,17843,17845,17847,17849,17852,17855,17857,17859],{"class":2227,"line":2228},[2225,17810,17811],{"class":2291},"enrollWebAuthn",[2225,17813,17524],{"class":2239},[2225,17815,2298],{"class":2235},[2225,17817,17818],{"class":2239}," ceremony: ",[2225,17820,2789],{"class":2235},[2225,17822,2705],{"class":7052},[2225,17824,2311],{"class":2235},[2225,17826,17827],{"class":6342}," UnlockedKeyring",[2225,17829,1381],{"class":2235},[2225,17831,8116],{"class":2774},[2225,17833,7079],{"class":6342},[2225,17835,6474],{"class":2235},[2225,17837,17568],{"class":2239},[2225,17839,17840],{"class":2235},">,",[2225,17842,17534],{"class":2239},[2225,17844,7011],{"class":2235},[2225,17846,17539],{"class":2239},[2225,17848,11740],{"class":6342},[2225,17850,17851],{"class":2235},"\u003C{",[2225,17853,17854],{"class":6342}," credentialId",[2225,17856,2311],{"class":2235},[2225,17858,6366],{"class":2239},[2225,17860,17861],{"class":2235}," }>\n",[937,17863,17864,17865,17868,17869,17871,17872,17874,17875,17878,17879,17881,17882,2065],{},"Native-WebAuthn enrollment helper. Runs the caller's ",[941,17866,17867],{},"ceremony"," against the unlocked keyring to produce the slot options (whose ",[941,17870,17574],{}," must be ",[941,17873,337],{}," and whose ",[941,17876,17877],{},"meta.credentialId"," must be present), then persists the slot. Gated by ",[941,17880,17553],{}," like ",[941,17883,17511],{},[1111,17885,17886,17891,17907],{},[1114,17887,17888,17890],{},[941,17889,2694],{}," — the vault whose keyring gains the WebAuthn slot.",[1114,17892,17893,17895,17896,17898,17899,17901,17902,17904,17905,1520],{},[941,17894,17867],{}," — a callback receiving the ",[941,17897,11493],{}," and returning ",[941,17900,17568],{},"; typically delegates to ",[941,17903,17801],{}," from ",[941,17906,17049],{},[1114,17908,17909,16475,17911,13046],{},[941,17910,13051],{},[941,17912,17553],{},[1591,17914,17916],{"id":17915},"listwebauthnslots",[941,17917,17918],{},"listWebAuthnSlots()",[2217,17920,17922],{"className":2219,"code":17921,"language":2221,"meta":1037,"style":1037},"listWebAuthnSlots(vault: string): Promise\u003CReadonlyArray\u003C{ id: string; enrolledAt: string; credentialId: string }>>\n",[941,17923,17924],{"__ignoreMap":1037},[2225,17925,17926,17929,17931,17933,17935,17937,17939,17941,17943,17945,17947,17950,17952,17954,17956,17958,17960,17962],{"class":2227,"line":2228},[2225,17927,17928],{"class":2291},"listWebAuthnSlots",[2225,17930,12765],{"class":2239},[2225,17932,11740],{"class":6342},[2225,17934,6474],{"class":2235},[2225,17936,17680],{"class":2239},[2225,17938,17851],{"class":2235},[2225,17940,7070],{"class":6342},[2225,17942,2311],{"class":2235},[2225,17944,6366],{"class":2239},[2225,17946,3769],{"class":2235},[2225,17948,17949],{"class":6342}," enrolledAt",[2225,17951,2311],{"class":2235},[2225,17953,6366],{"class":2239},[2225,17955,3769],{"class":2235},[2225,17957,17854],{"class":6342},[2225,17959,2311],{"class":2235},[2225,17961,6366],{"class":2239},[2225,17963,17964],{"class":2235}," }>>\n",[937,17966,17967,17968,17970,17971,968,17973,17976,17977,1520],{},"List the enrolled slots scoped to the ",[941,17969,337],{}," method — each with its slot ",[941,17972,6983],{},[941,17974,17975],{},"enrolledAt"," timestamp, and ",[941,17978,17979],{},"credentialId",[1111,17981,17982],{},[1114,17983,17984,17986],{},[941,17985,2694],{}," — the vault whose WebAuthn slots to list.",[1591,17988,17990],{"id":17989},"unlockviaauthenticator",[941,17991,17992],{},"unlockViaAuthenticator()",[2217,17994,17996],{"className":2219,"code":17995,"language":2221,"meta":1037,"style":1037},"unlockViaAuthenticator(vault: string, slotId: string, verify: (slot: KeyringAuthenticator) => Promise\u003CUnlockedKeyring>): Promise\u003CUnlockedKeyring>\n",[941,17997,17998],{"__ignoreMap":1037},[2225,17999,18000,18003,18005,18007,18009,18011,18014,18016,18019,18021,18024,18026,18028,18030,18032,18034,18036,18038,18040,18042,18044],{"class":2227,"line":2228},[2225,18001,18002],{"class":2291},"unlockViaAuthenticator",[2225,18004,17524],{"class":2239},[2225,18006,2298],{"class":2235},[2225,18008,17616],{"class":2239},[2225,18010,2298],{"class":2235},[2225,18012,18013],{"class":2239}," verify: ",[2225,18015,2789],{"class":2235},[2225,18017,18018],{"class":7052},"slot",[2225,18020,2311],{"class":2235},[2225,18022,18023],{"class":6342}," KeyringAuthenticator",[2225,18025,1381],{"class":2235},[2225,18027,8116],{"class":2774},[2225,18029,7079],{"class":6342},[2225,18031,6474],{"class":2235},[2225,18033,11493],{"class":2239},[2225,18035,6480],{"class":2235},[2225,18037,11737],{"class":2239},[2225,18039,11740],{"class":6342},[2225,18041,6474],{"class":2235},[2225,18043,11493],{"class":2239},[2225,18045,7093],{"class":2235},[937,18047,18048,18049,18052,18053,18055,18056,18058],{},"Open a session via an enrolled slot. The ",[941,18050,18051],{},"verify"," callback receives the stored slot and returns the unlocked keyring — typically delegating to the matching ",[941,18054,9945],{}," package; subsequent ",[941,18057,12972],{}," calls then see a tier-2 session.",[1111,18060,18061,18066,18073],{},[1114,18062,18063,18065],{},[941,18064,2694],{}," — the vault to unlock.",[1114,18067,18068,1169,18070,18072],{},[941,18069,17646],{},[941,18071,6983],{}," of the enrolled slot to unlock with.",[1114,18074,18075,17895,18077,18079,18080,1520],{},[941,18076,18051],{},[941,18078,17685],{}," and returning the ",[941,18081,11493],{},[1591,18083,18085,18086,18088],{"id":18084},"tier-2-on-families","Tier-2 ",[941,18087,1787],{}," families",[937,18090,18091,18092,18094],{},"The wrap material for each slot comes from an ",[941,18093,9945],{}," package — a crypto primitive only; storage, rate-limiting, and UI belong to the caller. The three families that serve tier 2:",[1111,18096,18097,18104,18111],{},[1114,18098,18099,18103],{},[950,18100,18101],{},[1007,18102,15953],{"href":15952}," — a routine tier-2 login password, a separate secret from the tier-1 master phrase (PBKDF2-SHA256, wrap-DEKs slot).",[1114,18105,18106,18110],{},[950,18107,18108],{},[1007,18109,15944],{"href":15943}," — hardware-key \u002F platform-passkey unlock via WebAuthn (Touch ID, Face ID, Windows Hello, YubiKey); the tier-2 method with no knowledge factor.",[1114,18112,18113,18117],{},[950,18114,18115],{},[1007,18116,15969],{"href":15968}," — split-key OIDC bridge (half the KEK on the device, half from the provider's key connector); requires operating a key-connector server.",[937,18119,18120,18125,18126,18129,18130,18132,18133,18135,18136,1520],{},[950,18121,18122,18123,957],{},"Federated trust (",[941,18124,15969],{}," When you unlock via OIDC, the provider — Google, Apple, LINE, Microsoft, Meta, AWS Cognito, any OIDC issuer — owns the ",[1092,18127,18128],{},"identity"," proof: the email-and-password, emailed-code, or biometric ceremony all happen at the provider, and noy-db only consumes the resulting ID token, so the provider's MFA strength flows into the tier-2 unlock for free. The provider does ",[950,18131,1004],{}," own your key — ",[941,18134,15969],{}," XOR-splits the KEK into a device half (never leaves the device) and a server half (released after login), so a compromised provider still cannot decrypt. Full trust model: ",[1007,18137,18139],{"href":18138},"..\u002F..\u002Ffamilies\u002Fon\u002Foidc#what-the-provider-is-responsible-for","on-oidc → what the provider is responsible for",[937,18141,18142,18143,1324,18146,968,18148,968,18150,968,18153,18155,18156,18158,18159,18161,18162,1520],{},"Not tier-2 authenticators, but sitting alongside the ladder: the ",[950,18144,18145],{},"step-up 2FA factors",[941,18147,15974],{},[941,18149,15979],{},[941,18151,18152],{},"on-recovery",[941,18154,15994],{},", and roaming ",[941,18157,15944],{}," supply fresh off-device proofs that policy gates can additionally demand (a fresh proof each time a gate asks); and ",[941,18160,15909],{}," is a delegated one-shot viewer session, not a session tier. See the ",[1007,18163,18165,18167],{"href":18164},"..\u002F..\u002Ffamilies\u002Fon\u002F",[941,18166,4883],{}," family catalog",[1022,18169,17096],{"id":18170},"tier-3-quick-unlock",[937,18172,18173,18174,18177,18178,1520],{},"Tier 3 is quick-resume after idle. It re-opens an ",[950,18175,18176],{},"already-unlocked"," session within a bounded idle window over the DEKs cached from a tier-1 or tier-2 unlock — it cannot open a cold vault, and on idle expiry the cache is wiped and tier 2 must be re-presented. It is never safe on a shared workstation, where the cached DEKs live on the device for the whole window. Full walkthrough: ",[1007,18179,18181],{"href":18180},"..\u002F..\u002Fservices\u002Fsession-tiers#tier-3-unlock-quick-resume","session-tiers → Tier 3",[1591,18183,18185],{"id":18184},"enrollunlock",[941,18186,18187],{},"enrollUnlock()",[2217,18189,18191],{"className":2219,"code":18190,"language":2221,"meta":1037,"style":1037},"enrollUnlock(vault: string, state: QuickUnlockState, factors?: FactorProofBundle): Promise\u003Cvoid>\n",[941,18192,18193],{"__ignoreMap":1037},[2225,18194,18195,18198,18200,18202,18205,18207,18209,18211,18213,18215],{"class":2227,"line":2228},[2225,18196,18197],{"class":2291},"enrollUnlock",[2225,18199,17524],{"class":2239},[2225,18201,2298],{"class":2235},[2225,18203,18204],{"class":2239}," state: QuickUnlockState",[2225,18206,2298],{"class":2235},[2225,18208,17534],{"class":2239},[2225,18210,7011],{"class":2235},[2225,18212,17539],{"class":2239},[2225,18214,11740],{"class":6342},[2225,18216,13016],{"class":2235},[937,18218,18219,18220,18222,18223,17904,18226,957],{},"Register a tier-3 quick-unlock state produced by an ",[941,18221,1787],{}," tier-3 primitive (e.g. ",[941,18224,18225],{},"enrollPin()",[941,18227,17062],{},[1111,18229,18230,18235,18243],{},[1114,18231,18232,18234],{},[941,18233,2694],{}," — the vault to register quick-unlock for.",[1114,18236,18237,1169,18239,18242],{},[941,18238,4656],{},[941,18240,18241],{},"QuickUnlockState"," minted from an already-unlocked keyring.",[1114,18244,18245,18247,18248,18251],{},[941,18246,13051],{}," — factor proofs presented if the ",[941,18249,18250],{},"rotate-unlock"," gate demands them.",[1591,18253,18255],{"id":18254},"unlockviapin",[941,18256,18257],{},"unlockViaPin()",[2217,18259,18261],{"className":2219,"code":18260,"language":2221,"meta":1037,"style":1037},"unlockViaPin(vault: string, resume: (state: QuickUnlockState) => Promise\u003CUnlockedKeyring>): Promise\u003CUnlockedKeyring | undefined>\n",[941,18262,18263],{"__ignoreMap":1037},[2225,18264,18265,18268,18270,18272,18275,18277,18279,18281,18284,18286,18288,18290,18292,18294,18296,18298,18300,18302,18305,18308],{"class":2227,"line":2228},[2225,18266,18267],{"class":2291},"unlockViaPin",[2225,18269,17524],{"class":2239},[2225,18271,2298],{"class":2235},[2225,18273,18274],{"class":2239}," resume: ",[2225,18276,2789],{"class":2235},[2225,18278,4656],{"class":7052},[2225,18280,2311],{"class":2235},[2225,18282,18283],{"class":6342}," QuickUnlockState",[2225,18285,1381],{"class":2235},[2225,18287,8116],{"class":2774},[2225,18289,7079],{"class":6342},[2225,18291,6474],{"class":2235},[2225,18293,11493],{"class":2239},[2225,18295,6480],{"class":2235},[2225,18297,11737],{"class":2239},[2225,18299,11740],{"class":6342},[2225,18301,6474],{"class":2235},[2225,18303,18304],{"class":2239},"UnlockedKeyring ",[2225,18306,18307],{"class":2235},"|",[2225,18309,18310],{"class":2235}," undefined>\n",[937,18312,18313,18314,18317,18318,18321,18322,17904,18325,18327],{},"Resume a session via the registered tier-3 state. Resolves to ",[941,18315,18316],{},"undefined"," when no tier-3 state is registered for the vault; the ",[941,18319,18320],{},"resume"," callback typically delegates to ",[941,18323,18324],{},"resumePin()",[941,18326,17062],{},", which throws past its TTL \u002F attempt limit.",[1111,18329,18330,18335],{},[1114,18331,18332,18334],{},[941,18333,2694],{}," — the vault to resume.",[1114,18336,18337,18339,18340,18079,18342,1520],{},[941,18338,18320],{}," — a callback receiving the stored ",[941,18341,18241],{},[941,18343,11493],{},[1591,18345,18347],{"id":18346},"clearquickunlock",[941,18348,18349],{},"clearQuickUnlock()",[2217,18351,18353],{"className":2219,"code":18352,"language":2221,"meta":1037,"style":1037},"clearQuickUnlock(vault: string): void\n",[941,18354,18355],{"__ignoreMap":1037},[2225,18356,18357,18360,18362],{"class":2227,"line":2228},[2225,18358,18359],{"class":2291},"clearQuickUnlock",[2225,18361,12765],{"class":2239},[2225,18363,12295],{"class":2235},[937,18365,18366],{},"Synchronously drop the registered tier-3 quick-unlock state for a vault.",[1111,18368,18369],{},[1114,18370,18371,18373],{},[941,18372,2694],{}," — the vault whose quick-unlock state to clear.",[1591,18375,18377,18378,9789],{"id":18376},"tier-3-on-family","Tier-3 ",[941,18379,1787],{},[1111,18381,18382],{},[1114,18383,18384,18388,18389,18392],{},[950,18385,18386],{},[1007,18387,15958],{"href":15957}," — the canonical tier-3 primitive: a session-resume PIN (or biometric quick-lock) that re-unlocks cached DEKs without re-typing the secret. It ",[1092,18390,18391],{},"never"," replaces the secret; it only resumes an already-unlocked session.",[1022,18394,18396],{"id":18395},"can-a-method-serve-a-different-tier","Can a method serve a different tier?",[937,18398,18399,18402,18403,18406,18407,18409,18410,18412],{},[950,18400,18401],{},"No — you cannot reassign a method's tier."," A factor's tier is pinned by what its unlock ",[1092,18404,18405],{},"yields",", not by any setting: tier 1 yields the KEK; tier 2 and tier 3 yield only the DEK set (",[941,18408,17129],{},"). You cannot promote ",[941,18411,15958],{}," into the cold-unlock primary — it holds no KEK, and its resume state must be minted from an already-unlocked session, so it structurally cannot open a cold vault.",[937,18414,18415,18418],{},[950,18416,18417],{},"But the tiers compose"," — a lighter factor can be the only thing a user ever presents, with a higher tier embedded behind it. Three shapes matter:",[937,18420,18421,18424],{},[950,18422,18423],{},"Authenticate with only a tier-2 method."," You can run a vault where no secret is typed at runtime, two ways:",[1111,18426,18427,18446],{},[1114,18428,18429,18433,18434,18436,18437,18439,18440,18442,18443,18445],{},[950,18430,18431],{},[941,18432,9953],{}," — pass a callback that returns an unlocked keyring from a WebAuthn \u002F OIDC ceremony. ",[941,18435,2847],{}," treats this as a ",[950,18438,16999],{}," session (most-privileged), so ",[941,18441,17301],{}," policy gates pass — but the keyring it returns still carries ",[941,18444,17129],{},", so KEK-requiring operations still cannot run.",[1114,18447,18448,953,18451,18453,18454,18456,18457,18460,18461,18464],{},[950,18449,18450],{},"managed mode",[941,18452,13512],{}," + a ",[941,18455,14350],{},") — the hub self-generates a 256-bit secret and seals it under a key your infrastructure holds; at cold start the provider auto-unseals it, so the user types ",[950,18458,18459],{},"nothing"," and the session is a full ",[950,18462,18463],{},"tier 1"," with a real KEK.",[937,18466,18467],{},"Either way, bootstrapping the first enrollment needs a real secret or recovery code once; after that the factor (or the provider) is the only thing presented.",[937,18469,18470,18473,18474,18477,18478,1261,18480,1261,18482,18484,18485,18487,18488,18490,18491,1324,18494,18496,18497,18500,18501,18503,18504,18507,18508,18510,18511,18513],{},[950,18471,18472],{},"What a tier-2 session can and cannot do."," A tier-2 ",[1092,18475,18476],{},"authenticator"," unlock (",[941,18479,15953],{},[941,18481,15944],{},[941,18483,15969],{},") always returns ",[941,18486,17129],{}," — it hands you the DEK set, not the key that wraps it. So the session ",[950,18489,5105],{}," read and write every collection it unwrapped, but ",[950,18492,18493],{},"cannot",[941,18495,10493],{},", rotate the secret, enroll another authenticator, or open another vault: those need the KEK, and on an ",[941,18498,18499],{},"activeTier: 2"," session they are additionally denied by the ",[941,18502,17160],{}," policy gate. There is ",[950,18505,18506],{},"no session step-up API"," — to reach tier 1 you re-open with the real secret (or ",[941,18509,9953],{}," \u002F managed mode). This is why a machine job that must ",[941,18512,10493],{}," or rotate runs under a real tier-1 secret (typically managed mode), never a tier-2 slot.",[937,18515,18516,18519,18520,18522],{},[950,18517,18518],{},"Pair a tier-3 PIN for fast resume."," Enroll ",[941,18521,15958],{}," after any unlock and the app resumes from the PIN within an idle window over the cached DEKs — a convenience layered on top of tier 1 or tier 2, never a replacement for either.",[937,18524,18525,18528,18529,18534,18535,18537,18538,18540,18541,18545],{},[950,18526,18527],{},"\"PIN-only\" apps — which lever you are actually pulling."," If you want the user to only ever type a short PIN, exactly one composition delivers it across cold starts: ",[950,18530,18531,18532,2065],{},"use the PIN as the tier-1 ",[941,18533,10695],{}," Validation is opt-in, so ",[941,18536,17434],{}," opens a vault and derives a real — if low-entropy — KEK; the same PIN then works for setup, cold unlock, and resume, because it ",[1092,18539,3082],{}," the master secret. Its low entropy becomes the vault's real strength floor, so pair it with your own rate-limiting or ",[1007,18542,18543],{"href":15983},[941,18544,15984],{}," lockout.",[937,18547,18548,18549,18551,18552,18554,18555,18557,18558,18560],{},"What you ",[950,18550,18493],{}," do is get \"only a PIN\" out of ",[941,18553,15958],{},": under managed mode the user types nothing at cold start (the provider unseals), and ",[941,18556,15958],{}," merely speeds a warm resume within a live session. So \"unlock with just a PIN\" is either a weak tier-1 secret (the PIN ",[1092,18559,3082],{}," the key) or managed mode (the user types nothing) — never the resume PIN acting as a cold primary.",[1022,18562,18564],{"id":18563},"combining-methods-alternatives-and-2fa","Combining methods — alternatives and 2FA",[937,18566,18567,18568,18570,18571,1520],{},"Two compositions get confused — one is ",[1092,18569,7493],{},", the other is ",[1092,18572,14128],{},[937,18574,18575,18578],{},[950,18576,18577],{},"Alternatives — any one unlocks (the multi-slot model)."," Enroll several tier-2 authenticators on one keyring; any single one opens the vault, so a user isn't locked out when they're away from one device. The slots are peers — adding or removing one is a constant-time keyring write that never re-keys the DEKs.",[2217,18580,18582],{"className":2219,"code":18581,"language":2221,"meta":1037,"style":1037},"\u002F\u002F Enroll two independent tier-2 slots on the same vault (wrap material comes from the on-* packages)\nawait db.team.enrollAuthenticator('acme', { id: 'passkey', method: 'webauthn', wrapped_kek, meta })\nawait db.team.enrollAuthenticator('acme', { id: 'work-pw', method: 'password', wrapped_deks, iv, meta })\n\n\u002F\u002F Unlock with EITHER — whichever the user has to hand:\nawait db.team.unlockViaAuthenticator('acme', 'passkey', verifyPasskey)   \u002F\u002F on the laptop\nawait db.team.unlockViaAuthenticator('acme', 'work-pw', verifyPassword)  \u002F\u002F or later, elsewhere\n",[941,18583,18584,18589,18652,18719,18723,18728,18766],{"__ignoreMap":1037},[2225,18585,18586],{"class":2227,"line":2228},[2225,18587,18588],{"class":2763},"\u002F\u002F Enroll two independent tier-2 slots on the same vault (wrap material comes from the on-* packages)\n",[2225,18590,18591,18593,18595,18597,18599,18601,18603,18605,18607,18609,18611,18613,18615,18617,18619,18621,18624,18626,18628,18630,18632,18634,18636,18638,18640,18643,18645,18648,18650],{"class":2227,"line":1041},[2225,18592,6500],{"class":2231},[2225,18594,2842],{"class":2239},[2225,18596,1520],{"class":2235},[2225,18598,868],{"class":2239},[2225,18600,1520],{"class":2235},[2225,18602,17521],{"class":2291},[2225,18604,2789],{"class":2239},[2225,18606,2805],{"class":2235},[2225,18608,6450],{"class":2252},[2225,18610,2805],{"class":2235},[2225,18612,2298],{"class":2235},[2225,18614,2236],{"class":2235},[2225,18616,7070],{"class":2307},[2225,18618,2311],{"class":2235},[2225,18620,2249],{"class":2235},[2225,18622,18623],{"class":2252},"passkey",[2225,18625,2805],{"class":2235},[2225,18627,2298],{"class":2235},[2225,18629,15939],{"class":2307},[2225,18631,2311],{"class":2235},[2225,18633,2249],{"class":2235},[2225,18635,337],{"class":2252},[2225,18637,2805],{"class":2235},[2225,18639,2298],{"class":2235},[2225,18641,18642],{"class":2239}," wrapped_kek",[2225,18644,2298],{"class":2235},[2225,18646,18647],{"class":2239}," meta ",[2225,18649,2359],{"class":2235},[2225,18651,2362],{"class":2239},[2225,18653,18654,18656,18658,18660,18662,18664,18666,18668,18670,18672,18674,18676,18678,18680,18682,18684,18687,18689,18691,18693,18695,18697,18699,18701,18703,18706,18708,18711,18713,18715,18717],{"class":2227,"line":1038},[2225,18655,6500],{"class":2231},[2225,18657,2842],{"class":2239},[2225,18659,1520],{"class":2235},[2225,18661,868],{"class":2239},[2225,18663,1520],{"class":2235},[2225,18665,17521],{"class":2291},[2225,18667,2789],{"class":2239},[2225,18669,2805],{"class":2235},[2225,18671,6450],{"class":2252},[2225,18673,2805],{"class":2235},[2225,18675,2298],{"class":2235},[2225,18677,2236],{"class":2235},[2225,18679,7070],{"class":2307},[2225,18681,2311],{"class":2235},[2225,18683,2249],{"class":2235},[2225,18685,18686],{"class":2252},"work-pw",[2225,18688,2805],{"class":2235},[2225,18690,2298],{"class":2235},[2225,18692,15939],{"class":2307},[2225,18694,2311],{"class":2235},[2225,18696,2249],{"class":2235},[2225,18698,313],{"class":2252},[2225,18700,2805],{"class":2235},[2225,18702,2298],{"class":2235},[2225,18704,18705],{"class":2239}," wrapped_deks",[2225,18707,2298],{"class":2235},[2225,18709,18710],{"class":2239}," iv",[2225,18712,2298],{"class":2235},[2225,18714,18647],{"class":2239},[2225,18716,2359],{"class":2235},[2225,18718,2362],{"class":2239},[2225,18720,18721],{"class":2227,"line":2288},[2225,18722,2285],{"emptyLinePlaceholder":2284},[2225,18724,18725],{"class":2227,"line":2304},[2225,18726,18727],{"class":2763},"\u002F\u002F Unlock with EITHER — whichever the user has to hand:\n",[2225,18729,18730,18732,18734,18736,18738,18740,18742,18744,18746,18748,18750,18752,18754,18756,18758,18760,18763],{"class":2227,"line":2356},[2225,18731,6500],{"class":2231},[2225,18733,2842],{"class":2239},[2225,18735,1520],{"class":2235},[2225,18737,868],{"class":2239},[2225,18739,1520],{"class":2235},[2225,18741,18002],{"class":2291},[2225,18743,2789],{"class":2239},[2225,18745,2805],{"class":2235},[2225,18747,6450],{"class":2252},[2225,18749,2805],{"class":2235},[2225,18751,2298],{"class":2235},[2225,18753,2249],{"class":2235},[2225,18755,18623],{"class":2252},[2225,18757,2805],{"class":2235},[2225,18759,2298],{"class":2235},[2225,18761,18762],{"class":2239}," verifyPasskey)   ",[2225,18764,18765],{"class":2763},"\u002F\u002F on the laptop\n",[2225,18767,18768,18770,18772,18774,18776,18778,18780,18782,18784,18786,18788,18790,18792,18794,18796,18798,18801],{"class":2227,"line":2830},[2225,18769,6500],{"class":2231},[2225,18771,2842],{"class":2239},[2225,18773,1520],{"class":2235},[2225,18775,868],{"class":2239},[2225,18777,1520],{"class":2235},[2225,18779,18002],{"class":2291},[2225,18781,2789],{"class":2239},[2225,18783,2805],{"class":2235},[2225,18785,6450],{"class":2252},[2225,18787,2805],{"class":2235},[2225,18789,2298],{"class":2235},[2225,18791,2249],{"class":2235},[2225,18793,18686],{"class":2252},[2225,18795,2805],{"class":2235},[2225,18797,2298],{"class":2235},[2225,18799,18800],{"class":2239}," verifyPassword)  ",[2225,18802,18803],{"class":2763},"\u002F\u002F or later, elsewhere\n",[937,18805,18806,18809,18810,18812,18813,18816,18817,18819],{},[950,18807,18808],{},"Two factors together — 2FA (the policy gate)."," The unlock is one factor; a ",[950,18811,16983],{}," demands a ",[1092,18814,18815],{},"second, fresh"," factor before a sensitive action. This is the ",[1092,18818,14128],{},": unlock with your passkey (something you have), then prove a TOTP code (something on your phone) before the vault will export.",[2217,18821,18823],{"className":2219,"code":18822,"language":2221,"meta":1037,"style":1037},"import { createNoydb, PERSONAL_POLICY, PolicyDeniedError } from '@noy-db\u002Fhub'\n\nconst db = await createNoydb({\n  store, user: 'alice', secret,\n  policy: {\n    ...PERSONAL_POLICY,\n    gates: {\n      ...PERSONAL_POLICY.gates,\n      \u002F\u002F exporting plaintext now needs a tier-2 session PLUS a fresh TOTP proof\n      'export-plaintext': { minTier: 2, factors: [{ anyOf: ['totp'], freshnessMs: 5 * 60_000 }] },\n    },\n  },\n})\n\n\u002F\u002F …unlock (tier 2) as usual, then present the second factor at the sensitive action:\ntry {\n  await db.checkGate('acme', 'export-plaintext', {\n    factors: [{ kind: 'totp', payload: { code: '123456' }, mintedAt: new Date().toISOString() }],\n  })\n  \u002F\u002F …run the export\n} catch (e) {\n  if (e instanceof PolicyDeniedError) {\n    \u002F\u002F e.reason: 'insufficient-tier' | 'missing-factor' | 'stale-proof' | …\n  }\n}\n",[941,18824,18825,18853,18857,18873,18895,18904,18913,18922,18935,18940,19009,19013,19018,19024,19028,19033,19039,19069,19139,19146,19151,19162,19179,19184,19188],{"__ignoreMap":1037},[2225,18826,18827,18829,18831,18833,18835,18838,18840,18843,18845,18847,18849,18851],{"class":2227,"line":2228},[2225,18828,2232],{"class":2231},[2225,18830,2236],{"class":2235},[2225,18832,2744],{"class":2239},[2225,18834,2298],{"class":2235},[2225,18836,18837],{"class":2239}," PERSONAL_POLICY",[2225,18839,2298],{"class":2235},[2225,18841,18842],{"class":2239}," PolicyDeniedError",[2225,18844,2243],{"class":2235},[2225,18846,2246],{"class":2231},[2225,18848,2249],{"class":2235},[2225,18850,1086],{"class":2252},[2225,18852,2256],{"class":2235},[2225,18854,18855],{"class":2227,"line":1041},[2225,18856,2285],{"emptyLinePlaceholder":2284},[2225,18858,18859,18861,18863,18865,18867,18869,18871],{"class":2227,"line":1038},[2225,18860,2775],{"class":2774},[2225,18862,2778],{"class":2239},[2225,18864,2781],{"class":2235},[2225,18866,2784],{"class":2231},[2225,18868,2744],{"class":2291},[2225,18870,2789],{"class":2239},[2225,18872,4200],{"class":2235},[2225,18874,18875,18877,18879,18881,18883,18885,18887,18889,18891,18893],{"class":2227,"line":2288},[2225,18876,15608],{"class":2239},[2225,18878,2298],{"class":2235},[2225,18880,2795],{"class":2307},[2225,18882,2311],{"class":2235},[2225,18884,2249],{"class":2235},[2225,18886,6408],{"class":2252},[2225,18888,2805],{"class":2235},[2225,18890,2298],{"class":2235},[2225,18892,2810],{"class":2239},[2225,18894,1707],{"class":2235},[2225,18896,18897,18900,18902],{"class":2227,"line":2304},[2225,18898,18899],{"class":2307},"  policy",[2225,18901,2311],{"class":2235},[2225,18903,2301],{"class":2235},[2225,18905,18906,18909,18911],{"class":2227,"line":2356},[2225,18907,18908],{"class":2235},"    ...",[2225,18910,12830],{"class":2239},[2225,18912,1707],{"class":2235},[2225,18914,18915,18918,18920],{"class":2227,"line":2830},[2225,18916,18917],{"class":2307},"    gates",[2225,18919,2311],{"class":2235},[2225,18921,2301],{"class":2235},[2225,18923,18924,18927,18929,18931,18933],{"class":2227,"line":2861},[2225,18925,18926],{"class":2235},"      ...",[2225,18928,12830],{"class":2239},[2225,18930,1520],{"class":2235},[2225,18932,12826],{"class":2239},[2225,18934,1707],{"class":2235},[2225,18936,18937],{"class":2227,"line":4246},[2225,18938,18939],{"class":2763},"      \u002F\u002F exporting plaintext now needs a tier-2 session PLUS a fresh TOTP proof\n",[2225,18941,18942,18945,18948,18950,18952,18954,18957,18959,18962,18964,18966,18968,18970,18972,18975,18977,18979,18981,18983,18985,18987,18989,18992,18994,18997,18999,19002,19004,19006],{"class":2227,"line":4253},[2225,18943,18944],{"class":2235},"      '",[2225,18946,18947],{"class":2307},"export-plaintext",[2225,18949,2805],{"class":2235},[2225,18951,2311],{"class":2235},[2225,18953,2236],{"class":2235},[2225,18955,18956],{"class":2307}," minTier",[2225,18958,2311],{"class":2235},[2225,18960,18961],{"class":6527}," 2",[2225,18963,2298],{"class":2235},[2225,18965,17534],{"class":2307},[2225,18967,2311],{"class":2235},[2225,18969,8667],{"class":2239},[2225,18971,2792],{"class":2235},[2225,18973,18974],{"class":2307}," anyOf",[2225,18976,2311],{"class":2235},[2225,18978,8667],{"class":2239},[2225,18980,2805],{"class":2235},[2225,18982,333],{"class":2252},[2225,18984,2805],{"class":2235},[2225,18986,8803],{"class":2239},[2225,18988,2298],{"class":2235},[2225,18990,18991],{"class":2307}," freshnessMs",[2225,18993,2311],{"class":2235},[2225,18995,18996],{"class":6527}," 5",[2225,18998,2263],{"class":2235},[2225,19000,19001],{"class":6527}," 60_000",[2225,19003,2243],{"class":2235},[2225,19005,8890],{"class":2239},[2225,19007,19008],{"class":2235},"},\n",[2225,19010,19011],{"class":2227,"line":4259},[2225,19012,8324],{"class":2235},[2225,19014,19015],{"class":2227,"line":6548},[2225,19016,19017],{"class":2235},"  },\n",[2225,19019,19020,19022],{"class":2227,"line":6576},[2225,19021,2359],{"class":2235},[2225,19023,2362],{"class":2239},[2225,19025,19026],{"class":2227,"line":6582},[2225,19027,2285],{"emptyLinePlaceholder":2284},[2225,19029,19030],{"class":2227,"line":6587},[2225,19031,19032],{"class":2763},"\u002F\u002F …unlock (tier 2) as usual, then present the second factor at the sensitive action:\n",[2225,19034,19035,19037],{"class":2227,"line":7174},[2225,19036,7554],{"class":2231},[2225,19038,2301],{"class":2235},[2225,19040,19041,19043,19045,19047,19049,19051,19053,19055,19057,19059,19061,19063,19065,19067],{"class":2227,"line":7180},[2225,19042,7561],{"class":2231},[2225,19044,2842],{"class":2239},[2225,19046,1520],{"class":2235},[2225,19048,12982],{"class":2291},[2225,19050,2789],{"class":2307},[2225,19052,2805],{"class":2235},[2225,19054,6450],{"class":2252},[2225,19056,2805],{"class":2235},[2225,19058,2298],{"class":2235},[2225,19060,2249],{"class":2235},[2225,19062,18947],{"class":2252},[2225,19064,2805],{"class":2235},[2225,19066,2298],{"class":2235},[2225,19068,2301],{"class":2235},[2225,19070,19071,19074,19076,19078,19080,19082,19084,19086,19088,19090,19092,19095,19097,19099,19102,19104,19106,19109,19111,19113,19116,19118,19120,19123,19125,19127,19130,19133,19135,19137],{"class":2227,"line":7220},[2225,19072,19073],{"class":2307},"    factors",[2225,19075,2311],{"class":2235},[2225,19077,8667],{"class":2307},[2225,19079,2792],{"class":2235},[2225,19081,8285],{"class":2307},[2225,19083,2311],{"class":2235},[2225,19085,2249],{"class":2235},[2225,19087,333],{"class":2252},[2225,19089,2805],{"class":2235},[2225,19091,2298],{"class":2235},[2225,19093,19094],{"class":2307}," payload",[2225,19096,2311],{"class":2235},[2225,19098,2236],{"class":2235},[2225,19100,19101],{"class":2307}," code",[2225,19103,2311],{"class":2235},[2225,19105,2249],{"class":2235},[2225,19107,19108],{"class":2252},"123456",[2225,19110,2805],{"class":2235},[2225,19112,10965],{"class":2235},[2225,19114,19115],{"class":2307}," mintedAt",[2225,19117,2311],{"class":2235},[2225,19119,8130],{"class":2235},[2225,19121,19122],{"class":2291}," Date",[2225,19124,4228],{"class":2307},[2225,19126,1520],{"class":2235},[2225,19128,19129],{"class":2291},"toISOString",[2225,19131,19132],{"class":2307},"() ",[2225,19134,2359],{"class":2235},[2225,19136,8803],{"class":2307},[2225,19138,1707],{"class":2235},[2225,19140,19141,19144],{"class":2227,"line":7225},[2225,19142,19143],{"class":2235},"  }",[2225,19145,2362],{"class":2307},[2225,19147,19148],{"class":2227,"line":7231},[2225,19149,19150],{"class":2763},"  \u002F\u002F …run the export\n",[2225,19152,19153,19155,19157,19160],{"class":2227,"line":7267},[2225,19154,2359],{"class":2235},[2225,19156,7598],{"class":2231},[2225,19158,19159],{"class":2239}," (e) ",[2225,19161,4200],{"class":2235},[2225,19163,19164,19166,19168,19170,19173,19175,19177],{"class":2227,"line":7272},[2225,19165,7608],{"class":2231},[2225,19167,953],{"class":2307},[2225,19169,12450],{"class":2239},[2225,19171,19172],{"class":2235}," instanceof",[2225,19174,18842],{"class":6342},[2225,19176,8495],{"class":2307},[2225,19178,4200],{"class":2235},[2225,19180,19181],{"class":2227,"line":7278},[2225,19182,19183],{"class":2763},"    \u002F\u002F e.reason: 'insufficient-tier' | 'missing-factor' | 'stale-proof' | …\n",[2225,19185,19186],{"class":2227,"line":7303},[2225,19187,7632],{"class":2235},[2225,19189,19190],{"class":2227,"line":7308},[2225,19191,7351],{"class":2235},[937,19193,19194,19199,19200,19202],{},[950,19195,19196,19197,17534],{},"Require two ",[1092,19198,14691],{}," (true 2-of-N) — set ",[941,19201,3236],{}," on the requirement:",[2217,19204,19206],{"className":2219,"code":19205,"language":2221,"meta":1037,"style":1037},"'rotate-secret': { minTier: 1, factors: [{ anyOf: ['totp', 'email-otp', 'recovery'], count: 2 }] }\n\u002F\u002F the gate now needs TWO different factors from the list, each freshly proven\n",[941,19207,19208,19281],{"__ignoreMap":1037},[2225,19209,19210,19212,19214,19216,19218,19220,19222,19224,19226,19228,19230,19232,19234,19236,19238,19240,19242,19244,19246,19248,19250,19252,19254,19256,19258,19260,19262,19264,19266,19268,19271,19273,19275,19277,19279],{"class":2227,"line":2228},[2225,19211,2805],{"class":2235},[2225,19213,15169],{"class":2252},[2225,19215,2805],{"class":2235},[2225,19217,2085],{"class":2239},[2225,19219,2792],{"class":2235},[2225,19221,18956],{"class":2307},[2225,19223,2311],{"class":2235},[2225,19225,9377],{"class":6527},[2225,19227,2298],{"class":2235},[2225,19229,17534],{"class":2307},[2225,19231,2311],{"class":2235},[2225,19233,8667],{"class":2239},[2225,19235,2792],{"class":2235},[2225,19237,18974],{"class":2307},[2225,19239,2311],{"class":2235},[2225,19241,8667],{"class":2239},[2225,19243,2805],{"class":2235},[2225,19245,333],{"class":2252},[2225,19247,2805],{"class":2235},[2225,19249,2298],{"class":2235},[2225,19251,2249],{"class":2235},[2225,19253,301],{"class":2252},[2225,19255,2805],{"class":2235},[2225,19257,2298],{"class":2235},[2225,19259,2249],{"class":2235},[2225,19261,321],{"class":2252},[2225,19263,2805],{"class":2235},[2225,19265,8803],{"class":2239},[2225,19267,2298],{"class":2235},[2225,19269,19270],{"class":2307}," count",[2225,19272,2311],{"class":2235},[2225,19274,18961],{"class":6527},[2225,19276,2243],{"class":2235},[2225,19278,8890],{"class":2239},[2225,19280,7351],{"class":2235},[2225,19282,19283],{"class":2227,"line":1041},[2225,19284,19285],{"class":2763},"\u002F\u002F the gate now needs TWO different factors from the list, each freshly proven\n",[937,19287,19288,19289,19301,19302,19304,19305,19308,19309,1520],{},"The mental model: ",[950,19290,19291,19292,19294,19295,19297,19298,19300],{},"alternatives (",[1092,19293,7493],{},") are multiple tier-2 slots; combined 2FA (",[1092,19296,14128],{},") is a policy gate's ",[941,19299,13051],{}," requirement layered on the unlock."," A gate can demand ",[941,19303,3236],{}," distinct factors within a ",[941,19306,19307],{},"freshnessMs"," window; the full DSL, factor kinds, and shipped presets live on ",[1007,19310,19312],{"href":19311},"..\u002F..\u002Fservices\u002Fsession-tiers#policy-gates","session-tiers → policy gates",[1022,19314,19315],{"id":613},"Auth introspection",[937,19317,19318,19319,19322,19323,1520],{},"Read-only views of a vault's auth setup: a plain-English summary, a Mermaid diagram of the tier model, and per-user (or bulk) enrollment summaries. The two vault-wide surfaces are ungated; the per-user surfaces are guarded by the ",[941,19320,19321],{},"view-user-auth"," gate (disabled by default) and pass their output through a strict field allowlist. Full treatment: ",[1007,19324,613],{"href":19325},"..\u002F..\u002Fservices\u002Fauth-introspection",[1591,19327,19329],{"id":19328},"describeauthconfig",[941,19330,19331],{},"describeAuthConfig()",[2217,19333,19335],{"className":2219,"code":19334,"language":2221,"meta":1037,"style":1037},"describeAuthConfig(vault: string): Promise\u003Cstring>\n",[941,19336,19337],{"__ignoreMap":1037},[2225,19338,19339,19342,19344,19346,19348,19350],{"class":2227,"line":2228},[2225,19340,19341],{"class":2291},"describeAuthConfig",[2225,19343,12765],{"class":2239},[2225,19345,11740],{"class":6342},[2225,19347,6474],{"class":2235},[2225,19349,7259],{"class":2239},[2225,19351,7093],{"class":2235},[937,19353,19354],{},"A plain-English summary of the vault's auth configuration — enrolled tiers, slots, and recovery profiles. Ungated.",[1111,19356,19357],{},[1114,19358,19359,19361],{},[941,19360,2694],{}," — the vault to describe.",[1591,19363,19365],{"id":19364},"diagramauthconfig",[941,19366,19367],{},"diagramAuthConfig()",[2217,19369,19371],{"className":2219,"code":19370,"language":2221,"meta":1037,"style":1037},"diagramAuthConfig(vault: string): Promise\u003Cstring>\n",[941,19372,19373],{"__ignoreMap":1037},[2225,19374,19375,19378,19380,19382,19384,19386],{"class":2227,"line":2228},[2225,19376,19377],{"class":2291},"diagramAuthConfig",[2225,19379,12765],{"class":2239},[2225,19381,11740],{"class":6342},[2225,19383,6474],{"class":2235},[2225,19385,7259],{"class":2239},[2225,19387,7093],{"class":2235},[937,19389,19390,19391,19394],{},"A Mermaid ",[941,19392,19393],{},"flowchart TB"," of the vault's tier model, for UI rendering. Ungated.",[1111,19396,19397],{},[1114,19398,19399,19401],{},[941,19400,2694],{}," — the vault to diagram.",[1591,19403,19405],{"id":19404},"describeuserauth",[941,19406,19407],{},"describeUserAuth()",[2217,19409,19411],{"className":2219,"code":19410,"language":2221,"meta":1037,"style":1037},"describeUserAuth(vault: string, userId: string, factors?: FactorProofBundle): Promise\u003Cstring>\n",[941,19412,19413],{"__ignoreMap":1037},[2225,19414,19415,19418,19420,19422,19425,19427,19429,19431,19433,19435,19437,19439],{"class":2227,"line":2228},[2225,19416,19417],{"class":2291},"describeUserAuth",[2225,19419,17524],{"class":2239},[2225,19421,2298],{"class":2235},[2225,19423,19424],{"class":2239}," userId: string",[2225,19426,2298],{"class":2235},[2225,19428,17534],{"class":2239},[2225,19430,7011],{"class":2235},[2225,19432,17539],{"class":2239},[2225,19434,11740],{"class":6342},[2225,19436,6474],{"class":2235},[2225,19438,7259],{"class":2239},[2225,19440,7093],{"class":2235},[937,19442,19443,19444,19446],{},"A per-user enrollment summary, passed through a strict field allowlist. Guarded by the ",[941,19445,19321],{}," gate (disabled by default).",[1111,19448,19449,19454,19459],{},[1114,19450,19451,19453],{},[941,19452,2694],{}," — the vault the user belongs to.",[1114,19455,19456,19458],{},[941,19457,10749],{}," — the principal whose auth to describe.",[1114,19460,19461,16475,19463,13046],{},[941,19462,13051],{},[941,19464,19321],{},[1591,19466,19468],{"id":19467},"describeallusersauth",[941,19469,19470],{},"describeAllUsersAuth()",[2217,19472,19474],{"className":2219,"code":19473,"language":2221,"meta":1037,"style":1037},"describeAllUsersAuth(vault: string, factors?: FactorProofBundle): Promise\u003CArray\u003C{ userId: string; description: string }>>\n",[941,19475,19476],{"__ignoreMap":1037},[2225,19477,19478,19481,19483,19485,19487,19489,19491,19493,19495,19498,19500,19502,19504,19506,19508,19511,19513,19515],{"class":2227,"line":2228},[2225,19479,19480],{"class":2291},"describeAllUsersAuth",[2225,19482,17524],{"class":2239},[2225,19484,2298],{"class":2235},[2225,19486,17534],{"class":2239},[2225,19488,7011],{"class":2235},[2225,19490,17539],{"class":2239},[2225,19492,11740],{"class":6342},[2225,19494,6474],{"class":2235},[2225,19496,19497],{"class":2239},"Array",[2225,19499,17851],{"class":2235},[2225,19501,10668],{"class":6342},[2225,19503,2311],{"class":2235},[2225,19505,6366],{"class":2239},[2225,19507,3769],{"class":2235},[2225,19509,19510],{"class":6342}," description",[2225,19512,2311],{"class":2235},[2225,19514,6366],{"class":2239},[2225,19516,17964],{"class":2235},[937,19518,19519,19520,19522,19523,13046],{},"The bulk variant of ",[941,19521,19417],{}," — one allowlisted description per user. Guarded by the ",[941,19524,19321],{},[1111,19526,19527,19532],{},[1114,19528,19529,19531],{},[941,19530,2694],{}," — the vault whose users to describe.",[1114,19533,19534,16475,19536,13046],{},[941,19535,13051],{},[941,19537,19321],{},[2645,19539,19540],{},"html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sHdIc, html code.shiki .sHdIc{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#EEFFFF;--shiki-default-font-style:italic;--shiki-dark:#BABED8;--shiki-dark-font-style:italic}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}",{"title":1037,"searchDepth":1038,"depth":1038,"links":19542},[19543,19544,19545,19546,19547,19548,19559,19566,19567,19568],{"id":16992,"depth":1041,"text":16993},{"id":17066,"depth":1041,"text":17067},{"id":17308,"depth":1041,"text":17309},{"id":17321,"depth":1041,"text":17322},{"id":17457,"depth":1041,"text":17458},{"id":17490,"depth":1041,"text":17491,"children":19549},[19550,19551,19552,19553,19554,19555,19556,19557],{"id":17508,"depth":1038,"text":17511},{"id":17596,"depth":1038,"text":17599},{"id":17658,"depth":1038,"text":17661},{"id":17711,"depth":1038,"text":17714},{"id":17798,"depth":1038,"text":17801},{"id":17915,"depth":1038,"text":17918},{"id":17989,"depth":1038,"text":17992},{"id":18084,"depth":1038,"text":19558},"Tier-2 on-* families",{"id":18170,"depth":1041,"text":17096,"children":19560},[19561,19562,19563,19564],{"id":18184,"depth":1038,"text":18187},{"id":18254,"depth":1038,"text":18257},{"id":18346,"depth":1038,"text":18349},{"id":18376,"depth":1038,"text":19565},"Tier-3 on-* family",{"id":18395,"depth":1041,"text":18396},{"id":18563,"depth":1041,"text":18564},{"id":613,"depth":1041,"text":19315,"children":19569},[19570,19571,19572,19573],{"id":19328,"depth":1038,"text":19331},{"id":19364,"depth":1038,"text":19367},{"id":19404,"depth":1038,"text":19407},{"id":19467,"depth":1038,"text":19470},"The strategy-free session-auth ladder: tier 1 (secret \u002F getKeyring), tier-2 authenticators and on-* families, tier-3 quick-unlock, and auth introspection.",{},{"title":73,"description":19574},"2UzuYLsMWZeDywZW3pxXRJbinK87EqIWg2pO4UODPX8",{"id":19579,"title":77,"access":12,"apiToc":2284,"body":19580,"category":1042,"description":20129,"extension":1044,"meta":20130,"navigation":1042,"nest":2635,"path":78,"seo":20131,"sinceVersion":1042,"status":1049,"stem":79,"subtitle":1042,"tier":6922,"tocExternal":1042,"__hash__":20132},"docs\u002Fcore\u002F2.noydb\u002F5.recovery.md",{"type":931,"value":19581,"toc":20117},[19582,19585,19594,19597,19622,19654,19658,19661,19667,19693,19703,19725,19731,19779,19790,19797,19803,19837,19844,19871,19875,19878,19884,19955,19961,19979,19985,20024,20035,20057,20063,20092,20095,20114],[934,19583,77],{"id":19584},"auth-recovery",[937,19586,19587,19589,19590,19593],{},[950,19588,15200],{}," is how a vault regains access when the factors that normally open it are lost — a forgotten secret, a lost passkey, a wiped device. Because noy-db is zero-knowledge, there is no server-side reset: recovery works only if a ",[950,19591,19592],{},"recovery profile"," was enrolled ahead of time, holding a separate path to the DEKs that does not collapse the security model.",[937,19595,19596],{},"Two profiles are implemented end to end:",[1111,19598,19599,19608],{},[1114,19600,19601,19604,19605,1520],{},[950,19602,19603],{},"paper"," — single-use codes; a matching code unwraps the cached DEKs, then rewraps them under a fresh KEK. Backed by ",[1007,19606,18152],{"href":19607},"..\u002F..\u002Ffamilies\u002Fon\u002Frecovery",[1114,19609,19610,19612,19613,9483,19616,19618,19619,19621],{},[950,19611,325],{}," — K-of-N shares reconstruct the DEK set; requires a ",[941,19614,19615],{},"NoydbShamir",[1007,19617,15994],{"href":15993},") at ",[941,19620,2134],{}," time.",[937,19623,19624,19625,997,19628,19631,19632,19634,19635,19637,19638,19644,19645,19647,19648,19650,19651,19653],{},"The reserved ",[941,19626,19627],{},"multi-channel",[941,19629,19630],{},"admin-mediated"," profiles ship only their API shape and throw ",[941,19633,16540],{}," in use. The profile lifecycle, the two shipped presets' ",[941,19636,16586],{}," gate, and managed-secret mode are covered in depth on ",[950,19639,19640],{},[1007,19641,19643],{"href":19642},"..\u002F..\u002Fservices\u002Fsession-tiers#rotate-and-recover-the-secret","session-tiers → Rotate and recover",". Secret reset itself — from a remembered secret (",[941,19646,15216],{},") or a recovery proof (",[941,19649,16494],{},") — lives on ",[1007,19652,69],{"href":11634},"; this page covers enrolling and managing the profiles and the recovery flows built on them.",[1022,19655,19657],{"id":19656},"recovery-profiles","Recovery profiles",[937,19659,19660],{},"Enroll, list, and regenerate the paper \u002F Shamir recovery profiles a vault can recover through.",[1591,19662,19664],{"id":19663},"enrollrecovery",[941,19665,19666],{},"enrollRecovery()",[2217,19668,19670],{"className":2219,"code":19669,"language":2221,"meta":1037,"style":1037},"enrollRecovery(vault: string, enrollment: RecoveryEnrollmentInput): Promise\u003CEnrollRecoveryResult>\n",[941,19671,19672],{"__ignoreMap":1037},[2225,19673,19674,19677,19679,19681,19684,19686,19688,19691],{"class":2227,"line":2228},[2225,19675,19676],{"class":2291},"enrollRecovery",[2225,19678,17524],{"class":2239},[2225,19680,2298],{"class":2235},[2225,19682,19683],{"class":2239}," enrollment: RecoveryEnrollmentInput): ",[2225,19685,11740],{"class":6342},[2225,19687,6474],{"class":2235},[2225,19689,19690],{"class":2239},"EnrollRecoveryResult",[2225,19692,7093],{"class":2235},[937,19694,19695,19696,19698,19699,19702],{},"Persist a recovery enrollment — a ",[941,19697,16536],{}," code sheet or a ",[941,19700,19701],{},"'shamir'"," K-of-N entry. The result carries the material to surface once (the paper codes, or the Shamir shares); the hub does not retain it.",[1111,19704,19705,19710],{},[1114,19706,19707,19709],{},[941,19708,2694],{}," — the vault to enroll recovery for.",[1114,19711,19712,12536,19715,19718,19719,4688,19722,957],{},[941,19713,19714],{},"enrollment",[941,19716,19717],{},"RecoveryEnrollmentInput"," selecting the profile and its parameters (e.g. ",[941,19720,19721],{},"{ profile: 'paper', entries }",[941,19723,19724],{},"{ profile: 'shamir', k, n }",[1591,19726,19728],{"id":19727},"listrecoveryentries",[941,19729,19730],{},"listRecoveryEntries()",[2217,19732,19734],{"className":2219,"code":19733,"language":2221,"meta":1037,"style":1037},"listRecoveryEntries(vault: string): Promise\u003C{ paper: ReadonlyArray\u003CPaperRecoveryEntry>; shamir: ReadonlyArray\u003CShamirRecoveryEntry> }>\n",[941,19735,19736],{"__ignoreMap":1037},[2225,19737,19738,19741,19743,19745,19747,19750,19752,19755,19757,19760,19763,19766,19768,19770,19772,19775,19777],{"class":2227,"line":2228},[2225,19739,19740],{"class":2291},"listRecoveryEntries",[2225,19742,12765],{"class":2239},[2225,19744,11740],{"class":6342},[2225,19746,17851],{"class":2235},[2225,19748,19749],{"class":6342}," paper",[2225,19751,2311],{"class":2235},[2225,19753,19754],{"class":2239}," ReadonlyArray",[2225,19756,6474],{"class":2235},[2225,19758,19759],{"class":2239},"PaperRecoveryEntry",[2225,19761,19762],{"class":2235},">;",[2225,19764,19765],{"class":6342}," shamir",[2225,19767,2311],{"class":2235},[2225,19769,19754],{"class":2239},[2225,19771,6474],{"class":2235},[2225,19773,19774],{"class":2239},"ShamirRecoveryEntry",[2225,19776,6480],{"class":2235},[2225,19778,17861],{"class":2235},[937,19780,19781,19782,997,19784,19786,19787,19789],{},"Read the persisted recovery entries, split into the ",[941,19783,19603],{},[941,19785,325],{}," sets. Used by ",[941,19788,19341],{}," and for showing a vault's recovery posture.",[1111,19791,19792],{},[1114,19793,19794,19796],{},[941,19795,2694],{}," — the vault whose recovery entries to read.",[1591,19798,19800],{"id":19799},"rotaterecovery",[941,19801,19802],{},"rotateRecovery()",[2217,19804,19806],{"className":2219,"code":19805,"language":2221,"meta":1037,"style":1037},"rotateRecovery(vault: string, options: RotateRecoveryOptions, factors?: FactorProofBundle): Promise\u003CRotateRecoveryResult>\n",[941,19807,19808],{"__ignoreMap":1037},[2225,19809,19810,19813,19815,19817,19820,19822,19824,19826,19828,19830,19832,19835],{"class":2227,"line":2228},[2225,19811,19812],{"class":2291},"rotateRecovery",[2225,19814,17524],{"class":2239},[2225,19816,2298],{"class":2235},[2225,19818,19819],{"class":2239}," options: RotateRecoveryOptions",[2225,19821,2298],{"class":2235},[2225,19823,17534],{"class":2239},[2225,19825,7011],{"class":2235},[2225,19827,17539],{"class":2239},[2225,19829,11740],{"class":6342},[2225,19831,6474],{"class":2235},[2225,19833,19834],{"class":2239},"RotateRecoveryResult",[2225,19836,7093],{"class":2235},[937,19838,19839,19840,19843],{},"Regenerate the recovery material for an already-enrolled profile — reissue a paper sheet, or re-share a Shamir entry — replacing the existing entry rather than minting one from scratch (it throws if none is enrolled). When a vault has multiple Shamir entries, pass ",[941,19841,19842],{},"entryId"," to disambiguate which one to rotate.",[1111,19845,19846,19851,19866],{},[1114,19847,19848,19850],{},[941,19849,2694],{}," — the vault whose recovery material to rotate.",[1114,19852,19853,12536,19855,19858,19859,19862,19863,19865],{},[941,19854,16362],{},[941,19856,19857],{},"RotateRecoveryOptions"," naming the ",[941,19860,19861],{},"profile"," (and ",[941,19864,19842],{}," for a specific Shamir entry).",[1114,19867,19868,19870],{},[941,19869,13051],{}," — factor proofs presented when the policy gate demands them.",[1022,19872,19874],{"id":19873},"recovery-flows","Recovery flows",[937,19876,19877],{},"The flows that consume a recovery profile: bootstrapping a managed-secret vault, resetting a managed secret, and peer-recovering another user's keyring.",[1591,19879,19881],{"id":19880},"openvaultandenrollrecovery",[941,19882,19883],{},"openVaultAndEnrollRecovery()",[2217,19885,19887],{"className":2219,"code":19886,"language":2221,"meta":1037,"style":1037},"openVaultAndEnrollRecovery(vault: string, opts: { recovery: ReadonlyArray\u003CRecoveryEnrollmentInput>; locale?: string }): Promise\u003C{ vault: Vault; recoveryEnrollments: ReadonlyArray\u003CEnrollRecoveryResult> }>\n",[941,19888,19889],{"__ignoreMap":1037},[2225,19890,19891,19894,19896,19898,19901,19903,19906,19908,19910,19912,19914,19916,19919,19921,19924,19926,19928,19930,19932,19934,19936,19938,19940,19943,19945,19947,19949,19951,19953],{"class":2227,"line":2228},[2225,19892,19893],{"class":2291},"openVaultAndEnrollRecovery",[2225,19895,17524],{"class":2239},[2225,19897,2298],{"class":2235},[2225,19899,19900],{"class":2239}," opts: ",[2225,19902,2792],{"class":2235},[2225,19904,19905],{"class":2307}," recovery",[2225,19907,2311],{"class":2235},[2225,19909,19754],{"class":2239},[2225,19911,6474],{"class":2235},[2225,19913,19717],{"class":2239},[2225,19915,6480],{"class":2235},[2225,19917,19918],{"class":2239},"; locale",[2225,19920,7011],{"class":2235},[2225,19922,19923],{"class":2239}," string ",[2225,19925,2359],{"class":2235},[2225,19927,11737],{"class":2239},[2225,19929,11740],{"class":6342},[2225,19931,17851],{"class":2235},[2225,19933,6466],{"class":6342},[2225,19935,2311],{"class":2235},[2225,19937,11993],{"class":2239},[2225,19939,3769],{"class":2235},[2225,19941,19942],{"class":6342}," recoveryEnrollments",[2225,19944,2311],{"class":2235},[2225,19946,19754],{"class":2239},[2225,19948,6474],{"class":2235},[2225,19950,19690],{"class":2239},[2225,19952,6480],{"class":2235},[2225,19954,17861],{"class":2235},[937,19956,19957,19958,19960],{},"Atomic create-and-enroll — required for managed-secret vaults, where ",[941,19959,16586],{}," cannot be left unenrolled (a managed-mode vault has no user-known secret to fall back on, so it needs at least one strong Shamir profile). Opens the vault and enrolls the supplied recovery profiles in one step.",[1111,19962,19963,19968,19974],{},[1114,19964,19965,19967],{},[941,19966,2694],{}," — the vault to open and enroll.",[1114,19969,19970,19973],{},[941,19971,19972],{},"opts.recovery"," — the recovery profiles to enroll (managed mode requires at least one strong \u002F Shamir profile).",[1114,19975,19976,19978],{},[941,19977,11777],{}," — optional default locale for the opened vault.",[1591,19980,19982],{"id":19981},"recovermanagedsecret",[941,19983,19984],{},"recoverManagedSecret()",[2217,19986,19988],{"className":2219,"code":19987,"language":2221,"meta":1037,"style":1037},"recoverManagedSecret(vault: string, options: { recoveryProof: RecoveryProof; secretPolicy?: SecretPolicy }): Promise\u003Cvoid>\n",[941,19989,19990],{"__ignoreMap":1037},[2225,19991,19992,19995,19997,19999,20002,20004,20007,20009,20012,20014,20016,20018,20020,20022],{"class":2227,"line":2228},[2225,19993,19994],{"class":2291},"recoverManagedSecret",[2225,19996,17524],{"class":2239},[2225,19998,2298],{"class":2235},[2225,20000,20001],{"class":2239}," options: ",[2225,20003,2792],{"class":2235},[2225,20005,20006],{"class":2307}," recoveryProof",[2225,20008,2311],{"class":2235},[2225,20010,20011],{"class":2239}," RecoveryProof; secretPolicy",[2225,20013,7011],{"class":2235},[2225,20015,16315],{"class":2239},[2225,20017,2359],{"class":2235},[2225,20019,11737],{"class":2239},[2225,20021,11740],{"class":6342},[2225,20023,13016],{"class":2235},[937,20025,20026,20027,20029,20030,20032,20033,1520],{},"Reset a managed-mode secret under a fresh sealing key. In managed mode ",[941,20028,15216],{}," is unavailable — the auto-generated secret can only be replaced by recovering it — so this is the sanctioned managed-mode reset. Requires ",[941,20031,13515],{}," to be supplied at ",[941,20034,2134],{},[1111,20036,20037,20042,20051],{},[1114,20038,20039,20041],{},[941,20040,2694],{}," — the managed-mode vault to recover.",[1114,20043,20044,1169,20047,20050],{},[941,20045,20046],{},"options.recoveryProof",[941,20048,20049],{},"RecoveryProof"," (paper code or Shamir reconstruction) authorizing the reset.",[1114,20052,20053,20056],{},[941,20054,20055],{},"options.secretPolicy"," — optional strength rules for the regenerated secret.",[1591,20058,20060],{"id":20059},"recoveruser",[941,20061,20062],{},"recoverUser()",[2217,20064,20066],{"className":2219,"code":20065,"language":2221,"meta":1037,"style":1037},"recoverUser(vault: string, options: RecoverUserOptions, factors?: FactorProofBundle): Promise\u003Cvoid>\n",[941,20067,20068],{"__ignoreMap":1037},[2225,20069,20070,20073,20075,20077,20080,20082,20084,20086,20088,20090],{"class":2227,"line":2228},[2225,20071,20072],{"class":2291},"recoverUser",[2225,20074,17524],{"class":2239},[2225,20076,2298],{"class":2235},[2225,20078,20079],{"class":2239}," options: RecoverUserOptions",[2225,20081,2298],{"class":2235},[2225,20083,17534],{"class":2239},[2225,20085,7011],{"class":2235},[2225,20087,17539],{"class":2239},[2225,20089,11740],{"class":6342},[2225,20091,13016],{"class":2235},[937,20093,20094],{},"Atomic peer-recovery of an existing user's keyring — re-establish a member's access from a recovery proof without a full re-grant ceremony.",[1111,20096,20097,20102,20110],{},[1114,20098,20099,20101],{},[941,20100,2694],{}," — the vault whose user to recover.",[1114,20103,20104,12536,20106,20109],{},[941,20105,16362],{},[941,20107,20108],{},"RecoverUserOptions"," naming the target user and the recovery proof.",[1114,20111,20112,19870],{},[941,20113,13051],{},[2645,20115,20116],{},"html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}",{"title":1037,"searchDepth":1038,"depth":1038,"links":20118},[20119,20124],{"id":19656,"depth":1041,"text":19657,"children":20120},[20121,20122,20123],{"id":19663,"depth":1038,"text":19666},{"id":19727,"depth":1038,"text":19730},{"id":19799,"depth":1038,"text":19802},{"id":19873,"depth":1041,"text":19874,"children":20125},[20126,20127,20128],{"id":19880,"depth":1038,"text":19883},{"id":19981,"depth":1038,"text":19984},{"id":20059,"depth":1038,"text":20062},"Regaining access when factors are lost: recovery-profile enrollment and management, plus the paper \u002F Shamir recovery flows for managed vaults.",{},{"title":77,"description":20129},"0IZiYUxDxfTv-kqkXmFWxNGnAkF1ZTqKC8iN3aIwNcY",{"id":20134,"title":81,"access":12,"apiToc":929,"body":20135,"category":1042,"description":21089,"extension":1044,"meta":21090,"navigation":1042,"nest":2635,"path":82,"seo":21091,"sinceVersion":1042,"status":1049,"stem":83,"subtitle":1042,"tier":6922,"tocExternal":1042,"__hash__":21092},"docs\u002Fcore\u002F2.noydb\u002F6.configuration.md",{"type":931,"value":20136,"toc":21076},[20137,20141,20160,20164,20180,20184,20363,20366,20456,20460,20498,20502,20645,20649,20713,20717,20861,20865,20984,20988,20998,21060,21062],[934,20138,20140],{"id":20139},"createnoydb-configuration","createNoydb configuration",[937,20142,960,20143,20147,20148,13571,20151,20153,20154,20157,20158,1520],{},[1007,20144,20146],{"href":20145},"#core-options","core options"," below cover every non-strategy key you pass to ",[941,20149,20150],{},"createNoydb(options)",[941,20152,943],{}," slots that unlock capabilities beyond the always-on kernel are the ",[950,20155,20156],{},"strategies"," — a separate directory: see ",[1007,20159,85],{"href":11645},[1022,20161,20163],{"id":20162},"core-options","Core options",[937,20165,20166,20167,20169,20170,968,20173,20176,20177,20179],{},"Every non-strategy key on the options object passed to ",[941,20168,20150],{},". Strategy slots (",[941,20171,20172],{},"historyStrategy",[941,20174,20175],{},"syncStrategy",", and the rest) are covered on the ",[1007,20178,85],{"href":11645}," page instead.",[1591,20181,20183],{"id":20182},"identity-and-keys","Identity and keys",[1338,20185,20186,20201],{},[1341,20187,20188],{},[1344,20189,20190,20192,20195,20198],{},[1347,20191,14769],{},[1347,20193,20194],{},"Type",[1347,20196,20197],{},"Default",[1347,20199,20200],{},"Meaning",[1357,20202,20203,20219,20244,20266,20305,20324,20343],{},[1344,20204,20205,20209,20213,20216],{},[1362,20206,20207],{},[941,20208,16225],{},[1362,20210,20211],{},[941,20212,7259],{},[1362,20214,20215],{},"— (required)",[1362,20217,20218],{},"The user identifier for this session.",[1344,20220,20221,20225,20229,20231],{},[1362,20222,20223],{},[941,20224,10695],{},[1362,20226,20227],{},[941,20228,7259],{},[1362,20230,3610],{},[1362,20232,20233,20234,4688,20236,20238,20239,997,20241,20243],{},"Secret for key derivation. Required unless ",[941,20235,11453],{},[941,20237,9953],{}," is supplied; supplying both ",[941,20240,10695],{},[941,20242,9953],{}," throws.",[1344,20245,20246,20250,20255,20257],{},[1362,20247,20248],{},[941,20249,9953],{},[1362,20251,20252],{},[941,20253,20254],{},"(vault: string) => Promise\u003CUnlockedKeyring>",[1362,20256,3610],{},[1362,20258,20259,20260,20263,20264,1520],{},"Callback that returns an unlocked keyring, for WebAuthn \u002F OIDC \u002F Shamir \u002F any unlock that supplies the keyring directly rather than deriving it from a secret. Called lazily on first use per vault and cached per ",[941,20261,20262],{},"(instance, vault)",". Mutually exclusive with ",[941,20265,10695],{},[1344,20267,20268,20273,20278,20283],{},[1362,20269,20270],{},[941,20271,20272],{},"secretMode",[1362,20274,20275],{},[941,20276,20277],{},"'standard' | 'managed'",[1362,20279,20280],{},[941,20281,20282],{},"'standard'",[1362,20284,20285,20287,20288,20290,20291,20294,20295,20297,20298,20300,20301,997,20303,1520],{},[941,20286,20282],{}," uses ",[941,20289,10695],{}," as the plaintext secret; ",[941,20292,20293],{},"'managed'"," generates a random 256-bit secret sealed under ",[941,20296,13515],{}," (rubber-hose-resistant — the user never sees it). ",[941,20299,20293],{}," is mutually exclusive with ",[941,20302,10695],{},[941,20304,9953],{},[1344,20306,20307,20311,20315,20317],{},[1362,20308,20309],{},[941,20310,13515],{},[1362,20312,20313],{},[941,20314,14350],{},[1362,20316,3610],{},[1362,20318,20319,20320,20323],{},"Seals \u002F unseals the auto-generated managed-mode secret. Required when ",[941,20321,20322],{},"secretMode === 'managed'",", ignored otherwise.",[1344,20325,20326,20331,20335,20337],{},[1362,20327,20328],{},[941,20329,20330],{},"shamirRecovery",[1362,20332,20333],{},[941,20334,19615],{},[1362,20336,3610],{},[1362,20338,20339,20340,20342],{},"Required to use the ",[941,20341,19701],{}," recovery profile.",[1344,20344,20345,20350,20355,20360],{},[1362,20346,20347],{},[941,20348,20349],{},"auth",[1362,20351,20352],{},[941,20353,20354],{},"'secret' | 'biometric'",[1362,20356,20357],{},[941,20358,20359],{},"'secret'",[1362,20361,20362],{},"Auth method.",[1591,20364,3135],{"id":20365},"encryption",[1338,20367,20368,20380],{},[1341,20369,20370],{},[1344,20371,20372,20374,20376,20378],{},[1347,20373,14769],{},[1347,20375,20194],{},[1347,20377,20197],{},[1347,20379,20200],{},[1357,20381,20382,20402,20432],{},[1344,20383,20384,20388,20392,20396],{},[1362,20385,20386],{},[941,20387,1624],{},[1362,20389,20390],{},[941,20391,16861],{},[1362,20393,20394],{},[941,20395,11790],{},[1362,20397,20398,20399,20401],{},"Enable encryption. ",[941,20400,2560],{}," stores raw JSON — testing\u002Fdebugging only.",[1344,20403,20404,20408,20412,20416],{},[1362,20405,20406],{},[941,20407,15076],{},[1362,20409,20410],{},[941,20411,16861],{},[1362,20413,20414],{},[941,20415,2560],{},[1362,20417,20418,20419,20421,20422,20424,20425,20427,20428,1520],{},"Lay plaintext records out as directly-inspectable store objects (fields inlined, ",[941,20420,15695],{},"). Requires ",[941,20423,11453],{}," — combined with encryption it throws ",[941,20426,15849],{}," at construction. Never enable for production or client data. See ",[1007,20429,20431],{"href":20430},".\u002Fenclave#debug-plaintext-layout-debugplaintext","Enclave → Debug-plaintext layout",[1344,20433,20434,20439,20444,20446],{},[1362,20435,20436],{},[941,20437,20438],{},"objectStore",[1362,20440,20441],{},[941,20442,20443],{},"ObjectProjection",[1362,20445,3610],{},[1362,20447,20448,20449,20452,20453,20455],{},"Object projection for ",[941,20450,20451],{},"external"," blob fields — routes raw bytes to a servable native object. Sees plaintext bytes, so it sits ",[950,20454,12229],{}," the zero-knowledge guarantee.",[1591,20457,20459],{"id":20458},"storage","Storage",[1338,20461,20462,20474],{},[1341,20463,20464],{},[1344,20465,20466,20468,20470,20472],{},[1347,20467,14769],{},[1347,20469,20194],{},[1347,20471,20197],{},[1347,20473,20200],{},[1357,20475,20476],{},[1344,20477,20478,20482,20486,20493],{},[1362,20479,20480],{},[941,20481,6951],{},[1362,20483,20484],{},[941,20485,3154],{},[1362,20487,20488,20489,20492],{},"built-in ",[941,20490,20491],{},"toMemory()"," (non-persistent)",[1362,20494,20495,20496,1520],{},"The ciphertext store — where encrypted envelopes are persisted. Without it, nothing survives the session. See ",[1007,20497,6891],{"href":16075},[1591,20499,20501],{"id":20500},"conflict-and-sync-scheduling","Conflict and sync scheduling",[1338,20503,20504,20516],{},[1341,20505,20506],{},[1344,20507,20508,20510,20512,20514],{},[1347,20509,14769],{},[1347,20511,20194],{},[1347,20513,20197],{},[1347,20515,20200],{},[1357,20517,20518,20538,20556,20603,20625],{},[1344,20519,20520,20525,20530,20535],{},[1362,20521,20522],{},[941,20523,20524],{},"conflict",[1362,20526,20527],{},[941,20528,20529],{},"ConflictStrategy",[1362,20531,20532],{},[941,20533,20534],{},"'version'",[1362,20536,20537],{},"Conflict-resolution strategy for concurrent writes.",[1344,20539,20540,20544,20549,20551],{},[1362,20541,20542],{},[941,20543,864],{},[1362,20545,20546],{},[941,20547,20548],{},"NoydbStore | SyncTarget | SyncTarget[]",[1362,20550,3610],{},[1362,20552,20553,20554,957],{},"Remote store target(s) for replication. Configuring this needs the sync service (see ",[1007,20555,85],{"href":11645},[1344,20557,20558,20563,20568,20575],{},[1362,20559,20560],{},[941,20561,20562],{},"syncPolicy",[1362,20564,20565],{},[941,20566,20567],{},"SyncPolicy",[1362,20569,20570,20571,20574],{},"— (a store-category policy is ",[1092,20572,20573],{},"resolved",", but resolving one does not start automation)",[1362,20576,20577,20578,20581,20582,20584,20585,20588,20589,1261,20592,1261,20595,20598,20599,1520],{},"Push\u002Fpull scheduling. ",[950,20579,20580],{},"Automation runs only when you declare this option"," (or a per-target ",[941,20583,796],{},") — passing ",[941,20586,20587],{},"sync:"," alone gives you explicit ",[941,20590,20591],{},"db.push()",[941,20593,20594],{},"db.pull()",[941,20596,20597],{},"db.sync()"," and nothing else. See ",[1007,20600,20602],{"href":20601},"\u002Fservices\u002Fsync#the-automation-ladder","sync → the automation ladder",[1344,20604,20605,20610,20614,20616],{},[1362,20606,20607],{},[941,20608,20609],{},"autoSync",[1362,20611,20612],{},[941,20613,16861],{},[1362,20615,3610],{},[1362,20617,20618,20621,20622,20624],{},[950,20619,20620],{},"Deprecated"," — use ",[941,20623,20562],{}," (which wins when both are set).",[1344,20626,20627,20632,20637,20639],{},[1362,20628,20629],{},[941,20630,20631],{},"syncInterval",[1362,20633,20634],{},[941,20635,20636],{},"number",[1362,20638,3610],{},[1362,20640,20641,20621,20643,1520],{},[950,20642,20620],{},[941,20644,20562],{},[1591,20646,20648],{"id":20647},"session-lifetime","Session lifetime",[1338,20650,20651,20663],{},[1341,20652,20653],{},[1344,20654,20655,20657,20659,20661],{},[1347,20656,14769],{},[1347,20658,20194],{},[1347,20660,20197],{},[1347,20662,20200],{},[1357,20664,20665,20692],{},[1344,20666,20667,20672,20677,20679],{},[1362,20668,20669],{},[941,20670,20671],{},"sessionTimeout",[1362,20673,20674,20676],{},[941,20675,20636],{}," (ms)",[1362,20678,8292],{},[1362,20680,20681,20683,20684,20687,20688,20691],{},[950,20682,20620],{}," — clears keys after inactivity; use ",[941,20685,20686],{},"sessionPolicy.idleTimeoutMs",". Still honored, but ",[941,20689,20690],{},"sessionPolicy"," wins.",[1344,20693,20694,20698,20703,20705],{},[1362,20695,20696],{},[941,20697,20690],{},[1362,20699,20700],{},[941,20701,20702],{},"SessionPolicy",[1362,20704,3610],{},[1362,20706,20707,20708,20710,20711,1520],{},"Session lifetime, re-auth requirements, and background-lock behavior. Enforcement needs the session service (see ",[1007,20709,85],{"href":11645},"); replaces the legacy ",[941,20712,20671],{},[1591,20714,20716],{"id":20715},"validation-policy-and-recovery","Validation, policy, and recovery",[1338,20718,20719,20731],{},[1341,20720,20721],{},[1344,20722,20723,20725,20727,20729],{},[1347,20724,14769],{},[1347,20726,20194],{},[1347,20728,20197],{},[1347,20730,20200],{},[1357,20732,20733,20753,20779,20801,20828],{},[1344,20734,20735,20739,20743,20747],{},[1362,20736,20737],{},[941,20738,17425],{},[1362,20740,20741],{},[941,20742,16861],{},[1362,20744,20745],{},[941,20746,2560],{},[1362,20748,20749,20750,1520],{},"Validate secret strength on first-time keyring creation; weak phrases throw ",[941,20751,20752],{},"WeakSecretError",[1344,20754,20755,20759,20763,20767],{},[1362,20756,20757],{},[941,20758,796],{},[1362,20760,20761],{},[941,20762,12870],{},[1362,20764,20765],{},[941,20766,12830],{},[1362,20768,20769,20770,20772,20773,20775,20776,20778],{},"Vault-level policy-gate document, persisted to ",[941,20771,12784],{}," on first-time creation and honored only at creation thereafter (change it later with ",[941,20774,12905],{},"). Use ",[941,20777,12834],{}," for regulated deployments.",[1344,20780,20781,20785,20790,20792],{},[1362,20782,20783],{},[941,20784,321],{},[1362,20786,20787],{},[941,20788,20789],{},"readonly RecoveryEnrollment[]",[1362,20791,3610],{},[1362,20793,20794,20795,20797,20798,20800],{},"Recovery profiles to enroll. ",[941,20796,16536],{}," is supported end-to-end; other profiles ship the API shape and throw ",[941,20799,16540],{}," in use.",[1344,20802,20803,20808,20812,20816],{},[1362,20804,20805],{},[941,20806,20807],{},"requireRecovery",[1362,20809,20810],{},[941,20811,16861],{},[1362,20813,20814],{},[941,20815,2560],{},[1362,20817,20818,20819,968,20821,20823,20824,20827],{},"When ",[941,20820,11790],{},[941,20822,12780],{}," rejects a vault with no recovery entries persisted (throws ",[941,20825,20826],{},"RecoveryNotEnrolledError","). Turn on for regulated environments.",[1344,20829,20830,20835,20840,20845],{},[1362,20831,20832],{},[941,20833,20834],{},"onInvalidKey",[1362,20836,20837],{},[941,20838,20839],{},"'error' | 'reset'",[1362,20841,20842],{},[941,20843,20844],{},"'error'",[1362,20846,20847,20848,20850,20851,5502,20853,20856,20857,20860],{},"What to do when an existing keyring can't be decrypted with the supplied credentials. ",[941,20849,20844],{}," propagates ",[941,20852,10290],{},[941,20854,20855],{},"'reset'"," deletes the stale keyring and re-initializes from scratch — ",[950,20858,20859],{},"all previously encrypted data becomes unrecoverable."," Secret path only.",[1591,20862,20864],{"id":20863},"public-envelope-history-and-translation","Public envelope, history, and translation",[1338,20866,20867,20879],{},[1341,20868,20869],{},[1344,20870,20871,20873,20875,20877],{},[1347,20872,14769],{},[1347,20874,20194],{},[1347,20876,20197],{},[1347,20878,20200],{},[1357,20880,20881,20899,20925,20962],{},[1344,20882,20883,20887,20892,20894],{},[1362,20884,20885],{},[941,20886,645],{},[1362,20888,20889],{},[941,20890,20891],{},"true | CoverSchema",[1362,20893,12320],{},[1362,20895,20896,20897,1520],{},"Enable the cover service — the owner-curated plaintext vault label. See ",[1007,20898,645],{"href":13068},[1344,20900,20901,20905,20910,20912],{},[1362,20902,20903],{},[941,20904,697],{},[1362,20906,20907],{},[941,20908,20909],{},"HistoryConfig",[1362,20911,3610],{},[1362,20913,20914,20915,968,20917,20920,20921,20924],{},"Audit-history configuration (",[941,20916,16929],{},[941,20918,20919],{},"maxVersions",", per-collection ",[941,20922,20923],{},"ledger"," participation).",[1344,20926,20927,20931,20936,20938],{},[1362,20928,20929],{},[941,20930,13335],{},[1362,20932,20933],{},[941,20934,20935],{},"PlaintextTranslatorFn",[1362,20937,3610],{},[1362,20939,20940,20941,16012,20944,1952,20947,20950,20951,20954,20955,20958,20959,1520],{},"Consumer-supplied translation function for ",[941,20942,20943],{},"i18nText",[941,20945,20946],{},"autoTranslate: true",[950,20948,20949],{},"Receives unencrypted text"," — configuring it lets plaintext leave the zero-knowledge boundary over whatever channel your implementation uses. Calling ",[941,20952,20953],{},"put()"," on an ",[941,20956,20957],{},"autoTranslate"," field without it throws ",[941,20960,20961],{},"TranslatorNotConfiguredError",[1344,20963,20964,20969,20973,20978],{},[1362,20965,20966],{},[941,20967,20968],{},"plaintextTranslatorName",[1362,20970,20971],{},[941,20972,7259],{},[1362,20974,20975],{},[941,20976,20977],{},"'anonymous'",[1362,20979,20980,20981,957],{},"Human-readable translator name recorded in the audit log (e.g. ",[941,20982,20983],{},"'deepl-pro-with-dpa'",[1591,20985,20987],{"id":20986},"internal-advanced","Internal \u002F advanced",[937,20989,20990,20991,20993,20994,20997],{},"A handful of keys exist only so ",[941,20992,2134],{}," can thread pre-resolved values into the constructor; they are ",[941,20995,20996],{},"@internal"," with no supported override, and applications should not set them.",[1338,20999,21000,21008],{},[1341,21001,21002],{},[1344,21003,21004,21006],{},[1347,21005,14769],{},[1347,21007,20200],{},[1357,21009,21010,21027,21037],{},[1344,21011,21012,21017],{},[1362,21013,21014],{},[941,21015,21016],{},"coordinationStrategy",[1362,21018,21019,21020,21026],{},"Drain-barrier coordination transport for the schema fence. ",[950,21021,21022,21023,21025],{},"Provider injection, not a ",[941,21024,943],{}," service"," — note the name doesn't follow the strategy pattern. Defaults to a store-backed provider that reproduces today's store-polling fence; real-time transports inject a push provider here.",[1344,21028,21029,21034],{},[1362,21030,21031],{},[941,21032,21033],{},"sessionId",[1362,21035,21036],{},"Stable id for the session that owns this instance's writers; defaults to a fresh ULID per instance.",[1344,21038,21039,21050],{},[1362,21040,21041,1368,21044,1368,21047],{},[941,21042,21043],{},"userApiFactory",[941,21045,21046],{},"policyFactory",[941,21048,21049],{},"policyCheckGateFn",[1362,21051,21052,21053,21056,21057,21059],{},"Pre-resolved factories\u002Ffunctions for the ",[941,21054,21055],{},"vault.user"," API and the policy engine, always resolved by ",[941,21058,2134],{}," itself. No supported override.",[1022,21061,2621],{"id":2620},[1111,21063,21064],{},[1114,21065,21066,1169,21070,21072,21073,21075],{},[950,21067,21068],{},[1007,21069,85],{"href":11645},[941,21071,943],{}," service-strategy slots you also pass to ",[941,21074,2134],{},": all 32, grouped by where the methods they unlock land (client \u002F vault \u002F collection), each linked to its service page.",{"title":1037,"searchDepth":1038,"depth":1038,"links":21077},[21078,21088],{"id":20162,"depth":1041,"text":20163,"children":21079},[21080,21081,21082,21083,21084,21085,21086,21087],{"id":20182,"depth":1038,"text":20183},{"id":20365,"depth":1038,"text":3135},{"id":20458,"depth":1038,"text":20459},{"id":20500,"depth":1038,"text":20501},{"id":20647,"depth":1038,"text":20648},{"id":20715,"depth":1038,"text":20716},{"id":20863,"depth":1038,"text":20864},{"id":20986,"depth":1038,"text":20987},{"id":2620,"depth":1041,"text":2621},"The full createNoydb reference: every non-strategy option key you pass to createNoydb(), grouped into detailed tables by concern.",{},{"title":81,"description":21089},"2BzrGNsvSK7M2xTMM82MRpGd3M64L-69zGhqp-icBqY",{"id":21094,"title":85,"access":12,"apiToc":929,"body":21095,"category":1042,"description":22190,"extension":1044,"meta":22191,"navigation":1042,"nest":2635,"path":86,"seo":22192,"sinceVersion":1042,"status":1049,"stem":87,"subtitle":1042,"tier":6922,"tocExternal":1042,"__hash__":22193},"docs\u002Fcore\u002F2.noydb\u002F7.strategies.md",{"type":931,"value":21096,"toc":22184},[21097,21101,21125,21152,21156,21162,21333,21359,21363,21369,21766,21800,21804,21809,22144,22164,22166],[934,21098,21100],{"id":21099},"service-strategies","Service strategies",[937,21102,21103,21104,21108,21109,21112,21113,21115,21116,21118,21119,21122,21123,1520],{},"The kernel session documented on the ",[1007,21105,21107],{"href":21106},".\u002Fmethods","Noydb reference"," works with zero add-ons. Every capability beyond it — history, sync, transactions, multi-user access, aggregation, and the rest — is an opt-in ",[950,21110,21111],{},"strategy"," you pass to ",[941,21114,2134],{}," through a ",[941,21117,943],{}," factory, and that tree-shakes out of your bundle when unused. Each strategy unlocks a set of methods; a method that needs a strategy throws a ",[941,21120,21121],{},"*NotEnabledError"," (or a documented equivalent) until you configure it. The plain-option keys you pass alongside these are on ",[1007,21124,81],{"href":13339},[937,21126,21127,21128,21131,21132,21134,21135,21137,21138,21140,21141,21144,21145,21148,21149,10077],{},"This directory is the full map, grouped by ",[950,21129,21130],{},"where the methods each slot unlocks appear"," — on the ",[941,21133,6590],{}," session (client-level), on a ",[941,21136,2694],{},", or on a ",[941,21139,6471],{},". The full set is thirty-two slots: ",[950,21142,21143],{},"twenty-seven single-value"," strategies and ",[950,21146,21147],{},"five array-valued"," ones (guards, deferred numbering, derivations, materialized views, overlays — each takes a list of handles, marked ",[1092,21150,21151],{},"(array)",[1022,21153,21155],{"id":21154},"client-level","Client-level",[937,21157,21158,21159,21161],{},"Strategies whose methods land on the ",[941,21160,6590],{}," session itself.",[1338,21163,21164,21182],{},[1341,21165,21166],{},[1344,21167,21168,21171,21176,21179],{},[1347,21169,21170],{},"Strategy slot",[1347,21172,21173,21175],{},[941,21174,943],{}," factory",[1347,21177,21178],{},"What it unlocks",[1347,21180,21181],{},"Service",[1357,21183,21184,21208,21237,21257,21284,21312],{},[1344,21185,21186,21189,21193,21204],{},[1362,21187,21188],{},"Multi-user access",[1362,21190,21191],{},[941,21192,3197],{},[1362,21194,21195,1261,21198,1261,21201,21203],{},[941,21196,21197],{},"db.grant()",[941,21199,21200],{},"db.revoke()",[941,21202,10734],{}," — grant or revoke a keyring, rotate a vault's collection DEKs in place",[1362,21205,21206],{},[1007,21207,868],{"href":14283},[1344,21209,21210,21213,21217,21232],{},[1362,21211,21212],{},"Sovereign custody",[1362,21214,21215],{},[941,21216,11541],{},[1362,21218,21219,21220,1261,21222,21224,21225,21228,21229,1381],{},"owner-only ",[941,21221,10087],{},[941,21223,10090],{}," plus the ",[941,21226,21227],{},"vault.custody.*"," facade (incl. ",[941,21230,21231],{},"liberate()",[1362,21233,21234],{},[1007,21235,661],{"href":21236},"..\u002F..\u002Fservices\u002Fcustody",[1344,21238,21239,21242,21247,21252],{},[1362,21240,21241],{},"Multi-record atomic writes",[1362,21243,21244],{},[941,21245,21246],{},"withTransactions()",[1362,21248,21249,21251],{},[941,21250,7989],{}," — all-or-nothing writes across vaults and collections (dry-run + amendment overloads)",[1362,21253,21254],{},[1007,21255,876],{"href":21256},"..\u002F..\u002Fservices\u002Ftransactions",[1344,21258,21259,21262,21267,21279],{},[1362,21260,21261],{},"Snapshot lifecycle",[1362,21263,21264],{},[941,21265,21266],{},"withSnapshots({ store })",[1362,21268,21269,1261,21272,1261,21275,21278],{},[941,21270,21271],{},"db.snapshot()",[941,21273,21274],{},"db.listSnapshots()",[941,21276,21277],{},"db.restoreSnapshot()"," into a snapshot store (integrity-verified on restore)",[1362,21280,21281],{},[1007,21282,856],{"href":21283},"..\u002F..\u002Fservices\u002Fsnapshots",[1344,21285,21286,21289,21293,21308],{},[1362,21287,21288],{},"Replication + presence",[1362,21290,21291],{},[941,21292,4676],{},[1362,21294,21295,1261,21297,1261,21299,1261,21301,21304,21305],{},[941,21296,20591],{},[941,21298,20594],{},[941,21300,20597],{},[941,21302,21303],{},"db.syncStatus()",", the sync-transaction overload, and ",[941,21306,21307],{},"collection.presence()",[1362,21309,21310],{},[1007,21311,864],{"href":15100},[1344,21313,21314,21317,21322,21328],{},[1362,21315,21316],{},"Session policy",[1362,21318,21319],{},[941,21320,21321],{},"withSession()",[1362,21323,21324,21325,21327],{},"enforces the configured ",[941,21326,20690],{}," — idle \u002F absolute timeouts, lock-on-background, global token revocation",[1362,21329,21330],{},[1007,21331,840],{"href":21332},"..\u002F..\u002Fservices\u002Fsession",[937,21334,21335,21336,21338,21339,21341,21342,21345,21346,21348,21349,21351,21352,21354,21355,997,21357,1520],{},"Notes. Without ",[941,21337,3197],{}," the floor is single-user and all three methods throw ",[941,21340,10329],{},"; custody methods throw ",[941,21343,21344],{},"CustodyNotEnabledError","; setting ",[941,21347,20690],{}," without ",[941,21350,21321],{}," throws at ",[941,21353,2134],{},". See also ",[1007,21356,844],{"href":12844},[1007,21358,14279],{"href":14278},[1022,21360,21362],{"id":21361},"vault-level","Vault-level",[937,21364,21365,21366,21368],{},"Strategies whose methods land on a ",[941,21367,2694],{}," handle.",[1338,21370,21371,21385],{},[1341,21372,21373],{},[1344,21374,21375,21377,21381,21383],{},[1347,21376,21170],{},[1347,21378,21379,21175],{},[941,21380,943],{},[1347,21382,21178],{},[1347,21384,21181],{},[1357,21386,21387,21414,21441,21466,21491,21511,21534,21568,21595,21634,21662,21692,21713,21738],{},[1344,21388,21389,21392,21397,21409],{},[1362,21390,21391],{},"Cold storage",[1362,21393,21394],{},[941,21395,21396],{},"withArchive({ store })",[1362,21398,21399,1261,21402,1261,21405,21408],{},[941,21400,21401],{},"vault.archive()",[941,21403,21404],{},"vault.restore()",[941,21406,21407],{},"vault.listArchived()"," against a second store",[1362,21410,21411],{},[1007,21412,605],{"href":21413},"..\u002F..\u002Fservices\u002Farchive",[1344,21415,21416,21419,21424,21437],{},[1362,21417,21418],{},"GDPR erasure",[1362,21420,21421],{},[941,21422,21423],{},"withForget({ subjects })",[1362,21425,21426,21429,21430,21433,21434,21436],{},[941,21427,21428],{},"vault.forget(subjectId)"," crypto-shred + ",[941,21431,21432],{},"vault.rebuildSubjectIndex()"," (forces ",[941,21435,14552],{},", needs the history ledger)",[1362,21438,21439],{},[1007,21440,685],{"href":14655},[1344,21442,21443,21446,21450,21461],{},[1362,21444,21445],{},"Localization",[1362,21447,21448],{},[941,21449,6739],{},[1362,21451,21452,1261,21454,21457,21458],{},[941,21453,20943],{},[941,21455,21456],{},"dictKey"," field resolution and validation, plus ",[941,21459,21460],{},"vault.dictionary(name)",[1362,21462,21463],{},[1007,21464,701],{"href":21465},"..\u002F..\u002Fservices\u002Fi18n",[1344,21467,21468,21471,21475,21487],{},[1362,21469,21470],{},"Accounting periods",[1362,21472,21473],{},[941,21474,6757],{},[1362,21476,21477,1261,21480,1261,21483,21486],{},[941,21478,21479],{},"vault.openPeriod()",[941,21481,21482],{},"vault.closePeriod()",[941,21484,21485],{},"vault.listPeriods()"," and the closed-period write guard",[1362,21488,21489],{},[1007,21490,776],{"href":16031},[1344,21492,21493,21496,21501,21506],{},[1362,21494,21495],{},"Vault frames",[1362,21497,21498],{},[941,21499,21500],{},"withShadow()",[1362,21502,21503],{},[941,21504,21505],{},"vault.frame()",[1362,21507,21508],{},[1007,21509,848],{"href":21510},"..\u002F..\u002Fservices\u002Fshadow",[1344,21512,21513,21516,21520,21529],{},[1362,21514,21515],{},"Consent audit",[1362,21517,21518],{},[941,21519,6748],{},[1362,21521,21522,21523,21525,21526],{},"a per-operation audit trail into ",[941,21524,6744],{},", read via ",[941,21527,21528],{},"vault.consentAudit()",[1362,21530,21531],{},[1007,21532,641],{"href":21533},"..\u002F..\u002Fservices\u002Fconsent",[1344,21535,21536,21539,21544,21564],{},[1362,21537,21538],{},"Document attestation",[1362,21540,21541],{},[941,21542,21543],{},"withAttestation()",[1362,21545,21546,1261,21549,1261,21552,1261,21555,1261,21558,1261,21561],{},[941,21547,21548],{},"vault.issueAttestation()",[941,21550,21551],{},"getDocumentSigningPublicKey()",[941,21553,21554],{},"revokeAttestation()",[941,21556,21557],{},"unrevokeAttestation()",[941,21559,21560],{},"getRevokedDocIds()",[941,21562,21563],{},"publishRevocationList()",[1362,21565,21566],{},[1007,21567,609],{"href":16003},[1344,21569,21570,21573,21578,21591],{},[1362,21571,21572],{},"Record-scoped sealing",[1362,21574,21575],{},[941,21576,21577],{},"withSealedRecord()",[1362,21579,21580,21581,1261,21584,1261,21587,21590],{},"grantor-side ",[941,21582,21583],{},"vault.sealRecordToHost()",[941,21585,21586],{},"revokeSealedRecord()",[941,21588,21589],{},"rotateRecordCek()"," (host-side opener stays ungated)",[1362,21592,21593],{},[1007,21594,824],{"href":13522},[1344,21596,21597,21600,21605,21629],{},[1362,21598,21599],{},"Data sovereignty",[1362,21601,21602],{},[941,21603,21604],{},"withPortability()",[1362,21606,3742,21607,21610,21611,968,21614,968,21617,968,21620,968,21623,968,21626],{},[941,21608,21609],{},"vault.user.*"," export \u002F withdrawal surface: ",[941,21612,21613],{},"exportMyAccessibleData()",[941,21615,21616],{},"unilateralWithdrawal()",[941,21618,21619],{},"requestWithdrawal()",[941,21621,21622],{},"listWithdrawalRequests()",[941,21624,21625],{},"approveWithdrawal()",[941,21627,21628],{},"rejectWithdrawal()",[1362,21630,21631],{},[1007,21632,800],{"href":21633},"..\u002F..\u002Fservices\u002Fportability",[1344,21635,21636,21639,21644,21658],{},[1362,21637,21638],{},"Credential broker",[1362,21640,21641],{},[941,21642,21643],{},"withBroker(config)",[1362,21645,21646,964,21649,1261,21652,1261,21655],{},[941,21647,21648],{},"vault.broker()",[941,21650,21651],{},".enroll()",[941,21653,21654],{},".rotate()",[941,21656,21657],{},".credentialSource()",[1362,21659,21660],{},[1007,21661,625],{"href":15340},[1344,21663,21664,21667,21672,21687],{},[1362,21665,21666],{},"Atomic sequences",[1362,21668,21669],{},[941,21670,21671],{},"withSequence()",[1362,21673,21674,964,21677,1261,21680,1261,21683,21686],{},[941,21675,21676],{},"vault.sequence(name)",[941,21678,21679],{},".next()",[941,21681,21682],{},".peek()",[941,21684,21685],{},".seedTo()",", a CAS-backed monotonic counter",[1362,21688,21689],{},[1007,21690,836],{"href":21691},"..\u002F..\u002Fservices\u002Fsequence",[1344,21693,21694,21697,21702,21709],{},[1362,21695,21696],{},"Partition extraction",[1362,21698,21699],{},[941,21700,21701],{},"withCargo()",[1362,21703,21704,21705,21708],{},"source-side ",[941,21706,21707],{},"extractPartition(vault, …)"," that lifts a re-keyed slice out as a portable partition",[1362,21710,21711],{},[1007,21712,14626],{"href":14625},[1344,21714,21715,21720,21725,21733],{},[1362,21716,21717,21718],{},"Materialized views ",[1092,21719,21151],{},[1362,21721,21722],{},[941,21723,21724],{},"withMaterializedView(...)",[1362,21726,21727,21728,21525,21731],{},"each handle registers a persisted, incrementally-maintained view; refresh with ",[941,21729,21730],{},"vault.refreshView()",[941,21732,2138],{},[1362,21734,21735],{},[1007,21736,739],{"href":21737},"..\u002F..\u002Fservices\u002Fmaterialized-views",[1344,21739,21740,21745,21750,21761],{},[1362,21741,21742,21743],{},"Gap-free serials ",[1092,21744,21151],{},[1362,21746,21747],{},[941,21748,21749],{},"withDeferredNumbering(...)",[1362,21751,21752,21753,21756,21757,21760],{},"each declares a numbering series; ",[941,21754,21755],{},"vault.sequence(series).next({ for })"," reserves a slot, ",[941,21758,21759],{},"vault.runNumberingPass(series)"," assigns gap-free serials",[1362,21762,21763],{},[1007,21764,764],{"href":21765},"..\u002F..\u002Fservices\u002Fnumbering",[937,21767,21768,21769,1746,21771,5502,21774,1746,21776,21779,21780,21783,21784,21787,21788,21791,21792,21795,21796,21799],{},"Notes. Missing-strategy throws: ",[941,21770,2546],{},[941,21772,21773],{},"ForgetStrategyNotConfiguredError",[941,21775,21648],{},[941,21777,21778],{},"BrokerNotEnabledError","; sealed-record → ",[941,21781,21782],{},"SealedRecordNotEnabledError","; portability → ",[941,21785,21786],{},"PortabilityNotEnabledError","; sequence → ",[941,21789,21790],{},"SequenceNotEnabledError","; cargo → ",[941,21793,21794],{},"CargoNotEnabledError","; attestation → ",[941,21797,21798],{},"AttestationNotEnabledError",". The recipient-side partition adopt \u002F decrypt functions stay ungated.",[1022,21801,21803],{"id":21802},"collection-level","Collection-level",[937,21805,21365,21806,21808],{},[941,21807,6471],{}," (or its query builder).",[1338,21810,21811,21825],{},[1341,21812,21813],{},[1344,21814,21815,21817,21821,21823],{},[1347,21816,21170],{},[1347,21818,21819,21175],{},[941,21820,943],{},[1347,21822,21178],{},[1347,21824,21181],{},[1357,21826,21827,21868,21887,21917,21941,21967,21998,22018,22051,22072,22096,22122],{},[1344,21828,21829,21832,21836,21863],{},[1362,21830,21831],{},"Versioning + audit ledger",[1362,21833,21834],{},[941,21835,6719],{},[1362,21837,21838,1261,21841,1261,21844,1261,21847,1261,21850,1261,21853,21856,21857,968,21860,1381],{},[941,21839,21840],{},"collection.history()",[941,21842,21843],{},"getVersion()",[941,21845,21846],{},"revert()",[941,21848,21849],{},"diff()",[941,21851,21852],{},"clearHistory()",[941,21854,21855],{},"pruneRecordHistory()",", per-record snapshots, and the hash-chained ledger (",[941,21858,21859],{},"vault.ledger()",[941,21861,21862],{},"vault.at()",[1362,21864,21865],{},[1007,21866,697],{"href":21867},"..\u002F..\u002Fservices\u002Fhistory",[1344,21869,21870,21873,21877,21883],{},[1362,21871,21872],{},"Binary attachments",[1362,21874,21875],{},[941,21876,6775],{},[1362,21878,21879,21882],{},[941,21880,21881],{},"collection.blob(id)"," storage for large binary fields",[1362,21884,21885],{},[1007,21886,621],{"href":15126},[1344,21888,21889,21892,21896,21912],{},[1362,21890,21891],{},"Secondary indexes",[1362,21893,21894],{},[941,21895,6766],{},[1362,21897,21898,21899,1261,21902,21904,21905,968,21908,21911],{},"eager ",[941,21900,21901],{},"==",[941,21903,4891],{}," fast-paths on declared ",[941,21906,21907],{},"indexes",[941,21909,21910],{},".lazyQuery()",", and index rebuild \u002F reconcile",[1362,21913,21914],{},[1007,21915,705],{"href":21916},"..\u002F..\u002Fservices\u002Findexing",[1344,21918,21919,21922,21927,21936],{},[1362,21920,21921],{},"Grouping + rollups",[1362,21923,21924],{},[941,21925,21926],{},"withReduce()",[1362,21928,21929,997,21932,21935],{},[941,21930,21931],{},".aggregate()",[941,21933,21934],{},".groupBy()"," on a query",[1362,21937,21938],{},[1007,21939,4449],{"href":21940},"..\u002F..\u002Fservices\u002Freduce",[1344,21942,21943,21946,21951,21962],{},[1362,21944,21945],{},"Conflict-free merges",[1362,21947,21948],{},[941,21949,21950],{},"withCrdt()",[1362,21952,21953,21954,1261,21957,1261,21960,1381],{},"the merge engine for CRDT collections (",[941,21955,21956],{},"lww-map",[941,21958,21959],{},"rga",[941,21961,287],{},[1362,21963,21964],{},[1007,21965,653],{"href":21966},"..\u002F..\u002Fservices\u002Fcrdt",[1344,21968,21969,21972,21977,21994],{},[1362,21970,21971],{},"Hierarchical access tiers",[1362,21973,21974],{},[941,21975,21976],{},"withTiers()",[1362,21978,21979,1261,21982,1261,21985,1261,21988,1261,21991],{},[941,21980,21981],{},"collection.putAtTier()",[941,21983,21984],{},"getAtTier()",[941,21986,21987],{},"listAtTier()",[941,21989,21990],{},"elevate()",[941,21992,21993],{},"demote()",[1362,21995,21996],{},[1007,21997,872],{"href":14616},[1344,21999,22000,22003,22008,22014],{},[1362,22001,22002],{},"Classified fields",[1362,22004,22005],{},[941,22006,22007],{},"withClassified()",[1362,22009,22010,22013],{},[941,22011,22012],{},"collection.reveal()"," for digest-only classified fields",[1362,22015,22016],{},[1007,22017,633],{"href":14740},[1344,22019,22020,22023,22028,22046],{},[1362,22021,22022],{},"Search + retrieval",[1362,22024,22025],{},[941,22026,22027],{},"withSearch()",[1362,22029,22030,1261,22033,1261,22036,1261,22039,1261,22042,22045],{},[941,22031,22032],{},"collection.search()",[941,22034,22035],{},"retrieve()",[941,22037,22038],{},"similarTo()",[941,22040,22041],{},"warmIndex()",[941,22043,22044],{},"flushIndex()"," and put-time embedding-vector compute",[1362,22047,22048],{},[1007,22049,4457],{"href":22050},"..\u002F..\u002Fservices\u002Fsearch",[1344,22052,22053,22056,22061,22067],{},[1362,22054,22055],{},"Bounded working set",[1362,22057,22058],{},[941,22059,22060],{},"withLazy()",[1362,22062,22063,22064,22066],{},"lazy mode's bounded-LRU working set for ",[941,22065,6836],{}," collections",[1362,22068,22069],{},[1007,22070,719],{"href":22071},"..\u002F..\u002Fservices\u002Flazy",[1344,22073,22074,22079,22084,22091],{},[1362,22075,22076,22077],{},"Write guards ",[1092,22078,21151],{},[1362,22080,22081],{},[941,22082,22083],{},"withGuard(...)",[1362,22085,22086,22087,22090],{},"each handle registers a ",[941,22088,22089],{},"collection.put()"," invariant, dispatched in registration order",[1362,22092,22093],{},[1007,22094,693],{"href":22095},"..\u002F..\u002Fservices\u002Fguards",[1344,22097,22098,22103,22108,22117],{},[1362,22099,22100,22101],{},"Projections ",[1092,22102,21151],{},[1362,22104,22105],{},[941,22106,22107],{},"withDerivation(...)",[1362,22109,22110,22111,22113,22114],{},"each projects a source collection's writes into an output on ",[941,22112,20953],{},"; backfill with ",[941,22115,22116],{},"vault.deriveAll()",[1362,22118,22119],{},[1007,22120,665],{"href":22121},"..\u002F..\u002Fservices\u002Fderivations",[1344,22123,22124,22129,22134,22139],{},[1362,22125,22126,22127],{},"Overlays ",[1092,22128,21151],{},[1362,22130,22131],{},[941,22132,22133],{},"withOverlayedView(...)",[1362,22135,22136,22137,1381],{},"each layers a computed overlay onto a base collection's reads (read through ",[941,22138,2138],{},[1362,22140,22141],{},[1007,22142,768],{"href":22143},"..\u002F..\u002Fservices\u002Foverlay-views",[937,22145,22146,22147,1746,22150,22153,22154,22157,22158,22161,22162,20243],{},"Notes. Without their strategy: CRDT collections throw on the first put \u002F sync-merge; ",[941,22148,22149],{},"reveal()",[941,22151,22152],{},"ClassifiedNotEnabledError","; tier methods → ",[941,22155,22156],{},"TiersNotEnabledError","; search methods → ",[941,22159,22160],{},"SearchNotEnabledError","; history snapshots and prune \u002F clear become silent no-ops while the read APIs throw; queries fall back to linear scans and ",[941,22163,21910],{},[1022,22165,2621],{"id":2620},[1111,22167,22168,22177],{},[1114,22169,22170,22174,22175,1520],{},[950,22171,22172],{},[1007,22173,81],{"href":13339}," — every non-strategy option key you pass to ",[941,22176,2134],{},[1114,22178,22179,22183],{},[950,22180,22181],{},[1007,22182,62],{"href":21106}," — the strategy-free kernel surface these slots build on.",{"title":1037,"searchDepth":1038,"depth":1038,"links":22185},[22186,22187,22188,22189],{"id":21154,"depth":1041,"text":21155},{"id":21361,"depth":1041,"text":21362},{"id":21802,"depth":1041,"text":21803},{"id":2620,"depth":1041,"text":2621},"The with*() service-strategy directory: all 32 opt-in slots that unlock capabilities beyond the kernel, grouped by client \u002F vault \u002F collection level.",{},{"title":85,"description":22190},"uf-mbVqfMdEaRZwBjwsmnvYxYtMX-hFTgRico_-wM6A",{"id":22195,"title":62,"access":12,"apiToc":929,"body":22196,"category":1042,"description":22301,"extension":1044,"meta":22302,"navigation":22303,"nest":2635,"path":57,"seo":22304,"sinceVersion":1042,"status":1049,"stem":58,"subtitle":1042,"tier":6922,"tocExternal":1042,"__hash__":22305},"docs\u002Fcore\u002F2.noydb\u002Findex.md",{"type":931,"value":22197,"toc":22298},[22198,22202,22211,22220,22224],[934,22199,22201],{"id":22200},"noydb-the-db-handle","Noydb — the db handle",[937,22203,22204,22207,22208,22210],{},[941,22205,22206],{},"const db = await createNoydb(…)"," returns a ",[941,22209,62],{}," — one unlocked session. It holds the encryption keys in memory while you work and hands them back to nobody.",[937,22212,22213,22214,1746,22216,1746,22218,11622],{},"A session is the top of the ",[941,22215,62],{},[941,22217,94],{},[941,22219,3121],{},[1022,22221,22223],{"id":22222},"in-this-section","In this section",[1111,22225,22226,22236,22247,22254,22261,22271,22278,22288],{},[1114,22227,22228,22232,22233,22235],{},[950,22229,22230],{},[1007,22231,62],{"href":21106}," — the object reference for the ",[941,22234,62],{}," instance: opening and reaching vaults, events and the write queue, policy and gates, and the session lifecycle.",[1114,22237,22238,22242,22243,1261,22245,957],{},[950,22239,22240],{},[1007,22241,645],{"href":13068}," — the owner-curated plaintext vault descriptor (",[941,22244,13061],{},[941,22246,13064],{},[1114,22248,22249,22253],{},[950,22250,22251],{},[1007,22252,21],{"href":2628}," — the crypto the session owns: the KEK\u002FDEK\u002FCEK hierarchy, the envelope format, and the in-memory enclave boundary.",[1114,22255,22256,22260],{},[950,22257,22258],{},[1007,22259,69],{"href":11634}," — the vault secret and single-user keyring primitives, plus the users home: change \u002F rotate \u002F recover the secret, update and list users, read the keyring, and the user-directory toggle.",[1114,22262,22263,22267,22268,22270],{},[950,22264,22265],{},[1007,22266,73],{"href":11638}," — the strategy-free session-auth ladder: tier 1 (secret \u002F getKeyring), tier-2 authenticators and their ",[941,22269,1787],{}," families, tier-3 quick-unlock, and auth introspection.",[1114,22272,22273,22277],{},[950,22274,22275],{},[1007,22276,77],{"href":11641}," — regaining access when factors are lost: recovery-profile enrollment and the paper \u002F Shamir recovery flows.",[1114,22279,22280,22284,22285,22287],{},[950,22281,22282],{},[1007,22283,81],{"href":13339}," — the full ",[941,22286,12780],{}," reference: every core option key in detailed tables.",[1114,22289,22290,22294,22295,22297],{},[950,22291,22292],{},[1007,22293,85],{"href":11645}," — the complete ",[941,22296,943],{}," service-strategy directory: all 32 slots and what each unlocks.",{"title":1037,"searchDepth":1038,"depth":1038,"links":22299},[22300],{"id":22222,"depth":1041,"text":22223},"The db handle from createNoydb(): one unlocked Noydb session that opens vaults, owns the keyring, and runs the client-side crypto. Section map inside.",{},{"title":10},{"title":62,"description":22301},"wRi-8FtIn21i_B7goV-bgGhS9Kt2fsW2kXf2PsboMF0",{"id":22307,"title":94,"access":12,"apiToc":2284,"body":22308,"category":1042,"description":23837,"extension":1044,"meta":23838,"navigation":1042,"nest":2635,"path":95,"seo":23839,"sinceVersion":1042,"status":1049,"stem":96,"subtitle":1042,"tier":6922,"tocExternal":1042,"__hash__":23840},"docs\u002Fcore\u002F3.vault\u002F1.methods.md",{"type":931,"value":22309,"toc":23809},[22310,22314,22337,22352,22357,22361,22371,22375,22562,22595,22807,22812,22832,22835,22839,22842,22848,22867,22881,22903,22908,22924,22941,22951,22956,22977,22994,22998,23004,23010,23028,23038,23044,23065,23068,23079,23085,23102,23108,23112,23115,23120,23134,23137,23141,23155,23162,23166,23191,23197,23239,23249,23282,23288,23326,23331,23350,23356,23396,23412,23434,23440,23476,23482,23498,23500,23509,23512,23533,23806],[934,22311,22313],{"id":22312},"vault-the-object-reference","Vault — the object reference",[937,22315,3289,22316,22318,22319,22322,22323,22325,22326,22329,22330,1746,22332,1746,22334,22336],{},[941,22317,94],{}," is the handle ",[941,22320,22321],{},"db.openVault(name)"," returns — one isolated, encrypted namespace, conventionally bound to ",[941,22324,2694],{},". It has its own keyring, so the DEKs that decrypt its collections are unwrapped from ",[1092,22327,22328],{},"this"," vault's key material and say nothing about any other vault. It sits in the middle of the ",[941,22331,62],{},[941,22333,94],{},[941,22335,3121],{}," hierarchy: the session opens it and owns the crypto, the vault opens collections and holds vault-scoped metadata, and the collections hold the records.",[937,22338,11625,22339,10313,22341,22343,22344,22347,22348,1520],{},[950,22340,11628],{},[941,22342,2134],{}," — the strategy-free kernel surface. The session-level primitives that produced this handle (opening, locking, policy) live on ",[1007,22345,62],{"href":22346},"..\u002Fnoydb\u002Fmethods","; anything a vault gains from an opt-in strategy is gathered at the end under ",[1007,22349,22351],{"href":22350},"#unlocked-by-services","Unlocked by services",[937,22353,11649,22354,22356],{},[950,22355,11652],{}," — opening collections, encrypted backup and restore, metadata and locale, session identity, and export authorization — each opening with a short note on what unites its methods, then the individual methods. The owner-curated public envelope is a passive read documented on its own service page, linked below.",[1022,22358,22360],{"id":22359},"opening-collections","Opening collections",[937,22362,22363,22364,22366,22367,22370],{},"These methods turn a collection name into a live, typed ",[941,22365,3121],{}," handle and let you discover the collections a vault holds. Reach for ",[941,22368,22369],{},"collection()"," first — it is the doorway to every read and write.",[1591,22372,22373],{"id":6471},[941,22374,22369],{},[2217,22376,22378],{"className":2219,"code":22377,"language":2221,"meta":1037,"style":1037},"collection\u003CT>(\n  collectionName: string,\n  options?: {\n    schema?: StandardSchemaV1\u003Cunknown, T>\n    indexes?: readonly IndexDef[]\n    prefetch?: boolean\n    cache?: CacheOptions\n    sensitive?: SensitiveOpt\u003CT>\n    perRecordKeys?: boolean\n    deterministicFields?: readonly string[]\n    acknowledgeDeterministicRisk?: boolean\n    provenance?: boolean\n    meta?: CollectionMeta\n    fieldMeta?: Record\u003Cstring, FieldMeta>\n    \u002F\u002F service options (refs, i18nFields, computed, classifiedFields, tiers,\n    \u002F\u002F blobFields, …) documented on their service pages\n  }\n): Collection\u003CT>\n",[941,22379,22380,22392,22399,22407,22427,22437,22446,22456,22472,22481,22491,22500,22509,22518,22537,22542,22547,22551],{"__ignoreMap":1037},[2225,22381,22382,22384,22386,22388,22390],{"class":2227,"line":2228},[2225,22383,6471],{"class":2291},[2225,22385,6474],{"class":2235},[2225,22387,11968],{"class":6342},[2225,22389,6480],{"class":2235},[2225,22391,11683],{"class":2239},[2225,22393,22394,22397],{"class":2227,"line":1041},[2225,22395,22396],{"class":2239},"  collectionName: string",[2225,22398,1707],{"class":2235},[2225,22400,22401,22403,22405],{"class":2227,"line":1038},[2225,22402,11872],{"class":2239},[2225,22404,7011],{"class":2235},[2225,22406,2301],{"class":2235},[2225,22408,22409,22412,22414,22417,22419,22422,22424],{"class":2227,"line":2288},[2225,22410,22411],{"class":2239},"    schema?",[2225,22413,2311],{"class":2235},[2225,22415,22416],{"class":2239}," StandardSchemaV1",[2225,22418,6474],{"class":2235},[2225,22420,22421],{"class":2239},"unknown",[2225,22423,2298],{"class":2235},[2225,22425,22426],{"class":2239}," T>\n",[2225,22428,22429,22432,22434],{"class":2227,"line":2304},[2225,22430,22431],{"class":2239},"    indexes?",[2225,22433,2311],{"class":2235},[2225,22435,22436],{"class":2239}," readonly IndexDef[]\n",[2225,22438,22439,22442,22444],{"class":2227,"line":2356},[2225,22440,22441],{"class":2239},"    prefetch",[2225,22443,7011],{"class":2235},[2225,22445,11718],{"class":2239},[2225,22447,22448,22451,22453],{"class":2227,"line":2830},[2225,22449,22450],{"class":2239},"    cache",[2225,22452,7011],{"class":2235},[2225,22454,22455],{"class":2239}," CacheOptions\n",[2225,22457,22458,22461,22463,22466,22468,22470],{"class":2227,"line":2861},[2225,22459,22460],{"class":2239},"    sensitive",[2225,22462,7011],{"class":2235},[2225,22464,22465],{"class":2239}," SensitiveOpt",[2225,22467,6474],{"class":2235},[2225,22469,11968],{"class":2239},[2225,22471,7093],{"class":2235},[2225,22473,22474,22477,22479],{"class":2227,"line":4246},[2225,22475,22476],{"class":2239},"    perRecordKeys",[2225,22478,7011],{"class":2235},[2225,22480,11718],{"class":2239},[2225,22482,22483,22486,22488],{"class":2227,"line":4253},[2225,22484,22485],{"class":2239},"    deterministicFields",[2225,22487,7011],{"class":2235},[2225,22489,22490],{"class":2239}," readonly string[]\n",[2225,22492,22493,22496,22498],{"class":2227,"line":4259},[2225,22494,22495],{"class":2239},"    acknowledgeDeterministicRisk",[2225,22497,7011],{"class":2235},[2225,22499,11718],{"class":2239},[2225,22501,22502,22505,22507],{"class":2227,"line":6548},[2225,22503,22504],{"class":2239},"    provenance",[2225,22506,7011],{"class":2235},[2225,22508,11718],{"class":2239},[2225,22510,22511,22513,22515],{"class":2227,"line":6576},[2225,22512,11723],{"class":2239},[2225,22514,7011],{"class":2235},[2225,22516,22517],{"class":2239}," CollectionMeta\n",[2225,22519,22520,22523,22525,22528,22530,22532,22534],{"class":2227,"line":6582},[2225,22521,22522],{"class":2239},"    fieldMeta",[2225,22524,7011],{"class":2235},[2225,22526,22527],{"class":2239}," Record",[2225,22529,6474],{"class":2235},[2225,22531,7259],{"class":2239},[2225,22533,2298],{"class":2235},[2225,22535,22536],{"class":2239}," FieldMeta>\n",[2225,22538,22539],{"class":2227,"line":6587},[2225,22540,22541],{"class":2763},"    \u002F\u002F service options (refs, i18nFields, computed, classifiedFields, tiers,\n",[2225,22543,22544],{"class":2227,"line":7174},[2225,22545,22546],{"class":2763},"    \u002F\u002F blobFields, …) documented on their service pages\n",[2225,22548,22549],{"class":2227,"line":7180},[2225,22550,7632],{"class":2235},[2225,22552,22553,22556,22558,22560],{"class":2227,"line":7220},[2225,22554,22555],{"class":2239},"): Collection",[2225,22557,6474],{"class":2235},[2225,22559,11968],{"class":2239},[2225,22561,7093],{"class":2235},[937,22563,22564,22565,22567,22568,1261,22570,1261,22572,1261,22574,22576,22577,22579,22580,22583,22584,22586,22587,1261,22589,22591,22592,22594],{},"Open (or reconstruct) a typed collection within this vault and cache the handle; the generic ",[941,22566,11968],{}," threads through ",[941,22569,3158],{},[941,22571,3161],{},[941,22573,5286],{},[941,22575,3239],{}," so records come back typed. Throws ",[941,22578,6792],{}," for names starting with ",[941,22581,22582],{},"_dict_"," or equal to ",[941,22585,6799],{}," (use ",[941,22588,21460],{},[941,22590,21676],{}," for those). Lazy mode combined with ",[941,22593,21907],{}," is rejected at construction time.",[1111,22596,22597,22603],{},[1114,22598,22599,22602],{},[941,22600,22601],{},"collectionName"," — the collection to open; created on first write.",[1114,22604,22605,22607,22608,22610,22611],{},[941,22606,16362],{}," — collection configuration. The ",[950,22609,2635],{}," members below work strategy-free:\n",[1111,22612,22613,22634,22640,22654,22664,22676,22688,22702,22711,22730,22738,22746],{},[1114,22614,22615,12536,22618,22623,22624,22626,22627,22630,22631,1520],{},[941,22616,22617],{},"options.schema",[1007,22619,22622],{"href":22620,"rel":22621},"https:\u002F\u002Fstandardschema.dev",[3371],"Standard Schema v1"," validator (Zod, Valibot, ArkType, …). Every ",[941,22625,20953],{}," is validated before encryption; every read is validated after decryption; failing records throw ",[941,22628,22629],{},"SchemaValidationError",". See ",[1007,22632,111],{"href":22633},"..\u002Fcollection\u002Fschema",[1114,22635,22636,22639],{},[941,22637,22638],{},"options.indexes"," — secondary indexes for the query DSL, computed in memory after decryption (the adapter never sees index data).",[1114,22641,22642,22645,22646,22648,22649,22651,22652,1520],{},[941,22643,22644],{},"options.prefetch"," — hydration mode. Default ",[941,22647,11790],{}," (eager: load everything on first access); ",[941,22650,2560],{}," is lazy mode — records load on demand, bounded by the LRU ",[941,22653,6840],{},[1114,22655,22656,22659,22660,22663],{},[941,22657,22658],{},"options.cache"," — LRU bounds (",[941,22661,22662],{},"{ maxRecords, maxBytes: '50MB' | 1024 }","). Required in lazy mode.",[1114,22665,22666,22669,22670,22672,22673,22675],{},[941,22667,22668],{},"options.sensitive"," — structural group-encryption: the named fields are sealed into their own ",[941,22671,2527],{}," envelope slot under a per-field key, kept out of the open ",[941,22674,1146],{}," blob. Default-off; byte-identical output when absent.",[1114,22677,22678,22681,22682,22684,22685,22687],{},[941,22679,22680],{},"options.perRecordKeys"," — when ",[941,22683,11790],{},", each record body is encrypted under a fresh per-record CEK wrapped under the collection DEK (",[941,22686,1149],{},"), stable across versions — the foundation for per-record erasure and record-scoped sealing. Off by default.",[1114,22689,22690,22693,22694,20421,22700,1520],{},[941,22691,22692],{},"options.deterministicFields"," — declare deterministic-encryption fields for blind equality search (see ",[1007,22695,22697],{"href":22696},"..\u002Fcollection\u002Fmethods#findbydet",[941,22698,22699],{},"findByDet()",[941,22701,14587],{},[1114,22703,22704,22707,22708,22710],{},[941,22705,22706],{},"options.acknowledgeDeterministicRisk"," — explicit acknowledgement that deterministic encryption leaks equality; required whenever ",[941,22709,14583],{}," is set.",[1114,22712,22713,22681,22716,968,22718,22720,22721,22724,22725,1261,22727,22729],{},[941,22714,22715],{},"options.provenance",[941,22717,11790],{},[941,22719,20953],{}," calls that supply a ",[941,22722,22723],{},"source"," option stamp ",[941,22726,1710],{},[941,22728,1713],{}," onto the unencrypted envelope metadata. Off by default (zero cost otherwise).",[1114,22731,22732,22735,22736,1520],{},[941,22733,22734],{},"options.meta"," — the collection's descriptive metadata (label \u002F description \u002F icon); surfaced by ",[941,22737,1380],{},[1114,22739,22740,22743,22744,1520],{},[941,22741,22742],{},"options.fieldMeta"," — per-field consumer-neutral descriptors (label \u002F unit \u002F semanticType \u002F sensitivity …); surfaced by ",[941,22745,1380],{},[1114,22747,22748,964,22751,968,22754,968,22757,968,22760,968,22763,968,22766,968,22768,968,22771,1261,22773,968,22776,968,22778,1261,22780,968,22783,968,22786,968,22788,1261,22791,968,22794,968,22796,22799,22800,22802,22803,1520],{},[950,22749,22750],{},"Service options",[941,22752,22753],{},"refs",[941,22755,22756],{},"i18nFields",[941,22758,22759],{},"dictKeyFields",[941,22761,22762],{},"lookupFields",[941,22764,22765],{},"moneyFields",[941,22767,637],{},[941,22769,22770],{},"classifiedFields",[941,22772,4452],{},[941,22774,22775],{},"textIndexes",[941,22777,653],{},[941,22779,872],{},[941,22781,22782],{},"tierMode",[941,22784,22785],{},"blobFields",[941,22787,605],{},[941,22789,22790],{},"persistJsonSchema",[941,22792,22793],{},"schemaUpdate",[941,22795,609],{},[941,22797,22798],{},"historyConfig"," take effect only when the matching strategy is wired into ",[941,22801,12780],{},"; each is documented on its ",[1007,22804,22806],{"href":22805},"..\u002F..\u002Fservices\u002F","service page",[1591,22808,22809],{"id":2698},[941,22810,22811],{},"collections()",[2217,22813,22815],{"className":2219,"code":22814,"language":2221,"meta":1037,"style":1037},"collections(): Promise\u003Cstring[]>\n",[941,22816,22817],{"__ignoreMap":1037},[2225,22818,22819,22821,22823,22825,22827,22830],{"class":2227,"line":2228},[2225,22820,2698],{"class":2291},[2225,22822,12809],{"class":2239},[2225,22824,11740],{"class":6342},[2225,22826,6474],{"class":2235},[2225,22828,22829],{"class":2239},"string[]",[2225,22831,7093],{"class":2235},[937,22833,22834],{},"List every collection name held in this vault.",[1022,22836,22838],{"id":22837},"backup-and-restore","Backup and restore",[937,22840,22841],{},"These methods produce and consume a verifiable, encrypted JSON backup of the whole vault — ciphertext end to end, tamper-evident via the embedded ledger head. Reach for them to snapshot a vault or move it between noy-db instances without ever exposing plaintext.",[1591,22843,22845],{"id":22844},"dump",[941,22846,22847],{},"dump()",[2217,22849,22851],{"className":2219,"code":22850,"language":2221,"meta":1037,"style":1037},"dump(): Promise\u003Cstring>\n",[941,22852,22853],{"__ignoreMap":1037},[2225,22854,22855,22857,22859,22861,22863,22865],{"class":2227,"line":2228},[2225,22856,22844],{"class":2291},[2225,22858,12809],{"class":2239},[2225,22860,11740],{"class":6342},[2225,22862,6474],{"class":2235},[2225,22864,7259],{"class":2239},[2225,22866,7093],{"class":2235},[937,22868,22869,22870,22872,22873,22876,22877,22880],{},"Dump the vault as a verifiable, encrypted JSON backup string. The backup embeds the current ledger head and the full ",[941,22871,6724],{}," internal collections, so the receiver can run ",[941,22874,22875],{},"verifyBackupIntegrity()"," after ",[941,22878,22879],{},"load()"," and detect tampering between dump and restore. Backups produced without a ledger (older formats, or hubs built without the history strategy) skip that integrity check with a warning — both modes round-trip cleanly.",[1061,22882,22883],{},[937,22884,22885,1324,22888,22890,22891,22893,22894,22898,22899,1520],{},[950,22886,22887],{},"Note:",[941,22889,22847],{}," is the encrypted-backup path; record bodies stay under the vault's DEKs. For a ",[1092,22892,2985],{}," export, use the ",[1007,22895,22896,13564],{"href":13561},[941,22897,1791],{}," — gated by ",[1007,22900,22902],{"href":22901},"#export-authorization","export authorization",[1591,22904,22906],{"id":22905},"load",[941,22907,22879],{},[2217,22909,22911],{"className":2219,"code":22910,"language":2221,"meta":1037,"style":1037},"load(backupJson: string): Promise\u003Cvoid>\n",[941,22912,22913],{"__ignoreMap":1037},[2225,22914,22915,22917,22920,22922],{"class":2227,"line":2228},[2225,22916,22905],{"class":2291},[2225,22918,22919],{"class":2239},"(backupJson: string): ",[2225,22921,11740],{"class":6342},[2225,22923,13016],{"class":2235},[937,22925,22926,22927,22929,22930,22932,22933,22936,22937,22940],{},"Restore a vault from a string produced by ",[941,22928,22847],{},". After loading, it runs ",[941,22931,22875],{}," and, on failure, throws ",[941,22934,22935],{},"BackupLedgerError"," (chain or head mismatch) or ",[941,22938,22939],{},"BackupCorruptedError"," (a data envelope no longer matches its ledger entry). The adapter state is already written by the time it throws, so the caller decides whether to accept the suspect state or wipe and retry. Legacy backups with no ledger load with a console warning and skip the check.",[1111,22942,22943],{},[1114,22944,22945,22948,22949,1520],{},[941,22946,22947],{},"backupJson"," — the JSON string returned by ",[941,22950,22847],{},[1591,22952,22954],{"id":22953},"verifybackupintegrity",[941,22955,22875],{},[2217,22957,22959],{"className":2219,"code":22958,"language":2221,"meta":1037,"style":1037},"verifyBackupIntegrity(): Promise\u003CVerifyBackupResult>\n",[941,22960,22961],{"__ignoreMap":1037},[2225,22962,22963,22966,22968,22970,22972,22975],{"class":2227,"line":2228},[2225,22964,22965],{"class":2291},"verifyBackupIntegrity",[2225,22967,12809],{"class":2239},[2225,22969,11740],{"class":6342},[2225,22971,6474],{"class":2235},[2225,22973,22974],{"class":2239},"VerifyBackupResult",[2225,22976,7093],{"class":2235},[937,22978,22979,22980,22983,22984,22987,22988,22991,22992,1520],{},"Run an end-to-end integrity check: walk the ledger hash chain, then cross-check every current data envelope's ",[941,22981,22982],{},"payloadHash"," against its ledger entry. Returns a discriminated union — ",[941,22985,22986],{},"{ ok: true, head, length }"," on success, or ",[941,22989,22990],{},"{ ok: false, kind: 'chain' | 'data', … }"," identifying whether the chain itself broke or a specific envelope diverged. Exposed for scheduled tamper detection on an in-place vault, not just during ",[941,22993,22879],{},[1022,22995,22997],{"id":22996},"metadata-and-locale","Metadata and locale",[937,22999,23000,23001,23003],{},"These read the vault's descriptive metadata and manage its default locale — the fallback used for i18n \u002F dictKey field resolution when a per-call ",[941,23002,11781],{}," is omitted.",[1591,23005,23007],{"id":23006},"getmeta",[941,23008,23009],{},"getMeta()",[2217,23011,23013],{"className":2219,"code":23012,"language":2221,"meta":1037,"style":1037},"getMeta(): VaultMeta | undefined\n",[941,23014,23015],{"__ignoreMap":1037},[2225,23016,23017,23020,23023,23025],{"class":2227,"line":2228},[2225,23018,23019],{"class":2291},"getMeta",[2225,23021,23022],{"class":2239},"(): VaultMeta ",[2225,23024,18307],{"class":2235},[2225,23026,23027],{"class":2235}," undefined\n",[937,23029,23030,23031,23034,23035,23037],{},"Return the vault-level descriptive metadata (label \u002F description), set once at construction via ",[941,23032,23033],{},"openVault(name, { meta })"," and first-wins thereafter. ",[941,23036,18316],{}," when none was supplied.",[1591,23039,23041],{"id":23040},"setlocale",[941,23042,23043],{},"setLocale()",[2217,23045,23047],{"className":2219,"code":23046,"language":2221,"meta":1037,"style":1037},"setLocale(locale: string | undefined): void\n",[941,23048,23049],{"__ignoreMap":1037},[2225,23050,23051,23054,23057,23059,23061,23063],{"class":2227,"line":2228},[2225,23052,23053],{"class":2291},"setLocale",[2225,23055,23056],{"class":2239},"(locale: string ",[2225,23058,18307],{"class":2235},[2225,23060,8483],{"class":2235},[2225,23062,11737],{"class":2239},[2225,23064,12295],{"class":2235},[937,23066,23067],{},"Set or update the vault-default locale at runtime — useful when the user switches their preferred language after opening the vault.",[1111,23069,23070],{},[1114,23071,23072,23075,23076,23078],{},[941,23073,23074],{},"locale"," — the new default locale; pass ",[941,23077,18316],{}," to clear it.",[1591,23080,23082],{"id":23081},"getlocale",[941,23083,23084],{},"getLocale()",[2217,23086,23088],{"className":2219,"code":23087,"language":2221,"meta":1037,"style":1037},"getLocale(): string | undefined\n",[941,23089,23090],{"__ignoreMap":1037},[2225,23091,23092,23095,23098,23100],{"class":2227,"line":2228},[2225,23093,23094],{"class":2291},"getLocale",[2225,23096,23097],{"class":2239},"(): string ",[2225,23099,18307],{"class":2235},[2225,23101,23027],{"class":2235},[937,23103,23104,23105,23107],{},"Return the current vault-default locale, or ",[941,23106,18316],{}," if none is set.",[1022,23109,23111],{"id":23110},"session-identity","Session identity",[937,23113,23114],{},"These read-only getters identify the principal backing this vault session. Reach for them to drive UI affordance gates, stamp audit trails, or confirm a caller's role before attempting a privileged action.",[1591,23116,23118],{"id":23117},"userid",[941,23119,10749],{},[2217,23121,23123],{"className":2219,"code":23122,"language":2221,"meta":1037,"style":1037},"get userId(): string\n",[941,23124,23125],{"__ignoreMap":1037},[2225,23126,23127,23129,23131],{"class":2227,"line":2228},[2225,23128,4978],{"class":2239},[2225,23130,10749],{"class":2291},[2225,23132,23133],{"class":2239},"(): string\n",[937,23135,23136],{},"The user id of the keyring backing this vault session — for UI affordances (\"you are alice\"), audit trails, and stamping records with the current actor.",[1591,23138,23139],{"id":16785},[941,23140,16785],{},[2217,23142,23144],{"className":2219,"code":23143,"language":2221,"meta":1037,"style":1037},"get role(): Role\n",[941,23145,23146],{"__ignoreMap":1037},[2225,23147,23148,23150,23152],{"class":2227,"line":2228},[2225,23149,4978],{"class":2239},[2225,23151,16785],{"class":2291},[2225,23153,23154],{"class":2239},"(): Role\n",[937,23156,23157,23158,23161],{},"The role of the keyring backing this session — one of ",[941,23159,23160],{},"owner | admin | operator | viewer | client",". Use it to gate UI affordances and approval workflows before attempting an action.",[1022,23163,23165],{"id":23164},"export-authorization","Export authorization",[937,23167,23168,23169,23171,23172,23175,23176,23179,23180,1261,23183,6000,23186,23190],{},"These pre-flight the current keyring's authorization to export or import data. Every ",[941,23170,9938],{}," package calls the ",[941,23173,23174],{},"assert*"," variant before it runs; the non-throwing ",[941,23177,23178],{},"can*"," variants are for UI affordances (grey out a button) without catching an exception. The plaintext export primitives themselves — ",[941,23181,23182],{},"exportStream()",[941,23184,23185],{},"exportJSON()",[1007,23187,23188,13564],{"href":13561},[941,23189,1791],{}," — run only after these checks pass.",[1591,23192,23194],{"id":23193},"assertcanexport",[941,23195,23196],{},"assertCanExport()",[2217,23198,23200],{"className":2219,"code":23199,"language":2221,"meta":1037,"style":1037},"assertCanExport(tier: 'plaintext', format: ExportFormat): void\nassertCanExport(tier: 'bundle'): void\n",[941,23201,23202,23223],{"__ignoreMap":1037},[2225,23203,23204,23207,23210,23212,23214,23216,23218,23221],{"class":2227,"line":2228},[2225,23205,23206],{"class":2291},"assertCanExport",[2225,23208,23209],{"class":2239},"(tier: ",[2225,23211,2805],{"class":2235},[2225,23213,2985],{"class":2252},[2225,23215,2805],{"class":2235},[2225,23217,2298],{"class":2235},[2225,23219,23220],{"class":2239}," format: ExportFormat): ",[2225,23222,12295],{"class":2235},[2225,23224,23225,23227,23229,23231,23233,23235,23237],{"class":2227,"line":1041},[2225,23226,23206],{"class":2291},[2225,23228,23209],{"class":2239},[2225,23230,2805],{"class":2235},[2225,23232,629],{"class":2252},[2225,23234,2805],{"class":2235},[2225,23236,11737],{"class":2239},[2225,23238,12295],{"class":2235},[937,23240,23241,23242,23244,23245,23248],{},"Authorize an export against the current keyring's ",[941,23243,11422],{},"; throws ",[941,23246,23247],{},"ExportCapabilityError"," when the keyring is not authorized. The plaintext tier defaults closed for every role (the owner must positively grant it per format); the bundle tier defaults on for owner \u002F admin, off for others.",[1111,23250,23251,23266],{},[1114,23252,23253,964,23255,23258,23259,23262,23263,23265],{},[941,23254,17414],{},[941,23256,23257],{},"'plaintext'"," (a decrypted per-format export) or ",[941,23260,23261],{},"'bundle'"," (the encrypted ",[941,23264,1402],{}," container).",[1114,23267,23268,23271,23272,23275,23276,968,23279,957],{},[941,23269,23270],{},"format"," — for the plaintext tier, the specific ",[941,23273,23274],{},"ExportFormat"," being authorized (e.g. ",[941,23277,23278],{},"'xlsx'",[941,23280,23281],{},"'csv'",[1591,23283,23285],{"id":23284},"canexport",[941,23286,23287],{},"canExport()",[2217,23289,23291],{"className":2219,"code":23290,"language":2221,"meta":1037,"style":1037},"canExport(tier: 'plaintext', format: ExportFormat): boolean\ncanExport(tier: 'bundle'): boolean\n",[941,23292,23293,23311],{"__ignoreMap":1037},[2225,23294,23295,23298,23300,23302,23304,23306,23308],{"class":2227,"line":2228},[2225,23296,23297],{"class":2291},"canExport",[2225,23299,23209],{"class":2239},[2225,23301,2805],{"class":2235},[2225,23303,2985],{"class":2252},[2225,23305,2805],{"class":2235},[2225,23307,2298],{"class":2235},[2225,23309,23310],{"class":2239}," format: ExportFormat): boolean\n",[2225,23312,23313,23315,23317,23319,23321,23323],{"class":2227,"line":1041},[2225,23314,23297],{"class":2291},[2225,23316,23209],{"class":2239},[2225,23318,2805],{"class":2235},[2225,23320,629],{"class":2252},[2225,23322,2805],{"class":2235},[2225,23324,23325],{"class":2239},"): boolean\n",[937,23327,23328,23329,1520],{},"Non-throwing read of the invoking keyring's export capability, with role-based defaults resolved — the UI-affordance companion to ",[941,23330,23196],{},[1111,23332,23333,23342],{},[1114,23334,23335,964,23337,4688,23339,23341],{},[941,23336,17414],{},[941,23338,23257],{},[941,23340,23261],{},", as above.",[1114,23343,23344,23346,23347,23349],{},[941,23345,23270],{}," — for the plaintext tier, the ",[941,23348,23274],{}," to check.",[1591,23351,23353],{"id":23352},"assertcanimport",[941,23354,23355],{},"assertCanImport()",[2217,23357,23359],{"className":2219,"code":23358,"language":2221,"meta":1037,"style":1037},"assertCanImport(tier: 'plaintext', format: ExportFormat): void\nassertCanImport(tier: 'bundle'): void\n",[941,23360,23361,23380],{"__ignoreMap":1037},[2225,23362,23363,23366,23368,23370,23372,23374,23376,23378],{"class":2227,"line":2228},[2225,23364,23365],{"class":2291},"assertCanImport",[2225,23367,23209],{"class":2239},[2225,23369,2805],{"class":2235},[2225,23371,2985],{"class":2252},[2225,23373,2805],{"class":2235},[2225,23375,2298],{"class":2235},[2225,23377,23220],{"class":2239},[2225,23379,12295],{"class":2235},[2225,23381,23382,23384,23386,23388,23390,23392,23394],{"class":2227,"line":1041},[2225,23383,23365],{"class":2291},[2225,23385,23209],{"class":2239},[2225,23387,2805],{"class":2235},[2225,23389,629],{"class":2252},[2225,23391,2805],{"class":2235},[2225,23393,11737],{"class":2239},[2225,23395,12295],{"class":2235},[937,23397,23398,23399,23244,23401,23404,23405,1261,23408,23411],{},"Authorize an import against the keyring's ",[941,23400,11438],{},[941,23402,23403],{},"ImportCapabilityError"," otherwise. Reader entry points (",[941,23406,23407],{},"fromString",[941,23409,23410],{},"fromBytes",") must call this before parsing. Default-closed for every role — including the owner for bundle import, because import is more dangerous than export (it corrupts rather than merely leaks).",[1111,23413,23414,23427],{},[1114,23415,23416,964,23418,23420,23421,23423,23424,23426],{},[941,23417,17414],{},[941,23419,23257],{}," (a per-format import) or ",[941,23422,23261],{}," (a ",[941,23425,1402],{}," bundle import).",[1114,23428,23429,23271,23431,23433],{},[941,23430,23270],{},[941,23432,23274],{}," being authorized.",[1591,23435,23437],{"id":23436},"canimport",[941,23438,23439],{},"canImport()",[2217,23441,23443],{"className":2219,"code":23442,"language":2221,"meta":1037,"style":1037},"canImport(tier: 'plaintext', format: ExportFormat): boolean\ncanImport(tier: 'bundle'): boolean\n",[941,23444,23445,23462],{"__ignoreMap":1037},[2225,23446,23447,23450,23452,23454,23456,23458,23460],{"class":2227,"line":2228},[2225,23448,23449],{"class":2291},"canImport",[2225,23451,23209],{"class":2239},[2225,23453,2805],{"class":2235},[2225,23455,2985],{"class":2252},[2225,23457,2805],{"class":2235},[2225,23459,2298],{"class":2235},[2225,23461,23310],{"class":2239},[2225,23463,23464,23466,23468,23470,23472,23474],{"class":2227,"line":1041},[2225,23465,23449],{"class":2291},[2225,23467,23209],{"class":2239},[2225,23469,2805],{"class":2235},[2225,23471,629],{"class":2252},[2225,23473,2805],{"class":2235},[2225,23475,23325],{"class":2239},[937,23477,23478,23479,23481],{},"Non-throwing read of the keyring's import capability — returns ",[941,23480,2560],{}," in every default-closed case.",[1111,23483,23484,23492],{},[1114,23485,23486,964,23488,4688,23490,23341],{},[941,23487,17414],{},[941,23489,23257],{},[941,23491,23261],{},[1114,23493,23494,23346,23496,23349],{},[941,23495,23270],{},[941,23497,23274],{},[1022,23499,13056],{"id":13055},[937,23501,23502,23504,23505,13070,23507,13073],{},[941,23503,13061],{}," — the owner-curated, deliberately-plaintext vault descriptor, readable before unlock so a bundle writer can label a vault — is documented on the ",[1007,23506,13069],{"href":13068},[941,23508,645],{},[1022,23510,22351],{"id":23511},"unlocked-by-services",[937,23513,23514,23515,23517,23518,23520,23521,23523,23524,23526,23527,23529,23530,23532],{},"Further methods appear on a ",[941,23516,94],{}," once you opt into the matching ",[950,23519,1047],{}," — a tree-shakeable ",[941,23522,943],{}," capability wired into ",[941,23525,12780],{},". Each stays gated (throwing a ",[941,23528,21121],{}," or the strategy's equivalent) until its strategy is present. Browse the ",[1007,23531,4272],{"href":22805},"; grouped by capability:",[1111,23534,23535,23548,23559,23572,23586,23599,23611,23627,23649,23662,23678,23700,23713,23741,23751,23760,23776,23789],{},[1114,23536,23537,964,23542,968,23545,1520],{},[950,23538,23539],{},[1007,23540,23541],{"href":21867},"History & time travel",[941,23543,23544],{},"at(timestamp)",[941,23546,23547],{},"ledger()",[1114,23549,23550,964,23556,1520],{},[950,23551,23552],{},[1007,23553,23555],{"href":23554},"..\u002F..\u002Fservices\u002Fforeign-refs","Foreign refs & integrity",[941,23557,23558],{},"checkIntegrity()",[1114,23560,23561,964,23566,968,23569,1520],{},[950,23562,23563],{},[1007,23564,23565],{"href":21465},"Dictionaries & i18n",[941,23567,23568],{},"dictionary(name)",[941,23570,23571],{},"resolveDictSource(...)",[1114,23573,23574,964,23580,968,23583,1520],{},[950,23575,23576],{},[1007,23577,23579],{"href":23578},"..\u002F..\u002Fservices\u002Flinks","Links",[941,23581,23582],{},"link(name, spec)",[941,23584,23585],{},"links(name)",[1114,23587,23588,964,23593,968,23596,1520],{},[950,23589,23590],{},[1007,23591,23592],{"href":21691},"Sequences & numbering",[941,23594,23595],{},"sequence(series, opts)",[941,23597,23598],{},"runNumberingPass(series)",[1114,23600,23601,964,23605,968,23608,1520],{},[950,23602,23603],{},[1007,23604,15714],{"href":15126},[941,23606,23607],{},"compact(...)",[941,23609,23610],{},"exportBlobs(...)",[1114,23612,23613,964,23618,968,23621,968,23624,1520],{},[950,23614,23615],{},[1007,23616,23617],{"href":21413},"Archive",[941,23619,23620],{},"archive(...)",[941,23622,23623],{},"restore(...)",[941,23625,23626],{},"listArchived(...)",[1114,23628,23629,964,23634,968,23637,968,23640,968,23643,968,23645,968,23647,1520],{},[950,23630,23631],{},[1007,23632,23633],{"href":16003},"Attestation",[941,23635,23636],{},"issueAttestation(...)",[941,23638,23639],{},"revokeAttestation(...)",[941,23641,23642],{},"unrevokeAttestation(...)",[941,23644,21560],{},[941,23646,21563],{},[941,23648,21551],{},[1114,23650,23651,964,23656,968,23659,1520],{},[950,23652,23653],{},[1007,23654,23655],{"href":14655},"Forget \u002F erasure",[941,23657,23658],{},"forget(subjectId)",[941,23660,23661],{},"rebuildSubjectIndex()",[1114,23663,23664,964,23669,968,23672,968,23675,1520],{},[950,23665,23666],{},[1007,23667,23668],{"href":13522},"Sealed records",[941,23670,23671],{},"sealRecordToHost(...)",[941,23673,23674],{},"revokeSealedRecord(...)",[941,23676,23677],{},"rotateRecordCek(...)",[1114,23679,23680,964,23685,968,23688,968,23691,968,23694,968,23697,1520],{},[950,23681,23682],{},[1007,23683,23684],{"href":14616},"Tiers & delegation",[941,23686,23687],{},"elevate(...)",[941,23689,23690],{},"delegate(...)",[941,23692,23693],{},"refreshDelegations(...)",[941,23695,23696],{},"revokeDelegation(...)",[941,23698,23699],{},"onCrossTierAccess(...)",[1114,23701,23702,964,23707,968,23710,1520],{},[950,23703,23704],{},[1007,23705,23706],{"href":21533},"Consent",[941,23708,23709],{},"withConsent(...)",[941,23711,23712],{},"consentAudit(...)",[1114,23714,23715,964,23720,968,23723,968,23726,968,23729,968,23732,968,23735,968,23738,1520],{},[950,23716,23717],{},[1007,23718,23719],{"href":16031},"Periods",[941,23721,23722],{},"closePeriod(...)",[941,23724,23725],{},"openPeriod(...)",[941,23727,23728],{},"listPeriods()",[941,23730,23731],{},"getPeriod(...)",[941,23733,23734],{},"freezePeriod(...)",[941,23736,23737],{},"archivePeriod(...)",[941,23739,23740],{},"purgePeriodTargets(...)",[1114,23742,23743,964,23748,1520],{},[950,23744,23745],{},[1007,23746,23747],{"href":21510},"Shadow \u002F frame",[941,23749,23750],{},"frame()",[1114,23752,23753,964,23757,1520],{},[950,23754,23755],{},[1007,23756,21638],{"href":15340},[941,23758,23759],{},"broker()",[1114,23761,23762,964,23767,968,23770,968,23773,1520],{},[950,23763,23764],{},[1007,23765,23766],{"href":21236},"Custody",[941,23768,23769],{},"custody.grantCustodian(...)",[941,23771,23772],{},"custody.revokeCustodian(...)",[941,23774,23775],{},"custody.liberate(...)",[1114,23777,23778,964,23783,968,23786,1520],{},[950,23779,23780],{},[1007,23781,23782],{"href":22121},"Derivations & materialized views",[941,23784,23785],{},"refreshView(name)",[941,23787,23788],{},"deriveAll(source)",[1114,23790,23791,964,23797,968,23800,968,23803,1520],{},[950,23792,23793],{},[1007,23794,23796],{"href":23795},"..\u002F..\u002Fservices\u002Fschema-update","Schema update & cutover",[941,23798,23799],{},"runSchemaCutover(...)",[941,23801,23802],{},"abortSchemaCutover()",[941,23804,23805],{},"migrateSatellitePerRecordKeys(...)",[2645,23807,23808],{},"html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}",{"title":1037,"searchDepth":1038,"depth":1038,"links":23810},[23811,23815,23820,23825,23829,23835,23836],{"id":22359,"depth":1041,"text":22360,"children":23812},[23813,23814],{"id":6471,"depth":1038,"text":22369},{"id":2698,"depth":1038,"text":22811},{"id":22837,"depth":1041,"text":22838,"children":23816},[23817,23818,23819],{"id":22844,"depth":1038,"text":22847},{"id":22905,"depth":1038,"text":22879},{"id":22953,"depth":1038,"text":22875},{"id":22996,"depth":1041,"text":22997,"children":23821},[23822,23823,23824],{"id":23006,"depth":1038,"text":23009},{"id":23040,"depth":1038,"text":23043},{"id":23081,"depth":1038,"text":23084},{"id":23110,"depth":1041,"text":23111,"children":23826},[23827,23828],{"id":23117,"depth":1038,"text":10749},{"id":16785,"depth":1038,"text":16785},{"id":23164,"depth":1041,"text":23165,"children":23830},[23831,23832,23833,23834],{"id":23193,"depth":1038,"text":23196},{"id":23284,"depth":1038,"text":23287},{"id":23352,"depth":1038,"text":23355},{"id":23436,"depth":1038,"text":23439},{"id":13055,"depth":1041,"text":13056},{"id":23511,"depth":1041,"text":22351},"The Vault object reference: open collections, encrypted tamper-evident backup\u002Frestore, metadata and locale, session identity, and export authorization.",{},{"title":94,"description":23837},"O9NzI1E2h8s_y1rblaULzhfXtqjJW-0Us7oBGRVBN8E",{"id":23842,"title":94,"access":12,"apiToc":929,"body":23843,"category":1042,"description":23901,"extension":1044,"meta":23902,"navigation":23903,"nest":2635,"path":89,"seo":23904,"sinceVersion":1042,"status":1049,"stem":90,"subtitle":1042,"tier":6922,"tocExternal":1042,"__hash__":23905},"docs\u002Fcore\u002F3.vault\u002Findex.md",{"type":931,"value":23844,"toc":23898},[23845,23849,23865,23872,23874,23885],[934,23846,23848],{"id":23847},"vault-an-isolated-namespace","Vault — an isolated namespace",[937,23850,3289,23851,23853,23854,23857,23858,1746,23860,1746,23862,23864],{},[950,23852,2694],{}," is an isolated namespace — one tenant, one company, one user. You open one with ",[941,23855,23856],{},"const vault = await db.openVault('acme')",", and it sits in the middle of the ",[941,23859,62],{},[941,23861,94],{},[941,23863,3121],{}," hierarchy: the session opens it, and every collection you read or write lives inside it.",[937,23866,23867,23868,23871],{},"Each vault has its ",[950,23869,23870],{},"own keyring",", so access to one vault says nothing about another. It holds the vault's typed collections plus its own descriptive metadata (a label \u002F description, applied first-wins on the first open) and a default locale for i18n resolution.",[1022,23873,22223],{"id":22222},[1111,23875,23876],{},[1114,23877,23878,22232,23882,23884],{},[950,23879,23880],{},[1007,23881,94],{"href":21106},[941,23883,94],{}," instance: opening collections, encrypted backup and restore, metadata and locale, session identity, and export authorization.",[937,23886,23887,23888,23890,23891,23894,23895,23897],{},"Everything a vault gains once you opt into a ",[950,23889,1047],{}," — dictionaries, links, sequences, blobs, archival, history and time-travel, periods, consent, tiers and delegation, forget\u002Ferasure, sealed records, attestation — is listed under ",[1007,23892,22351],{"href":23893},".\u002Fmethods#unlocked-by-services"," on the methods page, each linking its ",[1007,23896,4272],{"href":22805}," page.",{"title":1037,"searchDepth":1038,"depth":1038,"links":23899},[23900],{"id":22222,"depth":1041,"text":22223},"A vault: one isolated, encrypted namespace with its own keyring. What a vault is, how you open one, and the section map for its reference.",{},{"title":10},{"title":94,"description":23901},"GuGlpdjOrvGwFphKSPJkpf9AxvsN3UUDxjdR2xG7hak",{"id":23907,"title":103,"access":12,"apiToc":2284,"body":23908,"category":1042,"description":25745,"extension":1044,"meta":25746,"navigation":1042,"nest":2635,"path":104,"seo":25747,"sinceVersion":1042,"status":1049,"stem":105,"subtitle":1042,"tier":6922,"tocExternal":1042,"__hash__":25748},"docs\u002Fcore\u002F4.collection\u002F1.methods.md",{"type":931,"value":23909,"toc":25706},[23910,23914,23943,23959,23964,23968,23983,23987,24021,24030,24060,24064,24136,24148,24203,24211,24224,24286,24334,24360,24364,24390,24396,24403,24408,24433,24442,24454,24458,24461,24467,24527,24547,24588,24607,24613,24646,24658,24666,24672,24693,24699,24706,24710,24735,24739,24780,24813,24827,24831,24866,24898,24909,24914,24933,24936,24942,25002,25022,25036,25040,25046,25086,25124,25135,25139,25160,25165,25192,25211,25225,25231,25254,25268,25280,25283,25288,25292,25320,25335,25348,25354,25381,25387,25391,25407,25413,25419,25447,25452,25458,25475,25481,25487,25508,25518,25524,25537,25543,25545,25560,25703],[934,23911,23913],{"id":23912},"collection-the-object-reference","Collection — the object reference",[937,23915,3289,23916,22318,23918,23921,23922,968,23924,23927,23928,23930,23931,23935,23936,1746,23938,1746,23940,23942],{},[941,23917,3121],{},[941,23919,23920],{},"vault.collection\u003CT>(name)"," returns — a typed set of records inside a vault, conventionally bound to the collection's plural name (",[941,23923,6487],{},[941,23925,23926],{},"clients","). The generic ",[941,23929,11968],{}," threads through every read and write, so records come back typed, and an optional ",[1007,23932,23934],{"href":23933},".\u002Fschema","schema"," validates them on the way in and out. It sits at the bottom of the ",[941,23937,62],{},[941,23939,94],{},[941,23941,3121],{}," hierarchy — the object your application spends most of its time with.",[937,23944,11625,23945,10313,23947,23949,23950,23953,23954,23956,23957,1520],{},[950,23946,11628],{},[941,23948,2134],{}," — the strategy-free kernel surface. The full query DSL is covered in depth on ",[1007,23951,107],{"href":23952},".\u002Fquerying"," and the validation scaffold on ",[1007,23955,111],{"href":23933},"; this page documents each method's signature and contract. Anything a collection gains from an opt-in strategy — history, search, blobs, presence, tiers, classified fields — is gathered at the end under ",[1007,23958,22351],{"href":22350},[937,23960,11649,23961,23963],{},[950,23962,11652],{}," — reading and writing, bulk operations, querying and scanning, change events, deterministic lookup, and introspection — each opening with a short note on what unites its methods, then the individual methods.",[1022,23965,23967],{"id":23966},"reading-and-writing","Reading and writing",[937,23969,23970,23971,23974,23975,4688,23977,23979,23980,23982],{},"The four single-record primitives. All writes run inside the hub's write-queue tracker, so ",[941,23972,23973],{},"db.writeQueue.pending"," reflects an in-flight ",[941,23976,20953],{},[941,23978,9467],{},", and a completed write fires a ",[941,23981,12111],{}," event.",[1591,23984,23985],{"id":3158},[941,23986,14639],{},[2217,23988,23990],{"className":2219,"code":23989,"language":2221,"meta":1037,"style":1037},"get(id: string, locale?: LocaleReadOptions): Promise\u003CT | null>\n",[941,23991,23992],{"__ignoreMap":1037},[2225,23993,23994,23996,23999,24001,24004,24006,24009,24011,24013,24016,24018],{"class":2227,"line":2228},[2225,23995,3158],{"class":2291},[2225,23997,23998],{"class":2239},"(id: string",[2225,24000,2298],{"class":2235},[2225,24002,24003],{"class":2239}," locale",[2225,24005,7011],{"class":2235},[2225,24007,24008],{"class":2239}," LocaleReadOptions): ",[2225,24010,11740],{"class":6342},[2225,24012,6474],{"class":2235},[2225,24014,24015],{"class":2239},"T ",[2225,24017,18307],{"class":2235},[2225,24019,24020],{"class":2235}," null>\n",[937,24022,24023,24024,24026,24027,24029],{},"Fetch a single record by id, decrypted (and optionally locale-resolved). Returns ",[941,24025,6651],{}," when no record exists for ",[941,24028,6983],{},". Output is schema-validated after decryption when a schema is attached.",[1111,24031,24032,24037],{},[1114,24033,24034,24036],{},[941,24035,6983],{}," — the record identifier.",[1114,24038,24039,24041,24042,24044,24045,24047,24048,24051,24052,24055,24056,24059],{},[941,24040,23074],{}," — optional locale options. When provided, ",[941,24043,20943],{}," fields resolve to the requested locale string and ",[941,24046,21456],{}," fields gain a ",[941,24049,24050],{},"\u003Cfield>Label"," virtual field; pass ",[941,24053,24054],{},"{ locale: 'raw' }"," to return the full ",[941,24057,24058],{},"{ [locale]: string }"," map instead.",[1591,24061,24062],{"id":3161},[941,24063,20953],{},[2217,24065,24067],{"className":2219,"code":24066,"language":2221,"meta":1037,"style":1037},"put(\n  id: string,\n  record: T,\n  options?: {\n    reason?: string\n    source?: string\n    sourceTs?: string\n  }\n): Promise\u003Cvoid>\n",[941,24068,24069,24075,24082,24089,24097,24106,24115,24124,24128],{"__ignoreMap":1037},[2225,24070,24071,24073],{"class":2227,"line":2228},[2225,24072,3161],{"class":2291},[2225,24074,11683],{"class":2239},[2225,24076,24077,24080],{"class":2227,"line":1041},[2225,24078,24079],{"class":2239},"  id: string",[2225,24081,1707],{"class":2235},[2225,24083,24084,24087],{"class":2227,"line":1038},[2225,24085,24086],{"class":2239},"  record: T",[2225,24088,1707],{"class":2235},[2225,24090,24091,24093,24095],{"class":2227,"line":2288},[2225,24092,11872],{"class":2239},[2225,24094,7011],{"class":2235},[2225,24096,2301],{"class":2235},[2225,24098,24099,24102,24104],{"class":2227,"line":2304},[2225,24100,24101],{"class":2239},"    reason?",[2225,24103,2311],{"class":2235},[2225,24105,7014],{"class":2239},[2225,24107,24108,24111,24113],{"class":2227,"line":2356},[2225,24109,24110],{"class":2239},"    source",[2225,24112,7011],{"class":2235},[2225,24114,7014],{"class":2239},[2225,24116,24117,24120,24122],{"class":2227,"line":2830},[2225,24118,24119],{"class":2239},"    sourceTs",[2225,24121,7011],{"class":2235},[2225,24123,7014],{"class":2239},[2225,24125,24126],{"class":2227,"line":2861},[2225,24127,7632],{"class":2235},[2225,24129,24130,24132,24134],{"class":2227,"line":4246},[2225,24131,11737],{"class":2239},[2225,24133,11740],{"class":6342},[2225,24135,13016],{"class":2235},[937,24137,24138,24139,24141,24142,24144,24145,24147],{},"Create or update a record. When a schema is attached the record is validated (and possibly coerced) ",[950,24140,11357],{}," encryption; a rejected record throws ",[941,24143,22629],{},". Runs inside the write-queue tracker and fires a ",[941,24146,12111],{}," event on commit.",[1111,24149,24150,24154,24160,24169,24190],{},[1114,24151,24152,24036],{},[941,24153,6983],{},[1114,24155,24156,24159],{},[941,24157,24158],{},"record"," — the record body, validated by the collection's schema if one was attached.",[1114,24161,24162,24165,24166,957],{},[941,24163,24164],{},"options.reason"," — stamped onto the resulting ledger entry so audit consumers can filter on it (e.g. ",[941,24167,24168],{},"import:*",[1114,24170,24171,24174,24175,968,24178,24181,24182,1261,24184,24186,24187,24189],{},[941,24172,24173],{},"options.source"," — an opaque source id (",[941,24176,24177],{},"'crm-sync'",[941,24179,24180],{},"'firm-A'",") stamped as ",[941,24183,1710],{},[941,24185,1713],{}," when the collection has ",[941,24188,14896],{},"; ignored otherwise (zero cost).",[1114,24191,24192,24195,24196,24198,24199,24202],{},[941,24193,24194],{},"options.sourceTs"," — an ISO-8601 origin-timestamp override; supplied with ",[941,24197,22723],{}," on a provenance collection, it replaces the machine-stamped ",[941,24200,24201],{},"now()"," so re-merges preserve the origin refresh time across vaults.",[24204,24205,24207,24208,24210],"h4",{"id":24206},"inputs-put-refuses","Inputs ",[941,24209,20953],{}," refuses",[937,24212,10993,24213,24216,24217,24223],{},[941,24214,24215],{},"0.7.0-pre.5"," two boundary values throw a ",[950,24218,24219,24222],{},[941,24220,24221],{},"TypeError"," at the seal"," instead of being\ncoerced (noy-db#1220). TypeScript rejects both already; they arrive from untyped call sites — plain\nJavaScript, a JSON body, a generic helper:",[1338,24225,24226,24239],{},[1341,24227,24228],{},[1344,24229,24230,24233,24236],{},[1347,24231,24232],{},"Input",[1347,24234,24235],{},"Was silently",[1347,24237,24238],{},"Now",[1357,24240,24241,24262],{},[1344,24242,24243,24248,24258],{},[1362,24244,24245,24246],{},"a non-string ",[941,24247,6983],{},[1362,24249,24250,24253,24254,24257],{},[941,24251,24252],{},"String({})"," → the record is sealed at the address ",[941,24255,24256],{},"\"[object Object]\"",", which nothing queries",[1362,24259,24260],{},[941,24261,24221],{},[1344,24263,24264,24269,24282],{},[1362,24265,1415,24266,24268],{},[941,24267,18316],{}," record",[1362,24270,24271,1746,24274,24276,24277,964,24279,24281],{},[941,24272,24273],{},"JSON.stringify(undefined)",[941,24275,18316],{},", so the envelope is sealed over ",[950,24278,18459],{},[941,24280,1146],{}," a bare GCM tag over zero ciphertext",[1362,24283,24284],{},[941,24285,24221],{},[937,24287,24288,24289,24292,24293,24296,24297,24301,24302,24307,24308,24311,24312,24314,24315,24317,24318,24321,24322,24325,24326,24329,24330,24333],{},"Both produced envelopes that are ",[950,24290,24291],{},"valid and undecodable",": sealed correctly, addressed or filled\nwith nothing. The two read paths then disagreed about the same bytes, and neither named the cause —\nmeasured against hub ",[941,24294,24295],{},"0.7.0-pre.4",", reading an empty-sealed record returned ",[950,24298,24299],{},[941,24300,18316],{}," from the\ncached path and threw ",[950,24303,24304],{},[941,24305,24306],{},"SyntaxError: Unexpected end of JSON input"," out of ",[941,24309,24310],{},"decryptRecord"," on the\nhydrate path, where a genuinely absent record returns ",[941,24313,6651],{},". So the failure arrived either as a\nvalue ",[941,24316,14639],{}," does not declare — its return type is ",[941,24319,24320],{},"T | null",", and any caller writing ",[941,24323,24324],{},"if (!rec)","\nfolds it into ",[1092,24327,24328],{},"no such record"," anyway — or as an error that reads as ",[1092,24331,24332],{},"your store returned corrupt\nbytes",". The guards are stated as invariants on the values themselves, so they hold for every caller\nrather than for the two calls that surfaced them.",[7646,24335,24337,24347],{"color":10789,"icon":24336},"i-lucide-shield-off",[937,24338,24339,24342,24343,24346],{},[950,24340,24341],{},"This is not a format, encryption, or integrity change."," Envelopes that seal continue to seal\nidentically, ",[941,24344,24345],{},"NOYDB_ENVELOPE_GENERATION"," is unaffected, and nothing about your stored data changed —\nthere is no migration to plan. Only inputs that previously produced an unreadable record are now\nrefused.",[937,24348,24349,24352,24353,17904,24356,24359],{},[950,24350,24351],{},"And it repairs nothing."," Records already sealed over an empty plaintext stay exactly as\nunreadable. There is deliberately no recovery path: the hub cannot tell ",[1092,24354,24355],{},"\"sealed over nothing by\nmistake\"",[1092,24357,24358],{},"\"ciphertext this build cannot open\""," without opening the bytes, and treating the\nsecond as the first is the downgrade this whole error class exists to refuse. Re-write the affected\nrecords from your source of truth.",[1591,24361,24362],{"id":3164},[941,24363,9467],{},[2217,24365,24367],{"className":2219,"code":24366,"language":2221,"meta":1037,"style":1037},"delete(id: string): Promise\u003Cvoid>\n",[941,24368,24369],{"__ignoreMap":1037},[2225,24370,24371,24374,24376,24378,24380,24382,24384,24386,24388],{"class":2227,"line":2228},[2225,24372,24373],{"class":2235},"delete(",[2225,24375,6983],{"class":7052},[2225,24377,2311],{"class":2235},[2225,24379,6366],{"class":6342},[2225,24381,6255],{"class":2235},[2225,24383,7079],{"class":6342},[2225,24385,6474],{"class":2235},[2225,24387,7165],{"class":6342},[2225,24389,7093],{"class":2235},[937,24391,24392,24393,24395],{},"Delete a record by id. Runs inside the write-queue tracker and fires a ",[941,24394,12111],{}," event on commit. Deleting a non-existent id is not an error (idempotent).",[1111,24397,24398],{},[1114,24399,24400,24402],{},[941,24401,6983],{}," — the record identifier to delete.",[1591,24404,24405],{"id":3167},[941,24406,24407],{},"list()",[2217,24409,24411],{"className":2219,"code":24410,"language":2221,"meta":1037,"style":1037},"list(locale?: LocaleReadOptions): Promise\u003CT[]>\n",[941,24412,24413],{"__ignoreMap":1037},[2225,24414,24415,24417,24420,24422,24424,24426,24428,24431],{"class":2227,"line":2228},[2225,24416,3167],{"class":2291},[2225,24418,24419],{"class":2239},"(locale",[2225,24421,7011],{"class":2235},[2225,24423,24008],{"class":2239},[2225,24425,11740],{"class":6342},[2225,24427,6474],{"class":2235},[2225,24429,24430],{"class":2239},"T[]",[2225,24432,7093],{"class":2235},[937,24434,24435,24436,24441],{},"Return every record in the collection, decrypted. Throws in lazy mode — bulk listing defeats lazy hydration; use ",[1007,24437,24439],{"href":24438},"#scan",[941,24440,7906],{}," to iterate page-by-page instead.",[1111,24443,24444],{},[1114,24445,24446,24448,24449,957],{},[941,24447,23074],{}," — optional locale options, applied to each record before it is returned (see ",[1007,24450,24452],{"href":24451},"#get",[941,24453,14639],{},[1022,24455,24457],{"id":24456},"bulk-operations","Bulk operations",[937,24459,24460],{},"One round-trip for many records. Each item still flows through the normal per-record path (validation, ledger append, change event); the saving is that the adapter stays hot across the batch.",[1591,24462,24464],{"id":24463},"putmany",[941,24465,24466],{},"putMany()",[2217,24468,24470],{"className":2219,"code":24469,"language":2221,"meta":1037,"style":1037},"putMany(\n  entries: ReadonlyArray\u003Creadonly [id: string, record: T, opts?: PutManyItemOptions]>,\n  options?: PutManyOptions\n): Promise\u003CPutManyResult>\n",[941,24471,24472,24479,24505,24514],{"__ignoreMap":1037},[2225,24473,24474,24477],{"class":2227,"line":2228},[2225,24475,24476],{"class":2291},"putMany",[2225,24478,11683],{"class":2239},[2225,24480,24481,24484,24486,24489,24491,24494,24496,24498,24500,24503],{"class":2227,"line":1041},[2225,24482,24483],{"class":2239},"  entries: ReadonlyArray",[2225,24485,6474],{"class":2235},[2225,24487,24488],{"class":2239},"readonly [id: string",[2225,24490,2298],{"class":2235},[2225,24492,24493],{"class":2239}," record: T",[2225,24495,2298],{"class":2235},[2225,24497,8232],{"class":2239},[2225,24499,7011],{"class":2235},[2225,24501,24502],{"class":2239}," PutManyItemOptions]",[2225,24504,12006],{"class":2235},[2225,24506,24507,24509,24511],{"class":2227,"line":1038},[2225,24508,11872],{"class":2239},[2225,24510,7011],{"class":2235},[2225,24512,24513],{"class":2239}," PutManyOptions\n",[2225,24515,24516,24518,24520,24522,24525],{"class":2227,"line":2288},[2225,24517,11737],{"class":2239},[2225,24519,11740],{"class":6342},[2225,24521,6474],{"class":2235},[2225,24523,24524],{"class":2239},"PutManyResult",[2225,24526,7093],{"class":2235},[937,24528,24529,24530,24533,24534,24536,24537,24540,24541,24543,24544,24546],{},"Write many records in one call. ",[950,24531,24532],{},"Best-effort by default"," — if item 5 of 10 fails, items 1–4 are already persisted and 6–10 are still attempted; the returned ",[941,24535,24524],{}," lists every success and failure so the caller can roll forward or back. Pass ",[941,24538,24539],{},"{ atomic: true }"," for a transactional variant that pre-flights every ",[941,24542,8477],{}," and reverts on mid-batch failure, throwing rather than returning a mixed result. One ",[941,24545,12111],{}," event fires per successfully-written record.",[937,24548,2123,24549,968,24552,24554,24555,24562,24563,953,24566,24571,24572,24574,24575,24577,24578,24580,24581,24583,24584,1520],{},[941,24550,24551],{},"0.5.0",[941,24553,24539],{}," delegates the whole batch through ",[950,24556,24557,24558,24561],{},"one ",[941,24559,24560],{},"store.tx()"," call"," on a store that declares ",[941,24564,24565],{},"txAtomic",[1007,24567,24570],{"href":24568,"rel":24569},"https:\u002F\u002Fgithub.com\u002Fnoy-db\u002Fcore\u002Fissues\u002F921",[3371],"noy-db#921","): every leg carries CAS against the pre-flight snapshot, so the batch is genuinely all-or-nothing at the store, and history\u002Fledger\u002Fchange events fire per record ",[950,24573,10761],{}," the batch is durable, in entry order. An ineligible batch — duplicate ids, a store without ",[941,24576,24565],{},", or an atomic-path refusal such as a live ",[941,24579,12205],{}," hook or a derivation\u002Fmaterialized-view\u002FCRDT\u002Funique-constraint collection — keeps the sequential pre-flight → execute → best-effort-revert loop unchanged. Which stores declare ",[941,24582,24565],{}," is on the ",[1007,24585,24587],{"href":24586},"..\u002F..\u002Fservices\u002Fstorage-capability-matrix","store capabilities matrix",[1111,24589,24590,24598],{},[1114,24591,24592,1169,24594,24597],{},[941,24593,9095],{},[941,24595,24596],{},"[id, record, opts?]"," tuples to write, processed in order.",[1114,24599,24600,24602,24603,24606],{},[941,24601,16362],{}," — batch options; notably ",[941,24604,24605],{},"atomic"," to switch to the transactional executor.",[1591,24608,24610],{"id":24609},"getmany",[941,24611,24612],{},"getMany()",[2217,24614,24616],{"className":2219,"code":24615,"language":2221,"meta":1037,"style":1037},"getMany(ids: readonly string[]): Promise\u003CMap\u003Cstring, T | null>>\n",[941,24617,24618],{"__ignoreMap":1037},[2225,24619,24620,24623,24626,24628,24630,24632,24634,24636,24638,24641,24643],{"class":2227,"line":2228},[2225,24621,24622],{"class":2291},"getMany",[2225,24624,24625],{"class":2239},"(ids: readonly string[]): ",[2225,24627,11740],{"class":6342},[2225,24629,6474],{"class":2235},[2225,24631,8002],{"class":2239},[2225,24633,6474],{"class":2235},[2225,24635,7259],{"class":2239},[2225,24637,2298],{"class":2235},[2225,24639,24640],{"class":2239}," T ",[2225,24642,18307],{"class":2235},[2225,24644,24645],{"class":2235}," null>>\n",[937,24647,24648,24649,24651,24652,24654,24655,24657],{},"Read many records in one call, returning a ",[941,24650,8002],{}," in input order. Missing records surface as ",[941,24653,6651],{}," entries so the caller can distinguish \"not found\" from \"lookup failed\". Reads go through the same per-id path as ",[941,24656,14639],{},", keeping cache behavior consistent.",[1111,24659,24660],{},[1114,24661,24662,24665],{},[941,24663,24664],{},"ids"," — the record ids to fetch; the result Map preserves this order.",[1591,24667,24669],{"id":24668},"deletemany",[941,24670,24671],{},"deleteMany()",[2217,24673,24675],{"className":2219,"code":24674,"language":2221,"meta":1037,"style":1037},"deleteMany(ids: readonly string[]): Promise\u003CDeleteManyResult>\n",[941,24676,24677],{"__ignoreMap":1037},[2225,24678,24679,24682,24684,24686,24688,24691],{"class":2227,"line":2228},[2225,24680,24681],{"class":2291},"deleteMany",[2225,24683,24625],{"class":2239},[2225,24685,11740],{"class":6342},[2225,24687,6474],{"class":2235},[2225,24689,24690],{"class":2239},"DeleteManyResult",[2225,24692,7093],{"class":2235},[937,24694,24695,24696,24698],{},"Delete many records in one call. Same best-effort contract as ",[941,24697,24466],{},": a mid-batch failure still attempts the rest. Deleting a non-existent id is not a failure.",[1111,24700,24701],{},[1114,24702,24703,24705],{},[941,24704,24664],{}," — the record ids to delete.",[1022,24707,24709],{"id":24708},"querying-and-scanning","Querying and scanning",[937,24711,24712,24713,24716,24717,24719,24720,1261,24723,968,24726,24729,24730,24732,24733,1520],{},"The always-on read path. ",[941,24714,24715],{},"query()"," reads the decrypted in-memory cache with a chainable builder; ",[941,24718,7906],{}," streams from the store page-by-page for collections larger than RAM. This group documents each method's signature — the full DSL (operators, ",[941,24721,24722],{},".or()",[941,24724,24725],{},".and()",[941,24727,24728],{},".filter()",", streaming aggregation) is on ",[1007,24731,107],{"href":23952},". Joins, aggregates, live results, and indexed dispatch are opt-in ",[1007,24734,599],{"href":22805},[1591,24736,24737],{"id":5286},[941,24738,24715],{},[2217,24740,24742],{"className":2219,"code":24741,"language":2221,"meta":1037,"style":1037},"query(): Query\u003CT>\nquery(predicate: (record: T) => boolean): T[]\n",[941,24743,24744,24757],{"__ignoreMap":1037},[2225,24745,24746,24748,24751,24753,24755],{"class":2227,"line":2228},[2225,24747,5286],{"class":2291},[2225,24749,24750],{"class":2239},"(): Query",[2225,24752,6474],{"class":2235},[2225,24754,11968],{"class":2239},[2225,24756,7093],{"class":2235},[2225,24758,24759,24761,24764,24766,24768,24770,24773,24775,24777],{"class":2227,"line":1041},[2225,24760,5286],{"class":2291},[2225,24762,24763],{"class":2239},"(predicate: ",[2225,24765,2789],{"class":2235},[2225,24767,24158],{"class":7052},[2225,24769,2311],{"class":2235},[2225,24771,24772],{"class":6342}," T",[2225,24774,1381],{"class":2235},[2225,24776,8116],{"class":2774},[2225,24778,24779],{"class":2239}," boolean): T[]\n",[937,24781,24782,24783,24786,24787,1261,24789,1261,24791,1261,24793,24795,24796,1261,24798,1261,24800,24802,24803,4688,24808,24812],{},"Build a chainable query against the collection's decrypted in-memory cache. Called with no arguments it returns an immutable ",[941,24784,24785],{},"Query\u003CT>"," builder (",[941,24788,3016],{},[941,24790,3019],{},[941,24792,3022],{},[941,24794,3227],{}," + synchronous ",[941,24797,3230],{},[941,24799,3233],{},[941,24801,3236],{}," terminals). A backward-compatible overload accepts a predicate and returns the filtered records directly; prefer the chainable form for new code. Throws in lazy mode — use ",[1007,24804,24805],{"href":22071},[941,24806,24807],{},"lazyQuery()",[1007,24809,24810],{"href":24438},[941,24811,7906],{}," there.",[1111,24814,24815],{},[1114,24816,24817,964,24820,4724,24823,24826],{},[941,24818,24819],{},"predicate",[1092,24821,24822],{},"(legacy overload only)",[941,24824,24825],{},"(record) => boolean"," filter; returns the matching records array directly.",[1591,24828,24829],{"id":3239},[941,24830,7906],{},[2217,24832,24834],{"className":2219,"code":24833,"language":2221,"meta":1037,"style":1037},"scan(opts?: { pageSize?: number }): ScanBuilder\u003CT>\n",[941,24835,24836],{"__ignoreMap":1037},[2225,24837,24838,24840,24843,24845,24847,24850,24852,24855,24857,24860,24862,24864],{"class":2227,"line":2228},[2225,24839,3239],{"class":2291},[2225,24841,24842],{"class":2239},"(opts",[2225,24844,7011],{"class":2235},[2225,24846,2236],{"class":2235},[2225,24848,24849],{"class":2239}," pageSize?",[2225,24851,2311],{"class":2235},[2225,24853,24854],{"class":2239}," number ",[2225,24856,2359],{"class":2235},[2225,24858,24859],{"class":2239},"): ScanBuilder",[2225,24861,6474],{"class":2235},[2225,24863,11968],{"class":2239},[2225,24865,7093],{"class":2235},[937,24867,24868,24869,24872,24873,24876,24877,24880,24881,1261,24884,24886,24887,24889,24890,24893,24894,24897],{},"Stream every record page-by-page as an ",[941,24870,24871],{},"AsyncIterable\u003CT>",", decrypting at most ",[941,24874,24875],{},"pageSize"," records at a time — the way to process collections larger than RAM. The returned ",[941,24878,24879],{},"ScanBuilder"," chains ",[941,24882,24883],{},".where()",[941,24885,24728],{}," (same semantics as ",[941,24888,24715],{},") and carries a memory-bounded ",[941,24891,24892],{},".aggregate(spec)"," terminal (which requires the ",[1007,24895,24896],{"href":21940},"aggregate service","). Works in both eager and lazy mode.",[1111,24899,24900],{},[1114,24901,24902,24905,24906,1520],{},[941,24903,24904],{},"opts.pageSize"," — records decrypted per page. Default ",[941,24907,24908],{},"100",[1591,24910,24911],{"id":3236},[941,24912,24913],{},"count()",[2217,24915,24917],{"className":2219,"code":24916,"language":2221,"meta":1037,"style":1037},"count(): Promise\u003Cnumber>\n",[941,24918,24919],{"__ignoreMap":1037},[2225,24920,24921,24923,24925,24927,24929,24931],{"class":2227,"line":2228},[2225,24922,3236],{"class":2291},[2225,24924,12809],{"class":2239},[2225,24926,11740],{"class":6342},[2225,24928,6474],{"class":2235},[2225,24930,20636],{"class":2239},[2225,24932,7093],{"class":2235},[937,24934,24935],{},"Count the records in the collection. Instant in eager mode (the in-memory cache size); in lazy mode it counts live tier-0 envelopes by inspection, loading no record bodies.",[1591,24937,24939],{"id":24938},"listpage",[941,24940,24941],{},"listPage()",[2217,24943,24945],{"className":2219,"code":24944,"language":2221,"meta":1037,"style":1037},"listPage(opts?: { cursor?: string; limit?: number }): Promise\u003C{ items: T[]; nextCursor: string | null }>\n",[941,24946,24947],{"__ignoreMap":1037},[2225,24948,24949,24952,24954,24956,24958,24961,24963,24966,24968,24970,24972,24974,24976,24978,24981,24983,24985,24987,24989,24992,24994,24996,24998,25000],{"class":2227,"line":2228},[2225,24950,24951],{"class":2291},"listPage",[2225,24953,24842],{"class":2239},[2225,24955,7011],{"class":2235},[2225,24957,2236],{"class":2235},[2225,24959,24960],{"class":2239}," cursor?",[2225,24962,2311],{"class":2235},[2225,24964,24965],{"class":2239}," string; limit",[2225,24967,7011],{"class":2235},[2225,24969,24854],{"class":2239},[2225,24971,2359],{"class":2235},[2225,24973,11737],{"class":2239},[2225,24975,11740],{"class":6342},[2225,24977,17851],{"class":2235},[2225,24979,24980],{"class":6342}," items",[2225,24982,2311],{"class":2235},[2225,24984,24772],{"class":2239},[2225,24986,7262],{"class":2307},[2225,24988,3769],{"class":2235},[2225,24990,24991],{"class":6342}," nextCursor",[2225,24993,2311],{"class":2235},[2225,24995,6366],{"class":2239},[2225,24997,7087],{"class":2235},[2225,24999,7090],{"class":2235},[2225,25001,17861],{"class":2235},[937,25003,25004,25005,25007,25008,25011,25012,25014,25015,25018,25019,25021],{},"Fetch a single page of records via the adapter's optional ",[941,25006,24951],{}," extension, returning the decrypted records plus an opaque cursor for the next page. Omit ",[941,25009,25010],{},"cursor"," (or pass ",[941,25013,18316],{},") to start from the beginning; the final page returns ",[941,25016,25017],{},"nextCursor: null",". Adapters without native ",[941,25020,24951],{}," fall back to a synthetic list-and-slice, with a one-time console warning.",[1111,25023,25024,25030],{},[1114,25025,25026,25029],{},[941,25027,25028],{},"opts.cursor"," — the opaque cursor from the previous page; omit to start at the beginning.",[1114,25031,25032,25035],{},[941,25033,25034],{},"opts.limit"," — the page size.",[1022,25037,25039],{"id":25038},"change-events","Change events",[1591,25041,25043],{"id":25042},"subscribe",[941,25044,25045],{},"subscribe()",[2217,25047,25049],{"className":2219,"code":25048,"language":2221,"meta":1037,"style":1037},"subscribe(cb: (event: CollectionChangeEvent\u003CT>) => void): () => void\n",[941,25050,25051],{"__ignoreMap":1037},[2225,25052,25053,25055,25058,25060,25062,25064,25067,25069,25071,25074,25076,25078,25080,25082,25084],{"class":2227,"line":2228},[2225,25054,25042],{"class":2291},[2225,25056,25057],{"class":2239},"(cb: ",[2225,25059,2789],{"class":2235},[2225,25061,12307],{"class":7052},[2225,25063,2311],{"class":2235},[2225,25065,25066],{"class":6342}," CollectionChangeEvent",[2225,25068,6474],{"class":2235},[2225,25070,11968],{"class":6342},[2225,25072,25073],{"class":2235},">)",[2225,25075,8116],{"class":2774},[2225,25077,12717],{"class":2235},[2225,25079,11737],{"class":2239},[2225,25081,4228],{"class":2235},[2225,25083,8116],{"class":2774},[2225,25085,12288],{"class":2235},[937,25087,25088,25089,1261,25091,25093,25094,25097,25098,25100,25101,25104,25105,25108,25109,25112,25113,1261,25116,25119,25120,1520],{},"Subscribe to every committed ",[941,25090,3161],{},[941,25092,3164],{}," on this collection; returns an unsubscribe function. This is an ",[950,25095,25096],{},"event stream",", always-on — the callback fires ",[1092,25099,10761],{}," the store write commits, never for in-flight or rolled-back writes. The event is ",[941,25102,25103],{},"{ type: 'put', id, record }"," (the current decrypted value) or ",[941,25106,25107],{},"{ type: 'delete', id, record: null }",". For a ",[950,25110,25111],{},"reactive value"," that recomputes as matching data changes, use ",[941,25114,25115],{},"query().live()",[941,25117,25118],{},"query().subscribe(cb)"," — see the ",[1007,25121,25123],{"href":25122},"..\u002F..\u002Fservices\u002Flive","live service",[1111,25125,25126],{},[1114,25127,25128,12730,25131,25134],{},[941,25129,25130],{},"cb",[941,25132,25133],{},"CollectionChangeEvent\u003CT>"," as writes commit.",[1022,25136,25138],{"id":25137},"deterministic-lookup","Deterministic lookup",[937,25140,25141,25142,25144,25145,25147,25148,25151,25152,25155,25156,25159],{},"Blind equality search over deterministically-encrypted fields. These work strategy-free but require the collection to declare ",[941,25143,14583],{}," (with ",[941,25146,14587],{},") — the stored ",[941,25149,25150],{},"_det[field]"," ciphertext is compared to a freshly computed deterministic ciphertext, so ",[950,25153,25154],{},"no record bodies are decrypted during the search",". This deliberately leaks equality between records; see the ",[1007,25157,21],{"href":25158},"..\u002Fnoydb\u002Fenclave"," page for the trade-off.",[1591,25161,25163],{"id":25162},"findbydet",[941,25164,22699],{},[2217,25166,25168],{"className":2219,"code":25167,"language":2221,"meta":1037,"style":1037},"findByDet(field: string, value: unknown): Promise\u003CT | null>\n",[941,25169,25170],{"__ignoreMap":1037},[2225,25171,25172,25174,25177,25179,25182,25184,25186,25188,25190],{"class":2227,"line":2228},[2225,25173,1965],{"class":2291},[2225,25175,25176],{"class":2239},"(field: string",[2225,25178,2298],{"class":2235},[2225,25180,25181],{"class":2239}," value: unknown): ",[2225,25183,11740],{"class":6342},[2225,25185,6474],{"class":2235},[2225,25187,24015],{"class":2239},[2225,25189,18307],{"class":2235},[2225,25191,24020],{"class":2235},[937,25193,25194,25195,25198,25199,4765,25202,25204,25205,25207,25208,25210],{},"Return the first record whose deterministic ",[941,25196,25197],{},"field"," matches ",[941,25200,25201],{},"value",[941,25203,6651],{}," when none matches. Throws when ",[941,25206,25197],{}," is not declared in ",[941,25209,14583],{},", so a typo fails loudly at the call site.",[1111,25212,25213,25220],{},[1114,25214,25215,25217,25218,1520],{},[941,25216,25197],{}," — a field declared in the collection's ",[941,25219,14583],{},[1114,25221,25222,25224],{},[941,25223,25201],{}," — the plaintext value to match by deterministic ciphertext.",[1591,25226,25228],{"id":25227},"querybydet",[941,25229,25230],{},"queryByDet()",[2217,25232,25234],{"className":2219,"code":25233,"language":2221,"meta":1037,"style":1037},"queryByDet(field: string, value: unknown): Promise\u003CT[]>\n",[941,25235,25236],{"__ignoreMap":1037},[2225,25237,25238,25240,25242,25244,25246,25248,25250,25252],{"class":2227,"line":2228},[2225,25239,1968],{"class":2291},[2225,25241,25176],{"class":2239},[2225,25243,2298],{"class":2235},[2225,25245,25181],{"class":2239},[2225,25247,11740],{"class":6342},[2225,25249,6474],{"class":2235},[2225,25251,24430],{"class":2239},[2225,25253,7093],{"class":2235},[937,25255,25256,25257,25259,25260,25198,25262,25264,25265,25267],{},"Return ",[950,25258,2119],{}," record whose deterministic ",[941,25261,25197],{},[941,25263,25201],{}," — the same semantics as ",[941,25266,22699],{}," without the short-circuit.",[1111,25269,25270,25276],{},[1114,25271,25272,25217,25274,1520],{},[941,25273,25197],{},[941,25275,14583],{},[1114,25277,25278,25224],{},[941,25279,25201],{},[1022,25281,25282],{"id":711},"Introspection",[937,25284,25285,25286,1520],{},"Zero-I\u002FO reads of the collection's declared shape and cache state — for devtools, schema-aware exporters, and UI that renders from ",[941,25287,3926],{},[1591,25289,25290],{"id":6153},[941,25291,3926],{},[2217,25293,25295],{"className":2219,"code":25294,"language":2221,"meta":1037,"style":1037},"describe(): CollectionDescription\ndescribe(opts: DescribeOptions): Promise\u003CCollectionDescription>\n",[941,25296,25297,25304],{"__ignoreMap":1037},[2225,25298,25299,25301],{"class":2227,"line":2228},[2225,25300,6153],{"class":2291},[2225,25302,25303],{"class":2239},"(): CollectionDescription\n",[2225,25305,25306,25308,25311,25313,25315,25318],{"class":2227,"line":1041},[2225,25307,6153],{"class":2291},[2225,25309,25310],{"class":2239},"(opts: DescribeOptions): ",[2225,25312,11740],{"class":6342},[2225,25314,6474],{"class":2235},[2225,25316,25317],{"class":2239},"CollectionDescription",[2225,25319,7093],{"class":2235},[937,25321,25322,25323,1261,25325,1261,25327,1261,25329,1261,25331,25334],{},"Describe the collection's field schema from in-memory config, with zero store I\u002FO. The synchronous overload merges ",[941,25324,22765],{},[941,25326,22759],{},[941,25328,22753],{},[941,25330,637],{},[941,25332,25333],{},"fieldMeta"," (types inferred from config); the async overload additionally derives validator-exact types and resolves dynamic dict labels.",[1111,25336,25337],{},[1114,25338,25339,964,25341,1324,25344,25347],{},[941,25340,8097],{},[1092,25342,25343],{},"(async overload only)",[941,25345,25346],{},"DescribeOptions"," toggling validator-exact type derivation and dynamic-dict label resolution.",[1591,25349,25351],{"id":25350},"getschema",[941,25352,25353],{},"getSchema()",[2217,25355,25357],{"className":2219,"code":25356,"language":2221,"meta":1037,"style":1037},"getSchema(): StandardSchemaV1\u003Cunknown, T> | undefined\n",[941,25358,25359],{"__ignoreMap":1037},[2225,25360,25361,25364,25367,25369,25371,25373,25375,25377,25379],{"class":2227,"line":2228},[2225,25362,25363],{"class":2291},"getSchema",[2225,25365,25366],{"class":2239},"(): StandardSchemaV1",[2225,25368,6474],{"class":2235},[2225,25370,22421],{"class":2239},[2225,25372,2298],{"class":2235},[2225,25374,24772],{"class":2239},[2225,25376,6480],{"class":2235},[2225,25378,7087],{"class":2235},[2225,25380,23027],{"class":2235},[937,25382,25383,25384,25386],{},"Return the Standard Schema validator attached at construction, or ",[941,25385,18316],{}," if none was provided. Returned by reference — treat it as immutable.",[1591,25388,25389],{"id":23006},[941,25390,23009],{},[2217,25392,25394],{"className":2219,"code":25393,"language":2221,"meta":1037,"style":1037},"getMeta(): CollectionMeta | undefined\n",[941,25395,25396],{"__ignoreMap":1037},[2225,25397,25398,25400,25403,25405],{"class":2227,"line":2228},[2225,25399,23019],{"class":2291},[2225,25401,25402],{"class":2239},"(): CollectionMeta ",[2225,25404,18307],{"class":2235},[2225,25406,23027],{"class":2235},[937,25408,25409,25410,25412],{},"Return the collection's declared descriptive metadata (label \u002F description \u002F icon), or ",[941,25411,18316],{}," if none was set.",[1591,25414,25416],{"id":25415},"getfieldmeta",[941,25417,25418],{},"getFieldMeta()",[2217,25420,25422],{"className":2219,"code":25421,"language":2221,"meta":1037,"style":1037},"getFieldMeta(): Record\u003Cstring, FieldMeta> | undefined\n",[941,25423,25424],{"__ignoreMap":1037},[2225,25425,25426,25429,25432,25434,25436,25438,25441,25443,25445],{"class":2227,"line":2228},[2225,25427,25428],{"class":2291},"getFieldMeta",[2225,25430,25431],{"class":2239},"(): Record",[2225,25433,6474],{"class":2235},[2225,25435,7259],{"class":2239},[2225,25437,2298],{"class":2235},[2225,25439,25440],{"class":2239}," FieldMeta",[2225,25442,6480],{"class":2235},[2225,25444,7087],{"class":2235},[2225,25446,23027],{"class":2235},[937,25448,25449,25450,25412],{},"Return the declared consumer-neutral per-field metadata channel, or ",[941,25451,18316],{},[1591,25453,25455],{"id":25454},"getconfig",[941,25456,25457],{},"getConfig()",[2217,25459,25461],{"className":2219,"code":25460,"language":2221,"meta":1037,"style":1037},"getConfig(): CollectionConfig | undefined\n",[941,25462,25463],{"__ignoreMap":1037},[2225,25464,25465,25468,25471,25473],{"class":2227,"line":2228},[2225,25466,25467],{"class":2291},"getConfig",[2225,25469,25470],{"class":2239},"(): CollectionConfig ",[2225,25472,18307],{"class":2235},[2225,25474,23027],{"class":2235},[937,25476,25477,25478,25480],{},"Return a snapshot of every actively-set collection-level configuration option, or ",[941,25479,18316],{}," when nothing is configured.",[1591,25482,25484],{"id":25483},"tojsonschema",[941,25485,25486],{},"toJSONSchema()",[2217,25488,25490],{"className":2219,"code":25489,"language":2221,"meta":1037,"style":1037},"toJSONSchema(): Promise\u003Cobject>\n",[941,25491,25492],{"__ignoreMap":1037},[2225,25493,25494,25497,25499,25501,25503,25506],{"class":2227,"line":2228},[2225,25495,25496],{"class":2291},"toJSONSchema",[2225,25498,12809],{"class":2239},[2225,25500,11740],{"class":6342},[2225,25502,6474],{"class":2235},[2225,25504,25505],{"class":2239},"object",[2225,25507,7093],{"class":2235},[937,25509,25510,25511,25513,25514,25517],{},"Return a JSON Schema for this collection, with ",[941,25512,3926],{}," metadata attached as ",[941,25515,25516],{},"x-"," extensions.",[1591,25519,25521],{"id":25520},"cachestats",[941,25522,25523],{},"cacheStats()",[2217,25525,25527],{"className":2219,"code":25526,"language":2221,"meta":1037,"style":1037},"cacheStats(): CacheStats\n",[941,25528,25529],{"__ignoreMap":1037},[2225,25530,25531,25534],{"class":2227,"line":2228},[2225,25532,25533],{"class":2291},"cacheStats",[2225,25535,25536],{"class":2239},"(): CacheStats\n",[937,25538,25539,25540,957],{},"Cache statistics for devtools and monitoring. Eager mode returns size only (every read is a cache hit by construction); lazy mode returns the full LRU stats (",[941,25541,25542],{},"{ hits, misses, evictions, size, bytes }",[1022,25544,22351],{"id":23511},[937,25546,23514,25547,23517,25549,23520,25551,23523,25553,23526,25555,25557,25558,23532],{},[941,25548,3121],{},[950,25550,1047],{},[941,25552,943],{},[941,25554,12780],{},[941,25556,21121],{},") until its strategy is present. Browse the ",[1007,25559,4272],{"href":22805},[1111,25561,25562,25587,25610,25631,25640,25650,25671,25693],{},[1114,25563,25564,964,25569,968,25572,968,25575,968,25578,968,25581,968,25584,1520],{},[950,25565,25566],{},[1007,25567,25568],{"href":21867},"History",[941,25570,25571],{},"history(id)",[941,25573,25574],{},"getVersion(id, v)",[941,25576,25577],{},"revert(id, v)",[941,25579,25580],{},"diff(id, a, b?)",[941,25582,25583],{},"pruneRecordHistory(...)",[941,25585,25586],{},"clearHistory(...)",[1114,25588,25589,964,25594,968,25597,968,25600,968,25603,968,25606,968,25608,1520],{},[950,25590,25591],{},[1007,25592,25593],{"href":22050},"Search & retrieval",[941,25595,25596],{},"search(field, query)",[941,25598,25599],{},"retrieve(query)",[941,25601,25602],{},"similarTo(vector)",[941,25604,25605],{},"rebuildEmbeddings()",[941,25607,22041],{},[941,25609,22044],{},[1114,25611,25612,964,25616,968,25619,968,25622,968,25625,968,25628,1520],{},[950,25613,25614],{},[1007,25615,22002],{"href":14740},[941,25617,25618],{},"reveal(id, field)",[941,25620,25621],{},"verify(id, field, candidate)",[941,25623,25624],{},"verifyGroup(...)",[941,25626,25627],{},"findByDigest(field, candidate)",[941,25629,25630],{},"scrubEquatableTags(field)",[1114,25632,25633,964,25637,1520],{},[950,25634,25635],{},[1007,25636,15714],{"href":15126},[941,25638,25639],{},"blob(id)",[1114,25641,25642,964,25647,1520],{},[950,25643,25644],{},[1007,25645,25646],{"href":15100},"Presence",[941,25648,25649],{},"presence(opts?)",[1114,25651,25652,964,25657,968,25660,968,25663,968,25665,968,25668,1520],{},[950,25653,25654],{},[1007,25655,25656],{"href":14616},"Tiers",[941,25658,25659],{},"putAtTier(...)",[941,25661,25662],{},"getAtTier(id)",[941,25664,21987],{},[941,25666,25667],{},"elevate(id, tier)",[941,25669,25670],{},"demote(id, tier)",[1114,25672,25673,964,25678,25680,25681,1884,25684,968,25687,968,25690,1520],{},[950,25674,25675],{},[1007,25676,25677],{"href":21916},"Indexing & lazy mode",[941,25679,24807],{}," (see also ",[1007,25682,25683],{"href":22071},"lazy mode",[941,25685,25686],{},"rebuildIndexes()",[941,25688,25689],{},"reconcileIndex(field)",[941,25691,25692],{},"getIndexes()",[1114,25694,25695,964,25700,1520],{},[950,25696,25697],{},[1007,25698,25699],{"href":21966},"CRDT",[941,25701,25702],{},"getRaw(id)",[2645,25704,25705],{},"html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sHdIc, html code.shiki .sHdIc{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#EEFFFF;--shiki-default-font-style:italic;--shiki-dark:#BABED8;--shiki-dark-font-style:italic}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}",{"title":1037,"searchDepth":1038,"depth":1038,"links":25707},[25708,25717,25722,25728,25731,25735,25744],{"id":23966,"depth":1041,"text":23967,"children":25709},[25710,25711,25715,25716],{"id":3158,"depth":1038,"text":14639},{"id":3161,"depth":1038,"text":20953,"children":25712},[25713],{"id":24206,"depth":2288,"text":25714},"Inputs put() refuses",{"id":3164,"depth":1038,"text":9467},{"id":3167,"depth":1038,"text":24407},{"id":24456,"depth":1041,"text":24457,"children":25718},[25719,25720,25721],{"id":24463,"depth":1038,"text":24466},{"id":24609,"depth":1038,"text":24612},{"id":24668,"depth":1038,"text":24671},{"id":24708,"depth":1041,"text":24709,"children":25723},[25724,25725,25726,25727],{"id":5286,"depth":1038,"text":24715},{"id":3239,"depth":1038,"text":7906},{"id":3236,"depth":1038,"text":24913},{"id":24938,"depth":1038,"text":24941},{"id":25038,"depth":1041,"text":25039,"children":25729},[25730],{"id":25042,"depth":1038,"text":25045},{"id":25137,"depth":1041,"text":25138,"children":25732},[25733,25734],{"id":25162,"depth":1038,"text":22699},{"id":25227,"depth":1038,"text":25230},{"id":711,"depth":1041,"text":25282,"children":25736},[25737,25738,25739,25740,25741,25742,25743],{"id":6153,"depth":1038,"text":3926},{"id":25350,"depth":1038,"text":25353},{"id":23006,"depth":1038,"text":23009},{"id":25415,"depth":1038,"text":25418},{"id":25454,"depth":1038,"text":25457},{"id":25483,"depth":1038,"text":25486},{"id":25520,"depth":1038,"text":25523},{"id":23511,"depth":1041,"text":22351},"The Collection object reference: get\u002Fput\u002Fdelete\u002Flist, bulk ops, query\u002Fscan\u002Fcount\u002Fpaginate, change subscriptions, deterministic lookup, and introspection.",{},{"title":103,"description":25745},"UoNHsz5Ku6Y6SEjETU5aY0NmHSBYLysgP09heMUhwno",{"id":25750,"title":107,"access":12,"apiToc":929,"body":25751,"category":1042,"description":28243,"extension":1044,"meta":28244,"navigation":1042,"nest":2635,"path":108,"seo":28245,"sinceVersion":1042,"status":1049,"stem":109,"subtitle":1042,"tier":6922,"tocExternal":1042,"__hash__":28246},"docs\u002Fcore\u002F4.collection\u002F2.querying.md",{"type":931,"value":25752,"toc":28226},[25753,25756,25766,25793,25797,25816,26340,26485,26488,26614,26640,26644,26650,26780,26787,26791,26801,26948,26954,26963,27032,27038,27047,27123,27141,27234,27238,27262,27487,27501,27505,27516,27543,27547,27556,27610,27625,27629,27690,27694,27709,27939,27949,28138,28154,28173,28175,28223],[934,25754,107],{"id":25755},"querying",[1061,25757,25758],{},[937,25759,25760,6235,25763],{},[950,25761,25762],{},"Always-on. Filter, order, paginate, stream.",[941,25764,25765],{},"packages\u002Fhub\u002Fsrc\u002Fkernel\u002Fquery\u002F{builder,scan-builder,predicate}.ts",[937,25767,25768,25769,25775,25776,24786,25778,1261,25780,1261,25782,1261,25784,25786,25787,25789,25790,25792],{},"noy-db is an encrypted document store: the storage backend only ever holds ciphertext, so it can never run a query for you. Instead, every query runs ",[950,25770,25771,25772,25774],{},"inside ",[941,25773,1086],{},", after decryption",", against the collection's in-memory cache. This section covers the always-on read path: the chainable ",[941,25777,24715],{},[941,25779,3016],{},[941,25781,3019],{},[941,25783,3022],{},[941,25785,3227],{},") and the streaming ",[941,25788,7906],{}," builder for collections larger than RAM. Joins, aggregates, live query results, and indexed dispatch are opt-in ",[1007,25791,599],{"href":22805},"; everything below ships with every install.",[1022,25794,25796],{"id":25795},"eager-queries","Eager queries",[937,25798,25799,25802,25803,968,25805,968,25807,25809,25810,17244,25813,25815],{},[941,25800,25801],{},"collection.query()"," returns an immutable, chainable builder. Because it reads the already-decrypted in-memory cache, the terminals (",[941,25804,3230],{},[941,25806,3233],{},[941,25808,3236],{},") are ",[950,25811,25812],{},"synchronous",[941,25814,6500],{}," needed:",[2217,25817,25819],{"className":2219,"code":25818,"language":2221,"meta":1037,"style":1037},"import { createNoydb } from '@noy-db\u002Fhub'\n\ntype Invoice = { amount: number; status: string; client: string; date: string }\n\nconst db = await createNoydb({ user: 'alice', secret: 'a-strong-secret' })\nconst vault = await db.openVault('acme')\nconst invoices = vault.collection\u003CInvoice>('invoices')\n\nawait invoices.put('inv-1', { amount: 4200, status: 'paid', client: 'Acme GmbH', date: '2026-05-12' })\nawait invoices.put('inv-2', { amount: 1800, status: 'paid', client: 'Beta AG',   date: '2026-06-02' })\nawait invoices.put('inv-3', { amount: 950,  status: 'open', client: 'Acme GmbH', date: '2026-06-20' })\n\nconst recentPaid = invoices.query()\n  .where('status', '==', 'paid')\n  .where('amount', '>', 1000)\n  .orderBy('date', 'desc')\n  .limit(50)\n  .toArray()\n",[941,25820,25821,25839,25843,25887,25891,25933,25959,25989,25993,26064,26137,26210,26214,26231,26264,26293,26319,26332],{"__ignoreMap":1037},[2225,25822,25823,25825,25827,25829,25831,25833,25835,25837],{"class":2227,"line":2228},[2225,25824,2232],{"class":2231},[2225,25826,2236],{"class":2235},[2225,25828,2744],{"class":2239},[2225,25830,2243],{"class":2235},[2225,25832,2246],{"class":2231},[2225,25834,2249],{"class":2235},[2225,25836,1086],{"class":2252},[2225,25838,2256],{"class":2235},[2225,25840,25841],{"class":2227,"line":1041},[2225,25842,2285],{"emptyLinePlaceholder":2284},[2225,25844,25845,25847,25849,25851,25853,25855,25857,25859,25861,25863,25865,25867,25869,25872,25874,25876,25878,25881,25883,25885],{"class":2227,"line":1038},[2225,25846,6339],{"class":2774},[2225,25848,6343],{"class":6342},[2225,25850,6346],{"class":2235},[2225,25852,2236],{"class":2235},[2225,25854,6351],{"class":2307},[2225,25856,2311],{"class":2235},[2225,25858,6356],{"class":6342},[2225,25860,3769],{"class":2235},[2225,25862,6361],{"class":2307},[2225,25864,2311],{"class":2235},[2225,25866,6366],{"class":6342},[2225,25868,3769],{"class":2235},[2225,25870,25871],{"class":2307}," client",[2225,25873,2311],{"class":2235},[2225,25875,6366],{"class":6342},[2225,25877,3769],{"class":2235},[2225,25879,25880],{"class":2307}," date",[2225,25882,2311],{"class":2235},[2225,25884,6366],{"class":6342},[2225,25886,6369],{"class":2235},[2225,25888,25889],{"class":2227,"line":2288},[2225,25890,2285],{"emptyLinePlaceholder":2284},[2225,25892,25893,25895,25897,25899,25901,25903,25905,25907,25909,25911,25913,25915,25917,25919,25921,25923,25925,25927,25929,25931],{"class":2227,"line":2304},[2225,25894,2775],{"class":2774},[2225,25896,2778],{"class":2239},[2225,25898,2781],{"class":2235},[2225,25900,2784],{"class":2231},[2225,25902,2744],{"class":2291},[2225,25904,2789],{"class":2239},[2225,25906,2792],{"class":2235},[2225,25908,2795],{"class":2307},[2225,25910,2311],{"class":2235},[2225,25912,2249],{"class":2235},[2225,25914,6408],{"class":2252},[2225,25916,2805],{"class":2235},[2225,25918,2298],{"class":2235},[2225,25920,2810],{"class":2307},[2225,25922,2311],{"class":2235},[2225,25924,2249],{"class":2235},[2225,25926,6421],{"class":2252},[2225,25928,2805],{"class":2235},[2225,25930,2243],{"class":2235},[2225,25932,2362],{"class":2239},[2225,25934,25935,25937,25939,25941,25943,25945,25947,25949,25951,25953,25955,25957],{"class":2227,"line":2356},[2225,25936,2775],{"class":2774},[2225,25938,2835],{"class":2239},[2225,25940,2781],{"class":2235},[2225,25942,2784],{"class":2231},[2225,25944,2842],{"class":2239},[2225,25946,1520],{"class":2235},[2225,25948,2847],{"class":2291},[2225,25950,2789],{"class":2239},[2225,25952,2805],{"class":2235},[2225,25954,6450],{"class":2252},[2225,25956,2805],{"class":2235},[2225,25958,2362],{"class":2239},[2225,25960,25961,25963,25965,25967,25969,25971,25973,25975,25977,25979,25981,25983,25985,25987],{"class":2227,"line":2830},[2225,25962,2775],{"class":2774},[2225,25964,6461],{"class":2239},[2225,25966,2781],{"class":2235},[2225,25968,6466],{"class":2239},[2225,25970,1520],{"class":2235},[2225,25972,6471],{"class":2291},[2225,25974,6474],{"class":2235},[2225,25976,6477],{"class":6342},[2225,25978,6480],{"class":2235},[2225,25980,2789],{"class":2239},[2225,25982,2805],{"class":2235},[2225,25984,6487],{"class":2252},[2225,25986,2805],{"class":2235},[2225,25988,2362],{"class":2239},[2225,25990,25991],{"class":2227,"line":2861},[2225,25992,2285],{"emptyLinePlaceholder":2284},[2225,25994,25995,25997,25999,26001,26003,26005,26007,26009,26011,26013,26015,26017,26019,26022,26024,26026,26028,26030,26032,26034,26036,26038,26040,26042,26045,26047,26049,26051,26053,26055,26058,26060,26062],{"class":2227,"line":4246},[2225,25996,6500],{"class":2231},[2225,25998,6503],{"class":2239},[2225,26000,1520],{"class":2235},[2225,26002,3161],{"class":2291},[2225,26004,2789],{"class":2239},[2225,26006,2805],{"class":2235},[2225,26008,6514],{"class":2252},[2225,26010,2805],{"class":2235},[2225,26012,2298],{"class":2235},[2225,26014,2236],{"class":2235},[2225,26016,6351],{"class":2307},[2225,26018,2311],{"class":2235},[2225,26020,26021],{"class":6527}," 4200",[2225,26023,2298],{"class":2235},[2225,26025,6361],{"class":2307},[2225,26027,2311],{"class":2235},[2225,26029,2249],{"class":2235},[2225,26031,6539],{"class":2252},[2225,26033,2805],{"class":2235},[2225,26035,2298],{"class":2235},[2225,26037,25871],{"class":2307},[2225,26039,2311],{"class":2235},[2225,26041,2249],{"class":2235},[2225,26043,26044],{"class":2252},"Acme GmbH",[2225,26046,2805],{"class":2235},[2225,26048,2298],{"class":2235},[2225,26050,25880],{"class":2307},[2225,26052,2311],{"class":2235},[2225,26054,2249],{"class":2235},[2225,26056,26057],{"class":2252},"2026-05-12",[2225,26059,2805],{"class":2235},[2225,26061,2243],{"class":2235},[2225,26063,2362],{"class":2239},[2225,26065,26066,26068,26070,26072,26074,26076,26078,26081,26083,26085,26087,26089,26091,26094,26096,26098,26100,26102,26104,26106,26108,26110,26112,26114,26117,26119,26121,26124,26126,26128,26131,26133,26135],{"class":2227,"line":4253},[2225,26067,6500],{"class":2231},[2225,26069,6503],{"class":2239},[2225,26071,1520],{"class":2235},[2225,26073,3161],{"class":2291},[2225,26075,2789],{"class":2239},[2225,26077,2805],{"class":2235},[2225,26079,26080],{"class":2252},"inv-2",[2225,26082,2805],{"class":2235},[2225,26084,2298],{"class":2235},[2225,26086,2236],{"class":2235},[2225,26088,6351],{"class":2307},[2225,26090,2311],{"class":2235},[2225,26092,26093],{"class":6527}," 1800",[2225,26095,2298],{"class":2235},[2225,26097,6361],{"class":2307},[2225,26099,2311],{"class":2235},[2225,26101,2249],{"class":2235},[2225,26103,6539],{"class":2252},[2225,26105,2805],{"class":2235},[2225,26107,2298],{"class":2235},[2225,26109,25871],{"class":2307},[2225,26111,2311],{"class":2235},[2225,26113,2249],{"class":2235},[2225,26115,26116],{"class":2252},"Beta AG",[2225,26118,2805],{"class":2235},[2225,26120,2298],{"class":2235},[2225,26122,26123],{"class":2307},"   date",[2225,26125,2311],{"class":2235},[2225,26127,2249],{"class":2235},[2225,26129,26130],{"class":2252},"2026-06-02",[2225,26132,2805],{"class":2235},[2225,26134,2243],{"class":2235},[2225,26136,2362],{"class":2239},[2225,26138,26139,26141,26143,26145,26147,26149,26151,26154,26156,26158,26160,26162,26164,26167,26169,26172,26174,26176,26179,26181,26183,26185,26187,26189,26191,26193,26195,26197,26199,26201,26204,26206,26208],{"class":2227,"line":4259},[2225,26140,6500],{"class":2231},[2225,26142,6503],{"class":2239},[2225,26144,1520],{"class":2235},[2225,26146,3161],{"class":2291},[2225,26148,2789],{"class":2239},[2225,26150,2805],{"class":2235},[2225,26152,26153],{"class":2252},"inv-3",[2225,26155,2805],{"class":2235},[2225,26157,2298],{"class":2235},[2225,26159,2236],{"class":2235},[2225,26161,6351],{"class":2307},[2225,26163,2311],{"class":2235},[2225,26165,26166],{"class":6527}," 950",[2225,26168,2298],{"class":2235},[2225,26170,26171],{"class":2307},"  status",[2225,26173,2311],{"class":2235},[2225,26175,2249],{"class":2235},[2225,26177,26178],{"class":2252},"open",[2225,26180,2805],{"class":2235},[2225,26182,2298],{"class":2235},[2225,26184,25871],{"class":2307},[2225,26186,2311],{"class":2235},[2225,26188,2249],{"class":2235},[2225,26190,26044],{"class":2252},[2225,26192,2805],{"class":2235},[2225,26194,2298],{"class":2235},[2225,26196,25880],{"class":2307},[2225,26198,2311],{"class":2235},[2225,26200,2249],{"class":2235},[2225,26202,26203],{"class":2252},"2026-06-20",[2225,26205,2805],{"class":2235},[2225,26207,2243],{"class":2235},[2225,26209,2362],{"class":2239},[2225,26211,26212],{"class":2227,"line":6548},[2225,26213,2285],{"emptyLinePlaceholder":2284},[2225,26215,26216,26218,26221,26223,26225,26227,26229],{"class":2227,"line":6576},[2225,26217,2775],{"class":2774},[2225,26219,26220],{"class":2239}," recentPaid ",[2225,26222,2781],{"class":2235},[2225,26224,6503],{"class":2239},[2225,26226,1520],{"class":2235},[2225,26228,5286],{"class":2291},[2225,26230,8146],{"class":2239},[2225,26232,26233,26236,26238,26240,26242,26244,26246,26248,26250,26252,26254,26256,26258,26260,26262],{"class":2227,"line":6582},[2225,26234,26235],{"class":2235},"  .",[2225,26237,3016],{"class":2291},[2225,26239,2789],{"class":2239},[2225,26241,2805],{"class":2235},[2225,26243,6631],{"class":2252},[2225,26245,2805],{"class":2235},[2225,26247,2298],{"class":2235},[2225,26249,2249],{"class":2235},[2225,26251,21901],{"class":2252},[2225,26253,2805],{"class":2235},[2225,26255,2298],{"class":2235},[2225,26257,2249],{"class":2235},[2225,26259,6539],{"class":2252},[2225,26261,2805],{"class":2235},[2225,26263,2362],{"class":2239},[2225,26265,26266,26268,26270,26272,26274,26276,26278,26280,26282,26284,26286,26288,26291],{"class":2227,"line":6587},[2225,26267,26235],{"class":2235},[2225,26269,3016],{"class":2291},[2225,26271,2789],{"class":2239},[2225,26273,2805],{"class":2235},[2225,26275,6617],{"class":2252},[2225,26277,2805],{"class":2235},[2225,26279,2298],{"class":2235},[2225,26281,2249],{"class":2235},[2225,26283,6480],{"class":2252},[2225,26285,2805],{"class":2235},[2225,26287,2298],{"class":2235},[2225,26289,26290],{"class":6527}," 1000",[2225,26292,2362],{"class":2239},[2225,26294,26295,26297,26299,26301,26303,26306,26308,26310,26312,26315,26317],{"class":2227,"line":7174},[2225,26296,26235],{"class":2235},[2225,26298,3019],{"class":2291},[2225,26300,2789],{"class":2239},[2225,26302,2805],{"class":2235},[2225,26304,26305],{"class":2252},"date",[2225,26307,2805],{"class":2235},[2225,26309,2298],{"class":2235},[2225,26311,2249],{"class":2235},[2225,26313,26314],{"class":2252},"desc",[2225,26316,2805],{"class":2235},[2225,26318,2362],{"class":2239},[2225,26320,26321,26323,26325,26327,26330],{"class":2227,"line":7180},[2225,26322,26235],{"class":2235},[2225,26324,3022],{"class":2291},[2225,26326,2789],{"class":2239},[2225,26328,26329],{"class":6527},"50",[2225,26331,2362],{"class":2239},[2225,26333,26334,26336,26338],{"class":2227,"line":7220},[2225,26335,26235],{"class":2235},[2225,26337,3230],{"class":2291},[2225,26339,8146],{"class":2239},[2217,26341,26343],{"className":6604,"code":26342,"language":141,"meta":1037,"style":1037},"[\n  { \"amount\": 1800, \"status\": \"paid\", \"client\": \"Beta AG\",   \"date\": \"2026-06-02\" },\n  { \"amount\": 4200, \"status\": \"paid\", \"client\": \"Acme GmbH\", \"date\": \"2026-05-12\" }\n]\n",[941,26344,26345,26350,26416,26480],{"__ignoreMap":1037},[2225,26346,26347],{"class":2227,"line":2228},[2225,26348,26349],{"class":2235},"[\n",[2225,26351,26352,26355,26357,26359,26361,26363,26365,26367,26369,26371,26373,26375,26377,26379,26381,26383,26385,26387,26389,26391,26393,26395,26397,26399,26402,26404,26406,26408,26410,26412,26414],{"class":2227,"line":1041},[2225,26353,26354],{"class":2235},"  {",[2225,26356,6614],{"class":2235},[2225,26358,6617],{"class":2774},[2225,26360,6620],{"class":2235},[2225,26362,2311],{"class":2235},[2225,26364,26093],{"class":6527},[2225,26366,2298],{"class":2235},[2225,26368,6614],{"class":2235},[2225,26370,6631],{"class":2774},[2225,26372,6620],{"class":2235},[2225,26374,2311],{"class":2235},[2225,26376,6614],{"class":2235},[2225,26378,6539],{"class":2252},[2225,26380,6620],{"class":2235},[2225,26382,2298],{"class":2235},[2225,26384,6614],{"class":2235},[2225,26386,9913],{"class":2774},[2225,26388,6620],{"class":2235},[2225,26390,2311],{"class":2235},[2225,26392,6614],{"class":2235},[2225,26394,26116],{"class":2252},[2225,26396,6620],{"class":2235},[2225,26398,2298],{"class":2235},[2225,26400,26401],{"class":2235},"   \"",[2225,26403,26305],{"class":2774},[2225,26405,6620],{"class":2235},[2225,26407,2311],{"class":2235},[2225,26409,6614],{"class":2235},[2225,26411,26130],{"class":2252},[2225,26413,6620],{"class":2235},[2225,26415,2353],{"class":2235},[2225,26417,26418,26420,26422,26424,26426,26428,26430,26432,26434,26436,26438,26440,26442,26444,26446,26448,26450,26452,26454,26456,26458,26460,26462,26464,26466,26468,26470,26472,26474,26476,26478],{"class":2227,"line":1038},[2225,26419,26354],{"class":2235},[2225,26421,6614],{"class":2235},[2225,26423,6617],{"class":2774},[2225,26425,6620],{"class":2235},[2225,26427,2311],{"class":2235},[2225,26429,26021],{"class":6527},[2225,26431,2298],{"class":2235},[2225,26433,6614],{"class":2235},[2225,26435,6631],{"class":2774},[2225,26437,6620],{"class":2235},[2225,26439,2311],{"class":2235},[2225,26441,6614],{"class":2235},[2225,26443,6539],{"class":2252},[2225,26445,6620],{"class":2235},[2225,26447,2298],{"class":2235},[2225,26449,6614],{"class":2235},[2225,26451,9913],{"class":2774},[2225,26453,6620],{"class":2235},[2225,26455,2311],{"class":2235},[2225,26457,6614],{"class":2235},[2225,26459,26044],{"class":2252},[2225,26461,6620],{"class":2235},[2225,26463,2298],{"class":2235},[2225,26465,6614],{"class":2235},[2225,26467,26305],{"class":2774},[2225,26469,6620],{"class":2235},[2225,26471,2311],{"class":2235},[2225,26473,6614],{"class":2235},[2225,26475,26057],{"class":2252},[2225,26477,6620],{"class":2235},[2225,26479,6369],{"class":2235},[2225,26481,26482],{"class":2227,"line":2288},[2225,26483,26484],{"class":2235},"]\n",[937,26486,26487],{},"Two more terminals:",[2217,26489,26491],{"className":2219,"code":26490,"language":2221,"meta":1037,"style":1037},"const firstOpen = invoices.query().where('status', '==', 'open').first()\n\u002F\u002F → { amount: 950, status: 'open', client: 'Acme GmbH', date: '2026-06-20' }\n\u002F\u002F (or null when nothing matches)\n\nconst bigOnes = invoices.query().where('amount', '>', 1000).count()\n\u002F\u002F → 2   (counted after where\u002Ffilter, BEFORE limit\u002Foffset)\n",[941,26492,26493,26546,26551,26556,26560,26609],{"__ignoreMap":1037},[2225,26494,26495,26497,26500,26502,26504,26506,26508,26510,26512,26514,26516,26518,26520,26522,26524,26526,26528,26530,26532,26534,26536,26538,26540,26542,26544],{"class":2227,"line":2228},[2225,26496,2775],{"class":2774},[2225,26498,26499],{"class":2239}," firstOpen ",[2225,26501,2781],{"class":2235},[2225,26503,6503],{"class":2239},[2225,26505,1520],{"class":2235},[2225,26507,5286],{"class":2291},[2225,26509,4228],{"class":2239},[2225,26511,1520],{"class":2235},[2225,26513,3016],{"class":2291},[2225,26515,2789],{"class":2239},[2225,26517,2805],{"class":2235},[2225,26519,6631],{"class":2252},[2225,26521,2805],{"class":2235},[2225,26523,2298],{"class":2235},[2225,26525,2249],{"class":2235},[2225,26527,21901],{"class":2252},[2225,26529,2805],{"class":2235},[2225,26531,2298],{"class":2235},[2225,26533,2249],{"class":2235},[2225,26535,26178],{"class":2252},[2225,26537,2805],{"class":2235},[2225,26539,1381],{"class":2239},[2225,26541,1520],{"class":2235},[2225,26543,3233],{"class":2291},[2225,26545,8146],{"class":2239},[2225,26547,26548],{"class":2227,"line":1041},[2225,26549,26550],{"class":2763},"\u002F\u002F → { amount: 950, status: 'open', client: 'Acme GmbH', date: '2026-06-20' }\n",[2225,26552,26553],{"class":2227,"line":1038},[2225,26554,26555],{"class":2763},"\u002F\u002F (or null when nothing matches)\n",[2225,26557,26558],{"class":2227,"line":2288},[2225,26559,2285],{"emptyLinePlaceholder":2284},[2225,26561,26562,26564,26567,26569,26571,26573,26575,26577,26579,26581,26583,26585,26587,26589,26591,26593,26595,26597,26599,26601,26603,26605,26607],{"class":2227,"line":2304},[2225,26563,2775],{"class":2774},[2225,26565,26566],{"class":2239}," bigOnes ",[2225,26568,2781],{"class":2235},[2225,26570,6503],{"class":2239},[2225,26572,1520],{"class":2235},[2225,26574,5286],{"class":2291},[2225,26576,4228],{"class":2239},[2225,26578,1520],{"class":2235},[2225,26580,3016],{"class":2291},[2225,26582,2789],{"class":2239},[2225,26584,2805],{"class":2235},[2225,26586,6617],{"class":2252},[2225,26588,2805],{"class":2235},[2225,26590,2298],{"class":2235},[2225,26592,2249],{"class":2235},[2225,26594,6480],{"class":2252},[2225,26596,2805],{"class":2235},[2225,26598,2298],{"class":2235},[2225,26600,26290],{"class":6527},[2225,26602,1381],{"class":2239},[2225,26604,1520],{"class":2235},[2225,26606,3236],{"class":2291},[2225,26608,8146],{"class":2239},[2225,26610,26611],{"class":2227,"line":2356},[2225,26612,26613],{"class":2763},"\u002F\u002F → 2   (counted after where\u002Ffilter, BEFORE limit\u002Foffset)\n",[937,26615,26616,26617,26619,26620,26623,26624,26627,26628,26631,26632,26635,26636,26639],{},"Multiple ",[941,26618,24883],{}," calls are AND-combined. ",[941,26621,26622],{},".orderBy(field, direction?)"," defaults to ",[941,26625,26626],{},"'asc'","; repeated calls act as tie-breakers. ",[941,26629,26630],{},".offset(n)"," skips the first ",[1092,26633,26634],{},"n"," matches after ordering — combine with ",[941,26637,26638],{},".limit(n)"," for pagination.",[1591,26641,26643],{"id":26642},"operators","Operators",[937,26645,26646,26649],{},[941,26647,26648],{},".where(field, op, value)"," accepts these operators:",[1338,26651,26652,26662],{},[1341,26653,26654],{},[1344,26655,26656,26659],{},[1347,26657,26658],{},"Op",[1347,26660,26661],{},"Matches when",[1357,26663,26664,26676,26688,26714,26727,26748,26763],{},[1344,26665,26666,26671],{},[1362,26667,26668],{},[941,26669,26670],{},"'=='",[1362,26672,26673,26674],{},"field is strictly equal to ",[941,26675,25201],{},[1344,26677,26678,26683],{},[1362,26679,26680],{},[941,26681,26682],{},"'!='",[1362,26684,26685,26686],{},"field is strictly not equal to ",[941,26687,25201],{},[1344,26689,26690,26704],{},[1362,26691,26692,1324,26695,1324,26698,1324,26701],{},[941,26693,26694],{},"'\u003C'",[941,26696,26697],{},"'\u003C='",[941,26699,26700],{},"'>'",[941,26702,26703],{},"'>='",[1362,26705,26706,26707,26709,26710,26713],{},"field compares against ",[941,26708,25201],{}," — numbers and ",[941,26711,26712],{},"Date","s numerically, strings lexicographically",[1344,26715,26716,26721],{},[1362,26717,26718],{},[941,26719,26720],{},"'in'",[1362,26722,26723,26724],{},"field equals one element of the supplied array — ",[941,26725,26726],{},".where('status', 'in', ['paid', 'open'])",[1344,26728,26729,26734],{},[1362,26730,26731],{},[941,26732,26733],{},"'contains'",[1362,26735,5983,26736,26738,26739,26741,26742,26738,26745,26747],{},[950,26737,7259],{}," field contains ",[941,26740,25201],{}," as a substring, or an ",[950,26743,26744],{},"array",[941,26746,25201],{}," as an element",[1344,26749,26750,26755],{},[1362,26751,26752],{},[941,26753,26754],{},"'startsWith'",[1362,26756,26757,26758,964,26760],{},"a string field starts with ",[941,26759,25201],{},[941,26761,26762],{},".where('client', 'startsWith', 'Ac')",[1344,26764,26765,26770],{},[1362,26766,26767],{},[941,26768,26769],{},"'between'",[1362,26771,26772,26773,26776,26777],{},"field is inside the inclusive ",[941,26774,26775],{},"[lo, hi]"," range — ",[941,26778,26779],{},".where('amount', 'between', [1000, 5000])",[937,26781,26782,26783,26786],{},"Field names may be nested dot-paths (",[941,26784,26785],{},"'address.city'","). Type mismatches never throw — a record whose field has the wrong runtime type simply does not match.",[1591,26788,26790],{"id":26789},"or-and-groups","OR \u002F AND groups",[937,26792,26793,26796,26797,26800],{},[941,26794,26795],{},".or(callback)"," builds a group whose clauses are OR-combined; the group itself joins the rest of the plan with AND. ",[941,26798,26799],{},".and(callback)"," is the explicit AND counterpart, useful for grouping inside a larger OR:",[2217,26802,26804],{"className":2219,"code":26803,"language":2221,"meta":1037,"style":1037},"\u002F\u002F status == 'paid' AND (amount > 3000 OR date >= '2026-06-01')\nconst n = invoices.query()\n  .where('status', '==', 'paid')\n  .or(q => q.where('amount', '>', 3000).where('date', '>=', '2026-06-01'))\n  .count()\n\u002F\u002F → 2\n",[941,26805,26806,26811,26828,26860,26935,26943],{"__ignoreMap":1037},[2225,26807,26808],{"class":2227,"line":2228},[2225,26809,26810],{"class":2763},"\u002F\u002F status == 'paid' AND (amount > 3000 OR date >= '2026-06-01')\n",[2225,26812,26813,26815,26818,26820,26822,26824,26826],{"class":2227,"line":1041},[2225,26814,2775],{"class":2774},[2225,26816,26817],{"class":2239}," n ",[2225,26819,2781],{"class":2235},[2225,26821,6503],{"class":2239},[2225,26823,1520],{"class":2235},[2225,26825,5286],{"class":2291},[2225,26827,8146],{"class":2239},[2225,26829,26830,26832,26834,26836,26838,26840,26842,26844,26846,26848,26850,26852,26854,26856,26858],{"class":2227,"line":1038},[2225,26831,26235],{"class":2235},[2225,26833,3016],{"class":2291},[2225,26835,2789],{"class":2239},[2225,26837,2805],{"class":2235},[2225,26839,6631],{"class":2252},[2225,26841,2805],{"class":2235},[2225,26843,2298],{"class":2235},[2225,26845,2249],{"class":2235},[2225,26847,21901],{"class":2252},[2225,26849,2805],{"class":2235},[2225,26851,2298],{"class":2235},[2225,26853,2249],{"class":2235},[2225,26855,6539],{"class":2252},[2225,26857,2805],{"class":2235},[2225,26859,2362],{"class":2239},[2225,26861,26862,26864,26866,26868,26871,26873,26876,26878,26880,26882,26884,26886,26888,26890,26892,26894,26896,26898,26901,26903,26905,26907,26909,26911,26913,26915,26917,26919,26922,26924,26926,26928,26931,26933],{"class":2227,"line":2288},[2225,26863,26235],{"class":2235},[2225,26865,7493],{"class":2291},[2225,26867,2789],{"class":2239},[2225,26869,26870],{"class":7052},"q",[2225,26872,8116],{"class":2774},[2225,26874,26875],{"class":2239}," q",[2225,26877,1520],{"class":2235},[2225,26879,3016],{"class":2291},[2225,26881,2789],{"class":2239},[2225,26883,2805],{"class":2235},[2225,26885,6617],{"class":2252},[2225,26887,2805],{"class":2235},[2225,26889,2298],{"class":2235},[2225,26891,2249],{"class":2235},[2225,26893,6480],{"class":2252},[2225,26895,2805],{"class":2235},[2225,26897,2298],{"class":2235},[2225,26899,26900],{"class":6527}," 3000",[2225,26902,1381],{"class":2239},[2225,26904,1520],{"class":2235},[2225,26906,3016],{"class":2291},[2225,26908,2789],{"class":2239},[2225,26910,2805],{"class":2235},[2225,26912,26305],{"class":2252},[2225,26914,2805],{"class":2235},[2225,26916,2298],{"class":2235},[2225,26918,2249],{"class":2235},[2225,26920,26921],{"class":2252},">=",[2225,26923,2805],{"class":2235},[2225,26925,2298],{"class":2235},[2225,26927,2249],{"class":2235},[2225,26929,26930],{"class":2252},"2026-06-01",[2225,26932,2805],{"class":2235},[2225,26934,8606],{"class":2239},[2225,26936,26937,26939,26941],{"class":2227,"line":2304},[2225,26938,26235],{"class":2235},[2225,26940,3236],{"class":2291},[2225,26942,8146],{"class":2239},[2225,26944,26945],{"class":2227,"line":2356},[2225,26946,26947],{"class":2763},"\u002F\u002F → 2\n",[1591,26949,26951,26952],{"id":26950},"escape-hatch-filter","Escape hatch: ",[941,26953,24728],{},[937,26955,26956,26959,26960,26962],{},[941,26957,26958],{},".filter(fn)"," accepts a free-form predicate for conditions the operator set cannot express. It runs on every candidate record and can never drive an index, so prefer ",[941,26961,24883],{}," when either works:",[2217,26964,26966],{"className":2219,"code":26965,"language":2221,"meta":1037,"style":1037},"const juneCount = invoices.query()\n  .filter(inv => inv.date.startsWith('2026-06'))\n  .count()\n\u002F\u002F → 2\n",[941,26967,26968,26985,27020,27028],{"__ignoreMap":1037},[2225,26969,26970,26972,26975,26977,26979,26981,26983],{"class":2227,"line":2228},[2225,26971,2775],{"class":2774},[2225,26973,26974],{"class":2239}," juneCount ",[2225,26976,2781],{"class":2235},[2225,26978,6503],{"class":2239},[2225,26980,1520],{"class":2235},[2225,26982,5286],{"class":2291},[2225,26984,8146],{"class":2239},[2225,26986,26987,26989,26991,26993,26996,26998,27001,27003,27005,27007,27009,27011,27013,27016,27018],{"class":2227,"line":1041},[2225,26988,26235],{"class":2235},[2225,26990,8686],{"class":2291},[2225,26992,2789],{"class":2239},[2225,26994,26995],{"class":7052},"inv",[2225,26997,8116],{"class":2774},[2225,26999,27000],{"class":2239}," inv",[2225,27002,1520],{"class":2235},[2225,27004,26305],{"class":2239},[2225,27006,1520],{"class":2235},[2225,27008,8705],{"class":2291},[2225,27010,2789],{"class":2239},[2225,27012,2805],{"class":2235},[2225,27014,27015],{"class":2252},"2026-06",[2225,27017,2805],{"class":2235},[2225,27019,8606],{"class":2239},[2225,27021,27022,27024,27026],{"class":2227,"line":1038},[2225,27023,26235],{"class":2235},[2225,27025,3236],{"class":2291},[2225,27027,8146],{"class":2239},[2225,27029,27030],{"class":2227,"line":2288},[2225,27031,26947],{"class":2763},[1022,27033,27035,27036],{"id":27034},"streaming-with-scan","Streaming with ",[941,27037,7906],{},[937,27039,27040,27041,27043,27044,27046],{},"Eager queries materialize a snapshot of the whole collection. ",[941,27042,7906],{}," instead streams records page-by-page from the store, decrypting at most ",[941,27045,24875],{}," records (default 100) at a time — the way to process collections larger than RAM:",[2217,27048,27050],{"className":2219,"code":27049,"language":2221,"meta":1037,"style":1037},"for await (const inv of invoices.scan({ pageSize: 500 })) {\n  console.log(inv.client, inv.amount)\n}\n",[941,27051,27052,27091,27119],{"__ignoreMap":1037},[2225,27053,27054,27056,27058,27060,27062,27064,27067,27069,27071,27073,27075,27077,27080,27082,27085,27087,27089],{"class":2227,"line":2228},[2225,27055,4646],{"class":2231},[2225,27057,2784],{"class":2231},[2225,27059,953],{"class":2239},[2225,27061,2775],{"class":2774},[2225,27063,6553],{"class":2239},[2225,27065,27066],{"class":2235},"of",[2225,27068,6503],{"class":2239},[2225,27070,1520],{"class":2235},[2225,27072,3239],{"class":2291},[2225,27074,2789],{"class":2239},[2225,27076,2792],{"class":2235},[2225,27078,27079],{"class":2307}," pageSize",[2225,27081,2311],{"class":2235},[2225,27083,27084],{"class":6527}," 500",[2225,27086,2243],{"class":2235},[2225,27088,7620],{"class":2239},[2225,27090,4200],{"class":2235},[2225,27092,27093,27096,27098,27101,27103,27105,27107,27109,27111,27113,27115,27117],{"class":2227,"line":1041},[2225,27094,27095],{"class":2239},"  console",[2225,27097,1520],{"class":2235},[2225,27099,27100],{"class":2291},"log",[2225,27102,2789],{"class":2307},[2225,27104,26995],{"class":2239},[2225,27106,1520],{"class":2235},[2225,27108,9913],{"class":2239},[2225,27110,2298],{"class":2235},[2225,27112,27000],{"class":2239},[2225,27114,1520],{"class":2235},[2225,27116,6617],{"class":2239},[2225,27118,2362],{"class":2307},[2225,27120,27121],{"class":2227,"line":1038},[2225,27122,7351],{"class":2235},[937,27124,27125,22207,27127,27129,27130,27132,27133,1261,27135,27137,27138,27140],{},[941,27126,7906],{},[941,27128,24879],{}," that implements ",[941,27131,24871],{}," and also chains ",[941,27134,24883],{},[941,27136,24728],{}," with the same semantics as ",[941,27139,24715],{}," — non-matching records are dropped from the stream:",[2217,27142,27144],{"className":2219,"code":27143,"language":2221,"meta":1037,"style":1037},"for await (const inv of invoices.scan().where('status', '==', 'paid')) {\n  console.log(inv.client, inv.amount)  \u002F\u002F → \"Acme GmbH\" 4200, \"Beta AG\" 1800\n}\n",[941,27145,27146,27200,27230],{"__ignoreMap":1037},[2225,27147,27148,27150,27152,27154,27156,27158,27160,27162,27164,27166,27168,27170,27172,27174,27176,27178,27180,27182,27184,27186,27188,27190,27192,27194,27196,27198],{"class":2227,"line":2228},[2225,27149,4646],{"class":2231},[2225,27151,2784],{"class":2231},[2225,27153,953],{"class":2239},[2225,27155,2775],{"class":2774},[2225,27157,6553],{"class":2239},[2225,27159,27066],{"class":2235},[2225,27161,6503],{"class":2239},[2225,27163,1520],{"class":2235},[2225,27165,3239],{"class":2291},[2225,27167,4228],{"class":2239},[2225,27169,1520],{"class":2235},[2225,27171,3016],{"class":2291},[2225,27173,2789],{"class":2239},[2225,27175,2805],{"class":2235},[2225,27177,6631],{"class":2252},[2225,27179,2805],{"class":2235},[2225,27181,2298],{"class":2235},[2225,27183,2249],{"class":2235},[2225,27185,21901],{"class":2252},[2225,27187,2805],{"class":2235},[2225,27189,2298],{"class":2235},[2225,27191,2249],{"class":2235},[2225,27193,6539],{"class":2252},[2225,27195,2805],{"class":2235},[2225,27197,7620],{"class":2239},[2225,27199,4200],{"class":2235},[2225,27201,27202,27204,27206,27208,27210,27212,27214,27216,27218,27220,27222,27224,27227],{"class":2227,"line":1041},[2225,27203,27095],{"class":2239},[2225,27205,1520],{"class":2235},[2225,27207,27100],{"class":2291},[2225,27209,2789],{"class":2307},[2225,27211,26995],{"class":2239},[2225,27213,1520],{"class":2235},[2225,27215,9913],{"class":2239},[2225,27217,2298],{"class":2235},[2225,27219,27000],{"class":2239},[2225,27221,1520],{"class":2235},[2225,27223,6617],{"class":2239},[2225,27225,27226],{"class":2307},")  ",[2225,27228,27229],{"class":2763},"\u002F\u002F → \"Acme GmbH\" 4200, \"Beta AG\" 1800\n",[2225,27231,27232],{"class":2227,"line":1038},[2225,27233,7351],{"class":2235},[1591,27235,27237],{"id":27236},"streaming-aggregation","Streaming aggregation",[937,27239,27240,27242,27243,27245,27246,27248,27249,27251,27252,27254,27255,27258,27259,27261],{},[941,27241,7906],{}," has an async ",[941,27244,24892],{}," terminal that folds the stream through named reducers — one record at a time, so memory stays proportional to the number of reducers, never the number of records. It requires the ",[1007,27247,24896],{"href":21940},": wire ",[941,27250,21926],{}," into ",[941,27253,12780],{}," and import the reducer factories from the ",[941,27256,27257],{},"@noy-db\u002Fhub\u002Freduce"," subpath (without it, ",[941,27260,21931],{}," throws with a pointer to that subpath):",[2217,27263,27265],{"className":2219,"code":27264,"language":2221,"meta":1037,"style":1037},"import { createNoydb } from '@noy-db\u002Fhub'\nimport { withReduce, sum, count } from '@noy-db\u002Fhub\u002Freduce'\n\nconst db = await createNoydb({\n  user: 'alice',\n  secret: 'a-strong-secret',\n  reduceStrategy: withReduce(),\n})\n\u002F\u002F … open vault + collection, seed the three invoices as above …\n\nconst totals = await invoices.scan()\n  .where('status', '==', 'paid')\n  .aggregate({ total: sum('amount'), n: count() })\n\u002F\u002F → { total: 6000, n: 2 }\n",[941,27266,27267,27285,27313,27317,27333,27347,27361,27374,27380,27385,27389,27408,27440,27482],{"__ignoreMap":1037},[2225,27268,27269,27271,27273,27275,27277,27279,27281,27283],{"class":2227,"line":2228},[2225,27270,2232],{"class":2231},[2225,27272,2236],{"class":2235},[2225,27274,2744],{"class":2239},[2225,27276,2243],{"class":2235},[2225,27278,2246],{"class":2231},[2225,27280,2249],{"class":2235},[2225,27282,1086],{"class":2252},[2225,27284,2256],{"class":2235},[2225,27286,27287,27289,27291,27294,27296,27299,27301,27303,27305,27307,27309,27311],{"class":2227,"line":1041},[2225,27288,2232],{"class":2231},[2225,27290,2236],{"class":2235},[2225,27292,27293],{"class":2239}," withReduce",[2225,27295,2298],{"class":2235},[2225,27297,27298],{"class":2239}," sum",[2225,27300,2298],{"class":2235},[2225,27302,19270],{"class":2239},[2225,27304,2243],{"class":2235},[2225,27306,2246],{"class":2231},[2225,27308,2249],{"class":2235},[2225,27310,27257],{"class":2252},[2225,27312,2256],{"class":2235},[2225,27314,27315],{"class":2227,"line":1038},[2225,27316,2285],{"emptyLinePlaceholder":2284},[2225,27318,27319,27321,27323,27325,27327,27329,27331],{"class":2227,"line":2288},[2225,27320,2775],{"class":2774},[2225,27322,2778],{"class":2239},[2225,27324,2781],{"class":2235},[2225,27326,2784],{"class":2231},[2225,27328,2744],{"class":2291},[2225,27330,2789],{"class":2239},[2225,27332,4200],{"class":2235},[2225,27334,27335,27337,27339,27341,27343,27345],{"class":2227,"line":2304},[2225,27336,4205],{"class":2307},[2225,27338,2311],{"class":2235},[2225,27340,2249],{"class":2235},[2225,27342,6408],{"class":2252},[2225,27344,2805],{"class":2235},[2225,27346,1707],{"class":2235},[2225,27348,27349,27351,27353,27355,27357,27359],{"class":2227,"line":2356},[2225,27350,10423],{"class":2307},[2225,27352,2311],{"class":2235},[2225,27354,2249],{"class":2235},[2225,27356,6421],{"class":2252},[2225,27358,2805],{"class":2235},[2225,27360,1707],{"class":2235},[2225,27362,27363,27366,27368,27370,27372],{"class":2227,"line":2830},[2225,27364,27365],{"class":2307},"  reduceStrategy",[2225,27367,2311],{"class":2235},[2225,27369,27293],{"class":2291},[2225,27371,4228],{"class":2239},[2225,27373,1707],{"class":2235},[2225,27375,27376,27378],{"class":2227,"line":2861},[2225,27377,2359],{"class":2235},[2225,27379,2362],{"class":2239},[2225,27381,27382],{"class":2227,"line":4246},[2225,27383,27384],{"class":2763},"\u002F\u002F … open vault + collection, seed the three invoices as above …\n",[2225,27386,27387],{"class":2227,"line":4253},[2225,27388,2285],{"emptyLinePlaceholder":2284},[2225,27390,27391,27393,27396,27398,27400,27402,27404,27406],{"class":2227,"line":4259},[2225,27392,2775],{"class":2774},[2225,27394,27395],{"class":2239}," totals ",[2225,27397,2781],{"class":2235},[2225,27399,2784],{"class":2231},[2225,27401,6503],{"class":2239},[2225,27403,1520],{"class":2235},[2225,27405,3239],{"class":2291},[2225,27407,8146],{"class":2239},[2225,27409,27410,27412,27414,27416,27418,27420,27422,27424,27426,27428,27430,27432,27434,27436,27438],{"class":2227,"line":6548},[2225,27411,26235],{"class":2235},[2225,27413,3016],{"class":2291},[2225,27415,2789],{"class":2239},[2225,27417,2805],{"class":2235},[2225,27419,6631],{"class":2252},[2225,27421,2805],{"class":2235},[2225,27423,2298],{"class":2235},[2225,27425,2249],{"class":2235},[2225,27427,21901],{"class":2252},[2225,27429,2805],{"class":2235},[2225,27431,2298],{"class":2235},[2225,27433,2249],{"class":2235},[2225,27435,6539],{"class":2252},[2225,27437,2805],{"class":2235},[2225,27439,2362],{"class":2239},[2225,27441,27442,27444,27446,27448,27450,27453,27455,27457,27459,27461,27463,27465,27467,27469,27472,27474,27476,27478,27480],{"class":2227,"line":6576},[2225,27443,26235],{"class":2235},[2225,27445,4449],{"class":2291},[2225,27447,2789],{"class":2239},[2225,27449,2792],{"class":2235},[2225,27451,27452],{"class":2307}," total",[2225,27454,2311],{"class":2235},[2225,27456,27298],{"class":2291},[2225,27458,2789],{"class":2239},[2225,27460,2805],{"class":2235},[2225,27462,6617],{"class":2252},[2225,27464,2805],{"class":2235},[2225,27466,1381],{"class":2239},[2225,27468,2298],{"class":2235},[2225,27470,27471],{"class":2307}," n",[2225,27473,2311],{"class":2235},[2225,27475,19270],{"class":2291},[2225,27477,19132],{"class":2239},[2225,27479,2359],{"class":2235},[2225,27481,2362],{"class":2239},[2225,27483,27484],{"class":2227,"line":6582},[2225,27485,27486],{"class":2763},"\u002F\u002F → { total: 6000, n: 2 }\n",[937,27488,27489,27490,27493,27494,27497,27498,1520],{},"The eager counterpart — ",[941,27491,27492],{},"query().aggregate(spec).run()"," plus ",[941,27495,27496],{},"groupBy"," — is documented on the ",[1007,27499,27500],{"href":21940},"aggregate service page",[1022,27502,27504],{"id":27503},"lazy-mode","Lazy mode",[937,27506,27507,7356,27509,27512,27513,27515],{},[941,27508,24715],{},[950,27510,27511],{},"eager-mode only",": it reads the fully-hydrated in-memory cache. On a lazy collection (",[941,27514,6836],{},", bounded cache) it throws. Your options there:",[1111,27517,27518,27534],{},[1114,27519,27520,27523,27524,953,27527,27529,27530,22630,27532,1520],{},[941,27521,27522],{},"collection.lazyQuery()"," — the same chainable shape, but every read dispatches through persisted indexes and the terminals return Promises. Requires the ",[1007,27525,27526],{"href":21916},"indexing service",[941,27528,6766],{},") and every filtered\u002Fsorted field declared in ",[941,27531,21907],{},[1007,27533,25683],{"href":22071},[1114,27535,27536,27539,27540,1520],{},[941,27537,27538],{},"collection.scan()"," — works in both modes; stream and filter with ",[941,27541,27542],{},"for await",[1022,27544,27546],{"id":27545},"subscriptions-change-events","Subscriptions (change events)",[937,27548,27549,27552,27553,27555],{},[941,27550,27551],{},"collection.subscribe(cb)"," is part of the always-on core. It is an ",[950,27554,25096],{}," — the callback fires after every committed write, never for in-flight or rolled-back ones:",[2217,27557,27559],{"className":2219,"code":27558,"language":2221,"meta":1037,"style":1037},"const stop = invoices.subscribe(event => {\n  \u002F\u002F { type: 'put',    id, record }        — record is the current decrypted value\n  \u002F\u002F { type: 'delete', id, record: null }\n})\nstop()  \u002F\u002F unsubscribe\n",[941,27560,27561,27584,27589,27594,27600],{"__ignoreMap":1037},[2225,27562,27563,27565,27568,27570,27572,27574,27576,27578,27580,27582],{"class":2227,"line":2228},[2225,27564,2775],{"class":2774},[2225,27566,27567],{"class":2239}," stop ",[2225,27569,2781],{"class":2235},[2225,27571,6503],{"class":2239},[2225,27573,1520],{"class":2235},[2225,27575,25042],{"class":2291},[2225,27577,2789],{"class":2239},[2225,27579,12307],{"class":7052},[2225,27581,8116],{"class":2774},[2225,27583,2301],{"class":2235},[2225,27585,27586],{"class":2227,"line":1041},[2225,27587,27588],{"class":2763},"  \u002F\u002F { type: 'put',    id, record }        — record is the current decrypted value\n",[2225,27590,27591],{"class":2227,"line":1038},[2225,27592,27593],{"class":2763},"  \u002F\u002F { type: 'delete', id, record: null }\n",[2225,27595,27596,27598],{"class":2227,"line":2288},[2225,27597,2359],{"class":2235},[2225,27599,2362],{"class":2239},[2225,27601,27602,27605,27607],{"class":2227,"line":2304},[2225,27603,27604],{"class":2291},"stop",[2225,27606,6598],{"class":2239},[2225,27608,27609],{"class":2763},"\u002F\u002F unsubscribe\n",[937,27611,27612,27613,27616,27617,1261,27619,27621,27622,1520],{},"For a ",[950,27614,27615],{},"reactive query result"," — a value that recomputes whenever matching data changes — use ",[941,27618,25115],{},[941,27620,25118],{},", documented on the ",[1007,27623,27624],{"href":25122},"live service page",[1022,27626,27628],{"id":27627},"limits","Limits",[1111,27630,27631,27643,27649,27669],{},[1114,27632,27633,27636,27637,27639,27640,27642],{},[950,27634,27635],{},"Memory",": eager ",[941,27638,24715],{}," snapshots the whole collection cache. For collections that don't fit comfortably in memory, prefer ",[941,27641,7906],{}," (or lazy mode).",[1114,27644,27645,27648],{},[950,27646,27647],{},"No SQL pushdown",": the store holds only ciphertext, so it cannot evaluate a query. All filtering, ordering, and reducing happens inside the hub after decryption.\n",[1114,27650,27651,2085,27656,27658,27659,1625,27661,27663,27664,1625,27666,27668],{},[950,27652,27653,27655],{},[941,27654,24913],{}," and joins",[941,27657,24913],{}," reports matches after ",[941,27660,3016],{},[941,27662,8686],{}," but before ",[941,27665,3022],{},[941,27667,3227],{},", and never applies joins (joins attach fields; they never change the match set).",[1114,27670,27671,27674,27675,27678,27679,997,27682,27685,27686,27689],{},[950,27672,27673],{},"Cross-collection",": use ",[941,27676,27677],{},"query().join(field, { as })"," — see ",[1007,27680,715],{"href":27681},"..\u002F..\u002Fservices\u002Fjoins",[1007,27683,657],{"href":27684},"..\u002F..\u002Fservices\u002Fcross-join"," — or ",[941,27687,27688],{},"db.queryAcross()"," for reads spanning vaults.",[1022,27691,27693],{"id":27692},"discriminated-union-collections","Discriminated-union collections",[937,27695,20818,27696,27698,27699,27701,27702,1261,27704,1261,27706,27708],{},[941,27697,11968],{}," is a discriminated union, ",[941,27700,3121],{}," threads it correctly through ",[941,27703,3158],{},[941,27705,5286],{},[941,27707,3239],{}," — but narrowing a result to one union member is your application's job:",[2217,27710,27712],{"className":2219,"code":27711,"language":2221,"meta":1037,"style":1037},"type Invoice = { kind: 'invoice'; invoiceNo: string; amount: number }\ntype Payment = { kind: 'payment'; paymentNo: string; paidAt: string }\ntype Receipt = Invoice | Payment\n\nconst receipts = vault.collection\u003CReceipt>('receipts').query().toArray()\n\nfor (const r of receipts) {\n  if (r.kind === 'invoice') {\n    console.log(r.invoiceNo)  \u002F\u002F r: Invoice — no cast needed\n  }\n}\n",[941,27713,27714,27754,27796,27812,27816,27861,27865,27883,27908,27931,27935],{"__ignoreMap":1037},[2225,27715,27716,27718,27720,27722,27724,27726,27728,27730,27733,27735,27737,27740,27742,27744,27746,27748,27750,27752],{"class":2227,"line":2228},[2225,27717,6339],{"class":2774},[2225,27719,6343],{"class":6342},[2225,27721,6346],{"class":2235},[2225,27723,2236],{"class":2235},[2225,27725,8285],{"class":2307},[2225,27727,2311],{"class":2235},[2225,27729,2249],{"class":2235},[2225,27731,27732],{"class":2252},"invoice",[2225,27734,2805],{"class":2235},[2225,27736,3769],{"class":2235},[2225,27738,27739],{"class":2307}," invoiceNo",[2225,27741,2311],{"class":2235},[2225,27743,6366],{"class":6342},[2225,27745,3769],{"class":2235},[2225,27747,6351],{"class":2307},[2225,27749,2311],{"class":2235},[2225,27751,6356],{"class":6342},[2225,27753,6369],{"class":2235},[2225,27755,27756,27758,27761,27763,27765,27767,27769,27771,27774,27776,27778,27781,27783,27785,27787,27790,27792,27794],{"class":2227,"line":1041},[2225,27757,6339],{"class":2774},[2225,27759,27760],{"class":6342}," Payment",[2225,27762,6346],{"class":2235},[2225,27764,2236],{"class":2235},[2225,27766,8285],{"class":2307},[2225,27768,2311],{"class":2235},[2225,27770,2249],{"class":2235},[2225,27772,27773],{"class":2252},"payment",[2225,27775,2805],{"class":2235},[2225,27777,3769],{"class":2235},[2225,27779,27780],{"class":2307}," paymentNo",[2225,27782,2311],{"class":2235},[2225,27784,6366],{"class":6342},[2225,27786,3769],{"class":2235},[2225,27788,27789],{"class":2307}," paidAt",[2225,27791,2311],{"class":2235},[2225,27793,6366],{"class":6342},[2225,27795,6369],{"class":2235},[2225,27797,27798,27800,27803,27805,27807,27809],{"class":2227,"line":1038},[2225,27799,6339],{"class":2774},[2225,27801,27802],{"class":6342}," Receipt",[2225,27804,6346],{"class":2235},[2225,27806,6343],{"class":6342},[2225,27808,7087],{"class":2235},[2225,27810,27811],{"class":6342}," Payment\n",[2225,27813,27814],{"class":2227,"line":2288},[2225,27815,2285],{"emptyLinePlaceholder":2284},[2225,27817,27818,27820,27823,27825,27827,27829,27831,27833,27836,27838,27840,27842,27845,27847,27849,27851,27853,27855,27857,27859],{"class":2227,"line":2304},[2225,27819,2775],{"class":2774},[2225,27821,27822],{"class":2239}," receipts ",[2225,27824,2781],{"class":2235},[2225,27826,6466],{"class":2239},[2225,27828,1520],{"class":2235},[2225,27830,6471],{"class":2291},[2225,27832,6474],{"class":2235},[2225,27834,27835],{"class":6342},"Receipt",[2225,27837,6480],{"class":2235},[2225,27839,2789],{"class":2239},[2225,27841,2805],{"class":2235},[2225,27843,27844],{"class":2252},"receipts",[2225,27846,2805],{"class":2235},[2225,27848,1381],{"class":2239},[2225,27850,1520],{"class":2235},[2225,27852,5286],{"class":2291},[2225,27854,4228],{"class":2239},[2225,27856,1520],{"class":2235},[2225,27858,3230],{"class":2291},[2225,27860,8146],{"class":2239},[2225,27862,27863],{"class":2227,"line":2356},[2225,27864,2285],{"emptyLinePlaceholder":2284},[2225,27866,27867,27869,27871,27873,27876,27878,27881],{"class":2227,"line":2830},[2225,27868,4646],{"class":2231},[2225,27870,953],{"class":2239},[2225,27872,2775],{"class":2774},[2225,27874,27875],{"class":2239}," r ",[2225,27877,27066],{"class":2235},[2225,27879,27880],{"class":2239}," receipts) ",[2225,27882,4200],{"class":2235},[2225,27884,27885,27887,27889,27891,27893,27896,27898,27900,27902,27904,27906],{"class":2227,"line":2861},[2225,27886,7608],{"class":2231},[2225,27888,953],{"class":2307},[2225,27890,13217],{"class":2239},[2225,27892,1520],{"class":2235},[2225,27894,27895],{"class":2239},"kind",[2225,27897,9025],{"class":2235},[2225,27899,2249],{"class":2235},[2225,27901,27732],{"class":2252},[2225,27903,2805],{"class":2235},[2225,27905,8495],{"class":2307},[2225,27907,4200],{"class":2235},[2225,27909,27910,27913,27915,27917,27919,27921,27923,27926,27928],{"class":2227,"line":4246},[2225,27911,27912],{"class":2239},"    console",[2225,27914,1520],{"class":2235},[2225,27916,27100],{"class":2291},[2225,27918,2789],{"class":2307},[2225,27920,13217],{"class":2239},[2225,27922,1520],{"class":2235},[2225,27924,27925],{"class":2239},"invoiceNo",[2225,27927,27226],{"class":2307},[2225,27929,27930],{"class":2763},"\u002F\u002F r: Invoice — no cast needed\n",[2225,27932,27933],{"class":2227,"line":4253},[2225,27934,7632],{"class":2235},[2225,27936,27937],{"class":2227,"line":4259},[2225,27938,7351],{"class":2235},[937,27940,27941,27942,27944,27945,27948],{},"In plain ",[941,27943,2942],{}," files the inline comparison above narrows fine. In vue-tsc \u002F template contexts it sometimes does not — use the exported ",[941,27946,27947],{},"isDiscriminant"," type-guard, which works reliably everywhere:",[2217,27950,27952],{"className":2219,"code":27951,"language":2221,"meta":1037,"style":1037},"import { isDiscriminant } from '@noy-db\u002Fhub'\n\n\u002F\u002F Filter — result type is Invoice[], member-specific fields accessible without cast:\nconst invs = receipts.filter(r => isDiscriminant(r, 'kind', 'invoice'))\nconsole.log(invs[0].invoiceNo)  \u002F\u002F string — no cast\n\n\u002F\u002F Branch:\nfor (const r of receipts) {\n  if (isDiscriminant(r, 'kind', 'invoice')) {\n    console.log(r.invoiceNo)    \u002F\u002F r: Invoice here\n  }\n}\n",[941,27953,27954,27973,27977,27982,28027,28051,28055,28060,28076,28108,28130,28134],{"__ignoreMap":1037},[2225,27955,27956,27958,27960,27963,27965,27967,27969,27971],{"class":2227,"line":2228},[2225,27957,2232],{"class":2231},[2225,27959,2236],{"class":2235},[2225,27961,27962],{"class":2239}," isDiscriminant",[2225,27964,2243],{"class":2235},[2225,27966,2246],{"class":2231},[2225,27968,2249],{"class":2235},[2225,27970,1086],{"class":2252},[2225,27972,2256],{"class":2235},[2225,27974,27975],{"class":2227,"line":1041},[2225,27976,2285],{"emptyLinePlaceholder":2284},[2225,27978,27979],{"class":2227,"line":1038},[2225,27980,27981],{"class":2763},"\u002F\u002F Filter — result type is Invoice[], member-specific fields accessible without cast:\n",[2225,27983,27984,27986,27989,27991,27994,27996,27998,28000,28002,28004,28006,28009,28011,28013,28015,28017,28019,28021,28023,28025],{"class":2227,"line":2288},[2225,27985,2775],{"class":2774},[2225,27987,27988],{"class":2239}," invs ",[2225,27990,2781],{"class":2235},[2225,27992,27993],{"class":2239}," receipts",[2225,27995,1520],{"class":2235},[2225,27997,8686],{"class":2291},[2225,27999,2789],{"class":2239},[2225,28001,13217],{"class":7052},[2225,28003,8116],{"class":2774},[2225,28005,27962],{"class":2291},[2225,28007,28008],{"class":2239},"(r",[2225,28010,2298],{"class":2235},[2225,28012,2249],{"class":2235},[2225,28014,27895],{"class":2252},[2225,28016,2805],{"class":2235},[2225,28018,2298],{"class":2235},[2225,28020,2249],{"class":2235},[2225,28022,27732],{"class":2252},[2225,28024,2805],{"class":2235},[2225,28026,8606],{"class":2239},[2225,28028,28029,28032,28034,28036,28039,28041,28043,28045,28048],{"class":2227,"line":2304},[2225,28030,28031],{"class":2239},"console",[2225,28033,1520],{"class":2235},[2225,28035,27100],{"class":2291},[2225,28037,28038],{"class":2239},"(invs[",[2225,28040,5402],{"class":6527},[2225,28042,8803],{"class":2239},[2225,28044,1520],{"class":2235},[2225,28046,28047],{"class":2239},"invoiceNo)  ",[2225,28049,28050],{"class":2763},"\u002F\u002F string — no cast\n",[2225,28052,28053],{"class":2227,"line":2356},[2225,28054,2285],{"emptyLinePlaceholder":2284},[2225,28056,28057],{"class":2227,"line":2830},[2225,28058,28059],{"class":2763},"\u002F\u002F Branch:\n",[2225,28061,28062,28064,28066,28068,28070,28072,28074],{"class":2227,"line":2861},[2225,28063,4646],{"class":2231},[2225,28065,953],{"class":2239},[2225,28067,2775],{"class":2774},[2225,28069,27875],{"class":2239},[2225,28071,27066],{"class":2235},[2225,28073,27880],{"class":2239},[2225,28075,4200],{"class":2235},[2225,28077,28078,28080,28082,28084,28086,28088,28090,28092,28094,28096,28098,28100,28102,28104,28106],{"class":2227,"line":4246},[2225,28079,7608],{"class":2231},[2225,28081,953],{"class":2307},[2225,28083,27947],{"class":2291},[2225,28085,2789],{"class":2307},[2225,28087,13217],{"class":2239},[2225,28089,2298],{"class":2235},[2225,28091,2249],{"class":2235},[2225,28093,27895],{"class":2252},[2225,28095,2805],{"class":2235},[2225,28097,2298],{"class":2235},[2225,28099,2249],{"class":2235},[2225,28101,27732],{"class":2252},[2225,28103,2805],{"class":2235},[2225,28105,7620],{"class":2307},[2225,28107,4200],{"class":2235},[2225,28109,28110,28112,28114,28116,28118,28120,28122,28124,28127],{"class":2227,"line":4253},[2225,28111,27912],{"class":2239},[2225,28113,1520],{"class":2235},[2225,28115,27100],{"class":2291},[2225,28117,2789],{"class":2307},[2225,28119,13217],{"class":2239},[2225,28121,1520],{"class":2235},[2225,28123,27925],{"class":2239},[2225,28125,28126],{"class":2307},")    ",[2225,28128,28129],{"class":2763},"\u002F\u002F r: Invoice here\n",[2225,28131,28132],{"class":2227,"line":4259},[2225,28133,7632],{"class":2235},[2225,28135,28136],{"class":2227,"line":6548},[2225,28137,7351],{"class":2235},[937,28139,28140,28141,968,28144,968,28147,28150,28151,1520],{},"The discriminant key is a parameter — any field name works (",[941,28142,28143],{},"'kind'",[941,28145,28146],{},"'type'",[941,28148,28149],{},"'tag'",", …), e.g. ",[941,28152,28153],{},"isDiscriminant(r, 'type', 'A')",[1061,28155,28156],{},[937,28157,28158,28161,28162,28165,28166,28168,28169,28172],{},[950,28159,28160],{},"Note on type aliases."," Define the union as a type alias (",[941,28163,28164],{},"type Receipt = Invoice | Payment","). A plain ",[941,28167,6994],{}," may limit TypeScript's ability to distribute the ",[941,28170,28171],{},"Extract","-based narrowing correctly.",[1022,28174,2621],{"id":2620},[1111,28176,28177,28183,28189,28210,28216],{},[1114,28178,28179,28182],{},[1007,28180,28181],{"href":21916},"Indexing"," — fast-path equality \u002F orderBy dispatch",[1114,28184,28185,28188],{},[1007,28186,28187],{"href":27681},"Joins"," — ref-declared foreign-key joins",[1114,28190,28191,964,28194,1261,28197,1261,28199,1261,28202,1261,28205,6030,28208],{},[1007,28192,28193],{"href":21940},"Aggregate",[941,28195,28196],{},"sum",[941,28198,3236],{},[941,28200,28201],{},"avg",[941,28203,28204],{},"min",[941,28206,28207],{},"max",[941,28209,27496],{},[1114,28211,28212,28215],{},[1007,28213,28214],{"href":25122},"Live"," — reactive query results",[1114,28217,28218,28220,28221],{},[1007,28219,27504],{"href":22071}," — bounded-cache collections and ",[941,28222,24807],{},[2645,28224,28225],{},"html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html pre.shiki code .sHdIc, html code.shiki .sHdIc{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#EEFFFF;--shiki-default-font-style:italic;--shiki-dark:#BABED8;--shiki-dark-font-style:italic}",{"title":1037,"searchDepth":1038,"depth":1038,"links":28227},[28228,28234,28238,28239,28240,28241,28242],{"id":25795,"depth":1041,"text":25796,"children":28229},[28230,28231,28232],{"id":26642,"depth":1038,"text":26643},{"id":26789,"depth":1038,"text":26790},{"id":26950,"depth":1038,"text":28233},"Escape hatch: .filter()",{"id":27034,"depth":1041,"text":28235,"children":28236},"Streaming with scan()",[28237],{"id":27236,"depth":1038,"text":27237},{"id":27503,"depth":1041,"text":27504},{"id":27545,"depth":1041,"text":27546},{"id":27627,"depth":1041,"text":27628},{"id":27692,"depth":1041,"text":27693},{"id":2620,"depth":1041,"text":2621},"The always-on read path: the chainable query() builder (where \u002F orderBy \u002F limit \u002F offset), the streaming scan() builder, subscriptions, and the operator set.",{},{"title":107,"description":28243},"YyBmI7uLF8YxVEmmbwGy5Rwq2WsNrP5utWXFqVESLGA",{"id":28248,"title":111,"access":12,"apiToc":929,"body":28249,"category":1042,"description":28909,"extension":1044,"meta":28910,"navigation":1042,"nest":2635,"path":112,"seo":28911,"sinceVersion":1042,"status":1049,"stem":113,"subtitle":1042,"tier":6922,"tocExternal":1042,"__hash__":28912},"docs\u002Fcore\u002F4.collection\u002F3.schema.md",{"type":931,"value":28250,"toc":28904},[28251,28255,28265,28286,28295,28298,28301,28791,28841,28860,28864,28878,28880,28901],[934,28252,28254],{"id":28253},"schema-validation","Schema & validation",[1061,28256,28257],{},[937,28258,28259,6235,28262],{},[950,28260,28261],{},"Always-on. Lightweight typing scaffold.",[941,28263,28264],{},"packages\u002Fhub\u002Fsrc\u002Fkernel\u002Fschema.ts",[937,28266,28267,28268,28271,28272,28274,28275,28278,28279,28282,28283,28285],{},"noy-db stores typed records in ",[1007,28269,2698],{"href":28270},"..\u002F01-overview"," inside an encrypted vault. ",[950,28273,111],{}," is the always-on core feature that keeps those records well-shaped: attach any ",[1007,28276,22622],{"href":22620,"rel":28277},[3371]," validator (Zod, Valibot, ArkType, Effect Schema, …) to a collection via ",[941,28280,28281],{},"collection(name, { schema })",". Input is validated on ",[941,28284,20953],{}," before encryption; output is validated after decryption on read.",[937,28287,28288,28289,28292,28293,1520],{},"For typed relationships ",[1092,28290,28291],{},"between"," collections — foreign keys, integrity, joins — see ",[1007,28294,681],{"href":23554},[1022,28296,28297],{"id":28253},"Schema validation",[937,28299,28300],{},"Pass a Standard Schema validator when creating the collection:",[2217,28302,28304],{"className":2219,"code":28303,"language":2221,"meta":1037,"style":1037},"import { z } from 'zod'\nimport { createNoydb } from '@noy-db\u002Fhub'\n\nconst InvoiceSchema = z.object({\n  id:     z.string(),\n  amount: z.number().positive(),\n  status: z.enum(['draft', 'open', 'paid', 'overdue']),\n  date:   z.string().datetime(),\n})\n\nconst db = await createNoydb({ user: 'alice', secret: '…' })\nconst vault = await db.openVault('acme')\n\nconst invoices = vault.collection\u003Cz.infer\u003Ctypeof InvoiceSchema>>('invoices', {\n  schema: InvoiceSchema,\n})\n\nawait invoices.put('inv-1', { id: 'inv-1', amount: 120, status: 'open', date: '2026-07-01T00:00:00Z' })\n\u002F\u002F → stored (encrypted) — the validated value is what gets persisted\n\nawait invoices.put('inv-2', { id: 'inv-2', amount: -5, status: 'open', date: '2026-07-01T00:00:00Z' })\n\u002F\u002F → throws SchemaValidationError: \"Schema validation failed on put(inv-2): amount: …\"\n",[941,28305,28306,28326,28344,28348,28367,28385,28409,28462,28487,28493,28497,28539,28565,28569,28614,28625,28631,28635,28705,28710,28714,28786],{"__ignoreMap":1037},[2225,28307,28308,28310,28312,28315,28317,28319,28321,28324],{"class":2227,"line":2228},[2225,28309,2232],{"class":2231},[2225,28311,2236],{"class":2235},[2225,28313,28314],{"class":2239}," z",[2225,28316,2243],{"class":2235},[2225,28318,2246],{"class":2231},[2225,28320,2249],{"class":2235},[2225,28322,28323],{"class":2252},"zod",[2225,28325,2256],{"class":2235},[2225,28327,28328,28330,28332,28334,28336,28338,28340,28342],{"class":2227,"line":1041},[2225,28329,2232],{"class":2231},[2225,28331,2236],{"class":2235},[2225,28333,2744],{"class":2239},[2225,28335,2243],{"class":2235},[2225,28337,2246],{"class":2231},[2225,28339,2249],{"class":2235},[2225,28341,1086],{"class":2252},[2225,28343,2256],{"class":2235},[2225,28345,28346],{"class":2227,"line":1038},[2225,28347,2285],{"emptyLinePlaceholder":2284},[2225,28349,28350,28352,28355,28357,28359,28361,28363,28365],{"class":2227,"line":2288},[2225,28351,2775],{"class":2774},[2225,28353,28354],{"class":2239}," InvoiceSchema ",[2225,28356,2781],{"class":2235},[2225,28358,28314],{"class":2239},[2225,28360,1520],{"class":2235},[2225,28362,25505],{"class":2291},[2225,28364,2789],{"class":2239},[2225,28366,4200],{"class":2235},[2225,28368,28369,28372,28374,28377,28379,28381,28383],{"class":2227,"line":2304},[2225,28370,28371],{"class":2307},"  id",[2225,28373,2311],{"class":2235},[2225,28375,28376],{"class":2239},"     z",[2225,28378,1520],{"class":2235},[2225,28380,7259],{"class":2291},[2225,28382,4228],{"class":2239},[2225,28384,1707],{"class":2235},[2225,28386,28387,28390,28392,28394,28396,28398,28400,28402,28405,28407],{"class":2227,"line":2356},[2225,28388,28389],{"class":2307},"  amount",[2225,28391,2311],{"class":2235},[2225,28393,28314],{"class":2239},[2225,28395,1520],{"class":2235},[2225,28397,20636],{"class":2291},[2225,28399,4228],{"class":2239},[2225,28401,1520],{"class":2235},[2225,28403,28404],{"class":2291},"positive",[2225,28406,4228],{"class":2239},[2225,28408,1707],{"class":2235},[2225,28410,28411,28413,28415,28417,28419,28422,28425,28427,28430,28432,28434,28436,28438,28440,28442,28444,28446,28448,28450,28452,28455,28457,28460],{"class":2227,"line":2830},[2225,28412,26171],{"class":2307},[2225,28414,2311],{"class":2235},[2225,28416,28314],{"class":2239},[2225,28418,1520],{"class":2235},[2225,28420,28421],{"class":2291},"enum",[2225,28423,28424],{"class":2239},"([",[2225,28426,2805],{"class":2235},[2225,28428,28429],{"class":2252},"draft",[2225,28431,2805],{"class":2235},[2225,28433,2298],{"class":2235},[2225,28435,2249],{"class":2235},[2225,28437,26178],{"class":2252},[2225,28439,2805],{"class":2235},[2225,28441,2298],{"class":2235},[2225,28443,2249],{"class":2235},[2225,28445,6539],{"class":2252},[2225,28447,2805],{"class":2235},[2225,28449,2298],{"class":2235},[2225,28451,2249],{"class":2235},[2225,28453,28454],{"class":2252},"overdue",[2225,28456,2805],{"class":2235},[2225,28458,28459],{"class":2239},"])",[2225,28461,1707],{"class":2235},[2225,28463,28464,28467,28469,28472,28474,28476,28478,28480,28483,28485],{"class":2227,"line":2861},[2225,28465,28466],{"class":2307},"  date",[2225,28468,2311],{"class":2235},[2225,28470,28471],{"class":2239},"   z",[2225,28473,1520],{"class":2235},[2225,28475,7259],{"class":2291},[2225,28477,4228],{"class":2239},[2225,28479,1520],{"class":2235},[2225,28481,28482],{"class":2291},"datetime",[2225,28484,4228],{"class":2239},[2225,28486,1707],{"class":2235},[2225,28488,28489,28491],{"class":2227,"line":4246},[2225,28490,2359],{"class":2235},[2225,28492,2362],{"class":2239},[2225,28494,28495],{"class":2227,"line":4253},[2225,28496,2285],{"emptyLinePlaceholder":2284},[2225,28498,28499,28501,28503,28505,28507,28509,28511,28513,28515,28517,28519,28521,28523,28525,28527,28529,28531,28533,28535,28537],{"class":2227,"line":4259},[2225,28500,2775],{"class":2774},[2225,28502,2778],{"class":2239},[2225,28504,2781],{"class":2235},[2225,28506,2784],{"class":2231},[2225,28508,2744],{"class":2291},[2225,28510,2789],{"class":2239},[2225,28512,2792],{"class":2235},[2225,28514,2795],{"class":2307},[2225,28516,2311],{"class":2235},[2225,28518,2249],{"class":2235},[2225,28520,6408],{"class":2252},[2225,28522,2805],{"class":2235},[2225,28524,2298],{"class":2235},[2225,28526,2810],{"class":2307},[2225,28528,2311],{"class":2235},[2225,28530,2249],{"class":2235},[2225,28532,14475],{"class":2252},[2225,28534,2805],{"class":2235},[2225,28536,2243],{"class":2235},[2225,28538,2362],{"class":2239},[2225,28540,28541,28543,28545,28547,28549,28551,28553,28555,28557,28559,28561,28563],{"class":2227,"line":6548},[2225,28542,2775],{"class":2774},[2225,28544,2835],{"class":2239},[2225,28546,2781],{"class":2235},[2225,28548,2784],{"class":2231},[2225,28550,2842],{"class":2239},[2225,28552,1520],{"class":2235},[2225,28554,2847],{"class":2291},[2225,28556,2789],{"class":2239},[2225,28558,2805],{"class":2235},[2225,28560,6450],{"class":2252},[2225,28562,2805],{"class":2235},[2225,28564,2362],{"class":2239},[2225,28566,28567],{"class":2227,"line":6576},[2225,28568,2285],{"emptyLinePlaceholder":2284},[2225,28570,28571,28573,28575,28577,28579,28581,28583,28585,28588,28590,28593,28596,28599,28602,28604,28606,28608,28610,28612],{"class":2227,"line":6582},[2225,28572,2775],{"class":2774},[2225,28574,6461],{"class":2239},[2225,28576,2781],{"class":2235},[2225,28578,6466],{"class":2239},[2225,28580,1520],{"class":2235},[2225,28582,6471],{"class":2291},[2225,28584,6474],{"class":2235},[2225,28586,28587],{"class":6342},"z",[2225,28589,1520],{"class":2235},[2225,28591,28592],{"class":6342},"infer",[2225,28594,28595],{"class":2235},"\u003Ctypeof",[2225,28597,28598],{"class":2239}," InvoiceSchema",[2225,28600,28601],{"class":2235},">>",[2225,28603,2789],{"class":2239},[2225,28605,2805],{"class":2235},[2225,28607,6487],{"class":2252},[2225,28609,2805],{"class":2235},[2225,28611,2298],{"class":2235},[2225,28613,2301],{"class":2235},[2225,28615,28616,28619,28621,28623],{"class":2227,"line":6587},[2225,28617,28618],{"class":2307},"  schema",[2225,28620,2311],{"class":2235},[2225,28622,28598],{"class":2239},[2225,28624,1707],{"class":2235},[2225,28626,28627,28629],{"class":2227,"line":7174},[2225,28628,2359],{"class":2235},[2225,28630,2362],{"class":2239},[2225,28632,28633],{"class":2227,"line":7180},[2225,28634,2285],{"emptyLinePlaceholder":2284},[2225,28636,28637,28639,28641,28643,28645,28647,28649,28651,28653,28655,28657,28659,28661,28663,28665,28667,28669,28671,28673,28676,28678,28680,28682,28684,28686,28688,28690,28692,28694,28696,28699,28701,28703],{"class":2227,"line":7220},[2225,28638,6500],{"class":2231},[2225,28640,6503],{"class":2239},[2225,28642,1520],{"class":2235},[2225,28644,3161],{"class":2291},[2225,28646,2789],{"class":2239},[2225,28648,2805],{"class":2235},[2225,28650,6514],{"class":2252},[2225,28652,2805],{"class":2235},[2225,28654,2298],{"class":2235},[2225,28656,2236],{"class":2235},[2225,28658,7070],{"class":2307},[2225,28660,2311],{"class":2235},[2225,28662,2249],{"class":2235},[2225,28664,6514],{"class":2252},[2225,28666,2805],{"class":2235},[2225,28668,2298],{"class":2235},[2225,28670,6351],{"class":2307},[2225,28672,2311],{"class":2235},[2225,28674,28675],{"class":6527}," 120",[2225,28677,2298],{"class":2235},[2225,28679,6361],{"class":2307},[2225,28681,2311],{"class":2235},[2225,28683,2249],{"class":2235},[2225,28685,26178],{"class":2252},[2225,28687,2805],{"class":2235},[2225,28689,2298],{"class":2235},[2225,28691,25880],{"class":2307},[2225,28693,2311],{"class":2235},[2225,28695,2249],{"class":2235},[2225,28697,28698],{"class":2252},"2026-07-01T00:00:00Z",[2225,28700,2805],{"class":2235},[2225,28702,2243],{"class":2235},[2225,28704,2362],{"class":2239},[2225,28706,28707],{"class":2227,"line":7225},[2225,28708,28709],{"class":2763},"\u002F\u002F → stored (encrypted) — the validated value is what gets persisted\n",[2225,28711,28712],{"class":2227,"line":7231},[2225,28713,2285],{"emptyLinePlaceholder":2284},[2225,28715,28716,28718,28720,28722,28724,28726,28728,28730,28732,28734,28736,28738,28740,28742,28744,28746,28748,28750,28752,28755,28758,28760,28762,28764,28766,28768,28770,28772,28774,28776,28778,28780,28782,28784],{"class":2227,"line":7267},[2225,28717,6500],{"class":2231},[2225,28719,6503],{"class":2239},[2225,28721,1520],{"class":2235},[2225,28723,3161],{"class":2291},[2225,28725,2789],{"class":2239},[2225,28727,2805],{"class":2235},[2225,28729,26080],{"class":2252},[2225,28731,2805],{"class":2235},[2225,28733,2298],{"class":2235},[2225,28735,2236],{"class":2235},[2225,28737,7070],{"class":2307},[2225,28739,2311],{"class":2235},[2225,28741,2249],{"class":2235},[2225,28743,26080],{"class":2252},[2225,28745,2805],{"class":2235},[2225,28747,2298],{"class":2235},[2225,28749,6351],{"class":2307},[2225,28751,2311],{"class":2235},[2225,28753,28754],{"class":2235}," -",[2225,28756,28757],{"class":6527},"5",[2225,28759,2298],{"class":2235},[2225,28761,6361],{"class":2307},[2225,28763,2311],{"class":2235},[2225,28765,2249],{"class":2235},[2225,28767,26178],{"class":2252},[2225,28769,2805],{"class":2235},[2225,28771,2298],{"class":2235},[2225,28773,25880],{"class":2307},[2225,28775,2311],{"class":2235},[2225,28777,2249],{"class":2235},[2225,28779,28698],{"class":2252},[2225,28781,2805],{"class":2235},[2225,28783,2243],{"class":2235},[2225,28785,2362],{"class":2239},[2225,28787,28788],{"class":2227,"line":7272},[2225,28789,28790],{"class":2763},"\u002F\u002F → throws SchemaValidationError: \"Schema validation failed on put(inv-2): amount: …\"\n",[1111,28792,28793,28815,28834],{},[1114,28794,28795,28798,28799,1324,28801,28803,28804,28806,28807,28810,28811,28814],{},[950,28796,28797],{},"Input validation"," runs in ",[941,28800,20953],{},[1092,28802,11357],{}," encryption. A rejected record throws ",[941,28805,22629],{}," with ",[941,28808,28809],{},"direction: 'input'"," and the validator's full structured ",[941,28812,28813],{},"issues"," list — useful for rendering field-level messages in a UI.",[1114,28816,28817,28820,28821,1261,28823,1261,28825,1324,28827,28829,28830,28833],{},[950,28818,28819],{},"Output validation"," runs on records coming out of ",[941,28822,14639],{},[941,28824,24407],{},[941,28826,24715],{},[1092,28828,10761],{}," decryption. If stored data has drifted from the current schema, it throws with ",[941,28831,28832],{},"direction: 'output'"," instead of silently returning garbage.",[1114,28835,28836,28837,28840],{},"Validators may ",[950,28838,28839],{},"transform"," the input (coerce strings to numbers, strip unknown fields); the transformed value is what gets encrypted and stored.",[937,28842,960,28843,28845,28846,28848,28849,28852,28853,28856,28857,28859],{},[941,28844,11968],{}," parameter on ",[941,28847,3121],{}," is the validator's ",[950,28850,28851],{},"output"," type — whatever it produces after coercion and transforms. The ",[941,28854,28855],{},"InferOutput\u003Ctypeof schema>"," helper type (exported from ",[941,28858,1086],{},") derives it from a schema instance.",[1022,28861,28863],{"id":28862},"edge-cases","Edge cases",[1111,28865,28866,28869],{},[1114,28867,28868],{},"Schema validation is opt-in per collection; a vault can mix validated and unvalidated collections.",[1114,28870,28871,28872,28874,28875,28877],{},"When a collection also declares ",[941,28873,20943],{}," fields (see ",[1007,28876,701],{"href":21465},"), the schema validates first; the i18n required-translation check runs after it, on the schema-validated record.",[1022,28879,2621],{"id":2620},[1111,28881,28882,28887,28892],{},[1114,28883,28884,28886],{},[1007,28885,681],{"href":23554}," — typed references between collections",[1114,28888,28889],{},[1007,28890,28891],{"href":28270},"Core 01 — Vault & Collection model",[1114,28893,28894,964,28896,1261,28898,28900],{},[1007,28895,701],{"href":21465},[941,28897,20943],{},[941,28899,21456],{}," field descriptors layered on top",[2645,28902,28903],{},"html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":1037,"searchDepth":1038,"depth":1038,"links":28905},[28906,28907,28908],{"id":28253,"depth":1041,"text":28297},{"id":28862,"depth":1041,"text":28863},{"id":2620,"depth":1041,"text":2621},"The always-on typing scaffold: attach any Standard Schema v1 validator to a collection. Input is validated before encryption, output after decryption.",{},{"title":111,"description":28909},"TBjNCKrymnOXvmXQ49oJuHW0bwk9NrY0u7k0cXOauno",{"id":28914,"title":103,"access":12,"apiToc":929,"body":28915,"category":1042,"description":29003,"extension":1044,"meta":29004,"navigation":29005,"nest":2635,"path":98,"seo":29006,"sinceVersion":1042,"status":1049,"stem":99,"subtitle":1042,"tier":6922,"tocExternal":1042,"__hash__":29007},"docs\u002Fcore\u002F4.collection\u002Findex.md",{"type":931,"value":28916,"toc":29000},[28917,28921,28952,28955,28957,28994],[934,28918,28920],{"id":28919},"collection-a-typed-record-set","Collection — a typed record set",[937,28922,3289,28923,28925,28926,28928,28929,28932,28933,28935,28936,1261,28938,1261,28940,1261,28942,28944,28945,1746,28947,1746,28949,28951],{},[941,28924,3121],{}," is a typed set of records inside a ",[1007,28927,2694],{"href":11752},". You open one with ",[941,28930,28931],{},"const invoices = vault.collection\u003CInvoice>('invoices')",", and the TypeScript generic ",[941,28934,11968],{}," carries through ",[941,28937,3158],{},[941,28939,3161],{},[941,28941,5286],{},[941,28943,3239],{},", so records come back typed. It is the bottom of the ",[941,28946,62],{},[941,28948,94],{},[941,28950,3121],{}," hierarchy — the handle you actually read and write records through.",[937,28953,28954],{},"Records are encrypted inside the hub before they reach any storage backend, so the store only ever holds ciphertext and can never run a query. Everything a collection does — reading, writing, querying, validating — happens client-side, after decryption.",[1022,28956,22223],{"id":22222},[1111,28958,28959,28968,28987],{},[1114,28960,28961,22232,28965,28967],{},[950,28962,28963],{},[1007,28964,103],{"href":21106},[941,28966,3121],{}," instance: reading and writing, bulk operations, querying and scanning, change events, deterministic lookup, and introspection.",[1114,28969,28970,28974,28975,24786,28977,1261,28979,1261,28981,28983,28984,28986],{},[950,28971,28972],{},[1007,28973,107],{"href":23952}," — the always-on read path: the chainable ",[941,28976,24715],{},[941,28978,3016],{},[941,28980,3019],{},[941,28982,3022],{},"), the streaming ",[941,28985,7906],{}," builder, subscriptions, and the operator set.",[1114,28988,28989,28993],{},[950,28990,28991],{},[1007,28992,111],{"href":23933}," — the always-on typing scaffold: attach any Standard Schema v1 validator; input is validated before encryption, output after decryption.",[937,28995,28996,28997,28999],{},"Capabilities that add methods once their service is opted in — history, search, blobs, presence, tiers, classified fields, and more — are listed under ",[1007,28998,22351],{"href":23893}," on the methods page.",{"title":1037,"searchDepth":1038,"depth":1038,"links":29001},[29002],{"id":22222,"depth":1041,"text":22223},"A collection: the typed record set inside a vault. What a collection is, how you open one, and the section map for its reference, querying, and schema.",{},{"title":10},{"title":103,"description":29003},"ZnGbJzqwAoZ_zCoJaeSh5Vv4QzdLr8-bFSjrj3GCDH0",{"id":29009,"title":29010,"access":12,"apiToc":929,"body":29011,"category":1042,"description":29830,"extension":1044,"meta":29831,"navigation":29832,"nest":2635,"path":42,"seo":29833,"sinceVersion":1042,"status":1049,"stem":46,"subtitle":1042,"tier":6922,"tocExternal":1042,"__hash__":29834},"docs\u002Fcore\u002Findex.md","Core concepts",{"type":931,"value":29012,"toc":29820},[29013,29016,29037,29041,29081,29098,29102,29105,29122,29126,29132,29138,29157,29161,29164,29522,29529,29533,29536,29619,29638,29642,29650,29773,29775,29778,29807,29809,29818],[934,29014,29010],{"id":29015},"core-concepts",[937,29017,29018,29019,29022,29023,2878,29025,29027,29028,29030,29031,29033,29034,29036],{},"noy-db is an encrypted, offline-first document store for JavaScript\u002FTypeScript applications: your application holds the encryption keys, and the storage backend — a file, a browser database, a cloud bucket — only ever sees ciphertext. That property is called ",[950,29020,29021],{},"zero-knowledge",": whoever operates the storage can read nothing about your data. This section covers the ",[950,29024,43],{},[1092,29026,2635],{},"): the part of the ",[941,29029,1086],{}," package that every install ships and that cannot be opted out of. Everything beyond it — history, sync, transactions, aggregation, and the rest — is a ",[1007,29032,1047],{"href":6254}," you opt into with a ",[941,29035,943],{}," factory, and that tree-shakes out of your bundle when unused.",[1022,29038,29040],{"id":29039},"four-words-youll-see-everywhere","Four words you'll see everywhere",[1111,29042,29043,29056,29061,29070],{},[1114,29044,29045,29048,29049,29051,29052,29055],{},[950,29046,29047],{},"Hub"," — the npm package ",[941,29050,1086],{},", and by extension the core library it ships. The hub is the ",[1092,29053,29054],{},"trust boundary",": everything inside it may handle plaintext, everything outside it never does.",[1114,29057,29058,29060],{},[950,29059,94],{}," — a runtime data space (a tenant, a company, a user) that the hub opens for you. Not a package — the thing your records live in.",[1114,29062,29063,29065,29066,968,29068,957],{},[950,29064,103],{}," — a typed record set inside a vault (",[941,29067,6487],{},[941,29069,23926],{},[1114,29071,29072,29074,29075,29077,29078,29080],{},[950,29073,1245],{}," (and its inner ",[950,29076,1059],{},") — the always-on part ",[1092,29079,27066],{}," the hub: the layer this section documents, which every install ships. The enclave is the kernel's innermost crypto region, the only code that ever touches keys.",[937,29082,29083,29084,29086,29087,29089,29090,29092,29093,29095,29096,1520],{},"So: the ",[1092,29085,5188],{}," (a package) opens a ",[1092,29088,2694],{}," (a data space) holding ",[1092,29091,2698],{}," (typed records), and the ",[1092,29094,2635],{}," is the part of the hub that does this without any opt-ins. Full definitions live in the ",[1007,29097,477],{"href":484},[1022,29099,29101],{"id":29100},"why-a-fixed-core","Why a fixed core",[937,29103,29104],{},"The split exists for two reasons:",[1111,29106,29107,29116],{},[1114,29108,29109,29112,29113,29115],{},[950,29110,29111],{},"Zero-config completeness."," Using only what this section documents — no services, no extra packages, no ",[941,29114,6951],{}," option — you get a fully working encrypted document store: create a session, open a vault, put\u002Fget\u002Fquery typed records. Persistence, multi-user access, and every advanced capability are additions, never prerequisites.",[1114,29117,29118,29121],{},[950,29119,29120],{},"A small, non-negotiable trust surface."," The core's storage guarantees — records are encrypted inside the hub before any store sees them, keys live only in memory, queries run after decryption — hold no matter which services you add, because services sit inside the hub while the store stays outside the encryption boundary.",[1022,29123,29125],{"id":29124},"the-mental-model","The mental model",[937,29127,29128,29129,29131],{},"Everything in the core hangs off a three-layer hierarchy; every record is addressed by a ",[941,29130,6299],{}," triple:",[2217,29133,29136],{"className":29134,"code":29135,"language":5672},[5670],"Noydb                     ← one unlocked session, from createNoydb()\n  └── Vault               ← isolated namespace with its own keys (\"acme\")\n        └── Collection\u003CT> ← typed record set (\"invoices\")\n",[941,29137,29135],{"__ignoreMap":1037},[937,29139,29140,29141,29143,29144,29146,29147,29149,29150,29152,29153,29156],{},"Around that hierarchy, four core mechanisms do the work. When you ",[941,29142,20953],{}," a record, the hub validates it against your ",[950,29145,23934],{}," (if you declared one), encrypts it with a per-collection key from the vault's ",[950,29148,2705],{},", and hands the resulting ciphertext envelope to the ",[950,29151,6951],{}," — the pluggable backend. Reads reverse the path. ",[950,29154,29155],{},"Queries"," filter decrypted records inside the hub; the store never evaluates a predicate — it is a dumb, untrusted ciphertext shelf.",[1022,29158,29160],{"id":29159},"the-whole-core-in-one-round-trip","The whole core in one round trip",[937,29162,29163],{},"Every line below exercises a different core concern — session, vault, typed collection, encrypted write, decrypted read, in-hub query, key wipe:",[2217,29165,29167],{"className":2219,"code":29166,"language":2221,"meta":1037,"style":1037},"import { createNoydb } from '@noy-db\u002Fhub'\n\ntype Invoice = { amount: number; status: string }\n\n\u002F\u002F No `store` option → the built-in in-memory store; nothing persists\n\u002F\u002F past the session. Pass `store: …` to persist (see Stores below).\nconst db = await createNoydb({ user: 'alice', secret: 'a-strong-secret' })\nconst vault = await db.openVault('acme')\nconst invoices = vault.collection\u003CInvoice>('invoices')\n\nawait invoices.put('inv-1', { amount: 100, status: 'paid' })\nawait invoices.put('inv-2', { amount: 2500, status: 'open' })\n\nawait invoices.get('inv-1')\n\u002F\u002F → { amount: 100, status: 'paid' }\n\ninvoices.query().where('status', '==', 'paid').toArray()\n\u002F\u002F → [{ amount: 100, status: 'paid' }]\n\ndb.close() \u002F\u002F wipes the in-memory encryption keys\n",[941,29168,29169,29187,29191,29217,29221,29226,29231,29273,29299,29329,29333,29377,29422,29426,29446,29450,29454,29500,29505,29509],{"__ignoreMap":1037},[2225,29170,29171,29173,29175,29177,29179,29181,29183,29185],{"class":2227,"line":2228},[2225,29172,2232],{"class":2231},[2225,29174,2236],{"class":2235},[2225,29176,2744],{"class":2239},[2225,29178,2243],{"class":2235},[2225,29180,2246],{"class":2231},[2225,29182,2249],{"class":2235},[2225,29184,1086],{"class":2252},[2225,29186,2256],{"class":2235},[2225,29188,29189],{"class":2227,"line":1041},[2225,29190,2285],{"emptyLinePlaceholder":2284},[2225,29192,29193,29195,29197,29199,29201,29203,29205,29207,29209,29211,29213,29215],{"class":2227,"line":1038},[2225,29194,6339],{"class":2774},[2225,29196,6343],{"class":6342},[2225,29198,6346],{"class":2235},[2225,29200,2236],{"class":2235},[2225,29202,6351],{"class":2307},[2225,29204,2311],{"class":2235},[2225,29206,6356],{"class":6342},[2225,29208,3769],{"class":2235},[2225,29210,6361],{"class":2307},[2225,29212,2311],{"class":2235},[2225,29214,6366],{"class":6342},[2225,29216,6369],{"class":2235},[2225,29218,29219],{"class":2227,"line":2288},[2225,29220,2285],{"emptyLinePlaceholder":2284},[2225,29222,29223],{"class":2227,"line":2304},[2225,29224,29225],{"class":2763},"\u002F\u002F No `store` option → the built-in in-memory store; nothing persists\n",[2225,29227,29228],{"class":2227,"line":2356},[2225,29229,29230],{"class":2763},"\u002F\u002F past the session. Pass `store: …` to persist (see Stores below).\n",[2225,29232,29233,29235,29237,29239,29241,29243,29245,29247,29249,29251,29253,29255,29257,29259,29261,29263,29265,29267,29269,29271],{"class":2227,"line":2830},[2225,29234,2775],{"class":2774},[2225,29236,2778],{"class":2239},[2225,29238,2781],{"class":2235},[2225,29240,2784],{"class":2231},[2225,29242,2744],{"class":2291},[2225,29244,2789],{"class":2239},[2225,29246,2792],{"class":2235},[2225,29248,2795],{"class":2307},[2225,29250,2311],{"class":2235},[2225,29252,2249],{"class":2235},[2225,29254,6408],{"class":2252},[2225,29256,2805],{"class":2235},[2225,29258,2298],{"class":2235},[2225,29260,2810],{"class":2307},[2225,29262,2311],{"class":2235},[2225,29264,2249],{"class":2235},[2225,29266,6421],{"class":2252},[2225,29268,2805],{"class":2235},[2225,29270,2243],{"class":2235},[2225,29272,2362],{"class":2239},[2225,29274,29275,29277,29279,29281,29283,29285,29287,29289,29291,29293,29295,29297],{"class":2227,"line":2861},[2225,29276,2775],{"class":2774},[2225,29278,2835],{"class":2239},[2225,29280,2781],{"class":2235},[2225,29282,2784],{"class":2231},[2225,29284,2842],{"class":2239},[2225,29286,1520],{"class":2235},[2225,29288,2847],{"class":2291},[2225,29290,2789],{"class":2239},[2225,29292,2805],{"class":2235},[2225,29294,6450],{"class":2252},[2225,29296,2805],{"class":2235},[2225,29298,2362],{"class":2239},[2225,29300,29301,29303,29305,29307,29309,29311,29313,29315,29317,29319,29321,29323,29325,29327],{"class":2227,"line":4246},[2225,29302,2775],{"class":2774},[2225,29304,6461],{"class":2239},[2225,29306,2781],{"class":2235},[2225,29308,6466],{"class":2239},[2225,29310,1520],{"class":2235},[2225,29312,6471],{"class":2291},[2225,29314,6474],{"class":2235},[2225,29316,6477],{"class":6342},[2225,29318,6480],{"class":2235},[2225,29320,2789],{"class":2239},[2225,29322,2805],{"class":2235},[2225,29324,6487],{"class":2252},[2225,29326,2805],{"class":2235},[2225,29328,2362],{"class":2239},[2225,29330,29331],{"class":2227,"line":4253},[2225,29332,2285],{"emptyLinePlaceholder":2284},[2225,29334,29335,29337,29339,29341,29343,29345,29347,29349,29351,29353,29355,29357,29359,29361,29363,29365,29367,29369,29371,29373,29375],{"class":2227,"line":4259},[2225,29336,6500],{"class":2231},[2225,29338,6503],{"class":2239},[2225,29340,1520],{"class":2235},[2225,29342,3161],{"class":2291},[2225,29344,2789],{"class":2239},[2225,29346,2805],{"class":2235},[2225,29348,6514],{"class":2252},[2225,29350,2805],{"class":2235},[2225,29352,2298],{"class":2235},[2225,29354,2236],{"class":2235},[2225,29356,6351],{"class":2307},[2225,29358,2311],{"class":2235},[2225,29360,6528],{"class":6527},[2225,29362,2298],{"class":2235},[2225,29364,6361],{"class":2307},[2225,29366,2311],{"class":2235},[2225,29368,2249],{"class":2235},[2225,29370,6539],{"class":2252},[2225,29372,2805],{"class":2235},[2225,29374,2243],{"class":2235},[2225,29376,2362],{"class":2239},[2225,29378,29379,29381,29383,29385,29387,29389,29391,29393,29395,29397,29399,29401,29403,29406,29408,29410,29412,29414,29416,29418,29420],{"class":2227,"line":6548},[2225,29380,6500],{"class":2231},[2225,29382,6503],{"class":2239},[2225,29384,1520],{"class":2235},[2225,29386,3161],{"class":2291},[2225,29388,2789],{"class":2239},[2225,29390,2805],{"class":2235},[2225,29392,26080],{"class":2252},[2225,29394,2805],{"class":2235},[2225,29396,2298],{"class":2235},[2225,29398,2236],{"class":2235},[2225,29400,6351],{"class":2307},[2225,29402,2311],{"class":2235},[2225,29404,29405],{"class":6527}," 2500",[2225,29407,2298],{"class":2235},[2225,29409,6361],{"class":2307},[2225,29411,2311],{"class":2235},[2225,29413,2249],{"class":2235},[2225,29415,26178],{"class":2252},[2225,29417,2805],{"class":2235},[2225,29419,2243],{"class":2235},[2225,29421,2362],{"class":2239},[2225,29423,29424],{"class":2227,"line":6576},[2225,29425,2285],{"emptyLinePlaceholder":2284},[2225,29427,29428,29430,29432,29434,29436,29438,29440,29442,29444],{"class":2227,"line":6582},[2225,29429,6500],{"class":2231},[2225,29431,6503],{"class":2239},[2225,29433,1520],{"class":2235},[2225,29435,3158],{"class":2291},[2225,29437,2789],{"class":2239},[2225,29439,2805],{"class":2235},[2225,29441,6514],{"class":2252},[2225,29443,2805],{"class":2235},[2225,29445,2362],{"class":2239},[2225,29447,29448],{"class":2227,"line":6587},[2225,29449,6579],{"class":2763},[2225,29451,29452],{"class":2227,"line":7174},[2225,29453,2285],{"emptyLinePlaceholder":2284},[2225,29455,29456,29458,29460,29462,29464,29466,29468,29470,29472,29474,29476,29478,29480,29482,29484,29486,29488,29490,29492,29494,29496,29498],{"class":2227,"line":7180},[2225,29457,6487],{"class":2239},[2225,29459,1520],{"class":2235},[2225,29461,5286],{"class":2291},[2225,29463,4228],{"class":2239},[2225,29465,1520],{"class":2235},[2225,29467,3016],{"class":2291},[2225,29469,2789],{"class":2239},[2225,29471,2805],{"class":2235},[2225,29473,6631],{"class":2252},[2225,29475,2805],{"class":2235},[2225,29477,2298],{"class":2235},[2225,29479,2249],{"class":2235},[2225,29481,21901],{"class":2252},[2225,29483,2805],{"class":2235},[2225,29485,2298],{"class":2235},[2225,29487,2249],{"class":2235},[2225,29489,6539],{"class":2252},[2225,29491,2805],{"class":2235},[2225,29493,1381],{"class":2239},[2225,29495,1520],{"class":2235},[2225,29497,3230],{"class":2291},[2225,29499,8146],{"class":2239},[2225,29501,29502],{"class":2227,"line":7220},[2225,29503,29504],{"class":2763},"\u002F\u002F → [{ amount: 100, status: 'paid' }]\n",[2225,29506,29507],{"class":2227,"line":7225},[2225,29508,2285],{"emptyLinePlaceholder":2284},[2225,29510,29511,29513,29515,29517,29519],{"class":2227,"line":7231},[2225,29512,6590],{"class":2239},[2225,29514,1520],{"class":2235},[2225,29516,6595],{"class":2291},[2225,29518,19132],{"class":2239},[2225,29520,29521],{"class":2763},"\u002F\u002F wipes the in-memory encryption keys\n",[937,29523,29524,29525,1520],{},"For a guided version of this snippet with installation steps, start at ",[1007,29526,29528],{"href":29527},"..\u002Fstart\u002F","Getting started",[1022,29530,29532],{"id":29531},"reading-order","Reading order",[937,29534,29535],{},"The six pages, in a learning-path order — grasp the data model, then the three handles, then the storage and access layers:",[1234,29537,29538,29553,29565,29572,29591,29605],{},[1114,29539,29540,29545,29546,1746,29548,1746,29550,29552],{},[950,29541,29542],{},[1007,29543,10],{"href":29544},".\u002F01-overview"," — where a record lives: the ",[941,29547,62],{},[941,29549,94],{},[941,29551,3121],{}," hierarchy, the session lifecycle, and how the three handles map to their pages.",[1114,29554,29555,1169,29559,29561,29562,29564],{},[950,29556,29557],{},[1007,29558,6673],{"href":6672},[941,29560,6590],{}," handle: the ",[941,29563,62],{}," session (open vaults, transactions, keyring management) and the client-side encryption it owns.",[1114,29566,29567,29571],{},[950,29568,29569],{},[1007,29570,94],{"href":6683}," — the isolated, encrypted namespace: opening collections, backup\u002Frestore, and the methods that services unlock.",[1114,29573,29574,29578,29579,1625,29581,1625,29583,1625,29585,29587,29588,29590],{},[950,29575,29576],{},[1007,29577,103],{"href":6691}," — the typed record set: ",[941,29580,3158],{},[941,29582,3161],{},[941,29584,5286],{},[941,29586,3239],{},", the chainable query builder, streaming ",[941,29589,7906],{},", and Standard Schema validation.",[1114,29592,29593,29597,29598,29600,29601,29604],{},[950,29594,29595],{},[1007,29596,6891],{"href":6890}," — the pluggable ciphertext backend: the ",[941,29599,3154],{}," contract, the essential stores, and the extended cloud\u002FSQL\u002Fremote-FS family (per-store counts and capabilities: the generated ",[1007,29602,860],{"href":29603},"..\u002Fservices\u002Fstorage-capability-matrix"," matrix).",[1114,29606,29607,29611,29612,1625,29614,1625,29616,29618],{},[950,29608,29609],{},[1007,29610,6898],{"href":6897}," — how a vault admits users: the per-user keyring file, roles, and per-collection ",[941,29613,10026],{},[941,29615,10029],{},[941,29617,8292],{}," permissions (assumes the key hierarchy from the Client page above).",[1061,29620,29621],{},[937,29622,29623,29626,29627,29630,29631,968,29633,968,29635,29637],{},[950,29624,29625],{},"Relationships are not in the core."," Foreign keys, joins, and link sets are always-on kernel code, but a single-collection document store needs none of them — so they're documented as ",[1007,29628,681],{"href":29629},"..\u002Fservices\u002Fforeign-refs"," and its siblings (",[941,29632,715],{},[941,29634,657],{},[941,29636,723],{},") in the query catalog, keeping this section relationship-free.",[1022,29639,29641],{"id":29640},"how-it-maps-to-the-source-tree","How it maps to the source tree",[937,29643,29644,29645,29647,29648,2311],{},"Each page names its source of truth in the ",[941,29646,1072],{}," repo, under ",[941,29649,4033],{},[1338,29651,29652,29664],{},[1341,29653,29654],{},[1344,29655,29656,29659,29662],{},[1347,29657,29658],{},"Concern",[1347,29660,29661],{},"Source",[1347,29663,5721],{},[1357,29665,29666,29690,29709,29724,29740,29754],{},[1344,29667,29668,29671,29682],{},[1362,29669,29670],{},"Session, vault, collection",[1362,29672,29673,968,29676,968,29679],{},[941,29674,29675],{},"kernel\u002Fnoydb.ts",[941,29677,29678],{},"kernel\u002Fvault.ts",[941,29680,29681],{},"kernel\u002Fcollection.ts",[1362,29683,29684,1368,29686,1368,29688],{},[1007,29685,6673],{"href":6672},[1007,29687,94],{"href":6683},[1007,29689,103],{"href":6691},[1344,29691,29692,29695,29705],{},[1362,29693,29694],{},"Store contract + built-in memory store",[1362,29696,29697,953,29700,1884,29702],{},[941,29698,29699],{},"kernel\u002Ftypes.ts",[941,29701,3154],{},[941,29703,29704],{},"kernel\u002Fmemory-store.ts",[1362,29706,29707],{},[1007,29708,6891],{"href":6890},[1344,29710,29711,29713,29718],{},[1362,29712,28297],{},[1362,29714,29715],{},[941,29716,29717],{},"kernel\u002Fschema.ts",[1362,29719,29720],{},[1007,29721,29723],{"href":29722},".\u002Fcollection\u002Fschema","Collection → Schema & validation",[1344,29725,29726,29729,29734],{},[1362,29727,29728],{},"Query + scan builders",[1362,29730,29731],{},[941,29732,29733],{},"kernel\u002Fquery\u002F",[1362,29735,29736],{},[1007,29737,29739],{"href":29738},".\u002Fcollection\u002Fquerying","Collection → Querying",[1344,29741,29742,29745,29749],{},[1362,29743,29744],{},"Encryption + data format",[1362,29746,29747],{},[941,29748,1108],{},[1362,29750,29751],{},[1007,29752,29753],{"href":6655},"Client → Enclave",[1344,29755,29756,29759,29769],{},[1362,29757,29758],{},"Keyring format + roles",[1362,29760,29761,29764,29765,29768],{},[941,29762,29763],{},"with-party\u002Fteam\u002Fkeyring.ts"," (the single-owner keyring is always-on; multi-user ",[1092,29766,29767],{},"management"," is the opt-in team service)",[1362,29770,29771],{},[1007,29772,6898],{"href":6897},[1022,29774,15363],{"id":15362},[937,29776,29777],{},"Four consequences of the design that hold for every noy-db application, with or without services:",[1111,29779,29780,29786,29795,29801],{},[1114,29781,29782,29785],{},[950,29783,29784],{},"Encryption precedes storage."," A record is encrypted inside the hub before any store sees it; only ciphertext crosses the store boundary.",[1114,29787,29788,29791,29792,29794],{},[950,29789,29790],{},"Keys are session-scoped."," The key-encryption key and the per-collection data keys exist only in memory while the session is unlocked; ",[941,29793,13331],{}," wipes them.",[1114,29796,29797,29800],{},[950,29798,29799],{},"The store never computes."," Filtering, ordering, and pagination all happen inside the hub after decryption — the backend stores and returns envelopes, nothing more.",[1114,29802,29803,29806],{},[950,29804,29805],{},"You pay only for what you opt into."," An unused service tree-shakes out of the bundle; the core documented here is the entire mandatory footprint.",[1022,29808,1455],{"id":1454},[937,29810,29811,29812,29814,29815,1520],{},"Work through the six pages above in order, or jump into the ",[1007,29813,1019],{"href":1018}," to pick capabilities and land on the exact pages you need. For the deeper structural view — how the core, the services, and the satellite packages compose — see ",[1007,29816,5],{"href":29817},"..\u002Farchitecture\u002F",[2645,29819,6909],{},{"title":1037,"searchDepth":1038,"depth":1038,"links":29821},[29822,29823,29824,29825,29826,29827,29828,29829],{"id":29039,"depth":1041,"text":29040},{"id":29100,"depth":1041,"text":29101},{"id":29124,"depth":1041,"text":29125},{"id":29159,"depth":1041,"text":29160},{"id":29531,"depth":1041,"text":29532},{"id":29640,"depth":1041,"text":29641},{"id":15362,"depth":1041,"text":15363},{"id":1454,"depth":1041,"text":1455},"The always-on foundation of every noy-db install — vaults and collections, client-side encryption, pluggable stores, keyrings, schema validation, and queries.",{},{"title":10,"icon":19},{"title":29010,"description":29830},"oWhzL77duOnUsN2hHCZ2CCvaw0f8nAykHYHfxdG6MwU",{"id":29836,"title":129,"access":12,"apiToc":929,"body":29837,"category":1042,"description":30158,"extension":1044,"meta":30159,"navigation":1042,"nest":3303,"path":130,"seo":30160,"sinceVersion":1042,"status":1049,"stem":131,"subtitle":1042,"tier":1473,"tocExternal":1042,"__hash__":30161},"docs\u002Ffamilies\u002Fas\u002Faws-s3.md",{"type":931,"value":29838,"toc":30153},[29839,29841,29854,29858,29890,29894,30118,30135,30137,30150],[934,29840,129],{"id":129},[1061,29842,29843],{},[937,29844,29845,1368,29850,29853],{},[950,29846,29847],{},[941,29848,29849],{},"@noy-db\u002Fas-aws-s3",[941,29851,29852],{},"pnpm add @noy-db\u002Fas-aws-s3","\nHolds blob bytes as native, presigned-or-public S3 objects.",[1022,29855,29857],{"id":29856},"what-it-does","What it does",[937,29859,29860,29863,29864,29866,29867,29870,29871,29873,29874,2993,29877,29879,29880,29883,29884,29886,29887,29889],{},[941,29861,29862],{},"as-aws-s3"," is the one ",[941,29865,1791],{}," package that is ",[950,29868,29869],{},"not a vault exporter"," —\nit implements the hub's ",[941,29872,20443],{}," contract, so blob bytes can be\nserved directly from S3 or a CDN instead of round-tripping through the\nzero-knowledge store. Where ",[941,29875,29876],{},"@noy-db\u002Fto-aws-s3",[941,29878,1779],{}," store\n(ciphertext envelopes in and out), this projection sees ",[950,29881,29882],{},"raw bytes"," and\nlives outside the zero-knowledge guarantee. Wiring it up is a deliberate,\nper-field opt-in — only blob fields declared ",[941,29885,20451],{}," in a collection's\nblob config route their bytes here — and it sits outside the\n",[941,29888,11422],{}," gate entirely.",[1022,29891,29893],{"id":29892},"usage","Usage",[2217,29895,29897],{"className":2219,"code":29896,"language":2221,"meta":1037,"style":1037},"import { asAwsS3 } from '@noy-db\u002Fas-aws-s3'\n\nconst objects = asAwsS3({ bucket: 'acme-public-assets', region: 'eu-west-1' })\n\nawait objects.putObject('logos\u002Facme.png', bytes, { contentType: 'image\u002Fpng', public: true })\nobjects.publicUrl('logos\u002Facme.png')                                \u002F\u002F stable CDN\u002FS3 URL\nawait objects.objectUrl('docs\u002Fx.pdf')                               \u002F\u002F presigned GET (time-limited)\nawait objects.putUrl('videos\u002Fx.mp4', { contentType: 'video\u002Fmp4' })  \u002F\u002F presigned PUT (direct upload)\n",[941,29898,29899,29918,29922,29967,29971,30026,30050,30076],{"__ignoreMap":1037},[2225,29900,29901,29903,29905,29908,29910,29912,29914,29916],{"class":2227,"line":2228},[2225,29902,2232],{"class":2231},[2225,29904,2236],{"class":2235},[2225,29906,29907],{"class":2239}," asAwsS3",[2225,29909,2243],{"class":2235},[2225,29911,2246],{"class":2231},[2225,29913,2249],{"class":2235},[2225,29915,29849],{"class":2252},[2225,29917,2256],{"class":2235},[2225,29919,29920],{"class":2227,"line":1041},[2225,29921,2285],{"emptyLinePlaceholder":2284},[2225,29923,29924,29926,29929,29931,29933,29935,29937,29940,29942,29944,29947,29949,29951,29954,29956,29958,29961,29963,29965],{"class":2227,"line":1038},[2225,29925,2775],{"class":2774},[2225,29927,29928],{"class":2239}," objects ",[2225,29930,2781],{"class":2235},[2225,29932,29907],{"class":2291},[2225,29934,2789],{"class":2239},[2225,29936,2792],{"class":2235},[2225,29938,29939],{"class":2307}," bucket",[2225,29941,2311],{"class":2235},[2225,29943,2249],{"class":2235},[2225,29945,29946],{"class":2252},"acme-public-assets",[2225,29948,2805],{"class":2235},[2225,29950,2298],{"class":2235},[2225,29952,29953],{"class":2307}," region",[2225,29955,2311],{"class":2235},[2225,29957,2249],{"class":2235},[2225,29959,29960],{"class":2252},"eu-west-1",[2225,29962,2805],{"class":2235},[2225,29964,2243],{"class":2235},[2225,29966,2362],{"class":2239},[2225,29968,29969],{"class":2227,"line":2288},[2225,29970,2285],{"emptyLinePlaceholder":2284},[2225,29972,29973,29975,29978,29980,29983,29985,29987,29990,29992,29994,29997,29999,30001,30004,30006,30008,30011,30013,30015,30018,30020,30022,30024],{"class":2227,"line":2304},[2225,29974,6500],{"class":2231},[2225,29976,29977],{"class":2239}," objects",[2225,29979,1520],{"class":2235},[2225,29981,29982],{"class":2291},"putObject",[2225,29984,2789],{"class":2239},[2225,29986,2805],{"class":2235},[2225,29988,29989],{"class":2252},"logos\u002Facme.png",[2225,29991,2805],{"class":2235},[2225,29993,2298],{"class":2235},[2225,29995,29996],{"class":2239}," bytes",[2225,29998,2298],{"class":2235},[2225,30000,2236],{"class":2235},[2225,30002,30003],{"class":2307}," contentType",[2225,30005,2311],{"class":2235},[2225,30007,2249],{"class":2235},[2225,30009,30010],{"class":2252},"image\u002Fpng",[2225,30012,2805],{"class":2235},[2225,30014,2298],{"class":2235},[2225,30016,30017],{"class":2307}," public",[2225,30019,2311],{"class":2235},[2225,30021,2322],{"class":2321},[2225,30023,2243],{"class":2235},[2225,30025,2362],{"class":2239},[2225,30027,30028,30031,30033,30036,30038,30040,30042,30044,30047],{"class":2227,"line":2356},[2225,30029,30030],{"class":2239},"objects",[2225,30032,1520],{"class":2235},[2225,30034,30035],{"class":2291},"publicUrl",[2225,30037,2789],{"class":2239},[2225,30039,2805],{"class":2235},[2225,30041,29989],{"class":2252},[2225,30043,2805],{"class":2235},[2225,30045,30046],{"class":2239},")                                ",[2225,30048,30049],{"class":2763},"\u002F\u002F stable CDN\u002FS3 URL\n",[2225,30051,30052,30054,30056,30058,30061,30063,30065,30068,30070,30073],{"class":2227,"line":2830},[2225,30053,6500],{"class":2231},[2225,30055,29977],{"class":2239},[2225,30057,1520],{"class":2235},[2225,30059,30060],{"class":2291},"objectUrl",[2225,30062,2789],{"class":2239},[2225,30064,2805],{"class":2235},[2225,30066,30067],{"class":2252},"docs\u002Fx.pdf",[2225,30069,2805],{"class":2235},[2225,30071,30072],{"class":2239},")                               ",[2225,30074,30075],{"class":2763},"\u002F\u002F presigned GET (time-limited)\n",[2225,30077,30078,30080,30082,30084,30087,30089,30091,30094,30096,30098,30100,30102,30104,30106,30109,30111,30113,30115],{"class":2227,"line":2861},[2225,30079,6500],{"class":2231},[2225,30081,29977],{"class":2239},[2225,30083,1520],{"class":2235},[2225,30085,30086],{"class":2291},"putUrl",[2225,30088,2789],{"class":2239},[2225,30090,2805],{"class":2235},[2225,30092,30093],{"class":2252},"videos\u002Fx.mp4",[2225,30095,2805],{"class":2235},[2225,30097,2298],{"class":2235},[2225,30099,2236],{"class":2235},[2225,30101,30003],{"class":2307},[2225,30103,2311],{"class":2235},[2225,30105,2249],{"class":2235},[2225,30107,30108],{"class":2252},"video\u002Fmp4",[2225,30110,2805],{"class":2235},[2225,30112,2243],{"class":2235},[2225,30114,27226],{"class":2239},[2225,30116,30117],{"class":2763},"\u002F\u002F presigned PUT (direct upload)\n",[937,30119,30120,30121,30123,30124,30126,30127,968,30129,2462,30132,1520],{},"Prefer presigned (",[941,30122,30060],{},") over ",[941,30125,12],{}," for anything sensitive —\npublic objects are not crypto-shreddable. Requires the peer dependencies\n",[941,30128,1086],{},[941,30130,30131],{},"@aws-sdk\u002Fclient-s3",[941,30133,30134],{},"@aws-sdk\u002Fs3-request-presigner",[1022,30136,2621],{"id":2620},[1111,30138,30139,30144],{},[1114,30140,30141],{},[1007,30142,30143],{"href":2634},"as-* family",[1114,30145,30146,30149],{},[1007,30147,149],{"href":30148},".\u002Fnoydb"," — the encrypted, zero-knowledge-preserving alternative",[2645,30151,30152],{},"html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .sfNiH, html code.shiki .sfNiH{--shiki-light:#FF5370;--shiki-default:#FF9CAC;--shiki-dark:#FF9CAC}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":1037,"searchDepth":1038,"depth":1038,"links":30154},[30155,30156,30157],{"id":29856,"depth":1041,"text":29857},{"id":29892,"depth":1041,"text":29893},{"id":2620,"depth":1041,"text":2621},"AWS S3 object projection — serves blob bytes as native, directly-consumable S3 objects instead of encrypted envelopes.",{},{"title":129,"description":30158},"C1LwvHgQrsagasgnhhzWgdtta9CS8_ZjNohwgk-gx0g",{"id":30163,"title":133,"access":12,"apiToc":929,"body":30164,"category":1042,"description":30454,"extension":1044,"meta":30455,"navigation":1042,"nest":3303,"path":134,"seo":30456,"sinceVersion":1042,"status":1049,"stem":135,"subtitle":1042,"tier":1473,"tocExternal":1042,"__hash__":30457},"docs\u002Ffamilies\u002Fas\u002Fblob.md",{"type":931,"value":30165,"toc":30449},[30166,30168,30185,30187,30208,30210,30331,30427,30429,30446],[934,30167,133],{"id":133},[1061,30169,30170],{},[937,30171,30172,1368,30177,30180,30181,30184],{},[950,30173,30174],{},[941,30175,30176],{},"@noy-db\u002Fas-blob",[941,30178,30179],{},"pnpm add @noy-db\u002Fas-blob","\nPulls one blob out of a record's ",[941,30182,30183],{},"BlobSet"," as its native MIME bytes.",[1022,30186,29857],{"id":29856},[937,30188,30189,30192,30193,30196,30197,30200,30201,30204,30205,1520],{},[941,30190,30191],{},"as-blob"," extracts a single attachment — \"give me this PDF, not the\nwhole vault\" — as raw bytes plus its stored MIME type and filename. A\nsymmetric ",[941,30194,30195],{},"fromBytes()"," reader writes bytes back into a slot; because\nsingle-slot writes have no meaningful \"merge\", its policy alphabet is\n",[941,30198,30199],{},"'replace' | 'insert-only'"," (default ",[941,30202,30203],{},"'replace'",") rather than the\nthree-way policy other readers use, and the plan reports\n",[941,30206,30207],{},"status: 'added' | 'modified'",[1022,30209,29893],{"id":29892},[2217,30211,30213],{"className":2219,"code":30212,"language":2221,"meta":1037,"style":1037},"import { toBytes } from '@noy-db\u002Fas-blob'\n\nconst { bytes, mime, filename } = await toBytes(vault, {\n  collection: 'invoices',\n  id: '01HXX...',\n  slot: 'raw', \u002F\u002F optional, defaults to 'raw'\n})\n\u002F\u002F bytes: Uint8Array — decrypted PDF\u002FPNG\u002Fwhatever\n",[941,30214,30215,30234,30238,30271,30286,30301,30320,30326],{"__ignoreMap":1037},[2225,30216,30217,30219,30221,30224,30226,30228,30230,30232],{"class":2227,"line":2228},[2225,30218,2232],{"class":2231},[2225,30220,2236],{"class":2235},[2225,30222,30223],{"class":2239}," toBytes",[2225,30225,2243],{"class":2235},[2225,30227,2246],{"class":2231},[2225,30229,2249],{"class":2235},[2225,30231,30176],{"class":2252},[2225,30233,2256],{"class":2235},[2225,30235,30236],{"class":2227,"line":1041},[2225,30237,2285],{"emptyLinePlaceholder":2284},[2225,30239,30240,30242,30244,30246,30248,30251,30253,30256,30258,30260,30262,30264,30267,30269],{"class":2227,"line":1038},[2225,30241,2775],{"class":2774},[2225,30243,2236],{"class":2235},[2225,30245,29996],{"class":2239},[2225,30247,2298],{"class":2235},[2225,30249,30250],{"class":2239}," mime",[2225,30252,2298],{"class":2235},[2225,30254,30255],{"class":2239}," filename ",[2225,30257,2359],{"class":2235},[2225,30259,6346],{"class":2235},[2225,30261,2784],{"class":2231},[2225,30263,30223],{"class":2291},[2225,30265,30266],{"class":2239},"(vault",[2225,30268,2298],{"class":2235},[2225,30270,2301],{"class":2235},[2225,30272,30273,30276,30278,30280,30282,30284],{"class":2227,"line":2288},[2225,30274,30275],{"class":2307},"  collection",[2225,30277,2311],{"class":2235},[2225,30279,2249],{"class":2235},[2225,30281,6487],{"class":2252},[2225,30283,2805],{"class":2235},[2225,30285,1707],{"class":2235},[2225,30287,30288,30290,30292,30294,30297,30299],{"class":2227,"line":2304},[2225,30289,28371],{"class":2307},[2225,30291,2311],{"class":2235},[2225,30293,2249],{"class":2235},[2225,30295,30296],{"class":2252},"01HXX...",[2225,30298,2805],{"class":2235},[2225,30300,1707],{"class":2235},[2225,30302,30303,30306,30308,30310,30313,30315,30317],{"class":2227,"line":2356},[2225,30304,30305],{"class":2307},"  slot",[2225,30307,2311],{"class":2235},[2225,30309,2249],{"class":2235},[2225,30311,30312],{"class":2252},"raw",[2225,30314,2805],{"class":2235},[2225,30316,2298],{"class":2235},[2225,30318,30319],{"class":2763}," \u002F\u002F optional, defaults to 'raw'\n",[2225,30321,30322,30324],{"class":2227,"line":2830},[2225,30323,2359],{"class":2235},[2225,30325,2362],{"class":2239},[2225,30327,30328],{"class":2227,"line":2861},[2225,30329,30330],{"class":2763},"\u002F\u002F bytes: Uint8Array — decrypted PDF\u002FPNG\u002Fwhatever\n",[2217,30332,30334],{"className":2219,"code":30333,"language":2221,"meta":1037,"style":1037},"import { fromBytes } from '@noy-db\u002Fas-blob'\n\nconst plan = await fromBytes(vault, bytes, { collection: 'invoices', id: '01HXX...' })\n\u002F\u002F → AsBlobImportPlan { plan: VaultDiff, policy: 'replace', apply() }\nawait plan.apply()\n",[941,30335,30336,30355,30359,30408,30413],{"__ignoreMap":1037},[2225,30337,30338,30340,30342,30345,30347,30349,30351,30353],{"class":2227,"line":2228},[2225,30339,2232],{"class":2231},[2225,30341,2236],{"class":2235},[2225,30343,30344],{"class":2239}," fromBytes",[2225,30346,2243],{"class":2235},[2225,30348,2246],{"class":2231},[2225,30350,2249],{"class":2235},[2225,30352,30176],{"class":2252},[2225,30354,2256],{"class":2235},[2225,30356,30357],{"class":2227,"line":1041},[2225,30358,2285],{"emptyLinePlaceholder":2284},[2225,30360,30361,30363,30366,30368,30370,30372,30374,30376,30378,30380,30382,30384,30386,30388,30390,30392,30394,30396,30398,30400,30402,30404,30406],{"class":2227,"line":1038},[2225,30362,2775],{"class":2774},[2225,30364,30365],{"class":2239}," plan ",[2225,30367,2781],{"class":2235},[2225,30369,2784],{"class":2231},[2225,30371,30344],{"class":2291},[2225,30373,30266],{"class":2239},[2225,30375,2298],{"class":2235},[2225,30377,29996],{"class":2239},[2225,30379,2298],{"class":2235},[2225,30381,2236],{"class":2235},[2225,30383,7061],{"class":2307},[2225,30385,2311],{"class":2235},[2225,30387,2249],{"class":2235},[2225,30389,6487],{"class":2252},[2225,30391,2805],{"class":2235},[2225,30393,2298],{"class":2235},[2225,30395,7070],{"class":2307},[2225,30397,2311],{"class":2235},[2225,30399,2249],{"class":2235},[2225,30401,30296],{"class":2252},[2225,30403,2805],{"class":2235},[2225,30405,2243],{"class":2235},[2225,30407,2362],{"class":2239},[2225,30409,30410],{"class":2227,"line":2288},[2225,30411,30412],{"class":2763},"\u002F\u002F → AsBlobImportPlan { plan: VaultDiff, policy: 'replace', apply() }\n",[2225,30414,30415,30417,30420,30422,30425],{"class":2227,"line":2304},[2225,30416,6500],{"class":2231},[2225,30418,30419],{"class":2239}," plan",[2225,30421,1520],{"class":2235},[2225,30423,30424],{"class":2291},"apply",[2225,30426,8146],{"class":2239},[1022,30428,2621],{"id":2620},[1111,30430,30431,30435,30441],{},[1114,30432,30433],{},[1007,30434,30143],{"href":2634},[1114,30436,30437,30440],{},[1007,30438,165],{"href":30439},".\u002Fzip"," — records + every attached blob together",[1114,30442,30443,30445],{},[1007,30444,149],{"href":30148}," — encrypted-tier alternative",[2645,30447,30448],{},"html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":1037,"searchDepth":1038,"depth":1038,"links":30450},[30451,30452,30453],{"id":29856,"depth":1041,"text":29857},{"id":29892,"depth":1041,"text":29893},{"id":2620,"depth":1041,"text":2621},"Single attachment extraction — one PDF, JPEG, or other blob as native MIME bytes.",{},{"title":133,"description":30454},"rOK00GLjwmKqj1dmAwHtc30mKES5IQUwlqB3ef28itQ",{"id":30459,"title":137,"access":12,"apiToc":929,"body":30460,"category":1042,"description":30739,"extension":1044,"meta":30740,"navigation":1042,"nest":3303,"path":138,"seo":30741,"sinceVersion":1042,"status":1049,"stem":139,"subtitle":1042,"tier":1050,"tocExternal":1042,"__hash__":30742},"docs\u002Ffamilies\u002Fas\u002Fcsv.md",{"type":931,"value":30461,"toc":30734},[30462,30464,30477,30479,30521,30523,30612,30701,30712,30714,30732],[934,30463,137],{"id":137},[1061,30465,30466],{},[937,30467,30468,1368,30473,30476],{},[950,30469,30470],{},[941,30471,30472],{},"@noy-db\u002Fas-csv",[941,30474,30475],{},"pnpm add @noy-db\u002Fas-csv","\nDecrypts records from one collection and formats them as RFC 4180 CSV.",[1022,30478,29857],{"id":29856},[937,30480,30481,5296,30484,30487,30488,968,30491,1707,30494,30497,30498,968,30500,968,30502,4765,30505,30508,30509,1625,30511,30513,30514,30516,30517,30520],{},[941,30482,30483],{},"as-csv",[950,30485,30486],{},"reference implementation"," of the plaintext-tier\nshape — every other record formatter in the family (",[941,30489,30490],{},"as-json",[941,30492,30493],{},"as-xml",[941,30495,30496],{},"as-sql",", …) follows the same entry-point + authorization-gate structure.\nStrings containing ",[941,30499,2298],{},[941,30501,6620],{},[941,30503,30504],{},"\\r",[941,30506,30507],{},"\\n"," are quoted; embedded quotes are\ndoubled. ",[941,30510,6651],{},[941,30512,18316],{}," become empty fields, ",[941,30515,26712],{}," renders as ISO\n8601, and objects\u002Farrays are ",[941,30518,30519],{},"JSON.stringify","'d. A symmetric\ndecoder parses a CSV back into an import plan.",[1022,30522,29893],{"id":29892},[2217,30524,30526],{"className":2219,"code":30525,"language":2221,"meta":1037,"style":1037},"import { asCsv } from '@noy-db\u002Fas-csv'\n\nconst csv = await vault.export(asCsv(), { collections: ['invoices'] })\n\u002F\u002F id,client,amount,status\n\u002F\u002F inv-1,Globex,1500,paid\n\u002F\u002F inv-2,\"Acme, Inc.\",2400,draft\n",[941,30527,30528,30547,30551,30597,30602,30607],{"__ignoreMap":1037},[2225,30529,30530,30532,30534,30537,30539,30541,30543,30545],{"class":2227,"line":2228},[2225,30531,2232],{"class":2231},[2225,30533,2236],{"class":2235},[2225,30535,30536],{"class":2239}," asCsv",[2225,30538,2243],{"class":2235},[2225,30540,2246],{"class":2231},[2225,30542,2249],{"class":2235},[2225,30544,30472],{"class":2252},[2225,30546,2256],{"class":2235},[2225,30548,30549],{"class":2227,"line":1041},[2225,30550,2285],{"emptyLinePlaceholder":2284},[2225,30552,30553,30555,30558,30560,30562,30564,30566,30568,30570,30573,30575,30577,30579,30581,30583,30585,30587,30589,30591,30593,30595],{"class":2227,"line":1038},[2225,30554,2775],{"class":2774},[2225,30556,30557],{"class":2239}," csv ",[2225,30559,2781],{"class":2235},[2225,30561,2784],{"class":2231},[2225,30563,6466],{"class":2239},[2225,30565,1520],{"class":2235},[2225,30567,8080],{"class":2291},[2225,30569,2789],{"class":2239},[2225,30571,30572],{"class":2291},"asCsv",[2225,30574,4228],{"class":2239},[2225,30576,2298],{"class":2235},[2225,30578,2236],{"class":2235},[2225,30580,22066],{"class":2307},[2225,30582,2311],{"class":2235},[2225,30584,8667],{"class":2239},[2225,30586,2805],{"class":2235},[2225,30588,6487],{"class":2252},[2225,30590,2805],{"class":2235},[2225,30592,8890],{"class":2239},[2225,30594,2359],{"class":2235},[2225,30596,2362],{"class":2239},[2225,30598,30599],{"class":2227,"line":2288},[2225,30600,30601],{"class":2763},"\u002F\u002F id,client,amount,status\n",[2225,30603,30604],{"class":2227,"line":2304},[2225,30605,30606],{"class":2763},"\u002F\u002F inv-1,Globex,1500,paid\n",[2225,30608,30609],{"class":2227,"line":2356},[2225,30610,30611],{"class":2763},"\u002F\u002F inv-2,\"Acme, Inc.\",2400,draft\n",[2217,30613,30615],{"className":2219,"code":30614,"language":2221,"meta":1037,"style":1037},"import { asCsv } from '@noy-db\u002Fas-csv'\n\nconst plan = await vault.import(asCsv(), csv, { collection: 'invoices' })\n\u002F\u002F → ImportPlan { plan: VaultDiff, policy: 'merge', apply() }\nawait plan.apply()\n",[941,30616,30617,30635,30639,30684,30689],{"__ignoreMap":1037},[2225,30618,30619,30621,30623,30625,30627,30629,30631,30633],{"class":2227,"line":2228},[2225,30620,2232],{"class":2231},[2225,30622,2236],{"class":2235},[2225,30624,30536],{"class":2239},[2225,30626,2243],{"class":2235},[2225,30628,2246],{"class":2231},[2225,30630,2249],{"class":2235},[2225,30632,30472],{"class":2252},[2225,30634,2256],{"class":2235},[2225,30636,30637],{"class":2227,"line":1041},[2225,30638,2285],{"emptyLinePlaceholder":2284},[2225,30640,30641,30643,30645,30647,30649,30651,30653,30655,30657,30659,30661,30663,30666,30668,30670,30672,30674,30676,30678,30680,30682],{"class":2227,"line":1038},[2225,30642,2775],{"class":2774},[2225,30644,30365],{"class":2239},[2225,30646,2781],{"class":2235},[2225,30648,2784],{"class":2231},[2225,30650,6466],{"class":2239},[2225,30652,1520],{"class":2235},[2225,30654,2232],{"class":2291},[2225,30656,2789],{"class":2239},[2225,30658,30572],{"class":2291},[2225,30660,4228],{"class":2239},[2225,30662,2298],{"class":2235},[2225,30664,30665],{"class":2239}," csv",[2225,30667,2298],{"class":2235},[2225,30669,2236],{"class":2235},[2225,30671,7061],{"class":2307},[2225,30673,2311],{"class":2235},[2225,30675,2249],{"class":2235},[2225,30677,6487],{"class":2252},[2225,30679,2805],{"class":2235},[2225,30681,2243],{"class":2235},[2225,30683,2362],{"class":2239},[2225,30685,30686],{"class":2227,"line":2288},[2225,30687,30688],{"class":2763},"\u002F\u002F → ImportPlan { plan: VaultDiff, policy: 'merge', apply() }\n",[2225,30690,30691,30693,30695,30697,30699],{"class":2227,"line":2304},[2225,30692,6500],{"class":2231},[2225,30694,30419],{"class":2239},[2225,30696,1520],{"class":2235},[2225,30698,30424],{"class":2291},[2225,30700,8146],{"class":2239},[937,30702,30703,30704,30707,30708,1520],{},"Every call checks ",[941,30705,30706],{},"vault.assertCanExport('plaintext', 'csv')"," before\ndecrypting anything — see the ",[1007,30709,30711],{"href":30710},".\u002F#two-tiers-one-gate","family gate",[1022,30713,2621],{"id":2620},[1111,30715,30716,30720,30726],{},[1114,30717,30718],{},[1007,30719,30143],{"href":2634},[1114,30721,30722,30725],{},[1007,30723,141],{"href":30724},".\u002Fjson"," — structured, schema-aware alternative",[1114,30727,30728,30731],{},[1007,30729,157],{"href":30730},".\u002Fxlsx"," — multi-sheet spreadsheet with dictionary labels",[2645,30733,30448],{},{"title":1037,"searchDepth":1038,"depth":1038,"links":30735},[30736,30737,30738],{"id":29856,"depth":1041,"text":29857},{"id":29892,"depth":1041,"text":29893},{"id":2620,"depth":1041,"text":2621},"RFC 4180 CSV export of a single collection, with a symmetric reader for round-trip import.",{},{"title":137,"description":30739},"QxpxCAj0kAKDxHlgen-cCs51zrlT7fE0HXaZWFNY_rw",{"id":30744,"title":30745,"access":12,"apiToc":929,"body":30746,"category":1042,"description":31359,"extension":1044,"meta":31360,"navigation":31361,"nest":3303,"path":123,"seo":31362,"sinceVersion":1042,"status":1049,"stem":124,"subtitle":1042,"tier":1050,"tocExternal":1042,"__hash__":31363},"docs\u002Ffamilies\u002Fas\u002Findex.md","`@noy-db\u002Fas-*` — Portable-artefact exports",{"type":931,"value":30747,"toc":31345},[30748,30754,30765,30786,30790,30832,30839,30853,30862,30874,30880,30883,30949,30962,30966,30969,30973,31009,31013,31064,31068,31097,31101,31122,31126,31161,31165,31189,31193,31342],[934,30749,30751,30753],{"id":30750},"noy-dbas-portable-artefact-exports",[941,30752,9938],{}," — Portable-artefact exports",[1061,30755,30756],{},[937,30757,30758,30761,30762,30764],{},[950,30759,30760],{},"How data leaves the vault — and, for most packages, comes back in.","\nEach ",[941,30763,1791],{}," package takes an open vault and produces a portable\nartefact — spreadsheet, structured text, SQL dump, encrypted bundle —\nor the reverse.",[937,30766,960,30767,30769,30770,30776,30777,2601,30779,30781,30782,30785],{},[941,30768,1276],{}," prefix reads as ",[1092,30771,30772,30773,30775],{},"\"export ",[950,30774,4899],{}," XLSX \u002F CSV \u002F NoyDB.\""," This\nfamily is the read\u002Fwrite-side transform layer at the vault's edge: pure\nfunctions that turn decrypted records into a file-shaped artefact, and\n(for most formats) a symmetric reader that turns the artefact back into\nan import plan. Every call is gated by the caller's ",[941,30778,11422],{},[941,30780,11438],{}," on their ",[1007,30783,2705],{"href":30784},"..\u002F..\u002Fcore\u002F06-permissions-and-keyring","\n— the per-user record holding role, permissions, and wrapped keys.",[1022,30787,30789],{"id":30788},"two-tiers-one-gate","Two tiers, one gate",[937,30791,30792,30793,30795,30796,30799,30800,13571,30802,30804,30805,30808,30809,13571,30812,30814,30815,30817,30818,30820,30821,30824,30825,30827,30828,30831],{},"Every ",[941,30794,1791],{}," artefact sits in one of ",[950,30797,30798],{},"two tiers",", and both pass the same capability check on the\ncaller's ",[1007,30801,2705],{"href":30784},[950,30803,13574],{}," emits\n",[1092,30806,30807],{},"decrypted"," records in an open, portable shape (CSV, XLSX, JSON, NDJSON, XML, SQL, …) — gated by\n",[941,30810,30811],{},"exportCapability: 'plaintext'",[950,30813,13585],{}," is the encrypted, re-importable ",[941,30816,1402],{},"\ncontainer (",[1007,30819,149],{"href":30148},"), which never leaves ciphertext — gated by ",[941,30822,30823],{},"exportCapability: 'bundle'","\n(and the matching ",[941,30826,11438],{}," to read one back). A role without the tier's capability cannot\ninvoke the export at all. The cloud projection ",[1007,30829,129],{"href":30830},".\u002Faws-s3"," is the one exception — it\nwrites an object projection and is not vault-gated.",[1022,30833,30835,30836,30838],{"id":30834},"what-collections-costs","What ",[941,30837,2698],{}," costs",[937,30840,30841,30844,30845,30848,30849,30852],{},[941,30842,30843],{},"vault.export(fmt, { collections: ['invoices'] })"," always narrows ",[950,30846,30847],{},"what the\nartefact contains",". Whether it also narrows what the hub ",[1092,30850,30851],{},"reads"," depends on the\nhub version you have installed, so check yours before planning around it.",[937,30854,30855,30861],{},[950,30856,30857,30858,30860],{},"From ",[941,30859,1086],{}," 0.9.0"," the scope is passed down into the read: the hub lists\nonly the requested collections, so an excluded collection is neither fetched nor\ndecrypted, and the option is the exposure control it reads like.",[937,30863,30864,30870,30871,30873],{},[950,30865,30866,30867,30869],{},"Through ",[941,30868,1086],{}," 0.8.x"," it does not. Each collection in the vault is read\nand ",[950,30872,30807],{},", and the excluded ones are discarded afterwards.",[937,30875,30876,30877,30879],{},"::: warning On 0.8.x, this is an output filter and nothing more\nPassing ",[941,30878,2698],{}," on a large vault costs the same decryption work as\nexporting all of it. If you are reaching for it to keep a big collection out of\na hot path, or to limit how much plaintext the export touches, on 0.8.x it does\nneither. Correct for shaping the artefact, ineffective as a performance or\nexposure control — upgrade to 0.9.0 or later if you need the narrower read.\n:::",[937,30881,30882],{},"To settle which behaviour your installed hub has, ask the compiler rather than\nany prose, this page included:",[2217,30884,30886],{"className":2219,"code":30885,"language":2221,"meta":1037,"style":1037},"import type { ExportStreamOptions } from '@noy-db\u002Fhub'\n\n\u002F\u002F Compiles only on a hub whose export read narrows.\nconst scoped: ExportStreamOptions = { collections: ['invoices'] }\n",[941,30887,30888,30909,30913,30918],{"__ignoreMap":1037},[2225,30889,30890,30892,30894,30896,30899,30901,30903,30905,30907],{"class":2227,"line":2228},[2225,30891,2232],{"class":2231},[2225,30893,3399],{"class":2231},[2225,30895,2236],{"class":2235},[2225,30897,30898],{"class":2239}," ExportStreamOptions",[2225,30900,2243],{"class":2235},[2225,30902,2246],{"class":2231},[2225,30904,2249],{"class":2235},[2225,30906,1086],{"class":2252},[2225,30908,2256],{"class":2235},[2225,30910,30911],{"class":2227,"line":1041},[2225,30912,2285],{"emptyLinePlaceholder":2284},[2225,30914,30915],{"class":2227,"line":1038},[2225,30916,30917],{"class":2763},"\u002F\u002F Compiles only on a hub whose export read narrows.\n",[2225,30919,30920,30922,30925,30927,30929,30931,30933,30935,30937,30939,30941,30943,30945,30947],{"class":2227,"line":2288},[2225,30921,2775],{"class":2774},[2225,30923,30924],{"class":2239}," scoped",[2225,30926,2311],{"class":2235},[2225,30928,30898],{"class":6342},[2225,30930,6346],{"class":2235},[2225,30932,2236],{"class":2235},[2225,30934,22066],{"class":2307},[2225,30936,2311],{"class":2235},[2225,30938,8667],{"class":2239},[2225,30940,2805],{"class":2235},[2225,30942,6487],{"class":2252},[2225,30944,2805],{"class":2235},[2225,30946,8890],{"class":2239},[2225,30948,7351],{"class":2235},[937,30950,30951,30952,964,30955,30958,30959,30961],{},"Where the read does not narrow, that line fails with ",[941,30953,30954],{},"TS2353",[941,30956,30957],{},"'collections' does not exist in type 'ExportStreamOptions'",". ⛔ Do not answer this by grepping\nthe type for the word: ",[941,30960,2698],{}," appears in that interface's own prose on\nversions that have no such field, so a text search says yes when the answer is\nno. The assignment is the test; excess-property checking is what makes it one.",[1022,30963,30965],{"id":30964},"catalog","Catalog",[937,30967,30968],{},"10 packages across five shapes:",[1591,30970,30972],{"id":30971},"spreadsheet","Spreadsheet",[1338,30974,30975,30984],{},[1341,30976,30977],{},[1344,30978,30979,30981],{},[1347,30980,9530],{},[1347,30982,30983],{},"One-line",[1357,30985,30986,30996],{},[1344,30987,30988,30993],{},[1362,30989,30990],{},[1007,30991,137],{"href":30992},".\u002Fcsv",[1362,30994,30995],{},"RFC 4180 CSV per collection — the reference implementation of the plaintext-tier shape.",[1344,30997,30998,31002],{},[1362,30999,31000],{},[1007,31001,157],{"href":30730},[1362,31003,31004,31005,31008],{},"Multi-sheet ",[941,31006,31007],{},".xlsx"," with dictionary-label expansion and Unicode-safe shared strings.",[1591,31010,31012],{"id":31011},"structured-text","Structured text",[1338,31014,31015,31023],{},[1341,31016,31017],{},[1344,31018,31019,31021],{},[1347,31020,9530],{},[1347,31022,30983],{},[1357,31024,31025,31041,31054],{},[1344,31026,31027,31031],{},[1362,31028,31029],{},[1007,31030,141],{"href":30724},[1362,31032,31033,31034,1261,31037,31040],{},"Structured JSON grouped by collection, with ",[941,31035,31036],{},"pretty",[941,31038,31039],{},"includeMeta"," options.",[1344,31042,31043,31048],{},[1362,31044,31045],{},[1007,31046,145],{"href":31047},".\u002Fndjson",[1362,31049,31050,31051,1520],{},"Newline-delimited JSON, streamed record-by-record for pipelines and ",[941,31052,31053],{},"jq",[1344,31055,31056,31061],{},[1362,31057,31058],{},[1007,31059,161],{"href":31060},".\u002Fxml",[1362,31062,31063],{},"RFC-compliant escaped XML for legacy systems and banking batch imports.",[1591,31065,31067],{"id":31066},"database","Database",[1338,31069,31070,31078],{},[1341,31071,31072],{},[1344,31073,31074,31076],{},[1347,31075,9530],{},[1347,31077,30983],{},[1357,31079,31080],{},[1344,31081,31082,31087],{},[1362,31083,31084],{},[1007,31085,153],{"href":31086},".\u002Fsql",[1362,31088,31089,31090,6030,31093,31096],{},"Dialect-aware ",[941,31091,31092],{},"CREATE TABLE",[941,31094,31095],{},"INSERT"," dump for Postgres \u002F MySQL \u002F SQLite. Export-only.",[1591,31098,31100],{"id":31099},"cloud","Cloud",[1338,31102,31103,31111],{},[1341,31104,31105],{},[1344,31106,31107,31109],{},[1347,31108,9530],{},[1347,31110,30983],{},[1357,31112,31113],{},[1344,31114,31115,31119],{},[1362,31116,31117],{},[1007,31118,129],{"href":30830},[1362,31120,31121],{},"Object projection serving blob bytes as native S3 objects — not a vault exporter.",[1591,31123,31125],{"id":31124},"composite","Composite",[1338,31127,31128,31136],{},[1341,31129,31130],{},[1344,31131,31132,31134],{},[1347,31133,9530],{},[1347,31135,30983],{},[1357,31137,31138,31148],{},[1344,31139,31140,31145],{},[1362,31141,31142],{},[1007,31143,133],{"href":31144},".\u002Fblob",[1362,31146,31147],{},"Single attachment extraction — one PDF\u002FJPEG\u002Fetc. as native MIME bytes.",[1344,31149,31150,31154],{},[1362,31151,31152],{},[1007,31153,165],{"href":30439},[1362,31155,31156,31157,31160],{},"Records + attachments together as one ",[941,31158,31159],{},".zip",", optionally WinZip-AES-256 encrypted.",[1591,31162,31164],{"id":31163},"encrypted-bundle","Encrypted bundle",[1338,31166,31167,31175],{},[1341,31168,31169],{},[1344,31170,31171,31173],{},[1347,31172,9530],{},[1347,31174,30983],{},[1357,31176,31177],{},[1344,31178,31179,31183],{},[1362,31180,31181],{},[1007,31182,149],{"href":30148},[1362,31184,31185,31186,1520],{},"The sole encrypted-tier exporter — ciphertext in, ciphertext out via ",[941,31187,31188],{},"writePod()",[1022,31190,31192],{"id":31191},"compare","Compare",[1338,31194,31195,31209],{},[1341,31196,31197],{},[1344,31198,31199,31201,31204,31207],{},[1347,31200,9530],{},[1347,31202,31203],{},"Direction",[1347,31205,31206],{},"Round-trip?",[1347,31208,9542],{},[1357,31210,31211,31225,31238,31251,31264,31277,31290,31303,31316,31329],{},[1344,31212,31213,31217,31220,31222],{},[1362,31214,31215],{},[1007,31216,137],{"href":30992},[1362,31218,31219],{},"both",[1362,31221,9581],{},[1362,31223,31224],{},"reference plaintext-tier shape",[1344,31226,31227,31231,31233,31235],{},[1362,31228,31229],{},[1007,31230,157],{"href":30730},[1362,31232,31219],{},[1362,31234,9581],{},[1362,31236,31237],{},"dictionary-label inversion on import",[1344,31239,31240,31244,31246,31248],{},[1362,31241,31242],{},[1007,31243,141],{"href":30724},[1362,31245,31219],{},[1362,31247,9581],{},[1362,31249,31250],{},"grouped by collection",[1344,31252,31253,31257,31259,31261],{},[1362,31254,31255],{},[1007,31256,145],{"href":31047},[1362,31258,31219],{},[1362,31260,9581],{},[1362,31262,31263],{},"streaming, low memory",[1344,31265,31266,31270,31272,31274],{},[1362,31267,31268],{},[1007,31269,161],{"href":31060},[1362,31271,31219],{},[1362,31273,9581],{},[1362,31275,31276],{},"RFC-compliant escaping, legacy systems",[1344,31278,31279,31283,31285,31287],{},[1362,31280,31281],{},[1007,31282,153],{"href":31086},[1362,31284,8080],{},[1362,31286,9559],{},[1362,31288,31289],{},"dialect-aware, export-only, no reader",[1344,31291,31292,31296,31298,31300],{},[1362,31293,31294],{},[1007,31295,129],{"href":30830},[1362,31297,31219],{},[1362,31299,9559],{},[1362,31301,31302],{},"object projection, not vault-gated",[1344,31304,31305,31309,31311,31313],{},[1362,31306,31307],{},[1007,31308,133],{"href":31144},[1362,31310,31219],{},[1362,31312,9581],{},[1362,31314,31315],{},"single attachment, native MIME",[1344,31317,31318,31322,31324,31326],{},[1362,31319,31320],{},[1007,31321,165],{"href":30439},[1362,31323,31219],{},[1362,31325,9581],{},[1362,31327,31328],{},"records + blobs, optional encryption",[1344,31330,31331,31335,31337,31339],{},[1362,31332,31333],{},[1007,31334,149],{"href":30148},[1362,31336,8080],{},[1362,31338,9559],{},[1362,31340,31341],{},"whole-vault ciphertext, encrypted tier",[2645,31343,31344],{},"html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":1037,"searchDepth":1038,"depth":1038,"links":31346},[31347,31348,31350,31358],{"id":30788,"depth":1041,"text":30789},{"id":30834,"depth":1041,"text":31349},"What collections costs",{"id":30964,"depth":1041,"text":30965,"children":31351},[31352,31353,31354,31355,31356,31357],{"id":30971,"depth":1038,"text":30972},{"id":31011,"depth":1038,"text":31012},{"id":31066,"depth":1038,"text":31067},{"id":31099,"depth":1038,"text":31100},{"id":31124,"depth":1038,"text":31125},{"id":31163,"depth":1038,"text":31164},{"id":31191,"depth":1041,"text":31192},"The import\u002Fexport catalog: every as-* format — CSV, XLSX, JSON, NDJSON, XML, SQL, blob, ZIP, and the encrypted .noydb bundle.",{},{"title":122,"icon":127},{"title":30745,"description":31359},"2YcPBcSPGWum95j-uGVdymhDuZbkOUxXh-tWeah7HIQ",{"id":31365,"title":141,"access":12,"apiToc":929,"body":31366,"category":1042,"description":31600,"extension":1044,"meta":31601,"navigation":1042,"nest":3303,"path":142,"seo":31602,"sinceVersion":1042,"status":1049,"stem":143,"subtitle":1042,"tier":1050,"tocExternal":1042,"__hash__":31603},"docs\u002Ffamilies\u002Fas\u002Fjson.md",{"type":931,"value":31367,"toc":31595},[31368,31370,31383,31385,31401,31403,31474,31567,31574,31576,31592],[934,31369,141],{"id":141},[1061,31371,31372],{},[937,31373,31374,1368,31379,31382],{},[950,31375,31376],{},[941,31377,31378],{},"@noy-db\u002Fas-json",[941,31380,31381],{},"pnpm add @noy-db\u002Fas-json","\nDecrypts records and emits one structured JSON document per vault.",[1022,31384,29857],{"id":29856},[937,31386,31387,31389,31390,997,31393,31396,31397,31400],{},[941,31388,30490],{}," groups decrypted records by collection into a single JSON\ndocument, with ",[941,31391,31392],{},"pretty: true\u002Ffalse",[941,31394,31395],{},"includeMeta: true\u002Ffalse"," output\noptions. Two symmetric readers parse an artefact back into an import\nplan: the decoder accepts a serialized JSON string, and ",[941,31398,31399],{},"fromObject()"," for\nan already-parsed document.",[1022,31402,29893],{"id":29892},[2217,31404,31406],{"className":2219,"code":31405,"language":2221,"meta":1037,"style":1037},"import { asJson } from '@noy-db\u002Fas-json'\n\nconst json = await vault.export(asJson({ pretty: true }), {})\n",[941,31407,31408,31427,31431],{"__ignoreMap":1037},[2225,31409,31410,31412,31414,31417,31419,31421,31423,31425],{"class":2227,"line":2228},[2225,31411,2232],{"class":2231},[2225,31413,2236],{"class":2235},[2225,31415,31416],{"class":2239}," asJson",[2225,31418,2243],{"class":2235},[2225,31420,2246],{"class":2231},[2225,31422,2249],{"class":2235},[2225,31424,31378],{"class":2252},[2225,31426,2256],{"class":2235},[2225,31428,31429],{"class":2227,"line":1041},[2225,31430,2285],{"emptyLinePlaceholder":2284},[2225,31432,31433,31435,31438,31440,31442,31444,31446,31448,31450,31453,31455,31457,31460,31462,31464,31466,31468,31470,31472],{"class":2227,"line":1038},[2225,31434,2775],{"class":2774},[2225,31436,31437],{"class":2239}," json ",[2225,31439,2781],{"class":2235},[2225,31441,2784],{"class":2231},[2225,31443,6466],{"class":2239},[2225,31445,1520],{"class":2235},[2225,31447,8080],{"class":2291},[2225,31449,2789],{"class":2239},[2225,31451,31452],{"class":2291},"asJson",[2225,31454,2789],{"class":2239},[2225,31456,2792],{"class":2235},[2225,31458,31459],{"class":2307}," pretty",[2225,31461,2311],{"class":2235},[2225,31463,2322],{"class":2321},[2225,31465,2243],{"class":2235},[2225,31467,1381],{"class":2239},[2225,31469,2298],{"class":2235},[2225,31471,8896],{"class":2235},[2225,31473,2362],{"class":2239},[2217,31475,31477],{"className":2219,"code":31476,"language":2221,"meta":1037,"style":1037},"import { asJson } from '@noy-db\u002Fas-json'\n\nconst plan = await vault.import(asJson(), json, { collections: ['invoices'] })\n\u002F\u002F → AsJSONImportPlan { plan: VaultDiff, policy: 'merge', apply() }\nawait plan.apply()\n",[941,31478,31479,31497,31501,31550,31555],{"__ignoreMap":1037},[2225,31480,31481,31483,31485,31487,31489,31491,31493,31495],{"class":2227,"line":2228},[2225,31482,2232],{"class":2231},[2225,31484,2236],{"class":2235},[2225,31486,31416],{"class":2239},[2225,31488,2243],{"class":2235},[2225,31490,2246],{"class":2231},[2225,31492,2249],{"class":2235},[2225,31494,31378],{"class":2252},[2225,31496,2256],{"class":2235},[2225,31498,31499],{"class":2227,"line":1041},[2225,31500,2285],{"emptyLinePlaceholder":2284},[2225,31502,31503,31505,31507,31509,31511,31513,31515,31517,31519,31521,31523,31525,31528,31530,31532,31534,31536,31538,31540,31542,31544,31546,31548],{"class":2227,"line":1038},[2225,31504,2775],{"class":2774},[2225,31506,30365],{"class":2239},[2225,31508,2781],{"class":2235},[2225,31510,2784],{"class":2231},[2225,31512,6466],{"class":2239},[2225,31514,1520],{"class":2235},[2225,31516,2232],{"class":2291},[2225,31518,2789],{"class":2239},[2225,31520,31452],{"class":2291},[2225,31522,4228],{"class":2239},[2225,31524,2298],{"class":2235},[2225,31526,31527],{"class":2239}," json",[2225,31529,2298],{"class":2235},[2225,31531,2236],{"class":2235},[2225,31533,22066],{"class":2307},[2225,31535,2311],{"class":2235},[2225,31537,8667],{"class":2239},[2225,31539,2805],{"class":2235},[2225,31541,6487],{"class":2252},[2225,31543,2805],{"class":2235},[2225,31545,8890],{"class":2239},[2225,31547,2359],{"class":2235},[2225,31549,2362],{"class":2239},[2225,31551,31552],{"class":2227,"line":2288},[2225,31553,31554],{"class":2763},"\u002F\u002F → AsJSONImportPlan { plan: VaultDiff, policy: 'merge', apply() }\n",[2225,31556,31557,31559,31561,31563,31565],{"class":2227,"line":2304},[2225,31558,6500],{"class":2231},[2225,31560,30419],{"class":2239},[2225,31562,1520],{"class":2235},[2225,31564,30424],{"class":2291},[2225,31566,8146],{"class":2239},[937,31568,30703,31569,30707,31572,1520],{},[941,31570,31571],{},"vault.assertCanExport('plaintext', 'json')",[1007,31573,30711],{"href":30710},[1022,31575,2621],{"id":2620},[1111,31577,31578,31582,31587],{},[1114,31579,31580],{},[1007,31581,30143],{"href":2634},[1114,31583,31584,31586],{},[1007,31585,137],{"href":30992}," — flat, spreadsheet-friendly alternative",[1114,31588,31589,31591],{},[1007,31590,145],{"href":31047}," — streaming, one-record-at-a-time alternative",[2645,31593,31594],{},"html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .sfNiH, html code.shiki .sfNiH{--shiki-light:#FF5370;--shiki-default:#FF9CAC;--shiki-dark:#FF9CAC}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}",{"title":1037,"searchDepth":1038,"depth":1038,"links":31596},[31597,31598,31599],{"id":29856,"depth":1041,"text":29857},{"id":29892,"depth":1041,"text":29893},{"id":2620,"depth":1041,"text":2621},"Structured JSON export of a vault, grouped by collection, with round-trip readers for import.",{},{"title":141,"description":31600},"mAUL1oV_Uqd528v2A3IiD6kCjw1YIFQ8lEKUvDgX7Yg",{"id":31605,"title":145,"access":12,"apiToc":929,"body":31606,"category":1042,"description":31949,"extension":1044,"meta":31950,"navigation":1042,"nest":3303,"path":146,"seo":31951,"sinceVersion":1042,"status":1049,"stem":147,"subtitle":1042,"tier":1473,"tocExternal":1042,"__hash__":31952},"docs\u002Ffamilies\u002Fas\u002Fndjson.md",{"type":931,"value":31607,"toc":31944},[31608,31610,31623,31625,31649,31695,31697,31781,31853,31923,31925,31941],[934,31609,145],{"id":145},[1061,31611,31612],{},[937,31613,31614,1368,31619,31622],{},[950,31615,31616],{},[941,31617,31618],{},"@noy-db\u002Fas-ndjson",[941,31620,31621],{},"pnpm add @noy-db\u002Fas-ndjson","\nStreaming plaintext export — one JSON record per line.",[1022,31624,29857],{"id":29856},[937,31626,31627,31630,31631,31633,31634,31637,31638,997,31641,31644,31645,31648],{},[941,31628,31629],{},"as-ndjson"," is built for data pipelines, warehouse ingestion, and ",[941,31632,31053],{},"\nprocessing rather than human consumption. Besides the format instance\n",[941,31635,31636],{},"asNdjson()"," (export and import through the vault's format port), it ships\n",[941,31639,31640],{},"pipe(vault, sink, options)",[941,31642,31643],{},"stream(vault, options)"," — streaming exports\nwith O(one record) memory, so a large collection never buffers whole — and a\n",[941,31646,31647],{},"fromString()"," reader that parses newline-delimited JSON back into an import plan.",[937,31650,31651,9483,31657,31660,31661,968,31663,997,31666,31669,31670,31672,31673,1952,31676,997,31679,31682,31683,31686,31687,31690,31691,31694],{},[950,31652,31653,31654],{},"The vault needs ",[941,31655,31656],{},"formatsStrategy: withFormats()",[941,31658,31659],{},"@noy-db\u002Fhub\u002Fas",") for\n",[941,31662,31636],{},[941,31664,31665],{},"toString()",[941,31667,31668],{},"download()"," — since ",[941,31671,2126],{}," the last two go\nthrough the format port, and without the strategy they throw\n",[941,31674,31675],{},"FormatsNotEnabledError",[941,31677,31678],{},"stream()",[941,31680,31681],{},"pipe()"," stay on ",[941,31684,31685],{},"exportStream"," and do not\nneed it. ",[941,31688,31689],{},"write()"," (Node) requires ",[941,31692,31693],{},"acknowledgeRisks: true",", like every on-disk\nplaintext writer.",[1022,31696,29893],{"id":29892},[2217,31698,31700],{"className":2219,"code":31699,"language":2221,"meta":1037,"style":1037},"import { asNdjson } from '@noy-db\u002Fas-ndjson'\n\nconst ndjson = await vault.export(asNdjson(), { collections: ['invoices'] })\n\u002F\u002F {\"id\":\"inv-1\",\"client\":\"Globex\",\"amount\":1500}\n\u002F\u002F {\"id\":\"inv-2\",\"client\":\"Acme\",\"amount\":2400}\n",[941,31701,31702,31721,31725,31771,31776],{"__ignoreMap":1037},[2225,31703,31704,31706,31708,31711,31713,31715,31717,31719],{"class":2227,"line":2228},[2225,31705,2232],{"class":2231},[2225,31707,2236],{"class":2235},[2225,31709,31710],{"class":2239}," asNdjson",[2225,31712,2243],{"class":2235},[2225,31714,2246],{"class":2231},[2225,31716,2249],{"class":2235},[2225,31718,31618],{"class":2252},[2225,31720,2256],{"class":2235},[2225,31722,31723],{"class":2227,"line":1041},[2225,31724,2285],{"emptyLinePlaceholder":2284},[2225,31726,31727,31729,31732,31734,31736,31738,31740,31742,31744,31747,31749,31751,31753,31755,31757,31759,31761,31763,31765,31767,31769],{"class":2227,"line":1038},[2225,31728,2775],{"class":2774},[2225,31730,31731],{"class":2239}," ndjson ",[2225,31733,2781],{"class":2235},[2225,31735,2784],{"class":2231},[2225,31737,6466],{"class":2239},[2225,31739,1520],{"class":2235},[2225,31741,8080],{"class":2291},[2225,31743,2789],{"class":2239},[2225,31745,31746],{"class":2291},"asNdjson",[2225,31748,4228],{"class":2239},[2225,31750,2298],{"class":2235},[2225,31752,2236],{"class":2235},[2225,31754,22066],{"class":2307},[2225,31756,2311],{"class":2235},[2225,31758,8667],{"class":2239},[2225,31760,2805],{"class":2235},[2225,31762,6487],{"class":2252},[2225,31764,2805],{"class":2235},[2225,31766,8890],{"class":2239},[2225,31768,2359],{"class":2235},[2225,31770,2362],{"class":2239},[2225,31772,31773],{"class":2227,"line":2288},[2225,31774,31775],{"class":2763},"\u002F\u002F {\"id\":\"inv-1\",\"client\":\"Globex\",\"amount\":1500}\n",[2225,31777,31778],{"class":2227,"line":2304},[2225,31779,31780],{"class":2763},"\u002F\u002F {\"id\":\"inv-2\",\"client\":\"Acme\",\"amount\":2400}\n",[2217,31782,31784],{"className":2219,"code":31783,"language":2221,"meta":1037,"style":1037},"import { pipe } from '@noy-db\u002Fas-ndjson'\n\nawait pipe(vault, sink, { collections: ['invoices'], acknowledgeRisks: true })\n",[941,31785,31786,31805,31809],{"__ignoreMap":1037},[2225,31787,31788,31790,31792,31795,31797,31799,31801,31803],{"class":2227,"line":2228},[2225,31789,2232],{"class":2231},[2225,31791,2236],{"class":2235},[2225,31793,31794],{"class":2239}," pipe",[2225,31796,2243],{"class":2235},[2225,31798,2246],{"class":2231},[2225,31800,2249],{"class":2235},[2225,31802,31618],{"class":2252},[2225,31804,2256],{"class":2235},[2225,31806,31807],{"class":2227,"line":1041},[2225,31808,2285],{"emptyLinePlaceholder":2284},[2225,31810,31811,31813,31815,31817,31819,31822,31824,31826,31828,31830,31832,31834,31836,31838,31840,31842,31845,31847,31849,31851],{"class":2227,"line":1038},[2225,31812,6500],{"class":2231},[2225,31814,31794],{"class":2291},[2225,31816,30266],{"class":2239},[2225,31818,2298],{"class":2235},[2225,31820,31821],{"class":2239}," sink",[2225,31823,2298],{"class":2235},[2225,31825,2236],{"class":2235},[2225,31827,22066],{"class":2307},[2225,31829,2311],{"class":2235},[2225,31831,8667],{"class":2239},[2225,31833,2805],{"class":2235},[2225,31835,6487],{"class":2252},[2225,31837,2805],{"class":2235},[2225,31839,8803],{"class":2239},[2225,31841,2298],{"class":2235},[2225,31843,31844],{"class":2307}," acknowledgeRisks",[2225,31846,2311],{"class":2235},[2225,31848,2322],{"class":2321},[2225,31850,2243],{"class":2235},[2225,31852,2362],{"class":2239},[2217,31854,31856],{"className":2219,"code":31855,"language":2221,"meta":1037,"style":1037},"import { fromString } from '@noy-db\u002Fas-ndjson'\n\nconst plan = await fromString(vault, ndjson, { collection: 'invoices' })\n\u002F\u002F → AsNDJSONImportPlan { plan: VaultDiff, policy: 'merge', apply() }\n",[941,31857,31858,31877,31881,31918],{"__ignoreMap":1037},[2225,31859,31860,31862,31864,31867,31869,31871,31873,31875],{"class":2227,"line":2228},[2225,31861,2232],{"class":2231},[2225,31863,2236],{"class":2235},[2225,31865,31866],{"class":2239}," fromString",[2225,31868,2243],{"class":2235},[2225,31870,2246],{"class":2231},[2225,31872,2249],{"class":2235},[2225,31874,31618],{"class":2252},[2225,31876,2256],{"class":2235},[2225,31878,31879],{"class":2227,"line":1041},[2225,31880,2285],{"emptyLinePlaceholder":2284},[2225,31882,31883,31885,31887,31889,31891,31893,31895,31897,31900,31902,31904,31906,31908,31910,31912,31914,31916],{"class":2227,"line":1038},[2225,31884,2775],{"class":2774},[2225,31886,30365],{"class":2239},[2225,31888,2781],{"class":2235},[2225,31890,2784],{"class":2231},[2225,31892,31866],{"class":2291},[2225,31894,30266],{"class":2239},[2225,31896,2298],{"class":2235},[2225,31898,31899],{"class":2239}," ndjson",[2225,31901,2298],{"class":2235},[2225,31903,2236],{"class":2235},[2225,31905,7061],{"class":2307},[2225,31907,2311],{"class":2235},[2225,31909,2249],{"class":2235},[2225,31911,6487],{"class":2252},[2225,31913,2805],{"class":2235},[2225,31915,2243],{"class":2235},[2225,31917,2362],{"class":2239},[2225,31919,31920],{"class":2227,"line":2288},[2225,31921,31922],{"class":2763},"\u002F\u002F → AsNDJSONImportPlan { plan: VaultDiff, policy: 'merge', apply() }\n",[1022,31924,2621],{"id":2620},[1111,31926,31927,31931,31936],{},[1114,31928,31929],{},[1007,31930,30143],{"href":2634},[1114,31932,31933,31935],{},[1007,31934,141],{"href":30724}," — single-document alternative",[1114,31937,31938,31940],{},[1007,31939,137],{"href":30992}," — spreadsheet-friendly alternative",[2645,31942,31943],{},"html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sfNiH, html code.shiki .sfNiH{--shiki-light:#FF5370;--shiki-default:#FF9CAC;--shiki-dark:#FF9CAC}",{"title":1037,"searchDepth":1038,"depth":1038,"links":31945},[31946,31947,31948],{"id":29856,"depth":1041,"text":29857},{"id":29892,"depth":1041,"text":29893},{"id":2620,"depth":1041,"text":2621},"Newline-delimited JSON export, streamed record-by-record for data pipelines and jq.",{},{"title":145,"description":31949},"pF-Vgk8T32I5LrQboQuXqAbivEoKOSvHiFlbOVy0bEc",{"id":31954,"title":149,"access":12,"apiToc":929,"body":31955,"category":1042,"description":32174,"extension":1044,"meta":32175,"navigation":1042,"nest":3303,"path":150,"seo":32176,"sinceVersion":1042,"status":1049,"stem":151,"subtitle":1042,"tier":1050,"tocExternal":1042,"__hash__":32177},"docs\u002Ffamilies\u002Fas\u002Fnoydb.md",{"type":931,"value":31956,"toc":32169},[31957,31959,31975,31977,31995,31997,32135,32148,32150,32166],[934,31958,149],{"id":149},[1061,31960,31961],{},[937,31962,31963,1368,31968,31971,31972,31974],{},[950,31964,31965],{},[941,31966,31967],{},"@noy-db\u002Fas-noydb",[941,31969,31970],{},"pnpm add @noy-db\u002Fas-noydb","\nWraps ",[941,31973,31188],{}," — a whole-vault encrypted backup artefact.",[1022,31976,29857],{"id":29856},[937,31978,31979,31981,31982,31984,31985,1324,31987,31990,31991,31994],{},[941,31980,13592],{}," is the only member of the encrypted tier: the bundle it\nproduces is ciphertext end to end, inert without the vault's KEK\n(key-encryption key). Because no plaintext ever crosses the boundary,\n",[941,31983,31689],{}," has ",[950,31986,11313],{},[941,31988,31989],{},"acknowledgeRisks"," gate — the discipline that\nmatters is never storing the secret next to the bundle, not whether\nthe bytes are written. ",[941,31992,31993],{},"peek()"," inspects a received bundle's minimal\nheader (handle, format version, body size, body hash) without decrypting\nthe body, for due diligence before importing. There is no reader — bundle\nimport is whole-vault, not a per-record diff.",[1022,31996,29893],{"id":29892},[2217,31998,32000],{"className":2219,"code":31999,"language":2221,"meta":1037,"style":1037},"import { toBytes, write, download, peek } from '@noy-db\u002Fas-noydb'\n\nconst bundle = await toBytes(vault)          \u002F\u002F → Uint8Array, ciphertext end to end\nawait write(vault, '.\u002Fbackup.noydb')          \u002F\u002F Node disk write — no acknowledgeRisks needed\nawait download(vault, { filename: 'backup.noydb' }) \u002F\u002F browser save-as prompt\n\nconst header = peek(bundle)\n\u002F\u002F → { formatVersion: 1, handle: '01J9…', bodyBytes: 18432, bodySha256: '9f2c…' }\n",[941,32001,32002,32035,32039,32058,32081,32112,32116,32130],{"__ignoreMap":1037},[2225,32003,32004,32006,32008,32010,32012,32015,32017,32020,32022,32025,32027,32029,32031,32033],{"class":2227,"line":2228},[2225,32005,2232],{"class":2231},[2225,32007,2236],{"class":2235},[2225,32009,30223],{"class":2239},[2225,32011,2298],{"class":2235},[2225,32013,32014],{"class":2239}," write",[2225,32016,2298],{"class":2235},[2225,32018,32019],{"class":2239}," download",[2225,32021,2298],{"class":2235},[2225,32023,32024],{"class":2239}," peek",[2225,32026,2243],{"class":2235},[2225,32028,2246],{"class":2231},[2225,32030,2249],{"class":2235},[2225,32032,31967],{"class":2252},[2225,32034,2256],{"class":2235},[2225,32036,32037],{"class":2227,"line":1041},[2225,32038,2285],{"emptyLinePlaceholder":2284},[2225,32040,32041,32043,32046,32048,32050,32052,32055],{"class":2227,"line":1038},[2225,32042,2775],{"class":2774},[2225,32044,32045],{"class":2239}," bundle ",[2225,32047,2781],{"class":2235},[2225,32049,2784],{"class":2231},[2225,32051,30223],{"class":2291},[2225,32053,32054],{"class":2239},"(vault)          ",[2225,32056,32057],{"class":2763},"\u002F\u002F → Uint8Array, ciphertext end to end\n",[2225,32059,32060,32062,32064,32066,32068,32070,32073,32075,32078],{"class":2227,"line":2288},[2225,32061,6500],{"class":2231},[2225,32063,32014],{"class":2291},[2225,32065,30266],{"class":2239},[2225,32067,2298],{"class":2235},[2225,32069,2249],{"class":2235},[2225,32071,32072],{"class":2252},".\u002Fbackup.noydb",[2225,32074,2805],{"class":2235},[2225,32076,32077],{"class":2239},")          ",[2225,32079,32080],{"class":2763},"\u002F\u002F Node disk write — no acknowledgeRisks needed\n",[2225,32082,32083,32085,32087,32089,32091,32093,32096,32098,32100,32103,32105,32107,32109],{"class":2227,"line":2304},[2225,32084,6500],{"class":2231},[2225,32086,32019],{"class":2291},[2225,32088,30266],{"class":2239},[2225,32090,2298],{"class":2235},[2225,32092,2236],{"class":2235},[2225,32094,32095],{"class":2307}," filename",[2225,32097,2311],{"class":2235},[2225,32099,2249],{"class":2235},[2225,32101,32102],{"class":2252},"backup.noydb",[2225,32104,2805],{"class":2235},[2225,32106,2243],{"class":2235},[2225,32108,8495],{"class":2239},[2225,32110,32111],{"class":2763},"\u002F\u002F browser save-as prompt\n",[2225,32113,32114],{"class":2227,"line":2356},[2225,32115,2285],{"emptyLinePlaceholder":2284},[2225,32117,32118,32120,32123,32125,32127],{"class":2227,"line":2830},[2225,32119,2775],{"class":2774},[2225,32121,32122],{"class":2239}," header ",[2225,32124,2781],{"class":2235},[2225,32126,32024],{"class":2291},[2225,32128,32129],{"class":2239},"(bundle)\n",[2225,32131,32132],{"class":2227,"line":2861},[2225,32133,32134],{"class":2763},"\u002F\u002F → { formatVersion: 1, handle: '01J9…', bodyBytes: 18432, bodySha256: '9f2c…' }\n",[937,32136,32137,32138,32140,32141,1625,32143,32145,32146,1520],{},"Gated by ",[941,32139,13596],{},", on by default for ",[941,32142,9898],{},[941,32144,9901],{}," and\noff for every other role — see the ",[1007,32147,30711],{"href":30710},[1022,32149,2621],{"id":2620},[1111,32151,32152,32156,32161],{},[1114,32153,32154],{},[1007,32155,30143],{"href":2634},[1114,32157,32158,32160],{},[1007,32159,165],{"href":30439}," — plaintext-tier composite alternative for hand-offs",[1114,32162,32163,32165],{},[1007,32164,133],{"href":31144}," — single-attachment plaintext alternative",[2645,32167,32168],{},"html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":1037,"searchDepth":1038,"depth":1038,"links":32170},[32171,32172,32173],{"id":29856,"depth":1041,"text":29857},{"id":29892,"depth":1041,"text":29893},{"id":2620,"depth":1041,"text":2621},"Encrypted .noydb bundle export — ciphertext in, ciphertext out. The sole encrypted-tier package.",{},{"title":149,"description":32174},"mzCcInImOaxxHEib6-i79B4KSgg7h6X3KCBokpr_BL8",{"id":32179,"title":153,"access":12,"apiToc":929,"body":32180,"category":1042,"description":32379,"extension":1044,"meta":32380,"navigation":1042,"nest":3303,"path":154,"seo":32381,"sinceVersion":1042,"status":1049,"stem":155,"subtitle":1042,"tier":1473,"tocExternal":1042,"__hash__":32382},"docs\u002Ffamilies\u002Fas\u002Fsql.md",{"type":931,"value":32181,"toc":32374},[32182,32184,32202,32204,32232,32234,32355,32357,32372],[934,32183,153],{"id":153},[1061,32185,32186],{},[937,32187,32188,1368,32193,32196,32197,6030,32199,32201],{},[950,32189,32190],{},[941,32191,32192],{},"@noy-db\u002Fas-sql",[941,32194,32195],{},"pnpm add @noy-db\u002Fas-sql","\nDecrypts records and emits dialect-aware ",[941,32198,31092],{},[941,32200,31095],{}," statements.",[1022,32203,29857],{"id":29856},[937,32205,32206,32208,32209,968,32212,4765,32215,12582,32218,1707,32221,4765,32224,32227,32228,32231],{},[941,32207,30496],{}," is a one-way migration helper — it emits a SQL dump you can pipe\nstraight into ",[941,32210,32211],{},"psql -f",[941,32213,32214],{},"mysql \u003C",[941,32216,32217],{},"sqlite3 \u003C",[941,32219,32220],{},"schema+data",[941,32222,32223],{},"schema-only",[941,32225,32226],{},"data-only"," modes across Postgres, MySQL, and SQLite\ndialects. It is ",[950,32229,32230],{},"export-only",": there is no reader, because SQL-dialect\nparsing back into a diff is a tar pit (each dialect serializes types\ndifferently with no canonical AST).",[1022,32233,29893],{"id":29892},[2217,32235,32237],{"className":2219,"code":32236,"language":2221,"meta":1037,"style":1037},"import { asSql } from '@noy-db\u002Fas-sql'\n\nconst sql = await vault.export(asSql({ dialect: 'postgres', mode: 'schema+data' }), {\n  collections: ['invoices'],\n})\n\u002F\u002F CREATE TABLE invoices (...);\n\u002F\u002F INSERT INTO invoices (...) VALUES (...);\n",[941,32238,32239,32258,32262,32320,32339,32345,32350],{"__ignoreMap":1037},[2225,32240,32241,32243,32245,32248,32250,32252,32254,32256],{"class":2227,"line":2228},[2225,32242,2232],{"class":2231},[2225,32244,2236],{"class":2235},[2225,32246,32247],{"class":2239}," asSql",[2225,32249,2243],{"class":2235},[2225,32251,2246],{"class":2231},[2225,32253,2249],{"class":2235},[2225,32255,32192],{"class":2252},[2225,32257,2256],{"class":2235},[2225,32259,32260],{"class":2227,"line":1041},[2225,32261,2285],{"emptyLinePlaceholder":2284},[2225,32263,32264,32266,32269,32271,32273,32275,32277,32279,32281,32284,32286,32288,32291,32293,32295,32297,32299,32301,32304,32306,32308,32310,32312,32314,32316,32318],{"class":2227,"line":1038},[2225,32265,2775],{"class":2774},[2225,32267,32268],{"class":2239}," sql ",[2225,32270,2781],{"class":2235},[2225,32272,2784],{"class":2231},[2225,32274,6466],{"class":2239},[2225,32276,1520],{"class":2235},[2225,32278,8080],{"class":2291},[2225,32280,2789],{"class":2239},[2225,32282,32283],{"class":2291},"asSql",[2225,32285,2789],{"class":2239},[2225,32287,2792],{"class":2235},[2225,32289,32290],{"class":2307}," dialect",[2225,32292,2311],{"class":2235},[2225,32294,2249],{"class":2235},[2225,32296,407],{"class":2252},[2225,32298,2805],{"class":2235},[2225,32300,2298],{"class":2235},[2225,32302,32303],{"class":2307}," mode",[2225,32305,2311],{"class":2235},[2225,32307,2249],{"class":2235},[2225,32309,32220],{"class":2252},[2225,32311,2805],{"class":2235},[2225,32313,2243],{"class":2235},[2225,32315,1381],{"class":2239},[2225,32317,2298],{"class":2235},[2225,32319,2301],{"class":2235},[2225,32321,32322,32325,32327,32329,32331,32333,32335,32337],{"class":2227,"line":2288},[2225,32323,32324],{"class":2307},"  collections",[2225,32326,2311],{"class":2235},[2225,32328,8667],{"class":2239},[2225,32330,2805],{"class":2235},[2225,32332,6487],{"class":2252},[2225,32334,2805],{"class":2235},[2225,32336,8803],{"class":2239},[2225,32338,1707],{"class":2235},[2225,32340,32341,32343],{"class":2227,"line":2304},[2225,32342,2359],{"class":2235},[2225,32344,2362],{"class":2239},[2225,32346,32347],{"class":2227,"line":2356},[2225,32348,32349],{"class":2763},"\u002F\u002F CREATE TABLE invoices (...);\n",[2225,32351,32352],{"class":2227,"line":2830},[2225,32353,32354],{"class":2763},"\u002F\u002F INSERT INTO invoices (...) VALUES (...);\n",[1022,32356,2621],{"id":2620},[1111,32358,32359,32363,32367],{},[1114,32360,32361],{},[1007,32362,30143],{"href":2634},[1114,32364,32365,31940],{},[1007,32366,137],{"href":30992},[1114,32368,32369,32371],{},[1007,32370,141],{"href":30724}," — structured alternative",[2645,32373,30448],{},{"title":1037,"searchDepth":1038,"depth":1038,"links":32375},[32376,32377,32378],{"id":29856,"depth":1041,"text":29857},{"id":29892,"depth":1041,"text":29893},{"id":2620,"depth":1041,"text":2621},"Dialect-aware SQL migration dump for Postgres \u002F MySQL \u002F SQLite. Export-only.",{},{"title":153,"description":32379},"NEU3M7I9Ob-yKjsjO8_8QdYNGjwlx1lBymgrYmvFufE",{"id":32384,"title":157,"access":12,"apiToc":929,"body":32385,"category":1042,"description":32712,"extension":1044,"meta":32713,"navigation":1042,"nest":3303,"path":158,"seo":32714,"sinceVersion":1042,"status":1049,"stem":159,"subtitle":1042,"tier":1050,"tocExternal":1042,"__hash__":32715},"docs\u002Ffamilies\u002Fas\u002Fxlsx.md",{"type":931,"value":32386,"toc":32707},[32387,32389,32408,32410,32434,32436,32602,32683,32685,32704],[934,32388,157],{"id":157},[1061,32390,32391],{},[937,32392,32393,1368,32398,32401,32402,32404,32405,32407],{},[950,32394,32395],{},[941,32396,32397],{},"@noy-db\u002Fas-xlsx",[941,32399,32400],{},"pnpm add @noy-db\u002Fas-xlsx @noy-db\u002Fas-zip @noy-db\u002Fhub","\nDecrypts records and produces a real ",[941,32403,31007],{}," (Office Open XML) — one sheet per collection. The\nworkbook is ",[950,32406,2985],{},": it leaves noy-db's encryption behind.",[1022,32409,29857],{"id":29856},[937,32411,32412,32415,32416,32419,32420,32423,32424,32426,32427,32430,32431,32433],{},[941,32413,32414],{},"as-xlsx"," writes a workbook that opens natively in Excel, Numbers,\nLibreOffice Calc, and Google Sheets — one sheet per collection, with\ndictionary labels resolved instead of stable keys and Unicode-safe\nstrings routed through the shared-strings table. No heavyweight\nspreadsheet library: the only third-party runtime dependency is\n",[941,32417,32418],{},"fast-xml-parser",", alongside ",[941,32421,32422],{},"@noy-db\u002Fas-zip","'s zip writer. The symmetric\n",[941,32425,30195],{}," reader inverts human-readable dictionary labels back to\ntheir stable keys before building the import plan — supply a ",[941,32428,32429],{},"dicts","\noption explicitly, or populate vault dictionaries with ",[941,32432,6739],{}," for\nthe automatic path. Pure data export: styles, formulas, merged cells, and\ncharts are out of scope.",[1022,32435,29893],{"id":29892},[2217,32437,32439],{"className":2219,"code":32438,"language":2221,"meta":1037,"style":1037},"import { toBytes } from '@noy-db\u002Fas-xlsx'\n\nconst bytes = await toBytes(vault, {\n  sheets: [\n    { name: 'Invoices', collection: 'invoices', filter: (r) => r.status !== 'draft' },\n    { name: 'Payments', collection: 'payments' },\n  ],\n})\n\u002F\u002F → Uint8Array — a complete .xlsx workbook\n",[941,32440,32441,32459,32463,32482,32492,32554,32584,32591,32597],{"__ignoreMap":1037},[2225,32442,32443,32445,32447,32449,32451,32453,32455,32457],{"class":2227,"line":2228},[2225,32444,2232],{"class":2231},[2225,32446,2236],{"class":2235},[2225,32448,30223],{"class":2239},[2225,32450,2243],{"class":2235},[2225,32452,2246],{"class":2231},[2225,32454,2249],{"class":2235},[2225,32456,32397],{"class":2252},[2225,32458,2256],{"class":2235},[2225,32460,32461],{"class":2227,"line":1041},[2225,32462,2285],{"emptyLinePlaceholder":2284},[2225,32464,32465,32467,32470,32472,32474,32476,32478,32480],{"class":2227,"line":1038},[2225,32466,2775],{"class":2774},[2225,32468,32469],{"class":2239}," bytes ",[2225,32471,2781],{"class":2235},[2225,32473,2784],{"class":2231},[2225,32475,30223],{"class":2291},[2225,32477,30266],{"class":2239},[2225,32479,2298],{"class":2235},[2225,32481,2301],{"class":2235},[2225,32483,32484,32487,32489],{"class":2227,"line":2288},[2225,32485,32486],{"class":2307},"  sheets",[2225,32488,2311],{"class":2235},[2225,32490,32491],{"class":2239}," [\n",[2225,32493,32494,32497,32499,32501,32503,32506,32508,32510,32512,32514,32516,32518,32520,32522,32525,32527,32529,32531,32533,32535,32538,32540,32543,32546,32548,32550,32552],{"class":2227,"line":2304},[2225,32495,32496],{"class":2235},"    {",[2225,32498,8104],{"class":2307},[2225,32500,2311],{"class":2235},[2225,32502,2249],{"class":2235},[2225,32504,32505],{"class":2252},"Invoices",[2225,32507,2805],{"class":2235},[2225,32509,2298],{"class":2235},[2225,32511,7061],{"class":2307},[2225,32513,2311],{"class":2235},[2225,32515,2249],{"class":2235},[2225,32517,6487],{"class":2252},[2225,32519,2805],{"class":2235},[2225,32521,2298],{"class":2235},[2225,32523,32524],{"class":2291}," filter",[2225,32526,2311],{"class":2235},[2225,32528,953],{"class":2235},[2225,32530,13217],{"class":7052},[2225,32532,1381],{"class":2235},[2225,32534,8116],{"class":2774},[2225,32536,32537],{"class":2239}," r",[2225,32539,1520],{"class":2235},[2225,32541,32542],{"class":2239},"status ",[2225,32544,32545],{"class":2235},"!==",[2225,32547,2249],{"class":2235},[2225,32549,28429],{"class":2252},[2225,32551,2805],{"class":2235},[2225,32553,2353],{"class":2235},[2225,32555,32556,32558,32560,32562,32564,32567,32569,32571,32573,32575,32577,32580,32582],{"class":2227,"line":2356},[2225,32557,32496],{"class":2235},[2225,32559,8104],{"class":2307},[2225,32561,2311],{"class":2235},[2225,32563,2249],{"class":2235},[2225,32565,32566],{"class":2252},"Payments",[2225,32568,2805],{"class":2235},[2225,32570,2298],{"class":2235},[2225,32572,7061],{"class":2307},[2225,32574,2311],{"class":2235},[2225,32576,2249],{"class":2235},[2225,32578,32579],{"class":2252},"payments",[2225,32581,2805],{"class":2235},[2225,32583,2353],{"class":2235},[2225,32585,32586,32589],{"class":2227,"line":2830},[2225,32587,32588],{"class":2239},"  ]",[2225,32590,1707],{"class":2235},[2225,32592,32593,32595],{"class":2227,"line":2861},[2225,32594,2359],{"class":2235},[2225,32596,2362],{"class":2239},[2225,32598,32599],{"class":2227,"line":4246},[2225,32600,32601],{"class":2763},"\u002F\u002F → Uint8Array — a complete .xlsx workbook\n",[2217,32603,32605],{"className":2219,"code":32604,"language":2221,"meta":1037,"style":1037},"import { fromBytes } from '@noy-db\u002Fas-xlsx'\n\nconst plan = await fromBytes(vault, xlsx, { collection: 'invoices' })\n\u002F\u002F → plan rows carry stable keys ('paid'\u002F'draft'), inverted from dict labels\nawait plan.apply()\n",[941,32606,32607,32625,32629,32666,32671],{"__ignoreMap":1037},[2225,32608,32609,32611,32613,32615,32617,32619,32621,32623],{"class":2227,"line":2228},[2225,32610,2232],{"class":2231},[2225,32612,2236],{"class":2235},[2225,32614,30344],{"class":2239},[2225,32616,2243],{"class":2235},[2225,32618,2246],{"class":2231},[2225,32620,2249],{"class":2235},[2225,32622,32397],{"class":2252},[2225,32624,2256],{"class":2235},[2225,32626,32627],{"class":2227,"line":1041},[2225,32628,2285],{"emptyLinePlaceholder":2284},[2225,32630,32631,32633,32635,32637,32639,32641,32643,32645,32648,32650,32652,32654,32656,32658,32660,32662,32664],{"class":2227,"line":1038},[2225,32632,2775],{"class":2774},[2225,32634,30365],{"class":2239},[2225,32636,2781],{"class":2235},[2225,32638,2784],{"class":2231},[2225,32640,30344],{"class":2291},[2225,32642,30266],{"class":2239},[2225,32644,2298],{"class":2235},[2225,32646,32647],{"class":2239}," xlsx",[2225,32649,2298],{"class":2235},[2225,32651,2236],{"class":2235},[2225,32653,7061],{"class":2307},[2225,32655,2311],{"class":2235},[2225,32657,2249],{"class":2235},[2225,32659,6487],{"class":2252},[2225,32661,2805],{"class":2235},[2225,32663,2243],{"class":2235},[2225,32665,2362],{"class":2239},[2225,32667,32668],{"class":2227,"line":2288},[2225,32669,32670],{"class":2763},"\u002F\u002F → plan rows carry stable keys ('paid'\u002F'draft'), inverted from dict labels\n",[2225,32672,32673,32675,32677,32679,32681],{"class":2227,"line":2304},[2225,32674,6500],{"class":2231},[2225,32676,30419],{"class":2239},[2225,32678,1520],{"class":2235},[2225,32680,30424],{"class":2291},[2225,32682,8146],{"class":2239},[1022,32684,2621],{"id":2620},[1111,32686,32687,32691,32696],{},[1114,32688,32689],{},[1007,32690,30143],{"href":2634},[1114,32692,32693,32695],{},[1007,32694,137],{"href":30992}," — flat, single-sheet alternative",[1114,32697,32698,32700,32701,32703],{},[1007,32699,165],{"href":30439}," — the zip writer ",[941,32702,32414],{}," builds on",[2645,32705,32706],{},"html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .sHdIc, html code.shiki .sHdIc{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#EEFFFF;--shiki-default-font-style:italic;--shiki-dark:#BABED8;--shiki-dark-font-style:italic}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":1037,"searchDepth":1038,"depth":1038,"links":32708},[32709,32710,32711],{"id":29856,"depth":1041,"text":29857},{"id":29892,"depth":1041,"text":29893},{"id":2620,"depth":1041,"text":2621},"Multi-sheet, Unicode-safe .xlsx export with dictionary-label inversion on import.",{},{"title":157,"description":32712},"K44WYHotlnjrJFGB8THrkb3Hpx2XSWIAX3aUimPnW5Y",{"id":32717,"title":161,"access":12,"apiToc":929,"body":32718,"category":1042,"description":32934,"extension":1044,"meta":32935,"navigation":1042,"nest":3303,"path":162,"seo":32936,"sinceVersion":1042,"status":1049,"stem":163,"subtitle":1042,"tier":1473,"tocExternal":1042,"__hash__":32937},"docs\u002Ffamilies\u002Fas\u002Fxml.md",{"type":931,"value":32719,"toc":32929},[32720,32722,32735,32737,32745,32747,32821,32910,32912,32926],[934,32721,161],{"id":161},[1061,32723,32724],{},[937,32725,32726,1368,32731,32734],{},[950,32727,32728],{},[941,32729,32730],{},"@noy-db\u002Fas-xml",[941,32732,32733],{},"pnpm add @noy-db\u002Fas-xml","\nDecrypts records and formats them as XML.",[1022,32736,29857],{"id":29856},[937,32738,32739,32741,32742,32744],{},[941,32740,30493],{}," targets legacy systems, banking batch imports, SOAP endpoints,\nand accounting software that requires XML rather than JSON. The writer\nis dependency-free and RFC-compliant on entity escaping, with optional\nnamespaces, custom root\u002Frecord element names, and pretty-printing. The\nsymmetric decoder parses XML back into an import plan using\n",[941,32743,32418],{}," in strict mode.",[1022,32746,29893],{"id":29892},[2217,32748,32750],{"className":2219,"code":32749,"language":2221,"meta":1037,"style":1037},"import { asXml } from '@noy-db\u002Fas-xml'\n\nconst xml = await vault.export(asXml(), { collections: ['invoices'] })\n",[941,32751,32752,32771,32775],{"__ignoreMap":1037},[2225,32753,32754,32756,32758,32761,32763,32765,32767,32769],{"class":2227,"line":2228},[2225,32755,2232],{"class":2231},[2225,32757,2236],{"class":2235},[2225,32759,32760],{"class":2239}," asXml",[2225,32762,2243],{"class":2235},[2225,32764,2246],{"class":2231},[2225,32766,2249],{"class":2235},[2225,32768,32730],{"class":2252},[2225,32770,2256],{"class":2235},[2225,32772,32773],{"class":2227,"line":1041},[2225,32774,2285],{"emptyLinePlaceholder":2284},[2225,32776,32777,32779,32782,32784,32786,32788,32790,32792,32794,32797,32799,32801,32803,32805,32807,32809,32811,32813,32815,32817,32819],{"class":2227,"line":1038},[2225,32778,2775],{"class":2774},[2225,32780,32781],{"class":2239}," xml ",[2225,32783,2781],{"class":2235},[2225,32785,2784],{"class":2231},[2225,32787,6466],{"class":2239},[2225,32789,1520],{"class":2235},[2225,32791,8080],{"class":2291},[2225,32793,2789],{"class":2239},[2225,32795,32796],{"class":2291},"asXml",[2225,32798,4228],{"class":2239},[2225,32800,2298],{"class":2235},[2225,32802,2236],{"class":2235},[2225,32804,22066],{"class":2307},[2225,32806,2311],{"class":2235},[2225,32808,8667],{"class":2239},[2225,32810,2805],{"class":2235},[2225,32812,6487],{"class":2252},[2225,32814,2805],{"class":2235},[2225,32816,8890],{"class":2239},[2225,32818,2359],{"class":2235},[2225,32820,2362],{"class":2239},[2217,32822,32824],{"className":2219,"code":32823,"language":2221,"meta":1037,"style":1037},"import { asXml } from '@noy-db\u002Fas-xml'\n\nconst plan = await vault.import(asXml(), xml, { collection: 'invoices' })\n\u002F\u002F → AsXMLImportPlan { plan: VaultDiff, policy: 'merge', apply() }\nawait plan.apply()\n",[941,32825,32826,32844,32848,32893,32898],{"__ignoreMap":1037},[2225,32827,32828,32830,32832,32834,32836,32838,32840,32842],{"class":2227,"line":2228},[2225,32829,2232],{"class":2231},[2225,32831,2236],{"class":2235},[2225,32833,32760],{"class":2239},[2225,32835,2243],{"class":2235},[2225,32837,2246],{"class":2231},[2225,32839,2249],{"class":2235},[2225,32841,32730],{"class":2252},[2225,32843,2256],{"class":2235},[2225,32845,32846],{"class":2227,"line":1041},[2225,32847,2285],{"emptyLinePlaceholder":2284},[2225,32849,32850,32852,32854,32856,32858,32860,32862,32864,32866,32868,32870,32872,32875,32877,32879,32881,32883,32885,32887,32889,32891],{"class":2227,"line":1038},[2225,32851,2775],{"class":2774},[2225,32853,30365],{"class":2239},[2225,32855,2781],{"class":2235},[2225,32857,2784],{"class":2231},[2225,32859,6466],{"class":2239},[2225,32861,1520],{"class":2235},[2225,32863,2232],{"class":2291},[2225,32865,2789],{"class":2239},[2225,32867,32796],{"class":2291},[2225,32869,4228],{"class":2239},[2225,32871,2298],{"class":2235},[2225,32873,32874],{"class":2239}," xml",[2225,32876,2298],{"class":2235},[2225,32878,2236],{"class":2235},[2225,32880,7061],{"class":2307},[2225,32882,2311],{"class":2235},[2225,32884,2249],{"class":2235},[2225,32886,6487],{"class":2252},[2225,32888,2805],{"class":2235},[2225,32890,2243],{"class":2235},[2225,32892,2362],{"class":2239},[2225,32894,32895],{"class":2227,"line":2288},[2225,32896,32897],{"class":2763},"\u002F\u002F → AsXMLImportPlan { plan: VaultDiff, policy: 'merge', apply() }\n",[2225,32899,32900,32902,32904,32906,32908],{"class":2227,"line":2304},[2225,32901,6500],{"class":2231},[2225,32903,30419],{"class":2239},[2225,32905,1520],{"class":2235},[2225,32907,30424],{"class":2291},[2225,32909,8146],{"class":2239},[1022,32911,2621],{"id":2620},[1111,32913,32914,32918,32922],{},[1114,32915,32916],{},[1007,32917,30143],{"href":2634},[1114,32919,32920,32371],{},[1007,32921,141],{"href":30724},[1114,32923,32924,31940],{},[1007,32925,137],{"href":30992},[2645,32927,32928],{},"html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}",{"title":1037,"searchDepth":1038,"depth":1038,"links":32930},[32931,32932,32933],{"id":29856,"depth":1041,"text":29857},{"id":29892,"depth":1041,"text":29893},{"id":2620,"depth":1041,"text":2621},"XML export with RFC-compliant entity escaping, for legacy systems and banking batch imports.",{},{"title":161,"description":32934},"eF-pGtw1uXkqa7YUveld1xM9wwK_wJkh4RV6jsRSfo0",{"id":32939,"title":165,"access":12,"apiToc":929,"body":32940,"category":1042,"description":33276,"extension":1044,"meta":33277,"navigation":1042,"nest":3303,"path":166,"seo":33278,"sinceVersion":1042,"status":1049,"stem":167,"subtitle":1042,"tier":1473,"tocExternal":1042,"__hash__":33279},"docs\u002Ffamilies\u002Fas\u002Fzip.md",{"type":931,"value":32941,"toc":33271},[32942,32944,32965,32967,32995,32997,33156,33251,33253,33269],[934,32943,165],{"id":165},[1061,32945,32946],{},[937,32947,32948,1368,32952,32955,32956,32958,32959,32961,32962,32964],{},[950,32949,32950],{},[941,32951,32422],{},[941,32953,32954],{},"pnpm add @noy-db\u002Fas-zip","\nDecrypts a collection's records and every attached blob and bundles them into one ",[941,32957,31159],{},". The\narchive is ",[950,32960,2985],{}," unless you pass ",[941,32963,313],{}," (WinZip-AES-256, below) — and even then it is\nprotected by that password, not by the vault's keys.",[1022,32966,29857],{"id":29856},[937,32968,32969,32971,32972,32975,32976,32979,32980,32983,32984,32986,32987,32991,32992,32994],{},[941,32970,16044],{}," is the \"download this audit trail\" \u002F \"migrate this case folder\"\nprimitive — a folder-per-record archive (",[941,32973,32974],{},"records.json"," + a\n",[941,32977,32978],{},"manifest.json"," index + ",[941,32981,32982],{},"attachments\u002F\u003CrecordId>\u002F\u003Cslot>"," blob files) that\nis browsable without tooling. It ships a zero-dependency, STORE-only zip\nwriter (no deflate, since most attached blobs are already compressed).\nPass ",[941,32985,313],{}," to encrypt every entry with WinZip-AES-256 — the archive\nstays a valid single-disk ZIP that 7-Zip, Archive Utility, and WinRAR\nprompt for the password on extract (see the ",[1007,32988,32990],{"href":32989},"\u002Fzip-interop","interoperability matrix","\nfor exactly which third-party tools open these archives, and which archives the reader accepts).\nA symmetric ",[941,32993,30195],{}," reader reads a (optionally encrypted) archive back into an import plan.",[1022,32996,29893],{"id":29892},[2217,32998,33000],{"className":2219,"code":32999,"language":2221,"meta":1037,"style":1037},"import { toBytes } from '@noy-db\u002Fas-zip'\n\nconst bytes = await toBytes(vault, {\n  records: { collection: 'invoices', filter: (r) => r.status === 'paid' },\n  attachments: { slots: ['raw', 'thumb'] }, \u002F\u002F optional; default '*' = every slot\n  password: 'shared-with-recipient-2026',    \u002F\u002F optional — WinZip-AES-256\n})\n",[941,33001,33002,33020,33024,33042,33092,33131,33150],{"__ignoreMap":1037},[2225,33003,33004,33006,33008,33010,33012,33014,33016,33018],{"class":2227,"line":2228},[2225,33005,2232],{"class":2231},[2225,33007,2236],{"class":2235},[2225,33009,30223],{"class":2239},[2225,33011,2243],{"class":2235},[2225,33013,2246],{"class":2231},[2225,33015,2249],{"class":2235},[2225,33017,32422],{"class":2252},[2225,33019,2256],{"class":2235},[2225,33021,33022],{"class":2227,"line":1041},[2225,33023,2285],{"emptyLinePlaceholder":2284},[2225,33025,33026,33028,33030,33032,33034,33036,33038,33040],{"class":2227,"line":1038},[2225,33027,2775],{"class":2774},[2225,33029,32469],{"class":2239},[2225,33031,2781],{"class":2235},[2225,33033,2784],{"class":2231},[2225,33035,30223],{"class":2291},[2225,33037,30266],{"class":2239},[2225,33039,2298],{"class":2235},[2225,33041,2301],{"class":2235},[2225,33043,33044,33047,33049,33051,33053,33055,33057,33059,33061,33063,33065,33067,33069,33071,33073,33075,33077,33079,33081,33084,33086,33088,33090],{"class":2227,"line":2288},[2225,33045,33046],{"class":2307},"  records",[2225,33048,2311],{"class":2235},[2225,33050,2236],{"class":2235},[2225,33052,7061],{"class":2307},[2225,33054,2311],{"class":2235},[2225,33056,2249],{"class":2235},[2225,33058,6487],{"class":2252},[2225,33060,2805],{"class":2235},[2225,33062,2298],{"class":2235},[2225,33064,32524],{"class":2291},[2225,33066,2311],{"class":2235},[2225,33068,953],{"class":2235},[2225,33070,13217],{"class":7052},[2225,33072,1381],{"class":2235},[2225,33074,8116],{"class":2774},[2225,33076,32537],{"class":2239},[2225,33078,1520],{"class":2235},[2225,33080,32542],{"class":2239},[2225,33082,33083],{"class":2235},"===",[2225,33085,2249],{"class":2235},[2225,33087,6539],{"class":2252},[2225,33089,2805],{"class":2235},[2225,33091,2353],{"class":2235},[2225,33093,33094,33097,33099,33101,33104,33106,33108,33110,33112,33114,33116,33118,33121,33123,33125,33128],{"class":2227,"line":2304},[2225,33095,33096],{"class":2307},"  attachments",[2225,33098,2311],{"class":2235},[2225,33100,2236],{"class":2235},[2225,33102,33103],{"class":2307}," slots",[2225,33105,2311],{"class":2235},[2225,33107,8667],{"class":2239},[2225,33109,2805],{"class":2235},[2225,33111,30312],{"class":2252},[2225,33113,2805],{"class":2235},[2225,33115,2298],{"class":2235},[2225,33117,2249],{"class":2235},[2225,33119,33120],{"class":2252},"thumb",[2225,33122,2805],{"class":2235},[2225,33124,8890],{"class":2239},[2225,33126,33127],{"class":2235},"},",[2225,33129,33130],{"class":2763}," \u002F\u002F optional; default '*' = every slot\n",[2225,33132,33133,33136,33138,33140,33143,33145,33147],{"class":2227,"line":2356},[2225,33134,33135],{"class":2307},"  password",[2225,33137,2311],{"class":2235},[2225,33139,2249],{"class":2235},[2225,33141,33142],{"class":2252},"shared-with-recipient-2026",[2225,33144,2805],{"class":2235},[2225,33146,2298],{"class":2235},[2225,33148,33149],{"class":2763},"    \u002F\u002F optional — WinZip-AES-256\n",[2225,33151,33152,33154],{"class":2227,"line":2830},[2225,33153,2359],{"class":2235},[2225,33155,2362],{"class":2239},[2217,33157,33159],{"className":2219,"code":33158,"language":2221,"meta":1037,"style":1037},"import { fromBytes } from '@noy-db\u002Fas-zip'\n\nconst plan = await fromBytes(vault, bytes, {\n  collection: 'invoices',\n  password: 'shared-with-recipient-2026',\n})\nawait plan.apply()\n",[941,33160,33161,33179,33183,33205,33219,33233,33239],{"__ignoreMap":1037},[2225,33162,33163,33165,33167,33169,33171,33173,33175,33177],{"class":2227,"line":2228},[2225,33164,2232],{"class":2231},[2225,33166,2236],{"class":2235},[2225,33168,30344],{"class":2239},[2225,33170,2243],{"class":2235},[2225,33172,2246],{"class":2231},[2225,33174,2249],{"class":2235},[2225,33176,32422],{"class":2252},[2225,33178,2256],{"class":2235},[2225,33180,33181],{"class":2227,"line":1041},[2225,33182,2285],{"emptyLinePlaceholder":2284},[2225,33184,33185,33187,33189,33191,33193,33195,33197,33199,33201,33203],{"class":2227,"line":1038},[2225,33186,2775],{"class":2774},[2225,33188,30365],{"class":2239},[2225,33190,2781],{"class":2235},[2225,33192,2784],{"class":2231},[2225,33194,30344],{"class":2291},[2225,33196,30266],{"class":2239},[2225,33198,2298],{"class":2235},[2225,33200,29996],{"class":2239},[2225,33202,2298],{"class":2235},[2225,33204,2301],{"class":2235},[2225,33206,33207,33209,33211,33213,33215,33217],{"class":2227,"line":2288},[2225,33208,30275],{"class":2307},[2225,33210,2311],{"class":2235},[2225,33212,2249],{"class":2235},[2225,33214,6487],{"class":2252},[2225,33216,2805],{"class":2235},[2225,33218,1707],{"class":2235},[2225,33220,33221,33223,33225,33227,33229,33231],{"class":2227,"line":2304},[2225,33222,33135],{"class":2307},[2225,33224,2311],{"class":2235},[2225,33226,2249],{"class":2235},[2225,33228,33142],{"class":2252},[2225,33230,2805],{"class":2235},[2225,33232,1707],{"class":2235},[2225,33234,33235,33237],{"class":2227,"line":2356},[2225,33236,2359],{"class":2235},[2225,33238,2362],{"class":2239},[2225,33240,33241,33243,33245,33247,33249],{"class":2227,"line":2830},[2225,33242,6500],{"class":2231},[2225,33244,30419],{"class":2239},[2225,33246,1520],{"class":2235},[2225,33248,30424],{"class":2291},[2225,33250,8146],{"class":2239},[1022,33252,2621],{"id":2620},[1111,33254,33255,33259,33264],{},[1114,33256,33257],{},[1007,33258,30143],{"href":2634},[1114,33260,33261,33263],{},[1007,33262,133],{"href":31144}," — single attachment, no archive",[1114,33265,33266,33268],{},[1007,33267,149],{"href":30148}," — encrypted-tier alternative for multi-recipient hand-offs",[2645,33270,32706],{},{"title":1037,"searchDepth":1038,"depth":1038,"links":33272},[33273,33274,33275],{"id":29856,"depth":1041,"text":29857},{"id":29892,"depth":1041,"text":29893},{"id":2620,"depth":1041,"text":2621},"Composite archive bundling a collection's records plus every attached blob into one .zip.",{},{"title":165,"description":33276},"KyfoEXqKxCZEPaKoT31sR_7WbS8WC6wSssiAnkx1ZU8",{"id":33281,"title":175,"access":12,"apiToc":929,"body":33282,"category":1042,"description":33454,"extension":1044,"meta":33455,"navigation":1042,"nest":3303,"path":176,"seo":33456,"sinceVersion":1042,"status":1049,"stem":177,"subtitle":1042,"tier":1050,"tocExternal":1042,"__hash__":33457},"docs\u002Ffamilies\u002Fat\u002Faws-kms.md",{"type":931,"value":33283,"toc":33449},[33284,33286,33299,33301,33332,33348,33350,33427,33429,33447],[934,33285,175],{"id":175},[1061,33287,33288],{},[937,33289,33290,1368,33295,33298],{},[950,33291,33292],{},[941,33293,33294],{},"@noy-db\u002Fat-aws-kms",[941,33296,33297],{},"pnpm add @noy-db\u002Fat-aws-kms","\nCloud-KMS sealing — the key lives in AWS KMS, access controlled by IAM.",[1022,33300,29857],{"id":29856},[937,33302,33303,33306,33307,1625,33310,33313,33314,7356,33317,33320,33321,33324,33325,33328,33329,33331],{},[941,33304,33305],{},"atAwsKms({ keyId })"," seals and unseals the hub-generated\nmanaged-mode secret via authenticated KMS ",[941,33308,33309],{},"Encrypt",[941,33311,33312],{},"Decrypt"," calls,\nso CloudTrail records every time a user's vault is opened with no extra\ninstrumentation. Its ",[941,33315,33316],{},"pid",[941,33318,33319],{},"aws-kms:\u003CkeyId>"," (e.g.\n",[941,33322,33323],{},"aws-kms:arn:aws:kms:us-east-1:123456789012:key\u002Fabc","). Credentials\nresolve through the SDK's ambient chain (IAM role, env vars,\n",[941,33326,33327],{},"~\u002F.aws\u002Fcredentials",") — never pass raw credentials in the options; inject\na pre-built ",[941,33330,9913],{}," instead for tests or non-default auth. KMS supports\nautomatic rotation for symmetric keys with no app-side changes.",[937,33333,33334,33335,33338,33339,33342,33343,33345,33346,1520],{},"The package also exports ",[941,33336,33337],{},"awsKmsRecipientSealer()",", an asymmetric\n",[950,33340,33341],{},"recipient-target"," extension of the same host: a grantor seals bytes\nlocally against the host's published RSA public key with no KMS call and\nno AWS credentials, and only the host (via KMS ",[941,33344,33312],{},") can open them.\nThis is the building block for record-scoped sealing to a host — see\n",[1007,33347,824],{"href":825},[1022,33349,29893],{"id":29892},[2217,33351,33353],{"className":2219,"code":33352,"language":2221,"meta":1037,"style":1037},"import { atAwsKms } from '@noy-db\u002Fat-aws-kms'\n\nconst sealingKey = atAwsKms({\n  keyId: 'arn:aws:kms:us-east-1:123456789012:key\u002Fabc',\n})\nsealingKey.id \u002F\u002F → 'aws-kms:arn:aws:kms:us-east-1:123456789012:key\u002Fabc'\n",[941,33354,33355,33374,33378,33393,33409,33415],{"__ignoreMap":1037},[2225,33356,33357,33359,33361,33364,33366,33368,33370,33372],{"class":2227,"line":2228},[2225,33358,2232],{"class":2231},[2225,33360,2236],{"class":2235},[2225,33362,33363],{"class":2239}," atAwsKms",[2225,33365,2243],{"class":2235},[2225,33367,2246],{"class":2231},[2225,33369,2249],{"class":2235},[2225,33371,33294],{"class":2252},[2225,33373,2256],{"class":2235},[2225,33375,33376],{"class":2227,"line":1041},[2225,33377,2285],{"emptyLinePlaceholder":2284},[2225,33379,33380,33382,33385,33387,33389,33391],{"class":2227,"line":1038},[2225,33381,2775],{"class":2774},[2225,33383,33384],{"class":2239}," sealingKey ",[2225,33386,2781],{"class":2235},[2225,33388,33363],{"class":2291},[2225,33390,2789],{"class":2239},[2225,33392,4200],{"class":2235},[2225,33394,33395,33398,33400,33402,33405,33407],{"class":2227,"line":2288},[2225,33396,33397],{"class":2307},"  keyId",[2225,33399,2311],{"class":2235},[2225,33401,2249],{"class":2235},[2225,33403,33404],{"class":2252},"arn:aws:kms:us-east-1:123456789012:key\u002Fabc",[2225,33406,2805],{"class":2235},[2225,33408,1707],{"class":2235},[2225,33410,33411,33413],{"class":2227,"line":2304},[2225,33412,2359],{"class":2235},[2225,33414,2362],{"class":2239},[2225,33416,33417,33419,33421,33424],{"class":2227,"line":2356},[2225,33418,13515],{"class":2239},[2225,33420,1520],{"class":2235},[2225,33422,33423],{"class":2239},"id ",[2225,33425,33426],{"class":2763},"\u002F\u002F → 'aws-kms:arn:aws:kms:us-east-1:123456789012:key\u002Fabc'\n",[1022,33428,2621],{"id":2620},[1111,33430,33431,33436,33441],{},[1114,33432,33433],{},[1007,33434,33435],{"href":2634},"at-* family",[1114,33437,33438,33440],{},[1007,33439,828],{"href":829}," — the pid stability rule",[1114,33442,33443,33446],{},[1007,33444,187],{"href":33445},".\u002Fgcp-kms"," — the equivalent provider for Google Cloud KMS",[2645,33448,30448],{},{"title":1037,"searchDepth":1038,"depth":1038,"links":33450},[33451,33452,33453],{"id":29856,"depth":1041,"text":29857},{"id":29892,"depth":1041,"text":29893},{"id":2620,"depth":1041,"text":2621},"Seals the managed-secret vault key via AWS KMS Encrypt\u002FDecrypt, with CloudTrail logging every vault open.",{},{"title":175,"description":33454},"aFSKiCyPnA0kwQxO1kM6Hsihqzc9eiBCau6nJZEorzk",{"id":33459,"title":179,"access":12,"apiToc":929,"body":33460,"category":1042,"description":33638,"extension":1044,"meta":33639,"navigation":1042,"nest":3303,"path":180,"seo":33640,"sinceVersion":1042,"status":1049,"stem":181,"subtitle":1042,"tier":1050,"tocExternal":1042,"__hash__":33641},"docs\u002Ffamilies\u002Fat\u002Fazure-keyvault.md",{"type":931,"value":33461,"toc":33633},[33462,33464,33477,33479,33516,33537,33539,33613,33615,33631],[934,33463,179],{"id":179},[1061,33465,33466],{},[937,33467,33468,1368,33473,33476],{},[950,33469,33470],{},[941,33471,33472],{},"@noy-db\u002Fat-azure-keyvault",[941,33474,33475],{},"pnpm add @noy-db\u002Fat-azure-keyvault","\nCloud-KMS sealing — the key lives in Azure Key Vault, access controlled by RBAC\u002FKey Vault policy.",[1022,33478,29857],{"id":29856},[937,33480,33481,33484,33485,7356,33487,33320,33490,33493,33494,33497,33498,1625,33501,1625,33504,33507,33508,33511,33512,33515],{},[941,33482,33483],{},"atAzureKeyvault({ keyId })"," seals and unseals the\nhub-generated managed-mode secret via RSA encrypt\u002Fdecrypt against a\nKey Vault key, with Key Vault audit logging recording every vault open.\nIts ",[941,33486,33316],{},[941,33488,33489],{},"azure-kv:\u003CkeyId>",[941,33491,33492],{},"azure-kv:https:\u002F\u002Fmy-noydb-vault.vault.azure.net\u002Fkeys\u002Fnoydb-sealing\u002F0123456789abcdef",").\nCredentials resolve via ",[941,33495,33496],{},"DefaultAzureCredential"," (managed identity,\n",[941,33499,33500],{},"AZURE_CLIENT_ID",[941,33502,33503],{},"AZURE_TENANT_ID",[941,33505,33506],{},"AZURE_CLIENT_SECRET",", or\n",[941,33509,33510],{},"az login","); never pass raw credentials in the options — inject a\npre-built ",[941,33513,33514],{},"cryptographyClient"," instead.",[937,33517,33518,33521,33522,33525,33526,33529,33530,33533,33534,33536],{},[950,33519,33520],{},"Version-bound — always pin a versioned key id."," Azure's\n",[941,33523,33524],{},"CryptographyClient"," resolves the key version at construction, and every\ndecrypt call is pinned to that version. A versionless URL\n(",[941,33527,33528],{},"…\u002Fkeys\u002F\u003Cname>",") resolves to \"latest,\" so if the key auto-rotates, every\nsecret sealed under the previous version becomes permanently\nundecryptable. Rotation is a deliberate migration (",[941,33531,33532],{},"unseal"," under the\nold version, ",[941,33535,2145],{}," under the new), not transparent like the AWS\u002FGCP\nproviders.",[1022,33538,29893],{"id":29892},[2217,33540,33542],{"className":2219,"code":33541,"language":2221,"meta":1037,"style":1037},"import { atAzureKeyvault } from '@noy-db\u002Fat-azure-keyvault'\n\nconst sealingKey = atAzureKeyvault({\n  keyId: 'https:\u002F\u002Fmy-noydb-vault.vault.azure.net\u002Fkeys\u002Fnoydb-sealing\u002F0123456789abcdef',\n})\nsealingKey.id \u002F\u002F → 'azure-kv:https:\u002F\u002Fmy-noydb-vault.vault.azure.net\u002Fkeys\u002Fnoydb-sealing\u002F0123456789abcdef'\n",[941,33543,33544,33563,33567,33581,33596,33602],{"__ignoreMap":1037},[2225,33545,33546,33548,33550,33553,33555,33557,33559,33561],{"class":2227,"line":2228},[2225,33547,2232],{"class":2231},[2225,33549,2236],{"class":2235},[2225,33551,33552],{"class":2239}," atAzureKeyvault",[2225,33554,2243],{"class":2235},[2225,33556,2246],{"class":2231},[2225,33558,2249],{"class":2235},[2225,33560,33472],{"class":2252},[2225,33562,2256],{"class":2235},[2225,33564,33565],{"class":2227,"line":1041},[2225,33566,2285],{"emptyLinePlaceholder":2284},[2225,33568,33569,33571,33573,33575,33577,33579],{"class":2227,"line":1038},[2225,33570,2775],{"class":2774},[2225,33572,33384],{"class":2239},[2225,33574,2781],{"class":2235},[2225,33576,33552],{"class":2291},[2225,33578,2789],{"class":2239},[2225,33580,4200],{"class":2235},[2225,33582,33583,33585,33587,33589,33592,33594],{"class":2227,"line":2288},[2225,33584,33397],{"class":2307},[2225,33586,2311],{"class":2235},[2225,33588,2249],{"class":2235},[2225,33590,33591],{"class":2252},"https:\u002F\u002Fmy-noydb-vault.vault.azure.net\u002Fkeys\u002Fnoydb-sealing\u002F0123456789abcdef",[2225,33593,2805],{"class":2235},[2225,33595,1707],{"class":2235},[2225,33597,33598,33600],{"class":2227,"line":2304},[2225,33599,2359],{"class":2235},[2225,33601,2362],{"class":2239},[2225,33603,33604,33606,33608,33610],{"class":2227,"line":2356},[2225,33605,13515],{"class":2239},[2225,33607,1520],{"class":2235},[2225,33609,33423],{"class":2239},[2225,33611,33612],{"class":2763},"\u002F\u002F → 'azure-kv:https:\u002F\u002Fmy-noydb-vault.vault.azure.net\u002Fkeys\u002Fnoydb-sealing\u002F0123456789abcdef'\n",[1022,33614,2621],{"id":2620},[1111,33616,33617,33621,33625],{},[1114,33618,33619],{},[1007,33620,33435],{"href":2634},[1114,33622,33623,33440],{},[1007,33624,828],{"href":829},[1114,33626,33627,33630],{},[1007,33628,175],{"href":33629},".\u002Faws-kms"," — the equivalent provider for AWS KMS",[2645,33632,30448],{},{"title":1037,"searchDepth":1038,"depth":1038,"links":33634},[33635,33636,33637],{"id":29856,"depth":1041,"text":29857},{"id":29892,"depth":1041,"text":29893},{"id":2620,"depth":1041,"text":2621},"Seals the managed-secret vault key via Azure Key Vault RSA Encrypt\u002FDecrypt, with Key Vault audit logs recording every vault open.",{},{"title":179,"description":33638},"4KAJV_4pDdHHdb2EJtY2NKSB6AQAOnUmqibHnylJMZw",{"id":33643,"title":183,"access":12,"apiToc":929,"body":33644,"category":1042,"description":33818,"extension":1044,"meta":33819,"navigation":1042,"nest":3303,"path":184,"seo":33820,"sinceVersion":1042,"status":1049,"stem":185,"subtitle":1042,"tier":1050,"tocExternal":1042,"__hash__":33821},"docs\u002Ffamilies\u002Fat\u002Fenv.md",{"type":931,"value":33645,"toc":33813},[33646,33648,33665,33667,33699,33705,33707,33761,33792,33794,33810],[934,33647,183],{"id":183},[1061,33649,33650],{},[937,33651,33652,1368,33657,33660,33661,33664],{},[950,33653,33654],{},[941,33655,33656],{},"@noy-db\u002Fat-env",[941,33658,33659],{},"pnpm add @noy-db\u002Fat-env","\nZero-infrastructure sealing — the key lives in ",[941,33662,33663],{},"NOYDB_SEALING_KEY"," (or a custom env var).",[1022,33666,29857],{"id":29856},[937,33668,33669,33672,33673,33675,33676,33679,33680,33683,33684,33687,33688,33690,33691,33694,33695,33698],{},[941,33670,33671],{},"atEnv()"," reads a base64-encoded 32-byte AES-256-GCM key from\nan environment variable (default ",[941,33674,33663],{},", override with\n",[941,33677,33678],{},"{ envVar }",") and uses it to seal\u002Funseal the hub-generated managed-mode\nsecret. It validates the key at construction — throwing when the\nvariable is unset, not valid base64, or not exactly 32 bytes — and caches\nthe imported ",[941,33681,33682],{},"CryptoKey"," for the provider instance's lifetime. Sealed\noutput is ",[941,33685,33686],{},"[12-byte IV][ciphertext + 16-byte GCM tag]",". Its ",[941,33689,33316],{}," is\n",[941,33692,33693],{},"env:\u003CenvVar>"," (e.g. ",[941,33696,33697],{},"env:NOYDB_SEALING_KEY","), stored next to the sealed\nbytes as audit metadata.",[937,33700,33701,33702,33704],{},"The env var ",[1092,33703,3082],{}," the security boundary: anyone who can read your\nprocess's environment can decrypt every secret sealed under it. Fine\nfor platform-managed secrets (Kubernetes Secrets, Heroku config, systemd\nenv); avoid it on shared machines and multi-tenant hosts.",[1022,33706,29893],{"id":29892},[2217,33708,33710],{"className":2219,"code":33709,"language":2221,"meta":1037,"style":1037},"import { atEnv } from '@noy-db\u002Fat-env'\n\nconst sealingKey = atEnv() \u002F\u002F reads NOYDB_SEALING_KEY by default\nsealingKey.id \u002F\u002F → 'env:NOYDB_SEALING_KEY'\n",[941,33711,33712,33731,33735,33750],{"__ignoreMap":1037},[2225,33713,33714,33716,33718,33721,33723,33725,33727,33729],{"class":2227,"line":2228},[2225,33715,2232],{"class":2231},[2225,33717,2236],{"class":2235},[2225,33719,33720],{"class":2239}," atEnv",[2225,33722,2243],{"class":2235},[2225,33724,2246],{"class":2231},[2225,33726,2249],{"class":2235},[2225,33728,33656],{"class":2252},[2225,33730,2256],{"class":2235},[2225,33732,33733],{"class":2227,"line":1041},[2225,33734,2285],{"emptyLinePlaceholder":2284},[2225,33736,33737,33739,33741,33743,33745,33747],{"class":2227,"line":1038},[2225,33738,2775],{"class":2774},[2225,33740,33384],{"class":2239},[2225,33742,2781],{"class":2235},[2225,33744,33720],{"class":2291},[2225,33746,19132],{"class":2239},[2225,33748,33749],{"class":2763},"\u002F\u002F reads NOYDB_SEALING_KEY by default\n",[2225,33751,33752,33754,33756,33758],{"class":2227,"line":2288},[2225,33753,13515],{"class":2239},[2225,33755,1520],{"class":2235},[2225,33757,33423],{"class":2239},[2225,33759,33760],{"class":2763},"\u002F\u002F → 'env:NOYDB_SEALING_KEY'\n",[2217,33762,33766],{"className":33763,"code":33764,"language":33765,"meta":1037,"style":1037},"language-bash shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","export NOYDB_SEALING_KEY=$(openssl rand -base64 32)\n","bash",[941,33767,33768],{"__ignoreMap":1037},[2225,33769,33770,33772,33775,33778,33781,33784,33787,33790],{"class":2227,"line":2228},[2225,33771,8080],{"class":2774},[2225,33773,33774],{"class":2239}," NOYDB_SEALING_KEY",[2225,33776,33777],{"class":2235},"=$(",[2225,33779,33780],{"class":6342},"openssl",[2225,33782,33783],{"class":2252}," rand",[2225,33785,33786],{"class":2252}," -base64",[2225,33788,33789],{"class":6527}," 32",[2225,33791,2362],{"class":2235},[1022,33793,2621],{"id":2620},[1111,33795,33796,33800,33804],{},[1114,33797,33798],{},[1007,33799,33435],{"href":2634},[1114,33801,33802,33440],{},[1007,33803,828],{"href":829},[1114,33805,33806,33809],{},[1007,33807,191],{"href":33808},".\u002Fmacos-keychain"," — the on-host alternative for desktop apps",[2645,33811,33812],{},"html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}",{"title":1037,"searchDepth":1038,"depth":1038,"links":33814},[33815,33816,33817],{"id":29856,"depth":1041,"text":29857},{"id":29892,"depth":1041,"text":29893},{"id":2620,"depth":1041,"text":2621},"Seals the managed-secret vault key with a base64-encoded AES-256-GCM key read from an environment variable.",{},{"title":183,"description":33818},"q0wyvjuYNPaDE4mXzpHe6rVLtrDCQNEPFUzq6x3hzeA",{"id":33823,"title":187,"access":12,"apiToc":929,"body":33824,"category":1042,"description":33993,"extension":1044,"meta":33994,"navigation":1042,"nest":3303,"path":188,"seo":33995,"sinceVersion":1042,"status":1049,"stem":189,"subtitle":1042,"tier":1050,"tocExternal":1042,"__hash__":33996},"docs\u002Ffamilies\u002Fat\u002Fgcp-kms.md",{"type":931,"value":33825,"toc":33988},[33826,33828,33841,33843,33873,33893,33895,33970,33972,33986],[934,33827,187],{"id":187},[1061,33829,33830],{},[937,33831,33832,1368,33837,33840],{},[950,33833,33834],{},[941,33835,33836],{},"@noy-db\u002Fat-gcp-kms",[941,33838,33839],{},"pnpm add @noy-db\u002Fat-gcp-kms","\nCloud-KMS sealing — the key lives in Google Cloud KMS, access controlled by IAM.",[1022,33842,29857],{"id":29856},[937,33844,33845,33848,33849,1625,33851,33853,33854,7356,33856,33320,33859,33862,33863,33507,33866,33869,33870,33872],{},[941,33846,33847],{},"atGcpKms({ keyName })"," seals and unseals the hub-generated\nmanaged-mode secret via authenticated Cloud KMS ",[941,33850,33309],{},[941,33852,33312],{},"\ncalls, backed by Cloud Audit Logs recording every vault open with no\nextra instrumentation. Its ",[941,33855,33316],{},[941,33857,33858],{},"gcp-kms:\u003CkeyName>",[941,33860,33861],{},"gcp-kms:projects\u002Fmy-project\u002Flocations\u002Fglobal\u002FkeyRings\u002Fnoy-db-ring\u002FcryptoKeys\u002Fnoy-db-sealing",").\nCredentials resolve via Application Default Credentials — an attached\nservice account, ",[941,33864,33865],{},"GOOGLE_APPLICATION_CREDENTIALS",[941,33867,33868],{},"gcloud auth application-default login","; never pass raw credentials in\nthe options — inject a pre-built ",[941,33871,9913],{}," instead for tests or\nnon-default auth.",[937,33874,33875,33876,1625,33878,33880,33881,33884,33885,6030,33887,33889,33890,33892],{},"Same ",[941,33877,2145],{},[941,33879,33532],{}," contract as the AWS provider. Cloud KMS supports\nautomatic key-version rotation for symmetric keys with no app-side\nchanges — the ",[941,33882,33883],{},"keyName"," stays the same. Cross-key migration requires\nmanual re-sealing with ",[941,33886,33532],{},[941,33888,2145],{}," under the new key. Unlike\n",[941,33891,13485],{},", there is no recipient-target extension.",[1022,33894,29893],{"id":29892},[2217,33896,33898],{"className":2219,"code":33897,"language":2221,"meta":1037,"style":1037},"import { atGcpKms } from '@noy-db\u002Fat-gcp-kms'\n\nconst sealingKey = atGcpKms({\n  keyName: 'projects\u002Fmy-project\u002Flocations\u002Fglobal\u002FkeyRings\u002Fnoy-db-ring\u002FcryptoKeys\u002Fnoy-db-sealing',\n})\nsealingKey.id \u002F\u002F → 'gcp-kms:projects\u002Fmy-project\u002Flocations\u002Fglobal\u002FkeyRings\u002Fnoy-db-ring\u002FcryptoKeys\u002Fnoy-db-sealing'\n",[941,33899,33900,33919,33923,33937,33953,33959],{"__ignoreMap":1037},[2225,33901,33902,33904,33906,33909,33911,33913,33915,33917],{"class":2227,"line":2228},[2225,33903,2232],{"class":2231},[2225,33905,2236],{"class":2235},[2225,33907,33908],{"class":2239}," atGcpKms",[2225,33910,2243],{"class":2235},[2225,33912,2246],{"class":2231},[2225,33914,2249],{"class":2235},[2225,33916,33836],{"class":2252},[2225,33918,2256],{"class":2235},[2225,33920,33921],{"class":2227,"line":1041},[2225,33922,2285],{"emptyLinePlaceholder":2284},[2225,33924,33925,33927,33929,33931,33933,33935],{"class":2227,"line":1038},[2225,33926,2775],{"class":2774},[2225,33928,33384],{"class":2239},[2225,33930,2781],{"class":2235},[2225,33932,33908],{"class":2291},[2225,33934,2789],{"class":2239},[2225,33936,4200],{"class":2235},[2225,33938,33939,33942,33944,33946,33949,33951],{"class":2227,"line":2288},[2225,33940,33941],{"class":2307},"  keyName",[2225,33943,2311],{"class":2235},[2225,33945,2249],{"class":2235},[2225,33947,33948],{"class":2252},"projects\u002Fmy-project\u002Flocations\u002Fglobal\u002FkeyRings\u002Fnoy-db-ring\u002FcryptoKeys\u002Fnoy-db-sealing",[2225,33950,2805],{"class":2235},[2225,33952,1707],{"class":2235},[2225,33954,33955,33957],{"class":2227,"line":2304},[2225,33956,2359],{"class":2235},[2225,33958,2362],{"class":2239},[2225,33960,33961,33963,33965,33967],{"class":2227,"line":2356},[2225,33962,13515],{"class":2239},[2225,33964,1520],{"class":2235},[2225,33966,33423],{"class":2239},[2225,33968,33969],{"class":2763},"\u002F\u002F → 'gcp-kms:projects\u002Fmy-project\u002Flocations\u002Fglobal\u002FkeyRings\u002Fnoy-db-ring\u002FcryptoKeys\u002Fnoy-db-sealing'\n",[1022,33971,2621],{"id":2620},[1111,33973,33974,33978,33982],{},[1114,33975,33976],{},[1007,33977,33435],{"href":2634},[1114,33979,33980,33440],{},[1007,33981,828],{"href":829},[1114,33983,33984,33630],{},[1007,33985,175],{"href":33629},[2645,33987,30448],{},{"title":1037,"searchDepth":1038,"depth":1038,"links":33989},[33990,33991,33992],{"id":29856,"depth":1041,"text":29857},{"id":29892,"depth":1041,"text":29893},{"id":2620,"depth":1041,"text":2621},"Seals the managed-secret vault key via Google Cloud KMS Encrypt\u002FDecrypt, with Cloud Audit Logs recording every vault open.",{},{"title":187,"description":33993},"fB7XHk1wc9PBRqjFU3iq_Wu5UbX1PLkahNgD0PMGUbA",{"id":33998,"title":33999,"access":12,"apiToc":929,"body":34000,"category":1042,"description":34277,"extension":1044,"meta":34278,"navigation":34279,"nest":3303,"path":170,"seo":34280,"sinceVersion":1042,"status":1049,"stem":171,"subtitle":1042,"tier":1050,"tocExternal":1042,"__hash__":34281},"docs\u002Ffamilies\u002Fat\u002Findex.md","`@noy-db\u002Fat-*` — Sealing-key providers (trusted hosts)",{"type":931,"value":34001,"toc":34270},[34002,34008,34026,34037,34061,34063,34074,34077,34109,34113,34153,34168,34170],[934,34003,34005,34007],{"id":34004},"noy-dbat-sealing-key-providers-trusted-hosts",[941,34006,16186],{}," — Sealing-key providers (trusted hosts)",[1061,34009,34010],{},[937,34011,34012,34015,34016,34018,34019,34021,34022,34025],{},[950,34013,34014],{},"How a vault goes online without the user holding a key."," noy-db\nencrypts everything on the client under keys derived from a secret,\nso normally only the user can open their vault. The ",[941,34017,1139],{}," family covers\nthe server-side case: a host ",[1092,34020,13501],{}," control — a Lambda, an EC2 box, a\nmicroservice — holds a ",[950,34023,34024],{},"sealing key"," that encrypts (\"seals\") a\nmachine-generated vault secret, so your server code can open the\nvault and work on behalf of a user who never logs in and never memorizes\na secret.",[937,34027,960,34028,30769,34030,34036],{},[941,34029,1267],{},[1092,34031,34032,34033,34035],{},"\"sealed ",[950,34034,4887],{}," a trusted host.\""," Each package\nsupplies the sealing key from the host's environment — an env var, the OS\nkeychain, or a cloud KMS — never hard-coded, always under your account's\nown access controls and rotation policy.",[937,34038,34039,34042,34043,34045,34046,34048,34049,13538,34051,34054,34055,34057,34058,34060],{},[950,34040,34041],{},"Be clear about the trust boundary."," Unlike ",[941,34044,1779],{}," stores and ",[941,34047,1795],{},"\ntransports — which only ever see ciphertext — an ",[941,34050,1139],{},[950,34052,34053],{},"can\ndecrypt the vault it unseals",". This is the one non-zero-knowledge family\nin noy-db, and it's the point: the safeguard is ",[1092,34056,13544],{},", not secrecy. The\nsealed secret is stored per vault (at ",[941,34059,14380],{}," inside\nthat vault), so a host holding a sealing key can open exactly the vaults\nsealed under it — nothing else. Offline-first stays the default; online\nis least-privilege, on infrastructure you own.",[1022,34062,30965],{"id":30964},[937,34064,34065,34066,34069,34070,34073],{},"Start with an ",[950,34067,34068],{},"on-host"," provider for local dev and single-instance \u002F\nsingle-machine deployments — no cloud dependency, no extra credentials.\nMove to a ",[950,34071,34072],{},"cloud KMS"," provider once you need key rotation, an\naccess-control audit trail (every seal\u002Funseal is an authenticated KMS\ncall, so the cloud's audit log records every vault open for free), or\nmultiple instances sharing one sealing key without sharing a secret file.",[1591,34075,34076],{"id":34068},"On-host",[1338,34078,34079,34088],{},[1341,34080,34081],{},[1344,34082,34083,34085],{},[1347,34084,9530],{},[1347,34086,34087],{},"One-liner",[1357,34089,34090,34100],{},[1344,34091,34092,34097],{},[1362,34093,34094],{},[1007,34095,183],{"href":34096},".\u002Fenv",[1362,34098,34099],{},"Base64-encoded AES-256-GCM key from an environment variable — zero infrastructure.",[1344,34101,34102,34106],{},[1362,34103,34104],{},[1007,34105,191],{"href":33808},[1362,34107,34108],{},"Key stored in the user's macOS login Keychain — desktop apps, \"logged into my Mac\" = vault open.",[1591,34110,34112],{"id":34111},"cloud-kms","Cloud KMS",[1338,34114,34115,34123],{},[1341,34116,34117],{},[1344,34118,34119,34121],{},[1347,34120,9530],{},[1347,34122,34087],{},[1357,34124,34125,34134,34143],{},[1344,34126,34127,34131],{},[1362,34128,34129],{},[1007,34130,175],{"href":33629},[1362,34132,34133],{},"AWS KMS Encrypt\u002FDecrypt, IAM-controlled, CloudTrail-logged; also ships a recipient-target extension.",[1344,34135,34136,34140],{},[1362,34137,34138],{},[1007,34139,187],{"href":33445},[1362,34141,34142],{},"Google Cloud KMS Encrypt\u002FDecrypt, IAM-controlled, Cloud Audit Logs-logged.",[1344,34144,34145,34150],{},[1362,34146,34147],{},[1007,34148,179],{"href":34149},".\u002Fazure-keyvault",[1362,34151,34152],{},"Azure Key Vault RSA encrypt\u002Fdecrypt, RBAC-controlled, Key Vault-audit-logged; version-bound key id.",[937,34154,34155,34156,34158,34159,1707,34161,968,34163,34165,34166,1520],{},"Every provider implements the same ",[941,34157,14350],{}," contract (",[941,34160,6983],{},[941,34162,2145],{},[941,34164,33532],{},"), so switching backends is a one-line change to\n",[941,34167,13515],{},[1022,34169,31192],{"id":31191},[1338,34171,34172,34188],{},[1341,34173,34174],{},[1344,34175,34176,34179,34182,34185],{},[1347,34177,34178],{},"Provider",[1347,34180,34181],{},"Where the sealing key lives",[1347,34183,34184],{},"pid format",[1347,34186,34187],{},"Key rotation",[1357,34189,34190,34206,34222,34238,34254],{},[1344,34191,34192,34196,34199,34203],{},[1362,34193,34194],{},[1007,34195,183],{"href":34096},[1362,34197,34198],{},"env var",[1362,34200,34201],{},[941,34202,33693],{},[1362,34204,34205],{},"✗ — static key; no built-in rotation",[1344,34207,34208,34212,34215,34220],{},[1362,34209,34210],{},[1007,34211,191],{"href":33808},[1362,34213,34214],{},"OS keychain",[1362,34216,34217],{},[941,34218,34219],{},"macos-keychain:\u003Cservice>\u002F\u003Caccount>",[1362,34221,34205],{},[1344,34223,34224,34228,34231,34235],{},[1362,34225,34226],{},[1007,34227,175],{"href":33629},[1362,34229,34230],{},"AWS KMS",[1362,34232,34233],{},[941,34234,33319],{},[1362,34236,34237],{},"✓ — automatic rotation for symmetric keys, no app-side changes",[1344,34239,34240,34244,34247,34251],{},[1362,34241,34242],{},[1007,34243,187],{"href":33445},[1362,34245,34246],{},"GCP KMS",[1362,34248,34249],{},[941,34250,33858],{},[1362,34252,34253],{},"✓ — automatic key-version rotation, no app-side changes (cross-key migration is manual)",[1344,34255,34256,34260,34263,34267],{},[1362,34257,34258],{},[1007,34259,179],{"href":34149},[1362,34261,34262],{},"Azure Key Vault",[1362,34264,34265],{},[941,34266,33489],{},[1362,34268,34269],{},"✗ — version-bound; rotation is a deliberate manual re-seal migration",{"title":1037,"searchDepth":1038,"depth":1038,"links":34271},[34272,34276],{"id":30964,"depth":1041,"text":30965,"children":34273},[34274,34275],{"id":34068,"depth":1038,"text":34076},{"id":34111,"depth":1038,"text":34112},{"id":31191,"depth":1041,"text":31192},"The sealing-key providers: at-* hosts (env, keychain, cloud KMS) that unseal a vault without a typed secret — the one non-zero-knowledge family.",{},{"title":169,"icon":127},{"title":33999,"description":34277},"n8cNoZbpg7k7TcVcLRY1KVl4NYg4JMgJXRjMm2WFw_0",{"id":34283,"title":191,"access":12,"apiToc":929,"body":34284,"category":1042,"description":34459,"extension":1044,"meta":34460,"navigation":1042,"nest":3303,"path":192,"seo":34461,"sinceVersion":1042,"status":1049,"stem":193,"subtitle":1042,"tier":1050,"tocExternal":1042,"__hash__":34462},"docs\u002Ffamilies\u002Fat\u002Fmacos-keychain.md",{"type":931,"value":34285,"toc":34454},[34286,34288,34301,34303,34324,34336,34338,34435,34437,34452],[934,34287,191],{"id":191},[1061,34289,34290],{},[937,34291,34292,1368,34297,34300],{},[950,34293,34294],{},[941,34295,34296],{},"@noy-db\u002Fat-macos-keychain",[941,34298,34299],{},"pnpm add @noy-db\u002Fat-macos-keychain","\nDesktop-app sealing — the key lives in the OS Keychain, gated by \"I'm logged into my Mac.\"",[1022,34302,29857],{"id":29856},[937,34304,34305,34308,34309,34312,34313,34316,34317,7356,34319,33320,34321,957],{},[941,34306,34307],{},"atMacosKeychain({ service, account })"," binds the sealing\nkey to the user's macOS login Keychain via the native ",[941,34310,34311],{},"@napi-rs\u002Fkeyring","\naddon. The first call for a given ",[941,34314,34315],{},"(service, account)"," generates a fresh\n32-byte AES-256 key and stores it in the Keychain; later calls (same\nprocess, or any future process running as that OS user) retrieve the\nsame key and unseal transparently — no secret prompt ever appears.\nIts ",[941,34318,33316],{},[941,34320,34219],{},[941,34322,34323],{},"macos-keychain:com.acme.app\u002Falice@acme.example",[937,34325,34326,34327,1625,34329,34331,34332,34335],{},"The Keychain entry is the security boundary — any process running as the\nsame OS user can read it. Users can add Touch ID gating to the entry\nthemselves via Keychain Access; the package does not gate it\nprogrammatically. It exposes only ",[941,34328,2145],{},[941,34330,33532],{}," (self-targeted) — not\nthe ",[941,34333,34334],{},"RecipientSealer"," contract — and throws at construction on any\nnon-macOS platform.",[1022,34337,29893],{"id":29892},[2217,34339,34341],{"className":2219,"code":34340,"language":2221,"meta":1037,"style":1037},"import { atMacosKeychain } from '@noy-db\u002Fat-macos-keychain'\n\nconst sealingKey = atMacosKeychain({\n  service: 'com.acme.app',       \u002F\u002F your bundle id \u002F app namespace\n  account: 'alice@acme.example', \u002F\u002F per-user keychain item\n})\nsealingKey.id \u002F\u002F → 'macos-keychain:com.acme.app\u002Falice@acme.example'\n",[941,34342,34343,34362,34366,34380,34399,34418,34424],{"__ignoreMap":1037},[2225,34344,34345,34347,34349,34352,34354,34356,34358,34360],{"class":2227,"line":2228},[2225,34346,2232],{"class":2231},[2225,34348,2236],{"class":2235},[2225,34350,34351],{"class":2239}," atMacosKeychain",[2225,34353,2243],{"class":2235},[2225,34355,2246],{"class":2231},[2225,34357,2249],{"class":2235},[2225,34359,34296],{"class":2252},[2225,34361,2256],{"class":2235},[2225,34363,34364],{"class":2227,"line":1041},[2225,34365,2285],{"emptyLinePlaceholder":2284},[2225,34367,34368,34370,34372,34374,34376,34378],{"class":2227,"line":1038},[2225,34369,2775],{"class":2774},[2225,34371,33384],{"class":2239},[2225,34373,2781],{"class":2235},[2225,34375,34351],{"class":2291},[2225,34377,2789],{"class":2239},[2225,34379,4200],{"class":2235},[2225,34381,34382,34385,34387,34389,34392,34394,34396],{"class":2227,"line":2288},[2225,34383,34384],{"class":2307},"  service",[2225,34386,2311],{"class":2235},[2225,34388,2249],{"class":2235},[2225,34390,34391],{"class":2252},"com.acme.app",[2225,34393,2805],{"class":2235},[2225,34395,2298],{"class":2235},[2225,34397,34398],{"class":2763},"       \u002F\u002F your bundle id \u002F app namespace\n",[2225,34400,34401,34404,34406,34408,34411,34413,34415],{"class":2227,"line":2304},[2225,34402,34403],{"class":2307},"  account",[2225,34405,2311],{"class":2235},[2225,34407,2249],{"class":2235},[2225,34409,34410],{"class":2252},"alice@acme.example",[2225,34412,2805],{"class":2235},[2225,34414,2298],{"class":2235},[2225,34416,34417],{"class":2763}," \u002F\u002F per-user keychain item\n",[2225,34419,34420,34422],{"class":2227,"line":2356},[2225,34421,2359],{"class":2235},[2225,34423,2362],{"class":2239},[2225,34425,34426,34428,34430,34432],{"class":2227,"line":2830},[2225,34427,13515],{"class":2239},[2225,34429,1520],{"class":2235},[2225,34431,33423],{"class":2239},[2225,34433,34434],{"class":2763},"\u002F\u002F → 'macos-keychain:com.acme.app\u002Falice@acme.example'\n",[1022,34436,2621],{"id":2620},[1111,34438,34439,34443,34447],{},[1114,34440,34441],{},[1007,34442,33435],{"href":2634},[1114,34444,34445,33440],{},[1007,34446,828],{"href":829},[1114,34448,34449,34451],{},[1007,34450,183],{"href":34096}," — the server\u002Fcontainer alternative (no Keychain in a container)",[2645,34453,30448],{},{"title":1037,"searchDepth":1038,"depth":1038,"links":34455},[34456,34457,34458],{"id":29856,"depth":1041,"text":29857},{"id":29892,"depth":1041,"text":29893},{"id":2620,"depth":1041,"text":2621},"Seals the managed-secret vault key with a 32-byte AES-256-GCM key stored in the user's macOS login Keychain.",{},{"title":191,"description":34459},"vHXC5k0QfQxtk0jHBzisTGno6RbVTogIOWrhmJWix04",{"id":34464,"title":34465,"access":12,"apiToc":929,"body":34466,"category":1042,"description":34636,"extension":1044,"meta":34637,"navigation":34638,"nest":3303,"path":196,"seo":34639,"sinceVersion":1042,"status":1049,"stem":197,"subtitle":1042,"tier":1050,"tocExternal":1042,"__hash__":34640},"docs\u002Ffamilies\u002Fby\u002Findex.md","`@noy-db\u002Fby-*` — Session-share transports",{"type":931,"value":34467,"toc":34628},[34468,34475,34488,34490,34494,34520,34524,34549,34551,34597,34601],[934,34469,34471,34474],{"id":34470},"noy-dbby-session-share-transports",[941,34472,34473],{},"@noy-db\u002Fby-*"," — Session-share transports",[1061,34476,34477],{},[937,34478,34479,14636,34482,34484,34485,34487],{},[950,34480,34481],{},"Move an unlocked session between contexts.",[941,34483,1795],{}," package carries\nnoy-db's encrypted envelopes ",[950,34486,727],{}," between two running instances —\ntabs in the same browser, peers across a network — so they can see each\nother's changes. The transport itself never sees plaintext, only\nAES-256-GCM ciphertext.",[1022,34489,30965],{"id":30964},[1591,34491,34493],{"id":34492},"peer-to-peer","Peer-to-peer",[1338,34495,34496,34504],{},[1341,34497,34498],{},[1344,34499,34500,34502],{},[1347,34501,9530],{},[1347,34503,29857],{},[1357,34505,34506],{},[1344,34507,34508,34513],{},[1362,34509,34510],{},[1007,34511,201],{"href":34512},".\u002Fpeer",[1362,34514,34515,34516,34519],{},"WebRTC ",[941,34517,34518],{},"DataChannel"," transport between two browsers or Node processes — no relay server in the middle.",[1591,34521,34523],{"id":34522},"same-device-tabs","Same-device tabs",[1338,34525,34526,34534],{},[1341,34527,34528],{},[1344,34529,34530,34532],{},[1347,34531,9530],{},[1347,34533,29857],{},[1357,34535,34536],{},[1344,34537,34538,34543],{},[1362,34539,34540],{},[1007,34541,205],{"href":34542},".\u002Ftabs",[1362,34544,34545,34548],{},[941,34546,34547],{},"BroadcastChannel"," transport — sub-millisecond fan-out between tabs of the same browser origin.",[1022,34550,31192],{"id":31191},[1338,34552,34553,34568],{},[1341,34554,34555],{},[1344,34556,34557,34559,34562,34565],{},[1347,34558,9530],{},[1347,34560,34561],{},"Transport",[1347,34563,34564],{},"Scope",[1347,34566,34567],{},"Sees only ciphertext",[1357,34569,34570,34584],{},[1344,34571,34572,34576,34579,34582],{},[1362,34573,34574],{},[1007,34575,201],{"href":34512},[1362,34577,34578],{},"WebRTC data channel",[1362,34580,34581],{},"remote peers",[1362,34583,9581],{},[1344,34585,34586,34590,34592,34595],{},[1362,34587,34588],{},[1007,34589,205],{"href":34542},[1362,34591,34547],{},[1362,34593,34594],{},"same-device tabs",[1362,34596,9581],{},[1022,34598,34600],{"id":34599},"related","Related",[1111,34602,34603,34617],{},[1114,34604,34605,997,34609,34613,34614,34616],{},[1007,34606,34607,21025],{"href":15100},[941,34608,864],{},[1007,34610,34611,21025],{"href":21966},[941,34612,653],{}," — the hub-side strategies that produce the change stream a ",[941,34615,1795],{}," transport carries.",[1114,34618,34619,34623,34624,997,34626,1520],{},[1007,34620,34622],{"href":34621},"..\u002F..\u002Frecipes\u002Frealtime-crdt-app","Real-time CRDT app"," — the canonical real-time recipe; pairs naturally with ",[941,34625,16057],{},[941,34627,16061],{},{"title":1037,"searchDepth":1038,"depth":1038,"links":34629},[34630,34634,34635],{"id":30964,"depth":1041,"text":30965,"children":34631},[34632,34633],{"id":34492,"depth":1038,"text":34493},{"id":34522,"depth":1038,"text":34523},{"id":31191,"depth":1041,"text":31192},{"id":34599,"depth":1041,"text":34600},"The session-share transports: by-peer (WebRTC, same network) and by-tabs (BroadcastChannel, same browser) move an unlocked session between contexts.",{},{"title":195,"icon":127},{"title":34465,"description":34636},"fXmGCMqxxgEOaWlr6V4xooHJDkNCcuGGVeh3jL1FPR0",{"id":34642,"title":201,"access":12,"apiToc":929,"body":34643,"category":1042,"description":34992,"extension":1044,"meta":34993,"navigation":1042,"nest":3303,"path":202,"seo":34994,"sinceVersion":1042,"status":1049,"stem":203,"subtitle":1042,"tier":1050,"tocExternal":1042,"__hash__":34995},"docs\u002Ffamilies\u002Fby\u002Fpeer.md",{"type":931,"value":34644,"toc":34987},[34645,34647,34663,34665,34712,34730,34732,34962,34972,34974,34985],[934,34646,201],{"id":201},[1061,34648,34649],{},[937,34650,34651,1368,34656,34659,34660,34662],{},[950,34652,34653],{},[941,34654,34655],{},"@noy-db\u002Fby-peer",[941,34657,34658],{},"pnpm add @noy-db\u002Fby-peer @noy-db\u002Fhub","\nSyncs an unlocked vault directly between two browsers or Node processes over a WebRTC ",[941,34661,34518],{}," — no server in the middle.",[1022,34664,29857],{"id":29856},[937,34666,34667,34669,34670,34672,34673,34676,34677,34679,34680,34683,34684,34686,34687,34689,34690,34692,34693,28806,34696,34699,34700,997,34702,34704,34705,1261,34708,34711],{},[941,34668,34655],{}," opens a WebRTC ",[941,34671,34518],{}," between two peers and carries noy-db's encrypted envelopes across it — any TURN relay in the path only ever sees AES-256-GCM ciphertext. ",[941,34674,34675],{},"peerStore()"," wraps the far end as a ",[941,34678,3154],{}," that RPCs every operation to the remote peer; ",[941,34681,34682],{},"servePeerStore()"," runs on the other side and answers those RPCs against a local store. ",[941,34685,34675],{}," also slots into ",[941,34688,12780],{},"'s ",[941,34691,864],{}," option as a ",[941,34694,34695],{},"SyncTarget",[941,34697,34698],{},"role: 'sync-peer'",", pairing with the ",[941,34701,864],{},[941,34703,653],{}," services for collaborative editing. ",[941,34706,34707],{},"createOffer()",[941,34709,34710],{},"acceptOffer()"," are thin SDP handshake helpers — signaling itself (how the offer\u002Fanswer blobs travel) is left to the caller: QR code, chat message, pastebin, whatever fits.",[937,34713,34714,34717,34718,34721,34722,34725,34726,34729],{},[941,34715,34716],{},"servePeerStore"," accepts an ",[941,34719,34720],{},"allow"," whitelist to expose a read-only or append-only peer, and a ",[941,34723,34724],{},"leaderElection"," option (Web Locks API) so that when 3+ tabs share a channel — typically via ",[941,34727,34728],{},"@noy-db\u002Fby-tabs"," — only one tab answers RPCs at a time.",[1022,34731,29893],{"id":29892},[2217,34733,34735],{"className":2219,"code":34734,"language":2221,"meta":1037,"style":1037},"import { createNoydb } from '@noy-db\u002Fhub'\nimport { toBrowserIdb } from '@noy-db\u002Fto-browser-idb'\nimport { peerStore, createOffer, acceptOffer } from '@noy-db\u002Fby-peer'\n\n\u002F\u002F Peer A — initiator\nconst initiator = await createOffer()\nshareWithPeerB(initiator.offer)             \u002F\u002F QR code, Matrix, pastebin, …\nawait initiator.accept(await receiveAnswer())\nconst channel = await initiator.channel\n\nconst db = await createNoydb({\n  store: toBrowserIdb(),\n  sync: { store: peerStore({ channel }), role: 'sync-peer' },\n})\n",[941,34736,34737,34755,34774,34803,34807,34812,34827,34843,34865,34883,34887,34903,34915,34956],{"__ignoreMap":1037},[2225,34738,34739,34741,34743,34745,34747,34749,34751,34753],{"class":2227,"line":2228},[2225,34740,2232],{"class":2231},[2225,34742,2236],{"class":2235},[2225,34744,2744],{"class":2239},[2225,34746,2243],{"class":2235},[2225,34748,2246],{"class":2231},[2225,34750,2249],{"class":2235},[2225,34752,1086],{"class":2252},[2225,34754,2256],{"class":2235},[2225,34756,34757,34759,34761,34764,34766,34768,34770,34772],{"class":2227,"line":1041},[2225,34758,2232],{"class":2231},[2225,34760,2236],{"class":2235},[2225,34762,34763],{"class":2239}," toBrowserIdb",[2225,34765,2243],{"class":2235},[2225,34767,2246],{"class":2231},[2225,34769,2249],{"class":2235},[2225,34771,9573],{"class":2252},[2225,34773,2256],{"class":2235},[2225,34775,34776,34778,34780,34783,34785,34788,34790,34793,34795,34797,34799,34801],{"class":2227,"line":1038},[2225,34777,2232],{"class":2231},[2225,34779,2236],{"class":2235},[2225,34781,34782],{"class":2239}," peerStore",[2225,34784,2298],{"class":2235},[2225,34786,34787],{"class":2239}," createOffer",[2225,34789,2298],{"class":2235},[2225,34791,34792],{"class":2239}," acceptOffer",[2225,34794,2243],{"class":2235},[2225,34796,2246],{"class":2231},[2225,34798,2249],{"class":2235},[2225,34800,34655],{"class":2252},[2225,34802,2256],{"class":2235},[2225,34804,34805],{"class":2227,"line":2288},[2225,34806,2285],{"emptyLinePlaceholder":2284},[2225,34808,34809],{"class":2227,"line":2304},[2225,34810,34811],{"class":2763},"\u002F\u002F Peer A — initiator\n",[2225,34813,34814,34816,34819,34821,34823,34825],{"class":2227,"line":2356},[2225,34815,2775],{"class":2774},[2225,34817,34818],{"class":2239}," initiator ",[2225,34820,2781],{"class":2235},[2225,34822,2784],{"class":2231},[2225,34824,34787],{"class":2291},[2225,34826,8146],{"class":2239},[2225,34828,34829,34832,34835,34837,34840],{"class":2227,"line":2830},[2225,34830,34831],{"class":2291},"shareWithPeerB",[2225,34833,34834],{"class":2239},"(initiator",[2225,34836,1520],{"class":2235},[2225,34838,34839],{"class":2239},"offer)             ",[2225,34841,34842],{"class":2763},"\u002F\u002F QR code, Matrix, pastebin, …\n",[2225,34844,34845,34847,34850,34852,34855,34857,34859,34862],{"class":2227,"line":2861},[2225,34846,6500],{"class":2231},[2225,34848,34849],{"class":2239}," initiator",[2225,34851,1520],{"class":2235},[2225,34853,34854],{"class":2291},"accept",[2225,34856,2789],{"class":2239},[2225,34858,6500],{"class":2231},[2225,34860,34861],{"class":2291}," receiveAnswer",[2225,34863,34864],{"class":2239},"())\n",[2225,34866,34867,34869,34872,34874,34876,34878,34880],{"class":2227,"line":4246},[2225,34868,2775],{"class":2774},[2225,34870,34871],{"class":2239}," channel ",[2225,34873,2781],{"class":2235},[2225,34875,2784],{"class":2231},[2225,34877,34849],{"class":2239},[2225,34879,1520],{"class":2235},[2225,34881,34882],{"class":2239},"channel\n",[2225,34884,34885],{"class":2227,"line":4253},[2225,34886,2285],{"emptyLinePlaceholder":2284},[2225,34888,34889,34891,34893,34895,34897,34899,34901],{"class":2227,"line":4259},[2225,34890,2775],{"class":2774},[2225,34892,2778],{"class":2239},[2225,34894,2781],{"class":2235},[2225,34896,2784],{"class":2231},[2225,34898,2744],{"class":2291},[2225,34900,2789],{"class":2239},[2225,34902,4200],{"class":2235},[2225,34904,34905,34907,34909,34911,34913],{"class":2227,"line":6548},[2225,34906,15608],{"class":2307},[2225,34908,2311],{"class":2235},[2225,34910,34763],{"class":2291},[2225,34912,4228],{"class":2239},[2225,34914,1707],{"class":2235},[2225,34916,34917,34920,34922,34924,34926,34928,34930,34932,34934,34936,34938,34940,34942,34945,34947,34949,34952,34954],{"class":2227,"line":6576},[2225,34918,34919],{"class":2307},"  sync",[2225,34921,2311],{"class":2235},[2225,34923,2236],{"class":2235},[2225,34925,7564],{"class":2307},[2225,34927,2311],{"class":2235},[2225,34929,34782],{"class":2291},[2225,34931,2789],{"class":2239},[2225,34933,2792],{"class":2235},[2225,34935,34871],{"class":2239},[2225,34937,2359],{"class":2235},[2225,34939,1381],{"class":2239},[2225,34941,2298],{"class":2235},[2225,34943,34944],{"class":2307}," role",[2225,34946,2311],{"class":2235},[2225,34948,2249],{"class":2235},[2225,34950,34951],{"class":2252},"sync-peer",[2225,34953,2805],{"class":2235},[2225,34955,2353],{"class":2235},[2225,34957,34958,34960],{"class":2227,"line":6582},[2225,34959,2359],{"class":2235},[2225,34961,2362],{"class":2239},[937,34963,34964,34965,34967,34968,34971],{},"Peer B mirrors the handshake with ",[941,34966,34710],{}," and runs ",[941,34969,34970],{},"servePeerStore({ channel, store })"," so its local store answers the incoming RPC calls.",[1022,34973,2621],{"id":2620},[1111,34975,34976,34981],{},[1114,34977,34978],{},[1007,34979,34980],{"href":2634},"by-* family",[1114,34982,34983],{},[1007,34984,205],{"href":34542},[2645,34986,3254],{},{"title":1037,"searchDepth":1038,"depth":1038,"links":34988},[34989,34990,34991],{"id":29856,"depth":1041,"text":29857},{"id":29892,"depth":1041,"text":29893},{"id":2620,"depth":1041,"text":2621},"WebRTC peer-to-peer transport that carries encrypted envelopes directly between two noy-db instances, with no relay server in the middle.",{},{"title":201,"description":34992},"_-1ktt6qt2o-ZWlQK31X1L8aWWWNwtrlVFZclMi2gaw",{"id":34997,"title":205,"access":12,"apiToc":929,"body":34998,"category":1042,"description":35286,"extension":1044,"meta":35287,"navigation":1042,"nest":3303,"path":206,"seo":35288,"sinceVersion":1042,"status":1049,"stem":207,"subtitle":1042,"tier":1050,"tocExternal":1042,"__hash__":35289},"docs\u002Ffamilies\u002Fby\u002Ftabs.md",{"type":931,"value":34999,"toc":35281},[35000,35002,35017,35019,35062,35067,35069,35266,35268,35278],[934,35001,205],{"id":205},[1061,35003,35004],{},[937,35005,35006,1368,35010,35013,35014,35016],{},[950,35007,35008],{},[941,35009,34728],{},[941,35011,35012],{},"pnpm add @noy-db\u002Fby-tabs @noy-db\u002Fby-peer @noy-db\u002Fhub","\nSub-millisecond fan-out between tabs of the same browser origin over the native ",[941,35015,34547],{}," API.",[1022,35018,29857],{"id":29856},[937,35020,35021,35023,35024,35026,35027,35030,35031,35033,35034,1261,35036,17904,35038,35040,35041,35044,35045,35047,35048,35051,35052,35055,35056,968,35058,35061],{},[941,35022,34728],{}," wraps the browser's ",[941,35025,34547],{}," as a ",[941,35028,35029],{},"PeerChannel"," — the same channel shape every other ",[941,35032,1795],{}," transport implements — so it composes with ",[941,35035,34675],{},[941,35037,34682],{},[941,35039,34655],{},". One tab serves its store over the channel; the others operate on it live instead of each maintaining their own round-trip to IndexedDB. ",[941,35042,35043],{},"isTabsChannelAvailable()"," is a pre-flight check: it returns ",[941,35046,2560],{}," under SSR or Node, or in older browsers, where ",[941,35049,35050],{},"tabsChannel()"," falls back to a no-op channel (",[941,35053,35054],{},"isOpen"," stays ",[941,35057,2560],{},[941,35059,35060],{},"send()"," throws) so importing code doesn't crash before feature-detecting.",[937,35063,35064,35066],{},[941,35065,34547],{}," is same-origin by browser policy — only documents on the same scheme + host + port can subscribe. noy-db already encrypts at rest, so every tab on the channel sees AES-256-GCM ciphertext envelopes only; treat the channel as origin-scoped, not session-scoped, since untrusted same-origin iframes can still subscribe.",[1022,35068,29893],{"id":29892},[2217,35070,35072],{"className":2219,"code":35071,"language":2221,"meta":1037,"style":1037},"import { tabsChannel, isTabsChannelAvailable } from '@noy-db\u002Fby-tabs'\nimport { peerStore, servePeerStore } from '@noy-db\u002Fby-peer'\n\nif (!isTabsChannelAvailable()) {\n  \u002F\u002F Node, SSR, or an older browser — fall back to direct store access.\n}\n\n\u002F\u002F Tab A — proxy operations to Tab B's store.\nconst channel = tabsChannel({ name: 'noy-db:vault-acme' })\nconst remote = peerStore({ channel })\n\n\u002F\u002F Tab B — answer the RPC calls against its local store.\nservePeerStore({ channel: tabsChannel({ name: 'noy-db:vault-acme' }), store: localStore })\n",[941,35073,35074,35098,35121,35125,35143,35148,35152,35156,35161,35190,35211,35215,35220],{"__ignoreMap":1037},[2225,35075,35076,35078,35080,35083,35085,35088,35090,35092,35094,35096],{"class":2227,"line":2228},[2225,35077,2232],{"class":2231},[2225,35079,2236],{"class":2235},[2225,35081,35082],{"class":2239}," tabsChannel",[2225,35084,2298],{"class":2235},[2225,35086,35087],{"class":2239}," isTabsChannelAvailable",[2225,35089,2243],{"class":2235},[2225,35091,2246],{"class":2231},[2225,35093,2249],{"class":2235},[2225,35095,34728],{"class":2252},[2225,35097,2256],{"class":2235},[2225,35099,35100,35102,35104,35106,35108,35111,35113,35115,35117,35119],{"class":2227,"line":1041},[2225,35101,2232],{"class":2231},[2225,35103,2236],{"class":2235},[2225,35105,34782],{"class":2239},[2225,35107,2298],{"class":2235},[2225,35109,35110],{"class":2239}," servePeerStore",[2225,35112,2243],{"class":2235},[2225,35114,2246],{"class":2231},[2225,35116,2249],{"class":2235},[2225,35118,34655],{"class":2252},[2225,35120,2256],{"class":2235},[2225,35122,35123],{"class":2227,"line":1038},[2225,35124,2285],{"emptyLinePlaceholder":2284},[2225,35126,35127,35130,35132,35135,35138,35141],{"class":2227,"line":2288},[2225,35128,35129],{"class":2231},"if",[2225,35131,953],{"class":2239},[2225,35133,35134],{"class":2235},"!",[2225,35136,35137],{"class":2291},"isTabsChannelAvailable",[2225,35139,35140],{"class":2239},"()) ",[2225,35142,4200],{"class":2235},[2225,35144,35145],{"class":2227,"line":2304},[2225,35146,35147],{"class":2763},"  \u002F\u002F Node, SSR, or an older browser — fall back to direct store access.\n",[2225,35149,35150],{"class":2227,"line":2356},[2225,35151,7351],{"class":2235},[2225,35153,35154],{"class":2227,"line":2830},[2225,35155,2285],{"emptyLinePlaceholder":2284},[2225,35157,35158],{"class":2227,"line":2861},[2225,35159,35160],{"class":2763},"\u002F\u002F Tab A — proxy operations to Tab B's store.\n",[2225,35162,35163,35165,35167,35169,35171,35173,35175,35177,35179,35181,35184,35186,35188],{"class":2227,"line":4246},[2225,35164,2775],{"class":2774},[2225,35166,34871],{"class":2239},[2225,35168,2781],{"class":2235},[2225,35170,35082],{"class":2291},[2225,35172,2789],{"class":2239},[2225,35174,2792],{"class":2235},[2225,35176,8104],{"class":2307},[2225,35178,2311],{"class":2235},[2225,35180,2249],{"class":2235},[2225,35182,35183],{"class":2252},"noy-db:vault-acme",[2225,35185,2805],{"class":2235},[2225,35187,2243],{"class":2235},[2225,35189,2362],{"class":2239},[2225,35191,35192,35194,35197,35199,35201,35203,35205,35207,35209],{"class":2227,"line":4253},[2225,35193,2775],{"class":2774},[2225,35195,35196],{"class":2239}," remote ",[2225,35198,2781],{"class":2235},[2225,35200,34782],{"class":2291},[2225,35202,2789],{"class":2239},[2225,35204,2792],{"class":2235},[2225,35206,34871],{"class":2239},[2225,35208,2359],{"class":2235},[2225,35210,2362],{"class":2239},[2225,35212,35213],{"class":2227,"line":4259},[2225,35214,2285],{"emptyLinePlaceholder":2284},[2225,35216,35217],{"class":2227,"line":6548},[2225,35218,35219],{"class":2763},"\u002F\u002F Tab B — answer the RPC calls against its local store.\n",[2225,35221,35222,35224,35226,35228,35231,35233,35235,35237,35239,35241,35243,35245,35247,35249,35251,35253,35255,35257,35259,35262,35264],{"class":2227,"line":6576},[2225,35223,34716],{"class":2291},[2225,35225,2789],{"class":2239},[2225,35227,2792],{"class":2235},[2225,35229,35230],{"class":2307}," channel",[2225,35232,2311],{"class":2235},[2225,35234,35082],{"class":2291},[2225,35236,2789],{"class":2239},[2225,35238,2792],{"class":2235},[2225,35240,8104],{"class":2307},[2225,35242,2311],{"class":2235},[2225,35244,2249],{"class":2235},[2225,35246,35183],{"class":2252},[2225,35248,2805],{"class":2235},[2225,35250,2243],{"class":2235},[2225,35252,1381],{"class":2239},[2225,35254,2298],{"class":2235},[2225,35256,7564],{"class":2307},[2225,35258,2311],{"class":2235},[2225,35260,35261],{"class":2239}," localStore ",[2225,35263,2359],{"class":2235},[2225,35265,2362],{"class":2239},[1022,35267,2621],{"id":2620},[1111,35269,35270,35274],{},[1114,35271,35272],{},[1007,35273,34980],{"href":2634},[1114,35275,35276],{},[1007,35277,201],{"href":34512},[2645,35279,35280],{},"html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":1037,"searchDepth":1038,"depth":1038,"links":35282},[35283,35284,35285],{"id":29856,"depth":1041,"text":29857},{"id":29892,"depth":1041,"text":29893},{"id":2620,"depth":1041,"text":2621},"BroadcastChannel transport that fans a session out live across browser tabs of the same origin, without round-tripping the storage backend.",{},{"title":205,"description":35286},"xdMc2ACkQ18t7nYb-D-LuimFgt1OlD_Rlg__69VZSoQ",{"id":35291,"title":209,"access":12,"apiToc":929,"body":35292,"category":1042,"description":36031,"extension":1044,"meta":36032,"navigation":1042,"nest":3303,"path":210,"seo":36033,"sinceVersion":1042,"status":1049,"stem":211,"subtitle":1042,"tier":1473,"tocExternal":1042,"__hash__":36034},"docs\u002Ffamilies\u002Fclassification.md",{"type":931,"value":35293,"toc":36022},[35294,35297,35305,35339,35343,35346,35428,35437,35441,35592,35600,35604,35666,35670,35673,35741,35763,35767,35770,35787,35809,35835,35866,35895,35899,35988,35990],[934,35295,209],{"id":35296},"classifying-a-package",[1061,35298,35299],{},[937,35300,35301,35302,35304],{},"Companion: ",[1007,35303,37],{"href":38}," — the architecture view of the same\nprefixes.",[937,35306,35307,35308,35310,35311,968,35313,1707,35315,35317,35318,35320,35321,35323,35324,35327,35328,35331,35332,35335,35336,1520],{},"noy-db is an encrypted document store built as one core package, ",[941,35309,1086],{},", surrounded by\nsatellite npm packages whose names begin with a preposition — ",[941,35312,7461],{},[941,35314,15953],{},[941,35316,30483],{},", and so on. This page is the decision guide for where any package or capability\nbelongs in that catalog: in one of the six preposition ",[950,35319,116],{},", inside the hub as an\nopt-in ",[950,35322,1047],{},", or at the ",[950,35325,35326],{},"prefix-less top level"," (packages like ",[941,35329,35330],{},"@noy-db\u002Fcli"," that carry\nno preposition at all). It assumes you have read the\n",[1007,35333,35334],{"href":115},"Families & ports overview","; the structural background is in\n",[1007,35337,35338],{"href":38},"Architecture → Families",[1022,35340,35342],{"id":35341},"the-two-axes","The two axes",[937,35344,35345],{},"Two independent classification axes are easy to conflate:",[1234,35347,35348,35402],{},[1114,35349,35350,953,35353,1261,35355,1261,35357,1261,35359,1261,35361,1261,35363,35365,35366,35369,35370,35372,35373,35375,35376,35378,35379,35382,35383,35385,35386,35388,35389,35391,35392,35395,35396,35398,35399,35401],{},[950,35351,35352],{},"Package families",[941,35354,1260],{},[941,35356,1264],{},[941,35358,1267],{},[941,35360,1270],{},[941,35362,1273],{},[941,35364,1276],{},") — separate npm\npackages, classified by their ",[950,35367,35368],{},"relationship to the trust boundary",": the line around\n",[941,35371,1086],{}," inside which decrypted data and key material may exist. Everything outside\nthat line sees ciphertext, or plaintext only as an ordinary authorized consumer. Each\nfamily binds exactly one frozen ",[1007,35374,3292],{"href":30}," — a contract-only import\nsubpath such as ",[941,35377,3299],{}," whose export surface is snapshot-checked so it can only\nchange deliberately. The preposition is written from the ",[950,35380,35381],{},"vault owner's perspective",":\ndata goes ",[1092,35384,4879],{}," a backend, the owner gets ",[1092,35387,4883],{}," via a method, exports go out ",[1092,35390,4899],{}," a format,\nthe hub is adapted ",[1092,35393,35394],{},"into"," a host runtime, sessions sync ",[1092,35397,4895],{}," a transport, slices are\nsealed ",[1092,35400,4887],{}," a host you control.",[1114,35403,35404,953,35407,35409,35410,35412,35413,35416,35417,35419,35420,35422,35423,35425,35426,1520],{},[950,35405,35406],{},"Hub services",[941,35408,1031],{}," clusters inside ",[941,35411,4033],{},") — opt-in capabilities of\nthe vault itself, living ",[950,35414,35415],{},"inside the trust boundary",", enabled one at a time by passing a\n",[941,35418,943],{}," strategy factory to ",[941,35421,2134],{},", tree-shaken out of your bundle when unused.\nThe full catalog is the ",[1007,35424,597],{"href":598}," section; how the folders group is\n",[1007,35427,33],{"href":34},[937,35429,5149,35430,35432,35433,35436],{},[1092,35431,5152],{},". A service answers ",[1092,35434,35435],{},"\"what can the\nvault itself do?\"",". Nothing is ever both.",[1022,35438,35440],{"id":35439},"the-family-table-refined-glosses","The family table (refined glosses)",[1338,35442,35443,35460],{},[1341,35444,35445],{},[1344,35446,35447,35449,35451,35454,35456,35458],{},[1347,35448,4928],{},[1347,35450,4931],{},[1347,35452,35453],{},"Precise gloss",[1347,35455,4934],{},[1347,35457,4937],{},[1347,35459,4940],{},[1357,35461,35462,35486,35506,35525,35547,35566],{},[1344,35463,35464,35468,35472,35480,35482,35484],{},[1362,35465,35466],{},[941,35467,1260],{},[1362,35469,4953,35470,4956],{},[950,35471,4879],{},[1362,35473,5983,35474,12536,35477,35479],{},[950,35475,35476],{},"medium that persists ciphertext envelopes",[1092,35478,3016],{},", never a cache tier",[1362,35481,4959],{},[1362,35483,4962],{},[1362,35485,4965],{},[1344,35487,35488,35492,35496,35499,35501,35504],{},[1362,35489,35490],{},[941,35491,1264],{},[1362,35493,4978,35494,4981],{},[950,35495,4883],{},[1362,35497,35498],{},"unlock \u002F auth primitive",[1362,35500,4962],{},[1362,35502,35503],{},"Handles unlock material, never data-encryption keys (DEKs)",[1362,35505,4962],{},[1344,35507,35508,35512,35516,35519,35521,35523],{},[1362,35509,35510],{},[941,35511,1267],{},[1362,35513,5001,35514,5004],{},[950,35515,4887],{},[1362,35517,35518],{},"sealing-key provider (the one family that is not zero-knowledge, by design)",[1362,35520,5007],{},[1362,35522,5010],{},[1362,35524,4962],{},[1344,35526,35527,35531,35535,35541,35543,35545],{},[1362,35528,35529],{},[941,35530,1270],{},[1362,35532,5025,35533,5028],{},[950,35534,4891],{},[1362,35536,35537,35540],{},[950,35538,35539],{},"adapts the hub's existing public surface into a host runtime or protocol"," (React render loop, TanStack cache lifecycle, an LLM tool-calling loop, REST, Yjs)",[1362,35542,5031],{},[1362,35544,4962],{},[1362,35546,5036],{},[1344,35548,35549,35553,35557,35560,35562,35564],{},[1362,35550,35551],{},[941,35552,1273],{},[1362,35554,5049,35555,5052],{},[950,35556,4895],{},[1362,35558,35559],{},"session-share transport",[1362,35561,4962],{},[1362,35563,4962],{},[1362,35565,4962],{},[1344,35567,35568,35572,35576,35582,35584,35588],{},[1362,35569,35570],{},[941,35571,1276],{},[1362,35573,5071,35574,5074],{},[950,35575,4899],{},[1362,35577,35578,35581],{},[950,35579,35580],{},"pure read-side transform",": vault handle in → formatted bytes out",[1362,35583,5077],{},[1362,35585,35586],{},[950,35587,5082],{},[1362,35589,35590],{},[950,35591,5082],{},[937,35593,5093,35594,35596,35597,35599],{},[1092,35595,1659],{}," here is the hub's encrypted wire unit (",[941,35598,7084],{},"): ciphertext plus the\nmetadata a store needs to persist it — never decryptable by the store.",[1022,35601,35603],{"id":35602},"two-sharpened-glosses","Two sharpened glosses",[1111,35605,35606,35636],{},[1114,35607,35608,1324,35618,35620,35621,968,35624,968,35626,35629,35630,35632,35633,35635],{},[950,35609,35610,2993,35612,35614,35615,1520],{},[941,35611,1260],{},[1092,35613,3016],{},", not a ",[1092,35616,35617],{},"how fast",[941,35619,2992],{}," is not a cache — it is RAM used as a\nciphertext persistence backend, holding encrypted envelopes plus every reserved internal\ncollection (",[941,35622,35623],{},"_schemas",[941,35625,6715],{},[941,35627,35628],{},"_ftindex",", tombstones…). The kernel's internal record\ncache is the opposite object on every axis: plaintext, per-collection, inside the trust\nboundary, not pluggable, not a family member. They coexist because they answer different\nquestions. ",[941,35631,2992],{}," additionally serves as the reference store: the conformance oracle\nevery other ",[941,35634,1779],{}," must match, and the substrate that lets all of CI run with no real cloud\nservice.",[1114,35637,35638,1324,35643,35646,35647,2462,35650,35653,35654,35657,35658,35660,35661,997,35663,35665],{},[950,35639,35640,35642],{},[941,35641,1270],{}," means \"into a host\", not \"UI framework\".",[941,35644,35645],{},"in-ai"," (LLM function-calling adapter),\n",[941,35648,35649],{},"in-rest",[941,35651,35652],{},"in-yjs"," are full family members: each projects the hub's existing surface\ninto a foreign runtime's idiom and is meaningless without that runtime. The test is ",[1092,35655,35656],{},"whose\nlifecycle does the code serve"," — never the topic. ",[941,35659,35645],{}," sounds retrieval-flavored, but it\ncomputes no retrieval and persists no index; it emits ACL-scoped tool definitions and\ndispatches tool calls back through the public API. (Retrieval capability is the hub's\n",[1007,35662,4457],{"href":833},[1007,35664,705],{"href":706}," services, inside the trust\nboundary, where the encrypted index artifacts live.)",[1022,35667,35669],{"id":35668},"ports-vs-services-vs-combinators","Ports vs services vs combinators",[937,35671,35672],{},"Three store-flavored things exist and none duplicates another:",[1111,35674,35675,35710,35720],{},[1114,35676,35677,1169,35684,35686,35687,35689,35690,1261,35692,1261,35694,1261,35696,2601,35698,1261,35700,35702,35703,35705,35706,35709],{},[950,35678,35679,953,35682,1381],{},[941,35680,35681],{},"src\u002Fport\u002Fto",[941,35683,3299],{},[950,35685,4608],{},": a behavior-free re-export\nbarrel (the 6-method ",[941,35688,3154],{}," interface — ",[941,35691,3158],{},[941,35693,3161],{},[941,35695,3164],{},[941,35697,3167],{},[941,35699,3170],{},[941,35701,3173],{}," — plus envelope\u002Fop types and store errors). What a ",[941,35704,1779],{}," package\n",[1092,35707,35708],{},"binds",". Frozen; additive-only; every addition is surface the whole family supports\nforever.",[1114,35711,35712,1169,35716,35719],{},[950,35713,35714,9939],{},[941,35715,1779],{},[950,35717,35718],{},"adapters"," that implement the contract.",[1114,35721,35722,12536,35727,2085,35730,4621,35732,968,35734,1707,35736,4631,35738,35740],{},[950,35723,35724],{},[941,35725,35726],{},"src\u002Fwith-store",[950,35728,35729],{},"service of combinators over the contract",[941,35731,4620],{},[941,35733,4624],{},[941,35735,4627],{},[941,35737,4630],{},[941,35739,4634],{},", envelope-in\u002Fenvelope-out.\nIts audience is application authors composing stores; a store author never touches it.",[937,35742,35743,35744,35750,35751,35753,35754,1261,35757,1261,35760,35762],{},"The rule: ",[950,35745,35746,35747,35749],{},"contract doors stay behavior-free; anything that ",[1092,35748,3086],{}," something is a service.","\nThe moment routing logic lived in the port, every store adapter would inherit version coupling\nto it. (Same shape one level up: the ",[941,35752,3339],{}," port exports ",[941,35755,35756],{},"diffVault",[941,35758,35759],{},"writePod",[941,35761,94],{}," —\nthe primitives an exporter closes over — while the exporters themselves are separate\npackages.)",[1022,35764,35766],{"id":35765},"the-classification-rules","The classification rules",[937,35768,35769],{},"Apply in order; the first rule that bites decides.",[937,35771,35772,35779,35780,35783,35784,35786],{},[950,35773,35774,35775,35778],{},"R1 — Classify by the ",[1092,35776,35777],{},"worst"," thing the code must do, not the most recognizable.","\nAttestation issuance contains a conversion (record → QR credential), a signature, and a\nledger write. The conversion is the recognizable part; the key custody is the classifying\npart. Anything that must touch ",[941,35781,35782],{},"getDEK"," (the internal capability that hands a service its\ncollection's data-encryption key), mint or hold key material, or write vault state is a\n",[941,35785,1031],{}," service, no matter how export-shaped its output looks.",[937,35788,35789,35794,35795,35798,35799,35802,35803,35805,35806,35808],{},[950,35790,35791,35792,1520],{},"R2 — Capability lives in the hub; protocol projection lives in ",[941,35793,1783],{},"\nIf the code ",[1092,35796,35797],{},"adds an ability the vault didn't have"," (retrieval, history, attestation signing),\nit is a service inside the trust boundary. If it ",[1092,35800,35801],{},"re-expresses an existing ability in someone\nelse's idiom"," (hooks, query caches, tool definitions, REST routes), it is an ",[941,35804,1783],{}," binding —\neven when, like ",[941,35807,35645],{},", it has no external dependency and only the role test catches it.",[937,35810,35811,35814,35817,35818,35820,35821,35823,35824,35827,35828,35831,35832,35834],{},[950,35812,35813],{},"R3 — A family invariant outranks a single package's convenience.",[941,35815,35816],{},"as-attestation"," would \"work\" only if the ",[941,35819,3339],{}," port grew ",[941,35822,35782],{}," access and store writes —\nre-classifying all exporters from \"sees plaintext, holds nothing, writes nothing\"\n(one-sentence auditability) to \"may hold keys\". One package never buys that. Where a\ncapability has an export-shaped tail, split it: the key-touching part is a service; a\n",[1092,35825,35826],{},"downstream"," pure formatter over its already-persisted output (e.g. a printable-credential\nexporter reading ",[941,35829,35830],{},"_attestations"," through the vault handle) is a legitimate ",[941,35833,1791],{}," citizen.",[937,35836,35837,35840,35841,35844,35845,23423,35848,35850,35851,35854,35855,35857,35858,35861,35862,35865],{},[950,35838,35839],{},"R4 — Counterparty features ship as two packages.","\nWhen the primary user of some code is ",[1092,35842,35843],{},"not the vault owner"," (a relying party verifying a QR\ncredential), no owner-centric preposition fits — and forcing one would break the grammar's\npromise. The shape is: ",[950,35846,35847],{},"capability inside",[941,35849,1031],{}," service holding the keys) plus a\n",[950,35852,35853],{},"zero-dependency primitive outside"," (a prefix-less package the counterparty consumes without\ninstalling the hub or holding anything). Attestation is the type specimen: ",[941,35856,21543],{},"\n(the ",[1007,35859,35860],{"href":610},"attestation service",") plus the pure ",[941,35863,35864],{},"@noy-db\u002Fattestation"," package\n(verify, decode, revocation-list checks, all offline).",[937,35867,35868,35871,35872,35874,35875,997,35879,35881,35882,35884,35885,35887,35888,35891,35892,35894],{},[950,35869,35870],{},"R5 — No prefix means: holds no vault, and serves someone other than (or larger than) a\nsingle vault owner."," The prefix-less top level is a meaningful third tier, not a junk drawer:\n",[941,35873,5188],{}," (the core), ",[1007,35876,35877],{"href":214},[941,35878,5198],{},[941,35880,5209],{}," (tooling), ",[941,35883,609],{}," (counterparty verifier).\nThe fleet control plane is prefix-less for the same reason, and big enough to be its own repo:\n",[941,35886,5230],{},". If counterparty packages multiply (receipts, proofs, shared-view\ncredentials), a dedicated family (e.g. ",[941,35889,35890],{},"for-*",", \"verifiable ",[1092,35893,4646],{}," a counterparty\") may earn\nits place; with a single member it does not.",[1022,35896,35898],{"id":35897},"case-studies","Case studies",[1338,35900,35901,35914],{},[1341,35902,35903],{},[1344,35904,35905,35908,35911],{},[1347,35906,35907],{},"Question",[1347,35909,35910],{},"Answer",[1347,35912,35913],{},"Rule",[1357,35915,35916,35930,35947,35964],{},[1344,35917,35918,35924,35927],{},[1362,35919,35920,35921,35923],{},"Why does ",[941,35922,2992],{}," exist when the kernel has a cache?",[1362,35925,35926],{},"Different layers: ciphertext persistence backend vs plaintext read acceleration; also the conformance oracle and CI substrate",[1362,35928,35929],{},"table gloss 1",[1344,35931,35932,35941,35944],{},[1362,35933,35934,35935,35937,35938,35940],{},"Why both the ",[941,35936,3328],{}," port and ",[941,35939,992],{},"?",[1362,35942,35943],{},"Contract door (behavior-free, frozen) vs combinators over it (routing, middleware)",[1362,35945,35946],{},"R3's ancestor: doors stay behavior-free",[1344,35948,35949,35958,35961],{},[1362,35950,35951,35952,1261,35954,35957],{},"Why are ",[941,35953,35645],{},[941,35955,35956],{},"in-tanstack-query"," bindings, not lookup services?",[1362,35959,35960],{},"They add no capability; they project existing surface into a host runtime (TanStack's cache lifecycle; the LLM tool-calling loop). The search\u002Findexing services hold the encrypted indexes and must live inside",[1362,35962,35963],{},"R1, R2",[1344,35965,35966,35971,35985],{},[1362,35967,35968,35969,35940],{},"Why isn't attestation ",[941,35970,35816],{},[1362,35972,35973,35974,35976,35977,35979,35980,35982,35983],{},"Issuance mints\u002Fholds an Ed25519 signer under ",[941,35975,35782],{},", appends to ",[941,35978,35830],{},", and manages revocation — three things the ",[941,35981,3339],{}," port must never carry. The pure conversion tail already lives outside as ",[941,35984,35864],{},[1362,35986,35987],{},"R1, R3, R4",[1022,35989,2621],{"id":2620},[1111,35991,35992,35997,36002,36008,36017],{},[1114,35993,35994,35996],{},[1007,35995,4824],{"href":115}," — the usage-first overview and per-family catalogs.",[1114,35998,35999,36001],{},[1007,36000,35338],{"href":38}," — the structural trust-boundary view.",[1114,36003,36004,36007],{},[1007,36005,36006],{"href":30},"Architecture → Ports"," — the golden-frozen contract subpaths.",[1114,36009,36010,36013,36014,36016],{},[1007,36011,36012],{"href":34},"Architecture → Service layers"," — how ",[941,36015,1031],{}," folders group.",[1114,36018,36019,36021],{},[1007,36020,597],{"href":598}," — the catalog of everything inside the trust boundary.",{"title":1037,"searchDepth":1038,"depth":1038,"links":36023},[36024,36025,36026,36027,36028,36029,36030],{"id":35341,"depth":1041,"text":35342},{"id":35439,"depth":1041,"text":35440},{"id":35602,"depth":1041,"text":35603},{"id":35668,"depth":1041,"text":35669},{"id":35765,"depth":1041,"text":35766},{"id":35897,"depth":1041,"text":35898},{"id":2620,"depth":1041,"text":2621},"The two-axis model (satellite families vs. hub services) and the five rules that decide where a package or capability belongs.",{},{"title":209,"description":36031},"TDjGi0FJ-4kQA9EwVJ470r-HhaBCBt3GzRVzv7J8M6A",{"id":36036,"title":213,"access":12,"apiToc":929,"body":36037,"category":1042,"description":36744,"extension":1044,"meta":36745,"navigation":1042,"nest":3303,"path":214,"seo":36746,"sinceVersion":1042,"status":1049,"stem":215,"subtitle":1042,"tier":1050,"tocExternal":1042,"__hash__":36747},"docs\u002Ffamilies\u002Fcli.md",{"type":931,"value":36038,"toc":36735},[36039,36042,36063,36077,36083,36087,36197,36201,36235,36241,36267,36271,36291,36297,36307,36311,36320,36338,36420,36458,36462,36480,36504,36667,36673,36679,36683,36698,36700,36732],[934,36040,213],{"id":36041},"command-line-tools",[1061,36043,36044],{},[937,36045,36046,36050,36051,36055,36056,36059,36060,1381],{},[950,36047,36048],{},[941,36049,1072],{}," · ships with ",[950,36052,36053],{},[941,36054,5209],{}," — every scaffolded project has it; elsewhere ",[941,36057,36058],{},"pnpm add -D create-noy-db"," (or ",[941,36061,36062],{},"pnpm dlx create-noy-db noy-db …",[937,36064,36065,36066,36068,36069,17904,36071,36073,36074,36076],{},"One command line since ",[941,36067,7399],{},". Everything that used to live under a second bin, ",[941,36070,149],{},[941,36072,35330],{},", moved here; ",[941,36075,35330],{}," still installs, and every old spelling prints the new one and runs it, so nothing breaks — but there is one name to learn.",[2217,36078,36081],{"className":36079,"code":36080,"language":5672,"meta":1037},[5670],"Project\n  add \u003Ccollection>                             Scaffold a new collection store + page\n  add \u003C@noy-db\u002Fpackage> [--dev] [--dry-run]    Install a package, wire it, print the trust boundary\n  add --list [to|in|on|as|by|at]               The landscape — every package, by layer\n  user add \u003CuserId> \u003Crole> [options]           Grant a new user access to a vault\n  user rotate [options]                        Rotate DEKs for a vault\n  backup \u003Ctarget> [options]                    Dump a vault to a file\n  doctor [config] [--mode=prod] [--fix]        Readiness checks: secret, recovery, topology, capsule, deps, hygiene\n  doctor crypto                                In-memory crypto round-trip check\n\nPods (.noydb files)\n  pod inspect \u003Cfile.noydb>                     Print the bundle header (no secret)\n  pod verify  \u003Cfile.noydb>                     Verify bundle integrity (no secret)\n  describe \u003Cfile.noydb> [options]              YAML\u002FJSON schema audit (needs the secret)\n\nConfiguration\n  config validate \u003Cfile.js|mjs>                Sanity-check a NoydbOptions file\n  config scaffold [--profile=\u003Cid>]             Topology skeleton → stdout, .env template → stderr\n  config profiles                              List the ten topology profiles\n\nRunning\n  monitor \u003Cfile.js|mjs> [--interval=ms]        Live store metrics (the store must be metered)\n  inspect \u003Cfile.js|mjs> --vault=\u003Cname> [...]   Terminal inspector (installs on demand)\n",[941,36082,36080],{"__ignoreMap":1037},[1022,36084,36086],{"id":36085},"renamed-spellings","Renamed spellings",[1338,36088,36089,36100],{},[1341,36090,36091],{},[1344,36092,36093,36098],{},[1347,36094,36095,36096],{},"Before ",[941,36097,7399],{},[1347,36099,24238],{},[1357,36101,36102,36120,36144,36157,36175],{},[1344,36103,36104,36112],{},[1362,36105,36106,1261,36109],{},[941,36107,36108],{},"noydb inspect",[941,36110,36111],{},"noydb verify",[1362,36113,36114,1261,36117],{},[941,36115,36116],{},"noy-db pod inspect",[941,36118,36119],{},"noy-db pod verify",[1344,36121,36122,36133],{},[1362,36123,36124,968,36127,968,36130],{},[941,36125,36126],{},"noydb describe",[941,36128,36129],{},"noydb config …",[941,36131,36132],{},"noydb monitor",[1362,36134,36135,968,36138,968,36141],{},[941,36136,36137],{},"noy-db describe",[941,36139,36140],{},"noy-db config …",[941,36142,36143],{},"noy-db monitor",[1344,36145,36146,36152],{},[1362,36147,36148,36151],{},[941,36149,36150],{},"noy-db verify"," (the crypto self-test)",[1362,36153,36154],{},[941,36155,36156],{},"noy-db doctor crypto",[1344,36158,36159,36167],{},[1362,36160,36161,1261,36164],{},[941,36162,36163],{},"noy-db add user …",[941,36165,36166],{},"noy-db rotate",[1362,36168,36169,1261,36172],{},[941,36170,36171],{},"noy-db user add …",[941,36173,36174],{},"noy-db user rotate",[1344,36176,36177,36185],{},[1362,36178,36179,9483,36182,1381],{},[941,36180,36181],{},"noydb-inspect",[941,36183,36184],{},"in-devtools-tui",[1362,36186,36187,36190,36191,36196],{},[941,36188,36189],{},"noy-db inspect"," — reaches ",[1007,36192,36193],{"href":232},[941,36194,36195],{},"@noy-db\u002Fin-devtools-tui"," when it is installed, names it when it is not",[1022,36198,36200],{"id":36199},"doctor","Doctor",[937,36202,36203,36206,36207,36210,36211,36214,36215,36218,36219,36222,36223,36226,36227,36230,36231,36234],{},[941,36204,36205],{},"noy-db doctor"," answers ",[1092,36208,36209],{},"\"is this project ready?\""," without a secret. Checks carry stable ids by group — ",[941,36212,36213],{},"NOY-S"," secret handling, ",[941,36216,36217],{},"NOY-R"," recovery, ",[941,36220,36221],{},"NOY-T"," topology, ",[941,36224,36225],{},"NOY-C"," capsule, ",[941,36228,36229],{},"NOY-D"," dependencies, ",[941,36232,36233],{},"NOY-H"," hygiene — and each finding names the fix:",[2217,36236,36239],{"className":36237,"code":36238,"language":5672,"meta":1037},[5670],"noy-db doctor — mode dev, 6 check group(s)\n  ✗ NOY-D001  `@noy-db\u002Fhub` is not a dependency of this project  (.\u002Fpackage.json)\n      → add it: `pnpm add @noy-db\u002Fhub`\n  1 error · 0 warn · 0 info\n",[941,36240,36238],{"__ignoreMap":1037},[937,36242,36243,36246,36247,36250,36251,36254,36255,36258,36259,36262,36263,36266],{},[941,36244,36245],{},"--mode=prod"," tightens what counts as an error (a secret in a tracked file, no recovery enrolment for an owner, a plaintext capsule). ",[941,36248,36249],{},"--json"," emits the report for CI; ",[941,36252,36253],{},"--fail-level=warn"," makes warnings fail the step; ",[941,36256,36257],{},"--fix"," applies the safe fixes (today: ignoring ",[941,36260,36261],{},".env","). Satellites can register their own checks by exporting ",[941,36264,36265],{},"noydbDoctorChecks"," from their root entry.",[1022,36268,36270],{"id":36269},"add","Add",[937,36272,36273,36276,36277,36279,36280,36283,36284,36287,36288,2311],{},[941,36274,36275],{},"noy-db add \u003Cpackage>"," installs a ",[941,36278,5129],{}," package with the package manager your lockfile implies, wires it where a config can be edited (an ",[941,36281,36282],{},"in-nuxt"," module into ",[941,36285,36286],{},"nuxt.config","), prints the import to paste otherwise, and ",[950,36289,36290],{},"states the trust boundary the layer crosses",[2217,36292,36295],{"className":36293,"code":36294,"language":5672,"meta":1037},[5670],"$ noy-db add to-aws-s3 --dry-run\ndry: pnpm add @noy-db\u002Fto-aws-s3\n\nto- The store sees ciphertext only. It cannot read your records, and it cannot run a query.\n\nimport { toAwsS3 } from '@noy-db\u002Fto-aws-s3'\n",[941,36296,36294],{"__ignoreMap":1037},[937,36298,36299,36302,36303,36306],{},[941,36300,36301],{},"add --list [prefix]"," is the catalog by layer. It is fetched from this site (",[941,36304,36305],{},"\u002Fcatalog.json",", generated from the feature registry) and falls back to the copy shipped with the CLI, so it works offline.",[1022,36308,36310],{"id":36309},"offline-pod-forensics","Offline pod forensics",[937,36312,36313,36316,36317,36319],{},[941,36314,36315],{},"pod inspect"," prints the header a ",[941,36318,1402],{}," pod discloses to anyone holding the file — no secret involved:",[2217,36321,36323],{"className":33763,"code":36322,"language":33765,"meta":1037,"style":1037},"noy-db pod inspect acme.noydb\n",[941,36324,36325],{"__ignoreMap":1037},[2225,36326,36327,36329,36332,36335],{"class":2227,"line":2228},[2225,36328,1072],{"class":6342},[2225,36330,36331],{"class":2252}," pod",[2225,36333,36334],{"class":2252}," inspect",[2225,36336,36337],{"class":2252}," acme.noydb\n",[2217,36339,36341],{"className":6604,"code":36340,"language":141,"meta":1037,"style":1037},"{\n  \"formatVersion\": 1,\n  \"handle\": \"037VE50R64H6BS9Z4KHDCXWQ6B\",\n  \"bodyBytes\": 531,\n  \"bodySha256\": \"2c6366a64a37d734955496c29397967689f068dea63aebec2ac1a47354346f40\"\n}\n",[941,36342,36343,36347,36362,36382,36398,36416],{"__ignoreMap":1037},[2225,36344,36345],{"class":2227,"line":2228},[2225,36346,4200],{"class":2235},[2225,36348,36349,36351,36354,36356,36358,36360],{"class":2227,"line":1041},[2225,36350,9368],{"class":2235},[2225,36352,36353],{"class":2774},"formatVersion",[2225,36355,6620],{"class":2235},[2225,36357,2311],{"class":2235},[2225,36359,9377],{"class":6527},[2225,36361,1707],{"class":2235},[2225,36363,36364,36366,36369,36371,36373,36375,36378,36380],{"class":2227,"line":1038},[2225,36365,9368],{"class":2235},[2225,36367,36368],{"class":2774},"handle",[2225,36370,6620],{"class":2235},[2225,36372,2311],{"class":2235},[2225,36374,6614],{"class":2235},[2225,36376,36377],{"class":2252},"037VE50R64H6BS9Z4KHDCXWQ6B",[2225,36379,6620],{"class":2235},[2225,36381,1707],{"class":2235},[2225,36383,36384,36386,36389,36391,36393,36396],{"class":2227,"line":2288},[2225,36385,9368],{"class":2235},[2225,36387,36388],{"class":2774},"bodyBytes",[2225,36390,6620],{"class":2235},[2225,36392,2311],{"class":2235},[2225,36394,36395],{"class":6527}," 531",[2225,36397,1707],{"class":2235},[2225,36399,36400,36402,36405,36407,36409,36411,36414],{"class":2227,"line":2304},[2225,36401,9368],{"class":2235},[2225,36403,36404],{"class":2774},"bodySha256",[2225,36406,6620],{"class":2235},[2225,36408,2311],{"class":2235},[2225,36410,6614],{"class":2235},[2225,36412,36413],{"class":2252},"2c6366a64a37d734955496c29397967689f068dea63aebec2ac1a47354346f40",[2225,36415,9449],{"class":2235},[2225,36417,36418],{"class":2227,"line":2356},[2225,36419,7351],{"class":2235},[937,36421,36422,36425,36426,36428,36429,36431,36432,997,36435,36059,36438,36441,36442,36445,36446,36448,36449,36451,36452,36454,36455,36457],{},[941,36423,36424],{},"pod verify"," recomputes the integrity hashes (still no secret — it checks the ciphertext, it cannot read it). ",[941,36427,6153],{}," goes further and ",[950,36430,3086],{}," decrypt — pass ",[941,36433,36434],{},"--user",[941,36436,36437],{},"--secret",[941,36439,36440],{},"NOYDB_SECRET",") and it emits a YAML\u002FJSON audit of collections, fields, and schemas, with ",[941,36443,36444],{},"--schemas full|sidecar"," for JSON Schema output. Exit codes are scripting-friendly: ",[941,36447,5402],{}," ok, ",[941,36450,5437],{}," internal, ",[941,36453,5470],{}," usage, ",[941,36456,5512],{}," auth failure.",[1022,36459,36461],{"id":36460},"config-tools","Config tools",[937,36463,36464,36467,36468,36471,36472,36475,36476,36479],{},[941,36465,36466],{},"config validate"," sanity-checks a ",[941,36469,36470],{},"NoydbOptions"," file before you ship it. ",[941,36473,36474],{},"config profiles"," lists the ten deployment topologies; ",[941,36477,36478],{},"config scaffold"," emits a starting config for one of them (A is local-only single-user):",[2217,36481,36483],{"className":33763,"code":36482,"language":33765,"meta":1037,"style":1037},"noy-db config scaffold --profile=A > noydb.config.mjs\n",[941,36484,36485],{"__ignoreMap":1037},[2225,36486,36487,36489,36492,36495,36498,36501],{"class":2227,"line":2228},[2225,36488,1072],{"class":6342},[2225,36490,36491],{"class":2252}," config",[2225,36493,36494],{"class":2252}," scaffold",[2225,36496,36497],{"class":2252}," --profile=A",[2225,36499,36500],{"class":2235}," >",[2225,36502,36503],{"class":2252}," noydb.config.mjs\n",[2217,36505,36509],{"className":36506,"code":36507,"language":36508,"meta":1037,"style":1037},"language-js shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","\u002F\u002F ── noydb config (profile A) ───────────────────\n\u002F\u002F Local-only single-user. No cloud.\n\nimport { createNoydb } from '@noy-db\u002Fhub'\nimport { toFile } from '@noy-db\u002Fto-file'\n\nexport default {\n  store:  toFile({ dir: process.env.NOYDB_DATA_DIR ?? '.\u002Fdata' }),\n  user:   process.env.NOYDB_USER ?? 'owner',\n  secret: process.env.NOYDB_SECRET,\n}\n","js",[941,36510,36511,36516,36521,36525,36543,36561,36565,36574,36617,36645,36663],{"__ignoreMap":1037},[2225,36512,36513],{"class":2227,"line":2228},[2225,36514,36515],{"class":2763},"\u002F\u002F ── noydb config (profile A) ───────────────────\n",[2225,36517,36518],{"class":2227,"line":1041},[2225,36519,36520],{"class":2763},"\u002F\u002F Local-only single-user. No cloud.\n",[2225,36522,36523],{"class":2227,"line":1038},[2225,36524,2285],{"emptyLinePlaceholder":2284},[2225,36526,36527,36529,36531,36533,36535,36537,36539,36541],{"class":2227,"line":2288},[2225,36528,2232],{"class":2231},[2225,36530,2236],{"class":2235},[2225,36532,2744],{"class":2239},[2225,36534,2243],{"class":2235},[2225,36536,2246],{"class":2231},[2225,36538,2249],{"class":2235},[2225,36540,1086],{"class":2252},[2225,36542,2256],{"class":2235},[2225,36544,36545,36547,36549,36551,36553,36555,36557,36559],{"class":2227,"line":2304},[2225,36546,2232],{"class":2231},[2225,36548,2236],{"class":2235},[2225,36550,15573],{"class":2239},[2225,36552,2243],{"class":2235},[2225,36554,2246],{"class":2231},[2225,36556,2249],{"class":2235},[2225,36558,9551],{"class":2252},[2225,36560,2256],{"class":2235},[2225,36562,36563],{"class":2227,"line":2356},[2225,36564,2285],{"emptyLinePlaceholder":2284},[2225,36566,36567,36569,36572],{"class":2227,"line":2830},[2225,36568,8080],{"class":2231},[2225,36570,36571],{"class":2231}," default",[2225,36573,2301],{"class":2235},[2225,36575,36576,36578,36580,36583,36585,36587,36589,36591,36594,36596,36598,36600,36603,36605,36607,36609,36611,36613,36615],{"class":2227,"line":2861},[2225,36577,15608],{"class":2307},[2225,36579,2311],{"class":2235},[2225,36581,36582],{"class":2291},"  toFile",[2225,36584,2789],{"class":2239},[2225,36586,2792],{"class":2235},[2225,36588,15619],{"class":2307},[2225,36590,2311],{"class":2235},[2225,36592,36593],{"class":2239}," process",[2225,36595,1520],{"class":2235},[2225,36597,183],{"class":2239},[2225,36599,1520],{"class":2235},[2225,36601,36602],{"class":2239},"NOYDB_DATA_DIR ",[2225,36604,8381],{"class":2235},[2225,36606,2249],{"class":2235},[2225,36608,15626],{"class":2252},[2225,36610,2805],{"class":2235},[2225,36612,2243],{"class":2235},[2225,36614,1381],{"class":2239},[2225,36616,1707],{"class":2235},[2225,36618,36619,36621,36623,36626,36628,36630,36632,36635,36637,36639,36641,36643],{"class":2227,"line":4246},[2225,36620,4205],{"class":2307},[2225,36622,2311],{"class":2235},[2225,36624,36625],{"class":2239},"   process",[2225,36627,1520],{"class":2235},[2225,36629,183],{"class":2239},[2225,36631,1520],{"class":2235},[2225,36633,36634],{"class":2239},"NOYDB_USER ",[2225,36636,8381],{"class":2235},[2225,36638,2249],{"class":2235},[2225,36640,9898],{"class":2252},[2225,36642,2805],{"class":2235},[2225,36644,1707],{"class":2235},[2225,36646,36647,36649,36651,36653,36655,36657,36659,36661],{"class":2227,"line":4253},[2225,36648,10423],{"class":2307},[2225,36650,2311],{"class":2235},[2225,36652,36593],{"class":2239},[2225,36654,1520],{"class":2235},[2225,36656,183],{"class":2239},[2225,36658,1520],{"class":2235},[2225,36660,36440],{"class":2239},[2225,36662,1707],{"class":2235},[2225,36664,36665],{"class":2227,"line":4259},[2225,36666,7351],{"class":2235},[937,36668,36669,36670,36672],{},"The matching ",[941,36671,36261],{}," template goes to stderr, so the redirect above keeps the two halves apart.",[937,36674,36675,36676,1520],{},"For TypeScript configs, run under a TS-capable runtime: ",[941,36677,36678],{},"npx tsx $(which noy-db) config validate foo.ts",[1022,36680,36682],{"id":36681},"live-metrics","Live metrics",[937,36684,36685,36688,36689,36693,36694,36697],{},[941,36686,36687],{},"noy-db monitor \u003Cconfig>"," renders a live dashboard of operation counts and timings for a store that is already wrapped in ",[1007,36690,36691],{"href":396},[941,36692,5125],{}," — storage-side observability without touching record content. The config supplies the metered store; ",[941,36695,36696],{},"monitor"," no longer wraps it for you.",[1022,36699,2621],{"id":2620},[1111,36701,36702,36713,36727],{},[1114,36703,36704,1368,36707,36709,36710,36712],{},[1007,36705,36706],{"href":228},"in-devtools",[1007,36708,36184],{"href":232}," — the live-vault inspector pair that ",[941,36711,36189],{}," opens.",[1114,36714,36715,1169,36717,36719,36720,1625,36722,1625,36724,36726],{},[1007,36716,629],{"href":630},[941,36718,1402],{}," container format ",[941,36721,36315],{},[941,36723,36424],{},[941,36725,6153],{}," operate on.",[1114,36728,36729,36731],{},[1007,36730,455],{"href":456}," — the topologies the scaffold profiles map to.",[2645,36733,36734],{},"html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}",{"title":1037,"searchDepth":1038,"depth":1038,"links":36736},[36737,36738,36739,36740,36741,36742,36743],{"id":36085,"depth":1041,"text":36086},{"id":36199,"depth":1041,"text":36200},{"id":36269,"depth":1041,"text":36270},{"id":36309,"depth":1041,"text":36310},{"id":36460,"depth":1041,"text":36461},{"id":36681,"depth":1041,"text":36682},{"id":2620,"depth":1041,"text":2621},"The noy-db command line, one bin from create-noy-db: doctor, add, pod forensics, config scaffolds, live metrics, the terminal inspector.",{},{"title":213,"description":36744},"EbOh9ANoJ0hA2Qpcbhhd-zUM8Ydo-ig-gmKsGyveODk",{"id":36749,"title":223,"access":12,"apiToc":929,"body":36750,"category":1042,"description":37022,"extension":1044,"meta":37023,"navigation":1042,"nest":3303,"path":224,"seo":37024,"sinceVersion":1042,"status":1049,"stem":225,"subtitle":1042,"tier":1050,"tocExternal":1042,"__hash__":37025},"docs\u002Ffamilies\u002Fin\u002Fai.md",{"type":931,"value":36751,"toc":37017},[36752,36754,36767,36769,36821,36823,36994,36996,37014],[934,36753,223],{"id":223},[1061,36755,36756],{},[937,36757,36758,1368,36763,36766],{},[950,36759,36760],{},[941,36761,36762],{},"@noy-db\u002Fin-ai",[941,36764,36765],{},"pnpm add @noy-db\u002Fhub @noy-db\u002Fin-ai","\nLLM function-calling adapter for noy-db — ACL-scoped collections as tool definitions.",[1022,36768,29857],{"id":29856},[937,36770,36771,36774,36775,36778,36779,1261,36781,36783,36784,968,36787,36790,36791,1707,36794,968,36797,36800,36801,36804,36805,36808,36809,36812,36813,36816,36817,36820],{},[941,36772,36773],{},"buildToolset(vault, options?)"," walks the vault's accessible collections\nand emits a tool array in the format of your choice\n(",[941,36776,36777],{},"format: 'openai' | 'anthropic' | 'vercel-ai' | 'json-schema'","), gated by\nthe caller's keyring. Only ",[941,36780,3167],{},[941,36782,3158],{}," tools ship by default\n(",[941,36785,36786],{},"invoices_list",[941,36788,36789],{},"invoices_get","); mutation tools (",[941,36792,36793],{},"invoices_put",[941,36795,36796],{},"invoices_delete",[941,36798,36799],{},"invoices_count",") require opting in via ",[941,36802,36803],{},"operations",".\nCollections the keyring cannot decrypt never become tools.\n",[941,36806,36807],{},"invokeToolCall(vault, call, options?)"," dispatches an LLM-returned\n",[941,36810,36811],{},"{ name, args }"," call back to the vault — it re-checks permissions on\nevery invocation regardless of what was published, throwing\n",[941,36814,36815],{},"ToolDeniedError"," on a fabricated call or ",[941,36818,36819],{},"ToolNotFoundError"," on an\nunparseable name, so a hostile model can never mutate data it wasn't\nauthorized for.",[1022,36822,29893],{"id":29892},[2217,36824,36826],{"className":2219,"code":36825,"language":2221,"meta":1037,"style":1037},"import { buildToolset, invokeToolCall } from '@noy-db\u002Fin-ai'\n\nconst tools = buildToolset(vault, { format: 'anthropic', operations: ['list', 'get'] })\nconst result = await invokeToolCall(\n  vault,\n  { name: 'invoices_list', args: {} },\n  { allowedOperations: ['list', 'get'] },\n)\n",[941,36827,36828,36852,36856,36914,36929,36936,36961,36990],{"__ignoreMap":1037},[2225,36829,36830,36832,36834,36837,36839,36842,36844,36846,36848,36850],{"class":2227,"line":2228},[2225,36831,2232],{"class":2231},[2225,36833,2236],{"class":2235},[2225,36835,36836],{"class":2239}," buildToolset",[2225,36838,2298],{"class":2235},[2225,36840,36841],{"class":2239}," invokeToolCall",[2225,36843,2243],{"class":2235},[2225,36845,2246],{"class":2231},[2225,36847,2249],{"class":2235},[2225,36849,36762],{"class":2252},[2225,36851,2256],{"class":2235},[2225,36853,36854],{"class":2227,"line":1041},[2225,36855,2285],{"emptyLinePlaceholder":2284},[2225,36857,36858,36860,36863,36865,36867,36869,36871,36873,36876,36878,36880,36883,36885,36887,36890,36892,36894,36896,36898,36900,36902,36904,36906,36908,36910,36912],{"class":2227,"line":1038},[2225,36859,2775],{"class":2774},[2225,36861,36862],{"class":2239}," tools ",[2225,36864,2781],{"class":2235},[2225,36866,36836],{"class":2291},[2225,36868,30266],{"class":2239},[2225,36870,2298],{"class":2235},[2225,36872,2236],{"class":2235},[2225,36874,36875],{"class":2307}," format",[2225,36877,2311],{"class":2235},[2225,36879,2249],{"class":2235},[2225,36881,36882],{"class":2252},"anthropic",[2225,36884,2805],{"class":2235},[2225,36886,2298],{"class":2235},[2225,36888,36889],{"class":2307}," operations",[2225,36891,2311],{"class":2235},[2225,36893,8667],{"class":2239},[2225,36895,2805],{"class":2235},[2225,36897,3167],{"class":2252},[2225,36899,2805],{"class":2235},[2225,36901,2298],{"class":2235},[2225,36903,2249],{"class":2235},[2225,36905,3158],{"class":2252},[2225,36907,2805],{"class":2235},[2225,36909,8890],{"class":2239},[2225,36911,2359],{"class":2235},[2225,36913,2362],{"class":2239},[2225,36915,36916,36918,36921,36923,36925,36927],{"class":2227,"line":2288},[2225,36917,2775],{"class":2774},[2225,36919,36920],{"class":2239}," result ",[2225,36922,2781],{"class":2235},[2225,36924,2784],{"class":2231},[2225,36926,36841],{"class":2291},[2225,36928,11683],{"class":2239},[2225,36930,36931,36934],{"class":2227,"line":2304},[2225,36932,36933],{"class":2239},"  vault",[2225,36935,1707],{"class":2235},[2225,36937,36938,36940,36942,36944,36946,36948,36950,36952,36955,36957,36959],{"class":2227,"line":2356},[2225,36939,26354],{"class":2235},[2225,36941,8104],{"class":2307},[2225,36943,2311],{"class":2235},[2225,36945,2249],{"class":2235},[2225,36947,36786],{"class":2252},[2225,36949,2805],{"class":2235},[2225,36951,2298],{"class":2235},[2225,36953,36954],{"class":2307}," args",[2225,36956,2311],{"class":2235},[2225,36958,8896],{"class":2235},[2225,36960,2353],{"class":2235},[2225,36962,36963,36965,36968,36970,36972,36974,36976,36978,36980,36982,36984,36986,36988],{"class":2227,"line":2830},[2225,36964,26354],{"class":2235},[2225,36966,36967],{"class":2307}," allowedOperations",[2225,36969,2311],{"class":2235},[2225,36971,8667],{"class":2239},[2225,36973,2805],{"class":2235},[2225,36975,3167],{"class":2252},[2225,36977,2805],{"class":2235},[2225,36979,2298],{"class":2235},[2225,36981,2249],{"class":2235},[2225,36983,3158],{"class":2252},[2225,36985,2805],{"class":2235},[2225,36987,8890],{"class":2239},[2225,36989,19008],{"class":2235},[2225,36991,36992],{"class":2227,"line":2861},[2225,36993,2362],{"class":2239},[1022,36995,2621],{"id":2620},[1111,36997,36998,37003,37009],{},[1114,36999,37000],{},[1007,37001,37002],{"href":2634},"in-* family",[1114,37004,37005,37008],{},[1007,37006,263],{"href":37007},".\u002Frest"," — the equivalent protocol binding for HTTP instead of tool-calling",[1114,37010,37011,37013],{},[1007,37012,14279],{"href":30784}," — the ACL that gates tool generation",[2645,37015,37016],{},"html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":1037,"searchDepth":1038,"depth":1038,"links":37018},[37019,37020,37021],{"id":29856,"depth":1041,"text":29857},{"id":29892,"depth":1041,"text":29893},{"id":2620,"depth":1041,"text":2621},"LLM function-calling adapter — buildToolset(vault) emits permission-scoped tool definitions; invokeToolCall() dispatches and re-checks permissions.",{},{"title":223,"description":37022},"rem6teh9_DC36wba-f4fRjuuII9U_7TzrD3_iMSVOB0",{"id":37027,"title":227,"access":12,"apiToc":929,"body":37028,"category":1042,"description":37242,"extension":1044,"meta":37243,"navigation":1042,"nest":3303,"path":228,"seo":37244,"sinceVersion":1042,"status":1049,"stem":229,"subtitle":1042,"tier":1050,"tocExternal":1042,"__hash__":37245},"docs\u002Ffamilies\u002Fin\u002Fdevtools.md",{"type":931,"value":37029,"toc":37237},[37030,37032,37045,37047,37102,37104,37215,37217,37234],[934,37031,227],{"id":227},[1061,37033,37034],{},[937,37035,37036,1368,37041,37044],{},[950,37037,37038],{},[941,37039,37040],{},"@noy-db\u002Fin-devtools",[941,37042,37043],{},"pnpm add @noy-db\u002Fhub @noy-db\u002Fin-devtools","\nRead-only inspector core for noy-db.",[1022,37046,29857],{"id":29856},[937,37048,37049,37052,37053,37056,37057,37059,37060,37063,37064,37067,37068,1261,37071,37074,37075,37078,37079,37082,37083,37085,37086,37088,37089,37091,37092,37094,37095,37097,37098,37101],{},[941,37050,37051],{},"createInspector(db, { meter? })"," returns an ",[941,37054,37055],{},"Inspector"," facade:\n",[941,37058,7935],{}," lists accessible vaults; ",[941,37061,37062],{},"snapshot(vault)"," returns\ncollections with fields \u002F indexes \u002F refs \u002F stats; ",[941,37065,37066],{},"records(vault, name, { limit?, offset? })","\npages through decrypted rows (limit defaults to 50, hard ceiling 500);\n",[941,37069,37070],{},"subscribe(handler)",[941,37072,37073],{},"subscribeConflicts(handler)"," fire on writes and\nconflicts; ",[941,37076,37077],{},"pendingWrites()"," reports write-queue state; ",[941,37080,37081],{},"meterSnapshot()","\nneeds a ",[941,37084,9595],{}," handle passed as ",[941,37087,395],{}," (otherwise ",[941,37090,6651],{},"). It's\na pure consumer of public hub APIs — read-only and zero-knowledge-respecting:\nit never holds secrets and shows only what the caller's unlocked session\ncan already decrypt. ",[941,37093,7935],{}," requires a store that implements the\noptional ",[941,37096,3127],{}," capability; ",[941,37099,37100],{},"records()"," targets eager (non-lazy)\ncollections.",[1022,37103,29893],{"id":29892},[2217,37105,37107],{"className":2219,"code":37106,"language":2221,"meta":1037,"style":1037},"import { createInspector } from '@noy-db\u002Fin-devtools'\n\nconst inspector = createInspector(db)\nawait inspector.listVaults()                          \u002F\u002F → [{ id: 'acme', role: 'owner' }]\nawait inspector.snapshot(vault)                         \u002F\u002F → { vault, collections: [...] }\nawait inspector.records(vault, 'invoices', { limit: 50 })\n",[941,37108,37109,37128,37132,37146,37163,37179],{"__ignoreMap":1037},[2225,37110,37111,37113,37115,37118,37120,37122,37124,37126],{"class":2227,"line":2228},[2225,37112,2232],{"class":2231},[2225,37114,2236],{"class":2235},[2225,37116,37117],{"class":2239}," createInspector",[2225,37119,2243],{"class":2235},[2225,37121,2246],{"class":2231},[2225,37123,2249],{"class":2235},[2225,37125,37040],{"class":2252},[2225,37127,2256],{"class":2235},[2225,37129,37130],{"class":2227,"line":1041},[2225,37131,2285],{"emptyLinePlaceholder":2284},[2225,37133,37134,37136,37139,37141,37143],{"class":2227,"line":1038},[2225,37135,2775],{"class":2774},[2225,37137,37138],{"class":2239}," inspector ",[2225,37140,2781],{"class":2235},[2225,37142,37117],{"class":2291},[2225,37144,37145],{"class":2239},"(db)\n",[2225,37147,37148,37150,37153,37155,37157,37160],{"class":2227,"line":2288},[2225,37149,6500],{"class":2231},[2225,37151,37152],{"class":2239}," inspector",[2225,37154,1520],{"class":2235},[2225,37156,3127],{"class":2291},[2225,37158,37159],{"class":2239},"()                          ",[2225,37161,37162],{"class":2763},"\u002F\u002F → [{ id: 'acme', role: 'owner' }]\n",[2225,37164,37165,37167,37169,37171,37173,37176],{"class":2227,"line":2304},[2225,37166,6500],{"class":2231},[2225,37168,37152],{"class":2239},[2225,37170,1520],{"class":2235},[2225,37172,8882],{"class":2291},[2225,37174,37175],{"class":2239},"(vault)                         ",[2225,37177,37178],{"class":2763},"\u002F\u002F → { vault, collections: [...] }\n",[2225,37180,37181,37183,37185,37187,37189,37191,37193,37195,37197,37199,37201,37203,37206,37208,37211,37213],{"class":2227,"line":2356},[2225,37182,6500],{"class":2231},[2225,37184,37152],{"class":2239},[2225,37186,1520],{"class":2235},[2225,37188,9136],{"class":2291},[2225,37190,30266],{"class":2239},[2225,37192,2298],{"class":2235},[2225,37194,2249],{"class":2235},[2225,37196,6487],{"class":2252},[2225,37198,2805],{"class":2235},[2225,37200,2298],{"class":2235},[2225,37202,2236],{"class":2235},[2225,37204,37205],{"class":2307}," limit",[2225,37207,2311],{"class":2235},[2225,37209,37210],{"class":6527}," 50",[2225,37212,2243],{"class":2235},[2225,37214,2362],{"class":2239},[1022,37216,2621],{"id":2620},[1111,37218,37219,37223,37229],{},[1114,37220,37221],{},[1007,37222,37002],{"href":2634},[1114,37224,37225,37228],{},[1007,37226,231],{"href":37227},".\u002Fdevtools-tui"," — the terminal front-end built on this inspector",[1114,37230,37231,37233],{},[1007,37232,14279],{"href":30784}," — the ACL the inspector's views respect",[2645,37235,37236],{},"html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":1037,"searchDepth":1038,"depth":1038,"links":37238},[37239,37240,37241],{"id":29856,"depth":1041,"text":29857},{"id":29892,"depth":1041,"text":29893},{"id":2620,"depth":1041,"text":2621},"Framework-agnostic read-only inspector — createInspector(db) turns a live noy-db into serializable views for a CLI or browser panel.",{},{"title":227,"description":37242},"apDZv4H3PJASQGNiZhxVzNqEFn1Cyp2x8GGaso3etIs",{"id":37247,"title":231,"access":12,"apiToc":929,"body":37248,"category":1042,"description":37346,"extension":1044,"meta":37347,"navigation":1042,"nest":3303,"path":232,"seo":37348,"sinceVersion":1042,"status":1049,"stem":233,"subtitle":1042,"tier":1473,"tocExternal":1042,"__hash__":37349},"docs\u002Ffamilies\u002Fin\u002Fdevtools-tui.md",{"type":931,"value":37249,"toc":37341},[37250,37252,37264,37266,37289,37291,37306,37316,37325,37327,37338],[934,37251,231],{"id":231},[1061,37253,37254],{},[937,37255,37256,1368,37260,37263],{},[950,37257,37258],{},[941,37259,36195],{},[941,37261,37262],{},"pnpm add @noy-db\u002Fin-devtools-tui","\nInteractive terminal inspector for a live noy-db.",[1022,37265,29857],{"id":29856},[937,37267,37268,37270,37271,37276,37277,37279,37280,37285,37286,37288],{},[941,37269,36195],{}," is the terminal front-end of\n",[1007,37272,37274],{"href":37273},".\u002Fdevtools",[941,37275,36706],{}," — it wraps the same read-only ",[941,37278,37055],{},"\nfacade in an interactive TUI built on ",[1007,37281,37284],{"href":37282,"rel":37283},"https:\u002F\u002Fgithub.com\u002Fvadimdemedes\u002Fink",[3371],"ink",",\nand ships the ",[941,37287,36181],{}," binary. Point it at a JS\u002FMJS config file that\nexports your noy-db options and name the vault to open; nothing here adds\na capability beyond what the inspector already exposes.",[1022,37290,29893],{"id":29892},[2217,37292,37294],{"className":33763,"code":37293,"language":33765,"meta":1037,"style":1037},"noydb-inspect .\u002Fnoydb.config.mjs --vault=acme\n",[941,37295,37296],{"__ignoreMap":1037},[2225,37297,37298,37300,37303],{"class":2227,"line":2228},[2225,37299,36181],{"class":6342},[2225,37301,37302],{"class":2252}," .\u002Fnoydb.config.mjs",[2225,37304,37305],{"class":2252}," --vault=acme\n",[937,37307,37308,37309,37312,37313,37315],{},"From a scaffolded project the same screen is ",[941,37310,37311],{},"noy-db inspect .\u002Fnoydb.config.mjs --vault=acme"," — the one command line (since ",[941,37314,7399],{},") reaches this package when it is installed and names it when it is not.",[937,37317,37318,37319,1399,37322,37324],{},"The secret comes from ",[941,37320,37321],{},"--secret=…",[941,37323,36440],{},"\nenvironment variable, or an interactive masked prompt when running in a TTY.",[1022,37326,2621],{"id":2620},[1111,37328,37329,37333],{},[1114,37330,37331],{},[1007,37332,37002],{"href":2634},[1114,37334,37335,37337],{},[1007,37336,227],{"href":37273}," — the inspector core this TUI wraps",[2645,37339,37340],{},"html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":1037,"searchDepth":1038,"depth":1038,"links":37342},[37343,37344,37345],{"id":29856,"depth":1041,"text":29857},{"id":29892,"depth":1041,"text":29893},{"id":2620,"depth":1041,"text":2621},"Terminal inspector — an ink-based TUI over @noy-db\u002Fin-devtools, shipping the `noydb-inspect` binary.",{},{"title":231,"description":37346},"Yi9Rr-sbyhYF7dy_m9KohBlDEPS_Pu9ZgUJCwuM-B_c",{"id":37351,"title":37352,"access":12,"apiToc":929,"body":37353,"category":1042,"description":38183,"extension":1044,"meta":38184,"navigation":38185,"nest":3303,"path":218,"seo":38186,"sinceVersion":1042,"status":1049,"stem":219,"subtitle":1042,"tier":1050,"tocExternal":1042,"__hash__":38187},"docs\u002Ffamilies\u002Fin\u002Findex.md","`@noy-db\u002Fin-*` — Host-runtime integrations",{"type":931,"value":37354,"toc":38168},[37355,37362,37382,37399,37403,37447,37460,37462,37466,37611,37615,37626,37664,37667,37714,37718,37782,37786,37815,37818,37847,37850,37889,37891,38133,38137,38149,38151],[934,37356,37358,37361],{"id":37357},"noy-dbin-host-runtime-integrations",[941,37359,37360],{},"@noy-db\u002Fin-*"," — Host-runtime integrations",[1061,37363,37364],{},[937,37365,37366,15933,37369,37371,37372,37377,37378,37381],{},[950,37367,37368],{},"Where your code runs.",[941,37370,1783],{}," package is a thin binding that\nadapts the public surface of ",[1007,37373,37375],{"href":37374},"..\u002F..\u002Fcore\u002F",[941,37376,1086],{}," —\nnoy-db's core encrypted document store — ",[950,37379,37380],{},"into a host runtime or\nprotocol",": a framework, an LLM tool-calling loop, REST, Yjs. Every\nbinding is a peer dependency, so you don't pay for what you don't use.",[937,37383,960,37384,30769,37386,37392,37393,37395,37396,37398],{},[941,37385,1270],{},[1092,37387,37388,37389,37391],{},"\"runs ",[950,37390,4891],{}," a host\""," — each package re-expresses\nthe hub's existing surface in a host's idiom (a React render loop,\nTanStack's cache lifecycle, an LLM tool-calling loop, REST, Yjs) without\nadding a capability the hub didn't already have. ",[941,37394,1783],{}," packages are\noptional ergonomics: you can always use ",[941,37397,1086],{}," directly and write\nyour own binding for a runtime not listed here — most of these packages\nare small enough to read in one sitting.",[1022,37400,37402],{"id":37401},"one-repository-since-0100-pre0","One repository since 0.10.0-pre.0",[937,37404,30792,37405,37407,37408,37412,37413,37416,37417,968,37421,968,37426,2514,37430,37434,37435,32419,37440,37442,37443,37446],{},[941,37406,1783],{}," package publishes from the ",[950,37409,37410],{},[941,37411,9514],{}," repository as of ",[941,37414,37415],{},"0.10.0-pre.0",". Before that,\nfour — ",[1007,37418,37419],{"href":37007},[941,37420,263],{},[1007,37422,37424],{"href":37423},".\u002Frelay",[941,37425,259],{},[1007,37427,37428],{"href":37273},[941,37429,227],{},[1007,37431,37432],{"href":37227},[941,37433,231],{}," — published from ",[950,37436,37437],{},[941,37438,37439],{},"noy-db\u002Fcore",[941,37441,1086],{},", so\nthe ",[941,37444,37445],{},"0.9.x"," releases of those four are on core's version line.",[937,37448,37449,37450,37453,37454,37456,37457,37459],{},"That matters when you pin across that boundary: each repository cuts on its own clock, and a\nversion number is not a reliable way to tell who published it. Ask npm — ",[941,37451,37452],{},"npm view @noy-db\u002Fin-rest repository.url"," — rather than inferring from the ",[941,37455,1270],{}," prefix, which describes the ",[1092,37458,3706],{}," a package\nsits in and never who publishes it.",[1022,37461,30965],{"id":30964},[1591,37463,37465],{"id":37464},"frameworks","Frameworks",[1338,37467,37468,37476],{},[1341,37469,37470],{},[1344,37471,37472,37474],{},[1347,37473,9530],{},[1347,37475,29857],{},[1357,37477,37478,37505,37531,37551,37573,37593],{},[1344,37479,37480,37485],{},[1362,37481,37482],{},[1007,37483,283],{"href":37484},".\u002Fvue",[1362,37486,37487,37488,968,37491,968,37494,968,37497,37500,37501,37504],{},"Vue 3 composables — ",[941,37489,37490],{},"useNoydb",[941,37492,37493],{},"useCollection",[941,37495,37496],{},"useSync",[941,37498,37499],{},"useBlobURL"," — via a ",[941,37502,37503],{},"NoydbPlugin",". Works outside Nuxt too.",[1344,37506,37507,37512],{},[1362,37508,37509],{},[1007,37510,255],{"href":37511},".\u002Freact",[1362,37513,37514,37515,968,37517,968,37520,968,37522,968,37525,37527,37528,1520],{},"React hooks — ",[941,37516,37490],{},[941,37518,37519],{},"useVault",[941,37521,37493],{},[941,37523,37524],{},"useQuery",[941,37526,37496],{}," — built on ",[941,37529,37530],{},"useSyncExternalStore",[1344,37532,37533,37538],{},[1362,37534,37535],{},[1007,37536,267],{"href":37537},".\u002Fsolid",[1362,37539,37540,37541,968,37544,968,37547,37550],{},"SolidJS signal primitives — ",[941,37542,37543],{},"createCollectionSignal",[941,37545,37546],{},"createQuerySignal",[941,37548,37549],{},"createSyncSignal","; no DOM dependency, works in SSR and tests.",[1344,37552,37553,37558],{},[1362,37554,37555],{},[1007,37556,271],{"href":37557},".\u002Fsvelte",[1362,37559,37560,37561,968,37564,968,37567,17244,37570,37572],{},"Zero-dependency Svelte stores — ",[941,37562,37563],{},"collectionStore",[941,37565,37566],{},"queryStore",[941,37568,37569],{},"syncStore",[941,37571,271],{}," peer dependency.",[1344,37574,37575,37580],{},[1362,37576,37577],{},[1007,37578,243],{"href":37579},".\u002Fnuxt",[1362,37581,37582,37583,37585,37586,1261,37589,37592],{},"Nuxt 4 module — one ",[941,37584,149],{}," config block auto-imports the ",[941,37587,37588],{},"in-vue",[941,37590,37591],{},"in-pinia"," composables and wires an SSR-safe client plugin.",[1344,37594,37595,37600],{},[1362,37596,37597],{},[1007,37598,239],{"href":37599},".\u002Fnextjs",[1362,37601,37602,37603,37606,37607,37610],{},"Next.js App Router helpers — cookie-session server helpers, plus a ",[941,37604,37605],{},"\u002Fclient"," re-export of ",[941,37608,37609],{},"in-react","'s hooks.",[1591,37612,37614],{"id":37613},"shells","Shells",[937,37616,37617,37618,37621,37622,37625],{},"Where the app is ",[1092,37619,37620],{},"running",", rather than what it is built with. One SPA can be opened in all three\nshells, and they do not share storage — see ",[1007,37623,235],{"href":37624},".\u002Fliff"," for the handoff matrix.",[1338,37627,37628,37636],{},[1341,37629,37630],{},[1344,37631,37632,37634],{},[1347,37633,9530],{},[1347,37635,29857],{},[1357,37637,37638,37654],{},[1344,37639,37640,37644],{},[1362,37641,37642],{},[1007,37643,235],{"href":37624},[1362,37645,37646,37647,37650,37651,37653],{},"LIFF boot layer — ",[941,37648,37649],{},"initLiffApp()"," detects the shell, enforces LINE Login, reads the ID token for ",[941,37652,15969],{},", and ingests a share-link deep link.",[1344,37655,37656,37661],{},[1362,37657,37658],{},[1007,37659,251],{"href":37660},".\u002Fpwa",[1362,37662,37663],{},"Installed-app plumbing — storage persistence, a fail-closed eviction guard, install-prompt capture, display-mode detection, an online watcher.",[1591,37665,37666],{"id":4656},"State",[1338,37668,37669,37677],{},[1341,37670,37671],{},[1344,37672,37673,37675],{},[1347,37674,9530],{},[1347,37676,29857],{},[1357,37678,37679,37696],{},[1344,37680,37681,37686],{},[1362,37682,37683],{},[1007,37684,247],{"href":37685},".\u002Fpinia",[1362,37687,37688,37689,37692,37693,37695],{},"Pinia store factory — ",[941,37690,37691],{},"defineNoydbStore\u003CT>()"," with a chainable ",[941,37694,24715],{}," and optional schema validation.",[1344,37697,37698,37703],{},[1362,37699,37700],{},[1007,37701,291],{"href":37702},".\u002Fzustand",[1362,37704,37705,37706,37709,37710,37713],{},"Zustand adapter — ",[941,37707,37708],{},"createNoydbStore()"," mirrors ",[941,37711,37712],{},"defineNoydbStore"," for Zustand, auto-syncing from change events.",[1591,37715,37717],{"id":37716},"data-query","Data & query",[1338,37719,37720,37728],{},[1341,37721,37722],{},[1344,37723,37724,37726],{},[1347,37725,9530],{},[1347,37727,29857],{},[1357,37729,37730,37748,37758,37770],{},[1344,37731,37732,37737],{},[1362,37733,37734],{},[1007,37735,275],{"href":37736},".\u002Ftanstack-query",[1362,37738,37739,37740,37743,37744,37747],{},"Framework-free ",[941,37741,37742],{},"{ queryKey, queryFn }"," \u002F mutation option factories, plus ",[941,37745,37746],{},"bindInvalidation()"," for auto-invalidation on change events.",[1344,37749,37750,37755],{},[1362,37751,37752],{},[1007,37753,279],{"href":37754},".\u002Ftanstack-table",[1362,37756,37757],{},"Bridges TanStack Table state (sort \u002F filter \u002F pagination) onto the query DSL and back.",[1344,37759,37760,37764],{},[1362,37761,37762],{},[1007,37763,263],{"href":37007},[1362,37765,37766,37769],{},[941,37767,37768],{},"createRestHandler({ store, user })"," — a framework-neutral REST handler with Hono \u002F Express \u002F Fastify \u002F Nitro adapters.",[1344,37771,37772,37776],{},[1362,37773,37774],{},[1007,37775,259],{"href":37423},[1362,37777,37778,37781],{},[941,37779,37780],{},"createRelayHandler({ store })"," — a transport-neutral frame dispatcher over a narrowed store profile; whole-vault replace and vault enumeration are absent from the type.",[1591,37783,37785],{"id":37784},"realtime","Realtime",[1338,37787,37788,37796],{},[1341,37789,37790],{},[1344,37791,37792,37794],{},[1347,37793,9530],{},[1347,37795,29857],{},[1357,37797,37798],{},[1344,37799,37800,37805],{},[1362,37801,37802],{},[1007,37803,287],{"href":37804},".\u002Fyjs",[1362,37806,37807,37810,37811,37814],{},[941,37808,37809],{},"yjsCollection()"," stores Yjs ",[941,37812,37813],{},"Y.Doc"," CRDT state encrypted at rest — collaborative rich-text fields, offline merging.",[1591,37816,37817],{"id":223},"AI",[1338,37819,37820,37828],{},[1341,37821,37822],{},[1344,37823,37824,37826],{},[1347,37825,9530],{},[1347,37827,29857],{},[1357,37829,37830],{},[1344,37831,37832,37837],{},[1362,37833,37834],{},[1007,37835,223],{"href":37836},".\u002Fai",[1362,37838,37839,37842,37843,37846],{},[941,37840,37841],{},"buildToolset(vault)"," emits permission-scoped LLM tool definitions (OpenAI \u002F Anthropic \u002F Vercel AI \u002F JSON-Schema); ",[941,37844,37845],{},"invokeToolCall()"," dispatches and re-checks permissions.",[1591,37848,37849],{"id":227},"Devtools",[1338,37851,37852,37860],{},[1341,37853,37854],{},[1344,37855,37856,37858],{},[1347,37857,9530],{},[1347,37859,29857],{},[1357,37861,37862,37874],{},[1344,37863,37864,37868],{},[1362,37865,37866],{},[1007,37867,227],{"href":37273},[1362,37869,37870,37873],{},[941,37871,37872],{},"createInspector(db)"," — a read-only, framework-agnostic inspector: vaults, schema, stats, records, live writes.",[1344,37875,37876,37880],{},[1362,37877,37878],{},[1007,37879,231],{"href":37227},[1362,37881,37882,37883,37885,37886,37888],{},"Terminal front-end for ",[941,37884,36706],{},", built on ink — ships the ",[941,37887,36181],{}," binary.",[1022,37890,31192],{"id":31191},[1338,37892,37893,37908],{},[1341,37894,37895],{},[1344,37896,37897,37899,37902,37905],{},[1347,37898,9530],{},[1347,37900,37901],{},"Kind",[1347,37903,37904],{},"Reactive API",[1347,37906,37907],{},"SSR",[1357,37909,37910,37924,37937,37950,37962,37975,37987,38001,38013,38025,38037,38049,38061,38073,38085,38097,38109,38121],{},[1344,37911,37912,37916,37919,37922],{},[1362,37913,37914],{},[1007,37915,283],{"href":37484},[1362,37917,37918],{},"framework",[1362,37920,37921],{},"composable",[1362,37923,9559],{},[1344,37925,37926,37930,37932,37935],{},[1362,37927,37928],{},[1007,37929,255],{"href":37511},[1362,37931,37918],{},[1362,37933,37934],{},"hook",[1362,37936,9559],{},[1344,37938,37939,37943,37945,37948],{},[1362,37940,37941],{},[1007,37942,267],{"href":37537},[1362,37944,37918],{},[1362,37946,37947],{},"signal",[1362,37949,9581],{},[1344,37951,37952,37956,37958,37960],{},[1362,37953,37954],{},[1007,37955,271],{"href":37557},[1362,37957,37918],{},[1362,37959,6951],{},[1362,37961,9559],{},[1344,37963,37964,37968,37970,37973],{},[1362,37965,37966],{},[1007,37967,243],{"href":37579},[1362,37969,37918],{},[1362,37971,37972],{},"plugin",[1362,37974,9581],{},[1344,37976,37977,37981,37983,37985],{},[1362,37978,37979],{},[1007,37980,239],{"href":37599},[1362,37982,37918],{},[1362,37984,37934],{},[1362,37986,9581],{},[1344,37988,37989,37993,37996,37999],{},[1362,37990,37991],{},[1007,37992,235],{"href":37624},[1362,37994,37995],{},"shell",[1362,37997,37998],{},"n\u002Fa",[1362,38000,3610],{},[1344,38002,38003,38007,38009,38011],{},[1362,38004,38005],{},[1007,38006,251],{"href":37660},[1362,38008,37995],{},[1362,38010,37998],{},[1362,38012,3610],{},[1344,38014,38015,38019,38021,38023],{},[1362,38016,38017],{},[1007,38018,247],{"href":37685},[1362,38020,4656],{},[1362,38022,6951],{},[1362,38024,3610],{},[1344,38026,38027,38031,38033,38035],{},[1362,38028,38029],{},[1007,38030,291],{"href":37702},[1362,38032,4656],{},[1362,38034,6951],{},[1362,38036,3610],{},[1344,38038,38039,38043,38045,38047],{},[1362,38040,38041],{},[1007,38042,275],{"href":37736},[1362,38044,8673],{},[1362,38046,37998],{},[1362,38048,3610],{},[1344,38050,38051,38055,38057,38059],{},[1362,38052,38053],{},[1007,38054,279],{"href":37754},[1362,38056,8673],{},[1362,38058,37998],{},[1362,38060,3610],{},[1344,38062,38063,38067,38069,38071],{},[1362,38064,38065],{},[1007,38066,263],{"href":37007},[1362,38068,8673],{},[1362,38070,37998],{},[1362,38072,3610],{},[1344,38074,38075,38079,38081,38083],{},[1362,38076,38077],{},[1007,38078,259],{"href":37423},[1362,38080,8673],{},[1362,38082,37998],{},[1362,38084,3610],{},[1344,38086,38087,38091,38093,38095],{},[1362,38088,38089],{},[1007,38090,287],{"href":37804},[1362,38092,37784],{},[1362,38094,37998],{},[1362,38096,3610],{},[1344,38098,38099,38103,38105,38107],{},[1362,38100,38101],{},[1007,38102,223],{"href":37836},[1362,38104,223],{},[1362,38106,37998],{},[1362,38108,3610],{},[1344,38110,38111,38115,38117,38119],{},[1362,38112,38113],{},[1007,38114,227],{"href":37273},[1362,38116,227],{},[1362,38118,37998],{},[1362,38120,3610],{},[1344,38122,38123,38127,38129,38131],{},[1362,38124,38125],{},[1007,38126,231],{"href":37227},[1362,38128,227],{},[1362,38130,37998],{},[1362,38132,3610],{},[1022,38134,38136],{"id":38135},"no-binding-fits-your-runtime","No binding fits your runtime?",[937,38138,38139,38141,38142,1707,38145,38148],{},[941,38140,1086],{}," ships a stable event system (",[941,38143,38144],{},"db.on('change', …)",[941,38146,38147],{},"collection.subscribe(…)"," — the subscription returns an unsubscribe\nfunction) that any framework can drive a reactive wrapper against. Inspect\none of the bindings above to learn the pattern and write your own for\nanything not yet covered (Qwik, Astro, Alpine, …).",[1022,38150,2621],{"id":2620},[1111,38152,38153,38160],{},[1114,38154,38155,38159],{},[1007,38156,38157],{"href":37374},[941,38158,1086],{}," — the core client library every binding re-expresses in its host's idiom.",[1114,38161,38162,38164,38165,38167],{},[1007,38163,14279],{"href":30784}," — the per-user ACL that gates ",[941,38166,35645],{},"'s tool generation and the inspector's read-only views.",{"title":1037,"searchDepth":1038,"depth":1038,"links":38169},[38170,38171,38180,38181,38182],{"id":37401,"depth":1041,"text":37402},{"id":30964,"depth":1041,"text":30965,"children":38172},[38173,38174,38175,38176,38177,38178,38179],{"id":37464,"depth":1038,"text":37465},{"id":37613,"depth":1038,"text":37614},{"id":4656,"depth":1038,"text":37666},{"id":37716,"depth":1038,"text":37717},{"id":37784,"depth":1038,"text":37785},{"id":223,"depth":1038,"text":37817},{"id":227,"depth":1038,"text":37849},{"id":31191,"depth":1041,"text":31192},{"id":38135,"depth":1041,"text":38136},{"id":2620,"depth":1041,"text":2621},"The framework-integration catalog: every in-* binding — React, Vue, Svelte, Solid, Nuxt, Next.js, state stores, TanStack, REST, Yjs, and AI tooling.",{},{"title":217,"icon":127},{"title":37352,"description":38183},"3-K9sPZEtYPezzG_BJOX5edIpkUq_SkdBod8I5GJhzg",{"id":38189,"title":235,"access":12,"apiToc":929,"body":38190,"category":1042,"description":38546,"extension":1044,"meta":38547,"navigation":1042,"nest":3303,"path":236,"seo":38548,"sinceVersion":38549,"status":38550,"stem":237,"subtitle":1042,"tier":1473,"tocExternal":1042,"__hash__":38551},"docs\u002Ffamilies\u002Fin\u002Fliff.md",{"type":931,"value":38191,"toc":38539},[38192,38194,38207,38209,38227,38241,38347,38351,38365,38376,38405,38409,38415,38471,38488,38490,38509,38511,38536],[934,38193,235],{"id":235},[1061,38195,38196],{},[937,38197,38198,1368,38203,38206],{},[950,38199,38200],{},[941,38201,38202],{},"@noy-db\u002Fin-liff",[941,38204,38205],{},"pnpm add @noy-db\u002Fhub @noy-db\u002Fin-liff","\nThe boot layer for a noy-db portal opened from LINE.",[1022,38208,29857],{"id":29856},[937,38210,38211,38212,1952,38214,38216,38217,38220,38221,38223,38224,38226],{},"One SPA, three ",[950,38213,37613],{},[941,38215,37649],{}," works out which one it is running in\n(",[941,38218,38219],{},"'liff' | 'browser' | 'pwa'","), enforces LINE Login, reads the ID token that\n",[1007,38222,15969],{"href":310}," needs for its split-key unlock, and ingests a\n",[1007,38225,852],{"href":853}," deep link from the current location — in one call.",[937,38228,38229,38230,38233,38234,38237,38238,38240],{},"The LIFF SDK is ",[950,38231,38232],{},"injected, never a dependency",". Every entry point takes a ",[941,38235,38236],{},"LiffLike"," — the\nsix-member structural slice this package actually calls — so your app passes the real ",[941,38239,235],{}," global\nand your tests pass a fake.",[2217,38242,38244],{"className":2219,"code":38243,"language":2221,"meta":1037,"style":1037},"import liff from '@line\u002Fliff'\nimport { initLiffApp, getFreshIdToken, openExternal } from '@noy-db\u002Fin-liff'\n\nconst ctx = await initLiffApp({ liff, liffId: import.meta.env.VITE_LIFF_ID })\n\u002F\u002F → { shell, loggedIn, idToken, inClient, deepLink }\n",[941,38245,38246,38262,38291,38295,38342],{"__ignoreMap":1037},[2225,38247,38248,38250,38253,38255,38257,38260],{"class":2227,"line":2228},[2225,38249,2232],{"class":2231},[2225,38251,38252],{"class":2239}," liff ",[2225,38254,2272],{"class":2231},[2225,38256,2249],{"class":2235},[2225,38258,38259],{"class":2252},"@line\u002Fliff",[2225,38261,2256],{"class":2235},[2225,38263,38264,38266,38268,38271,38273,38276,38278,38281,38283,38285,38287,38289],{"class":2227,"line":1041},[2225,38265,2232],{"class":2231},[2225,38267,2236],{"class":2235},[2225,38269,38270],{"class":2239}," initLiffApp",[2225,38272,2298],{"class":2235},[2225,38274,38275],{"class":2239}," getFreshIdToken",[2225,38277,2298],{"class":2235},[2225,38279,38280],{"class":2239}," openExternal",[2225,38282,2243],{"class":2235},[2225,38284,2246],{"class":2231},[2225,38286,2249],{"class":2235},[2225,38288,38202],{"class":2252},[2225,38290,2256],{"class":2235},[2225,38292,38293],{"class":2227,"line":1038},[2225,38294,2285],{"emptyLinePlaceholder":2284},[2225,38296,38297,38299,38302,38304,38306,38308,38310,38312,38315,38317,38320,38322,38325,38327,38329,38331,38333,38335,38338,38340],{"class":2227,"line":2288},[2225,38298,2775],{"class":2774},[2225,38300,38301],{"class":2239}," ctx ",[2225,38303,2781],{"class":2235},[2225,38305,2784],{"class":2231},[2225,38307,38270],{"class":2291},[2225,38309,2789],{"class":2239},[2225,38311,2792],{"class":2235},[2225,38313,38314],{"class":2239}," liff",[2225,38316,2298],{"class":2235},[2225,38318,38319],{"class":2307}," liffId",[2225,38321,2311],{"class":2235},[2225,38323,38324],{"class":2231}," import",[2225,38326,1520],{"class":2235},[2225,38328,17587],{"class":2239},[2225,38330,1520],{"class":2235},[2225,38332,183],{"class":2239},[2225,38334,1520],{"class":2235},[2225,38336,38337],{"class":2239},"VITE_LIFF_ID ",[2225,38339,2359],{"class":2235},[2225,38341,2362],{"class":2239},[2225,38343,38344],{"class":2227,"line":2304},[2225,38345,38346],{"class":2763},"\u002F\u002F → { shell, loggedIn, idToken, inClient, deepLink }\n",[1022,38348,38350],{"id":38349},"the-token-lifecycle-is-the-part-to-get-right","The token lifecycle is the part to get right",[937,38352,38353,38354,38357,38358,38361,38362,38364],{},"LINE ID tokens live about an hour and there is ",[950,38355,38356],{},"no silent refresh",". Expiry is detected\nclient-side from the JWT ",[941,38359,38360],{},"exp"," — no network call, no signature check, because verification is the\nkey-connector server's job under ",[1007,38363,15969],{"href":310},"'s documented split.",[937,38366,38367,38368,38371,38372,38375],{},"The consequence is the useful bit: ",[950,38369,38370],{},"an unlocked noy-db session survives token expiry."," The token\nis only needed again at the next ",[941,38373,38374],{},"serverHalf"," fetch, so refresh it exactly there rather than on a\ntimer:",[2217,38377,38379],{"className":2219,"code":38378,"language":2221,"meta":1037,"style":1037},"\u002F\u002F right before an on-oidc serverHalf fetch — re-logs in on expiry (navigates away)\nconst token = getFreshIdToken(liff)\n\u002F\u002F or { onExpired: 'throw' } if you'd rather handle it yourself\n",[941,38380,38381,38386,38400],{"__ignoreMap":1037},[2225,38382,38383],{"class":2227,"line":2228},[2225,38384,38385],{"class":2763},"\u002F\u002F right before an on-oidc serverHalf fetch — re-logs in on expiry (navigates away)\n",[2225,38387,38388,38390,38393,38395,38397],{"class":2227,"line":1041},[2225,38389,2775],{"class":2774},[2225,38391,38392],{"class":2239}," token ",[2225,38394,2781],{"class":2235},[2225,38396,38275],{"class":2291},[2225,38398,38399],{"class":2239},"(liff)\n",[2225,38401,38402],{"class":2227,"line":1038},[2225,38403,38404],{"class":2763},"\u002F\u002F or { onExpired: 'throw' } if you'd rather handle it yourself\n",[1022,38406,38408],{"id":38407},"escaping-the-in-app-webview","Escaping the in-app WebView",[937,38410,38411,38414],{},[941,38412,38413],{},"openExternal(liff, url)"," breaks out of LINE's WebView. What that does depends on the platform, and\nthe difference matters because storage does not travel:",[1338,38416,38417,38427],{},[1341,38418,38419],{},[1344,38420,38421,38424],{},[1347,38422,38423],{},"Platform",[1347,38425,38426],{},"What happens",[1357,38428,38429,38443,38457],{},[1344,38430,38431,38436],{},[1362,38432,38433],{},[950,38434,38435],{},"Android",[1362,38437,38438,38439,38442],{},"In-scope links open the ",[950,38440,38441],{},"installed PWA directly"," (WebAPK link capture), which shares Chrome's origin storage.",[1344,38444,38445,38450],{},[1362,38446,38447],{},[950,38448,38449],{},"iOS",[1362,38451,38452,38453,38456],{},"Always opens Safari. The installed home-screen app cannot be targeted by URL and has its ",[950,38454,38455],{},"own storage partition"," — show an interstitial for the manual hop.",[1344,38458,38459,38464],{},[1362,38460,38461],{},[950,38462,38463],{},"All",[1362,38465,38466,38467,38470],{},"LINE's WebView storage is always isolated. The new shell starts ",[950,38468,38469],{},"empty"," and re-enrolls through the custodian re-invite — a ceremony, not a data transfer.",[7646,38472,38473],{"color":10789,"icon":10790},[937,38474,38475,38478,38479,38481,38482,1324,38484,38487],{},[950,38476,38477],{},"No WebAuthn inside LINE's WebView."," The in-LINE offline lock options are PIN and device-trust\n(",[1007,38480,15958],{"href":318},"); biometrics only become available after detaching to the browser or\nPWA shell. IndexedDB and ",[941,38483,2506],{},[1092,38485,38486],{},"are"," available in all three shells — LIFF apps are HTTPS\nby requirement.",[1022,38489,34564],{"id":13544},[937,38491,38492,38493,38496,38497,38500,38501,38504,38505,38508],{},"Login and ",[941,38494,38495],{},"openWindow"," only. No LIFF messaging or share APIs, no server code, no UI. The PWA-side\nhelpers — persistence, the eviction guard, install prompts — live in ",[1007,38498,38499],{"href":37660},"in-pwa",". The two\npackages share the ",[941,38502,38503],{},"AppShellContext"," union ",[950,38506,38507],{},"structurally",", with no dependency between them.",[1022,38510,2621],{"id":2620},[1111,38512,38513,38517,38522,38527],{},[1114,38514,38515],{},[1007,38516,37002],{"href":2634},[1114,38518,38519,38521],{},[1007,38520,251],{"href":37660}," — the other half of the portal shell",[1114,38523,38524,38526],{},[1007,38525,15969],{"href":310}," — the split-key unlock this feeds",[1114,38528,38529,38531,38532,38535],{},[1007,38530,852],{"href":853}," — the deep-link grammar ",[941,38533,38534],{},"initLiffApp"," ingests",[2645,38537,38538],{},"html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":1037,"searchDepth":1038,"depth":1038,"links":38540},[38541,38542,38543,38544,38545],{"id":29856,"depth":1041,"text":29857},{"id":38349,"depth":1041,"text":38350},{"id":38407,"depth":1041,"text":38408},{"id":13544,"depth":1041,"text":34564},{"id":2620,"depth":1041,"text":2621},"LIFF shell binding — initLiffApp() establishes the shell, enforces LINE Login, reads the ID token for on-oidc unlock, and ingests a share-link deep link.",{},{"title":235,"description":38546},"0.4.0-pre.12","experimental","YcCQ34rTv4yDxzEy-fuBoFVC0TmOPCrO3q2a28QJ8p0",{"id":38553,"title":239,"access":12,"apiToc":929,"body":38554,"category":1042,"description":38836,"extension":1044,"meta":38837,"navigation":1042,"nest":3303,"path":240,"seo":38838,"sinceVersion":1042,"status":1049,"stem":241,"subtitle":1042,"tier":1050,"tocExternal":1042,"__hash__":38839},"docs\u002Ffamilies\u002Fin\u002Fnextjs.md",{"type":931,"value":38555,"toc":38831},[38556,38558,38581,38583,38637,38639,38782,38813,38815,38828],[934,38557,239],{"id":239},[1061,38559,38560],{},[937,38561,38562,1368,38567,38570,38571,38574,38575,1261,38578,1520],{},[950,38563,38564],{},[941,38565,38566],{},"@noy-db\u002Fin-nextjs",[941,38568,38569],{},"pnpm add @noy-db\u002Fhub @noy-db\u002Fin-nextjs","\nNext.js App Router helpers for noy-db — thin bridge over ",[941,38572,38573],{},"@noy-db\u002Fin-react"," and Next's ",[941,38576,38577],{},"cookies()",[941,38579,38580],{},"headers()",[1022,38582,29857],{"id":29856},[937,38584,38585,38586,38589,38590,38593,38594,968,38596,38599,38600,38602,38603,1261,38606,38609,38610,38615,38616,38618,38619,968,38621,1707,38623,38625,38626,38629,38630,38633,38634,38636],{},"Two entry points. The ",[950,38587,38588],{},"default export"," is server-only: ",[941,38591,38592],{},"cookieSession()","\nreads\u002Fwrites an opaque session token (not the vault secret) via Next's\n",[941,38595,38577],{},[941,38597,38598],{},"configureNoydb({ factory, session? })"," wires a ",[941,38601,62],{},"\nfactory once, and ",[941,38604,38605],{},"withVault(name, handler)",[941,38607,38608],{},"withNoydb(handler)"," wrap\nroute handlers with a resolved vault or instance. ",[950,38611,38612],{},[941,38613,38614],{},"@noy-db\u002Fin-nextjs\u002Fclient","\nsimply re-exports ",[941,38617,38573],{},"'s hooks (",[941,38620,37490],{},[941,38622,37519],{},[941,38624,37493],{},", …) so ",[941,38627,38628],{},"'use client'"," files never pull in ",[941,38631,38632],{},"next\u002Fheaders",".\n",[941,38635,38632],{}," itself is imported dynamically, so the server helpers stay\ntest-runnable outside a Next runtime.",[1022,38638,29893],{"id":29892},[2217,38640,38642],{"className":2219,"code":38641,"language":2221,"meta":1037,"style":1037},"\u002F\u002F app\u002Fapi\u002Finvoices\u002Froute.ts\nimport { withVault } from '@noy-db\u002Fin-nextjs'\nimport { NextResponse } from 'next\u002Fserver'\n\nexport const GET = withVault('acme', async (vault) => {\n  const invoices = await vault.collection('invoices').list()\n  return NextResponse.json(invoices)\n})\n",[941,38643,38644,38649,38668,38688,38692,38728,38760,38776],{"__ignoreMap":1037},[2225,38645,38646],{"class":2227,"line":2228},[2225,38647,38648],{"class":2763},"\u002F\u002F app\u002Fapi\u002Finvoices\u002Froute.ts\n",[2225,38650,38651,38653,38655,38658,38660,38662,38664,38666],{"class":2227,"line":1041},[2225,38652,2232],{"class":2231},[2225,38654,2236],{"class":2235},[2225,38656,38657],{"class":2239}," withVault",[2225,38659,2243],{"class":2235},[2225,38661,2246],{"class":2231},[2225,38663,2249],{"class":2235},[2225,38665,38566],{"class":2252},[2225,38667,2256],{"class":2235},[2225,38669,38670,38672,38674,38677,38679,38681,38683,38686],{"class":2227,"line":1038},[2225,38671,2232],{"class":2231},[2225,38673,2236],{"class":2235},[2225,38675,38676],{"class":2239}," NextResponse",[2225,38678,2243],{"class":2235},[2225,38680,2246],{"class":2231},[2225,38682,2249],{"class":2235},[2225,38684,38685],{"class":2252},"next\u002Fserver",[2225,38687,2256],{"class":2235},[2225,38689,38690],{"class":2227,"line":2288},[2225,38691,2285],{"emptyLinePlaceholder":2284},[2225,38693,38694,38696,38698,38701,38703,38705,38707,38709,38711,38713,38715,38718,38720,38722,38724,38726],{"class":2227,"line":2304},[2225,38695,8080],{"class":2231},[2225,38697,8083],{"class":2774},[2225,38699,38700],{"class":2239}," GET ",[2225,38702,2781],{"class":2235},[2225,38704,38657],{"class":2291},[2225,38706,2789],{"class":2239},[2225,38708,2805],{"class":2235},[2225,38710,6450],{"class":2252},[2225,38712,2805],{"class":2235},[2225,38714,2298],{"class":2235},[2225,38716,38717],{"class":2774}," async",[2225,38719,953],{"class":2235},[2225,38721,2694],{"class":7052},[2225,38723,1381],{"class":2235},[2225,38725,8116],{"class":2774},[2225,38727,2301],{"class":2235},[2225,38729,38730,38732,38734,38736,38738,38740,38742,38744,38746,38748,38750,38752,38754,38756,38758],{"class":2227,"line":2356},[2225,38731,8123],{"class":2774},[2225,38733,6503],{"class":2239},[2225,38735,6346],{"class":2235},[2225,38737,2784],{"class":2231},[2225,38739,6466],{"class":2239},[2225,38741,1520],{"class":2235},[2225,38743,6471],{"class":2291},[2225,38745,2789],{"class":2307},[2225,38747,2805],{"class":2235},[2225,38749,6487],{"class":2252},[2225,38751,2805],{"class":2235},[2225,38753,1381],{"class":2307},[2225,38755,1520],{"class":2235},[2225,38757,3167],{"class":2291},[2225,38759,8146],{"class":2307},[2225,38761,38762,38764,38766,38768,38770,38772,38774],{"class":2227,"line":2830},[2225,38763,8220],{"class":2231},[2225,38765,38676],{"class":2239},[2225,38767,1520],{"class":2235},[2225,38769,141],{"class":2291},[2225,38771,2789],{"class":2307},[2225,38773,6487],{"class":2239},[2225,38775,2362],{"class":2307},[2225,38777,38778,38780],{"class":2227,"line":2861},[2225,38779,2359],{"class":2235},[2225,38781,2362],{"class":2239},[2217,38783,38787],{"className":38784,"code":38785,"language":38786,"meta":1037,"style":1037},"language-tsx shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","\u002F\u002F 'use client' component\nimport { useCollection } from '@noy-db\u002Fin-nextjs\u002Fclient'\n","tsx",[941,38788,38789,38794],{"__ignoreMap":1037},[2225,38790,38791],{"class":2227,"line":2228},[2225,38792,38793],{"class":2763},"\u002F\u002F 'use client' component\n",[2225,38795,38796,38798,38800,38803,38805,38807,38809,38811],{"class":2227,"line":1041},[2225,38797,2232],{"class":2231},[2225,38799,2236],{"class":2235},[2225,38801,38802],{"class":2239}," useCollection",[2225,38804,2243],{"class":2235},[2225,38806,2246],{"class":2231},[2225,38808,2249],{"class":2235},[2225,38810,38614],{"class":2252},[2225,38812,2256],{"class":2235},[1022,38814,2621],{"id":2620},[1111,38816,38817,38821],{},[1114,38818,38819],{},[1007,38820,37002],{"href":2634},[1114,38822,38823,38825,38826],{},[1007,38824,255],{"href":37511}," — the hooks re-exported under ",[941,38827,37605],{},[2645,38829,38830],{},"html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .sHdIc, html code.shiki .sHdIc{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#EEFFFF;--shiki-default-font-style:italic;--shiki-dark:#BABED8;--shiki-dark-font-style:italic}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":1037,"searchDepth":1038,"depth":1038,"links":38832},[38833,38834,38835],{"id":29856,"depth":1041,"text":29857},{"id":29892,"depth":1041,"text":29893},{"id":2620,"depth":1041,"text":2621},"Next.js App Router helpers — dual-entry: cookie-session server helpers at the root, and a `\u002Fclient` re-export of in-react's hooks.",{},{"title":239,"description":38836},"jQTJo4Ws8-BSAHoaGBzaKrZgvg_PCNfTmVxHziCI_ic",{"id":38841,"title":243,"access":12,"apiToc":929,"body":38842,"category":1042,"description":39162,"extension":1044,"meta":39163,"navigation":1042,"nest":3303,"path":244,"seo":39164,"sinceVersion":1042,"status":1049,"stem":245,"subtitle":1042,"tier":1050,"tocExternal":1042,"__hash__":39165},"docs\u002Ffamilies\u002Fin\u002Fnuxt.md",{"type":931,"value":38843,"toc":39157},[38844,38846,38868,38870,38924,38926,39004,39052,39076,39134,39136,39154],[934,38845,243],{"id":243},[1061,38847,38848],{},[937,38849,38850,1368,38855,38858,38859,38862,38863,1261,38865,38867],{},[950,38851,38852],{},[941,38853,38854],{},"@noy-db\u002Fin-nuxt",[941,38856,38857],{},"pnpm add @noy-db\u002Fin-nuxt @noy-db\u002Fhub @noy-db\u002Fin-pinia @noy-db\u002Fin-vue","\nNuxt 4 module — auto-imports, SSR-safe runtime plugin, and the in-pinia bridge. ",[950,38860,38861],{},"Nuxt 4+ only"," — Nuxt 3 apps should wire ",[941,38864,37588],{},[941,38866,37591],{}," by hand.",[1022,38869,29857],{"id":29856},[937,38871,38872,38873,10313,38876,38879,38880,38882,38883,968,38885,968,38887,9483,38889,38891,38892,968,38894,968,38897,9483,38900,38902,38903,38905,38906,38908,38909,38912,38913,38916,38917,38920,38921,38923],{},"Add ",[941,38874,38875],{},"'@noy-db\u002Fin-nuxt'",[941,38877,38878],{},"modules",", configure a ",[941,38881,149],{}," block, and every\ncomponent gets ",[941,38884,37490],{},[941,38886,37493],{},[941,38888,37496],{},[941,38890,37588],{},") plus\n",[941,38893,37712],{},[941,38895,38896],{},"setActiveNoydb",[941,38898,38899],{},"getActiveNoydb",[941,38901,37591],{},")\nauto-imported. The module does ",[950,38904,1004],{}," auto-instantiate the ",[941,38907,62],{},"\ninstance — its runtime plugin registers with ",[941,38910,38911],{},"mode: 'client'"," only, so the\nserver bundle never imports crypto code; you construct the instance\nyourself (typically in ",[941,38914,38915],{},"plugins\u002Fnoydb.client.ts",") and call\n",[941,38918,38919],{},"setActiveNoydb(db)",". During SSR, ",[941,38922,37493],{}," returns empty reactive\nstate and stores hydrate to their initial state — no keys or decryption\never reach the server.",[1022,38925,29893],{"id":29892},[2217,38927,38929],{"className":2219,"code":38928,"language":2221,"meta":1037,"style":1037},"\u002F\u002F nuxt.config.ts\nexport default defineNuxtConfig({\n  modules: ['@noy-db\u002Fin-nuxt'],\n  noydb: { store: 'to-browser-idb', pinia: true },\n})\n",[941,38930,38931,38936,38949,38968,38998],{"__ignoreMap":1037},[2225,38932,38933],{"class":2227,"line":2228},[2225,38934,38935],{"class":2763},"\u002F\u002F nuxt.config.ts\n",[2225,38937,38938,38940,38942,38945,38947],{"class":2227,"line":1041},[2225,38939,8080],{"class":2231},[2225,38941,36571],{"class":2231},[2225,38943,38944],{"class":2291}," defineNuxtConfig",[2225,38946,2789],{"class":2239},[2225,38948,4200],{"class":2235},[2225,38950,38951,38954,38956,38958,38960,38962,38964,38966],{"class":2227,"line":1038},[2225,38952,38953],{"class":2307},"  modules",[2225,38955,2311],{"class":2235},[2225,38957,8667],{"class":2239},[2225,38959,2805],{"class":2235},[2225,38961,38854],{"class":2252},[2225,38963,2805],{"class":2235},[2225,38965,8803],{"class":2239},[2225,38967,1707],{"class":2235},[2225,38969,38970,38973,38975,38977,38979,38981,38983,38985,38987,38989,38992,38994,38996],{"class":2227,"line":2288},[2225,38971,38972],{"class":2307},"  noydb",[2225,38974,2311],{"class":2235},[2225,38976,2236],{"class":2235},[2225,38978,7564],{"class":2307},[2225,38980,2311],{"class":2235},[2225,38982,2249],{"class":2235},[2225,38984,9507],{"class":2252},[2225,38986,2805],{"class":2235},[2225,38988,2298],{"class":2235},[2225,38990,38991],{"class":2307}," pinia",[2225,38993,2311],{"class":2235},[2225,38995,2322],{"class":2321},[2225,38997,2353],{"class":2235},[2225,38999,39000,39002],{"class":2227,"line":2304},[2225,39001,2359],{"class":2235},[2225,39003,2362],{"class":2239},[7646,39005,39007],{"color":10789,"icon":39006},"i-lucide-alert-triangle",[937,39008,39009,1324,39018,39021,39022,39025,39026,39029,39030,39032,39033,39036,39037,39040,39041,39044,39045,39048,39049,39051],{},[950,39010,39011,39012,39014,39015,1520],{},"The key is ",[941,39013,6951],{},", naming a package — not ",[941,39016,39017],{},"adapter: 'browser'",[941,39019,39020],{},"ModuleOptions.store"," accepts\n",[941,39023,39024],{},"'to-browser-idb' | 'to-browser-local' | 'to-memory' | 'to-file' | 'to-aws-dynamo' | 'to-aws-s3'","\nand defaults to ",[941,39027,39028],{},"'to-browser-idb'","; inside ",[941,39031,864],{}," the field is ",[941,39034,39035],{},"sync.store",", same package names.\nOlder material — including this module's own README before ",[941,39038,39039],{},"0.7.0-pre.13"," — showed ",[941,39042,39043],{},"adapter:"," in\nboth places; neither the key nor the value vocabulary is right. The option is only a ",[950,39046,39047],{},"hint"," to\nthe runtime plugin either way — you can always construct a store and pass it to ",[941,39050,2134],{},"\nyourself, which is what this module expects you to do.",[937,39053,39054,39055,968,39057,968,39059,953,39061,1884,39064,1617,39066,1884,39069,2462,39071,953,39073,957],{},"The full option set is ",[941,39056,6951],{},[941,39058,247],{},[941,39060,864],{},[941,39062,39063],{},"{ store, table, region, bucket, mode }",[941,39065,20349],{},[941,39067,39068],{},"{ mode, sessionTimeout }",[941,39070,227],{},[941,39072,263],{},[941,39074,39075],{},"{ enabled, basePath, authToken }",[2217,39077,39079],{"className":2219,"code":39078,"language":2221,"meta":1037,"style":1037},"\u002F\u002F stores\u002Finvoices.ts — defineNoydbStore is auto-imported\nexport const useInvoices = defineNoydbStore\u003CInvoice>('invoices', { compartment: 'C101' })\n",[941,39080,39081,39086],{"__ignoreMap":1037},[2225,39082,39083],{"class":2227,"line":2228},[2225,39084,39085],{"class":2763},"\u002F\u002F stores\u002Finvoices.ts — defineNoydbStore is auto-imported\n",[2225,39087,39088,39090,39092,39095,39097,39100,39102,39104,39106,39108,39110,39112,39114,39116,39118,39121,39123,39125,39128,39130,39132],{"class":2227,"line":1041},[2225,39089,8080],{"class":2231},[2225,39091,8083],{"class":2774},[2225,39093,39094],{"class":2239}," useInvoices ",[2225,39096,2781],{"class":2235},[2225,39098,39099],{"class":2291}," defineNoydbStore",[2225,39101,6474],{"class":2235},[2225,39103,6477],{"class":6342},[2225,39105,6480],{"class":2235},[2225,39107,2789],{"class":2239},[2225,39109,2805],{"class":2235},[2225,39111,6487],{"class":2252},[2225,39113,2805],{"class":2235},[2225,39115,2298],{"class":2235},[2225,39117,2236],{"class":2235},[2225,39119,39120],{"class":2307}," compartment",[2225,39122,2311],{"class":2235},[2225,39124,2249],{"class":2235},[2225,39126,39127],{"class":2252},"C101",[2225,39129,2805],{"class":2235},[2225,39131,2243],{"class":2235},[2225,39133,2362],{"class":2239},[1022,39135,2621],{"id":2620},[1111,39137,39138,39142,39149],{},[1114,39139,39140],{},[1007,39141,37002],{"href":2634},[1114,39143,39144,1368,39146,39148],{},[1007,39145,283],{"href":37484},[1007,39147,247],{"href":37685}," — the composables\u002Fstore this module auto-imports",[1114,39150,39151,39153],{},[1007,39152,563],{"href":564}," — a runnable Nuxt 4 reference app built on this module",[2645,39155,39156],{},"html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .sfNiH, html code.shiki .sfNiH{--shiki-light:#FF5370;--shiki-default:#FF9CAC;--shiki-dark:#FF9CAC}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}",{"title":1037,"searchDepth":1038,"depth":1038,"links":39158},[39159,39160,39161],{"id":29856,"depth":1041,"text":29857},{"id":29892,"depth":1041,"text":29893},{"id":2620,"depth":1041,"text":2621},"Nuxt 4 module — one `noydb` config block auto-imports the in-vue\u002Fin-pinia composables and wires an SSR-safe client-only runtime plugin.",{},{"title":243,"description":39162},"PfHnCU3KBhc-FvS_XlJGH7Bks0lkOgJ2umuLd1XF3xs",{"id":39167,"title":247,"access":12,"apiToc":929,"body":39168,"category":1042,"description":39453,"extension":1044,"meta":39454,"navigation":1042,"nest":3303,"path":248,"seo":39455,"sinceVersion":1042,"status":1049,"stem":249,"subtitle":1042,"tier":1050,"tocExternal":1042,"__hash__":39456},"docs\u002Ffamilies\u002Fin\u002Fpinia.md",{"type":931,"value":39169,"toc":39448},[39170,39172,39185,39187,39251,39253,39325,39422,39424,39445],[934,39171,247],{"id":247},[1061,39173,39174],{},[937,39175,39176,1368,39181,39184],{},[950,39177,39178],{},[941,39179,39180],{},"@noy-db\u002Fin-pinia",[941,39182,39183],{},"pnpm add @noy-db\u002Fin-pinia @noy-db\u002Fhub pinia vue","\nPinia integration for noy-db — drop-in encrypted Pinia stores for Vue 3 \u002F Nuxt 4.",[1022,39186,29857],{"id":29856},[937,39188,39189,39192,39193,39196,39197,968,39199,39202,39203,968,39206,1261,39208,1261,39211,968,39214,39217,39218,39220,39221,38633,39224,39226,39227,39229,39230,39233,39234,39237,39238,39241,39242,964,39244,1261,39247,39250],{},[941,39190,39191],{},"defineNoydbStore\u003CT>(name, { compartment, schema? })"," returns a real Pinia\nstore exposing ",[941,39194,39195],{},"items"," (reactive array), ",[941,39198,3236],{},[941,39200,39201],{},"$ready"," (resolves after\nfirst hydration), ",[941,39204,39205],{},"byId(id)",[941,39207,36269],{},[941,39209,39210],{},"update",[941,39212,39213],{},"remove",[941,39215,39216],{},"refresh()",", and\na chainable ",[941,39219,24715],{}," — the same query DSL as ",[941,39222,39223],{},"Collection.query()",[941,39225,38919],{}," wires the underlying ",[941,39228,62],{}," instance once at app\nstartup. ",[941,39231,39232],{},"storeToRefs",", Vue Devtools, SSR hydration, and\n",[941,39235,39236],{},"pinia-plugin-persistedstate"," all work unmodified. Pass any object with\n",[941,39239,39240],{},"parse(input): T"," (Zod, Valibot, …) as ",[941,39243,23934],{},[941,39245,39246],{},"add()",[941,39248,39249],{},"update()","\nthrow on validation failure before any encryption happens.",[1022,39252,29893],{"id":29892},[2217,39254,39256],{"className":2219,"code":39255,"language":2221,"meta":1037,"style":1037},"\u002F\u002F stores\u002Finvoices.ts\nimport { defineNoydbStore } from '@noy-db\u002Fin-pinia'\nexport const useInvoices = defineNoydbStore\u003CInvoice>('invoices', { compartment: 'C101' })\n",[941,39257,39258,39263,39281],{"__ignoreMap":1037},[2225,39259,39260],{"class":2227,"line":2228},[2225,39261,39262],{"class":2763},"\u002F\u002F stores\u002Finvoices.ts\n",[2225,39264,39265,39267,39269,39271,39273,39275,39277,39279],{"class":2227,"line":1041},[2225,39266,2232],{"class":2231},[2225,39268,2236],{"class":2235},[2225,39270,39099],{"class":2239},[2225,39272,2243],{"class":2235},[2225,39274,2246],{"class":2231},[2225,39276,2249],{"class":2235},[2225,39278,39180],{"class":2252},[2225,39280,2256],{"class":2235},[2225,39282,39283,39285,39287,39289,39291,39293,39295,39297,39299,39301,39303,39305,39307,39309,39311,39313,39315,39317,39319,39321,39323],{"class":2227,"line":1038},[2225,39284,8080],{"class":2231},[2225,39286,8083],{"class":2774},[2225,39288,39094],{"class":2239},[2225,39290,2781],{"class":2235},[2225,39292,39099],{"class":2291},[2225,39294,6474],{"class":2235},[2225,39296,6477],{"class":6342},[2225,39298,6480],{"class":2235},[2225,39300,2789],{"class":2239},[2225,39302,2805],{"class":2235},[2225,39304,6487],{"class":2252},[2225,39306,2805],{"class":2235},[2225,39308,2298],{"class":2235},[2225,39310,2236],{"class":2235},[2225,39312,39120],{"class":2307},[2225,39314,2311],{"class":2235},[2225,39316,2249],{"class":2235},[2225,39318,39127],{"class":2252},[2225,39320,2805],{"class":2235},[2225,39322,2243],{"class":2235},[2225,39324,2362],{"class":2239},[2217,39326,39328],{"className":2219,"code":39327,"language":2221,"meta":1037,"style":1037},"const invoices = useInvoices()\nawait invoices.$ready\nconst overdue = invoices.query().where('status', '==', 'open').orderBy('dueDate').toArray()\n",[941,39329,39330,39343,39354],{"__ignoreMap":1037},[2225,39331,39332,39334,39336,39338,39341],{"class":2227,"line":2228},[2225,39333,2775],{"class":2774},[2225,39335,6461],{"class":2239},[2225,39337,2781],{"class":2235},[2225,39339,39340],{"class":2291}," useInvoices",[2225,39342,8146],{"class":2239},[2225,39344,39345,39347,39349,39351],{"class":2227,"line":1041},[2225,39346,6500],{"class":2231},[2225,39348,6503],{"class":2239},[2225,39350,1520],{"class":2235},[2225,39352,39353],{"class":2239},"$ready\n",[2225,39355,39356,39358,39361,39363,39365,39367,39369,39371,39373,39375,39377,39379,39381,39383,39385,39387,39389,39391,39393,39395,39397,39399,39401,39403,39405,39407,39409,39412,39414,39416,39418,39420],{"class":2227,"line":1038},[2225,39357,2775],{"class":2774},[2225,39359,39360],{"class":2239}," overdue ",[2225,39362,2781],{"class":2235},[2225,39364,6503],{"class":2239},[2225,39366,1520],{"class":2235},[2225,39368,5286],{"class":2291},[2225,39370,4228],{"class":2239},[2225,39372,1520],{"class":2235},[2225,39374,3016],{"class":2291},[2225,39376,2789],{"class":2239},[2225,39378,2805],{"class":2235},[2225,39380,6631],{"class":2252},[2225,39382,2805],{"class":2235},[2225,39384,2298],{"class":2235},[2225,39386,2249],{"class":2235},[2225,39388,21901],{"class":2252},[2225,39390,2805],{"class":2235},[2225,39392,2298],{"class":2235},[2225,39394,2249],{"class":2235},[2225,39396,26178],{"class":2252},[2225,39398,2805],{"class":2235},[2225,39400,1381],{"class":2239},[2225,39402,1520],{"class":2235},[2225,39404,3019],{"class":2291},[2225,39406,2789],{"class":2239},[2225,39408,2805],{"class":2235},[2225,39410,39411],{"class":2252},"dueDate",[2225,39413,2805],{"class":2235},[2225,39415,1381],{"class":2239},[2225,39417,1520],{"class":2235},[2225,39419,3230],{"class":2291},[2225,39421,8146],{"class":2239},[1022,39423,2621],{"id":2620},[1111,39425,39426,39430,39437],{},[1114,39427,39428],{},[1007,39429,37002],{"href":2634},[1114,39431,39432,39434,39435,32703],{},[1007,39433,283],{"href":37484}," — the composables ",[941,39436,37591],{},[1114,39438,39439,39441,39442,39444],{},[1007,39440,243],{"href":37579}," — auto-imports ",[941,39443,37712],{}," for you",[2645,39446,39447],{},"html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":1037,"searchDepth":1038,"depth":1038,"links":39449},[39450,39451,39452],{"id":29856,"depth":1041,"text":29857},{"id":29892,"depth":1041,"text":29893},{"id":2620,"depth":1041,"text":2621},"Pinia store factory — defineNoydbStore\u003CT>() gives a typed, reactive, SSR-safe encrypted store with a chainable query() and schema validation.",{},{"title":247,"description":39453},"9V3v495aMj8EuPT-y-xbomAwL1EE3Ci_v7_Q6U5YCus",{"id":39458,"title":251,"access":12,"apiToc":929,"body":39459,"category":1042,"description":40120,"extension":1044,"meta":40121,"navigation":1042,"nest":3303,"path":252,"seo":40122,"sinceVersion":38549,"status":38550,"stem":253,"subtitle":1042,"tier":1473,"tocExternal":1042,"__hash__":40123},"docs\u002Ffamilies\u002Fin\u002Fpwa.md",{"type":931,"value":39460,"toc":40107},[39461,39463,39476,39480,39497,39510,39514,39525,39532,39585,39606,39613,39624,39761,39786,39800,39804,39919,39926,39965,39969,40029,40049,40053,40060,40078,40080,40104],[934,39462,251],{"id":251},[1061,39464,39465],{},[937,39466,39467,1368,39472,39475],{},[950,39468,39469],{},[941,39470,39471],{},"@noy-db\u002Fin-pwa",[941,39473,39474],{},"pnpm add @noy-db\u002Fhub @noy-db\u002Fin-pwa","\nBrowser-shell plumbing for an installed noy-db app.",[1022,39477,39479],{"id":39478},"the-premise-the-pwa-starts-empty","The premise: the PWA starts empty",[937,39481,39482,39483,39486,39487,39489,39490,39492,39493,39496],{},"An installed PWA lives in ",[950,39484,39485],{},"its own storage partition",". On first open it holds nothing: the app\nruns the online enrollment (unlock + re-invite via ",[1007,39488,15969],{"href":310},") and hydrates the\nvault from the firm's cloud store into ",[1007,39491,9507],{"href":359},". From then on the\ninstalled app is the ",[950,39494,39495],{},"offline-capable home of the vault"," — offline boot is a cached app shell\nplus the local IndexedDB vault.",[937,39498,39499,39500,39503,39504,39507,39508,1520],{},"This package is the plumbing around that lifecycle. It holds ",[950,39501,39502],{},"no keys"," and sees ",[950,39505,39506],{},"no plaintext",":\nevery helper operates on browser shell APIs or on opaque ciphertext-store presence, so it adds\nnothing to the ",[1007,39509,13661],{"href":464},[1022,39511,39513],{"id":39512},"eviction-is-the-top-risk","Eviction is the top risk",[937,39515,39516,39517,39520,39521,39524],{},"iOS ITP evicts all script-writable storage — IndexedDB included — of ",[950,39518,39519],{},"non-installed"," web content\nafter about seven days without interaction. Installed home-screen apps are safer, and\n",[941,39522,39523],{},"navigator.storage.persist()"," exempts an origin from best-effort eviction where granted. Two\nhelpers cover it.",[1591,39526,39528,39531],{"id":39527},"requestpersistence-ask-and-know-the-answer",[941,39529,39530],{},"requestPersistence()"," — ask, and know the answer",[2217,39533,39535],{"className":2219,"code":39534,"language":2221,"meta":1037,"style":1037},"import { requestPersistence } from '@noy-db\u002Fin-pwa'\n\nconst res = await requestPersistence()\n\u002F\u002F { persisted: boolean, quota?: number, usage?: number,\n\u002F\u002F   grantedBy: 'already' | 'granted' | 'denied' | 'unsupported' }\n",[941,39536,39537,39556,39560,39575,39580],{"__ignoreMap":1037},[2225,39538,39539,39541,39543,39546,39548,39550,39552,39554],{"class":2227,"line":2228},[2225,39540,2232],{"class":2231},[2225,39542,2236],{"class":2235},[2225,39544,39545],{"class":2239}," requestPersistence",[2225,39547,2243],{"class":2235},[2225,39549,2246],{"class":2231},[2225,39551,2249],{"class":2235},[2225,39553,39471],{"class":2252},[2225,39555,2256],{"class":2235},[2225,39557,39558],{"class":2227,"line":1041},[2225,39559,2285],{"emptyLinePlaceholder":2284},[2225,39561,39562,39564,39567,39569,39571,39573],{"class":2227,"line":1038},[2225,39563,2775],{"class":2774},[2225,39565,39566],{"class":2239}," res ",[2225,39568,2781],{"class":2235},[2225,39570,2784],{"class":2231},[2225,39572,39545],{"class":2291},[2225,39574,8146],{"class":2239},[2225,39576,39577],{"class":2227,"line":2288},[2225,39578,39579],{"class":2763},"\u002F\u002F { persisted: boolean, quota?: number, usage?: number,\n",[2225,39581,39582],{"class":2227,"line":2304},[2225,39583,39584],{"class":2763},"\u002F\u002F   grantedBy: 'already' | 'granted' | 'denied' | 'unsupported' }\n",[937,39586,39587,39588,6030,39591,39594,39595,39598,39599,39602,39603,1520],{},"Wraps ",[941,39589,39590],{},"persist()",[941,39592,39593],{},"estimate()"," with an explicit grant\u002Fdeny signal, and ",[950,39596,39597],{},"never throws"," —\nbrowsers without the Storage API resolve to ",[941,39600,39601],{},"grantedBy: 'unsupported'",". Call it during enrollment\nand warn the user on ",[941,39604,39605],{},"'denied'",[1591,39607,39609,39612],{"id":39608},"guardlocalvault-fail-closed-at-boot",[941,39610,39611],{},"guardLocalVault()"," — fail closed at boot",[937,39614,39615,39616,39619,39620,39623],{},"If the partition ",[1092,39617,39618],{},"was"," evicted, the app must never crash ",[950,39621,39622],{},"and never present a silent empty vault\nas truth",". The guard probes the store before the vault is opened and routes a miss into\nre-enrollment:",[2217,39625,39627],{"className":2219,"code":39626,"language":2221,"meta":1037,"style":1037},"import { guardLocalVault } from '@noy-db\u002Fin-pwa'\nimport { toBrowserIdb } from '@noy-db\u002Fto-browser-idb'\n\nconst store = toBrowserIdb()\nconst { healthy } = await guardLocalVault(store, 'firm', (why) => {\n  \u002F\u002F why: { present: false, reason: 'empty' | 'probe-failed', cause? }\n  routeToReEnrollment()\n})\nif (healthy) {\n  \u002F* open the vault normally *\u002F\n}\n",[941,39628,39629,39648,39666,39670,39683,39725,39730,39737,39743,39752,39757],{"__ignoreMap":1037},[2225,39630,39631,39633,39635,39638,39640,39642,39644,39646],{"class":2227,"line":2228},[2225,39632,2232],{"class":2231},[2225,39634,2236],{"class":2235},[2225,39636,39637],{"class":2239}," guardLocalVault",[2225,39639,2243],{"class":2235},[2225,39641,2246],{"class":2231},[2225,39643,2249],{"class":2235},[2225,39645,39471],{"class":2252},[2225,39647,2256],{"class":2235},[2225,39649,39650,39652,39654,39656,39658,39660,39662,39664],{"class":2227,"line":1041},[2225,39651,2232],{"class":2231},[2225,39653,2236],{"class":2235},[2225,39655,34763],{"class":2239},[2225,39657,2243],{"class":2235},[2225,39659,2246],{"class":2231},[2225,39661,2249],{"class":2235},[2225,39663,9573],{"class":2252},[2225,39665,2256],{"class":2235},[2225,39667,39668],{"class":2227,"line":1038},[2225,39669,2285],{"emptyLinePlaceholder":2284},[2225,39671,39672,39674,39677,39679,39681],{"class":2227,"line":2288},[2225,39673,2775],{"class":2774},[2225,39675,39676],{"class":2239}," store ",[2225,39678,2781],{"class":2235},[2225,39680,34763],{"class":2291},[2225,39682,8146],{"class":2239},[2225,39684,39685,39687,39689,39692,39694,39696,39698,39700,39703,39705,39707,39710,39712,39714,39716,39719,39721,39723],{"class":2227,"line":2304},[2225,39686,2775],{"class":2774},[2225,39688,2236],{"class":2235},[2225,39690,39691],{"class":2239}," healthy ",[2225,39693,2359],{"class":2235},[2225,39695,6346],{"class":2235},[2225,39697,2784],{"class":2231},[2225,39699,39637],{"class":2291},[2225,39701,39702],{"class":2239},"(store",[2225,39704,2298],{"class":2235},[2225,39706,2249],{"class":2235},[2225,39708,39709],{"class":2252},"firm",[2225,39711,2805],{"class":2235},[2225,39713,2298],{"class":2235},[2225,39715,953],{"class":2235},[2225,39717,39718],{"class":7052},"why",[2225,39720,1381],{"class":2235},[2225,39722,8116],{"class":2774},[2225,39724,2301],{"class":2235},[2225,39726,39727],{"class":2227,"line":2356},[2225,39728,39729],{"class":2763},"  \u002F\u002F why: { present: false, reason: 'empty' | 'probe-failed', cause? }\n",[2225,39731,39732,39735],{"class":2227,"line":2830},[2225,39733,39734],{"class":2291},"  routeToReEnrollment",[2225,39736,8146],{"class":2307},[2225,39738,39739,39741],{"class":2227,"line":2861},[2225,39740,2359],{"class":2235},[2225,39742,2362],{"class":2239},[2225,39744,39745,39747,39750],{"class":2227,"line":4246},[2225,39746,35129],{"class":2231},[2225,39748,39749],{"class":2239}," (healthy) ",[2225,39751,4200],{"class":2235},[2225,39753,39754],{"class":2227,"line":4253},[2225,39755,39756],{"class":2763},"  \u002F* open the vault normally *\u002F\n",[2225,39758,39759],{"class":2227,"line":4259},[2225,39760,7351],{"class":2235},[937,39762,39763,7356,39766,39769,39770,39775,39776,39778,39779,39781,39782,39785],{},[941,39764,39765],{},"probeLocalVault(store, vaultId)",[950,39767,39768],{},"store-agnostic"," — it speaks only the six-method\n",[1007,39771,39772,39774],{"href":30},[941,39773,3154],{}," contract",", never ",[941,39777,9507],{}," internals. It checks the\nvault's ",[941,39780,6709],{}," marker records first (every encrypted vault persists its owner keyring at\ncreation), then falls back to a ",[941,39783,39784],{},"loadAll()"," envelope scan for plaintext-mode vaults.",[937,39787,3289,39788,39791,39792,39795,39796,39799],{},[950,39789,39790],{},"broken store is treated exactly like a missing one",": probe errors resolve to\n",[941,39793,39794],{},"{ present: false, reason: 'probe-failed' }"," and trigger ",[941,39797,39798],{},"onEvicted",". The probe path never throws.",[1022,39801,39803],{"id":39802},"install-ux","Install UX",[2217,39805,39807],{"className":2219,"code":39806,"language":2221,"meta":1037,"style":1037},"import { captureInstallPrompt, getDisplayContext, isIosSafari } from '@noy-db\u002Fin-pwa'\n\nconst install = captureInstallPrompt()          \u002F\u002F once at boot — defers the browser prompt\nconst outcome = await install.promptInstall()   \u002F\u002F later, on a user gesture:\n                                                \u002F\u002F 'accepted' | 'dismissed' | 'unavailable'\n\ngetDisplayContext() \u002F\u002F 'pwa' (standalone display-mode \u002F iOS navigator.standalone) | 'browser'\nisIosSafari()       \u002F\u002F true → show the \"Add to Home Screen\" interstitial;\n                    \u002F\u002F iOS never fires beforeinstallprompt\n",[941,39808,39809,39838,39842,39859,39884,39889,39893,39903,39914],{"__ignoreMap":1037},[2225,39810,39811,39813,39815,39818,39820,39823,39825,39828,39830,39832,39834,39836],{"class":2227,"line":2228},[2225,39812,2232],{"class":2231},[2225,39814,2236],{"class":2235},[2225,39816,39817],{"class":2239}," captureInstallPrompt",[2225,39819,2298],{"class":2235},[2225,39821,39822],{"class":2239}," getDisplayContext",[2225,39824,2298],{"class":2235},[2225,39826,39827],{"class":2239}," isIosSafari",[2225,39829,2243],{"class":2235},[2225,39831,2246],{"class":2231},[2225,39833,2249],{"class":2235},[2225,39835,39471],{"class":2252},[2225,39837,2256],{"class":2235},[2225,39839,39840],{"class":2227,"line":1041},[2225,39841,2285],{"emptyLinePlaceholder":2284},[2225,39843,39844,39846,39849,39851,39853,39856],{"class":2227,"line":1038},[2225,39845,2775],{"class":2774},[2225,39847,39848],{"class":2239}," install ",[2225,39850,2781],{"class":2235},[2225,39852,39817],{"class":2291},[2225,39854,39855],{"class":2239},"()          ",[2225,39857,39858],{"class":2763},"\u002F\u002F once at boot — defers the browser prompt\n",[2225,39860,39861,39863,39866,39868,39870,39873,39875,39878,39881],{"class":2227,"line":2288},[2225,39862,2775],{"class":2774},[2225,39864,39865],{"class":2239}," outcome ",[2225,39867,2781],{"class":2235},[2225,39869,2784],{"class":2231},[2225,39871,39872],{"class":2239}," install",[2225,39874,1520],{"class":2235},[2225,39876,39877],{"class":2291},"promptInstall",[2225,39879,39880],{"class":2239},"()   ",[2225,39882,39883],{"class":2763},"\u002F\u002F later, on a user gesture:\n",[2225,39885,39886],{"class":2227,"line":2304},[2225,39887,39888],{"class":2763},"                                                \u002F\u002F 'accepted' | 'dismissed' | 'unavailable'\n",[2225,39890,39891],{"class":2227,"line":2356},[2225,39892,2285],{"emptyLinePlaceholder":2284},[2225,39894,39895,39898,39900],{"class":2227,"line":2830},[2225,39896,39897],{"class":2291},"getDisplayContext",[2225,39899,19132],{"class":2239},[2225,39901,39902],{"class":2763},"\u002F\u002F 'pwa' (standalone display-mode \u002F iOS navigator.standalone) | 'browser'\n",[2225,39904,39905,39908,39911],{"class":2227,"line":2861},[2225,39906,39907],{"class":2291},"isIosSafari",[2225,39909,39910],{"class":2239},"()       ",[2225,39912,39913],{"class":2763},"\u002F\u002F true → show the \"Add to Home Screen\" interstitial;\n",[2225,39915,39916],{"class":2227,"line":4246},[2225,39917,39918],{"class":2763},"                    \u002F\u002F iOS never fires beforeinstallprompt\n",[937,39920,39921,39922,39925],{},"The shell-context contract shared with ",[1007,39923,39924],{"href":37624},"in-liff"," is exported here as a plain string\nunion, with no runtime coupling in either direction:",[2217,39927,39929],{"className":2219,"code":39928,"language":2221,"meta":1037,"style":1037},"export type AppShellContext = 'liff' | 'browser' | 'pwa'\n",[941,39930,39931],{"__ignoreMap":1037},[2225,39932,39933,39935,39937,39940,39942,39944,39946,39948,39950,39952,39955,39957,39959,39961,39963],{"class":2227,"line":2228},[2225,39934,8080],{"class":2231},[2225,39936,3399],{"class":2774},[2225,39938,39939],{"class":6342}," AppShellContext",[2225,39941,6346],{"class":2235},[2225,39943,2249],{"class":2235},[2225,39945,235],{"class":2252},[2225,39947,2805],{"class":2235},[2225,39949,7087],{"class":2235},[2225,39951,2249],{"class":2235},[2225,39953,39954],{"class":2252},"browser",[2225,39956,2805],{"class":2235},[2225,39958,7087],{"class":2235},[2225,39960,2249],{"class":2235},[2225,39962,251],{"class":2252},[2225,39964,2256],{"class":2235},[1022,39966,39968],{"id":39967},"onlineoffline","Online\u002Foffline",[2217,39970,39972],{"className":2219,"code":39971,"language":2221,"meta":1037,"style":1037},"import { watchOnline } from '@noy-db\u002Fin-pwa'\n\nconst stop = watchOnline((online) => syncStrategy.setOnline(online))\n",[941,39973,39974,39993,39997],{"__ignoreMap":1037},[2225,39975,39976,39978,39980,39983,39985,39987,39989,39991],{"class":2227,"line":2228},[2225,39977,2232],{"class":2231},[2225,39979,2236],{"class":2235},[2225,39981,39982],{"class":2239}," watchOnline",[2225,39984,2243],{"class":2235},[2225,39986,2246],{"class":2231},[2225,39988,2249],{"class":2235},[2225,39990,39471],{"class":2252},[2225,39992,2256],{"class":2235},[2225,39994,39995],{"class":2227,"line":1041},[2225,39996,2285],{"emptyLinePlaceholder":2284},[2225,39998,39999,40001,40003,40005,40007,40009,40011,40014,40016,40018,40021,40023,40026],{"class":2227,"line":1038},[2225,40000,2775],{"class":2774},[2225,40002,27567],{"class":2239},[2225,40004,2781],{"class":2235},[2225,40006,39982],{"class":2291},[2225,40008,2789],{"class":2239},[2225,40010,2789],{"class":2235},[2225,40012,40013],{"class":7052},"online",[2225,40015,1381],{"class":2235},[2225,40017,8116],{"class":2774},[2225,40019,40020],{"class":2239}," syncStrategy",[2225,40022,1520],{"class":2235},[2225,40024,40025],{"class":2291},"setOnline",[2225,40027,40028],{"class":2239},"(online))\n",[937,40030,40031,40032,40035,40036,1625,40038,40041,40042,40044,40045,40048],{},"Fires immediately with ",[941,40033,40034],{},"navigator.onLine",", then on every ",[941,40037,40013],{},[941,40039,40040],{},"offline"," event — shaped for\nfeeding the ",[1007,40043,864],{"href":865}," engine's ",[941,40046,40047],{},"isOnline"," flag. Reconnect replay of the dirty queue\nlives in the sync service; this package does not import it.",[1022,40050,40052],{"id":40051},"service-worker-a-recipe-not-a-runtime","Service worker: a recipe, not a runtime",[937,40054,40055,40056,40059],{},"This package ships ",[950,40057,40058],{},"no service-worker code",". The app-shell SW is a recipe you copy and version\nyourself — see the package README.",[7646,40061,40062],{"color":10789,"icon":10790},[937,40063,40064,40067,40068,40070,40071,40074,40075,40077],{},[950,40065,40066],{},"Vault data never goes in the service-worker cache."," The vault lives encrypted in\n",[941,40069,9507],{},"; the worker caches the ",[950,40072,40073],{},"app shell only"," — HTML, JS, CSS, icons. Caching vault\nresponses would create a second, unmanaged copy of ciphertext outside the store contract, and a\n",[950,40076,2985],{}," copy if you cached decrypted API responses.",[1022,40079,2621],{"id":2620},[1111,40081,40082,40086,40090,40095],{},[1114,40083,40084],{},[1007,40085,37002],{"href":2634},[1114,40087,40088,38521],{},[1007,40089,235],{"href":37624},[1114,40091,40092,40094],{},[1007,40093,9507],{"href":359}," — where the local vault actually lives",[1114,40096,40097,40099,40100,40103],{},[1007,40098,864],{"href":865}," — the engine ",[941,40101,40102],{},"watchOnline"," feeds",[2645,40105,40106],{},"html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sHdIc, html code.shiki .sHdIc{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#EEFFFF;--shiki-default-font-style:italic;--shiki-dark:#BABED8;--shiki-dark-font-style:italic}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}",{"title":1037,"searchDepth":1038,"depth":1038,"links":40108},[40109,40110,40116,40117,40118,40119],{"id":39478,"depth":1041,"text":39479},{"id":39512,"depth":1041,"text":39513,"children":40111},[40112,40114],{"id":39527,"depth":1038,"text":40113},"requestPersistence() — ask, and know the answer",{"id":39608,"depth":1038,"text":40115},"guardLocalVault() — fail closed at boot",{"id":39802,"depth":1041,"text":39803},{"id":39967,"depth":1041,"text":39968},{"id":40051,"depth":1041,"text":40052},{"id":2620,"depth":1041,"text":2621},"PWA shell helpers — storage persistence with a grant\u002Fdeny signal, a fail-closed eviction guard, install-prompt capture, and an online watcher.",{},{"title":251,"description":40120},"Za8_M9CVc8ZNvykLY5r0NY8CnyPy8oNhNTr7hmffre4",{"id":40125,"title":255,"access":12,"apiToc":929,"body":40126,"category":1042,"description":40541,"extension":1044,"meta":40542,"navigation":1042,"nest":3303,"path":256,"seo":40543,"sinceVersion":1042,"status":1049,"stem":257,"subtitle":1042,"tier":1050,"tocExternal":1042,"__hash__":40544},"docs\u002Ffamilies\u002Fin\u002Freact.md",{"type":931,"value":40127,"toc":40536},[40128,40130,40142,40144,40167,40169,40513,40515,40533],[934,40129,255],{"id":255},[1061,40131,40132],{},[937,40133,40134,1368,40138,40141],{},[950,40135,40136],{},[941,40137,38573],{},[941,40139,40140],{},"pnpm add @noy-db\u002Fhub @noy-db\u002Fin-react","\nReact hooks for noy-db — zero deps beyond React.",[1022,40143,29857],{"id":29856},[937,40145,40146,40147,2085,40149,40151,40152,40154,40155,40157,40158,40160,40161,40163,40164,40166],{},"Five hooks built on React 18+'s ",[941,40148,37530],{},[941,40150,37490],{}," reads the\n",[941,40153,62],{}," instance from context, ",[941,40156,37519],{}," opens a vault and tracks its\nlifecycle, ",[941,40159,37493],{}," returns a reactive record list, ",[941,40162,37524],{}," returns\nthe reactive result of a query builder, and ",[941,40165,37496],{}," tracks the hub's\ncross-collection change stream. Change events — put, delete, or sync — drive\nre-renders automatically; no extra state library needed.",[1022,40168,29893],{"id":29892},[2217,40170,40172],{"className":38784,"code":40171,"language":38786,"meta":1037,"style":1037},"import { createNoydb } from '@noy-db\u002Fhub'\nimport { NoydbProvider, useVault, useCollection } from '@noy-db\u002Fin-react'\n\nconst db = await createNoydb({ user: 'me', secret: 'correct horse battery staple' })\n\nfunction App() {\n  return (\n    \u003CNoydbProvider db={db}>\n      \u003CInvoices \u002F>\n    \u003C\u002FNoydbProvider>\n  )\n}\n\nfunction Invoices() {\n  const { vault } = useVault('acme')\n  const { data, loading } = useCollection\u003CInvoice>(vault, 'invoices')\n  if (loading) return \u003Cp>Loading…\u003C\u002Fp>\n  return \u003Cul>{data.map((inv) => \u003Cli key={inv.id}>{inv.number}\u003C\u002Fli>)}\u003C\u002Ful>\n}\n",[941,40173,40174,40192,40220,40224,40267,40271,40282,40289,40307,40317,40326,40331,40335,40339,40350,40374,40413,40444,40509],{"__ignoreMap":1037},[2225,40175,40176,40178,40180,40182,40184,40186,40188,40190],{"class":2227,"line":2228},[2225,40177,2232],{"class":2231},[2225,40179,2236],{"class":2235},[2225,40181,2744],{"class":2239},[2225,40183,2243],{"class":2235},[2225,40185,2246],{"class":2231},[2225,40187,2249],{"class":2235},[2225,40189,1086],{"class":2252},[2225,40191,2256],{"class":2235},[2225,40193,40194,40196,40198,40201,40203,40206,40208,40210,40212,40214,40216,40218],{"class":2227,"line":1041},[2225,40195,2232],{"class":2231},[2225,40197,2236],{"class":2235},[2225,40199,40200],{"class":2239}," NoydbProvider",[2225,40202,2298],{"class":2235},[2225,40204,40205],{"class":2239}," useVault",[2225,40207,2298],{"class":2235},[2225,40209,38802],{"class":2239},[2225,40211,2243],{"class":2235},[2225,40213,2246],{"class":2231},[2225,40215,2249],{"class":2235},[2225,40217,38573],{"class":2252},[2225,40219,2256],{"class":2235},[2225,40221,40222],{"class":2227,"line":1038},[2225,40223,2285],{"emptyLinePlaceholder":2284},[2225,40225,40226,40228,40230,40232,40234,40236,40238,40240,40242,40244,40246,40248,40250,40252,40254,40256,40258,40261,40263,40265],{"class":2227,"line":2288},[2225,40227,2775],{"class":2774},[2225,40229,2778],{"class":2239},[2225,40231,2781],{"class":2235},[2225,40233,2784],{"class":2231},[2225,40235,2744],{"class":2291},[2225,40237,2789],{"class":2239},[2225,40239,2792],{"class":2235},[2225,40241,2795],{"class":2307},[2225,40243,2311],{"class":2235},[2225,40245,2249],{"class":2235},[2225,40247,4212],{"class":2252},[2225,40249,2805],{"class":2235},[2225,40251,2298],{"class":2235},[2225,40253,2810],{"class":2307},[2225,40255,2311],{"class":2235},[2225,40257,2249],{"class":2235},[2225,40259,40260],{"class":2252},"correct horse battery staple",[2225,40262,2805],{"class":2235},[2225,40264,2243],{"class":2235},[2225,40266,2362],{"class":2239},[2225,40268,40269],{"class":2227,"line":2304},[2225,40270,2285],{"emptyLinePlaceholder":2284},[2225,40272,40273,40275,40278,40280],{"class":2227,"line":2356},[2225,40274,12641],{"class":2774},[2225,40276,40277],{"class":2291}," App",[2225,40279,4228],{"class":2235},[2225,40281,2301],{"class":2235},[2225,40283,40284,40286],{"class":2227,"line":2830},[2225,40285,8220],{"class":2231},[2225,40287,40288],{"class":2307}," (\n",[2225,40290,40291,40294,40297,40299,40302,40304],{"class":2227,"line":2861},[2225,40292,40293],{"class":2235},"    \u003C",[2225,40295,40296],{"class":6342},"NoydbProvider",[2225,40298,2842],{"class":2774},[2225,40300,40301],{"class":2235},"={",[2225,40303,6590],{"class":2239},[2225,40305,40306],{"class":2235},"}>\n",[2225,40308,40309,40312,40314],{"class":2227,"line":4246},[2225,40310,40311],{"class":2235},"      \u003C",[2225,40313,32505],{"class":6342},[2225,40315,40316],{"class":2235}," \u002F>\n",[2225,40318,40319,40322,40324],{"class":2227,"line":4253},[2225,40320,40321],{"class":2235},"    \u003C\u002F",[2225,40323,40296],{"class":6342},[2225,40325,7093],{"class":2235},[2225,40327,40328],{"class":2227,"line":4259},[2225,40329,40330],{"class":2307},"  )\n",[2225,40332,40333],{"class":2227,"line":6548},[2225,40334,7351],{"class":2235},[2225,40336,40337],{"class":2227,"line":6576},[2225,40338,2285],{"emptyLinePlaceholder":2284},[2225,40340,40341,40343,40346,40348],{"class":2227,"line":6582},[2225,40342,12641],{"class":2774},[2225,40344,40345],{"class":2291}," Invoices",[2225,40347,4228],{"class":2235},[2225,40349,2301],{"class":2235},[2225,40351,40352,40354,40356,40358,40360,40362,40364,40366,40368,40370,40372],{"class":2227,"line":6587},[2225,40353,8123],{"class":2774},[2225,40355,2236],{"class":2235},[2225,40357,6466],{"class":2239},[2225,40359,2243],{"class":2235},[2225,40361,6346],{"class":2235},[2225,40363,40205],{"class":2291},[2225,40365,2789],{"class":2307},[2225,40367,2805],{"class":2235},[2225,40369,6450],{"class":2252},[2225,40371,2805],{"class":2235},[2225,40373,2362],{"class":2307},[2225,40375,40376,40378,40380,40382,40384,40387,40389,40391,40393,40395,40397,40399,40401,40403,40405,40407,40409,40411],{"class":2227,"line":7174},[2225,40377,8123],{"class":2774},[2225,40379,2236],{"class":2235},[2225,40381,7330],{"class":2239},[2225,40383,2298],{"class":2235},[2225,40385,40386],{"class":2239}," loading",[2225,40388,2243],{"class":2235},[2225,40390,6346],{"class":2235},[2225,40392,38802],{"class":2291},[2225,40394,6474],{"class":2235},[2225,40396,6477],{"class":6342},[2225,40398,6480],{"class":2235},[2225,40400,2789],{"class":2307},[2225,40402,2694],{"class":2239},[2225,40404,2298],{"class":2235},[2225,40406,2249],{"class":2235},[2225,40408,6487],{"class":2252},[2225,40410,2805],{"class":2235},[2225,40412,2362],{"class":2307},[2225,40414,40415,40417,40419,40422,40424,40427,40430,40432,40434,40437,40440,40442],{"class":2227,"line":7180},[2225,40416,7608],{"class":2231},[2225,40418,953],{"class":2307},[2225,40420,40421],{"class":2239},"loading",[2225,40423,8495],{"class":2307},[2225,40425,40426],{"class":2231},"return",[2225,40428,40429],{"class":2235}," \u003C",[2225,40431,937],{"class":2307},[2225,40433,6480],{"class":2235},[2225,40435,40436],{"class":2239},"Loading…",[2225,40438,40439],{"class":2235},"\u003C\u002F",[2225,40441,937],{"class":2307},[2225,40443,7093],{"class":2235},[2225,40445,40446,40448,40450,40452,40455,40457,40459,40461,40463,40465,40467,40469,40471,40473,40475,40477,40479,40481,40483,40485,40488,40490,40492,40494,40497,40499,40501,40503,40505,40507],{"class":2227,"line":7220},[2225,40447,8220],{"class":2231},[2225,40449,40429],{"class":2235},[2225,40451,1111],{"class":2307},[2225,40453,40454],{"class":2235},">{",[2225,40456,8673],{"class":2239},[2225,40458,1520],{"class":2235},[2225,40460,8244],{"class":2291},[2225,40462,2789],{"class":2239},[2225,40464,2789],{"class":2235},[2225,40466,26995],{"class":7052},[2225,40468,1381],{"class":2235},[2225,40470,8116],{"class":2774},[2225,40472,40429],{"class":2235},[2225,40474,1114],{"class":2307},[2225,40476,8153],{"class":2774},[2225,40478,40301],{"class":2235},[2225,40480,26995],{"class":2239},[2225,40482,1520],{"class":2235},[2225,40484,6983],{"class":2239},[2225,40486,40487],{"class":2235},"}>{",[2225,40489,26995],{"class":2239},[2225,40491,1520],{"class":2235},[2225,40493,20636],{"class":2239},[2225,40495,40496],{"class":2235},"}\u003C\u002F",[2225,40498,1114],{"class":2307},[2225,40500,6480],{"class":2235},[2225,40502,1381],{"class":2239},[2225,40504,40496],{"class":2235},[2225,40506,1111],{"class":2307},[2225,40508,7093],{"class":2235},[2225,40510,40511],{"class":2227,"line":7225},[2225,40512,7351],{"class":2235},[1022,40514,2621],{"id":2620},[1111,40516,40517,40521,40528],{},[1114,40518,40519],{},[1007,40520,37002],{"href":2634},[1114,40522,40523,40525,40526],{},[1007,40524,239],{"href":37599}," — App Router server helpers re-export these same hooks under ",[941,40527,37605],{},[1114,40529,40530,40532],{},[1007,40531,275],{"href":37736}," — framework-free alternative for query\u002Fmutation caching",[2645,40534,40535],{},"html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sHdIc, html code.shiki .sHdIc{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#EEFFFF;--shiki-default-font-style:italic;--shiki-dark:#BABED8;--shiki-dark-font-style:italic}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":1037,"searchDepth":1038,"depth":1038,"links":40537},[40538,40539,40540],{"id":29856,"depth":1041,"text":29857},{"id":29892,"depth":1041,"text":29893},{"id":2620,"depth":1041,"text":2621},"React hooks — useNoydb, useVault, useCollection, useQuery, useSync — built on useSyncExternalStore for concurrent-safe reactive subscriptions.",{},{"title":255,"description":40541},"awHlJDOjX2tZyS1TmCDgGyMsWL2OxPrG0060yXIDZSM",{"id":40546,"title":259,"access":12,"apiToc":929,"body":40547,"category":1042,"description":41067,"extension":1044,"meta":41068,"navigation":1042,"nest":3303,"path":260,"seo":41069,"sinceVersion":1042,"status":1049,"stem":261,"subtitle":1042,"tier":1473,"tocExternal":1042,"__hash__":41070},"docs\u002Ffamilies\u002Fin\u002Frelay.md",{"type":931,"value":40548,"toc":41059},[40549,40551,40564,40566,40578,40591,40612,40616,40650,40666,40705,40709,40725,40732,40738,40745,40747,40974,41004,41008,41034,41036,41056],[934,40550,259],{"id":259},[1061,40552,40553],{},[937,40554,40555,1368,40560,40563],{},[950,40556,40557],{},[941,40558,40559],{},"@noy-db\u002Fin-relay",[941,40561,40562],{},"pnpm add @noy-db\u002Fhub @noy-db\u002Fin-relay@next","\nServe a vault to LAN or remote clients over any transport, with the dangerous\nstore methods absent by construction.",[1022,40565,29857],{"id":29856},[937,40567,40568,2557,40570,40573,40574,40577],{},[941,40569,37780],{},[941,40571,40572],{},"(frame) => Promise\u003CRelayResult>"," — it takes a\n",[950,40575,40576],{},"decoded"," request frame and returns a decoded result, so you serve it over HTTP, a\nWebSocket, or QUIC without this package knowing which. The method vocabulary is fixed:",[2217,40579,40581],{"className":2219,"code":40580,"language":2221,"meta":1037,"style":1037},"RELAY_METHODS \u002F\u002F ['get', 'put', 'delete', 'list', 'loadAll', 'ping', 'listSince']\n",[941,40582,40583],{"__ignoreMap":1037},[2225,40584,40585,40588],{"class":2227,"line":2228},[2225,40586,40587],{"class":2239},"RELAY_METHODS ",[2225,40589,40590],{"class":2763},"\u002F\u002F ['get', 'put', 'delete', 'list', 'loadAll', 'ping', 'listSince']\n",[937,40592,40593,40594,40596,40597,40603,40604,12536,40606,40609,40610,1520],{},"Its ",[941,40595,6951],{}," is typed as ",[1007,40598,40600],{"href":40599},"..\u002F..\u002Fcore\u002F05-stores#the-contract",[941,40601,40602],{},"NoydbRelayStore"," from\n",[941,40605,3299],{},[950,40607,40608],{},"narrowed store profile",", not the full ",[941,40611,3154],{},[1022,40613,40615],{"id":40614},"two-exclusions-two-different-reasons","Two exclusions, two different reasons",[1338,40617,40618,40627],{},[1341,40619,40620],{},[1344,40621,40622,40625],{},[1347,40623,40624],{},"absent method",[1347,40626,39718],{},[1357,40628,40629,40638],{},[1344,40630,40631,40635],{},[1362,40632,40633],{},[941,40634,3173],{},[1362,40636,40637],{},"whole-vault replace — a relay that can be asked to overwrite a vault wholesale is a rollback superweapon aimed at the hosts trusting it",[1344,40639,40640,40644],{},[1362,40641,40642],{},[941,40643,3127],{},[1362,40645,40646,40647],{},"enumeration is an existence leak — a relay serves vaults the caller already names, and never answers ",[1092,40648,40649],{},"\"what else is here\"",[937,40651,40652,1324,40655,40658,40659,40662,40663,40665],{},[950,40653,40654],{},"The exclusions are structural.",[941,40656,40657],{},"RELAY_METHODS"," is an allowlist ",[1092,40660,40661],{},"by construction",": those\ntwo names are absent because they are absent from ",[941,40664,40602],{},", not because they were\nfiltered out. Adding either name to the list would not compile a call.",[7646,40667,40669],{"color":7648,"icon":40668},"i-lucide-shield",[937,40670,40671,1324,40680,40682,40683,40685,40686,40688,40689,40692,40693,40695,40696,1324,40699,40701,40702,40704],{},[950,40672,40673,40674,40676,40677,40679],{},"Why this is not ",[941,40675,35649],{}," with a narrower ",[941,40678,34720],{}," set.",[941,40681,35649],{}," types its ",[941,40684,6951],{}," as a\nfull ",[941,40687,3154],{}," and gates dispatch with a runtime ",[941,40690,40691],{},"Set"," — correct for ",[941,40694,35649],{},", whose job\nis to serve a whole store. Building a relay that way means handing the handler an object that\n",[950,40697,40698],{},"carries",[941,40700,3173],{}," and trusting a ",[941,40703,40691],{}," not to call it: structural absence downgraded to a\nruntime allowlist, which is the one property a relay exists to have.",[1022,40706,40708],{"id":40707},"unknown-method-never-forbidden","Unknown method, never forbidden",[937,40710,40711,40712,39775,40718,40721,40722,40724],{},"An excluded method is refused as ",[950,40713,40714,40717],{},[941,40715,40716],{},"400"," unknown",[941,40719,40720],{},"403"," forbidden. A ",[941,40723,40720],{}," would\nconfirm the method exists and is merely disallowed here, which names the excluded surface to\nanyone probing.",[937,40726,40727,40728,40731],{},"Measured against the published ",[941,40729,40730],{},"0.7.0-pre.16"," — an excluded method and a name that never\nexisted are indistinguishable:",[2217,40733,40736],{"className":40734,"code":40735,"language":5672},[5670],"get            → ok\nsaveAll        → status 400  UnknownRelayMethodError\nlistVaults     → status 400  UnknownRelayMethodError\ntotallyMadeUp  → status 400  UnknownRelayMethodError\n",[941,40737,40735],{"__ignoreMap":1037},[937,40739,40740,40741,40744],{},"That is the security posture, not a missing case. ",[941,40742,40743],{},"@doi-db\u002Fdaemon"," reached the same\nconclusion independently in its own native implementation.",[1022,40746,29893],{"id":29892},[2217,40748,40750],{"className":2219,"code":40749,"language":2221,"meta":1037,"style":1037},"import { createRelayHandler } from '@noy-db\u002Fin-relay'\nimport { toFile } from '@noy-db\u002Fto-file'\n\nconst handle = createRelayHandler({ store: toFile({ dir: '.\u002Fvaults' }) })\n\n\u002F\u002F Over any transport you like — here, a bare fetch handler.\nexport default async function (req: Request): Promise\u003CResponse> {\n  const frame = await req.json()\n  const result = await handle(frame)\n  return Response.json(result, { status: result.ok ? 200 : result.status })\n}\n",[941,40751,40752,40771,40789,40793,40837,40841,40846,40880,40900,40921,40970],{"__ignoreMap":1037},[2225,40753,40754,40756,40758,40761,40763,40765,40767,40769],{"class":2227,"line":2228},[2225,40755,2232],{"class":2231},[2225,40757,2236],{"class":2235},[2225,40759,40760],{"class":2239}," createRelayHandler",[2225,40762,2243],{"class":2235},[2225,40764,2246],{"class":2231},[2225,40766,2249],{"class":2235},[2225,40768,40559],{"class":2252},[2225,40770,2256],{"class":2235},[2225,40772,40773,40775,40777,40779,40781,40783,40785,40787],{"class":2227,"line":1041},[2225,40774,2232],{"class":2231},[2225,40776,2236],{"class":2235},[2225,40778,15573],{"class":2239},[2225,40780,2243],{"class":2235},[2225,40782,2246],{"class":2231},[2225,40784,2249],{"class":2235},[2225,40786,9551],{"class":2252},[2225,40788,2256],{"class":2235},[2225,40790,40791],{"class":2227,"line":1038},[2225,40792,2285],{"emptyLinePlaceholder":2284},[2225,40794,40795,40797,40800,40802,40804,40806,40808,40810,40812,40814,40816,40818,40820,40822,40824,40827,40829,40831,40833,40835],{"class":2227,"line":2288},[2225,40796,2775],{"class":2774},[2225,40798,40799],{"class":2239}," handle ",[2225,40801,2781],{"class":2235},[2225,40803,40760],{"class":2291},[2225,40805,2789],{"class":2239},[2225,40807,2792],{"class":2235},[2225,40809,7564],{"class":2307},[2225,40811,2311],{"class":2235},[2225,40813,15573],{"class":2291},[2225,40815,2789],{"class":2239},[2225,40817,2792],{"class":2235},[2225,40819,15619],{"class":2307},[2225,40821,2311],{"class":2235},[2225,40823,2249],{"class":2235},[2225,40825,40826],{"class":2252},".\u002Fvaults",[2225,40828,2805],{"class":2235},[2225,40830,2243],{"class":2235},[2225,40832,8495],{"class":2239},[2225,40834,2359],{"class":2235},[2225,40836,2362],{"class":2239},[2225,40838,40839],{"class":2227,"line":2304},[2225,40840,2285],{"emptyLinePlaceholder":2284},[2225,40842,40843],{"class":2227,"line":2356},[2225,40844,40845],{"class":2763},"\u002F\u002F Over any transport you like — here, a bare fetch handler.\n",[2225,40847,40848,40850,40852,40854,40857,40859,40862,40864,40867,40869,40871,40873,40876,40878],{"class":2227,"line":2830},[2225,40849,8080],{"class":2231},[2225,40851,36571],{"class":2231},[2225,40853,38717],{"class":2774},[2225,40855,40856],{"class":2774}," function",[2225,40858,953],{"class":2235},[2225,40860,40861],{"class":7052},"req",[2225,40863,2311],{"class":2235},[2225,40865,40866],{"class":6342}," Request",[2225,40868,6255],{"class":2235},[2225,40870,7079],{"class":6342},[2225,40872,6474],{"class":2235},[2225,40874,40875],{"class":6342},"Response",[2225,40877,6480],{"class":2235},[2225,40879,2301],{"class":2235},[2225,40881,40882,40884,40887,40889,40891,40894,40896,40898],{"class":2227,"line":2861},[2225,40883,8123],{"class":2774},[2225,40885,40886],{"class":2239}," frame",[2225,40888,6346],{"class":2235},[2225,40890,2784],{"class":2231},[2225,40892,40893],{"class":2239}," req",[2225,40895,1520],{"class":2235},[2225,40897,141],{"class":2291},[2225,40899,8146],{"class":2307},[2225,40901,40902,40904,40907,40909,40911,40914,40916,40919],{"class":2227,"line":4246},[2225,40903,8123],{"class":2774},[2225,40905,40906],{"class":2239}," result",[2225,40908,6346],{"class":2235},[2225,40910,2784],{"class":2231},[2225,40912,40913],{"class":2291}," handle",[2225,40915,2789],{"class":2307},[2225,40917,40918],{"class":2239},"frame",[2225,40920,2362],{"class":2307},[2225,40922,40923,40925,40928,40930,40932,40934,40936,40938,40940,40942,40944,40946,40948,40951,40954,40957,40960,40962,40964,40966,40968],{"class":2227,"line":4253},[2225,40924,8220],{"class":2231},[2225,40926,40927],{"class":2239}," Response",[2225,40929,1520],{"class":2235},[2225,40931,141],{"class":2291},[2225,40933,2789],{"class":2307},[2225,40935,11195],{"class":2239},[2225,40937,2298],{"class":2235},[2225,40939,2236],{"class":2235},[2225,40941,6361],{"class":2307},[2225,40943,2311],{"class":2235},[2225,40945,40906],{"class":2239},[2225,40947,1520],{"class":2235},[2225,40949,40950],{"class":2239},"ok",[2225,40952,40953],{"class":2235}," ?",[2225,40955,40956],{"class":6527}," 200",[2225,40958,40959],{"class":2235}," :",[2225,40961,40906],{"class":2239},[2225,40963,1520],{"class":2235},[2225,40965,6631],{"class":2239},[2225,40967,2243],{"class":2235},[2225,40969,2362],{"class":2307},[2225,40971,40972],{"class":2227,"line":4259},[2225,40973,7351],{"class":2235},[937,40975,40976,40979,40980,33507,40983,40986,40987,40989,40990,40992,40993,40996,40997,41000,41001,41003],{},[941,40977,40978],{},"RelayResult"," is a discriminated union: ",[941,40981,40982],{},"{ ok: true, id, value }",[941,40984,40985],{},"{ ok: false, id, status: 400 | 500, error: { name, message } }"," — a transport maps ",[941,40988,6631],{},"\ndirectly. Since ",[941,40991,2126],{}," a store failure reaches the client as the message ",[941,40994,40995],{},"'store error'",":\nthe original message is kept only for a ",[941,40998,40999],{},"501"," and for ",[941,41002,6860],{},", so an internal error detail\nno longer leaks to the caller.",[1022,41005,41007],{"id":41006},"install-note","Install note",[937,41009,30866,41010,41012,41013,41016,41017,41020,41021,41026,41027,41029,41030,41033],{},[941,41011,40730],{}," this package's ",[941,41014,41015],{},"latest"," dist-tag pointed at a pre-release, so a plain\n",[941,41018,41019],{},"npm i @noy-db\u002Fin-relay"," silently moved hub onto the pre line\n(",[1007,41022,41025],{"href":41023,"rel":41024},"https:\u002F\u002Fgithub.com\u002Fnoy-db\u002Fcore\u002Fissues\u002F1293",[3371],"noy-db#1293","). That ended when stables shipped: ",[941,41028,41015],{},"\nis a stable release, and ",[941,41031,41032],{},"@noy-db\u002Fin-relay@next"," is the pre line — install the one that matches\nyour hub.",[1022,41035,2621],{"id":2620},[1111,41037,41038,41042,41047],{},[1114,41039,41040],{},[1007,41041,37002],{"href":2634},[1114,41043,41044,41046],{},[1007,41045,263],{"href":37007}," — the full-store HTTP handler this deliberately is not",[1114,41048,41049,41052,41053,41055],{},[1007,41050,6930],{"href":41051},"..\u002F..\u002Fcore\u002F05-stores"," — the store contract ",[941,41054,40602],{}," narrows",[2645,41057,41058],{},"html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .sHdIc, html code.shiki .sHdIc{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#EEFFFF;--shiki-default-font-style:italic;--shiki-dark:#BABED8;--shiki-dark-font-style:italic}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}",{"title":1037,"searchDepth":1038,"depth":1038,"links":41060},[41061,41062,41063,41064,41065,41066],{"id":29856,"depth":1041,"text":29857},{"id":40614,"depth":1041,"text":40615},{"id":40707,"depth":1041,"text":40708},{"id":29892,"depth":1041,"text":29893},{"id":41006,"depth":1041,"text":41007},{"id":2620,"depth":1041,"text":2621},"Transport-neutral frame dispatcher over a narrowed store profile — whole-vault replace and vault enumeration are absent from the type, not filtered at runtime.",{},{"title":259,"description":41067},"nx_KSvVTVV4Af3EbBmgk00Bg9vFJGGe4bSL7RpA57oo",{"id":41072,"title":263,"access":12,"apiToc":929,"body":41073,"category":1042,"description":41313,"extension":1044,"meta":41314,"navigation":1042,"nest":3303,"path":264,"seo":41315,"sinceVersion":1042,"status":1049,"stem":265,"subtitle":1042,"tier":1473,"tocExternal":1042,"__hash__":41316},"docs\u002Ffamilies\u002Fin\u002Frest.md",{"type":931,"value":41074,"toc":41308},[41075,41077,41090,41092,41130,41182,41184,41292,41294,41305],[934,41076,263],{"id":263},[1061,41078,41079],{},[937,41080,41081,1368,41086,41089],{},[950,41082,41083],{},[941,41084,41085],{},"@noy-db\u002Fin-rest",[941,41087,41088],{},"pnpm add @noy-db\u002Fhub @noy-db\u002Fin-rest","\nServe a vault over HTTP — framework-neutral REST API integration for noy-db.",[1022,41091,29857],{"id":29856},[937,41093,41094,41097,41098,41101,41102,41105,41106,41109,41110,41113,41114,13571,41117,968,41120,968,41123,2462,41126,41129],{},[941,41095,41096],{},"createRestHandler({ store, user, ttlSeconds?, basePath? })"," builds a\n",[941,41099,41100],{},"NoydbRestHandler"," — a plain ",[941,41103,41104],{},"handle(req: RestRequest): Promise\u003CRestResponse>","\nfunction — against a framework-neutral request\u002Fresponse shape. Session\nstate (",[941,41107,41108],{},"ttlSeconds",", default ",[941,41111,41112],{},"900",") is tracked internally by a\n",[941,41115,41116],{},"SessionStore",[941,41118,41119],{},"\u002Fhono",[941,41121,41122],{},"\u002Fexpress",[941,41124,41125],{},"\u002Ffastify",[941,41127,41128],{},"\u002Fnitro"," subpath\nadapters wrap that handler for each server framework, so the routing logic\nis written once and reused everywhere.",[7646,41131,41133,41158],{"color":7648,"icon":41132},"i-lucide-package",[937,41134,41135,41138,41139,41141,41142,1707,41144,968,41147,997,41150,41153,41154,41157],{},[950,41136,41137],{},"The framework you pick is an optional peer."," Since ",[941,41140,10996],{}," the package declares ",[941,41143,1591],{},[941,41145,41146],{},"hono",[941,41148,41149],{},"express",[941,41151,41152],{},"fastify"," as optional ",[941,41155,41156],{},"peerDependencies"," with real version ranges\n(noy-db#1241). Each adapter subpath statically imports its framework, so previously they were\nundeclared runtime imports — no version range, and nothing for a resolver to check. Install the one\nyou use; the others stay absent and unresolved.",[2217,41159,41163],{"className":41160,"code":41161,"language":41162,"meta":1037,"style":1037},"language-sh shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","pnpm add @noy-db\u002Fin-rest hono     # or express@^5 \u002F fastify@^5 \u002F h3@^1.13\n","sh",[941,41164,41165],{"__ignoreMap":1037},[2225,41166,41167,41170,41173,41176,41179],{"class":2227,"line":2228},[2225,41168,41169],{"class":6342},"pnpm",[2225,41171,41172],{"class":2252}," add",[2225,41174,41175],{"class":2252}," @noy-db\u002Fin-rest",[2225,41177,41178],{"class":2252}," hono",[2225,41180,41181],{"class":2763},"     # or express@^5 \u002F fastify@^5 \u002F h3@^1.13\n",[1022,41183,29893],{"id":29892},[2217,41185,41187],{"className":2219,"code":41186,"language":2221,"meta":1037,"style":1037},"import { createRestHandler } from '@noy-db\u002Fin-rest'\nimport { honoAdapter } from '@noy-db\u002Fin-rest\u002Fhono'\n\nconst handler = createRestHandler({ store, user: 'api' })\napp.route('\u002Fapi\u002Fnoydb', honoAdapter(handler))\n",[941,41188,41189,41208,41228,41232,41266],{"__ignoreMap":1037},[2225,41190,41191,41193,41195,41198,41200,41202,41204,41206],{"class":2227,"line":2228},[2225,41192,2232],{"class":2231},[2225,41194,2236],{"class":2235},[2225,41196,41197],{"class":2239}," createRestHandler",[2225,41199,2243],{"class":2235},[2225,41201,2246],{"class":2231},[2225,41203,2249],{"class":2235},[2225,41205,41085],{"class":2252},[2225,41207,2256],{"class":2235},[2225,41209,41210,41212,41214,41217,41219,41221,41223,41226],{"class":2227,"line":1041},[2225,41211,2232],{"class":2231},[2225,41213,2236],{"class":2235},[2225,41215,41216],{"class":2239}," honoAdapter",[2225,41218,2243],{"class":2235},[2225,41220,2246],{"class":2231},[2225,41222,2249],{"class":2235},[2225,41224,41225],{"class":2252},"@noy-db\u002Fin-rest\u002Fhono",[2225,41227,2256],{"class":2235},[2225,41229,41230],{"class":2227,"line":1038},[2225,41231,2285],{"emptyLinePlaceholder":2284},[2225,41233,41234,41236,41239,41241,41243,41245,41247,41249,41251,41253,41255,41257,41260,41262,41264],{"class":2227,"line":2288},[2225,41235,2775],{"class":2774},[2225,41237,41238],{"class":2239}," handler ",[2225,41240,2781],{"class":2235},[2225,41242,41197],{"class":2291},[2225,41244,2789],{"class":2239},[2225,41246,2792],{"class":2235},[2225,41248,7564],{"class":2239},[2225,41250,2298],{"class":2235},[2225,41252,2795],{"class":2307},[2225,41254,2311],{"class":2235},[2225,41256,2249],{"class":2235},[2225,41258,41259],{"class":2252},"api",[2225,41261,2805],{"class":2235},[2225,41263,2243],{"class":2235},[2225,41265,2362],{"class":2239},[2225,41267,41268,41271,41273,41276,41278,41280,41283,41285,41287,41289],{"class":2227,"line":2304},[2225,41269,41270],{"class":2239},"app",[2225,41272,1520],{"class":2235},[2225,41274,41275],{"class":2291},"route",[2225,41277,2789],{"class":2239},[2225,41279,2805],{"class":2235},[2225,41281,41282],{"class":2252},"\u002Fapi\u002Fnoydb",[2225,41284,2805],{"class":2235},[2225,41286,2298],{"class":2235},[2225,41288,41216],{"class":2291},[2225,41290,41291],{"class":2239},"(handler))\n",[1022,41293,2621],{"id":2620},[1111,41295,41296,41300],{},[1114,41297,41298],{},[1007,41299,37002],{"href":2634},[1114,41301,41302,41304],{},[1007,41303,223],{"href":37836}," — another protocol-facing binding, for LLM tool-calling instead of HTTP",[2645,41306,41307],{},"html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}",{"title":1037,"searchDepth":1038,"depth":1038,"links":41309},[41310,41311,41312],{"id":29856,"depth":1041,"text":29857},{"id":29892,"depth":1041,"text":29893},{"id":2620,"depth":1041,"text":2621},"Framework-neutral REST handler — createRestHandler({ store, user }) with \u002Fhono, \u002Fexpress, \u002Ffastify, \u002Fnitro subpath adapters.",{},{"title":263,"description":41313},"9vl4w-dtYh-bEVburDwmHSEC3IjjXbicmBN7mN0CcQI",{"id":41318,"title":267,"access":12,"apiToc":929,"body":41319,"category":1042,"description":41557,"extension":1044,"meta":41558,"navigation":1042,"nest":3303,"path":268,"seo":41559,"sinceVersion":1042,"status":1049,"stem":269,"subtitle":1042,"tier":1050,"tocExternal":1042,"__hash__":41560},"docs\u002Ffamilies\u002Fin\u002Fsolid.md",{"type":931,"value":41320,"toc":41552},[41321,41323,41336,41338,41369,41371,41435,41536,41538,41549],[934,41322,267],{"id":267},[1061,41324,41325],{},[937,41326,41327,1368,41332,41335],{},[950,41328,41329],{},[941,41330,41331],{},"@noy-db\u002Fin-solid",[941,41333,41334],{},"pnpm add @noy-db\u002Fhub @noy-db\u002Fin-solid","\nSolidJS signal primitives for noy-db.",[1022,41337,29857],{"id":29856},[937,41339,41340,41341,6030,41344,41347,41348,2085,41351,2557,41353,41356,41357,41359,41360,41362,41363,41365,41366,1520],{},"Three primitives built on ",[941,41342,41343],{},"createSignal",[941,41345,41346],{},"createRenderEffect"," +\n",[941,41349,41350],{},"onCleanup",[941,41352,37543],{},[941,41354,41355],{},"[records, loading, error]","\naccessors for a collection's full list; ",[941,41358,37546],{}," returns the\nreactive result of a query builder; ",[941,41361,37549],{}," returns an accessor\nfor the hub's last change event. Subscriptions register synchronously\ninside ",[941,41364,41346],{}," (no scheduler deferral), so no event fired\nbetween mount and first flush is missed. No DOM dependency — works in SSR\nand test environments via ",[941,41367,41368],{},"createRoot",[1022,41370,29893],{"id":29892},[2217,41372,41374],{"className":2219,"code":41373,"language":2221,"meta":1037,"style":1037},"import { createCollectionSignal } from '@noy-db\u002Fin-solid'\n\nconst [invoices, loading] = createCollectionSignal\u003CInvoice>(vault, 'invoices')\n",[941,41375,41376,41395,41399],{"__ignoreMap":1037},[2225,41377,41378,41380,41382,41385,41387,41389,41391,41393],{"class":2227,"line":2228},[2225,41379,2232],{"class":2231},[2225,41381,2236],{"class":2235},[2225,41383,41384],{"class":2239}," createCollectionSignal",[2225,41386,2243],{"class":2235},[2225,41388,2246],{"class":2231},[2225,41390,2249],{"class":2235},[2225,41392,41331],{"class":2252},[2225,41394,2256],{"class":2235},[2225,41396,41397],{"class":2227,"line":1041},[2225,41398,2285],{"emptyLinePlaceholder":2284},[2225,41400,41401,41403,41405,41407,41409,41411,41413,41415,41417,41419,41421,41423,41425,41427,41429,41431,41433],{"class":2227,"line":1038},[2225,41402,2775],{"class":2774},[2225,41404,8667],{"class":2235},[2225,41406,6487],{"class":2239},[2225,41408,2298],{"class":2235},[2225,41410,40386],{"class":2239},[2225,41412,8803],{"class":2235},[2225,41414,6346],{"class":2235},[2225,41416,41384],{"class":2291},[2225,41418,6474],{"class":2235},[2225,41420,6477],{"class":6342},[2225,41422,6480],{"class":2235},[2225,41424,30266],{"class":2239},[2225,41426,2298],{"class":2235},[2225,41428,2249],{"class":2235},[2225,41430,6487],{"class":2252},[2225,41432,2805],{"class":2235},[2225,41434,2362],{"class":2239},[2217,41436,41438],{"className":38784,"code":41437,"language":38786,"meta":1037,"style":1037},"\u003CShow when={!loading()} fallback={\u003Cp>Loading…\u003C\u002Fp>}>\n  \u003CFor each={invoices()}>{(inv) => \u003Cli>{inv.number}\u003C\u002Fli>}\u003C\u002FFor>\n\u003C\u002FShow>\n",[941,41439,41440,41479,41528],{"__ignoreMap":1037},[2225,41441,41442,41444,41447,41450,41453,41455,41457,41460,41463,41466,41468,41470,41472,41474,41476],{"class":2227,"line":2228},[2225,41443,6474],{"class":2235},[2225,41445,41446],{"class":6342},"Show",[2225,41448,41449],{"class":2774}," when",[2225,41451,41452],{"class":2235},"={!",[2225,41454,40421],{"class":2291},[2225,41456,4228],{"class":2239},[2225,41458,41459],{"class":2235},"} ",[2225,41461,41462],{"class":2774},"fallback",[2225,41464,41465],{"class":2235},"={\u003C",[2225,41467,937],{"class":2307},[2225,41469,6480],{"class":2235},[2225,41471,40436],{"class":2239},[2225,41473,40439],{"class":2235},[2225,41475,937],{"class":2307},[2225,41477,41478],{"class":2235},">}>\n",[2225,41480,41481,41484,41487,41490,41492,41494,41496,41499,41501,41503,41505,41507,41509,41511,41513,41515,41517,41519,41521,41524,41526],{"class":2227,"line":1041},[2225,41482,41483],{"class":2235},"  \u003C",[2225,41485,41486],{"class":6342},"For",[2225,41488,41489],{"class":2774}," each",[2225,41491,40301],{"class":2235},[2225,41493,6487],{"class":2291},[2225,41495,4228],{"class":2239},[2225,41497,41498],{"class":2235},"}>{(",[2225,41500,26995],{"class":7052},[2225,41502,1381],{"class":2235},[2225,41504,8116],{"class":2774},[2225,41506,40429],{"class":2235},[2225,41508,1114],{"class":2307},[2225,41510,40454],{"class":2235},[2225,41512,26995],{"class":2239},[2225,41514,1520],{"class":2235},[2225,41516,20636],{"class":2239},[2225,41518,40496],{"class":2235},[2225,41520,1114],{"class":2307},[2225,41522,41523],{"class":2235},">}\u003C\u002F",[2225,41525,41486],{"class":6342},[2225,41527,7093],{"class":2235},[2225,41529,41530,41532,41534],{"class":2227,"line":1038},[2225,41531,40439],{"class":2235},[2225,41533,41446],{"class":6342},[2225,41535,7093],{"class":2235},[1022,41537,2621],{"id":2620},[1111,41539,41540,41544],{},[1114,41541,41542],{},[1007,41543,37002],{"href":2634},[1114,41545,41546,41548],{},[1007,41547,271],{"href":37557}," — the equivalent zero-dep store pattern for Svelte",[2645,41550,41551],{},"html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .sHdIc, html code.shiki .sHdIc{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#EEFFFF;--shiki-default-font-style:italic;--shiki-dark:#BABED8;--shiki-dark-font-style:italic}",{"title":1037,"searchDepth":1038,"depth":1038,"links":41553},[41554,41555,41556],{"id":29856,"depth":1041,"text":29857},{"id":29892,"depth":1041,"text":29893},{"id":2620,"depth":1041,"text":2621},"SolidJS signal primitives — createCollectionSignal, createQuerySignal, createSyncSignal — backed by noy-db change events, no DOM dependency.",{},{"title":267,"description":41557},"v3dxvTIM5NO4Sc_gwS6UnFJzg6TFTQSGcu7o5xIbtIQ",{"id":41562,"title":271,"access":12,"apiToc":929,"body":41563,"category":1042,"description":41677,"extension":1044,"meta":41678,"navigation":1042,"nest":3303,"path":272,"seo":41679,"sinceVersion":1042,"status":1049,"stem":273,"subtitle":1042,"tier":1050,"tocExternal":1042,"__hash__":41680},"docs\u002Ffamilies\u002Fin\u002Fsvelte.md",{"type":931,"value":41564,"toc":41672},[41565,41567,41582,41584,41609,41611,41656,41658,41669],[934,41566,271],{"id":271},[1061,41568,41569],{},[937,41570,41571,1368,41576,41579,41580,37572],{},[950,41572,41573],{},[941,41574,41575],{},"@noy-db\u002Fin-svelte",[941,41577,41578],{},"pnpm add @noy-db\u002Fhub @noy-db\u002Fin-svelte","\nSvelte stores for noy-db — no ",[941,41581,271],{},[1022,41583,29857],{"id":29856},[937,41585,41586,41587,41590,41591,2085,41593,41595,41596,5502,41599,41601,41602,41604,41605,41608],{},"Three store factories conforming to Svelte's standard store contract\n(",[941,41588,41589],{},"subscribe(fn) → unsubscribe","), re-implemented inline rather than\ndepending on ",[941,41592,271],{},[941,41594,37563],{}," gives a reactive\n",[941,41597,41598],{},"{ records, loading, error }",[941,41600,37566],{}," re-runs a query builder on\nevery change event; ",[941,41603,37569],{}," exposes the hub's last change event for\nstatus indicators. Works with Svelte 4's ",[941,41606,41607],{},"$store"," auto-subscription and\nSvelte 5 runes, and with any other consumer of the store contract (e.g.\nSvelteKit load functions).",[1022,41610,29893],{"id":29892},[2217,41612,41615],{"className":41613,"code":41614,"language":271,"meta":1037,"style":1037},"language-svelte shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","\u003Cscript>\n  import { collectionStore } from '@noy-db\u002Fin-svelte'\n  const invoices = collectionStore(vault, 'invoices')\n\u003C\u002Fscript>\n\n{#each $invoices.records as invoice}\n  {invoice.id}\n{\u002Feach}\n",[941,41616,41617,41622,41627,41632,41637,41641,41646,41651],{"__ignoreMap":1037},[2225,41618,41619],{"class":2227,"line":2228},[2225,41620,41621],{},"\u003Cscript>\n",[2225,41623,41624],{"class":2227,"line":1041},[2225,41625,41626],{},"  import { collectionStore } from '@noy-db\u002Fin-svelte'\n",[2225,41628,41629],{"class":2227,"line":1038},[2225,41630,41631],{},"  const invoices = collectionStore(vault, 'invoices')\n",[2225,41633,41634],{"class":2227,"line":2288},[2225,41635,41636],{},"\u003C\u002Fscript>\n",[2225,41638,41639],{"class":2227,"line":2304},[2225,41640,2285],{"emptyLinePlaceholder":2284},[2225,41642,41643],{"class":2227,"line":2356},[2225,41644,41645],{},"{#each $invoices.records as invoice}\n",[2225,41647,41648],{"class":2227,"line":2830},[2225,41649,41650],{},"  {invoice.id}\n",[2225,41652,41653],{"class":2227,"line":2861},[2225,41654,41655],{},"{\u002Feach}\n",[1022,41657,2621],{"id":2620},[1111,41659,41660,41664],{},[1114,41661,41662],{},[1007,41663,37002],{"href":2634},[1114,41665,41666,41668],{},[1007,41667,267],{"href":37537}," — the equivalent zero-dep signal pattern for SolidJS",[2645,41670,41671],{},"html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":1037,"searchDepth":1038,"depth":1038,"links":41673},[41674,41675,41676],{"id":29856,"depth":1041,"text":29857},{"id":29892,"depth":1041,"text":29893},{"id":2620,"depth":1041,"text":2621},"Zero-dependency Svelte stores — collectionStore, queryStore, syncStore — implementing the store contract inline for Svelte 4 and 5.",{},{"title":271,"description":41677},"Vyz6BPmQcvm8EjheLgz1FgTlm2Va4B6EQMdtfL1_YOc",{"id":41682,"title":275,"access":12,"apiToc":929,"body":41683,"category":1042,"description":41896,"extension":1044,"meta":41897,"navigation":1042,"nest":3303,"path":276,"seo":41898,"sinceVersion":1042,"status":1049,"stem":277,"subtitle":1042,"tier":1050,"tocExternal":1042,"__hash__":41899},"docs\u002Ffamilies\u002Fin\u002Ftanstack-query.md",{"type":931,"value":41684,"toc":41891},[41685,41687,41700,41702,41750,41752,41866,41868,41888],[934,41686,275],{"id":275},[1061,41688,41689],{},[937,41690,41691,1368,41696,41699],{},[950,41692,41693],{},[941,41694,41695],{},"@noy-db\u002Fin-tanstack-query",[941,41697,41698],{},"pnpm add @noy-db\u002Fhub @noy-db\u002Fin-tanstack-query","\nTanStack Query adapter for noy-db — framework-agnostic (React \u002F Vue \u002F Solid \u002F Svelte).",[1022,41701,29857],{"id":29856},[937,41703,41704,41705,1707,41708,2462,41711,41714,41715,41717,41718,41721,41722,1261,41725,41728,41729,41732,41733,41736,41737,41739,41740,41742,41743,41746,41747,41749],{},"Exports plain option factories, not hooks: ",[941,41706,41707],{},"collectionListOptions",[941,41709,41710],{},"collectionGetOptions",[941,41712,41713],{},"collectionQueryOptions"," each return\n",[941,41716,37742],{}," ready for any TanStack framework's ",[941,41719,41720],{},"useQuery()",";\n",[941,41723,41724],{},"collectionPutOptions",[941,41726,41727],{},"collectionDeleteOptions"," return the\n",[941,41730,41731],{},"useMutation()"," equivalent. ",[941,41734,41735],{},"collectionQueryKey(vault, collection, ...extra)","\nbuilds the canonical key. ",[941,41738,37746],{}," subscribes to a\n",[941,41741,103],{},"'s change stream and calls the query client's\n",[941,41744,41745],{},"invalidateQueries"," on the matching keys automatically — a drop-in\nreplacement for ad-hoc ",[941,41748,41745],{}," calls scattered through mutation\ncallbacks. Because these are factories rather than hooks, the same code\nworks unmodified across every TanStack framework binding.",[1022,41751,29893],{"id":29892},[2217,41753,41755],{"className":2219,"code":41754,"language":2221,"meta":1037,"style":1037},"import { useQuery } from '@tanstack\u002Freact-query'\nimport { collectionListOptions } from '@noy-db\u002Fin-tanstack-query'\n\nconst { data } = useQuery(\n  collectionListOptions('acme', 'invoices', () => vault.collection('invoices')),\n)\n",[941,41756,41757,41777,41796,41800,41817,41862],{"__ignoreMap":1037},[2225,41758,41759,41761,41763,41766,41768,41770,41772,41775],{"class":2227,"line":2228},[2225,41760,2232],{"class":2231},[2225,41762,2236],{"class":2235},[2225,41764,41765],{"class":2239}," useQuery",[2225,41767,2243],{"class":2235},[2225,41769,2246],{"class":2231},[2225,41771,2249],{"class":2235},[2225,41773,41774],{"class":2252},"@tanstack\u002Freact-query",[2225,41776,2256],{"class":2235},[2225,41778,41779,41781,41783,41786,41788,41790,41792,41794],{"class":2227,"line":1041},[2225,41780,2232],{"class":2231},[2225,41782,2236],{"class":2235},[2225,41784,41785],{"class":2239}," collectionListOptions",[2225,41787,2243],{"class":2235},[2225,41789,2246],{"class":2231},[2225,41791,2249],{"class":2235},[2225,41793,41695],{"class":2252},[2225,41795,2256],{"class":2235},[2225,41797,41798],{"class":2227,"line":1038},[2225,41799,2285],{"emptyLinePlaceholder":2284},[2225,41801,41802,41804,41806,41809,41811,41813,41815],{"class":2227,"line":2288},[2225,41803,2775],{"class":2774},[2225,41805,2236],{"class":2235},[2225,41807,41808],{"class":2239}," data ",[2225,41810,2359],{"class":2235},[2225,41812,6346],{"class":2235},[2225,41814,41765],{"class":2291},[2225,41816,11683],{"class":2239},[2225,41818,41819,41822,41824,41826,41828,41830,41832,41834,41836,41838,41840,41842,41844,41846,41848,41850,41852,41854,41856,41858,41860],{"class":2227,"line":2304},[2225,41820,41821],{"class":2291},"  collectionListOptions",[2225,41823,2789],{"class":2239},[2225,41825,2805],{"class":2235},[2225,41827,6450],{"class":2252},[2225,41829,2805],{"class":2235},[2225,41831,2298],{"class":2235},[2225,41833,2249],{"class":2235},[2225,41835,6487],{"class":2252},[2225,41837,2805],{"class":2235},[2225,41839,2298],{"class":2235},[2225,41841,13130],{"class":2235},[2225,41843,8116],{"class":2774},[2225,41845,6466],{"class":2239},[2225,41847,1520],{"class":2235},[2225,41849,6471],{"class":2291},[2225,41851,2789],{"class":2239},[2225,41853,2805],{"class":2235},[2225,41855,6487],{"class":2252},[2225,41857,2805],{"class":2235},[2225,41859,8454],{"class":2239},[2225,41861,1707],{"class":2235},[2225,41863,41864],{"class":2227,"line":2356},[2225,41865,2362],{"class":2239},[1022,41867,2621],{"id":2620},[1111,41869,41870,41874,41879],{},[1114,41871,41872],{},[1007,41873,37002],{"href":2634},[1114,41875,41876,41878],{},[1007,41877,279],{"href":37754}," — pairs with this for table state",[1114,41880,41881,964,41883,1261,41885,41887],{},[1007,41882,255],{"href":37511},[941,41884,37493],{},[941,41886,37524],{}," cover the same case with hooks instead",[2645,41889,41890],{},"html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":1037,"searchDepth":1038,"depth":1038,"links":41892},[41893,41894,41895],{"id":29856,"depth":1041,"text":29857},{"id":29892,"depth":1041,"text":29893},{"id":2620,"depth":1041,"text":2621},"Framework-free TanStack Query adapter — {queryKey, queryFn}\u002FmutationFn option factories plus bindInvalidation() for auto-invalidation on change events.",{},{"title":275,"description":41896},"9QYWSDcFcQ_WwzsJb_zJEJGam7y677h_l7MC-cMmmUc",{"id":41901,"title":279,"access":12,"apiToc":929,"body":41902,"category":1042,"description":42061,"extension":1044,"meta":42062,"navigation":1042,"nest":3303,"path":280,"seo":42063,"sinceVersion":1042,"status":1049,"stem":281,"subtitle":1042,"tier":1050,"tocExternal":1042,"__hash__":42064},"docs\u002Ffamilies\u002Fin\u002Ftanstack-table.md",{"type":931,"value":41903,"toc":42056},[41904,41906,41919,41921,41967,41969,42041,42043,42054],[934,41905,279],{"id":279},[1061,41907,41908],{},[937,41909,41910,1368,41915,41918],{},[950,41911,41912],{},[941,41913,41914],{},"@noy-db\u002Fin-tanstack-table",[941,41916,41917],{},"pnpm add @noy-db\u002Fhub @noy-db\u002Fin-tanstack-table","\nTanStack Table bridge for noy-db — pure adapter, no React \u002F Vue \u002F Solid coupling.",[1022,41920,29857],{"id":29856},[937,41922,41923,41926,41927,41930,41931,41933,41934,41937,41938,41941,41942,41945,41946,41948,41949,41952,41953,38633,41956,41959,41960,1707,41963,41966],{},[941,41924,41925],{},"buildQueryFromTableState(query, state)"," applies a Table ",[941,41928,41929],{},"TableState","\n(sorting, column filters, pagination) onto a noy-db ",[941,41932,24785],{}," chain:\nfilters become ",[941,41935,41936],{},".where(id, '==' | 'in', value)",", sorts become repeated\n",[941,41939,41940],{},".orderBy()",", pagination becomes ",[941,41943,41944],{},".offset().limit()"," — covering the common\ncase; consumers needing richer operators (",[941,41947,26921],{},", ranges) extend it with a\ncustom filter mapper. ",[941,41950,41951],{},"tableStateFromQuery(query)"," does the reverse, for\nround-tripping Table state through a URL or ",[941,41954,41955],{},"localStorage",[941,41957,41958],{},"resetTableState(pageSize?)"," returns an empty, first-page state for\n\"clear all\" buttons. Pair it with ",[941,41961,41962],{},"@tanstack\u002Freact-table",[941,41964,41965],{},"@tanstack\u002Fvue-table",", etc. to wire it into a component.",[1022,41968,29893],{"id":29892},[2217,41970,41972],{"className":2219,"code":41971,"language":2221,"meta":1037,"style":1037},"import { buildQueryFromTableState } from '@noy-db\u002Fin-tanstack-table'\n\nconst rows = buildQueryFromTableState(vault.collection('invoices').query(), tableState).toArray()\n",[941,41973,41974,41993,41997],{"__ignoreMap":1037},[2225,41975,41976,41978,41980,41983,41985,41987,41989,41991],{"class":2227,"line":2228},[2225,41977,2232],{"class":2231},[2225,41979,2236],{"class":2235},[2225,41981,41982],{"class":2239}," buildQueryFromTableState",[2225,41984,2243],{"class":2235},[2225,41986,2246],{"class":2231},[2225,41988,2249],{"class":2235},[2225,41990,41914],{"class":2252},[2225,41992,2256],{"class":2235},[2225,41994,41995],{"class":2227,"line":1041},[2225,41996,2285],{"emptyLinePlaceholder":2284},[2225,41998,41999,42001,42004,42006,42008,42010,42012,42014,42016,42018,42020,42022,42024,42026,42028,42030,42032,42035,42037,42039],{"class":2227,"line":1038},[2225,42000,2775],{"class":2774},[2225,42002,42003],{"class":2239}," rows ",[2225,42005,2781],{"class":2235},[2225,42007,41982],{"class":2291},[2225,42009,30266],{"class":2239},[2225,42011,1520],{"class":2235},[2225,42013,6471],{"class":2291},[2225,42015,2789],{"class":2239},[2225,42017,2805],{"class":2235},[2225,42019,6487],{"class":2252},[2225,42021,2805],{"class":2235},[2225,42023,1381],{"class":2239},[2225,42025,1520],{"class":2235},[2225,42027,5286],{"class":2291},[2225,42029,4228],{"class":2239},[2225,42031,2298],{"class":2235},[2225,42033,42034],{"class":2239}," tableState)",[2225,42036,1520],{"class":2235},[2225,42038,3230],{"class":2291},[2225,42040,8146],{"class":2239},[1022,42042,2621],{"id":2620},[1111,42044,42045,42049],{},[1114,42046,42047],{},[1007,42048,37002],{"href":2634},[1114,42050,42051,42053],{},[1007,42052,275],{"href":37736}," — pairs with this for data fetching\u002Fcaching",[2645,42055,41890],{},{"title":1037,"searchDepth":1038,"depth":1038,"links":42057},[42058,42059,42060],{"id":29856,"depth":1041,"text":29857},{"id":29892,"depth":1041,"text":29893},{"id":2620,"depth":1041,"text":2621},"TanStack Table bridge — buildQueryFromTableState() maps Table sorting\u002Ffiltering\u002Fpagination onto the query DSL; tableStateFromQuery() round-trips it back.",{},{"title":279,"description":42061},"nLxINXhXC_o7uokV-N-pOLllGqpwf3Y2ldeQstRhcT0",{"id":42066,"title":283,"access":12,"apiToc":929,"body":42067,"category":1042,"description":42664,"extension":1044,"meta":42665,"navigation":1042,"nest":3303,"path":284,"seo":42666,"sinceVersion":1042,"status":1049,"stem":285,"subtitle":1042,"tier":1050,"tocExternal":1042,"__hash__":42667},"docs\u002Ffamilies\u002Fin\u002Fvue.md",{"type":931,"value":42068,"toc":42659},[42069,42071,42084,42086,42098,42210,42220,42222,42436,42467,42638,42640,42656],[934,42070,283],{"id":283},[1061,42072,42073],{},[937,42074,42075,1368,42080,42083],{},[950,42076,42077],{},[941,42078,42079],{},"@noy-db\u002Fin-vue",[941,42081,42082],{},"pnpm add @noy-db\u002Fhub @noy-db\u002Fto-browser-idb @noy-db\u002Fin-vue","\nVue 3 composables for noy-db — works outside Nuxt too.",[1022,42085,29857],{"id":29856},[937,42087,42088,42089,42091,42092,1324,42095,42097],{},"Installs as a Vue plugin (",[941,42090,37503],{},") that provides an ",[950,42093,42094],{},"already-constructed",[941,42096,62],{},"\ninstance to every component under its scope — the plugin takes the instance, not the ingredients\nfor one, so unlock and store choice stay in your own bootstrap code.",[1338,42099,42100,42112],{},[1341,42101,42102],{},[1344,42103,42104,42106,42109],{},[1347,42105,37921],{},[1347,42107,42108],{},"signature",[1347,42110,42111],{},"returns",[1357,42113,42114,42128,42146,42162,42177,42195],{},[1344,42115,42116,42121,42123],{},[1362,42117,42118],{},[941,42119,42120],{},"useNoydb()",[1362,42122,3610],{},[1362,42124,42125,42126],{},"the injected ",[941,42127,62],{},[1344,42129,42130,42135,42140],{},[1362,42131,42132],{},[941,42133,42134],{},"useCollection\u003CT>(db, compartmentName, collectionName)",[1362,42136,42137,42138],{},"explicit ",[941,42139,6590],{},[1362,42141,42142,42145],{},[941,42143,42144],{},"{ data, loading, error, refresh }",", all refs",[1344,42147,42148,42153,42157],{},[1362,42149,42150],{},[941,42151,42152],{},"useSync(db, compartmentName)",[1362,42154,42137,42155],{},[941,42156,6590],{},[1362,42158,42159],{},[941,42160,42161],{},"{ status, syncing, push, pull, sync }",[1344,42163,42164,42169,42174],{},[1362,42165,42166],{},[941,42167,42168],{},"useLiveQuery\u003CR>(live)",[1362,42170,5983,42171],{},[941,42172,42173],{},"LiveQuery",[1362,42175,42176],{},"reactive results",[1344,42178,42179,42184,42186],{},[1362,42180,42181],{},[941,42182,42183],{},"useBlobURL\u003CT>(collection, idGetter, options?)",[1362,42185,3610],{},[1362,42187,42188,42191,42192],{},[941,42189,42190],{},"Ref\u003Cstring | null>",", auto-revoking the ",[941,42193,42194],{},"ObjectURL",[1344,42196,42197,42205,42207],{},[1362,42198,42199,1261,42202],{},[941,42200,42201],{},"useMigrationState(vaultName?)",[941,42203,42204],{},"(db, vaultName?)",[1362,42206,3610],{},[1362,42208,42209],{},"reactive schema-cutover state",[937,42211,42212,42215,42216,42219],{},[941,42213,42214],{},"NoydbKey"," is exported for ",[941,42217,42218],{},"inject(NoydbKey)"," where a composable does not fit.",[1022,42221,29893],{"id":29892},[2217,42223,42225],{"className":2219,"code":42224,"language":2221,"meta":1037,"style":1037},"\u002F\u002F main.ts\nimport { createApp } from 'vue'\nimport { NoydbPlugin } from '@noy-db\u002Fin-vue'\nimport { createNoydb } from '@noy-db\u002Fhub'\nimport { toBrowserIdb } from '@noy-db\u002Fto-browser-idb'\n\nconst db = await createNoydb({\n  store: toBrowserIdb({ obfuscate: true }),\n  user: 'alice',\n  secret: 'a-strong-secret',\n})\n\ncreateApp(App).use(NoydbPlugin, { instance: db }).mount('#app')\n",[941,42226,42227,42232,42251,42270,42288,42306,42310,42326,42351,42365,42379,42385,42389],{"__ignoreMap":1037},[2225,42228,42229],{"class":2227,"line":2228},[2225,42230,42231],{"class":2763},"\u002F\u002F main.ts\n",[2225,42233,42234,42236,42238,42241,42243,42245,42247,42249],{"class":2227,"line":1041},[2225,42235,2232],{"class":2231},[2225,42237,2236],{"class":2235},[2225,42239,42240],{"class":2239}," createApp",[2225,42242,2243],{"class":2235},[2225,42244,2246],{"class":2231},[2225,42246,2249],{"class":2235},[2225,42248,283],{"class":2252},[2225,42250,2256],{"class":2235},[2225,42252,42253,42255,42257,42260,42262,42264,42266,42268],{"class":2227,"line":1038},[2225,42254,2232],{"class":2231},[2225,42256,2236],{"class":2235},[2225,42258,42259],{"class":2239}," NoydbPlugin",[2225,42261,2243],{"class":2235},[2225,42263,2246],{"class":2231},[2225,42265,2249],{"class":2235},[2225,42267,42079],{"class":2252},[2225,42269,2256],{"class":2235},[2225,42271,42272,42274,42276,42278,42280,42282,42284,42286],{"class":2227,"line":2288},[2225,42273,2232],{"class":2231},[2225,42275,2236],{"class":2235},[2225,42277,2744],{"class":2239},[2225,42279,2243],{"class":2235},[2225,42281,2246],{"class":2231},[2225,42283,2249],{"class":2235},[2225,42285,1086],{"class":2252},[2225,42287,2256],{"class":2235},[2225,42289,42290,42292,42294,42296,42298,42300,42302,42304],{"class":2227,"line":2304},[2225,42291,2232],{"class":2231},[2225,42293,2236],{"class":2235},[2225,42295,34763],{"class":2239},[2225,42297,2243],{"class":2235},[2225,42299,2246],{"class":2231},[2225,42301,2249],{"class":2235},[2225,42303,9573],{"class":2252},[2225,42305,2256],{"class":2235},[2225,42307,42308],{"class":2227,"line":2356},[2225,42309,2285],{"emptyLinePlaceholder":2284},[2225,42311,42312,42314,42316,42318,42320,42322,42324],{"class":2227,"line":2830},[2225,42313,2775],{"class":2774},[2225,42315,2778],{"class":2239},[2225,42317,2781],{"class":2235},[2225,42319,2784],{"class":2231},[2225,42321,2744],{"class":2291},[2225,42323,2789],{"class":2239},[2225,42325,4200],{"class":2235},[2225,42327,42328,42330,42332,42334,42336,42338,42341,42343,42345,42347,42349],{"class":2227,"line":2861},[2225,42329,15608],{"class":2307},[2225,42331,2311],{"class":2235},[2225,42333,34763],{"class":2291},[2225,42335,2789],{"class":2239},[2225,42337,2792],{"class":2235},[2225,42339,42340],{"class":2307}," obfuscate",[2225,42342,2311],{"class":2235},[2225,42344,2322],{"class":2321},[2225,42346,2243],{"class":2235},[2225,42348,1381],{"class":2239},[2225,42350,1707],{"class":2235},[2225,42352,42353,42355,42357,42359,42361,42363],{"class":2227,"line":4246},[2225,42354,4205],{"class":2307},[2225,42356,2311],{"class":2235},[2225,42358,2249],{"class":2235},[2225,42360,6408],{"class":2252},[2225,42362,2805],{"class":2235},[2225,42364,1707],{"class":2235},[2225,42366,42367,42369,42371,42373,42375,42377],{"class":2227,"line":4253},[2225,42368,10423],{"class":2307},[2225,42370,2311],{"class":2235},[2225,42372,2249],{"class":2235},[2225,42374,6421],{"class":2252},[2225,42376,2805],{"class":2235},[2225,42378,1707],{"class":2235},[2225,42380,42381,42383],{"class":2227,"line":4259},[2225,42382,2359],{"class":2235},[2225,42384,2362],{"class":2239},[2225,42386,42387],{"class":2227,"line":6548},[2225,42388,2285],{"emptyLinePlaceholder":2284},[2225,42390,42391,42394,42397,42399,42402,42405,42407,42409,42412,42414,42416,42418,42420,42422,42425,42427,42429,42432,42434],{"class":2227,"line":6576},[2225,42392,42393],{"class":2291},"createApp",[2225,42395,42396],{"class":2239},"(App)",[2225,42398,1520],{"class":2235},[2225,42400,42401],{"class":2291},"use",[2225,42403,42404],{"class":2239},"(NoydbPlugin",[2225,42406,2298],{"class":2235},[2225,42408,2236],{"class":2235},[2225,42410,42411],{"class":2307}," instance",[2225,42413,2311],{"class":2235},[2225,42415,2778],{"class":2239},[2225,42417,2359],{"class":2235},[2225,42419,1381],{"class":2239},[2225,42421,1520],{"class":2235},[2225,42423,42424],{"class":2291},"mount",[2225,42426,2789],{"class":2239},[2225,42428,2805],{"class":2235},[2225,42430,42431],{"class":2252},"#app",[2225,42433,2805],{"class":2235},[2225,42435,2362],{"class":2239},[7646,42437,42438],{"color":10789,"icon":39006},[937,42439,42440,2462,42449,42451,42452,42456,42457,42459,42460,42462,42463,42466],{},[950,42441,42442,7356,42445,42448],{},[941,42443,42444],{},"NoydbPluginOptions",[941,42446,42447],{},"{ instance }"," and nothing else",[941,42450,12780],{}," takes ",[950,42453,42454],{},[941,42455,16225],{},",\nnot ",[941,42458,10749],{},". Older material — including this package's own README before ",[941,42461,39039],{}," — showed\n",[941,42464,42465],{},".use(NoydbPlugin, { adapter, userId })","; none of those keys exists. An unknown option is dropped\nsilently rather than rejected, so the mistake surfaces later as an unset identity.",[2217,42468,42471],{"className":42469,"code":42470,"language":283,"meta":1037,"style":1037},"language-vue shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","\u003Cscript setup lang=\"ts\">\nimport { useCollection, useNoydb } from '@noy-db\u002Fin-vue'\nconst { data: items } = useCollection\u003CInvoice>(useNoydb(), 'C101', 'invoices')\n\u003C\u002Fscript>\n\n\u003Ctemplate>\n  \u003Cli v-for=\"inv in items\" :key=\"inv.id\">{{ inv.customer }}: ฿{{ inv.amount }}\u003C\u002Fli>\n\u003C\u002Ftemplate>\n",[941,42472,42473,42496,42519,42568,42576,42580,42589,42630],{"__ignoreMap":1037},[2225,42474,42475,42477,42480,42483,42486,42488,42490,42492,42494],{"class":2227,"line":2228},[2225,42476,6474],{"class":2235},[2225,42478,42479],{"class":2307},"script",[2225,42481,42482],{"class":2774}," setup",[2225,42484,42485],{"class":2774}," lang",[2225,42487,2781],{"class":2235},[2225,42489,6620],{"class":2235},[2225,42491,2221],{"class":2252},[2225,42493,6620],{"class":2235},[2225,42495,7093],{"class":2235},[2225,42497,42498,42500,42502,42504,42506,42509,42511,42513,42515,42517],{"class":2227,"line":1041},[2225,42499,2232],{"class":2231},[2225,42501,2236],{"class":2235},[2225,42503,38802],{"class":2239},[2225,42505,2298],{"class":2235},[2225,42507,42508],{"class":2239}," useNoydb",[2225,42510,2243],{"class":2235},[2225,42512,2246],{"class":2231},[2225,42514,2249],{"class":2235},[2225,42516,42079],{"class":2252},[2225,42518,2256],{"class":2235},[2225,42520,42521,42523,42525,42527,42529,42532,42534,42536,42538,42540,42542,42544,42546,42548,42550,42552,42554,42556,42558,42560,42562,42564,42566],{"class":2227,"line":1038},[2225,42522,2775],{"class":2774},[2225,42524,2236],{"class":2235},[2225,42526,7330],{"class":2307},[2225,42528,2311],{"class":2235},[2225,42530,42531],{"class":2239}," items ",[2225,42533,2359],{"class":2235},[2225,42535,6346],{"class":2235},[2225,42537,38802],{"class":2291},[2225,42539,6474],{"class":2235},[2225,42541,6477],{"class":6342},[2225,42543,6480],{"class":2235},[2225,42545,2789],{"class":2239},[2225,42547,37490],{"class":2291},[2225,42549,4228],{"class":2239},[2225,42551,2298],{"class":2235},[2225,42553,2249],{"class":2235},[2225,42555,39127],{"class":2252},[2225,42557,2805],{"class":2235},[2225,42559,2298],{"class":2235},[2225,42561,2249],{"class":2235},[2225,42563,6487],{"class":2252},[2225,42565,2805],{"class":2235},[2225,42567,2362],{"class":2239},[2225,42569,42570,42572,42574],{"class":2227,"line":2288},[2225,42571,40439],{"class":2235},[2225,42573,42479],{"class":2307},[2225,42575,7093],{"class":2235},[2225,42577,42578],{"class":2227,"line":2304},[2225,42579,2285],{"emptyLinePlaceholder":2284},[2225,42581,42582,42584,42587],{"class":2227,"line":2356},[2225,42583,6474],{"class":2235},[2225,42585,42586],{"class":2307},"template",[2225,42588,7093],{"class":2235},[2225,42590,42591,42593,42595,42598,42600,42602,42605,42607,42610,42612,42614,42617,42619,42621,42624,42626,42628],{"class":2227,"line":2830},[2225,42592,41483],{"class":2235},[2225,42594,1114],{"class":2307},[2225,42596,42597],{"class":2774}," v-for",[2225,42599,2781],{"class":2235},[2225,42601,6620],{"class":2235},[2225,42603,42604],{"class":2252},"inv in items",[2225,42606,6620],{"class":2235},[2225,42608,42609],{"class":2774}," :key",[2225,42611,2781],{"class":2235},[2225,42613,6620],{"class":2235},[2225,42615,42616],{"class":2252},"inv.id",[2225,42618,6620],{"class":2235},[2225,42620,6480],{"class":2235},[2225,42622,42623],{"class":2239},"{{ inv.customer }}: ฿{{ inv.amount }}",[2225,42625,40439],{"class":2235},[2225,42627,1114],{"class":2307},[2225,42629,7093],{"class":2235},[2225,42631,42632,42634,42636],{"class":2227,"line":2861},[2225,42633,40439],{"class":2235},[2225,42635,42586],{"class":2307},[2225,42637,7093],{"class":2235},[1022,42639,2621],{"id":2620},[1111,42641,42642,42646,42651],{},[1114,42643,42644],{},[1007,42645,37002],{"href":2634},[1114,42647,42648,42650],{},[1007,42649,247],{"href":37685}," — store-shaped wrapper for the same composables",[1114,42652,42653,42655],{},[1007,42654,243],{"href":37579}," — auto-imports these composables via a Nuxt module",[2645,42657,42658],{},"html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .sfNiH, html code.shiki .sfNiH{--shiki-light:#FF5370;--shiki-default:#FF9CAC;--shiki-dark:#FF9CAC}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}",{"title":1037,"searchDepth":1038,"depth":1038,"links":42660},[42661,42662,42663],{"id":29856,"depth":1041,"text":29857},{"id":29892,"depth":1041,"text":29893},{"id":2620,"depth":1041,"text":2621},"Vue 3 composables — useNoydb, useCollection, useSync, useBlobURL — plus a NoydbPlugin and WebAuthn biometric unlock helpers.",{},{"title":283,"description":42664},"MDGUFpp3t88DadhM-cq3EHDgPpxpedHmTQP-IP_g6IM",{"id":42669,"title":287,"access":12,"apiToc":929,"body":42670,"category":1042,"description":42922,"extension":1044,"meta":42923,"navigation":1042,"nest":3303,"path":288,"seo":42924,"sinceVersion":1042,"status":1049,"stem":289,"subtitle":1042,"tier":1050,"tocExternal":1042,"__hash__":42925},"docs\u002Ffamilies\u002Fin\u002Fyjs.md",{"type":931,"value":42671,"toc":42917},[42672,42674,42690,42692,42721,42723,42899,42901,42914],[934,42673,287],{"id":287},[1061,42675,42676],{},[937,42677,42678,1368,42683,42686,42687,42689],{},[950,42679,42680],{},[941,42681,42682],{},"@noy-db\u002Fin-yjs",[941,42684,42685],{},"pnpm add @noy-db\u002Fhub @noy-db\u002Fin-yjs yjs","\nYjs ",[941,42688,37813],{}," interop for noy-db — collaborative rich-text editing, encrypted at rest.",[1022,42691,29857],{"id":29856},[937,42693,42694,42697,42698,968,42701,33507,42704,42707,42708,1261,42711,42714,42715,42717,42718,42720],{},[941,42695,42696],{},"yjsCollection(compartment, name, { yFields })"," wraps a collection whose\nnamed fields hold Yjs CRDT state, described via ",[941,42699,42700],{},"yText()",[941,42702,42703],{},"yMap()",[941,42705,42706],{},"yArray()"," field descriptors. ",[941,42709,42710],{},"getYDoc(id)",[941,42712,42713],{},"putYDoc(id, doc)"," round-trip\na ",[941,42716,37813],{}," through encryption — the CRDT update log is encrypted before it\nreaches any ",[941,42719,1779],{}," storage backend, so collaborative editing and offline\nmerging both happen without the store ever seeing plaintext.",[1022,42722,29893],{"id":29892},[2217,42724,42726],{"className":2219,"code":42725,"language":2221,"meta":1037,"style":1037},"import { yjsCollection, yText } from '@noy-db\u002Fin-yjs'\n\nconst notes = yjsCollection(comp, 'notes', { yFields: { body: yText() } })\nconst doc = await notes.getYDoc('note-1')\ndoc.getText('body').insert(0, 'Hello world')\nawait notes.putYDoc('note-1', doc)\n",[941,42727,42728,42752,42756,42804,42834,42875],{"__ignoreMap":1037},[2225,42729,42730,42732,42734,42737,42739,42742,42744,42746,42748,42750],{"class":2227,"line":2228},[2225,42731,2232],{"class":2231},[2225,42733,2236],{"class":2235},[2225,42735,42736],{"class":2239}," yjsCollection",[2225,42738,2298],{"class":2235},[2225,42740,42741],{"class":2239}," yText",[2225,42743,2243],{"class":2235},[2225,42745,2246],{"class":2231},[2225,42747,2249],{"class":2235},[2225,42749,42682],{"class":2252},[2225,42751,2256],{"class":2235},[2225,42753,42754],{"class":2227,"line":1041},[2225,42755,2285],{"emptyLinePlaceholder":2284},[2225,42757,42758,42760,42763,42765,42767,42770,42772,42774,42776,42778,42780,42782,42785,42787,42789,42792,42794,42796,42798,42800,42802],{"class":2227,"line":1038},[2225,42759,2775],{"class":2774},[2225,42761,42762],{"class":2239}," notes ",[2225,42764,2781],{"class":2235},[2225,42766,42736],{"class":2291},[2225,42768,42769],{"class":2239},"(comp",[2225,42771,2298],{"class":2235},[2225,42773,2249],{"class":2235},[2225,42775,2854],{"class":2252},[2225,42777,2805],{"class":2235},[2225,42779,2298],{"class":2235},[2225,42781,2236],{"class":2235},[2225,42783,42784],{"class":2307}," yFields",[2225,42786,2311],{"class":2235},[2225,42788,2236],{"class":2235},[2225,42790,42791],{"class":2307}," body",[2225,42793,2311],{"class":2235},[2225,42795,42741],{"class":2291},[2225,42797,19132],{"class":2239},[2225,42799,2359],{"class":2235},[2225,42801,2243],{"class":2235},[2225,42803,2362],{"class":2239},[2225,42805,42806,42808,42811,42813,42815,42818,42820,42823,42825,42827,42830,42832],{"class":2227,"line":2288},[2225,42807,2775],{"class":2774},[2225,42809,42810],{"class":2239}," doc ",[2225,42812,2781],{"class":2235},[2225,42814,2784],{"class":2231},[2225,42816,42817],{"class":2239}," notes",[2225,42819,1520],{"class":2235},[2225,42821,42822],{"class":2291},"getYDoc",[2225,42824,2789],{"class":2239},[2225,42826,2805],{"class":2235},[2225,42828,42829],{"class":2252},"note-1",[2225,42831,2805],{"class":2235},[2225,42833,2362],{"class":2239},[2225,42835,42836,42839,42841,42844,42846,42848,42851,42853,42855,42857,42860,42862,42864,42866,42868,42871,42873],{"class":2227,"line":2304},[2225,42837,42838],{"class":2239},"doc",[2225,42840,1520],{"class":2235},[2225,42842,42843],{"class":2291},"getText",[2225,42845,2789],{"class":2239},[2225,42847,2805],{"class":2235},[2225,42849,42850],{"class":2252},"body",[2225,42852,2805],{"class":2235},[2225,42854,1381],{"class":2239},[2225,42856,1520],{"class":2235},[2225,42858,42859],{"class":2291},"insert",[2225,42861,2789],{"class":2239},[2225,42863,5402],{"class":6527},[2225,42865,2298],{"class":2235},[2225,42867,2249],{"class":2235},[2225,42869,42870],{"class":2252},"Hello world",[2225,42872,2805],{"class":2235},[2225,42874,2362],{"class":2239},[2225,42876,42877,42879,42881,42883,42886,42888,42890,42892,42894,42896],{"class":2227,"line":2356},[2225,42878,6500],{"class":2231},[2225,42880,42817],{"class":2239},[2225,42882,1520],{"class":2235},[2225,42884,42885],{"class":2291},"putYDoc",[2225,42887,2789],{"class":2239},[2225,42889,2805],{"class":2235},[2225,42891,42829],{"class":2252},[2225,42893,2805],{"class":2235},[2225,42895,2298],{"class":2235},[2225,42897,42898],{"class":2239}," doc)\n",[1022,42900,2621],{"id":2620},[1111,42902,42903,42907],{},[1114,42904,42905],{},[1007,42906,37002],{"href":2634},[1114,42908,42909,1368,42911,42913],{},[1007,42910,223],{"href":37836},[1007,42912,263],{"href":37007}," — the other protocol\u002Fruntime bindings in the family",[2645,42915,42916],{},"html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":1037,"searchDepth":1038,"depth":1038,"links":42918},[42919,42920,42921],{"id":29856,"depth":1041,"text":29857},{"id":29892,"depth":1041,"text":29893},{"id":2620,"depth":1041,"text":2621},"Yjs Y.Doc interop — yjsCollection() stores CRDT state encrypted at rest for collaborative rich-text fields; the storage backend never sees plaintext.",{},{"title":287,"description":42922},"LI4mFPRkY1u8ie093diap8pINEGzw2ugwJ3yQae9KrQ",{"id":42927,"title":291,"access":12,"apiToc":929,"body":42928,"category":1042,"description":43125,"extension":1044,"meta":43126,"navigation":1042,"nest":3303,"path":292,"seo":43127,"sinceVersion":1042,"status":1049,"stem":293,"subtitle":1042,"tier":1050,"tocExternal":1042,"__hash__":43128},"docs\u002Ffamilies\u002Fin\u002Fzustand.md",{"type":931,"value":42929,"toc":43120},[42930,42932,42945,42947,42985,42987,43099,43101,43117],[934,42931,291],{"id":291},[1061,42933,42934],{},[937,42935,42936,1368,42941,42944],{},[950,42937,42938],{},[941,42939,42940],{},"@noy-db\u002Fin-zustand",[941,42942,42943],{},"pnpm add @noy-db\u002Fhub @noy-db\u002Fin-zustand zustand","\nZustand adapter for noy-db.",[1022,42946,29857],{"id":29856},[937,42948,42949,42952,42953,42956,42957,42959,42960,42962,42963,968,42965,2462,42967,1261,42969,2601,42971,42974,42975,42978,42979,42981,42982,42984],{},[941,42950,42951],{},"createNoydbStore(getCollection)"," produces a Zustand\n",[941,42954,42955],{},"StateCreator\u003CNoydbZustandSlice\u003CT>>"," backed by a ",[941,42958,3121],{},". The slice\nexposes ",[941,42961,9136],{}," (keyed by id), ",[941,42964,40421],{},[941,42966,12123],{},[941,42968,3161],{},[941,42970,39213],{},[941,42972,42973],{},"refresh"," methods; reads hydrate on first access and auto-refresh on every\nchange event from the collection, while writes go through the collection so\nthey're encrypted and replicated by the sync engine. ",[941,42976,42977],{},"getCollection"," is\ncalled lazily, so the store can be wired while the ",[941,42980,62],{}," instance is still\nopening asynchronously — the first ",[941,42983,39216],{}," waits for it to resolve.",[1022,42986,29893],{"id":29892},[2217,42988,42990],{"className":2219,"code":42989,"language":2221,"meta":1037,"style":1037},"import { create } from 'zustand'\nimport { createNoydbStore } from '@noy-db\u002Fin-zustand'\n\nconst useInvoices = create(\n  createNoydbStore(() => db.vault('acme').collection\u003CInvoice>('invoices')),\n)\n",[941,42991,42992,43011,43030,43034,43046,43095],{"__ignoreMap":1037},[2225,42993,42994,42996,42998,43001,43003,43005,43007,43009],{"class":2227,"line":2228},[2225,42995,2232],{"class":2231},[2225,42997,2236],{"class":2235},[2225,42999,43000],{"class":2239}," create",[2225,43002,2243],{"class":2235},[2225,43004,2246],{"class":2231},[2225,43006,2249],{"class":2235},[2225,43008,291],{"class":2252},[2225,43010,2256],{"class":2235},[2225,43012,43013,43015,43017,43020,43022,43024,43026,43028],{"class":2227,"line":1041},[2225,43014,2232],{"class":2231},[2225,43016,2236],{"class":2235},[2225,43018,43019],{"class":2239}," createNoydbStore",[2225,43021,2243],{"class":2235},[2225,43023,2246],{"class":2231},[2225,43025,2249],{"class":2235},[2225,43027,42940],{"class":2252},[2225,43029,2256],{"class":2235},[2225,43031,43032],{"class":2227,"line":1038},[2225,43033,2285],{"emptyLinePlaceholder":2284},[2225,43035,43036,43038,43040,43042,43044],{"class":2227,"line":2288},[2225,43037,2775],{"class":2774},[2225,43039,39094],{"class":2239},[2225,43041,2781],{"class":2235},[2225,43043,43000],{"class":2291},[2225,43045,11683],{"class":2239},[2225,43047,43048,43051,43053,43055,43057,43059,43061,43063,43065,43067,43069,43071,43073,43075,43077,43079,43081,43083,43085,43087,43089,43091,43093],{"class":2227,"line":2304},[2225,43049,43050],{"class":2291},"  createNoydbStore",[2225,43052,2789],{"class":2239},[2225,43054,4228],{"class":2235},[2225,43056,8116],{"class":2774},[2225,43058,2842],{"class":2239},[2225,43060,1520],{"class":2235},[2225,43062,2694],{"class":2291},[2225,43064,2789],{"class":2239},[2225,43066,2805],{"class":2235},[2225,43068,6450],{"class":2252},[2225,43070,2805],{"class":2235},[2225,43072,1381],{"class":2239},[2225,43074,1520],{"class":2235},[2225,43076,6471],{"class":2291},[2225,43078,6474],{"class":2235},[2225,43080,6477],{"class":6342},[2225,43082,6480],{"class":2235},[2225,43084,2789],{"class":2239},[2225,43086,2805],{"class":2235},[2225,43088,6487],{"class":2252},[2225,43090,2805],{"class":2235},[2225,43092,8454],{"class":2239},[2225,43094,1707],{"class":2235},[2225,43096,43097],{"class":2227,"line":2356},[2225,43098,2362],{"class":2239},[1022,43100,2621],{"id":2620},[1111,43102,43103,43107,43112],{},[1114,43104,43105],{},[1007,43106,37002],{"href":2634},[1114,43108,43109,43111],{},[1007,43110,255],{"href":37511}," — Zustand is commonly paired with React",[1114,43113,43114,43116],{},[1007,43115,247],{"href":37685}," — the equivalent store-factory pattern for Vue\u002FPinia",[2645,43118,43119],{},"html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":1037,"searchDepth":1038,"depth":1038,"links":43121},[43122,43123,43124],{"id":29856,"depth":1041,"text":29857},{"id":29892,"depth":1041,"text":29893},{"id":2620,"depth":1041,"text":2621},"Zustand adapter — createNoydbStore() mirrors defineNoydbStore for Zustand, auto-hydrating and auto-refreshing a state slice from a noy-db Collection.",{},{"title":291,"description":43125},"wfwm9uwNJj4zJGueMDSvybWY0MKFKZ_gs7sqbDF4mVM",{"id":43130,"title":4824,"access":12,"apiToc":929,"body":43131,"category":1042,"description":43610,"extension":1044,"meta":43611,"navigation":43612,"nest":3303,"path":115,"seo":43613,"sinceVersion":1042,"status":1049,"stem":119,"subtitle":1042,"tier":1050,"tocExternal":1042,"__hash__":43614},"docs\u002Ffamilies\u002Findex.md",{"type":931,"value":43132,"toc":43607},[43133,43136,43165,43192,43216,43255,43258,43349,43353,43375,43477,43487,43593,43604],[934,43134,4824],{"id":43135},"families-ports",[1061,43137,43138],{},[937,43139,43140,1324,43143,43145,43146,43148,43149,968,43151,968,43153,1707,43155,968,43157,968,43159,43161,43162,43164],{},[950,43141,43142],{},"Six prepositions, one trust boundary.",[941,43144,1086],{}," encrypts every\nrecord before it leaves the client; everything else ships as a\n",[950,43147,4836],{}," package named after what it does — ",[941,43150,1260],{},[941,43152,1264],{},[941,43154,1267],{},[941,43156,1270],{},[941,43158,1273],{},[941,43160,1276],{},". Each family binds exactly one frozen ",[950,43163,3292],{}," on the\nhub, so satellites for the same job are swappable without touching the\nrest of your app. This page maps all six families to their catalog.",[937,43166,43167,43168,43170,43171,43173,43174,43176,43177,968,43179,968,43181,968,43183,968,43185,968,43187,4850,43189,43191],{},"noy-db is an encrypted document store. Its core package, ",[941,43169,1086],{},", encrypts every record before it is written to storage — the hub is the ",[950,43172,29054],{},": the line inside which plaintext and key material are allowed to exist. Everything else ships as a ",[950,43175,4836],{},": a separate npm package whose name starts with a preposition — ",[941,43178,1260],{},[941,43180,1264],{},[941,43182,1267],{},[941,43184,1270],{},[941,43186,1273],{},[941,43188,1276],{},[950,43190,3303],{},", and it tells you at a glance what the package does and what it is allowed to see.",[937,43193,43194,43195,43197,43198,968,43200,968,43202,968,43204,968,43206,968,43208,43210,43211,43213,43214,23897],{},"Each family binds exactly one ",[950,43196,3292],{},": a frozen import subpath of the hub (",[941,43199,3299],{},[941,43201,3333],{},[941,43203,3336],{},[941,43205,3345],{},[941,43207,3342],{},[941,43209,3339],{},") whose export surface is ",[1092,43212,3319],{}," — checked against a committed snapshot by a test, so the contract can only change deliberately, never by accident. Satellites bind their port and nothing else — never hub internals — which is why you can swap one member of a family for another without touching the rest of your application. The full mechanism is on the ",[1007,43215,29],{"href":30},[2217,43217,43219],{"className":2219,"code":43218,"language":2221,"meta":1037,"style":1037},"\u002F\u002F A storage backend binds only the \u002Fto port — the ciphertext-facing contract.\n\u002F\u002F It never imports a hub-internal module:\nimport type { NoydbStore, EncryptedEnvelope } from '@noy-db\u002Fhub\u002Fto'\n",[941,43220,43221,43226,43231],{"__ignoreMap":1037},[2225,43222,43223],{"class":2227,"line":2228},[2225,43224,43225],{"class":2763},"\u002F\u002F A storage backend binds only the \u002Fto port — the ciphertext-facing contract.\n",[2225,43227,43228],{"class":2227,"line":1041},[2225,43229,43230],{"class":2763},"\u002F\u002F It never imports a hub-internal module:\n",[2225,43232,43233,43235,43237,43239,43241,43243,43245,43247,43249,43251,43253],{"class":2227,"line":1038},[2225,43234,2232],{"class":2231},[2225,43236,3399],{"class":2231},[2225,43238,2236],{"class":2235},[2225,43240,3404],{"class":2239},[2225,43242,2298],{"class":2235},[2225,43244,3409],{"class":2239},[2225,43246,2243],{"class":2235},[2225,43248,2246],{"class":2231},[2225,43250,2249],{"class":2235},[2225,43252,3299],{"class":2252},[2225,43254,2256],{"class":2235},[937,43256,43257],{},"The prepositions read from the vault owner's perspective, and each family's port grants it exactly the visibility its job needs — nothing more:",[1111,43259,43260,43275,43290,43303,43320,43334],{},[1114,43261,43262,43264,43265,43267,43268,968,43270,968,43272,43274],{},[941,43263,1260],{}," — data goes ",[950,43266,4879],{}," a storage backend (",[941,43269,7461],{},[941,43271,9507],{},[941,43273,5355],{},"). Sees ciphertext only.",[1114,43276,43277,43279,43280,43282,43283,968,43285,968,43287,43289],{},[941,43278,1264],{}," — you get ",[950,43281,4883],{}," (unlock the vault) via a method (",[941,43284,15953],{},[941,43286,15944],{},[941,43288,15974],{},"). Handles unlock material, never data keys.",[1114,43291,43292,43294,43295,43297,43298,968,43300,43302],{},[941,43293,1267],{}," — a machine-held vault secret is encrypted (\"sealed\") ",[950,43296,4887],{}," a host you control, so your own server code can open a vault (",[941,43299,13481],{},[941,43301,13485],{},"). The one family that is deliberately not zero-knowledge.",[1114,43304,43305,43307,43308,43310,43311,968,43313,968,43315,968,43317,43319],{},[941,43306,1270],{}," — the hub's public surface is adapted ",[950,43309,4891],{},"to a host runtime or protocol (",[941,43312,37609],{},[941,43314,36282],{},[941,43316,35645],{},[941,43318,35649],{},"). Reads decrypted data the same way your app code does.",[1114,43321,43322,43324,43325,43327,43328,43330,43331,43333],{},[941,43323,1273],{}," — sessions sync ",[950,43326,4895],{}," way of a transport (",[941,43329,16057],{}," over WebRTC, ",[941,43332,16061],{}," across browser tabs). Never sees plaintext.",[1114,43335,43336,43338,43339,43341,43342,968,43344,968,43346,43348],{},[941,43337,1276],{}," — data is exported ",[950,43340,4899],{}," a portable format (",[941,43343,30483],{},[941,43345,32414],{},[941,43347,13592],{},"). A pure read-side transform: decrypted records in, formatted bytes out.",[1022,43350,43352],{"id":43351},"installing-more-than-one-satellite","Installing more than one satellite",[937,43354,43355,43356,43358,43359,43361,43362,1617,43365,43368,43369,997,43371,43374],{},"Every satellite declares ",[941,43357,1086],{}," as a peer. From ",[941,43360,10996],{}," that peer is a ",[950,43363,43364],{},"caret range",[941,43366,43367],{},"^0.7.0-pre.9","), so satellites cut at different times are jointly satisfiable — one hub can satisfy\n",[941,43370,43367],{},[941,43372,43373],{},"^0.7.0-pre.10"," at once — while still stopping at the next minor, so a satellite\nnever claims to work against a hub line it has not seen.",[7646,43376,43377,43393,43457],{"color":10789,"icon":39006},[937,43378,43379,43385,43386,43389,43390,2311],{},[950,43380,43381,43382,43384],{},"Satellites published before ",[941,43383,10996],{}," pin hub exactly, and that cannot be changed."," Their\npublished manifests say ",[941,43387,43388],{},"\"@noy-db\u002Fhub\": \"0.7.0-pre.8\""," — an exact version, not a range — and a\npublished manifest is immutable. No single hub satisfies two different exact pins, so ",[950,43391,43392],{},"mixing two\nolder satellites from different cuts fails to install",[2217,43394,43396],{"className":41160,"code":43395,"language":41162,"meta":1037,"style":1037},"npm i @noy-db\u002Fas-csv@0.7.0-pre.6 @noy-db\u002Fin-vue@0.7.0-pre.5   # ERESOLVE\nnpm i @noy-db\u002Fas-csv@0.7.0-pre.6 @noy-db\u002Fin-vue@0.7.0-pre.6   # ok — same cut\nnpm i @noy-db\u002Fas-csv@0.7.0-pre.9 @noy-db\u002Fin-vue@0.7.0-pre.8   # ERESOLVE — pre.8 still pins exactly\nnpm i @noy-db\u002Fas-csv@0.7.0-pre.9 @noy-db\u002Fin-vue@0.7.0-pre.9   # ok\n",[941,43397,43398,43414,43428,43443],{"__ignoreMap":1037},[2225,43399,43400,43402,43405,43408,43411],{"class":2227,"line":2228},[2225,43401,13678],{"class":6342},[2225,43403,43404],{"class":2252}," i",[2225,43406,43407],{"class":2252}," @noy-db\u002Fas-csv@0.7.0-pre.6",[2225,43409,43410],{"class":2252}," @noy-db\u002Fin-vue@0.7.0-pre.5",[2225,43412,43413],{"class":2763},"   # ERESOLVE\n",[2225,43415,43416,43418,43420,43422,43425],{"class":2227,"line":1041},[2225,43417,13678],{"class":6342},[2225,43419,43404],{"class":2252},[2225,43421,43407],{"class":2252},[2225,43423,43424],{"class":2252}," @noy-db\u002Fin-vue@0.7.0-pre.6",[2225,43426,43427],{"class":2763},"   # ok — same cut\n",[2225,43429,43430,43432,43434,43437,43440],{"class":2227,"line":1038},[2225,43431,13678],{"class":6342},[2225,43433,43404],{"class":2252},[2225,43435,43436],{"class":2252}," @noy-db\u002Fas-csv@0.7.0-pre.9",[2225,43438,43439],{"class":2252}," @noy-db\u002Fin-vue@0.7.0-pre.8",[2225,43441,43442],{"class":2763},"   # ERESOLVE — pre.8 still pins exactly\n",[2225,43444,43445,43447,43449,43451,43454],{"class":2227,"line":2288},[2225,43446,13678],{"class":6342},[2225,43448,43404],{"class":2252},[2225,43450,43436],{"class":2252},[2225,43452,43453],{"class":2252}," @noy-db\u002Fin-vue@0.7.0-pre.9",[2225,43455,43456],{"class":2763},"   # ok\n",[937,43458,43459,43460,43462,43463,43469,43470,43472,43473,43476],{},"Note the third line: upgrading ",[1092,43461,11756],{}," package to a ranged release does not rescue the pairing, because\nthe exact pin on the other side is still there. The fix is forward-only — ",[950,43464,43465,43466,43468],{},"take every satellite from\n",[941,43467,10996],{}," or later",", after which their versions no longer have to match each other. Below that\nfloor, keep every ",[941,43471,5129],{}," satellite on one release. Reaching for ",[941,43474,43475],{},"--legacy-peer-deps"," instead\nsilences a real answer: the versions you asked for were never tested together.",[937,43478,43479,43480,43483,43484,1520],{},"Each family folder (labelled by its prefix in the sidebar) opens with a ",[950,43481,43482],{},"catalog Overview"," — every package grouped by category — and then a ",[950,43485,43486],{},"page per package",[1338,43488,43489,43501],{},[1341,43490,43491],{},[1344,43492,43493,43496,43498],{},[1347,43494,43495],{},"Family",[1347,43497,4928],{},[1347,43499,43500],{},"Catalog overview",[1357,43502,43503,43518,43533,43548,43563,43578],{},[1344,43504,43505,43508,43512],{},[1362,43506,43507],{},"Storage destinations",[1362,43509,43510],{},[941,43511,1779],{},[1362,43513,43514],{},[1007,43515,43517],{"href":43516},"to\u002F","to\u002Findex",[1344,43519,43520,43523,43527],{},[1362,43521,43522],{},"Framework integrations",[1362,43524,43525],{},[941,43526,1783],{},[1362,43528,43529],{},[1007,43530,43532],{"href":43531},"in\u002F","in\u002Findex",[1344,43534,43535,43538,43542],{},[1362,43536,43537],{},"Unlock \u002F auth methods",[1362,43539,43540],{},[941,43541,1787],{},[1362,43543,43544],{},[1007,43545,43547],{"href":43546},"on\u002F","on\u002Findex",[1344,43549,43550,43553,43557],{},[1362,43551,43552],{},"Portable-artefact exports",[1362,43554,43555],{},[941,43556,1791],{},[1362,43558,43559],{},[1007,43560,43562],{"href":43561},"as\u002F","as\u002Findex",[1344,43564,43565,43568,43572],{},[1362,43566,43567],{},"Session-share transports",[1362,43569,43570],{},[941,43571,1795],{},[1362,43573,43574],{},[1007,43575,43577],{"href":43576},"by\u002F","by\u002Findex",[1344,43579,43580,43583,43587],{},[1362,43581,43582],{},"Sealing-key providers",[1362,43584,43585],{},[941,43586,1139],{},[1362,43588,43589],{},[1007,43590,43592],{"href":43591},"at\u002F","at\u002Findex",[937,43594,1015,43595,43597,43598,43600,43601,1520],{},[1007,43596,1019],{"href":1018}," to pick capabilities and land on the exact pages you need. For the trust-boundary view of the same six families — what each may see, hold, or write — see ",[1007,43599,37],{"href":38},"; for the two-axis model (families vs. hub services) and the rules used to decide where a package belongs, see ",[1007,43602,209],{"href":43603},"classification",[2645,43605,43606],{},"html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}",{"title":1037,"searchDepth":1038,"depth":1038,"links":43608},[43609],{"id":43351,"depth":1041,"text":43352},"The six satellite package families — to\u002Fon\u002Fat\u002Fin\u002Fby\u002Fas — and the frozen @noy-db\u002Fhub subpath (port) each one binds instead of hub internals.",{},{"title":10,"icon":120},{"title":4824,"description":43610},"eGmrQSXFiFCNOweUCUTbjE62WzyE6VJAbWyzLsqIBj0",{"id":43616,"title":301,"access":12,"apiToc":929,"body":43617,"category":1042,"description":43879,"extension":1044,"meta":43880,"navigation":1042,"nest":3303,"path":302,"seo":43881,"sinceVersion":1042,"status":1049,"stem":303,"subtitle":1042,"tier":1050,"tocExternal":1042,"__hash__":43882},"docs\u002Ffamilies\u002Fon\u002Femail-otp.md",{"type":931,"value":43618,"toc":43874},[43619,43621,43634,43636,43683,43685,43853,43855,43872],[934,43620,301],{"id":301},[1061,43622,43623],{},[937,43624,43625,1368,43630,43633],{},[950,43626,43627],{},[941,43628,43629],{},"@noy-db\u002Fon-email-otp",[941,43631,43632],{},"pnpm add @noy-db\u002Fon-email-otp","\nEmail OTP second factor, bring-your-own transport.",[1022,43635,29857],{"id":29856},[937,43637,43638,43641,43642,43645,43646,43649,43650,43653,43654,43657,43658,43661,43662,43665,43666,7356,43669,968,43672,4765,43675,43678,43679,43682],{},[941,43639,43640],{},"issueEmailOtp({ email, transport, ttlSeconds, digits, maxAttempts })"," generates\na numeric code (default 6 digits), hashes it with a random salt\n(SHA-256(code ⊕ salt)) into an ",[941,43643,43644],{},"EmailOtpRecord"," — the plaintext code is\nnever stored, but ",[950,43647,43648],{},"treat the record as the code until it expires",": a 6-digit code has a\nmillion values, so anyone holding the salt and digest recovers it in milliseconds — and calls your ",[941,43651,43652],{},"transport"," function with the code to\ndeliver it, so any SMTP \u002F SES \u002F Postmark \u002F custom sender works. Default\nTTL is 300 seconds (5 minutes), default max attempts 5. ",[941,43655,43656],{},"verifyEmailOtp(input, record)"," increments ",[941,43659,43660],{},"attempts"," on every call and returns ",[941,43663,43664],{},"{ ok, reason }","\nwhere ",[941,43667,43668],{},"reason",[941,43670,43671],{},"'expired'",[941,43673,43674],{},"'mismatch'",[941,43676,43677],{},"'locked'"," once\n",[941,43680,43681],{},"maxAttempts"," is hit; the caller deletes the record on success or\nexhaustion.",[1022,43684,29893],{"id":29892},[2217,43686,43688],{"className":2219,"code":43687,"language":2221,"meta":1037,"style":1037},"import { issueEmailOtp, verifyEmailOtp } from '@noy-db\u002Fon-email-otp'\n\nconst { record } = await issueEmailOtp({\n  email: 'alice@acme.com',\n  transport: async ({ to, code }) => sendViaSES(to, `Your code: ${code}`),\n})\n\nconst result = await verifyEmailOtp(userEnteredCode, record)\nif (result.ok) { \u002F* second factor satisfied *\u002F }\n",[941,43689,43690,43714,43718,43739,43755,43804,43810,43814,43834],{"__ignoreMap":1037},[2225,43691,43692,43694,43696,43699,43701,43704,43706,43708,43710,43712],{"class":2227,"line":2228},[2225,43693,2232],{"class":2231},[2225,43695,2236],{"class":2235},[2225,43697,43698],{"class":2239}," issueEmailOtp",[2225,43700,2298],{"class":2235},[2225,43702,43703],{"class":2239}," verifyEmailOtp",[2225,43705,2243],{"class":2235},[2225,43707,2246],{"class":2231},[2225,43709,2249],{"class":2235},[2225,43711,43629],{"class":2252},[2225,43713,2256],{"class":2235},[2225,43715,43716],{"class":2227,"line":1041},[2225,43717,2285],{"emptyLinePlaceholder":2284},[2225,43719,43720,43722,43724,43727,43729,43731,43733,43735,43737],{"class":2227,"line":1038},[2225,43721,2775],{"class":2774},[2225,43723,2236],{"class":2235},[2225,43725,43726],{"class":2239}," record ",[2225,43728,2359],{"class":2235},[2225,43730,6346],{"class":2235},[2225,43732,2784],{"class":2231},[2225,43734,43698],{"class":2291},[2225,43736,2789],{"class":2239},[2225,43738,4200],{"class":2235},[2225,43740,43741,43744,43746,43748,43751,43753],{"class":2227,"line":2288},[2225,43742,43743],{"class":2307},"  email",[2225,43745,2311],{"class":2235},[2225,43747,2249],{"class":2235},[2225,43749,43750],{"class":2252},"alice@acme.com",[2225,43752,2805],{"class":2235},[2225,43754,1707],{"class":2235},[2225,43756,43757,43760,43762,43764,43767,43770,43772,43774,43777,43779,43782,43785,43787,43790,43793,43795,43797,43800,43802],{"class":2227,"line":2304},[2225,43758,43759],{"class":2291},"  transport",[2225,43761,2311],{"class":2235},[2225,43763,38717],{"class":2774},[2225,43765,43766],{"class":2235}," ({",[2225,43768,43769],{"class":7052}," to",[2225,43771,2298],{"class":2235},[2225,43773,19101],{"class":7052},[2225,43775,43776],{"class":2235}," })",[2225,43778,8116],{"class":2774},[2225,43780,43781],{"class":2291}," sendViaSES",[2225,43783,43784],{"class":2239},"(to",[2225,43786,2298],{"class":2235},[2225,43788,43789],{"class":2235}," `",[2225,43791,43792],{"class":2252},"Your code: ",[2225,43794,8197],{"class":2235},[2225,43796,941],{"class":2239},[2225,43798,43799],{"class":2235},"}`",[2225,43801,1381],{"class":2239},[2225,43803,1707],{"class":2235},[2225,43805,43806,43808],{"class":2227,"line":2356},[2225,43807,2359],{"class":2235},[2225,43809,2362],{"class":2239},[2225,43811,43812],{"class":2227,"line":2830},[2225,43813,2285],{"emptyLinePlaceholder":2284},[2225,43815,43816,43818,43820,43822,43824,43826,43829,43831],{"class":2227,"line":2861},[2225,43817,2775],{"class":2774},[2225,43819,36920],{"class":2239},[2225,43821,2781],{"class":2235},[2225,43823,2784],{"class":2231},[2225,43825,43703],{"class":2291},[2225,43827,43828],{"class":2239},"(userEnteredCode",[2225,43830,2298],{"class":2235},[2225,43832,43833],{"class":2239}," record)\n",[2225,43835,43836,43838,43841,43843,43846,43848,43851],{"class":2227,"line":4246},[2225,43837,35129],{"class":2231},[2225,43839,43840],{"class":2239}," (result",[2225,43842,1520],{"class":2235},[2225,43844,43845],{"class":2239},"ok) ",[2225,43847,2792],{"class":2235},[2225,43849,43850],{"class":2763}," \u002F* second factor satisfied *\u002F",[2225,43852,6369],{"class":2235},[1022,43854,2621],{"id":2620},[1111,43856,43857,43862,43867],{},[1114,43858,43859],{},[1007,43860,43861],{"href":2634},"on-* family",[1114,43863,43864],{},[1007,43865,333],{"href":43866},".\u002Ftotp",[1114,43868,43869],{},[1007,43870,313],{"href":43871},".\u002Fpassword",[2645,43873,32706],{},{"title":1037,"searchDepth":1038,"depth":1038,"links":43875},[43876,43877,43878],{"id":29856,"depth":1041,"text":29857},{"id":29892,"depth":1041,"text":29893},{"id":2620,"depth":1041,"text":2621},"Email one-time-passcode second factor with a caller-supplied transport (SMTP \u002F SES \u002F Postmark \u002F any function) and constant-time verification.",{},{"title":301,"description":43879},"tUf-vEJOhOFjbJRAtCQ4PWwAi6ZZcqB8Nkyv2-L7JYA",{"id":43884,"title":43885,"access":12,"apiToc":929,"body":43886,"category":1042,"description":44301,"extension":1044,"meta":44302,"navigation":44303,"nest":3303,"path":296,"seo":44304,"sinceVersion":1042,"status":1049,"stem":297,"subtitle":1042,"tier":1050,"tocExternal":1042,"__hash__":44305},"docs\u002Ffamilies\u002Fon\u002Findex.md","`@noy-db\u002Fon-*` — Authentication \u002F unlock paths",{"type":931,"value":43887,"toc":44289},[43888,43894,43908,43923,43927,43937,43939,43941,43945,43977,43981,44003,44005,44039,44043,44083,44087,44109,44113,44135,44137],[934,43889,43891,43893],{"id":43890},"noy-dbon-authentication-unlock-paths",[941,43892,9945],{}," — Authentication \u002F unlock paths",[1061,43895,43896],{},[937,43897,43898,43901,43902,43904,43905,43907],{},[950,43899,43900],{},"How a user proves who they are."," noy-db encrypts everything\nclient-side: each vault's data is protected by data-encryption keys\n(DEKs), held in the user's ",[950,43903,2705],{}," and wrapped by a single\nkey-encryption key (KEK). Each ",[941,43906,1787],{}," package is one way to unlock that\nkeyring. Composable — stack them however your threat model needs.",[937,43909,960,43910,30769,43912,43918,43919,43922],{},[941,43911,1264],{},[1092,43913,43914,43915,43917],{},"\"you get ",[950,43916,4883],{}," via this method.\""," Every\npackage in this family is a ",[950,43920,43921],{},"crypto primitive only"," — storage,\nrate-limiting, audit integration, and UI belong to the caller.",[1022,43924,43926],{"id":43925},"overview-by-tier-and-integration","Overview — by tier and integration",[937,43928,43929,43930,43932,43933,43936],{},"The whole family at a glance, next to the one kernel-built credential (the tier-1 ",[941,43931,10695],{},"). This is the same catalog surfaced on the ",[1007,43934,16960],{"href":43935},"..\u002F..\u002Fcore\u002Fnoydb\u002Fauthentication"," page — single-sourced:",[17317,43938],{},[1022,43940,30965],{"id":30964},[1591,43942,43944],{"id":43943},"device-sso","Device \u002F SSO",[1338,43946,43947,43955],{},[1341,43948,43949],{},[1344,43950,43951,43953],{},[1347,43952,9530],{},[1347,43954,29857],{},[1357,43956,43957,43967],{},[1344,43958,43959,43964],{},[1362,43960,43961],{},[1007,43962,337],{"href":43963},".\u002Fwebauthn",[1362,43965,43966],{},"Hardware-key passkey unlock via WebAuthn PRF — Touch ID, Face ID, Windows Hello, YubiKey. No secret at all.",[1344,43968,43969,43974],{},[1362,43970,43971],{},[1007,43972,309],{"href":43973},".\u002Foidc",[1362,43975,43976],{},"Split-key OIDC bridge — the KEK is XOR-split between a device half and a server half; the server never sees plaintext. Requires operating a key-connector server.",[1591,43978,43980],{"id":43979},"delegation","Delegation",[1338,43982,43983,43991],{},[1341,43984,43985],{},[1344,43986,43987,43989],{},[1347,43988,9530],{},[1347,43990,29857],{},[1357,43992,43993],{},[1344,43994,43995,44000],{},[1362,43996,43997],{},[1007,43998,305],{"href":43999},".\u002Fmagic-link",[1362,44001,44002],{},"One-shot viewer session — a single-use, vault-bound URL opens a read-only session; no account, no shared secret.",[1591,44004,15200],{"id":321},[1338,44006,44007,44015],{},[1341,44008,44009],{},[1344,44010,44011,44013],{},[1347,44012,9530],{},[1347,44014,29857],{},[1357,44016,44017,44026],{},[1344,44018,44019,44023],{},[1362,44020,44021],{},[1007,44022,321],{"href":11641},[1362,44024,44025],{},"Last-resort unlock via printable Base32 codes (28 chars, 120 bits of entropy) that burn on use.",[1344,44027,44028,44033],{},[1362,44029,44030],{},[1007,44031,325],{"href":44032},".\u002Fshamir",[1362,44034,44035,44036,44038],{},"k-of-n Shamir Secret Sharing of the KEK for multi-party custody; each share composable with another ",[941,44037,1787],{}," method.",[1591,44040,44042],{"id":44041},"second-factor","Second factor",[1338,44044,44045,44053],{},[1341,44046,44047],{},[1344,44048,44049,44051],{},[1347,44050,9530],{},[1347,44052,29857],{},[1357,44054,44055,44064,44074],{},[1344,44056,44057,44061],{},[1362,44058,44059],{},[1007,44060,333],{"href":43866},[1362,44062,44063],{},"RFC 6238 authenticator-app codes, zero dependencies, constant-time verification against the official test vectors.",[1344,44065,44066,44071],{},[1362,44067,44068],{},[1007,44069,301],{"href":44070},".\u002Femail-otp",[1362,44072,44073],{},"Email OTP with a caller-supplied transport (SMTP \u002F SES \u002F Postmark \u002F any function) and constant-time verification.",[1344,44075,44076,44080],{},[1362,44077,44078],{},[1007,44079,313],{"href":43871},[1362,44081,44082],{},"Routine tier-2 login password — a separate secret from the tier-1 master phrase, PBKDF2-SHA256 600K wrapping the DEK set.",[1591,44084,44086],{"id":44085},"local-lock","Local lock",[1338,44088,44089,44097],{},[1341,44090,44091],{},[1344,44092,44093,44095],{},[1347,44094,9530],{},[1347,44096,29857],{},[1357,44098,44099],{},[1344,44100,44101,44106],{},[1362,44102,44103],{},[1007,44104,317],{"href":44105},".\u002Fpin",[1362,44107,44108],{},"Session-resume PIN \u002F biometric quick-lock (tier 3) — re-unlocks cached DEKs without re-typing the secret.",[1591,44110,44112],{"id":44111},"defence","Defence",[1338,44114,44115,44123],{},[1341,44116,44117],{},[1344,44118,44119,44121],{},[1347,44120,9530],{},[1347,44122,29857],{},[1357,44124,44125],{},[1344,44126,44127,44132],{},[1362,44128,44129],{},[1007,44130,329],{"href":44131},".\u002Fthreat",[1362,44133,44134],{},"Lockout + duress + honeypot triad — pure stateful primitives for brute-force lockout, coercion response, and decoy vaults.",[1022,44136,31192],{"id":31191},[1338,44138,44139,44153],{},[1341,44140,44141],{},[1344,44142,44143,44145,44148,44151],{},[1347,44144,9530],{},[1347,44146,44147],{},"Proves",[1347,44149,44150],{},"Needs a server?",[1347,44152,2970],{},[1357,44154,44155,44169,44182,44196,44209,44222,44236,44249,44262,44276],{},[1344,44156,44157,44161,44164,44166],{},[1362,44158,44159],{},[1007,44160,337],{"href":43963},[1362,44162,44163],{},"possession-of-device (hardware authenticator, optional biometric)",[1362,44165,9559],{},[1362,44167,44168],{},"primary unlock",[1344,44170,44171,44175,44178,44180],{},[1362,44172,44173],{},[1007,44174,309],{"href":43973},[1362,44176,44177],{},"federated identity (OIDC ID token)",[1362,44179,9581],{},[1362,44181,44168],{},[1344,44183,44184,44188,44191,44193],{},[1362,44185,44186],{},[1007,44187,305],{"href":43999},[1362,44189,44190],{},"email-delegation (bearer of a single-use URL)",[1362,44192,9581],{},[1362,44194,44195],{},"delegated unlock",[1344,44197,44198,44202,44205,44207],{},[1362,44199,44200],{},[1007,44201,321],{"href":11641},[1362,44203,44204],{},"one-time recovery code (burns on use)",[1362,44206,9559],{},[1362,44208,321],{},[1344,44210,44211,44215,44218,44220],{},[1362,44212,44213],{},[1007,44214,325],{"href":44032},[1362,44216,44217],{},"recovery-shares (k-of-n reconstruction)",[1362,44219,9559],{},[1362,44221,321],{},[1344,44223,44224,44228,44231,44233],{},[1362,44225,44226],{},[1007,44227,333],{"href":43866},[1362,44229,44230],{},"one-time-code (authenticator app)",[1362,44232,9559],{},[1362,44234,44235],{},"step-up 2FA",[1344,44237,44238,44242,44245,44247],{},[1362,44239,44240],{},[1007,44241,301],{"href":44070},[1362,44243,44244],{},"one-time-code (email)",[1362,44246,9581],{},[1362,44248,44235],{},[1344,44250,44251,44255,44258,44260],{},[1362,44252,44253],{},[1007,44254,313],{"href":43871},[1362,44256,44257],{},"knowledge (tier-2 password)",[1362,44259,9559],{},[1362,44261,44168],{},[1344,44263,44264,44268,44271,44273],{},[1362,44265,44266],{},[1007,44267,317],{"href":44105},[1362,44269,44270],{},"knowledge (short PIN \u002F biometric)",[1362,44272,9559],{},[1362,44274,44275],{},"quick-resume (tier 3)",[1344,44277,44278,44282,44284,44286],{},[1362,44279,44280],{},[1007,44281,329],{"href":44131},[1362,44283,37998],{},[1362,44285,9559],{},[1362,44287,44288],{},"defence overlay",{"title":1037,"searchDepth":1038,"depth":1038,"links":44290},[44291,44292,44300],{"id":43925,"depth":1041,"text":43926},{"id":30964,"depth":1041,"text":30965,"children":44293},[44294,44295,44296,44297,44298,44299],{"id":43943,"depth":1038,"text":43944},{"id":43979,"depth":1038,"text":43980},{"id":321,"depth":1038,"text":15200},{"id":44041,"depth":1038,"text":44042},{"id":44085,"depth":1038,"text":44086},{"id":44111,"depth":1038,"text":44112},{"id":31191,"depth":1041,"text":31192},"The unlock catalog: every on-* method a user can prove identity with — passkeys, OIDC, magic links, TOTP, recovery codes, Shamir, PIN, and threat defence.",{},{"title":295,"icon":127},{"title":43885,"description":44301},"GPjOkxtQYPminPxg88knLSSiChPh0Kw6rrWMAGDJXD8",{"id":44307,"title":305,"access":12,"apiToc":929,"body":44308,"category":1042,"description":44562,"extension":1044,"meta":44563,"navigation":1042,"nest":3303,"path":306,"seo":44564,"sinceVersion":1042,"status":1049,"stem":307,"subtitle":1042,"tier":1050,"tocExternal":1042,"__hash__":44565},"docs\u002Ffamilies\u002Fon\u002Fmagic-link.md",{"type":931,"value":44309,"toc":44557},[44310,44312,44325,44327,44372,44374,44538,44540,44554],[934,44311,305],{"id":305},[1061,44313,44314],{},[937,44315,44316,1368,44321,44324],{},[950,44317,44318],{},[941,44319,44320],{},"@noy-db\u002Fon-magic-link",[941,44322,44323],{},"pnpm add @noy-db\u002Fon-magic-link","\nOne-shot viewer session via a single-use URL.",[1022,44326,29857],{"id":29856},[937,44328,44329,44332,44333,44336,44337,44340,44341,1952,44344,44347,44348,44350,44351,44354,44355,44357,44358,44361,44362,44364,44365,1261,44368,44371],{},[941,44330,44331],{},"createMagicLinkToken(vault, options)"," mints a ",[941,44334,44335],{},"MagicLinkToken"," — a ULID\ntoken, the target vault, an ",[941,44338,44339],{},"expiresAt",", and a role that's always\n",[941,44342,44343],{},"'viewer'",[941,44345,44346],{},"deriveMagicLinkKEK(serverSecret, token, vault)"," HKDF-derives\nthe same viewer KEK on both server (at grant time) and client (at unlock\ntime) from a server-held secret and the token — the ULID is hashed into\nthe HKDF salt so it can't be trivially inverted, and the vault id is\nbound into the ",[941,44349,11278],{}," field so a token for vault A can't unlock vault B.\n",[941,44352,44353],{},"buildMagicLinkKeyring"," assembles the resulting ",[941,44356,11493],{},", always\nviewer-scoped. Single-use enforcement (marking a token consumed) is the\nserver's job — ",[941,44359,44360],{},"isMagicLinkValid"," only checks ",[941,44363,44339],{},". A newer\ndelegation bridge (",[941,44366,44367],{},"issueMagicLinkDelegation",[941,44369,44370],{},"claimMagicLinkDelegation",")\nlayers scoped, tier-bound grants — whole-collection or single-record — on\ntop of the same primitive.",[1022,44373,29893],{"id":29892},[2217,44375,44377],{"className":2219,"code":44376,"language":2221,"meta":1037,"style":1037},"import { createMagicLinkToken, deriveMagicLinkKEK } from '@noy-db\u002Fon-magic-link'\n\n\u002F\u002F Server: mint and send\nconst link = createMagicLinkToken('acme', { ttlMs: 7 * 24 * 60 * 60 * 1000 })\nawait emailViewer(`https:\u002F\u002Fapp.example.com\u002Fview?t=${link.token}`)\n\n\u002F\u002F Server + client both derive the same KEK from the token\nconst kek = await deriveMagicLinkKEK(serverSecret, link.token, 'acme')\n",[941,44378,44379,44403,44407,44412,44465,44494,44498,44503],{"__ignoreMap":1037},[2225,44380,44381,44383,44385,44388,44390,44393,44395,44397,44399,44401],{"class":2227,"line":2228},[2225,44382,2232],{"class":2231},[2225,44384,2236],{"class":2235},[2225,44386,44387],{"class":2239}," createMagicLinkToken",[2225,44389,2298],{"class":2235},[2225,44391,44392],{"class":2239}," deriveMagicLinkKEK",[2225,44394,2243],{"class":2235},[2225,44396,2246],{"class":2231},[2225,44398,2249],{"class":2235},[2225,44400,44320],{"class":2252},[2225,44402,2256],{"class":2235},[2225,44404,44405],{"class":2227,"line":1041},[2225,44406,2285],{"emptyLinePlaceholder":2284},[2225,44408,44409],{"class":2227,"line":1038},[2225,44410,44411],{"class":2763},"\u002F\u002F Server: mint and send\n",[2225,44413,44414,44416,44419,44421,44423,44425,44427,44429,44431,44433,44435,44438,44440,44443,44445,44448,44450,44453,44455,44457,44459,44461,44463],{"class":2227,"line":2288},[2225,44415,2775],{"class":2774},[2225,44417,44418],{"class":2239}," link ",[2225,44420,2781],{"class":2235},[2225,44422,44387],{"class":2291},[2225,44424,2789],{"class":2239},[2225,44426,2805],{"class":2235},[2225,44428,6450],{"class":2252},[2225,44430,2805],{"class":2235},[2225,44432,2298],{"class":2235},[2225,44434,2236],{"class":2235},[2225,44436,44437],{"class":2307}," ttlMs",[2225,44439,2311],{"class":2235},[2225,44441,44442],{"class":6527}," 7",[2225,44444,2263],{"class":2235},[2225,44446,44447],{"class":6527}," 24",[2225,44449,2263],{"class":2235},[2225,44451,44452],{"class":6527}," 60",[2225,44454,2263],{"class":2235},[2225,44456,44452],{"class":6527},[2225,44458,2263],{"class":2235},[2225,44460,26290],{"class":6527},[2225,44462,2243],{"class":2235},[2225,44464,2362],{"class":2239},[2225,44466,44467,44469,44472,44474,44477,44480,44482,44485,44487,44490,44492],{"class":2227,"line":2304},[2225,44468,6500],{"class":2231},[2225,44470,44471],{"class":2291}," emailViewer",[2225,44473,2789],{"class":2239},[2225,44475,44476],{"class":2235},"`",[2225,44478,44479],{"class":2252},"https:\u002F\u002Fapp.example.com\u002Fview?t=",[2225,44481,8197],{"class":2235},[2225,44483,44484],{"class":2239},"link",[2225,44486,1520],{"class":2235},[2225,44488,44489],{"class":2239},"token",[2225,44491,43799],{"class":2235},[2225,44493,2362],{"class":2239},[2225,44495,44496],{"class":2227,"line":2356},[2225,44497,2285],{"emptyLinePlaceholder":2284},[2225,44499,44500],{"class":2227,"line":2830},[2225,44501,44502],{"class":2763},"\u002F\u002F Server + client both derive the same KEK from the token\n",[2225,44504,44505,44507,44510,44512,44514,44516,44519,44521,44524,44526,44528,44530,44532,44534,44536],{"class":2227,"line":2861},[2225,44506,2775],{"class":2774},[2225,44508,44509],{"class":2239}," kek ",[2225,44511,2781],{"class":2235},[2225,44513,2784],{"class":2231},[2225,44515,44392],{"class":2291},[2225,44517,44518],{"class":2239},"(serverSecret",[2225,44520,2298],{"class":2235},[2225,44522,44523],{"class":2239}," link",[2225,44525,1520],{"class":2235},[2225,44527,44489],{"class":2239},[2225,44529,2298],{"class":2235},[2225,44531,2249],{"class":2235},[2225,44533,6450],{"class":2252},[2225,44535,2805],{"class":2235},[2225,44537,2362],{"class":2239},[1022,44539,2621],{"id":2620},[1111,44541,44542,44546,44550],{},[1114,44543,44544],{},[1007,44545,43861],{"href":2634},[1114,44547,44548],{},[1007,44549,321],{"href":11641},[1114,44551,44552],{},[1007,44553,325],{"href":44032},[2645,44555,44556],{},"html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":1037,"searchDepth":1038,"depth":1038,"links":44558},[44559,44560,44561],{"id":29856,"depth":1041,"text":29857},{"id":29892,"depth":1041,"text":29893},{"id":2620,"depth":1041,"text":2621},"One-time URL that opens a noy-db vault in a viewer-scoped, TTL-bound session — no secret, no account.",{},{"title":305,"description":44562},"U399C_AYvIwxiAck4aytEpQULU6d9Mtw-x1gchzPj2I",{"id":44567,"title":309,"access":12,"apiToc":929,"body":44568,"category":1042,"description":44888,"extension":1044,"meta":44889,"navigation":1042,"nest":3303,"path":310,"seo":44890,"sinceVersion":1042,"status":1049,"stem":311,"subtitle":1042,"tier":1050,"tocExternal":1042,"__hash__":44891},"docs\u002Ffamilies\u002Fon\u002Foidc.md",{"type":931,"value":44569,"toc":44882},[44570,44572,44585,44587,44610,44656,44671,44679,44683,44697,44719,44722,44752,44769,44771,44864,44866,44880],[934,44571,309],{"id":309},[1061,44573,44574],{},[937,44575,44576,1368,44581,44584],{},[950,44577,44578],{},[941,44579,44580],{},"@noy-db\u002Fon-oidc",[941,44582,44583],{},"pnpm add @noy-db\u002Fon-oidc","\nSplit-key OIDC bridge for federated \"login with X\" unlock.",[1022,44586,29857],{"id":29856},[937,44588,44589,44592,44593,44595,44596,1952,44599,44602,44603,44605,44606,44609],{},[941,44590,44591],{},"enrollOidc(keyring, vault, config, idToken)"," generates a random device\nsecret (persisted to ",[941,44594,41955],{}," by default), HKDF-derives a device\nhalf from it, XORs that against the padded serialized keyring payload to\nget a server half, and PUTs the AES-GCM-encrypted server half to a\n",[950,44597,44598],{},"key-connector server you operate",[941,44600,44601],{},"unlockOidc(enrollment, config, idToken)"," fetches the server half back, re-derives the device half\nlocally, and XORs them to reconstruct the keyring. The server never sees\nthe unwrapped KEK or plaintext — only an encrypted half it cannot combine\nalone. Since ",[941,44604,2126],{}," the device secret and id are stored per\n(provider, ",[941,44607,44608],{},"sub",", vault), so enrolling a second vault no longer overwrites the first.",[937,44611,44612,44615,44616,44618,44619,44622,44623,44626,44627,1617,44630,44633,44634,10313,44637,44639,44640,44645,44646,44648,44649,44652,44653,957],{},[941,44613,44614],{},"enrollOidc"," also HMAC-tags the padded payload under a key derived from the device secret, ",[941,44617,44608],{},"\nand vault, and returns it on the enrollment record as ",[941,44620,44621],{},"integrityTag"," (never sent to the server);\n",[941,44624,44625],{},"unlockOidc"," verifies it before parsing and throws ",[941,44628,44629],{},"OidcEnrollmentIntegrityError",[941,44631,44632],{},"reason: 'tampered'",") on a mismatch. That closes a real attack: the key-connector holds the\ntransport key, and XOR is bit-malleable, so it could flip a known offset — ",[941,44635,44636],{},"'ro'",[941,44638,11380],{},".\n⚠️ ",[950,44641,44642,44643,2311],{},"Breaking for devices enrolled before ",[941,44644,2126],{}," a record without ",[941,44647,44621],{}," is\nrefused (",[941,44650,44651],{},"reason: 'legacy-enrollment'",") because its role and permissions cannot be verified; each\nsuch device enrolls again through a re-invite (",[941,44654,44655],{},"enrollOidcFromInvite",[937,44657,44658,44661,44662,968,44664,2423,44667,44670],{},[941,44659,44660],{},"knownProviders"," ships ready-made configs for ",[941,44663,2227],{},[941,44665,44666],{},"google",[941,44668,44669],{},"apple"," — pass your client ID and key-connector URL.",[937,44672,44673,44674,44678],{},"This package is the client side only. Using it as a tier-2 unlock\nrequires you to run a key-connector server that verifies ID tokens\nagainst the issuer's JWKS and stores per-user server halves — no hosted\nreference implementation ships. If you don't want to operate a server,\n",[1007,44675,44676],{"href":43963},[941,44677,15944],{},"'s platform passkey gives a similar\n\"login with X\" UX with no server infrastructure.",[1022,44680,44682],{"id":44681},"what-the-provider-is-responsible-for","What the provider is responsible for",[937,44684,44685,44686,44689,44690,44693,44694,44696],{},"Federating to an OIDC provider delegates exactly one thing: ",[950,44687,44688],{},"proving who you are."," The whole sign-in ceremony — email and password, an emailed code, a passkey, a biometric — happens ",[1092,44691,44692],{},"at the provider","; noy-db never sees it and only ever consumes the resulting ID token. So the provider is the trust anchor for ",[950,44695,18128],{},", and whatever assurance it enforces (MFA, biometric, device binding) flows into your tier-2 unlock for free.",[937,44698,44699,44700,44702,44703,44705,44706,44709,44710,44713,44714,44716,44717,1520],{},"What the provider is ",[950,44701,1004],{}," responsible for is your decryption key. Because noy-db is zero-knowledge, an ID token cannot decrypt anything on its own. ",[941,44704,15969],{}," XOR-splits the key into a ",[950,44707,44708],{},"device half"," — HKDF-derived from a per-device secret that never leaves the device and the provider never sees — and a ",[950,44711,44712],{},"server half",", stored on your key-connector encrypted under a key derived from the ID token. Login unlocks only the server half; the device half is XOR'd in locally. So a compromised provider — or a malicious key-connector — still cannot read your vault, because neither ever holds both halves. That is the whole point of the split-key design: you delegate ",[1092,44715,18128],{}," to the provider without handing it your ",[1092,44718,8673],{},[937,44720,44721],{},"Two consequences worth stating plainly:",[1111,44723,44724,44740],{},[1114,44725,44726,44729,44730,44732,44733,44735,44736,1520],{},[950,44727,44728],{},"It is a tier-2 unlock, with tier-2 limits."," Like every tier-2 authenticator, the OIDC unlock yields the DEK set with ",[941,44731,17129],{}," — the session can read and write, but cannot ",[941,44734,10493],{},", rotate the secret, enroll another authenticator, or open a second vault (those need a real tier-1 unlock). See ",[1007,44737,44739],{"href":44738},"..\u002F..\u002Fcore\u002Fnoydb\u002Fauthentication#can-a-method-serve-a-different-tier","Authentication → Can a method serve a different tier?",[1114,44741,44742,44745,44746,1261,44748,44751],{},[950,44743,44744],{},"Lose the device half and OIDC alone can't get you back in."," The provider re-proves your identity, not your device secret — so a new device needs a recovery profile (",[1007,44747,19603],{"href":11641},[1007,44749,44750],{"href":44032},"Shamir",") or a re-enrollment from a tier-1 unlock.",[937,44753,44754,44661,44756,968,44760,2462,44764,44768],{},[941,44755,44660],{},[950,44757,44758],{},[941,44759,44666],{},[950,44761,44762],{},[941,44763,44669],{},[950,44765,44766],{},[941,44767,2227],{},"; any other standards-compliant OIDC issuer (Microsoft Entra ID, Okta \u002F Auth0, Keycloak, AWS Cognito, Meta's OIDC flows, …) works by passing a generic config with your issuer URL, client ID, and key-connector URL.",[1022,44770,29893],{"id":29892},[2217,44772,44774],{"className":2219,"code":44773,"language":2221,"meta":1037,"style":1037},"import { knownProviders, unlockOidc } from '@noy-db\u002Fon-oidc'\n\nconst config = knownProviders.google('\u003Cclient-id>', 'https:\u002F\u002Fkey-connector.example.com')\nconst keyring = await unlockOidc(enrollment, config, idToken)\n",[941,44775,44776,44800,44804,44839],{"__ignoreMap":1037},[2225,44777,44778,44780,44782,44785,44787,44790,44792,44794,44796,44798],{"class":2227,"line":2228},[2225,44779,2232],{"class":2231},[2225,44781,2236],{"class":2235},[2225,44783,44784],{"class":2239}," knownProviders",[2225,44786,2298],{"class":2235},[2225,44788,44789],{"class":2239}," unlockOidc",[2225,44791,2243],{"class":2235},[2225,44793,2246],{"class":2231},[2225,44795,2249],{"class":2235},[2225,44797,44580],{"class":2252},[2225,44799,2256],{"class":2235},[2225,44801,44802],{"class":2227,"line":1041},[2225,44803,2285],{"emptyLinePlaceholder":2284},[2225,44805,44806,44808,44811,44813,44815,44817,44819,44821,44823,44826,44828,44830,44832,44835,44837],{"class":2227,"line":1038},[2225,44807,2775],{"class":2774},[2225,44809,44810],{"class":2239}," config ",[2225,44812,2781],{"class":2235},[2225,44814,44784],{"class":2239},[2225,44816,1520],{"class":2235},[2225,44818,44666],{"class":2291},[2225,44820,2789],{"class":2239},[2225,44822,2805],{"class":2235},[2225,44824,44825],{"class":2252},"\u003Cclient-id>",[2225,44827,2805],{"class":2235},[2225,44829,2298],{"class":2235},[2225,44831,2249],{"class":2235},[2225,44833,44834],{"class":2252},"https:\u002F\u002Fkey-connector.example.com",[2225,44836,2805],{"class":2235},[2225,44838,2362],{"class":2239},[2225,44840,44841,44843,44846,44848,44850,44852,44855,44857,44859,44861],{"class":2227,"line":2288},[2225,44842,2775],{"class":2774},[2225,44844,44845],{"class":2239}," keyring ",[2225,44847,2781],{"class":2235},[2225,44849,2784],{"class":2231},[2225,44851,44789],{"class":2291},[2225,44853,44854],{"class":2239},"(enrollment",[2225,44856,2298],{"class":2235},[2225,44858,36491],{"class":2239},[2225,44860,2298],{"class":2235},[2225,44862,44863],{"class":2239}," idToken)\n",[1022,44865,2621],{"id":2620},[1111,44867,44868,44872,44876],{},[1114,44869,44870],{},[1007,44871,43861],{"href":2634},[1114,44873,44874],{},[1007,44875,337],{"href":43963},[1114,44877,44878],{},[1007,44879,333],{"href":43866},[2645,44881,41890],{},{"title":1037,"searchDepth":1038,"depth":1038,"links":44883},[44884,44885,44886,44887],{"id":29856,"depth":1041,"text":29857},{"id":44681,"depth":1041,"text":44682},{"id":29892,"depth":1041,"text":29893},{"id":2620,"depth":1041,"text":2621},"Federated OIDC login (Google, Apple, LINE, Okta, ...) via a split-key key-connector bridge — the server never sees the unwrapped KEK or plaintext.",{},{"title":309,"description":44888},"x5A8H0vkR9o6IBf0xEqR-6VnHVeuDMLJz_owQ19UnHg",{"id":44893,"title":313,"access":12,"apiToc":929,"body":44894,"category":1042,"description":45126,"extension":1044,"meta":45127,"navigation":1042,"nest":3303,"path":314,"seo":45128,"sinceVersion":1042,"status":1049,"stem":315,"subtitle":1042,"tier":1050,"tocExternal":1042,"__hash__":45129},"docs\u002Ffamilies\u002Fon\u002Fpassword.md",{"type":931,"value":44895,"toc":45121},[44896,44898,44910,44912,44933,44935,45103,45105,45119],[934,44897,313],{"id":313},[1061,44899,44900],{},[937,44901,44902,1368,44906,44909],{},[950,44903,44904],{},[941,44905,17035],{},[941,44907,44908],{},"pnpm add @noy-db\u002Fon-password","\nRoutine tier-2 login password, distinct from the master phrase.",[1022,44911,29857],{"id":29856},[937,44913,44914,44917,44918,44920,44921,44923,44924,44927,44928,28806,44930,44932],{},[941,44915,44916],{},"enrollPasswordAuthenticator(keyring, { password, minLength, pattern, id })"," validates the password (default minimum 12 characters, optional\nregex pattern) against an already-unlocked keyring's DEKs, then wraps the\nserialized DEK set — not the KEK — under a PBKDF2-SHA256,\n600,000-iteration AES-GCM key: the \"wrap-DEKs\" variant of\n",[941,44919,17685],{},", matching the tier-1 phrase's iteration count. It\nreturns an ",[941,44922,17568],{}," value the caller hands to\n",[941,44925,44926],{},"db.team.enrollAuthenticator(vault, slot)",". Because it wraps DEKs and not the\nKEK, a password unlock produces an ",[941,44929,11493],{},[941,44931,17129],{}," —\nsensitive operations (enrolling new methods, rotating the phrase) still\nrequire a tier-1 unlock with the master phrase.",[1022,44934,29893],{"id":29892},[2217,44936,44938],{"className":2219,"code":44937,"language":2221,"meta":1037,"style":1037},"import { enrollPasswordAuthenticator } from '@noy-db\u002Fon-password'\n\nconst keyring = await db.team.getKeyring('acme')\nconst slot = await enrollPasswordAuthenticator(keyring, {\n  password: 'strong-password-2026',\n  minLength: 14,\n})\nawait db.team.enrollAuthenticator('acme', slot, { factors: [{ kind: 'totp' }] })\n",[941,44939,44940,44959,44963,44993,45013,45028,45040,45046],{"__ignoreMap":1037},[2225,44941,44942,44944,44946,44949,44951,44953,44955,44957],{"class":2227,"line":2228},[2225,44943,2232],{"class":2231},[2225,44945,2236],{"class":2235},[2225,44947,44948],{"class":2239}," enrollPasswordAuthenticator",[2225,44950,2243],{"class":2235},[2225,44952,2246],{"class":2231},[2225,44954,2249],{"class":2235},[2225,44956,17035],{"class":2252},[2225,44958,2256],{"class":2235},[2225,44960,44961],{"class":2227,"line":1041},[2225,44962,2285],{"emptyLinePlaceholder":2284},[2225,44964,44965,44967,44969,44971,44973,44975,44977,44979,44981,44983,44985,44987,44989,44991],{"class":2227,"line":1038},[2225,44966,2775],{"class":2774},[2225,44968,44845],{"class":2239},[2225,44970,2781],{"class":2235},[2225,44972,2784],{"class":2231},[2225,44974,2842],{"class":2239},[2225,44976,1520],{"class":2235},[2225,44978,868],{"class":2239},[2225,44980,1520],{"class":2235},[2225,44982,9953],{"class":2291},[2225,44984,2789],{"class":2239},[2225,44986,2805],{"class":2235},[2225,44988,6450],{"class":2252},[2225,44990,2805],{"class":2235},[2225,44992,2362],{"class":2239},[2225,44994,44995,44997,45000,45002,45004,45006,45009,45011],{"class":2227,"line":2288},[2225,44996,2775],{"class":2774},[2225,44998,44999],{"class":2239}," slot ",[2225,45001,2781],{"class":2235},[2225,45003,2784],{"class":2231},[2225,45005,44948],{"class":2291},[2225,45007,45008],{"class":2239},"(keyring",[2225,45010,2298],{"class":2235},[2225,45012,2301],{"class":2235},[2225,45014,45015,45017,45019,45021,45024,45026],{"class":2227,"line":2304},[2225,45016,33135],{"class":2307},[2225,45018,2311],{"class":2235},[2225,45020,2249],{"class":2235},[2225,45022,45023],{"class":2252},"strong-password-2026",[2225,45025,2805],{"class":2235},[2225,45027,1707],{"class":2235},[2225,45029,45030,45033,45035,45038],{"class":2227,"line":2356},[2225,45031,45032],{"class":2307},"  minLength",[2225,45034,2311],{"class":2235},[2225,45036,45037],{"class":6527}," 14",[2225,45039,1707],{"class":2235},[2225,45041,45042,45044],{"class":2227,"line":2830},[2225,45043,2359],{"class":2235},[2225,45045,2362],{"class":2239},[2225,45047,45048,45050,45052,45054,45056,45058,45060,45062,45064,45066,45068,45070,45073,45075,45077,45079,45081,45083,45085,45087,45089,45091,45093,45095,45097,45099,45101],{"class":2227,"line":2861},[2225,45049,6500],{"class":2231},[2225,45051,2842],{"class":2239},[2225,45053,1520],{"class":2235},[2225,45055,868],{"class":2239},[2225,45057,1520],{"class":2235},[2225,45059,17521],{"class":2291},[2225,45061,2789],{"class":2239},[2225,45063,2805],{"class":2235},[2225,45065,6450],{"class":2252},[2225,45067,2805],{"class":2235},[2225,45069,2298],{"class":2235},[2225,45071,45072],{"class":2239}," slot",[2225,45074,2298],{"class":2235},[2225,45076,2236],{"class":2235},[2225,45078,17534],{"class":2307},[2225,45080,2311],{"class":2235},[2225,45082,8667],{"class":2239},[2225,45084,2792],{"class":2235},[2225,45086,8285],{"class":2307},[2225,45088,2311],{"class":2235},[2225,45090,2249],{"class":2235},[2225,45092,333],{"class":2252},[2225,45094,2805],{"class":2235},[2225,45096,2243],{"class":2235},[2225,45098,8890],{"class":2239},[2225,45100,2359],{"class":2235},[2225,45102,2362],{"class":2239},[1022,45104,2621],{"id":2620},[1111,45106,45107,45111,45115],{},[1114,45108,45109],{},[1007,45110,43861],{"href":2634},[1114,45112,45113],{},[1007,45114,333],{"href":43866},[1114,45116,45117],{},[1007,45118,317],{"href":44105},[2645,45120,42916],{},{"title":1037,"searchDepth":1038,"depth":1038,"links":45122},[45123,45124,45125],{"id":29856,"depth":1041,"text":29857},{"id":29892,"depth":1041,"text":29893},{"id":2620,"depth":1041,"text":2621},"Tier-2 password authenticator slot — a separate, routinely-typed secret from the rarely-typed tier-1 master phrase.",{},{"title":313,"description":45126},"GdePYEcAhjWNEWYMOOv0q3Rr9S_p-UvEqn3_eEnqbk0",{"id":45131,"title":317,"access":12,"apiToc":929,"body":45132,"category":1042,"description":45325,"extension":1044,"meta":45326,"navigation":1042,"nest":3303,"path":318,"seo":45327,"sinceVersion":1042,"status":1049,"stem":319,"subtitle":1042,"tier":1050,"tocExternal":1042,"__hash__":45328},"docs\u002Ffamilies\u002Fon\u002Fpin.md",{"type":931,"value":45133,"toc":45320},[45134,45136,45148,45150,45174,45176,45301,45303,45317],[934,45135,317],{"id":317},[1061,45137,45138],{},[937,45139,45140,1368,45144,45147],{},[950,45141,45142],{},[941,45143,17062],{},[941,45145,45146],{},"pnpm add @noy-db\u002Fon-pin","\nTier-3 quick-resume PIN, never a secret replacement.",[1022,45149,29857],{"id":29856},[937,45151,45152,45155,45156,45159,45160,45163,45164,4688,45167,45170,45171,45173],{},[941,45153,45154],{},"enrollPin(keyring, { pin, ttlMs, maxAttempts })"," takes an\nalready-unlocked keyring, PBKDF2-derives an AES-GCM key from the PIN, and\nencrypts the serialized keyring into a ",[941,45157,45158],{},"PinResumeState"," — this requires\nthe keyring's DEKs to be extractable (the hub creates them extractable by\ndefault). Default resume window is 15 minutes, default max attempts 5.\n",[941,45161,45162],{},"resumePin(state, { pin })"," decrypts within the TTL and attempt budget,\nthrowing ",[941,45165,45166],{},"PinExpiredError",[941,45168,45169],{},"PinAttemptsExceededError"," as appropriate.\nThe returned keyring has ",[941,45172,17129],{}," — PIN resume reconstructs only the\nDEKs, sufficient for reads\u002Fwrites but not for opening additional vaults,\nre-enrolling, or key rotation, which still need the full secret flow.",[1022,45175,29893],{"id":29892},[2217,45177,45179],{"className":2219,"code":45178,"language":2221,"meta":1037,"style":1037},"import { enrollPin, resumePin } from '@noy-db\u002Fon-pin'\n\nconst state = await enrollPin(keyring, { pin: '4821', ttlMs: 15 * 60 * 1000 })\n\u002F\u002F persist `state` locally (e.g. sessionStorage) for the resume window\n\nconst resumed = await resumePin(state, { pin: userEnteredPin })\n",[941,45180,45181,45205,45209,45261,45266,45270],{"__ignoreMap":1037},[2225,45182,45183,45185,45187,45190,45192,45195,45197,45199,45201,45203],{"class":2227,"line":2228},[2225,45184,2232],{"class":2231},[2225,45186,2236],{"class":2235},[2225,45188,45189],{"class":2239}," enrollPin",[2225,45191,2298],{"class":2235},[2225,45193,45194],{"class":2239}," resumePin",[2225,45196,2243],{"class":2235},[2225,45198,2246],{"class":2231},[2225,45200,2249],{"class":2235},[2225,45202,17062],{"class":2252},[2225,45204,2256],{"class":2235},[2225,45206,45207],{"class":2227,"line":1041},[2225,45208,2285],{"emptyLinePlaceholder":2284},[2225,45210,45211,45213,45216,45218,45220,45222,45224,45226,45228,45231,45233,45235,45238,45240,45242,45244,45246,45249,45251,45253,45255,45257,45259],{"class":2227,"line":1038},[2225,45212,2775],{"class":2774},[2225,45214,45215],{"class":2239}," state ",[2225,45217,2781],{"class":2235},[2225,45219,2784],{"class":2231},[2225,45221,45189],{"class":2291},[2225,45223,45008],{"class":2239},[2225,45225,2298],{"class":2235},[2225,45227,2236],{"class":2235},[2225,45229,45230],{"class":2307}," pin",[2225,45232,2311],{"class":2235},[2225,45234,2249],{"class":2235},[2225,45236,45237],{"class":2252},"4821",[2225,45239,2805],{"class":2235},[2225,45241,2298],{"class":2235},[2225,45243,44437],{"class":2307},[2225,45245,2311],{"class":2235},[2225,45247,45248],{"class":6527}," 15",[2225,45250,2263],{"class":2235},[2225,45252,44452],{"class":6527},[2225,45254,2263],{"class":2235},[2225,45256,26290],{"class":6527},[2225,45258,2243],{"class":2235},[2225,45260,2362],{"class":2239},[2225,45262,45263],{"class":2227,"line":2288},[2225,45264,45265],{"class":2763},"\u002F\u002F persist `state` locally (e.g. sessionStorage) for the resume window\n",[2225,45267,45268],{"class":2227,"line":2304},[2225,45269,2285],{"emptyLinePlaceholder":2284},[2225,45271,45272,45274,45277,45279,45281,45283,45286,45288,45290,45292,45294,45297,45299],{"class":2227,"line":2356},[2225,45273,2775],{"class":2774},[2225,45275,45276],{"class":2239}," resumed ",[2225,45278,2781],{"class":2235},[2225,45280,2784],{"class":2231},[2225,45282,45194],{"class":2291},[2225,45284,45285],{"class":2239},"(state",[2225,45287,2298],{"class":2235},[2225,45289,2236],{"class":2235},[2225,45291,45230],{"class":2307},[2225,45293,2311],{"class":2235},[2225,45295,45296],{"class":2239}," userEnteredPin ",[2225,45298,2359],{"class":2235},[2225,45300,2362],{"class":2239},[1022,45302,2621],{"id":2620},[1111,45304,45305,45309,45313],{},[1114,45306,45307],{},[1007,45308,43861],{"href":2634},[1114,45310,45311],{},[1007,45312,313],{"href":43871},[1114,45314,45315],{},[1007,45316,329],{"href":44131},[2645,45318,45319],{},"html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":1037,"searchDepth":1038,"depth":1038,"links":45321},[45322,45323,45324],{"id":29856,"depth":1041,"text":29857},{"id":29892,"depth":1041,"text":29893},{"id":2620,"depth":1041,"text":2621},"Session-resume PIN or biometric quick-lock — re-unlocks an already-unlocked session's cached DEKs without re-typing the secret.",{},{"title":317,"description":45325},"INVHeGRCcmpQ46KXLpjARZYG9913TfQ9woG9yTrC0NA",{"id":45330,"title":321,"access":12,"apiToc":929,"body":45331,"category":1042,"description":45733,"extension":1044,"meta":45734,"navigation":1042,"nest":3303,"path":322,"seo":45735,"sinceVersion":1042,"status":1049,"stem":323,"subtitle":1042,"tier":1050,"tocExternal":1042,"__hash__":45736},"docs\u002Ffamilies\u002Fon\u002Frecovery.md",{"type":931,"value":45332,"toc":45728},[45333,45335,45348,45350,45381,45401,45425,45427,45710,45712,45726],[934,45334,321],{"id":321},[1061,45336,45337],{},[937,45338,45339,1368,45344,45347],{},[950,45340,45341],{},[941,45342,45343],{},"@noy-db\u002Fon-recovery",[941,45345,45346],{},"pnpm add @noy-db\u002Fon-recovery","\nLast-resort unlock via one-time printable codes.",[1022,45349,29857],{"id":29856},[937,45351,45352,45355,45356,968,45359,45362,45363,45366,45367,45369,45370,7356,45373,45376,45377,45380],{},[941,45353,45354],{},"generateRecoveryCodeSet({ deks, count })"," mints (default 10, clamped\n1–100) 28-character codes — a 24-char Base32 body plus a 4-char checksum,\nRFC 4648 alphabet (no confusing ",[941,45357,45358],{},"0\u002FO",[941,45360,45361],{},"1\u002FI\u002FL","), 120 bits of entropy each\n— and returns the plaintext ",[941,45364,45365],{},"codes"," (show once, never store) plus\n",[941,45368,19759],{}," records (safe to persist). ",[941,45371,45372],{},"deks",[950,45374,45375],{},"required",": it\nis the vault's current DEK set, typically\n",[941,45378,45379],{},"(await db.team.getKeyring(vault)).deks",", and each entry wraps those DEKs\nagainst one code.",[937,45382,45383,45386,45387,2601,45390,1261,45393,45396,45397,45400],{},[941,45384,45385],{},"parseRecoveryCode"," validates and normalizes user input (",[941,45388,45389],{},"valid",[941,45391,45392],{},"invalid-checksum",[941,45394,45395],{},"invalid-format","), and the hub's\n",[941,45398,45399],{},"unwrapDeksFromPaperEntry(entry, code)"," recovers the DEK set from one\nenrolled entry; the caller deletes the matching entry on success so the\ncode burns itself and can never be replayed. Crypto layer only — storage,\naudit, and rate-limiting are the caller's job via the hub's keyring +\nledger APIs. Never enroll it as the only unlock method.",[1061,45402,45403],{},[937,45404,45405,45408,45409,45412,45413,45416,45417,45420,45421,45424],{},[950,45406,45407],{},"There is no KEK path."," Recovery codes wrap the DEK set directly. Older\nmaterial — including this package's own README before ",[941,45410,45411],{},"0.7.0"," (noy-db\n#1252) — described a ",[941,45414,45415],{},"kek"," option, ",[941,45418,45419],{},"unwrapKEKFromRecovery",", and\n",[941,45422,45423],{},"_recovery_\u003CN>"," keyring entries; none of those exist in any shipped\nversion, having been removed with the tier-2 wrap-DEKs unification.",[1022,45426,29893],{"id":29892},[2217,45428,45430],{"className":2219,"code":45429,"language":2221,"meta":1037,"style":1037},"import { generateRecoveryCodeSet, formatRecoveryCode, parseRecoveryCode } from '@noy-db\u002Fon-recovery'\nimport { unwrapDeksFromPaperEntry } from '@noy-db\u002Fhub'\n\n\u002F\u002F Enrolment — `deks` is the vault's current DEK set, and is required.\nconst { deks } = await db.team.getKeyring(vault)\nconst { codes, entries } = await generateRecoveryCodeSet({ deks, count: 10 })\ndisplayRecoveryCodes(codes.map(formatRecoveryCode)) \u002F\u002F 'QWJC-3K7M-PX2A-6RT4-BND6-HFGE-5SYU'\nawait persistRecoveryEntries(entries) \u002F\u002F stored alongside the vault, located by codeId\n\n\u002F\u002F Recovery — the user types one code off the printed sheet.\nconst parsed = parseRecoveryCode(userTypedCode)\nif (parsed.status === 'valid') {\n  const entry = await findRecoveryEntry(parsed.code)\n  const recovered = await unwrapDeksFromPaperEntry(entry, parsed.code)\n  await burnRecoveryEntry(entry) \u002F\u002F one use only — delete on success\n}\n",[941,45431,45432,45461,45480,45484,45489,45517,45559,45577,45590,45594,45599,45613,45636,45661,45690,45706],{"__ignoreMap":1037},[2225,45433,45434,45436,45438,45441,45443,45446,45448,45451,45453,45455,45457,45459],{"class":2227,"line":2228},[2225,45435,2232],{"class":2231},[2225,45437,2236],{"class":2235},[2225,45439,45440],{"class":2239}," generateRecoveryCodeSet",[2225,45442,2298],{"class":2235},[2225,45444,45445],{"class":2239}," formatRecoveryCode",[2225,45447,2298],{"class":2235},[2225,45449,45450],{"class":2239}," parseRecoveryCode",[2225,45452,2243],{"class":2235},[2225,45454,2246],{"class":2231},[2225,45456,2249],{"class":2235},[2225,45458,45343],{"class":2252},[2225,45460,2256],{"class":2235},[2225,45462,45463,45465,45467,45470,45472,45474,45476,45478],{"class":2227,"line":1041},[2225,45464,2232],{"class":2231},[2225,45466,2236],{"class":2235},[2225,45468,45469],{"class":2239}," unwrapDeksFromPaperEntry",[2225,45471,2243],{"class":2235},[2225,45473,2246],{"class":2231},[2225,45475,2249],{"class":2235},[2225,45477,1086],{"class":2252},[2225,45479,2256],{"class":2235},[2225,45481,45482],{"class":2227,"line":1038},[2225,45483,2285],{"emptyLinePlaceholder":2284},[2225,45485,45486],{"class":2227,"line":2288},[2225,45487,45488],{"class":2763},"\u002F\u002F Enrolment — `deks` is the vault's current DEK set, and is required.\n",[2225,45490,45491,45493,45495,45498,45500,45502,45504,45506,45508,45510,45512,45514],{"class":2227,"line":2304},[2225,45492,2775],{"class":2774},[2225,45494,2236],{"class":2235},[2225,45496,45497],{"class":2239}," deks ",[2225,45499,2359],{"class":2235},[2225,45501,6346],{"class":2235},[2225,45503,2784],{"class":2231},[2225,45505,2842],{"class":2239},[2225,45507,1520],{"class":2235},[2225,45509,868],{"class":2239},[2225,45511,1520],{"class":2235},[2225,45513,9953],{"class":2291},[2225,45515,45516],{"class":2239},"(vault)\n",[2225,45518,45519,45521,45523,45526,45528,45531,45533,45535,45537,45539,45541,45543,45546,45548,45550,45552,45555,45557],{"class":2227,"line":2356},[2225,45520,2775],{"class":2774},[2225,45522,2236],{"class":2235},[2225,45524,45525],{"class":2239}," codes",[2225,45527,2298],{"class":2235},[2225,45529,45530],{"class":2239}," entries ",[2225,45532,2359],{"class":2235},[2225,45534,6346],{"class":2235},[2225,45536,2784],{"class":2231},[2225,45538,45440],{"class":2291},[2225,45540,2789],{"class":2239},[2225,45542,2792],{"class":2235},[2225,45544,45545],{"class":2239}," deks",[2225,45547,2298],{"class":2235},[2225,45549,19270],{"class":2307},[2225,45551,2311],{"class":2235},[2225,45553,45554],{"class":6527}," 10",[2225,45556,2243],{"class":2235},[2225,45558,2362],{"class":2239},[2225,45560,45561,45564,45567,45569,45571,45574],{"class":2227,"line":2830},[2225,45562,45563],{"class":2291},"displayRecoveryCodes",[2225,45565,45566],{"class":2239},"(codes",[2225,45568,1520],{"class":2235},[2225,45570,8244],{"class":2291},[2225,45572,45573],{"class":2239},"(formatRecoveryCode)) ",[2225,45575,45576],{"class":2763},"\u002F\u002F 'QWJC-3K7M-PX2A-6RT4-BND6-HFGE-5SYU'\n",[2225,45578,45579,45581,45584,45587],{"class":2227,"line":2861},[2225,45580,6500],{"class":2231},[2225,45582,45583],{"class":2291}," persistRecoveryEntries",[2225,45585,45586],{"class":2239},"(entries) ",[2225,45588,45589],{"class":2763},"\u002F\u002F stored alongside the vault, located by codeId\n",[2225,45591,45592],{"class":2227,"line":4246},[2225,45593,2285],{"emptyLinePlaceholder":2284},[2225,45595,45596],{"class":2227,"line":4253},[2225,45597,45598],{"class":2763},"\u002F\u002F Recovery — the user types one code off the printed sheet.\n",[2225,45600,45601,45603,45606,45608,45610],{"class":2227,"line":4259},[2225,45602,2775],{"class":2774},[2225,45604,45605],{"class":2239}," parsed ",[2225,45607,2781],{"class":2235},[2225,45609,45450],{"class":2291},[2225,45611,45612],{"class":2239},"(userTypedCode)\n",[2225,45614,45615,45617,45620,45622,45624,45626,45628,45630,45632,45634],{"class":2227,"line":6548},[2225,45616,35129],{"class":2231},[2225,45618,45619],{"class":2239}," (parsed",[2225,45621,1520],{"class":2235},[2225,45623,32542],{"class":2239},[2225,45625,33083],{"class":2235},[2225,45627,2249],{"class":2235},[2225,45629,45389],{"class":2252},[2225,45631,2805],{"class":2235},[2225,45633,8495],{"class":2239},[2225,45635,4200],{"class":2235},[2225,45637,45638,45640,45643,45645,45647,45650,45652,45655,45657,45659],{"class":2227,"line":6576},[2225,45639,8123],{"class":2774},[2225,45641,45642],{"class":2239}," entry",[2225,45644,6346],{"class":2235},[2225,45646,2784],{"class":2231},[2225,45648,45649],{"class":2291}," findRecoveryEntry",[2225,45651,2789],{"class":2307},[2225,45653,45654],{"class":2239},"parsed",[2225,45656,1520],{"class":2235},[2225,45658,941],{"class":2239},[2225,45660,2362],{"class":2307},[2225,45662,45663,45665,45668,45670,45672,45674,45676,45679,45681,45684,45686,45688],{"class":2227,"line":6582},[2225,45664,8123],{"class":2774},[2225,45666,45667],{"class":2239}," recovered",[2225,45669,6346],{"class":2235},[2225,45671,2784],{"class":2231},[2225,45673,45469],{"class":2291},[2225,45675,2789],{"class":2307},[2225,45677,45678],{"class":2239},"entry",[2225,45680,2298],{"class":2235},[2225,45682,45683],{"class":2239}," parsed",[2225,45685,1520],{"class":2235},[2225,45687,941],{"class":2239},[2225,45689,2362],{"class":2307},[2225,45691,45692,45694,45697,45699,45701,45703],{"class":2227,"line":6587},[2225,45693,7561],{"class":2231},[2225,45695,45696],{"class":2291}," burnRecoveryEntry",[2225,45698,2789],{"class":2307},[2225,45700,45678],{"class":2239},[2225,45702,8495],{"class":2307},[2225,45704,45705],{"class":2763},"\u002F\u002F one use only — delete on success\n",[2225,45707,45708],{"class":2227,"line":7174},[2225,45709,7351],{"class":2235},[1022,45711,2621],{"id":2620},[1111,45713,45714,45718,45722],{},[1114,45715,45716],{},[1007,45717,43861],{"href":2634},[1114,45719,45720],{},[1007,45721,325],{"href":44032},[1114,45723,45724],{},[1007,45725,305],{"href":43999},[2645,45727,44556],{},{"title":1037,"searchDepth":1038,"depth":1038,"links":45729},[45730,45731,45732],{"id":29856,"depth":1041,"text":29857},{"id":29892,"depth":1041,"text":29893},{"id":2620,"depth":1041,"text":2621},"Printable, burn-on-use recovery codes — the last-resort unlock when secret, passkey, and OIDC are all unavailable.",{},{"title":321,"description":45733},"ZGbN-_p4VLvpjTilk_Zv6R_OCLhiOMV_nrt-56noK58",{"id":45738,"title":325,"access":12,"apiToc":929,"body":45739,"category":1042,"description":45988,"extension":1044,"meta":45989,"navigation":1042,"nest":3303,"path":326,"seo":45990,"sinceVersion":1042,"status":1049,"stem":327,"subtitle":1042,"tier":1473,"tocExternal":1042,"__hash__":45991},"docs\u002Ffamilies\u002Fon\u002Fshamir.md",{"type":931,"value":45740,"toc":45983},[45741,45743,45756,45758,45822,45828,45830,45965,45967,45981],[934,45742,325],{"id":325},[1061,45744,45745],{},[937,45746,45747,1368,45752,45755],{},[950,45748,45749],{},[941,45750,45751],{},"@noy-db\u002Fon-shamir",[941,45753,45754],{},"pnpm add @noy-db\u002Fon-shamir","\nMulti-party k-of-n secret sharing of the KEK.",[1022,45757,29857],{"id":29856},[937,45759,45760,45763,45764,45766,45767,1324,45769,45772,45773,45775,45776,45779,45780,45782,45783,997,45786,45789,45790,1261,45793,45796,45797,45800,45801,1261,45804,45807,45808,45810,45811,1261,45814,45817,45818,45821],{},[941,45761,45762],{},"splitKEK(kek, { k, n })"," runs Shamir Secret Sharing over GF(2^8),\nbyte-wise, on a ",[941,45765,33682],{},", returning ",[941,45768,26634],{},[941,45770,45771],{},"RawShare"," structures; any ",[941,45774,8693],{},"\nof them reconstruct the key via ",[941,45777,45778],{},"combineKEK(shares)"," — fewer than ",[941,45781,8693],{},"\nleaks zero bits. Pure TypeScript (~120 LoC), zero cryptographic\ndependencies beyond Web Crypto's ",[941,45784,45785],{},"getRandomValues",[941,45787,45788],{},"subtle.importKey",".\nShares serialize to Base32 (",[941,45791,45792],{},"encodeShareBase32",[941,45794,45795],{},"decodeShareBase32",",\nwith a human-readable ",[941,45798,45799],{},"SHAMIR_S{x}_K{k}N{n}__"," prefix) or JSON\n(",[941,45802,45803],{},"encodeShareJSON",[941,45805,45806],{},"decodeShareJSON",") for storage inside other ",[941,45809,1787],{},"\nkeyring entries. Low-level ",[941,45812,45813],{},"splitSecret",[941,45815,45816],{},"combineSecret"," operate on raw\n",[941,45819,45820],{},"Uint8Array"," bytes for custom integrations.",[937,45823,45824,45825,45827],{},"The defining feature is composability: each share can itself sit behind\nany other ",[941,45826,1787],{}," method — share 1 behind WebAuthn, share 2 behind OIDC,\nshare 3 printed on paper. Not constant-time by design; the threat model\nassumes a trusted combine device.",[1022,45829,29893],{"id":29892},[2217,45831,45833],{"className":2219,"code":45832,"language":2221,"meta":1037,"style":1037},"import { splitKEK, combineKEK, encodeShareBase32, decodeShareBase32 } from '@noy-db\u002Fon-shamir'\n\nconst shares = await splitKEK(currentKEK, { k: 2, n: 3 })\nconst shareStrings = shares.map(encodeShareBase32)\n\u002F\u002F distribute: shareStrings[0] behind WebAuthn, [1] behind OIDC, [2] on paper\n\nconst kek = await combineKEK([decodeShareBase32(a), decodeShareBase32(b)])\n",[941,45834,45835,45869,45873,45911,45930,45935,45939],{"__ignoreMap":1037},[2225,45836,45837,45839,45841,45844,45846,45849,45851,45854,45856,45859,45861,45863,45865,45867],{"class":2227,"line":2228},[2225,45838,2232],{"class":2231},[2225,45840,2236],{"class":2235},[2225,45842,45843],{"class":2239}," splitKEK",[2225,45845,2298],{"class":2235},[2225,45847,45848],{"class":2239}," combineKEK",[2225,45850,2298],{"class":2235},[2225,45852,45853],{"class":2239}," encodeShareBase32",[2225,45855,2298],{"class":2235},[2225,45857,45858],{"class":2239}," decodeShareBase32",[2225,45860,2243],{"class":2235},[2225,45862,2246],{"class":2231},[2225,45864,2249],{"class":2235},[2225,45866,45751],{"class":2252},[2225,45868,2256],{"class":2235},[2225,45870,45871],{"class":2227,"line":1041},[2225,45872,2285],{"emptyLinePlaceholder":2284},[2225,45874,45875,45877,45880,45882,45884,45886,45889,45891,45893,45895,45897,45899,45901,45903,45905,45907,45909],{"class":2227,"line":1038},[2225,45876,2775],{"class":2774},[2225,45878,45879],{"class":2239}," shares ",[2225,45881,2781],{"class":2235},[2225,45883,2784],{"class":2231},[2225,45885,45843],{"class":2291},[2225,45887,45888],{"class":2239},"(currentKEK",[2225,45890,2298],{"class":2235},[2225,45892,2236],{"class":2235},[2225,45894,8700],{"class":2307},[2225,45896,2311],{"class":2235},[2225,45898,18961],{"class":6527},[2225,45900,2298],{"class":2235},[2225,45902,27471],{"class":2307},[2225,45904,2311],{"class":2235},[2225,45906,14460],{"class":6527},[2225,45908,2243],{"class":2235},[2225,45910,2362],{"class":2239},[2225,45912,45913,45915,45918,45920,45923,45925,45927],{"class":2227,"line":2288},[2225,45914,2775],{"class":2774},[2225,45916,45917],{"class":2239}," shareStrings ",[2225,45919,2781],{"class":2235},[2225,45921,45922],{"class":2239}," shares",[2225,45924,1520],{"class":2235},[2225,45926,8244],{"class":2291},[2225,45928,45929],{"class":2239},"(encodeShareBase32)\n",[2225,45931,45932],{"class":2227,"line":2304},[2225,45933,45934],{"class":2763},"\u002F\u002F distribute: shareStrings[0] behind WebAuthn, [1] behind OIDC, [2] on paper\n",[2225,45936,45937],{"class":2227,"line":2356},[2225,45938,2285],{"emptyLinePlaceholder":2284},[2225,45940,45941,45943,45945,45947,45949,45951,45953,45955,45958,45960,45962],{"class":2227,"line":2830},[2225,45942,2775],{"class":2774},[2225,45944,44509],{"class":2239},[2225,45946,2781],{"class":2235},[2225,45948,2784],{"class":2231},[2225,45950,45848],{"class":2291},[2225,45952,28424],{"class":2239},[2225,45954,45795],{"class":2291},[2225,45956,45957],{"class":2239},"(a)",[2225,45959,2298],{"class":2235},[2225,45961,45858],{"class":2291},[2225,45963,45964],{"class":2239},"(b)])\n",[1022,45966,2621],{"id":2620},[1111,45968,45969,45973,45977],{},[1114,45970,45971],{},[1007,45972,43861],{"href":2634},[1114,45974,45975],{},[1007,45976,329],{"href":44131},[1114,45978,45979],{},[1007,45980,321],{"href":11641},[2645,45982,45319],{},{"title":1037,"searchDepth":1038,"depth":1038,"links":45984},[45985,45986,45987],{"id":29856,"depth":1041,"text":29857},{"id":29892,"depth":1041,"text":29893},{"id":2620,"depth":1041,"text":2621},"k-of-n Shamir Secret Sharing of the vault KEK — any k of n shares reconstruct it; fewer than k leak nothing.",{},{"title":325,"description":45988},"_bt484t7FtXlASA8MatY_7gySyShvFI9Vk4ztqSlJvo",{"id":45993,"title":329,"access":12,"apiToc":929,"body":45994,"category":1042,"description":46237,"extension":1044,"meta":46238,"navigation":1042,"nest":3303,"path":330,"seo":46239,"sinceVersion":1042,"status":1049,"stem":331,"subtitle":1042,"tier":1473,"tocExternal":1042,"__hash__":46240},"docs\u002Ffamilies\u002Fon\u002Fthreat.md",{"type":931,"value":45995,"toc":46232},[45996,45998,46011,46013,46047,46049,46214,46216,46230],[934,45997,329],{"id":329},[1061,45999,46000],{},[937,46001,46002,1368,46007,46010],{},[950,46003,46004],{},[941,46005,46006],{},"@noy-db\u002Fon-threat",[941,46008,46009],{},"pnpm add @noy-db\u002Fon-threat","\nLockout + duress + honeypot defence triad.",[1022,46012,29857],{"id":29856},[937,46014,46015,46016,46019,46020,5502,46023,46026,46027,46030,46031,46034,46035,46038,46039,46042,46043,46046],{},"Three independent, opt-in mechanisms, all pure logic — the caller\npersists state and wires the resulting actions. ",[941,46017,46018],{},"initialLockoutState()","\nseeds a ",[941,46021,46022],{},"LockoutState",[941,46024,46025],{},"recordFailure(state, config)"," accumulates failed\nattempts within a window (default threshold 5 in 15 minutes), triggers a\ncooldown lockout (default 5 minutes) past the threshold, and after\n",[941,46028,46029],{},"maxStrikes"," rounds (default 3) flips the state to a terminal wipe signal\nfor the caller to act on; ",[941,46032,46033],{},"recordSuccess(state)"," resets it.\n",[941,46036,46037],{},"checkDuress(input, digest, salt)"," compares an entered secret against\na stored duress digest — a match signals the caller's ",[941,46040,46041],{},"onDuress"," handler\nto purge DEKs and delete the keyring. ",[941,46044,46045],{},"checkHoneypot"," (an alias of the\nsame detection pair) instead routes to a pre-seeded decoy vault. None of\nthe three mechanisms require server cooperation.",[1022,46048,29893],{"id":29892},[2217,46050,46052],{"className":2219,"code":46051,"language":2221,"meta":1037,"style":1037},"import { initialLockoutState, recordFailure, checkDuress } from '@noy-db\u002Fon-threat'\n\nlet state = initialLockoutState()\n\n\u002F\u002F on each failed unlock attempt\nconst outcome = recordFailure(state)\nif (outcome.wipe) await destroyVault()\nelse if (outcome.locked) showLockout(outcome.unlockAt)\n\n\u002F\u002F separately, check every entered secret against the duress digest\nif (await checkDuress(enteredSecret, duressDigest, duressSalt)) {\n  await triggerDuressWipe()\n}\n",[941,46053,46054,46083,46087,46100,46104,46109,46122,46141,46167,46171,46176,46201,46210],{"__ignoreMap":1037},[2225,46055,46056,46058,46060,46063,46065,46068,46070,46073,46075,46077,46079,46081],{"class":2227,"line":2228},[2225,46057,2232],{"class":2231},[2225,46059,2236],{"class":2235},[2225,46061,46062],{"class":2239}," initialLockoutState",[2225,46064,2298],{"class":2235},[2225,46066,46067],{"class":2239}," recordFailure",[2225,46069,2298],{"class":2235},[2225,46071,46072],{"class":2239}," checkDuress",[2225,46074,2243],{"class":2235},[2225,46076,2246],{"class":2231},[2225,46078,2249],{"class":2235},[2225,46080,46006],{"class":2252},[2225,46082,2256],{"class":2235},[2225,46084,46085],{"class":2227,"line":1041},[2225,46086,2285],{"emptyLinePlaceholder":2284},[2225,46088,46089,46092,46094,46096,46098],{"class":2227,"line":1038},[2225,46090,46091],{"class":2774},"let",[2225,46093,45215],{"class":2239},[2225,46095,2781],{"class":2235},[2225,46097,46062],{"class":2291},[2225,46099,8146],{"class":2239},[2225,46101,46102],{"class":2227,"line":2288},[2225,46103,2285],{"emptyLinePlaceholder":2284},[2225,46105,46106],{"class":2227,"line":2304},[2225,46107,46108],{"class":2763},"\u002F\u002F on each failed unlock attempt\n",[2225,46110,46111,46113,46115,46117,46119],{"class":2227,"line":2356},[2225,46112,2775],{"class":2774},[2225,46114,39865],{"class":2239},[2225,46116,2781],{"class":2235},[2225,46118,46067],{"class":2291},[2225,46120,46121],{"class":2239},"(state)\n",[2225,46123,46124,46126,46129,46131,46134,46136,46139],{"class":2227,"line":2830},[2225,46125,35129],{"class":2231},[2225,46127,46128],{"class":2239}," (outcome",[2225,46130,1520],{"class":2235},[2225,46132,46133],{"class":2239},"wipe) ",[2225,46135,6500],{"class":2231},[2225,46137,46138],{"class":2291}," destroyVault",[2225,46140,8146],{"class":2239},[2225,46142,46143,46146,46149,46151,46153,46156,46159,46162,46164],{"class":2227,"line":2861},[2225,46144,46145],{"class":2231},"else",[2225,46147,46148],{"class":2231}," if",[2225,46150,46128],{"class":2239},[2225,46152,1520],{"class":2235},[2225,46154,46155],{"class":2239},"locked) ",[2225,46157,46158],{"class":2291},"showLockout",[2225,46160,46161],{"class":2239},"(outcome",[2225,46163,1520],{"class":2235},[2225,46165,46166],{"class":2239},"unlockAt)\n",[2225,46168,46169],{"class":2227,"line":4246},[2225,46170,2285],{"emptyLinePlaceholder":2284},[2225,46172,46173],{"class":2227,"line":4253},[2225,46174,46175],{"class":2763},"\u002F\u002F separately, check every entered secret against the duress digest\n",[2225,46177,46178,46180,46182,46184,46186,46189,46191,46194,46196,46199],{"class":2227,"line":4259},[2225,46179,35129],{"class":2231},[2225,46181,953],{"class":2239},[2225,46183,6500],{"class":2231},[2225,46185,46072],{"class":2291},[2225,46187,46188],{"class":2239},"(enteredSecret",[2225,46190,2298],{"class":2235},[2225,46192,46193],{"class":2239}," duressDigest",[2225,46195,2298],{"class":2235},[2225,46197,46198],{"class":2239}," duressSalt)) ",[2225,46200,4200],{"class":2235},[2225,46202,46203,46205,46208],{"class":2227,"line":6548},[2225,46204,7561],{"class":2231},[2225,46206,46207],{"class":2291}," triggerDuressWipe",[2225,46209,8146],{"class":2307},[2225,46211,46212],{"class":2227,"line":6576},[2225,46213,7351],{"class":2235},[1022,46215,2621],{"id":2620},[1111,46217,46218,46222,46226],{},[1114,46219,46220],{},[1007,46221,43861],{"href":2634},[1114,46223,46224],{},[1007,46225,325],{"href":44032},[1114,46227,46228],{},[1007,46229,317],{"href":44105},[2645,46231,32168],{},{"title":1037,"searchDepth":1038,"depth":1038,"links":46233},[46234,46235,46236],{"id":29856,"depth":1041,"text":29857},{"id":29892,"depth":1041,"text":29893},{"id":2620,"depth":1041,"text":2621},"Lockout, duress-secret data destruction, and honeypot decoy — stateful defence primitives layered on top of any unlock method.",{},{"title":329,"description":46237},"-CEDt1a00XAIg24Snlji89k_Om2cpkyxphpH6Z5apNE",{"id":46242,"title":333,"access":12,"apiToc":929,"body":46243,"category":1042,"description":46566,"extension":1044,"meta":46567,"navigation":1042,"nest":3303,"path":334,"seo":46568,"sinceVersion":1042,"status":1049,"stem":335,"subtitle":1042,"tier":1050,"tocExternal":1042,"__hash__":46569},"docs\u002Ffamilies\u002Fon\u002Ftotp.md",{"type":931,"value":46244,"toc":46561},[46245,46247,46260,46262,46284,46286,46416,46440,46543,46545,46559],[934,46246,333],{"id":333},[1061,46248,46249],{},[937,46250,46251,1368,46256,46259],{},[950,46252,46253],{},[941,46254,46255],{},"@noy-db\u002Fon-totp",[941,46257,46258],{},"pnpm add @noy-db\u002Fon-totp","\nAuthenticator-app second factor (RFC 6238).",[1022,46261,29857],{"id":29856},[937,46263,46264,46267,46268,46271,46272,46275,46276,46279,46280,46283],{},[941,46265,46266],{},"generateTotpSecret()"," returns a random 20-byte Base32 secret (the RFC 4226\nrecommended minimum). ",[941,46269,46270],{},"totpProvisioningUri(secret, { account, issuer, digits, period, algorithm })"," builds a standard ",[941,46273,46274],{},"otpauth:\u002F\u002Ftotp\u002F"," URI for\nQR-code scanning into any authenticator app. ",[941,46277,46278],{},"generateTotpCode(secret, options)"," computes the current code; ",[941,46281,46282],{},"verifyTotp(secret, code, options)","\nchecks a user-entered code with constant-time comparison and a\nconfigurable clock-drift window (default ±1 step) to tolerate\nauthenticator\u002Fserver skew. Defaults: 6 digits, 30s period, SHA1 (for\ncompatibility with Google Authenticator and similar apps). Zero\ndependencies — HMAC via Web Crypto, validated against the official RFC\ntest vectors.",[1022,46285,29893],{"id":29892},[2217,46287,46289],{"className":2219,"code":46288,"language":2221,"meta":1037,"style":1037},"import { generateTotpSecret, totpProvisioningUri, verifyTotp } from '@noy-db\u002Fon-totp'\n\nconst secret = generateTotpSecret()\nconst uri = totpProvisioningUri(secret, { account: 'alice@acme.com', issuer: 'Acme Notes' })\n\u002F\u002F show `uri` as a QR code\n\nconst ok = await verifyTotp(secret, userEnteredCode) \u002F\u002F constant-time, ±1 step window\n",[941,46290,46291,46320,46324,46337,46384,46389,46393],{"__ignoreMap":1037},[2225,46292,46293,46295,46297,46300,46302,46305,46307,46310,46312,46314,46316,46318],{"class":2227,"line":2228},[2225,46294,2232],{"class":2231},[2225,46296,2236],{"class":2235},[2225,46298,46299],{"class":2239}," generateTotpSecret",[2225,46301,2298],{"class":2235},[2225,46303,46304],{"class":2239}," totpProvisioningUri",[2225,46306,2298],{"class":2235},[2225,46308,46309],{"class":2239}," verifyTotp",[2225,46311,2243],{"class":2235},[2225,46313,2246],{"class":2231},[2225,46315,2249],{"class":2235},[2225,46317,46255],{"class":2252},[2225,46319,2256],{"class":2235},[2225,46321,46322],{"class":2227,"line":1041},[2225,46323,2285],{"emptyLinePlaceholder":2284},[2225,46325,46326,46328,46331,46333,46335],{"class":2227,"line":1038},[2225,46327,2775],{"class":2774},[2225,46329,46330],{"class":2239}," secret ",[2225,46332,2781],{"class":2235},[2225,46334,46299],{"class":2291},[2225,46336,8146],{"class":2239},[2225,46338,46339,46341,46344,46346,46348,46351,46353,46355,46358,46360,46362,46364,46366,46368,46371,46373,46375,46378,46380,46382],{"class":2227,"line":2288},[2225,46340,2775],{"class":2774},[2225,46342,46343],{"class":2239}," uri ",[2225,46345,2781],{"class":2235},[2225,46347,46304],{"class":2291},[2225,46349,46350],{"class":2239},"(secret",[2225,46352,2298],{"class":2235},[2225,46354,2236],{"class":2235},[2225,46356,46357],{"class":2307}," account",[2225,46359,2311],{"class":2235},[2225,46361,2249],{"class":2235},[2225,46363,43750],{"class":2252},[2225,46365,2805],{"class":2235},[2225,46367,2298],{"class":2235},[2225,46369,46370],{"class":2307}," issuer",[2225,46372,2311],{"class":2235},[2225,46374,2249],{"class":2235},[2225,46376,46377],{"class":2252},"Acme Notes",[2225,46379,2805],{"class":2235},[2225,46381,2243],{"class":2235},[2225,46383,2362],{"class":2239},[2225,46385,46386],{"class":2227,"line":2304},[2225,46387,46388],{"class":2763},"\u002F\u002F show `uri` as a QR code\n",[2225,46390,46391],{"class":2227,"line":2356},[2225,46392,2285],{"emptyLinePlaceholder":2284},[2225,46394,46395,46397,46400,46402,46404,46406,46408,46410,46413],{"class":2227,"line":2830},[2225,46396,2775],{"class":2774},[2225,46398,46399],{"class":2239}," ok ",[2225,46401,2781],{"class":2235},[2225,46403,2784],{"class":2231},[2225,46405,46309],{"class":2291},[2225,46407,46350],{"class":2239},[2225,46409,2298],{"class":2235},[2225,46411,46412],{"class":2239}," userEnteredCode) ",[2225,46414,46415],{"class":2763},"\u002F\u002F constant-time, ±1 step window\n",[937,46417,46418,46419,46422,46423,968,46425,46428,46429,46431,46432,46435,46436,46439],{},"⛔ ",[950,46420,46421],{},"Replay is the caller's to prevent."," Without it, one code verifies for the whole drift\nwindow (~90 s) — anyone who sees it can reuse it. Since ",[941,46424,2126],{},[941,46426,46427],{},"verifyTotpStep"," returns the\nstep a code matched (or ",[941,46430,6651],{},"), and the ",[941,46433,46434],{},"lastUsedStep"," option on both verifiers refuses that step\nand every earlier one (RFC 6238 §5.2). The package stays stateless, so ",[950,46437,46438],{},"you persist the step"," —\nper secret, somewhere the party presenting codes cannot rewind it:",[2217,46441,46443],{"className":2219,"code":46442,"language":2221,"meta":1037,"style":1037},"import { verifyTotpStep } from '@noy-db\u002Fon-totp'\n\nconst step = await verifyTotpStep(secret, userEnteredCode, { lastUsedStep })\nif (step === null) throw new Error('bad or reused code')\nlastUsedStep = step \u002F\u002F store it before treating the factor as passed\n",[941,46444,46445,46464,46468,46499,46531],{"__ignoreMap":1037},[2225,46446,46447,46449,46451,46454,46456,46458,46460,46462],{"class":2227,"line":2228},[2225,46448,2232],{"class":2231},[2225,46450,2236],{"class":2235},[2225,46452,46453],{"class":2239}," verifyTotpStep",[2225,46455,2243],{"class":2235},[2225,46457,2246],{"class":2231},[2225,46459,2249],{"class":2235},[2225,46461,46255],{"class":2252},[2225,46463,2256],{"class":2235},[2225,46465,46466],{"class":2227,"line":1041},[2225,46467,2285],{"emptyLinePlaceholder":2284},[2225,46469,46470,46472,46475,46477,46479,46481,46483,46485,46488,46490,46492,46495,46497],{"class":2227,"line":1038},[2225,46471,2775],{"class":2774},[2225,46473,46474],{"class":2239}," step ",[2225,46476,2781],{"class":2235},[2225,46478,2784],{"class":2231},[2225,46480,46453],{"class":2291},[2225,46482,46350],{"class":2239},[2225,46484,2298],{"class":2235},[2225,46486,46487],{"class":2239}," userEnteredCode",[2225,46489,2298],{"class":2235},[2225,46491,2236],{"class":2235},[2225,46493,46494],{"class":2239}," lastUsedStep ",[2225,46496,2359],{"class":2235},[2225,46498,2362],{"class":2239},[2225,46500,46501,46503,46506,46508,46510,46512,46515,46517,46520,46522,46524,46527,46529],{"class":2227,"line":2288},[2225,46502,35129],{"class":2231},[2225,46504,46505],{"class":2239}," (step ",[2225,46507,33083],{"class":2235},[2225,46509,7090],{"class":2235},[2225,46511,8495],{"class":2239},[2225,46513,46514],{"class":2231},"throw",[2225,46516,8130],{"class":2235},[2225,46518,46519],{"class":2291}," Error",[2225,46521,2789],{"class":2239},[2225,46523,2805],{"class":2235},[2225,46525,46526],{"class":2252},"bad or reused code",[2225,46528,2805],{"class":2235},[2225,46530,2362],{"class":2239},[2225,46532,46533,46536,46538,46540],{"class":2227,"line":2304},[2225,46534,46535],{"class":2239},"lastUsedStep ",[2225,46537,2781],{"class":2235},[2225,46539,46474],{"class":2239},[2225,46541,46542],{"class":2763},"\u002F\u002F store it before treating the factor as passed\n",[1022,46544,2621],{"id":2620},[1111,46546,46547,46551,46555],{},[1114,46548,46549],{},[1007,46550,43861],{"href":2634},[1114,46552,46553],{},[1007,46554,301],{"href":44070},[1114,46556,46557],{},[1007,46558,313],{"href":43871},[2645,46560,30448],{},{"title":1037,"searchDepth":1038,"depth":1038,"links":46562},[46563,46564,46565],{"id":29856,"depth":1041,"text":29857},{"id":29892,"depth":1041,"text":29893},{"id":2620,"depth":1041,"text":2621},"RFC 6238 TOTP authenticator-app codes — zero-dependency secret generation, otpauth:\u002F\u002F provisioning URIs, constant-time verification.",{},{"title":333,"description":46566},"51Cwo3siSWXWY0NqpG83eqvNDxrJnLOJvnlIm4rhODY",{"id":46571,"title":337,"access":12,"apiToc":929,"body":46572,"category":1042,"description":46847,"extension":1044,"meta":46848,"navigation":1042,"nest":3303,"path":338,"seo":46849,"sinceVersion":1042,"status":1049,"stem":339,"subtitle":1042,"tier":1050,"tocExternal":1042,"__hash__":46850},"docs\u002Ffamilies\u002Fon\u002Fwebauthn.md",{"type":931,"value":46573,"toc":46842},[46574,46576,46588,46590,46608,46610,46743,46746,46823,46825,46839],[934,46575,337],{"id":337},[1061,46577,46578],{},[937,46579,46580,1368,46584,46587],{},[950,46581,46582],{},[941,46583,17049],{},[941,46585,46586],{},"pnpm add @noy-db\u002Fon-webauthn","\nHardware-key passkey unlock — Touch ID, Face ID, Windows Hello, YubiKey.",[1022,46589,29857],{"id":29856},[937,46591,46592,46595,46596,46599,46600,46602,46603,34689,46605,46607],{},[941,46593,46594],{},"enrollWebAuthn(keyring, vault, options)"," creates a WebAuthn credential —\nrequesting the PRF extension for deterministic key derivation, falling\nback to a rawId-derived key when PRF isn't available — and wraps the\nkeyring summary under it. ",[941,46597,46598],{},"unlockWebAuthn(enrollment)"," triggers the\nassertion prompt and returns an ",[941,46601,11493],{}," directly, so it plugs\nstraight into ",[941,46604,12780],{},[941,46606,9953],{}," callback with no secret\nbridge required. It's the tier-2 method with no knowledge factor at all —\nthe physical authenticator (and, optionally, the platform's user\nverification) is the whole proof.",[1022,46609,29893],{"id":29892},[2217,46611,46613],{"className":2219,"code":46612,"language":2221,"meta":1037,"style":1037},"import { createNoydb } from '@noy-db\u002Fhub'\nimport { unlockWebAuthn } from '@noy-db\u002Fon-webauthn'\n\nconst enrollment = await loadEnrollmentFromIDB() \u002F\u002F your storage of choice\n\nconst db = await createNoydb({\n  store,\n  user: 'alice',\n  getKeyring: (vault) => unlockWebAuthn(enrollment),\n})\n",[941,46614,46615,46633,46652,46656,46675,46679,46695,46701,46715,46737],{"__ignoreMap":1037},[2225,46616,46617,46619,46621,46623,46625,46627,46629,46631],{"class":2227,"line":2228},[2225,46618,2232],{"class":2231},[2225,46620,2236],{"class":2235},[2225,46622,2744],{"class":2239},[2225,46624,2243],{"class":2235},[2225,46626,2246],{"class":2231},[2225,46628,2249],{"class":2235},[2225,46630,1086],{"class":2252},[2225,46632,2256],{"class":2235},[2225,46634,46635,46637,46639,46642,46644,46646,46648,46650],{"class":2227,"line":1041},[2225,46636,2232],{"class":2231},[2225,46638,2236],{"class":2235},[2225,46640,46641],{"class":2239}," unlockWebAuthn",[2225,46643,2243],{"class":2235},[2225,46645,2246],{"class":2231},[2225,46647,2249],{"class":2235},[2225,46649,17049],{"class":2252},[2225,46651,2256],{"class":2235},[2225,46653,46654],{"class":2227,"line":1038},[2225,46655,2285],{"emptyLinePlaceholder":2284},[2225,46657,46658,46660,46663,46665,46667,46670,46672],{"class":2227,"line":2288},[2225,46659,2775],{"class":2774},[2225,46661,46662],{"class":2239}," enrollment ",[2225,46664,2781],{"class":2235},[2225,46666,2784],{"class":2231},[2225,46668,46669],{"class":2291}," loadEnrollmentFromIDB",[2225,46671,19132],{"class":2239},[2225,46673,46674],{"class":2763},"\u002F\u002F your storage of choice\n",[2225,46676,46677],{"class":2227,"line":2304},[2225,46678,2285],{"emptyLinePlaceholder":2284},[2225,46680,46681,46683,46685,46687,46689,46691,46693],{"class":2227,"line":2356},[2225,46682,2775],{"class":2774},[2225,46684,2778],{"class":2239},[2225,46686,2781],{"class":2235},[2225,46688,2784],{"class":2231},[2225,46690,2744],{"class":2291},[2225,46692,2789],{"class":2239},[2225,46694,4200],{"class":2235},[2225,46696,46697,46699],{"class":2227,"line":2830},[2225,46698,15608],{"class":2239},[2225,46700,1707],{"class":2235},[2225,46702,46703,46705,46707,46709,46711,46713],{"class":2227,"line":2861},[2225,46704,4205],{"class":2307},[2225,46706,2311],{"class":2235},[2225,46708,2249],{"class":2235},[2225,46710,6408],{"class":2252},[2225,46712,2805],{"class":2235},[2225,46714,1707],{"class":2235},[2225,46716,46717,46720,46722,46724,46726,46728,46730,46732,46735],{"class":2227,"line":4246},[2225,46718,46719],{"class":2291},"  getKeyring",[2225,46721,2311],{"class":2235},[2225,46723,953],{"class":2235},[2225,46725,2694],{"class":7052},[2225,46727,1381],{"class":2235},[2225,46729,8116],{"class":2774},[2225,46731,46641],{"class":2291},[2225,46733,46734],{"class":2239},"(enrollment)",[2225,46736,1707],{"class":2235},[2225,46738,46739,46741],{"class":2227,"line":4253},[2225,46740,2359],{"class":2235},[2225,46742,2362],{"class":2239},[937,46744,46745],{},"Enrolling, from an already-unlocked keyring:",[2217,46747,46749],{"className":2219,"code":46748,"language":2221,"meta":1037,"style":1037},"import { enrollWebAuthn } from '@noy-db\u002Fon-webauthn'\n\nconst enrollment = await enrollWebAuthn(keyring, 'acme', { rp: { name: 'Acme Notes' } })\n",[941,46750,46751,46770,46774],{"__ignoreMap":1037},[2225,46752,46753,46755,46757,46760,46762,46764,46766,46768],{"class":2227,"line":2228},[2225,46754,2232],{"class":2231},[2225,46756,2236],{"class":2235},[2225,46758,46759],{"class":2239}," enrollWebAuthn",[2225,46761,2243],{"class":2235},[2225,46763,2246],{"class":2231},[2225,46765,2249],{"class":2235},[2225,46767,17049],{"class":2252},[2225,46769,2256],{"class":2235},[2225,46771,46772],{"class":2227,"line":1041},[2225,46773,2285],{"emptyLinePlaceholder":2284},[2225,46775,46776,46778,46780,46782,46784,46786,46788,46790,46792,46794,46796,46798,46800,46803,46805,46807,46809,46811,46813,46815,46817,46819,46821],{"class":2227,"line":1038},[2225,46777,2775],{"class":2774},[2225,46779,46662],{"class":2239},[2225,46781,2781],{"class":2235},[2225,46783,2784],{"class":2231},[2225,46785,46759],{"class":2291},[2225,46787,45008],{"class":2239},[2225,46789,2298],{"class":2235},[2225,46791,2249],{"class":2235},[2225,46793,6450],{"class":2252},[2225,46795,2805],{"class":2235},[2225,46797,2298],{"class":2235},[2225,46799,2236],{"class":2235},[2225,46801,46802],{"class":2307}," rp",[2225,46804,2311],{"class":2235},[2225,46806,2236],{"class":2235},[2225,46808,8104],{"class":2307},[2225,46810,2311],{"class":2235},[2225,46812,2249],{"class":2235},[2225,46814,46377],{"class":2252},[2225,46816,2805],{"class":2235},[2225,46818,2243],{"class":2235},[2225,46820,2243],{"class":2235},[2225,46822,2362],{"class":2239},[1022,46824,2621],{"id":2620},[1111,46826,46827,46831,46835],{},[1114,46828,46829],{},[1007,46830,43861],{"href":2634},[1114,46832,46833],{},[1007,46834,309],{"href":43973},[1114,46836,46837],{},[1007,46838,305],{"href":43999},[2645,46840,46841],{},"html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .sHdIc, html code.shiki .sHdIc{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#EEFFFF;--shiki-default-font-style:italic;--shiki-dark:#BABED8;--shiki-dark-font-style:italic}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":1037,"searchDepth":1038,"depth":1038,"links":46843},[46844,46845,46846],{"id":29856,"depth":1041,"text":29857},{"id":29892,"depth":1041,"text":29893},{"id":2620,"depth":1041,"text":2621},"Hardware-key passkey unlock via WebAuthn PRF — Touch ID, Face ID, Windows Hello, YubiKey, FIDO2, no secret at all.",{},{"title":337,"description":46847},"bM3uVSe27G1Udg_ZCPBtYxN5DULE3ldRAE8df81PjZI",{"id":46852,"title":347,"access":12,"apiToc":929,"body":46853,"category":1042,"description":47077,"extension":1044,"meta":47078,"navigation":1042,"nest":3303,"path":348,"seo":47079,"sinceVersion":1042,"status":1049,"stem":349,"subtitle":1042,"tier":1050,"tocExternal":1042,"__hash__":47080},"docs\u002Ffamilies\u002Fto\u002Faws-dynamo.md",{"type":931,"value":46854,"toc":47071},[46855,46857,46870,46874,46876,46905,46907,46933,46937,46980,47056,47058,47069],[934,46856,347],{"id":347},[1061,46858,46859],{},[937,46860,46861,1368,46866,46869],{},[950,46862,46863],{},[941,46864,46865],{},"@noy-db\u002Fto-aws-dynamo",[941,46867,46868],{},"pnpm add @noy-db\u002Fto-aws-dynamo @aws-sdk\u002Fclient-dynamodb @aws-sdk\u002Flib-dynamodb","\nSingle-table DynamoDB, zero-knowledge cloud sync.",[46871,46872],"service-meta",{":rows":46873},"[{\"label\":\"Shape\",\"value\":\"record\"},{\"label\":\"Atomic CAS\",\"value\":\"✓\"},{\"label\":\"Locality\",\"value\":\"cloud\"},{\"label\":\"Speed\",\"value\":\"slow\"},{\"label\":\"Persistence\",\"value\":\"durable\"}]",[1022,46875,29857],{"id":29856},[937,46877,46878,46879,46882,46883,46886,46887,46890,46891,46893,46894,46897,46898,997,46901,46904],{},"Stores records single-table style (",[941,46880,46881],{},"pk"," = vault, ",[941,46884,46885],{},"sk"," = ",[941,46888,46889],{},"{collection}#{id}","), so\n",[941,46892,39784],{}," for a collection is a single ",[941,46895,46896],{},"Query"," rather than N requests.\n",[941,46899,46900],{},"@aws-sdk\u002Fclient-dynamodb",[941,46902,46903],{},"@aws-sdk\u002Flib-dynamodb"," are peer dependencies you\ninstall and wire up in your app.",[1022,46906,9542],{"id":2854},[1111,46908,46909,46916,46919,46927],{},[1114,46910,46911,9750,46913,46915],{},[941,46912,7811],{},[941,46914,9717],{}," on the version attribute.",[1114,46917,46918],{},"Record shape, one item per record.",[1114,46920,46921,46922,46924,46925,1520],{},"Pairs naturally with ",[941,46923,5358],{}," for blob-heavy collections via ",[941,46926,4620],{},[1114,46928,46929,46930,1520],{},"Factory: ",[941,46931,46932],{},"toAwsDynamo(options: DynamoOptions)",[1022,46934,46936],{"id":46935},"store-locator-descriptor","Store-locator descriptor",[937,46938,46939,46940,46944,46945,968,46948,1952,46951,7356,46954,46957,46958,46964,46965,46968,46969,1324,46972,46975,46976,46979],{},"See ",[1007,46941,46943],{"href":46942},".\u002Fstore-locator","store-locator descriptors"," for the general model.\n",[941,46946,46947],{},"kind: 'aws-dynamo'",[941,46949,46950],{},"class: 'cloud'",[941,46952,46953],{},"address",[941,46955,46956],{},"{ table, region?, endpoint? }",". Takes ",[950,46959,46960,46961,46963],{},"no ",[941,46962,16362],{}," argument"," — this store has no\nserializable tuning today. ",[941,46966,46967],{},"binding.client?"," carries a pre-built document\nclient, which always wins over address-derived construction; AWS credentials\narrive via a ",[941,46970,46971],{},"kind: 'aws'",[941,46973,46974],{},"StoreCredentialSource"," at ",[941,46977,46978],{},"resolve()"," time, or\nimplicitly via the SDK's default provider chain.",[2217,46981,46983],{"className":2219,"code":46982,"language":2221,"meta":1037,"style":1037},"import { dynamoStoreDescriptor, registerDynamoStore } from '@noy-db\u002Fto-aws-dynamo'\n\nconst descriptor = dynamoStoreDescriptor({ table: 'noydb', region: 'us-east-1' })\n",[941,46984,46985,47009,47013],{"__ignoreMap":1037},[2225,46986,46987,46989,46991,46994,46996,46999,47001,47003,47005,47007],{"class":2227,"line":2228},[2225,46988,2232],{"class":2231},[2225,46990,2236],{"class":2235},[2225,46992,46993],{"class":2239}," dynamoStoreDescriptor",[2225,46995,2298],{"class":2235},[2225,46997,46998],{"class":2239}," registerDynamoStore",[2225,47000,2243],{"class":2235},[2225,47002,2246],{"class":2231},[2225,47004,2249],{"class":2235},[2225,47006,46865],{"class":2252},[2225,47008,2256],{"class":2235},[2225,47010,47011],{"class":2227,"line":1041},[2225,47012,2285],{"emptyLinePlaceholder":2284},[2225,47014,47015,47017,47020,47022,47024,47026,47028,47031,47033,47035,47037,47039,47041,47043,47045,47047,47050,47052,47054],{"class":2227,"line":1038},[2225,47016,2775],{"class":2774},[2225,47018,47019],{"class":2239}," descriptor ",[2225,47021,2781],{"class":2235},[2225,47023,46993],{"class":2291},[2225,47025,2789],{"class":2239},[2225,47027,2792],{"class":2235},[2225,47029,47030],{"class":2307}," table",[2225,47032,2311],{"class":2235},[2225,47034,2249],{"class":2235},[2225,47036,149],{"class":2252},[2225,47038,2805],{"class":2235},[2225,47040,2298],{"class":2235},[2225,47042,29953],{"class":2307},[2225,47044,2311],{"class":2235},[2225,47046,2249],{"class":2235},[2225,47048,47049],{"class":2252},"us-east-1",[2225,47051,2805],{"class":2235},[2225,47053,2243],{"class":2235},[2225,47055,2362],{"class":2239},[1022,47057,2621],{"id":2620},[1111,47059,47060,47065],{},[1114,47061,47062],{},[1007,47063,47064],{"href":2634},"to-* family",[1114,47066,47067],{},[1007,47068,129],{"href":30830},[2645,47070,37016],{},{"title":1037,"searchDepth":1038,"depth":1038,"links":47072},[47073,47074,47075,47076],{"id":29856,"depth":1041,"text":29857},{"id":2854,"depth":1041,"text":9542},{"id":46935,"depth":1041,"text":46936},{"id":2620,"depth":1041,"text":2621},"AWS DynamoDB adapter for noy-db — single-table design, so loadAll() is a single Query, with atomic CAS via ConditionExpression.",{},{"title":347,"description":47077},"CVxO3kiCvf6K8LGizKt4QD_VuhzCO28cveR9V7_QJs0",{"id":47082,"title":129,"access":12,"apiToc":929,"body":47083,"category":1042,"description":47316,"extension":1044,"meta":47317,"navigation":1042,"nest":3303,"path":351,"seo":47318,"sinceVersion":1042,"status":1049,"stem":352,"subtitle":1042,"tier":1050,"tocExternal":1042,"__hash__":47319},"docs\u002Ffamilies\u002Fto\u002Faws-s3.md",{"type":931,"value":47084,"toc":47310},[47085,47087,47099,47102,47104,47118,47120,47180,47182,47216,47290,47292,47308],[934,47086,129],{"id":129},[1061,47088,47089],{},[937,47090,47091,1368,47095,47098],{},[950,47092,47093],{},[941,47094,29876],{},[941,47096,47097],{},"pnpm add @noy-db\u002Fto-aws-s3 @aws-sdk\u002Fclient-s3","\nEncrypted object storage with zero-knowledge cloud sync.",[46871,47100],{":rows":47101},"[{\"label\":\"Shape\",\"value\":\"record + bundle\"},{\"label\":\"Atomic CAS\",\"value\":\"✓\"},{\"label\":\"Locality\",\"value\":\"cloud\"},{\"label\":\"Speed\",\"value\":\"slow\"},{\"label\":\"Persistence\",\"value\":\"durable\"}]",[1022,47103,29857],{"id":29856},[937,47105,47106,47107,47109,47110,47113,47114,47117],{},"Stores one encrypted envelope per record as an S3 object. ",[941,47108,30131],{}," is\na peer dependency you install and pass in as an ",[941,47111,47112],{},"S3Client",". Also ships ",[941,47115,47116],{},"s3Bundle()","\nfor storing a whole vault as a single bundle rather than record-per-object.",[1022,47119,9542],{"id":2854},[1111,47121,47122,47155,47164,47171],{},[1114,47123,47124,47131,47132,47135,47136,997,47139,47142,47143,47146,47147,47149,47150,957],{},[950,47125,47126,47128,47129],{},[941,47127,8710],{}," must not end in ",[941,47130,1625],{}," — the separator is appended for you, and since\n",[941,47133,47134],{},"0.7.0-pre.0"," a trailing slash is refused at construction rather than silently\nproducing double-separator keys. ",[941,47137,47138],{},"tenant-a",[941,47140,47141],{},"tenant-a\u002F"," address ",[1092,47144,47145],{},"different\nobjects",", so if a store already holds data under a trailing-slash prefix, copy\nthe objects to the single-separator keys ",[950,47148,11357],{}," fixing the config\n(",[1007,47151,47154],{"href":47152,"rel":47153},"https:\u002F\u002Fgithub.com\u002Fnoy-db\u002Fto\u002Fissues\u002F109",[3371],"noy-db-to#109",[1114,47156,47157,47159,47160,1261,47162,957],{},[941,47158,7811],{}," via conditional writes (",[941,47161,9732],{},[941,47163,9735],{},[1114,47165,47166,47168,47169,1520],{},[941,47167,39784],{}," is O(N) requests — fine up to ~10K records; for larger vaults keep\nindexed records in DynamoDB and route blobs to S3 via ",[941,47170,4620],{},[1114,47172,47173,47174,47176,47177,1520],{},"Record shape by default, or bundle shape via ",[941,47175,47116],{},". Factory: ",[941,47178,47179],{},"toAwsS3(options: S3Options)",[1022,47181,46936],{"id":46935},[937,47183,46939,47184,46944,47186,968,47189,1952,47191,7356,47193,5502,47196,7356,47198,1952,47201,47203,47204,47206,47207,47209,47210,1324,47212,46975,47214,46979],{},[1007,47185,46943],{"href":46942},[941,47187,47188],{},"kind: 'aws-s3'",[941,47190,46950],{},[941,47192,46953],{},[941,47194,47195],{},"{ bucket, region?, prefix? }",[941,47197,16362],{},[941,47199,47200],{},"{ clockUncertaintyMs? }",[941,47202,46967],{}," carries a\npre-built ",[941,47205,47112],{},", which always wins over region\u002Fcredentials-derived\nconstruction — this is ",[941,47208,5358],{},"'s binding-slot citizen. AWS credentials\narrive via a ",[941,47211,46971],{},[941,47213,46974],{},[941,47215,46978],{},[2217,47217,47219],{"className":2219,"code":47218,"language":2221,"meta":1037,"style":1037},"import { s3StoreDescriptor, registerS3Store } from '@noy-db\u002Fto-aws-s3'\n\nconst descriptor = s3StoreDescriptor({ bucket: 'my-vault', region: 'us-east-1' })\n",[941,47220,47221,47245,47249],{"__ignoreMap":1037},[2225,47222,47223,47225,47227,47230,47232,47235,47237,47239,47241,47243],{"class":2227,"line":2228},[2225,47224,2232],{"class":2231},[2225,47226,2236],{"class":2235},[2225,47228,47229],{"class":2239}," s3StoreDescriptor",[2225,47231,2298],{"class":2235},[2225,47233,47234],{"class":2239}," registerS3Store",[2225,47236,2243],{"class":2235},[2225,47238,2246],{"class":2231},[2225,47240,2249],{"class":2235},[2225,47242,29876],{"class":2252},[2225,47244,2256],{"class":2235},[2225,47246,47247],{"class":2227,"line":1041},[2225,47248,2285],{"emptyLinePlaceholder":2284},[2225,47250,47251,47253,47255,47257,47259,47261,47263,47265,47267,47269,47272,47274,47276,47278,47280,47282,47284,47286,47288],{"class":2227,"line":1038},[2225,47252,2775],{"class":2774},[2225,47254,47019],{"class":2239},[2225,47256,2781],{"class":2235},[2225,47258,47229],{"class":2291},[2225,47260,2789],{"class":2239},[2225,47262,2792],{"class":2235},[2225,47264,29939],{"class":2307},[2225,47266,2311],{"class":2235},[2225,47268,2249],{"class":2235},[2225,47270,47271],{"class":2252},"my-vault",[2225,47273,2805],{"class":2235},[2225,47275,2298],{"class":2235},[2225,47277,29953],{"class":2307},[2225,47279,2311],{"class":2235},[2225,47281,2249],{"class":2235},[2225,47283,47049],{"class":2252},[2225,47285,2805],{"class":2235},[2225,47287,2243],{"class":2235},[2225,47289,2362],{"class":2239},[1022,47291,2621],{"id":2620},[1111,47293,47294,47298,47303],{},[1114,47295,47296],{},[1007,47297,47064],{"href":2634},[1114,47299,47300],{},[1007,47301,347],{"href":47302},".\u002Faws-dynamo",[1114,47304,47305],{},[1007,47306,375],{"href":47307},".\u002Fcloudflare-r2",[2645,47309,37016],{},{"title":1037,"searchDepth":1038,"depth":1038,"links":47311},[47312,47313,47314,47315],{"id":29856,"depth":1041,"text":29857},{"id":2854,"depth":1041,"text":9542},{"id":46935,"depth":1041,"text":46936},{"id":2620,"depth":1041,"text":2621},"AWS S3 adapter for noy-db — one encrypted object per record, atomic CAS via conditional writes, plus a whole-vault bundle mode.",{},{"title":129,"description":47316},"TVQZcumMd10yfVptI_u5dwp6ywlqUKPk9eZU5U3eLjY",{"id":47321,"title":354,"access":12,"apiToc":929,"body":47322,"category":1042,"description":47954,"extension":1044,"meta":47955,"navigation":1042,"nest":3303,"path":355,"seo":47956,"sinceVersion":1042,"status":1049,"stem":356,"subtitle":1042,"tier":1050,"tocExternal":1042,"__hash__":47957},"docs\u002Ffamilies\u002Fto\u002Fbrowser-fs.md",{"type":931,"value":47323,"toc":47943},[47324,47326,47345,47348,47350,47361,47369,47375,47459,47485,47489,47496,47505,47509,47530,47539,47615,47618,47625,47672,47681,47733,47737,47844,47861,47863,47910,47912,47940],[934,47325,354],{"id":354},[1061,47327,47328],{},[937,47329,47330,1368,47335,47338,47339,47344],{},[950,47331,47332],{},[941,47333,47334],{},"@noy-db\u002Fto-browser-fs",[941,47336,47337],{},"pnpm add @noy-db\u002Fto-browser-fs","\nThe browser sibling to ",[1007,47340,47342],{"href":47341},".\u002Ffile",[941,47343,7461],{}," — same on-disk layout, a\ndirectory handle instead of a path.",[46871,47346],{":rows":47347},"[{\"label\":\"Shape\",\"value\":\"record\"},{\"label\":\"Atomic CAS\",\"value\":\"✗\"},{\"label\":\"Locality\",\"value\":\"local\"},{\"label\":\"Speed\",\"value\":\"medium\"},{\"label\":\"Persistence\",\"value\":\"durable\"}]",[1022,47349,29857],{"id":29856},[937,47351,47352,47353,47356,47357,47360],{},"Takes a ",[941,47354,47355],{},"FileSystemDirectoryHandle"," rather than a path, so a page can read and\nwrite a folder the operating system has already mounted — a Windows SMB share,\na NAS volume, a USB stick. ",[950,47358,47359],{},"The adapter never speaks a wire protocol",": once\nthe volume is mounted, the protocol stops mattering.",[937,47362,47363,47364,47368],{},"The layout is byte-identical to ",[1007,47365,47366],{"href":47341},[941,47367,7461],{},", so a Node process and a\nbrowser can point at the same folder and read each other's writes:",[2217,47370,47373],{"className":47371,"code":47372,"language":5672},[5670],"{root}\u002F\n  {vault}\u002F\n    {collection}\u002F\n      {id}.json          ← EncryptedEnvelope, pretty-printed by default\n    _keyring\u002F\n      {userId}.json\n    _sync\u002F\n      meta.json\n",[941,47374,47372],{"__ignoreMap":1037},[2217,47376,47378],{"className":2219,"code":47377,"language":2221,"meta":1037,"style":1037},"import { toBrowserFs } from '@noy-db\u002Fto-browser-fs'\n\nconst handle = await window.showDirectoryPicker({ mode: 'readwrite' })\nconst store = toBrowserFs({ handle })\n",[941,47379,47380,47399,47403,47439],{"__ignoreMap":1037},[2225,47381,47382,47384,47386,47389,47391,47393,47395,47397],{"class":2227,"line":2228},[2225,47383,2232],{"class":2231},[2225,47385,2236],{"class":2235},[2225,47387,47388],{"class":2239}," toBrowserFs",[2225,47390,2243],{"class":2235},[2225,47392,2246],{"class":2231},[2225,47394,2249],{"class":2235},[2225,47396,47334],{"class":2252},[2225,47398,2256],{"class":2235},[2225,47400,47401],{"class":2227,"line":1041},[2225,47402,2285],{"emptyLinePlaceholder":2284},[2225,47404,47405,47407,47409,47411,47413,47416,47418,47421,47423,47425,47427,47429,47431,47433,47435,47437],{"class":2227,"line":1038},[2225,47406,2775],{"class":2774},[2225,47408,40799],{"class":2239},[2225,47410,2781],{"class":2235},[2225,47412,2784],{"class":2231},[2225,47414,47415],{"class":2239}," window",[2225,47417,1520],{"class":2235},[2225,47419,47420],{"class":2291},"showDirectoryPicker",[2225,47422,2789],{"class":2239},[2225,47424,2792],{"class":2235},[2225,47426,32303],{"class":2307},[2225,47428,2311],{"class":2235},[2225,47430,2249],{"class":2235},[2225,47432,9587],{"class":2252},[2225,47434,2805],{"class":2235},[2225,47436,2243],{"class":2235},[2225,47438,2362],{"class":2239},[2225,47440,47441,47443,47445,47447,47449,47451,47453,47455,47457],{"class":2227,"line":2288},[2225,47442,2775],{"class":2774},[2225,47444,39676],{"class":2239},[2225,47446,2781],{"class":2235},[2225,47448,47388],{"class":2291},[2225,47450,2789],{"class":2239},[2225,47452,2792],{"class":2235},[2225,47454,40799],{"class":2239},[2225,47456,2359],{"class":2235},[2225,47458,2362],{"class":2239},[937,47460,47461,10746,47464,47466,47467,30200,47469,47471,47472,47474,47475,30200,47478,47480,47481,47484],{},[941,47462,47463],{},"BrowserFsOptions",[941,47465,36368],{}," (required), ",[941,47468,31036],{},[941,47470,11790],{},", matching\n",[941,47473,7461],{},") and ",[941,47476,47477],{},"verifyWrites",[941,47479,11790],{}," — reads every write back and throws\n",[941,47482,47483],{},"FsWriteVerifyError"," on a mismatch, at the cost of one extra read per write).",[1022,47486,47488],{"id":47487},"️-chromium-desktop-only-by-construction","⚠️ Chromium desktop only, by construction",[937,47490,47491,47492,47495],{},"This store needs the File System Access API. That is not a gap waiting to be\nfilled: ",[950,47493,47494],{},"Safari's OPFS is origin-private and cannot see a mounted volume at\nall",", so no amount of polyfilling reaches the same capability.",[937,47497,47498,47499,2557,47502,47504],{},"Treat it as progressive enhancement and never as the only path, or users on\nother browsers have no store. The degradation is cheap to write, because\n",[941,47500,47501],{},"recallDirectory()",[941,47503,6651],{}," where the API is absent — a missing\ncapability arrives as \"no folder chosen\" rather than as an exception.",[1022,47506,47508],{"id":47507},"permissions-spend-the-gesture-first","Permissions — spend the gesture first",[937,47510,47511,47514,47515,47518,47519,47522,47523,47525,47526,47529],{},[941,47512,47513],{},"requestPermission()"," needs ",[950,47516,47517],{},"transient user activation"," (~5 s in Chrome), and\na vault unlock's PBKDF2 blows straight past that window. So ",[941,47520,47521],{},"requestAccess()"," is\nthe ",[950,47524,1320],{}," method in this package that prompts — reads, writes, sync and ",[941,47527,47528],{},"ping","\nnever do.",[937,47531,46418,47532,47535,47536,47538],{},[950,47533,47534],{},"Order matters, and the intuitive order is the broken one."," Ask for\npermission ",[1092,47537,11357],{}," the slow work, not when the first write needs it:",[2217,47540,47542],{"className":2219,"code":47541,"language":2221,"meta":1037,"style":1037},"if (await store.access() !== 'granted') {\n  if (!await store.requestAccess()) return   \u002F\u002F user declined\n}\nawait unlockVault(password)                  \u002F\u002F activation already spent\n",[941,47543,47544,47574,47598,47602],{"__ignoreMap":1037},[2225,47545,47546,47548,47550,47552,47554,47556,47559,47561,47563,47565,47568,47570,47572],{"class":2227,"line":2228},[2225,47547,35129],{"class":2231},[2225,47549,953],{"class":2239},[2225,47551,6500],{"class":2231},[2225,47553,7564],{"class":2239},[2225,47555,1520],{"class":2235},[2225,47557,47558],{"class":2291},"access",[2225,47560,19132],{"class":2239},[2225,47562,32545],{"class":2235},[2225,47564,2249],{"class":2235},[2225,47566,47567],{"class":2252},"granted",[2225,47569,2805],{"class":2235},[2225,47571,8495],{"class":2239},[2225,47573,4200],{"class":2235},[2225,47575,47576,47578,47580,47582,47584,47586,47588,47591,47593,47595],{"class":2227,"line":1041},[2225,47577,7608],{"class":2231},[2225,47579,953],{"class":2307},[2225,47581,35134],{"class":2235},[2225,47583,6500],{"class":2231},[2225,47585,7564],{"class":2239},[2225,47587,1520],{"class":2235},[2225,47589,47590],{"class":2291},"requestAccess",[2225,47592,35140],{"class":2307},[2225,47594,40426],{"class":2231},[2225,47596,47597],{"class":2763},"   \u002F\u002F user declined\n",[2225,47599,47600],{"class":2227,"line":1038},[2225,47601,7351],{"class":2235},[2225,47603,47604,47606,47609,47612],{"class":2227,"line":2288},[2225,47605,6500],{"class":2231},[2225,47607,47608],{"class":2291}," unlockVault",[2225,47610,47611],{"class":2239},"(password)                  ",[2225,47613,47614],{"class":2763},"\u002F\u002F activation already spent\n",[937,47616,47617],{},"Unlock first and the activation has expired by the time you ask, so the prompt\nnever appears and the write fails on a permission you were never able to\nrequest.",[1591,47619,47621,47624],{"id":47620},"access-reports-four-states-not-two",[941,47622,47623],{},"access()"," reports four states, not two",[2217,47626,47628],{"className":2219,"code":47627,"language":2221,"meta":1037,"style":1037},"type FsAccessState = 'granted' | 'prompt' | 'denied' | 'unreachable'\n",[941,47629,47630],{"__ignoreMap":1037},[2225,47631,47632,47634,47637,47639,47641,47643,47645,47647,47649,47652,47654,47656,47658,47661,47663,47665,47667,47670],{"class":2227,"line":2228},[2225,47633,6339],{"class":2774},[2225,47635,47636],{"class":6342}," FsAccessState",[2225,47638,6346],{"class":2235},[2225,47640,2249],{"class":2235},[2225,47642,47567],{"class":2252},[2225,47644,2805],{"class":2235},[2225,47646,7087],{"class":2235},[2225,47648,2249],{"class":2235},[2225,47650,47651],{"class":2252},"prompt",[2225,47653,2805],{"class":2235},[2225,47655,7087],{"class":2235},[2225,47657,2249],{"class":2235},[2225,47659,47660],{"class":2252},"denied",[2225,47662,2805],{"class":2235},[2225,47664,7087],{"class":2235},[2225,47666,2249],{"class":2235},[2225,47668,47669],{"class":2252},"unreachable",[2225,47671,2256],{"class":2235},[937,47673,47674,47676,47677,47680],{},[941,47675,47623],{}," never prompts, and only touches the volume when permission is already\ngranted — probing a dead mount can block for seconds. The four states exist\nbecause ",[950,47678,47679],{},"\"click to reconnect\" and \"you're off the office network\" are different\nsituations for the user",", and must not be told apart by string-matching an\nerror message.",[1338,47682,47683,47692],{},[1341,47684,47685],{},[1344,47686,47687,47689],{},[1347,47688,4656],{},[1347,47690,47691],{},"what to show",[1357,47693,47694,47703,47715,47724],{},[1344,47695,47696,47700],{},[1362,47697,47698],{},[941,47699,47567],{},[1362,47701,47702],{},"nothing — proceed",[1344,47704,47705,47709],{},[1362,47706,47707],{},[941,47708,47651],{},[1362,47710,47711,47712,47714],{},"a button; ",[941,47713,47521],{}," must run inside its click",[1344,47716,47717,47721],{},[1362,47718,47719],{},[941,47720,47660],{},[1362,47722,47723],{},"the user said no — offer to pick a different folder",[1344,47725,47726,47730],{},[1362,47727,47728],{},[941,47729,47669],{},[1362,47731,47732],{},"the volume is gone: unplugged, unmounted, off the network",[1022,47734,47736],{"id":47735},"remembering-a-folder-across-reloads","Remembering a folder across reloads",[2217,47738,47740],{"className":2219,"code":47739,"language":2221,"meta":1037,"style":1037},"import { rememberDirectory, recallDirectory, forgetDirectory } from '@noy-db\u002Fto-browser-fs'\n\nawait rememberDirectory('vault-folder', handle)   \u002F\u002F persists the handle in IndexedDB\nconst again = await recallDirectory('vault-folder')  \u002F\u002F → handle, or null\nawait forgetDirectory('vault-folder')             \u002F\u002F user chose elsewhere, or signed out\n",[941,47741,47742,47771,47775,47798,47824],{"__ignoreMap":1037},[2225,47743,47744,47746,47748,47751,47753,47756,47758,47761,47763,47765,47767,47769],{"class":2227,"line":2228},[2225,47745,2232],{"class":2231},[2225,47747,2236],{"class":2235},[2225,47749,47750],{"class":2239}," rememberDirectory",[2225,47752,2298],{"class":2235},[2225,47754,47755],{"class":2239}," recallDirectory",[2225,47757,2298],{"class":2235},[2225,47759,47760],{"class":2239}," forgetDirectory",[2225,47762,2243],{"class":2235},[2225,47764,2246],{"class":2231},[2225,47766,2249],{"class":2235},[2225,47768,47334],{"class":2252},[2225,47770,2256],{"class":2235},[2225,47772,47773],{"class":2227,"line":1041},[2225,47774,2285],{"emptyLinePlaceholder":2284},[2225,47776,47777,47779,47781,47783,47785,47788,47790,47792,47795],{"class":2227,"line":1038},[2225,47778,6500],{"class":2231},[2225,47780,47750],{"class":2291},[2225,47782,2789],{"class":2239},[2225,47784,2805],{"class":2235},[2225,47786,47787],{"class":2252},"vault-folder",[2225,47789,2805],{"class":2235},[2225,47791,2298],{"class":2235},[2225,47793,47794],{"class":2239}," handle)   ",[2225,47796,47797],{"class":2763},"\u002F\u002F persists the handle in IndexedDB\n",[2225,47799,47800,47802,47805,47807,47809,47811,47813,47815,47817,47819,47821],{"class":2227,"line":2288},[2225,47801,2775],{"class":2774},[2225,47803,47804],{"class":2239}," again ",[2225,47806,2781],{"class":2235},[2225,47808,2784],{"class":2231},[2225,47810,47755],{"class":2291},[2225,47812,2789],{"class":2239},[2225,47814,2805],{"class":2235},[2225,47816,47787],{"class":2252},[2225,47818,2805],{"class":2235},[2225,47820,27226],{"class":2239},[2225,47822,47823],{"class":2763},"\u002F\u002F → handle, or null\n",[2225,47825,47826,47828,47830,47832,47834,47836,47838,47841],{"class":2227,"line":2304},[2225,47827,6500],{"class":2231},[2225,47829,47760],{"class":2291},[2225,47831,2789],{"class":2239},[2225,47833,2805],{"class":2235},[2225,47835,47787],{"class":2252},[2225,47837,2805],{"class":2235},[2225,47839,47840],{"class":2239},")             ",[2225,47842,47843],{"class":2763},"\u002F\u002F user chose elsewhere, or signed out\n",[937,47845,1649,47846,47849,47850,47853,47854,47856,47857,47860],{},[950,47847,47848],{},"A recalled handle is usable but not necessarily permitted."," Persisting it\nsaves the user re-picking the folder; it does not carry the permission with it.\nAlways re-check ",[941,47851,47852],{},"store.access()"," after recalling, and be ready to call\n",[941,47855,47521],{}," from a fresh user gesture — ",[941,47858,47859],{},"'prompt'"," is a normal state on a\nnew page load, not an error.",[1022,47862,9542],{"id":2854},[1111,47864,47865,47882,47891,47894,47905],{},[1114,47866,47867,47870,47871,47875,47876,47881],{},[941,47868,47869],{},"casAtomic: false"," — the version check is read-then-write, as with\n",[1007,47872,47873],{"href":47341},[941,47874,7461],{},". Two tabs writing the same record can interleave; use\n",[1007,47877,47879],{"href":47878},".\u002Fbrowser-idb",[941,47880,9507],{}," as the primary store where that matters.",[1114,47883,47884,1764,47887,47890],{},[941,47885,47886],{},"txAtomic: false",[941,47888,47889],{},"db.transaction()"," takes the per-record OCC path.",[1114,47892,47893],{},"Record shape only; no whole-vault bundle store.",[1114,47895,47896,47897,968,47900,968,47903,1520],{},"Errors: ",[941,47898,47899],{},"FsPermissionError",[941,47901,47902],{},"FsUnreachableError",[941,47904,47483],{},[1114,47906,46929,47907,1520],{},[941,47908,47909],{},"toBrowserFs(options: BrowserFsOptions)",[1022,47911,2621],{"id":2620},[1111,47913,47914,47918,47926,47931,47936],{},[1114,47915,47916],{},[1007,47917,47064],{"href":2634},[1114,47919,47920,47922,47923,7564],{},[1007,47921,358],{"href":47878}," — the recommended browser ",[1092,47924,47925],{},"primary",[1114,47927,47928,47930],{},[1007,47929,383],{"href":47341}," — the Node sibling sharing this layout",[1114,47932,47933,47935],{},[1007,47934,523],{"href":524}," — this store as a backup target rather than a primary store",[1114,47937,47938],{},[1007,47939,860],{"href":861},[2645,47941,47942],{},"html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}",{"title":1037,"searchDepth":1038,"depth":1038,"links":47944},[47945,47946,47947,47951,47952,47953],{"id":29856,"depth":1041,"text":29857},{"id":47487,"depth":1041,"text":47488},{"id":47507,"depth":1041,"text":47508,"children":47948},[47949],{"id":47620,"depth":1038,"text":47950},"access() reports four states, not two",{"id":47735,"depth":1041,"text":47736},{"id":2854,"depth":1041,"text":9542},{"id":2620,"depth":1041,"text":2621},"File System Access store — a page reads and writes a folder the OS has mounted, in a layout byte-identical to to-file. Chromium-only by construction.",{},{"title":354,"description":47954},"l3B0oUFsWzEHhyHgLSy0yrnZmDcy2VQQgAp7Wm3xAX4",{"id":47959,"title":358,"access":12,"apiToc":929,"body":47960,"category":1042,"description":48026,"extension":1044,"meta":48027,"navigation":1042,"nest":3303,"path":359,"seo":48028,"sinceVersion":1042,"status":1049,"stem":360,"subtitle":1042,"tier":6922,"tocExternal":1042,"__hash__":48029},"docs\u002Ffamilies\u002Fto\u002Fbrowser-idb.md",{"type":931,"value":47961,"toc":48021},[47962,47964,47976,47979,47981,47987,47989,48008,48010],[934,47963,358],{"id":358},[1061,47965,47966],{},[937,47967,47968,1368,47972,47975],{},[950,47969,47970],{},[941,47971,9573],{},[941,47973,47974],{},"pnpm add @noy-db\u002Fto-browser-idb","\nThe recommended browser store for PWAs and web apps.",[46871,47977],{":rows":47978},"[{\"label\":\"Shape\",\"value\":\"record\"},{\"label\":\"Atomic CAS\",\"value\":\"✓\"},{\"label\":\"Locality\",\"value\":\"local\"},{\"label\":\"Speed\",\"value\":\"fast\"},{\"label\":\"Persistence\",\"value\":\"durable\"}]",[1022,47980,29857],{"id":29856},[937,47982,47983,47984,47986],{},"Stores each record as an encrypted envelope in IndexedDB. IndexedDB quota is\ntypically 50–80% of available disk, far beyond what ",[941,47985,41955],{},"'s ~5 MB cap\nallows, making it the default choice for browser-hosted vaults.",[1022,47988,9542],{"id":2854},[1111,47990,47991,47999,48002],{},[1114,47992,47993,47995,47996,47998],{},[941,47994,7811],{}," — check and write happen inside a single ",[941,47997,9587],{}," IDB\ntransaction per put, so writes from two tabs cannot interleave.",[1114,48000,48001],{},"Record shape, one envelope per record.",[1114,48003,48004,48005,1520],{},"Optional key obfuscation. Factory: ",[941,48006,48007],{},"toBrowserIdb(options?: BrowserIdbOptions)",[1022,48009,2621],{"id":2620},[1111,48011,48012,48016],{},[1114,48013,48014],{},[1007,48015,47064],{"href":2634},[1114,48017,48018],{},[1007,48019,362],{"href":48020},".\u002Fbrowser-local",{"title":1037,"searchDepth":1038,"depth":1038,"links":48022},[48023,48024,48025],{"id":29856,"depth":1041,"text":29857},{"id":2854,"depth":1041,"text":9542},{"id":2620,"depth":1041,"text":2621},"IndexedDB adapter for noy-db with optional key obfuscation — the recommended browser store for PWAs and web apps.",{},{"title":358,"description":48026},"Ekp1YD1RLgUkXizf7adeu-2DJDNJXoiqssRR2lURSN8",{"id":48031,"title":362,"access":12,"apiToc":929,"body":48032,"category":1042,"description":48215,"extension":1044,"meta":48216,"navigation":1042,"nest":3303,"path":363,"seo":48217,"sinceVersion":1042,"status":1049,"stem":364,"subtitle":1042,"tier":1050,"tocExternal":1042,"__hash__":48218},"docs\u002Ffamilies\u002Fto\u002Fbrowser-local.md",{"type":931,"value":48033,"toc":48209},[48034,48036,48051,48054,48056,48071,48073,48088,48090,48121,48193,48195,48206],[934,48035,362],{"id":362},[1061,48037,48038],{},[937,48039,48040,1368,48045,48048,48050],{},[950,48041,48042],{},[941,48043,48044],{},"@noy-db\u002Fto-browser-local",[941,48046,48047],{},"pnpm add @noy-db\u002Fto-browser-local",[941,48049,41955],{},"-backed, for small vaults.",[46871,48052],{":rows":48053},"[{\"label\":\"Shape\",\"value\":\"record\"},{\"label\":\"Atomic CAS\",\"value\":\"✗\"},{\"label\":\"Locality\",\"value\":\"local\"},{\"label\":\"Speed\",\"value\":\"fast\"},{\"label\":\"Persistence\",\"value\":\"durable\"}]",[1022,48055,29857],{"id":29856},[937,48057,48058,48059,48061,48062,48065,48066,48070],{},"Stores encrypted envelopes in ",[941,48060,41955],{}," — synchronous access, small vaults\nonly (under the browser's ~5 MB cap). Optional ",[941,48063,48064],{},"obfuscate"," mode hashes storage\nkeys so vault\u002Fcollection\u002Frecord names aren't readable in DevTools. Prefer\n",[1007,48067,48068],{"href":47878},[941,48069,358],{}," for most browser apps.",[1022,48072,9542],{"id":2854},[1111,48074,48075,48080,48083],{},[1114,48076,48077,48079],{},[941,48078,47869],{}," — single-tab only; cross-tab writes share storage with no\nserialization, so concurrent tabs can race.",[1114,48081,48082],{},"Record shape, one key per record.",[1114,48084,46929,48085,1520],{},[941,48086,48087],{},"toBrowserLocal(options?: BrowserLocalOptions)",[1022,48089,46936],{"id":46935},[937,48091,46939,48092,46944,48094,968,48097,1952,48100,7356,48102,41721,48105,7356,48107,48110,48111,48113,48114,997,48117,48120],{},[1007,48093,46943],{"href":46942},[941,48095,48096],{},"kind: 'browser-local'",[941,48098,48099],{},"class: 'browser'",[941,48101,46953],{},[941,48103,48104],{},"{ prefix? }",[941,48106,16362],{},[941,48108,48109],{},"{ obfuscate?, clockUncertaintyMs? }",". The simplest citizen of\nits tier: ",[941,48112,41955],{}," needs no credentials and no transport, so both\n",[941,48115,48116],{},"binding",[941,48118,48119],{},"credentials"," are ignored at resolve time.",[2217,48122,48124],{"className":2219,"code":48123,"language":2221,"meta":1037,"style":1037},"import { browserLocalStoreDescriptor, registerBrowserLocalStore } from '@noy-db\u002Fto-browser-local'\n\nconst descriptor = browserLocalStoreDescriptor({ prefix: 'myapp' }, { obfuscate: true })\n",[941,48125,48126,48150,48154],{"__ignoreMap":1037},[2225,48127,48128,48130,48132,48135,48137,48140,48142,48144,48146,48148],{"class":2227,"line":2228},[2225,48129,2232],{"class":2231},[2225,48131,2236],{"class":2235},[2225,48133,48134],{"class":2239}," browserLocalStoreDescriptor",[2225,48136,2298],{"class":2235},[2225,48138,48139],{"class":2239}," registerBrowserLocalStore",[2225,48141,2243],{"class":2235},[2225,48143,2246],{"class":2231},[2225,48145,2249],{"class":2235},[2225,48147,48044],{"class":2252},[2225,48149,2256],{"class":2235},[2225,48151,48152],{"class":2227,"line":1041},[2225,48153,2285],{"emptyLinePlaceholder":2284},[2225,48155,48156,48158,48160,48162,48164,48166,48168,48170,48172,48174,48177,48179,48181,48183,48185,48187,48189,48191],{"class":2227,"line":1038},[2225,48157,2775],{"class":2774},[2225,48159,47019],{"class":2239},[2225,48161,2781],{"class":2235},[2225,48163,48134],{"class":2291},[2225,48165,2789],{"class":2239},[2225,48167,2792],{"class":2235},[2225,48169,8638],{"class":2307},[2225,48171,2311],{"class":2235},[2225,48173,2249],{"class":2235},[2225,48175,48176],{"class":2252},"myapp",[2225,48178,2805],{"class":2235},[2225,48180,10965],{"class":2235},[2225,48182,2236],{"class":2235},[2225,48184,42340],{"class":2307},[2225,48186,2311],{"class":2235},[2225,48188,2322],{"class":2321},[2225,48190,2243],{"class":2235},[2225,48192,2362],{"class":2239},[1022,48194,2621],{"id":2620},[1111,48196,48197,48201],{},[1114,48198,48199],{},[1007,48200,47064],{"href":2634},[1114,48202,48203,48205],{},[1007,48204,358],{"href":47878}," — the recommended browser store",[2645,48207,48208],{},"html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .sfNiH, html code.shiki .sfNiH{--shiki-light:#FF5370;--shiki-default:#FF9CAC;--shiki-dark:#FF9CAC}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":1037,"searchDepth":1038,"depth":1038,"links":48210},[48211,48212,48213,48214],{"id":29856,"depth":1041,"text":29857},{"id":2854,"depth":1041,"text":9542},{"id":46935,"depth":1041,"text":46936},{"id":2620,"depth":1041,"text":2621},"localStorage adapter for noy-db with optional key obfuscation — small vaults under the 5 MB cap, synchronous access.",{},{"title":362,"description":48215},"8tySKLIE2OlaJwzZ-XCrdILwOFIg9FosVBEBFXQvFGQ",{"id":48220,"title":366,"access":12,"apiToc":929,"body":48221,"category":1042,"description":48888,"extension":1044,"meta":48889,"navigation":48890,"nest":3303,"path":367,"seo":48891,"sinceVersion":1042,"status":1049,"stem":368,"subtitle":1042,"tier":1050,"tocExternal":1042,"__hash__":48892},"docs\u002Ffamilies\u002Fto\u002Fchoosing-a-store.md",{"type":931,"value":48222,"toc":48880},[48223,48226,48241,48245,48253,48256,48259,48263,48419,48432,48436,48439,48470,48473,48566,48570,48573,48583,48619,48630,48695,48705,48715,48719,48811,48832,48847,48849,48878],[934,48224,366],{"id":48225},"choosing-a-store",[937,48227,30792,48228,48230,48231,48233,48234,48240],{},[941,48229,1779],{}," factory returns the same ",[941,48232,3154],{}," contract, so choosing between them is a plain conditional at construction time. There is ",[950,48235,46960,48236,48239],{},[941,48237,48238],{},"pickStore()"," helper",", and that absence is a decision rather than an oversight.",[1022,48242,48244],{"id":48243},"why-there-is-no-helper","Why there is no helper",[937,48246,3289,48247,48249,48250,48252],{},[941,48248,48238],{}," in the hub would have to know about the ",[941,48251,1779],{}," packages in order to choose between them. That means either importing them — which the one-way seam law forbids, since the hub may never import a satellite — or taking them as arguments, at which point it is the conditional you already wrote with a function call wrapped around it.",[937,48254,48255],{},"It would also be the hub's only API aware of satellite packages, and it would sit on the always-on floor that the bundle-size gate exists to keep small.",[937,48257,48258],{},"Environment detection is genuinely the app's job. It depends on bundler targets, SSR, React Native, Electron main-versus-renderer, and test environments — things the hub cannot see and should not guess.",[1022,48260,48262],{"id":48261},"the-pattern","The pattern",[2217,48264,48266],{"className":2219,"code":48265,"language":2221,"meta":1037,"style":1037},"import { createNoydb } from '@noy-db\u002Fhub'\nimport { toBrowserIdb } from '@noy-db\u002Fto-browser-idb'\nimport { toFile } from '@noy-db\u002Fto-file'\n\nconst store =\n  typeof indexedDB !== 'undefined'\n    ? toBrowserIdb()\n    : toFile({ dir: '.\u002Fdata' })\n\nconst db = await createNoydb({ store, user, secret })\n",[941,48267,48268,48286,48304,48322,48326,48335,48351,48360,48385,48389],{"__ignoreMap":1037},[2225,48269,48270,48272,48274,48276,48278,48280,48282,48284],{"class":2227,"line":2228},[2225,48271,2232],{"class":2231},[2225,48273,2236],{"class":2235},[2225,48275,2744],{"class":2239},[2225,48277,2243],{"class":2235},[2225,48279,2246],{"class":2231},[2225,48281,2249],{"class":2235},[2225,48283,1086],{"class":2252},[2225,48285,2256],{"class":2235},[2225,48287,48288,48290,48292,48294,48296,48298,48300,48302],{"class":2227,"line":1041},[2225,48289,2232],{"class":2231},[2225,48291,2236],{"class":2235},[2225,48293,34763],{"class":2239},[2225,48295,2243],{"class":2235},[2225,48297,2246],{"class":2231},[2225,48299,2249],{"class":2235},[2225,48301,9573],{"class":2252},[2225,48303,2256],{"class":2235},[2225,48305,48306,48308,48310,48312,48314,48316,48318,48320],{"class":2227,"line":1038},[2225,48307,2232],{"class":2231},[2225,48309,2236],{"class":2235},[2225,48311,15573],{"class":2239},[2225,48313,2243],{"class":2235},[2225,48315,2246],{"class":2231},[2225,48317,2249],{"class":2235},[2225,48319,9551],{"class":2252},[2225,48321,2256],{"class":2235},[2225,48323,48324],{"class":2227,"line":2288},[2225,48325,2285],{"emptyLinePlaceholder":2284},[2225,48327,48328,48330,48332],{"class":2227,"line":2304},[2225,48329,2775],{"class":2774},[2225,48331,39676],{"class":2239},[2225,48333,48334],{"class":2235},"=\n",[2225,48336,48337,48340,48343,48345,48347,48349],{"class":2227,"line":2356},[2225,48338,48339],{"class":2235},"  typeof",[2225,48341,48342],{"class":2239}," indexedDB ",[2225,48344,32545],{"class":2235},[2225,48346,2249],{"class":2235},[2225,48348,18316],{"class":2252},[2225,48350,2256],{"class":2235},[2225,48352,48353,48356,48358],{"class":2227,"line":2830},[2225,48354,48355],{"class":2235},"    ?",[2225,48357,34763],{"class":2291},[2225,48359,8146],{"class":2239},[2225,48361,48362,48365,48367,48369,48371,48373,48375,48377,48379,48381,48383],{"class":2227,"line":2861},[2225,48363,48364],{"class":2235},"    :",[2225,48366,15573],{"class":2291},[2225,48368,2789],{"class":2239},[2225,48370,2792],{"class":2235},[2225,48372,15619],{"class":2307},[2225,48374,2311],{"class":2235},[2225,48376,2249],{"class":2235},[2225,48378,15626],{"class":2252},[2225,48380,2805],{"class":2235},[2225,48382,2243],{"class":2235},[2225,48384,2362],{"class":2239},[2225,48386,48387],{"class":2227,"line":4246},[2225,48388,2285],{"emptyLinePlaceholder":2284},[2225,48390,48391,48393,48395,48397,48399,48401,48403,48405,48407,48409,48411,48413,48415,48417],{"class":2227,"line":4253},[2225,48392,2775],{"class":2774},[2225,48394,2778],{"class":2239},[2225,48396,2781],{"class":2235},[2225,48398,2784],{"class":2231},[2225,48400,2744],{"class":2291},[2225,48402,2789],{"class":2239},[2225,48404,2792],{"class":2235},[2225,48406,7564],{"class":2239},[2225,48408,2298],{"class":2235},[2225,48410,2795],{"class":2239},[2225,48412,2298],{"class":2235},[2225,48414,46330],{"class":2239},[2225,48416,2359],{"class":2235},[2225,48418,2362],{"class":2239},[937,48420,48421,1324,48424,48427,48428,48431],{},[950,48422,48423],{},"Prefer feature detection over platform sniffing.",[941,48425,48426],{},"typeof indexedDB !== 'undefined'"," survives bundling, SSR and test environments; ",[941,48429,48430],{},"process.platform"," does not.",[1022,48433,48435],{"id":48434},"multi-store-topologies","Multi-store topologies",[937,48437,48438],{},"Two composition seams, for when one store is not enough:",[1111,48440,48441,48458],{},[1114,48442,48443,48448,48449,968,48451,968,48454,48457],{},[950,48444,48445],{},[941,48446,48447],{},"routeStore()"," multiplexes one handle across many backends by rule — per collection, per vault prefix, blobs to object storage, age-based cold tiering, quota overflow. It returns a real store plus a runtime control surface (",[941,48450,12962],{},[941,48452,48453],{},"suspend",[941,48455,48456],{},"compact","), so routes can be swapped or suspended without rebuilding the instance.",[1114,48459,48460,48465,48466,48469],{},[950,48461,48462],{},[941,48463,48464],{},"withSync({ sync: [...] })"," takes a primary remote, or an array of ",[941,48467,48468],{},"{ store, role }"," targets — so \"local platform store plus cloud replica\" is expressible per platform.",[937,48471,48472],{},"They nest freely. You can meter a routed store, or route to metered stores:",[2217,48474,48476],{"className":2219,"code":48475,"language":2221,"meta":1037,"style":1037},"const pg = toMeter(toPostgres({ \u002F* … *\u002F }))\nconst s3 = toMeter(toAwsS3({ \u002F* … *\u002F }))\nconst store = routeStore({ default: pg, blobs: s3 })\n",[941,48477,48478,48505,48531],{"__ignoreMap":1037},[2225,48479,48480,48482,48485,48487,48490,48492,48495,48497,48499,48501,48503],{"class":2227,"line":2228},[2225,48481,2775],{"class":2774},[2225,48483,48484],{"class":2239}," pg ",[2225,48486,2781],{"class":2235},[2225,48488,48489],{"class":2291}," toMeter",[2225,48491,2789],{"class":2239},[2225,48493,48494],{"class":2291},"toPostgres",[2225,48496,2789],{"class":2239},[2225,48498,2792],{"class":2235},[2225,48500,10968],{"class":2763},[2225,48502,2243],{"class":2235},[2225,48504,8606],{"class":2239},[2225,48506,48507,48509,48512,48514,48516,48518,48521,48523,48525,48527,48529],{"class":2227,"line":1041},[2225,48508,2775],{"class":2774},[2225,48510,48511],{"class":2239}," s3 ",[2225,48513,2781],{"class":2235},[2225,48515,48489],{"class":2291},[2225,48517,2789],{"class":2239},[2225,48519,48520],{"class":2291},"toAwsS3",[2225,48522,2789],{"class":2239},[2225,48524,2792],{"class":2235},[2225,48526,10968],{"class":2763},[2225,48528,2243],{"class":2235},[2225,48530,8606],{"class":2239},[2225,48532,48533,48535,48537,48539,48542,48544,48546,48548,48550,48553,48555,48558,48560,48562,48564],{"class":2227,"line":1038},[2225,48534,2775],{"class":2774},[2225,48536,39676],{"class":2239},[2225,48538,2781],{"class":2235},[2225,48540,48541],{"class":2291}," routeStore",[2225,48543,2789],{"class":2239},[2225,48545,2792],{"class":2235},[2225,48547,36571],{"class":2307},[2225,48549,2311],{"class":2235},[2225,48551,48552],{"class":2239}," pg",[2225,48554,2298],{"class":2235},[2225,48556,48557],{"class":2307}," blobs",[2225,48559,2311],{"class":2235},[2225,48561,48511],{"class":2239},[2225,48563,2359],{"class":2235},[2225,48565,2362],{"class":2239},[1022,48567,48569],{"id":48568},"a-stores-locality-changes-what-an-unlock-error-means","A store's locality changes what an unlock error MEANS",[937,48571,48572],{},"This is a store-choice consequence, and it is easy to get wrong because the error is identical either way.",[937,48574,48575,48576,48578,48579,48582],{},"When an unlock fails — a ",[941,48577,11045],{},", say — many apps translate it into advice. ",[950,48580,48581],{},"That translation is only valid for a local store",", and it must branch on two things, in this order.",[937,48584,48585,1324,48588,48591,48592,48594,48595,48598,48599,1746,48602,13571,48605,48608,48609,968,48612,968,48615,48618],{},[950,48586,48587],{},"1. The reason code, not the message.",[941,48589,48590],{},"error.details.reason"," is a discriminated label, and one value is explicitly ",[1092,48593,1004],{}," an attack: ",[941,48596,48597],{},"'format-superseded'"," means the file declares an older keyring format than this build writes — a format transition. Say so, and name ",[941,48600,48601],{},"error.details.format.from",[941,48603,48604],{},".to",[1092,48606,48607],{},"absence"," reasons — ",[941,48610,48611],{},"'canary-missing'",[941,48613,48614],{},"'roster-key-missing'",[941,48616,48617],{},"'roster-tag-missing'"," — are genuinely unclassifiable: a store that stripped a field and a file written before the field existed are byte-identical, so they must be presented as neither an upgrade nor an attack.",[937,48620,48621,48624,48625,968,48627,48629],{},[950,48622,48623],{},"2. Where the store is."," Advice that is correct for a local store (",[941,48626,9507],{},[941,48628,7461],{},") can be harmful for a remote one:",[1338,48631,48632,48644],{},[1341,48633,48634],{},[1344,48635,48636,48638,48641],{},[1347,48637,6951],{},[1347,48639,48640],{},"the same error can mean",[1347,48642,48643],{},"so \"remove the vault from this device\"",[1357,48645,48646,48661,48680],{},[1344,48647,48648,48655,48658],{},[1362,48649,48650,48651,968,48653,1381],{},"local (",[941,48652,9507],{},[941,48654,7461],{},[1362,48656,48657],{},"the device is already compromised",[1362,48659,48660],{},"is reasonable — there is no evidence to preserve",[1344,48662,48663,48672,48677],{},[1362,48664,48665,48666,968,48668,48671],{},"remote (",[941,48667,16057],{},[941,48669,48670],{},"to-rest",", a LAN daemon)",[1362,48673,48674],{},[950,48675,48676],{},"the store altered your data",[1362,48678,48679],{},"⛔ destroys the only copy of the evidence",[1344,48681,48682,48685,48688],{},[1362,48683,48684],{},"a thin guest holding nothing locally",[1362,48686,48687],{},"same as remote",[1362,48689,48690,48691,48694],{},"⛔ is also ",[1092,48692,48693],{},"meaningless"," — there is nothing to remove",[937,48696,1649,48697,1324,48700,17904,48702,48704],{},[950,48698,48699],{},"The remote case is not hypothetical and it does not need a server.",[941,48701,34675],{},[941,48703,34655],{}," routes every store operation to another machine, so a guest opening a vault served by someone else's device is on a remote, separately administered store — with no cloud involved.",[937,48706,48707,48708,1324,48711,48714],{},"⭐ ",[950,48709,48710],{},"In both branches, say what to preserve before removing anything.",[1092,48712,48713],{},"\"Export or copy the raw store contents first, then remove\""," is safe under either premise and no less actionable. The safe advice does not depend on knowing which case you are in — which is the point, because for the absence reasons you cannot know.",[1022,48716,48718],{"id":48717},"the-platform-matrix-honestly","The platform matrix, honestly",[1338,48720,48721,48732],{},[1341,48722,48723],{},[1344,48724,48725,48727,48729],{},[1347,48726,38423],{},[1347,48728,9665],{},[1347,48730,48731],{},"Status",[1357,48733,48734,48746,48757,48768,48779,48792],{},[1344,48735,48736,48739,48743],{},[1362,48737,48738],{},"Browser",[1362,48740,48741],{},[941,48742,9573],{},[1362,48744,48745],{},"ships",[1344,48747,48748,48751,48755],{},[1362,48749,48750],{},"Node \u002F Electron main \u002F CLI",[1362,48752,48753],{},[941,48754,9551],{},[1362,48756,48745],{},[1344,48758,48759,48762,48766],{},[1362,48760,48761],{},"Tests, REPL, hot cache",[1362,48763,48764],{},[941,48765,9638],{},[1362,48767,48745],{},[1344,48769,48770,48773,48777],{},[1362,48771,48772],{},"SQL \u002F cloud \u002F remote filesystems",[1362,48774,3742,48775,9789],{},[941,48776,5351],{},[1362,48778,48745],{},[1344,48780,48781,48784,48789],{},[1362,48782,48783],{},"macOS (Node)",[1362,48785,48786],{},[941,48787,48788],{},"@noy-db\u002Fto-icloud",[1362,48790,48791],{},"ships — with a caveat below",[1344,48793,48794,48799,48806],{},[1362,48795,48796],{},[950,48797,48798],{},"iOS \u002F iPadOS",[1362,48800,48801],{},[950,48802,48803],{},[941,48804,48805],{},"to-cloudkit",[1362,48807,48808],{},[950,48809,48810],{},"not shipped",[937,48812,48813,1324,48816,48819,48820,48825,48826,48828,48829,48831],{},[950,48814,48815],{},"The Apple gap is real, and worth knowing before you design around it.",[941,48817,48818],{},"to-icloud"," is Node-on-macOS only: it reaches the iCloud Drive folder through the filesystem. iOS and iPadOS have no equivalent folder access, so they need CloudKit — which is deferred (",[1007,48821,48824],{"href":48822,"rel":48823},"https:\u002F\u002Fgithub.com\u002Fnoy-db\u002Fto\u002Fissues\u002F16",[3371],"noy-db-to#16","). Until it ships, an iOS target has no first-party Apple-native store. Use a cloud store from the ",[941,48827,5351],{}," family, or ",[941,48830,9507],{}," inside a web view.",[937,48833,48834,48835,48837,48838,48841,48842,957],{},"One further caveat on ",[941,48836,48818],{},": modern macOS evicts to APFS dataless files rather than ",[941,48839,48840],{},".icloud"," stubs, which makes its eviction detection macOS-version-sensitive (",[1007,48843,48846],{"href":48844,"rel":48845},"https:\u002F\u002Fgithub.com\u002Fnoy-db\u002Fto\u002Fissues\u002F15",[3371],"noy-db-to#15",[1022,48848,2621],{"id":2620},[1111,48850,48851,48859,48867,48872],{},[1114,48852,48853,48858],{},[1007,48854,48855,48857],{"href":2634},[941,48856,1779],{}," family index"," — the full storage-destination catalog",[1114,48860,48861,964,48864,48866],{},[1007,48862,808],{"href":48863},"..\u002F..\u002Fservices\u002Frouting",[941,48865,48447],{}," and its routing axes",[1114,48868,48869,48871],{},[1007,48870,864],{"href":15100}," — replica topologies",[1114,48873,48874,48877],{},[1007,48875,48876],{"href":24586},"storage capability matrix"," — what each backend actually guarantees",[2645,48879,32928],{},{"title":1037,"searchDepth":1038,"depth":1038,"links":48881},[48882,48883,48884,48885,48886,48887],{"id":48243,"depth":1041,"text":48244},{"id":48261,"depth":1041,"text":48262},{"id":48434,"depth":1041,"text":48435},{"id":48568,"depth":1041,"text":48569},{"id":48717,"depth":1041,"text":48718},{"id":2620,"depth":1041,"text":2621},"How to pick a to-* store per platform, why the hub ships no pickStore() helper, and the one real gap in the platform matrix.",{},{"title":366,"icon":369},{"title":366,"description":48888},"U6nhd4G86yO7B34hczijI4ErYzT3Ues4plS9EXMDLbc",{"id":48894,"title":371,"access":12,"apiToc":929,"body":48895,"category":1042,"description":49105,"extension":1044,"meta":49106,"navigation":1042,"nest":3303,"path":372,"seo":49107,"sinceVersion":1042,"status":1049,"stem":373,"subtitle":1042,"tier":1050,"tocExternal":1042,"__hash__":49108},"docs\u002Ffamilies\u002Fto\u002Fcloudflare-d1.md",{"type":931,"value":48896,"toc":49099},[48897,48899,48912,48915,48917,48931,48933,48950,48952,48999,49080,49082,49097],[934,48898,371],{"id":371},[1061,48900,48901],{},[937,48902,48903,1368,48908,48911],{},[950,48904,48905],{},[941,48906,48907],{},"@noy-db\u002Fto-cloudflare-d1",[941,48909,48910],{},"pnpm add @noy-db\u002Fto-cloudflare-d1","\nSQLite at the edge, running inside Cloudflare Workers.",[46871,48913],{":rows":48914},"[{\"label\":\"Shape\",\"value\":\"record\"},{\"label\":\"Atomic CAS\",\"value\":\"✓\"},{\"label\":\"Locality\",\"value\":\"edge\"},{\"label\":\"Speed\",\"value\":\"medium\"},{\"label\":\"Persistence\",\"value\":\"durable\"}]",[1022,48916,29857],{"id":29856},[937,48918,48919,48920,48923,48924,1625,48927,48930],{},"Accepts a ",[941,48921,48922],{},"D1Database"," binding (the one Workers hands you in your runtime\nenvironment) and speaks the noy-db store contract via D1's ",[941,48925,48926],{},"prepare",[941,48928,48929],{},"bind"," API.\nNo external driver to install — D1 is available directly inside the Workers\nruntime.",[1022,48932,9542],{"id":2854},[1111,48934,48935,48942,48945],{},[1114,48936,48937,9750,48939,48941],{},[941,48938,7811],{},[941,48940,9779],{}," inside a D1 batch.",[1114,48943,48944],{},"Record shape, one row per record.",[1114,48946,46929,48947,1520],{},[941,48948,48949],{},"toCloudflareD1(options: D1StoreOptions)",[1022,48951,46936],{"id":46935},[937,48953,46939,48954,46944,48956,968,48959,1952,48961,7356,48963,2085,48966,48968,48969,5502,48972,48974,48975,48978,48979,48981,48982,48984,48985,7356,48987,1952,48990,7356,48993,48995,48996,48998],{},[1007,48955,46943],{"href":46942},[941,48957,48958],{},"kind: 'cloudflare-d1'",[941,48960,46950],{},[941,48962,46953],{},[941,48964,48965],{},"{ binding?, database?, table? }",[941,48967,1338],{}," maps to ",[941,48970,48971],{},"tableName",[941,48973,48116],{}," (the Workers\n",[941,48976,48977],{},"env.\u003CBINDING>"," name — note the name collision with the locator's own\n",[941,48980,48116],{}," slot below) and ",[941,48983,31066],{}," are identity-only, not consumed by the\nfactory. ",[941,48986,16362],{},[941,48988,48989],{},"{ autoMigrate? }",[941,48991,48992],{},"binding.client",[950,48994,45375],{}," —\nthis store has no client library of its own; ",[941,48997,46978],{}," without it throws.",[2217,49000,49002],{"className":2219,"code":49001,"language":2221,"meta":1037,"style":1037},"import { cloudflareD1StoreDescriptor, registerCloudflareD1Store } from '@noy-db\u002Fto-cloudflare-d1'\n\nconst descriptor = cloudflareD1StoreDescriptor({ binding: 'DB', database: 'noydb' })\n\u002F\u002F later: locator.resolve(descriptor, { binding: { client: env.DB } })\n",[941,49003,49004,49028,49032,49075],{"__ignoreMap":1037},[2225,49005,49006,49008,49010,49013,49015,49018,49020,49022,49024,49026],{"class":2227,"line":2228},[2225,49007,2232],{"class":2231},[2225,49009,2236],{"class":2235},[2225,49011,49012],{"class":2239}," cloudflareD1StoreDescriptor",[2225,49014,2298],{"class":2235},[2225,49016,49017],{"class":2239}," registerCloudflareD1Store",[2225,49019,2243],{"class":2235},[2225,49021,2246],{"class":2231},[2225,49023,2249],{"class":2235},[2225,49025,48907],{"class":2252},[2225,49027,2256],{"class":2235},[2225,49029,49030],{"class":2227,"line":1041},[2225,49031,2285],{"emptyLinePlaceholder":2284},[2225,49033,49034,49036,49038,49040,49042,49044,49046,49049,49051,49053,49056,49058,49060,49063,49065,49067,49069,49071,49073],{"class":2227,"line":1038},[2225,49035,2775],{"class":2774},[2225,49037,47019],{"class":2239},[2225,49039,2781],{"class":2235},[2225,49041,49012],{"class":2291},[2225,49043,2789],{"class":2239},[2225,49045,2792],{"class":2235},[2225,49047,49048],{"class":2307}," binding",[2225,49050,2311],{"class":2235},[2225,49052,2249],{"class":2235},[2225,49054,49055],{"class":2252},"DB",[2225,49057,2805],{"class":2235},[2225,49059,2298],{"class":2235},[2225,49061,49062],{"class":2307}," database",[2225,49064,2311],{"class":2235},[2225,49066,2249],{"class":2235},[2225,49068,149],{"class":2252},[2225,49070,2805],{"class":2235},[2225,49072,2243],{"class":2235},[2225,49074,2362],{"class":2239},[2225,49076,49077],{"class":2227,"line":2288},[2225,49078,49079],{"class":2763},"\u002F\u002F later: locator.resolve(descriptor, { binding: { client: env.DB } })\n",[1022,49081,2621],{"id":2620},[1111,49083,49084,49088,49092],{},[1114,49085,49086],{},[1007,49087,47064],{"href":2634},[1114,49089,49090],{},[1007,49091,375],{"href":47307},[1114,49093,49094],{},[1007,49095,422],{"href":49096},".\u002Fsqlite",[2645,49098,30448],{},{"title":1037,"searchDepth":1038,"depth":1038,"links":49100},[49101,49102,49103,49104],{"id":29856,"depth":1041,"text":29857},{"id":2854,"depth":1041,"text":9542},{"id":46935,"depth":1041,"text":46936},{"id":2620,"depth":1041,"text":2621},"Cloudflare D1 adapter for noy-db — SQLite at the edge inside Cloudflare Workers, speaking the store contract via D1's prepare\u002Fbind API.",{},{"title":371,"description":49105},"mC95LLM5Cpyth4Ac7IszivpfcDw1VbUUJPd3lyPjrHM",{"id":49110,"title":375,"access":12,"apiToc":929,"body":49111,"category":1042,"description":49486,"extension":1044,"meta":49487,"navigation":1042,"nest":3303,"path":376,"seo":49488,"sinceVersion":1042,"status":1049,"stem":377,"subtitle":1042,"tier":1050,"tocExternal":1042,"__hash__":49489},"docs\u002Ffamilies\u002Fto\u002Fcloudflare-r2.md",{"type":931,"value":49112,"toc":49479},[49113,49115,49128,49131,49133,49145,49147,49201,49205,49224,49339,49341,49385,49458,49460,49476],[934,49114,375],{"id":375},[1061,49116,49117],{},[937,49118,49119,1368,49124,49127],{},[950,49120,49121],{},[941,49122,49123],{},"@noy-db\u002Fto-cloudflare-r2",[941,49125,49126],{},"pnpm add @noy-db\u002Fto-cloudflare-r2","\nZero egress fees, S3-API-compatible.",[46871,49129],{":rows":49130},"[{\"label\":\"Shape\",\"value\":\"record\"},{\"label\":\"Atomic CAS\",\"value\":\"✓\"},{\"label\":\"Locality\",\"value\":\"edge\"},{\"label\":\"Speed\",\"value\":\"slow\"},{\"label\":\"Persistence\",\"value\":\"durable\"}]",[1022,49132,29857],{"id":29856},[937,49134,49135,49136,49140,49141,49144],{},"A thin factory around ",[1007,49137,49138],{"href":30830},[941,49139,29876],{},", pre-configured for the R2\nendpoint (",[941,49142,49143],{},"r2EndpointFor(accountId)","). Same object-per-record shape and API surface\nas S3 — just point it at your Cloudflare account.",[1022,49146,9542],{"id":2854},[1111,49148,49149,49174,49190,49196],{},[1114,49150,49151,49157,49158,49161,49162,49164,49165,49167,49168,1617,49171,957],{},[950,49152,49153,47128,49155],{},[941,49154,8710],{},[941,49156,1625],{}," — forwarded to ",[941,49159,49160],{},"toAwsS3()",", so it inherits that\npackage's refusal (since ",[941,49163,47134],{},"); the error names ",[941,49166,29876],{},"\neven when you called ",[941,49169,49170],{},"toCloudflareR2()",[1007,49172,47154],{"href":47152,"rel":49173},[3371],[1114,49175,49176,49178,49179,49181,49182,1261,49184,49186,49187,1520],{},[941,49177,7811],{}," — inherits ",[941,49180,5358],{},"'s conditional-write CAS\n(",[941,49183,9732],{},[941,49185,9735],{},"), plus ",[941,49188,49189],{},"serverWriteTime: true",[1114,49191,49192,49193,49195],{},"Record shape, one object per record, same ",[941,49194,39784],{}," O(N)-requests caveat as S3.",[1114,49197,46929,49198,1520],{},[941,49199,49200],{},"toCloudflareR2(options: R2Options)",[1022,49202,49204],{"id":49203},"breaking-plaintext-keys-removed","BREAKING: plaintext keys removed",[937,49206,49207,1261,49210,49213,49214,49217,49218,49220,49221,49223],{},[941,49208,49209],{},"accessKeyId",[941,49211,49212],{},"secretAccessKey"," are no longer accepted on ",[941,49215,49216],{},"R2Options",".\nAuthentication now has exactly two paths — a ",[941,49219,48119],{}," source or a\npre-built ",[941,49222,9913],{}," — because the descriptor path cannot offer a plaintext\nkey pair without breaking the credentialless-by-construction rule, and\nleaving it on the direct path would split auth into two diverging stories.\nMigrate by wrapping the static keys in a credential source:",[2217,49225,49227],{"className":2219,"code":49226,"language":2221,"meta":1037,"style":1037},"\u002F\u002F before\ntoCloudflareR2({ bucket, accountId, accessKeyId, secretAccessKey })\n\n\u002F\u002F after\ntoCloudflareR2({ bucket, accountId, credentials: async () => ({\n  kind: 'aws', accessKeyId, secretAccessKey,\n}) })\n",[941,49228,49229,49234,49264,49268,49273,49304,49329],{"__ignoreMap":1037},[2225,49230,49231],{"class":2227,"line":2228},[2225,49232,49233],{"class":2763},"\u002F\u002F before\n",[2225,49235,49236,49239,49241,49243,49245,49247,49250,49252,49255,49257,49260,49262],{"class":2227,"line":1041},[2225,49237,49238],{"class":2291},"toCloudflareR2",[2225,49240,2789],{"class":2239},[2225,49242,2792],{"class":2235},[2225,49244,29939],{"class":2239},[2225,49246,2298],{"class":2235},[2225,49248,49249],{"class":2239}," accountId",[2225,49251,2298],{"class":2235},[2225,49253,49254],{"class":2239}," accessKeyId",[2225,49256,2298],{"class":2235},[2225,49258,49259],{"class":2239}," secretAccessKey ",[2225,49261,2359],{"class":2235},[2225,49263,2362],{"class":2239},[2225,49265,49266],{"class":2227,"line":1038},[2225,49267,2285],{"emptyLinePlaceholder":2284},[2225,49269,49270],{"class":2227,"line":2288},[2225,49271,49272],{"class":2763},"\u002F\u002F after\n",[2225,49274,49275,49277,49279,49281,49283,49285,49287,49289,49292,49294,49296,49298,49300,49302],{"class":2227,"line":2304},[2225,49276,49238],{"class":2291},[2225,49278,2789],{"class":2239},[2225,49280,2792],{"class":2235},[2225,49282,29939],{"class":2239},[2225,49284,2298],{"class":2235},[2225,49286,49249],{"class":2239},[2225,49288,2298],{"class":2235},[2225,49290,49291],{"class":2291}," credentials",[2225,49293,2311],{"class":2235},[2225,49295,38717],{"class":2774},[2225,49297,13130],{"class":2235},[2225,49299,8116],{"class":2774},[2225,49301,953],{"class":2239},[2225,49303,4200],{"class":2235},[2225,49305,49306,49309,49311,49313,49316,49318,49320,49322,49324,49327],{"class":2227,"line":2356},[2225,49307,49308],{"class":2307},"  kind",[2225,49310,2311],{"class":2235},[2225,49312,2249],{"class":2235},[2225,49314,49315],{"class":2252},"aws",[2225,49317,2805],{"class":2235},[2225,49319,2298],{"class":2235},[2225,49321,49254],{"class":2239},[2225,49323,2298],{"class":2235},[2225,49325,49326],{"class":2239}," secretAccessKey",[2225,49328,1707],{"class":2235},[2225,49330,49331,49333,49335,49337],{"class":2227,"line":2830},[2225,49332,2359],{"class":2235},[2225,49334,8495],{"class":2239},[2225,49336,2359],{"class":2235},[2225,49338,2362],{"class":2239},[1022,49340,46936],{"id":46935},[937,49342,46939,49343,46944,49345,968,49348,1952,49350,7356,49352,964,49355,49358,49359,49361,49362,49365,49366,7356,49368,1952,49370,49372,49373,49375,49376,49378,49379,49381,46975,49383,19621],{},[1007,49344,46943],{"href":46942},[941,49346,49347],{},"kind: 'cloudflare-r2'",[941,49349,46950],{},[941,49351,46953],{},[941,49353,49354],{},"{ bucket, accountId?, prefix?, endpoint? }",[941,49356,49357],{},"endpoint"," is location, not tuning, so it\nlives here rather than in ",[941,49360,16362],{},", and supplements ",[941,49363,49364],{},"accountId"," rather than\nreplacing it. ",[941,49367,16362],{},[941,49369,47200],{},[941,49371,46967],{},"\ncarries a pre-built ",[941,49374,47112],{}," (a shared R2-pointed client, a Workers\nbinding, or a test fake), which always wins over ",[941,49377,49364],{}," + credentials.\nR2 keys are S3-compatible, so credentials arrive via a ",[941,49380,46971],{},[941,49382,46974],{},[941,49384,46978],{},[2217,49386,49388],{"className":2219,"code":49387,"language":2221,"meta":1037,"style":1037},"import { r2StoreDescriptor, registerR2Store } from '@noy-db\u002Fto-cloudflare-r2'\n\nconst descriptor = r2StoreDescriptor({ bucket: 'my-vault', accountId: '...' })\n",[941,49389,49390,49414,49418],{"__ignoreMap":1037},[2225,49391,49392,49394,49396,49399,49401,49404,49406,49408,49410,49412],{"class":2227,"line":2228},[2225,49393,2232],{"class":2231},[2225,49395,2236],{"class":2235},[2225,49397,49398],{"class":2239}," r2StoreDescriptor",[2225,49400,2298],{"class":2235},[2225,49402,49403],{"class":2239}," registerR2Store",[2225,49405,2243],{"class":2235},[2225,49407,2246],{"class":2231},[2225,49409,2249],{"class":2235},[2225,49411,49123],{"class":2252},[2225,49413,2256],{"class":2235},[2225,49415,49416],{"class":2227,"line":1041},[2225,49417,2285],{"emptyLinePlaceholder":2284},[2225,49419,49420,49422,49424,49426,49428,49430,49432,49434,49436,49438,49440,49442,49444,49446,49448,49450,49452,49454,49456],{"class":2227,"line":1038},[2225,49421,2775],{"class":2774},[2225,49423,47019],{"class":2239},[2225,49425,2781],{"class":2235},[2225,49427,49398],{"class":2291},[2225,49429,2789],{"class":2239},[2225,49431,2792],{"class":2235},[2225,49433,29939],{"class":2307},[2225,49435,2311],{"class":2235},[2225,49437,2249],{"class":2235},[2225,49439,47271],{"class":2252},[2225,49441,2805],{"class":2235},[2225,49443,2298],{"class":2235},[2225,49445,49249],{"class":2307},[2225,49447,2311],{"class":2235},[2225,49449,2249],{"class":2235},[2225,49451,8670],{"class":2252},[2225,49453,2805],{"class":2235},[2225,49455,2243],{"class":2235},[2225,49457,2362],{"class":2239},[1022,49459,2621],{"id":2620},[1111,49461,49462,49466,49471],{},[1114,49463,49464],{},[1007,49465,47064],{"href":2634},[1114,49467,49468,49470],{},[1007,49469,129],{"href":30830}," — the store this wraps",[1114,49472,49473],{},[1007,49474,371],{"href":49475},".\u002Fcloudflare-d1",[2645,49477,49478],{},"html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}",{"title":1037,"searchDepth":1038,"depth":1038,"links":49480},[49481,49482,49483,49484,49485],{"id":29856,"depth":1041,"text":29857},{"id":2854,"depth":1041,"text":9542},{"id":49203,"depth":1041,"text":49204},{"id":46935,"depth":1041,"text":46936},{"id":2620,"depth":1041,"text":2621},"Cloudflare R2 adapter for noy-db — S3-compatible object storage with zero egress fees; a thin factory over to-aws-s3 pointed at the R2 endpoint.",{},{"title":375,"description":49486},"0rWADxeEzMM1wafaRTxuNDAhE6aL1vVYCLlbZ2BPLZs",{"id":49491,"title":379,"access":12,"apiToc":929,"body":49492,"category":1042,"description":49693,"extension":1044,"meta":49694,"navigation":1042,"nest":3303,"path":380,"seo":49695,"sinceVersion":1042,"status":1049,"stem":381,"subtitle":1042,"tier":1473,"tocExternal":1042,"__hash__":49696},"docs\u002Ffamilies\u002Fto\u002Fdrive.md",{"type":931,"value":49493,"toc":49687},[49494,49496,49509,49512,49514,49524,49526,49554,49556,49609,49672,49674,49685],[934,49495,379],{"id":379},[1061,49497,49498],{},[937,49499,49500,1368,49505,49508],{},[950,49501,49502],{},[941,49503,49504],{},"@noy-db\u002Fto-drive",[941,49506,49507],{},"pnpm add @noy-db\u002Fto-drive","\nThe whole vault as a single Google Drive file.",[46871,49510],{":rows":49511},"[{\"label\":\"Shape\",\"value\":\"bundle\"},{\"label\":\"Atomic CAS\",\"value\":\"n\u002Fa\"},{\"label\":\"Locality\",\"value\":\"cloud\"},{\"label\":\"Speed\",\"value\":\"slow\"},{\"label\":\"Persistence\",\"value\":\"durable\"}]",[1022,49513,29857],{"id":29856},[937,49515,49516,49517,49519,49520,49523],{},"Stores the whole vault as a single ",[941,49518,1402],{}," bundle, named by ULID (never the vault\nname) and placed in Drive's hidden ",[941,49521,49522],{},"appDataFolder"," by default — opaque filenames\nnever leak what the vault is about. Driver-agnostic: bring your own OAuth'd Drive\nclient.",[1022,49525,9542],{"id":2854},[1111,49527,49528,49544,49549],{},[1114,49529,49530,49531,49534,49535,49537,49538,17904,49541,49543],{},"Bundle-only (",[941,49532,49533],{},"NoydbPodStore","), not the six-method ",[941,49536,3154],{}," contract — wrap\nit with ",[941,49539,49540],{},"wrapPodStore()",[941,49542,1086],{}," to get the standard record-level\nsurface.",[1114,49545,49546,49548],{},[941,49547,9539],{},": n\u002Fa — whole-vault file, no per-record CAS.",[1114,49550,46929,49551,1520],{},[941,49552,49553],{},"toDrive(options: DriveStoreOptions)",[1022,49555,46936],{"id":46935},[937,49557,46939,49558,46944,49560,968,49563,1952,49565,7356,49567,49570,49571,49573,49574,7356,49576,49579,49580,5584,49583,33690,49585,23423,49587,49590,49591,49594,49595,49598,49599,22207,49602,49604,49605,49608],{},[1007,49559,46943],{"href":46942},[941,49561,49562],{},"kind: 'drive'",[941,49564,46950],{},[941,49566,46953],{},[941,49568,49569],{},"{ parentId? }"," — omitted\nmeans Drive's ",[941,49572,49522],{},", the store's existing default. ",[941,49575,16362],{},[941,49577,49578],{},"{ suffix? }"," (bundle filename suffix, default ",[941,49581,49582],{},"'.noydb'",[941,49584,48992],{},[950,49586,45375],{},[941,49588,49589],{},"DriveClient",") — this store has no client library of its\nown; ",[941,49592,49593],{},"binding.handles?"," carries the per-device ",[941,49596,49597],{},"HandleStore",", omitted leaves\nthe in-memory default. Because ",[941,49600,49601],{},"toDrive()",[941,49603,49533],{},", its\nfactory's return type is deliberately unnarrowed and registers via a\ndocumented ",[941,49606,49607],{},"as unknown as StoreFactory"," cast.",[2217,49610,49612],{"className":2219,"code":49611,"language":2221,"meta":1037,"style":1037},"import { driveStoreDescriptor, registerDriveStore } from '@noy-db\u002Fto-drive'\n\nconst descriptor = driveStoreDescriptor({ parentId: 'root-folder-id' })\n",[941,49613,49614,49638,49642],{"__ignoreMap":1037},[2225,49615,49616,49618,49620,49623,49625,49628,49630,49632,49634,49636],{"class":2227,"line":2228},[2225,49617,2232],{"class":2231},[2225,49619,2236],{"class":2235},[2225,49621,49622],{"class":2239}," driveStoreDescriptor",[2225,49624,2298],{"class":2235},[2225,49626,49627],{"class":2239}," registerDriveStore",[2225,49629,2243],{"class":2235},[2225,49631,2246],{"class":2231},[2225,49633,2249],{"class":2235},[2225,49635,49504],{"class":2252},[2225,49637,2256],{"class":2235},[2225,49639,49640],{"class":2227,"line":1041},[2225,49641,2285],{"emptyLinePlaceholder":2284},[2225,49643,49644,49646,49648,49650,49652,49654,49656,49659,49661,49663,49666,49668,49670],{"class":2227,"line":1038},[2225,49645,2775],{"class":2774},[2225,49647,47019],{"class":2239},[2225,49649,2781],{"class":2235},[2225,49651,49622],{"class":2291},[2225,49653,2789],{"class":2239},[2225,49655,2792],{"class":2235},[2225,49657,49658],{"class":2307}," parentId",[2225,49660,2311],{"class":2235},[2225,49662,2249],{"class":2235},[2225,49664,49665],{"class":2252},"root-folder-id",[2225,49667,2805],{"class":2235},[2225,49669,2243],{"class":2235},[2225,49671,2362],{"class":2239},[1022,49673,2621],{"id":2620},[1111,49675,49676,49680],{},[1114,49677,49678],{},[1007,49679,47064],{"href":2634},[1114,49681,49682],{},[1007,49683,387],{"href":49684},".\u002Ficloud",[2645,49686,37016],{},{"title":1037,"searchDepth":1038,"depth":1038,"links":49688},[49689,49690,49691,49692],{"id":29856,"depth":1041,"text":29857},{"id":2854,"depth":1041,"text":9542},{"id":46935,"depth":1041,"text":46936},{"id":2620,"depth":1041,"text":2621},"Google Drive bundle store for noy-db — the whole vault as one .noydb file, named by opaque ULID, placed in Drive's hidden appDataFolder by default.",{},{"title":379,"description":49693},"vy1iIwq382RLZKhOJ6s9Brhm3H-VCTRu1y-YZl1vILU",{"id":49698,"title":383,"access":12,"apiToc":929,"body":49699,"category":1042,"description":49890,"extension":1044,"meta":49891,"navigation":1042,"nest":3303,"path":384,"seo":49892,"sinceVersion":1042,"status":1049,"stem":385,"subtitle":1042,"tier":6922,"tocExternal":1042,"__hash__":49893},"docs\u002Ffamilies\u002Fto\u002Ffile.md",{"type":931,"value":49700,"toc":49884},[49701,49703,49715,49717,49719,49726,49728,49757,49759,49806,49864,49866,49881],[934,49702,383],{"id":383},[1061,49704,49705],{},[937,49706,49707,1368,49711,49714],{},[950,49708,49709],{},[941,49710,9551],{},[941,49712,49713],{},"pnpm add @noy-db\u002Fto-file","\nLocal disk \u002F USB stick storage, one JSON file per record.",[46871,49716],{":rows":48053},[1022,49718,29857],{"id":29856},[937,49720,49721,49722,49725],{},"Lays a vault out as plain files — ",[941,49723,49724],{},"{dir}\u002F{vault}\u002F{collection}\u002F{id}.json",", one\npretty-printed encrypted envelope per record — inspectable (but not readable) with\nany editor. Good for USB-stick workflows (air-gapped portability), local-first\ndesktop apps, network-drive sharing with per-user secrets, and backup-friendly\nstorage.",[1022,49727,9542],{"id":2854},[1111,49729,49730,49735,49752],{},[1114,49731,49732,49733,1520],{},"No compare-and-swap primitive at the filesystem layer — ",[941,49734,47869],{},[1114,49736,49737,49738,1261,49741,49744,49745,49747,49748,49751],{},"Record shape by default; also ships ",[941,49739,49740],{},"savePod",[941,49742,49743],{},"loadPod"," for whole-vault\n",[941,49746,1402],{}," bundle files, and ",[941,49749,49750],{},"exportBlobsToDirectory"," with target-profile filename\nsanitization and Zip-Slip path containment.",[1114,49753,46929,49754,1520],{},[941,49755,49756],{},"toFile(options: JsonFileOptions)",[1022,49758,46936],{"id":46935},[937,49760,46939,49761,49763,49764,49767,49768,968,49771,49774,49775,49778,49779,49782,49783,7356,49785,13675,49788,1952,49790,49792,49793,49795,49796,49798,49799,49801,49802,49805],{},[1007,49762,46943],{"href":46942}," for the general model —\nthis store is the seam's ",[941,49765,49766],{},"local","-class reference implementation. ",[941,49769,49770],{},"kind: 'file'",[941,49772,49773],{},"class: 'local'",". Unlike the rest of the family, ",[941,49776,49777],{},"fileStoreDescriptor","\ntakes the base directory directly rather than an address object:\n",[941,49780,49781],{},"fileStoreDescriptor(dir: string): StoreDescriptor"," — internally\n",[941,49784,46953],{},[941,49786,49787],{},"{ dir }",[941,49789,16362],{},[941,49791,7461],{}," is credentialless, so\n",[941,49794,48119],{}," is unused; ",[941,49797,48116],{}," may carry a device-local directory\noverride — a bare string or ",[941,49800,49787],{}," — applied in place of the descriptor's\n",[941,49803,49804],{},"dir"," (e.g. a different mount point on this device).",[2217,49807,49809],{"className":2219,"code":49808,"language":2221,"meta":1037,"style":1037},"import { fileStoreDescriptor, registerFileStore } from '@noy-db\u002Fto-file'\n\nconst descriptor = fileStoreDescriptor('.\u002Fdata')\n\u002F\u002F later, on a different device: locator.resolve(descriptor, { binding: '\u002Fmnt\u002Fusb\u002Fdata' })\n",[941,49810,49811,49835,49839,49859],{"__ignoreMap":1037},[2225,49812,49813,49815,49817,49820,49822,49825,49827,49829,49831,49833],{"class":2227,"line":2228},[2225,49814,2232],{"class":2231},[2225,49816,2236],{"class":2235},[2225,49818,49819],{"class":2239}," fileStoreDescriptor",[2225,49821,2298],{"class":2235},[2225,49823,49824],{"class":2239}," registerFileStore",[2225,49826,2243],{"class":2235},[2225,49828,2246],{"class":2231},[2225,49830,2249],{"class":2235},[2225,49832,9551],{"class":2252},[2225,49834,2256],{"class":2235},[2225,49836,49837],{"class":2227,"line":1041},[2225,49838,2285],{"emptyLinePlaceholder":2284},[2225,49840,49841,49843,49845,49847,49849,49851,49853,49855,49857],{"class":2227,"line":1038},[2225,49842,2775],{"class":2774},[2225,49844,47019],{"class":2239},[2225,49846,2781],{"class":2235},[2225,49848,49819],{"class":2291},[2225,49850,2789],{"class":2239},[2225,49852,2805],{"class":2235},[2225,49854,15626],{"class":2252},[2225,49856,2805],{"class":2235},[2225,49858,2362],{"class":2239},[2225,49860,49861],{"class":2227,"line":2288},[2225,49862,49863],{"class":2763},"\u002F\u002F later, on a different device: locator.resolve(descriptor, { binding: '\u002Fmnt\u002Fusb\u002Fdata' })\n",[1022,49865,2621],{"id":2620},[1111,49867,49868,49872,49877],{},[1114,49869,49870],{},[1007,49871,47064],{"href":2634},[1114,49873,49874],{},[1007,49875,391],{"href":49876},".\u002Fmemory",[1114,49878,49879],{},[1007,49880,422],{"href":49096},[2645,49882,49883],{},"html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":1037,"searchDepth":1038,"depth":1038,"links":49885},[49886,49887,49888,49889],{"id":29856,"depth":1041,"text":29857},{"id":2854,"depth":1041,"text":9542},{"id":46935,"depth":1041,"text":46936},{"id":2620,"depth":1041,"text":2621},"JSON file adapter for noy-db — encrypted document store on local disk, USB sticks, or network drives.",{},{"title":383,"description":49890},"4RUeOvzRGdgqjZzhoUKBbxp2lDZ96LOI3bl6lNBAiIw",{"id":49895,"title":387,"access":12,"apiToc":929,"body":49896,"category":1042,"description":50194,"extension":1044,"meta":50195,"navigation":1042,"nest":3303,"path":388,"seo":50196,"sinceVersion":1042,"status":1049,"stem":389,"subtitle":1042,"tier":1473,"tocExternal":1042,"__hash__":50197},"docs\u002Ffamilies\u002Fto\u002Ficloud.md",{"type":931,"value":49897,"toc":50187},[49898,49900,49912,49914,49916,49926,49930,49939,50009,50023,50040,50042,50064,50066,50110,50172,50174,50185],[934,49899,387],{"id":387},[1061,49901,49902],{},[937,49903,49904,1368,49908,49911],{},[950,49905,49906],{},[941,49907,48788],{},[941,49909,49910],{},"pnpm add @noy-db\u002Fto-icloud","\nThe whole vault as a single iCloud Drive bundle (macOS).",[46871,49913],{":rows":49511},[1022,49915,29857],{"id":29856},[937,49917,49918,49919,49921,49922,49925],{},"Treats each vault as a single ",[941,49920,1402],{}," bundle on iCloud Drive. Handles both shapes\niCloud uses when it offloads a file's contents to save local disk space, and raises\n",[941,49923,49924],{},"PodVersionConflictError"," on parallel-device conflict copies so you merge\ndeliberately instead of silently losing writes.",[1022,49927,49929],{"id":49928},"eviction-has-two-shapes","Eviction has two shapes",[937,49931,49932,49933,49935,49936,49938],{},"Which one you get depends on the macOS version, and the modern one is ",[950,49934,1004],{}," the\n",[941,49937,48840],{}," stub most documentation describes:",[1338,49940,49941,49953],{},[1341,49942,49943],{},[1344,49944,49945,49947,49950],{},[1347,49946],{},[1347,49948,49949],{},"modern (verified on Darwin 24.6 \u002F Sequoia)",[1347,49951,49952],{},"legacy",[1357,49954,49955,49981,49995],{},[1344,49956,49957,49960,49974],{},[1362,49958,49959],{},"On disk",[1362,49961,49962,49963,964,49966,49969,49970,49973],{},"the canonical file, ",[950,49964,49965],{},"APFS-dataless",[941,49967,49968],{},"stat"," reports full logical size, ",[941,49971,49972],{},"du"," reports 0 bytes",[1362,49975,49976,49977,49980],{},"a separate ",[941,49978,49979],{},"\u003Cname>.icloud"," placeholder",[1344,49982,49983,49986,49992],{},[1362,49984,49985],{},"A plain read",[1362,49987,49988,49991],{},[950,49989,49990],{},"blocks transparently"," while the OS rehydrates, then succeeds (~0.7 s for a 195 KB bundle)",[1362,49993,49994],{},"fails; needs an explicit download trigger",[1344,49996,49997,50000,50003],{},[1362,49998,49999],{},"Store path taken",[1362,50001,50002],{},"the ordinary read path",[1362,50004,50005,50006],{},"stub detection → ",[941,50007,50008],{},"brctl download",[937,50010,50011,50012,50015,50016,50019,50020,50022],{},"On current macOS the stub-detection sequence never fires: ",[941,50013,50014],{},"stat(path)"," succeeds, so\nthe store is correct here through its normal path. The stub branch and\n",[941,50017,50018],{},"triggerDownload"," are the ",[950,50021,49952],{}," shape, kept for older macOS and other sync\nsurfaces — not the common case.",[7646,50024,50026],{"color":10789,"icon":50025},"i-lucide-clock",[937,50027,50028,50031,50032,50035,50036,50039],{},[950,50029,50030],{},"The consequence is latency, not errors."," A read of an evicted bundle can block on\nthe network with no advance signal. Telling evicted-dataless from locally-present\nwould need an ",[941,50033,50034],{},"st_blocks","-vs-logical-size comparison, which the duck-typed\n",[941,50037,50038],{},"ICloudFs"," facade does not expose — so there is no way to check first. Budget for a\nslow first read rather than trying to detect one.",[1022,50041,9542],{"id":2854},[1111,50043,50044,50054,50058],{},[1114,50045,49530,50046,49534,50048,49537,50050,17904,50052,49543],{},[941,50047,49533],{},[941,50049,3154],{},[941,50051,49540],{},[941,50053,1086],{},[1114,50055,50056,49548],{},[941,50057,9539],{},[1114,50059,50060,50061,1520],{},"Node on macOS only. Factory: ",[941,50062,50063],{},"toIcloud(options: ICloudStoreOptions)",[1022,50065,46936],{"id":46935},[937,50067,46939,50068,46944,50070,968,50073,1952,50075,7356,50077,964,50080,50082,50083,50086,50087,7356,50089,1952,50091,7356,50093,50095,50096,50098,50099,50101,50102,22207,50104,50106,50107,50109],{},[1007,50069,46943],{"href":46942},[941,50071,50072],{},"kind: 'icloud'",[941,50074,49773],{},[941,50076,46953],{},[941,50078,50079],{},"{ folder }",[950,50081,45375],{},",\nunlike most stores in its tier ",[941,50084,50085],{},"toIcloud()"," has no default. ",[941,50088,16362],{},[941,50090,49578],{},[941,50092,48992],{},[950,50094,45375],{}," (an ",[941,50097,50038],{},") — this store\nhas no filesystem facade of its own; ",[941,50100,46978],{}," without it throws. Because\n",[941,50103,50085],{},[941,50105,49533],{},", its factory's return type is\ndeliberately unnarrowed — wrap the resolved store with ",[941,50108,49540],{},"\nif you need the six-method surface.",[2217,50111,50113],{"className":2219,"code":50112,"language":2221,"meta":1037,"style":1037},"import { icloudStoreDescriptor, registerIcloudStore } from '@noy-db\u002Fto-icloud'\n\nconst descriptor = icloudStoreDescriptor({ folder: '\u002FUsers\u002Fme\u002FLibrary\u002FMobile Documents\u002F…' })\n",[941,50114,50115,50139,50143],{"__ignoreMap":1037},[2225,50116,50117,50119,50121,50124,50126,50129,50131,50133,50135,50137],{"class":2227,"line":2228},[2225,50118,2232],{"class":2231},[2225,50120,2236],{"class":2235},[2225,50122,50123],{"class":2239}," icloudStoreDescriptor",[2225,50125,2298],{"class":2235},[2225,50127,50128],{"class":2239}," registerIcloudStore",[2225,50130,2243],{"class":2235},[2225,50132,2246],{"class":2231},[2225,50134,2249],{"class":2235},[2225,50136,48788],{"class":2252},[2225,50138,2256],{"class":2235},[2225,50140,50141],{"class":2227,"line":1041},[2225,50142,2285],{"emptyLinePlaceholder":2284},[2225,50144,50145,50147,50149,50151,50153,50155,50157,50159,50161,50163,50166,50168,50170],{"class":2227,"line":1038},[2225,50146,2775],{"class":2774},[2225,50148,47019],{"class":2239},[2225,50150,2781],{"class":2235},[2225,50152,50123],{"class":2291},[2225,50154,2789],{"class":2239},[2225,50156,2792],{"class":2235},[2225,50158,4702],{"class":2307},[2225,50160,2311],{"class":2235},[2225,50162,2249],{"class":2235},[2225,50164,50165],{"class":2252},"\u002FUsers\u002Fme\u002FLibrary\u002FMobile Documents\u002F…",[2225,50167,2805],{"class":2235},[2225,50169,2243],{"class":2235},[2225,50171,2362],{"class":2239},[1022,50173,2621],{"id":2620},[1111,50175,50176,50180],{},[1114,50177,50178],{},[1007,50179,47064],{"href":2634},[1114,50181,50182],{},[1007,50183,379],{"href":50184},".\u002Fdrive",[2645,50186,37016],{},{"title":1037,"searchDepth":1038,"depth":1038,"links":50188},[50189,50190,50191,50192,50193],{"id":29856,"depth":1041,"text":29857},{"id":49928,"depth":1041,"text":49929},{"id":2854,"depth":1041,"text":9542},{"id":46935,"depth":1041,"text":46936},{"id":2620,"depth":1041,"text":2621},"iCloud Drive bundle store (Node on macOS) — handles both eviction shapes (modern dataless-in-place, legacy .icloud stubs) and device conflict copies.",{},{"title":387,"description":50194},"vTom-UvASwqCUgIeM87qW4fB2f0nVoTio6CNTvSvOuQ",{"id":50199,"title":50200,"access":12,"apiToc":929,"body":50201,"category":1042,"description":51117,"extension":1044,"meta":51118,"navigation":51119,"nest":3303,"path":342,"seo":51120,"sinceVersion":1042,"status":1049,"stem":343,"subtitle":1042,"tier":1050,"tocExternal":1042,"__hash__":51121},"docs\u002Ffamilies\u002Fto\u002Findex.md","`@noy-db\u002Fto-*` — Storage destinations",{"type":931,"value":50202,"toc":51105},[50203,50210,50221,50255,50257,50260,50324,50328,50405,50409,50454,50472,50475,50575,50579,50668,50672,50717,50726,50730,50773,50775,50784,51092],[934,50204,50206,50209],{"id":50205},"noy-dbto-storage-destinations",[941,50207,50208],{},"@noy-db\u002Fto-*"," — Storage destinations",[1061,50211,50212],{},[937,50213,50214,50217,50218,50220],{},[950,50215,50216],{},"Where your ciphertext lives."," Pick one backend per vault — or several,\nvia ",[941,50219,4620],{}," — and swap it later without touching your app code.",[937,50222,960,50223,30769,50225,50231,50232,50234,50235,50239,50240,50243,50244,50247,50248,50251,50252,50254],{},[941,50224,1260],{},[1092,50226,50227,50228,50230],{},"\"data goes ",[950,50229,4879],{}," a backend.\""," Every ",[941,50233,1779],{},"\npackage implements the same 6-method ",[1007,50236,50237,39774],{"href":41051},[941,50238,3154],{},"\nand only ever sees ",[950,50241,50242],{},"encrypted envelopes"," — never plaintext, and never a\nquery (queries run inside the hub, after decryption). A few essential\nstores ship alongside noy-db; the extended stores — cloud object stores, SQL\ndatabases, remote filesystems, personal drives, and Cloudflare adapters —\nare published from the companion\n",[1007,50245,5351],{"href":6965,"rel":50246},[3371]," repo under the same\n",[941,50249,50250],{},"@noy-db"," npm names, installed exactly like a core store. Which repo\npublishes each one, and the current counts, are in the generated\n",[1007,50253,860],{"href":24586}," matrix —\nits Scope column, not this paragraph, is the authority.",[1022,50256,30965],{"id":30964},[1591,50258,50259],{"id":49766},"Local",[1338,50261,50262,50271],{},[1341,50263,50264],{},[1344,50265,50266,50268],{},[1347,50267,9530],{},[1347,50269,50270],{},"Description",[1357,50272,50273,50289,50300,50311],{},[1344,50274,50275,50281],{},[1362,50276,50277],{},[1007,50278,50279],{"href":49876},[941,50280,391],{},[1362,50282,50283,50284,968,50286,1520],{},"Fuller in-memory store. Tests, REPL, ephemeral caches. ",[941,50285,7811],{},[941,50287,50288],{},"txAtomic: true",[1344,50290,50291,50297],{},[1362,50292,50293],{},[1007,50294,50295],{"href":47341},[941,50296,383],{},[1362,50298,50299],{},"Local disk \u002F USB stick. One JSON file per record, inspectable with any editor.",[1344,50301,50302,50308],{},[1362,50303,50304],{},[1007,50305,50306],{"href":47878},[941,50307,358],{},[1362,50309,50310],{},"The recommended browser store for PWAs. IndexedDB quota is typically 50–80% of available disk; CAS is atomic.",[1344,50312,50313,50319],{},[1362,50314,50315],{},[1007,50316,50317],{"href":48020},[941,50318,362],{},[1362,50320,50321,50323],{},[941,50322,41955],{}," for small vaults under the 5 MB cap, synchronous access. Optional key obfuscation.",[1591,50325,50327],{"id":50326},"cloud-object-kv","Cloud object & KV",[1338,50329,50330,50338],{},[1341,50331,50332],{},[1344,50333,50334,50336],{},[1347,50335,9530],{},[1347,50337,50270],{},[1357,50339,50340,50354,50377,50391],{},[1344,50341,50342,50348],{},[1362,50343,50344],{},[1007,50345,50346],{"href":30830},[941,50347,129],{},[1362,50349,50350,50351,50353],{},"S3 object per record. Atomic CAS via conditional writes. ",[941,50352,39784],{}," is O(N) requests.",[1344,50355,50356,50362],{},[1362,50357,50358],{},[1007,50359,50360],{"href":47302},[941,50361,347],{},[1362,50363,50364,50365,46882,50367,46886,50369,50371,50372,50374,50375,1520],{},"DynamoDB single-table (",[941,50366,46881],{},[941,50368,46885],{},[941,50370,46889],{},"), so ",[941,50373,39784],{}," is a single ",[941,50376,46896],{},[1344,50378,50379,50385],{},[1362,50380,50381],{},[1007,50382,50383],{"href":47307},[941,50384,375],{},[1362,50386,50387,50388,50390],{},"Zero egress fees. S3-API-compatible: a thin factory around ",[941,50389,129],{}," pointed at the R2 endpoint.",[1344,50392,50393,50399],{},[1362,50394,50395],{},[1007,50396,50397],{"href":49475},[941,50398,371],{},[1362,50400,50401,50402,50404],{},"Edge SQLite inside Cloudflare Workers via the ",[941,50403,48922],{}," binding.",[1591,50406,50408],{"id":50407},"cloud-sync-drives","Cloud-sync drives",[1338,50410,50411,50419],{},[1341,50412,50413],{},[1344,50414,50415,50417],{},[1347,50416,9530],{},[1347,50418,50270],{},[1357,50420,50421,50437],{},[1344,50422,50423,50429],{},[1362,50424,50425],{},[1007,50426,50427],{"href":50184},[941,50428,379],{},[1362,50430,50431,50432,50434,50435,1520],{},"Google Drive bundle store. Whole vault as one ",[941,50433,1402],{}," bundle, opaque ULID filename, in the hidden ",[941,50436,49522],{},[1344,50438,50439,50445],{},[1362,50440,50441],{},[1007,50442,50443],{"href":49684},[941,50444,387],{},[1362,50446,50447,50448,50450,50451,50453],{},"iCloud Drive bundle store (Node on macOS). Detects ",[941,50449,48840],{}," eviction stubs; raises ",[941,50452,49924],{}," on conflicts.",[937,50455,50456,997,50458,50460,50461,50463,50464,17904,50466,50468,50469,50471],{},[941,50457,379],{},[941,50459,387],{}," are bundle stores (",[941,50462,49533],{},"): wrap them with\n",[941,50465,49540],{},[941,50467,1086],{}," to get the standard six-method\n",[941,50470,3154],{}," surface.",[1591,50473,50474],{"id":153},"SQL",[1338,50476,50477,50485],{},[1341,50478,50479],{},[1344,50480,50481,50483],{},[1347,50482,9530],{},[1347,50484,50270],{},[1357,50486,50487,50507,50523,50545,50560],{},[1344,50488,50489,50496],{},[1362,50490,50491],{},[1007,50492,50494],{"href":50493},".\u002Fpostgres",[941,50495,407],{},[1362,50497,50498,50499,50502,50503,50506],{},"Encrypted-envelope KV in a single table with a ",[941,50500,50501],{},"jsonb"," column. Any duck-typed ",[941,50504,50505],{},"query(sql, params)"," client.",[1344,50508,50509,50516],{},[1362,50510,50511],{},[1007,50512,50514],{"href":50513},".\u002Fmysql",[941,50515,399],{},[1362,50517,50518,50519,50522],{},"Encrypted-envelope KV with a ",[941,50520,50521],{},"JSON"," column, for MySQL 8+ \u002F MariaDB.",[1344,50524,50525,50531],{},[1362,50526,50527],{},[1007,50528,50529],{"href":49096},[941,50530,422],{},[1362,50532,50533,50534,3155,50536,968,50539,968,50542,957],{},"Single-file SQLite for 10K+ records. Duck-typed ",[941,50535,31067],{},[941,50537,50538],{},"better-sqlite3",[941,50540,50541],{},"node:sqlite",[941,50543,50544],{},"bun:sqlite",[1344,50546,50547,50554],{},[1362,50548,50549],{},[1007,50550,50552],{"href":50551},".\u002Fturso",[941,50553,439],{},[1362,50555,50556,50557,50559],{},"Hosted libSQL with multi-region replication. Same table\u002FDDL as ",[941,50558,422],{}," — a factory swap.",[1344,50561,50562,50569],{},[1362,50563,50564],{},[1007,50565,50567],{"href":50566},".\u002Fsupabase",[941,50568,435],{},[1362,50570,50571,50572,50574],{},"Thin factory over ",[941,50573,407],{}," for the Postgres connection you already have. Never talks to Supabase REST\u002FRealtime.",[1591,50576,50578],{"id":50577},"remote-filesystems","Remote filesystems",[1338,50580,50581,50589],{},[1341,50582,50583],{},[1344,50584,50585,50587],{},[1347,50586,9530],{},[1347,50588,50270],{},[1357,50590,50591,50606,50626,50638,50654],{},[1344,50592,50593,50600],{},[1362,50594,50595],{},[1007,50596,50598],{"href":50597},".\u002Fssh",[941,50599,426],{},[1362,50601,50602,50603,1520],{},"SFTP over SSH, keys only (no passwords). Any Linux\u002FmacOS server with ",[941,50604,50605],{},"sshd",[1344,50607,50608,50615],{},[1362,50609,50610],{},[1007,50611,50613],{"href":50612},".\u002Fwebdav",[941,50614,443],{},[1362,50616,50617,50618,50621,50622,50625],{},"Nextcloud \u002F ownCloud \u002F Apache ",[941,50619,50620],{},"mod_dav",", or any RFC 4918 server. Pure ",[941,50623,50624],{},"fetch()","-based, zero dependencies.",[1344,50627,50628,50635],{},[1362,50629,50630],{},[1007,50631,50633],{"href":50632},".\u002Fsmb",[941,50634,418],{},[1362,50636,50637],{},"SMB\u002FCIFS (NTLM or Kerberos). Windows file servers, NAS devices, corporate shared drives.",[1344,50639,50640,50647],{},[1362,50641,50642],{},[1007,50643,50645],{"href":50644},".\u002Fnfs",[941,50646,403],{},[1362,50648,50649,50650,50653],{},"File store for a pre-mounted NFS path, with pre-flight mount diagnostics (",[941,50651,50652],{},"nolock",", stale attribute caching).",[1344,50655,50656,50662],{},[1362,50657,50658],{},[1007,50659,50660],{"href":37007},[941,50661,263],{},[1362,50663,50664,50665,50667],{},"Any HTTP endpoint running an ",[941,50666,41085],{}," relay. The server holds ciphertext it cannot open.",[1591,50669,50671],{"id":50670},"diagnostics-wrappers","Diagnostics & wrappers",[1338,50673,50674,50682],{},[1341,50675,50676],{},[1344,50677,50678,50680],{},[1347,50679,9530],{},[1347,50681,50270],{},[1357,50683,50684,50705],{},[1344,50685,50686,50693],{},[1362,50687,50688],{},[1007,50689,50691],{"href":50690},".\u002Fprobe",[941,50692,411],{},[1362,50694,50695,50696,1261,50698,1261,50700,1261,50703,1520],{},"Diagnostic companion. Not a backend — benchmarks any store and reports its suitability for ",[941,50697,47925],{},[941,50699,34951],{},[941,50701,50702],{},"backup",[941,50704,605],{},[1344,50706,50707,50714],{},[1362,50708,50709],{},[1007,50710,50712],{"href":50711},".\u002Fmeter",[941,50713,395],{},[1362,50715,50716],{},"Pass-through metrics wrapper. Wraps any store and records op latency, error rate, and CAS conflicts.",[937,50718,50719,50720],{},"→ Source and changelogs: ",[950,50721,50722],{},[1007,50723,50725],{"href":6965,"rel":50724},[3371],"github.com\u002Fnoy-db\u002Fto",[1591,50727,50729],{"id":50728},"concepts","Concepts",[1338,50731,50732,50740],{},[1341,50733,50734],{},[1344,50735,50736,50738],{},[1347,50737,5721],{},[1347,50739,50270],{},[1357,50741,50742,50758],{},[1344,50743,50744,50752],{},[1362,50745,50746],{},[1007,50747,50749],{"href":50748},".\u002Fchoosing-a-store",[941,50750,50751],{},"choosing a store",[1362,50753,50754,50755,50757],{},"How to pick a ",[941,50756,1779],{}," store per platform, and the one real gap in the matrix.",[1344,50759,50760,50766],{},[1362,50761,50762],{},[1007,50763,50764],{"href":46942},[941,50765,46943],{},[1362,50767,50768,50769,50772],{},"A credentialless, JSON-serializable second construction path alongside ",[941,50770,50771],{},"to\u003CKind>()"," — build from data, resolve back to a live store.",[1022,50774,31192],{"id":31191},[937,50776,50777,50779,50780,50783],{},[941,50778,9539],{}," is the load-bearing capability: whether ",[941,50781,50782],{},"put(expectedVersion)","\nfails atomically on a version mismatch, or is only an advisory check.",[1338,50785,50786,50801],{},[1341,50787,50788],{},[1344,50789,50790,50792,50795,50798],{},[1347,50791,9665],{},[1347,50793,50794],{"align":9536},"Atomic CAS",[1347,50796,50797],{},"Shape",[1347,50799,50800],{},"Best for",[1357,50802,50803,50816,50829,50842,50855,50869,50882,50895,50908,50921,50934,50947,50960,50973,50986,50999,51012,51025,51038,51051,51064,51078],{},[1344,50804,50805,50809,50811,50813],{},[1362,50806,50807],{},[941,50808,391],{},[1362,50810,9581],{"align":9536},[1362,50812,24158],{},[1362,50814,50815],{},"tests, REPL, ephemeral caches",[1344,50817,50818,50822,50824,50826],{},[1362,50819,50820],{},[941,50821,383],{},[1362,50823,9559],{"align":9536},[1362,50825,24158],{},[1362,50827,50828],{},"USB stick, local-first apps",[1344,50830,50831,50835,50837,50839],{},[1362,50832,50833],{},[941,50834,358],{},[1362,50836,9581],{"align":9536},[1362,50838,24158],{},[1362,50840,50841],{},"PWAs, browser apps",[1344,50843,50844,50848,50850,50852],{},[1362,50845,50846],{},[941,50847,362],{},[1362,50849,9559],{"align":9536},[1362,50851,24158],{},[1362,50853,50854],{},"tiny vaults under 5 MB",[1344,50856,50857,50861,50863,50866],{},[1362,50858,50859],{},[941,50860,129],{},[1362,50862,9581],{"align":9536},[1362,50864,50865],{},"record + bundle",[1362,50867,50868],{},"S3 object storage, blobs",[1344,50870,50871,50875,50877,50879],{},[1362,50872,50873],{},[941,50874,347],{},[1362,50876,9581],{"align":9536},[1362,50878,24158],{},[1362,50880,50881],{},"single-table DynamoDB, low latency",[1344,50883,50884,50888,50890,50892],{},[1362,50885,50886],{},[941,50887,375],{},[1362,50889,9581],{"align":9536},[1362,50891,24158],{},[1362,50893,50894],{},"zero-egress object storage",[1344,50896,50897,50901,50903,50905],{},[1362,50898,50899],{},[941,50900,371],{},[1362,50902,9581],{"align":9536},[1362,50904,24158],{},[1362,50906,50907],{},"edge SQLite in Workers",[1344,50909,50910,50914,50916,50918],{},[1362,50911,50912],{},[941,50913,379],{},[1362,50915,37998],{"align":9536},[1362,50917,629],{},[1362,50919,50920],{},"Google Drive backup",[1344,50922,50923,50927,50929,50931],{},[1362,50924,50925],{},[941,50926,387],{},[1362,50928,37998],{"align":9536},[1362,50930,629],{},[1362,50932,50933],{},"iCloud Drive backup (macOS)",[1344,50935,50936,50940,50942,50944],{},[1362,50937,50938],{},[941,50939,407],{},[1362,50941,9581],{"align":9536},[1362,50943,24158],{},[1362,50945,50946],{},"Postgres-backed apps",[1344,50948,50949,50953,50955,50957],{},[1362,50950,50951],{},[941,50952,399],{},[1362,50954,9581],{"align":9536},[1362,50956,24158],{},[1362,50958,50959],{},"MySQL 8+ \u002F MariaDB apps",[1344,50961,50962,50966,50968,50970],{},[1362,50963,50964],{},[941,50965,422],{},[1362,50967,9581],{"align":9536},[1362,50969,24158],{},[1362,50971,50972],{},"10K+ records, single file",[1344,50974,50975,50979,50981,50983],{},[1362,50976,50977],{},[941,50978,439],{},[1362,50980,9581],{"align":9536},[1362,50982,24158],{},[1362,50984,50985],{},"hosted libSQL, multi-region",[1344,50987,50988,50992,50994,50996],{},[1362,50989,50990],{},[941,50991,435],{},[1362,50993,9581],{"align":9536},[1362,50995,24158],{},[1362,50997,50998],{},"existing Supabase Postgres project",[1344,51000,51001,51005,51007,51009],{},[1362,51002,51003],{},[941,51004,426],{},[1362,51006,9559],{"align":9536},[1362,51008,24158],{},[1362,51010,51011],{},"SFTP servers, key-only auth",[1344,51013,51014,51018,51020,51022],{},[1362,51015,51016],{},[941,51017,443],{},[1362,51019,9559],{"align":9536},[1362,51021,24158],{},[1362,51023,51024],{},"Nextcloud \u002F ownCloud servers",[1344,51026,51027,51031,51033,51035],{},[1362,51028,51029],{},[941,51030,418],{},[1362,51032,9559],{"align":9536},[1362,51034,24158],{},[1362,51036,51037],{},"Windows file servers, NAS",[1344,51039,51040,51044,51046,51048],{},[1362,51041,51042],{},[941,51043,403],{},[1362,51045,9559],{"align":9536},[1362,51047,24158],{},[1362,51049,51050],{},"pre-mounted NFS paths",[1344,51052,51053,51057,51059,51061],{},[1362,51054,51055],{},[941,51056,263],{},[1362,51058,9581],{"align":9536},[1362,51060,24158],{},[1362,51062,51063],{},"an in-rest relay over HTTP",[1344,51065,51066,51070,51072,51075],{},[1362,51067,51068],{},[941,51069,411],{},[1362,51071,37998],{"align":9536},[1362,51073,51074],{},"diagnostic",[1362,51076,51077],{},"benchmarking a store before committing",[1344,51079,51080,51084,51086,51089],{},[1362,51081,51082],{},[941,51083,395],{},[1362,51085,37998],{"align":9536},[1362,51087,51088],{},"wrapper",[1362,51090,51091],{},"production latency \u002F error metrics",[937,51093,51094,51095,1261,51097,51099,51100,968,51102,51104],{},"Stores without atomic CAS are still safe as single-writer primaries or as\n",[941,51096,50702],{},[941,51098,605],{}," targets — pair them with a CAS-capable primary (e.g.\n",[941,51101,407],{},[941,51103,347],{},") when multiple writers are involved.",{"title":1037,"searchDepth":1038,"depth":1038,"links":51106},[51107,51116],{"id":30964,"depth":1041,"text":30965,"children":51108},[51109,51110,51111,51112,51113,51114,51115],{"id":49766,"depth":1038,"text":50259},{"id":50326,"depth":1038,"text":50327},{"id":50407,"depth":1038,"text":50408},{"id":153,"depth":1038,"text":50474},{"id":50577,"depth":1038,"text":50578},{"id":50670,"depth":1038,"text":50671},{"id":50728,"depth":1038,"text":50729},{"id":31191,"depth":1041,"text":31192},"The storage-destination catalog: every to-* store from local file and IndexedDB to cloud, SQL, and remote filesystems — all handling ciphertext only.",{},{"title":341,"icon":127},{"title":50200,"description":51117},"VmiJyqSy9CQBggS48CXiFtHdXe59pOG4hgcWLpp-13Q",{"id":51123,"title":391,"access":12,"apiToc":929,"body":51124,"category":1042,"description":51208,"extension":1044,"meta":51209,"navigation":1042,"nest":3303,"path":392,"seo":51210,"sinceVersion":1042,"status":1049,"stem":393,"subtitle":1042,"tier":6922,"tocExternal":1042,"__hash__":51211},"docs\u002Ffamilies\u002Fto\u002Fmemory.md",{"type":931,"value":51125,"toc":51203},[51126,51128,51140,51143,51145,51165,51167,51187,51189],[934,51127,391],{"id":391},[1061,51129,51130],{},[937,51131,51132,1368,51136,51139],{},[950,51133,51134],{},[941,51135,9638],{},[941,51137,51138],{},"pnpm add @noy-db\u002Fto-memory","\nThe fuller in-memory store — reach for it once the kernel's built-in default isn't enough.",[46871,51141],{":rows":51142},"[{\"label\":\"Shape\",\"value\":\"record\"},{\"label\":\"Atomic CAS\",\"value\":\"✓\"},{\"label\":\"Locality\",\"value\":\"local\"},{\"label\":\"Speed\",\"value\":\"fast\"},{\"label\":\"Persistence\",\"value\":\"ephemeral\"}]",[1022,51144,29857],{"id":29856},[937,51146,51147,51149,51150,51152,51153,51155,51156,51158,51159,51161,51162,51164],{},[941,51148,2134],{}," already ships a built-in in-memory default store, so ",[941,51151,2992],{}," isn't\na prerequisite to start. It's the package to reach for once you need the optional\n",[941,51154,3154],{}," methods the built-in default omits: ",[941,51157,3127],{},", atomic multi-record ",[941,51160,4399],{},",\nor ",[941,51163,47528],{},". Data lives only in the current process — it's gone when the process exits.\nUse it for unit\u002Fintegration tests, short-lived scripts, benchmarks, and prototyping\nbefore wiring up persistent storage.",[1022,51166,9542],{"id":2854},[1111,51168,51169,51179,51182],{},[1114,51170,51171,968,51173,51175,51176,51178],{},[941,51172,7811],{},[941,51174,50288],{}," — synchronous ops over nested ",[941,51177,8002],{},"s are\ninherently atomic, making it a great sanity backstop to test other stores against.",[1114,51180,51181],{},"Record shape (one envelope per record), not bundle.",[1114,51183,46929,51184,1520],{},[941,51185,51186],{},"toMemory(opts?: { clockUncertaintyMs?: number })",[1022,51188,2621],{"id":2620},[1111,51190,51191,51195,51199],{},[1114,51192,51193],{},[1007,51194,47064],{"href":2634},[1114,51196,51197],{},[1007,51198,383],{"href":47341},[1114,51200,51201],{},[1007,51202,411],{"href":50690},{"title":1037,"searchDepth":1038,"depth":1038,"links":51204},[51205,51206,51207],{"id":29856,"depth":1041,"text":29857},{"id":2854,"depth":1041,"text":9542},{"id":2620,"depth":1041,"text":2621},"Fuller in-memory store for noy-db — tests, REPLs, and ephemeral caches, with listVaults, ping, and atomic multi-record tx.",{},{"title":391,"description":51208},"SWWopQG-UcW_CPvlAZbdGlB3LVDUL8x3ClpSghkDqHo",{"id":51213,"title":395,"access":12,"apiToc":929,"body":51214,"category":1042,"description":51341,"extension":1044,"meta":51342,"navigation":1042,"nest":3303,"path":396,"seo":51343,"sinceVersion":1042,"status":1049,"stem":397,"subtitle":1042,"tier":6922,"tocExternal":1042,"__hash__":51344},"docs\u002Ffamilies\u002Fto\u002Fmeter.md",{"type":931,"value":51215,"toc":51336},[51216,51218,51230,51233,51235,51276,51278,51324,51326],[934,51217,395],{"id":395},[1061,51219,51220],{},[937,51221,51222,1368,51226,51229],{},[950,51223,51224],{},[941,51225,9595],{},[941,51227,51228],{},"pnpm add @noy-db\u002Fto-meter","\nWraps any store and records what your app is actually doing.",[46871,51231],{":rows":51232},"[{\"label\":\"Shape\",\"value\":\"n\u002Fa — pass-through wrapper\"},{\"label\":\"Wraps\",\"value\":\"any NoydbStore\"}]",[1022,51234,29857],{"id":29856},[937,51236,51237,51239,51240,51242,51243,51245,51246,1625,51248,1625,51250,1625,51252,1625,51254,1625,51256,51258,51259,1625,51262,51265,51266,51268,51269,1261,51272,51275],{},[941,51238,9600],{}," wraps any ",[941,51241,3154],{}," and returns the same six-method surface\nplus a ",[941,51244,395],{}," handle, so app code doesn't change. It records real per-method\n(",[941,51247,3158],{},[941,51249,3161],{},[941,51251,3164],{},[941,51253,3167],{},[941,51255,3170],{},[941,51257,3173],{},") latency percentiles, error\nrates, byte counts, and CAS conflicts. An optional synthetic liveness probe emits\n",[941,51260,51261],{},"degraded",[941,51263,51264],{},"restored"," events. Unlike the ",[1007,51267,411],{"href":50690}," — whose\n",[941,51270,51271],{},"runStoreProbe()",[941,51273,51274],{},"probeTopology()"," this same package ships — it measures live\ntraffic rather than synthetic benchmarks.",[1022,51277,9542],{"id":2854},[1111,51279,51280,51307,51321],{},[1114,51281,51282,51285,51286,953,51288,1261,51290,2601,51292,1884,51294,968,51297,2462,51300,51303,51304,957],{},[941,51283,51284],{},"meter.snapshot()"," returns per-method stats plus ",[941,51287,6631],{},[941,51289,40950],{},[941,51291,51261],{},[941,51293,47669],{},[941,51295,51296],{},"casConflicts",[941,51298,51299],{},"totalCalls",[941,51301,51302],{},"windowMs"," (time since start\nor last ",[941,51305,51306],{},"reset()",[1114,51308,51309,51311,51312,968,51314,51317,51318,51320],{},[941,51310,395],{}," also offers ",[941,51313,51306],{},[941,51315,51316],{},"subscribe(listener)"," for degraded\u002Frestored\ntransitions, and ",[941,51319,12797],{}," to stop the optional liveness timer.",[1114,51322,51323],{},"Pass-through wrapper — the wrapped store's behaviour is unchanged.",[1022,51325,2621],{"id":2620},[1111,51327,51328,51332],{},[1114,51329,51330],{},[1007,51331,47064],{"href":2634},[1114,51333,51334],{},[1007,51335,411],{"href":50690},{"title":1037,"searchDepth":1038,"depth":1038,"links":51337},[51338,51339,51340],{"id":29856,"depth":1041,"text":29857},{"id":2854,"depth":1041,"text":9542},{"id":2620,"depth":1041,"text":2621},"Pass-through metrics wrapper for any to-* store — records per-method latency, error rate, and CAS conflicts on real traffic without changing behaviour.",{},{"title":395,"description":51341},"3dW7Ue1s-5IjWnyZ6Xk0K9JRKOXsQB4PPasFjNPMg2U",{"id":51346,"title":399,"access":12,"apiToc":929,"body":51347,"category":1042,"description":51528,"extension":1044,"meta":51529,"navigation":1042,"nest":3303,"path":400,"seo":51530,"sinceVersion":1042,"status":1049,"stem":401,"subtitle":1042,"tier":1050,"tocExternal":1042,"__hash__":51531},"docs\u002Ffamilies\u002Fto\u002Fmysql.md",{"type":931,"value":51348,"toc":51522},[51349,51351,51367,51370,51372,51378,51380,51396,51398,51430,51504,51506,51520],[934,51350,399],{"id":399},[1061,51352,51353],{},[937,51354,51355,1368,51360,51363,51364,51366],{},[950,51356,51357],{},[941,51358,51359],{},"@noy-db\u002Fto-mysql",[941,51361,51362],{},"pnpm add @noy-db\u002Fto-mysql","\nEncrypted-envelope KV with a ",[941,51365,50521],{}," column.",[46871,51368],{":rows":51369},"[{\"label\":\"Shape\",\"value\":\"record\"},{\"label\":\"Atomic CAS\",\"value\":\"✓\"},{\"label\":\"Locality\",\"value\":\"remote\"},{\"label\":\"Speed\",\"value\":\"medium\"},{\"label\":\"Persistence\",\"value\":\"durable\"}]",[1022,51371,29857],{"id":29856},[937,51373,51374,51375,51377],{},"Stores each record's ciphertext envelope in a ",[941,51376,50521],{}," column, for MySQL 8+ or\nMariaDB. Works with any duck-typed mysql2-style pool\u002Fconnection.",[1022,51379,9542],{"id":2854},[1111,51381,51382,51389,51391],{},[1114,51383,51384,9750,51386,51388],{},[941,51385,7811],{},[941,51387,9779],{}," plus a row-count check.",[1114,51390,48944],{},[1114,51392,46929,51393,1520],{},[941,51394,51395],{},"toMysql(options: MysqlStoreOptions)",[1022,51397,46936],{"id":46935},[937,51399,46939,51400,46944,51402,968,51405,1952,51407,7356,51409,2874,51412,48968,51414,5502,51416,51418,51419,7356,51421,1952,51423,7356,51425,51427,51428,48998],{},[1007,51401,46943],{"href":46942},[941,51403,51404],{},"kind: 'mysql'",[941,51406,46950],{},[941,51408,46953],{},[941,51410,51411],{},"{ database?, table? }",[941,51413,1338],{},[941,51415,48971],{},[941,51417,31066],{}," is identity-only, not consumed by\nthe factory — the connection already carries it. ",[941,51420,16362],{},[941,51422,48989],{},[941,51424,48992],{},[950,51426,45375],{}," — this store has no client\nlibrary of its own; ",[941,51429,46978],{},[2217,51431,51433],{"className":2219,"code":51432,"language":2221,"meta":1037,"style":1037},"import { mysqlStoreDescriptor, registerMysqlStore } from '@noy-db\u002Fto-mysql'\n\nconst descriptor = mysqlStoreDescriptor({ database: 'app', table: 'noydb_envelopes' })\n",[941,51434,51435,51459,51463],{"__ignoreMap":1037},[2225,51436,51437,51439,51441,51444,51446,51449,51451,51453,51455,51457],{"class":2227,"line":2228},[2225,51438,2232],{"class":2231},[2225,51440,2236],{"class":2235},[2225,51442,51443],{"class":2239}," mysqlStoreDescriptor",[2225,51445,2298],{"class":2235},[2225,51447,51448],{"class":2239}," registerMysqlStore",[2225,51450,2243],{"class":2235},[2225,51452,2246],{"class":2231},[2225,51454,2249],{"class":2235},[2225,51456,51359],{"class":2252},[2225,51458,2256],{"class":2235},[2225,51460,51461],{"class":2227,"line":1041},[2225,51462,2285],{"emptyLinePlaceholder":2284},[2225,51464,51465,51467,51469,51471,51473,51475,51477,51479,51481,51483,51485,51487,51489,51491,51493,51495,51498,51500,51502],{"class":2227,"line":1038},[2225,51466,2775],{"class":2774},[2225,51468,47019],{"class":2239},[2225,51470,2781],{"class":2235},[2225,51472,51443],{"class":2291},[2225,51474,2789],{"class":2239},[2225,51476,2792],{"class":2235},[2225,51478,49062],{"class":2307},[2225,51480,2311],{"class":2235},[2225,51482,2249],{"class":2235},[2225,51484,41270],{"class":2252},[2225,51486,2805],{"class":2235},[2225,51488,2298],{"class":2235},[2225,51490,47030],{"class":2307},[2225,51492,2311],{"class":2235},[2225,51494,2249],{"class":2235},[2225,51496,51497],{"class":2252},"noydb_envelopes",[2225,51499,2805],{"class":2235},[2225,51501,2243],{"class":2235},[2225,51503,2362],{"class":2239},[1022,51505,2621],{"id":2620},[1111,51507,51508,51512,51516],{},[1114,51509,51510],{},[1007,51511,47064],{"href":2634},[1114,51513,51514],{},[1007,51515,407],{"href":50493},[1114,51517,51518],{},[1007,51519,422],{"href":49096},[2645,51521,37016],{},{"title":1037,"searchDepth":1038,"depth":1038,"links":51523},[51524,51525,51526,51527],{"id":29856,"depth":1041,"text":29857},{"id":2854,"depth":1041,"text":9542},{"id":46935,"depth":1041,"text":46936},{"id":2620,"depth":1041,"text":2621},"MySQL \u002F MariaDB adapter for noy-db — encrypted-envelope KV with a JSON column, for MySQL 8+ \u002F MariaDB, with atomic CAS.",{},{"title":399,"description":51528},"9iMiE9iicjkdPxFILuIO5M6411N6rdk1g5GeSLps4mM",{"id":51533,"title":403,"access":12,"apiToc":929,"body":51534,"category":1042,"description":51835,"extension":1044,"meta":51836,"navigation":1042,"nest":3303,"path":404,"seo":51837,"sinceVersion":1042,"status":1049,"stem":405,"subtitle":1042,"tier":1473,"tocExternal":1042,"__hash__":51838},"docs\u002Ffamilies\u002Fto\u002Fnfs.md",{"type":931,"value":51535,"toc":51828},[51536,51538,51554,51557,51559,51578,51580,51603,51607,51637,51660,51680,51682,51728,51810,51812,51826],[934,51537,403],{"id":403},[1061,51539,51540],{},[937,51541,51542,1368,51547,51550,51551,51553],{},[950,51543,51544],{},[941,51545,51546],{},"@noy-db\u002Fto-nfs",[941,51548,51549],{},"pnpm add @noy-db\u002Fto-nfs","\nSame layout as ",[941,51552,7461],{},", plus NFS mount diagnostics.",[46871,51555],{":rows":51556},"[{\"label\":\"Shape\",\"value\":\"record\"},{\"label\":\"Atomic CAS\",\"value\":\"✗\"},{\"label\":\"Locality\",\"value\":\"remote\"},{\"label\":\"Speed\",\"value\":\"slow\"},{\"label\":\"Persistence\",\"value\":\"durable\"}]",[1022,51558,29857],{"id":29856},[937,51560,51561,51562,51566,51567,51570,51571,51573,51574,51577],{},"Uses the same JSON-file indexed layout as ",[1007,51563,51564],{"href":47341},[941,51565,383],{}," for a ",[950,51568,51569],{},"pre-mounted","\nNFS path, with pre-flight checks that flag mount options known to silently break\nversioning guarantees on stock NFS mounts: ",[941,51572,50652],{}," (POSIX locks disabled →\nsilent concurrent-write corruption), stale attribute caching (",[941,51575,51576],{},"noac","), and wrong\nfilesystem type.",[1022,51579,9542],{"id":2854},[1111,51581,51582,51587,51598],{},[1114,51583,51584,51586],{},[941,51585,47869],{}," — no CAS primitive; the pre-flight checks exist precisely\nbecause concurrent writers on a misconfigured mount can corrupt data silently.",[1114,51588,51589,51590,51593,51594,51597],{},"Record shape, one file per record. Exposes a ",[941,51591,51592],{},"diagnostics()"," method returning\n",[941,51595,51596],{},"{ risks, info }"," for the mount.",[1114,51599,46929,51600,1520],{},[941,51601,51602],{},"toNfs(options: NfsStoreOptions)",[1022,51604,51606],{"id":51605},"the-mount-is-cross-checked-against-the-address","The mount is cross-checked against the address",[937,51608,30857,51609,51612,51613,51616,51617,1324,51619,51621,51622,51625,51626,51629,51630,51633,51634,51636],{},[941,51610,51611],{},"0.6.0-pre.1"," the store compares the ",[941,51614,51615],{},"server:\u002Fexport"," it was told about\nagainst the device string of the mount it actually opened — for an NFS mount the\ndevice ",[1092,51618,3082],{},[941,51620,51615],{},", and the detector was already parsing it out of\n",[941,51623,51624],{},"\u002Fproc\u002Fmounts"," and discarding it. A pod claiming ",[941,51627,51628],{},"nas.local:\u002Fexports\u002Fvaults","\nresolved against a ",[941,51631,51632],{},"mountPath"," pointing somewhere else no longer writes to the\nwrong export silently: ",[941,51635,51592],{}," adds a risk on mismatch.",[937,51638,51639,51642,51643,2514,51646,51649,51650,51656,51657,51659],{},[941,51640,51641],{},"onDeviceMismatch: 'warn' | 'error'"," on both ",[941,51644,51645],{},"NfsStoreOptions",[941,51647,51648],{},"NfsDescriptorOptions"," picks what happens, and ",[950,51651,51652,51653],{},"defaults to ",[941,51654,51655],{},"'warn'"," — an\nexisting consumer whose descriptor is merely imprecise must not break on upgrade.\nSet ",[941,51658,20844],{}," when the address is meant to be authoritative.",[937,51661,51662,51663,51665,51666,51668,51669,51675,51676,51679],{},"The check is gated on both address halves ",[1092,51664,14128],{}," a device from the detector, so a\ndetector that cannot report one (every pre-",[941,51667,51611],{}," implementation, including\nan injected test double) never manufactures a mismatch. Only trailing slashes are\nnormalized; host case is left alone. ",[950,51670,51671,51672,51674],{},"This changes ",[941,51673,51592],{}," output"," — assert\nagainst ",[941,51677,51678],{},"risks"," by predicate, not by position.",[1022,51681,46936],{"id":46935},[937,51683,46939,51684,46944,51686,968,51689,1952,51692,7356,51694,51697,51698,51700,51701,51704,51705,51708,51709,51711,51712,51714,51715,38633,51719,7356,51721,1952,51724,51727],{},[1007,51685,46943],{"href":46942},[941,51687,51688],{},"kind: 'nfs'",[941,51690,51691],{},"class: 'lan'",[941,51693,46953],{},[941,51695,51696],{},"{ server?, export? }",", the\nlogical ",[941,51699,51615],{}," identity. It still does not ",[1092,51702,51703],{},"select"," the mount —\n",[941,51706,51707],{},"binding.mountPath"," is what the store actually opens, and is ",[950,51710,45375],{},", since\nwhere an export is mounted is device-local and never travels in a descriptor —\nbut from ",[941,51713,51611],{}," the factory forwards both halves and the store\n",[1007,51716,51718],{"href":51717},"#the-mount-is-cross-checked-against-the-address","cross-checks them against the real mount",[941,51720,16362],{},[941,51722,51723],{},"{ onNolock?, onDeviceMismatch? }",[941,51725,51726],{},"binding.mountDetector?"," carries\nthe existing test-injection seam through unchanged.",[2217,51729,51731],{"className":2219,"code":51730,"language":2221,"meta":1037,"style":1037},"import { nfsStoreDescriptor, registerNfsStore } from '@noy-db\u002Fto-nfs'\n\nconst descriptor = nfsStoreDescriptor({ server: 'nas.local', export: '\u002Fvaults' })\n\u002F\u002F later: locator.resolve(descriptor, { binding: { mountPath: '\u002Fmnt\u002Fnas' } })\n",[941,51732,51733,51757,51761,51805],{"__ignoreMap":1037},[2225,51734,51735,51737,51739,51742,51744,51747,51749,51751,51753,51755],{"class":2227,"line":2228},[2225,51736,2232],{"class":2231},[2225,51738,2236],{"class":2235},[2225,51740,51741],{"class":2239}," nfsStoreDescriptor",[2225,51743,2298],{"class":2235},[2225,51745,51746],{"class":2239}," registerNfsStore",[2225,51748,2243],{"class":2235},[2225,51750,2246],{"class":2231},[2225,51752,2249],{"class":2235},[2225,51754,51546],{"class":2252},[2225,51756,2256],{"class":2235},[2225,51758,51759],{"class":2227,"line":1041},[2225,51760,2285],{"emptyLinePlaceholder":2284},[2225,51762,51763,51765,51767,51769,51771,51773,51775,51778,51780,51782,51785,51787,51789,51792,51794,51796,51799,51801,51803],{"class":2227,"line":1038},[2225,51764,2775],{"class":2774},[2225,51766,47019],{"class":2239},[2225,51768,2781],{"class":2235},[2225,51770,51741],{"class":2291},[2225,51772,2789],{"class":2239},[2225,51774,2792],{"class":2235},[2225,51776,51777],{"class":2307}," server",[2225,51779,2311],{"class":2235},[2225,51781,2249],{"class":2235},[2225,51783,51784],{"class":2252},"nas.local",[2225,51786,2805],{"class":2235},[2225,51788,2298],{"class":2235},[2225,51790,51791],{"class":2307}," export",[2225,51793,2311],{"class":2235},[2225,51795,2249],{"class":2235},[2225,51797,51798],{"class":2252},"\u002Fvaults",[2225,51800,2805],{"class":2235},[2225,51802,2243],{"class":2235},[2225,51804,2362],{"class":2239},[2225,51806,51807],{"class":2227,"line":2288},[2225,51808,51809],{"class":2763},"\u002F\u002F later: locator.resolve(descriptor, { binding: { mountPath: '\u002Fmnt\u002Fnas' } })\n",[1022,51811,2621],{"id":2620},[1111,51813,51814,51818,51822],{},[1114,51815,51816],{},[1007,51817,47064],{"href":2634},[1114,51819,51820],{},[1007,51821,383],{"href":47341},[1114,51823,51824],{},[1007,51825,418],{"href":50632},[2645,51827,30448],{},{"title":1037,"searchDepth":1038,"depth":1038,"links":51829},[51830,51831,51832,51833,51834],{"id":29856,"depth":1041,"text":29857},{"id":2854,"depth":1041,"text":9542},{"id":51605,"depth":1041,"text":51606},{"id":46935,"depth":1041,"text":46936},{"id":2620,"depth":1041,"text":2621},"File store for a pre-mounted NFS path, with pre-flight mount diagnostics for nolock \u002F noac options that silently break versioning guarantees.",{},{"title":403,"description":51835},"ERlCkAP6rxsu2zUZhBXp2k4TE5F7dg7AK0S5x0GLyfo",{"id":51840,"title":407,"access":12,"apiToc":929,"body":51841,"category":1042,"description":52047,"extension":1044,"meta":52048,"navigation":1042,"nest":3303,"path":408,"seo":52049,"sinceVersion":1042,"status":1049,"stem":409,"subtitle":1042,"tier":1050,"tocExternal":1042,"__hash__":52050},"docs\u002Ffamilies\u002Fto\u002Fpostgres.md",{"type":931,"value":51842,"toc":52041},[51843,51845,51858,51860,51862,51883,51885,51901,51903,51936,52022,52024,52039],[934,51844,407],{"id":407},[1061,51846,51847],{},[937,51848,51849,1368,51854,51857],{},[950,51850,51851],{},[941,51852,51853],{},"@noy-db\u002Fto-postgres",[941,51855,51856],{},"pnpm add @noy-db\u002Fto-postgres","\nEncrypted-envelope KV in a single Postgres table.",[46871,51859],{":rows":51369},[1022,51861,29857],{"id":29856},[937,51863,51374,51864,51866,51867,1746,51869,2085,51872,51875,51876,968,51879,51882],{},[941,51865,50501],{}," column of a single table.\nWorks with any duck-typed client exposing ",[941,51868,50505],{},[941,51870,51871],{},"{ rows }",[941,51873,51874],{},"pg","\n(node-postgres), ",[941,51877,51878],{},"@vercel\u002Fpostgres",[941,51880,51881],{},"@neondatabase\u002Fserverless",", a raw drizzle-orm\npool, or postgres.js via a light shim.",[1022,51884,9542],{"id":2854},[1111,51886,51887,51894,51896],{},[1114,51888,51889,9750,51891,1520],{},[941,51890,7811],{},[941,51892,51893],{},"UPDATE … WHERE v = ? RETURNING id",[1114,51895,48944],{},[1114,51897,46929,51898,1520],{},[941,51899,51900],{},"toPostgres(options: PostgresStoreOptions)",[1022,51902,46936],{"id":46935},[937,51904,46939,51905,46944,51907,968,51910,1952,51912,7356,51914,2085,51917,48968,51919,5502,51921,997,51923,51925,51926,7356,51928,1952,51930,7356,51932,48995,51934,48998],{},[1007,51906,46943],{"href":46942},[941,51908,51909],{},"kind: 'postgres'",[941,51911,46950],{},[941,51913,46953],{},[941,51915,51916],{},"{ database?, schema?, table? }",[941,51918,1338],{},[941,51920,48971],{},[941,51922,31066],{},[941,51924,23934],{}," are\nidentity-only, not consumed by the factory — the connection already carries\nthem. ",[941,51927,16362],{},[941,51929,48989],{},[941,51931,48992],{},[950,51933,45375],{},[941,51935,46978],{},[2217,51937,51939],{"className":2219,"code":51938,"language":2221,"meta":1037,"style":1037},"import { postgresStoreDescriptor, registerPostgresStore } from '@noy-db\u002Fto-postgres'\n\nconst descriptor = postgresStoreDescriptor({ database: 'app', schema: 'public', table: 'noydb_envelopes' })\n",[941,51940,51941,51965,51969],{"__ignoreMap":1037},[2225,51942,51943,51945,51947,51950,51952,51955,51957,51959,51961,51963],{"class":2227,"line":2228},[2225,51944,2232],{"class":2231},[2225,51946,2236],{"class":2235},[2225,51948,51949],{"class":2239}," postgresStoreDescriptor",[2225,51951,2298],{"class":2235},[2225,51953,51954],{"class":2239}," registerPostgresStore",[2225,51956,2243],{"class":2235},[2225,51958,2246],{"class":2231},[2225,51960,2249],{"class":2235},[2225,51962,51853],{"class":2252},[2225,51964,2256],{"class":2235},[2225,51966,51967],{"class":2227,"line":1041},[2225,51968,2285],{"emptyLinePlaceholder":2284},[2225,51970,51971,51973,51975,51977,51979,51981,51983,51985,51987,51989,51991,51993,51995,51998,52000,52002,52004,52006,52008,52010,52012,52014,52016,52018,52020],{"class":2227,"line":1038},[2225,51972,2775],{"class":2774},[2225,51974,47019],{"class":2239},[2225,51976,2781],{"class":2235},[2225,51978,51949],{"class":2291},[2225,51980,2789],{"class":2239},[2225,51982,2792],{"class":2235},[2225,51984,49062],{"class":2307},[2225,51986,2311],{"class":2235},[2225,51988,2249],{"class":2235},[2225,51990,41270],{"class":2252},[2225,51992,2805],{"class":2235},[2225,51994,2298],{"class":2235},[2225,51996,51997],{"class":2307}," schema",[2225,51999,2311],{"class":2235},[2225,52001,2249],{"class":2235},[2225,52003,12],{"class":2252},[2225,52005,2805],{"class":2235},[2225,52007,2298],{"class":2235},[2225,52009,47030],{"class":2307},[2225,52011,2311],{"class":2235},[2225,52013,2249],{"class":2235},[2225,52015,51497],{"class":2252},[2225,52017,2805],{"class":2235},[2225,52019,2243],{"class":2235},[2225,52021,2362],{"class":2239},[1022,52023,2621],{"id":2620},[1111,52025,52026,52030,52034],{},[1114,52027,52028],{},[1007,52029,47064],{"href":2634},[1114,52031,52032],{},[1007,52033,399],{"href":50513},[1114,52035,52036,52038],{},[1007,52037,435],{"href":50566}," — a thin factory over this store",[2645,52040,37016],{},{"title":1037,"searchDepth":1038,"depth":1038,"links":52042},[52043,52044,52045,52046],{"id":29856,"depth":1041,"text":29857},{"id":2854,"depth":1041,"text":9542},{"id":46935,"depth":1041,"text":46936},{"id":2620,"depth":1041,"text":2621},"PostgreSQL adapter for noy-db — encrypted-envelope KV in a single table with a jsonb column, atomic CAS via UPDATE ... WHERE v = ? RETURNING id.",{},{"title":407,"description":52047},"dshQD2qb23rBkiZ_rMJaZT00DBpb_9UJH21hF4NmMEQ",{"id":52052,"title":411,"access":12,"apiToc":929,"body":52053,"category":1042,"description":52235,"extension":1044,"meta":52236,"navigation":1042,"nest":3303,"path":412,"seo":52237,"sinceVersion":1042,"status":1049,"stem":413,"subtitle":1042,"tier":6922,"tocExternal":1042,"__hash__":52238},"docs\u002Ffamilies\u002Fto\u002Fprobe.md",{"type":931,"value":52054,"toc":52230},[52055,52057,52069,52105,52134,52137,52139,52160,52162,52215,52217,52227],[934,52056,411],{"id":411},[1061,52058,52059],{},[937,52060,52061,1368,52066,52068],{},[950,52062,52063,52064],{},"Ships in ",[941,52065,9595],{},[941,52067,51228],{},"\nNot a backend — a 5-axis benchmark you point at any store.",[52070,52071,52072],"alert",{"type":10789},[937,52073,52074,52079,52080,997,52082,52084,52085,2419,52089,52092,52093,52096,52097,52100,52101,52104],{},[950,52075,52076,52078],{},[941,52077,9616],{}," is retired."," The package is deprecated on npm and receives\nno releases. Its two entry points — ",[941,52081,51271],{},[941,52083,51274],{}," —\nwere absorbed into ",[1007,52086,52087],{"href":50711},[941,52088,9595],{},[941,52090,52091],{},"0.4.0-pre.8","; import them\nfrom there. A diagnostic that returns no store never fitted the ",[941,52094,52095],{},"to\u003CBackend>()","\nstore contract, and probing to ",[1092,52098,52099],{},"choose"," a store then metering to ",[1092,52102,52103],{},"watch"," it are\none workflow.",[2217,52106,52108],{"className":2219,"code":52107,"language":2221,"meta":1037,"style":1037},"import { runStoreProbe, probeTopology } from '@noy-db\u002Fto-meter'\n",[941,52109,52110],{"__ignoreMap":1037},[2225,52111,52112,52114,52116,52119,52121,52124,52126,52128,52130,52132],{"class":2227,"line":2228},[2225,52113,2232],{"class":2231},[2225,52115,2236],{"class":2235},[2225,52117,52118],{"class":2239}," runStoreProbe",[2225,52120,2298],{"class":2235},[2225,52122,52123],{"class":2239}," probeTopology",[2225,52125,2243],{"class":2235},[2225,52127,2246],{"class":2231},[2225,52129,2249],{"class":2235},[2225,52131,9595],{"class":2252},[2225,52133,2256],{"class":2235},[46871,52135],{":rows":52136},"[{\"label\":\"Ships in\",\"value\":\"@noy-db\u002Fto-meter\"},{\"label\":\"Shape\",\"value\":\"n\u002Fa — diagnostic, returns no store\"},{\"label\":\"Wraps\",\"value\":\"any NoydbStore\"}]",[1022,52138,29857],{"id":29856},[937,52140,52141,52144,52145,52147,52148,968,52150,968,52152,2462,52154,1952,52156,52159],{},[941,52142,52143],{},"runStoreProbe"," exercises the 6-method ",[941,52146,3154],{}," contract across five axes —\nwrite latency, CAS integrity, hydration cost, sync economics, and network\nresilience — and produces a structured, risk-scored suitability report for the\nroles ",[941,52149,47925],{},[941,52151,34951],{},[941,52153,50702],{},[941,52155,605],{},[941,52157,52158],{},"probeTopology"," runs a\nsetup-time topology check.",[1022,52161,9542],{"id":2854},[1111,52163,52164,52192,52212],{},[1114,52165,52166,52167,52170,52171,1617,52173,968,52176,52179,52180,953,52183,52185,52186,52188,52189,1520],{},"Each ",[941,52168,52169],{},"suitability.risks"," entry carries a machine-readable ",[941,52172,941],{},[941,52174,52175],{},"'slow-write-p99'",[941,52177,52178],{},"'cas-unsupported'",", …), a ",[941,52181,52182],{},"severity",[941,52184,51655],{}," or\n",[941,52187,20844],{},"), and a human-readable ",[941,52190,52191],{},"message",[1114,52193,52194,52195,52198,52199,52202,52203,997,52205,52208,52209,52211],{},"Any error-severity risk empties ",[941,52196,52197],{},"recommended"," entirely; ",[941,52200,52201],{},"slow-write-p99"," drops\n",[941,52204,47925],{},[941,52206,52207],{},"cas-unsupported"," drops ",[941,52210,34951],{}," — other warn-level findings\nare reported without affecting the recommendation.",[1114,52213,52214],{},"Doesn't persist anything itself — it's diagnostic tooling, not a storage tier.",[1022,52216,2621],{"id":2620},[1111,52218,52219,52223],{},[1114,52220,52221],{},[1007,52222,47064],{"href":2634},[1114,52224,52225],{},[1007,52226,395],{"href":50711},[2645,52228,52229],{},"html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":1037,"searchDepth":1038,"depth":1038,"links":52231},[52232,52233,52234],{"id":29856,"depth":1041,"text":29857},{"id":2854,"depth":1041,"text":9542},{"id":2620,"depth":1041,"text":2621},"Store diagnostics, shipped in @noy-db\u002Fto-meter since the @noy-db\u002Fto-probe package was retired — benchmarks any NoydbStore and scores its role suitability.",{},{"title":411,"description":52235},"4l_7waEtBW_22qysH0UYMtYDwyzpPiQEvSUM_Vg0Dmg",{"id":52240,"title":263,"access":12,"apiToc":929,"body":52241,"category":1042,"description":52872,"extension":1044,"meta":52873,"navigation":1042,"nest":3303,"path":415,"seo":52874,"sinceVersion":1042,"status":1049,"stem":416,"subtitle":1042,"tier":1473,"tocExternal":1042,"__hash__":52875},"docs\u002Ffamilies\u002Fto\u002Frest.md",{"type":931,"value":52242,"toc":52865},[52243,52245,52261,52264,52266,52279,52285,52445,52449,52465,52467,52709,52711,52783,52850,52852,52863],[934,52244,263],{"id":263},[1061,52246,52247],{},[937,52248,52249,1368,52254,52257,52258,52260],{},[950,52250,52251],{},[941,52252,52253],{},"@noy-db\u002Fto-rest",[941,52255,52256],{},"pnpm add @noy-db\u002Fto-rest","\nTalks to an ",[941,52259,41085],{}," relay over HTTP.",[46871,52262],{":rows":52263},"[{\"label\":\"Shape\",\"value\":\"record\"},{\"label\":\"Atomic CAS\",\"value\":\"✓ (delegated to the server's store)\"},{\"label\":\"Locality\",\"value\":\"remote\"},{\"label\":\"Speed\",\"value\":\"network-bound\"},{\"label\":\"Persistence\",\"value\":\"durable (whatever the server's store provides)\"}]",[1022,52265,29857],{"id":29856},[937,52267,52268,52269,52271,52272,10313,52275,52278],{},"Sends encrypted envelopes to an HTTP endpoint running ",[941,52270,41085],{},". Every store method POSTs ",[941,52273,52274],{},"{ method, args }",[941,52276,52277],{},"{baseUrl}\u002Frpc","; the relay forwards the call to its own backing store and returns the result unchanged.",[937,52280,52281,52282,52284],{},"Encryption stays on this side. The client encrypts before the request and decrypts after the response, so the server handles ciphertext only — the same trust model as any other ",[941,52283,1779],{}," store, reached over a network hop instead of a disk or an SDK.",[2217,52286,52288],{"className":2219,"code":52287,"language":2221,"meta":1037,"style":1037},"import { createNoydb } from '@noy-db\u002Fhub'\nimport { toRest } from '@noy-db\u002Fto-rest'\n\nconst db = await createNoydb({\n  store: toRest({\n    baseUrl: 'https:\u002F\u002Frelay.example.com\u002Fapi\u002Fnoydb',\n    headers: { authorization: `Bearer ${API_KEY}` },\n  }),\n  user: 'alice',\n  secret: 'a-strong-secret',\n})\n",[941,52289,52290,52308,52327,52331,52347,52359,52375,52403,52411,52425,52439],{"__ignoreMap":1037},[2225,52291,52292,52294,52296,52298,52300,52302,52304,52306],{"class":2227,"line":2228},[2225,52293,2232],{"class":2231},[2225,52295,2236],{"class":2235},[2225,52297,2744],{"class":2239},[2225,52299,2243],{"class":2235},[2225,52301,2246],{"class":2231},[2225,52303,2249],{"class":2235},[2225,52305,1086],{"class":2252},[2225,52307,2256],{"class":2235},[2225,52309,52310,52312,52314,52317,52319,52321,52323,52325],{"class":2227,"line":1041},[2225,52311,2232],{"class":2231},[2225,52313,2236],{"class":2235},[2225,52315,52316],{"class":2239}," toRest",[2225,52318,2243],{"class":2235},[2225,52320,2246],{"class":2231},[2225,52322,2249],{"class":2235},[2225,52324,52253],{"class":2252},[2225,52326,2256],{"class":2235},[2225,52328,52329],{"class":2227,"line":1038},[2225,52330,2285],{"emptyLinePlaceholder":2284},[2225,52332,52333,52335,52337,52339,52341,52343,52345],{"class":2227,"line":2288},[2225,52334,2775],{"class":2774},[2225,52336,2778],{"class":2239},[2225,52338,2781],{"class":2235},[2225,52340,2784],{"class":2231},[2225,52342,2744],{"class":2291},[2225,52344,2789],{"class":2239},[2225,52346,4200],{"class":2235},[2225,52348,52349,52351,52353,52355,52357],{"class":2227,"line":2304},[2225,52350,15608],{"class":2307},[2225,52352,2311],{"class":2235},[2225,52354,52316],{"class":2291},[2225,52356,2789],{"class":2239},[2225,52358,4200],{"class":2235},[2225,52360,52361,52364,52366,52368,52371,52373],{"class":2227,"line":2356},[2225,52362,52363],{"class":2307},"    baseUrl",[2225,52365,2311],{"class":2235},[2225,52367,2249],{"class":2235},[2225,52369,52370],{"class":2252},"https:\u002F\u002Frelay.example.com\u002Fapi\u002Fnoydb",[2225,52372,2805],{"class":2235},[2225,52374,1707],{"class":2235},[2225,52376,52377,52380,52382,52384,52387,52389,52391,52394,52396,52399,52401],{"class":2227,"line":2830},[2225,52378,52379],{"class":2307},"    headers",[2225,52381,2311],{"class":2235},[2225,52383,2236],{"class":2235},[2225,52385,52386],{"class":2307}," authorization",[2225,52388,2311],{"class":2235},[2225,52390,43789],{"class":2235},[2225,52392,52393],{"class":2252},"Bearer ",[2225,52395,8197],{"class":2235},[2225,52397,52398],{"class":2239},"API_KEY",[2225,52400,43799],{"class":2235},[2225,52402,2353],{"class":2235},[2225,52404,52405,52407,52409],{"class":2227,"line":2861},[2225,52406,19143],{"class":2235},[2225,52408,1381],{"class":2239},[2225,52410,1707],{"class":2235},[2225,52412,52413,52415,52417,52419,52421,52423],{"class":2227,"line":4246},[2225,52414,4205],{"class":2307},[2225,52416,2311],{"class":2235},[2225,52418,2249],{"class":2235},[2225,52420,6408],{"class":2252},[2225,52422,2805],{"class":2235},[2225,52424,1707],{"class":2235},[2225,52426,52427,52429,52431,52433,52435,52437],{"class":2227,"line":4253},[2225,52428,10423],{"class":2307},[2225,52430,2311],{"class":2235},[2225,52432,2249],{"class":2235},[2225,52434,6421],{"class":2252},[2225,52436,2805],{"class":2235},[2225,52438,1707],{"class":2235},[2225,52440,52441,52443],{"class":2227,"line":4259},[2225,52442,2359],{"class":2235},[2225,52444,2362],{"class":2239},[1022,52446,52448],{"id":52447},"what-it-pairs-with","What it pairs with",[937,52450,52451,52452,34689,52457,52460,52461,52464],{},"The server half is ",[1007,52453,52455],{"href":52454},"..\u002Fin\u002Frest",[941,52456,41085],{},[941,52458,52459],{},"createRestHandler",", which needs ",[950,52462,52463],{},">= 0.6.0-pre.0",". Earlier in-rest versions exposed a different HTTP surface — one that unlocked vaults server-side — which has been removed. There is no package dependency between the two; the coupling is the wire protocol.",[1022,52466,9542],{"id":2854},[1111,52468,52469,52625,52672,52685,52701,52704],{},[1114,52470,52471,52473,52474,52477,52478,1324,52481,52483,52484,39775,52488,52490,52491,52493,52494,52496,52497],{},[941,52472,6860],{}," is re-hydrated from any ",[941,52475,52476],{},"409"," whose error body is ",[950,52479,52480],{},"named",[941,52482,6860],{},", so\ncompare-and-set semantics survive the wire hop. Match it with\n",[1007,52485,52486],{"href":40599},[941,52487,7486],{},[941,52489,7490],{}," — the error is\nconstructed inside ",[941,52492,48670],{},"'s copy of ",[941,52495,3299],{},", which need not be your caller's copy.",[7646,52498,52500,52533],{"color":10789,"icon":52499},"i-lucide-arrow-up-circle",[937,52501,52502,52509,52510,52512,52513,52516,52517,52520,52521,52523,52524,52529,52530,52532],{},[950,52503,52504,52505,52508],{},"Upgrade to ",[941,52506,52507],{},"0.7.0-pre.1"," or later."," Through ",[941,52511,47134],{}," the re-hydration ",[1092,52514,52515],{},"also"," required\n",[941,52518,52519],{},"typeof version === 'number'",", so a ",[941,52522,52476],{}," that omitted the field fell through to a ",[950,52525,52526,52527],{},"generic\n",[941,52528,12126],{}," — a lost CAS race arriving as something no retry loop can recognize. ",[941,52531,52507],{}," keys\non the name alone.",[1338,52534,52535,52550],{},[1341,52536,52537],{},[1344,52538,52539,52542,52546],{},[1347,52540,52541],{},"server sends",[1347,52543,52544],{},[941,52545,47134],{},[1347,52547,52548],{},[941,52549,52507],{},[1357,52551,52552,52583,52610],{},[1344,52553,52554,52565,52573],{},[1362,52555,52556,968,52558,52561,52562],{},[941,52557,52476],{},[941,52559,52560],{},"name: 'ConflictError'",", no ",[941,52563,52564],{},"version",[1362,52566,52567,964,52569,1324,52571],{},[941,52568,12126],{},[941,52570,7486],{},[950,52572,2560],{},[1362,52574,52575,968,52577,1324,52580],{},[941,52576,6860],{},[941,52578,52579],{},".version",[941,52581,52582],{},"NaN",[1344,52584,52585,52594,52602],{},[1362,52586,52587,968,52589,968,52591],{},[941,52588,52476],{},[941,52590,52560],{},[941,52592,52593],{},"version: 3",[1362,52595,52596,968,52598,1324,52600],{},[941,52597,6860],{},[941,52599,52579],{},[941,52601,5512],{},[1362,52603,52604,968,52606,1324,52608],{},[941,52605,6860],{},[941,52607,52579],{},[941,52609,5512],{},[1344,52611,52612,52617,52621],{},[1362,52613,52614,52616],{},[941,52615,52476],{}," named anything else",[1362,52618,52619],{},[941,52620,12126],{},[1362,52622,52623],{},[941,52624,12126],{},[1114,52626,52627,1324,52634,52636,52637,52640,52641,52646,52647,52650,52651,52653,52654,52657,52658,52661,52662,52665,52666,52668,52669,52671],{},[950,52628,52629,7356,52631,52633],{},[941,52630,52579],{},[941,52632,52582],{}," on any conflict from a current relay.",[941,52635,35649],{}," stopped sending the field\nin ",[941,52638,52639],{},"0.7.0-pre.7"," — it disclosed another writer's progress counter\n(",[1007,52642,52645],{"href":52643,"rel":52644},"https:\u002F\u002Fgithub.com\u002Fnoy-db\u002Fcore\u002Fissues\u002F1218",[3371],"noy-db#1218",", closed) — so its 409 body is now\n",[941,52648,52649],{},"{ name, message }"," and nothing else. ",[941,52652,52582],{}," is deliberate, chosen over a ",[941,52655,52656],{},"-1"," sentinel so it\ncannot masquerade as a real stored counter. Test with ",[941,52659,52660],{},"Number.isNaN(err.version)"," before using it\nand treat a wire conflict as ",[1092,52663,52664],{},"conflict, position unknown",": re-read and re-apply rather than\nreasoning from the number. ",[941,52667,6860],{}," raised by a ",[950,52670,49766],{}," store still carries a real\nversion; only the wire hop drops it.",[1114,52673,52674,1261,52677,1261,52679,1261,52681,52684],{},[941,52675,52676],{},"401",[941,52678,40720],{},[941,52680,40999],{},[941,52682,52683],{},"5xx"," map to auth, capability, not-implemented and server errors respectively — a client can branch on them without parsing prose.",[1114,52686,52687,52688,968,52690,968,52692,52694,52695,52697,52698,52700],{},"Trailing optional arguments (",[941,52689,8477],{},[941,52691,25010],{},[941,52693,3022],{},") are trimmed before serialization: JSON has no ",[941,52696,18316],{},", and a ",[941,52699,6651],{}," must not reach the server looking like a real value.",[1114,52702,52703],{},"Atomicity is whatever the server's backing store offers — the relay does not add or remove guarantees.",[1114,52705,46929,52706,1520],{},[941,52707,52708],{},"toRest(options: RestStoreOptions)",[1022,52710,46936],{"id":46935},[937,52712,46939,52713,46944,52715,968,52718,1952,52720,52722,52723,52726,52727,52730,52731,52734,52735,7356,52737,968,52740,7356,52742,52745,52746,52748,52749,52751,52752,52754,52755,52758,52759,52762,52764,52765,52768,52769,52772,52773,52775,52776,1625,52779,52782],{},[1007,52714,46943],{"href":46942},[941,52716,52717],{},"kind: 'rest'",[941,52719,46950],{},[941,52721,48670],{},"'s direct options violate the\ncredentialless rule twice — ",[941,52724,52725],{},"fetch"," is a function, and ",[941,52728,52729],{},"headers"," is where a\nstatic ",[941,52732,52733],{},"Authorization"," lives — so they're partitioned rather than forwarded:\n",[941,52736,46953],{},[941,52738,52739],{},"{ baseUrl }",[941,52741,16362],{},[941,52743,52744],{},"{ timeoutMs? }",", and the\ndevice-local ",[941,52747,52725],{}," plus non-auth ",[941,52750,52729],{}," (tenant routing, tracing) ride\n",[941,52753,48116],{},". Auth rides ",[941,52756,52757],{},"RestStoreOptions.credentials",", a ",[941,52760,52761],{},"kind: 'token'",[941,52763,46974],{}," that becomes ",[941,52766,52767],{},"Authorization: Bearer \u003Ctoken>",". Header\nassembly happens ",[950,52770,52771],{},"per request",", so an expiring token refreshes without\nrebuilding the store — the source is invoked once per operation, and\n",[941,52774,44339],{}," is not consulted (unlike ",[941,52777,52778],{},"to-webdav",[941,52780,52781],{},"to-turso",", which cache\nwithin a refresh-skew window).",[2217,52784,52786],{"className":2219,"code":52785,"language":2221,"meta":1037,"style":1037},"import { restStoreDescriptor, registerRestStore } from '@noy-db\u002Fto-rest'\n\nconst descriptor = restStoreDescriptor({ baseUrl: 'https:\u002F\u002Frelay.example.com\u002Fapi\u002Fnoydb' })\n\u002F\u002F later: locator.resolve(descriptor, { credentials: async () => ({ kind: 'token', token }) })\n",[941,52787,52788,52812,52816,52845],{"__ignoreMap":1037},[2225,52789,52790,52792,52794,52797,52799,52802,52804,52806,52808,52810],{"class":2227,"line":2228},[2225,52791,2232],{"class":2231},[2225,52793,2236],{"class":2235},[2225,52795,52796],{"class":2239}," restStoreDescriptor",[2225,52798,2298],{"class":2235},[2225,52800,52801],{"class":2239}," registerRestStore",[2225,52803,2243],{"class":2235},[2225,52805,2246],{"class":2231},[2225,52807,2249],{"class":2235},[2225,52809,52253],{"class":2252},[2225,52811,2256],{"class":2235},[2225,52813,52814],{"class":2227,"line":1041},[2225,52815,2285],{"emptyLinePlaceholder":2284},[2225,52817,52818,52820,52822,52824,52826,52828,52830,52833,52835,52837,52839,52841,52843],{"class":2227,"line":1038},[2225,52819,2775],{"class":2774},[2225,52821,47019],{"class":2239},[2225,52823,2781],{"class":2235},[2225,52825,52796],{"class":2291},[2225,52827,2789],{"class":2239},[2225,52829,2792],{"class":2235},[2225,52831,52832],{"class":2307}," baseUrl",[2225,52834,2311],{"class":2235},[2225,52836,2249],{"class":2235},[2225,52838,52370],{"class":2252},[2225,52840,2805],{"class":2235},[2225,52842,2243],{"class":2235},[2225,52844,2362],{"class":2239},[2225,52846,52847],{"class":2227,"line":2288},[2225,52848,52849],{"class":2763},"\u002F\u002F later: locator.resolve(descriptor, { credentials: async () => ({ kind: 'token', token }) })\n",[1022,52851,2621],{"id":2620},[1111,52853,52854,52858],{},[1114,52855,52856],{},[1007,52857,47064],{"href":2634},[1114,52859,52860,52862],{},[1007,52861,35649],{"href":52454}," — the server half",[2645,52864,32928],{},{"title":1037,"searchDepth":1038,"depth":1038,"links":52866},[52867,52868,52869,52870,52871],{"id":29856,"depth":1041,"text":29857},{"id":52447,"depth":1041,"text":52448},{"id":2854,"depth":1041,"text":9542},{"id":46935,"depth":1041,"text":46936},{"id":2620,"depth":1041,"text":2621},"HTTP adapter for noy-db — a store that speaks to an @noy-db\u002Fin-rest ciphertext relay, so the server holds encrypted blobs it cannot open.",{},{"title":263,"description":52872},"emvvUqp968euC1BpFhV1KfveL8uAxDBrI3kSedyTDQM",{"id":52877,"title":418,"access":12,"apiToc":929,"body":52878,"category":1042,"description":53074,"extension":1044,"meta":53075,"navigation":1042,"nest":3303,"path":419,"seo":53076,"sinceVersion":1042,"status":1049,"stem":420,"subtitle":1042,"tier":1473,"tocExternal":1042,"__hash__":53077},"docs\u002Ffamilies\u002Fto\u002Fsmb.md",{"type":931,"value":52879,"toc":53068},[52880,52882,52895,52897,52899,52902,52904,52919,52921,52962,53050,53052,53066],[934,52881,418],{"id":418},[1061,52883,52884],{},[937,52885,52886,1368,52891,52894],{},[950,52887,52888],{},[941,52889,52890],{},"@noy-db\u002Fto-smb",[941,52892,52893],{},"pnpm add @noy-db\u002Fto-smb","\nSpeaks SMB\u002FCIFS directly — no OS mount required.",[46871,52896],{":rows":51556},[1022,52898,29857],{"id":29856},[937,52900,52901],{},"Stores encrypted envelopes on SMB\u002FCIFS shares — Windows file servers, NAS devices\n(Synology\u002FQNAP\u002FNetgear), and corporate shared drives — authenticating via NTLM or\nKerberos, without needing the share mounted at the OS level.",[1022,52903,9542],{"id":2854},[1111,52905,52906,52911,52914],{},[1114,52907,52908,52910],{},[941,52909,47869],{}," — a tmp-file-then-rename write pattern avoids partial writes,\nbut there's no CAS primitive over SMB\u002FCIFS.",[1114,52912,52913],{},"Record shape, one file per record.",[1114,52915,46929,52916,1520],{},[941,52917,52918],{},"toSmb(options: SmbStoreOptions)",[1022,52920,46936],{"id":46935},[937,52922,46939,52923,46944,52925,968,52928,1952,52930,7356,52932,2874,52935,48968,52938,5502,52941,997,52943,52946,52947,33690,52949,1952,52952,7356,52954,50095,52956,52959,52960,48998],{},[1007,52924,46943],{"href":46942},[941,52926,52927],{},"kind: 'smb'",[941,52929,51691],{},[941,52931,46953],{},[941,52933,52934],{},"{ host?, share?, path? }",[941,52936,52937],{},"path",[941,52939,52940],{},"remotePath",[941,52942,14346],{},[941,52944,52945],{},"share"," are identity-only, not\nconsumed by the factory — the connection already carries them. ",[941,52948,16362],{},[941,52950,52951],{},"{ name? }",[941,52953,48992],{},[950,52955,45375],{},[941,52957,52958],{},"SmbHandle",") — this store\nhas no client library of its own; ",[941,52961,46978],{},[2217,52963,52965],{"className":2219,"code":52964,"language":2221,"meta":1037,"style":1037},"import { smbStoreDescriptor, registerSmbStore } from '@noy-db\u002Fto-smb'\n\nconst descriptor = smbStoreDescriptor({ host: 'nas.local', share: 'vaults', path: 'noydb' })\n",[941,52966,52967,52991,52995],{"__ignoreMap":1037},[2225,52968,52969,52971,52973,52976,52978,52981,52983,52985,52987,52989],{"class":2227,"line":2228},[2225,52970,2232],{"class":2231},[2225,52972,2236],{"class":2235},[2225,52974,52975],{"class":2239}," smbStoreDescriptor",[2225,52977,2298],{"class":2235},[2225,52979,52980],{"class":2239}," registerSmbStore",[2225,52982,2243],{"class":2235},[2225,52984,2246],{"class":2231},[2225,52986,2249],{"class":2235},[2225,52988,52890],{"class":2252},[2225,52990,2256],{"class":2235},[2225,52992,52993],{"class":2227,"line":1041},[2225,52994,2285],{"emptyLinePlaceholder":2284},[2225,52996,52997,52999,53001,53003,53005,53007,53009,53011,53013,53015,53017,53019,53021,53024,53026,53028,53031,53033,53035,53038,53040,53042,53044,53046,53048],{"class":2227,"line":1038},[2225,52998,2775],{"class":2774},[2225,53000,47019],{"class":2239},[2225,53002,2781],{"class":2235},[2225,53004,52975],{"class":2291},[2225,53006,2789],{"class":2239},[2225,53008,2792],{"class":2235},[2225,53010,17373],{"class":2307},[2225,53012,2311],{"class":2235},[2225,53014,2249],{"class":2235},[2225,53016,51784],{"class":2252},[2225,53018,2805],{"class":2235},[2225,53020,2298],{"class":2235},[2225,53022,53023],{"class":2307}," share",[2225,53025,2311],{"class":2235},[2225,53027,2249],{"class":2235},[2225,53029,53030],{"class":2252},"vaults",[2225,53032,2805],{"class":2235},[2225,53034,2298],{"class":2235},[2225,53036,53037],{"class":2307}," path",[2225,53039,2311],{"class":2235},[2225,53041,2249],{"class":2235},[2225,53043,149],{"class":2252},[2225,53045,2805],{"class":2235},[2225,53047,2243],{"class":2235},[2225,53049,2362],{"class":2239},[1022,53051,2621],{"id":2620},[1111,53053,53054,53058,53062],{},[1114,53055,53056],{},[1007,53057,47064],{"href":2634},[1114,53059,53060],{},[1007,53061,403],{"href":50644},[1114,53063,53064],{},[1007,53065,426],{"href":50597},[2645,53067,37016],{},{"title":1037,"searchDepth":1038,"depth":1038,"links":53069},[53070,53071,53072,53073],{"id":29856,"depth":1041,"text":29857},{"id":2854,"depth":1041,"text":9542},{"id":46935,"depth":1041,"text":46936},{"id":2620,"depth":1041,"text":2621},"SMB\u002FCIFS network file store for noy-db — Windows file servers, NAS devices, and corporate shared drives via NTLM or Kerberos, no OS mount required.",{},{"title":418,"description":53074},"idgBBPnGwN6lUIGGh2DaO7pQl6fHlJSHHfC-IFPLiDc",{"id":53079,"title":422,"access":12,"apiToc":929,"body":53080,"category":1042,"description":53289,"extension":1044,"meta":53290,"navigation":1042,"nest":3303,"path":423,"seo":53291,"sinceVersion":1042,"status":1049,"stem":424,"subtitle":1042,"tier":1050,"tocExternal":1042,"__hash__":53292},"docs\u002Ffamilies\u002Fto\u002Fsqlite.md",{"type":931,"value":53081,"toc":53283},[53082,53084,53097,53099,53101,53118,53120,53149,53151,53190,53265,53267,53281],[934,53083,422],{"id":422},[1061,53085,53086],{},[937,53087,53088,1368,53093,53096],{},[950,53089,53090],{},[941,53091,53092],{},"@noy-db\u002Fto-sqlite",[941,53094,53095],{},"pnpm add @noy-db\u002Fto-sqlite","\nSingle-file local encrypted document store.",[46871,53098],{":rows":47978},[1022,53100,29857],{"id":29856},[937,53102,53103,53104,53106,53107,53109,53110,968,53112,53114,53115,53117],{},"Persists a vault to a single SQLite file — a good fit once a vault outgrows\n",[941,53105,7461],{},"'s one-JSON-per-record layout (10K+ records). Accepts a duck-typed\n",[941,53108,31067],{}," interface, so ",[941,53111,50538],{},[941,53113,50541],{}," (Node 22+), ",[941,53116,50544],{},",\nor any driver matching the same shape all work.",[1022,53119,9542],{"id":2854},[1111,53121,53122,53134,53136,53144],{},[1114,53123,53124,9750,53126,53129,53130,53133],{},[941,53125,7811],{},[941,53127,53128],{},"UPDATE … WHERE _v = ?"," inside a ",[941,53131,53132],{},"BEGIN IMMEDIATE","\ntransaction.",[1114,53135,48944],{},[1114,53137,53138,53139,53143],{},"Same table and DDL as ",[1007,53140,53141],{"href":50551},[941,53142,439],{},", so moving from a local file to\nhosted libSQL is a factory swap.",[1114,53145,46929,53146,1520],{},[941,53147,53148],{},"toSqlite(options: SqliteStoreOptions)",[1022,53150,46936],{"id":46935},[937,53152,46939,53153,46944,53155,968,53158,1952,53160,7356,53162,2085,53165,53167,53168,5502,53170,53172,53173,7356,53175,38633,53177,7356,53179,23423,53181,53184,53185,53187,53188,48998],{},[1007,53154,46943],{"href":46942},[941,53156,53157],{},"kind: 'sqlite'",[941,53159,49773],{},[941,53161,46953],{},[941,53163,53164],{},"{ file?, table? }",[941,53166,1338],{},"\nmaps to ",[941,53169,48971],{},[941,53171,383],{}," is identity-only, not consumed by the factory —\nthe connection already carries it. ",[941,53174,16362],{},[941,53176,48989],{},[941,53178,48992],{},[950,53180,45375],{},[941,53182,53183],{},"SqliteDatabase",", mapped onto the\nstore's own ",[941,53186,6590],{}," option) — this store has no client library of its own;\n",[941,53189,46978],{},[2217,53191,53193],{"className":2219,"code":53192,"language":2221,"meta":1037,"style":1037},"import { sqliteStoreDescriptor, registerSqliteStore } from '@noy-db\u002Fto-sqlite'\n\nconst descriptor = sqliteStoreDescriptor({ file: '.\u002Fvault.db', table: 'noydb_envelopes' })\n",[941,53194,53195,53219,53223],{"__ignoreMap":1037},[2225,53196,53197,53199,53201,53204,53206,53209,53211,53213,53215,53217],{"class":2227,"line":2228},[2225,53198,2232],{"class":2231},[2225,53200,2236],{"class":2235},[2225,53202,53203],{"class":2239}," sqliteStoreDescriptor",[2225,53205,2298],{"class":2235},[2225,53207,53208],{"class":2239}," registerSqliteStore",[2225,53210,2243],{"class":2235},[2225,53212,2246],{"class":2231},[2225,53214,2249],{"class":2235},[2225,53216,53092],{"class":2252},[2225,53218,2256],{"class":2235},[2225,53220,53221],{"class":2227,"line":1041},[2225,53222,2285],{"emptyLinePlaceholder":2284},[2225,53224,53225,53227,53229,53231,53233,53235,53237,53240,53242,53244,53247,53249,53251,53253,53255,53257,53259,53261,53263],{"class":2227,"line":1038},[2225,53226,2775],{"class":2774},[2225,53228,47019],{"class":2239},[2225,53230,2781],{"class":2235},[2225,53232,53203],{"class":2291},[2225,53234,2789],{"class":2239},[2225,53236,2792],{"class":2235},[2225,53238,53239],{"class":2307}," file",[2225,53241,2311],{"class":2235},[2225,53243,2249],{"class":2235},[2225,53245,53246],{"class":2252},".\u002Fvault.db",[2225,53248,2805],{"class":2235},[2225,53250,2298],{"class":2235},[2225,53252,47030],{"class":2307},[2225,53254,2311],{"class":2235},[2225,53256,2249],{"class":2235},[2225,53258,51497],{"class":2252},[2225,53260,2805],{"class":2235},[2225,53262,2243],{"class":2235},[2225,53264,2362],{"class":2239},[1022,53266,2621],{"id":2620},[1111,53268,53269,53273,53277],{},[1114,53270,53271],{},[1007,53272,47064],{"href":2634},[1114,53274,53275],{},[1007,53276,439],{"href":50551},[1114,53278,53279],{},[1007,53280,383],{"href":47341},[2645,53282,37016],{},{"title":1037,"searchDepth":1038,"depth":1038,"links":53284},[53285,53286,53287,53288],{"id":29856,"depth":1041,"text":29857},{"id":2854,"depth":1041,"text":9542},{"id":46935,"depth":1041,"text":46936},{"id":2620,"depth":1041,"text":2621},"Single-file SQLite adapter for noy-db, for 10K+ records — duck-typed Database interface accepts better-sqlite3, node:sqlite, or bun:sqlite.",{},{"title":422,"description":53289},"x4SJzlcDkXAidLbCEd6HjSAVr2w8G76D1sTUzMCUz1s",{"id":53294,"title":426,"access":12,"apiToc":929,"body":53295,"category":1042,"description":53496,"extension":1044,"meta":53497,"navigation":1042,"nest":3303,"path":427,"seo":53498,"sinceVersion":1042,"status":1049,"stem":428,"subtitle":1042,"tier":1473,"tocExternal":1042,"__hash__":53499},"docs\u002Ffamilies\u002Fto\u002Fssh.md",{"type":931,"value":53296,"toc":53490},[53297,53299,53312,53314,53316,53330,53332,53346,53348,53388,53472,53474,53488],[934,53298,426],{"id":426},[1061,53300,53301],{},[937,53302,53303,1368,53308,53311],{},[950,53304,53305],{},[941,53306,53307],{},"@noy-db\u002Fto-ssh",[941,53309,53310],{},"pnpm add @noy-db\u002Fto-ssh","\nRemote encrypted document storage over SSH.",[46871,53313],{":rows":51556},[1022,53315,29857],{"id":29856},[937,53317,53318,53319,53321,53322,53325,53326,53329],{},"Stores encrypted envelopes over SFTP. Any Linux\u002FmacOS server with ",[941,53320,50605],{}," running\nbecomes a noy-db backend; leverages your existing ",[941,53323,53324],{},"~\u002F.ssh"," keys or ",[941,53327,53328],{},"ssh-agent",".\nKey-only auth — no passwords.",[1022,53331,9542],{"id":2854},[1111,53333,53334,53339,53341],{},[1114,53335,53336,53338],{},[941,53337,47869],{}," — key-only auth, no compare-and-swap primitive over SFTP.",[1114,53340,52913],{},[1114,53342,46929,53343,1520],{},[941,53344,53345],{},"toSsh(options: SshStoreOptions)",[1022,53347,46936],{"id":46935},[937,53349,46939,53350,46944,53352,968,53355,1952,53357,7356,53359,2085,53362,53167,53364,5502,53366,997,53368,53370,53371,7356,53373,38633,53375,7356,53377,50095,53379,53382,53383,53187,53386,48998],{},[1007,53351,46943],{"href":46942},[941,53353,53354],{},"kind: 'ssh'",[941,53356,51691],{},[941,53358,46953],{},[941,53360,53361],{},"{ host?, port?, path? }",[941,53363,52937],{},[941,53365,52940],{},[941,53367,14346],{},[941,53369,3292],{}," are identity-only, not consumed by\nthe factory — the connection already carries them. ",[941,53372,16362],{},[941,53374,52951],{},[941,53376,48992],{},[950,53378,45375],{},[941,53380,53381],{},"SftpHandle",", mapped onto the store's\nown ",[941,53384,53385],{},"sftp",[941,53387,46978],{},[2217,53389,53391],{"className":2219,"code":53390,"language":2221,"meta":1037,"style":1037},"import { sshStoreDescriptor, registerSshStore } from '@noy-db\u002Fto-ssh'\n\nconst descriptor = sshStoreDescriptor({ host: 'server.example.com', port: 22, path: 'noydb' })\n",[941,53392,53393,53417,53421],{"__ignoreMap":1037},[2225,53394,53395,53397,53399,53402,53404,53407,53409,53411,53413,53415],{"class":2227,"line":2228},[2225,53396,2232],{"class":2231},[2225,53398,2236],{"class":2235},[2225,53400,53401],{"class":2239}," sshStoreDescriptor",[2225,53403,2298],{"class":2235},[2225,53405,53406],{"class":2239}," registerSshStore",[2225,53408,2243],{"class":2235},[2225,53410,2246],{"class":2231},[2225,53412,2249],{"class":2235},[2225,53414,53307],{"class":2252},[2225,53416,2256],{"class":2235},[2225,53418,53419],{"class":2227,"line":1041},[2225,53420,2285],{"emptyLinePlaceholder":2284},[2225,53422,53423,53425,53427,53429,53431,53433,53435,53437,53439,53441,53444,53446,53448,53451,53453,53456,53458,53460,53462,53464,53466,53468,53470],{"class":2227,"line":1038},[2225,53424,2775],{"class":2774},[2225,53426,47019],{"class":2239},[2225,53428,2781],{"class":2235},[2225,53430,53401],{"class":2291},[2225,53432,2789],{"class":2239},[2225,53434,2792],{"class":2235},[2225,53436,17373],{"class":2307},[2225,53438,2311],{"class":2235},[2225,53440,2249],{"class":2235},[2225,53442,53443],{"class":2252},"server.example.com",[2225,53445,2805],{"class":2235},[2225,53447,2298],{"class":2235},[2225,53449,53450],{"class":2307}," port",[2225,53452,2311],{"class":2235},[2225,53454,53455],{"class":6527}," 22",[2225,53457,2298],{"class":2235},[2225,53459,53037],{"class":2307},[2225,53461,2311],{"class":2235},[2225,53463,2249],{"class":2235},[2225,53465,149],{"class":2252},[2225,53467,2805],{"class":2235},[2225,53469,2243],{"class":2235},[2225,53471,2362],{"class":2239},[1022,53473,2621],{"id":2620},[1111,53475,53476,53480,53484],{},[1114,53477,53478],{},[1007,53479,47064],{"href":2634},[1114,53481,53482],{},[1007,53483,443],{"href":50612},[1114,53485,53486],{},[1007,53487,418],{"href":50632},[2645,53489,42916],{},{"title":1037,"searchDepth":1038,"depth":1038,"links":53491},[53492,53493,53494,53495],{"id":29856,"depth":1041,"text":29857},{"id":2854,"depth":1041,"text":9542},{"id":46935,"depth":1041,"text":46936},{"id":2620,"depth":1041,"text":2621},"SFTP-over-SSH store for noy-db, key-only auth — any Linux\u002FmacOS server with sshd becomes a backend.",{},{"title":426,"description":53496},"RMVrJ4jTzKSqDrI23uDxeH2fR7e2Cao0yffl3gYAHnU",{"id":53501,"title":430,"access":12,"apiToc":929,"body":53502,"category":1042,"description":54514,"extension":1044,"meta":54515,"navigation":54516,"nest":3303,"path":431,"seo":54517,"sinceVersion":1042,"status":1049,"stem":432,"subtitle":1042,"tier":1050,"tocExternal":1042,"__hash__":54518},"docs\u002Ffamilies\u002Fto\u002Fstore-locator.md",{"type":931,"value":53503,"toc":54500},[53504,53507,53533,53537,53550,53562,53637,53641,53654,53660,53735,53741,53753,53757,53796,53828,53850,53865,53869,53872,53878,53909,53913,53936,53973,53983,53987,53992,54130,54136,54140,54442,54452,54458,54464,54482,54484,54497],[934,53505,430],{"id":53506},"store-locator-descriptors",[1061,53508,53509],{},[937,53510,53511,53514,53515,53517,53518,53520,53521,53524,53525,53528,53529,53532],{},[950,53512,53513],{},"A second construction path."," Alongside every ",[941,53516,50771],{}," factory, each\n",[941,53519,1779],{}," store now exports a ",[941,53522,53523],{},"StoreDescriptor"," builder, a ",[941,53526,53527],{},"StoreFactory",", and\na ",[941,53530,53531],{},"register\u003CKind>Store()"," — plain data in, a live store out.",[1022,53534,53536],{"id":53535},"the-problem-this-solves","The problem this solves",[937,53538,53539,53540,53543,53544,53547,53548,1520],{},"Calling ",[941,53541,53542],{},"toPostgres({ client, table: 'noydb_envelopes' })"," directly is fine\nwhen the same code both knows the connection and builds the store. It stops\nbeing fine the moment \"which store, and where\" needs to be ",[950,53545,53546],{},"recorded"," —\nwritten into a pod, a manifest, a sync payload, or a Studio config screen —\nand reconstructed later, possibly on a different device, possibly by code\nthat has never heard of ",[941,53549,5355],{},[937,53551,3289,53552,53554,53555,53558,53559,53561],{},[941,53553,53523],{}," is that record: plain JSON stating ",[1092,53556,53557],{},"which"," store and\n",[1092,53560,3016],{},", with nothing in it that a secrets audit would flag.",[2217,53563,53565],{"className":2219,"code":53564,"language":2221,"meta":1037,"style":1037},"interface StoreDescriptor {\n  readonly kind: string        \u002F\u002F registered factory key, e.g. 'postgres'\n  readonly class: StoreClass   \u002F\u002F 'local' | 'browser' | 'lan' | 'cloud'\n  readonly address: unknown    \u002F\u002F kind-specific serializable location\n  readonly options?: unknown   \u002F\u002F kind-specific serializable tuning\n}\n",[941,53566,53567,53576,53590,53605,53619,53633],{"__ignoreMap":1037},[2225,53568,53569,53571,53574],{"class":2227,"line":2228},[2225,53570,6994],{"class":2774},[2225,53572,53573],{"class":6342}," StoreDescriptor",[2225,53575,2301],{"class":2235},[2225,53577,53578,53581,53583,53585,53587],{"class":2227,"line":1041},[2225,53579,53580],{"class":2774},"  readonly",[2225,53582,8285],{"class":2307},[2225,53584,2311],{"class":2235},[2225,53586,6366],{"class":6342},[2225,53588,53589],{"class":2763},"        \u002F\u002F registered factory key, e.g. 'postgres'\n",[2225,53591,53592,53594,53597,53599,53602],{"class":2227,"line":1038},[2225,53593,53580],{"class":2774},[2225,53595,53596],{"class":2307}," class",[2225,53598,2311],{"class":2235},[2225,53600,53601],{"class":6342}," StoreClass",[2225,53603,53604],{"class":2763},"   \u002F\u002F 'local' | 'browser' | 'lan' | 'cloud'\n",[2225,53606,53607,53609,53612,53614,53616],{"class":2227,"line":2288},[2225,53608,53580],{"class":2774},[2225,53610,53611],{"class":2307}," address",[2225,53613,2311],{"class":2235},[2225,53615,40717],{"class":6342},[2225,53617,53618],{"class":2763},"    \u002F\u002F kind-specific serializable location\n",[2225,53620,53621,53623,53626,53628,53630],{"class":2227,"line":2304},[2225,53622,53580],{"class":2774},[2225,53624,53625],{"class":2307}," options",[2225,53627,7011],{"class":2235},[2225,53629,40717],{"class":6342},[2225,53631,53632],{"class":2763},"   \u002F\u002F kind-specific serializable tuning\n",[2225,53634,53635],{"class":2227,"line":2356},[2225,53636,7351],{"class":2235},[1022,53638,53640],{"id":53639},"credentialless-by-construction","Credentialless by construction",[937,53642,53643,53644,53646,53647,53649,53650,53653],{},"No field on a ",[941,53645,53523],{}," may be a function, a ",[941,53648,46974],{},",\nor a ",[941,53651,53652],{},"StoreCredentials"," value — enforced by the type, not by convention. A\ndescriptor is safe to persist alongside the rest of a pod's manifest without\nthe review a secret would demand, because there is structurally nowhere on\nit for a secret to hide.",[937,53655,53656,53657,53659],{},"Everything that isn't serializable identity travels separately, supplied to\n",[941,53658,46978],{}," at the moment a descriptor becomes a live store:",[1338,53661,53662,53674],{},[1341,53663,53664],{},[1344,53665,53666,53668,53671],{},[1347,53667,14760],{},[1347,53669,53670],{},"Carries",[1347,53672,53673],{},"Rides in",[1357,53675,53676,53688,53699,53718],{},[1344,53677,53678,53682,53685],{},[1362,53679,53680],{},[941,53681,46953],{},[1362,53683,53684],{},"Where the backend is",[1362,53686,53687],{},"the descriptor (serialized)",[1344,53689,53690,53694,53697],{},[1362,53691,53692],{},[941,53693,16362],{},[1362,53695,53696],{},"Serializable tuning",[1362,53698,53687],{},[1344,53700,53701,53705,53711],{},[1362,53702,53703],{},[941,53704,48116],{},[1362,53706,53707,53708,53710],{},"Device-local objects — a live client, a custom ",[941,53709,52725],{},", a mount path, a drive handle",[1362,53712,53713,34689,53715,53717],{},[941,53714,46978],{},[941,53716,8097],{},", device-side only",[1344,53719,53720,53724,53729],{},[1362,53721,53722],{},[941,53723,48119],{},[1362,53725,3289,53726,53728],{},[941,53727,46974],{}," (the hub's credential-broker seam)",[1362,53730,53731,34689,53733,53717],{},[941,53732,46978],{},[941,53734,8097],{},[1022,53736,53738,53740],{"id":53737},"class-the-closed-union",[941,53739,7670],{},": the closed union",[937,53742,53743,7356,53746,53749,53750,53752],{},[941,53744,53745],{},"StoreClass",[941,53747,53748],{},"'local' | 'browser' | 'lan' | 'cloud'"," — a broad topology\nbucket a caller or a UI can group and filter descriptors by without knowing\nevery concrete ",[941,53751,27895],{}," string. It says nothing about auth or transport, only\nwhere the backend physically lives relative to the device resolving the\ndescriptor.",[1022,53754,53756],{"id":53755},"the-four-slots-in-practice","The four slots, in practice",[937,53758,53759,53763,53764,53767,53768,53771,53772,53775,53776,53778,53779,53781,53782,53785,53786,1625,53788,53790,53791,1625,53793,53795],{},[950,53760,53761],{},[941,53762,46953],{}," is where a store's factory disagrees the most from store to\nstore, and deliberately so — each borrows its own ecosystem's vocabulary so\na descriptor reads as familiar rather than invented: ",[941,53765,53766],{},"{ database, schema, table }"," for Postgres, ",[941,53769,53770],{},"{ url, table }"," for Turso, ",[941,53773,53774],{},"{ bucket, region, prefix }"," for S3. Some address fields are consumed by the factory (",[941,53777,1338],{},"\nmaps to a store's ",[941,53780,48971],{},"); others are ",[950,53783,53784],{},"identity-only"," — present so a\npersisted descriptor is legible to a human or to Studio, but not read by the\nfactory because the live connection already carries that information (a\nPostgres ",[941,53787,31066],{},[941,53789,23934],{},", an SMB ",[941,53792,14346],{},[941,53794,52945],{},"). Each store's page\nstates which is which.",[937,53797,53798,53803,53804,53806,53807,968,53809,968,53811,968,53813,53816,53817,53819,53820,968,53822,53824,53825,53827],{},[950,53799,53800,53802],{},[941,53801,48116],{}," carries what is device-local, named for what it actually is.","\nWhere a store injects a live client, the field is ",[941,53805,9913],{},", uniformly,\nacross every store that takes one — whatever the store's own direct-call\noption calls it (",[941,53808,6590],{},[941,53810,418],{},[941,53812,53385],{},[941,53814,53815],{},"fs","), the factory maps ",[941,53818,48992],{},"\nonto it. Where the device-local thing isn't a client — a WebDAV transport\noverride, an NFS mount point — the binding field is named for that instead\n(",[941,53821,52725],{},[941,53823,51632],{},"), because calling a filesystem path ",[941,53826,9913],{}," would be a\nlie in service of uniformity.",[937,53829,53830,53831,52561,53833,53836,53837,53840,53841,53846,53847,53849],{},"Some stores carry no client library at all — no ",[941,53832,51874],{},[941,53834,53835],{},"mysql2",", no\n",[941,53838,53839],{},"@libsql\u002Fclient"," — by design, so the adapter stays dependency-free and the\ncaller owns pooling, retries, and TLS. For those, ",[950,53842,53843,53845],{},[941,53844,48992],{}," is\nrequired, not an escape hatch",": the descriptor is portable identity rather\nthan a construction recipe, and ",[941,53848,46978],{}," throws a clear, consistently\nworded error if it's missing.",[937,53851,53852,2993,53856,53858,53859,53861,53862,53864],{},[950,53853,53854],{},[941,53855,48119],{},[941,53857,46974],{}," — an async function the store\ninvokes to get a ",[941,53860,53652],{}," value (an AWS key pair, a bearer token,\n…) — supplied to ",[941,53863,46978],{},", never to the descriptor. It is how the same\ndescriptor resolves against a caller's ambient credential broker on one\ndevice and a different one on another.",[1022,53866,53868],{"id":53867},"precedence","Precedence",[937,53870,53871],{},"Inside a factory, later always wins:",[2217,53873,53876],{"className":53874,"code":53875,"language":5672},[5670],"options → address → binding\n",[941,53877,53875],{"__ignoreMap":1037},[937,53879,53880,53881,53883,53884,52185,53886,53888,53889,53891,53892,1625,53895,53897,53898,53901,53902,53905,53906,53908],{},"A device-local ",[941,53882,48116],{}," supersedes anything derived from ",[941,53885,46953],{},[941,53887,16362],{}," — an injected ",[941,53890,9913],{}," always wins over ",[941,53893,53894],{},"region",[941,53896,49357],{},", a\n",[941,53899,53900],{},"binding.dir"," override always wins over ",[941,53903,53904],{},"address.dir",". This is what makes a\ndescriptor authored on one machine usable on another: the parts that can't\ntravel are exactly the parts a fresh ",[941,53907,48116],{}," supplies.",[1022,53910,53912],{"id":53911},"a-descriptor-may-only-set-declared-keys","A descriptor may only set declared keys",[937,53914,53915,997,53917,53919,53920,53922,53923,53925,53926,53928,53929,53931,53932,53935],{},[941,53916,46953],{},[941,53918,16362],{}," are typed ",[941,53921,22421],{}," on ",[941,53924,53523],{},", so nothing\nstructurally stops a caller putting an extra key in either bag. From\n",[941,53927,51611],{}," every extended ",[941,53930,1779],{}," factory ",[950,53933,53934],{},"destructures the named fields it\ndeclares"," instead of spreading that bag into the store options, so an\nundeclared key cannot reach the store at all.",[937,53937,53938,53939,53942,53943,53946,53947,53950,53951,53954,53955,53957,53958,1261,53960,53962,53963,53965,53966,53968,53969,53972],{},"This was a real hole, not a tidiness fix, and it only bit where the ",[1092,53940,53941],{},"winning","\nkey was applied conditionally. ",[941,53944,53945],{},"to-nfs"," is the sharp case: ",[941,53948,53949],{},"mountDetector"," is a\nbinding-owned slot applied only when ",[941,53952,53953],{},"binding.mountDetector"," was absent, so a\nmatching key smuggled through ",[941,53956,16362],{}," survived and won — silently disabling\nthe ",[941,53959,50652],{},[941,53961,51576],{}," \u002F fstype safety diagnostics that are the entire reason\nthat store exists. ",[941,53964,5355],{}," had the same shape via ",[941,53967,48971],{}," (applied\nonly when ",[941,53970,53971],{},"address.table"," was set).",[937,53974,53975,53976,53979,53980,53982],{},"The practical consequence: ",[950,53977,53978],{},"a key that used to quietly take effect now quietly\ndoes nothing."," If you were relying on an undeclared key, move it to the slot\nthat declares it — a device-local object belongs in ",[941,53981,48116],{},", not in the\nserialized bag.",[1022,53984,53986],{"id":53985},"the-six-exports","The six exports",[937,53988,30792,53989,53991],{},[941,53990,1779],{}," store that has adopted the seam exports the same shape:",[2217,53993,53995],{"className":2219,"code":53994,"language":2221,"meta":1037,"style":1037},"export interface \u003CKind>Address              \u002F\u002F serializable \"where\"\nexport interface \u003CKind>DescriptorOptions    \u002F\u002F serializable tuning; omitted where none exists\nexport interface \u003CKind>Binding              \u002F\u002F device-local objects; omitted where none exists\nexport function \u003Ckind>StoreDescriptor(address, options?): StoreDescriptor\nexport const \u003Ckind>StoreFactory: StoreFactory\nexport function register\u003CKind>Store(locator: StoreLocator): void\n",[941,53996,53997,54016,54034,54052,54080,54099],{"__ignoreMap":1037},[2225,53998,53999,54001,54004,54006,54008,54010,54013],{"class":2227,"line":2228},[2225,54000,8080],{"class":2231},[2225,54002,54003],{"class":2774}," interface",[2225,54005,40429],{"class":2235},[2225,54007,37901],{"class":6342},[2225,54009,6480],{"class":2235},[2225,54011,54012],{"class":6342},"Address",[2225,54014,54015],{"class":2763},"              \u002F\u002F serializable \"where\"\n",[2225,54017,54018,54020,54022,54024,54026,54028,54031],{"class":2227,"line":1041},[2225,54019,8080],{"class":6342},[2225,54021,54003],{"class":6342},[2225,54023,40429],{"class":2235},[2225,54025,37901],{"class":6342},[2225,54027,6480],{"class":2235},[2225,54029,54030],{"class":6342},"DescriptorOptions",[2225,54032,54033],{"class":2763},"    \u002F\u002F serializable tuning; omitted where none exists\n",[2225,54035,54036,54038,54040,54042,54044,54046,54049],{"class":2227,"line":1038},[2225,54037,8080],{"class":6342},[2225,54039,54003],{"class":6342},[2225,54041,40429],{"class":2235},[2225,54043,37901],{"class":6342},[2225,54045,6480],{"class":2235},[2225,54047,54048],{"class":6342},"Binding",[2225,54050,54051],{"class":2763},"              \u002F\u002F device-local objects; omitted where none exists\n",[2225,54053,54054,54056,54058,54060,54062,54064,54066,54068,54070,54072,54074,54077],{"class":2227,"line":2288},[2225,54055,8080],{"class":6342},[2225,54057,40856],{"class":6342},[2225,54059,40429],{"class":2235},[2225,54061,27895],{"class":6342},[2225,54063,6480],{"class":2235},[2225,54065,53523],{"class":6342},[2225,54067,2789],{"class":2239},[2225,54069,46953],{"class":6342},[2225,54071,968],{"class":2239},[2225,54073,16362],{"class":6342},[2225,54075,54076],{"class":2239},"?): ",[2225,54078,54079],{"class":6342},"StoreDescriptor\n",[2225,54081,54082,54084,54086,54088,54090,54092,54094,54096],{"class":2227,"line":2304},[2225,54083,8080],{"class":6342},[2225,54085,8083],{"class":6342},[2225,54087,40429],{"class":2235},[2225,54089,27895],{"class":6342},[2225,54091,6480],{"class":2235},[2225,54093,53527],{"class":6342},[2225,54095,2085],{"class":2239},[2225,54097,54098],{"class":6342},"StoreFactory\n",[2225,54100,54101,54103,54105,54108,54110,54112,54114,54116,54118,54121,54123,54126,54128],{"class":2227,"line":2356},[2225,54102,8080],{"class":6342},[2225,54104,40856],{"class":6342},[2225,54106,54107],{"class":6342}," register",[2225,54109,6474],{"class":2235},[2225,54111,37901],{"class":6342},[2225,54113,6480],{"class":2235},[2225,54115,9665],{"class":6342},[2225,54117,2789],{"class":2239},[2225,54119,54120],{"class":6342},"locator",[2225,54122,2085],{"class":2239},[2225,54124,54125],{"class":6342},"StoreLocator",[2225,54127,11737],{"class":2239},[2225,54129,12295],{"class":6342},[937,54131,54132,54133,54135],{},"This is purely additive — ",[941,54134,50771],{}," is unchanged and remains the direct\nconstruction path. Nothing about existing callers moves.",[1022,54137,54139],{"id":54138},"worked-example","Worked example",[2217,54141,54143],{"className":2219,"code":54142,"language":2221,"meta":1037,"style":1037},"import { createStoreLocator } from '@noy-db\u002Fhub\u002Fto'\nimport { postgresStoreDescriptor, registerPostgresStore } from '@noy-db\u002Fto-postgres'\nimport { Client } from 'pg'\n\n\u002F\u002F 1. Build a descriptor — plain JSON, safe to persist.\nconst descriptor = postgresStoreDescriptor(\n  { database: 'app', schema: 'public', table: 'noydb_envelopes' },\n  { autoMigrate: true },\n)\n\n\u002F\u002F 2. Register the kinds a given process knows how to resolve.\nconst locator = createStoreLocator()\nregisterPostgresStore(locator)\n\n\u002F\u002F 3. Later — possibly after a round-trip through a pod or a config\n\u002F\u002F    screen — resolve back to a live store. The connection is device-local\n\u002F\u002F    and supplied here, never carried on the descriptor.\nconst client = new Client({ connectionString: process.env.DATABASE_URL })\nawait client.connect()\nconst store = await locator.resolve(descriptor, { binding: { client } })\n\n\u002F\u002F 4. Use it like any other NoydbStore.\nawait store.ping()\n",[941,54144,54145,54164,54186,54205,54209,54214,54226,54266,54279,54283,54287,54292,54305,54313,54317,54322,54327,54332,54369,54382,54421,54425,54430],{"__ignoreMap":1037},[2225,54146,54147,54149,54151,54154,54156,54158,54160,54162],{"class":2227,"line":2228},[2225,54148,2232],{"class":2231},[2225,54150,2236],{"class":2235},[2225,54152,54153],{"class":2239}," createStoreLocator",[2225,54155,2243],{"class":2235},[2225,54157,2246],{"class":2231},[2225,54159,2249],{"class":2235},[2225,54161,3299],{"class":2252},[2225,54163,2256],{"class":2235},[2225,54165,54166,54168,54170,54172,54174,54176,54178,54180,54182,54184],{"class":2227,"line":1041},[2225,54167,2232],{"class":2231},[2225,54169,2236],{"class":2235},[2225,54171,51949],{"class":2239},[2225,54173,2298],{"class":2235},[2225,54175,51954],{"class":2239},[2225,54177,2243],{"class":2235},[2225,54179,2246],{"class":2231},[2225,54181,2249],{"class":2235},[2225,54183,51853],{"class":2252},[2225,54185,2256],{"class":2235},[2225,54187,54188,54190,54192,54195,54197,54199,54201,54203],{"class":2227,"line":1038},[2225,54189,2232],{"class":2231},[2225,54191,2236],{"class":2235},[2225,54193,54194],{"class":2239}," Client",[2225,54196,2243],{"class":2235},[2225,54198,2246],{"class":2231},[2225,54200,2249],{"class":2235},[2225,54202,51874],{"class":2252},[2225,54204,2256],{"class":2235},[2225,54206,54207],{"class":2227,"line":2288},[2225,54208,2285],{"emptyLinePlaceholder":2284},[2225,54210,54211],{"class":2227,"line":2304},[2225,54212,54213],{"class":2763},"\u002F\u002F 1. Build a descriptor — plain JSON, safe to persist.\n",[2225,54215,54216,54218,54220,54222,54224],{"class":2227,"line":2356},[2225,54217,2775],{"class":2774},[2225,54219,47019],{"class":2239},[2225,54221,2781],{"class":2235},[2225,54223,51949],{"class":2291},[2225,54225,11683],{"class":2239},[2225,54227,54228,54230,54232,54234,54236,54238,54240,54242,54244,54246,54248,54250,54252,54254,54256,54258,54260,54262,54264],{"class":2227,"line":2830},[2225,54229,26354],{"class":2235},[2225,54231,49062],{"class":2307},[2225,54233,2311],{"class":2235},[2225,54235,2249],{"class":2235},[2225,54237,41270],{"class":2252},[2225,54239,2805],{"class":2235},[2225,54241,2298],{"class":2235},[2225,54243,51997],{"class":2307},[2225,54245,2311],{"class":2235},[2225,54247,2249],{"class":2235},[2225,54249,12],{"class":2252},[2225,54251,2805],{"class":2235},[2225,54253,2298],{"class":2235},[2225,54255,47030],{"class":2307},[2225,54257,2311],{"class":2235},[2225,54259,2249],{"class":2235},[2225,54261,51497],{"class":2252},[2225,54263,2805],{"class":2235},[2225,54265,2353],{"class":2235},[2225,54267,54268,54270,54273,54275,54277],{"class":2227,"line":2861},[2225,54269,26354],{"class":2235},[2225,54271,54272],{"class":2307}," autoMigrate",[2225,54274,2311],{"class":2235},[2225,54276,2322],{"class":2321},[2225,54278,2353],{"class":2235},[2225,54280,54281],{"class":2227,"line":4246},[2225,54282,2362],{"class":2239},[2225,54284,54285],{"class":2227,"line":4253},[2225,54286,2285],{"emptyLinePlaceholder":2284},[2225,54288,54289],{"class":2227,"line":4259},[2225,54290,54291],{"class":2763},"\u002F\u002F 2. Register the kinds a given process knows how to resolve.\n",[2225,54293,54294,54296,54299,54301,54303],{"class":2227,"line":6548},[2225,54295,2775],{"class":2774},[2225,54297,54298],{"class":2239}," locator ",[2225,54300,2781],{"class":2235},[2225,54302,54153],{"class":2291},[2225,54304,8146],{"class":2239},[2225,54306,54307,54310],{"class":2227,"line":6576},[2225,54308,54309],{"class":2291},"registerPostgresStore",[2225,54311,54312],{"class":2239},"(locator)\n",[2225,54314,54315],{"class":2227,"line":6582},[2225,54316,2285],{"emptyLinePlaceholder":2284},[2225,54318,54319],{"class":2227,"line":6587},[2225,54320,54321],{"class":2763},"\u002F\u002F 3. Later — possibly after a round-trip through a pod or a config\n",[2225,54323,54324],{"class":2227,"line":7174},[2225,54325,54326],{"class":2763},"\u002F\u002F    screen — resolve back to a live store. The connection is device-local\n",[2225,54328,54329],{"class":2227,"line":7180},[2225,54330,54331],{"class":2763},"\u002F\u002F    and supplied here, never carried on the descriptor.\n",[2225,54333,54334,54336,54339,54341,54343,54345,54347,54349,54352,54354,54356,54358,54360,54362,54365,54367],{"class":2227,"line":7220},[2225,54335,2775],{"class":2774},[2225,54337,54338],{"class":2239}," client ",[2225,54340,2781],{"class":2235},[2225,54342,8130],{"class":2235},[2225,54344,54194],{"class":2291},[2225,54346,2789],{"class":2239},[2225,54348,2792],{"class":2235},[2225,54350,54351],{"class":2307}," connectionString",[2225,54353,2311],{"class":2235},[2225,54355,36593],{"class":2239},[2225,54357,1520],{"class":2235},[2225,54359,183],{"class":2239},[2225,54361,1520],{"class":2235},[2225,54363,54364],{"class":2239},"DATABASE_URL ",[2225,54366,2359],{"class":2235},[2225,54368,2362],{"class":2239},[2225,54370,54371,54373,54375,54377,54380],{"class":2227,"line":7225},[2225,54372,6500],{"class":2231},[2225,54374,25871],{"class":2239},[2225,54376,1520],{"class":2235},[2225,54378,54379],{"class":2291},"connect",[2225,54381,8146],{"class":2239},[2225,54383,54384,54386,54388,54390,54392,54395,54397,54400,54403,54405,54407,54409,54411,54413,54415,54417,54419],{"class":2227,"line":7231},[2225,54385,2775],{"class":2774},[2225,54387,39676],{"class":2239},[2225,54389,2781],{"class":2235},[2225,54391,2784],{"class":2231},[2225,54393,54394],{"class":2239}," locator",[2225,54396,1520],{"class":2235},[2225,54398,54399],{"class":2291},"resolve",[2225,54401,54402],{"class":2239},"(descriptor",[2225,54404,2298],{"class":2235},[2225,54406,2236],{"class":2235},[2225,54408,49048],{"class":2307},[2225,54410,2311],{"class":2235},[2225,54412,2236],{"class":2235},[2225,54414,54338],{"class":2239},[2225,54416,2359],{"class":2235},[2225,54418,2243],{"class":2235},[2225,54420,2362],{"class":2239},[2225,54422,54423],{"class":2227,"line":7267},[2225,54424,2285],{"emptyLinePlaceholder":2284},[2225,54426,54427],{"class":2227,"line":7272},[2225,54428,54429],{"class":2763},"\u002F\u002F 4. Use it like any other NoydbStore.\n",[2225,54431,54432,54434,54436,54438,54440],{"class":2227,"line":7278},[2225,54433,6500],{"class":2231},[2225,54435,7564],{"class":2239},[2225,54437,1520],{"class":2235},[2225,54439,47528],{"class":2291},[2225,54441,8146],{"class":2239},[937,54443,54444,54447,54448,54451],{},[941,54445,54446],{},"JSON.parse(JSON.stringify(descriptor))"," round-trips ",[941,54449,54450],{},"descriptor"," exactly —\nthat equality is the credentialless guarantee made concrete: nothing was\nlost, because nothing sensitive was ever on it.",[1022,54453,54455,54456],{"id":54454},"when-to-reach-for-this-instead-of-tokind","When to reach for this instead of ",[941,54457,50771],{},[937,54459,54460,54461,54463],{},"Call ",[941,54462,50771],{}," directly when the code that knows the connection is the\nsame code building the store — the common case, and still the simplest one.",[937,54465,54466,54467,54469,54470,54473,54474,54476,54477,2514,54479,54481],{},"Reach for the descriptor path when \"which store, and where\" needs to be\n",[950,54468,8673],{},": written into a pod's manifest so a vault records its own storage\ntopology, offered as an option in a configuration UI without hardcoding a\n",[941,54471,54472],{},"switch"," over every ",[941,54475,1779],{}," package, or handed across a process boundary\n(main → renderer, server → CLI) where only one side holds the live\nconnection. The descriptor is the part that travels; ",[941,54478,48116],{},[941,54480,48119],{}," are supplied wherever the descriptor lands.",[1022,54483,2621],{"id":2620},[1111,54485,54486,54492],{},[1114,54487,54488,48858],{},[1007,54489,54490,48857],{"href":2634},[941,54491,1779],{},[1114,54493,54494,54496],{},[1007,54495,50751],{"href":50748}," — picking a backend per platform",[2645,54498,54499],{},"html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .sfNiH, html code.shiki .sfNiH{--shiki-light:#FF5370;--shiki-default:#FF9CAC;--shiki-dark:#FF9CAC}",{"title":1037,"searchDepth":1038,"depth":1038,"links":54501},[54502,54503,54504,54506,54507,54508,54509,54510,54511,54513],{"id":53535,"depth":1041,"text":53536},{"id":53639,"depth":1041,"text":53640},{"id":53737,"depth":1041,"text":54505},"class: the closed union",{"id":53755,"depth":1041,"text":53756},{"id":53867,"depth":1041,"text":53868},{"id":53911,"depth":1041,"text":53912},{"id":53985,"depth":1041,"text":53986},{"id":54138,"depth":1041,"text":54139},{"id":54454,"depth":1041,"text":54512},"When to reach for this instead of to\u003CKind>()",{"id":2620,"depth":1041,"text":2621},"A credentialless, JSON-serializable StoreDescriptor plus a StoreLocator registry that reconstructs a live to-* store from data.",{},{"title":430,"icon":433},{"title":430,"description":54514},"CnhFIKoCftc96gqvAFLlw0ZN42-IjGBQ0w0mAtO3iXU",{"id":54520,"title":435,"access":12,"apiToc":929,"body":54521,"category":1042,"description":54720,"extension":1044,"meta":54721,"navigation":1042,"nest":3303,"path":436,"seo":54722,"sinceVersion":1042,"status":1049,"stem":437,"subtitle":1042,"tier":1050,"tocExternal":1042,"__hash__":54723},"docs\u002Ffamilies\u002Fto\u002Fsupabase.md",{"type":931,"value":54522,"toc":54714},[54523,54525,54540,54543,54545,54557,54559,54581,54583,54625,54700,54702,54712],[934,54524,435],{"id":435},[1061,54526,54527],{},[937,54528,54529,1368,54534,54537,54538,1520],{},[950,54530,54531],{},[941,54532,54533],{},"@noy-db\u002Fto-supabase",[941,54535,54536],{},"pnpm add @noy-db\u002Fto-supabase","\nPostgres + Storage combo, built on ",[941,54539,5355],{},[46871,54541],{":rows":54542},"[{\"label\":\"Shape\",\"value\":\"record\"},{\"label\":\"Atomic CAS\",\"value\":\"✓\"},{\"label\":\"Locality\",\"value\":\"cloud\"},{\"label\":\"Speed\",\"value\":\"medium\"},{\"label\":\"Persistence\",\"value\":\"durable\"}]",[1022,54544,29857],{"id":29856},[937,54546,54547,54548,54552,54553,54556],{},"A thin factory over ",[1007,54549,54550],{"href":50493},[941,54551,51853],{}," for the Postgres\nconnection you already have (",[941,54554,54555],{},"pg.Pool",", Supabase's serverless driver, …), with\noptional blob routing via Supabase Storage.",[1022,54558,9542],{"id":2854},[1111,54560,54561,54570,54576],{},[1114,54562,54563,54565,54566,1617,54568,957],{},[941,54564,7811],{}," — delegates CAS to ",[941,54567,5355],{},[941,54569,51893],{},[1114,54571,54572,54573,54575],{},"Record shape, one row per record — inherits the full ",[941,54574,5355],{}," feature set.",[1114,54577,46929,54578,1520],{},[941,54579,54580],{},"toSupabase(options: SupabaseStoreOptions)",[1022,54582,46936],{"id":46935},[937,54584,46939,54585,46944,54587,968,54590,1952,54592,7356,54594,2085,54597,48968,54599,5502,54601,997,54604,51925,54606,54608,54609,7356,54611,1952,54613,7356,54615,54617,54618,54621,54622,54624],{},[1007,54586,46943],{"href":46942},[941,54588,54589],{},"kind: 'supabase'",[941,54591,46950],{},[941,54593,46953],{},[941,54595,54596],{},"{ projectRef?, schema?, table? }",[941,54598,1338],{},[941,54600,48971],{},[941,54602,54603],{},"projectRef",[941,54605,23934],{},[941,54607,54603],{}," is the stable identifier (a full URL is derivable from\nit). ",[941,54610,16362],{},[941,54612,48989],{},[941,54614,48992],{},[950,54616,45375],{}," (a\n",[941,54619,54620],{},"PostgresClient",") — this store has no client library of its own; ",[941,54623,46978],{},"\nwithout it throws.",[2217,54626,54628],{"className":2219,"code":54627,"language":2221,"meta":1037,"style":1037},"import { supabaseStoreDescriptor, registerSupabaseStore } from '@noy-db\u002Fto-supabase'\n\nconst descriptor = supabaseStoreDescriptor({ projectRef: 'abcxyz', schema: 'public' })\n",[941,54629,54630,54654,54658],{"__ignoreMap":1037},[2225,54631,54632,54634,54636,54639,54641,54644,54646,54648,54650,54652],{"class":2227,"line":2228},[2225,54633,2232],{"class":2231},[2225,54635,2236],{"class":2235},[2225,54637,54638],{"class":2239}," supabaseStoreDescriptor",[2225,54640,2298],{"class":2235},[2225,54642,54643],{"class":2239}," registerSupabaseStore",[2225,54645,2243],{"class":2235},[2225,54647,2246],{"class":2231},[2225,54649,2249],{"class":2235},[2225,54651,54533],{"class":2252},[2225,54653,2256],{"class":2235},[2225,54655,54656],{"class":2227,"line":1041},[2225,54657,2285],{"emptyLinePlaceholder":2284},[2225,54659,54660,54662,54664,54666,54668,54670,54672,54675,54677,54679,54682,54684,54686,54688,54690,54692,54694,54696,54698],{"class":2227,"line":1038},[2225,54661,2775],{"class":2774},[2225,54663,47019],{"class":2239},[2225,54665,2781],{"class":2235},[2225,54667,54638],{"class":2291},[2225,54669,2789],{"class":2239},[2225,54671,2792],{"class":2235},[2225,54673,54674],{"class":2307}," projectRef",[2225,54676,2311],{"class":2235},[2225,54678,2249],{"class":2235},[2225,54680,54681],{"class":2252},"abcxyz",[2225,54683,2805],{"class":2235},[2225,54685,2298],{"class":2235},[2225,54687,51997],{"class":2307},[2225,54689,2311],{"class":2235},[2225,54691,2249],{"class":2235},[2225,54693,12],{"class":2252},[2225,54695,2805],{"class":2235},[2225,54697,2243],{"class":2235},[2225,54699,2362],{"class":2239},[1022,54701,2621],{"id":2620},[1111,54703,54704,54708],{},[1114,54705,54706],{},[1007,54707,47064],{"href":2634},[1114,54709,54710,49470],{},[1007,54711,407],{"href":50493},[2645,54713,37016],{},{"title":1037,"searchDepth":1038,"depth":1038,"links":54715},[54716,54717,54718,54719],{"id":29856,"depth":1041,"text":29857},{"id":2854,"depth":1041,"text":9542},{"id":46935,"depth":1041,"text":46936},{"id":2620,"depth":1041,"text":2621},"Supabase adapter for noy-db — a thin factory over to-postgres for the Postgres connection you already have, inheriting its full feature set.",{},{"title":435,"description":54720},"3GHnhqjNA8cz4a0K4MG771YOEC5G78s8_P1SvAq4m78",{"id":54725,"title":439,"access":12,"apiToc":929,"body":54726,"category":1042,"description":54923,"extension":1044,"meta":54924,"navigation":1042,"nest":3303,"path":440,"seo":54925,"sinceVersion":1042,"status":1049,"stem":441,"subtitle":1042,"tier":1050,"tocExternal":1042,"__hash__":54926},"docs\u002Ffamilies\u002Fto\u002Fturso.md",{"type":931,"value":54727,"toc":54917},[54728,54730,54743,54745,54747,54757,54759,54778,54780,54840,54903,54905,54915],[934,54729,439],{"id":439},[1061,54731,54732],{},[937,54733,54734,1368,54739,54742],{},[950,54735,54736],{},[941,54737,54738],{},"@noy-db\u002Fto-turso",[941,54740,54741],{},"pnpm add @noy-db\u002Fto-turso","\nHosted libSQL (SQLite fork) with multi-region replication.",[46871,54744],{":rows":54542},[1022,54746,29857],{"id":29856},[937,54748,39587,54749,54751,54752,54756],{},[941,54750,53839],{}," through a small async shim that speaks the same noy-db\nstore contract as ",[1007,54753,54754],{"href":49096},[941,54755,422],{},", using the same table and DDL — moving\nfrom a local SQLite file to Turso is a factory swap, nothing else changes.",[1022,54758,9542],{"id":2854},[1111,54760,54761,54771,54773],{},[1114,54762,54763,54765,54766,953,54768,54770],{},[941,54764,7811],{},", same mechanism as ",[941,54767,5361],{},[941,54769,53128],{},"),\nnatively async over libSQL.",[1114,54772,48944],{},[1114,54774,46929,54775,1520],{},[941,54776,54777],{},"toTurso(options: TursoStoreOptions)",[1022,54779,46936],{"id":46935},[937,54781,46939,54782,46944,54784,968,54787,1952,54789,7356,54791,964,54794,33690,54797,54799,54800,54803,54804,54806,54807,48968,54809,38633,54811,7356,54813,54816,54817,1324,54819,54821,54822,54824,54825,54828,1324,54830,54833,54834,54836,54837,54839],{},[1007,54783,46943],{"href":46942},[941,54785,54786],{},"kind: 'turso'",[941,54788,46950],{},[941,54790,46953],{},[941,54792,54793],{},"{ url, table? }",[941,54795,54796],{},"url",[950,54798,45375],{},": the connection URL used to live only inside a ",[941,54801,54802],{},"clientFactory","\nclosure, invisible on a persisted descriptor; putting it on ",[941,54805,46953],{}," states\nwhich database a descriptor refers to. ",[941,54808,1338],{},[941,54810,48971],{},[941,54812,16362],{},[941,54814,54815],{},"{ autoMigrate?, clockUncertaintyMs? }",". Unlike the rest of its\ntier, ",[941,54818,52781],{},[1092,54820,5105],{}," build its own connection: ",[941,54823,48116],{}," accepts ",[941,54826,54827],{},"client?",[950,54829,7493],{},[941,54831,54832],{},"clientFactory?"," (paired with ",[941,54835,48119],{}," for the auth token), and\n",[941,54838,46978],{}," throws unless one of the two is present.",[2217,54841,54843],{"className":2219,"code":54842,"language":2221,"meta":1037,"style":1037},"import { tursoStoreDescriptor, registerTursoStore } from '@noy-db\u002Fto-turso'\n\nconst descriptor = tursoStoreDescriptor({ url: 'libsql:\u002F\u002Fmy-db.turso.io' })\n",[941,54844,54845,54869,54873],{"__ignoreMap":1037},[2225,54846,54847,54849,54851,54854,54856,54859,54861,54863,54865,54867],{"class":2227,"line":2228},[2225,54848,2232],{"class":2231},[2225,54850,2236],{"class":2235},[2225,54852,54853],{"class":2239}," tursoStoreDescriptor",[2225,54855,2298],{"class":2235},[2225,54857,54858],{"class":2239}," registerTursoStore",[2225,54860,2243],{"class":2235},[2225,54862,2246],{"class":2231},[2225,54864,2249],{"class":2235},[2225,54866,54738],{"class":2252},[2225,54868,2256],{"class":2235},[2225,54870,54871],{"class":2227,"line":1041},[2225,54872,2285],{"emptyLinePlaceholder":2284},[2225,54874,54875,54877,54879,54881,54883,54885,54887,54890,54892,54894,54897,54899,54901],{"class":2227,"line":1038},[2225,54876,2775],{"class":2774},[2225,54878,47019],{"class":2239},[2225,54880,2781],{"class":2235},[2225,54882,54853],{"class":2291},[2225,54884,2789],{"class":2239},[2225,54886,2792],{"class":2235},[2225,54888,54889],{"class":2307}," url",[2225,54891,2311],{"class":2235},[2225,54893,2249],{"class":2235},[2225,54895,54896],{"class":2252},"libsql:\u002F\u002Fmy-db.turso.io",[2225,54898,2805],{"class":2235},[2225,54900,2243],{"class":2235},[2225,54902,2362],{"class":2239},[1022,54904,2621],{"id":2620},[1111,54906,54907,54911],{},[1114,54908,54909],{},[1007,54910,47064],{"href":2634},[1114,54912,54913],{},[1007,54914,422],{"href":49096},[2645,54916,37016],{},{"title":1037,"searchDepth":1038,"depth":1038,"links":54918},[54919,54920,54921,54922],{"id":29856,"depth":1041,"text":29857},{"id":2854,"depth":1041,"text":9542},{"id":46935,"depth":1041,"text":46936},{"id":2620,"depth":1041,"text":2621},"Turso \u002F libSQL adapter for noy-db — hosted SQLite with multi-region replication, using the same table and DDL as to-sqlite.",{},{"title":439,"description":54923},"2Br8zfVW0Si3yhgHuQK4bDy2xq5LW6HMN9UHxDlb-OA",{"id":54928,"title":443,"access":12,"apiToc":929,"body":54929,"category":1042,"description":55103,"extension":1044,"meta":55104,"navigation":1042,"nest":3303,"path":444,"seo":55105,"sinceVersion":1042,"status":1049,"stem":445,"subtitle":1042,"tier":1473,"tocExternal":1042,"__hash__":55106},"docs\u002Ffamilies\u002Fto\u002Fwebdav.md",{"type":931,"value":54930,"toc":55097},[54931,54933,54946,54948,54950,54959,54961,54975,54977,55017,55079,55081,55095],[934,54932,443],{"id":443},[1061,54934,54935],{},[937,54936,54937,1368,54942,54945],{},[950,54938,54939],{},[941,54940,54941],{},"@noy-db\u002Fto-webdav",[941,54943,54944],{},"pnpm add @noy-db\u002Fto-webdav","\nNextcloud \u002F ownCloud \u002F any RFC 4918 server.",[46871,54947],{":rows":51556},[1022,54949,29857],{"id":29856},[937,54951,54952,54953,54955,54956,54958],{},"Stores encrypted envelopes over the WebDAV protocol. Works with Nextcloud,\nownCloud, Apache ",[941,54954,50620],{},", or any RFC 4918-compliant server. Pure ",[941,54957,50624],{},"-based,\nzero dependencies.",[1022,54960,9542],{"id":2854},[1111,54962,54963,54968,54970],{},[1114,54964,54965,54967],{},[941,54966,47869],{}," — WebDAV has no atomic CAS primitive; last-write-wins.",[1114,54969,52913],{},[1114,54971,46929,54972,1520],{},[941,54973,54974],{},"toWebdav(options: WebDAVStoreOptions)",[1022,54976,46936],{"id":46935},[937,54978,46939,54979,54981,54982,968,54985,1952,54987,7356,54989,5502,54992,54994,54995,38633,54998,1625,55001,55004,55005,1324,55007,46975,55009,55011,55012,55014,55015,1520],{},[1007,54980,46943],{"href":46942}," for the general model —\nthis store is one of the seam's two reference implementations. ",[941,54983,54984],{},"kind: 'webdav'",[941,54986,51691],{},[941,54988,46953],{},[941,54990,54991],{},"{ baseUrl, prefix? }",[941,54993,16362],{}," is\nthe serializable server-quirk flags ",[941,54996,54997],{},"{ autoMkcol?, eagerMkcol? }",[941,54999,55000],{},"binding.fetch?",[941,55002,55003],{},"binding.headers?"," carry a device-local transport override\n(a wrapper injecting session auth, or a test fake) or static headers; a\n",[941,55006,52761],{},[941,55008,46974],{},[941,55010,46978],{}," time becomes a\nrolling ",[941,55013,52767],{},", cached and refreshed within a skew\nwindow of ",[941,55016,44339],{},[2217,55018,55020],{"className":2219,"code":55019,"language":2221,"meta":1037,"style":1037},"import { webdavStoreDescriptor, registerWebdavStore } from '@noy-db\u002Fto-webdav'\n\nconst descriptor = webdavStoreDescriptor({ baseUrl: 'https:\u002F\u002Fcloud.example.com\u002Fremote.php\u002Fdav' })\n",[941,55021,55022,55046,55050],{"__ignoreMap":1037},[2225,55023,55024,55026,55028,55031,55033,55036,55038,55040,55042,55044],{"class":2227,"line":2228},[2225,55025,2232],{"class":2231},[2225,55027,2236],{"class":2235},[2225,55029,55030],{"class":2239}," webdavStoreDescriptor",[2225,55032,2298],{"class":2235},[2225,55034,55035],{"class":2239}," registerWebdavStore",[2225,55037,2243],{"class":2235},[2225,55039,2246],{"class":2231},[2225,55041,2249],{"class":2235},[2225,55043,54941],{"class":2252},[2225,55045,2256],{"class":2235},[2225,55047,55048],{"class":2227,"line":1041},[2225,55049,2285],{"emptyLinePlaceholder":2284},[2225,55051,55052,55054,55056,55058,55060,55062,55064,55066,55068,55070,55073,55075,55077],{"class":2227,"line":1038},[2225,55053,2775],{"class":2774},[2225,55055,47019],{"class":2239},[2225,55057,2781],{"class":2235},[2225,55059,55030],{"class":2291},[2225,55061,2789],{"class":2239},[2225,55063,2792],{"class":2235},[2225,55065,52832],{"class":2307},[2225,55067,2311],{"class":2235},[2225,55069,2249],{"class":2235},[2225,55071,55072],{"class":2252},"https:\u002F\u002Fcloud.example.com\u002Fremote.php\u002Fdav",[2225,55074,2805],{"class":2235},[2225,55076,2243],{"class":2235},[2225,55078,2362],{"class":2239},[1022,55080,2621],{"id":2620},[1111,55082,55083,55087,55091],{},[1114,55084,55085],{},[1007,55086,47064],{"href":2634},[1114,55088,55089],{},[1007,55090,426],{"href":50597},[1114,55092,55093],{},[1007,55094,403],{"href":50644},[2645,55096,37016],{},{"title":1037,"searchDepth":1038,"depth":1038,"links":55098},[55099,55100,55101,55102],{"id":29856,"depth":1041,"text":29857},{"id":2854,"depth":1041,"text":9542},{"id":46935,"depth":1041,"text":46936},{"id":2620,"depth":1041,"text":2621},"WebDAV adapter for noy-db — encrypted object storage over WebDAV, works with Nextcloud, ownCloud, Apache mod_dav, or any RFC 4918 server.",{},{"title":443,"description":55103},"qvxnZOL_ACAblsVOWG_M6OIlwfYa7mvtHZ4vSCWAkE0",{"id":55108,"title":455,"access":12,"apiToc":929,"body":55109,"category":1042,"description":55467,"extension":1044,"meta":55468,"navigation":1042,"nest":1042,"path":456,"seo":55469,"sinceVersion":1042,"status":1049,"stem":457,"subtitle":1042,"tier":1050,"tocExternal":1042,"__hash__":55470},"docs\u002Ffaq\u002F1.where-can-noy-db-run.md",{"type":931,"value":55110,"toc":55459},[55111,55114,55133,55250,55254,55269,55279,55297,55301,55324,55343,55347,55352,55391,55397,55401,55420,55424,55439,55441],[934,55112,455],{"id":55113},"where-can-noy-db-run",[937,55115,55116,55117,55120,55121,2462,55124,55127,55128,55132],{},"noy-db is a TypeScript library (ESM, Node ≥22 or a modern browser — it needs the Web Crypto API and nothing else cryptographic). It runs ",[1092,55118,55119],{},"inside your application process","; there is no noy-db server. That makes the topology question really two questions per deployment: ",[950,55122,55123],{},"where does the code run",[950,55125,55126],{},"where does the ciphertext live"," (any ",[1007,55129,55130,7564],{"href":115},[941,55131,1779],{},"). The five common answers:",[1338,55134,55135,55154],{},[1341,55136,55137],{},[1344,55138,55139,55142,55145,55148,55151],{},[1347,55140,55141],{},"Topology",[1347,55143,55144],{},"Persistence",[1347,55146,55147],{},"Sync",[1347,55149,55150],{},"Multi-user",[1347,55152,55153],{},"Zero-knowledge holds?",[1357,55155,55156,55177,55198,55218,55233],{},[1344,55157,55158,55161,55168,55171,55174],{},[1362,55159,55160],{},"Device-only, no server",[1362,55162,55163,55164,1261,55166],{},"✅ ",[941,55165,9507],{},[941,55167,7461],{},[1362,55169,55170],{},"➖ none until you add a shared store",[1362,55172,55173],{},"✅ on-device grants",[1362,55175,55176],{},"✅ keys never leave the device",[1344,55178,55179,55182,55187,55190,55192],{},[1362,55180,55181],{},"Server-only, behind your API",[1362,55183,55184,55185],{},"✅ any ",[941,55186,1779],{},[1362,55188,55189],{},"✅",[1362,55191,55189],{},[1362,55193,55194,55195,55197],{},"⚠️ toward the ",[1092,55196,6951],{},", yes — but the unlocked server holds keys",[1344,55199,55200,55203,55208,55210,55212],{},[1362,55201,55202],{},"Serverless",[1362,55204,55205,55206],{},"✅ cloud ",[941,55207,1779],{},[1362,55209,55189],{},[1362,55211,55189],{},[1362,55213,55214,55215,55217],{},"⚠️ same nuance; ",[941,55216,1139],{}," sealing is the unlock path",[1344,55219,55220,55223,55226,55228,55230],{},[1362,55221,55222],{},"Container",[1362,55224,55225],{},"✅ volume or external store",[1362,55227,55189],{},[1362,55229,55189],{},[1362,55231,55232],{},"same as its server topology",[1344,55234,55235,55238,55242,55245,55247],{},[1362,55236,55237],{},"Always-on server",[1362,55239,55184,55240],{},[941,55241,1779],{},[1362,55243,55244],{},"✅ (can also host the rendezvous)",[1362,55246,55189],{},[1362,55248,55249],{},"⚠️ same server nuance",[1022,55251,55253],{"id":55252},"can-it-run-only-on-my-iphone-with-no-server","Can it run only on my iPhone, with no server?",[937,55255,55256,55259,55260,55263,55264,55268],{},[950,55257,55258],{},"Yes."," This is the design's home ground: offline-first, encryption on the client, no backend required. In a browser (or a web app installed on the phone — see ",[1007,55261,55262],{"href":460},"PWA","), persist with ",[1007,55265,55266],{"href":359},[941,55267,9507],{},"; everything — encryption, queries, grants, history — happens on the device, and the only thing ever written to storage is ciphertext.",[937,55270,55271,55272,968,55274,55278],{},"What you give up without any server: nothing functional on the device, but there is no cross-device sync until you add a shared store or transport the devices can both reach (",[1007,55273,864],{"href":865},[1007,55275,55276],{"href":115},[941,55277,16057],{}," for same-network WebRTC).",[937,55280,55281,55282,55285,55286,55289,55290,55293,55294,1520],{},"Proven by: the ",[1007,55283,55284],{"href":560},"personal notebook recipe"," (core-only, no services), storage showcases ",[941,55287,55288],{},"01","–",[941,55291,55292],{},"03",", and the ",[1007,55295,55296],{"href":925},"ciphertext walk-through",[1022,55298,55300],{"id":55299},"can-i-use-it-server-only-behind-my-api","Can I use it server-only, behind my API?",[937,55302,55303,55305,55306,55310,55311,1261,55313,55315,55316,55320,55321,957],{},[950,55304,55258],{}," The hub runs on Node ≥22 with any store — ",[1007,55307,55308],{"href":384},[941,55309,7461],{}," on a volume, or ",[941,55312,5355],{},[941,55314,5358],{}," \u002F any extended store — and your API exposes whatever surface you choose (there is also ",[1007,55317,55318],{"href":264},[941,55319,35649],{},", proven by showcase ",[941,55322,55323],{},"47",[937,55325,55326,55327,55330,55331,55334,55335,55339,55340,55342],{},"The honest nuance: in this topology ",[1092,55328,55329],{},"your server"," opens the vault, so the server holds the keys in memory while unlocked. Zero-knowledge still holds ",[950,55332,55333],{},"toward the storage backend"," — Postgres or S3 sees only ciphertext and can't run queries — but the trust boundary is now your server process, not the end-user's device. For unattended server unlock, the ",[1007,55336,55337,9789],{"href":115},[941,55338,1139],{}," (env var, OS keychain, cloud KMS) seals the secret — and it is, by design, the one non-zero-knowledge family: a host you control ",[1092,55341,5105],{}," decrypt the slice it unseals.",[1022,55344,55346],{"id":55345},"serverless-only","Serverless only?",[937,55348,55349],{},[950,55350,55351],{},"Yes, with the key-handling done right.",[1111,55353,55354,55375],{},[1114,55355,55356,55359,55360,55363,55364,55366,55367,55370,55371,55374],{},[950,55357,55358],{},"AWS Lambda"," — proven end-to-end by the ",[1007,55361,55362],{"href":520},"AWS-KMS attestation recipe"," (a Lambda KMS-decrypts a sealed record from S3 and renders it), with ",[941,55365,33294],{}," as the sealing provider and a deployable CDK stack. Showcase ",[941,55368,55369],{},"97"," additionally proves the ",[1007,55372,55373],{"href":626},"credential broker"," minting short-lived STS credentials against real S3 + DynamoDB.",[1114,55376,55377,55379,55380,1261,55382,55384,55385,968,55388,957],{},[950,55378,13802],{}," — the extended ",[941,55381,9772],{},[941,55383,9760],{}," stores target Workers backends, and the showcase suite ships a workerd vitest config (",[941,55386,55387],{},"pnpm test:workers",[941,55389,55390],{},"@cloudflare\u002Fvitest-pool-workers",[937,55392,55393,55394,55396],{},"Two serverless-specific notes: derive-on-unlock costs real CPU (PBKDF2 at 600,000 iterations is ~200 ms by design), so prefer ",[941,55395,1139],{}," sealing over secret-per-invocation; and keep functions warm or scope them to flows where a cold unlock per request is acceptable.",[1022,55398,55400],{"id":55399},"in-a-container","In a container?",[937,55402,55403,55406,55407,55409,55410,1625,55412,55414,55415,55419],{},[950,55404,55405],{},"Yes — it's just Node."," Any image with Node ≥22 runs the hub. Persistence is the only decision: a mounted volume with ",[941,55408,7461],{},", or (usually better for containers) an external store like ",[941,55411,5355],{},[941,55413,5358],{}," so the container stays disposable. Unlock via ",[1007,55416,55417],{"href":115},[941,55418,13481],{}," or your orchestrator's secret store feeding the secret.",[1022,55421,55423],{"id":55422},"on-an-always-on-server","On an always-on server?",[937,55425,55426,55429,55430,55432,55433,55435,55436,1520],{},[950,55427,55428],{},"Yes"," — the classic deployment, and the one you'd pick when something must be continuously reachable: hosting the ",[1007,55431,625],{"href":626}," (whose host carries four standing obligations), serving as the shared ",[1007,55434,864],{"href":865}," rendezvous store other devices push\u002Fpull through, or running scheduled passes like ",[1007,55437,55438],{"href":765},"deferred numbering",[1022,55440,2621],{"id":2620},[1111,55442,55443,55448,55454],{},[1114,55444,55445,55447],{},[1007,55446,576],{"href":577}," — execute the proofs cited above yourself.",[1114,55449,55450,55453],{},[1007,55451,55452],{"href":460},"Mobile & PWA"," — the device-side half of this question.",[1114,55455,55456,55458],{},[1007,55457,463],{"href":464}," — what each topology's trust boundary means.",{"title":1037,"searchDepth":1038,"depth":1038,"links":55460},[55461,55462,55463,55464,55465,55466],{"id":55252,"depth":1041,"text":55253},{"id":55299,"depth":1041,"text":55300},{"id":55345,"depth":1041,"text":55346},{"id":55399,"depth":1041,"text":55400},{"id":55422,"depth":1041,"text":55423},{"id":2620,"depth":1041,"text":2621},"Topology answers with proofs: device-only with no server, server-only behind an API, serverless, containers, and always-on servers.",{},{"title":455,"description":55467},"NEqWqUzOLay_i-ACoR-h8pkARFtrxxd5tmFAthNQ5VU",{"id":55472,"title":459,"access":12,"apiToc":929,"body":55473,"category":1042,"description":55698,"extension":1044,"meta":55699,"navigation":1042,"nest":1042,"path":460,"seo":55700,"sinceVersion":1042,"status":1049,"stem":461,"subtitle":1042,"tier":1050,"tocExternal":1042,"__hash__":55701},"docs\u002Ffaq\u002F2.mobile-and-pwa.md",{"type":931,"value":55474,"toc":55693},[55475,55478,55501,55553,55557,55574,55584,55602,55611,55617,55634,55638,55650,55673,55679,55681],[934,55476,459],{"id":55477},"native-mobile-pwa",[937,55479,55480,55481,953,55484,55486,55487,55490,55491,55494,55495,55497,55498,2311],{},"noy-db's runtime requirements are deliberately small: a JavaScript engine with the ",[950,55482,55483],{},"Web Crypto API",[941,55485,2506],{},") and, for browser persistence, ",[950,55488,55489],{},"IndexedDB",". Which mobile paths satisfy that today, honestly labeled — ",[1092,55492,55493],{},"proven"," (covered by the test suite), ",[1092,55496,11192],{}," (same runtime, not covered), or ",[1092,55499,55500],{},"not supported",[1338,55502,55503,55512],{},[1341,55504,55505],{},[1344,55506,55507,55510],{},[1347,55508,55509],{},"Path",[1347,55511,48731],{},[1357,55513,55514,55524,55534,55545],{},[1344,55515,55516,55519],{},[1362,55517,55518],{},"PWA \u002F browser app",[1362,55520,55163,55521],{},[950,55522,55523],{},"Proven",[1344,55525,55526,55529],{},[1362,55527,55528],{},"WebView wrapper (Capacitor, Tauri mobile, …)",[1362,55530,1649,55531],{},[950,55532,55533],{},"Expected, untested",[1344,55535,55536,55539],{},[1362,55537,55538],{},"React Native \u002F Expo",[1362,55540,55541,55542],{},"❌ ",[950,55543,55544],{},"Not supported today",[1344,55546,55547,55550],{},[1362,55548,55549],{},"Fully native (Swift \u002F Kotlin)",[1362,55551,55552],{},"❌ Not a target — noy-db is a JS library",[1022,55554,55556],{"id":55555},"can-i-build-a-pwa","Can I build a PWA?",[937,55558,55559,55562,55563,55567,55568,55570,55571,55573],{},[950,55560,55561],{},"Yes — this is the strongest mobile story."," A PWA is a browser app, and the browser is a first-class noy-db runtime: persist with ",[1007,55564,55565],{"href":359},[941,55566,9507],{},", and every envelope in IndexedDB is ciphertext (open DevTools → Application → IndexedDB and check — the same experiment as ",[1007,55569,924],{"href":925},"). Offline-first is the default behavior, not a mode: reads and writes need no network, and ",[1007,55572,864],{"href":865}," catches up when you're back online.",[937,55575,10993,55576,55578,55579,55581,55582,1520],{},[941,55577,38549],{}," the shell plumbing has a package of its own: ",[1007,55580,38499],{"href":252}," covers\nstorage persistence with an explicit grant\u002Fdeny signal, install-prompt capture, display-mode\ndetection, and an online watcher shaped for the sync engine. It holds no keys and sees no\nplaintext, so it adds nothing to the ",[1007,55583,13661],{"href":464},[937,55585,55586,55589,55590,55592,55593,55595,55596,55598,55599,55601],{},[950,55587,55588],{},"Eviction is the risk worth designing for."," iOS ITP evicts script-writable storage — IndexedDB\nincluded — of ",[1092,55591,39519],{}," web content after about seven days without interaction. Installed\nhome-screen apps are safer, and ",[941,55594,39523],{}," exempts an origin where granted.\n",[941,55597,39611],{}," probes the store before the vault opens and ",[950,55600,14135],{},": if the partition\nwas evicted, the app routes into re-enrollment instead of presenting a silent empty vault as truth.",[937,55603,55604,55605,55607,55608,55610],{},"The service worker stays app-level, and ",[941,55606,38499],{}," deliberately ships ",[950,55609,11313],{}," service-worker runtime —\nonly a recipe you copy and version yourself, because a worker that cached vault responses would\ncreate a second, unmanaged copy of ciphertext outside the store contract.",[937,55612,55613,55614,55616],{},"Opening the same app from LINE is a related but separate shell: see\n",[1007,55615,39924],{"href":236},", including why storage never travels between the two.",[937,55618,55619,55620,55622,55623,55289,55626,55629,55630,1520],{},"Proven by: storage showcase ",[941,55621,55292],{}," (browser-idb), the framework binding showcases (",[941,55624,55625],{},"37",[941,55627,55628],{},"49","), and the multi-tab coordination transport ",[1007,55631,55632],{"href":115},[941,55633,16061],{},[1022,55635,55637],{"id":55636},"can-i-build-a-native-ios-or-android-app-with-noy-db","Can I build a native iOS or Android app with noy-db?",[937,55639,55640,55643,55644,55646,55647,55649],{},[950,55641,55642],{},"Via a WebView wrapper: expected to work, not yet covered by the test suite."," Capacitor-style apps embed a real browser engine (WKWebView \u002F Android WebView), which provides the same Web Crypto and IndexedDB the PWA path uses — so the runtime contract is satisfied. It is labeled ",[1092,55645,11192],{}," rather than ",[1092,55648,55493],{}," because no showcase currently runs inside a wrapper shell; verify in your target WebView before committing to it.",[937,55651,55652,55655,55656,55658,55659,55661,55662,55666,55667,55672],{},[950,55653,55654],{},"React Native \u002F Expo: not supported today."," This is a plain gap, not a caveat: RN's JS engines don't ship ",[941,55657,2506],{}," or IndexedDB, no ",[941,55660,1779],{}," store targets React Native storage, and nothing in the hub or the showcase suite exercises RN. Making it work would take two parts — a Web Crypto implementation backed by native crypto, and an RN storage adapter written against the published ",[1007,55663,55664],{"href":115},[941,55665,3299],{}," store contract (the same seam all seventeen extended stores bind). If this path matters to you, say so on the ",[1007,55668,55671],{"href":55669,"rel":55670},"https:\u002F\u002Fgithub.com\u002Fnoy-db\u002Fcore\u002Fissues",[3371],"issue tracker"," — it's the kind of gap that gets prioritized by demand.",[937,55674,55675,55678],{},[950,55676,55677],{},"Fully native apps"," (Swift\u002FKotlin, no JS runtime) are out of scope: noy-db is a TypeScript library, not a portable core with FFI bindings.",[1022,55680,2621],{"id":2620},[1111,55682,55683,55688],{},[1114,55684,55685,55687],{},[1007,55686,455],{"href":456}," — the server-side half of the topology question.",[1114,55689,55690,55692],{},[1007,55691,6891],{"href":51}," — the persistence contract behind every path above.",{"title":1037,"searchDepth":1038,"depth":1038,"links":55694},[55695,55696,55697],{"id":55555,"depth":1041,"text":55556},{"id":55636,"depth":1041,"text":55637},{"id":2620,"depth":1041,"text":2621},"Verified mobile support: PWAs and browser apps are the proven path; WebView wrappers are expected but untested; React Native is not supported today.",{},{"title":459,"description":55698},"vh7_bh82G8opbqtQ-_5gB9gl8v0zxupswYUj4ioQL5g",{"id":55703,"title":463,"access":12,"apiToc":929,"body":55704,"category":1042,"description":56144,"extension":1044,"meta":56145,"navigation":1042,"nest":1042,"path":464,"seo":56146,"sinceVersion":1042,"status":1049,"stem":465,"subtitle":1042,"tier":1050,"tocExternal":1042,"__hash__":56147},"docs\u002Ffaq\u002F3.threat-model.md",{"type":931,"value":55705,"toc":56131},[55706,55710,55719,55723,55735,55760,55764,55767,55771,55777,55787,55791,55802,55808,55871,55875,55881,55901,55904,55999,56003,56006,56060,56064,56081,56085,56088,56091,56101,56103],[934,55707,55709],{"id":55708},"threat-model-what-noy-db-protects-against-and-what-it-cant","Threat model — what noy-db protects against, and what it can't",[937,55711,55712,55713,55715,55716,55718],{},"A zero-knowledge claim is only useful if its limits are stated as clearly as its protections. This page walks the common attacker positions; each section says what holds, what does ",[950,55714,1004],{},", and which mitigation applies. The cryptographic facts are documented on the ",[1007,55717,1059],{"href":66}," page: AES-256-GCM per record (random 96-bit IV per operation), PBKDF2-SHA256 at 600,000 iterations for key derivation, AES-KW key wrapping, all through the platform's Web Crypto API with zero npm crypto dependencies.",[1022,55720,55722],{"id":55721},"the-storage-or-server-is-compromised","The storage or server is compromised",[937,55724,55725,55728,55729,55732,55733,1520],{},[950,55726,55727],{},"This is the design case: record content is protected in full."," A store — disk, IndexedDB, cloud bucket, database — holds only ciphertext envelopes; it cannot read records, cannot run queries (queries execute inside the hub after decryption), and a tampered envelope fails AES-GCM authentication with a ",[941,55730,55731],{},"TAMPERED"," error rather than decrypting to something wrong. A cloud admin, a stolen backup, a subpoenaed bucket: all get the same ciphertext. Verify it yourself in one minute: ",[1007,55734,924],{"href":925},[937,55736,55737,55738,55740,55741,13635,55743,55745,55746,953,55749,55752,55753,953,55757,957],{},"What it does ",[950,55739,1004],{}," hide: envelope metadata the store needs to operate — record count, ids, sizes, write timestamps, version numbers. An observer of the store learns ",[1092,55742,13634],{},[1092,55744,13638],{}," — unless a collection opts a field into equality search, a separately-acknowledged trade that reveals which records share a value for that field without revealing the value itself. Two forms exist, each behind its own explicit gate: ",[1007,55747,55748],{"href":66},"deterministic fields",[941,55750,55751],{},"acknowledgeDeterministicRisk","), and classified fields marked ",[1007,55754,55755],{"href":634},[941,55756,13650],{},[941,55758,55759],{},"acknowledgeEquatableRisk",[1022,55761,55763],{"id":55762},"a-network-observer","A network observer",[937,55765,55766],{},"Envelopes cross the wire in the same encrypted form they're stored in, so a passive observer of your traffic to a store or sync target reads nothing — independent of (and additional to) transport TLS. The metadata caveat above applies equally here.",[1022,55768,55770],{"id":55769},"the-device-is-stolen-vault-locked","The device is stolen (vault locked)",[937,55772,55773,55774,55776],{},"The at-rest protection is the secret-derived key hierarchy: PBKDF2 at 600,000 iterations makes each guess cost ~200 ms, which makes brute force infeasible against a 12+ character or 4+ word diceware secret — and that quality is on you; a weak secret is the single easiest way to lose the whole property. The keyring on disk holds only ",[1092,55775,9875],{}," keys.",[937,55778,55779,55780,55783,55784,55786],{},"If the vault was ",[950,55781,55782],{},"unlocked"," when the device was taken, the session's keys are in process memory until ",[941,55785,13331],{}," wipes them — see the next section, because a live unlocked session is the real attack surface.",[1022,55788,55790],{"id":55789},"a-malicious-browser-extension-or-compromised-client","A malicious browser extension, or compromised client",[937,55792,55793,55794,55797,55798,55801],{},"The question evaluators ask most, so here is the honest answer first: ",[950,55795,55796],{},"noy-db cannot protect an unlocked session from code that gets into the page."," Any XSS or malicious dependency runs in the session's own JS context and reads what the session reads. A malicious extension's content scripts run in an isolated world — they can't reach the page's JS memory directly, but they can read and modify everything the page ",[1092,55799,55800],{},"renders",", capture what's typed (including a secret), and, where the page's CSP permits script injection, escalate into the page context itself. In practice, treat an attacker inside the browser profile as able to observe the unlocked session. No client-side encryption library can prevent this; one that claims to should worry you.",[937,55803,55804,55805,2311],{},"What noy-db provides is a set of ways to ",[950,55806,55807],{},"shrink the window and the blast radius",[1111,55809,55810,55819,55839,55852,55865],{},[1114,55811,55812,55818],{},[950,55813,55814],{},[1007,55815,55816],{"href":338},[941,55817,15944],{}," — unlock with a platform passkey instead of a typed secret: there is nothing to keylog, and the credential lives in the platform secure enclave; the wrapped key material is encrypted under the WebAuthn credential and never appears as a typeable secret in the page.",[1114,55820,55821,964,55824,55828,55829,55831,55832,55835,55836,55838],{},[950,55822,55823],{},"Session hygiene",[1007,55825,55826],{"href":841},[941,55827,21321],{}," issues session tokens that expire on their TTL, and defines a ",[941,55830,20690],{}," (idle\u002Fabsolute timeouts, re-auth for sensitive operations) that your app enforces by driving a ",[941,55833,55834],{},"createEnforcer()"," at its own call sites — the policy is not applied automatically. ",[941,55837,13331],{}," wipes the in-memory keys (the memory-dump mitigation from the security policy). A short-lived session means a shorter window for anything watching it.",[1114,55840,55841,55844,55845,55847,55848,55851],{},[950,55842,55843],{},"Scoped grants"," — in a multi-user vault (",[1007,55846,868],{"href":869},"), stealing a viewer session yields read-only access to what that keyring covers, not the vault; ",[1007,55849,55850],{"href":873},"tiered records"," keep higher-tier fields sealed even inside an unlocked session until elevation.",[1114,55853,55854,964,55857,55861,55862,957],{},[950,55855,55856],{},"Duress and lockout signals",[1007,55858,55859],{"href":330},[941,55860,15984],{}," adds lockout and duress behaviors around the unlock ceremony (showcase ",[941,55863,55864],{},"31",[1114,55866,55867,55870],{},[950,55868,55869],{},"App-level hygiene still matters"," — a strict CSP, dependency auditing, and not embedding untrusted scripts are your app's job; noy-db runs inside whatever page you give it.",[1022,55872,55874],{"id":55873},"a-phishing-page-imitating-the-unlock-screen","A phishing page imitating the unlock screen",[937,55876,55877,55878,55880],{},"Tier 2 already resists this: a passkey is bound to an origin, so a look-alike page cannot make it sign. The exposed surface is ",[950,55879,18463],{}," — a phrase the user types into a field, on the rare occasions the app asks for it. A page that reproduces your unlock screen convincingly enough gets that phrase, and with it the vault.",[937,55882,55883,953,55887,55890,55891,55893,55894,55897,55898,55900],{},[1007,55884,55886],{"href":55885},"\u002Fservices\u002Fsession-tiers#tier-1-echo-secret","Echo mode",[941,55888,55889],{},"secretMode: 'echo'",") is the answer to that specific attack. The tier-1 secret becomes one sentence enrolled as three parts — the user types the ",[950,55892,47651],{},", the vault reveals the ",[950,55895,55896],{},"echo"," (the middle of the user's own sentence), the user recognizes it and types the ",[950,55899,8364],{},". The vault has to prove it holds the secret before the user finishes handing over theirs.",[937,55902,55903],{},"Grading the attacker by what they actually hold:",[1338,55905,55906,55919],{},[1341,55907,55908],{},[1344,55909,55910,55913,55916],{},[1347,55911,55912],{},"Attacker",[1347,55914,55915],{},"Holds",[1347,55917,55918],{},"Outcome",[1357,55920,55921,55937,55964,55980],{},[1344,55922,55923,55928,55931],{},[1362,55924,55925],{},[950,55926,55927],{},"A — static clone",[1362,55929,55930],{},"nothing but your look-alike page",[1362,55932,55933,55936],{},[950,55934,55935],{},"Defeated."," It cannot produce the echo, and the user is told to expect one.",[1344,55938,55939,55944,55947],{},[1362,55940,55941],{},[950,55942,55943],{},"B — file-holder",[1362,55945,55946],{},"a copy of the keyring or pod file, including any reveal blob it carries",[1362,55948,55949,55956,55957,55960,55961,55963],{},[950,55950,55951,55952,55955],{},"Defeated only by a ",[941,55953,55954],{},"sealed"," reveal",", whose blob needs the enrolling device's key store to open. A ",[941,55958,55959],{},"portable"," reveal is decryptable by anyone who holds the file ",[1092,55962,14128],{}," knows the prompt — that is a deliberate trade for a reveal that travels with a shared pod.",[1344,55965,55966,55971,55974],{},[1362,55967,55968],{},[950,55969,55970],{},"C — flow substitution",[1362,55972,55973],{},"nothing; asks the user to \"enter your full secret instead\"",[1362,55975,55976,55979],{},[950,55977,55978],{},"Defeated structurally."," No single typed string can ever derive an echo vault's key, so no legitimate single-field flow exists to imitate. The key-derivation input is a length-prefixed encoding of the three parts starting with a byte that text encoding can never produce, and the key-derivation dispatch refuses a plain string on an echo keyring outright.",[1344,55981,55982,55987,55993],{},[1362,55983,55984],{},[950,55985,55986],{},"Malicious player",[1362,55988,55989,55990,55992],{},"the pod ",[1092,55991,14128],{}," the user interface",[1362,55994,55995,55998],{},[950,55996,55997],{},"Out of scope."," Nothing survives typing a secret into hostile code. Echo mode targets remote phishing pages and honest-but-lazy viewers, not an application the user has been persuaded to trust.",[1591,56000,56002],{"id":56001},"what-echo-mode-does-not-hide","What echo mode does not hide",[937,56004,56005],{},"Stated plainly, because they are the price of the design:",[1111,56007,56008,56029,56035,56041,56050],{},[1114,56009,56010,56013,56014,56017,56018,56020,56021,56024,56025,56028],{},[950,56011,56012],{},"The prompt and the echo are each an offline guessing oracle for a file-holder."," The keyring stores a verifier for both (they are what gate the reveal, and what verify a typed echo on the degraded path). Anyone holding the file can test candidate prompts and candidate echoes offline — at 600,000 PBKDF2 iterations per guess, but without limit. The prompt therefore carries its own minimum-length floor. The ",[950,56015,56016],{},"echo has no entropy floor at all",": it is a free-form human phrase, so its resistance is whatever the user gave it. \"Attacker-B resistance\" for a ",[941,56019,55954],{}," reveal means B cannot ",[1092,56022,56023],{},"read"," the echo — never that B cannot ",[1092,56026,56027],{},"grind"," for it.",[1114,56030,56031,56034],{},[950,56032,56033],{},"A sealed reveal blob is unpadded, so its length discloses the echo's byte length."," Not the echo, but its size.",[1114,56036,56037,56040],{},[950,56038,56039],{},"The revealed echo is on-screen material"," — shoulder-surfable, and captured by anything that can read what the page renders (see the previous section). Masking is a user-interface mitigation, hinted at by the keyring's optional mask hint; it is not a cryptographic one.",[1114,56042,56043,56046,56047,56049],{},[950,56044,56045],{},"Recognition checks fail when they become routine."," This is why the same idea failed as the \"site recognition image\" of the 2000s: shown a familiar image on every login, users stop noticing when it is missing. Echo mode survives that only because tier 1 ",[1092,56048,3082],{}," rare — daily use runs on tier 2 and tier 3, so the ceremony fires on high-attention events (enrollment, recovery, a new device) and never becomes background noise. An application that puts the echo ceremony in the daily path throws away the property it was buying.",[1114,56051,56052,56055,56056,56059],{},[950,56053,56054],{},"The content of the secret is the user's job, and it is the part most often got wrong."," An echo drawn from public biography — a school, a nickname, a birthplace — is guessable by an acquaintance and often by a search engine, which hands attacker B the oracle for free. The ",[1007,56057,56058],{"href":540},"enrollment recipe"," is the guidance; the hub cannot validate it, because no validator can tell a private memory from a published fact.",[1022,56061,56063],{"id":56062},"reverse-engineering-the-client-code","Reverse engineering the client code",[937,56065,56066,56069,56070,56072,56073,56077,56078,56080],{},[950,56067,56068],{},"Reading the code reveals no secret — by design, there is nothing secret in it."," noy-db's source is readable, its algorithms are the standard ones named above, and its security reduces entirely to key material, which the code never contains: keys are derived from the user's secret, held by the platform authenticator (",[941,56071,1787],{},"), or sealed at a host you control (",[1007,56074,56075],{"href":115},[941,56076,1139],{}," — the one family that is deliberately ",[1092,56079,1004],{}," zero-knowledge: a host you run can decrypt the scoped slice it unseals, which is its purpose). Decompiling the app tells an attacker how the lock works, not where the key is. Kerckhoffs' principle, applied.",[1022,56082,56084],{"id":56083},"a-revoked-user","A revoked user",[937,56086,56087],{},"Revocation always re-encrypts under new data keys, so a revoked member's cached key material stops opening anything new. What no system can do: un-read what they already read while authorized — treat revocation as stopping future access, not as recalling the past.",[1022,56089,16104],{"id":56090},"reporting-a-vulnerability",[937,56092,56093,56094,953,56098,957],{},"Never a public issue: report it privately through GitHub's private vulnerability reporting on ",[1007,56095,37439],{"href":56096,"rel":56097},"https:\u002F\u002Fgithub.com\u002Fnoy-db\u002Fcore\u002Fsecurity",[3371],[950,56099,56100],{},"Report a vulnerability",[1022,56102,2621],{"id":2620},[1111,56104,56105,56111,56116,56121,56126],{},[1114,56106,56107,56110],{},[1007,56108,56109],{"href":66},"Core 02 — Client (Enclave)"," — the key hierarchy behind every claim above.",[1114,56112,56113,56115],{},[1007,56114,844],{"href":845}," — the three-tier ladder, the three tier-1 modes, and the policy gates that guard sensitive actions.",[1114,56117,56118,56120],{},[1007,56119,539],{"href":540}," — choosing three parts an acquaintance cannot guess.",[1114,56122,56123,56125],{},[1007,56124,924],{"href":925}," — the one-minute verification.",[1114,56127,56128,56130],{},[1007,56129,455],{"href":456}," — how the trust boundary moves per topology.",{"title":1037,"searchDepth":1038,"depth":1038,"links":56132},[56133,56134,56135,56136,56137,56140,56141,56142,56143],{"id":55721,"depth":1041,"text":55722},{"id":55762,"depth":1041,"text":55763},{"id":55769,"depth":1041,"text":55770},{"id":55789,"depth":1041,"text":55790},{"id":55873,"depth":1041,"text":55874,"children":56138},[56139],{"id":56001,"depth":1038,"text":56002},{"id":56062,"depth":1041,"text":56063},{"id":56083,"depth":1041,"text":56084},{"id":56090,"depth":1041,"text":16104},{"id":2620,"depth":1041,"text":2621},"What noy-db protects against and what it cannot — by attacker position, from storage compromise to malicious extensions, with limits stated first-class.",{},{"title":463,"description":56144},"PkybgzgHWQsAB2_5w_V45HNzU6WHxkrVd4y8i9giBW8",{"id":56149,"title":467,"access":12,"apiToc":929,"body":56150,"category":1042,"description":56383,"extension":1044,"meta":56384,"navigation":1042,"nest":1042,"path":468,"seo":56385,"sinceVersion":1042,"status":1049,"stem":469,"subtitle":1042,"tier":1050,"tocExternal":1042,"__hash__":56386},"docs\u002Ffaq\u002F4.community-and-ecosystem.md",{"type":931,"value":56151,"toc":56377},[56152,56155,56159,56162,56241,56249,56256,56260,56269,56339,56343,56362,56364],[934,56153,467],{"id":56154},"community-ecosystem",[1022,56156,56158],{"id":56157},"where-do-i-ask-questions-or-report-bugs","Where do I ask questions or report bugs?",[937,56160,56161],{},"GitHub issue trackers, per repository — there is no forum, Discord, or discussions board today, and this page will say so until that changes:",[1338,56163,56164,56174],{},[1341,56165,56166],{},[1344,56167,56168,56171],{},[1347,56169,56170],{},"About",[1347,56172,56173],{},"Where",[1357,56175,56176,56201,56216,56233],{},[1344,56177,56178,56195],{},[1362,56179,56180,56181,56183,56184,1625,56186,1625,56188,1625,56190,1625,56192,56194],{},"The core library, ",[941,56182,943],{}," services, ",[941,56185,1264],{},[941,56187,1270],{},[941,56189,1276],{},[941,56191,1273],{},[941,56193,1267],{}," satellites",[1362,56196,56197],{},[1007,56198,56200],{"href":55669,"rel":56199},[3371],"noy-db\u002Fcore issues",[1344,56202,56203,56209],{},[1362,56204,56205,56206,56208],{},"The extended ",[941,56207,1779],{}," stores (postgres, aws, cloudflare, supabase, …)",[1362,56210,56211],{},[1007,56212,56215],{"href":56213,"rel":56214},"https:\u002F\u002Fgithub.com\u002Fnoy-db\u002Fto\u002Fissues",[3371],"noy-db\u002Fto issues",[1344,56217,56218,56226],{},[1362,56219,56220,56221,968,56223,1381],{},"The UI family (",[941,56222,3936],{},[941,56224,56225],{},"@noy-db\u002Fui-nuxt",[1362,56227,56228],{},[1007,56229,56232],{"href":56230,"rel":56231},"https:\u002F\u002Fgithub.com\u002Fnoy-db\u002Fui\u002Fissues",[3371],"noy-db\u002Fui issues",[1344,56234,56235,56238],{},[1362,56236,56237],{},"This documentation",[1362,56239,56240],{},"the noy-db tracker",[937,56242,56243,56246,56247,957],{},[950,56244,56245],{},"Security reports are the exception",": never a public issue — use GitHub's private vulnerability reporting on the noy-db repository (see the ",[1007,56248,13661],{"href":16103},[937,56250,56251,56252,56255],{},"Questions are welcome as issues; recurring ones become ",[1007,56253,56254],{"href":448},"FAQ"," entries.",[1022,56257,56259],{"id":56258},"is-there-a-marketplace","Is there a marketplace?",[937,56261,56262,56265,56266,56268],{},[950,56263,56264],{},"No marketplace — and here is the more useful answer: the extension points are published contracts, so you don't need one."," Every satellite family binds exactly one frozen subpath of ",[941,56267,1086],{},", which means anyone can build and ship their own adapter without noy-db's cooperation:",[1111,56270,56271,56292,56316],{},[1114,56272,56273,1169,56276,56280,56281,56283,56284,56286,56287,56291],{},[950,56274,56275],{},"More store adapters",[1007,56277,56278,5141],{"href":115},[941,56279,1779],{}," already spans 22 stores across two repos (3 essential in ",[941,56282,1072],{},", 19 extended in ",[941,56285,5351],{},": cloud object stores, SQL, serverless databases, remote filesystems). All bind the same published ",[1007,56288,56289],{"href":30},[941,56290,3299],{}," contract.",[1114,56293,56294,56297,56298,56300,56301,2116,56303,56305,56306,56308,56309,56315],{},[950,56295,56296],{},"Write your own store"," — implement ",[941,56299,3154],{}," (and optionally ",[941,56302,49533],{},[941,56304,3299],{}," against a ranged ",[941,56307,1086],{}," peer dependency. The ",[1007,56310,56312,56314],{"href":6965,"rel":56311},[3371],[941,56313,5351],{}," repository"," is the working reference: nineteen adapters built exactly this way, each validated by the same conformance tests yours can run. Your package never sees plaintext — the contract hands you ciphertext envelopes, which is why a third-party store requires no trust review of its author.",[1114,56317,56318,56321,56322,56324,56325,56327,56328,56330,56331,56333,56334,13571,56336,56338],{},[950,56319,56320],{},"Other plugin kinds"," — the same pattern per family: unlock methods bind ",[941,56323,3333],{},", export formats ",[941,56326,3339],{},", framework bindings ",[941,56329,3345],{},", sync transports ",[941,56332,3342],{},", sealing providers ",[941,56335,3336],{},[1007,56337,4824],{"href":30}," page defines each seam.",[1022,56340,56342],{"id":56341},"data-shape-templates","Data-shape templates?",[937,56344,56345,56346,56349,56350,56353,56354,56357,56358,56361],{},"The recipes double as today's shape gallery — each ships a complete typed schema you can copy: invoices and clients (",[1007,56347,56348],{"href":508},"accounting","), notes (",[1007,56351,56352],{"href":560},"personal notebook","), a per-user preferences envelope (",[1007,56355,56356],{"href":594},"user preferences","), MIME email records (",[1007,56359,56360],{"href":544},"email archive","). A standalone template gallery is planned but does not exist yet; until it does, start from the recipe closest to your shape.",[1022,56363,2621],{"id":2620},[1111,56365,56366,56372],{},[1114,56367,56368,56371],{},[1007,56369,56370],{"href":448},"FAQ overview"," — the other evaluator questions.",[1114,56373,56374,56376],{},[1007,56375,499],{"href":500}," — including the \"compose your own\" path when nothing matches.",{"title":1037,"searchDepth":1038,"depth":1038,"links":56378},[56379,56380,56381,56382],{"id":56157,"depth":1041,"text":56158},{"id":56258,"depth":1041,"text":56259},{"id":56341,"depth":1041,"text":56342},{"id":2620,"depth":1041,"text":2621},"Where to ask questions and report bugs, how security reports work, and the honest marketplace answer: a published store contract anyone can build against.",{},{"title":467,"description":56383},"1t0jZk33efsLdS6hAFY2eiuNhjPugWQISOocIpr9bx0",{"id":56388,"title":471,"access":12,"apiToc":929,"body":56389,"category":1042,"description":56490,"extension":1044,"meta":56491,"navigation":1042,"nest":1042,"path":472,"seo":56492,"sinceVersion":1042,"status":1049,"stem":473,"subtitle":1042,"tier":1050,"tocExternal":1042,"__hash__":56493},"docs\u002Ffaq\u002F5.licensing.md",{"type":931,"value":56390,"toc":56485},[56391,56394,56398,56437,56440,56455,56459,56471,56475],[934,56392,471],{"id":56393},"licensing",[1022,56395,56397],{"id":56396},"which-licence-does-noy-db-ship-under","Which licence does noy-db ship under?",[937,56399,56400,56403,56404,56407,56408,56410,56411,56413,56414,56416,56417,968,56419,1707,56421,968,56423,968,56425,56428,56429,56432,56433,56436],{},[950,56401,56402],{},"Apache-2.0",", from ",[941,56405,56406],{},"0.8.0-pre.0"," on. Every ",[941,56409,56406],{}," tarball across the\npublished ",[941,56412,5129],{}," families (",[941,56415,5188],{}," and the core packages, ",[941,56418,1779],{},[941,56420,1791],{},[941,56422,1787],{},[941,56424,1139],{},[941,56426,56427],{},"ui",") carries a ",[941,56430,56431],{},"LICENSE"," and a ",[941,56434,56435],{},"NOTICE"," file.",[937,56438,56439],{},"Relicensed from MIT to Apache-2.0 from this version on. Earlier versions remain MIT.",[937,56441,56442,56443,56445,56446,56448,56449,56451,56452,56454],{},"A licence change is a version event, not a retro-edit: ",[941,56444,45411],{}," — the last stable\nrelease — and every earlier version stay MIT exactly as published. Nothing already on\nnpm was altered. If you pin ",[941,56447,45411],{},", you hold MIT code; the moment you move to the\n",[941,56450,56406],{}," line you hold Apache-2.0 code, and the ",[941,56453,56431],{}," inside the installed\npackage is the authoritative text for the version you actually resolved.",[1022,56456,56458],{"id":56457},"trademark","Trademark",[937,56460,56461,56462,56465,56466,1520],{},"The names and marks are governed separately from the code licence. The policy is\npublished in the family's ",[941,56463,56464],{},".github"," repository:\n",[1007,56467,56470],{"href":56468,"rel":56469},"https:\u002F\u002Fgithub.com\u002Fnoy-db\u002F.github\u002Fblob\u002Fmain\u002FTRADEMARK.md",[3371],"TRADEMARK.md",[1022,56472,56474],{"id":56473},"which-line-does-this-site-document","Which line does this site document?",[937,56476,56477,56478,56480,56481,56484],{},"The footer of every page names the rail and version this site documents. The\nlicence statement on this page applies to that line; for any other version, read\nthe ",[941,56479,56431],{}," file shipped in its tarball (",[941,56482,56483],{},"npm pack @noy-db\u002Fhub@\u003Cversion> --dry-run","\nlists it).",{"title":1037,"searchDepth":1038,"depth":1038,"links":56486},[56487,56488,56489],{"id":56396,"depth":1041,"text":56397},{"id":56457,"depth":1041,"text":56458},{"id":56473,"depth":1041,"text":56474},"Which licence each published noy-db version carries, where the trademark policy lives, and what the pre-release line means for the licence you are reading.",{},{"title":471,"description":56490},"B1JOvtEky3AadSZ5fxn0h0USRK8c2qmJQ5DYGhf7X-M",{"id":56495,"title":56254,"access":12,"apiToc":929,"body":56496,"category":1042,"description":56618,"extension":1044,"meta":56619,"navigation":56620,"nest":1042,"path":448,"seo":56621,"sinceVersion":1042,"status":1049,"stem":452,"subtitle":1042,"tier":1050,"tocExternal":1042,"__hash__":56622},"docs\u002Ffaq\u002Findex.md",{"type":931,"value":56497,"toc":56611},[56498,56500,56503,56507,56534,56538,56550,56554,56573,56575,56589,56591,56604],[934,56499,56254],{"id":449},[937,56501,56502],{},"Direct answers to the questions developers evaluate noy-db with. Every \"yes\" on these pages links a runnable proof; every \"no\" or \"partially\" says what's missing — the same rule the rest of this site follows.",[1022,56504,56506],{"id":56505},"where-can-it-run","Where can it run?",[1111,56508,56509,56514,56519,56524,56529],{},[1114,56510,56511],{},[1007,56512,55253],{"href":56513},"\u002Ffaq\u002Fwhere-can-noy-db-run#can-it-run-only-on-my-iphone-with-no-server",[1114,56515,56516],{},[1007,56517,55300],{"href":56518},"\u002Ffaq\u002Fwhere-can-noy-db-run#can-i-use-it-server-only-behind-my-api",[1114,56520,56521],{},[1007,56522,55346],{"href":56523},"\u002Ffaq\u002Fwhere-can-noy-db-run#serverless-only",[1114,56525,56526],{},[1007,56527,55400],{"href":56528},"\u002Ffaq\u002Fwhere-can-noy-db-run#in-a-container",[1114,56530,56531],{},[1007,56532,55423],{"href":56533},"\u002Ffaq\u002Fwhere-can-noy-db-run#on-an-always-on-server",[1022,56535,56537],{"id":56536},"apps","Apps",[1111,56539,56540,56545],{},[1114,56541,56542],{},[1007,56543,55637],{"href":56544},"\u002Ffaq\u002Fmobile-and-pwa#can-i-build-a-native-ios-or-android-app-with-noy-db",[1114,56546,56547],{},[1007,56548,55556],{"href":56549},"\u002Ffaq\u002Fmobile-and-pwa#can-i-build-a-pwa",[1022,56551,56553],{"id":56552},"security","Security",[1111,56555,56556,56562,56568],{},[1114,56557,56558],{},[1007,56559,56561],{"href":56560},"\u002Ffaq\u002Fthreat-model#a-malicious-browser-extension-or-compromised-client","Can a browser extension steal the secret?",[1114,56563,56564],{},[1007,56565,56567],{"href":56566},"\u002Ffaq\u002Fthreat-model#reverse-engineering-the-client-code","Can the software be reverse-engineered?",[1114,56569,56570],{},[1007,56571,56572],{"href":464},"What does zero-knowledge actually protect against?",[1022,56574,467],{"id":56154},[1111,56576,56577,56583],{},[1114,56578,56579],{},[1007,56580,56582],{"href":56581},"\u002Ffaq\u002Fcommunity-and-ecosystem#where-do-i-ask-questions-or-report-bugs","Is there a community page?",[1114,56584,56585],{},[1007,56586,56588],{"href":56587},"\u002Ffaq\u002Fcommunity-and-ecosystem#is-there-a-marketplace","Is there a marketplace — data shapes, more store adapters, plugins?",[1022,56590,471],{"id":56393},[1111,56592,56593,56598],{},[1114,56594,56595],{},[1007,56596,56397],{"href":56597},"\u002Ffaq\u002Flicensing#which-licence-does-noy-db-ship-under",[1114,56599,56600],{},[1007,56601,56603],{"href":56602},"\u002Ffaq\u002Flicensing#trademark","Where is the trademark policy?",[937,56605,56606,56607,1520],{},"Recurring evaluator questions become entries here — if yours is missing, ask it as an issue on the ",[1007,56608,56610],{"href":55669,"rel":56609},[3371],"noy-db tracker",{"title":1037,"searchDepth":1038,"depth":1038,"links":56612},[56613,56614,56615,56616,56617],{"id":56505,"depth":1041,"text":56506},{"id":56536,"depth":1041,"text":56537},{"id":56552,"depth":1041,"text":56553},{"id":56154,"depth":1041,"text":467},{"id":56393,"depth":1041,"text":471},"The questions evaluating developers actually arrive with — where noy-db runs, mobile and PWA support, the threat model, community, and the adapter ecosystem.",{},{"title":10,"icon":453},{"title":56254,"description":56618},"SYULU5_DGtHUoktOOoCnQxdEbK5bC7i8u3FDnj2aPxs",{"id":56624,"title":483,"access":12,"apiToc":929,"body":56625,"category":1042,"description":56785,"extension":1044,"meta":56786,"navigation":1042,"nest":1042,"path":484,"seo":56787,"sinceVersion":1042,"status":1049,"stem":485,"subtitle":1042,"tier":1473,"tocExternal":1042,"__hash__":56788},"docs\u002Fglossary\u002Fcore-vocabulary.md",{"type":931,"value":56626,"toc":56778},[56627,56630,56636,56638,56643,56681,56687,56689,56707,56716,56718,56739,56743,56753,56761,56763],[934,56628,483],{"id":56629},"core-vocabulary",[937,56631,56632,56633,56635],{},"The four structural terms below appear on nearly every page of this site. Each has exactly one meaning; where a word could plausibly cover two things (as ",[1092,56634,5188],{}," often does in conversation), the distinctions are spelled out here.",[1022,56637,29047],{"id":5188},[937,56639,56640,56642],{},[941,56641,1086],{}," — the core npm package, and by extension the library it ships. The word carries three closely-related senses, always resolvable from context:",[1234,56644,56645,56654,56660],{},[1114,56646,56647,56650,56651,957],{},[950,56648,56649],{},"The package"," you install (",[941,56652,56653],{},"pnpm add @noy-db\u002Fhub",[1114,56655,56656,56659],{},[950,56657,56658],{},"The core library"," — the code that encrypts, decrypts, validates, and queries.",[1114,56661,56662,56665,56666,968,56668,968,56670,968,56672,968,56674,968,56676,56678,56679,1520],{},[950,56663,56664],{},"The trust boundary"," — the architectural region inside which plaintext and keys may exist. Every satellite package (",[941,56667,1260],{},[941,56669,1264],{},[941,56671,1267],{},[941,56673,1270],{},[941,56675,1273],{},[941,56677,1276],{},") sits outside it and binds one published subpath; see ",[1007,56680,37],{"href":38},[937,56682,56683,56684,56686],{},"What the hub is ",[1092,56685,1004],{},": a server, a service you run, or a place data lives. It is a library inside your application process.",[1022,56688,94],{"id":2694},[937,56690,3289,56691,56693,56694,968,56697,56700,56701,56703,56704,56706],{},[950,56692,2694],{}," is an isolated runtime data space with its own keys — typically one tenant, company, or user (",[941,56695,56696],{},"\"acme\"",[941,56698,56699],{},"\"firm-2026\"","). A vault is what ",[941,56702,22321],{}," returns; your records, keyrings, and per-vault policy live in it. One ",[941,56705,62],{}," session can open many vaults, and no key material is shared between them.",[937,56708,56709,56710,56712,56713,56715],{},"A vault is ",[1092,56711,8673],{},", not code — it is stored (as ciphertext envelopes) in whatever ",[1007,56714,6951],{"href":51}," you configured, and exists at runtime as the decrypted working set the hub maintains for you.",[1022,56717,103],{"id":6471},[937,56719,3289,56720,56722,56723,968,56725,56727,56728,56730,56731,28935,56733,1625,56735,1625,56737,1520],{},[950,56721,6471],{}," is a typed record set inside a vault (",[941,56724,6487],{},[941,56726,23926],{},"). Every record is addressed by the ",[941,56729,6299],{}," triple. The TypeScript type parameter on ",[941,56732,23920],{},[941,56734,3158],{},[941,56736,3161],{},[941,56738,5286],{},[1022,56740,56742],{"id":56741},"kernel-and-enclave","Kernel and enclave",[937,56744,960,56745,56747,56748,56750,56751,1520],{},[950,56746,2635],{}," is the always-on part of the hub — sessions, vaults, collections, encryption, keyring, schema, query — the layer documented under ",[1007,56749,29010],{"href":42},". It cannot be opted out of; everything beyond it is an opt-in ",[1007,56752,1047],{"href":598},[937,56754,960,56755,56757,56758,56760],{},[950,56756,1059],{}," is the kernel's innermost region: the only code that ever holds raw key material. Containment is strict — enclave ⊂ kernel ⊂ hub — and each ring only narrows what the code inside may touch; see ",[1007,56759,5],{"href":6}," for the containment nest and the invariant it enforces.",[1022,56762,2621],{"id":2620},[1111,56764,56765,56770],{},[1114,56766,56767,56769],{},[1007,56768,29010],{"href":42}," — the kernel, taught page by page.",[1114,56771,56772,56774,56775,56777],{},[1007,56773,37],{"href":38}," — who everything ",[1092,56776,12229],{}," the hub is to the vault.",{"title":1037,"searchDepth":1038,"depth":1038,"links":56779},[56780,56781,56782,56783,56784],{"id":5188,"depth":1041,"text":29047},{"id":2694,"depth":1041,"text":94},{"id":6471,"depth":1041,"text":103},{"id":56741,"depth":1041,"text":56742},{"id":2620,"depth":1041,"text":2621},"Canonical definitions for the four structural terms every page relies on: hub, vault, collection, and capsule\u002Fenclave-aes.",{},{"title":483,"description":56785},"c09yb4qeI-sqJwCwFdmrRN09T5eaikr4V0xJO4D2RZk",{"id":56790,"title":487,"access":12,"apiToc":929,"body":56791,"category":1042,"description":57025,"extension":1044,"meta":57026,"navigation":1042,"nest":1042,"path":488,"seo":57027,"sinceVersion":9471,"status":38550,"stem":489,"subtitle":1042,"tier":1473,"tocExternal":1042,"__hash__":57028},"docs\u002Fglossary\u002Fdeletion.md",{"type":931,"value":56792,"toc":57017},[56793,56796,56815,56819,56833,56837,56882,56886,56903,56907,56918,56972,56976,56979,56983,57011],[934,56794,487],{"id":56795},"deletion-vocabulary",[937,56797,56798,56799,56801,56802,56804,56805,56808,56809,56811,56812,56814],{},"noy-db is an encrypted, offline-first document store; a single database instance is called a\n",[1007,56800,2694],{"href":48},", and every record is persisted as an encrypted\n",[950,56803,1659],{},". \"Deleting\" a record means different things depending on whether the vault\n",[1007,56806,56807],{"href":865},"syncs"," and whether the goal is removal or provable erasure — and the vocabulary\ncrosses package boundaries: the envelope field travels the store contract every ",[941,56810,1779],{}," backend\nmust honor, sync enforces the semantics, and ",[1007,56813,776],{"href":777}," reclaims the space.\nThe terms below pin each meaning down.",[1022,56816,56818],{"id":56817},"physical-delete","Physical delete",[937,56820,56821,56822,56825,56826,56829,56830,56832],{},"The plain case: ",[941,56823,56824],{},"collection.delete(id)"," on a ",[950,56827,56828],{},"non-synced"," collection removes the envelope from\nthe store. Nothing is left behind. This is what ",[941,56831,9467],{}," always did, and still does wherever no\nsync target is configured.",[1022,56834,56836],{"id":56835},"delete-marker","Delete marker",[937,56838,14704,56839,56842,56843,56845,56846,17904,56848,1884,56851,56853,56854,56856,56857,1625,56860,56863,56864,1746,56866,56868,56869,56872,56873,56875,56876,56878,56879,56881],{},[950,56840,56841],{},"synced"," vault (since ",[941,56844,9471],{},", on ",[941,56847,41015],{},[941,56849,56850],{},"0.3.0",[941,56852,56824],{}," instead\nwrites a version-ordered envelope with ",[941,56855,9459],{}," and an empty body. The marker is a first-class\nwrite: it propagates on ",[941,56858,56859],{},"push()",[941,56861,56862],{},"pull()"," like any edit, so an offline peer can no longer\nresurrect the deleted record by pushing its stale copy — the marker outranks it. Reads treat a\nmarker as absence (",[941,56865,14639],{},[941,56867,6651],{},"), and a legitimate re-create at a ",[950,56870,56871],{},"higher version","\nresurrects the id. The ",[941,56874,15087],{}," field is part of the store contract (see\n",[1007,56877,6891],{"href":51},"): every ",[941,56880,1779],{}," store round-trips it unchanged.",[1022,56883,56885],{"id":56884},"forget-tombstone","Forget tombstone",[937,56887,30835,56888,56892,56893,2085,56896,56899,56900],{},[1007,56889,56890],{"href":686},[941,56891,2546],{}," leaves behind: a crypto-shredded envelope whose\nper-record key material is destroyed, making the body permanently undecryptable while proving —\nvia the ledger — that the record existed and was erased. Unlike a delete marker, a forget\ntombstone is ",[950,56894,56895],{},"terminal",[1007,56897,864],{"href":56898},"\u002Fservices\u002Fsync#erasures-are-terminal"," never overwrites it with\na live envelope regardless of version, conflict resolvers are never consulted against it, and no\nre-create resurrects the id. Rule of thumb: ",[1092,56901,56902],{},"delete converges, forget is forever.",[1022,56904,56906],{"id":56905},"erasure-residue","Erasure residue",[937,56908,30835,56909,56913,56914,56917],{},[1007,56910,56911],{"href":686},[941,56912,2546],{}," reports when it ",[950,56915,56916],{},"could not"," erase something it found —\nsurfaced rather than silently dropped, so an audit can tell exactly what survived and why. Two\ndistinct classes:",[1111,56919,56920,56940],{},[1114,56921,56922,953,56925,56928,56929,56932,56933,1261,56936,56939],{},[950,56923,56924],{},"Scope residue",[941,56926,56927],{},"scopedPurgeResidue",") — under the ",[941,56930,56931],{},"scopedPurge"," opt-in, an undeclared\ncollection's sealed-CEK envelopes or blobs are skipped-and-reported\n(",[941,56934,56935],{},"skipped-undeclared-sealed-cek",[941,56937,56938],{},"skipped-undeclared-blob-scan",") rather than scanned.",[1114,56941,56942,56945,56946,56949,56950,2874,56953,56956,56957,56960,56961,56964,56965,56968,56969,1520],{},[950,56943,56944],{},"Derived residue"," — a materialized-view output row belonging to the forgotten subject that\nresisted erasure. Since ",[941,56947,56948],{},"0.3.0-pre.13"," (#785) the reason is split by ",[950,56951,56952],{},"compliance meaning",[941,56954,56955],{},"derivedResidueUndecodable"," (ownership ",[950,56958,56959],{},"unconfirmed"," — the row's ownership stamp couldn't be\ndecoded, e.g. it was elevated above tier 0) versus ",[941,56962,56963],{},"derivedResidueDeclined"," (ownership\n",[950,56966,56967],{},"confirmed"," — the row decoded and matched, but the tier gate declined its erasure, leaving a\nlive tier-holder-decryptable copy). Reading \"undecodable\" as \"couldn't tell whose it was\" is only\ntrue for the first; the second is a known, deliberate retention. That tier gate is documented on\n",[1007,56970,872],{"href":56971},"\u002Fservices\u002Ftiers#readwrite-gating",[1022,56973,56975],{"id":56974},"resurrection-window","Resurrection window",[937,56977,56978],{},"The interval during which purging a delete marker would be unsafe: a peer that has been offline\nsince before the delete still holds a live copy, and without the marker its next push would\nsilently bring the record back. No single vault can prove every peer has converged — which is why\nmarker purging is gated on an operator-asserted safe-point, the closed accounting period.",[1022,56980,56982],{"id":56981},"freeze-target-purge","Freeze \u002F target-purge",[937,56984,56985,56986,56988,56989,11737,56992,953,56995,56998,56999,953,57002,57005,57006,1625,57008,57010],{},"The two reclamation operations ",[1007,56987,776],{"href":777}," builds on a closed period\n(since ",[941,56990,56991],{},"0.3.0-pre.8",[950,56993,56994],{},"freeze",[941,56996,56997],{},"vault.freezePeriod()",") physically purges the period's delete\nmarkers from the local store; ",[950,57000,57001],{},"target-purge",[941,57003,57004],{},"vault.purgePeriodTargets()",") sweeps them off\npush-only sync targets (",[941,57007,50702],{},[941,57009,605],{}," roles), never off bidirectional peers. Both record a\ncompanion + ledger entry and leave forget tombstones, history, and live records untouched.",[937,57012,57013,57014,1520],{},"For the full mechanics, start at ",[1007,57015,9476],{"href":57016},"\u002Fservices\u002Fsync#deletes-converge-as-delete-markers",{"title":1037,"searchDepth":1038,"depth":1038,"links":57018},[57019,57020,57021,57022,57023,57024],{"id":56817,"depth":1041,"text":56818},{"id":56835,"depth":1041,"text":56836},{"id":56884,"depth":1041,"text":56885},{"id":56905,"depth":1041,"text":56906},{"id":56974,"depth":1041,"text":56975},{"id":56981,"depth":1041,"text":56982},"Canonical terms for the three ways a record stops existing — physical delete, delete marker, and forget tombstone — plus the retention terms built on them.",{},{"title":487,"description":57025},"vv5MPcZ06ucY6nstFGwXprDDmxe7gA_20TTCyyrJejs",{"id":57030,"title":475,"access":12,"apiToc":929,"body":57031,"category":1042,"description":57230,"extension":1044,"meta":57231,"navigation":57232,"nest":1042,"path":476,"seo":57233,"sinceVersion":1042,"status":1049,"stem":480,"subtitle":1042,"tier":1473,"tocExternal":1042,"__hash__":57234},"docs\u002Fglossary\u002Findex.md",{"type":931,"value":57032,"toc":57224},[57033,57035,57044,57047,57051,57054,57084,57090,57093,57134,57139,57149,57178,57182,57187,57219],[934,57034,475],{"id":477},[937,57036,57037,57038,57040,57041,57043],{},"noy-db is an encrypted, offline-first document store; a single database instance is called a ",[950,57039,2694],{},". Some of its vocabulary spans more than one package — a term coined for one feature is reused by adapters, by the docs, and by ",[1007,57042,1212],{"href":34},", the companion control plane that manages a fleet of vaults. This section pins those shared terms down so each one means exactly one thing everywhere it appears.",[937,57045,57046],{},"The glossary currently contains four vocabulary sets:",[1022,57048,57049],{"id":56629},[1007,57050,483],{"href":484},[937,57052,57053],{},"The four structural terms every page relies on, each with exactly one meaning:",[1111,57055,57056,57063,57071,57078],{},[1114,57057,57058,1169,57060,57062],{},[950,57059,29047],{},[941,57061,1086],{}," package \u002F core library \u002F trust boundary (the three senses spelled out).",[1114,57064,57065,57067,57068,57070],{},[950,57066,94],{}," — an isolated runtime data space with its own keys; what ",[941,57069,1749],{}," returns.",[1114,57072,57073,57075,57076,1520],{},[950,57074,103],{}," — a typed record set inside a vault, addressed by ",[941,57077,6299],{},[1114,57079,57080,57083],{},[950,57081,57082],{},"Kernel \u002F enclave"," — the always-on part of the hub, and its innermost key-holding region (enclave ⊂ kernel ⊂ hub).",[1022,57085,57087],{"id":57086},"schema-migration",[1007,57088,57089],{"href":496},"Schema migration",[937,57091,57092],{},"Terms for evolving a record schema from one generation to the next:",[1111,57094,57095,57107,57113,57119],{},[1114,57096,57097,57100,57101,10313,57104,1520],{},[950,57098,57099],{},"Transform"," — a pure function mapping one decrypted record from schema generation ",[1092,57102,57103],{},"N",[1092,57105,57106],{},"N+1",[1114,57108,57109,57112],{},[950,57110,57111],{},"Cutover"," — the coordinated in-place transition of one vault to the next generation (fence, quiesce, transform, bump).",[1114,57114,57115,57118],{},[950,57116,57117],{},"Rollout"," — the orchestration of Cutovers across a set of vaults, owned by klum-db.",[1114,57120,960,57121,964,57124,968,57127,2462,57130,57133],{},[950,57122,57123],{},"strategy axis",[1092,57125,57126],{},"in-place",[1092,57128,57129],{},"expand–contract",[1092,57131,57132],{},"blue-green",", and which side of the vault boundary each falls on.",[1022,57135,57137],{"id":57136},"satellite-collection-vocabulary",[1007,57138,491],{"href":492},[937,57140,57141,57142,57144,57145,57148],{},"Terms for ",[1007,57143,6875],{"href":813}," (experimental, since ",[941,57146,57147],{},"0.3.0-pre.6",") — off-row storage that splits a record's heavy, rarely-read fields into a second encrypted envelope so hot reads never pay to decrypt them:",[1111,57150,57151,57157,57166,57172],{},[1114,57152,57153,57156],{},[950,57154,57155],{},"Satellite (collection)"," — the companion collection holding the heavy fields, keyed 1:1 by the base record's id.",[1114,57158,57159,57162,57163,1520],{},[950,57160,57161],{},"Joined handle"," — the merged base ⊕ satellite view returned by ",[941,57164,57165],{},"vault.joined(name)",[1114,57167,57168,57171],{},[950,57169,57170],{},"Existence authority"," — the rule that the base row alone decides whether a record exists.",[1114,57173,57174,57177],{},[950,57175,57176],{},"Pairing marker"," — the persisted record that locks a satellite's configuration to its first declaration.",[1022,57179,57180],{"id":56795},[1007,57181,487],{"href":488},[937,57183,57184,57185,6255],{},"Terms for the three ways a record stops existing (experimental, since ",[941,57186,9471],{},[1111,57188,57189,57194,57205,57213],{},[1114,57190,57191,57193],{},[950,57192,56818],{}," — plain removal of the envelope on a non-synced collection.",[1114,57195,57196,57198,57199,57201,57202,57204],{},[950,57197,56836],{}," — the version-ordered ",[941,57200,15087],{}," envelope a synced ",[941,57203,9467],{}," writes so the delete converges across peers; a higher-version re-create resurrects the id.",[1114,57206,57207,57209,57210,57212],{},[950,57208,56885],{}," — the terminal crypto-shred ",[941,57211,2546],{}," leaves; sync never lets a live envelope win against it.",[1114,57214,57215,57218],{},[950,57216,57217],{},"Resurrection window \u002F freeze \u002F target-purge"," — why markers can't be purged early, and the period-gated operations that reclaim them.",[937,57220,57221,57222,1520],{},"New vocabulary sets are added here as features introduce terms that cross package boundaries. For a task-oriented entry point into the rest of the docs, use the ",[1007,57223,1019],{"href":1018},{"title":1037,"searchDepth":1038,"depth":1038,"links":57225},[57226,57227,57228,57229],{"id":56629,"depth":1041,"text":483},{"id":57086,"depth":1041,"text":57089},{"id":57136,"depth":1041,"text":491},{"id":56795,"depth":1041,"text":487},"Canonical definitions for the vocabulary shared across the noy-db family — schema-migration terms, satellite-collection terms, and deletion terms.",{},{"title":10,"icon":481},{"title":475,"description":57230},"7KJ4jchXLcxy1p1MaxUgrr3jVyYgcdyhnUhQkaM1fdM",{"id":57236,"title":491,"access":12,"apiToc":929,"body":57237,"category":1042,"description":57866,"extension":1044,"meta":57867,"navigation":1042,"nest":1042,"path":492,"seo":57868,"sinceVersion":57147,"status":38550,"stem":493,"subtitle":1042,"tier":1473,"tocExternal":1042,"__hash__":57869},"docs\u002Fglossary\u002Fsatellite-collections.md",{"type":931,"value":57238,"toc":57859},[57239,57241,57264,57267,57287,57737,57745,57748,57782,57785,57823,57826,57846,57848,57857],[934,57240,491],{"id":57136},[937,57242,57037,57243,57245,57246,57248,57249,57251,57252,57144,57255,57257,57258,57263],{},[1007,57244,2694],{"href":48},", and records live in ",[950,57247,2698],{}," within it. Every record is stored as a single ",[950,57250,1659],{}," — one AES-256-GCM ciphertext blob that is decrypted whole or not at all; there is no partial decryption. ",[1007,57253,57254],{"href":813},"Satellite collections",[941,57256,57147],{},") split a record's small, frequently-read fields and its large, rarely-read fields into two collections — two envelopes — so hot reads never pay to decrypt the heavy fields. The terms below name the moving parts; the ",[1007,57259,57260,57262],{"href":813},[941,57261,812],{}," service page"," covers the full mechanics.",[1022,57265,57155],{"id":57266},"satellite-collection",[937,57268,57269,57270,57273,57274,57277,57278,57282,57283,57286],{},"A collection hung off a ",[950,57271,57272],{},"base"," collection, sharing the base's record id 1:1 and holding the heavy or less-accessed fields in its own envelope. Declared with the ",[941,57275,57276],{},"satelliteOf"," option on ",[1007,57279,57280],{"href":89},[941,57281,2138],{},"; the ",[941,57284,57285],{},"fields"," list is the routing table naming exactly which fields the satellite owns.",[2217,57288,57290],{"className":2219,"code":57289,"language":2221,"meta":1037,"style":1037},"import { createNoydb } from '@noy-db\u002Fhub'\nimport { toFile } from '@noy-db\u002Fto-file'\n\nconst db = await createNoydb({\n  store: toFile({ dir: '.\u002Fdata' }),\n  user: 'ann',\n  secret: 'a-strong-secret',\n})\nconst vault = await db.openVault('mail')\n\n\u002F\u002F Base: small, hot fields — cheap to decrypt on every list.\nconst msgs = vault.collection('msgs')\n\n\u002F\u002F Satellite: heavy fields, same record ids, its own envelope per record.\nvault.collection('msgs_text', {\n  satelliteOf: 'msgs',\n  fields: ['subject', 'body'], \u002F\u002F routing table — the fields this satellite owns\n  joined: 'msgs_full',         \u002F\u002F optional: registers the joined handle\n})\n\n\u002F\u002F Joined handle: the full record, split on write, merged on read.\nconst full = vault.joined('msgs_full')\nawait full.put('m1', { from: 'ann@example.com', subject: 'Hi', body: 'Long message body…' })\n\nawait msgs.get('m1') \u002F\u002F hot read — the heavy envelope is never fetched or decrypted\n\u002F\u002F → { from: 'ann@example.com' }\n\nawait full.get('m1')\n\u002F\u002F → { subject: 'Hi', body: 'Long message body…', from: 'ann@example.com' }\n",[941,57291,57292,57310,57328,57332,57348,57376,57391,57405,57411,57438,57442,57447,57473,57477,57482,57503,57518,57549,57568,57574,57578,57583,57609,57675,57679,57703,57708,57712,57732],{"__ignoreMap":1037},[2225,57293,57294,57296,57298,57300,57302,57304,57306,57308],{"class":2227,"line":2228},[2225,57295,2232],{"class":2231},[2225,57297,2236],{"class":2235},[2225,57299,2744],{"class":2239},[2225,57301,2243],{"class":2235},[2225,57303,2246],{"class":2231},[2225,57305,2249],{"class":2235},[2225,57307,1086],{"class":2252},[2225,57309,2256],{"class":2235},[2225,57311,57312,57314,57316,57318,57320,57322,57324,57326],{"class":2227,"line":1041},[2225,57313,2232],{"class":2231},[2225,57315,2236],{"class":2235},[2225,57317,15573],{"class":2239},[2225,57319,2243],{"class":2235},[2225,57321,2246],{"class":2231},[2225,57323,2249],{"class":2235},[2225,57325,9551],{"class":2252},[2225,57327,2256],{"class":2235},[2225,57329,57330],{"class":2227,"line":1038},[2225,57331,2285],{"emptyLinePlaceholder":2284},[2225,57333,57334,57336,57338,57340,57342,57344,57346],{"class":2227,"line":2288},[2225,57335,2775],{"class":2774},[2225,57337,2778],{"class":2239},[2225,57339,2781],{"class":2235},[2225,57341,2784],{"class":2231},[2225,57343,2744],{"class":2291},[2225,57345,2789],{"class":2239},[2225,57347,4200],{"class":2235},[2225,57349,57350,57352,57354,57356,57358,57360,57362,57364,57366,57368,57370,57372,57374],{"class":2227,"line":2304},[2225,57351,15608],{"class":2307},[2225,57353,2311],{"class":2235},[2225,57355,15573],{"class":2291},[2225,57357,2789],{"class":2239},[2225,57359,2792],{"class":2235},[2225,57361,15619],{"class":2307},[2225,57363,2311],{"class":2235},[2225,57365,2249],{"class":2235},[2225,57367,15626],{"class":2252},[2225,57369,2805],{"class":2235},[2225,57371,2243],{"class":2235},[2225,57373,1381],{"class":2239},[2225,57375,1707],{"class":2235},[2225,57377,57378,57380,57382,57384,57387,57389],{"class":2227,"line":2356},[2225,57379,4205],{"class":2307},[2225,57381,2311],{"class":2235},[2225,57383,2249],{"class":2235},[2225,57385,57386],{"class":2252},"ann",[2225,57388,2805],{"class":2235},[2225,57390,1707],{"class":2235},[2225,57392,57393,57395,57397,57399,57401,57403],{"class":2227,"line":2830},[2225,57394,10423],{"class":2307},[2225,57396,2311],{"class":2235},[2225,57398,2249],{"class":2235},[2225,57400,6421],{"class":2252},[2225,57402,2805],{"class":2235},[2225,57404,1707],{"class":2235},[2225,57406,57407,57409],{"class":2227,"line":2861},[2225,57408,2359],{"class":2235},[2225,57410,2362],{"class":2239},[2225,57412,57413,57415,57417,57419,57421,57423,57425,57427,57429,57431,57434,57436],{"class":2227,"line":4246},[2225,57414,2775],{"class":2774},[2225,57416,2835],{"class":2239},[2225,57418,2781],{"class":2235},[2225,57420,2784],{"class":2231},[2225,57422,2842],{"class":2239},[2225,57424,1520],{"class":2235},[2225,57426,2847],{"class":2291},[2225,57428,2789],{"class":2239},[2225,57430,2805],{"class":2235},[2225,57432,57433],{"class":2252},"mail",[2225,57435,2805],{"class":2235},[2225,57437,2362],{"class":2239},[2225,57439,57440],{"class":2227,"line":4253},[2225,57441,2285],{"emptyLinePlaceholder":2284},[2225,57443,57444],{"class":2227,"line":4259},[2225,57445,57446],{"class":2763},"\u002F\u002F Base: small, hot fields — cheap to decrypt on every list.\n",[2225,57448,57449,57451,57454,57456,57458,57460,57462,57464,57466,57469,57471],{"class":2227,"line":6548},[2225,57450,2775],{"class":2774},[2225,57452,57453],{"class":2239}," msgs ",[2225,57455,2781],{"class":2235},[2225,57457,6466],{"class":2239},[2225,57459,1520],{"class":2235},[2225,57461,6471],{"class":2291},[2225,57463,2789],{"class":2239},[2225,57465,2805],{"class":2235},[2225,57467,57468],{"class":2252},"msgs",[2225,57470,2805],{"class":2235},[2225,57472,2362],{"class":2239},[2225,57474,57475],{"class":2227,"line":6576},[2225,57476,2285],{"emptyLinePlaceholder":2284},[2225,57478,57479],{"class":2227,"line":6582},[2225,57480,57481],{"class":2763},"\u002F\u002F Satellite: heavy fields, same record ids, its own envelope per record.\n",[2225,57483,57484,57486,57488,57490,57492,57494,57497,57499,57501],{"class":2227,"line":6587},[2225,57485,2694],{"class":2239},[2225,57487,1520],{"class":2235},[2225,57489,6471],{"class":2291},[2225,57491,2789],{"class":2239},[2225,57493,2805],{"class":2235},[2225,57495,57496],{"class":2252},"msgs_text",[2225,57498,2805],{"class":2235},[2225,57500,2298],{"class":2235},[2225,57502,2301],{"class":2235},[2225,57504,57505,57508,57510,57512,57514,57516],{"class":2227,"line":7174},[2225,57506,57507],{"class":2307},"  satelliteOf",[2225,57509,2311],{"class":2235},[2225,57511,2249],{"class":2235},[2225,57513,57468],{"class":2252},[2225,57515,2805],{"class":2235},[2225,57517,1707],{"class":2235},[2225,57519,57520,57523,57525,57527,57529,57532,57534,57536,57538,57540,57542,57544,57546],{"class":2227,"line":7180},[2225,57521,57522],{"class":2307},"  fields",[2225,57524,2311],{"class":2235},[2225,57526,8667],{"class":2239},[2225,57528,2805],{"class":2235},[2225,57530,57531],{"class":2252},"subject",[2225,57533,2805],{"class":2235},[2225,57535,2298],{"class":2235},[2225,57537,2249],{"class":2235},[2225,57539,42850],{"class":2252},[2225,57541,2805],{"class":2235},[2225,57543,8803],{"class":2239},[2225,57545,2298],{"class":2235},[2225,57547,57548],{"class":2763}," \u002F\u002F routing table — the fields this satellite owns\n",[2225,57550,57551,57554,57556,57558,57561,57563,57565],{"class":2227,"line":7220},[2225,57552,57553],{"class":2307},"  joined",[2225,57555,2311],{"class":2235},[2225,57557,2249],{"class":2235},[2225,57559,57560],{"class":2252},"msgs_full",[2225,57562,2805],{"class":2235},[2225,57564,2298],{"class":2235},[2225,57566,57567],{"class":2763},"         \u002F\u002F optional: registers the joined handle\n",[2225,57569,57570,57572],{"class":2227,"line":7225},[2225,57571,2359],{"class":2235},[2225,57573,2362],{"class":2239},[2225,57575,57576],{"class":2227,"line":7231},[2225,57577,2285],{"emptyLinePlaceholder":2284},[2225,57579,57580],{"class":2227,"line":7267},[2225,57581,57582],{"class":2763},"\u002F\u002F Joined handle: the full record, split on write, merged on read.\n",[2225,57584,57585,57587,57590,57592,57594,57596,57599,57601,57603,57605,57607],{"class":2227,"line":7272},[2225,57586,2775],{"class":2774},[2225,57588,57589],{"class":2239}," full ",[2225,57591,2781],{"class":2235},[2225,57593,6466],{"class":2239},[2225,57595,1520],{"class":2235},[2225,57597,57598],{"class":2291},"joined",[2225,57600,2789],{"class":2239},[2225,57602,2805],{"class":2235},[2225,57604,57560],{"class":2252},[2225,57606,2805],{"class":2235},[2225,57608,2362],{"class":2239},[2225,57610,57611,57613,57616,57618,57620,57622,57624,57627,57629,57631,57633,57635,57637,57639,57642,57644,57646,57649,57651,57653,57656,57658,57660,57662,57664,57666,57669,57671,57673],{"class":2227,"line":7278},[2225,57612,6500],{"class":2231},[2225,57614,57615],{"class":2239}," full",[2225,57617,1520],{"class":2235},[2225,57619,3161],{"class":2291},[2225,57621,2789],{"class":2239},[2225,57623,2805],{"class":2235},[2225,57625,57626],{"class":2252},"m1",[2225,57628,2805],{"class":2235},[2225,57630,2298],{"class":2235},[2225,57632,2236],{"class":2235},[2225,57634,2246],{"class":2307},[2225,57636,2311],{"class":2235},[2225,57638,2249],{"class":2235},[2225,57640,57641],{"class":2252},"ann@example.com",[2225,57643,2805],{"class":2235},[2225,57645,2298],{"class":2235},[2225,57647,57648],{"class":2307}," subject",[2225,57650,2311],{"class":2235},[2225,57652,2249],{"class":2235},[2225,57654,57655],{"class":2252},"Hi",[2225,57657,2805],{"class":2235},[2225,57659,2298],{"class":2235},[2225,57661,42791],{"class":2307},[2225,57663,2311],{"class":2235},[2225,57665,2249],{"class":2235},[2225,57667,57668],{"class":2252},"Long message body…",[2225,57670,2805],{"class":2235},[2225,57672,2243],{"class":2235},[2225,57674,2362],{"class":2239},[2225,57676,57677],{"class":2227,"line":7303},[2225,57678,2285],{"emptyLinePlaceholder":2284},[2225,57680,57681,57683,57686,57688,57690,57692,57694,57696,57698,57700],{"class":2227,"line":7308},[2225,57682,6500],{"class":2231},[2225,57684,57685],{"class":2239}," msgs",[2225,57687,1520],{"class":2235},[2225,57689,3158],{"class":2291},[2225,57691,2789],{"class":2239},[2225,57693,2805],{"class":2235},[2225,57695,57626],{"class":2252},[2225,57697,2805],{"class":2235},[2225,57699,8495],{"class":2239},[2225,57701,57702],{"class":2763},"\u002F\u002F hot read — the heavy envelope is never fetched or decrypted\n",[2225,57704,57705],{"class":2227,"line":7314},[2225,57706,57707],{"class":2763},"\u002F\u002F → { from: 'ann@example.com' }\n",[2225,57709,57710],{"class":2227,"line":7348},[2225,57711,2285],{"emptyLinePlaceholder":2284},[2225,57713,57714,57716,57718,57720,57722,57724,57726,57728,57730],{"class":2227,"line":8613},[2225,57715,6500],{"class":2231},[2225,57717,57615],{"class":2239},[2225,57719,1520],{"class":2235},[2225,57721,3158],{"class":2291},[2225,57723,2789],{"class":2239},[2225,57725,2805],{"class":2235},[2225,57727,57626],{"class":2252},[2225,57729,2805],{"class":2235},[2225,57731,2362],{"class":2239},[2225,57733,57734],{"class":2227,"line":8633},[2225,57735,57736],{"class":2763},"\u002F\u002F → { subject: 'Hi', body: 'Long message body…', from: 'ann@example.com' }\n",[1111,57738,57739],{},[1114,57740,57741,57744],{},[950,57742,57743],{},"Lineage:"," the zero-knowledge-store analog of PostgreSQL TOAST \u002F vertical partitioning \u002F Data Vault 2.0's hub-and-satellite pattern.",[1022,57746,57161],{"id":57747},"joined-handle",[937,57749,57750,57753,57754,57756,57757,57759,57760,57762,57763,1261,57765,1261,57767,1261,57769,1261,57771,57773,57774,57776,57777,968,57779,57781],{},[941,57751,57752],{},"JoinedHandle\u003CT>",", obtained via ",[941,57755,57165],{}," once a satellite declares the ",[941,57758,57598],{}," option. It reads and writes the full record: a read merges base ⊕ satellite — a disjoint field-union computed from two O(1) same-id gets, with every declared satellite field defaulting to ",[941,57761,6651],{}," when no satellite row exists yet; a write splits the record by the routing table and fans out to both collections. The handle is deliberately narrow — ",[941,57764,3158],{},[941,57766,3161],{},[941,57768,3164],{},[941,57770,3167],{},[941,57772,6153],{}," only — and is not typed as ",[941,57775,3121],{},": reactive members (",[941,57778,25042],{},[941,57780,727],{},", …) are simply absent from the type rather than stubbed.",[1022,57783,57170],{"id":57784},"existence-authority",[937,57786,57787,57788,57791,57792,57794,57795,57797,57798,57800,57801,57804,57805,57810,57811,57813,57814,57817,57818,4688,57820,57822],{},"The base row is the sole authority on whether a record exists. A satellite row is existence-filtered: ",[941,57789,57790],{},"satellite.get"," and joined ",[941,57793,3158],{}," return ",[941,57796,6651],{},", and satellite ",[941,57799,24407],{}," drops the id, whenever the base row is absent or ",[950,57802,57803],{},"tombstoned",". A tombstone is the residue a ",[1007,57806,57807,57809],{"href":66},[941,57808,2546],{}," crypto-shred"," leaves behind — the envelope rewritten with its ciphertext and wrapped record key stripped, so the record reads as permanently gone. (Satellite ",[941,57812,24715],{}," cannot be existence-filtered and refuses outright with ",[941,57815,57816],{},"SatelliteConfigError","; use ",[941,57819,24407],{},[941,57821,14639],{}," instead.) A base-less satellite envelope is dead ciphertext — unreachable through every enumerated read surface, though not physically swept in v1.",[1022,57824,57176],{"id":57825},"pairing-marker",[937,57827,57828,57829,57832,57833,57835,57836,57838,57839,1069,57842,57845],{},"The persisted ",[941,57830,57831],{},"{ base, fieldsHash, joined }"," record written into the reserved ",[941,57834,35623],{}," collection the first time a satellite is declared. A later declaration whose base, field set, or joined name diverges from the marker is refused with a ",[941,57837,57816],{}," — refusal rule ",[950,57840,57841],{},"R-S9",[1007,57843,57844],{"href":813},"satellites refusal matrix"," — so the same logical record can never silently split differently across app versions.",[1022,57847,2621],{"id":2620},[1111,57849,57850],{},[1114,57851,57852,57856],{},[1007,57853,57854],{"href":813},[941,57855,812],{}," (experimental) — declaration options, the three handles, the full refusal matrix, convergence rules, and v1 caveats.",[2645,57858,30448],{},{"title":1037,"searchDepth":1038,"depth":1038,"links":57860},[57861,57862,57863,57864,57865],{"id":57266,"depth":1041,"text":57155},{"id":57747,"depth":1041,"text":57161},{"id":57784,"depth":1041,"text":57170},{"id":57825,"depth":1041,"text":57176},{"id":2620,"depth":1041,"text":2621},"Canonical terms for satellite collections: satellite, joined handle, existence authority, and pairing marker.",{},{"title":491,"description":57866},"U1Aqi78bFWOZFhLfP3arpIF7z-4rR3nGmXP1he_mThA",{"id":57871,"title":495,"access":12,"apiToc":929,"body":57872,"category":1042,"description":58202,"extension":1044,"meta":58203,"navigation":1042,"nest":1042,"path":496,"seo":58204,"sinceVersion":1042,"status":1049,"stem":497,"subtitle":1042,"tier":1473,"tocExternal":1042,"__hash__":58205},"docs\u002Fglossary\u002Fschema-migration.md",{"type":931,"value":57873,"toc":58189},[57874,57877,57896,57900,57903,57916,57923,57935,57964,57970,57989,58016,58023,58034,58060,58064,58073,58140,58144,58157,58159],[934,57875,495],{"id":57876},"schema-migration-vocabulary",[937,57878,57879,57880,57882,57883,57886,57887,953,57889,57891,57892,57895],{},"When you change the shape of your records — add a field, drop one, restructure — the data already stored has to be carried from the old shape to the new. In noy-db, an encrypted, offline-first document store, a single database instance is called a ",[1007,57881,2694],{"href":48},"; each vault tracks a ",[950,57884,57885],{},"schema generation"," number that advances when its record shape changes. ",[1007,57888,1212],{"href":34},[941,57890,5230],{},") is the companion control plane — the ",[1092,57893,57894],{},"lobby"," — that manages a fleet of vaults. This page pins down the three canonical terms for that evolution and which side of the vault ↔ lobby boundary owns each, so every package and page in the family means the same thing by them.",[1022,57897,57899],{"id":57898},"the-ladder","The ladder",[937,57901,57902],{},"Three nested concepts. Compose them as one sentence:",[1061,57904,57905],{},[937,57906,3289,57907,57909,57910,57912,57913,57915],{},[950,57908,57117],{}," applies a ",[950,57911,57099],{}," to a vault group by performing a ",[950,57914,57111],{}," on each member vault.",[1591,57917,57919,57920],{"id":57918},"transform-noy-db","Transform — ",[1092,57921,57922],{},"(noy-db)",[937,57924,57925,57926,57929,57930,1746,57932,57934],{},"A pure, deterministic function mapping ",[950,57927,57928],{},"one decrypted record"," from schema generation ",[1092,57931,57103],{},[1092,57933,57106],{},". No I\u002FO, no coordination, no cross-record or cross-vault awareness. The unit of change.",[1111,57936,57937,57942,57958],{},[1114,57938,57939,57941],{},[950,57940,57743],{}," the \"T\" in ETL; an Avro\u002FProtobuf schema-evolution upgrade function; the body of a Rails\u002FFlyway\u002FAlembic migration.",[1114,57943,57944,1324,57947,57950,57951,57953,57954,57957],{},[950,57945,57946],{},"Code:",[941,57948,57949],{},"TransformFn"," \u002F the ",[941,57952,28839],{}," passed to the ",[941,57955,57956],{},"coordinatedCutover"," strategy.",[1114,57959,57960,57963],{},[950,57961,57962],{},"Owner:"," noy-db — it runs on decrypted records, so it's crypto-bound.",[1591,57965,57967,57968],{"id":57966},"cutover-noy-db","Cutover — ",[1092,57969,57922],{},[937,57971,57972,57973,57975,57976,57979,57980,1746,57982,57984,57985,57988],{},"The coordinated, ",[950,57974,57126],{}," transition of ",[950,57977,57978],{},"one vault"," from generation ",[1092,57981,57103],{},[1092,57983,57106],{},": raise a drain fence → quiesce all live clients to a barrier (or time out) → apply the Transform → bump the vault's generation. Multi-client by construction; ",[950,57986,57987],{},"indivisible from the vault's write-queue and write-gate"," — while the fence is up, writes are refused.",[1111,57990,57991,57996,58011],{},[1114,57992,57993,57995],{},[950,57994,57743],{}," a \"cutover\" (the controlled switch); the live phase of an online schema change (gh-ost \u002F pt-osc); quiesce \u002F drain \u002F fence \u002F barrier from distributed systems.",[1114,57997,57998,1324,58000,58003,58004,58007,58008,957],{},[950,57999,57946],{},[941,58001,58002],{},"vault.runSchemaCutover()",", driven by ",[941,58005,58006],{},"SchemaFenceController"," (the fence state machine: ",[941,58009,58010],{},"normal → draining → migrating → complete → normal",[1114,58012,58013,58015],{},[950,58014,57962],{}," noy-db — it's concurrency control for one vault, which cannot be split from that vault's write path.",[1591,58017,58019,58020],{"id":58018},"rollout-klum-db","Rollout — ",[1092,58021,58022],{},"(klum-db)",[937,58024,58025,58026,58029,58030,58033],{},"The orchestration of a Cutover across a ",[950,58027,58028],{},"set of one-or-more vaults"," (a group \u002F fleet): select the members behind the target generation, migrate them in controlled batches, record per-vault status, and emit lifecycle events. A shard that fails is recorded, not fatal — the run continues past it, and because already-migrated vaults are skipped, a re-run resumes exactly where the last one stopped. Coordinates across the ",[950,58031,58032],{},"vault boundary","; delegates each vault's Cutover to that vault.",[1111,58035,58036,58041,58055],{},[1114,58037,58038,58040],{},[950,58039,57743],{}," a \"rolling rollout \u002F upgrade\" (Kubernetes); fleet-wide migration; phased rollout.",[1114,58042,58043,1324,58045,58048,58049,58052,58053,1520],{},[950,58044,57946],{},[941,58046,58047],{},"VaultGroup.rolloutSchema()"," (the fleet) \u002F ",[941,58050,58051],{},"cutoverShard()"," (one shard) — in ",[941,58054,5230],{},[1114,58056,58057,58059],{},[950,58058,57962],{}," klum-db — coordination across multiple vaults is the Lobby's job.",[1022,58061,58063],{"id":58062},"strategy-axis-orthogonal-to-the-ladder","Strategy axis (orthogonal to the ladder)",[937,58065,58066,58069,58070,58072],{},[1092,58067,58068],{},"How"," a Cutover\u002FRollout reshapes data is a separate dimension. In-place is the strategy noy-db implements today (the ",[941,58071,57956],{}," strategy); the other two rows classify where those industry approaches sit relative to the vault boundary:",[1338,58074,58075,58090],{},[1341,58076,58077],{},[1344,58078,58079,58082,58085,58087],{},[1347,58080,58081],{},"Strategy",[1347,58083,58084],{},"Industry term",[1347,58086,29857],{},[1347,58088,58089],{},"Falls under",[1357,58091,58092,58108,58124],{},[1344,58093,58094,58099,58102,58105],{},[1362,58095,58096],{},[950,58097,58098],{},"In-place",[1362,58100,58101],{},"online schema change",[1362,58103,58104],{},"Mutate the existing vault behind the fence (the implemented model)",[1362,58106,58107],{},"Cutover → noy-db",[1344,58109,58110,58115,58118,58121],{},[1362,58111,58112],{},[950,58113,58114],{},"Expand–Contract",[1362,58116,58117],{},"Parallel Change (Fowler)",[1362,58119,58120],{},"Add the new shape additively, dual-write \u002F backfill, then drop the old — within a vault",[1362,58122,58123],{},"Cutover (multi-step) → noy-db",[1344,58125,58126,58131,58134,58137],{},[1362,58127,58128],{},[950,58129,58130],{},"Blue-Green",[1362,58132,58133],{},"blue-green migration",[1362,58135,58136],{},"New-schema vault, transform-copy across, flip clients, retire old",[1362,58138,58139],{},"Rollout → klum-db (cross-vault by construction)",[1022,58141,58143],{"id":58142},"boundary-rule","Boundary rule",[937,58145,58146,58149,58150,58153,58154,58156],{},[950,58147,58148],{},"Transform + Cutover = noy-db"," (one vault's data + concurrency control, indivisible from the write path). ",[950,58151,58152],{},"Rollout = klum-db"," (coordination across the vault boundary). \"Schema migration\" is the informal umbrella for the whole activity — ",[1092,58155,1004],{}," a precise rung; don't use it as a tier label.",[1022,58158,2621],{"id":2620},[1111,58160,58161,58166,58183],{},[1114,58162,58163,58165],{},[1007,58164,111],{"href":112}," — how a collection's schema is declared and validated.",[1114,58167,58168,58170,58171,1261,58174,1261,58177,1261,58180,58182],{},[1007,58169,820],{"href":821}," — the strategies (",[941,58172,58173],{},"blindUpdate",[941,58175,58176],{},"additiveOnly",[941,58178,58179],{},"lockSchema",[941,58181,57956],{},") that decide what happens when a schema change is detected.",[1114,58184,58185,58188],{},[1007,58186,58187],{"href":476},"Glossary overview"," — the other shared vocabulary sets.",{"title":1037,"searchDepth":1038,"depth":1038,"links":58190},[58191,58199,58200,58201],{"id":57898,"depth":1041,"text":57899,"children":58192},[58193,58195,58197],{"id":57918,"depth":1038,"text":58194},"Transform — (noy-db)",{"id":57966,"depth":1038,"text":58196},"Cutover — (noy-db)",{"id":58018,"depth":1038,"text":58198},"Rollout — (klum-db)",{"id":58062,"depth":1041,"text":58063},{"id":58142,"depth":1041,"text":58143},{"id":2620,"depth":1041,"text":2621},"Canonical terms for evolving a record schema across generations: Transform, Cutover, and Rollout — and which side of the vault ↔ lobby boundary owns each.",{},{"title":495,"description":58202},"C7ddr9RJpcbVNmb1voht473sJej49I7ztywKQr7WNFA",{"id":58207,"title":507,"access":12,"apiToc":929,"body":58208,"category":1042,"description":61760,"extension":1044,"meta":61761,"navigation":1042,"nest":1042,"path":508,"seo":61762,"sinceVersion":1042,"status":1049,"stem":509,"subtitle":1042,"tier":1050,"tocExternal":1042,"__hash__":61763},"docs\u002Frecipes\u002Faccounting-app.md",{"type":931,"value":58209,"toc":61748},[58210,58213,58243,58254,58273,58277,58391,58395,58416,58781,58785,58801,59385,59389,59628,59640,59688,59825,59834,59866,60112,60120,60209,60218,60222,60252,60353,60492,60510,60878,60882,60893,61067,61071,61077,61199,61369,61373,61383,61534,61538,61565,61589,61640,61694,61696,61745],[934,58211,507],{"id":58212},"accounting-app",[1061,58214,58215],{},[937,58216,58217,58219,58220,58223,58224,6030,58227,6030,58230,6030,58233,6030,58236,6030,58239,58242],{},[950,58218,1500],{}," compliance-heavy verticals — accounting, legal, healthcare records, insurance — where every change needs to be auditable, periods need to seal at year-end, attachments are normal, and labels need translation.\n",[950,58221,58222],{},"Bundle:"," core + ",[941,58225,58226],{},"withHistory",[941,58228,58229],{},"withPeriods",[941,58231,58232],{},"withBlobs",[941,58234,58235],{},"withI18n",[941,58237,58238],{},"withConsent",[941,58240,58241],{},"withReduce"," (~13,000 LOC).",[937,58244,58245,58246,58253],{},"Verified by: ",[1007,58247,58250],{"href":58248,"rel":58249},"https:\u002F\u002Fgithub.com\u002Fnoy-db\u002Fdocs\u002Fblob\u002Fmain\u002Fshowcases\u002Fsrc\u002Frecipe-accounting-app.recipe.test.ts",[3371],[941,58251,58252],{},"showcases\u002Fsrc\u002Frecipe-accounting-app.recipe.test.ts"," — the runnable form of this recipe.",[937,58255,58256,58257,58259,58260,58263,58264,58266,58267,58269,58270,58272],{},"noy-db is an encrypted document store: records are encrypted inside ",[941,58258,1086],{}," on the client before they ever reach a storage backend, and each tenant's data lives in its own ",[1007,58261,2694],{"href":58262},"..\u002Fcore\u002F01-overview"," — an isolated, independently keyed space of collections. Every capability beyond basic CRUD is an opt-in ",[950,58265,1047],{},": you import a ",[941,58268,943],{}," factory from a hub subpath and pass it to ",[941,58271,2134],{},"; services you don't import never enter your bundle. This recipe composes six of them into a working accounting backend.",[1022,58274,58276],{"id":58275},"what-this-gets-you","What this gets you",[1338,58278,58279,58288],{},[1341,58280,58281],{},[1344,58282,58283,58285],{},[1347,58284,21181],{},[1347,58286,58287],{},"What it adds",[1357,58289,58290,58315,58329,58338,58360,58376],{},[1344,58291,58292,58296],{},[1362,58293,58294],{},[941,58295,6719],{},[1362,58297,58298,58299,968,58301,968,58303,968,58305,58307,58308,2462,58311,58314],{},"Per-record version snapshots (",[941,58300,21840],{},[941,58302,21843],{},[941,58304,21846],{},[941,58306,21849],{},"), a hash-chained audit ledger, point-in-time reads via ",[941,58309,58310],{},"vault.at(timestamp)",[941,58312,58313],{},"vault.verifyBackupIntegrity()"," for tamper detection",[1344,58316,58317,58321],{},[1362,58318,58319],{},[941,58320,6757],{},[1362,58322,58323,1261,58325,58328],{},[941,58324,21482],{},[941,58326,58327],{},"openPeriod()"," with a write-guard that rejects writes to records whose business date falls inside a closed period",[1344,58330,58331,58335],{},[1362,58332,58333],{},[941,58334,6775],{},[1362,58336,58337],{},"Binary attachments (receipts, invoice PDFs) with content-addressed deduplication and MIME detection from magic bytes",[1344,58339,58340,58344],{},[1362,58341,58342],{},[941,58343,6739],{},[1362,58345,58346,58349,58350,968,58352,58355,58356,58359],{},[941,58347,58348],{},"dictKey()"," for shared label dictionaries (",[941,58351,6631],{},[941,58353,58354],{},"taxClass",") + ",[941,58357,58358],{},"i18nText()"," for free-form translatable fields",[1344,58361,58362,58366],{},[1362,58363,58364],{},[941,58365,6748],{},[1362,58367,58368,58369,58372,58373,58375],{},"GDPR\u002FPIPL-friendly consent audit log: ",[941,58370,58371],{},"vault.withConsent()"," scopes, ",[941,58374,21528],{}," queries",[1344,58377,58378,58382],{},[1362,58379,58380],{},[941,58381,21926],{},[1362,58383,58384,968,58386,968,58388,58390],{},[941,58385,28196],{},[941,58387,3236],{},[941,58389,27496],{}," and friends for reports",[1022,58392,58394],{"id":58393},"setup","Setup",[937,58396,960,58397,58399,58400,964,58403,34689,58405,4688,58407,58410,58411,17904,58413,58415],{},[941,58398,48494],{}," store takes any client exposing ",[941,58401,58402],{},"query(sql, params) → { rows }",[941,58404,51874],{},[941,58406,6673],{},[941,58408,58409],{},"Pool"," works as-is. For local development or tests, swap in ",[941,58412,20491],{},[941,58414,9638],{},"; nothing else changes.",[2217,58417,58419],{"className":2219,"code":58418,"language":2221,"meta":1037,"style":1037},"import { createNoydb } from '@noy-db\u002Fhub'\nimport { withHistory } from '@noy-db\u002Fhub\u002Fhistory'\nimport { withPeriods } from '@noy-db\u002Fhub\u002Fperiods'\nimport { withBlobs } from '@noy-db\u002Fhub\u002Fblobs'\nimport { withI18n } from '@noy-db\u002Fhub\u002Fi18n'\nimport { withConsent } from '@noy-db\u002Fhub\u002Fconsent'\nimport { withReduce } from '@noy-db\u002Fhub\u002Freduce'\nimport { toPostgres } from '@noy-db\u002Fto-postgres'\nimport { Pool } from 'pg'\n\nconst db = await createNoydb({\n  store: toPostgres({ client: new Pool({ connectionString: process.env.DATABASE_URL }) }),\n  user: 'admin@firm.example',\n  secret: process.env.NOYDB_SECRET!,\n\n  historyStrategy: withHistory(),\n  periodsStrategy: withPeriods(),\n  blobsStrategy: withBlobs(),\n  i18nStrategy: withI18n(),\n  consentStrategy: withConsent(),\n  reduceStrategy: withReduce(),\n})\n",[941,58420,58421,58439,58457,58477,58497,58517,58537,58555,58574,58593,58597,58613,58661,58676,58695,58699,58711,58724,58737,58750,58763,58775],{"__ignoreMap":1037},[2225,58422,58423,58425,58427,58429,58431,58433,58435,58437],{"class":2227,"line":2228},[2225,58424,2232],{"class":2231},[2225,58426,2236],{"class":2235},[2225,58428,2744],{"class":2239},[2225,58430,2243],{"class":2235},[2225,58432,2246],{"class":2231},[2225,58434,2249],{"class":2235},[2225,58436,1086],{"class":2252},[2225,58438,2256],{"class":2235},[2225,58440,58441,58443,58445,58447,58449,58451,58453,58455],{"class":2227,"line":1041},[2225,58442,2232],{"class":2231},[2225,58444,2236],{"class":2235},[2225,58446,4142],{"class":2239},[2225,58448,2243],{"class":2235},[2225,58450,2246],{"class":2231},[2225,58452,2249],{"class":2235},[2225,58454,4151],{"class":2252},[2225,58456,2256],{"class":2235},[2225,58458,58459,58461,58463,58466,58468,58470,58472,58475],{"class":2227,"line":1038},[2225,58460,2232],{"class":2231},[2225,58462,2236],{"class":2235},[2225,58464,58465],{"class":2239}," withPeriods",[2225,58467,2243],{"class":2235},[2225,58469,2246],{"class":2231},[2225,58471,2249],{"class":2235},[2225,58473,58474],{"class":2252},"@noy-db\u002Fhub\u002Fperiods",[2225,58476,2256],{"class":2235},[2225,58478,58479,58481,58483,58486,58488,58490,58492,58495],{"class":2227,"line":2288},[2225,58480,2232],{"class":2231},[2225,58482,2236],{"class":2235},[2225,58484,58485],{"class":2239}," withBlobs",[2225,58487,2243],{"class":2235},[2225,58489,2246],{"class":2231},[2225,58491,2249],{"class":2235},[2225,58493,58494],{"class":2252},"@noy-db\u002Fhub\u002Fblobs",[2225,58496,2256],{"class":2235},[2225,58498,58499,58501,58503,58506,58508,58510,58512,58515],{"class":2227,"line":2304},[2225,58500,2232],{"class":2231},[2225,58502,2236],{"class":2235},[2225,58504,58505],{"class":2239}," withI18n",[2225,58507,2243],{"class":2235},[2225,58509,2246],{"class":2231},[2225,58511,2249],{"class":2235},[2225,58513,58514],{"class":2252},"@noy-db\u002Fhub\u002Fi18n",[2225,58516,2256],{"class":2235},[2225,58518,58519,58521,58523,58526,58528,58530,58532,58535],{"class":2227,"line":2356},[2225,58520,2232],{"class":2231},[2225,58522,2236],{"class":2235},[2225,58524,58525],{"class":2239}," withConsent",[2225,58527,2243],{"class":2235},[2225,58529,2246],{"class":2231},[2225,58531,2249],{"class":2235},[2225,58533,58534],{"class":2252},"@noy-db\u002Fhub\u002Fconsent",[2225,58536,2256],{"class":2235},[2225,58538,58539,58541,58543,58545,58547,58549,58551,58553],{"class":2227,"line":2830},[2225,58540,2232],{"class":2231},[2225,58542,2236],{"class":2235},[2225,58544,27293],{"class":2239},[2225,58546,2243],{"class":2235},[2225,58548,2246],{"class":2231},[2225,58550,2249],{"class":2235},[2225,58552,27257],{"class":2252},[2225,58554,2256],{"class":2235},[2225,58556,58557,58559,58561,58564,58566,58568,58570,58572],{"class":2227,"line":2861},[2225,58558,2232],{"class":2231},[2225,58560,2236],{"class":2235},[2225,58562,58563],{"class":2239}," toPostgres",[2225,58565,2243],{"class":2235},[2225,58567,2246],{"class":2231},[2225,58569,2249],{"class":2235},[2225,58571,51853],{"class":2252},[2225,58573,2256],{"class":2235},[2225,58575,58576,58578,58580,58583,58585,58587,58589,58591],{"class":2227,"line":4246},[2225,58577,2232],{"class":2231},[2225,58579,2236],{"class":2235},[2225,58581,58582],{"class":2239}," Pool",[2225,58584,2243],{"class":2235},[2225,58586,2246],{"class":2231},[2225,58588,2249],{"class":2235},[2225,58590,51874],{"class":2252},[2225,58592,2256],{"class":2235},[2225,58594,58595],{"class":2227,"line":4253},[2225,58596,2285],{"emptyLinePlaceholder":2284},[2225,58598,58599,58601,58603,58605,58607,58609,58611],{"class":2227,"line":4259},[2225,58600,2775],{"class":2774},[2225,58602,2778],{"class":2239},[2225,58604,2781],{"class":2235},[2225,58606,2784],{"class":2231},[2225,58608,2744],{"class":2291},[2225,58610,2789],{"class":2239},[2225,58612,4200],{"class":2235},[2225,58614,58615,58617,58619,58621,58623,58625,58627,58629,58631,58633,58635,58637,58639,58641,58643,58645,58647,58649,58651,58653,58655,58657,58659],{"class":2227,"line":6548},[2225,58616,15608],{"class":2307},[2225,58618,2311],{"class":2235},[2225,58620,58563],{"class":2291},[2225,58622,2789],{"class":2239},[2225,58624,2792],{"class":2235},[2225,58626,25871],{"class":2307},[2225,58628,2311],{"class":2235},[2225,58630,8130],{"class":2235},[2225,58632,58582],{"class":2291},[2225,58634,2789],{"class":2239},[2225,58636,2792],{"class":2235},[2225,58638,54351],{"class":2307},[2225,58640,2311],{"class":2235},[2225,58642,36593],{"class":2239},[2225,58644,1520],{"class":2235},[2225,58646,183],{"class":2239},[2225,58648,1520],{"class":2235},[2225,58650,54364],{"class":2239},[2225,58652,2359],{"class":2235},[2225,58654,8495],{"class":2239},[2225,58656,2359],{"class":2235},[2225,58658,1381],{"class":2239},[2225,58660,1707],{"class":2235},[2225,58662,58663,58665,58667,58669,58672,58674],{"class":2227,"line":6576},[2225,58664,4205],{"class":2307},[2225,58666,2311],{"class":2235},[2225,58668,2249],{"class":2235},[2225,58670,58671],{"class":2252},"admin@firm.example",[2225,58673,2805],{"class":2235},[2225,58675,1707],{"class":2235},[2225,58677,58678,58680,58682,58684,58686,58688,58690,58692],{"class":2227,"line":6582},[2225,58679,10423],{"class":2307},[2225,58681,2311],{"class":2235},[2225,58683,36593],{"class":2239},[2225,58685,1520],{"class":2235},[2225,58687,183],{"class":2239},[2225,58689,1520],{"class":2235},[2225,58691,36440],{"class":2239},[2225,58693,58694],{"class":2235},"!,\n",[2225,58696,58697],{"class":2227,"line":6587},[2225,58698,2285],{"emptyLinePlaceholder":2284},[2225,58700,58701,58703,58705,58707,58709],{"class":2227,"line":7174},[2225,58702,4221],{"class":2307},[2225,58704,2311],{"class":2235},[2225,58706,4142],{"class":2291},[2225,58708,4228],{"class":2239},[2225,58710,1707],{"class":2235},[2225,58712,58713,58716,58718,58720,58722],{"class":2227,"line":7180},[2225,58714,58715],{"class":2307},"  periodsStrategy",[2225,58717,2311],{"class":2235},[2225,58719,58465],{"class":2291},[2225,58721,4228],{"class":2239},[2225,58723,1707],{"class":2235},[2225,58725,58726,58729,58731,58733,58735],{"class":2227,"line":7220},[2225,58727,58728],{"class":2307},"  blobsStrategy",[2225,58730,2311],{"class":2235},[2225,58732,58485],{"class":2291},[2225,58734,4228],{"class":2239},[2225,58736,1707],{"class":2235},[2225,58738,58739,58742,58744,58746,58748],{"class":2227,"line":7225},[2225,58740,58741],{"class":2307},"  i18nStrategy",[2225,58743,2311],{"class":2235},[2225,58745,58505],{"class":2291},[2225,58747,4228],{"class":2239},[2225,58749,1707],{"class":2235},[2225,58751,58752,58755,58757,58759,58761],{"class":2227,"line":7231},[2225,58753,58754],{"class":2307},"  consentStrategy",[2225,58756,2311],{"class":2235},[2225,58758,58525],{"class":2291},[2225,58760,4228],{"class":2239},[2225,58762,1707],{"class":2235},[2225,58764,58765,58767,58769,58771,58773],{"class":2227,"line":7267},[2225,58766,27365],{"class":2307},[2225,58768,2311],{"class":2235},[2225,58770,27293],{"class":2291},[2225,58772,4228],{"class":2239},[2225,58774,1707],{"class":2235},[2225,58776,58777,58779],{"class":2227,"line":7272},[2225,58778,2359],{"class":2235},[2225,58780,2362],{"class":2239},[1022,58782,58784],{"id":58783},"define-collections-with-the-right-options","Define collections with the right options",[937,58786,3289,58787,58789,58790,58793,58794,58796,58797,58800],{},[941,58788,21456],{}," field stores a stable key (",[941,58791,58792],{},"'paid'",") and resolves to a per-locale label on read; an ",[941,58795,20943],{}," field stores a ",[941,58798,58799],{},"{ locale: string }"," map and resolves to one locale's string on read.",[2217,58802,58804],{"className":2219,"code":58803,"language":2221,"meta":1037,"style":1037},"import { dictKey, i18nText } from '@noy-db\u002Fhub'\n\ninterface Invoice {\n  id: string\n  clientId: string\n  status: string         \u002F\u002F dictKey — stores the stable key, labels resolve per locale\n  description: Record\u003Cstring, string>  \u002F\u002F i18nText — one string per language\n  amount: number\n  date: string           \u002F\u002F YYYY-MM-DD — the business date used by closePeriod\n}\n\nconst vault = await db.openVault('firm-2026')\n\n\u002F\u002F Seed the shared status dictionary once per vault.\nawait vault.dictionary('status').putAll({\n  draft:   { en: 'Draft',    th: 'ฉบับร่าง',     ar: 'مسودة' },\n  open:    { en: 'Open',     th: 'เปิดใช้งาน',   ar: 'مفتوح' },\n  paid:    { en: 'Paid',     th: 'ชำระแล้ว',     ar: 'مدفوع' },\n  overdue: { en: 'Overdue',  th: 'เกินกำหนด',    ar: 'متأخر' },\n})\n\nconst invoices = vault.collection\u003CInvoice>('invoices', {\n  dictKeyFields: {\n    status: dictKey('status', ['draft', 'open', 'paid', 'overdue'] as const),\n  },\n  i18nFields: {\n    description: i18nText({ languages: ['en', 'th', 'ar'], required: 'all' }),\n  },\n})\n",[941,58805,58806,58830,58834,58842,58850,58859,58870,58892,58900,58911,58915,58919,58946,58950,58955,58985,59037,59087,59135,59185,59191,59195,59227,59236,59297,59301,59310,59375,59379],{"__ignoreMap":1037},[2225,58807,58808,58810,58812,58815,58817,58820,58822,58824,58826,58828],{"class":2227,"line":2228},[2225,58809,2232],{"class":2231},[2225,58811,2236],{"class":2235},[2225,58813,58814],{"class":2239}," dictKey",[2225,58816,2298],{"class":2235},[2225,58818,58819],{"class":2239}," i18nText",[2225,58821,2243],{"class":2235},[2225,58823,2246],{"class":2231},[2225,58825,2249],{"class":2235},[2225,58827,1086],{"class":2252},[2225,58829,2256],{"class":2235},[2225,58831,58832],{"class":2227,"line":1041},[2225,58833,2285],{"emptyLinePlaceholder":2284},[2225,58835,58836,58838,58840],{"class":2227,"line":1038},[2225,58837,6994],{"class":2774},[2225,58839,6343],{"class":6342},[2225,58841,2301],{"class":2235},[2225,58843,58844,58846,58848],{"class":2227,"line":2288},[2225,58845,28371],{"class":2307},[2225,58847,2311],{"class":2235},[2225,58849,7014],{"class":6342},[2225,58851,58852,58855,58857],{"class":2227,"line":2304},[2225,58853,58854],{"class":2307},"  clientId",[2225,58856,2311],{"class":2235},[2225,58858,7014],{"class":6342},[2225,58860,58861,58863,58865,58867],{"class":2227,"line":2356},[2225,58862,26171],{"class":2307},[2225,58864,2311],{"class":2235},[2225,58866,6366],{"class":6342},[2225,58868,58869],{"class":2763},"         \u002F\u002F dictKey — stores the stable key, labels resolve per locale\n",[2225,58871,58872,58875,58877,58879,58881,58883,58885,58887,58889],{"class":2227,"line":2830},[2225,58873,58874],{"class":2307},"  description",[2225,58876,2311],{"class":2235},[2225,58878,22527],{"class":6342},[2225,58880,6474],{"class":2235},[2225,58882,7259],{"class":6342},[2225,58884,2298],{"class":2235},[2225,58886,6366],{"class":6342},[2225,58888,6480],{"class":2235},[2225,58890,58891],{"class":2763},"  \u002F\u002F i18nText — one string per language\n",[2225,58893,58894,58896,58898],{"class":2227,"line":2861},[2225,58895,28389],{"class":2307},[2225,58897,2311],{"class":2235},[2225,58899,12024],{"class":6342},[2225,58901,58902,58904,58906,58908],{"class":2227,"line":4246},[2225,58903,28466],{"class":2307},[2225,58905,2311],{"class":2235},[2225,58907,6366],{"class":6342},[2225,58909,58910],{"class":2763},"           \u002F\u002F YYYY-MM-DD — the business date used by closePeriod\n",[2225,58912,58913],{"class":2227,"line":4253},[2225,58914,7351],{"class":2235},[2225,58916,58917],{"class":2227,"line":4259},[2225,58918,2285],{"emptyLinePlaceholder":2284},[2225,58920,58921,58923,58925,58927,58929,58931,58933,58935,58937,58939,58942,58944],{"class":2227,"line":6548},[2225,58922,2775],{"class":2774},[2225,58924,2835],{"class":2239},[2225,58926,2781],{"class":2235},[2225,58928,2784],{"class":2231},[2225,58930,2842],{"class":2239},[2225,58932,1520],{"class":2235},[2225,58934,2847],{"class":2291},[2225,58936,2789],{"class":2239},[2225,58938,2805],{"class":2235},[2225,58940,58941],{"class":2252},"firm-2026",[2225,58943,2805],{"class":2235},[2225,58945,2362],{"class":2239},[2225,58947,58948],{"class":2227,"line":6576},[2225,58949,2285],{"emptyLinePlaceholder":2284},[2225,58951,58952],{"class":2227,"line":6582},[2225,58953,58954],{"class":2763},"\u002F\u002F Seed the shared status dictionary once per vault.\n",[2225,58956,58957,58959,58961,58963,58966,58968,58970,58972,58974,58976,58978,58981,58983],{"class":2227,"line":6587},[2225,58958,6500],{"class":2231},[2225,58960,6466],{"class":2239},[2225,58962,1520],{"class":2235},[2225,58964,58965],{"class":2291},"dictionary",[2225,58967,2789],{"class":2239},[2225,58969,2805],{"class":2235},[2225,58971,6631],{"class":2252},[2225,58973,2805],{"class":2235},[2225,58975,1381],{"class":2239},[2225,58977,1520],{"class":2235},[2225,58979,58980],{"class":2291},"putAll",[2225,58982,2789],{"class":2239},[2225,58984,4200],{"class":2235},[2225,58986,58987,58990,58992,58995,58998,59000,59002,59005,59007,59009,59012,59014,59016,59019,59021,59023,59026,59028,59030,59033,59035],{"class":2227,"line":7174},[2225,58988,58989],{"class":2307},"  draft",[2225,58991,2311],{"class":2235},[2225,58993,58994],{"class":2235},"   {",[2225,58996,58997],{"class":2307}," en",[2225,58999,2311],{"class":2235},[2225,59001,2249],{"class":2235},[2225,59003,59004],{"class":2252},"Draft",[2225,59006,2805],{"class":2235},[2225,59008,2298],{"class":2235},[2225,59010,59011],{"class":2307},"    th",[2225,59013,2311],{"class":2235},[2225,59015,2249],{"class":2235},[2225,59017,59018],{"class":2252},"ฉบับร่าง",[2225,59020,2805],{"class":2235},[2225,59022,2298],{"class":2235},[2225,59024,59025],{"class":2307},"     ar",[2225,59027,2311],{"class":2235},[2225,59029,2249],{"class":2235},[2225,59031,59032],{"class":2252},"مسودة",[2225,59034,2805],{"class":2235},[2225,59036,2353],{"class":2235},[2225,59038,59039,59042,59044,59046,59048,59050,59052,59055,59057,59059,59062,59064,59066,59069,59071,59073,59076,59078,59080,59083,59085],{"class":2227,"line":7180},[2225,59040,59041],{"class":2307},"  open",[2225,59043,2311],{"class":2235},[2225,59045,32496],{"class":2235},[2225,59047,58997],{"class":2307},[2225,59049,2311],{"class":2235},[2225,59051,2249],{"class":2235},[2225,59053,59054],{"class":2252},"Open",[2225,59056,2805],{"class":2235},[2225,59058,2298],{"class":2235},[2225,59060,59061],{"class":2307},"     th",[2225,59063,2311],{"class":2235},[2225,59065,2249],{"class":2235},[2225,59067,59068],{"class":2252},"เปิดใช้งาน",[2225,59070,2805],{"class":2235},[2225,59072,2298],{"class":2235},[2225,59074,59075],{"class":2307},"   ar",[2225,59077,2311],{"class":2235},[2225,59079,2249],{"class":2235},[2225,59081,59082],{"class":2252},"مفتوح",[2225,59084,2805],{"class":2235},[2225,59086,2353],{"class":2235},[2225,59088,59089,59092,59094,59096,59098,59100,59102,59105,59107,59109,59111,59113,59115,59118,59120,59122,59124,59126,59128,59131,59133],{"class":2227,"line":7220},[2225,59090,59091],{"class":2307},"  paid",[2225,59093,2311],{"class":2235},[2225,59095,32496],{"class":2235},[2225,59097,58997],{"class":2307},[2225,59099,2311],{"class":2235},[2225,59101,2249],{"class":2235},[2225,59103,59104],{"class":2252},"Paid",[2225,59106,2805],{"class":2235},[2225,59108,2298],{"class":2235},[2225,59110,59061],{"class":2307},[2225,59112,2311],{"class":2235},[2225,59114,2249],{"class":2235},[2225,59116,59117],{"class":2252},"ชำระแล้ว",[2225,59119,2805],{"class":2235},[2225,59121,2298],{"class":2235},[2225,59123,59025],{"class":2307},[2225,59125,2311],{"class":2235},[2225,59127,2249],{"class":2235},[2225,59129,59130],{"class":2252},"مدفوع",[2225,59132,2805],{"class":2235},[2225,59134,2353],{"class":2235},[2225,59136,59137,59140,59142,59144,59146,59148,59150,59153,59155,59157,59160,59162,59164,59167,59169,59171,59174,59176,59178,59181,59183],{"class":2227,"line":7225},[2225,59138,59139],{"class":2307},"  overdue",[2225,59141,2311],{"class":2235},[2225,59143,2236],{"class":2235},[2225,59145,58997],{"class":2307},[2225,59147,2311],{"class":2235},[2225,59149,2249],{"class":2235},[2225,59151,59152],{"class":2252},"Overdue",[2225,59154,2805],{"class":2235},[2225,59156,2298],{"class":2235},[2225,59158,59159],{"class":2307},"  th",[2225,59161,2311],{"class":2235},[2225,59163,2249],{"class":2235},[2225,59165,59166],{"class":2252},"เกินกำหนด",[2225,59168,2805],{"class":2235},[2225,59170,2298],{"class":2235},[2225,59172,59173],{"class":2307},"    ar",[2225,59175,2311],{"class":2235},[2225,59177,2249],{"class":2235},[2225,59179,59180],{"class":2252},"متأخر",[2225,59182,2805],{"class":2235},[2225,59184,2353],{"class":2235},[2225,59186,59187,59189],{"class":2227,"line":7231},[2225,59188,2359],{"class":2235},[2225,59190,2362],{"class":2239},[2225,59192,59193],{"class":2227,"line":7267},[2225,59194,2285],{"emptyLinePlaceholder":2284},[2225,59196,59197,59199,59201,59203,59205,59207,59209,59211,59213,59215,59217,59219,59221,59223,59225],{"class":2227,"line":7272},[2225,59198,2775],{"class":2774},[2225,59200,6461],{"class":2239},[2225,59202,2781],{"class":2235},[2225,59204,6466],{"class":2239},[2225,59206,1520],{"class":2235},[2225,59208,6471],{"class":2291},[2225,59210,6474],{"class":2235},[2225,59212,6477],{"class":6342},[2225,59214,6480],{"class":2235},[2225,59216,2789],{"class":2239},[2225,59218,2805],{"class":2235},[2225,59220,6487],{"class":2252},[2225,59222,2805],{"class":2235},[2225,59224,2298],{"class":2235},[2225,59226,2301],{"class":2235},[2225,59228,59229,59232,59234],{"class":2227,"line":7278},[2225,59230,59231],{"class":2307},"  dictKeyFields",[2225,59233,2311],{"class":2235},[2225,59235,2301],{"class":2235},[2225,59237,59238,59241,59243,59245,59247,59249,59251,59253,59255,59257,59259,59261,59263,59265,59267,59269,59271,59273,59275,59277,59279,59281,59283,59285,59287,59289,59291,59293,59295],{"class":2227,"line":7303},[2225,59239,59240],{"class":2307},"    status",[2225,59242,2311],{"class":2235},[2225,59244,58814],{"class":2291},[2225,59246,2789],{"class":2239},[2225,59248,2805],{"class":2235},[2225,59250,6631],{"class":2252},[2225,59252,2805],{"class":2235},[2225,59254,2298],{"class":2235},[2225,59256,8667],{"class":2239},[2225,59258,2805],{"class":2235},[2225,59260,28429],{"class":2252},[2225,59262,2805],{"class":2235},[2225,59264,2298],{"class":2235},[2225,59266,2249],{"class":2235},[2225,59268,26178],{"class":2252},[2225,59270,2805],{"class":2235},[2225,59272,2298],{"class":2235},[2225,59274,2249],{"class":2235},[2225,59276,6539],{"class":2252},[2225,59278,2805],{"class":2235},[2225,59280,2298],{"class":2235},[2225,59282,2249],{"class":2235},[2225,59284,28454],{"class":2252},[2225,59286,2805],{"class":2235},[2225,59288,8890],{"class":2239},[2225,59290,4899],{"class":2231},[2225,59292,8083],{"class":2774},[2225,59294,1381],{"class":2239},[2225,59296,1707],{"class":2235},[2225,59298,59299],{"class":2227,"line":7308},[2225,59300,19017],{"class":2235},[2225,59302,59303,59306,59308],{"class":2227,"line":7314},[2225,59304,59305],{"class":2307},"  i18nFields",[2225,59307,2311],{"class":2235},[2225,59309,2301],{"class":2235},[2225,59311,59312,59315,59317,59319,59321,59323,59326,59328,59330,59332,59335,59337,59339,59341,59343,59345,59347,59349,59352,59354,59356,59358,59360,59362,59364,59367,59369,59371,59373],{"class":2227,"line":7348},[2225,59313,59314],{"class":2307},"    description",[2225,59316,2311],{"class":2235},[2225,59318,58819],{"class":2291},[2225,59320,2789],{"class":2239},[2225,59322,2792],{"class":2235},[2225,59324,59325],{"class":2307}," languages",[2225,59327,2311],{"class":2235},[2225,59329,8667],{"class":2239},[2225,59331,2805],{"class":2235},[2225,59333,59334],{"class":2252},"en",[2225,59336,2805],{"class":2235},[2225,59338,2298],{"class":2235},[2225,59340,2249],{"class":2235},[2225,59342,1347],{"class":2252},[2225,59344,2805],{"class":2235},[2225,59346,2298],{"class":2235},[2225,59348,2249],{"class":2235},[2225,59350,59351],{"class":2252},"ar",[2225,59353,2805],{"class":2235},[2225,59355,8803],{"class":2239},[2225,59357,2298],{"class":2235},[2225,59359,8299],{"class":2307},[2225,59361,2311],{"class":2235},[2225,59363,2249],{"class":2235},[2225,59365,59366],{"class":2252},"all",[2225,59368,2805],{"class":2235},[2225,59370,2243],{"class":2235},[2225,59372,1381],{"class":2239},[2225,59374,1707],{"class":2235},[2225,59376,59377],{"class":2227,"line":8613},[2225,59378,19017],{"class":2235},[2225,59380,59381,59383],{"class":2227,"line":8633},[2225,59382,2359],{"class":2235},[2225,59384,2362],{"class":2239},[1022,59386,59388],{"id":59387},"write-every-change-is-auditable","Write — every change is auditable",[2217,59390,59392],{"className":2219,"code":59391,"language":2221,"meta":1037,"style":1037},"await invoices.put('inv-001', {\n  id: 'inv-001',\n  clientId: 'client-42',\n  status: 'open',\n  description: {\n    en: 'Quarterly tax filing',\n    th: 'การยื่นภาษีรายไตรมาส',\n    ar: 'الإقرار الضريبي الفصلي',\n  },\n  amount: 15_000,\n  date: '2026-03-15',\n})\n\n\u002F\u002F History is recorded automatically. Edit again (say, in April):\nawait invoices.put('inv-001', {\n  ...(await invoices.get('inv-001'))!,\n  status: 'paid',\n})\n",[941,59393,59394,59417,59431,59446,59460,59468,59484,59499,59514,59518,59529,59544,59550,59554,59559,59581,59608,59622],{"__ignoreMap":1037},[2225,59395,59396,59398,59400,59402,59404,59406,59408,59411,59413,59415],{"class":2227,"line":2228},[2225,59397,6500],{"class":2231},[2225,59399,6503],{"class":2239},[2225,59401,1520],{"class":2235},[2225,59403,3161],{"class":2291},[2225,59405,2789],{"class":2239},[2225,59407,2805],{"class":2235},[2225,59409,59410],{"class":2252},"inv-001",[2225,59412,2805],{"class":2235},[2225,59414,2298],{"class":2235},[2225,59416,2301],{"class":2235},[2225,59418,59419,59421,59423,59425,59427,59429],{"class":2227,"line":1041},[2225,59420,28371],{"class":2307},[2225,59422,2311],{"class":2235},[2225,59424,2249],{"class":2235},[2225,59426,59410],{"class":2252},[2225,59428,2805],{"class":2235},[2225,59430,1707],{"class":2235},[2225,59432,59433,59435,59437,59439,59442,59444],{"class":2227,"line":1038},[2225,59434,58854],{"class":2307},[2225,59436,2311],{"class":2235},[2225,59438,2249],{"class":2235},[2225,59440,59441],{"class":2252},"client-42",[2225,59443,2805],{"class":2235},[2225,59445,1707],{"class":2235},[2225,59447,59448,59450,59452,59454,59456,59458],{"class":2227,"line":2288},[2225,59449,26171],{"class":2307},[2225,59451,2311],{"class":2235},[2225,59453,2249],{"class":2235},[2225,59455,26178],{"class":2252},[2225,59457,2805],{"class":2235},[2225,59459,1707],{"class":2235},[2225,59461,59462,59464,59466],{"class":2227,"line":2304},[2225,59463,58874],{"class":2307},[2225,59465,2311],{"class":2235},[2225,59467,2301],{"class":2235},[2225,59469,59470,59473,59475,59477,59480,59482],{"class":2227,"line":2356},[2225,59471,59472],{"class":2307},"    en",[2225,59474,2311],{"class":2235},[2225,59476,2249],{"class":2235},[2225,59478,59479],{"class":2252},"Quarterly tax filing",[2225,59481,2805],{"class":2235},[2225,59483,1707],{"class":2235},[2225,59485,59486,59488,59490,59492,59495,59497],{"class":2227,"line":2830},[2225,59487,59011],{"class":2307},[2225,59489,2311],{"class":2235},[2225,59491,2249],{"class":2235},[2225,59493,59494],{"class":2252},"การยื่นภาษีรายไตรมาส",[2225,59496,2805],{"class":2235},[2225,59498,1707],{"class":2235},[2225,59500,59501,59503,59505,59507,59510,59512],{"class":2227,"line":2861},[2225,59502,59173],{"class":2307},[2225,59504,2311],{"class":2235},[2225,59506,2249],{"class":2235},[2225,59508,59509],{"class":2252},"الإقرار الضريبي الفصلي",[2225,59511,2805],{"class":2235},[2225,59513,1707],{"class":2235},[2225,59515,59516],{"class":2227,"line":4246},[2225,59517,19017],{"class":2235},[2225,59519,59520,59522,59524,59527],{"class":2227,"line":4253},[2225,59521,28389],{"class":2307},[2225,59523,2311],{"class":2235},[2225,59525,59526],{"class":6527}," 15_000",[2225,59528,1707],{"class":2235},[2225,59530,59531,59533,59535,59537,59540,59542],{"class":2227,"line":4259},[2225,59532,28466],{"class":2307},[2225,59534,2311],{"class":2235},[2225,59536,2249],{"class":2235},[2225,59538,59539],{"class":2252},"2026-03-15",[2225,59541,2805],{"class":2235},[2225,59543,1707],{"class":2235},[2225,59545,59546,59548],{"class":2227,"line":6548},[2225,59547,2359],{"class":2235},[2225,59549,2362],{"class":2239},[2225,59551,59552],{"class":2227,"line":6576},[2225,59553,2285],{"emptyLinePlaceholder":2284},[2225,59555,59556],{"class":2227,"line":6582},[2225,59557,59558],{"class":2763},"\u002F\u002F History is recorded automatically. Edit again (say, in April):\n",[2225,59560,59561,59563,59565,59567,59569,59571,59573,59575,59577,59579],{"class":2227,"line":6587},[2225,59562,6500],{"class":2231},[2225,59564,6503],{"class":2239},[2225,59566,1520],{"class":2235},[2225,59568,3161],{"class":2291},[2225,59570,2789],{"class":2239},[2225,59572,2805],{"class":2235},[2225,59574,59410],{"class":2252},[2225,59576,2805],{"class":2235},[2225,59578,2298],{"class":2235},[2225,59580,2301],{"class":2235},[2225,59582,59583,59586,59588,59590,59592,59594,59596,59598,59600,59602,59604,59606],{"class":2227,"line":7174},[2225,59584,59585],{"class":2235},"  ...",[2225,59587,2789],{"class":2239},[2225,59589,6500],{"class":2231},[2225,59591,6503],{"class":2239},[2225,59593,1520],{"class":2235},[2225,59595,3158],{"class":2291},[2225,59597,2789],{"class":2239},[2225,59599,2805],{"class":2235},[2225,59601,59410],{"class":2252},[2225,59603,2805],{"class":2235},[2225,59605,8454],{"class":2239},[2225,59607,58694],{"class":2235},[2225,59609,59610,59612,59614,59616,59618,59620],{"class":2227,"line":7180},[2225,59611,26171],{"class":2307},[2225,59613,2311],{"class":2235},[2225,59615,2249],{"class":2235},[2225,59617,6539],{"class":2252},[2225,59619,2805],{"class":2235},[2225,59621,1707],{"class":2235},[2225,59623,59624,59626],{"class":2227,"line":7220},[2225,59625,2359],{"class":2235},[2225,59627,2362],{"class":2239},[937,59629,59630,59631,59633,59634,59636,59637,59639],{},"A locale-aware read resolves ",[941,59632,20943],{}," fields to the requested language and adds a ",[941,59635,24050],{}," virtual field for each ",[941,59638,21456],{}," field:",[2217,59641,59643],{"className":2219,"code":59642,"language":2221,"meta":1037,"style":1037},"const th = await invoices.get('inv-001', { locale: 'th' })\n",[941,59644,59645],{"__ignoreMap":1037},[2225,59646,59647,59649,59652,59654,59656,59658,59660,59662,59664,59666,59668,59670,59672,59674,59676,59678,59680,59682,59684,59686],{"class":2227,"line":2228},[2225,59648,2775],{"class":2774},[2225,59650,59651],{"class":2239}," th ",[2225,59653,2781],{"class":2235},[2225,59655,2784],{"class":2231},[2225,59657,6503],{"class":2239},[2225,59659,1520],{"class":2235},[2225,59661,3158],{"class":2291},[2225,59663,2789],{"class":2239},[2225,59665,2805],{"class":2235},[2225,59667,59410],{"class":2252},[2225,59669,2805],{"class":2235},[2225,59671,2298],{"class":2235},[2225,59673,2236],{"class":2235},[2225,59675,24003],{"class":2307},[2225,59677,2311],{"class":2235},[2225,59679,2249],{"class":2235},[2225,59681,1347],{"class":2252},[2225,59683,2805],{"class":2235},[2225,59685,2243],{"class":2235},[2225,59687,2362],{"class":2239},[2217,59689,59691],{"className":6604,"code":59690,"language":141,"meta":1037,"style":1037},"{\n  \"id\": \"inv-001\",\n  \"clientId\": \"client-42\",\n  \"status\": \"paid\",\n  \"statusLabel\": \"ชำระแล้ว\",\n  \"description\": \"การยื่นภาษีรายไตรมาส\",\n  \"amount\": 15000,\n  \"date\": \"2026-03-15\"\n}\n",[941,59692,59693,59697,59715,59734,59752,59771,59790,59805,59821],{"__ignoreMap":1037},[2225,59694,59695],{"class":2227,"line":2228},[2225,59696,4200],{"class":2235},[2225,59698,59699,59701,59703,59705,59707,59709,59711,59713],{"class":2227,"line":1041},[2225,59700,9368],{"class":2235},[2225,59702,6983],{"class":2774},[2225,59704,6620],{"class":2235},[2225,59706,2311],{"class":2235},[2225,59708,6614],{"class":2235},[2225,59710,59410],{"class":2252},[2225,59712,6620],{"class":2235},[2225,59714,1707],{"class":2235},[2225,59716,59717,59719,59722,59724,59726,59728,59730,59732],{"class":2227,"line":1038},[2225,59718,9368],{"class":2235},[2225,59720,59721],{"class":2774},"clientId",[2225,59723,6620],{"class":2235},[2225,59725,2311],{"class":2235},[2225,59727,6614],{"class":2235},[2225,59729,59441],{"class":2252},[2225,59731,6620],{"class":2235},[2225,59733,1707],{"class":2235},[2225,59735,59736,59738,59740,59742,59744,59746,59748,59750],{"class":2227,"line":2288},[2225,59737,9368],{"class":2235},[2225,59739,6631],{"class":2774},[2225,59741,6620],{"class":2235},[2225,59743,2311],{"class":2235},[2225,59745,6614],{"class":2235},[2225,59747,6539],{"class":2252},[2225,59749,6620],{"class":2235},[2225,59751,1707],{"class":2235},[2225,59753,59754,59756,59759,59761,59763,59765,59767,59769],{"class":2227,"line":2304},[2225,59755,9368],{"class":2235},[2225,59757,59758],{"class":2774},"statusLabel",[2225,59760,6620],{"class":2235},[2225,59762,2311],{"class":2235},[2225,59764,6614],{"class":2235},[2225,59766,59117],{"class":2252},[2225,59768,6620],{"class":2235},[2225,59770,1707],{"class":2235},[2225,59772,59773,59775,59778,59780,59782,59784,59786,59788],{"class":2227,"line":2356},[2225,59774,9368],{"class":2235},[2225,59776,59777],{"class":2774},"description",[2225,59779,6620],{"class":2235},[2225,59781,2311],{"class":2235},[2225,59783,6614],{"class":2235},[2225,59785,59494],{"class":2252},[2225,59787,6620],{"class":2235},[2225,59789,1707],{"class":2235},[2225,59791,59792,59794,59796,59798,59800,59803],{"class":2227,"line":2830},[2225,59793,9368],{"class":2235},[2225,59795,6617],{"class":2774},[2225,59797,6620],{"class":2235},[2225,59799,2311],{"class":2235},[2225,59801,59802],{"class":6527}," 15000",[2225,59804,1707],{"class":2235},[2225,59806,59807,59809,59811,59813,59815,59817,59819],{"class":2227,"line":2861},[2225,59808,9368],{"class":2235},[2225,59810,26305],{"class":2774},[2225,59812,6620],{"class":2235},[2225,59814,2311],{"class":2235},[2225,59816,6614],{"class":2235},[2225,59818,59539],{"class":2252},[2225,59820,9449],{"class":2235},[2225,59822,59823],{"class":2227,"line":4246},[2225,59824,7351],{"class":2235},[937,59826,59827,59830,59831,2311],{},[941,59828,59829],{},"collection.history(id)"," returns the superseded versions, newest first — the current version lives in the collection itself. Each entry is ",[941,59832,59833],{},"{ version, timestamp, userId, record }",[2217,59835,59837],{"className":2219,"code":59836,"language":2221,"meta":1037,"style":1037},"const history = await invoices.history('inv-001')\n",[941,59838,59839],{"__ignoreMap":1037},[2225,59840,59841,59843,59846,59848,59850,59852,59854,59856,59858,59860,59862,59864],{"class":2227,"line":2228},[2225,59842,2775],{"class":2774},[2225,59844,59845],{"class":2239}," history ",[2225,59847,2781],{"class":2235},[2225,59849,2784],{"class":2231},[2225,59851,6503],{"class":2239},[2225,59853,1520],{"class":2235},[2225,59855,697],{"class":2291},[2225,59857,2789],{"class":2239},[2225,59859,2805],{"class":2235},[2225,59861,59410],{"class":2252},[2225,59863,2805],{"class":2235},[2225,59865,2362],{"class":2239},[2217,59867,59869],{"className":6604,"code":59868,"language":141,"meta":1037,"style":1037},"[\n  {\n    \"version\": 1,\n    \"timestamp\": \"2026-03-15T09:12:44.318Z\",\n    \"userId\": \"admin@firm.example\",\n    \"record\": {\n      \"id\": \"inv-001\",\n      \"clientId\": \"client-42\",\n      \"status\": \"open\",\n      \"description\": {\n        \"en\": \"Quarterly tax filing\",\n        \"th\": \"การยื่นภาษีรายไตรมาส\",\n        \"ar\": \"الإقرار الضريبي الفصلي\"\n      },\n      \"amount\": 15000,\n      \"date\": \"2026-03-15\"\n    }\n  }\n]\n",[941,59870,59871,59875,59880,59894,59914,59932,59944,59963,59981,59999,60011,60030,60048,60064,60069,60083,60099,60104,60108],{"__ignoreMap":1037},[2225,59872,59873],{"class":2227,"line":2228},[2225,59874,26349],{"class":2235},[2225,59876,59877],{"class":2227,"line":1041},[2225,59878,59879],{"class":2235},"  {\n",[2225,59881,59882,59884,59886,59888,59890,59892],{"class":2227,"line":1038},[2225,59883,14201],{"class":2235},[2225,59885,52564],{"class":2774},[2225,59887,6620],{"class":2235},[2225,59889,2311],{"class":2235},[2225,59891,9377],{"class":6527},[2225,59893,1707],{"class":2235},[2225,59895,59896,59898,59901,59903,59905,59907,59910,59912],{"class":2227,"line":2288},[2225,59897,14201],{"class":2235},[2225,59899,59900],{"class":2774},"timestamp",[2225,59902,6620],{"class":2235},[2225,59904,2311],{"class":2235},[2225,59906,6614],{"class":2235},[2225,59908,59909],{"class":2252},"2026-03-15T09:12:44.318Z",[2225,59911,6620],{"class":2235},[2225,59913,1707],{"class":2235},[2225,59915,59916,59918,59920,59922,59924,59926,59928,59930],{"class":2227,"line":2304},[2225,59917,14201],{"class":2235},[2225,59919,10749],{"class":2774},[2225,59921,6620],{"class":2235},[2225,59923,2311],{"class":2235},[2225,59925,6614],{"class":2235},[2225,59927,58671],{"class":2252},[2225,59929,6620],{"class":2235},[2225,59931,1707],{"class":2235},[2225,59933,59934,59936,59938,59940,59942],{"class":2227,"line":2356},[2225,59935,14201],{"class":2235},[2225,59937,24158],{"class":2774},[2225,59939,6620],{"class":2235},[2225,59941,2311],{"class":2235},[2225,59943,2301],{"class":2235},[2225,59945,59946,59949,59951,59953,59955,59957,59959,59961],{"class":2227,"line":2830},[2225,59947,59948],{"class":2235},"      \"",[2225,59950,6983],{"class":6342},[2225,59952,6620],{"class":2235},[2225,59954,2311],{"class":2235},[2225,59956,6614],{"class":2235},[2225,59958,59410],{"class":2252},[2225,59960,6620],{"class":2235},[2225,59962,1707],{"class":2235},[2225,59964,59965,59967,59969,59971,59973,59975,59977,59979],{"class":2227,"line":2861},[2225,59966,59948],{"class":2235},[2225,59968,59721],{"class":6342},[2225,59970,6620],{"class":2235},[2225,59972,2311],{"class":2235},[2225,59974,6614],{"class":2235},[2225,59976,59441],{"class":2252},[2225,59978,6620],{"class":2235},[2225,59980,1707],{"class":2235},[2225,59982,59983,59985,59987,59989,59991,59993,59995,59997],{"class":2227,"line":4246},[2225,59984,59948],{"class":2235},[2225,59986,6631],{"class":6342},[2225,59988,6620],{"class":2235},[2225,59990,2311],{"class":2235},[2225,59992,6614],{"class":2235},[2225,59994,26178],{"class":2252},[2225,59996,6620],{"class":2235},[2225,59998,1707],{"class":2235},[2225,60000,60001,60003,60005,60007,60009],{"class":2227,"line":4253},[2225,60002,59948],{"class":2235},[2225,60004,59777],{"class":6342},[2225,60006,6620],{"class":2235},[2225,60008,2311],{"class":2235},[2225,60010,2301],{"class":2235},[2225,60012,60013,60016,60018,60020,60022,60024,60026,60028],{"class":2227,"line":4259},[2225,60014,60015],{"class":2235},"        \"",[2225,60017,59334],{"class":6527},[2225,60019,6620],{"class":2235},[2225,60021,2311],{"class":2235},[2225,60023,6614],{"class":2235},[2225,60025,59479],{"class":2252},[2225,60027,6620],{"class":2235},[2225,60029,1707],{"class":2235},[2225,60031,60032,60034,60036,60038,60040,60042,60044,60046],{"class":2227,"line":6548},[2225,60033,60015],{"class":2235},[2225,60035,1347],{"class":6527},[2225,60037,6620],{"class":2235},[2225,60039,2311],{"class":2235},[2225,60041,6614],{"class":2235},[2225,60043,59494],{"class":2252},[2225,60045,6620],{"class":2235},[2225,60047,1707],{"class":2235},[2225,60049,60050,60052,60054,60056,60058,60060,60062],{"class":2227,"line":6576},[2225,60051,60015],{"class":2235},[2225,60053,59351],{"class":6527},[2225,60055,6620],{"class":2235},[2225,60057,2311],{"class":2235},[2225,60059,6614],{"class":2235},[2225,60061,59509],{"class":2252},[2225,60063,9449],{"class":2235},[2225,60065,60066],{"class":2227,"line":6582},[2225,60067,60068],{"class":2235},"      },\n",[2225,60070,60071,60073,60075,60077,60079,60081],{"class":2227,"line":6587},[2225,60072,59948],{"class":2235},[2225,60074,6617],{"class":6342},[2225,60076,6620],{"class":2235},[2225,60078,2311],{"class":2235},[2225,60080,59802],{"class":6527},[2225,60082,1707],{"class":2235},[2225,60084,60085,60087,60089,60091,60093,60095,60097],{"class":2227,"line":7174},[2225,60086,59948],{"class":2235},[2225,60088,26305],{"class":6342},[2225,60090,6620],{"class":2235},[2225,60092,2311],{"class":2235},[2225,60094,6614],{"class":2235},[2225,60096,59539],{"class":2252},[2225,60098,9449],{"class":2235},[2225,60100,60101],{"class":2227,"line":7180},[2225,60102,60103],{"class":2235},"    }\n",[2225,60105,60106],{"class":2227,"line":7220},[2225,60107,7632],{"class":2235},[2225,60109,60110],{"class":2227,"line":7225},[2225,60111,26484],{"class":2235},[937,60113,60114,60115,60117,60118,6255],{},"To read what a record looked like at a past instant, ",[941,60116,21862],{}," returns a read-only view of the whole vault at that timestamp (an ISO string or a ",[941,60119,26712],{},[2217,60121,60123],{"className":2219,"code":60122,"language":2221,"meta":1037,"style":1037},"const q1End = vault.at('2026-03-31T23:59:59Z')\nconst asOfQ1 = await q1End.collection\u003CInvoice>('invoices').get('inv-001')\n\u002F\u002F → { id: 'inv-001', status: 'open', amount: 15000, date: '2026-03-15', ... }\n\u002F\u002F   — version 1: the April status edit hadn't happened yet at that instant\n",[941,60124,60125,60151,60199,60204],{"__ignoreMap":1037},[2225,60126,60127,60129,60132,60134,60136,60138,60140,60142,60144,60147,60149],{"class":2227,"line":2228},[2225,60128,2775],{"class":2774},[2225,60130,60131],{"class":2239}," q1End ",[2225,60133,2781],{"class":2235},[2225,60135,6466],{"class":2239},[2225,60137,1520],{"class":2235},[2225,60139,4887],{"class":2291},[2225,60141,2789],{"class":2239},[2225,60143,2805],{"class":2235},[2225,60145,60146],{"class":2252},"2026-03-31T23:59:59Z",[2225,60148,2805],{"class":2235},[2225,60150,2362],{"class":2239},[2225,60152,60153,60155,60158,60160,60162,60165,60167,60169,60171,60173,60175,60177,60179,60181,60183,60185,60187,60189,60191,60193,60195,60197],{"class":2227,"line":1041},[2225,60154,2775],{"class":2774},[2225,60156,60157],{"class":2239}," asOfQ1 ",[2225,60159,2781],{"class":2235},[2225,60161,2784],{"class":2231},[2225,60163,60164],{"class":2239}," q1End",[2225,60166,1520],{"class":2235},[2225,60168,6471],{"class":2291},[2225,60170,6474],{"class":2235},[2225,60172,6477],{"class":6342},[2225,60174,6480],{"class":2235},[2225,60176,2789],{"class":2239},[2225,60178,2805],{"class":2235},[2225,60180,6487],{"class":2252},[2225,60182,2805],{"class":2235},[2225,60184,1381],{"class":2239},[2225,60186,1520],{"class":2235},[2225,60188,3158],{"class":2291},[2225,60190,2789],{"class":2239},[2225,60192,2805],{"class":2235},[2225,60194,59410],{"class":2252},[2225,60196,2805],{"class":2235},[2225,60198,2362],{"class":2239},[2225,60200,60201],{"class":2227,"line":1038},[2225,60202,60203],{"class":2763},"\u002F\u002F → { id: 'inv-001', status: 'open', amount: 15000, date: '2026-03-15', ... }\n",[2225,60205,60206],{"class":2227,"line":2288},[2225,60207,60208],{"class":2763},"\u002F\u002F   — version 1: the April status edit hadn't happened yet at that instant\n",[937,60210,60211,60212,60215,60216,1520],{},"Writes on a time-machine view throw ",[941,60213,60214],{},"ReadOnlyAtInstantError",", and accuracy is bounded by history retention: if per-collection retention pruned older versions, reads before the oldest retained snapshot return ",[941,60217,6651],{},[1022,60219,60221],{"id":60220},"close-the-period-q1-books-are-sealed","Close the period — Q1 books are sealed",[937,60223,60224,60227,60228,60230,60231,60234,60235,60238,60239,60241,60242,60244,60245,60247,60248,60251],{},[941,60225,60226],{},"closePeriod"," takes a unique ",[941,60229,4914],{},", an inclusive ISO ",[941,60232,60233],{},"endDate"," cutoff, and an optional ",[941,60236,60237],{},"dateField"," naming the record field that carries the business date. Pass ",[941,60240,60237],{}," for accounting workflows: an invoice booked late (written after the close) is still rejected when its ",[941,60243,26305],{}," falls inside the closed period. Without ",[941,60246,60237],{},", the guard falls back to the write timestamp on the record's ",[1007,60249,1659],{"href":60250},"..\u002Fcore\u002Fnoydb\u002Fenclave"," (the encrypted wrapper each record is stored as), which rarely matches accounting semantics.",[2217,60253,60255],{"className":2219,"code":60254,"language":2221,"meta":1037,"style":1037},"import { PeriodClosedError } from '@noy-db\u002Fhub'\n\nconst period = await vault.closePeriod({\n  name: 'Q1-2026',\n  endDate: '2026-03-31',\n  dateField: 'date',\n})\n",[941,60256,60257,60276,60280,60301,60316,60332,60347],{"__ignoreMap":1037},[2225,60258,60259,60261,60263,60266,60268,60270,60272,60274],{"class":2227,"line":2228},[2225,60260,2232],{"class":2231},[2225,60262,2236],{"class":2235},[2225,60264,60265],{"class":2239}," PeriodClosedError",[2225,60267,2243],{"class":2235},[2225,60269,2246],{"class":2231},[2225,60271,2249],{"class":2235},[2225,60273,1086],{"class":2252},[2225,60275,2256],{"class":2235},[2225,60277,60278],{"class":2227,"line":1041},[2225,60279,2285],{"emptyLinePlaceholder":2284},[2225,60281,60282,60284,60287,60289,60291,60293,60295,60297,60299],{"class":2227,"line":1038},[2225,60283,2775],{"class":2774},[2225,60285,60286],{"class":2239}," period ",[2225,60288,2781],{"class":2235},[2225,60290,2784],{"class":2231},[2225,60292,6466],{"class":2239},[2225,60294,1520],{"class":2235},[2225,60296,60226],{"class":2291},[2225,60298,2789],{"class":2239},[2225,60300,4200],{"class":2235},[2225,60302,60303,60305,60307,60309,60312,60314],{"class":2227,"line":2288},[2225,60304,7008],{"class":2307},[2225,60306,2311],{"class":2235},[2225,60308,2249],{"class":2235},[2225,60310,60311],{"class":2252},"Q1-2026",[2225,60313,2805],{"class":2235},[2225,60315,1707],{"class":2235},[2225,60317,60318,60321,60323,60325,60328,60330],{"class":2227,"line":2304},[2225,60319,60320],{"class":2307},"  endDate",[2225,60322,2311],{"class":2235},[2225,60324,2249],{"class":2235},[2225,60326,60327],{"class":2252},"2026-03-31",[2225,60329,2805],{"class":2235},[2225,60331,1707],{"class":2235},[2225,60333,60334,60337,60339,60341,60343,60345],{"class":2227,"line":2356},[2225,60335,60336],{"class":2307},"  dateField",[2225,60338,2311],{"class":2235},[2225,60340,2249],{"class":2235},[2225,60342,26305],{"class":2252},[2225,60344,2805],{"class":2235},[2225,60346,1707],{"class":2235},[2225,60348,60349,60351],{"class":2227,"line":2830},[2225,60350,2359],{"class":2235},[2225,60352,2362],{"class":2239},[2217,60354,60356],{"className":6604,"code":60355,"language":141,"meta":1037,"style":1037},"{\n  \"name\": \"Q1-2026\",\n  \"kind\": \"closed\",\n  \"endDate\": \"2026-03-31\",\n  \"closedAt\": \"2026-04-01T08:00:00.000Z\",\n  \"closedBy\": \"admin@firm.example\",\n  \"priorPeriodHash\": \"\",\n  \"dateField\": \"date\"\n}\n",[941,60357,60358,60362,60380,60399,60417,60437,60456,60472,60488],{"__ignoreMap":1037},[2225,60359,60360],{"class":2227,"line":2228},[2225,60361,4200],{"class":2235},[2225,60363,60364,60366,60368,60370,60372,60374,60376,60378],{"class":2227,"line":1041},[2225,60365,9368],{"class":2235},[2225,60367,4914],{"class":2774},[2225,60369,6620],{"class":2235},[2225,60371,2311],{"class":2235},[2225,60373,6614],{"class":2235},[2225,60375,60311],{"class":2252},[2225,60377,6620],{"class":2235},[2225,60379,1707],{"class":2235},[2225,60381,60382,60384,60386,60388,60390,60392,60395,60397],{"class":2227,"line":1038},[2225,60383,9368],{"class":2235},[2225,60385,27895],{"class":2774},[2225,60387,6620],{"class":2235},[2225,60389,2311],{"class":2235},[2225,60391,6614],{"class":2235},[2225,60393,60394],{"class":2252},"closed",[2225,60396,6620],{"class":2235},[2225,60398,1707],{"class":2235},[2225,60400,60401,60403,60405,60407,60409,60411,60413,60415],{"class":2227,"line":2288},[2225,60402,9368],{"class":2235},[2225,60404,60233],{"class":2774},[2225,60406,6620],{"class":2235},[2225,60408,2311],{"class":2235},[2225,60410,6614],{"class":2235},[2225,60412,60327],{"class":2252},[2225,60414,6620],{"class":2235},[2225,60416,1707],{"class":2235},[2225,60418,60419,60421,60424,60426,60428,60430,60433,60435],{"class":2227,"line":2304},[2225,60420,9368],{"class":2235},[2225,60422,60423],{"class":2774},"closedAt",[2225,60425,6620],{"class":2235},[2225,60427,2311],{"class":2235},[2225,60429,6614],{"class":2235},[2225,60431,60432],{"class":2252},"2026-04-01T08:00:00.000Z",[2225,60434,6620],{"class":2235},[2225,60436,1707],{"class":2235},[2225,60438,60439,60441,60444,60446,60448,60450,60452,60454],{"class":2227,"line":2356},[2225,60440,9368],{"class":2235},[2225,60442,60443],{"class":2774},"closedBy",[2225,60445,6620],{"class":2235},[2225,60447,2311],{"class":2235},[2225,60449,6614],{"class":2235},[2225,60451,58671],{"class":2252},[2225,60453,6620],{"class":2235},[2225,60455,1707],{"class":2235},[2225,60457,60458,60460,60463,60465,60467,60470],{"class":2227,"line":2830},[2225,60459,9368],{"class":2235},[2225,60461,60462],{"class":2774},"priorPeriodHash",[2225,60464,6620],{"class":2235},[2225,60466,2311],{"class":2235},[2225,60468,60469],{"class":2235}," \"\"",[2225,60471,1707],{"class":2235},[2225,60473,60474,60476,60478,60480,60482,60484,60486],{"class":2227,"line":2861},[2225,60475,9368],{"class":2235},[2225,60477,60237],{"class":2774},[2225,60479,6620],{"class":2235},[2225,60481,2311],{"class":2235},[2225,60483,6614],{"class":2235},[2225,60485,26305],{"class":2252},[2225,60487,9449],{"class":2235},[2225,60489,60490],{"class":2227,"line":4246},[2225,60491,7351],{"class":2235},[937,60493,60494,60496,60497,2085,60500,60502,60503,60506,60507,60509],{},[941,60495,60226],{}," returns the stored ",[941,60498,60499],{},"PeriodRecord",[941,60501,60462],{}," chains to the previous close (empty string for a vault's first period; ",[941,60504,60505],{},"priorPeriodName"," is only present from the second close onward), and ",[941,60508,60443],{}," is the calling user. The record itself is written through the same ledger-instrumented path as any other write, so its own closure is tamper-evident.",[2217,60511,60513],{"className":2219,"code":60512,"language":2221,"meta":1037,"style":1037},"\u002F\u002F Any later attempt to put a record whose `date` is on or before\n\u002F\u002F 2026-03-31 throws PeriodClosedError:\ntry {\n  await invoices.put('inv-late', {\n    id: 'inv-late',\n    clientId: 'client-42',\n    status: 'open',\n    description: { en: 'Late entry', th: 'รายการล่าช้า', ar: 'قيد متأخر' },\n    amount: 1_000,\n    date: '2026-03-15',  \u002F\u002F inside Q1 — but Q1 is closed\n  })\n} catch (err) {\n  if (err instanceof PeriodClosedError) {\n    \u002F\u002F user-facing: \"you can't book to a closed period\"\n  }\n}\n\n\u002F\u002F A record dated after the cutoff is accepted as usual:\nawait invoices.put('inv-042', {\n  id: 'inv-042',\n  clientId: 'client-42',\n  status: 'open',\n  description: { en: 'April retainer', th: 'ค่าบริการเมษายน', ar: 'أتعاب أبريل' },\n  amount: 8_000,\n  date: '2026-04-02',  \u002F\u002F after endDate — fine\n})\n",[941,60514,60515,60520,60525,60531,60554,60569,60584,60598,60647,60659,60677,60683,60693,60709,60714,60718,60722,60726,60731,60754,60768,60782,60796,60843,60854,60872],{"__ignoreMap":1037},[2225,60516,60517],{"class":2227,"line":2228},[2225,60518,60519],{"class":2763},"\u002F\u002F Any later attempt to put a record whose `date` is on or before\n",[2225,60521,60522],{"class":2227,"line":1041},[2225,60523,60524],{"class":2763},"\u002F\u002F 2026-03-31 throws PeriodClosedError:\n",[2225,60526,60527,60529],{"class":2227,"line":1038},[2225,60528,7554],{"class":2231},[2225,60530,2301],{"class":2235},[2225,60532,60533,60535,60537,60539,60541,60543,60545,60548,60550,60552],{"class":2227,"line":2288},[2225,60534,7561],{"class":2231},[2225,60536,6503],{"class":2239},[2225,60538,1520],{"class":2235},[2225,60540,3161],{"class":2291},[2225,60542,2789],{"class":2307},[2225,60544,2805],{"class":2235},[2225,60546,60547],{"class":2252},"inv-late",[2225,60549,2805],{"class":2235},[2225,60551,2298],{"class":2235},[2225,60553,2301],{"class":2235},[2225,60555,60556,60559,60561,60563,60565,60567],{"class":2227,"line":2304},[2225,60557,60558],{"class":2307},"    id",[2225,60560,2311],{"class":2235},[2225,60562,2249],{"class":2235},[2225,60564,60547],{"class":2252},[2225,60566,2805],{"class":2235},[2225,60568,1707],{"class":2235},[2225,60570,60571,60574,60576,60578,60580,60582],{"class":2227,"line":2356},[2225,60572,60573],{"class":2307},"    clientId",[2225,60575,2311],{"class":2235},[2225,60577,2249],{"class":2235},[2225,60579,59441],{"class":2252},[2225,60581,2805],{"class":2235},[2225,60583,1707],{"class":2235},[2225,60585,60586,60588,60590,60592,60594,60596],{"class":2227,"line":2830},[2225,60587,59240],{"class":2307},[2225,60589,2311],{"class":2235},[2225,60591,2249],{"class":2235},[2225,60593,26178],{"class":2252},[2225,60595,2805],{"class":2235},[2225,60597,1707],{"class":2235},[2225,60599,60600,60602,60604,60606,60608,60610,60612,60615,60617,60619,60622,60624,60626,60629,60631,60633,60636,60638,60640,60643,60645],{"class":2227,"line":2861},[2225,60601,59314],{"class":2307},[2225,60603,2311],{"class":2235},[2225,60605,2236],{"class":2235},[2225,60607,58997],{"class":2307},[2225,60609,2311],{"class":2235},[2225,60611,2249],{"class":2235},[2225,60613,60614],{"class":2252},"Late entry",[2225,60616,2805],{"class":2235},[2225,60618,2298],{"class":2235},[2225,60620,60621],{"class":2307}," th",[2225,60623,2311],{"class":2235},[2225,60625,2249],{"class":2235},[2225,60627,60628],{"class":2252},"รายการล่าช้า",[2225,60630,2805],{"class":2235},[2225,60632,2298],{"class":2235},[2225,60634,60635],{"class":2307}," ar",[2225,60637,2311],{"class":2235},[2225,60639,2249],{"class":2235},[2225,60641,60642],{"class":2252},"قيد متأخر",[2225,60644,2805],{"class":2235},[2225,60646,2353],{"class":2235},[2225,60648,60649,60652,60654,60657],{"class":2227,"line":4246},[2225,60650,60651],{"class":2307},"    amount",[2225,60653,2311],{"class":2235},[2225,60655,60656],{"class":6527}," 1_000",[2225,60658,1707],{"class":2235},[2225,60660,60661,60664,60666,60668,60670,60672,60674],{"class":2227,"line":4253},[2225,60662,60663],{"class":2307},"    date",[2225,60665,2311],{"class":2235},[2225,60667,2249],{"class":2235},[2225,60669,59539],{"class":2252},[2225,60671,2805],{"class":2235},[2225,60673,2298],{"class":2235},[2225,60675,60676],{"class":2763},"  \u002F\u002F inside Q1 — but Q1 is closed\n",[2225,60678,60679,60681],{"class":2227,"line":4259},[2225,60680,19143],{"class":2235},[2225,60682,2362],{"class":2307},[2225,60684,60685,60687,60689,60691],{"class":2227,"line":6548},[2225,60686,2359],{"class":2235},[2225,60688,7598],{"class":2231},[2225,60690,7601],{"class":2239},[2225,60692,4200],{"class":2235},[2225,60694,60695,60697,60699,60701,60703,60705,60707],{"class":2227,"line":6576},[2225,60696,7608],{"class":2231},[2225,60698,953],{"class":2307},[2225,60700,7617],{"class":2239},[2225,60702,19172],{"class":2235},[2225,60704,60265],{"class":6342},[2225,60706,8495],{"class":2307},[2225,60708,4200],{"class":2235},[2225,60710,60711],{"class":2227,"line":6582},[2225,60712,60713],{"class":2763},"    \u002F\u002F user-facing: \"you can't book to a closed period\"\n",[2225,60715,60716],{"class":2227,"line":6587},[2225,60717,7632],{"class":2235},[2225,60719,60720],{"class":2227,"line":7174},[2225,60721,7351],{"class":2235},[2225,60723,60724],{"class":2227,"line":7180},[2225,60725,2285],{"emptyLinePlaceholder":2284},[2225,60727,60728],{"class":2227,"line":7220},[2225,60729,60730],{"class":2763},"\u002F\u002F A record dated after the cutoff is accepted as usual:\n",[2225,60732,60733,60735,60737,60739,60741,60743,60745,60748,60750,60752],{"class":2227,"line":7225},[2225,60734,6500],{"class":2231},[2225,60736,6503],{"class":2239},[2225,60738,1520],{"class":2235},[2225,60740,3161],{"class":2291},[2225,60742,2789],{"class":2239},[2225,60744,2805],{"class":2235},[2225,60746,60747],{"class":2252},"inv-042",[2225,60749,2805],{"class":2235},[2225,60751,2298],{"class":2235},[2225,60753,2301],{"class":2235},[2225,60755,60756,60758,60760,60762,60764,60766],{"class":2227,"line":7231},[2225,60757,28371],{"class":2307},[2225,60759,2311],{"class":2235},[2225,60761,2249],{"class":2235},[2225,60763,60747],{"class":2252},[2225,60765,2805],{"class":2235},[2225,60767,1707],{"class":2235},[2225,60769,60770,60772,60774,60776,60778,60780],{"class":2227,"line":7267},[2225,60771,58854],{"class":2307},[2225,60773,2311],{"class":2235},[2225,60775,2249],{"class":2235},[2225,60777,59441],{"class":2252},[2225,60779,2805],{"class":2235},[2225,60781,1707],{"class":2235},[2225,60783,60784,60786,60788,60790,60792,60794],{"class":2227,"line":7272},[2225,60785,26171],{"class":2307},[2225,60787,2311],{"class":2235},[2225,60789,2249],{"class":2235},[2225,60791,26178],{"class":2252},[2225,60793,2805],{"class":2235},[2225,60795,1707],{"class":2235},[2225,60797,60798,60800,60802,60804,60806,60808,60810,60813,60815,60817,60819,60821,60823,60826,60828,60830,60832,60834,60836,60839,60841],{"class":2227,"line":7278},[2225,60799,58874],{"class":2307},[2225,60801,2311],{"class":2235},[2225,60803,2236],{"class":2235},[2225,60805,58997],{"class":2307},[2225,60807,2311],{"class":2235},[2225,60809,2249],{"class":2235},[2225,60811,60812],{"class":2252},"April retainer",[2225,60814,2805],{"class":2235},[2225,60816,2298],{"class":2235},[2225,60818,60621],{"class":2307},[2225,60820,2311],{"class":2235},[2225,60822,2249],{"class":2235},[2225,60824,60825],{"class":2252},"ค่าบริการเมษายน",[2225,60827,2805],{"class":2235},[2225,60829,2298],{"class":2235},[2225,60831,60635],{"class":2307},[2225,60833,2311],{"class":2235},[2225,60835,2249],{"class":2235},[2225,60837,60838],{"class":2252},"أتعاب أبريل",[2225,60840,2805],{"class":2235},[2225,60842,2353],{"class":2235},[2225,60844,60845,60847,60849,60852],{"class":2227,"line":7303},[2225,60846,28389],{"class":2307},[2225,60848,2311],{"class":2235},[2225,60850,60851],{"class":6527}," 8_000",[2225,60853,1707],{"class":2235},[2225,60855,60856,60858,60860,60862,60865,60867,60869],{"class":2227,"line":7308},[2225,60857,28466],{"class":2307},[2225,60859,2311],{"class":2235},[2225,60861,2249],{"class":2235},[2225,60863,60864],{"class":2252},"2026-04-02",[2225,60866,2805],{"class":2235},[2225,60868,2298],{"class":2235},[2225,60870,60871],{"class":2763},"  \u002F\u002F after endDate — fine\n",[2225,60873,60874,60876],{"class":2227,"line":7314},[2225,60875,2359],{"class":2235},[2225,60877,2362],{"class":2239},[1022,60879,60881],{"id":60880},"attach-files","Attach files",[937,60883,60884,60885,60888,60889,60892],{},"Blobs are named ",[950,60886,60887],{},"slots"," on a record. ",[941,60890,60891],{},"blob(id).put(slotName, bytes, opts?)"," uploads and attaches; identical content is stored once (content-addressed deduplication), and the MIME type is detected from the file's magic bytes when you don't pass one.",[2217,60894,60896],{"className":2219,"code":60895,"language":2221,"meta":1037,"style":1037},"import { readFile } from 'node:fs\u002Fpromises'\n\nconst pdfBytes = new Uint8Array(await readFile('.\u002Freceipt.pdf'))\n\nconst receipt = invoices.blob('inv-001')\nawait receipt.put('receipt.pdf', pdfBytes, { mimeType: 'application\u002Fpdf' })\n\n\u002F\u002F Read it back — returns the raw bytes, or null if the slot doesn't exist.\nconst bytes = await receipt.get('receipt.pdf')\n\u002F\u002F → Uint8Array(48213) — the PDF, decrypted\n",[941,60897,60898,60918,60922,60953,60957,60982,61027,61031,61036,61062],{"__ignoreMap":1037},[2225,60899,60900,60902,60904,60907,60909,60911,60913,60916],{"class":2227,"line":2228},[2225,60901,2232],{"class":2231},[2225,60903,2236],{"class":2235},[2225,60905,60906],{"class":2239}," readFile",[2225,60908,2243],{"class":2235},[2225,60910,2246],{"class":2231},[2225,60912,2249],{"class":2235},[2225,60914,60915],{"class":2252},"node:fs\u002Fpromises",[2225,60917,2256],{"class":2235},[2225,60919,60920],{"class":2227,"line":1041},[2225,60921,2285],{"emptyLinePlaceholder":2284},[2225,60923,60924,60926,60929,60931,60933,60936,60938,60940,60942,60944,60946,60949,60951],{"class":2227,"line":1038},[2225,60925,2775],{"class":2774},[2225,60927,60928],{"class":2239}," pdfBytes ",[2225,60930,2781],{"class":2235},[2225,60932,8130],{"class":2235},[2225,60934,60935],{"class":2291}," Uint8Array",[2225,60937,2789],{"class":2239},[2225,60939,6500],{"class":2231},[2225,60941,60906],{"class":2291},[2225,60943,2789],{"class":2239},[2225,60945,2805],{"class":2235},[2225,60947,60948],{"class":2252},".\u002Freceipt.pdf",[2225,60950,2805],{"class":2235},[2225,60952,8606],{"class":2239},[2225,60954,60955],{"class":2227,"line":2288},[2225,60956,2285],{"emptyLinePlaceholder":2284},[2225,60958,60959,60961,60964,60966,60968,60970,60972,60974,60976,60978,60980],{"class":2227,"line":2304},[2225,60960,2775],{"class":2774},[2225,60962,60963],{"class":2239}," receipt ",[2225,60965,2781],{"class":2235},[2225,60967,6503],{"class":2239},[2225,60969,1520],{"class":2235},[2225,60971,133],{"class":2291},[2225,60973,2789],{"class":2239},[2225,60975,2805],{"class":2235},[2225,60977,59410],{"class":2252},[2225,60979,2805],{"class":2235},[2225,60981,2362],{"class":2239},[2225,60983,60984,60986,60989,60991,60993,60995,60997,61000,61002,61004,61007,61009,61011,61014,61016,61018,61021,61023,61025],{"class":2227,"line":2356},[2225,60985,6500],{"class":2231},[2225,60987,60988],{"class":2239}," receipt",[2225,60990,1520],{"class":2235},[2225,60992,3161],{"class":2291},[2225,60994,2789],{"class":2239},[2225,60996,2805],{"class":2235},[2225,60998,60999],{"class":2252},"receipt.pdf",[2225,61001,2805],{"class":2235},[2225,61003,2298],{"class":2235},[2225,61005,61006],{"class":2239}," pdfBytes",[2225,61008,2298],{"class":2235},[2225,61010,2236],{"class":2235},[2225,61012,61013],{"class":2307}," mimeType",[2225,61015,2311],{"class":2235},[2225,61017,2249],{"class":2235},[2225,61019,61020],{"class":2252},"application\u002Fpdf",[2225,61022,2805],{"class":2235},[2225,61024,2243],{"class":2235},[2225,61026,2362],{"class":2239},[2225,61028,61029],{"class":2227,"line":2830},[2225,61030,2285],{"emptyLinePlaceholder":2284},[2225,61032,61033],{"class":2227,"line":2861},[2225,61034,61035],{"class":2763},"\u002F\u002F Read it back — returns the raw bytes, or null if the slot doesn't exist.\n",[2225,61037,61038,61040,61042,61044,61046,61048,61050,61052,61054,61056,61058,61060],{"class":2227,"line":4246},[2225,61039,2775],{"class":2774},[2225,61041,32469],{"class":2239},[2225,61043,2781],{"class":2235},[2225,61045,2784],{"class":2231},[2225,61047,60988],{"class":2239},[2225,61049,1520],{"class":2235},[2225,61051,3158],{"class":2291},[2225,61053,2789],{"class":2239},[2225,61055,2805],{"class":2235},[2225,61057,60999],{"class":2252},[2225,61059,2805],{"class":2235},[2225,61061,2362],{"class":2239},[2225,61063,61064],{"class":2227,"line":4253},[2225,61065,61066],{"class":2763},"\u002F\u002F → Uint8Array(48213) — the PDF, decrypted\n",[1022,61068,61070],{"id":61069},"log-who-read-what-and-why","Log who read what, and why",[937,61072,61073,61074,61076],{},"Reads and writes performed inside a ",[941,61075,58371],{}," scope each append one entry to the encrypted consent-audit log — outside a scope, nothing is logged. Store the SHA-256 of the consent receipt the actor presented (a signed banner click, an authorisation PDF), not the receipt itself.",[2217,61078,61080],{"className":2219,"code":61079,"language":2221,"meta":1037,"style":1037},"await vault.withConsent(\n  { purpose: 'quarterly-review', consentHash: '7f3a…' },\n  async () => invoices.get('inv-001'),\n)\n\nconst log = await vault.consentAudit({ since: '2026-01-01T00:00:00Z' })\n",[941,61081,61082,61094,61126,61153,61157,61161],{"__ignoreMap":1037},[2225,61083,61084,61086,61088,61090,61092],{"class":2227,"line":2228},[2225,61085,6500],{"class":2231},[2225,61087,6466],{"class":2239},[2225,61089,1520],{"class":2235},[2225,61091,58238],{"class":2291},[2225,61093,11683],{"class":2239},[2225,61095,61096,61098,61101,61103,61105,61108,61110,61112,61115,61117,61119,61122,61124],{"class":2227,"line":1041},[2225,61097,26354],{"class":2235},[2225,61099,61100],{"class":2307}," purpose",[2225,61102,2311],{"class":2235},[2225,61104,2249],{"class":2235},[2225,61106,61107],{"class":2252},"quarterly-review",[2225,61109,2805],{"class":2235},[2225,61111,2298],{"class":2235},[2225,61113,61114],{"class":2307}," consentHash",[2225,61116,2311],{"class":2235},[2225,61118,2249],{"class":2235},[2225,61120,61121],{"class":2252},"7f3a…",[2225,61123,2805],{"class":2235},[2225,61125,2353],{"class":2235},[2225,61127,61128,61131,61133,61135,61137,61139,61141,61143,61145,61147,61149,61151],{"class":2227,"line":1038},[2225,61129,61130],{"class":2774},"  async",[2225,61132,13130],{"class":2235},[2225,61134,8116],{"class":2774},[2225,61136,6503],{"class":2239},[2225,61138,1520],{"class":2235},[2225,61140,3158],{"class":2291},[2225,61142,2789],{"class":2239},[2225,61144,2805],{"class":2235},[2225,61146,59410],{"class":2252},[2225,61148,2805],{"class":2235},[2225,61150,1381],{"class":2239},[2225,61152,1707],{"class":2235},[2225,61154,61155],{"class":2227,"line":2288},[2225,61156,2362],{"class":2239},[2225,61158,61159],{"class":2227,"line":2304},[2225,61160,2285],{"emptyLinePlaceholder":2284},[2225,61162,61163,61165,61168,61170,61172,61174,61176,61179,61181,61183,61186,61188,61190,61193,61195,61197],{"class":2227,"line":2356},[2225,61164,2775],{"class":2774},[2225,61166,61167],{"class":2239}," log ",[2225,61169,2781],{"class":2235},[2225,61171,2784],{"class":2231},[2225,61173,6466],{"class":2239},[2225,61175,1520],{"class":2235},[2225,61177,61178],{"class":2291},"consentAudit",[2225,61180,2789],{"class":2239},[2225,61182,2792],{"class":2235},[2225,61184,61185],{"class":2307}," since",[2225,61187,2311],{"class":2235},[2225,61189,2249],{"class":2235},[2225,61191,61192],{"class":2252},"2026-01-01T00:00:00Z",[2225,61194,2805],{"class":2235},[2225,61196,2243],{"class":2235},[2225,61198,2362],{"class":2239},[2217,61200,61202],{"className":6604,"code":61201,"language":141,"meta":1037,"style":1037},"[\n  {\n    \"id\": \"01JZX8Q4Y3…\",\n    \"timestamp\": \"2026-04-14T10:02:11.907Z\",\n    \"actor\": \"admin@firm.example\",\n    \"purpose\": \"quarterly-review\",\n    \"consentHash\": \"7f3a…\",\n    \"op\": \"get\",\n    \"collection\": \"invoices\",\n    \"recordId\": \"inv-001\"\n  }\n]\n",[941,61203,61204,61208,61212,61231,61250,61269,61288,61307,61326,61344,61361,61365],{"__ignoreMap":1037},[2225,61205,61206],{"class":2227,"line":2228},[2225,61207,26349],{"class":2235},[2225,61209,61210],{"class":2227,"line":1041},[2225,61211,59879],{"class":2235},[2225,61213,61214,61216,61218,61220,61222,61224,61227,61229],{"class":2227,"line":1038},[2225,61215,14201],{"class":2235},[2225,61217,6983],{"class":2774},[2225,61219,6620],{"class":2235},[2225,61221,2311],{"class":2235},[2225,61223,6614],{"class":2235},[2225,61225,61226],{"class":2252},"01JZX8Q4Y3…",[2225,61228,6620],{"class":2235},[2225,61230,1707],{"class":2235},[2225,61232,61233,61235,61237,61239,61241,61243,61246,61248],{"class":2227,"line":2288},[2225,61234,14201],{"class":2235},[2225,61236,59900],{"class":2774},[2225,61238,6620],{"class":2235},[2225,61240,2311],{"class":2235},[2225,61242,6614],{"class":2235},[2225,61244,61245],{"class":2252},"2026-04-14T10:02:11.907Z",[2225,61247,6620],{"class":2235},[2225,61249,1707],{"class":2235},[2225,61251,61252,61254,61257,61259,61261,61263,61265,61267],{"class":2227,"line":2304},[2225,61253,14201],{"class":2235},[2225,61255,61256],{"class":2774},"actor",[2225,61258,6620],{"class":2235},[2225,61260,2311],{"class":2235},[2225,61262,6614],{"class":2235},[2225,61264,58671],{"class":2252},[2225,61266,6620],{"class":2235},[2225,61268,1707],{"class":2235},[2225,61270,61271,61273,61276,61278,61280,61282,61284,61286],{"class":2227,"line":2356},[2225,61272,14201],{"class":2235},[2225,61274,61275],{"class":2774},"purpose",[2225,61277,6620],{"class":2235},[2225,61279,2311],{"class":2235},[2225,61281,6614],{"class":2235},[2225,61283,61107],{"class":2252},[2225,61285,6620],{"class":2235},[2225,61287,1707],{"class":2235},[2225,61289,61290,61292,61295,61297,61299,61301,61303,61305],{"class":2227,"line":2830},[2225,61291,14201],{"class":2235},[2225,61293,61294],{"class":2774},"consentHash",[2225,61296,6620],{"class":2235},[2225,61298,2311],{"class":2235},[2225,61300,6614],{"class":2235},[2225,61302,61121],{"class":2252},[2225,61304,6620],{"class":2235},[2225,61306,1707],{"class":2235},[2225,61308,61309,61311,61314,61316,61318,61320,61322,61324],{"class":2227,"line":2861},[2225,61310,14201],{"class":2235},[2225,61312,61313],{"class":2774},"op",[2225,61315,6620],{"class":2235},[2225,61317,2311],{"class":2235},[2225,61319,6614],{"class":2235},[2225,61321,3158],{"class":2252},[2225,61323,6620],{"class":2235},[2225,61325,1707],{"class":2235},[2225,61327,61328,61330,61332,61334,61336,61338,61340,61342],{"class":2227,"line":4246},[2225,61329,14201],{"class":2235},[2225,61331,6471],{"class":2774},[2225,61333,6620],{"class":2235},[2225,61335,2311],{"class":2235},[2225,61337,6614],{"class":2235},[2225,61339,6487],{"class":2252},[2225,61341,6620],{"class":2235},[2225,61343,1707],{"class":2235},[2225,61345,61346,61348,61351,61353,61355,61357,61359],{"class":2227,"line":4253},[2225,61347,14201],{"class":2235},[2225,61349,61350],{"class":2774},"recordId",[2225,61352,6620],{"class":2235},[2225,61354,2311],{"class":2235},[2225,61356,6614],{"class":2235},[2225,61358,59410],{"class":2252},[2225,61360,9449],{"class":2235},[2225,61362,61363],{"class":2227,"line":4259},[2225,61364,7632],{"class":2235},[2225,61366,61367],{"class":2227,"line":6548},[2225,61368,26484],{"class":2235},[1022,61370,61372],{"id":61371},"run-reports","Run reports",[937,61374,61375,61376,61379,61380,61382],{},"Queries run inside the hub after decryption, so ",[941,61377,61378],{},".run()"," is synchronous. Each result row carries the group key under the ",[941,61381,27496],{}," field's name plus your named reducers:",[2217,61384,61386],{"className":2219,"code":61385,"language":2221,"meta":1037,"style":1037},"import { sum, count } from '@noy-db\u002Fhub\u002Freduce'\n\nconst byClient = invoices.query()\n  .where('status', '==', 'paid')\n  .groupBy('clientId')\n  .aggregate({ total: sum('amount'), invoiceCount: count() })\n  .run()\n\u002F\u002F → [{ clientId: 'client-42', total: 78500, invoiceCount: 12 }, ...]\n",[941,61387,61388,61410,61414,61431,61463,61479,61520,61529],{"__ignoreMap":1037},[2225,61389,61390,61392,61394,61396,61398,61400,61402,61404,61406,61408],{"class":2227,"line":2228},[2225,61391,2232],{"class":2231},[2225,61393,2236],{"class":2235},[2225,61395,27298],{"class":2239},[2225,61397,2298],{"class":2235},[2225,61399,19270],{"class":2239},[2225,61401,2243],{"class":2235},[2225,61403,2246],{"class":2231},[2225,61405,2249],{"class":2235},[2225,61407,27257],{"class":2252},[2225,61409,2256],{"class":2235},[2225,61411,61412],{"class":2227,"line":1041},[2225,61413,2285],{"emptyLinePlaceholder":2284},[2225,61415,61416,61418,61421,61423,61425,61427,61429],{"class":2227,"line":1038},[2225,61417,2775],{"class":2774},[2225,61419,61420],{"class":2239}," byClient ",[2225,61422,2781],{"class":2235},[2225,61424,6503],{"class":2239},[2225,61426,1520],{"class":2235},[2225,61428,5286],{"class":2291},[2225,61430,8146],{"class":2239},[2225,61432,61433,61435,61437,61439,61441,61443,61445,61447,61449,61451,61453,61455,61457,61459,61461],{"class":2227,"line":2288},[2225,61434,26235],{"class":2235},[2225,61436,3016],{"class":2291},[2225,61438,2789],{"class":2239},[2225,61440,2805],{"class":2235},[2225,61442,6631],{"class":2252},[2225,61444,2805],{"class":2235},[2225,61446,2298],{"class":2235},[2225,61448,2249],{"class":2235},[2225,61450,21901],{"class":2252},[2225,61452,2805],{"class":2235},[2225,61454,2298],{"class":2235},[2225,61456,2249],{"class":2235},[2225,61458,6539],{"class":2252},[2225,61460,2805],{"class":2235},[2225,61462,2362],{"class":2239},[2225,61464,61465,61467,61469,61471,61473,61475,61477],{"class":2227,"line":2304},[2225,61466,26235],{"class":2235},[2225,61468,27496],{"class":2291},[2225,61470,2789],{"class":2239},[2225,61472,2805],{"class":2235},[2225,61474,59721],{"class":2252},[2225,61476,2805],{"class":2235},[2225,61478,2362],{"class":2239},[2225,61480,61481,61483,61485,61487,61489,61491,61493,61495,61497,61499,61501,61503,61505,61507,61510,61512,61514,61516,61518],{"class":2227,"line":2356},[2225,61482,26235],{"class":2235},[2225,61484,4449],{"class":2291},[2225,61486,2789],{"class":2239},[2225,61488,2792],{"class":2235},[2225,61490,27452],{"class":2307},[2225,61492,2311],{"class":2235},[2225,61494,27298],{"class":2291},[2225,61496,2789],{"class":2239},[2225,61498,2805],{"class":2235},[2225,61500,6617],{"class":2252},[2225,61502,2805],{"class":2235},[2225,61504,1381],{"class":2239},[2225,61506,2298],{"class":2235},[2225,61508,61509],{"class":2307}," invoiceCount",[2225,61511,2311],{"class":2235},[2225,61513,19270],{"class":2291},[2225,61515,19132],{"class":2239},[2225,61517,2359],{"class":2235},[2225,61519,2362],{"class":2239},[2225,61521,61522,61524,61527],{"class":2227,"line":2830},[2225,61523,26235],{"class":2235},[2225,61525,61526],{"class":2291},"run",[2225,61528,8146],{"class":2239},[2225,61530,61531],{"class":2227,"line":2861},[2225,61532,61533],{"class":2763},"\u002F\u002F → [{ clientId: 'client-42', total: 78500, invoiceCount: 12 }, ...]\n",[1022,61535,61537],{"id":61536},"verify-nothing-was-tampered-with","Verify nothing was tampered with",[937,61539,61540,61542,61543,61545,61546,28806,61549,61552,61553,61556,61557,61560,61561,997,61563,6255],{},[941,61541,58313],{}," walks the hash-chained ledger and cross-checks it against the stored data envelopes. It returns ",[941,61544,22986],{}," on a clean chain; on failure, ",[941,61547,61548],{},"ok: false",[941,61550,61551],{},"kind: 'chain'"," (the ledger chain diverged, ",[941,61554,61555],{},"divergedAt"," is the index) or ",[941,61558,61559],{},"kind: 'data'"," (a data envelope doesn't match the ledger, with the offending ",[941,61562,6471],{},[941,61564,6983],{},[2217,61566,61568],{"className":2219,"code":61567,"language":2221,"meta":1037,"style":1037},"const verdict = await vault.verifyBackupIntegrity()\n",[941,61569,61570],{"__ignoreMap":1037},[2225,61571,61572,61574,61577,61579,61581,61583,61585,61587],{"class":2227,"line":2228},[2225,61573,2775],{"class":2774},[2225,61575,61576],{"class":2239}," verdict ",[2225,61578,2781],{"class":2235},[2225,61580,2784],{"class":2231},[2225,61582,6466],{"class":2239},[2225,61584,1520],{"class":2235},[2225,61586,22965],{"class":2291},[2225,61588,8146],{"class":2239},[2217,61590,61592],{"className":6604,"code":61591,"language":141,"meta":1037,"style":1037},"{ \"ok\": true, \"head\": \"9c41f7…\", \"length\": 128 }\n",[941,61593,61594],{"__ignoreMap":1037},[2225,61595,61596,61598,61600,61602,61604,61606,61609,61611,61614,61616,61618,61620,61623,61625,61627,61629,61631,61633,61635,61638],{"class":2227,"line":2228},[2225,61597,2792],{"class":2235},[2225,61599,6614],{"class":2235},[2225,61601,40950],{"class":2774},[2225,61603,6620],{"class":2235},[2225,61605,2311],{"class":2235},[2225,61607,61608],{"class":2235}," true,",[2225,61610,6614],{"class":2235},[2225,61612,61613],{"class":2774},"head",[2225,61615,6620],{"class":2235},[2225,61617,2311],{"class":2235},[2225,61619,6614],{"class":2235},[2225,61621,61622],{"class":2252},"9c41f7…",[2225,61624,6620],{"class":2235},[2225,61626,2298],{"class":2235},[2225,61628,6614],{"class":2235},[2225,61630,8742],{"class":2774},[2225,61632,6620],{"class":2235},[2225,61634,2311],{"class":2235},[2225,61636,61637],{"class":6527}," 128",[2225,61639,6369],{"class":2235},[2217,61641,61643],{"className":2219,"code":61642,"language":2221,"meta":1037,"style":1037},"if (!verdict.ok) {\n  \u002F\u002F verdict.kind === 'chain' → ledger modified out-of-band; verdict.divergedAt\n  \u002F\u002F verdict.kind === 'data'  → an envelope was altered; verdict.collection, verdict.id\n  console.error(verdict.message)\n}\n",[941,61644,61645,61662,61667,61672,61690],{"__ignoreMap":1037},[2225,61646,61647,61649,61651,61653,61656,61658,61660],{"class":2227,"line":2228},[2225,61648,35129],{"class":2231},[2225,61650,953],{"class":2239},[2225,61652,35134],{"class":2235},[2225,61654,61655],{"class":2239},"verdict",[2225,61657,1520],{"class":2235},[2225,61659,43845],{"class":2239},[2225,61661,4200],{"class":2235},[2225,61663,61664],{"class":2227,"line":1041},[2225,61665,61666],{"class":2763},"  \u002F\u002F verdict.kind === 'chain' → ledger modified out-of-band; verdict.divergedAt\n",[2225,61668,61669],{"class":2227,"line":1038},[2225,61670,61671],{"class":2763},"  \u002F\u002F verdict.kind === 'data'  → an envelope was altered; verdict.collection, verdict.id\n",[2225,61673,61674,61676,61678,61680,61682,61684,61686,61688],{"class":2227,"line":2288},[2225,61675,27095],{"class":2239},[2225,61677,1520],{"class":2235},[2225,61679,12123],{"class":2291},[2225,61681,2789],{"class":2307},[2225,61683,61655],{"class":2239},[2225,61685,1520],{"class":2235},[2225,61687,52191],{"class":2239},[2225,61689,2362],{"class":2307},[2225,61691,61692],{"class":2227,"line":2304},[2225,61693,7351],{"class":2235},[1022,61695,2621],{"id":2620},[1111,61697,61698,61704,61710,61716,61727,61733,61739],{},[1114,61699,61700,61703],{},[1007,61701,697],{"href":61702},"..\u002Fservices\u002Fhistory"," — versions, ledger, time machine, integrity",[1114,61705,61706,61709],{},[1007,61707,776],{"href":61708},"..\u002Fservices\u002Fperiods"," — the close-period write-guard in depth",[1114,61711,61712,61715],{},[1007,61713,621],{"href":61714},"..\u002Fservices\u002Fblobs"," — slots, chunking, external projections",[1114,61717,61718,61721,61722,968,61724,61726],{},[1007,61719,701],{"href":61720},"..\u002Fservices\u002Fi18n"," — dictionaries, ",[941,61723,21456],{},[941,61725,20943],{}," policies",[1114,61728,61729,61732],{},[1007,61730,641],{"href":61731},"..\u002Fservices\u002Fconsent"," — consent scopes and audit queries",[1114,61734,61735,61738],{},[1007,61736,4449],{"href":61737},"..\u002Fservices\u002Freduce"," — reducers, groupBy, live aggregates",[1114,61740,61741,61744],{},[1007,61742,61743],{"href":6254},"Services overview"," — the full opt-in catalog",[2645,61746,61747],{},"html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}",{"title":1037,"searchDepth":1038,"depth":1038,"links":61749},[61750,61751,61752,61753,61754,61755,61756,61757,61758,61759],{"id":58275,"depth":1041,"text":58276},{"id":58393,"depth":1041,"text":58394},{"id":58783,"depth":1041,"text":58784},{"id":59387,"depth":1041,"text":59388},{"id":60220,"depth":1041,"text":60221},{"id":60880,"depth":1041,"text":60881},{"id":61069,"depth":1041,"text":61070},{"id":61371,"depth":1041,"text":61372},{"id":61536,"depth":1041,"text":61537},{"id":2620,"depth":1041,"text":2621},"Build an auditable, period-sealing accounting app — version history, closed periods, file attachments, localised labels, consent logging, and reports.",{},{"title":507,"description":61760},"iyy24BGnc2rn8IWAA84Q_FFOT8pegiJhjKsg7XeTwmg",{"id":61765,"title":511,"access":12,"apiToc":929,"body":61766,"category":1042,"description":65325,"extension":1044,"meta":65326,"navigation":1042,"nest":1042,"path":512,"seo":65327,"sinceVersion":1042,"status":1049,"stem":513,"subtitle":1042,"tier":1050,"tocExternal":1042,"__hash__":65328},"docs\u002Frecipes\u002Fanalytics-app.md",{"type":931,"value":61767,"toc":65312},[61768,61771,61789,61823,61829,61831,61929,61931,61947,62273,62277,62290,62675,62678,63443,63447,63461,63546,63655,63659,63668,63763,63962,63971,63975,63987,64167,64352,64365,64369,64381,64472,64476,64493,64775,64883,64887,64922,65186,65202,65206,65270,65272,65309],[934,61769,511],{"id":61770},"analytics-app",[1061,61772,61773],{},[937,61774,61775,61777,61778,58223,61780,6030,61782,6030,61784,6030,61786,61788],{},[950,61776,1500],{}," apps that need fast filtering, joining across collections, and pre-aggregated reports — dashboards, BI consoles, search-driven UIs.\n",[950,61779,58222],{},[941,61781,6766],{},[941,61783,21926],{},[941,61785,22060],{},[941,61787,21321],{},". No history, no blobs, no sync, no i18n by default — add them as needed.",[937,61790,61791,61794,61795,61798,61799,61801,61802,6030,61804,968,61806,968,61809,61812,61813,61815,61816,968,61818,2462,61820,61822],{},[950,61792,61793],{},"Verified by:"," the runnable test ",[941,61796,61797],{},"showcases\u002Fsrc\u002Frecipe-analytics.recipe.test.ts"," in the documentation repository covers the indexed ",[941,61800,21901],{}," filter, ",[941,61803,21934],{},[941,61805,21931],{},[941,61807,61808],{},".join()",[941,61810,61811],{},"scan().aggregate()",", and the error thrown when ",[941,61814,21926],{}," is omitted. Lazy mode, multi-store routing, and session-policy behavior (the sections below covering ",[941,61817,22060],{},[941,61819,48447],{},[941,61821,20690],{},") are not exercised by an automated test in this repository.",[937,61824,61825,61826,61828],{},"noy-db is an encrypted, offline-first document store: every record is encrypted inside your application process — in ",[941,61827,1086],{},", the trust boundary — before it reaches any storage backend, and queries run inside the hub after decryption. The backend only ever holds ciphertext and never executes a query. That shapes how analytics work: filtering, joining, and aggregating are hub-side operations, and this recipe assembles the opt-in services that keep them fast and memory-bounded at thousands to tens of thousands of records.",[1022,61830,58276],{"id":58275},[1338,61832,61833,61841],{},[1341,61834,61835],{},[1344,61836,61837,61839],{},[1347,61838,21181],{},[1347,61840,58287],{},[1357,61842,61843,61866,61889,61903,61916],{},[1344,61844,61845,61851],{},[1362,61846,61847],{},[1007,61848,61849],{"href":706},[941,61850,6766],{},[1362,61852,61853,61854,997,61856,61858,61859,61861,61862,61865],{},"Eager-mode ",[941,61855,21901],{},[941,61857,4891],{}," fast-paths; lazy-mode ",[941,61860,21910],{}," for on-demand indexed reads; persisted ",[941,61863,61864],{},"_idx\u002F\u003Cfield>\u002F\u003CrecordId>"," side-cars maintained on every put\u002Fdelete",[1344,61867,61868,61874],{},[1362,61869,61870],{},[1007,61871,61872],{"href":805},[941,61873,21926],{},[1362,61875,61876,968,61878,968,61880,968,61882,968,61884,61886,61887],{},[941,61877,28196],{},[941,61879,28201],{},[941,61881,3236],{},[941,61883,28204],{},[941,61885,28207],{}," reducers + ",[941,61888,21934],{},[1344,61890,61891,61897],{},[1362,61892,61893],{},[1007,61894,61895],{"href":720},[941,61896,22060],{},[1362,61898,61899,61900,61902],{},"Lazy mode (",[941,61901,6836],{},"): per-id on-demand reads over a bounded LRU working set, for collections bigger than you want in memory",[1344,61904,61905,61910],{},[1362,61906,61907,61909],{},[1007,61908,28187],{"href":716}," (always-core)",[1362,61911,61912,61913,61915],{},"Foreign-key eager joins via ",[941,61914,61808],{},", with a nested-loop \u002F hash strategy picked automatically",[1344,61917,61918,61924],{},[1362,61919,61920],{},[1007,61921,61922],{"href":841},[941,61923,21321],{},[1362,61925,61926,61927],{},"Session policy enforcement — e.g. an idle timeout for analyst sessions via ",[941,61928,20690],{},[1022,61930,58394],{"id":58393},[937,61932,61933,61934,61938,61939,61941,61942,1625,61944,61946],{},"Storage is pluggable; this recipe uses ",[1007,61935,61936],{"href":342},[941,61937,51853],{},", which accepts any client exposing ",[941,61940,58402],{}," (node-postgres ",[941,61943,58409],{},[941,61945,6673],{},", Neon, Vercel Postgres, …).",[2217,61948,61950],{"className":2219,"code":61949,"language":2221,"meta":1037,"style":1037},"import { createNoydb } from '@noy-db\u002Fhub'\nimport { withIndexing } from '@noy-db\u002Fhub\u002Findexing'\nimport { withReduce } from '@noy-db\u002Fhub\u002Freduce'\nimport { withLazy } from '@noy-db\u002Fhub\u002Flazy'\nimport { withSession } from '@noy-db\u002Fhub\u002Fsession'\nimport { toPostgres } from '@noy-db\u002Fto-postgres'\nimport { Pool } from 'pg'\n\nconst db = await createNoydb({\n  store: toPostgres({ client: new Pool({ connectionString: process.env.DATABASE_URL }) }),\n  user: 'analyst@example.com',\n  secret: process.env.NOYDB_SECRET!,\n\n  indexingStrategy: withIndexing(),\n  reduceStrategy: withReduce(),\n  lazyStrategy: withLazy(),\n  sessionStrategy: withSession(),\n\n  sessionPolicy: { idleTimeoutMs: 30 * 60_000 }, \u002F\u002F requires sessionStrategy\n})\n",[941,61951,61952,61970,61988,62006,62026,62046,62064,62082,62086,62102,62150,62165,62183,62187,62199,62211,62224,62237,62241,62267],{"__ignoreMap":1037},[2225,61953,61954,61956,61958,61960,61962,61964,61966,61968],{"class":2227,"line":2228},[2225,61955,2232],{"class":2231},[2225,61957,2236],{"class":2235},[2225,61959,2744],{"class":2239},[2225,61961,2243],{"class":2235},[2225,61963,2246],{"class":2231},[2225,61965,2249],{"class":2235},[2225,61967,1086],{"class":2252},[2225,61969,2256],{"class":2235},[2225,61971,61972,61974,61976,61978,61980,61982,61984,61986],{"class":2227,"line":1041},[2225,61973,2232],{"class":2231},[2225,61975,2236],{"class":2235},[2225,61977,4165],{"class":2239},[2225,61979,2243],{"class":2235},[2225,61981,2246],{"class":2231},[2225,61983,2249],{"class":2235},[2225,61985,4174],{"class":2252},[2225,61987,2256],{"class":2235},[2225,61989,61990,61992,61994,61996,61998,62000,62002,62004],{"class":2227,"line":1038},[2225,61991,2232],{"class":2231},[2225,61993,2236],{"class":2235},[2225,61995,27293],{"class":2239},[2225,61997,2243],{"class":2235},[2225,61999,2246],{"class":2231},[2225,62001,2249],{"class":2235},[2225,62003,27257],{"class":2252},[2225,62005,2256],{"class":2235},[2225,62007,62008,62010,62012,62015,62017,62019,62021,62024],{"class":2227,"line":2288},[2225,62009,2232],{"class":2231},[2225,62011,2236],{"class":2235},[2225,62013,62014],{"class":2239}," withLazy",[2225,62016,2243],{"class":2235},[2225,62018,2246],{"class":2231},[2225,62020,2249],{"class":2235},[2225,62022,62023],{"class":2252},"@noy-db\u002Fhub\u002Flazy",[2225,62025,2256],{"class":2235},[2225,62027,62028,62030,62032,62035,62037,62039,62041,62044],{"class":2227,"line":2304},[2225,62029,2232],{"class":2231},[2225,62031,2236],{"class":2235},[2225,62033,62034],{"class":2239}," withSession",[2225,62036,2243],{"class":2235},[2225,62038,2246],{"class":2231},[2225,62040,2249],{"class":2235},[2225,62042,62043],{"class":2252},"@noy-db\u002Fhub\u002Fsession",[2225,62045,2256],{"class":2235},[2225,62047,62048,62050,62052,62054,62056,62058,62060,62062],{"class":2227,"line":2356},[2225,62049,2232],{"class":2231},[2225,62051,2236],{"class":2235},[2225,62053,58563],{"class":2239},[2225,62055,2243],{"class":2235},[2225,62057,2246],{"class":2231},[2225,62059,2249],{"class":2235},[2225,62061,51853],{"class":2252},[2225,62063,2256],{"class":2235},[2225,62065,62066,62068,62070,62072,62074,62076,62078,62080],{"class":2227,"line":2830},[2225,62067,2232],{"class":2231},[2225,62069,2236],{"class":2235},[2225,62071,58582],{"class":2239},[2225,62073,2243],{"class":2235},[2225,62075,2246],{"class":2231},[2225,62077,2249],{"class":2235},[2225,62079,51874],{"class":2252},[2225,62081,2256],{"class":2235},[2225,62083,62084],{"class":2227,"line":2861},[2225,62085,2285],{"emptyLinePlaceholder":2284},[2225,62087,62088,62090,62092,62094,62096,62098,62100],{"class":2227,"line":4246},[2225,62089,2775],{"class":2774},[2225,62091,2778],{"class":2239},[2225,62093,2781],{"class":2235},[2225,62095,2784],{"class":2231},[2225,62097,2744],{"class":2291},[2225,62099,2789],{"class":2239},[2225,62101,4200],{"class":2235},[2225,62103,62104,62106,62108,62110,62112,62114,62116,62118,62120,62122,62124,62126,62128,62130,62132,62134,62136,62138,62140,62142,62144,62146,62148],{"class":2227,"line":4253},[2225,62105,15608],{"class":2307},[2225,62107,2311],{"class":2235},[2225,62109,58563],{"class":2291},[2225,62111,2789],{"class":2239},[2225,62113,2792],{"class":2235},[2225,62115,25871],{"class":2307},[2225,62117,2311],{"class":2235},[2225,62119,8130],{"class":2235},[2225,62121,58582],{"class":2291},[2225,62123,2789],{"class":2239},[2225,62125,2792],{"class":2235},[2225,62127,54351],{"class":2307},[2225,62129,2311],{"class":2235},[2225,62131,36593],{"class":2239},[2225,62133,1520],{"class":2235},[2225,62135,183],{"class":2239},[2225,62137,1520],{"class":2235},[2225,62139,54364],{"class":2239},[2225,62141,2359],{"class":2235},[2225,62143,8495],{"class":2239},[2225,62145,2359],{"class":2235},[2225,62147,1381],{"class":2239},[2225,62149,1707],{"class":2235},[2225,62151,62152,62154,62156,62158,62161,62163],{"class":2227,"line":4259},[2225,62153,4205],{"class":2307},[2225,62155,2311],{"class":2235},[2225,62157,2249],{"class":2235},[2225,62159,62160],{"class":2252},"analyst@example.com",[2225,62162,2805],{"class":2235},[2225,62164,1707],{"class":2235},[2225,62166,62167,62169,62171,62173,62175,62177,62179,62181],{"class":2227,"line":6548},[2225,62168,10423],{"class":2307},[2225,62170,2311],{"class":2235},[2225,62172,36593],{"class":2239},[2225,62174,1520],{"class":2235},[2225,62176,183],{"class":2239},[2225,62178,1520],{"class":2235},[2225,62180,36440],{"class":2239},[2225,62182,58694],{"class":2235},[2225,62184,62185],{"class":2227,"line":6576},[2225,62186,2285],{"emptyLinePlaceholder":2284},[2225,62188,62189,62191,62193,62195,62197],{"class":2227,"line":6582},[2225,62190,4235],{"class":2307},[2225,62192,2311],{"class":2235},[2225,62194,4165],{"class":2291},[2225,62196,4228],{"class":2239},[2225,62198,1707],{"class":2235},[2225,62200,62201,62203,62205,62207,62209],{"class":2227,"line":6587},[2225,62202,27365],{"class":2307},[2225,62204,2311],{"class":2235},[2225,62206,27293],{"class":2291},[2225,62208,4228],{"class":2239},[2225,62210,1707],{"class":2235},[2225,62212,62213,62216,62218,62220,62222],{"class":2227,"line":7174},[2225,62214,62215],{"class":2307},"  lazyStrategy",[2225,62217,2311],{"class":2235},[2225,62219,62014],{"class":2291},[2225,62221,4228],{"class":2239},[2225,62223,1707],{"class":2235},[2225,62225,62226,62229,62231,62233,62235],{"class":2227,"line":7180},[2225,62227,62228],{"class":2307},"  sessionStrategy",[2225,62230,2311],{"class":2235},[2225,62232,62034],{"class":2291},[2225,62234,4228],{"class":2239},[2225,62236,1707],{"class":2235},[2225,62238,62239],{"class":2227,"line":7220},[2225,62240,2285],{"emptyLinePlaceholder":2284},[2225,62242,62243,62246,62248,62250,62253,62255,62258,62260,62262,62264],{"class":2227,"line":7225},[2225,62244,62245],{"class":2307},"  sessionPolicy",[2225,62247,2311],{"class":2235},[2225,62249,2236],{"class":2235},[2225,62251,62252],{"class":2307}," idleTimeoutMs",[2225,62254,2311],{"class":2235},[2225,62256,62257],{"class":6527}," 30",[2225,62259,2263],{"class":2235},[2225,62261,19001],{"class":6527},[2225,62263,10965],{"class":2235},[2225,62265,62266],{"class":2763}," \u002F\u002F requires sessionStrategy\n",[2225,62268,62269,62271],{"class":2227,"line":7231},[2225,62270,2359],{"class":2235},[2225,62272,2362],{"class":2239},[1022,62274,62276],{"id":62275},"declare-indexes-and-references-up-front","Declare indexes and references up front",[937,62278,62279,62280,62283,62284,62287,62288,1520],{},"Indexes are declared as field names. References (",[941,62281,62282],{},"ref()",") declare a soft foreign key by naming the ",[950,62285,62286],{},"target collection"," — the declaration is what later powers ",[941,62289,61808],{},[2217,62291,62293],{"className":2219,"code":62292,"language":2221,"meta":1037,"style":1037},"import { ref } from '@noy-db\u002Fhub'\n\ninterface Client {\n  id: string\n  name: string\n  segment: 'enterprise' | 'sme' | 'consumer'\n}\n\ninterface Invoice {\n  id: string\n  clientId: string\n  amount: number\n  status: 'draft' | 'open' | 'paid' | 'overdue'\n  date: string\n  region: 'EU' | 'US' | 'APAC'\n}\n\nconst vault = await db.openVault('reporting')\n\nconst clients = vault.collection\u003CClient>('clients')\n\nconst invoices = vault.collection\u003CInvoice>('invoices', {\n  indexes: ['clientId', 'status', 'date', 'region'],\n  refs: { clientId: ref('clients') },  \u002F\u002F FK by collection *name*\n})\n",[941,62294,62295,62314,62318,62326,62334,62342,62374,62378,62382,62390,62398,62406,62414,62450,62458,62489,62493,62497,62524,62528,62559,62563,62595,62638,62669],{"__ignoreMap":1037},[2225,62296,62297,62299,62301,62304,62306,62308,62310,62312],{"class":2227,"line":2228},[2225,62298,2232],{"class":2231},[2225,62300,2236],{"class":2235},[2225,62302,62303],{"class":2239}," ref",[2225,62305,2243],{"class":2235},[2225,62307,2246],{"class":2231},[2225,62309,2249],{"class":2235},[2225,62311,1086],{"class":2252},[2225,62313,2256],{"class":2235},[2225,62315,62316],{"class":2227,"line":1041},[2225,62317,2285],{"emptyLinePlaceholder":2284},[2225,62319,62320,62322,62324],{"class":2227,"line":1038},[2225,62321,6994],{"class":2774},[2225,62323,54194],{"class":6342},[2225,62325,2301],{"class":2235},[2225,62327,62328,62330,62332],{"class":2227,"line":2288},[2225,62329,28371],{"class":2307},[2225,62331,2311],{"class":2235},[2225,62333,7014],{"class":6342},[2225,62335,62336,62338,62340],{"class":2227,"line":2304},[2225,62337,7008],{"class":2307},[2225,62339,2311],{"class":2235},[2225,62341,7014],{"class":6342},[2225,62343,62344,62347,62349,62351,62354,62356,62358,62360,62363,62365,62367,62369,62372],{"class":2227,"line":2356},[2225,62345,62346],{"class":2307},"  segment",[2225,62348,2311],{"class":2235},[2225,62350,2249],{"class":2235},[2225,62352,62353],{"class":2252},"enterprise",[2225,62355,2805],{"class":2235},[2225,62357,7087],{"class":2235},[2225,62359,2249],{"class":2235},[2225,62361,62362],{"class":2252},"sme",[2225,62364,2805],{"class":2235},[2225,62366,7087],{"class":2235},[2225,62368,2249],{"class":2235},[2225,62370,62371],{"class":2252},"consumer",[2225,62373,2256],{"class":2235},[2225,62375,62376],{"class":2227,"line":2830},[2225,62377,7351],{"class":2235},[2225,62379,62380],{"class":2227,"line":2861},[2225,62381,2285],{"emptyLinePlaceholder":2284},[2225,62383,62384,62386,62388],{"class":2227,"line":4246},[2225,62385,6994],{"class":2774},[2225,62387,6343],{"class":6342},[2225,62389,2301],{"class":2235},[2225,62391,62392,62394,62396],{"class":2227,"line":4253},[2225,62393,28371],{"class":2307},[2225,62395,2311],{"class":2235},[2225,62397,7014],{"class":6342},[2225,62399,62400,62402,62404],{"class":2227,"line":4259},[2225,62401,58854],{"class":2307},[2225,62403,2311],{"class":2235},[2225,62405,7014],{"class":6342},[2225,62407,62408,62410,62412],{"class":2227,"line":6548},[2225,62409,28389],{"class":2307},[2225,62411,2311],{"class":2235},[2225,62413,12024],{"class":6342},[2225,62415,62416,62418,62420,62422,62424,62426,62428,62430,62432,62434,62436,62438,62440,62442,62444,62446,62448],{"class":2227,"line":6576},[2225,62417,26171],{"class":2307},[2225,62419,2311],{"class":2235},[2225,62421,2249],{"class":2235},[2225,62423,28429],{"class":2252},[2225,62425,2805],{"class":2235},[2225,62427,7087],{"class":2235},[2225,62429,2249],{"class":2235},[2225,62431,26178],{"class":2252},[2225,62433,2805],{"class":2235},[2225,62435,7087],{"class":2235},[2225,62437,2249],{"class":2235},[2225,62439,6539],{"class":2252},[2225,62441,2805],{"class":2235},[2225,62443,7087],{"class":2235},[2225,62445,2249],{"class":2235},[2225,62447,28454],{"class":2252},[2225,62449,2256],{"class":2235},[2225,62451,62452,62454,62456],{"class":2227,"line":6582},[2225,62453,28466],{"class":2307},[2225,62455,2311],{"class":2235},[2225,62457,7014],{"class":6342},[2225,62459,62460,62462,62464,62466,62469,62471,62473,62475,62478,62480,62482,62484,62487],{"class":2227,"line":6587},[2225,62461,7766],{"class":2307},[2225,62463,2311],{"class":2235},[2225,62465,2249],{"class":2235},[2225,62467,62468],{"class":2252},"EU",[2225,62470,2805],{"class":2235},[2225,62472,7087],{"class":2235},[2225,62474,2249],{"class":2235},[2225,62476,62477],{"class":2252},"US",[2225,62479,2805],{"class":2235},[2225,62481,7087],{"class":2235},[2225,62483,2249],{"class":2235},[2225,62485,62486],{"class":2252},"APAC",[2225,62488,2256],{"class":2235},[2225,62490,62491],{"class":2227,"line":7174},[2225,62492,7351],{"class":2235},[2225,62494,62495],{"class":2227,"line":7180},[2225,62496,2285],{"emptyLinePlaceholder":2284},[2225,62498,62499,62501,62503,62505,62507,62509,62511,62513,62515,62517,62520,62522],{"class":2227,"line":7220},[2225,62500,2775],{"class":2774},[2225,62502,2835],{"class":2239},[2225,62504,2781],{"class":2235},[2225,62506,2784],{"class":2231},[2225,62508,2842],{"class":2239},[2225,62510,1520],{"class":2235},[2225,62512,2847],{"class":2291},[2225,62514,2789],{"class":2239},[2225,62516,2805],{"class":2235},[2225,62518,62519],{"class":2252},"reporting",[2225,62521,2805],{"class":2235},[2225,62523,2362],{"class":2239},[2225,62525,62526],{"class":2227,"line":7225},[2225,62527,2285],{"emptyLinePlaceholder":2284},[2225,62529,62530,62532,62535,62537,62539,62541,62543,62545,62547,62549,62551,62553,62555,62557],{"class":2227,"line":7231},[2225,62531,2775],{"class":2774},[2225,62533,62534],{"class":2239}," clients ",[2225,62536,2781],{"class":2235},[2225,62538,6466],{"class":2239},[2225,62540,1520],{"class":2235},[2225,62542,6471],{"class":2291},[2225,62544,6474],{"class":2235},[2225,62546,6673],{"class":6342},[2225,62548,6480],{"class":2235},[2225,62550,2789],{"class":2239},[2225,62552,2805],{"class":2235},[2225,62554,23926],{"class":2252},[2225,62556,2805],{"class":2235},[2225,62558,2362],{"class":2239},[2225,62560,62561],{"class":2227,"line":7267},[2225,62562,2285],{"emptyLinePlaceholder":2284},[2225,62564,62565,62567,62569,62571,62573,62575,62577,62579,62581,62583,62585,62587,62589,62591,62593],{"class":2227,"line":7272},[2225,62566,2775],{"class":2774},[2225,62568,6461],{"class":2239},[2225,62570,2781],{"class":2235},[2225,62572,6466],{"class":2239},[2225,62574,1520],{"class":2235},[2225,62576,6471],{"class":2291},[2225,62578,6474],{"class":2235},[2225,62580,6477],{"class":6342},[2225,62582,6480],{"class":2235},[2225,62584,2789],{"class":2239},[2225,62586,2805],{"class":2235},[2225,62588,6487],{"class":2252},[2225,62590,2805],{"class":2235},[2225,62592,2298],{"class":2235},[2225,62594,2301],{"class":2235},[2225,62596,62597,62600,62602,62604,62606,62608,62610,62612,62614,62616,62618,62620,62622,62624,62626,62628,62630,62632,62634,62636],{"class":2227,"line":7278},[2225,62598,62599],{"class":2307},"  indexes",[2225,62601,2311],{"class":2235},[2225,62603,8667],{"class":2239},[2225,62605,2805],{"class":2235},[2225,62607,59721],{"class":2252},[2225,62609,2805],{"class":2235},[2225,62611,2298],{"class":2235},[2225,62613,2249],{"class":2235},[2225,62615,6631],{"class":2252},[2225,62617,2805],{"class":2235},[2225,62619,2298],{"class":2235},[2225,62621,2249],{"class":2235},[2225,62623,26305],{"class":2252},[2225,62625,2805],{"class":2235},[2225,62627,2298],{"class":2235},[2225,62629,2249],{"class":2235},[2225,62631,53894],{"class":2252},[2225,62633,2805],{"class":2235},[2225,62635,8803],{"class":2239},[2225,62637,1707],{"class":2235},[2225,62639,62640,62643,62645,62647,62650,62652,62654,62656,62658,62660,62662,62664,62666],{"class":2227,"line":7303},[2225,62641,62642],{"class":2307},"  refs",[2225,62644,2311],{"class":2235},[2225,62646,2236],{"class":2235},[2225,62648,62649],{"class":2307}," clientId",[2225,62651,2311],{"class":2235},[2225,62653,62303],{"class":2291},[2225,62655,2789],{"class":2239},[2225,62657,2805],{"class":2235},[2225,62659,23926],{"class":2252},[2225,62661,2805],{"class":2235},[2225,62663,8495],{"class":2239},[2225,62665,33127],{"class":2235},[2225,62667,62668],{"class":2763},"  \u002F\u002F FK by collection *name*\n",[2225,62670,62671,62673],{"class":2227,"line":7308},[2225,62672,2359],{"class":2235},[2225,62674,2362],{"class":2239},[937,62676,62677],{},"A small seed so the outputs below are concrete:",[2217,62679,62681],{"className":2219,"code":62680,"language":2221,"meta":1037,"style":1037},"await clients.put('c1', { id: 'c1', name: 'Acme',  segment: 'enterprise' })\nawait clients.put('c2', { id: 'c2', name: 'Bevco', segment: 'sme' })\nawait clients.put('c3', { id: 'c3', name: 'Cosmo', segment: 'consumer' })\n\nawait invoices.put('i1', { id: 'i1', clientId: 'c1', amount: 1000, status: 'paid',    date: '2026-05-02', region: 'EU'   })\nawait invoices.put('i2', { id: 'i2', clientId: 'c1', amount: 2500, status: 'paid',    date: '2026-06-14', region: 'EU'   })\nawait invoices.put('i3', { id: 'i3', clientId: 'c2', amount:  500, status: 'paid',    date: '2026-06-20', region: 'US'   })\nawait invoices.put('i4', { id: 'i4', clientId: 'c2', amount:  300, status: 'open',    date: '2026-06-28', region: 'US'   })\nawait invoices.put('i5', { id: 'i5', clientId: 'c3', amount:  100, status: 'overdue', date: '2026-04-30', region: 'APAC' })\nawait invoices.put('i6', { id: 'i6', clientId: 'c1', amount:  900, status: 'paid',    date: '2026-07-01', region: 'APAC' })\n",[941,62682,62683,62746,62809,62871,62875,62970,63064,63158,63253,63348],{"__ignoreMap":1037},[2225,62684,62685,62687,62690,62692,62694,62696,62698,62701,62703,62705,62707,62709,62711,62713,62715,62717,62719,62721,62723,62725,62728,62730,62732,62734,62736,62738,62740,62742,62744],{"class":2227,"line":2228},[2225,62686,6500],{"class":2231},[2225,62688,62689],{"class":2239}," clients",[2225,62691,1520],{"class":2235},[2225,62693,3161],{"class":2291},[2225,62695,2789],{"class":2239},[2225,62697,2805],{"class":2235},[2225,62699,62700],{"class":2252},"c1",[2225,62702,2805],{"class":2235},[2225,62704,2298],{"class":2235},[2225,62706,2236],{"class":2235},[2225,62708,7070],{"class":2307},[2225,62710,2311],{"class":2235},[2225,62712,2249],{"class":2235},[2225,62714,62700],{"class":2252},[2225,62716,2805],{"class":2235},[2225,62718,2298],{"class":2235},[2225,62720,8104],{"class":2307},[2225,62722,2311],{"class":2235},[2225,62724,2249],{"class":2235},[2225,62726,62727],{"class":2252},"Acme",[2225,62729,2805],{"class":2235},[2225,62731,2298],{"class":2235},[2225,62733,62346],{"class":2307},[2225,62735,2311],{"class":2235},[2225,62737,2249],{"class":2235},[2225,62739,62353],{"class":2252},[2225,62741,2805],{"class":2235},[2225,62743,2243],{"class":2235},[2225,62745,2362],{"class":2239},[2225,62747,62748,62750,62752,62754,62756,62758,62760,62763,62765,62767,62769,62771,62773,62775,62777,62779,62781,62783,62785,62787,62790,62792,62794,62797,62799,62801,62803,62805,62807],{"class":2227,"line":1041},[2225,62749,6500],{"class":2231},[2225,62751,62689],{"class":2239},[2225,62753,1520],{"class":2235},[2225,62755,3161],{"class":2291},[2225,62757,2789],{"class":2239},[2225,62759,2805],{"class":2235},[2225,62761,62762],{"class":2252},"c2",[2225,62764,2805],{"class":2235},[2225,62766,2298],{"class":2235},[2225,62768,2236],{"class":2235},[2225,62770,7070],{"class":2307},[2225,62772,2311],{"class":2235},[2225,62774,2249],{"class":2235},[2225,62776,62762],{"class":2252},[2225,62778,2805],{"class":2235},[2225,62780,2298],{"class":2235},[2225,62782,8104],{"class":2307},[2225,62784,2311],{"class":2235},[2225,62786,2249],{"class":2235},[2225,62788,62789],{"class":2252},"Bevco",[2225,62791,2805],{"class":2235},[2225,62793,2298],{"class":2235},[2225,62795,62796],{"class":2307}," segment",[2225,62798,2311],{"class":2235},[2225,62800,2249],{"class":2235},[2225,62802,62362],{"class":2252},[2225,62804,2805],{"class":2235},[2225,62806,2243],{"class":2235},[2225,62808,2362],{"class":2239},[2225,62810,62811,62813,62815,62817,62819,62821,62823,62826,62828,62830,62832,62834,62836,62838,62840,62842,62844,62846,62848,62850,62853,62855,62857,62859,62861,62863,62865,62867,62869],{"class":2227,"line":1038},[2225,62812,6500],{"class":2231},[2225,62814,62689],{"class":2239},[2225,62816,1520],{"class":2235},[2225,62818,3161],{"class":2291},[2225,62820,2789],{"class":2239},[2225,62822,2805],{"class":2235},[2225,62824,62825],{"class":2252},"c3",[2225,62827,2805],{"class":2235},[2225,62829,2298],{"class":2235},[2225,62831,2236],{"class":2235},[2225,62833,7070],{"class":2307},[2225,62835,2311],{"class":2235},[2225,62837,2249],{"class":2235},[2225,62839,62825],{"class":2252},[2225,62841,2805],{"class":2235},[2225,62843,2298],{"class":2235},[2225,62845,8104],{"class":2307},[2225,62847,2311],{"class":2235},[2225,62849,2249],{"class":2235},[2225,62851,62852],{"class":2252},"Cosmo",[2225,62854,2805],{"class":2235},[2225,62856,2298],{"class":2235},[2225,62858,62796],{"class":2307},[2225,62860,2311],{"class":2235},[2225,62862,2249],{"class":2235},[2225,62864,62371],{"class":2252},[2225,62866,2805],{"class":2235},[2225,62868,2243],{"class":2235},[2225,62870,2362],{"class":2239},[2225,62872,62873],{"class":2227,"line":2288},[2225,62874,2285],{"emptyLinePlaceholder":2284},[2225,62876,62877,62879,62881,62883,62885,62887,62889,62892,62894,62896,62898,62900,62902,62904,62906,62908,62910,62912,62914,62916,62918,62920,62922,62924,62926,62928,62930,62932,62934,62936,62938,62940,62942,62944,62946,62948,62951,62953,62955,62957,62959,62961,62963,62965,62968],{"class":2227,"line":2304},[2225,62878,6500],{"class":2231},[2225,62880,6503],{"class":2239},[2225,62882,1520],{"class":2235},[2225,62884,3161],{"class":2291},[2225,62886,2789],{"class":2239},[2225,62888,2805],{"class":2235},[2225,62890,62891],{"class":2252},"i1",[2225,62893,2805],{"class":2235},[2225,62895,2298],{"class":2235},[2225,62897,2236],{"class":2235},[2225,62899,7070],{"class":2307},[2225,62901,2311],{"class":2235},[2225,62903,2249],{"class":2235},[2225,62905,62891],{"class":2252},[2225,62907,2805],{"class":2235},[2225,62909,2298],{"class":2235},[2225,62911,62649],{"class":2307},[2225,62913,2311],{"class":2235},[2225,62915,2249],{"class":2235},[2225,62917,62700],{"class":2252},[2225,62919,2805],{"class":2235},[2225,62921,2298],{"class":2235},[2225,62923,6351],{"class":2307},[2225,62925,2311],{"class":2235},[2225,62927,26290],{"class":6527},[2225,62929,2298],{"class":2235},[2225,62931,6361],{"class":2307},[2225,62933,2311],{"class":2235},[2225,62935,2249],{"class":2235},[2225,62937,6539],{"class":2252},[2225,62939,2805],{"class":2235},[2225,62941,2298],{"class":2235},[2225,62943,60663],{"class":2307},[2225,62945,2311],{"class":2235},[2225,62947,2249],{"class":2235},[2225,62949,62950],{"class":2252},"2026-05-02",[2225,62952,2805],{"class":2235},[2225,62954,2298],{"class":2235},[2225,62956,29953],{"class":2307},[2225,62958,2311],{"class":2235},[2225,62960,2249],{"class":2235},[2225,62962,62468],{"class":2252},[2225,62964,2805],{"class":2235},[2225,62966,62967],{"class":2235},"   }",[2225,62969,2362],{"class":2239},[2225,62971,62972,62974,62976,62978,62980,62982,62984,62987,62989,62991,62993,62995,62997,62999,63001,63003,63005,63007,63009,63011,63013,63015,63017,63019,63021,63023,63025,63027,63029,63031,63033,63035,63037,63039,63041,63043,63046,63048,63050,63052,63054,63056,63058,63060,63062],{"class":2227,"line":2356},[2225,62973,6500],{"class":2231},[2225,62975,6503],{"class":2239},[2225,62977,1520],{"class":2235},[2225,62979,3161],{"class":2291},[2225,62981,2789],{"class":2239},[2225,62983,2805],{"class":2235},[2225,62985,62986],{"class":2252},"i2",[2225,62988,2805],{"class":2235},[2225,62990,2298],{"class":2235},[2225,62992,2236],{"class":2235},[2225,62994,7070],{"class":2307},[2225,62996,2311],{"class":2235},[2225,62998,2249],{"class":2235},[2225,63000,62986],{"class":2252},[2225,63002,2805],{"class":2235},[2225,63004,2298],{"class":2235},[2225,63006,62649],{"class":2307},[2225,63008,2311],{"class":2235},[2225,63010,2249],{"class":2235},[2225,63012,62700],{"class":2252},[2225,63014,2805],{"class":2235},[2225,63016,2298],{"class":2235},[2225,63018,6351],{"class":2307},[2225,63020,2311],{"class":2235},[2225,63022,29405],{"class":6527},[2225,63024,2298],{"class":2235},[2225,63026,6361],{"class":2307},[2225,63028,2311],{"class":2235},[2225,63030,2249],{"class":2235},[2225,63032,6539],{"class":2252},[2225,63034,2805],{"class":2235},[2225,63036,2298],{"class":2235},[2225,63038,60663],{"class":2307},[2225,63040,2311],{"class":2235},[2225,63042,2249],{"class":2235},[2225,63044,63045],{"class":2252},"2026-06-14",[2225,63047,2805],{"class":2235},[2225,63049,2298],{"class":2235},[2225,63051,29953],{"class":2307},[2225,63053,2311],{"class":2235},[2225,63055,2249],{"class":2235},[2225,63057,62468],{"class":2252},[2225,63059,2805],{"class":2235},[2225,63061,62967],{"class":2235},[2225,63063,2362],{"class":2239},[2225,63065,63066,63068,63070,63072,63074,63076,63078,63081,63083,63085,63087,63089,63091,63093,63095,63097,63099,63101,63103,63105,63107,63109,63111,63113,63115,63118,63120,63122,63124,63126,63128,63130,63132,63134,63136,63138,63140,63142,63144,63146,63148,63150,63152,63154,63156],{"class":2227,"line":2830},[2225,63067,6500],{"class":2231},[2225,63069,6503],{"class":2239},[2225,63071,1520],{"class":2235},[2225,63073,3161],{"class":2291},[2225,63075,2789],{"class":2239},[2225,63077,2805],{"class":2235},[2225,63079,63080],{"class":2252},"i3",[2225,63082,2805],{"class":2235},[2225,63084,2298],{"class":2235},[2225,63086,2236],{"class":2235},[2225,63088,7070],{"class":2307},[2225,63090,2311],{"class":2235},[2225,63092,2249],{"class":2235},[2225,63094,63080],{"class":2252},[2225,63096,2805],{"class":2235},[2225,63098,2298],{"class":2235},[2225,63100,62649],{"class":2307},[2225,63102,2311],{"class":2235},[2225,63104,2249],{"class":2235},[2225,63106,62762],{"class":2252},[2225,63108,2805],{"class":2235},[2225,63110,2298],{"class":2235},[2225,63112,6351],{"class":2307},[2225,63114,2311],{"class":2235},[2225,63116,63117],{"class":6527},"  500",[2225,63119,2298],{"class":2235},[2225,63121,6361],{"class":2307},[2225,63123,2311],{"class":2235},[2225,63125,2249],{"class":2235},[2225,63127,6539],{"class":2252},[2225,63129,2805],{"class":2235},[2225,63131,2298],{"class":2235},[2225,63133,60663],{"class":2307},[2225,63135,2311],{"class":2235},[2225,63137,2249],{"class":2235},[2225,63139,26203],{"class":2252},[2225,63141,2805],{"class":2235},[2225,63143,2298],{"class":2235},[2225,63145,29953],{"class":2307},[2225,63147,2311],{"class":2235},[2225,63149,2249],{"class":2235},[2225,63151,62477],{"class":2252},[2225,63153,2805],{"class":2235},[2225,63155,62967],{"class":2235},[2225,63157,2362],{"class":2239},[2225,63159,63160,63162,63164,63166,63168,63170,63172,63175,63177,63179,63181,63183,63185,63187,63189,63191,63193,63195,63197,63199,63201,63203,63205,63207,63209,63212,63214,63216,63218,63220,63222,63224,63226,63228,63230,63232,63235,63237,63239,63241,63243,63245,63247,63249,63251],{"class":2227,"line":2861},[2225,63161,6500],{"class":2231},[2225,63163,6503],{"class":2239},[2225,63165,1520],{"class":2235},[2225,63167,3161],{"class":2291},[2225,63169,2789],{"class":2239},[2225,63171,2805],{"class":2235},[2225,63173,63174],{"class":2252},"i4",[2225,63176,2805],{"class":2235},[2225,63178,2298],{"class":2235},[2225,63180,2236],{"class":2235},[2225,63182,7070],{"class":2307},[2225,63184,2311],{"class":2235},[2225,63186,2249],{"class":2235},[2225,63188,63174],{"class":2252},[2225,63190,2805],{"class":2235},[2225,63192,2298],{"class":2235},[2225,63194,62649],{"class":2307},[2225,63196,2311],{"class":2235},[2225,63198,2249],{"class":2235},[2225,63200,62762],{"class":2252},[2225,63202,2805],{"class":2235},[2225,63204,2298],{"class":2235},[2225,63206,6351],{"class":2307},[2225,63208,2311],{"class":2235},[2225,63210,63211],{"class":6527},"  300",[2225,63213,2298],{"class":2235},[2225,63215,6361],{"class":2307},[2225,63217,2311],{"class":2235},[2225,63219,2249],{"class":2235},[2225,63221,26178],{"class":2252},[2225,63223,2805],{"class":2235},[2225,63225,2298],{"class":2235},[2225,63227,60663],{"class":2307},[2225,63229,2311],{"class":2235},[2225,63231,2249],{"class":2235},[2225,63233,63234],{"class":2252},"2026-06-28",[2225,63236,2805],{"class":2235},[2225,63238,2298],{"class":2235},[2225,63240,29953],{"class":2307},[2225,63242,2311],{"class":2235},[2225,63244,2249],{"class":2235},[2225,63246,62477],{"class":2252},[2225,63248,2805],{"class":2235},[2225,63250,62967],{"class":2235},[2225,63252,2362],{"class":2239},[2225,63254,63255,63257,63259,63261,63263,63265,63267,63270,63272,63274,63276,63278,63280,63282,63284,63286,63288,63290,63292,63294,63296,63298,63300,63302,63304,63307,63309,63311,63313,63315,63317,63319,63321,63323,63325,63327,63330,63332,63334,63336,63338,63340,63342,63344,63346],{"class":2227,"line":4246},[2225,63256,6500],{"class":2231},[2225,63258,6503],{"class":2239},[2225,63260,1520],{"class":2235},[2225,63262,3161],{"class":2291},[2225,63264,2789],{"class":2239},[2225,63266,2805],{"class":2235},[2225,63268,63269],{"class":2252},"i5",[2225,63271,2805],{"class":2235},[2225,63273,2298],{"class":2235},[2225,63275,2236],{"class":2235},[2225,63277,7070],{"class":2307},[2225,63279,2311],{"class":2235},[2225,63281,2249],{"class":2235},[2225,63283,63269],{"class":2252},[2225,63285,2805],{"class":2235},[2225,63287,2298],{"class":2235},[2225,63289,62649],{"class":2307},[2225,63291,2311],{"class":2235},[2225,63293,2249],{"class":2235},[2225,63295,62825],{"class":2252},[2225,63297,2805],{"class":2235},[2225,63299,2298],{"class":2235},[2225,63301,6351],{"class":2307},[2225,63303,2311],{"class":2235},[2225,63305,63306],{"class":6527},"  100",[2225,63308,2298],{"class":2235},[2225,63310,6361],{"class":2307},[2225,63312,2311],{"class":2235},[2225,63314,2249],{"class":2235},[2225,63316,28454],{"class":2252},[2225,63318,2805],{"class":2235},[2225,63320,2298],{"class":2235},[2225,63322,25880],{"class":2307},[2225,63324,2311],{"class":2235},[2225,63326,2249],{"class":2235},[2225,63328,63329],{"class":2252},"2026-04-30",[2225,63331,2805],{"class":2235},[2225,63333,2298],{"class":2235},[2225,63335,29953],{"class":2307},[2225,63337,2311],{"class":2235},[2225,63339,2249],{"class":2235},[2225,63341,62486],{"class":2252},[2225,63343,2805],{"class":2235},[2225,63345,2243],{"class":2235},[2225,63347,2362],{"class":2239},[2225,63349,63350,63352,63354,63356,63358,63360,63362,63365,63367,63369,63371,63373,63375,63377,63379,63381,63383,63385,63387,63389,63391,63393,63395,63397,63399,63402,63404,63406,63408,63410,63412,63414,63416,63418,63420,63422,63425,63427,63429,63431,63433,63435,63437,63439,63441],{"class":2227,"line":4253},[2225,63351,6500],{"class":2231},[2225,63353,6503],{"class":2239},[2225,63355,1520],{"class":2235},[2225,63357,3161],{"class":2291},[2225,63359,2789],{"class":2239},[2225,63361,2805],{"class":2235},[2225,63363,63364],{"class":2252},"i6",[2225,63366,2805],{"class":2235},[2225,63368,2298],{"class":2235},[2225,63370,2236],{"class":2235},[2225,63372,7070],{"class":2307},[2225,63374,2311],{"class":2235},[2225,63376,2249],{"class":2235},[2225,63378,63364],{"class":2252},[2225,63380,2805],{"class":2235},[2225,63382,2298],{"class":2235},[2225,63384,62649],{"class":2307},[2225,63386,2311],{"class":2235},[2225,63388,2249],{"class":2235},[2225,63390,62700],{"class":2252},[2225,63392,2805],{"class":2235},[2225,63394,2298],{"class":2235},[2225,63396,6351],{"class":2307},[2225,63398,2311],{"class":2235},[2225,63400,63401],{"class":6527},"  900",[2225,63403,2298],{"class":2235},[2225,63405,6361],{"class":2307},[2225,63407,2311],{"class":2235},[2225,63409,2249],{"class":2235},[2225,63411,6539],{"class":2252},[2225,63413,2805],{"class":2235},[2225,63415,2298],{"class":2235},[2225,63417,60663],{"class":2307},[2225,63419,2311],{"class":2235},[2225,63421,2249],{"class":2235},[2225,63423,63424],{"class":2252},"2026-07-01",[2225,63426,2805],{"class":2235},[2225,63428,2298],{"class":2235},[2225,63430,29953],{"class":2307},[2225,63432,2311],{"class":2235},[2225,63434,2249],{"class":2235},[2225,63436,62486],{"class":2252},[2225,63438,2805],{"class":2235},[2225,63440,2243],{"class":2235},[2225,63442,2362],{"class":2239},[1022,63444,63446],{"id":63445},"filter-fast","Filter fast",[937,63448,63449,63450,63452,63453,63455,63456,997,63458,63460],{},"In the default (eager) mode the whole collection is decrypted into memory on first access, and ",[941,63451,24715],{}," terminals are synchronous. With ",[941,63454,6766],{}," enabled, ",[941,63457,21901],{},[941,63459,4891],{}," predicates on declared fields dispatch through a hash index instead of a linear scan.",[2217,63462,63464],{"className":2219,"code":63463,"language":2221,"meta":1037,"style":1037},"const overdue = invoices.query()\n  .where('status', '==', 'overdue')\n  .orderBy('date', 'desc')\n  .toArray()\n",[941,63465,63466,63482,63514,63538],{"__ignoreMap":1037},[2225,63467,63468,63470,63472,63474,63476,63478,63480],{"class":2227,"line":2228},[2225,63469,2775],{"class":2774},[2225,63471,39360],{"class":2239},[2225,63473,2781],{"class":2235},[2225,63475,6503],{"class":2239},[2225,63477,1520],{"class":2235},[2225,63479,5286],{"class":2291},[2225,63481,8146],{"class":2239},[2225,63483,63484,63486,63488,63490,63492,63494,63496,63498,63500,63502,63504,63506,63508,63510,63512],{"class":2227,"line":1041},[2225,63485,26235],{"class":2235},[2225,63487,3016],{"class":2291},[2225,63489,2789],{"class":2239},[2225,63491,2805],{"class":2235},[2225,63493,6631],{"class":2252},[2225,63495,2805],{"class":2235},[2225,63497,2298],{"class":2235},[2225,63499,2249],{"class":2235},[2225,63501,21901],{"class":2252},[2225,63503,2805],{"class":2235},[2225,63505,2298],{"class":2235},[2225,63507,2249],{"class":2235},[2225,63509,28454],{"class":2252},[2225,63511,2805],{"class":2235},[2225,63513,2362],{"class":2239},[2225,63515,63516,63518,63520,63522,63524,63526,63528,63530,63532,63534,63536],{"class":2227,"line":1038},[2225,63517,26235],{"class":2235},[2225,63519,3019],{"class":2291},[2225,63521,2789],{"class":2239},[2225,63523,2805],{"class":2235},[2225,63525,26305],{"class":2252},[2225,63527,2805],{"class":2235},[2225,63529,2298],{"class":2235},[2225,63531,2249],{"class":2235},[2225,63533,26314],{"class":2252},[2225,63535,2805],{"class":2235},[2225,63537,2362],{"class":2239},[2225,63539,63540,63542,63544],{"class":2227,"line":2288},[2225,63541,26235],{"class":2235},[2225,63543,3230],{"class":2291},[2225,63545,8146],{"class":2239},[2217,63547,63549],{"className":6604,"code":63548,"language":141,"meta":1037,"style":1037},"[\n  { \"id\": \"i5\", \"clientId\": \"c3\", \"amount\": 100, \"status\": \"overdue\", \"date\": \"2026-04-30\", \"region\": \"APAC\" }\n]\n",[941,63550,63551,63555,63651],{"__ignoreMap":1037},[2225,63552,63553],{"class":2227,"line":2228},[2225,63554,26349],{"class":2235},[2225,63556,63557,63559,63561,63563,63565,63567,63569,63571,63573,63575,63577,63579,63581,63583,63585,63587,63589,63591,63593,63595,63597,63599,63601,63603,63605,63607,63609,63611,63613,63615,63617,63619,63621,63623,63625,63627,63629,63631,63633,63635,63637,63639,63641,63643,63645,63647,63649],{"class":2227,"line":1041},[2225,63558,26354],{"class":2235},[2225,63560,6614],{"class":2235},[2225,63562,6983],{"class":2774},[2225,63564,6620],{"class":2235},[2225,63566,2311],{"class":2235},[2225,63568,6614],{"class":2235},[2225,63570,63269],{"class":2252},[2225,63572,6620],{"class":2235},[2225,63574,2298],{"class":2235},[2225,63576,6614],{"class":2235},[2225,63578,59721],{"class":2774},[2225,63580,6620],{"class":2235},[2225,63582,2311],{"class":2235},[2225,63584,6614],{"class":2235},[2225,63586,62825],{"class":2252},[2225,63588,6620],{"class":2235},[2225,63590,2298],{"class":2235},[2225,63592,6614],{"class":2235},[2225,63594,6617],{"class":2774},[2225,63596,6620],{"class":2235},[2225,63598,2311],{"class":2235},[2225,63600,6528],{"class":6527},[2225,63602,2298],{"class":2235},[2225,63604,6614],{"class":2235},[2225,63606,6631],{"class":2774},[2225,63608,6620],{"class":2235},[2225,63610,2311],{"class":2235},[2225,63612,6614],{"class":2235},[2225,63614,28454],{"class":2252},[2225,63616,6620],{"class":2235},[2225,63618,2298],{"class":2235},[2225,63620,6614],{"class":2235},[2225,63622,26305],{"class":2774},[2225,63624,6620],{"class":2235},[2225,63626,2311],{"class":2235},[2225,63628,6614],{"class":2235},[2225,63630,63329],{"class":2252},[2225,63632,6620],{"class":2235},[2225,63634,2298],{"class":2235},[2225,63636,6614],{"class":2235},[2225,63638,53894],{"class":2774},[2225,63640,6620],{"class":2235},[2225,63642,2311],{"class":2235},[2225,63644,6614],{"class":2235},[2225,63646,62486],{"class":2252},[2225,63648,6620],{"class":2235},[2225,63650,6369],{"class":2235},[2225,63652,63653],{"class":2227,"line":1038},[2225,63654,26484],{"class":2235},[1022,63656,63658],{"id":63657},"join-across-collections","Join across collections",[937,63660,63661,63664,63665,63667],{},[941,63662,63663],{},".join(field, { as })"," attaches the referenced record under an alias. It requires the ",[941,63666,62282],{}," declaration from above and is projection-only — it enriches rows, it never filters them.",[2217,63669,63671],{"className":2219,"code":63670,"language":2221,"meta":1037,"style":1037},"const enriched = invoices.query()\n  .where('status', '==', 'paid')\n  .join('clientId', { as: 'client' })\n  .toArray()\n",[941,63672,63673,63690,63722,63755],{"__ignoreMap":1037},[2225,63674,63675,63677,63680,63682,63684,63686,63688],{"class":2227,"line":2228},[2225,63676,2775],{"class":2774},[2225,63678,63679],{"class":2239}," enriched ",[2225,63681,2781],{"class":2235},[2225,63683,6503],{"class":2239},[2225,63685,1520],{"class":2235},[2225,63687,5286],{"class":2291},[2225,63689,8146],{"class":2239},[2225,63691,63692,63694,63696,63698,63700,63702,63704,63706,63708,63710,63712,63714,63716,63718,63720],{"class":2227,"line":1041},[2225,63693,26235],{"class":2235},[2225,63695,3016],{"class":2291},[2225,63697,2789],{"class":2239},[2225,63699,2805],{"class":2235},[2225,63701,6631],{"class":2252},[2225,63703,2805],{"class":2235},[2225,63705,2298],{"class":2235},[2225,63707,2249],{"class":2235},[2225,63709,21901],{"class":2252},[2225,63711,2805],{"class":2235},[2225,63713,2298],{"class":2235},[2225,63715,2249],{"class":2235},[2225,63717,6539],{"class":2252},[2225,63719,2805],{"class":2235},[2225,63721,2362],{"class":2239},[2225,63723,63724,63726,63729,63731,63733,63735,63737,63739,63741,63743,63745,63747,63749,63751,63753],{"class":2227,"line":1038},[2225,63725,26235],{"class":2235},[2225,63727,63728],{"class":2291},"join",[2225,63730,2789],{"class":2239},[2225,63732,2805],{"class":2235},[2225,63734,59721],{"class":2252},[2225,63736,2805],{"class":2235},[2225,63738,2298],{"class":2235},[2225,63740,2236],{"class":2235},[2225,63742,2266],{"class":2307},[2225,63744,2311],{"class":2235},[2225,63746,2249],{"class":2235},[2225,63748,9913],{"class":2252},[2225,63750,2805],{"class":2235},[2225,63752,2243],{"class":2235},[2225,63754,2362],{"class":2239},[2225,63756,63757,63759,63761],{"class":2227,"line":2288},[2225,63758,26235],{"class":2235},[2225,63760,3230],{"class":2291},[2225,63762,8146],{"class":2239},[2217,63764,63766],{"className":6604,"code":63765,"language":141,"meta":1037,"style":1037},"[\n  {\n    \"id\": \"i1\", \"clientId\": \"c1\", \"amount\": 1000, \"status\": \"paid\",\n    \"date\": \"2026-05-02\", \"region\": \"EU\",\n    \"client\": { \"id\": \"c1\", \"name\": \"Acme\", \"segment\": \"enterprise\" }\n  },\n  { \"…\": \"3 more rows (i2, i3, i6), each carrying its joined client record\" }\n]\n",[941,63767,63768,63772,63776,63838,63872,63933,63937,63958],{"__ignoreMap":1037},[2225,63769,63770],{"class":2227,"line":2228},[2225,63771,26349],{"class":2235},[2225,63773,63774],{"class":2227,"line":1041},[2225,63775,59879],{"class":2235},[2225,63777,63778,63780,63782,63784,63786,63788,63790,63792,63794,63796,63798,63800,63802,63804,63806,63808,63810,63812,63814,63816,63818,63820,63822,63824,63826,63828,63830,63832,63834,63836],{"class":2227,"line":1038},[2225,63779,14201],{"class":2235},[2225,63781,6983],{"class":2774},[2225,63783,6620],{"class":2235},[2225,63785,2311],{"class":2235},[2225,63787,6614],{"class":2235},[2225,63789,62891],{"class":2252},[2225,63791,6620],{"class":2235},[2225,63793,2298],{"class":2235},[2225,63795,6614],{"class":2235},[2225,63797,59721],{"class":2774},[2225,63799,6620],{"class":2235},[2225,63801,2311],{"class":2235},[2225,63803,6614],{"class":2235},[2225,63805,62700],{"class":2252},[2225,63807,6620],{"class":2235},[2225,63809,2298],{"class":2235},[2225,63811,6614],{"class":2235},[2225,63813,6617],{"class":2774},[2225,63815,6620],{"class":2235},[2225,63817,2311],{"class":2235},[2225,63819,26290],{"class":6527},[2225,63821,2298],{"class":2235},[2225,63823,6614],{"class":2235},[2225,63825,6631],{"class":2774},[2225,63827,6620],{"class":2235},[2225,63829,2311],{"class":2235},[2225,63831,6614],{"class":2235},[2225,63833,6539],{"class":2252},[2225,63835,6620],{"class":2235},[2225,63837,1707],{"class":2235},[2225,63839,63840,63842,63844,63846,63848,63850,63852,63854,63856,63858,63860,63862,63864,63866,63868,63870],{"class":2227,"line":2288},[2225,63841,14201],{"class":2235},[2225,63843,26305],{"class":2774},[2225,63845,6620],{"class":2235},[2225,63847,2311],{"class":2235},[2225,63849,6614],{"class":2235},[2225,63851,62950],{"class":2252},[2225,63853,6620],{"class":2235},[2225,63855,2298],{"class":2235},[2225,63857,6614],{"class":2235},[2225,63859,53894],{"class":2774},[2225,63861,6620],{"class":2235},[2225,63863,2311],{"class":2235},[2225,63865,6614],{"class":2235},[2225,63867,62468],{"class":2252},[2225,63869,6620],{"class":2235},[2225,63871,1707],{"class":2235},[2225,63873,63874,63876,63878,63880,63882,63884,63886,63888,63890,63892,63894,63896,63898,63900,63902,63904,63906,63908,63910,63912,63914,63916,63918,63921,63923,63925,63927,63929,63931],{"class":2227,"line":2304},[2225,63875,14201],{"class":2235},[2225,63877,9913],{"class":2774},[2225,63879,6620],{"class":2235},[2225,63881,2311],{"class":2235},[2225,63883,2236],{"class":2235},[2225,63885,6614],{"class":2235},[2225,63887,6983],{"class":6342},[2225,63889,6620],{"class":2235},[2225,63891,2311],{"class":2235},[2225,63893,6614],{"class":2235},[2225,63895,62700],{"class":2252},[2225,63897,6620],{"class":2235},[2225,63899,2298],{"class":2235},[2225,63901,6614],{"class":2235},[2225,63903,4914],{"class":6342},[2225,63905,6620],{"class":2235},[2225,63907,2311],{"class":2235},[2225,63909,6614],{"class":2235},[2225,63911,62727],{"class":2252},[2225,63913,6620],{"class":2235},[2225,63915,2298],{"class":2235},[2225,63917,6614],{"class":2235},[2225,63919,63920],{"class":6342},"segment",[2225,63922,6620],{"class":2235},[2225,63924,2311],{"class":2235},[2225,63926,6614],{"class":2235},[2225,63928,62353],{"class":2252},[2225,63930,6620],{"class":2235},[2225,63932,6369],{"class":2235},[2225,63934,63935],{"class":2227,"line":2356},[2225,63936,19017],{"class":2235},[2225,63938,63939,63941,63943,63945,63947,63949,63951,63954,63956],{"class":2227,"line":2830},[2225,63940,26354],{"class":2235},[2225,63942,6614],{"class":2235},[2225,63944,14475],{"class":2774},[2225,63946,6620],{"class":2235},[2225,63948,2311],{"class":2235},[2225,63950,6614],{"class":2235},[2225,63952,63953],{"class":2252},"3 more rows (i2, i3, i6), each carrying its joined client record",[2225,63955,6620],{"class":2235},[2225,63957,6369],{"class":2235},[2225,63959,63960],{"class":2227,"line":2861},[2225,63961,26484],{"class":2235},[937,63963,63964,63965,63967,63968,63970],{},"The join strategy is automatic: nested-loop when the target collection exposes its built-in primary-key lookup (the normal case for any ",[941,63966,103],{}," target — O(1) per row via the collection's own id map, unrelated to whether ",[941,63969,6766],{}," declares an index on the join field), falling back to hash join only for custom query sources that don't expose that lookup.",[1022,63972,63974],{"id":63973},"aggregates-per-group","Aggregates per group",[937,63976,63977,63980,63981,63983,63984,63986],{},[941,63978,63979],{},".groupBy(field)"," buckets the matching records, then ",[941,63982,24892],{}," computes named reducers per bucket. ",[941,63985,61378],{}," returns the rows synchronously; each row carries the group key under the grouping field's own name plus every reducer output.",[2217,63988,63990],{"className":2219,"code":63989,"language":2221,"meta":1037,"style":1037},"import { sum, avg, count } from '@noy-db\u002Fhub\u002Freduce'\n\nconst byRegion = invoices.query()\n  .where('status', '==', 'paid')\n  .groupBy('region')\n  .aggregate({\n    total: sum('amount'),\n    avg: avg('amount'),\n    n: count(),\n  })\n  .run()\n",[941,63991,63992,64019,64023,64040,64072,64088,64098,64119,64140,64153,64159],{"__ignoreMap":1037},[2225,63993,63994,63996,63998,64000,64002,64005,64007,64009,64011,64013,64015,64017],{"class":2227,"line":2228},[2225,63995,2232],{"class":2231},[2225,63997,2236],{"class":2235},[2225,63999,27298],{"class":2239},[2225,64001,2298],{"class":2235},[2225,64003,64004],{"class":2239}," avg",[2225,64006,2298],{"class":2235},[2225,64008,19270],{"class":2239},[2225,64010,2243],{"class":2235},[2225,64012,2246],{"class":2231},[2225,64014,2249],{"class":2235},[2225,64016,27257],{"class":2252},[2225,64018,2256],{"class":2235},[2225,64020,64021],{"class":2227,"line":1041},[2225,64022,2285],{"emptyLinePlaceholder":2284},[2225,64024,64025,64027,64030,64032,64034,64036,64038],{"class":2227,"line":1038},[2225,64026,2775],{"class":2774},[2225,64028,64029],{"class":2239}," byRegion ",[2225,64031,2781],{"class":2235},[2225,64033,6503],{"class":2239},[2225,64035,1520],{"class":2235},[2225,64037,5286],{"class":2291},[2225,64039,8146],{"class":2239},[2225,64041,64042,64044,64046,64048,64050,64052,64054,64056,64058,64060,64062,64064,64066,64068,64070],{"class":2227,"line":2288},[2225,64043,26235],{"class":2235},[2225,64045,3016],{"class":2291},[2225,64047,2789],{"class":2239},[2225,64049,2805],{"class":2235},[2225,64051,6631],{"class":2252},[2225,64053,2805],{"class":2235},[2225,64055,2298],{"class":2235},[2225,64057,2249],{"class":2235},[2225,64059,21901],{"class":2252},[2225,64061,2805],{"class":2235},[2225,64063,2298],{"class":2235},[2225,64065,2249],{"class":2235},[2225,64067,6539],{"class":2252},[2225,64069,2805],{"class":2235},[2225,64071,2362],{"class":2239},[2225,64073,64074,64076,64078,64080,64082,64084,64086],{"class":2227,"line":2304},[2225,64075,26235],{"class":2235},[2225,64077,27496],{"class":2291},[2225,64079,2789],{"class":2239},[2225,64081,2805],{"class":2235},[2225,64083,53894],{"class":2252},[2225,64085,2805],{"class":2235},[2225,64087,2362],{"class":2239},[2225,64089,64090,64092,64094,64096],{"class":2227,"line":2356},[2225,64091,26235],{"class":2235},[2225,64093,4449],{"class":2291},[2225,64095,2789],{"class":2239},[2225,64097,4200],{"class":2235},[2225,64099,64100,64103,64105,64107,64109,64111,64113,64115,64117],{"class":2227,"line":2830},[2225,64101,64102],{"class":2307},"    total",[2225,64104,2311],{"class":2235},[2225,64106,27298],{"class":2291},[2225,64108,2789],{"class":2239},[2225,64110,2805],{"class":2235},[2225,64112,6617],{"class":2252},[2225,64114,2805],{"class":2235},[2225,64116,1381],{"class":2239},[2225,64118,1707],{"class":2235},[2225,64120,64121,64124,64126,64128,64130,64132,64134,64136,64138],{"class":2227,"line":2861},[2225,64122,64123],{"class":2307},"    avg",[2225,64125,2311],{"class":2235},[2225,64127,64004],{"class":2291},[2225,64129,2789],{"class":2239},[2225,64131,2805],{"class":2235},[2225,64133,6617],{"class":2252},[2225,64135,2805],{"class":2235},[2225,64137,1381],{"class":2239},[2225,64139,1707],{"class":2235},[2225,64141,64142,64145,64147,64149,64151],{"class":2227,"line":4246},[2225,64143,64144],{"class":2307},"    n",[2225,64146,2311],{"class":2235},[2225,64148,19270],{"class":2291},[2225,64150,4228],{"class":2239},[2225,64152,1707],{"class":2235},[2225,64154,64155,64157],{"class":2227,"line":4253},[2225,64156,19143],{"class":2235},[2225,64158,2362],{"class":2239},[2225,64160,64161,64163,64165],{"class":2227,"line":4259},[2225,64162,26235],{"class":2235},[2225,64164,61526],{"class":2291},[2225,64166,8146],{"class":2239},[2217,64168,64170],{"className":6604,"code":64169,"language":141,"meta":1037,"style":1037},"[\n  { \"region\": \"EU\",   \"total\": 3500, \"avg\": 1750, \"n\": 2 },\n  { \"region\": \"US\",   \"total\": 500,  \"avg\": 500,  \"n\": 1 },\n  { \"region\": \"APAC\", \"total\": 900,  \"avg\": 900,  \"n\": 1 }\n]\n",[941,64171,64172,64176,64235,64291,64348],{"__ignoreMap":1037},[2225,64173,64174],{"class":2227,"line":2228},[2225,64175,26349],{"class":2235},[2225,64177,64178,64180,64182,64184,64186,64188,64190,64192,64194,64196,64198,64201,64203,64205,64208,64210,64212,64214,64216,64218,64221,64223,64225,64227,64229,64231,64233],{"class":2227,"line":1041},[2225,64179,26354],{"class":2235},[2225,64181,6614],{"class":2235},[2225,64183,53894],{"class":2774},[2225,64185,6620],{"class":2235},[2225,64187,2311],{"class":2235},[2225,64189,6614],{"class":2235},[2225,64191,62468],{"class":2252},[2225,64193,6620],{"class":2235},[2225,64195,2298],{"class":2235},[2225,64197,26401],{"class":2235},[2225,64199,64200],{"class":2774},"total",[2225,64202,6620],{"class":2235},[2225,64204,2311],{"class":2235},[2225,64206,64207],{"class":6527}," 3500",[2225,64209,2298],{"class":2235},[2225,64211,6614],{"class":2235},[2225,64213,28201],{"class":2774},[2225,64215,6620],{"class":2235},[2225,64217,2311],{"class":2235},[2225,64219,64220],{"class":6527}," 1750",[2225,64222,2298],{"class":2235},[2225,64224,6614],{"class":2235},[2225,64226,26634],{"class":2774},[2225,64228,6620],{"class":2235},[2225,64230,2311],{"class":2235},[2225,64232,18961],{"class":6527},[2225,64234,2353],{"class":2235},[2225,64236,64237,64239,64241,64243,64245,64247,64249,64251,64253,64255,64257,64259,64261,64263,64265,64267,64269,64271,64273,64275,64277,64279,64281,64283,64285,64287,64289],{"class":2227,"line":1038},[2225,64238,26354],{"class":2235},[2225,64240,6614],{"class":2235},[2225,64242,53894],{"class":2774},[2225,64244,6620],{"class":2235},[2225,64246,2311],{"class":2235},[2225,64248,6614],{"class":2235},[2225,64250,62477],{"class":2252},[2225,64252,6620],{"class":2235},[2225,64254,2298],{"class":2235},[2225,64256,26401],{"class":2235},[2225,64258,64200],{"class":2774},[2225,64260,6620],{"class":2235},[2225,64262,2311],{"class":2235},[2225,64264,27084],{"class":6527},[2225,64266,2298],{"class":2235},[2225,64268,9368],{"class":2235},[2225,64270,28201],{"class":2774},[2225,64272,6620],{"class":2235},[2225,64274,2311],{"class":2235},[2225,64276,27084],{"class":6527},[2225,64278,2298],{"class":2235},[2225,64280,9368],{"class":2235},[2225,64282,26634],{"class":2774},[2225,64284,6620],{"class":2235},[2225,64286,2311],{"class":2235},[2225,64288,9377],{"class":6527},[2225,64290,2353],{"class":2235},[2225,64292,64293,64295,64297,64299,64301,64303,64305,64307,64309,64311,64313,64315,64317,64319,64322,64324,64326,64328,64330,64332,64334,64336,64338,64340,64342,64344,64346],{"class":2227,"line":2288},[2225,64294,26354],{"class":2235},[2225,64296,6614],{"class":2235},[2225,64298,53894],{"class":2774},[2225,64300,6620],{"class":2235},[2225,64302,2311],{"class":2235},[2225,64304,6614],{"class":2235},[2225,64306,62486],{"class":2252},[2225,64308,6620],{"class":2235},[2225,64310,2298],{"class":2235},[2225,64312,6614],{"class":2235},[2225,64314,64200],{"class":2774},[2225,64316,6620],{"class":2235},[2225,64318,2311],{"class":2235},[2225,64320,64321],{"class":6527}," 900",[2225,64323,2298],{"class":2235},[2225,64325,9368],{"class":2235},[2225,64327,28201],{"class":2774},[2225,64329,6620],{"class":2235},[2225,64331,2311],{"class":2235},[2225,64333,64321],{"class":6527},[2225,64335,2298],{"class":2235},[2225,64337,9368],{"class":2235},[2225,64339,26634],{"class":2774},[2225,64341,6620],{"class":2235},[2225,64343,2311],{"class":2235},[2225,64345,9377],{"class":6527},[2225,64347,6369],{"class":2235},[2225,64349,64350],{"class":2227,"line":2304},[2225,64351,26484],{"class":2235},[937,64353,64354,64355,64357,64358,64360,64361,64364],{},"For a dashboard that stays current, the same ",[941,64356,24892],{}," wrapper also exposes ",[941,64359,3027],{},", which re-runs the reduction whenever the underlying data changes (call ",[941,64362,64363],{},".stop()"," on the handle when done).",[1022,64366,64368],{"id":64367},"streaming-aggregates-over-very-large-sets","Streaming aggregates over very large sets",[937,64370,64371,64372,64374,64375,64377,64378,64380],{},"When the match set is too large to hold in memory, ",[941,64373,7906],{}," streams records page by page (decrypting at most ",[941,64376,24875],{}," at a time) and reduces with O(reducers) memory — no row ceiling. Its ",[941,64379,21931],{}," terminal returns a promise.",[2217,64382,64384],{"className":2219,"code":64383,"language":2221,"meta":1037,"style":1037},"const total = await invoices.scan()\n  .where('status', '==', 'paid')\n  .aggregate({ sum: sum('amount') })\n\u002F\u002F → { sum: 4900 }\n",[941,64385,64386,64405,64437,64467],{"__ignoreMap":1037},[2225,64387,64388,64390,64393,64395,64397,64399,64401,64403],{"class":2227,"line":2228},[2225,64389,2775],{"class":2774},[2225,64391,64392],{"class":2239}," total ",[2225,64394,2781],{"class":2235},[2225,64396,2784],{"class":2231},[2225,64398,6503],{"class":2239},[2225,64400,1520],{"class":2235},[2225,64402,3239],{"class":2291},[2225,64404,8146],{"class":2239},[2225,64406,64407,64409,64411,64413,64415,64417,64419,64421,64423,64425,64427,64429,64431,64433,64435],{"class":2227,"line":1041},[2225,64408,26235],{"class":2235},[2225,64410,3016],{"class":2291},[2225,64412,2789],{"class":2239},[2225,64414,2805],{"class":2235},[2225,64416,6631],{"class":2252},[2225,64418,2805],{"class":2235},[2225,64420,2298],{"class":2235},[2225,64422,2249],{"class":2235},[2225,64424,21901],{"class":2252},[2225,64426,2805],{"class":2235},[2225,64428,2298],{"class":2235},[2225,64430,2249],{"class":2235},[2225,64432,6539],{"class":2252},[2225,64434,2805],{"class":2235},[2225,64436,2362],{"class":2239},[2225,64438,64439,64441,64443,64445,64447,64449,64451,64453,64455,64457,64459,64461,64463,64465],{"class":2227,"line":1038},[2225,64440,26235],{"class":2235},[2225,64442,4449],{"class":2291},[2225,64444,2789],{"class":2239},[2225,64446,2792],{"class":2235},[2225,64448,27298],{"class":2307},[2225,64450,2311],{"class":2235},[2225,64452,27298],{"class":2291},[2225,64454,2789],{"class":2239},[2225,64456,2805],{"class":2235},[2225,64458,6617],{"class":2252},[2225,64460,2805],{"class":2235},[2225,64462,8495],{"class":2239},[2225,64464,2359],{"class":2235},[2225,64466,2362],{"class":2239},[2225,64468,64469],{"class":2227,"line":2288},[2225,64470,64471],{"class":2763},"\u002F\u002F → { sum: 4900 }\n",[1022,64473,64475],{"id":64474},"beyond-memory-lazy-mode","Beyond memory: lazy mode",[937,64477,64478,64479,64482,64483,4688,64486,64489,64490,957],{},"Eager mode loads every record of a collection into memory. For collections beyond that, declare the collection lazy: records are fetched per-id on demand into a bounded LRU cache, and reads go through the persisted index side-cars instead of a full snapshot. Lazy mode requires ",[941,64480,64481],{},"lazyStrategy: withLazy()"," plus a cache budget — at least one of ",[941,64484,64485],{},"maxRecords",[941,64487,64488],{},"maxBytes"," (a number of ",[950,64491,64492],{},"bytes",[2217,64494,64496],{"className":2219,"code":64495,"language":2221,"meta":1037,"style":1037},"const invoicesLazy = vault.collection\u003CInvoice>('invoices', {\n  prefetch: false,                                            \u002F\u002F lazy — nothing bulk-loaded at open\n  cache: { maxRecords: 5_000, maxBytes: 50 * 1024 * 1024 },   \u002F\u002F 50 MB budget\n  indexes: ['clientId', 'status', 'date', 'region'],\n  refs: { clientId: ref('clients') },\n})\n\n\u002F\u002F In lazy mode query() throws — use lazyQuery(), whose terminals are async.\n\u002F\u002F Every field used in where()\u002ForderBy() must be a declared index,\n\u002F\u002F otherwise the terminal throws IndexRequiredError.\nconst recentOverdue = await invoicesLazy.lazyQuery()\n  .where('status', '==', 'overdue')\n  .orderBy('date', 'desc')\n  .limit(50)\n  .toArray()\n",[941,64497,64498,64531,64545,64585,64627,64653,64659,64663,64668,64673,64678,64699,64731,64755,64767],{"__ignoreMap":1037},[2225,64499,64500,64502,64505,64507,64509,64511,64513,64515,64517,64519,64521,64523,64525,64527,64529],{"class":2227,"line":2228},[2225,64501,2775],{"class":2774},[2225,64503,64504],{"class":2239}," invoicesLazy ",[2225,64506,2781],{"class":2235},[2225,64508,6466],{"class":2239},[2225,64510,1520],{"class":2235},[2225,64512,6471],{"class":2291},[2225,64514,6474],{"class":2235},[2225,64516,6477],{"class":6342},[2225,64518,6480],{"class":2235},[2225,64520,2789],{"class":2239},[2225,64522,2805],{"class":2235},[2225,64524,6487],{"class":2252},[2225,64526,2805],{"class":2235},[2225,64528,2298],{"class":2235},[2225,64530,2301],{"class":2235},[2225,64532,64533,64536,64538,64540,64542],{"class":2227,"line":1041},[2225,64534,64535],{"class":2307},"  prefetch",[2225,64537,2311],{"class":2235},[2225,64539,2341],{"class":2321},[2225,64541,2298],{"class":2235},[2225,64543,64544],{"class":2763},"                                            \u002F\u002F lazy — nothing bulk-loaded at open\n",[2225,64546,64547,64550,64552,64554,64557,64559,64562,64564,64567,64569,64571,64573,64576,64578,64580,64582],{"class":2227,"line":1038},[2225,64548,64549],{"class":2307},"  cache",[2225,64551,2311],{"class":2235},[2225,64553,2236],{"class":2235},[2225,64555,64556],{"class":2307}," maxRecords",[2225,64558,2311],{"class":2235},[2225,64560,64561],{"class":6527}," 5_000",[2225,64563,2298],{"class":2235},[2225,64565,64566],{"class":2307}," maxBytes",[2225,64568,2311],{"class":2235},[2225,64570,37210],{"class":6527},[2225,64572,2263],{"class":2235},[2225,64574,64575],{"class":6527}," 1024",[2225,64577,2263],{"class":2235},[2225,64579,64575],{"class":6527},[2225,64581,10965],{"class":2235},[2225,64583,64584],{"class":2763},"   \u002F\u002F 50 MB budget\n",[2225,64586,64587,64589,64591,64593,64595,64597,64599,64601,64603,64605,64607,64609,64611,64613,64615,64617,64619,64621,64623,64625],{"class":2227,"line":2288},[2225,64588,62599],{"class":2307},[2225,64590,2311],{"class":2235},[2225,64592,8667],{"class":2239},[2225,64594,2805],{"class":2235},[2225,64596,59721],{"class":2252},[2225,64598,2805],{"class":2235},[2225,64600,2298],{"class":2235},[2225,64602,2249],{"class":2235},[2225,64604,6631],{"class":2252},[2225,64606,2805],{"class":2235},[2225,64608,2298],{"class":2235},[2225,64610,2249],{"class":2235},[2225,64612,26305],{"class":2252},[2225,64614,2805],{"class":2235},[2225,64616,2298],{"class":2235},[2225,64618,2249],{"class":2235},[2225,64620,53894],{"class":2252},[2225,64622,2805],{"class":2235},[2225,64624,8803],{"class":2239},[2225,64626,1707],{"class":2235},[2225,64628,64629,64631,64633,64635,64637,64639,64641,64643,64645,64647,64649,64651],{"class":2227,"line":2304},[2225,64630,62642],{"class":2307},[2225,64632,2311],{"class":2235},[2225,64634,2236],{"class":2235},[2225,64636,62649],{"class":2307},[2225,64638,2311],{"class":2235},[2225,64640,62303],{"class":2291},[2225,64642,2789],{"class":2239},[2225,64644,2805],{"class":2235},[2225,64646,23926],{"class":2252},[2225,64648,2805],{"class":2235},[2225,64650,8495],{"class":2239},[2225,64652,19008],{"class":2235},[2225,64654,64655,64657],{"class":2227,"line":2356},[2225,64656,2359],{"class":2235},[2225,64658,2362],{"class":2239},[2225,64660,64661],{"class":2227,"line":2830},[2225,64662,2285],{"emptyLinePlaceholder":2284},[2225,64664,64665],{"class":2227,"line":2861},[2225,64666,64667],{"class":2763},"\u002F\u002F In lazy mode query() throws — use lazyQuery(), whose terminals are async.\n",[2225,64669,64670],{"class":2227,"line":4246},[2225,64671,64672],{"class":2763},"\u002F\u002F Every field used in where()\u002ForderBy() must be a declared index,\n",[2225,64674,64675],{"class":2227,"line":4253},[2225,64676,64677],{"class":2763},"\u002F\u002F otherwise the terminal throws IndexRequiredError.\n",[2225,64679,64680,64682,64685,64687,64689,64692,64694,64697],{"class":2227,"line":4259},[2225,64681,2775],{"class":2774},[2225,64683,64684],{"class":2239}," recentOverdue ",[2225,64686,2781],{"class":2235},[2225,64688,2784],{"class":2231},[2225,64690,64691],{"class":2239}," invoicesLazy",[2225,64693,1520],{"class":2235},[2225,64695,64696],{"class":2291},"lazyQuery",[2225,64698,8146],{"class":2239},[2225,64700,64701,64703,64705,64707,64709,64711,64713,64715,64717,64719,64721,64723,64725,64727,64729],{"class":2227,"line":6548},[2225,64702,26235],{"class":2235},[2225,64704,3016],{"class":2291},[2225,64706,2789],{"class":2239},[2225,64708,2805],{"class":2235},[2225,64710,6631],{"class":2252},[2225,64712,2805],{"class":2235},[2225,64714,2298],{"class":2235},[2225,64716,2249],{"class":2235},[2225,64718,21901],{"class":2252},[2225,64720,2805],{"class":2235},[2225,64722,2298],{"class":2235},[2225,64724,2249],{"class":2235},[2225,64726,28454],{"class":2252},[2225,64728,2805],{"class":2235},[2225,64730,2362],{"class":2239},[2225,64732,64733,64735,64737,64739,64741,64743,64745,64747,64749,64751,64753],{"class":2227,"line":6576},[2225,64734,26235],{"class":2235},[2225,64736,3019],{"class":2291},[2225,64738,2789],{"class":2239},[2225,64740,2805],{"class":2235},[2225,64742,26305],{"class":2252},[2225,64744,2805],{"class":2235},[2225,64746,2298],{"class":2235},[2225,64748,2249],{"class":2235},[2225,64750,26314],{"class":2252},[2225,64752,2805],{"class":2235},[2225,64754,2362],{"class":2239},[2225,64756,64757,64759,64761,64763,64765],{"class":2227,"line":6582},[2225,64758,26235],{"class":2235},[2225,64760,3022],{"class":2291},[2225,64762,2789],{"class":2239},[2225,64764,26329],{"class":6527},[2225,64766,2362],{"class":2239},[2225,64768,64769,64771,64773],{"class":2227,"line":6587},[2225,64770,26235],{"class":2235},[2225,64772,3230],{"class":2291},[2225,64774,8146],{"class":2239},[2217,64776,64777],{"className":6604,"code":63548,"language":141,"meta":1037,"style":1037},[941,64778,64779,64783,64879],{"__ignoreMap":1037},[2225,64780,64781],{"class":2227,"line":2228},[2225,64782,26349],{"class":2235},[2225,64784,64785,64787,64789,64791,64793,64795,64797,64799,64801,64803,64805,64807,64809,64811,64813,64815,64817,64819,64821,64823,64825,64827,64829,64831,64833,64835,64837,64839,64841,64843,64845,64847,64849,64851,64853,64855,64857,64859,64861,64863,64865,64867,64869,64871,64873,64875,64877],{"class":2227,"line":1041},[2225,64786,26354],{"class":2235},[2225,64788,6614],{"class":2235},[2225,64790,6983],{"class":2774},[2225,64792,6620],{"class":2235},[2225,64794,2311],{"class":2235},[2225,64796,6614],{"class":2235},[2225,64798,63269],{"class":2252},[2225,64800,6620],{"class":2235},[2225,64802,2298],{"class":2235},[2225,64804,6614],{"class":2235},[2225,64806,59721],{"class":2774},[2225,64808,6620],{"class":2235},[2225,64810,2311],{"class":2235},[2225,64812,6614],{"class":2235},[2225,64814,62825],{"class":2252},[2225,64816,6620],{"class":2235},[2225,64818,2298],{"class":2235},[2225,64820,6614],{"class":2235},[2225,64822,6617],{"class":2774},[2225,64824,6620],{"class":2235},[2225,64826,2311],{"class":2235},[2225,64828,6528],{"class":6527},[2225,64830,2298],{"class":2235},[2225,64832,6614],{"class":2235},[2225,64834,6631],{"class":2774},[2225,64836,6620],{"class":2235},[2225,64838,2311],{"class":2235},[2225,64840,6614],{"class":2235},[2225,64842,28454],{"class":2252},[2225,64844,6620],{"class":2235},[2225,64846,2298],{"class":2235},[2225,64848,6614],{"class":2235},[2225,64850,26305],{"class":2774},[2225,64852,6620],{"class":2235},[2225,64854,2311],{"class":2235},[2225,64856,6614],{"class":2235},[2225,64858,63329],{"class":2252},[2225,64860,6620],{"class":2235},[2225,64862,2298],{"class":2235},[2225,64864,6614],{"class":2235},[2225,64866,53894],{"class":2774},[2225,64868,6620],{"class":2235},[2225,64870,2311],{"class":2235},[2225,64872,6614],{"class":2235},[2225,64874,62486],{"class":2252},[2225,64876,6620],{"class":2235},[2225,64878,6369],{"class":2235},[2225,64880,64881],{"class":2227,"line":1038},[2225,64882,26484],{"class":2235},[1022,64884,64886],{"id":64885},"multi-store-routing-hot-vs-cold","Multi-store routing — hot vs. cold",[937,64888,64889,64890,17904,64892,64894,64895,64898,64899,64902,64903,64906,64907,64909,64910,64913,64914,64917,64918,64921],{},"For datasets that outgrow one backend, ",[941,64891,48447],{},[941,64893,1086],{}," (always-core) multiplexes a single logical store across several physical ones: a ",[941,64896,64897],{},"default"," store, per-collection ",[941,64900,64901],{},"routes",", vault-name prefixes (",[941,64904,64905],{},"vaultRoutes","), blob tiering (",[941,64908,621],{},"), and age-based hot\u002Fcold tiering (",[941,64911,64912],{},"age","). With age tiering, records whose last modification is older than ",[941,64915,64916],{},"coldAfterDays"," migrate to the cold store during ",[941,64919,64920],{},"compact()",", and reads fall through to the cold tier transparently.",[2217,64923,64925],{"className":2219,"code":64924,"language":2221,"meta":1037,"style":1037},"import { createNoydb, routeStore } from '@noy-db\u002Fhub'\nimport { toPostgres } from '@noy-db\u002Fto-postgres'\nimport { toAwsS3 } from '@noy-db\u002Fto-aws-s3'\nimport { Pool } from 'pg'\n\nconst db = await createNoydb({\n  store: routeStore({\n    default: toPostgres({ client: new Pool({ connectionString: process.env.DATABASE_URL }) }),\n    age: {\n      cold: toAwsS3({ bucket: 'reporting-archive' }),\n      coldAfterDays: 90,\n    },\n  }),\n  user: 'analyst@example.com',\n  secret: process.env.NOYDB_SECRET!,\n})\n",[941,64926,64927,64949,64967,64986,65004,65008,65024,65036,65085,65094,65124,65136,65140,65148,65162,65180],{"__ignoreMap":1037},[2225,64928,64929,64931,64933,64935,64937,64939,64941,64943,64945,64947],{"class":2227,"line":2228},[2225,64930,2232],{"class":2231},[2225,64932,2236],{"class":2235},[2225,64934,2744],{"class":2239},[2225,64936,2298],{"class":2235},[2225,64938,48541],{"class":2239},[2225,64940,2243],{"class":2235},[2225,64942,2246],{"class":2231},[2225,64944,2249],{"class":2235},[2225,64946,1086],{"class":2252},[2225,64948,2256],{"class":2235},[2225,64950,64951,64953,64955,64957,64959,64961,64963,64965],{"class":2227,"line":1041},[2225,64952,2232],{"class":2231},[2225,64954,2236],{"class":2235},[2225,64956,58563],{"class":2239},[2225,64958,2243],{"class":2235},[2225,64960,2246],{"class":2231},[2225,64962,2249],{"class":2235},[2225,64964,51853],{"class":2252},[2225,64966,2256],{"class":2235},[2225,64968,64969,64971,64973,64976,64978,64980,64982,64984],{"class":2227,"line":1038},[2225,64970,2232],{"class":2231},[2225,64972,2236],{"class":2235},[2225,64974,64975],{"class":2239}," toAwsS3",[2225,64977,2243],{"class":2235},[2225,64979,2246],{"class":2231},[2225,64981,2249],{"class":2235},[2225,64983,29876],{"class":2252},[2225,64985,2256],{"class":2235},[2225,64987,64988,64990,64992,64994,64996,64998,65000,65002],{"class":2227,"line":2288},[2225,64989,2232],{"class":2231},[2225,64991,2236],{"class":2235},[2225,64993,58582],{"class":2239},[2225,64995,2243],{"class":2235},[2225,64997,2246],{"class":2231},[2225,64999,2249],{"class":2235},[2225,65001,51874],{"class":2252},[2225,65003,2256],{"class":2235},[2225,65005,65006],{"class":2227,"line":2304},[2225,65007,2285],{"emptyLinePlaceholder":2284},[2225,65009,65010,65012,65014,65016,65018,65020,65022],{"class":2227,"line":2356},[2225,65011,2775],{"class":2774},[2225,65013,2778],{"class":2239},[2225,65015,2781],{"class":2235},[2225,65017,2784],{"class":2231},[2225,65019,2744],{"class":2291},[2225,65021,2789],{"class":2239},[2225,65023,4200],{"class":2235},[2225,65025,65026,65028,65030,65032,65034],{"class":2227,"line":2830},[2225,65027,15608],{"class":2307},[2225,65029,2311],{"class":2235},[2225,65031,48541],{"class":2291},[2225,65033,2789],{"class":2239},[2225,65035,4200],{"class":2235},[2225,65037,65038,65041,65043,65045,65047,65049,65051,65053,65055,65057,65059,65061,65063,65065,65067,65069,65071,65073,65075,65077,65079,65081,65083],{"class":2227,"line":2861},[2225,65039,65040],{"class":2307},"    default",[2225,65042,2311],{"class":2235},[2225,65044,58563],{"class":2291},[2225,65046,2789],{"class":2239},[2225,65048,2792],{"class":2235},[2225,65050,25871],{"class":2307},[2225,65052,2311],{"class":2235},[2225,65054,8130],{"class":2235},[2225,65056,58582],{"class":2291},[2225,65058,2789],{"class":2239},[2225,65060,2792],{"class":2235},[2225,65062,54351],{"class":2307},[2225,65064,2311],{"class":2235},[2225,65066,36593],{"class":2239},[2225,65068,1520],{"class":2235},[2225,65070,183],{"class":2239},[2225,65072,1520],{"class":2235},[2225,65074,54364],{"class":2239},[2225,65076,2359],{"class":2235},[2225,65078,8495],{"class":2239},[2225,65080,2359],{"class":2235},[2225,65082,1381],{"class":2239},[2225,65084,1707],{"class":2235},[2225,65086,65087,65090,65092],{"class":2227,"line":4246},[2225,65088,65089],{"class":2307},"    age",[2225,65091,2311],{"class":2235},[2225,65093,2301],{"class":2235},[2225,65095,65096,65099,65101,65103,65105,65107,65109,65111,65113,65116,65118,65120,65122],{"class":2227,"line":4253},[2225,65097,65098],{"class":2307},"      cold",[2225,65100,2311],{"class":2235},[2225,65102,64975],{"class":2291},[2225,65104,2789],{"class":2239},[2225,65106,2792],{"class":2235},[2225,65108,29939],{"class":2307},[2225,65110,2311],{"class":2235},[2225,65112,2249],{"class":2235},[2225,65114,65115],{"class":2252},"reporting-archive",[2225,65117,2805],{"class":2235},[2225,65119,2243],{"class":2235},[2225,65121,1381],{"class":2239},[2225,65123,1707],{"class":2235},[2225,65125,65126,65129,65131,65134],{"class":2227,"line":4259},[2225,65127,65128],{"class":2307},"      coldAfterDays",[2225,65130,2311],{"class":2235},[2225,65132,65133],{"class":6527}," 90",[2225,65135,1707],{"class":2235},[2225,65137,65138],{"class":2227,"line":6548},[2225,65139,8324],{"class":2235},[2225,65141,65142,65144,65146],{"class":2227,"line":6576},[2225,65143,19143],{"class":2235},[2225,65145,1381],{"class":2239},[2225,65147,1707],{"class":2235},[2225,65149,65150,65152,65154,65156,65158,65160],{"class":2227,"line":6582},[2225,65151,4205],{"class":2307},[2225,65153,2311],{"class":2235},[2225,65155,2249],{"class":2235},[2225,65157,62160],{"class":2252},[2225,65159,2805],{"class":2235},[2225,65161,1707],{"class":2235},[2225,65163,65164,65166,65168,65170,65172,65174,65176,65178],{"class":2227,"line":6587},[2225,65165,10423],{"class":2307},[2225,65167,2311],{"class":2235},[2225,65169,36593],{"class":2239},[2225,65171,1520],{"class":2235},[2225,65173,183],{"class":2239},[2225,65175,1520],{"class":2235},[2225,65177,36440],{"class":2239},[2225,65179,58694],{"class":2235},[2225,65181,65182,65184],{"class":2227,"line":7174},[2225,65183,2359],{"class":2235},[2225,65185,2362],{"class":2239},[937,65187,65188,65189,65191,65192,65195,65196,65198,65199,65201],{},"Every routed store still only ever sees ciphertext — routing decides ",[1092,65190,3016],{}," an ",[1007,65193,6955],{"href":65194},"\u002Fcore\u002Fnoydb\u002Fenclave#envelope"," (the ciphertext wrapper each record is stored as) lands, never ",[1092,65197,13638],{}," is inside it. See ",[1007,65200,808],{"href":809}," for middleware (retry, circuit breaker, metrics) around stores.",[1022,65203,65205],{"id":65204},"performance-ceilings-to-know-about","Performance ceilings to know about",[1111,65207,65208,65228,65240,65251,65262],{},[1114,65209,65210,2085,65213,65216,65217,65220,65221,1625,65224,65227],{},[950,65211,65212],{},"Eager joins",[941,65214,65215],{},"JoinTooLargeError"," at 50,000 rows per side (a one-shot warning fires at 80%). Override per join leg with ",[941,65218,65219],{},"{ maxRows }"," if you've measured. Narrow the left side with ",[941,65222,65223],{},"where()",[941,65225,65226],{},"limit()"," first.",[1114,65229,65230,65232,65233,65236,65237,65239],{},[950,65231,27496],{},": warns at 10,000 distinct groups, throws ",[941,65234,65235],{},"GroupCardinalityError"," at 100,000. The cap is not overridable — beyond it you probably want a streaming aggregate or an external OLAP system. Grouping on a high-uniqueness field like ",[941,65238,6983],{}," is almost always a query mistake.",[1114,65241,65242,2085,65245,997,65248,65250],{},[950,65243,65244],{},"Aggregations and counts ignore joins",[941,65246,65247],{},".count()",[941,65249,21931],{}," reduce over base records only; joined fields are projection-only and cannot feed a reducer.",[1114,65252,65253,65256,65257,1261,65259,65261],{},[950,65254,65255],{},"Lazy mode cache",": tune ",[941,65258,64485],{},[941,65260,64488],{}," to your working set; the LRU evicts least-recently-used entries. An unbounded lazy cache is rejected at declaration time.",[1114,65263,65264,65266,65267,65269],{},[950,65265,6870],{},": a collection mixing small hot fields with large rarely-read ones pays full-envelope decrypt cost on every read; see ",[1007,65268,6875],{"href":813}," (experimental) to split them off-row.",[1022,65271,2621],{"id":2620},[1111,65273,65274,65279,65284,65289,65294,65299,65304],{},[1114,65275,65276,65278],{},[1007,65277,705],{"href":706}," — eager fast-paths, persisted side-cars, rebuild\u002Freconcile",[1114,65280,65281,65283],{},[1007,65282,4449],{"href":805}," — reducers, groupBy, live aggregations",[1114,65285,65286,65288],{},[1007,65287,715],{"href":716}," — FK joins, strategies, ceilings",[1114,65290,65291,65293],{},[1007,65292,719],{"href":720}," — lazy mode and the LRU working set",[1114,65295,65296,65298],{},[1007,65297,808],{"href":809}," — multi-store routing and store middleware",[1114,65300,65301,65303],{},[1007,65302,840],{"href":841}," — session policy and idle timeout",[1114,65305,65306,65308],{},[1007,65307,597],{"href":598}," — the full service catalog",[2645,65310,65311],{},"html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sfNiH, html code.shiki .sfNiH{--shiki-light:#FF5370;--shiki-default:#FF9CAC;--shiki-dark:#FF9CAC}",{"title":1037,"searchDepth":1038,"depth":1038,"links":65313},[65314,65315,65316,65317,65318,65319,65320,65321,65322,65323,65324],{"id":58275,"depth":1041,"text":58276},{"id":58393,"depth":1041,"text":58394},{"id":62275,"depth":1041,"text":62276},{"id":63445,"depth":1041,"text":63446},{"id":63657,"depth":1041,"text":63658},{"id":63973,"depth":1041,"text":63974},{"id":64367,"depth":1041,"text":64368},{"id":64474,"depth":1041,"text":64475},{"id":64885,"depth":1041,"text":64886},{"id":65204,"depth":1041,"text":65205},{"id":2620,"depth":1041,"text":2621},"Assemble fast filtering, joins, grouped and streaming aggregates, lazy mode for beyond-memory collections, and hot\u002Fcold multi-store routing.",{},{"title":511,"description":65325},"r1BcdXiH3G9GLnQ-pbvclN71WoKz1lvLZ1xCnk38Iwo",{"id":65330,"title":515,"access":12,"apiToc":929,"body":65331,"category":1042,"description":68311,"extension":1044,"meta":68312,"navigation":1042,"nest":1042,"path":516,"seo":68313,"sinceVersion":1042,"status":1049,"stem":517,"subtitle":1042,"tier":1050,"tocExternal":1042,"__hash__":68314},"docs\u002Frecipes\u002Fattestation-verifier.md",{"type":931,"value":65332,"toc":68300},[65333,65336,65355,65364,65378,65381,65416,65420,65438,65987,65990,66027,66034,66044,66048,66057,66222,66228,66232,66252,67313,67316,67388,67392,67395,67547,67738,67744,67806,67810,67817,67859,67962,67968,68018,68033,68036,68039,68177,68181,68207,68211,68274,68276,68297],[934,65334,515],{"id":65335},"attestation-verifier",[1061,65337,65338],{},[937,65339,65340,65342,65343,58223,65345,65347,65348,65351,65352,65354],{},[950,65341,1500],{}," issuers of printed or forwarded documents (invoices, certificates, receipts) who want holders to verify authenticity fully offline — no server contacted, no document contents ever transmitted.\n",[950,65344,58222],{},[941,65346,21543],{}," (issuer side, ",[941,65349,65350],{},"@noy-db\u002Fhub\u002Fattestation",") — the offline verifier itself uses the separate zero-dependency ",[941,65353,35864],{}," package, no vault required.",[937,65356,58245,65357,58253],{},[1007,65358,65361],{"href":65359,"rel":65360},"https:\u002F\u002Fgithub.com\u002Fnoy-db\u002Fdocs\u002Fblob\u002Fmain\u002Fshowcases\u002Fsrc\u002Frecipe-attestation-verifier.recipe.test.ts",[3371],[941,65362,65363],{},"showcases\u002Fsrc\u002Frecipe-attestation-verifier.recipe.test.ts",[937,65365,65366,65367,65370,65371,65373,65374,65377],{},"This recipe verifies that a printed or forwarded document — an invoice, a\ncertificate, a receipt — is ",[950,65368,65369],{},"authentic and unaltered, fully offline",": no\nserver is contacted during verification, and no server ever holds or returns\nthe document's contents. The issuing firm keeps its records in a noy-db\n",[950,65372,2694],{}," — an end-to-end-encrypted document store (see\n",[1007,65375,65376],{"href":48},"Vaults & collections",") — and mints a signed,\nper-field commitment that travels inside a QR code on the document itself.\nAnyone holding the paper recomputes the commitment from the values they can\nread off it and checks the firm's Ed25519 signature against a public key\nbundled into the verifier — a single static HTML page with no backend.",[937,65379,65380],{},"Three parties are involved, and only the first one runs noy-db:",[1234,65382,65383,65397,65407],{},[1114,65384,65385,65388,65389,65391,65392,65394,65395,1520],{},[950,65386,65387],{},"The firm (issuer)"," — opens its vault, declares which fields of a record\nare attested, and calls ",[941,65390,21548],{},". This is the\n",[1007,65393,35860],{"href":610},", enabled with\n",[941,65396,21543],{},[1114,65398,65399,65402,65403,65406],{},[950,65400,65401],{},"The document"," — carries the QR. The QR contains ",[950,65404,65405],{},"no field values",",\nonly salted hashes of them plus the signature: holding the QR alone\nreveals nothing.",[1114,65408,65409,65412,65413,65415],{},[950,65410,65411],{},"The verifier (third party)"," — a static page or script that imports only\n",[941,65414,35864],{},", a pure, zero-dependency package that runs in Node\nand the browser. No vault, no keys to protect, no network.",[1022,65417,65419],{"id":65418},"issue-a-signed-qr-firm-side","Issue a signed QR (firm side)",[937,65421,65422,65423,65425,65426,65429,65430,65433,65434,957],{},"The firm declares an ",[941,65424,609],{}," field schema on the collection: which\nrecord fields are committed, and how each value is normalized before hashing\n(so ",[941,65427,65428],{},"\"1,234.50\""," typed by a human later matches the stored number ",[941,65431,65432],{},"1234.5"," —\nsee ",[1007,65435,65437],{"href":65436},"#field-normalizers","Field normalizers",[2217,65439,65441],{"className":2219,"code":65440,"language":2221,"meta":1037,"style":1037},"import { createNoydb } from '@noy-db\u002Fhub'\nimport { withAttestation } from '@noy-db\u002Fhub\u002Fattestation'\n\ninterface Invoice { id: string; invoiceNo: string; total: number; issueDate: string }\n\nconst attestation = {\n  fields: [\n    { path: 'invoiceNo', normalize: 'alnum-upper' as const },\n    { path: 'total',     normalize: 'cents'       as const },\n    { path: 'issueDate', normalize: 'iso-date'    as const },\n  ],\n}\n\nconst db = await createNoydb({\n  user: 'firm',                            \u002F\u002F omitting `store` uses the built-in in-memory store;\n  secret: 'firm-secret-2026',          \u002F\u002F pass to-file \u002F to-postgres \u002F … in production\n  attestationStrategy: withAttestation(),  \u002F\u002F opt-in: attestation stays out of the bundle otherwise\n})\nconst vault = await db.openVault('books')\n\nconst invoices = vault.collection\u003CInvoice>('invoices', { attestation })\nawait invoices.put('inv-1', {\n  id: 'inv-1', invoiceNo: 'INV-1042', total: 1234.5, issueDate: '2026-05-29',\n})\n\nconst { docId, qr, keyId } = await vault.issueAttestation('invoices', 'inv-1')\n\u002F\u002F → docId : '01JZK7Y1Q2M4N8R3T5V7X9ZABC'  — fresh 26-char id of THIS attestation (used for revocation)\n\u002F\u002F → qr    : 'eyJ2IjoxLCJkb2NJZCI6...'     — base64url payload; render it as a QR on the document\n\u002F\u002F → keyId : 'bd7e8268071ac41f'            — which signing key produced the signature\n\nconst { publicKeyB64 } = await vault.getDocumentSigningPublicKey()\n\u002F\u002F → { keyId: 'bd7e8268071ac41f', publicKeyB64: 'hpbs5UQivCLgPolFRoj5h1Q12OCXFlZkZfff-mEiAPM' }\n",[941,65442,65443,65461,65480,65484,65525,65529,65540,65548,65582,65617,65652,65658,65662,65666,65682,65699,65717,65733,65739,65766,65770,65808,65830,65879,65885,65889,65939,65944,65949,65954,65958,65982],{"__ignoreMap":1037},[2225,65444,65445,65447,65449,65451,65453,65455,65457,65459],{"class":2227,"line":2228},[2225,65446,2232],{"class":2231},[2225,65448,2236],{"class":2235},[2225,65450,2744],{"class":2239},[2225,65452,2243],{"class":2235},[2225,65454,2246],{"class":2231},[2225,65456,2249],{"class":2235},[2225,65458,1086],{"class":2252},[2225,65460,2256],{"class":2235},[2225,65462,65463,65465,65467,65470,65472,65474,65476,65478],{"class":2227,"line":1041},[2225,65464,2232],{"class":2231},[2225,65466,2236],{"class":2235},[2225,65468,65469],{"class":2239}," withAttestation",[2225,65471,2243],{"class":2235},[2225,65473,2246],{"class":2231},[2225,65475,2249],{"class":2235},[2225,65477,65350],{"class":2252},[2225,65479,2256],{"class":2235},[2225,65481,65482],{"class":2227,"line":1038},[2225,65483,2285],{"emptyLinePlaceholder":2284},[2225,65485,65486,65488,65490,65492,65494,65496,65498,65500,65502,65504,65506,65508,65510,65512,65514,65516,65519,65521,65523],{"class":2227,"line":2288},[2225,65487,6994],{"class":2774},[2225,65489,6343],{"class":6342},[2225,65491,2236],{"class":2235},[2225,65493,7070],{"class":2307},[2225,65495,2311],{"class":2235},[2225,65497,6366],{"class":6342},[2225,65499,3769],{"class":2235},[2225,65501,27739],{"class":2307},[2225,65503,2311],{"class":2235},[2225,65505,6366],{"class":6342},[2225,65507,3769],{"class":2235},[2225,65509,27452],{"class":2307},[2225,65511,2311],{"class":2235},[2225,65513,6356],{"class":6342},[2225,65515,3769],{"class":2235},[2225,65517,65518],{"class":2307}," issueDate",[2225,65520,2311],{"class":2235},[2225,65522,6366],{"class":6342},[2225,65524,6369],{"class":2235},[2225,65526,65527],{"class":2227,"line":2304},[2225,65528,2285],{"emptyLinePlaceholder":2284},[2225,65530,65531,65533,65536,65538],{"class":2227,"line":2356},[2225,65532,2775],{"class":2774},[2225,65534,65535],{"class":2239}," attestation ",[2225,65537,2781],{"class":2235},[2225,65539,2301],{"class":2235},[2225,65541,65542,65544,65546],{"class":2227,"line":2830},[2225,65543,57522],{"class":2307},[2225,65545,2311],{"class":2235},[2225,65547,32491],{"class":2239},[2225,65549,65550,65552,65554,65556,65558,65560,65562,65564,65567,65569,65571,65574,65576,65578,65580],{"class":2227,"line":2861},[2225,65551,32496],{"class":2235},[2225,65553,53037],{"class":2307},[2225,65555,2311],{"class":2235},[2225,65557,2249],{"class":2235},[2225,65559,27925],{"class":2252},[2225,65561,2805],{"class":2235},[2225,65563,2298],{"class":2235},[2225,65565,65566],{"class":2307}," normalize",[2225,65568,2311],{"class":2235},[2225,65570,2249],{"class":2235},[2225,65572,65573],{"class":2252},"alnum-upper",[2225,65575,2805],{"class":2235},[2225,65577,2266],{"class":2231},[2225,65579,8083],{"class":2774},[2225,65581,2353],{"class":2235},[2225,65583,65584,65586,65588,65590,65592,65594,65596,65598,65601,65603,65605,65608,65610,65613,65615],{"class":2227,"line":4246},[2225,65585,32496],{"class":2235},[2225,65587,53037],{"class":2307},[2225,65589,2311],{"class":2235},[2225,65591,2249],{"class":2235},[2225,65593,64200],{"class":2252},[2225,65595,2805],{"class":2235},[2225,65597,2298],{"class":2235},[2225,65599,65600],{"class":2307},"     normalize",[2225,65602,2311],{"class":2235},[2225,65604,2249],{"class":2235},[2225,65606,65607],{"class":2252},"cents",[2225,65609,2805],{"class":2235},[2225,65611,65612],{"class":2231},"       as",[2225,65614,8083],{"class":2774},[2225,65616,2353],{"class":2235},[2225,65618,65619,65621,65623,65625,65627,65630,65632,65634,65636,65638,65640,65643,65645,65648,65650],{"class":2227,"line":4253},[2225,65620,32496],{"class":2235},[2225,65622,53037],{"class":2307},[2225,65624,2311],{"class":2235},[2225,65626,2249],{"class":2235},[2225,65628,65629],{"class":2252},"issueDate",[2225,65631,2805],{"class":2235},[2225,65633,2298],{"class":2235},[2225,65635,65566],{"class":2307},[2225,65637,2311],{"class":2235},[2225,65639,2249],{"class":2235},[2225,65641,65642],{"class":2252},"iso-date",[2225,65644,2805],{"class":2235},[2225,65646,65647],{"class":2231},"    as",[2225,65649,8083],{"class":2774},[2225,65651,2353],{"class":2235},[2225,65653,65654,65656],{"class":2227,"line":4259},[2225,65655,32588],{"class":2239},[2225,65657,1707],{"class":2235},[2225,65659,65660],{"class":2227,"line":6548},[2225,65661,7351],{"class":2235},[2225,65663,65664],{"class":2227,"line":6576},[2225,65665,2285],{"emptyLinePlaceholder":2284},[2225,65667,65668,65670,65672,65674,65676,65678,65680],{"class":2227,"line":6582},[2225,65669,2775],{"class":2774},[2225,65671,2778],{"class":2239},[2225,65673,2781],{"class":2235},[2225,65675,2784],{"class":2231},[2225,65677,2744],{"class":2291},[2225,65679,2789],{"class":2239},[2225,65681,4200],{"class":2235},[2225,65683,65684,65686,65688,65690,65692,65694,65696],{"class":2227,"line":6587},[2225,65685,4205],{"class":2307},[2225,65687,2311],{"class":2235},[2225,65689,2249],{"class":2235},[2225,65691,39709],{"class":2252},[2225,65693,2805],{"class":2235},[2225,65695,2298],{"class":2235},[2225,65697,65698],{"class":2763},"                            \u002F\u002F omitting `store` uses the built-in in-memory store;\n",[2225,65700,65701,65703,65705,65707,65710,65712,65714],{"class":2227,"line":7174},[2225,65702,10423],{"class":2307},[2225,65704,2311],{"class":2235},[2225,65706,2249],{"class":2235},[2225,65708,65709],{"class":2252},"firm-secret-2026",[2225,65711,2805],{"class":2235},[2225,65713,2298],{"class":2235},[2225,65715,65716],{"class":2763},"          \u002F\u002F pass to-file \u002F to-postgres \u002F … in production\n",[2225,65718,65719,65722,65724,65726,65728,65730],{"class":2227,"line":7180},[2225,65720,65721],{"class":2307},"  attestationStrategy",[2225,65723,2311],{"class":2235},[2225,65725,65469],{"class":2291},[2225,65727,4228],{"class":2239},[2225,65729,2298],{"class":2235},[2225,65731,65732],{"class":2763},"  \u002F\u002F opt-in: attestation stays out of the bundle otherwise\n",[2225,65734,65735,65737],{"class":2227,"line":7220},[2225,65736,2359],{"class":2235},[2225,65738,2362],{"class":2239},[2225,65740,65741,65743,65745,65747,65749,65751,65753,65755,65757,65759,65762,65764],{"class":2227,"line":7225},[2225,65742,2775],{"class":2774},[2225,65744,2835],{"class":2239},[2225,65746,2781],{"class":2235},[2225,65748,2784],{"class":2231},[2225,65750,2842],{"class":2239},[2225,65752,1520],{"class":2235},[2225,65754,2847],{"class":2291},[2225,65756,2789],{"class":2239},[2225,65758,2805],{"class":2235},[2225,65760,65761],{"class":2252},"books",[2225,65763,2805],{"class":2235},[2225,65765,2362],{"class":2239},[2225,65767,65768],{"class":2227,"line":7231},[2225,65769,2285],{"emptyLinePlaceholder":2284},[2225,65771,65772,65774,65776,65778,65780,65782,65784,65786,65788,65790,65792,65794,65796,65798,65800,65802,65804,65806],{"class":2227,"line":7267},[2225,65773,2775],{"class":2774},[2225,65775,6461],{"class":2239},[2225,65777,2781],{"class":2235},[2225,65779,6466],{"class":2239},[2225,65781,1520],{"class":2235},[2225,65783,6471],{"class":2291},[2225,65785,6474],{"class":2235},[2225,65787,6477],{"class":6342},[2225,65789,6480],{"class":2235},[2225,65791,2789],{"class":2239},[2225,65793,2805],{"class":2235},[2225,65795,6487],{"class":2252},[2225,65797,2805],{"class":2235},[2225,65799,2298],{"class":2235},[2225,65801,2236],{"class":2235},[2225,65803,65535],{"class":2239},[2225,65805,2359],{"class":2235},[2225,65807,2362],{"class":2239},[2225,65809,65810,65812,65814,65816,65818,65820,65822,65824,65826,65828],{"class":2227,"line":7272},[2225,65811,6500],{"class":2231},[2225,65813,6503],{"class":2239},[2225,65815,1520],{"class":2235},[2225,65817,3161],{"class":2291},[2225,65819,2789],{"class":2239},[2225,65821,2805],{"class":2235},[2225,65823,6514],{"class":2252},[2225,65825,2805],{"class":2235},[2225,65827,2298],{"class":2235},[2225,65829,2301],{"class":2235},[2225,65831,65832,65834,65836,65838,65840,65842,65844,65846,65848,65850,65853,65855,65857,65859,65861,65864,65866,65868,65870,65872,65875,65877],{"class":2227,"line":7278},[2225,65833,28371],{"class":2307},[2225,65835,2311],{"class":2235},[2225,65837,2249],{"class":2235},[2225,65839,6514],{"class":2252},[2225,65841,2805],{"class":2235},[2225,65843,2298],{"class":2235},[2225,65845,27739],{"class":2307},[2225,65847,2311],{"class":2235},[2225,65849,2249],{"class":2235},[2225,65851,65852],{"class":2252},"INV-1042",[2225,65854,2805],{"class":2235},[2225,65856,2298],{"class":2235},[2225,65858,27452],{"class":2307},[2225,65860,2311],{"class":2235},[2225,65862,65863],{"class":6527}," 1234.5",[2225,65865,2298],{"class":2235},[2225,65867,65518],{"class":2307},[2225,65869,2311],{"class":2235},[2225,65871,2249],{"class":2235},[2225,65873,65874],{"class":2252},"2026-05-29",[2225,65876,2805],{"class":2235},[2225,65878,1707],{"class":2235},[2225,65880,65881,65883],{"class":2227,"line":7303},[2225,65882,2359],{"class":2235},[2225,65884,2362],{"class":2239},[2225,65886,65887],{"class":2227,"line":7308},[2225,65888,2285],{"emptyLinePlaceholder":2284},[2225,65890,65891,65893,65895,65898,65900,65903,65905,65908,65910,65912,65914,65916,65918,65921,65923,65925,65927,65929,65931,65933,65935,65937],{"class":2227,"line":7314},[2225,65892,2775],{"class":2774},[2225,65894,2236],{"class":2235},[2225,65896,65897],{"class":2239}," docId",[2225,65899,2298],{"class":2235},[2225,65901,65902],{"class":2239}," qr",[2225,65904,2298],{"class":2235},[2225,65906,65907],{"class":2239}," keyId ",[2225,65909,2359],{"class":2235},[2225,65911,6346],{"class":2235},[2225,65913,2784],{"class":2231},[2225,65915,6466],{"class":2239},[2225,65917,1520],{"class":2235},[2225,65919,65920],{"class":2291},"issueAttestation",[2225,65922,2789],{"class":2239},[2225,65924,2805],{"class":2235},[2225,65926,6487],{"class":2252},[2225,65928,2805],{"class":2235},[2225,65930,2298],{"class":2235},[2225,65932,2249],{"class":2235},[2225,65934,6514],{"class":2252},[2225,65936,2805],{"class":2235},[2225,65938,2362],{"class":2239},[2225,65940,65941],{"class":2227,"line":7348},[2225,65942,65943],{"class":2763},"\u002F\u002F → docId : '01JZK7Y1Q2M4N8R3T5V7X9ZABC'  — fresh 26-char id of THIS attestation (used for revocation)\n",[2225,65945,65946],{"class":2227,"line":8613},[2225,65947,65948],{"class":2763},"\u002F\u002F → qr    : 'eyJ2IjoxLCJkb2NJZCI6...'     — base64url payload; render it as a QR on the document\n",[2225,65950,65951],{"class":2227,"line":8633},[2225,65952,65953],{"class":2763},"\u002F\u002F → keyId : 'bd7e8268071ac41f'            — which signing key produced the signature\n",[2225,65955,65956],{"class":2227,"line":8662},[2225,65957,2285],{"emptyLinePlaceholder":2284},[2225,65959,65960,65962,65964,65967,65969,65971,65973,65975,65977,65980],{"class":2227,"line":8747},[2225,65961,2775],{"class":2774},[2225,65963,2236],{"class":2235},[2225,65965,65966],{"class":2239}," publicKeyB64 ",[2225,65968,2359],{"class":2235},[2225,65970,6346],{"class":2235},[2225,65972,2784],{"class":2231},[2225,65974,6466],{"class":2239},[2225,65976,1520],{"class":2235},[2225,65978,65979],{"class":2291},"getDocumentSigningPublicKey",[2225,65981,8146],{"class":2239},[2225,65983,65984],{"class":2227,"line":8752},[2225,65985,65986],{"class":2763},"\u002F\u002F → { keyId: 'bd7e8268071ac41f', publicKeyB64: 'hpbs5UQivCLgPolFRoj5h1Q12OCXFlZkZfff-mEiAPM' }\n",[937,65988,65989],{},"Two rules the hub enforces on this side:",[1111,65991,65992,66013],{},[1114,65993,65994,1324,66000,66002,66003,66005,66006,66009,66010,1520],{},[950,65995,65996,65997,65999],{},"Only the ",[941,65998,9898],{}," role can issue.",[941,66001,65920],{}," and the first\n(key-minting) call to ",[941,66004,65979],{}," throw\n",[941,66007,66008],{},"AttestationError"," for any other role — signing is the firm's identity\noperation. Once minted, the signing key is stable: every later issuance\nreuses the same ",[941,66011,66012],{},"keyId",[1114,66014,66015,66018,66019,66021,66022,6789,66025,1520],{},[950,66016,66017],{},"The collection must declare the schema."," Calling ",[941,66020,65920],{}," on a\ncollection created without ",[941,66023,66024],{},"{ attestation }",[941,66026,66008],{},[937,66028,66029,66030,66033],{},"The values are hashed from the ",[950,66031,66032],{},"canonical stored record"," (independent of any\ndisplay locale), each issuance uses a fresh random salt, and an encrypted\nindex entry is kept in the vault so the firm can revoke later.",[937,66035,66036,66037,66040,66041,1520],{},"To render the QR into a generated PDF server-side, see the companion recipe\n",[1007,66038,66039],{"href":520},"AWS-KMS PDF attestation"," — it is the\ngeneration side only and is ",[950,66042,66043],{},"not required to verify",[1022,66045,66047],{"id":66046},"what-travels-in-the-qr","What travels in the QR",[937,66049,960,66050,66053,66054,2311],{},[941,66051,66052],{},"qr"," string is base64url-encoded JSON. Decoded with ",[941,66055,66056],{},"decodeQr()",[2217,66058,66060],{"className":6604,"code":66059,"language":141,"meta":1037,"style":1037},"{\n  \"v\": 1,\n  \"docId\": \"01JZK7Y1Q2M4N8R3T5V7X9ZABC\",\n  \"salt\": \"fjFzQREHeuT_sow-L2GkgA\",\n  \"alg\": \"ed25519\",\n  \"keyId\": \"bd7e8268071ac41f\",\n  \"fieldHashes\": [\"WsR5Ol9nkUCpjbDNn47Z5mMAaM1…\", \"PUOQQylsmF2G1m069L8ojsFv8iQ…\", \"hBUvDo8iEFc8HPDzdmMoxJF18lD…\"],\n  \"sig\": \"0ghYQP_bd5O6qDth33-Xz08Q7_JbL96zX4HfVPKLYP8t…\"\n}\n",[941,66061,66062,66066,66080,66100,66120,66140,66159,66200,66218],{"__ignoreMap":1037},[2225,66063,66064],{"class":2227,"line":2228},[2225,66065,4200],{"class":2235},[2225,66067,66068,66070,66072,66074,66076,66078],{"class":2227,"line":1041},[2225,66069,9368],{"class":2235},[2225,66071,8160],{"class":2774},[2225,66073,6620],{"class":2235},[2225,66075,2311],{"class":2235},[2225,66077,9377],{"class":6527},[2225,66079,1707],{"class":2235},[2225,66081,66082,66084,66087,66089,66091,66093,66096,66098],{"class":2227,"line":1038},[2225,66083,9368],{"class":2235},[2225,66085,66086],{"class":2774},"docId",[2225,66088,6620],{"class":2235},[2225,66090,2311],{"class":2235},[2225,66092,6614],{"class":2235},[2225,66094,66095],{"class":2252},"01JZK7Y1Q2M4N8R3T5V7X9ZABC",[2225,66097,6620],{"class":2235},[2225,66099,1707],{"class":2235},[2225,66101,66102,66104,66107,66109,66111,66113,66116,66118],{"class":2227,"line":2288},[2225,66103,9368],{"class":2235},[2225,66105,66106],{"class":2774},"salt",[2225,66108,6620],{"class":2235},[2225,66110,2311],{"class":2235},[2225,66112,6614],{"class":2235},[2225,66114,66115],{"class":2252},"fjFzQREHeuT_sow-L2GkgA",[2225,66117,6620],{"class":2235},[2225,66119,1707],{"class":2235},[2225,66121,66122,66124,66127,66129,66131,66133,66136,66138],{"class":2227,"line":2304},[2225,66123,9368],{"class":2235},[2225,66125,66126],{"class":2774},"alg",[2225,66128,6620],{"class":2235},[2225,66130,2311],{"class":2235},[2225,66132,6614],{"class":2235},[2225,66134,66135],{"class":2252},"ed25519",[2225,66137,6620],{"class":2235},[2225,66139,1707],{"class":2235},[2225,66141,66142,66144,66146,66148,66150,66152,66155,66157],{"class":2227,"line":2356},[2225,66143,9368],{"class":2235},[2225,66145,66012],{"class":2774},[2225,66147,6620],{"class":2235},[2225,66149,2311],{"class":2235},[2225,66151,6614],{"class":2235},[2225,66153,66154],{"class":2252},"bd7e8268071ac41f",[2225,66156,6620],{"class":2235},[2225,66158,1707],{"class":2235},[2225,66160,66161,66163,66166,66168,66170,66172,66174,66177,66179,66181,66183,66186,66188,66190,66192,66195,66197],{"class":2227,"line":2830},[2225,66162,9368],{"class":2235},[2225,66164,66165],{"class":2774},"fieldHashes",[2225,66167,6620],{"class":2235},[2225,66169,2311],{"class":2235},[2225,66171,8667],{"class":2235},[2225,66173,6620],{"class":2235},[2225,66175,66176],{"class":2252},"WsR5Ol9nkUCpjbDNn47Z5mMAaM1…",[2225,66178,6620],{"class":2235},[2225,66180,2298],{"class":2235},[2225,66182,6614],{"class":2235},[2225,66184,66185],{"class":2252},"PUOQQylsmF2G1m069L8ojsFv8iQ…",[2225,66187,6620],{"class":2235},[2225,66189,2298],{"class":2235},[2225,66191,6614],{"class":2235},[2225,66193,66194],{"class":2252},"hBUvDo8iEFc8HPDzdmMoxJF18lD…",[2225,66196,6620],{"class":2235},[2225,66198,66199],{"class":2235},"],\n",[2225,66201,66202,66204,66207,66209,66211,66213,66216],{"class":2227,"line":2861},[2225,66203,9368],{"class":2235},[2225,66205,66206],{"class":2774},"sig",[2225,66208,6620],{"class":2235},[2225,66210,2311],{"class":2235},[2225,66212,6614],{"class":2235},[2225,66214,66215],{"class":2252},"0ghYQP_bd5O6qDth33-Xz08Q7_JbL96zX4HfVPKLYP8t…",[2225,66217,9449],{"class":2235},[2225,66219,66220],{"class":2227,"line":4246},[2225,66221,7351],{"class":2235},[937,66223,66224,66225,1520],{},"One SHA-256 hash per schema field, in schema order — never the values\nthemselves. Each hash covers the per-document salt, the field path, and the\nnormalized value, so a low-entropy value cannot be brute-forced across\ndocuments and two fields sharing a value still hash differently. The\nsignature covers ",[941,66226,66227],{},"{v, docId, salt, keyId, fieldHashes}",[1022,66229,66231],{"id":66230},"the-offline-verifier","The offline verifier",[937,66233,66234,66235,66237,66238,968,66241,2462,66244,66247,66248,66251],{},"The verify side composes three functions from ",[941,66236,35864],{}," —\n",[941,66239,66240],{},"decodeQr",[941,66242,66243],{},"verifyAttestation",[941,66245,66246],{},"verifyRevocationList"," — into a single\n",[941,66249,66250],{},"verifyDocument()"," that always returns one of six verdicts instead of\nthrowing on bad input. This is the complete verifier core:",[2217,66253,66255],{"className":2219,"code":66254,"language":2221,"meta":1037,"style":1037},"import {\n  decodeQr, verifyAttestation, verifyRevocationList,\n  type AttestationFieldSchema, type QrPayload, type RevocationList,\n} from '@noy-db\u002Fattestation'\n\nexport interface VerifierConfig {\n  publicKeys: Record\u003Cstring, string>   \u002F\u002F keyId → publicKeyB64, bundled at build time\n  fieldSchema: AttestationFieldSchema  \u002F\u002F same schema the firm declared\n  revocationList?: RevocationList      \u002F\u002F optional, signed by the firm\n}\n\nexport type VerifierOutcome =\n  | 'authentic-valid'\n  | 'authentic-revoked'\n  | 'altered'\n  | 'signature-invalid'\n  | 'unknown-key'\n  | 'unreadable-qr'\n\nexport interface Verdict {\n  outcome: VerifierOutcome\n  perField: Array\u003C{ path: string; match: boolean }>\n  revocationTrusted: boolean | null    \u002F\u002F null: no list · false: list failed its own signature\n  keyId?: string\n  docId?: string\n}\n\nexport async function verifyDocument(\n  qr: string,\n  claimedFields: Record\u003Cstring, unknown>,\n  config: VerifierConfig,\n): Promise\u003CVerdict> {\n  let payload: QrPayload\n  try {\n    payload = decodeQr(qr)\n  } catch {\n    return { outcome: 'unreadable-qr', perField: [], revocationTrusted: null }\n  }\n\n  const matchedKey = config.publicKeys[payload.keyId]\n  if (matchedKey === undefined) {\n    return { outcome: 'unknown-key', perField: [], revocationTrusted: null, keyId: payload.keyId, docId: payload.docId }\n  }\n\n  \u002F\u002F The revocation list is untrusted input (often network-fetched): verify its\n  \u002F\u002F OWN signature first, and only honor it if that check passes.\n  let revocationTrusted: boolean | null = null\n  if (config.revocationList) {\n    revocationTrusted = await verifyRevocationList(config.revocationList, matchedKey)\n  }\n\n  const result = await verifyAttestation({\n    qr,\n    claimedFields,\n    fieldSchema: config.fieldSchema,\n    publicKeys: config.publicKeys,\n    ...(revocationTrusted === true && config.revocationList ? { revocation: { list: config.revocationList } } : {}),\n  })\n\n  const perField = result.perField.map((f) => ({ path: f.path, match: f.match }))\n  const allMatch = perField.length > 0 && perField.every((f) => f.match)\n\n  let outcome: VerifierOutcome\n  if (!result.signatureValid) outcome = 'signature-invalid'\n  else if (!allMatch) outcome = 'altered'\n  else if (result.revoked === true) outcome = 'authentic-revoked'\n  else outcome = 'authentic-valid'\n\n  return { outcome, perField, revocationTrusted, keyId: payload.keyId, docId: payload.docId }\n}\n",[941,66256,66257,66263,66280,66304,66316,66320,66331,66353,66365,66377,66381,66385,66397,66409,66420,66431,66442,66453,66464,66468,66479,66489,66518,66534,66542,66551,66555,66559,66572,66583,66602,66613,66628,66640,66647,66663,66671,66710,66714,66718,66745,66762,66820,66824,66828,66833,66838,66856,66874,66899,66903,66907,66923,66930,66937,66953,66968,67022,67028,67033,67095,67141,67146,67157,67186,67213,67245,67260,67265,67308],{"__ignoreMap":1037},[2225,66258,66259,66261],{"class":2227,"line":2228},[2225,66260,2232],{"class":2231},[2225,66262,2301],{"class":2235},[2225,66264,66265,66268,66270,66273,66275,66278],{"class":2227,"line":1041},[2225,66266,66267],{"class":2239},"  decodeQr",[2225,66269,2298],{"class":2235},[2225,66271,66272],{"class":2239}," verifyAttestation",[2225,66274,2298],{"class":2235},[2225,66276,66277],{"class":2239}," verifyRevocationList",[2225,66279,1707],{"class":2235},[2225,66281,66282,66285,66288,66290,66292,66295,66297,66299,66302],{"class":2227,"line":1038},[2225,66283,66284],{"class":2231},"  type",[2225,66286,66287],{"class":2239}," AttestationFieldSchema",[2225,66289,2298],{"class":2235},[2225,66291,3399],{"class":2231},[2225,66293,66294],{"class":2239}," QrPayload",[2225,66296,2298],{"class":2235},[2225,66298,3399],{"class":2231},[2225,66300,66301],{"class":2239}," RevocationList",[2225,66303,1707],{"class":2235},[2225,66305,66306,66308,66310,66312,66314],{"class":2227,"line":2288},[2225,66307,2359],{"class":2235},[2225,66309,2246],{"class":2231},[2225,66311,2249],{"class":2235},[2225,66313,35864],{"class":2252},[2225,66315,2256],{"class":2235},[2225,66317,66318],{"class":2227,"line":2304},[2225,66319,2285],{"emptyLinePlaceholder":2284},[2225,66321,66322,66324,66326,66329],{"class":2227,"line":2356},[2225,66323,8080],{"class":2231},[2225,66325,54003],{"class":2774},[2225,66327,66328],{"class":6342}," VerifierConfig",[2225,66330,2301],{"class":2235},[2225,66332,66333,66336,66338,66340,66342,66344,66346,66348,66350],{"class":2227,"line":2830},[2225,66334,66335],{"class":2307},"  publicKeys",[2225,66337,2311],{"class":2235},[2225,66339,22527],{"class":6342},[2225,66341,6474],{"class":2235},[2225,66343,7259],{"class":6342},[2225,66345,2298],{"class":2235},[2225,66347,6366],{"class":6342},[2225,66349,6480],{"class":2235},[2225,66351,66352],{"class":2763},"   \u002F\u002F keyId → publicKeyB64, bundled at build time\n",[2225,66354,66355,66358,66360,66362],{"class":2227,"line":2861},[2225,66356,66357],{"class":2307},"  fieldSchema",[2225,66359,2311],{"class":2235},[2225,66361,66287],{"class":6342},[2225,66363,66364],{"class":2763},"  \u002F\u002F same schema the firm declared\n",[2225,66366,66367,66370,66372,66374],{"class":2227,"line":4246},[2225,66368,66369],{"class":2307},"  revocationList",[2225,66371,7011],{"class":2235},[2225,66373,66301],{"class":6342},[2225,66375,66376],{"class":2763},"      \u002F\u002F optional, signed by the firm\n",[2225,66378,66379],{"class":2227,"line":4253},[2225,66380,7351],{"class":2235},[2225,66382,66383],{"class":2227,"line":4259},[2225,66384,2285],{"emptyLinePlaceholder":2284},[2225,66386,66387,66389,66391,66394],{"class":2227,"line":6548},[2225,66388,8080],{"class":2231},[2225,66390,3399],{"class":2774},[2225,66392,66393],{"class":6342}," VerifierOutcome",[2225,66395,66396],{"class":2235}," =\n",[2225,66398,66399,66402,66404,66407],{"class":2227,"line":6576},[2225,66400,66401],{"class":2235},"  |",[2225,66403,2249],{"class":2235},[2225,66405,66406],{"class":2252},"authentic-valid",[2225,66408,2256],{"class":2235},[2225,66410,66411,66413,66415,66418],{"class":2227,"line":6582},[2225,66412,66401],{"class":2235},[2225,66414,2249],{"class":2235},[2225,66416,66417],{"class":2252},"authentic-revoked",[2225,66419,2256],{"class":2235},[2225,66421,66422,66424,66426,66429],{"class":2227,"line":6587},[2225,66423,66401],{"class":2235},[2225,66425,2249],{"class":2235},[2225,66427,66428],{"class":2252},"altered",[2225,66430,2256],{"class":2235},[2225,66432,66433,66435,66437,66440],{"class":2227,"line":7174},[2225,66434,66401],{"class":2235},[2225,66436,2249],{"class":2235},[2225,66438,66439],{"class":2252},"signature-invalid",[2225,66441,2256],{"class":2235},[2225,66443,66444,66446,66448,66451],{"class":2227,"line":7180},[2225,66445,66401],{"class":2235},[2225,66447,2249],{"class":2235},[2225,66449,66450],{"class":2252},"unknown-key",[2225,66452,2256],{"class":2235},[2225,66454,66455,66457,66459,66462],{"class":2227,"line":7220},[2225,66456,66401],{"class":2235},[2225,66458,2249],{"class":2235},[2225,66460,66461],{"class":2252},"unreadable-qr",[2225,66463,2256],{"class":2235},[2225,66465,66466],{"class":2227,"line":7225},[2225,66467,2285],{"emptyLinePlaceholder":2284},[2225,66469,66470,66472,66474,66477],{"class":2227,"line":7231},[2225,66471,8080],{"class":2231},[2225,66473,54003],{"class":2774},[2225,66475,66476],{"class":6342}," Verdict",[2225,66478,2301],{"class":2235},[2225,66480,66481,66484,66486],{"class":2227,"line":7267},[2225,66482,66483],{"class":2307},"  outcome",[2225,66485,2311],{"class":2235},[2225,66487,66488],{"class":6342}," VerifierOutcome\n",[2225,66490,66491,66494,66496,66499,66501,66503,66505,66507,66509,66512,66514,66516],{"class":2227,"line":7272},[2225,66492,66493],{"class":2307},"  perField",[2225,66495,2311],{"class":2235},[2225,66497,66498],{"class":6342}," Array",[2225,66500,17851],{"class":2235},[2225,66502,53037],{"class":2307},[2225,66504,2311],{"class":2235},[2225,66506,6366],{"class":6342},[2225,66508,3769],{"class":2235},[2225,66510,66511],{"class":2307}," match",[2225,66513,2311],{"class":2235},[2225,66515,7723],{"class":6342},[2225,66517,17861],{"class":2235},[2225,66519,66520,66523,66525,66527,66529,66531],{"class":2227,"line":7278},[2225,66521,66522],{"class":2307},"  revocationTrusted",[2225,66524,2311],{"class":2235},[2225,66526,7723],{"class":6342},[2225,66528,7087],{"class":2235},[2225,66530,7090],{"class":6342},[2225,66532,66533],{"class":2763},"    \u002F\u002F null: no list · false: list failed its own signature\n",[2225,66535,66536,66538,66540],{"class":2227,"line":7303},[2225,66537,33397],{"class":2307},[2225,66539,7011],{"class":2235},[2225,66541,7014],{"class":6342},[2225,66543,66544,66547,66549],{"class":2227,"line":7308},[2225,66545,66546],{"class":2307},"  docId",[2225,66548,7011],{"class":2235},[2225,66550,7014],{"class":6342},[2225,66552,66553],{"class":2227,"line":7314},[2225,66554,7351],{"class":2235},[2225,66556,66557],{"class":2227,"line":7348},[2225,66558,2285],{"emptyLinePlaceholder":2284},[2225,66560,66561,66563,66565,66567,66570],{"class":2227,"line":8613},[2225,66562,8080],{"class":2231},[2225,66564,38717],{"class":2774},[2225,66566,40856],{"class":2774},[2225,66568,66569],{"class":2291}," verifyDocument",[2225,66571,11683],{"class":2235},[2225,66573,66574,66577,66579,66581],{"class":2227,"line":8633},[2225,66575,66576],{"class":7052},"  qr",[2225,66578,2311],{"class":2235},[2225,66580,6366],{"class":6342},[2225,66582,1707],{"class":2235},[2225,66584,66585,66588,66590,66592,66594,66596,66598,66600],{"class":2227,"line":8662},[2225,66586,66587],{"class":7052},"  claimedFields",[2225,66589,2311],{"class":2235},[2225,66591,22527],{"class":6342},[2225,66593,6474],{"class":2235},[2225,66595,7259],{"class":6342},[2225,66597,2298],{"class":2235},[2225,66599,40717],{"class":6342},[2225,66601,12006],{"class":2235},[2225,66603,66604,66607,66609,66611],{"class":2227,"line":8747},[2225,66605,66606],{"class":7052},"  config",[2225,66608,2311],{"class":2235},[2225,66610,66328],{"class":6342},[2225,66612,1707],{"class":2235},[2225,66614,66615,66617,66619,66621,66624,66626],{"class":2227,"line":8752},[2225,66616,6255],{"class":2235},[2225,66618,7079],{"class":6342},[2225,66620,6474],{"class":2235},[2225,66622,66623],{"class":6342},"Verdict",[2225,66625,6480],{"class":2235},[2225,66627,2301],{"class":2235},[2225,66629,66630,66633,66635,66637],{"class":2227,"line":8768},[2225,66631,66632],{"class":2774},"  let",[2225,66634,19094],{"class":2239},[2225,66636,2311],{"class":2235},[2225,66638,66639],{"class":6342}," QrPayload\n",[2225,66641,66642,66645],{"class":2227,"line":8785},[2225,66643,66644],{"class":2231},"  try",[2225,66646,2301],{"class":2235},[2225,66648,66649,66652,66654,66657,66659,66661],{"class":2227,"line":8815},[2225,66650,66651],{"class":2239},"    payload",[2225,66653,6346],{"class":2235},[2225,66655,66656],{"class":2291}," decodeQr",[2225,66658,2789],{"class":2307},[2225,66660,66052],{"class":2239},[2225,66662,2362],{"class":2307},[2225,66664,66665,66667,66669],{"class":2227,"line":8854},[2225,66666,19143],{"class":2235},[2225,66668,7598],{"class":2231},[2225,66670,2301],{"class":2235},[2225,66672,66673,66676,66678,66681,66683,66685,66687,66689,66691,66694,66696,66699,66701,66704,66706,66708],{"class":2227,"line":8874},[2225,66674,66675],{"class":2231},"    return",[2225,66677,2236],{"class":2235},[2225,66679,66680],{"class":2307}," outcome",[2225,66682,2311],{"class":2235},[2225,66684,2249],{"class":2235},[2225,66686,66461],{"class":2252},[2225,66688,2805],{"class":2235},[2225,66690,2298],{"class":2235},[2225,66692,66693],{"class":2307}," perField",[2225,66695,2311],{"class":2235},[2225,66697,66698],{"class":2307}," []",[2225,66700,2298],{"class":2235},[2225,66702,66703],{"class":2307}," revocationTrusted",[2225,66705,2311],{"class":2235},[2225,66707,7090],{"class":2235},[2225,66709,6369],{"class":2235},[2225,66711,66712],{"class":2227,"line":8911},[2225,66713,7632],{"class":2235},[2225,66715,66716],{"class":2227,"line":8916},[2225,66717,2285],{"emptyLinePlaceholder":2284},[2225,66719,66720,66722,66725,66727,66729,66731,66734,66736,66739,66741,66743],{"class":2227,"line":8924},[2225,66721,8123],{"class":2774},[2225,66723,66724],{"class":2239}," matchedKey",[2225,66726,6346],{"class":2235},[2225,66728,36491],{"class":2239},[2225,66730,1520],{"class":2235},[2225,66732,66733],{"class":2239},"publicKeys",[2225,66735,8885],{"class":2307},[2225,66737,66738],{"class":2239},"payload",[2225,66740,1520],{"class":2235},[2225,66742,66012],{"class":2239},[2225,66744,26484],{"class":2307},[2225,66746,66747,66749,66751,66754,66756,66758,66760],{"class":2227,"line":8929},[2225,66748,7608],{"class":2231},[2225,66750,953],{"class":2307},[2225,66752,66753],{"class":2239},"matchedKey",[2225,66755,9025],{"class":2235},[2225,66757,8483],{"class":2235},[2225,66759,8495],{"class":2307},[2225,66761,4200],{"class":2235},[2225,66763,66764,66766,66768,66770,66772,66774,66776,66778,66780,66782,66784,66786,66788,66790,66792,66795,66798,66800,66802,66804,66806,66808,66810,66812,66814,66816,66818],{"class":2227,"line":8949},[2225,66765,66675],{"class":2231},[2225,66767,2236],{"class":2235},[2225,66769,66680],{"class":2307},[2225,66771,2311],{"class":2235},[2225,66773,2249],{"class":2235},[2225,66775,66450],{"class":2252},[2225,66777,2805],{"class":2235},[2225,66779,2298],{"class":2235},[2225,66781,66693],{"class":2307},[2225,66783,2311],{"class":2235},[2225,66785,66698],{"class":2307},[2225,66787,2298],{"class":2235},[2225,66789,66703],{"class":2307},[2225,66791,2311],{"class":2235},[2225,66793,66794],{"class":2235}," null,",[2225,66796,66797],{"class":2307}," keyId",[2225,66799,2311],{"class":2235},[2225,66801,19094],{"class":2239},[2225,66803,1520],{"class":2235},[2225,66805,66012],{"class":2239},[2225,66807,2298],{"class":2235},[2225,66809,65897],{"class":2307},[2225,66811,2311],{"class":2235},[2225,66813,19094],{"class":2239},[2225,66815,1520],{"class":2235},[2225,66817,66086],{"class":2239},[2225,66819,6369],{"class":2235},[2225,66821,66822],{"class":2227,"line":8955},[2225,66823,7632],{"class":2235},[2225,66825,66826],{"class":2227,"line":8983},[2225,66827,2285],{"emptyLinePlaceholder":2284},[2225,66829,66830],{"class":2227,"line":9016},[2225,66831,66832],{"class":2763},"  \u002F\u002F The revocation list is untrusted input (often network-fetched): verify its\n",[2225,66834,66835],{"class":2227,"line":9063},[2225,66836,66837],{"class":2763},"  \u002F\u002F OWN signature first, and only honor it if that check passes.\n",[2225,66839,66840,66842,66844,66846,66848,66850,66852,66854],{"class":2227,"line":9068},[2225,66841,66632],{"class":2774},[2225,66843,66703],{"class":2239},[2225,66845,2311],{"class":2235},[2225,66847,7723],{"class":6342},[2225,66849,7087],{"class":2235},[2225,66851,7090],{"class":6342},[2225,66853,6346],{"class":2235},[2225,66855,8384],{"class":2235},[2225,66857,66858,66860,66862,66865,66867,66870,66872],{"class":2227,"line":9106},[2225,66859,7608],{"class":2231},[2225,66861,953],{"class":2307},[2225,66863,66864],{"class":2239},"config",[2225,66866,1520],{"class":2235},[2225,66868,66869],{"class":2239},"revocationList",[2225,66871,8495],{"class":2307},[2225,66873,4200],{"class":2235},[2225,66875,66876,66879,66881,66883,66885,66887,66889,66891,66893,66895,66897],{"class":2227,"line":9143},[2225,66877,66878],{"class":2239},"    revocationTrusted",[2225,66880,6346],{"class":2235},[2225,66882,2784],{"class":2231},[2225,66884,66277],{"class":2291},[2225,66886,2789],{"class":2307},[2225,66888,66864],{"class":2239},[2225,66890,1520],{"class":2235},[2225,66892,66869],{"class":2239},[2225,66894,2298],{"class":2235},[2225,66896,66724],{"class":2239},[2225,66898,2362],{"class":2307},[2225,66900,66901],{"class":2227,"line":9177},[2225,66902,7632],{"class":2235},[2225,66904,66905],{"class":2227,"line":9183},[2225,66906,2285],{"emptyLinePlaceholder":2284},[2225,66908,66909,66911,66913,66915,66917,66919,66921],{"class":2227,"line":9188},[2225,66910,8123],{"class":2774},[2225,66912,40906],{"class":2239},[2225,66914,6346],{"class":2235},[2225,66916,2784],{"class":2231},[2225,66918,66272],{"class":2291},[2225,66920,2789],{"class":2307},[2225,66922,4200],{"class":2235},[2225,66924,66925,66928],{"class":2227,"line":9193},[2225,66926,66927],{"class":2239},"    qr",[2225,66929,1707],{"class":2235},[2225,66931,66932,66935],{"class":2227,"line":9198},[2225,66933,66934],{"class":2239},"    claimedFields",[2225,66936,1707],{"class":2235},[2225,66938,66939,66942,66944,66946,66948,66951],{"class":2227,"line":9205},[2225,66940,66941],{"class":2307},"    fieldSchema",[2225,66943,2311],{"class":2235},[2225,66945,36491],{"class":2239},[2225,66947,1520],{"class":2235},[2225,66949,66950],{"class":2239},"fieldSchema",[2225,66952,1707],{"class":2235},[2225,66954,66955,66958,66960,66962,66964,66966],{"class":2227,"line":9210},[2225,66956,66957],{"class":2307},"    publicKeys",[2225,66959,2311],{"class":2235},[2225,66961,36491],{"class":2239},[2225,66963,1520],{"class":2235},[2225,66965,66733],{"class":2239},[2225,66967,1707],{"class":2235},[2225,66969,66970,66972,66974,66977,66979,66981,66983,66985,66987,66989,66991,66993,66996,66998,67000,67002,67004,67006,67008,67010,67012,67014,67016,67018,67020],{"class":2227,"line":9270},[2225,66971,18908],{"class":2235},[2225,66973,2789],{"class":2307},[2225,66975,66976],{"class":2239},"revocationTrusted",[2225,66978,9025],{"class":2235},[2225,66980,2322],{"class":2321},[2225,66982,8486],{"class":2235},[2225,66984,36491],{"class":2239},[2225,66986,1520],{"class":2235},[2225,66988,66869],{"class":2239},[2225,66990,40953],{"class":2235},[2225,66992,2236],{"class":2235},[2225,66994,66995],{"class":2307}," revocation",[2225,66997,2311],{"class":2235},[2225,66999,2236],{"class":2235},[2225,67001,8618],{"class":2307},[2225,67003,2311],{"class":2235},[2225,67005,36491],{"class":2239},[2225,67007,1520],{"class":2235},[2225,67009,66869],{"class":2239},[2225,67011,2243],{"class":2235},[2225,67013,2243],{"class":2235},[2225,67015,40959],{"class":2235},[2225,67017,8896],{"class":2235},[2225,67019,1381],{"class":2307},[2225,67021,1707],{"class":2235},[2225,67023,67024,67026],{"class":2227,"line":9297},[2225,67025,19143],{"class":2235},[2225,67027,2362],{"class":2307},[2225,67029,67031],{"class":2227,"line":67030},59,[2225,67032,2285],{"emptyLinePlaceholder":2284},[2225,67034,67036,67038,67040,67042,67044,67046,67049,67051,67053,67055,67057,67060,67062,67064,67066,67068,67070,67072,67075,67077,67079,67081,67083,67085,67087,67089,67091,67093],{"class":2227,"line":67035},60,[2225,67037,8123],{"class":2774},[2225,67039,66693],{"class":2239},[2225,67041,6346],{"class":2235},[2225,67043,40906],{"class":2239},[2225,67045,1520],{"class":2235},[2225,67047,67048],{"class":2239},"perField",[2225,67050,1520],{"class":2235},[2225,67052,8244],{"class":2291},[2225,67054,2789],{"class":2307},[2225,67056,2789],{"class":2235},[2225,67058,67059],{"class":7052},"f",[2225,67061,1381],{"class":2235},[2225,67063,8116],{"class":2774},[2225,67065,953],{"class":2307},[2225,67067,2792],{"class":2235},[2225,67069,53037],{"class":2307},[2225,67071,2311],{"class":2235},[2225,67073,67074],{"class":2239}," f",[2225,67076,1520],{"class":2235},[2225,67078,52937],{"class":2239},[2225,67080,2298],{"class":2235},[2225,67082,66511],{"class":2307},[2225,67084,2311],{"class":2235},[2225,67086,67074],{"class":2239},[2225,67088,1520],{"class":2235},[2225,67090,7482],{"class":2239},[2225,67092,2243],{"class":2235},[2225,67094,8606],{"class":2307},[2225,67096,67098,67100,67103,67105,67107,67109,67111,67113,67115,67117,67119,67121,67123,67125,67127,67129,67131,67133,67135,67137,67139],{"class":2227,"line":67097},61,[2225,67099,8123],{"class":2774},[2225,67101,67102],{"class":2239}," allMatch",[2225,67104,6346],{"class":2235},[2225,67106,66693],{"class":2239},[2225,67108,1520],{"class":2235},[2225,67110,8742],{"class":2239},[2225,67112,36500],{"class":2235},[2225,67114,8464],{"class":6527},[2225,67116,8486],{"class":2235},[2225,67118,66693],{"class":2239},[2225,67120,1520],{"class":2235},[2225,67122,2119],{"class":2291},[2225,67124,2789],{"class":2307},[2225,67126,2789],{"class":2235},[2225,67128,67059],{"class":7052},[2225,67130,1381],{"class":2235},[2225,67132,8116],{"class":2774},[2225,67134,67074],{"class":2239},[2225,67136,1520],{"class":2235},[2225,67138,7482],{"class":2239},[2225,67140,2362],{"class":2307},[2225,67142,67144],{"class":2227,"line":67143},62,[2225,67145,2285],{"emptyLinePlaceholder":2284},[2225,67147,67149,67151,67153,67155],{"class":2227,"line":67148},63,[2225,67150,66632],{"class":2774},[2225,67152,66680],{"class":2239},[2225,67154,2311],{"class":2235},[2225,67156,66488],{"class":6342},[2225,67158,67160,67162,67164,67166,67168,67170,67173,67175,67178,67180,67182,67184],{"class":2227,"line":67159},64,[2225,67161,7608],{"class":2231},[2225,67163,953],{"class":2307},[2225,67165,35134],{"class":2235},[2225,67167,11195],{"class":2239},[2225,67169,1520],{"class":2235},[2225,67171,67172],{"class":2239},"signatureValid",[2225,67174,8495],{"class":2307},[2225,67176,67177],{"class":2239},"outcome",[2225,67179,6346],{"class":2235},[2225,67181,2249],{"class":2235},[2225,67183,66439],{"class":2252},[2225,67185,2256],{"class":2235},[2225,67187,67189,67192,67194,67196,67198,67201,67203,67205,67207,67209,67211],{"class":2227,"line":67188},65,[2225,67190,67191],{"class":2231},"  else",[2225,67193,46148],{"class":2231},[2225,67195,953],{"class":2307},[2225,67197,35134],{"class":2235},[2225,67199,67200],{"class":2239},"allMatch",[2225,67202,8495],{"class":2307},[2225,67204,67177],{"class":2239},[2225,67206,6346],{"class":2235},[2225,67208,2249],{"class":2235},[2225,67210,66428],{"class":2252},[2225,67212,2256],{"class":2235},[2225,67214,67216,67218,67220,67222,67224,67226,67229,67231,67233,67235,67237,67239,67241,67243],{"class":2227,"line":67215},66,[2225,67217,67191],{"class":2231},[2225,67219,46148],{"class":2231},[2225,67221,953],{"class":2307},[2225,67223,11195],{"class":2239},[2225,67225,1520],{"class":2235},[2225,67227,67228],{"class":2239},"revoked",[2225,67230,9025],{"class":2235},[2225,67232,2322],{"class":2321},[2225,67234,8495],{"class":2307},[2225,67236,67177],{"class":2239},[2225,67238,6346],{"class":2235},[2225,67240,2249],{"class":2235},[2225,67242,66417],{"class":2252},[2225,67244,2256],{"class":2235},[2225,67246,67248,67250,67252,67254,67256,67258],{"class":2227,"line":67247},67,[2225,67249,67191],{"class":2231},[2225,67251,66680],{"class":2239},[2225,67253,6346],{"class":2235},[2225,67255,2249],{"class":2235},[2225,67257,66406],{"class":2252},[2225,67259,2256],{"class":2235},[2225,67261,67263],{"class":2227,"line":67262},68,[2225,67264,2285],{"emptyLinePlaceholder":2284},[2225,67266,67268,67270,67272,67274,67276,67278,67280,67282,67284,67286,67288,67290,67292,67294,67296,67298,67300,67302,67304,67306],{"class":2227,"line":67267},69,[2225,67269,8220],{"class":2231},[2225,67271,2236],{"class":2235},[2225,67273,66680],{"class":2239},[2225,67275,2298],{"class":2235},[2225,67277,66693],{"class":2239},[2225,67279,2298],{"class":2235},[2225,67281,66703],{"class":2239},[2225,67283,2298],{"class":2235},[2225,67285,66797],{"class":2307},[2225,67287,2311],{"class":2235},[2225,67289,19094],{"class":2239},[2225,67291,1520],{"class":2235},[2225,67293,66012],{"class":2239},[2225,67295,2298],{"class":2235},[2225,67297,65897],{"class":2307},[2225,67299,2311],{"class":2235},[2225,67301,19094],{"class":2239},[2225,67303,1520],{"class":2235},[2225,67305,66086],{"class":2239},[2225,67307,6369],{"class":2235},[2225,67309,67311],{"class":2227,"line":67310},70,[2225,67312,7351],{"class":2235},[937,67314,67315],{},"The six verdicts:",[1338,67317,67318,67326],{},[1341,67319,67320],{},[1344,67321,67322,67324],{},[1347,67323,66623],{},[1347,67325,20200],{},[1357,67327,67328,67337,67346,67358,67367,67379],{},[1344,67329,67330,67334],{},[1362,67331,67332],{},[941,67333,66406],{},[1362,67335,67336],{},"Signature verifies with a bundled key, every field matches, not revoked.",[1344,67338,67339,67343],{},[1362,67340,67341],{},[941,67342,66417],{},[1362,67344,67345],{},"Genuinely issued by the firm, later withdrawn (on the trusted revocation list).",[1344,67347,67348,67352],{},[1362,67349,67350],{},[941,67351,66428],{},[1362,67353,67354,67355,67357],{},"Signature is valid but at least one typed value does not match its committed hash — ",[941,67356,67048],{}," localizes which.",[1344,67359,67360,67364],{},[1362,67361,67362],{},[941,67363,66439],{},[1362,67365,67366],{},"The payload does not verify against the key it names — forged or corrupted.",[1344,67368,67369,67373],{},[1362,67370,67371],{},[941,67372,66450],{},[1362,67374,67375,67376,67378],{},"The QR names a ",[941,67377,66012],{}," this verifier build does not bundle — update the verifier.",[1344,67380,67381,67385],{},[1362,67382,67383],{},[941,67384,66461],{},[1362,67386,67387],{},"The scanned\u002Fpasted text is not a valid attestation payload.",[1022,67389,67391],{"id":67390},"run-a-verification","Run a verification",[937,67393,67394],{},"The third party scans the QR and types the values as printed on the paper:",[2217,67396,67398],{"className":2219,"code":67397,"language":2221,"meta":1037,"style":1037},"const config = {\n  publicKeys: { [keyId]: publicKeyB64 },  \u002F\u002F published by the firm, bundled at build time\n  fieldSchema: attestation,               \u002F\u002F the same three-field schema\n}\n\n\u002F\u002F A human reads \"1,234.50\" off the printed invoice. The firm stored the\n\u002F\u002F number 1234.5 — the 'cents' normalizer canonicalizes both to the same\n\u002F\u002F integer of cents, so the commitment matches with no float math.\nconst printed = { invoiceNo: 'INV-1042', total: '1234.50', issueDate: '2026-05-29' }\n\nconst verdict = await verifyDocument(qr, printed, config)\n",[941,67399,67400,67410,67433,67447,67451,67455,67460,67465,67470,67518,67522],{"__ignoreMap":1037},[2225,67401,67402,67404,67406,67408],{"class":2227,"line":2228},[2225,67403,2775],{"class":2774},[2225,67405,44810],{"class":2239},[2225,67407,2781],{"class":2235},[2225,67409,2301],{"class":2235},[2225,67411,67412,67414,67416,67418,67420,67422,67424,67426,67428,67430],{"class":2227,"line":1041},[2225,67413,66335],{"class":2307},[2225,67415,2311],{"class":2235},[2225,67417,2236],{"class":2235},[2225,67419,8667],{"class":2307},[2225,67421,66012],{"class":2239},[2225,67423,8803],{"class":2307},[2225,67425,2311],{"class":2235},[2225,67427,65966],{"class":2239},[2225,67429,33127],{"class":2235},[2225,67431,67432],{"class":2763},"  \u002F\u002F published by the firm, bundled at build time\n",[2225,67434,67435,67437,67439,67442,67444],{"class":2227,"line":1038},[2225,67436,66357],{"class":2307},[2225,67438,2311],{"class":2235},[2225,67440,67441],{"class":2239}," attestation",[2225,67443,2298],{"class":2235},[2225,67445,67446],{"class":2763},"               \u002F\u002F the same three-field schema\n",[2225,67448,67449],{"class":2227,"line":2288},[2225,67450,7351],{"class":2235},[2225,67452,67453],{"class":2227,"line":2304},[2225,67454,2285],{"emptyLinePlaceholder":2284},[2225,67456,67457],{"class":2227,"line":2356},[2225,67458,67459],{"class":2763},"\u002F\u002F A human reads \"1,234.50\" off the printed invoice. The firm stored the\n",[2225,67461,67462],{"class":2227,"line":2830},[2225,67463,67464],{"class":2763},"\u002F\u002F number 1234.5 — the 'cents' normalizer canonicalizes both to the same\n",[2225,67466,67467],{"class":2227,"line":2861},[2225,67468,67469],{"class":2763},"\u002F\u002F integer of cents, so the commitment matches with no float math.\n",[2225,67471,67472,67474,67477,67479,67481,67483,67485,67487,67489,67491,67493,67495,67497,67499,67502,67504,67506,67508,67510,67512,67514,67516],{"class":2227,"line":4246},[2225,67473,2775],{"class":2774},[2225,67475,67476],{"class":2239}," printed ",[2225,67478,2781],{"class":2235},[2225,67480,2236],{"class":2235},[2225,67482,27739],{"class":2307},[2225,67484,2311],{"class":2235},[2225,67486,2249],{"class":2235},[2225,67488,65852],{"class":2252},[2225,67490,2805],{"class":2235},[2225,67492,2298],{"class":2235},[2225,67494,27452],{"class":2307},[2225,67496,2311],{"class":2235},[2225,67498,2249],{"class":2235},[2225,67500,67501],{"class":2252},"1234.50",[2225,67503,2805],{"class":2235},[2225,67505,2298],{"class":2235},[2225,67507,65518],{"class":2307},[2225,67509,2311],{"class":2235},[2225,67511,2249],{"class":2235},[2225,67513,65874],{"class":2252},[2225,67515,2805],{"class":2235},[2225,67517,6369],{"class":2235},[2225,67519,67520],{"class":2227,"line":4253},[2225,67521,2285],{"emptyLinePlaceholder":2284},[2225,67523,67524,67526,67528,67530,67532,67534,67537,67539,67542,67544],{"class":2227,"line":4259},[2225,67525,2775],{"class":2774},[2225,67527,61576],{"class":2239},[2225,67529,2781],{"class":2235},[2225,67531,2784],{"class":2231},[2225,67533,66569],{"class":2291},[2225,67535,67536],{"class":2239},"(qr",[2225,67538,2298],{"class":2235},[2225,67540,67541],{"class":2239}," printed",[2225,67543,2298],{"class":2235},[2225,67545,67546],{"class":2239}," config)\n",[2217,67548,67550],{"className":6604,"code":67549,"language":141,"meta":1037,"style":1037},"{\n  \"outcome\": \"authentic-valid\",\n  \"perField\": [\n    { \"path\": \"invoiceNo\", \"match\": true },\n    { \"path\": \"total\", \"match\": true },\n    { \"path\": \"issueDate\", \"match\": true }\n  ],\n  \"revocationTrusted\": null,\n  \"keyId\": \"bd7e8268071ac41f\",\n  \"docId\": \"01JZK7Y1Q2M4N8R3T5V7X9ZABC\"\n}\n",[941,67551,67552,67556,67574,67586,67618,67650,67682,67687,67700,67718,67734],{"__ignoreMap":1037},[2225,67553,67554],{"class":2227,"line":2228},[2225,67555,4200],{"class":2235},[2225,67557,67558,67560,67562,67564,67566,67568,67570,67572],{"class":2227,"line":1041},[2225,67559,9368],{"class":2235},[2225,67561,67177],{"class":2774},[2225,67563,6620],{"class":2235},[2225,67565,2311],{"class":2235},[2225,67567,6614],{"class":2235},[2225,67569,66406],{"class":2252},[2225,67571,6620],{"class":2235},[2225,67573,1707],{"class":2235},[2225,67575,67576,67578,67580,67582,67584],{"class":2227,"line":1038},[2225,67577,9368],{"class":2235},[2225,67579,67048],{"class":2774},[2225,67581,6620],{"class":2235},[2225,67583,2311],{"class":2235},[2225,67585,32491],{"class":2235},[2225,67587,67588,67590,67592,67594,67596,67598,67600,67602,67604,67606,67608,67610,67612,67614,67616],{"class":2227,"line":2288},[2225,67589,32496],{"class":2235},[2225,67591,6614],{"class":2235},[2225,67593,52937],{"class":6342},[2225,67595,6620],{"class":2235},[2225,67597,2311],{"class":2235},[2225,67599,6614],{"class":2235},[2225,67601,27925],{"class":2252},[2225,67603,6620],{"class":2235},[2225,67605,2298],{"class":2235},[2225,67607,6614],{"class":2235},[2225,67609,7482],{"class":6342},[2225,67611,6620],{"class":2235},[2225,67613,2311],{"class":2235},[2225,67615,2322],{"class":2235},[2225,67617,2353],{"class":2235},[2225,67619,67620,67622,67624,67626,67628,67630,67632,67634,67636,67638,67640,67642,67644,67646,67648],{"class":2227,"line":2304},[2225,67621,32496],{"class":2235},[2225,67623,6614],{"class":2235},[2225,67625,52937],{"class":6342},[2225,67627,6620],{"class":2235},[2225,67629,2311],{"class":2235},[2225,67631,6614],{"class":2235},[2225,67633,64200],{"class":2252},[2225,67635,6620],{"class":2235},[2225,67637,2298],{"class":2235},[2225,67639,6614],{"class":2235},[2225,67641,7482],{"class":6342},[2225,67643,6620],{"class":2235},[2225,67645,2311],{"class":2235},[2225,67647,2322],{"class":2235},[2225,67649,2353],{"class":2235},[2225,67651,67652,67654,67656,67658,67660,67662,67664,67666,67668,67670,67672,67674,67676,67678,67680],{"class":2227,"line":2356},[2225,67653,32496],{"class":2235},[2225,67655,6614],{"class":2235},[2225,67657,52937],{"class":6342},[2225,67659,6620],{"class":2235},[2225,67661,2311],{"class":2235},[2225,67663,6614],{"class":2235},[2225,67665,65629],{"class":2252},[2225,67667,6620],{"class":2235},[2225,67669,2298],{"class":2235},[2225,67671,6614],{"class":2235},[2225,67673,7482],{"class":6342},[2225,67675,6620],{"class":2235},[2225,67677,2311],{"class":2235},[2225,67679,2322],{"class":2235},[2225,67681,6369],{"class":2235},[2225,67683,67684],{"class":2227,"line":2830},[2225,67685,67686],{"class":2235},"  ],\n",[2225,67688,67689,67691,67693,67695,67697],{"class":2227,"line":2861},[2225,67690,9368],{"class":2235},[2225,67692,66976],{"class":2774},[2225,67694,6620],{"class":2235},[2225,67696,2311],{"class":2235},[2225,67698,67699],{"class":2235}," null,\n",[2225,67701,67702,67704,67706,67708,67710,67712,67714,67716],{"class":2227,"line":4246},[2225,67703,9368],{"class":2235},[2225,67705,66012],{"class":2774},[2225,67707,6620],{"class":2235},[2225,67709,2311],{"class":2235},[2225,67711,6614],{"class":2235},[2225,67713,66154],{"class":2252},[2225,67715,6620],{"class":2235},[2225,67717,1707],{"class":2235},[2225,67719,67720,67722,67724,67726,67728,67730,67732],{"class":2227,"line":4253},[2225,67721,9368],{"class":2235},[2225,67723,66086],{"class":2774},[2225,67725,6620],{"class":2235},[2225,67727,2311],{"class":2235},[2225,67729,6614],{"class":2235},[2225,67731,66095],{"class":2252},[2225,67733,9449],{"class":2235},[2225,67735,67736],{"class":2227,"line":4259},[2225,67737,7351],{"class":2235},[937,67739,67740,67741,67743],{},"Tamper with a single value and the verdict flips to ",[941,67742,66428],{},", naming the\nfield:",[2217,67745,67747],{"className":2219,"code":67746,"language":2221,"meta":1037,"style":1037},"const tampered = await verifyDocument(qr, { ...printed, total: '9999.00' }, config)\n\u002F\u002F → tampered.outcome                                          === 'altered'\n\u002F\u002F → tampered.perField.find((f) => f.path === 'total')?.match  === false\n\u002F\u002F → tampered.perField.find((f) => f.path === 'invoiceNo')?.match === true\n",[941,67748,67749,67791,67796,67801],{"__ignoreMap":1037},[2225,67750,67751,67753,67756,67758,67760,67762,67764,67766,67768,67771,67774,67776,67778,67780,67782,67785,67787,67789],{"class":2227,"line":2228},[2225,67752,2775],{"class":2774},[2225,67754,67755],{"class":2239}," tampered ",[2225,67757,2781],{"class":2235},[2225,67759,2784],{"class":2231},[2225,67761,66569],{"class":2291},[2225,67763,67536],{"class":2239},[2225,67765,2298],{"class":2235},[2225,67767,2236],{"class":2235},[2225,67769,67770],{"class":2235}," ...",[2225,67772,67773],{"class":2239},"printed",[2225,67775,2298],{"class":2235},[2225,67777,27452],{"class":2307},[2225,67779,2311],{"class":2235},[2225,67781,2249],{"class":2235},[2225,67783,67784],{"class":2252},"9999.00",[2225,67786,2805],{"class":2235},[2225,67788,10965],{"class":2235},[2225,67790,67546],{"class":2239},[2225,67792,67793],{"class":2227,"line":1041},[2225,67794,67795],{"class":2763},"\u002F\u002F → tampered.outcome                                          === 'altered'\n",[2225,67797,67798],{"class":2227,"line":1038},[2225,67799,67800],{"class":2763},"\u002F\u002F → tampered.perField.find((f) => f.path === 'total')?.match  === false\n",[2225,67802,67803],{"class":2227,"line":2288},[2225,67804,67805],{"class":2763},"\u002F\u002F → tampered.perField.find((f) => f.path === 'invoiceNo')?.match === true\n",[1022,67807,67809],{"id":67808},"revocation-is-it-still-valid-today","Revocation — \"is it still valid today?\"",[937,67811,67812,67813,67816],{},"A signature never expires, so the firm answers \"still valid?\" with a signed\n",[950,67814,67815],{},"revocation list",". Firm side:",[2217,67818,67820],{"className":2219,"code":67819,"language":2221,"meta":1037,"style":1037},"await vault.revokeAttestation(docId)          \u002F\u002F vault.unrevokeAttestation(docId) undoes it\nconst list = await vault.publishRevocationList()\n",[941,67821,67822,67839],{"__ignoreMap":1037},[2225,67823,67824,67826,67828,67830,67833,67836],{"class":2227,"line":2228},[2225,67825,6500],{"class":2231},[2225,67827,6466],{"class":2239},[2225,67829,1520],{"class":2235},[2225,67831,67832],{"class":2291},"revokeAttestation",[2225,67834,67835],{"class":2239},"(docId)          ",[2225,67837,67838],{"class":2763},"\u002F\u002F vault.unrevokeAttestation(docId) undoes it\n",[2225,67840,67841,67843,67846,67848,67850,67852,67854,67857],{"class":2227,"line":1041},[2225,67842,2775],{"class":2774},[2225,67844,67845],{"class":2239}," list ",[2225,67847,2781],{"class":2235},[2225,67849,2784],{"class":2231},[2225,67851,6466],{"class":2239},[2225,67853,1520],{"class":2235},[2225,67855,67856],{"class":2291},"publishRevocationList",[2225,67858,8146],{"class":2239},[2217,67860,67862],{"className":6604,"code":67861,"language":141,"meta":1037,"style":1037},"{\n  \"v\": 1,\n  \"revokedDocIds\": [\"01JZK7Y1Q2M4N8R3T5V7X9ZABC\"],\n  \"asOf\": \"2026-05-29T00:00:00.000Z\",\n  \"keyId\": \"bd7e8268071ac41f\",\n  \"sig\": \"JzGDN88XzGYB6Im0NC-XLUVAUEjQTHCqqyOhuoT72sFU…\"\n}\n",[941,67863,67864,67868,67882,67903,67923,67941,67958],{"__ignoreMap":1037},[2225,67865,67866],{"class":2227,"line":2228},[2225,67867,4200],{"class":2235},[2225,67869,67870,67872,67874,67876,67878,67880],{"class":2227,"line":1041},[2225,67871,9368],{"class":2235},[2225,67873,8160],{"class":2774},[2225,67875,6620],{"class":2235},[2225,67877,2311],{"class":2235},[2225,67879,9377],{"class":6527},[2225,67881,1707],{"class":2235},[2225,67883,67884,67886,67889,67891,67893,67895,67897,67899,67901],{"class":2227,"line":1038},[2225,67885,9368],{"class":2235},[2225,67887,67888],{"class":2774},"revokedDocIds",[2225,67890,6620],{"class":2235},[2225,67892,2311],{"class":2235},[2225,67894,8667],{"class":2235},[2225,67896,6620],{"class":2235},[2225,67898,66095],{"class":2252},[2225,67900,6620],{"class":2235},[2225,67902,66199],{"class":2235},[2225,67904,67905,67907,67910,67912,67914,67916,67919,67921],{"class":2227,"line":2288},[2225,67906,9368],{"class":2235},[2225,67908,67909],{"class":2774},"asOf",[2225,67911,6620],{"class":2235},[2225,67913,2311],{"class":2235},[2225,67915,6614],{"class":2235},[2225,67917,67918],{"class":2252},"2026-05-29T00:00:00.000Z",[2225,67920,6620],{"class":2235},[2225,67922,1707],{"class":2235},[2225,67924,67925,67927,67929,67931,67933,67935,67937,67939],{"class":2227,"line":2304},[2225,67926,9368],{"class":2235},[2225,67928,66012],{"class":2774},[2225,67930,6620],{"class":2235},[2225,67932,2311],{"class":2235},[2225,67934,6614],{"class":2235},[2225,67936,66154],{"class":2252},[2225,67938,6620],{"class":2235},[2225,67940,1707],{"class":2235},[2225,67942,67943,67945,67947,67949,67951,67953,67956],{"class":2227,"line":2356},[2225,67944,9368],{"class":2235},[2225,67946,66206],{"class":2774},[2225,67948,6620],{"class":2235},[2225,67950,2311],{"class":2235},[2225,67952,6614],{"class":2235},[2225,67954,67955],{"class":2252},"JzGDN88XzGYB6Im0NC-XLUVAUEjQTHCqqyOhuoT72sFU…",[2225,67957,9449],{"class":2235},[2225,67959,67960],{"class":2227,"line":2830},[2225,67961,7351],{"class":2235},[937,67963,67964,67965,67967],{},"The list is signed with the same document-signing key, so the verifier can\ntrust a copy from anywhere — bundle it at build time or fetch it when online.\nPass it as ",[941,67966,66869],{}," in the config; a revoked document then reads:",[2217,67969,67971],{"className":2219,"code":67970,"language":2221,"meta":1037,"style":1037},"const v = await verifyDocument(qr, printed, { ...config, revocationList: list })\n\u002F\u002F → v.outcome === 'authentic-revoked'   (issued by the firm, since withdrawn)\n",[941,67972,67973,68013],{"__ignoreMap":1037},[2225,67974,67975,67977,67980,67982,67984,67986,67988,67990,67992,67994,67996,67998,68000,68002,68005,68007,68009,68011],{"class":2227,"line":2228},[2225,67976,2775],{"class":2774},[2225,67978,67979],{"class":2239}," v ",[2225,67981,2781],{"class":2235},[2225,67983,2784],{"class":2231},[2225,67985,66569],{"class":2291},[2225,67987,67536],{"class":2239},[2225,67989,2298],{"class":2235},[2225,67991,67541],{"class":2239},[2225,67993,2298],{"class":2235},[2225,67995,2236],{"class":2235},[2225,67997,67770],{"class":2235},[2225,67999,66864],{"class":2239},[2225,68001,2298],{"class":2235},[2225,68003,68004],{"class":2307}," revocationList",[2225,68006,2311],{"class":2235},[2225,68008,67845],{"class":2239},[2225,68010,2359],{"class":2235},[2225,68012,2362],{"class":2239},[2225,68014,68015],{"class":2227,"line":1041},[2225,68016,68017],{"class":2763},"\u002F\u002F → v.outcome === 'authentic-revoked'   (issued by the firm, since withdrawn)\n",[937,68019,68020,68021,7356,68023,68025,68026,68028,68029,68032],{},"If the list fails its own signature check, ",[941,68022,66976],{},[941,68024,2560],{},",\nthe list is ignored, and an otherwise-authentic document stays\n",[941,68027,66406],{}," — a UI should downgrade the wording (\"revocation status\ncould not be confirmed\") but a bad list can ",[950,68030,68031],{},"never flip a real authenticity\npass into a failure",", and never hide a signature or field mismatch.",[1022,68034,65437],{"id":68035},"field-normalizers",[937,68037,68038],{},"Both sides normalize a value to a canonical string before hashing, with the\nsame function — so a value presented identically on both sides always\nround-trips. The seven built-in normalizers:",[1338,68040,68041,68054],{},[1341,68042,68043],{},[1344,68044,68045,68048,68051],{},[1347,68046,68047],{},"Normalizer",[1347,68049,68050],{},"Canonical form",[1347,68052,68053],{},"Example",[1357,68055,68056,68074,68091,68107,68124,68142,68161],{},[1344,68057,68058,68063,68066],{},[1362,68059,68060],{},[941,68061,68062],{},"trim",[1362,68064,68065],{},"trimmed string",[1362,68067,68068,1746,68071],{},[941,68069,68070],{},"'  ACME Co '",[941,68072,68073],{},"'ACME Co'",[1344,68075,68076,68080,68083],{},[1362,68077,68078],{},[941,68079,11226],{},[1362,68081,68082],{},"trimmed, lowercase",[1362,68084,68085,1746,68088],{},[941,68086,68087],{},"'ACME'",[941,68089,68090],{},"'acme'",[1344,68092,68093,68098,68101],{},[1362,68094,68095],{},[941,68096,68097],{},"upper",[1362,68099,68100],{},"trimmed, uppercase",[1362,68102,68103,1746,68105],{},[941,68104,68090],{},[941,68106,68087],{},[1344,68108,68109,68113,68116],{},[1362,68110,68111],{},[941,68112,65573],{},[1362,68114,68115],{},"alphanumerics only, uppercase",[1362,68117,68118,1746,68121],{},[941,68119,68120],{},"'inv-1042'",[941,68122,68123],{},"'INV1042'",[1344,68125,68126,68131,68134],{},[1362,68127,68128],{},[941,68129,68130],{},"digits",[1362,68132,68133],{},"digits only",[1362,68135,68136,1746,68139],{},[941,68137,68138],{},"'+66 2-123-4567'",[941,68140,68141],{},"'6621234567'",[1344,68143,68144,68148,68151],{},[1362,68145,68146],{},[941,68147,65607],{},[1362,68149,68150],{},"integer of cents",[1362,68152,68153,997,68155,1746,68158],{},[941,68154,65432],{},[941,68156,68157],{},"'฿1,234.50'",[941,68159,68160],{},"'123450'",[1344,68162,68163,68167,68170],{},[1362,68164,68165],{},[941,68166,65642],{},[1362,68168,68169],{},"UTC calendar date",[1362,68171,68172,1746,68175],{},[941,68173,68174],{},"'2026-05-29'",[941,68176,68174],{},[1022,68178,68180],{"id":68179},"trust-model","Trust model",[1111,68182,68183,68195,68201],{},[1114,68184,68185,68188,68189,68191,68192,68194],{},[950,68186,68187],{},"The trust anchor is bundled, not fetched."," The verifier ships the firm's\npublic key(s) and the field schema inside its build — there is nothing to\ntrust at verification time. ",[941,68190,66733],{}," is a map keyed by ",[941,68193,66012],{}," and each\nQR names the key that signed it, so one verifier build can bundle several\nfirm keys (or several firms) and route each document to the right one.",[1114,68196,68197,68200],{},[950,68198,68199],{},"The QR is not a data leak."," It carries salted hashes and a signature —\nno field values. Verification requires the paper (or a copy of the values).",[1114,68202,68203,68206],{},[950,68204,68205],{},"Verification proves issuance, not truth."," A green verdict means \"the\nfirm signed exactly these values for this document\" — nothing more.",[1022,68208,68210],{"id":68209},"limits-gotchas","Limits & gotchas",[1111,68212,68213,68236,68245,68258],{},[1114,68214,68215,68221,68222,68224,68225,68227,68228,68231,68232,68235],{},[950,68216,68217,68220],{},[941,68218,68219],{},"verifyDocument"," can still throw on bad claimed values",": typing a\nnon-numeric string into a ",[941,68223,65607],{}," field or an unparseable date into an\n",[941,68226,65642],{}," field throws from the normalizer, and omitting a declared field\nfrom ",[941,68229,68230],{},"claimedFields"," entirely throws before hashing. Wrap the call in\n",[941,68233,68234],{},"try\u002Fcatch"," in a UI and render a \"could not verify\" state.",[1114,68237,68238,68241,68242,68244],{},[950,68239,68240],{},"The verifier's schema must match the issuing schema exactly"," (same\nfields, same order). A field-count mismatch is reported as ",[941,68243,66428],{}," with\nevery field marked non-matching — not as a distinct verdict.",[1114,68246,68247,6793,68252,68254,68255,68257],{},[950,68248,68249,68250,29977],{},"Prefer ISO date strings over ",[941,68251,26712],{},[941,68253,65642],{}," fields: the\nnormalizer takes the UTC calendar date, so a local-time ",[941,68256,26712],{}," near\nmidnight can shift by a day in non-UTC environments.",[1114,68259,68260,2085,68263,68265,68266,68268,68269,68271,68272,1520],{},[950,68261,68262],{},"Revocation is per attestation, not per record",[941,68264,67832],{},"\ntakes the ",[941,68267,66086],{}," returned by ",[941,68270,65920],{},". Re-issuing the same\nrecord mints a new ",[941,68273,66086],{},[1022,68275,2621],{"id":2620},[1111,68277,68278,68287,68292],{},[1114,68279,68280,68282,68283,968,68285,957],{},[1007,68281,35860],{"href":610}," — issue-side reference\n(",[941,68284,65350],{},[941,68286,21543],{},[1114,68288,68289,68291],{},[1007,68290,66039],{"href":520}," — the\ngeneration side: render the QR into a server-generated PDF.",[1114,68293,68294,68296],{},[1007,68295,65376],{"href":48}," — what a vault is\nand how collections are declared.",[2645,68298,68299],{},"html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sHdIc, html code.shiki .sHdIc{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#EEFFFF;--shiki-default-font-style:italic;--shiki-dark:#BABED8;--shiki-dark-font-style:italic}html pre.shiki code .sfNiH, html code.shiki .sfNiH{--shiki-light:#FF5370;--shiki-default:#FF9CAC;--shiki-dark:#FF9CAC}",{"title":1037,"searchDepth":1038,"depth":1038,"links":68301},[68302,68303,68304,68305,68306,68307,68308,68309,68310],{"id":65418,"depth":1041,"text":65419},{"id":66046,"depth":1041,"text":66047},{"id":66230,"depth":1041,"text":66231},{"id":67390,"depth":1041,"text":67391},{"id":67808,"depth":1041,"text":67809},{"id":68035,"depth":1041,"text":65437},{"id":68179,"depth":1041,"text":68180},{"id":68209,"depth":1041,"text":68210},{"id":2620,"depth":1041,"text":2621},"Issue a signed per-field commitment as a QR code, then verify it fully offline against the firm's published Ed25519 key — six verdicts, revocation included.",{},{"title":515,"description":68311},"teKwiONI3bODddZKGsUaQFhuXsNlxDiZhlcWpLLUz0w",{"id":68316,"title":519,"access":12,"apiToc":929,"body":68317,"category":1042,"description":70009,"extension":1044,"meta":70010,"navigation":1042,"nest":1042,"path":520,"seo":70011,"sinceVersion":1042,"status":1049,"stem":521,"subtitle":1042,"tier":1050,"tocExternal":1042,"__hash__":70012},"docs\u002Frecipes\u002Faws-kms-pdf-attestation.md",{"type":931,"value":68318,"toc":69997},[68319,68322,68341,68351,68373,68384,68403,68407,68461,68465,68510,68514,68527,68988,69076,69095,69099,69108,69313,69334,69338,69341,69385,69473,69499,69503,69506,69650,69667,69671,69682,69847,69887,69909,69913,69945,69949,69965,69972,69974,69994],[934,68320,519],{"id":68321},"aws-kms-attestation",[1061,68323,68324],{},[937,68325,68326,68328,68329,58223,68331,953,68333,58355,68335,953,68337,68340],{},[950,68327,1500],{}," teams that issue PDF documents (invoices, certificates, statements) and want to render the attestation QR server-side, sealed under a key held in AWS KMS.\n",[950,68330,58222],{},[941,68332,21543],{},[941,68334,65350],{},[941,68336,33294],{},[941,68338,68339],{},"atAwsKms",") — the render Lambda itself is hub-free.",[937,68342,58245,68343,68350],{},[1007,68344,68347],{"href":68345,"rel":68346},"https:\u002F\u002Fgithub.com\u002Fnoy-db\u002Fdocs\u002Fblob\u002Fmain\u002Fshowcases\u002Fsrc\u002Frecipe-aws-kms-pdf-attestation.recipe.test.ts",[3371],[941,68348,68349],{},"showcases\u002Fsrc\u002Frecipe-aws-kms-pdf-attestation.recipe.test.ts"," — the CI-safe generation-side slice of this recipe.",[937,68352,68353,68354,68357,68358,68360,68361,68364,68365,68368,68369,68372],{},"You issue documents — invoices, certificates, statements — and want anyone\nholding a printed or forwarded copy to check, ",[950,68355,68356],{},"fully offline",", that it is\nauthentic and unaltered. noy-db's ",[1007,68359,35860],{"href":610},"\ncovers the trust part: the issuing firm signs a per-field commitment over the\ndocument's declared fields and encodes it as a QR credential. This recipe is\nthe ",[950,68362,68363],{},"generation half",": a small, deployable AWS Lambda that takes a\nKMS-encrypted document record from S3, decrypts it, and renders an HTML\ninvoice to PDF with the attestation ",[950,68366,68367],{},"QR embedded as vector SVG"," — so the QR\nsurvives print and rescan. It pairs with the\n",[1007,68370,68371],{"href":516},"offline verifier recipe",", which checks the QR\nwith no server involved.",[937,68374,68375,68376,68378,68379,68383],{},"Along the way it exercises ",[950,68377,1135],{}," — encrypting a payload under a key held\nby infrastructure you control, here AWS KMS via\n",[1007,68380,68381],{"href":170},[941,68382,33294],{},". The Lambda itself never opens a noy-db\nvault: it only decrypts one small render payload per request.",[7646,68385,68386],{"color":7648,"icon":41132},[937,68387,68388,68389,68395,68396,968,68398,968,68400,68402],{},"Recipes are reference source, not npm packages. The full working code —\nincluding the CDK deploy stack — lives at\n",[1007,68390,68393],{"href":68391,"rel":68392},"https:\u002F\u002Fgithub.com\u002Fnoy-db\u002Fdocs\u002Ftree\u002Fmain\u002Frecipes\u002Faws-kms-pdf-attestation",[3371],[941,68394,521],{},";\ncopy what you need into your project. The published packages it builds on\n(",[941,68397,1086],{},[941,68399,33294],{},[941,68401,35864],{},") are on npm.",[1022,68404,68406],{"id":68405},"what-it-shows","What it shows",[1111,68408,68409,68419,68433,68453],{},[1114,68410,68411,1324,68414,68416,68417,1520],{},[950,68412,68413],{},"Issue (hub):",[941,68415,21548],{}," — the firm mints the signed QR\ncredential for a record. Opt-in via ",[941,68418,21543],{},[1114,68420,68421,1324,68424,17904,68426,68428,68429,68432],{},[950,68422,68423],{},"Seal (at-aws-kms):",[941,68425,68339],{},[941,68427,33294],{},"\nKMS-encrypts the render payload ",[941,68430,68431],{},"{ docId, fields, qr }"," before it touches S3.",[1114,68434,68435,68438,68439,68442,68443,68445,68446,6030,68449,68452],{},[950,68436,68437],{},"Render (hub-free Lambda):"," S3 ",[941,68440,68441],{},"GetObject"," → KMS ",[941,68444,33312],{}," → HTML with an\ninline-SVG QR → headless Chromium (",[941,68447,68448],{},"puppeteer-core",[941,68450,68451],{},"@sparticuz\u002Fchromium",")\n→ PDF.",[1114,68454,68455,68458,68459,1520],{},[950,68456,68457],{},"Verify (third party):"," the embedded QR still verifies offline via the\nverifier recipe's ",[941,68460,66250],{},[1022,68462,68464],{"id":68463},"data-flow","Data flow",[1234,68466,68467,68479,68491,68500],{},[1114,68468,68469,1324,68472,68475,68476,1520],{},[950,68470,68471],{},"Issue (firm, hub):",[941,68473,68474],{},"vault.issueAttestation('invoices', id)"," →\n",[941,68477,68478],{},"{ docId, qr, keyId, publicKeyB64 }",[1114,68480,68481,1324,68484,68487,68488,1520],{},[950,68482,68483],{},"Seal (firm):",[941,68485,68486],{},"sealAndUpload({ docId, fields, qr })"," — KMS Encrypt →\nS3 ",[941,68489,68490],{},"docs\u002F\u003CdocId>",[1114,68492,68493,1324,68496,68499],{},[950,68494,68495],{},"Render (Lambda):",[941,68497,68498],{},"GET \u003Cfunction-url>?d=\u003CdocId>&exp=…&sig=…"," → verify\nthe signed link → S3 GetObject → KMS Decrypt → PDF with the vector QR.",[1114,68501,68502,68505,68506,1746,68508,1520],{},[950,68503,68504],{},"Verify (third party, offline):"," scan the QR on the PDF →\n",[941,68507,66250],{},[941,68509,66406],{},[1022,68511,68513],{"id":68512},"step-1-issue-the-attestation-firm-side-hub","Step 1 — Issue the attestation (firm side, hub)",[937,68515,68516,68517,68520,68521,997,68523,68526],{},"The firm declares which fields the QR commits to (an ",[1092,68518,68519],{},"attestation\nfield-schema","), writes the record, and mints the credential. Normalizers make\nthe stored value and what a human later reads off the paper compare equal\n(",[941,68522,65432],{},[941,68524,68525],{},"\"1234.50\""," both normalize to the same cents value).",[2217,68528,68530],{"className":2219,"code":68529,"language":2221,"meta":1037,"style":1037},"import { createNoydb } from '@noy-db\u002Fhub'\nimport { withAttestation } from '@noy-db\u002Fhub\u002Fattestation'\nimport { toMemory } from '@noy-db\u002Fto-memory'\nimport type { AttestationFieldSchema } from '@noy-db\u002Fattestation'\n\nconst attestation: AttestationFieldSchema = {\n  fields: [\n    { path: 'invoiceNo', normalize: 'alnum-upper' },\n    { path: 'total',     normalize: 'cents' },\n    { path: 'issueDate', normalize: 'iso-date' },\n  ],\n}\n\nconst db = await createNoydb({\n  store: toMemory(),\n  user: 'firm',\n  secret: 'firm-pass-2026',\n  attestationStrategy: withAttestation(), \u002F\u002F attestation is opt-in\n})\nconst vault = await db.openVault('books')\n\nawait vault\n  .collection('invoices', { attestation })\n  .put('inv-1', { id: 'inv-1', invoiceNo: 'INV-1042', total: 1234.5, issueDate: '2026-05-29' })\n\nconst { docId, qr, keyId, publicKeyB64 } = await vault.issueAttestation('invoices', 'inv-1')\n",[941,68531,68532,68550,68568,68587,68607,68611,68625,68633,68661,68689,68717,68723,68727,68731,68747,68759,68773,68788,68803,68809,68835,68839,68846,68870,68934,68938],{"__ignoreMap":1037},[2225,68533,68534,68536,68538,68540,68542,68544,68546,68548],{"class":2227,"line":2228},[2225,68535,2232],{"class":2231},[2225,68537,2236],{"class":2235},[2225,68539,2744],{"class":2239},[2225,68541,2243],{"class":2235},[2225,68543,2246],{"class":2231},[2225,68545,2249],{"class":2235},[2225,68547,1086],{"class":2252},[2225,68549,2256],{"class":2235},[2225,68551,68552,68554,68556,68558,68560,68562,68564,68566],{"class":2227,"line":1041},[2225,68553,2232],{"class":2231},[2225,68555,2236],{"class":2235},[2225,68557,65469],{"class":2239},[2225,68559,2243],{"class":2235},[2225,68561,2246],{"class":2231},[2225,68563,2249],{"class":2235},[2225,68565,65350],{"class":2252},[2225,68567,2256],{"class":2235},[2225,68569,68570,68572,68574,68577,68579,68581,68583,68585],{"class":2227,"line":1038},[2225,68571,2232],{"class":2231},[2225,68573,2236],{"class":2235},[2225,68575,68576],{"class":2239}," toMemory",[2225,68578,2243],{"class":2235},[2225,68580,2246],{"class":2231},[2225,68582,2249],{"class":2235},[2225,68584,9638],{"class":2252},[2225,68586,2256],{"class":2235},[2225,68588,68589,68591,68593,68595,68597,68599,68601,68603,68605],{"class":2227,"line":2288},[2225,68590,2232],{"class":2231},[2225,68592,3399],{"class":2231},[2225,68594,2236],{"class":2235},[2225,68596,66287],{"class":2239},[2225,68598,2243],{"class":2235},[2225,68600,2246],{"class":2231},[2225,68602,2249],{"class":2235},[2225,68604,35864],{"class":2252},[2225,68606,2256],{"class":2235},[2225,68608,68609],{"class":2227,"line":2304},[2225,68610,2285],{"emptyLinePlaceholder":2284},[2225,68612,68613,68615,68617,68619,68621,68623],{"class":2227,"line":2356},[2225,68614,2775],{"class":2774},[2225,68616,67441],{"class":2239},[2225,68618,2311],{"class":2235},[2225,68620,66287],{"class":6342},[2225,68622,6346],{"class":2235},[2225,68624,2301],{"class":2235},[2225,68626,68627,68629,68631],{"class":2227,"line":2830},[2225,68628,57522],{"class":2307},[2225,68630,2311],{"class":2235},[2225,68632,32491],{"class":2239},[2225,68634,68635,68637,68639,68641,68643,68645,68647,68649,68651,68653,68655,68657,68659],{"class":2227,"line":2861},[2225,68636,32496],{"class":2235},[2225,68638,53037],{"class":2307},[2225,68640,2311],{"class":2235},[2225,68642,2249],{"class":2235},[2225,68644,27925],{"class":2252},[2225,68646,2805],{"class":2235},[2225,68648,2298],{"class":2235},[2225,68650,65566],{"class":2307},[2225,68652,2311],{"class":2235},[2225,68654,2249],{"class":2235},[2225,68656,65573],{"class":2252},[2225,68658,2805],{"class":2235},[2225,68660,2353],{"class":2235},[2225,68662,68663,68665,68667,68669,68671,68673,68675,68677,68679,68681,68683,68685,68687],{"class":2227,"line":4246},[2225,68664,32496],{"class":2235},[2225,68666,53037],{"class":2307},[2225,68668,2311],{"class":2235},[2225,68670,2249],{"class":2235},[2225,68672,64200],{"class":2252},[2225,68674,2805],{"class":2235},[2225,68676,2298],{"class":2235},[2225,68678,65600],{"class":2307},[2225,68680,2311],{"class":2235},[2225,68682,2249],{"class":2235},[2225,68684,65607],{"class":2252},[2225,68686,2805],{"class":2235},[2225,68688,2353],{"class":2235},[2225,68690,68691,68693,68695,68697,68699,68701,68703,68705,68707,68709,68711,68713,68715],{"class":2227,"line":4253},[2225,68692,32496],{"class":2235},[2225,68694,53037],{"class":2307},[2225,68696,2311],{"class":2235},[2225,68698,2249],{"class":2235},[2225,68700,65629],{"class":2252},[2225,68702,2805],{"class":2235},[2225,68704,2298],{"class":2235},[2225,68706,65566],{"class":2307},[2225,68708,2311],{"class":2235},[2225,68710,2249],{"class":2235},[2225,68712,65642],{"class":2252},[2225,68714,2805],{"class":2235},[2225,68716,2353],{"class":2235},[2225,68718,68719,68721],{"class":2227,"line":4259},[2225,68720,32588],{"class":2239},[2225,68722,1707],{"class":2235},[2225,68724,68725],{"class":2227,"line":6548},[2225,68726,7351],{"class":2235},[2225,68728,68729],{"class":2227,"line":6576},[2225,68730,2285],{"emptyLinePlaceholder":2284},[2225,68732,68733,68735,68737,68739,68741,68743,68745],{"class":2227,"line":6582},[2225,68734,2775],{"class":2774},[2225,68736,2778],{"class":2239},[2225,68738,2781],{"class":2235},[2225,68740,2784],{"class":2231},[2225,68742,2744],{"class":2291},[2225,68744,2789],{"class":2239},[2225,68746,4200],{"class":2235},[2225,68748,68749,68751,68753,68755,68757],{"class":2227,"line":6587},[2225,68750,15608],{"class":2307},[2225,68752,2311],{"class":2235},[2225,68754,68576],{"class":2291},[2225,68756,4228],{"class":2239},[2225,68758,1707],{"class":2235},[2225,68760,68761,68763,68765,68767,68769,68771],{"class":2227,"line":7174},[2225,68762,4205],{"class":2307},[2225,68764,2311],{"class":2235},[2225,68766,2249],{"class":2235},[2225,68768,39709],{"class":2252},[2225,68770,2805],{"class":2235},[2225,68772,1707],{"class":2235},[2225,68774,68775,68777,68779,68781,68784,68786],{"class":2227,"line":7180},[2225,68776,10423],{"class":2307},[2225,68778,2311],{"class":2235},[2225,68780,2249],{"class":2235},[2225,68782,68783],{"class":2252},"firm-pass-2026",[2225,68785,2805],{"class":2235},[2225,68787,1707],{"class":2235},[2225,68789,68790,68792,68794,68796,68798,68800],{"class":2227,"line":7220},[2225,68791,65721],{"class":2307},[2225,68793,2311],{"class":2235},[2225,68795,65469],{"class":2291},[2225,68797,4228],{"class":2239},[2225,68799,2298],{"class":2235},[2225,68801,68802],{"class":2763}," \u002F\u002F attestation is opt-in\n",[2225,68804,68805,68807],{"class":2227,"line":7225},[2225,68806,2359],{"class":2235},[2225,68808,2362],{"class":2239},[2225,68810,68811,68813,68815,68817,68819,68821,68823,68825,68827,68829,68831,68833],{"class":2227,"line":7231},[2225,68812,2775],{"class":2774},[2225,68814,2835],{"class":2239},[2225,68816,2781],{"class":2235},[2225,68818,2784],{"class":2231},[2225,68820,2842],{"class":2239},[2225,68822,1520],{"class":2235},[2225,68824,2847],{"class":2291},[2225,68826,2789],{"class":2239},[2225,68828,2805],{"class":2235},[2225,68830,65761],{"class":2252},[2225,68832,2805],{"class":2235},[2225,68834,2362],{"class":2239},[2225,68836,68837],{"class":2227,"line":7267},[2225,68838,2285],{"emptyLinePlaceholder":2284},[2225,68840,68841,68843],{"class":2227,"line":7272},[2225,68842,6500],{"class":2231},[2225,68844,68845],{"class":2239}," vault\n",[2225,68847,68848,68850,68852,68854,68856,68858,68860,68862,68864,68866,68868],{"class":2227,"line":7278},[2225,68849,26235],{"class":2235},[2225,68851,6471],{"class":2291},[2225,68853,2789],{"class":2239},[2225,68855,2805],{"class":2235},[2225,68857,6487],{"class":2252},[2225,68859,2805],{"class":2235},[2225,68861,2298],{"class":2235},[2225,68863,2236],{"class":2235},[2225,68865,65535],{"class":2239},[2225,68867,2359],{"class":2235},[2225,68869,2362],{"class":2239},[2225,68871,68872,68874,68876,68878,68880,68882,68884,68886,68888,68890,68892,68894,68896,68898,68900,68902,68904,68906,68908,68910,68912,68914,68916,68918,68920,68922,68924,68926,68928,68930,68932],{"class":2227,"line":7303},[2225,68873,26235],{"class":2235},[2225,68875,3161],{"class":2291},[2225,68877,2789],{"class":2239},[2225,68879,2805],{"class":2235},[2225,68881,6514],{"class":2252},[2225,68883,2805],{"class":2235},[2225,68885,2298],{"class":2235},[2225,68887,2236],{"class":2235},[2225,68889,7070],{"class":2307},[2225,68891,2311],{"class":2235},[2225,68893,2249],{"class":2235},[2225,68895,6514],{"class":2252},[2225,68897,2805],{"class":2235},[2225,68899,2298],{"class":2235},[2225,68901,27739],{"class":2307},[2225,68903,2311],{"class":2235},[2225,68905,2249],{"class":2235},[2225,68907,65852],{"class":2252},[2225,68909,2805],{"class":2235},[2225,68911,2298],{"class":2235},[2225,68913,27452],{"class":2307},[2225,68915,2311],{"class":2235},[2225,68917,65863],{"class":6527},[2225,68919,2298],{"class":2235},[2225,68921,65518],{"class":2307},[2225,68923,2311],{"class":2235},[2225,68925,2249],{"class":2235},[2225,68927,65874],{"class":2252},[2225,68929,2805],{"class":2235},[2225,68931,2243],{"class":2235},[2225,68933,2362],{"class":2239},[2225,68935,68936],{"class":2227,"line":7308},[2225,68937,2285],{"emptyLinePlaceholder":2284},[2225,68939,68940,68942,68944,68946,68948,68950,68952,68954,68956,68958,68960,68962,68964,68966,68968,68970,68972,68974,68976,68978,68980,68982,68984,68986],{"class":2227,"line":7314},[2225,68941,2775],{"class":2774},[2225,68943,2236],{"class":2235},[2225,68945,65897],{"class":2239},[2225,68947,2298],{"class":2235},[2225,68949,65902],{"class":2239},[2225,68951,2298],{"class":2235},[2225,68953,66797],{"class":2239},[2225,68955,2298],{"class":2235},[2225,68957,65966],{"class":2239},[2225,68959,2359],{"class":2235},[2225,68961,6346],{"class":2235},[2225,68963,2784],{"class":2231},[2225,68965,6466],{"class":2239},[2225,68967,1520],{"class":2235},[2225,68969,65920],{"class":2291},[2225,68971,2789],{"class":2239},[2225,68973,2805],{"class":2235},[2225,68975,6487],{"class":2252},[2225,68977,2805],{"class":2235},[2225,68979,2298],{"class":2235},[2225,68981,2249],{"class":2235},[2225,68983,6514],{"class":2252},[2225,68985,2805],{"class":2235},[2225,68987,2362],{"class":2239},[2217,68989,68991],{"className":6604,"code":68990,"language":141,"meta":1037,"style":1037},"{\n  \"docId\": \"01JWK4Y4B7Q6…\",\n  \"qr\": \"eyJ2IjoxLCJkb2NJZCI6IjAxSldLNFk0…\",\n  \"keyId\": \"k1_Zm5vcXAx…\",\n  \"publicKeyB64\": \"hV3nJq0T…\"\n}\n",[941,68992,68993,68997,69016,69035,69054,69072],{"__ignoreMap":1037},[2225,68994,68995],{"class":2227,"line":2228},[2225,68996,4200],{"class":2235},[2225,68998,68999,69001,69003,69005,69007,69009,69012,69014],{"class":2227,"line":1041},[2225,69000,9368],{"class":2235},[2225,69002,66086],{"class":2774},[2225,69004,6620],{"class":2235},[2225,69006,2311],{"class":2235},[2225,69008,6614],{"class":2235},[2225,69010,69011],{"class":2252},"01JWK4Y4B7Q6…",[2225,69013,6620],{"class":2235},[2225,69015,1707],{"class":2235},[2225,69017,69018,69020,69022,69024,69026,69028,69031,69033],{"class":2227,"line":1038},[2225,69019,9368],{"class":2235},[2225,69021,66052],{"class":2774},[2225,69023,6620],{"class":2235},[2225,69025,2311],{"class":2235},[2225,69027,6614],{"class":2235},[2225,69029,69030],{"class":2252},"eyJ2IjoxLCJkb2NJZCI6IjAxSldLNFk0…",[2225,69032,6620],{"class":2235},[2225,69034,1707],{"class":2235},[2225,69036,69037,69039,69041,69043,69045,69047,69050,69052],{"class":2227,"line":2288},[2225,69038,9368],{"class":2235},[2225,69040,66012],{"class":2774},[2225,69042,6620],{"class":2235},[2225,69044,2311],{"class":2235},[2225,69046,6614],{"class":2235},[2225,69048,69049],{"class":2252},"k1_Zm5vcXAx…",[2225,69051,6620],{"class":2235},[2225,69053,1707],{"class":2235},[2225,69055,69056,69058,69061,69063,69065,69067,69070],{"class":2227,"line":2304},[2225,69057,9368],{"class":2235},[2225,69059,69060],{"class":2774},"publicKeyB64",[2225,69062,6620],{"class":2235},[2225,69064,2311],{"class":2235},[2225,69066,6614],{"class":2235},[2225,69068,69069],{"class":2252},"hV3nJq0T…",[2225,69071,9449],{"class":2235},[2225,69073,69074],{"class":2227,"line":2356},[2225,69075,7351],{"class":2235},[937,69077,69078,69080,69081,69084,69085,69087,69088,69090,69091,69094],{},[941,69079,66052],{}," is a base64url-encoded JSON payload\n",[941,69082,69083],{},"{ v, docId, salt, alg: 'ed25519', keyId, fieldHashes, sig }"," — salted\nper-field hashes plus the firm's Ed25519 signature. ",[941,69086,66012],{}," is a fingerprint\nof the signing public key, so the verifier can pick the right key after\nrotation. ",[941,69089,69060],{}," (also available later via\n",[941,69092,69093],{},"vault.getDocumentSigningPublicKey()",") is what you hand to verifiers.",[1022,69096,69098],{"id":69097},"step-2-seal-and-upload-the-render-payload","Step 2 — Seal and upload the render payload",[937,69100,69101,69102,69104,69105,69107],{},"The firm KMS-encrypts (seals) the small render payload and puts the ciphertext\nin S3. This is ",[941,69103,33294],{}," in action — the same\n",[941,69106,68339],{}," used to seal vault secrets at a trusted host:",[2217,69109,69111],{"className":2219,"code":69110,"language":2221,"meta":1037,"style":1037},"import { sealAndUpload } from '@noy-db\u002Frecipe-aws-kms-pdf-attestation\u002Fseal'\nimport type { RenderPayload } from '@noy-db\u002Frecipe-aws-kms-pdf-attestation'\n\nconst payload: RenderPayload = {\n  docId,\n  qr,\n  fields: { invoiceNo: 'INV-1042', total: 1234.5, issueDate: '2026-05-29' },\n}\n\nawait sealAndUpload(payload, {\n  keyId: 'arn:aws:kms:eu-west-1:123456789012:key\u002Fabc',\n  bucket: 'firm-docs',\n  key: `docs\u002F${docId}`,\n})\n\u002F\u002F → S3 object docs\u002F\u003CdocId> now holds the KMS ciphertext; AWS credentials\n\u002F\u002F   come from the SDK's ambient chain (IAM role, env vars, ~\u002F.aws\u002Fcredentials)\n",[941,69112,69113,69133,69155,69159,69173,69179,69185,69225,69229,69233,69246,69261,69277,69297,69303,69308],{"__ignoreMap":1037},[2225,69114,69115,69117,69119,69122,69124,69126,69128,69131],{"class":2227,"line":2228},[2225,69116,2232],{"class":2231},[2225,69118,2236],{"class":2235},[2225,69120,69121],{"class":2239}," sealAndUpload",[2225,69123,2243],{"class":2235},[2225,69125,2246],{"class":2231},[2225,69127,2249],{"class":2235},[2225,69129,69130],{"class":2252},"@noy-db\u002Frecipe-aws-kms-pdf-attestation\u002Fseal",[2225,69132,2256],{"class":2235},[2225,69134,69135,69137,69139,69141,69144,69146,69148,69150,69153],{"class":2227,"line":1041},[2225,69136,2232],{"class":2231},[2225,69138,3399],{"class":2231},[2225,69140,2236],{"class":2235},[2225,69142,69143],{"class":2239}," RenderPayload",[2225,69145,2243],{"class":2235},[2225,69147,2246],{"class":2231},[2225,69149,2249],{"class":2235},[2225,69151,69152],{"class":2252},"@noy-db\u002Frecipe-aws-kms-pdf-attestation",[2225,69154,2256],{"class":2235},[2225,69156,69157],{"class":2227,"line":1038},[2225,69158,2285],{"emptyLinePlaceholder":2284},[2225,69160,69161,69163,69165,69167,69169,69171],{"class":2227,"line":2288},[2225,69162,2775],{"class":2774},[2225,69164,19094],{"class":2239},[2225,69166,2311],{"class":2235},[2225,69168,69143],{"class":6342},[2225,69170,6346],{"class":2235},[2225,69172,2301],{"class":2235},[2225,69174,69175,69177],{"class":2227,"line":2304},[2225,69176,66546],{"class":2239},[2225,69178,1707],{"class":2235},[2225,69180,69181,69183],{"class":2227,"line":2356},[2225,69182,66576],{"class":2239},[2225,69184,1707],{"class":2235},[2225,69186,69187,69189,69191,69193,69195,69197,69199,69201,69203,69205,69207,69209,69211,69213,69215,69217,69219,69221,69223],{"class":2227,"line":2830},[2225,69188,57522],{"class":2307},[2225,69190,2311],{"class":2235},[2225,69192,2236],{"class":2235},[2225,69194,27739],{"class":2307},[2225,69196,2311],{"class":2235},[2225,69198,2249],{"class":2235},[2225,69200,65852],{"class":2252},[2225,69202,2805],{"class":2235},[2225,69204,2298],{"class":2235},[2225,69206,27452],{"class":2307},[2225,69208,2311],{"class":2235},[2225,69210,65863],{"class":6527},[2225,69212,2298],{"class":2235},[2225,69214,65518],{"class":2307},[2225,69216,2311],{"class":2235},[2225,69218,2249],{"class":2235},[2225,69220,65874],{"class":2252},[2225,69222,2805],{"class":2235},[2225,69224,2353],{"class":2235},[2225,69226,69227],{"class":2227,"line":2861},[2225,69228,7351],{"class":2235},[2225,69230,69231],{"class":2227,"line":4246},[2225,69232,2285],{"emptyLinePlaceholder":2284},[2225,69234,69235,69237,69239,69242,69244],{"class":2227,"line":4253},[2225,69236,6500],{"class":2231},[2225,69238,69121],{"class":2291},[2225,69240,69241],{"class":2239},"(payload",[2225,69243,2298],{"class":2235},[2225,69245,2301],{"class":2235},[2225,69247,69248,69250,69252,69254,69257,69259],{"class":2227,"line":4259},[2225,69249,33397],{"class":2307},[2225,69251,2311],{"class":2235},[2225,69253,2249],{"class":2235},[2225,69255,69256],{"class":2252},"arn:aws:kms:eu-west-1:123456789012:key\u002Fabc",[2225,69258,2805],{"class":2235},[2225,69260,1707],{"class":2235},[2225,69262,69263,69266,69268,69270,69273,69275],{"class":2227,"line":6548},[2225,69264,69265],{"class":2307},"  bucket",[2225,69267,2311],{"class":2235},[2225,69269,2249],{"class":2235},[2225,69271,69272],{"class":2252},"firm-docs",[2225,69274,2805],{"class":2235},[2225,69276,1707],{"class":2235},[2225,69278,69279,69282,69284,69286,69289,69291,69293,69295],{"class":2227,"line":6576},[2225,69280,69281],{"class":2307},"  key",[2225,69283,2311],{"class":2235},[2225,69285,43789],{"class":2235},[2225,69287,69288],{"class":2252},"docs\u002F",[2225,69290,8197],{"class":2235},[2225,69292,66086],{"class":2239},[2225,69294,43799],{"class":2235},[2225,69296,1707],{"class":2235},[2225,69298,69299,69301],{"class":2227,"line":6582},[2225,69300,2359],{"class":2235},[2225,69302,2362],{"class":2239},[2225,69304,69305],{"class":2227,"line":6587},[2225,69306,69307],{"class":2763},"\u002F\u002F → S3 object docs\u002F\u003CdocId> now holds the KMS ciphertext; AWS credentials\n",[2225,69309,69310],{"class":2227,"line":7174},[2225,69311,69312],{"class":2763},"\u002F\u002F   come from the SDK's ambient chain (IAM role, env vars, ~\u002F.aws\u002Fcredentials)\n",[937,69314,69315,69316,1755,69319,69322,69323,69326,69327,69329,69330,69333],{},"Under the hood it is three lines: ",[941,69317,69318],{},"atAwsKms({ keyId }).seal(bytes)",[941,69320,69321],{},"PutObjectCommand",". The payload is capped at ",[950,69324,69325],{},"4 KB"," (the AWS KMS ",[941,69328,33309],{},"\nplaintext limit); ",[941,69331,69332],{},"encodeRenderPayload"," throws past that. Attestation payloads\n(declared fields + QR string) are normally far under it.",[1022,69335,69337],{"id":69336},"step-3-the-render-lambda","Step 3 — The render Lambda",[937,69339,69340],{},"The deployed handler is hub-free — it never opens a vault. Per request it:",[1234,69342,69343,69348,69358,69382],{},[1114,69344,69345,69346,3769],{},"verifies the signed share link (see below) — otherwise ",[950,69347,40720],{},[1114,69349,69350,53922,69352,69354,69355,3769],{},[941,69351,68441],{},[941,69353,68490],{}," — missing → ",[950,69356,69357],{},"404",[1114,69359,69360,69361,1746,69363,1746,69366,69369,69370,69373,69374,69377,69378,69381],{},"KMS ",[941,69362,33312],{},[941,69364,69365],{},"decodeRenderPayload",[941,69367,69368],{},"buildInvoiceHtml"," (QR as inline\n",[941,69371,69372],{},"\u003Csvg>",", drawn with ",[941,69375,69376],{},"qrcode",") → ",[941,69379,69380],{},"renderPdf"," via headless Chromium;",[1114,69383,69384],{},"returns the PDF:",[2217,69386,69388],{"className":6604,"code":69387,"language":141,"meta":1037,"style":1037},"{\n  \"statusCode\": 200,\n  \"headers\": { \"content-type\": \"application\u002Fpdf\" },\n  \"isBase64Encoded\": true,\n  \"body\": \"JVBERi0xLjQK…\"\n}\n",[941,69389,69390,69394,69409,69438,69452,69469],{"__ignoreMap":1037},[2225,69391,69392],{"class":2227,"line":2228},[2225,69393,4200],{"class":2235},[2225,69395,69396,69398,69401,69403,69405,69407],{"class":2227,"line":1041},[2225,69397,9368],{"class":2235},[2225,69399,69400],{"class":2774},"statusCode",[2225,69402,6620],{"class":2235},[2225,69404,2311],{"class":2235},[2225,69406,40956],{"class":6527},[2225,69408,1707],{"class":2235},[2225,69410,69411,69413,69415,69417,69419,69421,69423,69426,69428,69430,69432,69434,69436],{"class":2227,"line":1038},[2225,69412,9368],{"class":2235},[2225,69414,52729],{"class":2774},[2225,69416,6620],{"class":2235},[2225,69418,2311],{"class":2235},[2225,69420,2236],{"class":2235},[2225,69422,6614],{"class":2235},[2225,69424,69425],{"class":6342},"content-type",[2225,69427,6620],{"class":2235},[2225,69429,2311],{"class":2235},[2225,69431,6614],{"class":2235},[2225,69433,61020],{"class":2252},[2225,69435,6620],{"class":2235},[2225,69437,2353],{"class":2235},[2225,69439,69440,69442,69445,69447,69449],{"class":2227,"line":2288},[2225,69441,9368],{"class":2235},[2225,69443,69444],{"class":2774},"isBase64Encoded",[2225,69446,6620],{"class":2235},[2225,69448,2311],{"class":2235},[2225,69450,69451],{"class":2235}," true,\n",[2225,69453,69454,69456,69458,69460,69462,69464,69467],{"class":2227,"line":2304},[2225,69455,9368],{"class":2235},[2225,69457,42850],{"class":2774},[2225,69459,6620],{"class":2235},[2225,69461,2311],{"class":2235},[2225,69463,6614],{"class":2235},[2225,69465,69466],{"class":2252},"JVBERi0xLjQK…",[2225,69468,9449],{"class":2235},[2225,69470,69471],{"class":2227,"line":2356},[2225,69472,7351],{"class":2235},[937,69474,69475,69476,968,69479,1707,69482,30200,69485,2098,69488,69491,69492,69495,69496,69498],{},"Configuration is four environment variables: ",[941,69477,69478],{},"DOCS_BUCKET",[941,69480,69481],{},"KMS_KEY_ID",[941,69483,69484],{},"DOCS_PREFIX",[941,69486,69487],{},"docs",[941,69489,69490],{},"SHARE_SECRET_ARN"," (a Secrets Manager ARN;\nthe secret value is fetched once per cold start and cached). The handler\nfactory ",[941,69493,69494],{},"makeHandler(deps)"," takes the S3\u002FKMS clients and ",[941,69497,69380],{}," as\ninjected dependencies, so the whole flow unit-tests with mocks and no AWS.",[1022,69500,69502],{"id":69501},"step-4-verify-the-qr-offline","Step 4 — Verify the QR offline",[937,69504,69505],{},"Anyone with the PDF (or a print of it) recomputes the commitment from what\nthey can read on the paper and checks the signature — no network, no firm\ninvolvement:",[2217,69507,69509],{"className":2219,"code":69508,"language":2221,"meta":1037,"style":1037},"import { verifyDocument } from '@noy-db\u002Frecipe-attestation-verifier'\n\nconst printed = { invoiceNo: 'INV-1042', total: '1234.50', issueDate: '2026-05-29' }\nconst verdict = await verifyDocument(qr, printed, {\n  publicKeys: { [keyId]: publicKeyB64 },\n  fieldSchema: attestation,\n})\nverdict.outcome \u002F\u002F → 'authentic-valid'\n",[941,69510,69511,69530,69534,69580,69602,69622,69632,69638],{"__ignoreMap":1037},[2225,69512,69513,69515,69517,69519,69521,69523,69525,69528],{"class":2227,"line":2228},[2225,69514,2232],{"class":2231},[2225,69516,2236],{"class":2235},[2225,69518,66569],{"class":2239},[2225,69520,2243],{"class":2235},[2225,69522,2246],{"class":2231},[2225,69524,2249],{"class":2235},[2225,69526,69527],{"class":2252},"@noy-db\u002Frecipe-attestation-verifier",[2225,69529,2256],{"class":2235},[2225,69531,69532],{"class":2227,"line":1041},[2225,69533,2285],{"emptyLinePlaceholder":2284},[2225,69535,69536,69538,69540,69542,69544,69546,69548,69550,69552,69554,69556,69558,69560,69562,69564,69566,69568,69570,69572,69574,69576,69578],{"class":2227,"line":1038},[2225,69537,2775],{"class":2774},[2225,69539,67476],{"class":2239},[2225,69541,2781],{"class":2235},[2225,69543,2236],{"class":2235},[2225,69545,27739],{"class":2307},[2225,69547,2311],{"class":2235},[2225,69549,2249],{"class":2235},[2225,69551,65852],{"class":2252},[2225,69553,2805],{"class":2235},[2225,69555,2298],{"class":2235},[2225,69557,27452],{"class":2307},[2225,69559,2311],{"class":2235},[2225,69561,2249],{"class":2235},[2225,69563,67501],{"class":2252},[2225,69565,2805],{"class":2235},[2225,69567,2298],{"class":2235},[2225,69569,65518],{"class":2307},[2225,69571,2311],{"class":2235},[2225,69573,2249],{"class":2235},[2225,69575,65874],{"class":2252},[2225,69577,2805],{"class":2235},[2225,69579,6369],{"class":2235},[2225,69581,69582,69584,69586,69588,69590,69592,69594,69596,69598,69600],{"class":2227,"line":2288},[2225,69583,2775],{"class":2774},[2225,69585,61576],{"class":2239},[2225,69587,2781],{"class":2235},[2225,69589,2784],{"class":2231},[2225,69591,66569],{"class":2291},[2225,69593,67536],{"class":2239},[2225,69595,2298],{"class":2235},[2225,69597,67541],{"class":2239},[2225,69599,2298],{"class":2235},[2225,69601,2301],{"class":2235},[2225,69603,69604,69606,69608,69610,69612,69614,69616,69618,69620],{"class":2227,"line":2304},[2225,69605,66335],{"class":2307},[2225,69607,2311],{"class":2235},[2225,69609,2236],{"class":2235},[2225,69611,8667],{"class":2307},[2225,69613,66012],{"class":2239},[2225,69615,8803],{"class":2307},[2225,69617,2311],{"class":2235},[2225,69619,65966],{"class":2239},[2225,69621,19008],{"class":2235},[2225,69623,69624,69626,69628,69630],{"class":2227,"line":2356},[2225,69625,66357],{"class":2307},[2225,69627,2311],{"class":2235},[2225,69629,67441],{"class":2239},[2225,69631,1707],{"class":2235},[2225,69633,69634,69636],{"class":2227,"line":2830},[2225,69635,2359],{"class":2235},[2225,69637,2362],{"class":2239},[2225,69639,69640,69642,69644,69647],{"class":2227,"line":2861},[2225,69641,61655],{"class":2239},[2225,69643,1520],{"class":2235},[2225,69645,69646],{"class":2239},"outcome ",[2225,69648,69649],{"class":2763},"\u002F\u002F → 'authentic-valid'\n",[937,69651,69652,69653,69655,69656,968,69658,968,69660,968,69662,69664,69665,1520],{},"Other outcomes: ",[941,69654,66428],{}," (with the differing field localized),\n",[941,69657,66439],{},[941,69659,66417],{},[941,69661,66450],{},[941,69663,66461],{},". The\nfull verifier — including a self-contained static HTML page — is the\n",[1007,69666,68371],{"href":516},[1022,69668,69670],{"id":69669},"gating-the-endpoint-stateless-signed-share-links","Gating the endpoint — stateless signed share links",[937,69672,69673,69674,69677,69678,69681],{},"The render endpoint returns decrypted document content, so it must not be\nopen. Instead of an AWS authorizer, Cognito, or any identity provider, the\nLambda gates itself with a ",[950,69675,69676],{},"stateless signed magic link",": an HMAC-SHA256\nover ",[941,69679,69680],{},"canonicalJson({ v, docId, exp })"," keyed by a Secrets Manager secret.",[2217,69683,69685],{"className":2219,"code":69684,"language":2221,"meta":1037,"style":1037},"import { mintShareLink } from '@noy-db\u002Frecipe-aws-kms-pdf-attestation\u002Fshare-link'\n\n\u002F\u002F The share-signing secret is an ASCII string (here from Secrets Manager or an\n\u002F\u002F env var); its utf8 bytes are the HMAC key — mint and verify must encode it\n\u002F\u002F identically.\nconst shareSecret = new TextEncoder().encode(process.env.SHARE_SECRET!)\n\nconst url = await mintShareLink(docId, {\n  secret: shareSecret,\n  baseUrl: 'https:\u002F\u002Fabc123.lambda-url.eu-west-1.on.aws\u002F',\n  ttlMs: 60 * 60 * 1000,               \u002F\u002F optional; default 24 h, capped at 7 d\n})\n\u002F\u002F → 'https:\u002F\u002Fabc123.lambda-url.eu-west-1.on.aws\u002F?d=01JWK4Y4B7Q6…&exp=1751975432000&sig=tJ9vJq…'\n",[941,69686,69687,69707,69711,69716,69721,69726,69763,69767,69787,69798,69814,69836,69842],{"__ignoreMap":1037},[2225,69688,69689,69691,69693,69696,69698,69700,69702,69705],{"class":2227,"line":2228},[2225,69690,2232],{"class":2231},[2225,69692,2236],{"class":2235},[2225,69694,69695],{"class":2239}," mintShareLink",[2225,69697,2243],{"class":2235},[2225,69699,2246],{"class":2231},[2225,69701,2249],{"class":2235},[2225,69703,69704],{"class":2252},"@noy-db\u002Frecipe-aws-kms-pdf-attestation\u002Fshare-link",[2225,69706,2256],{"class":2235},[2225,69708,69709],{"class":2227,"line":1041},[2225,69710,2285],{"emptyLinePlaceholder":2284},[2225,69712,69713],{"class":2227,"line":1038},[2225,69714,69715],{"class":2763},"\u002F\u002F The share-signing secret is an ASCII string (here from Secrets Manager or an\n",[2225,69717,69718],{"class":2227,"line":2288},[2225,69719,69720],{"class":2763},"\u002F\u002F env var); its utf8 bytes are the HMAC key — mint and verify must encode it\n",[2225,69722,69723],{"class":2227,"line":2304},[2225,69724,69725],{"class":2763},"\u002F\u002F identically.\n",[2225,69727,69728,69730,69733,69735,69737,69740,69742,69744,69747,69750,69752,69754,69756,69759,69761],{"class":2227,"line":2356},[2225,69729,2775],{"class":2774},[2225,69731,69732],{"class":2239}," shareSecret ",[2225,69734,2781],{"class":2235},[2225,69736,8130],{"class":2235},[2225,69738,69739],{"class":2291}," TextEncoder",[2225,69741,4228],{"class":2239},[2225,69743,1520],{"class":2235},[2225,69745,69746],{"class":2291},"encode",[2225,69748,69749],{"class":2239},"(process",[2225,69751,1520],{"class":2235},[2225,69753,183],{"class":2239},[2225,69755,1520],{"class":2235},[2225,69757,69758],{"class":2239},"SHARE_SECRET",[2225,69760,35134],{"class":2235},[2225,69762,2362],{"class":2239},[2225,69764,69765],{"class":2227,"line":2830},[2225,69766,2285],{"emptyLinePlaceholder":2284},[2225,69768,69769,69771,69774,69776,69778,69780,69783,69785],{"class":2227,"line":2861},[2225,69770,2775],{"class":2774},[2225,69772,69773],{"class":2239}," url ",[2225,69775,2781],{"class":2235},[2225,69777,2784],{"class":2231},[2225,69779,69695],{"class":2291},[2225,69781,69782],{"class":2239},"(docId",[2225,69784,2298],{"class":2235},[2225,69786,2301],{"class":2235},[2225,69788,69789,69791,69793,69796],{"class":2227,"line":4246},[2225,69790,10423],{"class":2307},[2225,69792,2311],{"class":2235},[2225,69794,69795],{"class":2239}," shareSecret",[2225,69797,1707],{"class":2235},[2225,69799,69800,69803,69805,69807,69810,69812],{"class":2227,"line":4253},[2225,69801,69802],{"class":2307},"  baseUrl",[2225,69804,2311],{"class":2235},[2225,69806,2249],{"class":2235},[2225,69808,69809],{"class":2252},"https:\u002F\u002Fabc123.lambda-url.eu-west-1.on.aws\u002F",[2225,69811,2805],{"class":2235},[2225,69813,1707],{"class":2235},[2225,69815,69816,69819,69821,69823,69825,69827,69829,69831,69833],{"class":2227,"line":4259},[2225,69817,69818],{"class":2307},"  ttlMs",[2225,69820,2311],{"class":2235},[2225,69822,44452],{"class":6527},[2225,69824,2263],{"class":2235},[2225,69826,44452],{"class":6527},[2225,69828,2263],{"class":2235},[2225,69830,26290],{"class":6527},[2225,69832,2298],{"class":2235},[2225,69834,69835],{"class":2763},"               \u002F\u002F optional; default 24 h, capped at 7 d\n",[2225,69837,69838,69840],{"class":2227,"line":6548},[2225,69839,2359],{"class":2235},[2225,69841,2362],{"class":2239},[2225,69843,69844],{"class":2227,"line":6576},[2225,69845,69846],{"class":2763},"\u002F\u002F → 'https:\u002F\u002Fabc123.lambda-url.eu-west-1.on.aws\u002F?d=01JWK4Y4B7Q6…&exp=1751975432000&sig=tJ9vJq…'\n",[937,69848,69849,69850,69853,69854,4688,69857,69860,69861,69863,69864,1261,69867,1261,69870,2601,69873,69876,69877,69879,69880,69883,69884,69886],{},"On the Lambda side, ",[941,69851,69852],{},"verifyShareToken"," checks the signature in constant time\nand returns either ",[941,69855,69856],{},"{ ok: true, docId }",[941,69858,69859],{},"{ ok: false, reason }"," with\n",[941,69862,43668],{}," one of ",[941,69865,69866],{},"missing-token",[941,69868,69869],{},"malformed",[941,69871,69872],{},"expired",[941,69874,69875],{},"invalid-signature"," — each rejected with ",[950,69878,40720],{},". A bare ",[941,69881,69882],{},"?docId="," request is\n",[941,69885,69866],{},": there is no unauthenticated path. Properties to be aware of:",[1111,69888,69889,69896,69902],{},[1114,69890,69891,69892,69895],{},"Links are ",[950,69893,69894],{},"bearer capabilities",": multi-use within their TTL, safe to\nforward by design.",[1114,69897,69898,69901],{},[950,69899,69900],{},"Revocation = rotate the share secret"," — every live link is invalidated\nat once. Rotating the share secret never touches the KMS data key, so\ndocument access and link revocation stay independent.",[1114,69903,69904,69905,69908],{},"The Lambda Function URL stays ",[941,69906,69907],{},"authType: NONE"," at the AWS layer — the\nLambda itself is the gate.",[1022,69910,69912],{"id":69911},"trust-model-and-limits","Trust model and limits",[1111,69914,69915,69922,69934,69940],{},[1114,69916,69917,69918,69921],{},"The QR carries the signed per-field commitment; the PDF is ",[950,69919,69920],{},"generated,\nnever verified server-side",". Trust lives in the offline verification, not\nin the rendering endpoint.",[1114,69923,69924,69925,69927,69928,69933],{},"The KMS key holder (your AWS account) ",[1092,69926,5105],{}," decrypt the render payloads —\nthat is the deliberate ",[1007,69929,69930,69932],{"href":170},[941,69931,1139],{}," trust boundary",". The payload is\nonly the declared fields plus the QR, not the vault.",[1114,69935,69936,69937,69939],{},"Render payloads are limited to 4 KB by KMS ",[941,69938,33309],{},"; envelope encryption\nfor larger payloads is out of this recipe's scope.",[1114,69941,69942,69944],{},[941,69943,69380],{}," needs a Chromium binary, so the PDF step only runs deployed (or\nlocally with Chromium available) — not in plain CI.",[1022,69946,69948],{"id":69947},"deploying-and-testing","Deploying and testing",[937,69950,69951,69952,69955,69956,69959,69960,1520],{},"The recipe ships a CDK stack (",[941,69953,69954],{},"infra\u002F",") that provisions the whole thing with\nleast-privilege grants: a rotation-enabled KMS key, a private S3 bucket, a\nSecrets Manager–generated share secret (256-bit, never present in the\ntemplate), and the render Lambda as an ",[950,69957,69958],{},"arm64 zip function + public Chromium\nLambda layer"," — ~1.7 MB artifact, no Docker or ECR in the deploy path (a\ncontainer variant is included for air-gapped setups). Step-by-step deploy,\nlive verification, and teardown are in the recipe's\n",[1007,69961,69964],{"href":69962,"rel":69963},"https:\u002F\u002Fgithub.com\u002Fnoy-db\u002Fdocs\u002Fblob\u002Fmain\u002Frecipes\u002Faws-kms-pdf-attestation\u002FRUNBOOK.md",[3371],"RUNBOOK",[937,69966,69967,69968,69971],{},"The data path — issue → seal → decrypt → render HTML → offline verify — is\ncovered by an AWS-free test slice in the repository's test suite (mock KMS and\nS3 clients, no Chromium); the recipe's own ",[941,69969,69970],{},"pnpm test"," exercises each module\nthe same way, with the S3\u002FKMS clients injected as mocks.",[1022,69973,2621],{"id":2620},[1111,69975,69976,69981,69987],{},[1114,69977,69978,69980],{},[1007,69979,35860],{"href":610}," — the issue\u002Frevoke API inside the hub",[1114,69982,69983,69986],{},[1007,69984,69985],{"href":516},"Offline document-attestation verifier"," — the other half",[1114,69988,69989,69993],{},[1007,69990,69991,9789],{"href":170},[941,69992,16186],{}," — sealing-key providers and their trust boundary",[2645,69995,69996],{},"html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":1037,"searchDepth":1038,"depth":1038,"links":69998},[69999,70000,70001,70002,70003,70004,70005,70006,70007,70008],{"id":68405,"depth":1041,"text":68406},{"id":68463,"depth":1041,"text":68464},{"id":68512,"depth":1041,"text":68513},{"id":69097,"depth":1041,"text":69098},{"id":69336,"depth":1041,"text":69337},{"id":69501,"depth":1041,"text":69502},{"id":69669,"depth":1041,"text":69670},{"id":69911,"depth":1041,"text":69912},{"id":69947,"depth":1041,"text":69948},{"id":2620,"depth":1041,"text":2621},"Deploy an AWS Lambda that KMS-decrypts a sealed record from S3 and renders a PDF with the attestation QR embedded — verifiable fully offline.",{},{"title":519,"description":70009},"pmhz8zp1zDYYN0fb5owR8jkg46E0Lv8LlWNcIk7g6oM",{"id":70014,"title":523,"access":12,"apiToc":929,"body":70015,"category":1042,"description":71527,"extension":1044,"meta":71528,"navigation":1042,"nest":1042,"path":524,"seo":71529,"sinceVersion":1042,"status":1049,"stem":525,"subtitle":1042,"tier":1050,"tocExternal":1042,"__hash__":71530},"docs\u002Frecipes\u002Fbrowser-durability.md",{"type":931,"value":70016,"toc":71515},[70017,70020,70043,70068,70075,70090,70094,70097,70099,70169,70171,70315,70328,70332,70485,70512,70527,70531,70545,70885,70909,70936,70939,70943,70946,71148,71151,71179,71199,71203,71327,71334,71392,71395,71399,71412,71415,71418,71462,71477,71479,71512],[934,70018,523],{"id":70019},"browser-durability",[1061,70021,70022],{},[937,70023,70024,70026,70027,58223,70029,70033,70034,70037,70038,70042],{},[950,70025,1500],{}," anyone shipping noy-db in a browser with no server of their own, who has realised that IndexedDB is not a backup.\n",[950,70028,58222],{},[1007,70030,70031],{"href":359},[941,70032,9573],{}," for the live vault, ",[941,70035,70036],{},"@noy-db\u002Fhub\u002Fpod"," for the backup, and optionally ",[1007,70039,70040],{"href":355},[941,70041,47334],{}," for the unattended tier.",[937,70044,70045,70046,70053,70054,70057,70058,70060,70061,70063,70064,70067],{},"Verified by\n",[1007,70047,70050],{"href":70048,"rel":70049},"https:\u002F\u002Fgithub.com\u002Fnoy-db\u002Fdocs\u002Fblob\u002Fmain\u002Fshowcases\u002Fsrc\u002Frecipe-browser-durability.recipe.test.ts",[3371],[941,70051,70052],{},"showcases\u002Fsrc\u002Frecipe-browser-durability.recipe.test.ts","\n— the pod round trip and the change-event trigger below are the code that runs there, against\nan in-memory store. ⛔ The File System Access tier and ",[941,70055,70056],{},"event.remote === true"," are ",[950,70059,1004],{},"\ncovered: the first is browser-only, and the second needs a second tab applying a signal. What\nthe test does pin is that a ",[1092,70062,49766],{}," write leaves ",[941,70065,70066],{},"remote"," falsy, so the guard below cannot\nsilently drop the backup it exists to take.",[937,70069,70070,70071,70074],{},"A browser vault has one durability problem and it is not encryption: ",[950,70072,70073],{},"IndexedDB is\nevictable",". The user clears site data, the browser reclaims storage under pressure, the\nprofile is reset — and a vault that existed only in IDB is gone. The fix is not a\ndifferent store. It is a second copy, in a format the user can keep.",[937,70076,70077,70078,2085,70082,70085,70086,70089],{},"That copy is a ",[950,70079,70080,36331],{},[941,70081,1402],{},[941,70083,70084],{},"writePod(vault)"," returns the whole vault as bytes, and\n",[941,70087,70088],{},"readPod(bytes)"," reads it back. Both live in the hub, so a browser backup needs no store\npackage at all — the store keeps your live data, the pod is how you leave.",[1022,70091,70093],{"id":70092},"prerequisites","Prerequisites",[937,70095,70096],{},"None beyond the Bundle line. The manual tier below works in every browser; the\nunattended tier needs the File System Access API and degrades to the manual tier where\nit is absent.",[1022,70098,58276],{"id":58275},[1338,70100,70101,70111],{},[1341,70102,70103],{},[1344,70104,70105,70108],{},[1347,70106,70107],{},"Capability",[1347,70109,70110],{},"Provided by",[1357,70112,70113,70125,70137,70146,70160],{},[1344,70114,70115,70118],{},[1362,70116,70117],{},"Live vault that survives reload",[1362,70119,70120,70122,70123],{},[941,70121,9578],{}," — IndexedDB, ",[941,70124,7811],{},[1344,70126,70127,70130],{},[1362,70128,70129],{},"A portable, encrypted copy the user holds",[1362,70131,70132,70134,70135,53239],{},[941,70133,70084],{}," → a ",[941,70136,1402],{},[1344,70138,70139,70142],{},[1362,70140,70141],{},"Restore into a fresh browser",[1362,70143,70144],{},[941,70145,70088],{},[1344,70147,70148,70151],{},[1362,70149,70150],{},"Backup on a timer, few clicks",[1362,70152,70153,6030,70156,70159],{},[941,70154,70155],{},"to-browser-fs",[941,70157,70158],{},"rememberDirectory()"," — see the permission caveat below",[1344,70161,70162,70165],{},[1362,70163,70164],{},"Backup when data actually changes",[1362,70166,70167],{},[941,70168,38144],{},[1022,70170,58394],{"id":58393},[2217,70172,70174],{"className":2219,"code":70173,"language":2221,"meta":1037,"style":1037},"import { createNoydb, writePod, readPod } from '@noy-db\u002Fhub'\nimport { toBrowserIdb } from '@noy-db\u002Fto-browser-idb'\n\nconst db = await createNoydb({\n  store: toBrowserIdb(),\n  user: 'alice',\n  secret: 'a-real-passphrase-2026',\n})\nconst vault = await db.openVault('notes')\n",[941,70175,70176,70204,70222,70226,70242,70254,70268,70283,70289],{"__ignoreMap":1037},[2225,70177,70178,70180,70182,70184,70186,70189,70191,70194,70196,70198,70200,70202],{"class":2227,"line":2228},[2225,70179,2232],{"class":2231},[2225,70181,2236],{"class":2235},[2225,70183,2744],{"class":2239},[2225,70185,2298],{"class":2235},[2225,70187,70188],{"class":2239}," writePod",[2225,70190,2298],{"class":2235},[2225,70192,70193],{"class":2239}," readPod",[2225,70195,2243],{"class":2235},[2225,70197,2246],{"class":2231},[2225,70199,2249],{"class":2235},[2225,70201,1086],{"class":2252},[2225,70203,2256],{"class":2235},[2225,70205,70206,70208,70210,70212,70214,70216,70218,70220],{"class":2227,"line":1041},[2225,70207,2232],{"class":2231},[2225,70209,2236],{"class":2235},[2225,70211,34763],{"class":2239},[2225,70213,2243],{"class":2235},[2225,70215,2246],{"class":2231},[2225,70217,2249],{"class":2235},[2225,70219,9573],{"class":2252},[2225,70221,2256],{"class":2235},[2225,70223,70224],{"class":2227,"line":1038},[2225,70225,2285],{"emptyLinePlaceholder":2284},[2225,70227,70228,70230,70232,70234,70236,70238,70240],{"class":2227,"line":2288},[2225,70229,2775],{"class":2774},[2225,70231,2778],{"class":2239},[2225,70233,2781],{"class":2235},[2225,70235,2784],{"class":2231},[2225,70237,2744],{"class":2291},[2225,70239,2789],{"class":2239},[2225,70241,4200],{"class":2235},[2225,70243,70244,70246,70248,70250,70252],{"class":2227,"line":2304},[2225,70245,15608],{"class":2307},[2225,70247,2311],{"class":2235},[2225,70249,34763],{"class":2291},[2225,70251,4228],{"class":2239},[2225,70253,1707],{"class":2235},[2225,70255,70256,70258,70260,70262,70264,70266],{"class":2227,"line":2356},[2225,70257,4205],{"class":2307},[2225,70259,2311],{"class":2235},[2225,70261,2249],{"class":2235},[2225,70263,6408],{"class":2252},[2225,70265,2805],{"class":2235},[2225,70267,1707],{"class":2235},[2225,70269,70270,70272,70274,70276,70279,70281],{"class":2227,"line":2830},[2225,70271,10423],{"class":2307},[2225,70273,2311],{"class":2235},[2225,70275,2249],{"class":2235},[2225,70277,70278],{"class":2252},"a-real-passphrase-2026",[2225,70280,2805],{"class":2235},[2225,70282,1707],{"class":2235},[2225,70284,70285,70287],{"class":2227,"line":2861},[2225,70286,2359],{"class":2235},[2225,70288,2362],{"class":2239},[2225,70290,70291,70293,70295,70297,70299,70301,70303,70305,70307,70309,70311,70313],{"class":2227,"line":4246},[2225,70292,2775],{"class":2774},[2225,70294,2835],{"class":2239},[2225,70296,2781],{"class":2235},[2225,70298,2784],{"class":2231},[2225,70300,2842],{"class":2239},[2225,70302,1520],{"class":2235},[2225,70304,2847],{"class":2291},[2225,70306,2789],{"class":2239},[2225,70308,2805],{"class":2235},[2225,70310,2854],{"class":2252},[2225,70312,2805],{"class":2235},[2225,70314,2362],{"class":2239},[937,70316,70317,997,70319,70322,70323,70327],{},[941,70318,35759],{},[941,70320,70321],{},"readPod"," are also available from the ",[1007,70324,70325],{"href":630},[941,70326,70036],{},"\nsubpath if you prefer to keep the root barrel small.",[1022,70329,70331],{"id":70330},"tier-1-the-download-which-works-everywhere","Tier 1 — the download, which works everywhere",[2217,70333,70335],{"className":2219,"code":70334,"language":2221,"meta":1037,"style":1037},"const bytes = await writePod(vault)\nconst url = URL.createObjectURL(new Blob([bytes], { type: 'application\u002Foctet-stream' }))\nconst a = document.createElement('a')\na.href = url\na.download = podFilename('notes')\na.click()\nURL.revokeObjectURL(url)\n",[941,70336,70337,70351,70397,70423,70437,70461,70472],{"__ignoreMap":1037},[2225,70338,70339,70341,70343,70345,70347,70349],{"class":2227,"line":2228},[2225,70340,2775],{"class":2774},[2225,70342,32469],{"class":2239},[2225,70344,2781],{"class":2235},[2225,70346,2784],{"class":2231},[2225,70348,70188],{"class":2291},[2225,70350,45516],{"class":2239},[2225,70352,70353,70355,70357,70359,70362,70364,70367,70369,70372,70375,70378,70380,70382,70384,70386,70388,70391,70393,70395],{"class":2227,"line":1041},[2225,70354,2775],{"class":2774},[2225,70356,69773],{"class":2239},[2225,70358,2781],{"class":2235},[2225,70360,70361],{"class":2239}," URL",[2225,70363,1520],{"class":2235},[2225,70365,70366],{"class":2291},"createObjectURL",[2225,70368,2789],{"class":2239},[2225,70370,70371],{"class":2235},"new",[2225,70373,70374],{"class":2291}," Blob",[2225,70376,70377],{"class":2239},"([bytes]",[2225,70379,2298],{"class":2235},[2225,70381,2236],{"class":2235},[2225,70383,3399],{"class":2307},[2225,70385,2311],{"class":2235},[2225,70387,2249],{"class":2235},[2225,70389,70390],{"class":2252},"application\u002Foctet-stream",[2225,70392,2805],{"class":2235},[2225,70394,2243],{"class":2235},[2225,70396,8606],{"class":2239},[2225,70398,70399,70401,70403,70405,70408,70410,70413,70415,70417,70419,70421],{"class":2227,"line":1038},[2225,70400,2775],{"class":2774},[2225,70402,4724],{"class":2239},[2225,70404,2781],{"class":2235},[2225,70406,70407],{"class":2239}," document",[2225,70409,1520],{"class":2235},[2225,70411,70412],{"class":2291},"createElement",[2225,70414,2789],{"class":2239},[2225,70416,2805],{"class":2235},[2225,70418,1007],{"class":2252},[2225,70420,2805],{"class":2235},[2225,70422,2362],{"class":2239},[2225,70424,70425,70427,70429,70432,70434],{"class":2227,"line":2288},[2225,70426,1007],{"class":2239},[2225,70428,1520],{"class":2235},[2225,70430,70431],{"class":2239},"href ",[2225,70433,2781],{"class":2235},[2225,70435,70436],{"class":2239}," url\n",[2225,70438,70439,70441,70443,70446,70448,70451,70453,70455,70457,70459],{"class":2227,"line":2304},[2225,70440,1007],{"class":2239},[2225,70442,1520],{"class":2235},[2225,70444,70445],{"class":2239},"download ",[2225,70447,2781],{"class":2235},[2225,70449,70450],{"class":2291}," podFilename",[2225,70452,2789],{"class":2239},[2225,70454,2805],{"class":2235},[2225,70456,2854],{"class":2252},[2225,70458,2805],{"class":2235},[2225,70460,2362],{"class":2239},[2225,70462,70463,70465,70467,70470],{"class":2227,"line":2356},[2225,70464,1007],{"class":2239},[2225,70466,1520],{"class":2235},[2225,70468,70469],{"class":2291},"click",[2225,70471,8146],{"class":2239},[2225,70473,70474,70477,70479,70482],{"class":2227,"line":2830},[2225,70475,70476],{"class":2239},"URL",[2225,70478,1520],{"class":2235},[2225,70480,70481],{"class":2291},"revokeObjectURL",[2225,70483,70484],{"class":2239},"(url)\n",[937,70486,70487,7356,70489,70492,70493,70496,70497,70499,70500,70503,70504,70507,70508,70511],{},[941,70488,35759],{},[950,70490,70491],{},"async"," — it returns a ",[941,70494,70495],{},"Promise\u003CUint8Array>",", not bytes. The file is a\n",[941,70498,1402],{}," container: magic ",[941,70501,70502],{},"NDB1",", a minimum-disclosure unencrypted header, and a\ncompressed body. There is no ",[941,70505,70506],{},".pod"," and no ",[941,70509,70510],{},".bundle"," extension.",[937,70513,70514,70515,70518,70519,70522,70523,70526],{},"::: warning A timer cannot click this link\nA ",[941,70516,70517],{},"\u003Ca download>"," that fires from a user gesture downloads. The same line on a\n",[941,70520,70521],{},"setInterval"," does not — browsers block downloads with no user activation behind it.\n",[950,70524,70525],{},"A pure SPA therefore cannot back itself up on a schedule",", and no amount of code\nchanges that. It is the reason tier 2 exists.\n:::",[1022,70528,70530],{"id":70529},"tier-2-one-grant-then-unattended","Tier 2 — one grant, then unattended",[937,70532,70533,70537,70538,70540,70541,70544],{},[1007,70534,70535],{"href":355},[941,70536,47334],{}," is usually read as a ",[1092,70539,6951],{},". Here it\nis a ",[950,70542,70543],{},"backup target",", which is a much easier thing for it to be: a pod write is one\nwhole file, so none of the per-record concerns that make a filesystem an awkward primary\nstore apply.",[2217,70546,70548],{"className":2219,"code":70547,"language":2221,"meta":1037,"style":1037},"import { rememberDirectory, recallDirectory } from '@noy-db\u002Fto-browser-fs'\n\n\u002F\u002F Once, from a click: the user picks a folder and the handle is persisted in IDB.\nasync function grantBackupFolder() {\n  const handle = await (window as any).showDirectoryPicker({ mode: 'readwrite' })\n  await rememberDirectory('backups', handle)\n}\n\n\u002F\u002F Afterwards, with no interaction — including on a timer.\nasync function backupTo(handle: any, name: string, bytes: Uint8Array) {\n  const file = await handle.getFileHandle(name, { create: true })\n  const w = await file.createWritable()\n  await w.write(bytes)\n  await w.close()\n}\n\nconst handle = await recallDirectory('backups')\nif (handle) {\n  await backupTo(handle, podFilename('notes'), await writePod(vault))\n} else {\n  \u002F\u002F No grant yet, or a browser without the API — fall back to tier 1.\n}\n",[941,70549,70550,70572,70576,70581,70594,70637,70658,70662,70666,70671,70708,70743,70763,70780,70792,70796,70800,70822,70831,70867,70876,70881],{"__ignoreMap":1037},[2225,70551,70552,70554,70556,70558,70560,70562,70564,70566,70568,70570],{"class":2227,"line":2228},[2225,70553,2232],{"class":2231},[2225,70555,2236],{"class":2235},[2225,70557,47750],{"class":2239},[2225,70559,2298],{"class":2235},[2225,70561,47755],{"class":2239},[2225,70563,2243],{"class":2235},[2225,70565,2246],{"class":2231},[2225,70567,2249],{"class":2235},[2225,70569,47334],{"class":2252},[2225,70571,2256],{"class":2235},[2225,70573,70574],{"class":2227,"line":1041},[2225,70575,2285],{"emptyLinePlaceholder":2284},[2225,70577,70578],{"class":2227,"line":1038},[2225,70579,70580],{"class":2763},"\u002F\u002F Once, from a click: the user picks a folder and the handle is persisted in IDB.\n",[2225,70582,70583,70585,70587,70590,70592],{"class":2227,"line":2288},[2225,70584,70491],{"class":2774},[2225,70586,40856],{"class":2774},[2225,70588,70589],{"class":2291}," grantBackupFolder",[2225,70591,4228],{"class":2235},[2225,70593,2301],{"class":2235},[2225,70595,70596,70598,70600,70602,70604,70606,70608,70610,70613,70615,70617,70619,70621,70623,70625,70627,70629,70631,70633,70635],{"class":2227,"line":2304},[2225,70597,8123],{"class":2774},[2225,70599,40913],{"class":2239},[2225,70601,6346],{"class":2235},[2225,70603,2784],{"class":2231},[2225,70605,953],{"class":2307},[2225,70607,12429],{"class":2239},[2225,70609,2266],{"class":2231},[2225,70611,70612],{"class":6342}," any",[2225,70614,1381],{"class":2307},[2225,70616,1520],{"class":2235},[2225,70618,47420],{"class":2291},[2225,70620,2789],{"class":2307},[2225,70622,2792],{"class":2235},[2225,70624,32303],{"class":2307},[2225,70626,2311],{"class":2235},[2225,70628,2249],{"class":2235},[2225,70630,9587],{"class":2252},[2225,70632,2805],{"class":2235},[2225,70634,2243],{"class":2235},[2225,70636,2362],{"class":2307},[2225,70638,70639,70641,70643,70645,70647,70650,70652,70654,70656],{"class":2227,"line":2356},[2225,70640,7561],{"class":2231},[2225,70642,47750],{"class":2291},[2225,70644,2789],{"class":2307},[2225,70646,2805],{"class":2235},[2225,70648,70649],{"class":2252},"backups",[2225,70651,2805],{"class":2235},[2225,70653,2298],{"class":2235},[2225,70655,40913],{"class":2239},[2225,70657,2362],{"class":2307},[2225,70659,70660],{"class":2227,"line":2830},[2225,70661,7351],{"class":2235},[2225,70663,70664],{"class":2227,"line":2861},[2225,70665,2285],{"emptyLinePlaceholder":2284},[2225,70667,70668],{"class":2227,"line":4246},[2225,70669,70670],{"class":2763},"\u002F\u002F Afterwards, with no interaction — including on a timer.\n",[2225,70672,70673,70675,70677,70680,70682,70684,70686,70688,70690,70692,70694,70696,70698,70700,70702,70704,70706],{"class":2227,"line":4253},[2225,70674,70491],{"class":2774},[2225,70676,40856],{"class":2774},[2225,70678,70679],{"class":2291}," backupTo",[2225,70681,2789],{"class":2235},[2225,70683,36368],{"class":7052},[2225,70685,2311],{"class":2235},[2225,70687,70612],{"class":6342},[2225,70689,2298],{"class":2235},[2225,70691,8104],{"class":7052},[2225,70693,2311],{"class":2235},[2225,70695,6366],{"class":6342},[2225,70697,2298],{"class":2235},[2225,70699,29996],{"class":7052},[2225,70701,2311],{"class":2235},[2225,70703,60935],{"class":6342},[2225,70705,1381],{"class":2235},[2225,70707,2301],{"class":2235},[2225,70709,70710,70712,70714,70716,70718,70720,70722,70725,70727,70729,70731,70733,70735,70737,70739,70741],{"class":2227,"line":4259},[2225,70711,8123],{"class":2774},[2225,70713,53239],{"class":2239},[2225,70715,6346],{"class":2235},[2225,70717,2784],{"class":2231},[2225,70719,40913],{"class":2239},[2225,70721,1520],{"class":2235},[2225,70723,70724],{"class":2291},"getFileHandle",[2225,70726,2789],{"class":2307},[2225,70728,4914],{"class":2239},[2225,70730,2298],{"class":2235},[2225,70732,2236],{"class":2235},[2225,70734,43000],{"class":2307},[2225,70736,2311],{"class":2235},[2225,70738,2322],{"class":2321},[2225,70740,2243],{"class":2235},[2225,70742,2362],{"class":2307},[2225,70744,70745,70747,70750,70752,70754,70756,70758,70761],{"class":2227,"line":6548},[2225,70746,8123],{"class":2774},[2225,70748,70749],{"class":2239}," w",[2225,70751,6346],{"class":2235},[2225,70753,2784],{"class":2231},[2225,70755,53239],{"class":2239},[2225,70757,1520],{"class":2235},[2225,70759,70760],{"class":2291},"createWritable",[2225,70762,8146],{"class":2307},[2225,70764,70765,70767,70769,70771,70774,70776,70778],{"class":2227,"line":6576},[2225,70766,7561],{"class":2231},[2225,70768,70749],{"class":2239},[2225,70770,1520],{"class":2235},[2225,70772,70773],{"class":2291},"write",[2225,70775,2789],{"class":2307},[2225,70777,64492],{"class":2239},[2225,70779,2362],{"class":2307},[2225,70781,70782,70784,70786,70788,70790],{"class":2227,"line":6582},[2225,70783,7561],{"class":2231},[2225,70785,70749],{"class":2239},[2225,70787,1520],{"class":2235},[2225,70789,6595],{"class":2291},[2225,70791,8146],{"class":2307},[2225,70793,70794],{"class":2227,"line":6587},[2225,70795,7351],{"class":2235},[2225,70797,70798],{"class":2227,"line":7174},[2225,70799,2285],{"emptyLinePlaceholder":2284},[2225,70801,70802,70804,70806,70808,70810,70812,70814,70816,70818,70820],{"class":2227,"line":7180},[2225,70803,2775],{"class":2774},[2225,70805,40799],{"class":2239},[2225,70807,2781],{"class":2235},[2225,70809,2784],{"class":2231},[2225,70811,47755],{"class":2291},[2225,70813,2789],{"class":2239},[2225,70815,2805],{"class":2235},[2225,70817,70649],{"class":2252},[2225,70819,2805],{"class":2235},[2225,70821,2362],{"class":2239},[2225,70823,70824,70826,70829],{"class":2227,"line":7220},[2225,70825,35129],{"class":2231},[2225,70827,70828],{"class":2239}," (handle) ",[2225,70830,4200],{"class":2235},[2225,70832,70833,70835,70837,70839,70841,70843,70845,70847,70849,70851,70853,70855,70857,70859,70861,70863,70865],{"class":2227,"line":7225},[2225,70834,7561],{"class":2231},[2225,70836,70679],{"class":2291},[2225,70838,2789],{"class":2307},[2225,70840,36368],{"class":2239},[2225,70842,2298],{"class":2235},[2225,70844,70450],{"class":2291},[2225,70846,2789],{"class":2307},[2225,70848,2805],{"class":2235},[2225,70850,2854],{"class":2252},[2225,70852,2805],{"class":2235},[2225,70854,1381],{"class":2307},[2225,70856,2298],{"class":2235},[2225,70858,2784],{"class":2231},[2225,70860,70188],{"class":2291},[2225,70862,2789],{"class":2307},[2225,70864,2694],{"class":2239},[2225,70866,8606],{"class":2307},[2225,70868,70869,70871,70874],{"class":2227,"line":7231},[2225,70870,2359],{"class":2235},[2225,70872,70873],{"class":2231}," else",[2225,70875,2301],{"class":2235},[2225,70877,70878],{"class":2227,"line":7267},[2225,70879,70880],{"class":2763},"  \u002F\u002F No grant yet, or a browser without the API — fall back to tier 1.\n",[2225,70882,70883],{"class":2227,"line":7272},[2225,70884,7351],{"class":2235},[937,70886,70887,70888,70891,70892,2557,70895,70897,70898,70904,70905,70908],{},"The handle survives reload because ",[941,70889,70890],{},"rememberDirectory"," puts it in IndexedDB;\n",[941,70893,70894],{},"recallDirectory",[941,70896,6651],{}," when there is nothing stored, which is also what you get\nin a browser that never had the API. ",[950,70899,70900,70901,70903],{},"That ",[941,70902,6651],{}," is the whole degradation story"," — a\nmissing capability reads as \"no folder granted\", the app falls back to the download, and\nnothing throws. ",[941,70906,70907],{},"forgetDirectory"," drops the grant.",[937,70910,1649,70911,70913,70914,70916,70917,70919,70920,70922,70923,70925,70926,70929,70930,70935],{},[950,70912,47848],{}," Remembering it saves the\nuser re-picking the folder; it does not carry the permission across reloads. Check\n",[941,70915,47852],{}," and be ready to call ",[941,70918,47521],{}," from a fresh click — ",[941,70921,47859],{}," is\na normal state on a new page load, not an error. And ",[941,70924,47521],{}," is the only call\nthat prompts, so ",[950,70927,70928],{},"spend the gesture before any slow work",": a vault unlock's PBKDF2\noutlasts Chrome's ~5-second activation window, and asking afterwards means the prompt\nnever appears. The ",[1007,70931,70932,70934],{"href":355},[941,70933,70155],{}," page"," has the four access\nstates and the ordering in full.",[937,70937,70938],{},"Availability is narrow — the File System Access API is a Chromium-desktop feature, and\nnot a gap waiting to be filled: Safari's OPFS is origin-private and cannot see a mounted\nvolume at all. Treat tier 2 as progressive enhancement and never as the only path, or\nSafari and Firefox users have no backup.",[1022,70940,70942],{"id":70941},"backing-up-when-data-changes-not-when-a-clock-fires","Backing up when data changes, not when a clock fires",[937,70944,70945],{},"A wall-clock timer backs up an idle vault and misses a burst. Bind the change event\ninstead:",[2217,70947,70949],{"className":2219,"code":70948,"language":2221,"meta":1037,"style":1037},"let dirty = false\n\ndb.on('change', (event) => {\n  \u002F\u002F Another tab already wrote this. Let the tab that made the change own the backup.\n  if (event.remote) return\n  dirty = true\n})\n\nsetInterval(async () => {\n  if (!dirty) return\n  dirty = false\n  const handle = await recallDirectory('backups')\n  if (handle) await backupTo(handle, podFilename('notes'), await writePod(vault))\n}, 60_000)\n",[941,70950,70951,70963,70967,70995,71000,71017,71027,71033,71037,71051,71066,71074,71096,71140],{"__ignoreMap":1037},[2225,70952,70953,70955,70958,70960],{"class":2227,"line":2228},[2225,70954,46091],{"class":2774},[2225,70956,70957],{"class":2239}," dirty ",[2225,70959,2781],{"class":2235},[2225,70961,70962],{"class":2321}," false\n",[2225,70964,70965],{"class":2227,"line":1041},[2225,70966,2285],{"emptyLinePlaceholder":2284},[2225,70968,70969,70971,70973,70975,70977,70979,70981,70983,70985,70987,70989,70991,70993],{"class":2227,"line":1038},[2225,70970,6590],{"class":2239},[2225,70972,1520],{"class":2235},[2225,70974,4883],{"class":2291},[2225,70976,2789],{"class":2239},[2225,70978,2805],{"class":2235},[2225,70980,12111],{"class":2252},[2225,70982,2805],{"class":2235},[2225,70984,2298],{"class":2235},[2225,70986,953],{"class":2235},[2225,70988,12307],{"class":7052},[2225,70990,1381],{"class":2235},[2225,70992,8116],{"class":2774},[2225,70994,2301],{"class":2235},[2225,70996,70997],{"class":2227,"line":2288},[2225,70998,70999],{"class":2763},"  \u002F\u002F Another tab already wrote this. Let the tab that made the change own the backup.\n",[2225,71001,71002,71004,71006,71008,71010,71012,71014],{"class":2227,"line":2304},[2225,71003,7608],{"class":2231},[2225,71005,953],{"class":2307},[2225,71007,12307],{"class":2239},[2225,71009,1520],{"class":2235},[2225,71011,70066],{"class":2239},[2225,71013,8495],{"class":2307},[2225,71015,71016],{"class":2231},"return\n",[2225,71018,71019,71022,71024],{"class":2227,"line":2356},[2225,71020,71021],{"class":2239},"  dirty",[2225,71023,6346],{"class":2235},[2225,71025,71026],{"class":2321}," true\n",[2225,71028,71029,71031],{"class":2227,"line":2830},[2225,71030,2359],{"class":2235},[2225,71032,2362],{"class":2239},[2225,71034,71035],{"class":2227,"line":2861},[2225,71036,2285],{"emptyLinePlaceholder":2284},[2225,71038,71039,71041,71043,71045,71047,71049],{"class":2227,"line":4246},[2225,71040,70521],{"class":2291},[2225,71042,2789],{"class":2239},[2225,71044,70491],{"class":2774},[2225,71046,13130],{"class":2235},[2225,71048,8116],{"class":2774},[2225,71050,2301],{"class":2235},[2225,71052,71053,71055,71057,71059,71062,71064],{"class":2227,"line":4253},[2225,71054,7608],{"class":2231},[2225,71056,953],{"class":2307},[2225,71058,35134],{"class":2235},[2225,71060,71061],{"class":2239},"dirty",[2225,71063,8495],{"class":2307},[2225,71065,71016],{"class":2231},[2225,71067,71068,71070,71072],{"class":2227,"line":4259},[2225,71069,71021],{"class":2239},[2225,71071,6346],{"class":2235},[2225,71073,70962],{"class":2321},[2225,71075,71076,71078,71080,71082,71084,71086,71088,71090,71092,71094],{"class":2227,"line":6548},[2225,71077,8123],{"class":2774},[2225,71079,40913],{"class":2239},[2225,71081,6346],{"class":2235},[2225,71083,2784],{"class":2231},[2225,71085,47755],{"class":2291},[2225,71087,2789],{"class":2307},[2225,71089,2805],{"class":2235},[2225,71091,70649],{"class":2252},[2225,71093,2805],{"class":2235},[2225,71095,2362],{"class":2307},[2225,71097,71098,71100,71102,71104,71106,71108,71110,71112,71114,71116,71118,71120,71122,71124,71126,71128,71130,71132,71134,71136,71138],{"class":2227,"line":6576},[2225,71099,7608],{"class":2231},[2225,71101,953],{"class":2307},[2225,71103,36368],{"class":2239},[2225,71105,8495],{"class":2307},[2225,71107,6500],{"class":2231},[2225,71109,70679],{"class":2291},[2225,71111,2789],{"class":2307},[2225,71113,36368],{"class":2239},[2225,71115,2298],{"class":2235},[2225,71117,70450],{"class":2291},[2225,71119,2789],{"class":2307},[2225,71121,2805],{"class":2235},[2225,71123,2854],{"class":2252},[2225,71125,2805],{"class":2235},[2225,71127,1381],{"class":2307},[2225,71129,2298],{"class":2235},[2225,71131,2784],{"class":2231},[2225,71133,70188],{"class":2291},[2225,71135,2789],{"class":2307},[2225,71137,2694],{"class":2239},[2225,71139,8606],{"class":2307},[2225,71141,71142,71144,71146],{"class":2227,"line":6582},[2225,71143,33127],{"class":2235},[2225,71145,19001],{"class":6527},[2225,71147,2362],{"class":2239},[937,71149,71150],{},"Two details decide whether this works, and both are easy to get wrong:",[937,71152,46418,71153,1324,71160,71162,71163,71166,71167,10746,71169,968,71171,968,71173,997,71175,71178],{},[950,71154,71155,71156,11060,71158,1520],{},"Bind ",[941,71157,38144],{},[941,71159,38147],{},[941,71161,25042],{}," is a\nper-collection wrapper that ",[950,71164,71165],{},"hydrates the record on every event"," — on a lazy collection\nthat is a decrypt per change, paid to learn a fact you could have had for nothing. It also\nonly covers collections you had already subscribed to, so a collection created later is\nsilently unbacked. ",[941,71168,12114],{},[941,71170,2694],{},[941,71172,6471],{},[941,71174,6983],{},[941,71176,71177],{},"action"," —\neverything a \"something changed, back up\" trigger needs, with no record read.",[937,71180,46418,71181,71187,71188,71190,71191,71194,71195,71198],{},[950,71182,71183,71184,1520],{},"Honour ",[941,71185,71186],{},"event.remote"," It is ",[941,71189,11790],{}," when the change arrived by applying another\ntab's signal rather than from a write here. Ignore it and every open tab writes its own\npod for the same logical change: ",[950,71192,71193],{},"N tabs, N identical backups",", each a full vault write.\nThe debounce above does not save you — each tab debounces its own copy. Hub itself reads\nthis flag for echo suppression in ",[941,71196,71197],{},"ChangeBroadcast",", so you are following an established\nrule, not inventing one.",[1022,71200,71202],{"id":71201},"filenames-and-retention","Filenames and retention",[2217,71204,71206],{"className":2219,"code":71205,"language":2221,"meta":1037,"style":1037},"const podFilename = (vault: string) =>\n  `${vault}-${new Date().toISOString().replace(\u002F[-:]\u002Fg, '').replace(\u002F\\.\\d+Z$\u002F, 'Z')}.noydb`\n\u002F\u002F → notes-20260917T081500Z.noydb\n",[941,71207,71208,71230,71322],{"__ignoreMap":1037},[2225,71209,71210,71212,71215,71217,71219,71221,71223,71225,71227],{"class":2227,"line":2228},[2225,71211,2775],{"class":2774},[2225,71213,71214],{"class":2239}," podFilename ",[2225,71216,2781],{"class":2235},[2225,71218,953],{"class":2235},[2225,71220,2694],{"class":7052},[2225,71222,2311],{"class":2235},[2225,71224,6366],{"class":6342},[2225,71226,1381],{"class":2235},[2225,71228,71229],{"class":2774}," =>\n",[2225,71231,71232,71235,71237,71239,71242,71244,71246,71248,71250,71252,71254,71256,71258,71261,71263,71266,71269,71272,71275,71277,71279,71281,71283,71285,71287,71289,71292,71295,71298,71301,71304,71306,71308,71310,71312,71314,71316,71318,71320],{"class":2227,"line":1041},[2225,71233,71234],{"class":2235},"  `${",[2225,71236,2694],{"class":2239},[2225,71238,2359],{"class":2235},[2225,71240,71241],{"class":2252},"-",[2225,71243,8197],{"class":2235},[2225,71245,70371],{"class":2235},[2225,71247,19122],{"class":2291},[2225,71249,4228],{"class":2239},[2225,71251,1520],{"class":2235},[2225,71253,19129],{"class":2291},[2225,71255,4228],{"class":2239},[2225,71257,1520],{"class":2235},[2225,71259,71260],{"class":2291},"replace",[2225,71262,2789],{"class":2239},[2225,71264,71265],{"class":2235},"\u002F[",[2225,71267,71268],{"class":2252},"-:",[2225,71270,71271],{"class":2235},"]\u002F",[2225,71273,71274],{"class":6527},"g",[2225,71276,2298],{"class":2235},[2225,71278,12500],{"class":2235},[2225,71280,1381],{"class":2239},[2225,71282,1520],{"class":2235},[2225,71284,71260],{"class":2291},[2225,71286,2789],{"class":2239},[2225,71288,1625],{"class":2235},[2225,71290,71291],{"class":2239},"\\.",[2225,71293,71294],{"class":2252},"\\d",[2225,71296,71297],{"class":2235},"+",[2225,71299,71300],{"class":2252},"Z",[2225,71302,71303],{"class":2231},"$",[2225,71305,1625],{"class":2235},[2225,71307,2298],{"class":2235},[2225,71309,2249],{"class":2235},[2225,71311,71300],{"class":2252},[2225,71313,2805],{"class":2235},[2225,71315,1381],{"class":2239},[2225,71317,2359],{"class":2235},[2225,71319,1402],{"class":2252},[2225,71321,8659],{"class":2235},[2225,71323,71324],{"class":2227,"line":1038},[2225,71325,71326],{"class":2763},"\u002F\u002F → notes-20260917T081500Z.noydb\n",[937,71328,71329,71330,71333],{},"Compact UTC ISO-8601 makes ",[950,71331,71332],{},"lexical sort equal chronological sort",", so retention is a\nsort and a slice rather than date parsing:",[2217,71335,71337],{"className":2219,"code":71336,"language":2221,"meta":1037,"style":1037},"const keepLastN = (names: string[], n: number) => names.sort().slice(-n)\n",[941,71338,71339],{"__ignoreMap":1037},[2225,71340,71341,71343,71346,71348,71350,71353,71355,71357,71359,71361,71363,71365,71367,71369,71371,71374,71376,71379,71381,71383,71385,71387,71389],{"class":2227,"line":2228},[2225,71342,2775],{"class":2774},[2225,71344,71345],{"class":2239}," keepLastN ",[2225,71347,2781],{"class":2235},[2225,71349,953],{"class":2235},[2225,71351,71352],{"class":7052},"names",[2225,71354,2311],{"class":2235},[2225,71356,6366],{"class":6342},[2225,71358,7262],{"class":2239},[2225,71360,2298],{"class":2235},[2225,71362,27471],{"class":7052},[2225,71364,2311],{"class":2235},[2225,71366,6356],{"class":6342},[2225,71368,1381],{"class":2235},[2225,71370,8116],{"class":2774},[2225,71372,71373],{"class":2239}," names",[2225,71375,1520],{"class":2235},[2225,71377,71378],{"class":2291},"sort",[2225,71380,4228],{"class":2239},[2225,71382,1520],{"class":2235},[2225,71384,8733],{"class":2291},[2225,71386,2789],{"class":2239},[2225,71388,71241],{"class":2235},[2225,71390,71391],{"class":2239},"n)\n",[937,71393,71394],{},"Keep-last-N, not grandfather-father-son. A browser backup competes with the user's disk\nand their patience, and a rotation scheme nobody can explain gets turned off.",[1022,71396,71398],{"id":71397},"why-full-pods-not-incremental","Why full pods, not incremental",[937,71400,71401,71404,71405,71407,71408,71411],{},[941,71402,71403],{},"WritePodOptions"," has a ",[941,71406,7883],{}," option, and a nightly delta is tempting. Resist it for\nthis use case: ",[950,71409,71410],{},"incremental backups turn restore into a chain replay."," A full pod\nrestores by itself, from one file, with no ordering to reconstruct and no missing link to\ndiscover at the worst possible moment. The vault that fits in a browser is the vault\nwhere a full pod is cheap — the trade only starts to favour deltas at a size IndexedDB\nwas not going to hold anyway.",[1022,71413,71414],{"id":18051},"Verify",[937,71416,71417],{},"Prove the round trip, in a fresh context, before you trust any of it:",[2217,71419,71421],{"className":2219,"code":71420,"language":2221,"meta":1037,"style":1037},"const bytes = await writePod(vault)\n\u002F\u002F … clear site data, or open a different profile …\n\nconst restored = await readPod(bytes)\n",[941,71422,71423,71437,71442,71446],{"__ignoreMap":1037},[2225,71424,71425,71427,71429,71431,71433,71435],{"class":2227,"line":2228},[2225,71426,2775],{"class":2774},[2225,71428,32469],{"class":2239},[2225,71430,2781],{"class":2235},[2225,71432,2784],{"class":2231},[2225,71434,70188],{"class":2291},[2225,71436,45516],{"class":2239},[2225,71438,71439],{"class":2227,"line":1041},[2225,71440,71441],{"class":2763},"\u002F\u002F … clear site data, or open a different profile …\n",[2225,71443,71444],{"class":2227,"line":1038},[2225,71445,2285],{"emptyLinePlaceholder":2284},[2225,71447,71448,71450,71453,71455,71457,71459],{"class":2227,"line":2288},[2225,71449,2775],{"class":2774},[2225,71451,71452],{"class":2239}," restored ",[2225,71454,2781],{"class":2235},[2225,71456,2784],{"class":2231},[2225,71458,70193],{"class":2291},[2225,71460,71461],{"class":2239},"(bytes)\n",[937,71463,71464,22207,71466,71469,71470,71473,71474,71476],{},[941,71465,70321],{},[941,71467,71468],{},"PodReadResult",", not a vault — read its contents to confirm the\nrecords are there. ⭐ ",[950,71471,71472],{},"Do this once against a genuinely cleared profile."," A backup\nverified only by re-reading it in the tab that wrote it has tested your ",[941,71475,45820],{},", not\nyour durability story.",[1022,71478,2621],{"id":2620},[1111,71480,71481,71488,71495,71504],{},[1114,71482,71483,71487],{},[1007,71484,71485],{"href":359},[941,71486,9507],{}," — the live store, and its atomicity guarantee",[1114,71489,71490,71494],{},[1007,71491,71492],{"href":355},[941,71493,70155],{}," — the backup target's full API, its four access states, and the activation-window trap",[1114,71496,71497,1169,71499,71501,71502,53625],{},[1007,71498,629],{"href":630},[941,71500,1402],{}," container format, header disclosure, and ",[941,71503,70321],{},[1114,71505,71506,964,71508,71511],{},[1007,71507,727],{"href":728},[941,71509,71510],{},"db.on('change')","'s neighbour, for reacting in the UI rather than backing up",[2645,71513,71514],{},"html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sHdIc, html code.shiki .sHdIc{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#EEFFFF;--shiki-default-font-style:italic;--shiki-dark:#BABED8;--shiki-dark-font-style:italic}html pre.shiki code .sfNiH, html code.shiki .sfNiH{--shiki-light:#FF5370;--shiki-default:#FF9CAC;--shiki-dark:#FF9CAC}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}",{"title":1037,"searchDepth":1038,"depth":1038,"links":71516},[71517,71518,71519,71520,71521,71522,71523,71524,71525,71526],{"id":70092,"depth":1041,"text":70093},{"id":58275,"depth":1041,"text":58276},{"id":58393,"depth":1041,"text":58394},{"id":70330,"depth":1041,"text":70331},{"id":70529,"depth":1041,"text":70530},{"id":70941,"depth":1041,"text":70942},{"id":71201,"depth":1041,"text":71202},{"id":71397,"depth":1041,"text":71398},{"id":18051,"depth":1041,"text":71414},{"id":2620,"depth":1041,"text":2621},"A pure SPA that survives a cleared cache: to-browser-idb for the live vault, a .noydb pod for the backup, and two tiers of getting that pod to disk.",{},{"title":523,"description":71527},"OE2zfUT5qUbkrLP6ZY2G_Zte9uB-81KtDxF6ABfVZf8",{"id":71532,"title":527,"access":12,"apiToc":929,"body":71533,"category":1042,"description":72348,"extension":1044,"meta":72349,"navigation":1042,"nest":1042,"path":528,"seo":72350,"sinceVersion":1042,"status":1049,"stem":529,"subtitle":1042,"tier":1050,"tocExternal":1042,"__hash__":72351},"docs\u002Frecipes\u002Fcloud-store.md",{"type":931,"value":71534,"toc":72340},[71535,71538,71565,71578,71596,71598,71655,71659,71785,71801,71805,71818,71955,71962,72056,72063,72067,72072,72282,72295,72297,72300,72302,72337],[934,71536,527],{"id":71537},"production-cloud-store",[1061,71539,71540],{},[937,71541,71542,71544,71545,58223,71547,71549,71550,71552,71553,71555,71556,71558,71559,1625,71561,71564],{},[950,71543,1500],{}," a team moving a vault off the laptop — a real database backend, a server that unlocks without a human, and data placed on the right storage tier.\n",[950,71546,58222],{},[941,71548,51853],{}," (or any extended ",[941,71551,1779],{}," store) + ",[941,71554,33656],{}," (sealing) + ",[941,71557,45751],{}," (the mandated recovery) — ",[941,71560,4620],{},[941,71562,71563],{},"wrapStore"," are core exports.",[937,71566,58245,71567,71574,71575,71577],{},[1007,71568,71571],{"href":71569,"rel":71570},"https:\u002F\u002Fgithub.com\u002Fnoy-db\u002Fdocs\u002Fblob\u002Fmain\u002Fshowcases\u002Fsrc\u002Frecipe-cloud-store.recipe.test.ts",[3371],[941,71572,71573],{},"showcases\u002Fsrc\u002Frecipe-cloud-store.recipe.test.ts"," — the code below is the same code that runs there (the real published ",[941,71576,5355],{}," adapter over the same in-memory wire its conformance suite uses).",[937,71579,71580,71581,71584,71585,71588,71589,71591,71592,71595],{},"Three production concerns, one story: the ",[950,71582,71583],{},"backend"," (Postgres holds ciphertext rows and can't read one of them), the ",[950,71586,71587],{},"unlock"," (no secret typed on a server — ",[941,71590,1139],{}," sealing, with the recovery mandate that makes it safe), and ",[950,71593,71594],{},"placement"," (the audit log on its own backend, resilience middleware around everything).",[1022,71597,70093],{"id":70092},[1111,71599,71600,71608,71628,71642],{},[1114,71601,71602,1324,71605],{},[950,71603,71604],{},"Packages:",[941,71606,71607],{},"pnpm add @noy-db\u002Fhub @noy-db\u002Fto-postgres @noy-db\u002Fat-env @noy-db\u002Fon-shamir",[1114,71609,71610,964,71613,71615,71616,71619,71620,71624,71625,71627],{},[950,71611,71612],{},"A sealing key in the environment",[941,71614,33663],{},", 32 random bytes base64 (",[941,71617,71618],{},"openssl rand -base64 32","), injected by your secret manager. The ",[1007,71621,71622,9789],{"href":115},[941,71623,1139],{}," is deliberately ",[1092,71626,1004],{}," zero-knowledge: the host holding this key can decrypt what it unseals — that's its purpose.",[1114,71629,71630,71635,71636,71639,71640,1520],{},[950,71631,71632],{},[941,71633,71634],{},"shamirRecovery: shamirRecoveryProvider()"," — managed mode ",[950,71637,71638],{},"refuses"," a vault with no strong recovery enrolled (see below); the Shamir profile needs its provider passed to ",[941,71641,12780],{},[1114,71643,71644,71648,71649,71651,71652,71654],{},[950,71645,3289,71646],{},[941,71647,54620],{}," — any object with ",[941,71650,58402],{},"; hand it your ",[941,71653,51874],{}," pool.",[1022,71656,71658],{"id":71657},"the-backend-postgres-holds-ciphertext","The backend — Postgres holds ciphertext",[2217,71660,71662],{"className":2219,"code":71661,"language":2221,"meta":1037,"style":1037},"import { createNoydb } from '@noy-db\u002Fhub'\nimport { toPostgres } from '@noy-db\u002Fto-postgres'\nimport { Pool } from 'pg'\n\nconst pool = new Pool({ connectionString: process.env.DATABASE_URL })\nconst store = toPostgres({ client: pool })   \u002F\u002F autoMigrate: true — creates noydb_envelopes\n",[941,71663,71664,71682,71700,71718,71722,71757],{"__ignoreMap":1037},[2225,71665,71666,71668,71670,71672,71674,71676,71678,71680],{"class":2227,"line":2228},[2225,71667,2232],{"class":2231},[2225,71669,2236],{"class":2235},[2225,71671,2744],{"class":2239},[2225,71673,2243],{"class":2235},[2225,71675,2246],{"class":2231},[2225,71677,2249],{"class":2235},[2225,71679,1086],{"class":2252},[2225,71681,2256],{"class":2235},[2225,71683,71684,71686,71688,71690,71692,71694,71696,71698],{"class":2227,"line":1041},[2225,71685,2232],{"class":2231},[2225,71687,2236],{"class":2235},[2225,71689,58563],{"class":2239},[2225,71691,2243],{"class":2235},[2225,71693,2246],{"class":2231},[2225,71695,2249],{"class":2235},[2225,71697,51853],{"class":2252},[2225,71699,2256],{"class":2235},[2225,71701,71702,71704,71706,71708,71710,71712,71714,71716],{"class":2227,"line":1038},[2225,71703,2232],{"class":2231},[2225,71705,2236],{"class":2235},[2225,71707,58582],{"class":2239},[2225,71709,2243],{"class":2235},[2225,71711,2246],{"class":2231},[2225,71713,2249],{"class":2235},[2225,71715,51874],{"class":2252},[2225,71717,2256],{"class":2235},[2225,71719,71720],{"class":2227,"line":2288},[2225,71721,2285],{"emptyLinePlaceholder":2284},[2225,71723,71724,71726,71729,71731,71733,71735,71737,71739,71741,71743,71745,71747,71749,71751,71753,71755],{"class":2227,"line":2304},[2225,71725,2775],{"class":2774},[2225,71727,71728],{"class":2239}," pool ",[2225,71730,2781],{"class":2235},[2225,71732,8130],{"class":2235},[2225,71734,58582],{"class":2291},[2225,71736,2789],{"class":2239},[2225,71738,2792],{"class":2235},[2225,71740,54351],{"class":2307},[2225,71742,2311],{"class":2235},[2225,71744,36593],{"class":2239},[2225,71746,1520],{"class":2235},[2225,71748,183],{"class":2239},[2225,71750,1520],{"class":2235},[2225,71752,54364],{"class":2239},[2225,71754,2359],{"class":2235},[2225,71756,2362],{"class":2239},[2225,71758,71759,71761,71763,71765,71767,71769,71771,71773,71775,71777,71779,71782],{"class":2227,"line":2356},[2225,71760,2775],{"class":2774},[2225,71762,39676],{"class":2239},[2225,71764,2781],{"class":2235},[2225,71766,58563],{"class":2291},[2225,71768,2789],{"class":2239},[2225,71770,2792],{"class":2235},[2225,71772,25871],{"class":2307},[2225,71774,2311],{"class":2235},[2225,71776,71728],{"class":2239},[2225,71778,2359],{"class":2235},[2225,71780,71781],{"class":2239},")   ",[2225,71783,71784],{"class":2763},"\u002F\u002F autoMigrate: true — creates noydb_envelopes\n",[937,71786,71787,71788,968,71790,968,71792,71794,71795,71797,71798,71800],{},"One table, envelope rows, versioned writes. A DBA with full access sees ",[941,71789,6983],{},[941,71791,6471],{},[941,71793,8160],{},", and an ",[941,71796,1659],{}," column whose ",[941,71799,1146],{}," is ciphertext — the paired test asserts the dump contains none of the record content.",[1022,71802,71804],{"id":71803},"the-unlock-managed-secret-mandated-recovery","The unlock — managed secret, mandated recovery",[937,71806,71807,71808,71810,71811,71814,71815,71817],{},"A server can't type a secret. In ",[950,71809,18450],{}," the hub ",[1092,71812,71813],{},"generates"," one, seals it under your provider's key, and persists the sealed envelope inside the vault — mutually exclusive with ",[941,71816,10695],{}," by design:",[2217,71819,71821],{"className":2219,"code":71820,"language":2221,"meta":1037,"style":1037},"import { atEnv } from '@noy-db\u002Fat-env'\nimport { shamirRecoveryProvider } from '@noy-db\u002Fon-shamir'\n\nconst db = await createNoydb({\n  store,\n  user: 'server',\n  secretMode: 'managed',\n  sealingKey: atEnv(),          \u002F\u002F reads NOYDB_SEALING_KEY\n  shamirRecovery: shamirRecoveryProvider(),  \u002F\u002F backs the mandated recovery profile\n})\n",[941,71822,71823,71841,71860,71864,71880,71886,71901,71917,71933,71949],{"__ignoreMap":1037},[2225,71824,71825,71827,71829,71831,71833,71835,71837,71839],{"class":2227,"line":2228},[2225,71826,2232],{"class":2231},[2225,71828,2236],{"class":2235},[2225,71830,33720],{"class":2239},[2225,71832,2243],{"class":2235},[2225,71834,2246],{"class":2231},[2225,71836,2249],{"class":2235},[2225,71838,33656],{"class":2252},[2225,71840,2256],{"class":2235},[2225,71842,71843,71845,71847,71850,71852,71854,71856,71858],{"class":2227,"line":1041},[2225,71844,2232],{"class":2231},[2225,71846,2236],{"class":2235},[2225,71848,71849],{"class":2239}," shamirRecoveryProvider",[2225,71851,2243],{"class":2235},[2225,71853,2246],{"class":2231},[2225,71855,2249],{"class":2235},[2225,71857,45751],{"class":2252},[2225,71859,2256],{"class":2235},[2225,71861,71862],{"class":2227,"line":1038},[2225,71863,2285],{"emptyLinePlaceholder":2284},[2225,71865,71866,71868,71870,71872,71874,71876,71878],{"class":2227,"line":2288},[2225,71867,2775],{"class":2774},[2225,71869,2778],{"class":2239},[2225,71871,2781],{"class":2235},[2225,71873,2784],{"class":2231},[2225,71875,2744],{"class":2291},[2225,71877,2789],{"class":2239},[2225,71879,4200],{"class":2235},[2225,71881,71882,71884],{"class":2227,"line":2304},[2225,71883,15608],{"class":2239},[2225,71885,1707],{"class":2235},[2225,71887,71888,71890,71892,71894,71897,71899],{"class":2227,"line":2356},[2225,71889,4205],{"class":2307},[2225,71891,2311],{"class":2235},[2225,71893,2249],{"class":2235},[2225,71895,71896],{"class":2252},"server",[2225,71898,2805],{"class":2235},[2225,71900,1707],{"class":2235},[2225,71902,71903,71906,71908,71910,71913,71915],{"class":2227,"line":2830},[2225,71904,71905],{"class":2307},"  secretMode",[2225,71907,2311],{"class":2235},[2225,71909,2249],{"class":2235},[2225,71911,71912],{"class":2252},"managed",[2225,71914,2805],{"class":2235},[2225,71916,1707],{"class":2235},[2225,71918,71919,71922,71924,71926,71928,71930],{"class":2227,"line":2861},[2225,71920,71921],{"class":2307},"  sealingKey",[2225,71923,2311],{"class":2235},[2225,71925,33720],{"class":2291},[2225,71927,4228],{"class":2239},[2225,71929,2298],{"class":2235},[2225,71931,71932],{"class":2763},"          \u002F\u002F reads NOYDB_SEALING_KEY\n",[2225,71934,71935,71938,71940,71942,71944,71946],{"class":2227,"line":4246},[2225,71936,71937],{"class":2307},"  shamirRecovery",[2225,71939,2311],{"class":2235},[2225,71941,71849],{"class":2291},[2225,71943,4228],{"class":2239},[2225,71945,2298],{"class":2235},[2225,71947,71948],{"class":2763},"  \u002F\u002F backs the mandated recovery profile\n",[2225,71950,71951,71953],{"class":2227,"line":4253},[2225,71952,2359],{"class":2235},[2225,71954,2362],{"class":2239},[937,71956,71957,71958,71961],{},"And here is the part that surprises everyone the first time: ",[950,71959,71960],{},"managed mode refuses a vault with no strong recovery enrolled."," A sealed secret nobody can recover would turn sealing-key loss into total data loss, so the first open must enroll one:",[2217,71963,71965],{"className":2219,"code":71964,"language":2221,"meta":1037,"style":1037},"const { vault } = await db.team.openVaultAndEnrollRecovery('acme', {\n  recovery: [{ profile: 'shamir', k: 2, n: 3 }],  \u002F\u002F 3 shares, any 2 recover\n})\n",[941,71966,71967,72003,72050],{"__ignoreMap":1037},[2225,71968,71969,71971,71973,71975,71977,71979,71981,71983,71985,71987,71989,71991,71993,71995,71997,71999,72001],{"class":2227,"line":2228},[2225,71970,2775],{"class":2774},[2225,71972,2236],{"class":2235},[2225,71974,2835],{"class":2239},[2225,71976,2359],{"class":2235},[2225,71978,6346],{"class":2235},[2225,71980,2784],{"class":2231},[2225,71982,2842],{"class":2239},[2225,71984,1520],{"class":2235},[2225,71986,868],{"class":2239},[2225,71988,1520],{"class":2235},[2225,71990,19893],{"class":2291},[2225,71992,2789],{"class":2239},[2225,71994,2805],{"class":2235},[2225,71996,6450],{"class":2252},[2225,71998,2805],{"class":2235},[2225,72000,2298],{"class":2235},[2225,72002,2301],{"class":2235},[2225,72004,72005,72008,72010,72012,72014,72017,72019,72021,72023,72025,72027,72029,72031,72033,72035,72037,72039,72041,72043,72045,72047],{"class":2227,"line":1041},[2225,72006,72007],{"class":2307},"  recovery",[2225,72009,2311],{"class":2235},[2225,72011,8667],{"class":2239},[2225,72013,2792],{"class":2235},[2225,72015,72016],{"class":2307}," profile",[2225,72018,2311],{"class":2235},[2225,72020,2249],{"class":2235},[2225,72022,325],{"class":2252},[2225,72024,2805],{"class":2235},[2225,72026,2298],{"class":2235},[2225,72028,8700],{"class":2307},[2225,72030,2311],{"class":2235},[2225,72032,18961],{"class":6527},[2225,72034,2298],{"class":2235},[2225,72036,27471],{"class":2307},[2225,72038,2311],{"class":2235},[2225,72040,14460],{"class":6527},[2225,72042,2243],{"class":2235},[2225,72044,8803],{"class":2239},[2225,72046,2298],{"class":2235},[2225,72048,72049],{"class":2763},"  \u002F\u002F 3 shares, any 2 recover\n",[2225,72051,72052,72054],{"class":2227,"line":1038},[2225,72053,2359],{"class":2235},[2225,72055,2362],{"class":2239},[937,72057,72058,72059,72062],{},"Distribute the three Shamir shares to separate custodians. From then on, every process restart is just ",[941,72060,72061],{},"openVault('acme')"," — same store, same env key, no secret anywhere in your code or config. The paired test proves the round trip: boot #1 writes, boot #2 (a fresh instance) reads it back.",[1022,72064,72066],{"id":72065},"the-placement-hotcold-routing-resilience","The placement — hot\u002Fcold routing + resilience",[937,72068,72069,72071],{},[941,72070,48447],{}," is a core export — collections map to backends, and the result satisfies the same store contract as any single backend:",[2217,72073,72075],{"className":2219,"code":72074,"language":2221,"meta":1037,"style":1037},"import { routeStore, wrapStore, withRetry, withCircuitBreaker } from '@noy-db\u002Fhub'\nimport { s3Bundle } from '@noy-db\u002Fto-aws-s3'\n\nconst resilient = wrapStore(\n  routeStore({\n    default: store,                          \u002F\u002F Postgres — the hot path\n    routes: { auditLog: coldObjectStore },   \u002F\u002F the append-heavy log on cheap storage\n  }),\n  withRetry({ maxRetries: 3 }),\n  withCircuitBreaker({ failureThreshold: 5 }),\n)\n\nconst db = await createNoydb({ store: resilient, \u002F* …unlock as above *\u002F })\n",[941,72076,72077,72110,72129,72133,72146,72155,72168,72190,72198,72220,72242,72246,72250],{"__ignoreMap":1037},[2225,72078,72079,72081,72083,72085,72087,72090,72092,72095,72097,72100,72102,72104,72106,72108],{"class":2227,"line":2228},[2225,72080,2232],{"class":2231},[2225,72082,2236],{"class":2235},[2225,72084,48541],{"class":2239},[2225,72086,2298],{"class":2235},[2225,72088,72089],{"class":2239}," wrapStore",[2225,72091,2298],{"class":2235},[2225,72093,72094],{"class":2239}," withRetry",[2225,72096,2298],{"class":2235},[2225,72098,72099],{"class":2239}," withCircuitBreaker",[2225,72101,2243],{"class":2235},[2225,72103,2246],{"class":2231},[2225,72105,2249],{"class":2235},[2225,72107,1086],{"class":2252},[2225,72109,2256],{"class":2235},[2225,72111,72112,72114,72116,72119,72121,72123,72125,72127],{"class":2227,"line":1041},[2225,72113,2232],{"class":2231},[2225,72115,2236],{"class":2235},[2225,72117,72118],{"class":2239}," s3Bundle",[2225,72120,2243],{"class":2235},[2225,72122,2246],{"class":2231},[2225,72124,2249],{"class":2235},[2225,72126,29876],{"class":2252},[2225,72128,2256],{"class":2235},[2225,72130,72131],{"class":2227,"line":1038},[2225,72132,2285],{"emptyLinePlaceholder":2284},[2225,72134,72135,72137,72140,72142,72144],{"class":2227,"line":2288},[2225,72136,2775],{"class":2774},[2225,72138,72139],{"class":2239}," resilient ",[2225,72141,2781],{"class":2235},[2225,72143,72089],{"class":2291},[2225,72145,11683],{"class":2239},[2225,72147,72148,72151,72153],{"class":2227,"line":2304},[2225,72149,72150],{"class":2291},"  routeStore",[2225,72152,2789],{"class":2239},[2225,72154,4200],{"class":2235},[2225,72156,72157,72159,72161,72163,72165],{"class":2227,"line":2356},[2225,72158,65040],{"class":2307},[2225,72160,2311],{"class":2235},[2225,72162,7564],{"class":2239},[2225,72164,2298],{"class":2235},[2225,72166,72167],{"class":2763},"                          \u002F\u002F Postgres — the hot path\n",[2225,72169,72170,72173,72175,72177,72180,72182,72185,72187],{"class":2227,"line":2830},[2225,72171,72172],{"class":2307},"    routes",[2225,72174,2311],{"class":2235},[2225,72176,2236],{"class":2235},[2225,72178,72179],{"class":2307}," auditLog",[2225,72181,2311],{"class":2235},[2225,72183,72184],{"class":2239}," coldObjectStore ",[2225,72186,33127],{"class":2235},[2225,72188,72189],{"class":2763},"   \u002F\u002F the append-heavy log on cheap storage\n",[2225,72191,72192,72194,72196],{"class":2227,"line":2861},[2225,72193,19143],{"class":2235},[2225,72195,1381],{"class":2239},[2225,72197,1707],{"class":2235},[2225,72199,72200,72203,72205,72207,72210,72212,72214,72216,72218],{"class":2227,"line":4246},[2225,72201,72202],{"class":2291},"  withRetry",[2225,72204,2789],{"class":2239},[2225,72206,2792],{"class":2235},[2225,72208,72209],{"class":2307}," maxRetries",[2225,72211,2311],{"class":2235},[2225,72213,14460],{"class":6527},[2225,72215,2243],{"class":2235},[2225,72217,1381],{"class":2239},[2225,72219,1707],{"class":2235},[2225,72221,72222,72225,72227,72229,72232,72234,72236,72238,72240],{"class":2227,"line":4253},[2225,72223,72224],{"class":2291},"  withCircuitBreaker",[2225,72226,2789],{"class":2239},[2225,72228,2792],{"class":2235},[2225,72230,72231],{"class":2307}," failureThreshold",[2225,72233,2311],{"class":2235},[2225,72235,18996],{"class":6527},[2225,72237,2243],{"class":2235},[2225,72239,1381],{"class":2239},[2225,72241,1707],{"class":2235},[2225,72243,72244],{"class":2227,"line":4259},[2225,72245,2362],{"class":2239},[2225,72247,72248],{"class":2227,"line":6548},[2225,72249,2285],{"emptyLinePlaceholder":2284},[2225,72251,72252,72254,72256,72258,72260,72262,72264,72266,72268,72270,72273,72275,72278,72280],{"class":2227,"line":6576},[2225,72253,2775],{"class":2774},[2225,72255,2778],{"class":2239},[2225,72257,2781],{"class":2235},[2225,72259,2784],{"class":2231},[2225,72261,2744],{"class":2291},[2225,72263,2789],{"class":2239},[2225,72265,2792],{"class":2235},[2225,72267,7564],{"class":2307},[2225,72269,2311],{"class":2235},[2225,72271,72272],{"class":2239}," resilient",[2225,72274,2298],{"class":2235},[2225,72276,72277],{"class":2763}," \u002F* …unlock as above *\u002F",[2225,72279,2243],{"class":2235},[2225,72281,2362],{"class":2239},[937,72283,72284,72285,72287,72288,72290,72291,72294],{},"Placement is per collection and exact — the paired test asserts each envelope lands on exactly one backend. For aging records rather than whole collections, ",[1007,72286,605],{"href":606}," sweeps by policy; for period-based migration, ",[1007,72289,776],{"href":777},"' ",[941,72292,72293],{},"migratePeriod"," works on a cold route.",[1022,72296,71414],{"id":18051},[937,72298,72299],{},"Run the paired walkthrough: ciphertext-only rows (the DBA's view is asserted), the managed-mode recovery refusal path, the sealed secret surviving a restart, and exact hot\u002Fcold placement.",[1022,72301,2621],{"id":2620},[1111,72303,72304,72309,72317,72332],{},[1114,72305,72306,72308],{},[1007,72307,455],{"href":456}," — this recipe is the \"server-only behind my API\" topology, made concrete.",[1114,72310,72311,1368,72313,72316],{},[1007,72312,860],{"href":861},[1007,72314,72315],{"href":115},"to-* catalog"," — picking the backend.",[1114,72318,72319,72321,72322,968,72325,968,72327,968,72330,957],{},[1007,72320,808],{"href":809}," — the full middleware set (",[941,72323,72324],{},"health-check",[941,72326,6840],{},[941,72328,72329],{},"logging",[941,72331,751],{},[1114,72333,72334,72336],{},[1007,72335,625],{"href":626}," — short-lived cloud credentials when the store is AWS and the client shouldn't hold keys.",[2645,72338,72339],{},"html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}",{"title":1037,"searchDepth":1038,"depth":1038,"links":72341},[72342,72343,72344,72345,72346,72347],{"id":70092,"depth":1041,"text":70093},{"id":71657,"depth":1041,"text":71658},{"id":71803,"depth":1041,"text":71804},{"id":72065,"depth":1041,"text":72066},{"id":18051,"depth":1041,"text":71414},{"id":2620,"depth":1041,"text":2621},"A Postgres-backed vault with unattended server unlock: at-env managed secret, the mandated Shamir recovery, and hot\u002Fcold placement via routeStore.",{},{"title":527,"description":72348},"E1-GkGMllfPDxoivQ69Aidq23K-R2L0o6nwa3FUkhn0",{"id":72353,"title":531,"access":12,"apiToc":929,"body":72354,"category":1042,"description":73716,"extension":1044,"meta":73717,"navigation":1042,"nest":1042,"path":532,"seo":73718,"sinceVersion":1042,"status":1049,"stem":533,"subtitle":1042,"tier":1050,"tocExternal":1042,"__hash__":73719},"docs\u002Frecipes\u002Fcompose-your-own.md",{"type":931,"value":72355,"toc":73704},[72356,72359,72373,72376,72380,72384,72389,72505,72509,72514,72648,72652,72657,72777,72781,72786,72837,72841,72850,73420,73424,73429,73657,73661,73685,73687],[934,72357,531],{"id":72358},"compose-your-own-setup",[937,72360,72361,72362,72365,72366,72369,72370,72372],{},"When no ",[1007,72363,72364],{"href":500},"recipe"," matches your use case, compose your own: every noy-db build is the same four decisions, and this page lists every option each decision offers, with exactly what it adds to your ",[941,72367,72368],{},"createNoydb({...})",". It is generated from the same data as the interactive ",[1007,72371,1019],{"href":1018}," — use that for a guided version that writes the code block for you; use this page to read, search, or diff the whole option space at once.",[937,72374,72375],{},"The floor — an always-on kernel of ~6,500 LOC — gives you encrypted typed records, queries, and the in-memory store with zero opt-ins. Everything below is added only when you choose it, and tree-shakes away when you don't.",[1022,72377,72379],{"id":72378},"the-four-decisions","The four decisions",[1591,72381,72383],{"id":72382},"d1-architecture-persistence","D1 — Architecture & persistence",[937,72385,72386],{},[1092,72387,72388],{},"where data lives.",[1338,72390,72391,72403],{},[1341,72392,72393],{},[1344,72394,72395,72397,72400],{},[1347,72396,70107],{},[1347,72398,72399],{},"What to add",[1347,72401,72402],{},"Depth",[1357,72404,72405,72415,72425,72435,72445,72455,72465,72475,72485,72495],{},[1344,72406,72407,72410,72413],{},[1362,72408,72409],{},"In-memory (dev \u002F test)",[1362,72411,72412],{},"kernel built-in default",[1362,72414,6922],{},[1344,72416,72417,72420,72423],{},[1362,72418,72419],{},"Local — file \u002F browser-IDB",[1362,72421,72422],{},"family to-file \u002F to-browser-idb",[1362,72424,6922],{},[1344,72426,72427,72430,72433],{},[1362,72428,72429],{},"Pod — data at rest (.noydb backup \u002F transport)",[1362,72431,72432],{},"pod layer",[1362,72434,1050],{},[1344,72436,72437,72440,72443],{},[1362,72438,72439],{},"Snapshots — checkpoint \u002F restore + retention",[1362,72441,72442],{},"snapshots service",[1362,72444,1050],{},[1344,72446,72447,72450,72453],{},[1362,72448,72449],{},"Multi-tab (same browser)",[1362,72451,72452],{},"family by-tabs",[1362,72454,1050],{},[1344,72456,72457,72460,72463],{},[1362,72458,72459],{},"Same-room P2P (same network)",[1362,72461,72462],{},"sync + family by-peer",[1362,72464,1050],{},[1344,72466,72467,72470,72473],{},[1362,72468,72469],{},"Multiple stores \u002F routing",[1362,72471,72472],{},"routing service",[1362,72474,1473],{},[1344,72476,72477,72480,72483],{},[1362,72478,72479],{},"Blob attachments",[1362,72481,72482],{},"blobs service",[1362,72484,1050],{},[1344,72486,72487,72490,72493],{},[1362,72488,72489],{},"Cloud + attestation",[1362,72491,72492],{},"to-aws-* + at-* sealing + attestation",[1362,72494,1473],{},[1344,72496,72497,72500,72503],{},[1362,72498,72499],{},"Fleet \u002F multi-vault",[1362,72501,72502],{},"cargo + pod → klum-db",[1362,72504,1473],{},[1591,72506,72508],{"id":72507},"d2-encryption-identity-access","D2 — Encryption, identity & access",[937,72510,72511],{},[1092,72512,72513],{},"who unlocks, who reads what.",[1338,72515,72516,72526],{},[1341,72517,72518],{},[1344,72519,72520,72522,72524],{},[1347,72521,70107],{},[1347,72523,72399],{},[1347,72525,72402],{},[1357,72527,72528,72538,72548,72558,72568,72578,72588,72598,72608,72618,72628,72638],{},[1344,72529,72530,72533,72536],{},[1362,72531,72532],{},"Encrypt \u002F decrypt at rest",[1362,72534,72535],{},"kernel enclave — always-on",[1362,72537,6922],{},[1344,72539,72540,72543,72546],{},[1362,72541,72542],{},"3-tier unlock — secret · authenticate · unlock",[1362,72544,72545],{},"session + on-webauthn \u002F on-oidc \u002F on-pin",[1362,72547,1050],{},[1344,72549,72550,72553,72556],{},[1362,72551,72552],{},"Mixed step-up (policy gates + 2FA factors)",[1362,72554,72555],{},"tiers + on-totp \u002F on-shamir",[1362,72557,1473],{},[1344,72559,72560,72563,72566],{},[1362,72561,72562],{},"Public directory — readable label before unlock",[1362,72564,72565],{},"public-envelope service",[1362,72567,1050],{},[1344,72569,72570,72573,72576],{},[1362,72571,72572],{},"Recover credentials — paper \u002F Shamir",[1362,72574,72575],{},"on-recovery \u002F on-shamir",[1362,72577,1050],{},[1344,72579,72580,72583,72586],{},[1362,72581,72582],{},"Sensitive fields — verify-without-reveal, blind index",[1362,72584,72585],{},"classified service",[1362,72587,1050],{},[1344,72589,72590,72593,72596],{},[1362,72591,72592],{},"Multiple users",[1362,72594,72595],{},"team service (withTeam())",[1362,72597,1050],{},[1344,72599,72600,72603,72606],{},[1362,72601,72602],{},"Roles · grant \u002F revoke \u002F rotate (DEK\u002FKEK)",[1362,72604,72605],{},"team service",[1362,72607,1050],{},[1344,72609,72610,72613,72616],{},[1362,72611,72612],{},"Multiple owners (co-ownership \u002F deed)",[1362,72614,72615],{},"team + createDeedOwner (core)",[1362,72617,1473],{},[1344,72619,72620,72623,72626],{},[1362,72621,72622],{},"Delegation (magic-link grant)",[1362,72624,72625],{},"team \u002F magic-link",[1362,72627,1473],{},[1344,72629,72630,72633,72636],{},[1362,72631,72632],{},"Export",[1362,72634,72635],{},"family as-* + portability",[1362,72637,1050],{},[1344,72639,72640,72643,72646],{},[1362,72641,72642],{},"Audited access \u002F export of sensitive data",[1362,72644,72645],{},"classified + consent + history",[1362,72647,1473],{},[1591,72649,72651],{"id":72650},"d3-data-query","D3 — Data & query",[937,72653,72654],{},[1092,72655,72656],{},"shape and operations.",[1338,72658,72659,72669],{},[1341,72660,72661],{},[1344,72662,72663,72665,72667],{},[1347,72664,70107],{},[1347,72666,72399],{},[1347,72668,72402],{},[1357,72670,72671,72681,72691,72701,72710,72719,72729,72739,72748,72758,72767],{},[1344,72672,72673,72676,72679],{},[1362,72674,72675],{},"Typed records + refs",[1362,72677,72678],{},"kernel schema — free",[1362,72680,6922],{},[1344,72682,72683,72686,72689],{},[1362,72684,72685],{},"Field metadata \u002F describe()",[1362,72687,72688],{},"kernel \u002Fui port",[1362,72690,1050],{},[1344,72692,72693,72696,72699],{},[1362,72694,72695],{},"Joins · cross-join",[1362,72697,72698],{},"joins (+ indexing)",[1362,72700,1050],{},[1344,72702,72703,72706,72708],{},[1362,72704,72705],{},"Aggregate \u002F groupBy",[1362,72707,24896],{},[1362,72709,1050],{},[1344,72711,72712,72715,72717],{},[1362,72713,72714],{},"Index",[1362,72716,27526],{},[1362,72718,1050],{},[1344,72720,72721,72724,72727],{},[1362,72722,72723],{},"Search \u002F full-text (BM25 + lexical)",[1362,72725,72726],{},"search service",[1362,72728,1050],{},[1344,72730,72731,72734,72737],{},[1362,72732,72733],{},"Versioning \u002F time-machine",[1362,72735,72736],{},"history service",[1362,72738,1050],{},[1344,72740,72741,72744,72746],{},[1362,72742,72743],{},"Live \u002F reactive",[1362,72745,25123],{},[1362,72747,1050],{},[1344,72749,72750,72753,72756],{},[1362,72751,72752],{},"i18n \u002F multi-locale",[1362,72754,72755],{},"i18n service",[1362,72757,1050],{},[1344,72759,72760,72762,72765],{},[1362,72761,25699],{},[1362,72763,72764],{},"crdt service",[1362,72766,1473],{},[1344,72768,72769,72772,72775],{},[1362,72770,72771],{},"Derived \u002F materialized \u002F overlay views",[1362,72773,72774],{},"derivations · materialized-views · overlay-views",[1362,72776,1473],{},[1591,72778,72780],{"id":72779},"d4-schema-evolution-operations","D4 — Schema evolution & operations",[937,72782,72783],{},[1092,72784,72785],{},"lifecycle — mostly planned.",[1338,72787,72788,72798],{},[1341,72789,72790],{},[1344,72791,72792,72794,72796],{},[1347,72793,70107],{},[1347,72795,72399],{},[1347,72797,72402],{},[1357,72799,72800,72813,72825],{},[1344,72801,72802,72805,72808],{},[1362,72803,72804],{},"Schema update — field rename + backfill",[1362,72806,72807],{},"@noy-db\u002Fhub\u002Fmigrations",[1362,72809,72810],{},[1092,72811,72812],{},"(planned)",[1344,72814,72815,72818,72821],{},[1362,72816,72817],{},"Migration between hub versions",[1362,72819,72820],{},"migrations + peer-range policy",[1362,72822,72823],{},[1092,72824,72812],{},[1344,72826,72827,72830,72833],{},[1362,72828,72829],{},"Coordinated cut-over (multi-consumer)",[1362,72831,72832],{},"migration recipe + minVersion guard",[1362,72834,72835],{},[1092,72836,72812],{},[1022,72838,72840],{"id":72839},"the-service-catalog","The service catalog",[937,72842,72843,72844,72846,72847,72849],{},"Each service is one ",[941,72845,943],{}," factory passed to ",[941,72848,12780],{}," under its option key. Its page states its prerequisites; its LOC is the approximate bundle cost you accept by opting in.",[1338,72851,72852,72866],{},[1341,72853,72854],{},[1344,72855,72856,72858,72861,72864],{},[1347,72857,21181],{},[1347,72859,72860],{},"Opt-in",[1347,72862,72863],{},"Adds ~LOC",[1347,72865,29857],{},[1357,72867,72868,72889,72915,72934,72959,72983,73003,73025,73048,73071,73094,73115,73136,73158,73179,73200,73223,73245,73266,73287,73308,73328,73352,73374,73397],{},[1344,72869,72870,72874,72883,72886],{},[1362,72871,72872],{},[941,72873,705],{},[1362,72875,72876,1746,72878,953,72881,1381],{},[941,72877,6766],{},[941,72879,72880],{},"indexingStrategy",[941,72882,4174],{},[1362,72884,72885],{},"~886",[1362,72887,72888],{},"Eager + lazy persisted indexes (equality + orderBy).",[1344,72890,72891,72895,72906,72909],{},[1362,72892,72893],{},[941,72894,715],{},[1362,72896,72897,1746,72900,953,72903,1381],{},[941,72898,72899],{},"withJoins()",[941,72901,72902],{},"joinsStrategy",[941,72904,72905],{},"@noy-db\u002Fhub\u002Fjoins",[1362,72907,72908],{},"~470",[1362,72910,72911,72912],{},"Multi-FK joins — indexed nested-loop \u002F hash strategy. ",[1092,72913,72914],{},"(always-core today; subpath reserved)",[1344,72916,72917,72921,72930,72932],{},[1362,72918,72919],{},[941,72920,804],{},[1362,72922,72923,1746,72925,953,72928,1381],{},[941,72924,21926],{},[941,72926,72927],{},"reduceStrategy",[941,72929,27257],{},[1362,72931,72885],{},[1362,72933],{},[1344,72935,72936,72940,72950,72953],{},[1362,72937,72938],{},[941,72939,4457],{},[1362,72941,72942,1746,72944,953,72947,1381],{},[941,72943,22027],{},[941,72945,72946],{},"searchStrategy",[941,72948,72949],{},"@noy-db\u002Fhub\u002Fsearch",[1362,72951,72952],{},"~400",[1362,72954,72955,72956],{},"Full-text — BM25 scan (L0) + client lexical index (L1). ",[1092,72957,72958],{},"(preview)",[1344,72960,72961,72965,72976,72979],{},[1362,72962,72963],{},[941,72964,727],{},[1362,72966,72967,1746,72970,953,72973,1381],{},[941,72968,72969],{},"withLive()",[941,72971,72972],{},"liveStrategy",[941,72974,72975],{},"@noy-db\u002Fhub\u002Flive",[1362,72977,72978],{},"~210",[1362,72980,72981],{},[1092,72982,72914],{},[1344,72984,72985,72989,72997,73000],{},[1362,72986,72987],{},[941,72988,697],{},[1362,72990,72991,1746,72993,953,72995,1381],{},[941,72992,6719],{},[941,72994,20172],{},[941,72996,4151],{},[1362,72998,72999],{},"~1880",[1362,73001,73002],{},"Versioning, diff, revert, time-machine, audit ledger.",[1344,73004,73005,73009,73019,73022],{},[1362,73006,73007],{},[941,73008,653],{},[1362,73010,73011,1746,73013,953,73016,1381],{},[941,73012,21950],{},[941,73014,73015],{},"crdtStrategy",[941,73017,73018],{},"@noy-db\u002Fhub\u002Fcrdt",[1362,73020,73021],{},"~221",[1362,73023,73024],{},"LWW-Map \u002F RGA \u002F Yjs interop.",[1344,73026,73027,73031,73042,73045],{},[1362,73028,73029],{},[941,73030,665],{},[1362,73032,73033,1746,73036,953,73039,1381],{},[941,73034,73035],{},"withDerivations()",[941,73037,73038],{},"derivationsStrategy",[941,73040,73041],{},"@noy-db\u002Fhub\u002Fderivations",[1362,73043,73044],{},"~550",[1362,73046,73047],{},"Deterministic derived data — source row → typed outputs.",[1344,73049,73050,73054,73065,73068],{},[1362,73051,73052],{},[941,73053,739],{},[1362,73055,73056,1746,73059,953,73062,1381],{},[941,73057,73058],{},"withMaterializedViews()",[941,73060,73061],{},"materializedViewsStrategy",[941,73063,73064],{},"@noy-db\u002Fhub\u002Fmaterialized-views",[1362,73066,73067],{},"~1400",[1362,73069,73070],{},"Query-level materialized views with refresh modes.",[1344,73072,73073,73077,73088,73091],{},[1362,73074,73075],{},[941,73076,768],{},[1362,73078,73079,1746,73082,953,73085,1381],{},[941,73080,73081],{},"withOverlayViews()",[941,73083,73084],{},"overlayViewsStrategy",[941,73086,73087],{},"@noy-db\u002Fhub\u002Foverlay-views",[1362,73089,73090],{},"~600",[1362,73092,73093],{},"Operator-editable overlay over a materialized view.",[1344,73095,73096,73100,73109,73112],{},[1362,73097,73098],{},[941,73099,621],{},[1362,73101,73102,1746,73104,953,73107,1381],{},[941,73103,6775],{},[941,73105,73106],{},"blobsStrategy",[941,73108,58494],{},[1362,73110,73111],{},"~2376",[1362,73113,73114],{},"Binary attachments + compaction + MIME-magic.",[1344,73116,73117,73121,73130,73133],{},[1362,73118,73119],{},[941,73120,701],{},[1362,73122,73123,1746,73125,953,73128,1381],{},[941,73124,6739],{},[941,73126,73127],{},"i18nStrategy",[941,73129,58514],{},[1362,73131,73132],{},"~854",[1362,73134,73135],{},"Multi-locale records + dict-key resolution.",[1344,73137,73138,73142,73152,73155],{},[1362,73139,73140],{},[941,73141,633],{},[1362,73143,73144,1746,73146,953,73149,1381],{},[941,73145,22007],{},[941,73147,73148],{},"classifiedStrategy",[941,73150,73151],{},"@noy-db\u002Fhub\u002Fclassified",[1362,73153,73154],{},"~1150",[1362,73156,73157],{},"Sensitive fields — verify-without-reveal, blind index.",[1344,73159,73160,73164,73173,73176],{},[1362,73161,73162],{},[941,73163,776],{},[1362,73165,73166,1746,73168,953,73171,1381],{},[941,73167,6757],{},[941,73169,73170],{},"periodsStrategy",[941,73172,58474],{},[1362,73174,73175],{},"~334",[1362,73177,73178],{},"Accounting periods + closed-period write guard.",[1344,73180,73181,73185,73194,73197],{},[1362,73182,73183],{},[941,73184,641],{},[1362,73186,73187,1746,73189,953,73192,1381],{},[941,73188,6748],{},[941,73190,73191],{},"consentStrategy",[941,73193,58534],{},[1362,73195,73196],{},"~194",[1362,73198,73199],{},"Consent audit log (GDPR \u002F PIPL-friendly).",[1344,73201,73202,73206,73217,73220],{},[1362,73203,73204],{},[941,73205,693],{},[1362,73207,73208,1746,73211,953,73214,1381],{},[941,73209,73210],{},"withGuards()",[941,73212,73213],{},"guardsStrategy",[941,73215,73216],{},"@noy-db\u002Fhub\u002Fguards",[1362,73218,73219],{},"~700",[1362,73221,73222],{},"Record lock + field freeze + role-gated amendment.",[1344,73224,73225,73229,73239,73242],{},[1362,73226,73227],{},[941,73228,800],{},[1362,73230,73231,1746,73233,953,73236,1381],{},[941,73232,21604],{},[941,73234,73235],{},"portabilityStrategy",[941,73237,73238],{},"@noy-db\u002Fhub\u002Fportability",[1362,73240,73241],{},"~300",[1362,73243,73244],{},"Client data portability + public-envelope label.",[1344,73246,73247,73251,73260,73263],{},[1362,73248,73249],{},[941,73250,609],{},[1362,73252,73253,1746,73255,953,73258,1381],{},[941,73254,21543],{},[941,73256,73257],{},"attestationStrategy",[941,73259,65350],{},[1362,73261,73262],{},"~500",[1362,73264,73265],{},"Signed attestation \u002F verification of records.",[1344,73267,73268,73272,73281,73284],{},[1362,73269,73270],{},[941,73271,868],{},[1362,73273,73274,1746,73276,953,73279,1381],{},[941,73275,3197],{},[941,73277,73278],{},"teamStrategy",[941,73280,4680],{},[1362,73282,73283],{},"~1000",[1362,73285,73286],{},"Multi-user grant \u002F revoke \u002F rotate + magic-link + delegation.",[1344,73288,73289,73293,73302,73305],{},[1362,73290,73291],{},[941,73292,840],{},[1362,73294,73295,1746,73297,953,73300,1381],{},[941,73296,21321],{},[941,73298,73299],{},"sessionStrategy",[941,73301,62043],{},[1362,73303,73304],{},"~839",[1362,73306,73307],{},"Token sessions + dev-unlock + policy enforcement.",[1344,73309,73310,73314,73322,73325],{},[1362,73311,73312],{},[941,73313,864],{},[1362,73315,73316,1746,73318,953,73320,1381],{},[941,73317,4676],{},[941,73319,20175],{},[941,73321,4672],{},[1362,73323,73324],{},"~856",[1362,73326,73327],{},"P2P replication engine + presence.",[1344,73329,73330,73334,73344,73346],{},[1362,73331,73332],{},[941,73333,872],{},[1362,73335,73336,1746,73338,953,73341,1381],{},[941,73337,21976],{},[941,73339,73340],{},"tiersStrategy",[941,73342,73343],{},"@noy-db\u002Fhub\u002Ftiers",[1362,73345,3610],{},[1362,73347,73348,73349],{},"Three-tier auth + policy gates (design). ",[1092,73350,73351],{},"(design)",[1344,73353,73354,73358,73368,73371],{},[1362,73355,73356],{},[941,73357,848],{},[1362,73359,73360,1746,73362,953,73365,1381],{},[941,73361,21500],{},[941,73363,73364],{},"shadowStrategy",[941,73366,73367],{},"@noy-db\u002Fhub\u002Fshadow",[1362,73369,73370],{},"~129",[1362,73372,73373],{},"Read-only vault.frame() views.",[1344,73375,73376,73380,73391,73394],{},[1362,73377,73378],{},[941,73379,856],{},[1362,73381,73382,1746,73385,953,73388,1381],{},[941,73383,73384],{},"withSnapshots()",[941,73386,73387],{},"snapshotsStrategy",[941,73389,73390],{},"@noy-db\u002Fhub\u002Fsnapshots",[1362,73392,73393],{},"~200",[1362,73395,73396],{},"Vault checkpoint \u002F restore + retention.",[1344,73398,73399,73403,73414,73417],{},[1362,73400,73401],{},[941,73402,808],{},[1362,73404,73405,17904,73407,73410,73411,73413],{},[941,73406,48447],{},[941,73408,73409],{},"@noy-db\u002Fhub\u002Fstore"," — wraps a store, not a ",[941,73412,12780],{}," option",[1362,73415,73416],{},"~1800",[1362,73418,73419],{},"Multi-store routing + middleware + sync-policy.",[1022,73421,73423],{"id":73422},"the-six-families","The six families",[937,73425,73426,73427,6255],{},"Satellite packages named by their relationship to the trust boundary — each binds exactly one port subpath and nothing else (see ",[1007,73428,4824],{"href":115},[1338,73430,73431,73445],{},[1341,73432,73433],{},[1344,73434,73435,73437,73440,73442],{},[1347,73436,43495],{},[1347,73438,73439],{},"Port",[1347,73441,7834],{},[1347,73443,73444],{},"Members",[1357,73446,73447,73502,73537,73564,73605,73624],{},[1344,73448,73449,73453,73457,73460],{},[1362,73450,73451],{},[941,73452,1779],{},[1362,73454,73455],{},[941,73456,3328],{},[1362,73458,73459],{},"Storage destinations — the only layer that touches ciphertext on the wire.",[1362,73461,73462,1324,73464,1324,73466,1368,73468,1324,73470,1324,73472,1324,73474,1368,73476,1324,73478,1324,73480,1324,73482,1324,73484,1368,73486,1324,73488,1324,73490,1324,73492,1368,73494,1324,73496,1368,73498,1324,73500],{},[941,73463,383],{},[941,73465,358],{},[941,73467,362],{},[941,73469,129],{},[941,73471,347],{},[941,73473,375],{},[941,73475,371],{},[941,73477,407],{},[941,73479,399],{},[941,73481,422],{},[941,73483,439],{},[941,73485,435],{},[941,73487,443],{},[941,73489,426],{},[941,73491,418],{},[941,73493,403],{},[941,73495,387],{},[941,73497,379],{},[941,73499,411],{},[941,73501,395],{},[1344,73503,73504,73508,73512,73515],{},[1362,73505,73506],{},[941,73507,1787],{},[1362,73509,73510],{},[941,73511,3333],{},[1362,73513,73514],{},"Unlock \u002F auth methods — how a user proves identity to open the vault.",[1362,73516,73517,1324,73519,1368,73521,1368,73523,1324,73525,1368,73527,1324,73529,1324,73531,1368,73533,1368,73535],{},[941,73518,337],{},[941,73520,309],{},[941,73522,305],{},[941,73524,321],{},[941,73526,325],{},[941,73528,333],{},[941,73530,301],{},[941,73532,313],{},[941,73534,317],{},[941,73536,329],{},[1344,73538,73539,73543,73547,73550],{},[1362,73540,73541],{},[941,73542,1139],{},[1362,73544,73545],{},[941,73546,3336],{},[1362,73548,73549],{},"Sealing-key providers — the one non-zero-knowledge family: a host you control can decrypt the scoped slice it unseals.",[1362,73551,73552,1324,73554,1368,73556,1324,73558,1324,73561],{},[941,73553,183],{},[941,73555,191],{},[941,73557,49315],{},[941,73559,73560],{},"gcp",[941,73562,73563],{},"azure",[1344,73565,73566,73570,73574,73577],{},[1362,73567,73568],{},[941,73569,1783],{},[1362,73571,73572],{},[941,73573,3345],{},[1362,73575,73576],{},"Framework integrations — reactive glue between a vault and your UI layer.",[1362,73578,73579,1324,73581,1324,73583,1324,73585,1324,73587,1324,73589,1368,73591,1324,73593,1368,73595,1324,73597,1324,73599,1368,73601,1368,73603],{},[941,73580,283],{},[941,73582,255],{},[941,73584,267],{},[941,73586,271],{},[941,73588,243],{},[941,73590,239],{},[941,73592,247],{},[941,73594,291],{},[941,73596,275],{},[941,73598,279],{},[941,73600,263],{},[941,73602,287],{},[941,73604,223],{},[1344,73606,73607,73611,73615,73618],{},[1362,73608,73609],{},[941,73610,1795],{},[1362,73612,73613],{},[941,73614,3342],{},[1362,73616,73617],{},"Session-share transports — move an unlocked session between contexts.",[1362,73619,73620,1368,73622],{},[941,73621,201],{},[941,73623,205],{},[1344,73625,73626,73630,73634,73637],{},[1362,73627,73628],{},[941,73629,1791],{},[1362,73631,73632],{},[941,73633,3339],{},[1362,73635,73636],{},"Import \u002F export data slices — portable artefacts in and out of a vault.",[1362,73638,73639,1324,73641,1368,73643,1324,73645,1324,73647,1368,73649,1368,73651,1324,73653,1368,73655],{},[941,73640,137],{},[941,73642,157],{},[941,73644,141],{},[941,73646,145],{},[941,73648,161],{},[941,73650,153],{},[941,73652,133],{},[941,73654,165],{},[941,73656,149],{},[1022,73658,73660],{"id":73659},"worked-compositions","Worked compositions",[937,73662,73663,73664,73666,73667,73669,73670,73673,73674,73677,73678,73681,73682,73684],{},"The recipes are this page's worked examples, smallest to most opted-in: ",[1007,73665,56352],{"href":560}," (core only) → ",[1007,73668,568],{"href":569}," (a framework binding + a store) → ",[1007,73671,73672],{"href":590},"unlock ceremonies"," (on-* + session) → ",[1007,73675,73676],{"href":573},"collaborative app"," (crdt + sync + team) → ",[1007,73679,73680],{"href":508},"accounting app"," (six services composed). Each shows the resulting ",[941,73683,72368],{}," block in its Setup section.",[1022,73686,2621],{"id":2620},[1111,73688,73689,73694,73699],{},[1114,73690,73691,73693],{},[1007,73692,1019],{"href":1018}," — the interactive form of this page: tick capabilities, watch the code write itself.",[1114,73695,73696,73698],{},[1007,73697,597],{"href":598}," — every catalog entry's full page, prerequisites included.",[1114,73700,73701,73703],{},[1007,73702,499],{"href":500}," — start from the closest worked composition and add what's missing.",{"title":1037,"searchDepth":1038,"depth":1038,"links":73705},[73706,73712,73713,73714,73715],{"id":72378,"depth":1041,"text":72379,"children":73707},[73708,73709,73710,73711],{"id":72382,"depth":1038,"text":72383},{"id":72507,"depth":1038,"text":72508},{"id":72650,"depth":1038,"text":72651},{"id":72779,"depth":1038,"text":72780},{"id":72839,"depth":1041,"text":72840},{"id":73422,"depth":1041,"text":73423},{"id":73659,"depth":1041,"text":73660},{"id":2620,"depth":1041,"text":2621},"The static form of the Orientation map: the four decision dimensions, every capability's exact requirements, and the full service catalog with costs.",{},{"title":531,"description":73716},"UWOwVk_2EpZMN4_a-hIdLA-6XbxblWdKF-5lraTnyWY",{"id":73721,"title":535,"access":12,"apiToc":929,"body":73722,"category":1042,"description":76498,"extension":1044,"meta":76499,"navigation":1042,"nest":1042,"path":536,"seo":76500,"sinceVersion":1042,"status":1049,"stem":537,"subtitle":1042,"tier":1050,"tocExternal":1042,"__hash__":76501},"docs\u002Frecipes\u002Fconfidential-hr.md",{"type":931,"value":73723,"toc":76486},[73724,73727,73762,73772,73779,73781,73824,73826,73927,73929,73932,74442,74446,74459,74760,74772,74801,74805,74817,75004,75028,75099,75103,75109,75311,75336,75361,75365,75375,75650,75657,75678,75691,75695,75704,76048,76060,76188,76192,76199,76442,76457,76459,76483],[934,73725,535],{"id":73726},"confidential-hr-records",[1061,73728,73729],{},[937,73730,73731,73733,73734,58223,73736,953,73738,58355,73740,953,73742,58355,73744,953,73746,58355,73748,953,73750,58355,73752,953,73754,58355,73756,953,73759,957],{},[950,73732,1500],{}," teams holding co-located records of mixed sensitivity in one collection — an employee's public basics beside their salary — who need the sensitive fields cryptographically gated, not just hidden by a UI check.\n",[950,73735,58222],{},[941,73737,21976],{},[941,73739,73343],{},[941,73741,3197],{},[941,73743,4680],{},[941,73745,6775],{},[941,73747,58494],{},[941,73749,22027],{},[941,73751,1086],{},[941,73753,6719],{},[941,73755,4151],{},[941,73757,73758],{},"withForget()",[941,73760,73761],{},"@noy-db\u002Fhub\u002Fforget",[937,73763,58245,73764,73771],{},[1007,73765,73768],{"href":73766,"rel":73767},"https:\u002F\u002Fgithub.com\u002Fnoy-db\u002Fdocs\u002Fblob\u002Fmain\u002Fshowcases\u002Fsrc\u002Frecipe-confidential-hr.recipe.test.ts",[3371],[941,73769,73770],{},"showcases\u002Fsrc\u002Frecipe-confidential-hr.recipe.test.ts"," — the code below is the same code that runs there.",[937,73773,73774,73775,73778],{},"An HR vault stores each employee's public basics — name, role, start date — right next to their salary, performance notes, and signed contract PDFs. Those two halves need different confidentiality. ",[1007,73776,73777],{"href":873},"Record tiers"," give the sensitive half its own per-tier data-encryption key (DEK): a caller at the public floor cannot decrypt a record parked above it, cannot see it on a plain read, and cannot search or overwrite it — while the public basics stay readable to everyone with the collection DEK. This recipe walks the whole tier surface: gated reads, a TTL-bound elevation for a privileged write, a delegation to a payroll reviewer, tier-aware search, a contract blob that follows its record's tier, and a right-to-erasure that shreds across tiers.",[1022,73780,70093],{"id":70092},[1111,73782,73783,73792,73804,73817],{},[1114,73784,73785,73786,73788,73789,957],{},"The Bundle line's six strategies, passed to ",[941,73787,2134],{}," together (see ",[1007,73790,58394],{"href":73791},"#setup",[1114,73793,73794,964,73797,73800,73801,73803],{},[950,73795,73796],{},"Every tiered collection must declare its tiers",[941,73798,73799],{},"vault.collection(name, { tiers: [0, 1] })",". A collection that never declares ",[941,73802,872],{}," is a plain tier-0 collection.",[1114,73805,73806,73812,73813,73816],{},[950,73807,73808,73809],{},"The blob-owning collection also needs ",[941,73810,73811],{},"perRecordKeys: true"," — a blob can only be tier-isolated when its owning record carries a per-record key; ",[941,73814,73815],{},"blobTierPolicy: 'isolate'"," then keeps the content unshared so it re-keys with the record.",[1114,73818,73819,73820,73823],{},"The vault owner (",[941,73821,73822],{},"hr-admin"," here) auto-mints tier DEKs at write time. A lower-tier session or a delegate is what makes the gate observable.",[1022,73825,58276],{"id":58275},[1338,73827,73828,73836],{},[1341,73829,73830],{},[1344,73831,73832,73834],{},[1347,73833,70107],{},[1347,73835,70110],{},[1357,73837,73838,73854,73863,73873,73887,73900,73915],{},[1344,73839,73840,73843],{},[1362,73841,73842],{},"Salary parked above the public floor, invisible to a below-tier read",[1362,73844,73845,6030,73847,73850,73851],{},[941,73846,21976],{},[941,73848,73849],{},"putAtTier()"," \u002F the collection's ",[941,73852,73853],{},"tiers: [0, 1]",[1344,73855,73856,73859],{},[1362,73857,73858],{},"A cleared read that decrypts above your inherent tier",[1362,73860,73861],{},[941,73862,21984],{},[1344,73864,73865,73868],{},[1362,73866,73867],{},"A time-boxed, audited session that writes at an elevated tier",[1362,73869,73870],{},[941,73871,73872],{},"vault.elevate(tier, { ttlMs, reason })",[1344,73874,73875,73878],{},[1362,73876,73877],{},"Handing a reviewer scoped, expiring access without a full grant",[1362,73879,73880,73883,73884,73886],{},[941,73881,73882],{},"vault.delegate()"," (needs ",[941,73885,3197],{}," for the reviewer's keyring)",[1344,73888,73889,73892],{},[1362,73890,73891],{},"Semantic search that skips — never refuses — elevated records",[1362,73893,73894,73896,73897,73899],{},[941,73895,22027],{}," + a collection ",[941,73898,4452],{}," encoder",[1344,73901,73902,73905],{},[1362,73903,73904],{},"A contract PDF whose storage tier follows its owning record",[1362,73906,73907,6030,73909,6030,73912],{},[941,73908,6775],{},[941,73910,73911],{},"blobTierPolicy",[941,73913,73914],{},"blob(id).atTier()",[1344,73916,73917,73920],{},[1362,73918,73919],{},"Right-to-erasure that crypto-shreds across every tier",[1362,73921,73922,1395,73924,73926],{},[941,73923,73758],{},[941,73925,6719],{}," for the ledger proof)",[1022,73928,58394],{"id":58393},[937,73930,73931],{},"Every beat below shares one wiring — the vault owner's console:",[2217,73933,73935],{"className":2219,"code":73934,"language":2221,"meta":1037,"style":1037},"import { createNoydb, withSearch } from '@noy-db\u002Fhub'\nimport { withTiers } from '@noy-db\u002Fhub\u002Ftiers'\nimport { withTeam } from '@noy-db\u002Fhub\u002Fteam'\nimport { withBlobs } from '@noy-db\u002Fhub\u002Fblobs'\nimport { withHistory } from '@noy-db\u002Fhub\u002Fhistory'\nimport { withForget } from '@noy-db\u002Fhub\u002Fforget'\nimport { toMemory } from '@noy-db\u002Fto-memory'\n\ninterface Employee { employeeId: string; name: string; role: string; startDate: string }\ninterface Salary { employeeId: string; amount: string; notes: string }\ninterface Contract { employeeId: string; title: string }\ninterface Review { employeeId: string; notes: string }\n\nconst PUBLIC = 0\nconst CONFIDENTIAL = 1\n\nconst db = await createNoydb({\n  store: toMemory(),\n  user: 'hr-admin',\n  secret: 'hr-console-secret-2026',\n  tiersStrategy: withTiers(),\n  teamStrategy: withTeam(),\n  blobsStrategy: withBlobs(),\n  searchStrategy: withSearch(),\n  historyStrategy: withHistory(),\n  forgetStrategy: withForget({ subjects: { employees: 'employeeId', salaries: 'employeeId' } }),\n})\n\nconst vault = await db.openVault('acme-hr')\n",[941,73936,73937,73960,73979,73997,74015,74033,74052,74070,74074,74117,74150,74176,74201,74205,74217,74229,74233,74249,74261,74275,74290,74303,74315,74327,74340,74352,74405,74411,74415],{"__ignoreMap":1037},[2225,73938,73939,73941,73943,73945,73947,73950,73952,73954,73956,73958],{"class":2227,"line":2228},[2225,73940,2232],{"class":2231},[2225,73942,2236],{"class":2235},[2225,73944,2744],{"class":2239},[2225,73946,2298],{"class":2235},[2225,73948,73949],{"class":2239}," withSearch",[2225,73951,2243],{"class":2235},[2225,73953,2246],{"class":2231},[2225,73955,2249],{"class":2235},[2225,73957,1086],{"class":2252},[2225,73959,2256],{"class":2235},[2225,73961,73962,73964,73966,73969,73971,73973,73975,73977],{"class":2227,"line":1041},[2225,73963,2232],{"class":2231},[2225,73965,2236],{"class":2235},[2225,73967,73968],{"class":2239}," withTiers",[2225,73970,2243],{"class":2235},[2225,73972,2246],{"class":2231},[2225,73974,2249],{"class":2235},[2225,73976,73343],{"class":2252},[2225,73978,2256],{"class":2235},[2225,73980,73981,73983,73985,73987,73989,73991,73993,73995],{"class":2227,"line":1038},[2225,73982,2232],{"class":2231},[2225,73984,2236],{"class":2235},[2225,73986,10373],{"class":2239},[2225,73988,2243],{"class":2235},[2225,73990,2246],{"class":2231},[2225,73992,2249],{"class":2235},[2225,73994,4680],{"class":2252},[2225,73996,2256],{"class":2235},[2225,73998,73999,74001,74003,74005,74007,74009,74011,74013],{"class":2227,"line":2288},[2225,74000,2232],{"class":2231},[2225,74002,2236],{"class":2235},[2225,74004,58485],{"class":2239},[2225,74006,2243],{"class":2235},[2225,74008,2246],{"class":2231},[2225,74010,2249],{"class":2235},[2225,74012,58494],{"class":2252},[2225,74014,2256],{"class":2235},[2225,74016,74017,74019,74021,74023,74025,74027,74029,74031],{"class":2227,"line":2304},[2225,74018,2232],{"class":2231},[2225,74020,2236],{"class":2235},[2225,74022,4142],{"class":2239},[2225,74024,2243],{"class":2235},[2225,74026,2246],{"class":2231},[2225,74028,2249],{"class":2235},[2225,74030,4151],{"class":2252},[2225,74032,2256],{"class":2235},[2225,74034,74035,74037,74039,74042,74044,74046,74048,74050],{"class":2227,"line":2356},[2225,74036,2232],{"class":2231},[2225,74038,2236],{"class":2235},[2225,74040,74041],{"class":2239}," withForget",[2225,74043,2243],{"class":2235},[2225,74045,2246],{"class":2231},[2225,74047,2249],{"class":2235},[2225,74049,73761],{"class":2252},[2225,74051,2256],{"class":2235},[2225,74053,74054,74056,74058,74060,74062,74064,74066,74068],{"class":2227,"line":2830},[2225,74055,2232],{"class":2231},[2225,74057,2236],{"class":2235},[2225,74059,68576],{"class":2239},[2225,74061,2243],{"class":2235},[2225,74063,2246],{"class":2231},[2225,74065,2249],{"class":2235},[2225,74067,9638],{"class":2252},[2225,74069,2256],{"class":2235},[2225,74071,74072],{"class":2227,"line":2861},[2225,74073,2285],{"emptyLinePlaceholder":2284},[2225,74075,74076,74078,74081,74083,74086,74088,74090,74092,74094,74096,74098,74100,74102,74104,74106,74108,74111,74113,74115],{"class":2227,"line":4246},[2225,74077,6994],{"class":2774},[2225,74079,74080],{"class":6342}," Employee",[2225,74082,2236],{"class":2235},[2225,74084,74085],{"class":2307}," employeeId",[2225,74087,2311],{"class":2235},[2225,74089,6366],{"class":6342},[2225,74091,3769],{"class":2235},[2225,74093,8104],{"class":2307},[2225,74095,2311],{"class":2235},[2225,74097,6366],{"class":6342},[2225,74099,3769],{"class":2235},[2225,74101,34944],{"class":2307},[2225,74103,2311],{"class":2235},[2225,74105,6366],{"class":6342},[2225,74107,3769],{"class":2235},[2225,74109,74110],{"class":2307}," startDate",[2225,74112,2311],{"class":2235},[2225,74114,6366],{"class":6342},[2225,74116,6369],{"class":2235},[2225,74118,74119,74121,74124,74126,74128,74130,74132,74134,74136,74138,74140,74142,74144,74146,74148],{"class":2227,"line":4253},[2225,74120,6994],{"class":2774},[2225,74122,74123],{"class":6342}," Salary",[2225,74125,2236],{"class":2235},[2225,74127,74085],{"class":2307},[2225,74129,2311],{"class":2235},[2225,74131,6366],{"class":6342},[2225,74133,3769],{"class":2235},[2225,74135,6351],{"class":2307},[2225,74137,2311],{"class":2235},[2225,74139,6366],{"class":6342},[2225,74141,3769],{"class":2235},[2225,74143,42817],{"class":2307},[2225,74145,2311],{"class":2235},[2225,74147,6366],{"class":6342},[2225,74149,6369],{"class":2235},[2225,74151,74152,74154,74157,74159,74161,74163,74165,74167,74170,74172,74174],{"class":2227,"line":4259},[2225,74153,6994],{"class":2774},[2225,74155,74156],{"class":6342}," Contract",[2225,74158,2236],{"class":2235},[2225,74160,74085],{"class":2307},[2225,74162,2311],{"class":2235},[2225,74164,6366],{"class":6342},[2225,74166,3769],{"class":2235},[2225,74168,74169],{"class":2307}," title",[2225,74171,2311],{"class":2235},[2225,74173,6366],{"class":6342},[2225,74175,6369],{"class":2235},[2225,74177,74178,74180,74183,74185,74187,74189,74191,74193,74195,74197,74199],{"class":2227,"line":6548},[2225,74179,6994],{"class":2774},[2225,74181,74182],{"class":6342}," Review",[2225,74184,2236],{"class":2235},[2225,74186,74085],{"class":2307},[2225,74188,2311],{"class":2235},[2225,74190,6366],{"class":6342},[2225,74192,3769],{"class":2235},[2225,74194,42817],{"class":2307},[2225,74196,2311],{"class":2235},[2225,74198,6366],{"class":6342},[2225,74200,6369],{"class":2235},[2225,74202,74203],{"class":2227,"line":6576},[2225,74204,2285],{"emptyLinePlaceholder":2284},[2225,74206,74207,74209,74212,74214],{"class":2227,"line":6582},[2225,74208,2775],{"class":2774},[2225,74210,74211],{"class":2239}," PUBLIC ",[2225,74213,2781],{"class":2235},[2225,74215,74216],{"class":6527}," 0\n",[2225,74218,74219,74221,74224,74226],{"class":2227,"line":6587},[2225,74220,2775],{"class":2774},[2225,74222,74223],{"class":2239}," CONFIDENTIAL ",[2225,74225,2781],{"class":2235},[2225,74227,74228],{"class":6527}," 1\n",[2225,74230,74231],{"class":2227,"line":7174},[2225,74232,2285],{"emptyLinePlaceholder":2284},[2225,74234,74235,74237,74239,74241,74243,74245,74247],{"class":2227,"line":7180},[2225,74236,2775],{"class":2774},[2225,74238,2778],{"class":2239},[2225,74240,2781],{"class":2235},[2225,74242,2784],{"class":2231},[2225,74244,2744],{"class":2291},[2225,74246,2789],{"class":2239},[2225,74248,4200],{"class":2235},[2225,74250,74251,74253,74255,74257,74259],{"class":2227,"line":7220},[2225,74252,15608],{"class":2307},[2225,74254,2311],{"class":2235},[2225,74256,68576],{"class":2291},[2225,74258,4228],{"class":2239},[2225,74260,1707],{"class":2235},[2225,74262,74263,74265,74267,74269,74271,74273],{"class":2227,"line":7225},[2225,74264,4205],{"class":2307},[2225,74266,2311],{"class":2235},[2225,74268,2249],{"class":2235},[2225,74270,73822],{"class":2252},[2225,74272,2805],{"class":2235},[2225,74274,1707],{"class":2235},[2225,74276,74277,74279,74281,74283,74286,74288],{"class":2227,"line":7231},[2225,74278,10423],{"class":2307},[2225,74280,2311],{"class":2235},[2225,74282,2249],{"class":2235},[2225,74284,74285],{"class":2252},"hr-console-secret-2026",[2225,74287,2805],{"class":2235},[2225,74289,1707],{"class":2235},[2225,74291,74292,74295,74297,74299,74301],{"class":2227,"line":7267},[2225,74293,74294],{"class":2307},"  tiersStrategy",[2225,74296,2311],{"class":2235},[2225,74298,73968],{"class":2291},[2225,74300,4228],{"class":2239},[2225,74302,1707],{"class":2235},[2225,74304,74305,74307,74309,74311,74313],{"class":2227,"line":7272},[2225,74306,10439],{"class":2307},[2225,74308,2311],{"class":2235},[2225,74310,10373],{"class":2291},[2225,74312,4228],{"class":2239},[2225,74314,1707],{"class":2235},[2225,74316,74317,74319,74321,74323,74325],{"class":2227,"line":7278},[2225,74318,58728],{"class":2307},[2225,74320,2311],{"class":2235},[2225,74322,58485],{"class":2291},[2225,74324,4228],{"class":2239},[2225,74326,1707],{"class":2235},[2225,74328,74329,74332,74334,74336,74338],{"class":2227,"line":7303},[2225,74330,74331],{"class":2307},"  searchStrategy",[2225,74333,2311],{"class":2235},[2225,74335,73949],{"class":2291},[2225,74337,4228],{"class":2239},[2225,74339,1707],{"class":2235},[2225,74341,74342,74344,74346,74348,74350],{"class":2227,"line":7308},[2225,74343,4221],{"class":2307},[2225,74345,2311],{"class":2235},[2225,74347,4142],{"class":2291},[2225,74349,4228],{"class":2239},[2225,74351,1707],{"class":2235},[2225,74353,74354,74357,74359,74361,74363,74365,74368,74370,74372,74375,74377,74379,74382,74384,74386,74389,74391,74393,74395,74397,74399,74401,74403],{"class":2227,"line":7314},[2225,74355,74356],{"class":2307},"  forgetStrategy",[2225,74358,2311],{"class":2235},[2225,74360,74041],{"class":2291},[2225,74362,2789],{"class":2239},[2225,74364,2792],{"class":2235},[2225,74366,74367],{"class":2307}," subjects",[2225,74369,2311],{"class":2235},[2225,74371,2236],{"class":2235},[2225,74373,74374],{"class":2307}," employees",[2225,74376,2311],{"class":2235},[2225,74378,2249],{"class":2235},[2225,74380,74381],{"class":2252},"employeeId",[2225,74383,2805],{"class":2235},[2225,74385,2298],{"class":2235},[2225,74387,74388],{"class":2307}," salaries",[2225,74390,2311],{"class":2235},[2225,74392,2249],{"class":2235},[2225,74394,74381],{"class":2252},[2225,74396,2805],{"class":2235},[2225,74398,2243],{"class":2235},[2225,74400,2243],{"class":2235},[2225,74402,1381],{"class":2239},[2225,74404,1707],{"class":2235},[2225,74406,74407,74409],{"class":2227,"line":7348},[2225,74408,2359],{"class":2235},[2225,74410,2362],{"class":2239},[2225,74412,74413],{"class":2227,"line":8613},[2225,74414,2285],{"emptyLinePlaceholder":2284},[2225,74416,74417,74419,74421,74423,74425,74427,74429,74431,74433,74435,74438,74440],{"class":2227,"line":8633},[2225,74418,2775],{"class":2774},[2225,74420,2835],{"class":2239},[2225,74422,2781],{"class":2235},[2225,74424,2784],{"class":2231},[2225,74426,2842],{"class":2239},[2225,74428,1520],{"class":2235},[2225,74430,2847],{"class":2291},[2225,74432,2789],{"class":2239},[2225,74434,2805],{"class":2235},[2225,74436,74437],{"class":2252},"acme-hr",[2225,74439,2805],{"class":2235},[2225,74441,2362],{"class":2239},[1022,74443,74445],{"id":74444},"gated-reads-public-basics-stay-readable-salary-does-not","Gated reads — public basics stay readable, salary does not",[937,74447,74448,74449,74452,74453,74456,74457,1520],{},"The employee's basics live at tier 0; the salary is ",[1092,74450,74451],{},"born"," confidential via a direct tiered write (",[941,74454,74455],{},"putAtTier","), never a plain ",[941,74458,20953],{},[2217,74460,74462],{"className":2219,"code":74461,"language":2221,"meta":1037,"style":1037},"const employees = vault.collection\u003CEmployee>('employees', { tiers: [PUBLIC, CONFIDENTIAL] })\nconst salaries = vault.collection\u003CSalary>('salaries', { tiers: [PUBLIC, CONFIDENTIAL] })\n\nawait employees.put('e1', { employeeId: 'e1', name: 'Dana Lopez', role: 'Engineer', startDate: '2024-02-01' })\nawait salaries.putAtTier('e1', { employeeId: 'e1', amount: '145000', notes: 'strong Q1' }, CONFIDENTIAL)\n\nawait employees.get('e1')     \u002F\u002F → { …, role: 'Engineer' } — tier-0 basics, readable\nawait salaries.get('e1')      \u002F\u002F → null — the read gate runs off the live `_tier` header before any decrypt\n",[941,74463,74464,74516,74565,74569,74645,74708,74712,74736],{"__ignoreMap":1037},[2225,74465,74466,74468,74471,74473,74475,74477,74479,74481,74484,74486,74488,74490,74493,74495,74497,74499,74502,74504,74507,74509,74512,74514],{"class":2227,"line":2228},[2225,74467,2775],{"class":2774},[2225,74469,74470],{"class":2239}," employees ",[2225,74472,2781],{"class":2235},[2225,74474,6466],{"class":2239},[2225,74476,1520],{"class":2235},[2225,74478,6471],{"class":2291},[2225,74480,6474],{"class":2235},[2225,74482,74483],{"class":6342},"Employee",[2225,74485,6480],{"class":2235},[2225,74487,2789],{"class":2239},[2225,74489,2805],{"class":2235},[2225,74491,74492],{"class":2252},"employees",[2225,74494,2805],{"class":2235},[2225,74496,2298],{"class":2235},[2225,74498,2236],{"class":2235},[2225,74500,74501],{"class":2307}," tiers",[2225,74503,2311],{"class":2235},[2225,74505,74506],{"class":2239}," [PUBLIC",[2225,74508,2298],{"class":2235},[2225,74510,74511],{"class":2239}," CONFIDENTIAL] ",[2225,74513,2359],{"class":2235},[2225,74515,2362],{"class":2239},[2225,74517,74518,74520,74523,74525,74527,74529,74531,74533,74536,74538,74540,74542,74545,74547,74549,74551,74553,74555,74557,74559,74561,74563],{"class":2227,"line":1041},[2225,74519,2775],{"class":2774},[2225,74521,74522],{"class":2239}," salaries ",[2225,74524,2781],{"class":2235},[2225,74526,6466],{"class":2239},[2225,74528,1520],{"class":2235},[2225,74530,6471],{"class":2291},[2225,74532,6474],{"class":2235},[2225,74534,74535],{"class":6342},"Salary",[2225,74537,6480],{"class":2235},[2225,74539,2789],{"class":2239},[2225,74541,2805],{"class":2235},[2225,74543,74544],{"class":2252},"salaries",[2225,74546,2805],{"class":2235},[2225,74548,2298],{"class":2235},[2225,74550,2236],{"class":2235},[2225,74552,74501],{"class":2307},[2225,74554,2311],{"class":2235},[2225,74556,74506],{"class":2239},[2225,74558,2298],{"class":2235},[2225,74560,74511],{"class":2239},[2225,74562,2359],{"class":2235},[2225,74564,2362],{"class":2239},[2225,74566,74567],{"class":2227,"line":1038},[2225,74568,2285],{"emptyLinePlaceholder":2284},[2225,74570,74571,74573,74575,74577,74579,74581,74583,74586,74588,74590,74592,74594,74596,74598,74600,74602,74604,74606,74608,74610,74613,74615,74617,74619,74621,74623,74626,74628,74630,74632,74634,74636,74639,74641,74643],{"class":2227,"line":2288},[2225,74572,6500],{"class":2231},[2225,74574,74374],{"class":2239},[2225,74576,1520],{"class":2235},[2225,74578,3161],{"class":2291},[2225,74580,2789],{"class":2239},[2225,74582,2805],{"class":2235},[2225,74584,74585],{"class":2252},"e1",[2225,74587,2805],{"class":2235},[2225,74589,2298],{"class":2235},[2225,74591,2236],{"class":2235},[2225,74593,74085],{"class":2307},[2225,74595,2311],{"class":2235},[2225,74597,2249],{"class":2235},[2225,74599,74585],{"class":2252},[2225,74601,2805],{"class":2235},[2225,74603,2298],{"class":2235},[2225,74605,8104],{"class":2307},[2225,74607,2311],{"class":2235},[2225,74609,2249],{"class":2235},[2225,74611,74612],{"class":2252},"Dana Lopez",[2225,74614,2805],{"class":2235},[2225,74616,2298],{"class":2235},[2225,74618,34944],{"class":2307},[2225,74620,2311],{"class":2235},[2225,74622,2249],{"class":2235},[2225,74624,74625],{"class":2252},"Engineer",[2225,74627,2805],{"class":2235},[2225,74629,2298],{"class":2235},[2225,74631,74110],{"class":2307},[2225,74633,2311],{"class":2235},[2225,74635,2249],{"class":2235},[2225,74637,74638],{"class":2252},"2024-02-01",[2225,74640,2805],{"class":2235},[2225,74642,2243],{"class":2235},[2225,74644,2362],{"class":2239},[2225,74646,74647,74649,74651,74653,74655,74657,74659,74661,74663,74665,74667,74669,74671,74673,74675,74677,74679,74681,74683,74685,74688,74690,74692,74694,74696,74698,74701,74703,74705],{"class":2227,"line":2304},[2225,74648,6500],{"class":2231},[2225,74650,74388],{"class":2239},[2225,74652,1520],{"class":2235},[2225,74654,74455],{"class":2291},[2225,74656,2789],{"class":2239},[2225,74658,2805],{"class":2235},[2225,74660,74585],{"class":2252},[2225,74662,2805],{"class":2235},[2225,74664,2298],{"class":2235},[2225,74666,2236],{"class":2235},[2225,74668,74085],{"class":2307},[2225,74670,2311],{"class":2235},[2225,74672,2249],{"class":2235},[2225,74674,74585],{"class":2252},[2225,74676,2805],{"class":2235},[2225,74678,2298],{"class":2235},[2225,74680,6351],{"class":2307},[2225,74682,2311],{"class":2235},[2225,74684,2249],{"class":2235},[2225,74686,74687],{"class":2252},"145000",[2225,74689,2805],{"class":2235},[2225,74691,2298],{"class":2235},[2225,74693,42817],{"class":2307},[2225,74695,2311],{"class":2235},[2225,74697,2249],{"class":2235},[2225,74699,74700],{"class":2252},"strong Q1",[2225,74702,2805],{"class":2235},[2225,74704,10965],{"class":2235},[2225,74706,74707],{"class":2239}," CONFIDENTIAL)\n",[2225,74709,74710],{"class":2227,"line":2356},[2225,74711,2285],{"emptyLinePlaceholder":2284},[2225,74713,74714,74716,74718,74720,74722,74724,74726,74728,74730,74733],{"class":2227,"line":2830},[2225,74715,6500],{"class":2231},[2225,74717,74374],{"class":2239},[2225,74719,1520],{"class":2235},[2225,74721,3158],{"class":2291},[2225,74723,2789],{"class":2239},[2225,74725,2805],{"class":2235},[2225,74727,74585],{"class":2252},[2225,74729,2805],{"class":2235},[2225,74731,74732],{"class":2239},")     ",[2225,74734,74735],{"class":2763},"\u002F\u002F → { …, role: 'Engineer' } — tier-0 basics, readable\n",[2225,74737,74738,74740,74742,74744,74746,74748,74750,74752,74754,74757],{"class":2227,"line":2861},[2225,74739,6500],{"class":2231},[2225,74741,74388],{"class":2239},[2225,74743,1520],{"class":2235},[2225,74745,3158],{"class":2291},[2225,74747,2789],{"class":2239},[2225,74749,2805],{"class":2235},[2225,74751,74585],{"class":2252},[2225,74753,2805],{"class":2235},[2225,74755,74756],{"class":2239},")      ",[2225,74758,74759],{"class":2763},"\u002F\u002F → null — the read gate runs off the live `_tier` header before any decrypt\n",[937,74761,74762,74763,74765,74766,74768,74769,74771],{},"A plain ",[941,74764,14639],{}," on a confidential record returns ",[941,74767,6651],{}," for a below-tier caller — it is treated as absent, not refused. The sanctioned way to read above your inherent tier is ",[941,74770,21984],{},", which succeeds for a caller who holds the tier DEK (here, the owner) and fires a cross-tier access event:",[2217,74773,74775],{"className":2219,"code":74774,"language":2221,"meta":1037,"style":1037},"await salaries.getAtTier('e1') \u002F\u002F → { employeeId: 'e1', amount: '145000', notes: 'strong Q1' }\n",[941,74776,74777],{"__ignoreMap":1037},[2225,74778,74779,74781,74783,74785,74788,74790,74792,74794,74796,74798],{"class":2227,"line":2228},[2225,74780,6500],{"class":2231},[2225,74782,74388],{"class":2239},[2225,74784,1520],{"class":2235},[2225,74786,74787],{"class":2291},"getAtTier",[2225,74789,2789],{"class":2239},[2225,74791,2805],{"class":2235},[2225,74793,74585],{"class":2252},[2225,74795,2805],{"class":2235},[2225,74797,8495],{"class":2239},[2225,74799,74800],{"class":2763},"\u002F\u002F → { employeeId: 'e1', amount: '145000', notes: 'strong Q1' }\n",[1022,74802,74804],{"id":74803},"scoped-elevation-a-ttl-bound-privileged-write","Scoped elevation — a TTL-bound privileged write",[937,74806,7479,74807,74809,74810,74812,74813,74816],{},[1092,74808,39210],{}," a salary, a session briefly elevates. ",[941,74811,73872],{}," returns a handle whose ",[950,74814,74815],{},"writes"," land at the elevated tier; reads on the original vault stay at the caller's inherent tier.",[2217,74818,74820],{"className":2219,"code":74819,"language":2221,"meta":1037,"style":1037},"const elevated = await vault.elevate(CONFIDENTIAL, {\n  ttlMs: 15 * 60_000,\n  reason: 'annual review adjustment',\n})\n\u002F\u002F → ElevatedHandle { tier: 1, reason: 'annual review adjustment', expiresAt: \u003Cnow + ttlMs> }\n\nawait elevated.collection\u003CSalary>('salaries').put('e1', {\n  employeeId: 'e1', amount: '152000', notes: 'raise approved',\n})\nawait salaries.getAtTier('e1') \u002F\u002F → { …, amount: '152000' }\n",[941,74821,74822,74846,74860,74876,74882,74887,74891,74934,74975,74981],{"__ignoreMap":1037},[2225,74823,74824,74826,74829,74831,74833,74835,74837,74839,74842,74844],{"class":2227,"line":2228},[2225,74825,2775],{"class":2774},[2225,74827,74828],{"class":2239}," elevated ",[2225,74830,2781],{"class":2235},[2225,74832,2784],{"class":2231},[2225,74834,6466],{"class":2239},[2225,74836,1520],{"class":2235},[2225,74838,14606],{"class":2291},[2225,74840,74841],{"class":2239},"(CONFIDENTIAL",[2225,74843,2298],{"class":2235},[2225,74845,2301],{"class":2235},[2225,74847,74848,74850,74852,74854,74856,74858],{"class":2227,"line":1041},[2225,74849,69818],{"class":2307},[2225,74851,2311],{"class":2235},[2225,74853,45248],{"class":6527},[2225,74855,2263],{"class":2235},[2225,74857,19001],{"class":6527},[2225,74859,1707],{"class":2235},[2225,74861,74862,74865,74867,74869,74872,74874],{"class":2227,"line":1038},[2225,74863,74864],{"class":2307},"  reason",[2225,74866,2311],{"class":2235},[2225,74868,2249],{"class":2235},[2225,74870,74871],{"class":2252},"annual review adjustment",[2225,74873,2805],{"class":2235},[2225,74875,1707],{"class":2235},[2225,74877,74878,74880],{"class":2227,"line":2288},[2225,74879,2359],{"class":2235},[2225,74881,2362],{"class":2239},[2225,74883,74884],{"class":2227,"line":2304},[2225,74885,74886],{"class":2763},"\u002F\u002F → ElevatedHandle { tier: 1, reason: 'annual review adjustment', expiresAt: \u003Cnow + ttlMs> }\n",[2225,74888,74889],{"class":2227,"line":2356},[2225,74890,2285],{"emptyLinePlaceholder":2284},[2225,74892,74893,74895,74898,74900,74902,74904,74906,74908,74910,74912,74914,74916,74918,74920,74922,74924,74926,74928,74930,74932],{"class":2227,"line":2830},[2225,74894,6500],{"class":2231},[2225,74896,74897],{"class":2239}," elevated",[2225,74899,1520],{"class":2235},[2225,74901,6471],{"class":2291},[2225,74903,6474],{"class":2235},[2225,74905,74535],{"class":6342},[2225,74907,6480],{"class":2235},[2225,74909,2789],{"class":2239},[2225,74911,2805],{"class":2235},[2225,74913,74544],{"class":2252},[2225,74915,2805],{"class":2235},[2225,74917,1381],{"class":2239},[2225,74919,1520],{"class":2235},[2225,74921,3161],{"class":2291},[2225,74923,2789],{"class":2239},[2225,74925,2805],{"class":2235},[2225,74927,74585],{"class":2252},[2225,74929,2805],{"class":2235},[2225,74931,2298],{"class":2235},[2225,74933,2301],{"class":2235},[2225,74935,74936,74939,74941,74943,74945,74947,74949,74951,74953,74955,74958,74960,74962,74964,74966,74968,74971,74973],{"class":2227,"line":2861},[2225,74937,74938],{"class":2307},"  employeeId",[2225,74940,2311],{"class":2235},[2225,74942,2249],{"class":2235},[2225,74944,74585],{"class":2252},[2225,74946,2805],{"class":2235},[2225,74948,2298],{"class":2235},[2225,74950,6351],{"class":2307},[2225,74952,2311],{"class":2235},[2225,74954,2249],{"class":2235},[2225,74956,74957],{"class":2252},"152000",[2225,74959,2805],{"class":2235},[2225,74961,2298],{"class":2235},[2225,74963,42817],{"class":2307},[2225,74965,2311],{"class":2235},[2225,74967,2249],{"class":2235},[2225,74969,74970],{"class":2252},"raise approved",[2225,74972,2805],{"class":2235},[2225,74974,1707],{"class":2235},[2225,74976,74977,74979],{"class":2227,"line":4246},[2225,74978,2359],{"class":2235},[2225,74980,2362],{"class":2239},[2225,74982,74983,74985,74987,74989,74991,74993,74995,74997,74999,75001],{"class":2227,"line":4253},[2225,74984,6500],{"class":2231},[2225,74986,74388],{"class":2239},[2225,74988,1520],{"class":2235},[2225,74990,74787],{"class":2291},[2225,74992,2789],{"class":2239},[2225,74994,2805],{"class":2235},[2225,74996,74585],{"class":2252},[2225,74998,2805],{"class":2235},[2225,75000,8495],{"class":2239},[2225,75002,75003],{"class":2763},"\u002F\u002F → { …, amount: '152000' }\n",[937,75005,75006,75007,6789,75010,75013,75014,75016,75017,75020,75021,75023,75024,75027],{},"Only one elevation may be live per vault — a second ",[941,75008,75009],{},"vault.elevate(...)",[941,75011,75012],{},"AlreadyElevatedError",". The TTL is enforced lazily: the handle checks ",[941,75015,44339],{}," on every operation, so a forgotten ",[941,75018,75019],{},"release()"," cannot keep elevated writes alive. Calling ",[941,75022,75019],{}," (or letting ",[941,75025,75026],{},"ttlMs"," elapse) makes the handle inert, and the next write through it throws:",[2217,75029,75031],{"className":2219,"code":75030,"language":2221,"meta":1037,"style":1037},"await elevated.release()\nawait elevated.collection\u003CSalary>('salaries').put('e1', { \u002F* … *\u002F })\n\u002F\u002F → throws ElevationExpiredError\n",[941,75032,75033,75046,75094],{"__ignoreMap":1037},[2225,75034,75035,75037,75039,75041,75044],{"class":2227,"line":2228},[2225,75036,6500],{"class":2231},[2225,75038,74897],{"class":2239},[2225,75040,1520],{"class":2235},[2225,75042,75043],{"class":2291},"release",[2225,75045,8146],{"class":2239},[2225,75047,75048,75050,75052,75054,75056,75058,75060,75062,75064,75066,75068,75070,75072,75074,75076,75078,75080,75082,75084,75086,75088,75090,75092],{"class":2227,"line":1041},[2225,75049,6500],{"class":2231},[2225,75051,74897],{"class":2239},[2225,75053,1520],{"class":2235},[2225,75055,6471],{"class":2291},[2225,75057,6474],{"class":2235},[2225,75059,74535],{"class":6342},[2225,75061,6480],{"class":2235},[2225,75063,2789],{"class":2239},[2225,75065,2805],{"class":2235},[2225,75067,74544],{"class":2252},[2225,75069,2805],{"class":2235},[2225,75071,1381],{"class":2239},[2225,75073,1520],{"class":2235},[2225,75075,3161],{"class":2291},[2225,75077,2789],{"class":2239},[2225,75079,2805],{"class":2235},[2225,75081,74585],{"class":2252},[2225,75083,2805],{"class":2235},[2225,75085,2298],{"class":2235},[2225,75087,2236],{"class":2235},[2225,75089,10968],{"class":2763},[2225,75091,2243],{"class":2235},[2225,75093,2362],{"class":2239},[2225,75095,75096],{"class":2227,"line":1038},[2225,75097,75098],{"class":2763},"\u002F\u002F → throws ElevationExpiredError\n",[1022,75100,75102],{"id":75101},"delegation-scoped-access-for-a-payroll-reviewer","Delegation — scoped access for a payroll reviewer",[937,75104,75105,75106,75108],{},"A reviewer who needs the confidential tier without a standing grant gets a time-boxed delegation. The reviewer must already have a keyring in the vault, so grant them in first (",[941,75107,3197],{},"), then delegate:",[2217,75110,75112],{"className":2219,"code":75111,"language":2221,"meta":1037,"style":1037},"await db.grant('acme-hr', {\n  userId: 'payroll', displayName: 'Payroll Reviewer', role: 'operator', secret: 'payroll-pass-2026',\n})\n\nconst token = await vault.delegate({\n  toUser: 'payroll',\n  tier: CONFIDENTIAL,\n  collection: 'salaries',\n  until: new Date(Date.now() + 60 * 60_000),\n})\n\u002F\u002F → DelegationToken { id, toUser: 'payroll', fromUser: 'hr-admin', tier: 1,\n\u002F\u002F                     collection: 'salaries', wrappedDek, sealedCek, until, createdAt }\n",[941,75113,75114,75136,75190,75196,75200,75221,75236,75248,75262,75295,75301,75306],{"__ignoreMap":1037},[2225,75115,75116,75118,75120,75122,75124,75126,75128,75130,75132,75134],{"class":2227,"line":2228},[2225,75117,6500],{"class":2231},[2225,75119,2842],{"class":2239},[2225,75121,1520],{"class":2235},[2225,75123,10493],{"class":2291},[2225,75125,2789],{"class":2239},[2225,75127,2805],{"class":2235},[2225,75129,74437],{"class":2252},[2225,75131,2805],{"class":2235},[2225,75133,2298],{"class":2235},[2225,75135,2301],{"class":2235},[2225,75137,75138,75140,75142,75144,75147,75149,75151,75154,75156,75158,75161,75163,75165,75167,75169,75171,75173,75175,75177,75179,75181,75183,75186,75188],{"class":2227,"line":1041},[2225,75139,10510],{"class":2307},[2225,75141,2311],{"class":2235},[2225,75143,2249],{"class":2235},[2225,75145,75146],{"class":2252},"payroll",[2225,75148,2805],{"class":2235},[2225,75150,2298],{"class":2235},[2225,75152,75153],{"class":2307}," displayName",[2225,75155,2311],{"class":2235},[2225,75157,2249],{"class":2235},[2225,75159,75160],{"class":2252},"Payroll Reviewer",[2225,75162,2805],{"class":2235},[2225,75164,2298],{"class":2235},[2225,75166,34944],{"class":2307},[2225,75168,2311],{"class":2235},[2225,75170,2249],{"class":2235},[2225,75172,9907],{"class":2252},[2225,75174,2805],{"class":2235},[2225,75176,2298],{"class":2235},[2225,75178,2810],{"class":2307},[2225,75180,2311],{"class":2235},[2225,75182,2249],{"class":2235},[2225,75184,75185],{"class":2252},"payroll-pass-2026",[2225,75187,2805],{"class":2235},[2225,75189,1707],{"class":2235},[2225,75191,75192,75194],{"class":2227,"line":1038},[2225,75193,2359],{"class":2235},[2225,75195,2362],{"class":2239},[2225,75197,75198],{"class":2227,"line":2288},[2225,75199,2285],{"emptyLinePlaceholder":2284},[2225,75201,75202,75204,75206,75208,75210,75212,75214,75217,75219],{"class":2227,"line":2304},[2225,75203,2775],{"class":2774},[2225,75205,38392],{"class":2239},[2225,75207,2781],{"class":2235},[2225,75209,2784],{"class":2231},[2225,75211,6466],{"class":2239},[2225,75213,1520],{"class":2235},[2225,75215,75216],{"class":2291},"delegate",[2225,75218,2789],{"class":2239},[2225,75220,4200],{"class":2235},[2225,75222,75223,75226,75228,75230,75232,75234],{"class":2227,"line":2356},[2225,75224,75225],{"class":2307},"  toUser",[2225,75227,2311],{"class":2235},[2225,75229,2249],{"class":2235},[2225,75231,75146],{"class":2252},[2225,75233,2805],{"class":2235},[2225,75235,1707],{"class":2235},[2225,75237,75238,75241,75243,75246],{"class":2227,"line":2830},[2225,75239,75240],{"class":2307},"  tier",[2225,75242,2311],{"class":2235},[2225,75244,75245],{"class":2239}," CONFIDENTIAL",[2225,75247,1707],{"class":2235},[2225,75249,75250,75252,75254,75256,75258,75260],{"class":2227,"line":2861},[2225,75251,30275],{"class":2307},[2225,75253,2311],{"class":2235},[2225,75255,2249],{"class":2235},[2225,75257,74544],{"class":2252},[2225,75259,2805],{"class":2235},[2225,75261,1707],{"class":2235},[2225,75263,75264,75267,75269,75271,75273,75276,75278,75281,75283,75285,75287,75289,75291,75293],{"class":2227,"line":4246},[2225,75265,75266],{"class":2307},"  until",[2225,75268,2311],{"class":2235},[2225,75270,8130],{"class":2235},[2225,75272,19122],{"class":2291},[2225,75274,75275],{"class":2239},"(Date",[2225,75277,1520],{"class":2235},[2225,75279,75280],{"class":2291},"now",[2225,75282,19132],{"class":2239},[2225,75284,71297],{"class":2235},[2225,75286,44452],{"class":6527},[2225,75288,2263],{"class":2235},[2225,75290,19001],{"class":6527},[2225,75292,1381],{"class":2239},[2225,75294,1707],{"class":2235},[2225,75296,75297,75299],{"class":2227,"line":4253},[2225,75298,2359],{"class":2235},[2225,75300,2362],{"class":2239},[2225,75302,75303],{"class":2227,"line":4259},[2225,75304,75305],{"class":2763},"\u002F\u002F → DelegationToken { id, toUser: 'payroll', fromUser: 'hr-admin', tier: 1,\n",[2225,75307,75308],{"class":2227,"line":6548},[2225,75309,75310],{"class":2763},"\u002F\u002F                     collection: 'salaries', wrappedDek, sealedCek, until, createdAt }\n",[937,75312,75313,75314,75316,75317,75320,75321,75324,75325,75327,75328,75331,75332,75335],{},"Naming ",[941,75315,6471],{}," makes this the ",[950,75318,75319],{},"per-collection"," form, so the token carries a single\n",[941,75322,75323],{},"wrappedDek",". Omit ",[941,75326,6471],{}," and you get the collection-wide form instead — a ",[941,75329,75330],{},"wrappedDeks","\nmap covering every collection the grantor held at that tier ",[1092,75333,75334],{},"when the token was issued",". For a\nreviewer who only needs salaries, name the collection: it is the narrower grant.",[937,75337,75338,75339,75342,75343,75345,75346,75349,75350,75353,75354,75357,75358,75360],{},"The tier DEK is wrapped under a per-token content key, and that key is sealed to the reviewer's\ninbox public half (",[941,75340,75341],{},"sealedCek",") — so ",[941,75344,73822],{}," delegates without knowing anything of the\nreviewer's secret. The reviewer's side then calls ",[941,75347,75348],{},"vault.refreshDelegations()"," to merge it,\nwhich grants a cleared read until ",[941,75351,75352],{},"until"," passes; ",[941,75355,75356],{},"vault.revokeDelegation(token.id)"," cancels it\nearly. Merging is explicit — opening the vault does not do it, and ",[1007,75359,872],{"href":873}," says\nwhy. The runnable test asserts issuance and the token's shape; the cross-session merge is a\nsecond-session concern beyond one in-memory process.",[1022,75362,75364],{"id":75363},"tier-aware-search-elevated-records-are-skipped-not-refused","Tier-aware search — elevated records are skipped, not refused",[937,75366,75367,75368,75370,75371,75374],{},"A collection with an ",[941,75369,4452],{}," encoder derives a ",[941,75372,75373],{},"_vec"," sidecar per record. Elevating a record purges that sidecar: it is no longer searchable above tier 0, and a below-tier search simply omits it — no error.",[2217,75376,75378],{"className":2219,"code":75377,"language":2221,"meta":1037,"style":1037},"const reviews = vault.collection\u003CReview>('reviews', { tiers: [PUBLIC, CONFIDENTIAL], embeddings: enc() })\n\nawait reviews.put('r1', { employeeId: 'e1', notes: 'strong performer bonus recommended' })\nawait reviews.put('r2', { employeeId: 'e2', notes: 'strong performer bonus recommended' })\nawait reviews.elevate('r1', CONFIDENTIAL)\n\nconst hits = await reviews.retrieve('strong performer bonus', { mode: 'semantic' })\nhits.map((h) => h.id) \u002F\u002F → ['r2'] — the elevated r1 is absent, not an error\n",[941,75379,75380,75442,75446,75497,75547,75569,75573,75619],{"__ignoreMap":1037},[2225,75381,75382,75384,75387,75389,75391,75393,75395,75397,75400,75402,75404,75406,75409,75411,75413,75415,75417,75419,75421,75423,75426,75428,75431,75433,75436,75438,75440],{"class":2227,"line":2228},[2225,75383,2775],{"class":2774},[2225,75385,75386],{"class":2239}," reviews ",[2225,75388,2781],{"class":2235},[2225,75390,6466],{"class":2239},[2225,75392,1520],{"class":2235},[2225,75394,6471],{"class":2291},[2225,75396,6474],{"class":2235},[2225,75398,75399],{"class":6342},"Review",[2225,75401,6480],{"class":2235},[2225,75403,2789],{"class":2239},[2225,75405,2805],{"class":2235},[2225,75407,75408],{"class":2252},"reviews",[2225,75410,2805],{"class":2235},[2225,75412,2298],{"class":2235},[2225,75414,2236],{"class":2235},[2225,75416,74501],{"class":2307},[2225,75418,2311],{"class":2235},[2225,75420,74506],{"class":2239},[2225,75422,2298],{"class":2235},[2225,75424,75425],{"class":2239}," CONFIDENTIAL]",[2225,75427,2298],{"class":2235},[2225,75429,75430],{"class":2307}," embeddings",[2225,75432,2311],{"class":2235},[2225,75434,75435],{"class":2291}," enc",[2225,75437,19132],{"class":2239},[2225,75439,2359],{"class":2235},[2225,75441,2362],{"class":2239},[2225,75443,75444],{"class":2227,"line":1041},[2225,75445,2285],{"emptyLinePlaceholder":2284},[2225,75447,75448,75450,75453,75455,75457,75459,75461,75464,75466,75468,75470,75472,75474,75476,75478,75480,75482,75484,75486,75488,75491,75493,75495],{"class":2227,"line":1038},[2225,75449,6500],{"class":2231},[2225,75451,75452],{"class":2239}," reviews",[2225,75454,1520],{"class":2235},[2225,75456,3161],{"class":2291},[2225,75458,2789],{"class":2239},[2225,75460,2805],{"class":2235},[2225,75462,75463],{"class":2252},"r1",[2225,75465,2805],{"class":2235},[2225,75467,2298],{"class":2235},[2225,75469,2236],{"class":2235},[2225,75471,74085],{"class":2307},[2225,75473,2311],{"class":2235},[2225,75475,2249],{"class":2235},[2225,75477,74585],{"class":2252},[2225,75479,2805],{"class":2235},[2225,75481,2298],{"class":2235},[2225,75483,42817],{"class":2307},[2225,75485,2311],{"class":2235},[2225,75487,2249],{"class":2235},[2225,75489,75490],{"class":2252},"strong performer bonus recommended",[2225,75492,2805],{"class":2235},[2225,75494,2243],{"class":2235},[2225,75496,2362],{"class":2239},[2225,75498,75499,75501,75503,75505,75507,75509,75511,75514,75516,75518,75520,75522,75524,75526,75529,75531,75533,75535,75537,75539,75541,75543,75545],{"class":2227,"line":2288},[2225,75500,6500],{"class":2231},[2225,75502,75452],{"class":2239},[2225,75504,1520],{"class":2235},[2225,75506,3161],{"class":2291},[2225,75508,2789],{"class":2239},[2225,75510,2805],{"class":2235},[2225,75512,75513],{"class":2252},"r2",[2225,75515,2805],{"class":2235},[2225,75517,2298],{"class":2235},[2225,75519,2236],{"class":2235},[2225,75521,74085],{"class":2307},[2225,75523,2311],{"class":2235},[2225,75525,2249],{"class":2235},[2225,75527,75528],{"class":2252},"e2",[2225,75530,2805],{"class":2235},[2225,75532,2298],{"class":2235},[2225,75534,42817],{"class":2307},[2225,75536,2311],{"class":2235},[2225,75538,2249],{"class":2235},[2225,75540,75490],{"class":2252},[2225,75542,2805],{"class":2235},[2225,75544,2243],{"class":2235},[2225,75546,2362],{"class":2239},[2225,75548,75549,75551,75553,75555,75557,75559,75561,75563,75565,75567],{"class":2227,"line":2304},[2225,75550,6500],{"class":2231},[2225,75552,75452],{"class":2239},[2225,75554,1520],{"class":2235},[2225,75556,14606],{"class":2291},[2225,75558,2789],{"class":2239},[2225,75560,2805],{"class":2235},[2225,75562,75463],{"class":2252},[2225,75564,2805],{"class":2235},[2225,75566,2298],{"class":2235},[2225,75568,74707],{"class":2239},[2225,75570,75571],{"class":2227,"line":2356},[2225,75572,2285],{"emptyLinePlaceholder":2284},[2225,75574,75575,75577,75580,75582,75584,75586,75588,75591,75593,75595,75598,75600,75602,75604,75606,75608,75610,75613,75615,75617],{"class":2227,"line":2830},[2225,75576,2775],{"class":2774},[2225,75578,75579],{"class":2239}," hits ",[2225,75581,2781],{"class":2235},[2225,75583,2784],{"class":2231},[2225,75585,75452],{"class":2239},[2225,75587,1520],{"class":2235},[2225,75589,75590],{"class":2291},"retrieve",[2225,75592,2789],{"class":2239},[2225,75594,2805],{"class":2235},[2225,75596,75597],{"class":2252},"strong performer bonus",[2225,75599,2805],{"class":2235},[2225,75601,2298],{"class":2235},[2225,75603,2236],{"class":2235},[2225,75605,32303],{"class":2307},[2225,75607,2311],{"class":2235},[2225,75609,2249],{"class":2235},[2225,75611,75612],{"class":2252},"semantic",[2225,75614,2805],{"class":2235},[2225,75616,2243],{"class":2235},[2225,75618,2362],{"class":2239},[2225,75620,75621,75624,75626,75628,75630,75632,75635,75637,75639,75642,75644,75647],{"class":2227,"line":2861},[2225,75622,75623],{"class":2239},"hits",[2225,75625,1520],{"class":2235},[2225,75627,8244],{"class":2291},[2225,75629,2789],{"class":2239},[2225,75631,2789],{"class":2235},[2225,75633,75634],{"class":7052},"h",[2225,75636,1381],{"class":2235},[2225,75638,8116],{"class":2774},[2225,75640,75641],{"class":2239}," h",[2225,75643,1520],{"class":2235},[2225,75645,75646],{"class":2239},"id) ",[2225,75648,75649],{"class":2763},"\u002F\u002F → ['r2'] — the elevated r1 is absent, not an error\n",[937,75651,75652,75653,75656],{},"A bulk re-derive counts the elevated record in ",[941,75654,75655],{},"skipped"," rather than writing a searchable sidecar above tier 0:",[2217,75658,75660],{"className":2219,"code":75659,"language":2221,"meta":1037,"style":1037},"await reviews.rebuildEmbeddings() \u002F\u002F → { rebuilt: 1, skipped: 1 }\n",[941,75661,75662],{"__ignoreMap":1037},[2225,75663,75664,75666,75668,75670,75673,75675],{"class":2227,"line":2228},[2225,75665,6500],{"class":2231},[2225,75667,75452],{"class":2239},[2225,75669,1520],{"class":2235},[2225,75671,75672],{"class":2291},"rebuildEmbeddings",[2225,75674,19132],{"class":2239},[2225,75676,75677],{"class":2763},"\u002F\u002F → { rebuilt: 1, skipped: 1 }\n",[937,75679,2789,75680,75683,75684,75687,75688,75690],{},[941,75681,75682],{},"enc()"," is any encoder returning a ",[941,75685,75686],{},"Float32Array","; the test uses a deterministic stub — see ",[1007,75689,673],{"href":674}," for real encoders.)",[1022,75692,75694],{"id":75693},"tiered-blobs-a-contract-that-follows-its-record","Tiered blobs — a contract that follows its record",[937,75696,75697,75698,75700,75701,75703],{},"A signed contract PDF is a blob owned by an employee record. On a ",[941,75699,14552],{}," collection with ",[941,75702,73815],{},", the blob's storage tier follows its owning record.",[2217,75705,75707],{"className":2219,"code":75706,"language":2221,"meta":1037,"style":1037},"const contracts = vault.collection\u003CContract>('contracts', {\n  tiers: [PUBLIC, CONFIDENTIAL],\n  perRecordKeys: true,\n  blobTierPolicy: 'isolate',\n})\n\nawait contracts.put('c1', { employeeId: 'e1', title: 'Signed offer letter' })\nconst pdf = new Uint8Array([0x25, 0x50, 0x44, 0x46, 0x2d, 0x31, 0x2e, 0x37]) \u002F\u002F \"%PDF-1.7\"\nawait contracts.blob('c1').put('contract', pdf, { mimeType: 'application\u002Fpdf' })\n\nawait contracts.elevate('c1', CONFIDENTIAL)\nawait contracts.get('c1')                \u002F\u002F → null — record gated\nawait contracts.blob('c1').get('contract') \u002F\u002F → null — the tier-0 blob surface is gated too\n",[941,75708,75709,75744,75759,75770,75786,75792,75796,75846,75905,75961,75965,75987,76011],{"__ignoreMap":1037},[2225,75710,75711,75713,75716,75718,75720,75722,75724,75726,75729,75731,75733,75735,75738,75740,75742],{"class":2227,"line":2228},[2225,75712,2775],{"class":2774},[2225,75714,75715],{"class":2239}," contracts ",[2225,75717,2781],{"class":2235},[2225,75719,6466],{"class":2239},[2225,75721,1520],{"class":2235},[2225,75723,6471],{"class":2291},[2225,75725,6474],{"class":2235},[2225,75727,75728],{"class":6342},"Contract",[2225,75730,6480],{"class":2235},[2225,75732,2789],{"class":2239},[2225,75734,2805],{"class":2235},[2225,75736,75737],{"class":2252},"contracts",[2225,75739,2805],{"class":2235},[2225,75741,2298],{"class":2235},[2225,75743,2301],{"class":2235},[2225,75745,75746,75749,75751,75753,75755,75757],{"class":2227,"line":1041},[2225,75747,75748],{"class":2307},"  tiers",[2225,75750,2311],{"class":2235},[2225,75752,74506],{"class":2239},[2225,75754,2298],{"class":2235},[2225,75756,75425],{"class":2239},[2225,75758,1707],{"class":2235},[2225,75760,75761,75764,75766,75768],{"class":2227,"line":1038},[2225,75762,75763],{"class":2307},"  perRecordKeys",[2225,75765,2311],{"class":2235},[2225,75767,2322],{"class":2321},[2225,75769,1707],{"class":2235},[2225,75771,75772,75775,75777,75779,75782,75784],{"class":2227,"line":2288},[2225,75773,75774],{"class":2307},"  blobTierPolicy",[2225,75776,2311],{"class":2235},[2225,75778,2249],{"class":2235},[2225,75780,75781],{"class":2252},"isolate",[2225,75783,2805],{"class":2235},[2225,75785,1707],{"class":2235},[2225,75787,75788,75790],{"class":2227,"line":2304},[2225,75789,2359],{"class":2235},[2225,75791,2362],{"class":2239},[2225,75793,75794],{"class":2227,"line":2356},[2225,75795,2285],{"emptyLinePlaceholder":2284},[2225,75797,75798,75800,75803,75805,75807,75809,75811,75813,75815,75817,75819,75821,75823,75825,75827,75829,75831,75833,75835,75837,75840,75842,75844],{"class":2227,"line":2830},[2225,75799,6500],{"class":2231},[2225,75801,75802],{"class":2239}," contracts",[2225,75804,1520],{"class":2235},[2225,75806,3161],{"class":2291},[2225,75808,2789],{"class":2239},[2225,75810,2805],{"class":2235},[2225,75812,62700],{"class":2252},[2225,75814,2805],{"class":2235},[2225,75816,2298],{"class":2235},[2225,75818,2236],{"class":2235},[2225,75820,74085],{"class":2307},[2225,75822,2311],{"class":2235},[2225,75824,2249],{"class":2235},[2225,75826,74585],{"class":2252},[2225,75828,2805],{"class":2235},[2225,75830,2298],{"class":2235},[2225,75832,74169],{"class":2307},[2225,75834,2311],{"class":2235},[2225,75836,2249],{"class":2235},[2225,75838,75839],{"class":2252},"Signed offer letter",[2225,75841,2805],{"class":2235},[2225,75843,2243],{"class":2235},[2225,75845,2362],{"class":2239},[2225,75847,75848,75850,75853,75855,75857,75859,75861,75864,75866,75869,75871,75874,75876,75879,75881,75884,75886,75889,75891,75894,75896,75899,75902],{"class":2227,"line":2861},[2225,75849,2775],{"class":2774},[2225,75851,75852],{"class":2239}," pdf ",[2225,75854,2781],{"class":2235},[2225,75856,8130],{"class":2235},[2225,75858,60935],{"class":2291},[2225,75860,28424],{"class":2239},[2225,75862,75863],{"class":6527},"0x25",[2225,75865,2298],{"class":2235},[2225,75867,75868],{"class":6527}," 0x50",[2225,75870,2298],{"class":2235},[2225,75872,75873],{"class":6527}," 0x44",[2225,75875,2298],{"class":2235},[2225,75877,75878],{"class":6527}," 0x46",[2225,75880,2298],{"class":2235},[2225,75882,75883],{"class":6527}," 0x2d",[2225,75885,2298],{"class":2235},[2225,75887,75888],{"class":6527}," 0x31",[2225,75890,2298],{"class":2235},[2225,75892,75893],{"class":6527}," 0x2e",[2225,75895,2298],{"class":2235},[2225,75897,75898],{"class":6527}," 0x37",[2225,75900,75901],{"class":2239},"]) ",[2225,75903,75904],{"class":2763},"\u002F\u002F \"%PDF-1.7\"\n",[2225,75906,75907,75909,75911,75913,75915,75917,75919,75921,75923,75925,75927,75929,75931,75933,75936,75938,75940,75943,75945,75947,75949,75951,75953,75955,75957,75959],{"class":2227,"line":4246},[2225,75908,6500],{"class":2231},[2225,75910,75802],{"class":2239},[2225,75912,1520],{"class":2235},[2225,75914,133],{"class":2291},[2225,75916,2789],{"class":2239},[2225,75918,2805],{"class":2235},[2225,75920,62700],{"class":2252},[2225,75922,2805],{"class":2235},[2225,75924,1381],{"class":2239},[2225,75926,1520],{"class":2235},[2225,75928,3161],{"class":2291},[2225,75930,2789],{"class":2239},[2225,75932,2805],{"class":2235},[2225,75934,75935],{"class":2252},"contract",[2225,75937,2805],{"class":2235},[2225,75939,2298],{"class":2235},[2225,75941,75942],{"class":2239}," pdf",[2225,75944,2298],{"class":2235},[2225,75946,2236],{"class":2235},[2225,75948,61013],{"class":2307},[2225,75950,2311],{"class":2235},[2225,75952,2249],{"class":2235},[2225,75954,61020],{"class":2252},[2225,75956,2805],{"class":2235},[2225,75958,2243],{"class":2235},[2225,75960,2362],{"class":2239},[2225,75962,75963],{"class":2227,"line":4253},[2225,75964,2285],{"emptyLinePlaceholder":2284},[2225,75966,75967,75969,75971,75973,75975,75977,75979,75981,75983,75985],{"class":2227,"line":4259},[2225,75968,6500],{"class":2231},[2225,75970,75802],{"class":2239},[2225,75972,1520],{"class":2235},[2225,75974,14606],{"class":2291},[2225,75976,2789],{"class":2239},[2225,75978,2805],{"class":2235},[2225,75980,62700],{"class":2252},[2225,75982,2805],{"class":2235},[2225,75984,2298],{"class":2235},[2225,75986,74707],{"class":2239},[2225,75988,75989,75991,75993,75995,75997,75999,76001,76003,76005,76008],{"class":2227,"line":6548},[2225,75990,6500],{"class":2231},[2225,75992,75802],{"class":2239},[2225,75994,1520],{"class":2235},[2225,75996,3158],{"class":2291},[2225,75998,2789],{"class":2239},[2225,76000,2805],{"class":2235},[2225,76002,62700],{"class":2252},[2225,76004,2805],{"class":2235},[2225,76006,76007],{"class":2239},")                ",[2225,76009,76010],{"class":2763},"\u002F\u002F → null — record gated\n",[2225,76012,76013,76015,76017,76019,76021,76023,76025,76027,76029,76031,76033,76035,76037,76039,76041,76043,76045],{"class":2227,"line":6576},[2225,76014,6500],{"class":2231},[2225,76016,75802],{"class":2239},[2225,76018,1520],{"class":2235},[2225,76020,133],{"class":2291},[2225,76022,2789],{"class":2239},[2225,76024,2805],{"class":2235},[2225,76026,62700],{"class":2252},[2225,76028,2805],{"class":2235},[2225,76030,1381],{"class":2239},[2225,76032,1520],{"class":2235},[2225,76034,3158],{"class":2291},[2225,76036,2789],{"class":2239},[2225,76038,2805],{"class":2235},[2225,76040,75935],{"class":2252},[2225,76042,2805],{"class":2235},[2225,76044,8495],{"class":2239},[2225,76046,76047],{"class":2763},"\u002F\u002F → null — the tier-0 blob surface is gated too\n",[937,76049,76050,76051,76053,76054,76056,76057,76059],{},"The cleared ",[941,76052,73914],{}," path — the ",[941,76055,21984],{}," analogue for attachments — sees through the gate; a ",[941,76058,21993],{}," re-homes the blob back to tier 0, where plain reads work again:",[2217,76061,76063],{"className":2219,"code":76062,"language":2221,"meta":1037,"style":1037},"const cleared = await contracts.blob('c1').atTier()\nawait cleared.get('contract')            \u002F\u002F → the PDF bytes\n\nawait contracts.demote('c1', PUBLIC)\nawait contracts.blob('c1').get('contract') \u002F\u002F → the PDF bytes, plain read restored\n",[941,76064,76065,76099,76124,76128,76151],{"__ignoreMap":1037},[2225,76066,76067,76069,76072,76074,76076,76078,76080,76082,76084,76086,76088,76090,76092,76094,76097],{"class":2227,"line":2228},[2225,76068,2775],{"class":2774},[2225,76070,76071],{"class":2239}," cleared ",[2225,76073,2781],{"class":2235},[2225,76075,2784],{"class":2231},[2225,76077,75802],{"class":2239},[2225,76079,1520],{"class":2235},[2225,76081,133],{"class":2291},[2225,76083,2789],{"class":2239},[2225,76085,2805],{"class":2235},[2225,76087,62700],{"class":2252},[2225,76089,2805],{"class":2235},[2225,76091,1381],{"class":2239},[2225,76093,1520],{"class":2235},[2225,76095,76096],{"class":2291},"atTier",[2225,76098,8146],{"class":2239},[2225,76100,76101,76103,76106,76108,76110,76112,76114,76116,76118,76121],{"class":2227,"line":1041},[2225,76102,6500],{"class":2231},[2225,76104,76105],{"class":2239}," cleared",[2225,76107,1520],{"class":2235},[2225,76109,3158],{"class":2291},[2225,76111,2789],{"class":2239},[2225,76113,2805],{"class":2235},[2225,76115,75935],{"class":2252},[2225,76117,2805],{"class":2235},[2225,76119,76120],{"class":2239},")            ",[2225,76122,76123],{"class":2763},"\u002F\u002F → the PDF bytes\n",[2225,76125,76126],{"class":2227,"line":1038},[2225,76127,2285],{"emptyLinePlaceholder":2284},[2225,76129,76130,76132,76134,76136,76138,76140,76142,76144,76146,76148],{"class":2227,"line":2288},[2225,76131,6500],{"class":2231},[2225,76133,75802],{"class":2239},[2225,76135,1520],{"class":2235},[2225,76137,14609],{"class":2291},[2225,76139,2789],{"class":2239},[2225,76141,2805],{"class":2235},[2225,76143,62700],{"class":2252},[2225,76145,2805],{"class":2235},[2225,76147,2298],{"class":2235},[2225,76149,76150],{"class":2239}," PUBLIC)\n",[2225,76152,76153,76155,76157,76159,76161,76163,76165,76167,76169,76171,76173,76175,76177,76179,76181,76183,76185],{"class":2227,"line":2304},[2225,76154,6500],{"class":2231},[2225,76156,75802],{"class":2239},[2225,76158,1520],{"class":2235},[2225,76160,133],{"class":2291},[2225,76162,2789],{"class":2239},[2225,76164,2805],{"class":2235},[2225,76166,62700],{"class":2252},[2225,76168,2805],{"class":2235},[2225,76170,1381],{"class":2239},[2225,76172,1520],{"class":2235},[2225,76174,3158],{"class":2291},[2225,76176,2789],{"class":2239},[2225,76178,2805],{"class":2235},[2225,76180,75935],{"class":2252},[2225,76182,2805],{"class":2235},[2225,76184,8495],{"class":2239},[2225,76186,76187],{"class":2763},"\u002F\u002F → the PDF bytes, plain read restored\n",[1022,76189,76191],{"id":76190},"right-to-erasure-across-tiers","Right-to-erasure across tiers",[937,76193,76194,76195,76198],{},"When an employee leaves, ",[941,76196,76197],{},"vault.forget(subject)"," crypto-shreds every record of that subject — honoring tier, so the tier-0 record and the elevated salary both go.",[2217,76200,76202],{"className":2219,"code":76201,"language":2221,"meta":1037,"style":1037},"await employees.put('e9', { employeeId: 'e9', name: 'Sam Rivera', role: 'Analyst', startDate: '2020-01-01' })\nawait salaries.putAtTier('e9', { employeeId: 'e9', amount: '99000', notes: 'departing' }, CONFIDENTIAL)\n\nconst result = await vault.forget('e9')\nresult.recordsShredded          \u002F\u002F → 2 (across both tiers)\nresult.collections              \u002F\u002F → includes 'employees' and 'salaries'\nawait employees.get('e9')       \u002F\u002F → null\nawait salaries.getAtTier('e9')  \u002F\u002F → null\n",[941,76203,76204,76280,76342,76346,76372,76384,76396,76420],{"__ignoreMap":1037},[2225,76205,76206,76208,76210,76212,76214,76216,76218,76221,76223,76225,76227,76229,76231,76233,76235,76237,76239,76241,76243,76245,76248,76250,76252,76254,76256,76258,76261,76263,76265,76267,76269,76271,76274,76276,76278],{"class":2227,"line":2228},[2225,76207,6500],{"class":2231},[2225,76209,74374],{"class":2239},[2225,76211,1520],{"class":2235},[2225,76213,3161],{"class":2291},[2225,76215,2789],{"class":2239},[2225,76217,2805],{"class":2235},[2225,76219,76220],{"class":2252},"e9",[2225,76222,2805],{"class":2235},[2225,76224,2298],{"class":2235},[2225,76226,2236],{"class":2235},[2225,76228,74085],{"class":2307},[2225,76230,2311],{"class":2235},[2225,76232,2249],{"class":2235},[2225,76234,76220],{"class":2252},[2225,76236,2805],{"class":2235},[2225,76238,2298],{"class":2235},[2225,76240,8104],{"class":2307},[2225,76242,2311],{"class":2235},[2225,76244,2249],{"class":2235},[2225,76246,76247],{"class":2252},"Sam Rivera",[2225,76249,2805],{"class":2235},[2225,76251,2298],{"class":2235},[2225,76253,34944],{"class":2307},[2225,76255,2311],{"class":2235},[2225,76257,2249],{"class":2235},[2225,76259,76260],{"class":2252},"Analyst",[2225,76262,2805],{"class":2235},[2225,76264,2298],{"class":2235},[2225,76266,74110],{"class":2307},[2225,76268,2311],{"class":2235},[2225,76270,2249],{"class":2235},[2225,76272,76273],{"class":2252},"2020-01-01",[2225,76275,2805],{"class":2235},[2225,76277,2243],{"class":2235},[2225,76279,2362],{"class":2239},[2225,76281,76282,76284,76286,76288,76290,76292,76294,76296,76298,76300,76302,76304,76306,76308,76310,76312,76314,76316,76318,76320,76323,76325,76327,76329,76331,76333,76336,76338,76340],{"class":2227,"line":1041},[2225,76283,6500],{"class":2231},[2225,76285,74388],{"class":2239},[2225,76287,1520],{"class":2235},[2225,76289,74455],{"class":2291},[2225,76291,2789],{"class":2239},[2225,76293,2805],{"class":2235},[2225,76295,76220],{"class":2252},[2225,76297,2805],{"class":2235},[2225,76299,2298],{"class":2235},[2225,76301,2236],{"class":2235},[2225,76303,74085],{"class":2307},[2225,76305,2311],{"class":2235},[2225,76307,2249],{"class":2235},[2225,76309,76220],{"class":2252},[2225,76311,2805],{"class":2235},[2225,76313,2298],{"class":2235},[2225,76315,6351],{"class":2307},[2225,76317,2311],{"class":2235},[2225,76319,2249],{"class":2235},[2225,76321,76322],{"class":2252},"99000",[2225,76324,2805],{"class":2235},[2225,76326,2298],{"class":2235},[2225,76328,42817],{"class":2307},[2225,76330,2311],{"class":2235},[2225,76332,2249],{"class":2235},[2225,76334,76335],{"class":2252},"departing",[2225,76337,2805],{"class":2235},[2225,76339,10965],{"class":2235},[2225,76341,74707],{"class":2239},[2225,76343,76344],{"class":2227,"line":1038},[2225,76345,2285],{"emptyLinePlaceholder":2284},[2225,76347,76348,76350,76352,76354,76356,76358,76360,76362,76364,76366,76368,76370],{"class":2227,"line":2288},[2225,76349,2775],{"class":2774},[2225,76351,36920],{"class":2239},[2225,76353,2781],{"class":2235},[2225,76355,2784],{"class":2231},[2225,76357,6466],{"class":2239},[2225,76359,1520],{"class":2235},[2225,76361,685],{"class":2291},[2225,76363,2789],{"class":2239},[2225,76365,2805],{"class":2235},[2225,76367,76220],{"class":2252},[2225,76369,2805],{"class":2235},[2225,76371,2362],{"class":2239},[2225,76373,76374,76376,76378,76381],{"class":2227,"line":2304},[2225,76375,11195],{"class":2239},[2225,76377,1520],{"class":2235},[2225,76379,76380],{"class":2239},"recordsShredded          ",[2225,76382,76383],{"class":2763},"\u002F\u002F → 2 (across both tiers)\n",[2225,76385,76386,76388,76390,76393],{"class":2227,"line":2356},[2225,76387,11195],{"class":2239},[2225,76389,1520],{"class":2235},[2225,76391,76392],{"class":2239},"collections              ",[2225,76394,76395],{"class":2763},"\u002F\u002F → includes 'employees' and 'salaries'\n",[2225,76397,76398,76400,76402,76404,76406,76408,76410,76412,76414,76417],{"class":2227,"line":2830},[2225,76399,6500],{"class":2231},[2225,76401,74374],{"class":2239},[2225,76403,1520],{"class":2235},[2225,76405,3158],{"class":2291},[2225,76407,2789],{"class":2239},[2225,76409,2805],{"class":2235},[2225,76411,76220],{"class":2252},[2225,76413,2805],{"class":2235},[2225,76415,76416],{"class":2239},")       ",[2225,76418,76419],{"class":2763},"\u002F\u002F → null\n",[2225,76421,76422,76424,76426,76428,76430,76432,76434,76436,76438,76440],{"class":2227,"line":2861},[2225,76423,6500],{"class":2231},[2225,76425,74388],{"class":2239},[2225,76427,1520],{"class":2235},[2225,76429,74787],{"class":2291},[2225,76431,2789],{"class":2239},[2225,76433,2805],{"class":2235},[2225,76435,76220],{"class":2252},[2225,76437,2805],{"class":2235},[2225,76439,27226],{"class":2239},[2225,76441,76419],{"class":2763},[937,76443,960,76444,76447,76448,997,76450,76452,76453,957],{},[941,76445,76446],{},"ForgetResult"," also carries the tier-specific residue channels — ",[941,76449,56955],{},[941,76451,56963],{},". They are empty here and populate only when a materialized view spans an elevated record (see ",[1007,76454,76456],{"href":76455},"\u002Fglossary\u002Fdeletion#erasure-residue","erasure residue",[1022,76458,2621],{"id":2620},[1111,76460,76461,76466,76473,76478],{},[1114,76462,76463,76465],{},[1007,76464,872],{"href":873}," — the record-tier system this recipe exercises end to end: elevate\u002Fdemote, tier DEKs, gating semantics, and the threat model",[1114,76467,76468,76470,76471],{},[1007,76469,621],{"href":622}," — tiered attachments and ",[941,76472,73911],{},[1114,76474,76475,76477],{},[1007,76476,685],{"href":686}," — crypto-shred and how it honors tier",[1114,76479,76480,76482],{},[1007,76481,673],{"href":674}," — the semantic encoder whose elevated records are skipped, not refused",[2645,76484,76485],{},"html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html pre.shiki code .sHdIc, html code.shiki .sHdIc{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#EEFFFF;--shiki-default-font-style:italic;--shiki-dark:#BABED8;--shiki-dark-font-style:italic}html pre.shiki code .sfNiH, html code.shiki .sfNiH{--shiki-light:#FF5370;--shiki-default:#FF9CAC;--shiki-dark:#FF9CAC}",{"title":1037,"searchDepth":1038,"depth":1038,"links":76487},[76488,76489,76490,76491,76492,76493,76494,76495,76496,76497],{"id":70092,"depth":1041,"text":70093},{"id":58275,"depth":1041,"text":58276},{"id":58393,"depth":1041,"text":58394},{"id":74444,"depth":1041,"text":74445},{"id":74803,"depth":1041,"text":74804},{"id":75101,"depth":1041,"text":75102},{"id":75363,"depth":1041,"text":75364},{"id":75693,"depth":1041,"text":75694},{"id":76190,"depth":1041,"text":76191},{"id":2620,"depth":1041,"text":2621},"An HR vault where salary, notes, and contracts sit one record tier above the public basics — gated reads, TTL elevation, delegation, and tiered blobs.",{},{"title":535,"description":76498},"s1fTWYO4my-kEFJOZnGchjc5mP_Gue7lkt0AovpPvm4",{"id":76503,"title":539,"access":12,"apiToc":929,"body":76504,"category":1042,"description":77882,"extension":1044,"meta":77883,"navigation":1042,"nest":1042,"path":540,"seo":77884,"sinceVersion":11337,"status":1049,"stem":541,"subtitle":1042,"tier":1050,"tocExternal":1042,"__hash__":77885},"docs\u002Frecipes\u002Fecho-secret-enrollment.md",{"type":931,"value":76505,"toc":77869},[76506,76509,76530,76542,76545,76547,76576,76578,76630,76634,76641,76647,76654,76657,76726,76737,76741,76744,76749,76752,76758,76764,76778,76781,76785,76788,76806,76812,76816,76836,76843,76993,77013,77017,77027,77111,77118,77161,77171,77175,77178,77186,77196,77208,77210,77213,77471,77473,77476,77481,77651,77657,77837,77840,77842,77866],[934,76507,539],{"id":76508},"composing-an-echo-secret",[1061,76510,76511],{},[937,76512,76513,76515,76516,76519,76520,76522,76523,28806,76525,76527,76528,1163],{},[950,76514,1500],{}," an app developer writing the enrollment screen for a vault in ",[1007,76517,76518],{"href":55885},"echo mode"," — and, through that screen, the person choosing the secret.\n",[950,76521,58222],{}," core only — ",[941,76524,1086],{},[941,76526,55889],{},". No satellite package, no ",[941,76529,943],{},[937,76531,76532,76533,76535,76536,76538,76539,76541],{},"An echo secret is one sentence, enrolled as three parts. The user types the first part (the ",[950,76534,47651],{},"), the vault answers with the second (the ",[950,76537,55896],{},"), the user recognizes it and types the third (the ",[950,76540,8364],{},"). That middle step is the whole point: a phishing page can copy your unlock screen pixel for pixel, but it cannot produce the middle of a sentence it has never seen.",[937,76543,76544],{},"Which makes the choice of sentence the load-bearing decision. The mechanism is only as good as the echo the user picks, and the failure mode is specific and common: people reach for autobiography, and autobiography is searchable. This page is the guidance to put in front of them.",[1022,76546,70093],{"id":70092},[1111,76548,76549,76564,76570],{},[1114,76550,76551,964,76554,46975,76556,76558,76559,76561,76562,1520],{},[950,76552,76553],{},"A vault in echo mode",[941,76555,55889],{},[941,76557,12780],{},". The mode is chosen at enrollment and recorded in the keyring; see ",[1007,76560,844],{"href":55885}," for the full API, the reveal policies, and how to migrate an existing vault with ",[941,76563,15216],{},[1114,76565,76566,76569],{},[950,76567,76568],{},"An enrollment screen with three fields."," Not one field with separators — the three parts are three inputs. A single-field entry for an echo vault cannot work and must not be offered.",[1114,76571,76572,76575],{},[950,76573,76574],{},"Nothing else."," No package to install, no strategy to wire in.",[1022,76577,58276],{"id":58275},[1338,76579,76580,76590],{},[1341,76581,76582],{},[1344,76583,76584,76587],{},[1347,76585,76586],{},"What the user gets",[1347,76588,76589],{},"Which part provides it",[1357,76591,76592,76602,76610,76620],{},[1344,76593,76594,76597],{},[1362,76595,76596],{},"A phishing page cannot complete the unlock",[1362,76598,3742,76599,76601],{},[950,76600,55896],{},", revealed by the vault before the user finishes",[1344,76603,76604,76607],{},[1362,76605,76606],{},"No single-string form to be tricked into typing",[1362,76608,76609],{},"the three-part key derivation — no concatenation is equivalent to it",[1344,76611,76612,76615],{},[1362,76613,76614],{},"A retrieval cue for a secret typed twice a year",[1362,76616,3742,76617,76619],{},[950,76618,55896],{},", which is the middle of the user's own sentence",[1344,76621,76622,76625],{},[1362,76623,76624],{},"Resistance to someone who guesses the first part",[1362,76626,3742,76627,76629],{},[950,76628,8364],{},", which the echo does not disclose",[1022,76631,76633],{"id":76632},"rule-1-one-sentence-split-in-three","Rule 1 — one sentence, split in three",[937,76635,76636,76637,76640],{},"Do not ask for three unrelated secrets. Ask for ",[950,76638,76639],{},"one sentence the user will still recognize in three years",", then split it at two natural pauses:",[2217,76642,76645],{"className":76643,"code":76644,"language":5672},[5670],"prompt   \"the part you type to start\"\necho     \"the part we will show you\"\nkey      \"the part you type to finish\"\n",[941,76646,76644],{"__ignoreMap":1037},[937,76648,76649,76650,76653],{},"A sentence survives in memory as a unit; three unrelated fragments do not. The split also has to fall where the sentence ",[1092,76651,76652],{},"bends",", so that seeing the echo makes the key feel inevitable rather than arbitrary.",[937,76655,76656],{},"Shape to aim for:",[2217,76658,76660],{"className":2219,"code":76659,"language":2221,"meta":1037,"style":1037},"const parts = {\n  prompt: 'mi chiamo vicio',\n  echo:   'ma da piccolo al santanna mi chiamavano',\n  key:    'ciccio',\n}\n",[941,76661,76662,76673,76689,76706,76722],{"__ignoreMap":1037},[2225,76663,76664,76666,76669,76671],{"class":2227,"line":2228},[2225,76665,2775],{"class":2774},[2225,76667,76668],{"class":2239}," parts ",[2225,76670,2781],{"class":2235},[2225,76672,2301],{"class":2235},[2225,76674,76675,76678,76680,76682,76685,76687],{"class":2227,"line":1041},[2225,76676,76677],{"class":2307},"  prompt",[2225,76679,2311],{"class":2235},[2225,76681,2249],{"class":2235},[2225,76683,76684],{"class":2252},"mi chiamo vicio",[2225,76686,2805],{"class":2235},[2225,76688,1707],{"class":2235},[2225,76690,76691,76694,76696,76699,76702,76704],{"class":2227,"line":1038},[2225,76692,76693],{"class":2307},"  echo",[2225,76695,2311],{"class":2235},[2225,76697,76698],{"class":2235},"   '",[2225,76700,76701],{"class":2252},"ma da piccolo al santanna mi chiamavano",[2225,76703,2805],{"class":2235},[2225,76705,1707],{"class":2235},[2225,76707,76708,76710,76712,76715,76718,76720],{"class":2227,"line":2288},[2225,76709,69281],{"class":2307},[2225,76711,2311],{"class":2235},[2225,76713,76714],{"class":2235},"    '",[2225,76716,76717],{"class":2252},"ciccio",[2225,76719,2805],{"class":2235},[2225,76721,1707],{"class":2235},[2225,76723,76724],{"class":2227,"line":2304},[2225,76725,7351],{"class":2235},[937,76727,76728,76729,76732,76733,76736],{},"That example is the right ",[950,76730,76731],{},"shape"," — a short opener, a longer middle that carries the recognition, a short ending that completes it — and deliberately the wrong ",[950,76734,76735],{},"content",". Every clause in it is a true biographical fact: a real name, a real school, a real childhood nickname. Which brings us to the rule that matters most.",[1022,76738,76740],{"id":76739},"rule-2-the-echo-must-be-unguessable-by-an-acquaintance","Rule 2 — the echo must be unguessable by an acquaintance",[937,76742,76743],{},"The echo is what an attacker most wants, because a guessed prompt yields a revealed echo, and an echo in hand makes a perfect imitation of your unlock screen possible. So the question to put to the user is not \"is this memorable?\" but:",[1061,76745,76746],{},[937,76747,76748],{},"Could someone who knows me — or someone who reads my public profiles for ten minutes — write this sentence?",[937,76750,76751],{},"If the answer is anything but a firm no, the sentence fails.",[937,76753,76754,76757],{},[950,76755,76756],{},"Never use"," (all of it is public, inferable, or asked over dinner): your name, a parent's or child's name, a school or university, a home town or street, a nickname anyone has used out loud, an employer, a wedding date, a pet's name, a sports team, a birthday.",[937,76759,76760,76763],{},[950,76761,76762],{},"Use instead",", either of:",[1111,76765,76766,76772],{},[1114,76767,76768,76771],{},[950,76769,76770],{},"A private memory"," — something true that was never told to anyone. The thought you had in a specific waiting room; what you were reading the night before a move; the thing you almost said and did not. True memories are durable, and there is nobody to have overheard them.",[1114,76773,76774,76777],{},[950,76775,76776],{},"A deliberately false fact"," — a real-sounding sentence that is not true of you. Invented, memorized once, and therefore unavailable to anyone researching your actual life. A false fact is often easier to keep private than a true one, because it never appears anywhere.",[937,76779,76780],{},"The hub cannot check any of this. No validator can distinguish a private memory from a published one, so the guidance lives here and on your enrollment screen, and nowhere in the code.",[1022,76782,76784],{"id":76783},"rule-3-longer-echo-better-echo","Rule 3 — longer echo, better echo",[937,76786,76787],{},"The echo carries the recognition, so length costs the user nothing at unlock — they read it, they do not type it. Length buys two things:",[1111,76789,76790,76796],{},[1114,76791,76792,76795],{},[950,76793,76794],{},"Recognition quality."," A seven-word middle is unmistakably yours; a two-word one might be anyone's.",[1114,76797,76798,76801,76802,76805],{},[950,76799,76800],{},"Resistance to offline guessing."," Anyone holding a copy of the keyring or pod file can test candidate echoes offline against the stored verifier, without limit. Each guess costs 600,000 PBKDF2 iterations, and there is no minimum-entropy floor on an echo — so the only thing standing between a short, ordinary echo and a determined file-holder is how ordinary it is. See the ",[1007,76803,13661],{"href":76804},"\u002Ffaq\u002Fthreat-model#a-phishing-page-imitating-the-unlock-screen"," for the full accounting.",[937,76807,76808,76809,76811],{},"Aim for a middle part of five words or more. Length in the ",[950,76810,8364],{}," matters for the same reason and is worth encouraging, though the key is typed, so there is a real trade against usability.",[1022,76813,76815],{"id":76814},"rule-4-write-it-as-a-natural-sentence","Rule 4 — write it as a natural sentence",[937,76817,76818,76819,76822,76823,968,76826,968,76829,968,76832,76835],{},"The validator was tuned for this: echo-mode parts allow words of ",[950,76820,76821],{},"one character or more",", relaxed from the standard phrase's three-character floor, precisely so that Romance-language function words — ",[1092,76824,76825],{},"mi",[1092,76827,76828],{},"da",[1092,76830,76831],{},"al",[1092,76833,76834],{},"è"," — do not get a valid sentence rejected. Word-count floors are unchanged: at least three words in the prompt, at least six across all three parts combined.",[937,76837,76838,76839,76842],{},"What has not been relaxed is the ",[950,76840,76841],{},"character pattern",": lowercase letters and single spaces only. No capitals, no digits, no punctuation. A sentence with a period or an apostrophe in it must lose them at enrollment:",[2217,76844,76846],{"className":2219,"code":76845,"language":2221,"meta":1037,"style":1037},"import { validateEchoSecret } from '@noy-db\u002Fhub'\n\nvalidateEchoSecret({\n  prompt: 'mi chiamo vicio',\n  echo: 'ma da piccolo al santanna mi chiamavano',\n  key: 'ciccio',\n})\n\u002F\u002F → { ok: true, words: 11 }\n\nvalidateEchoSecret({ prompt: 'ciao', echo: 'come stai oggi amico', key: 'bene grazie' })\n\u002F\u002F → { ok: false, reason: 'too-few-words', minimum: 3, got: 1 }   — the prompt is one word\n",[941,76847,76848,76867,76871,76880,76894,76908,76922,76928,76933,76937,76988],{"__ignoreMap":1037},[2225,76849,76850,76852,76854,76857,76859,76861,76863,76865],{"class":2227,"line":2228},[2225,76851,2232],{"class":2231},[2225,76853,2236],{"class":2235},[2225,76855,76856],{"class":2239}," validateEchoSecret",[2225,76858,2243],{"class":2235},[2225,76860,2246],{"class":2231},[2225,76862,2249],{"class":2235},[2225,76864,1086],{"class":2252},[2225,76866,2256],{"class":2235},[2225,76868,76869],{"class":2227,"line":1041},[2225,76870,2285],{"emptyLinePlaceholder":2284},[2225,76872,76873,76876,76878],{"class":2227,"line":1038},[2225,76874,76875],{"class":2291},"validateEchoSecret",[2225,76877,2789],{"class":2239},[2225,76879,4200],{"class":2235},[2225,76881,76882,76884,76886,76888,76890,76892],{"class":2227,"line":2288},[2225,76883,76677],{"class":2307},[2225,76885,2311],{"class":2235},[2225,76887,2249],{"class":2235},[2225,76889,76684],{"class":2252},[2225,76891,2805],{"class":2235},[2225,76893,1707],{"class":2235},[2225,76895,76896,76898,76900,76902,76904,76906],{"class":2227,"line":2304},[2225,76897,76693],{"class":2307},[2225,76899,2311],{"class":2235},[2225,76901,2249],{"class":2235},[2225,76903,76701],{"class":2252},[2225,76905,2805],{"class":2235},[2225,76907,1707],{"class":2235},[2225,76909,76910,76912,76914,76916,76918,76920],{"class":2227,"line":2356},[2225,76911,69281],{"class":2307},[2225,76913,2311],{"class":2235},[2225,76915,2249],{"class":2235},[2225,76917,76717],{"class":2252},[2225,76919,2805],{"class":2235},[2225,76921,1707],{"class":2235},[2225,76923,76924,76926],{"class":2227,"line":2830},[2225,76925,2359],{"class":2235},[2225,76927,2362],{"class":2239},[2225,76929,76930],{"class":2227,"line":2861},[2225,76931,76932],{"class":2763},"\u002F\u002F → { ok: true, words: 11 }\n",[2225,76934,76935],{"class":2227,"line":4246},[2225,76936,2285],{"emptyLinePlaceholder":2284},[2225,76938,76939,76941,76943,76945,76948,76950,76952,76955,76957,76959,76962,76964,76966,76969,76971,76973,76975,76977,76979,76982,76984,76986],{"class":2227,"line":4253},[2225,76940,76875],{"class":2291},[2225,76942,2789],{"class":2239},[2225,76944,2792],{"class":2235},[2225,76946,76947],{"class":2307}," prompt",[2225,76949,2311],{"class":2235},[2225,76951,2249],{"class":2235},[2225,76953,76954],{"class":2252},"ciao",[2225,76956,2805],{"class":2235},[2225,76958,2298],{"class":2235},[2225,76960,76961],{"class":2307}," echo",[2225,76963,2311],{"class":2235},[2225,76965,2249],{"class":2235},[2225,76967,76968],{"class":2252},"come stai oggi amico",[2225,76970,2805],{"class":2235},[2225,76972,2298],{"class":2235},[2225,76974,8153],{"class":2307},[2225,76976,2311],{"class":2235},[2225,76978,2249],{"class":2235},[2225,76980,76981],{"class":2252},"bene grazie",[2225,76983,2805],{"class":2235},[2225,76985,2243],{"class":2235},[2225,76987,2362],{"class":2239},[2225,76989,76990],{"class":2227,"line":4259},[2225,76991,76992],{"class":2763},"\u002F\u002F → { ok: false, reason: 'too-few-words', minimum: 3, got: 1 }   — the prompt is one word\n",[937,76994,76995,76996,76999,77000,77003,77004,77006,77007,56432,77009,77012],{},"Show the user ",[1092,76997,76998],{},"which part"," failed. ",[941,77001,77002],{},"assertStrongEchoSecret"," throws a ",[941,77005,20752],{}," carrying a ",[941,77008,43668],{},[941,77010,77011],{},"suggestion",", and the suggestion is prompt-specific when the prompt is what failed.",[1022,77014,77016],{"id":77015},"rule-5-set-a-mask-hint-and-keep-it-a-hint","Rule 5 — set a mask hint, and keep it a hint",[937,77018,77019,77020,77023,77024,2311],{},"The revealed echo appears on a screen, so it is shoulder-surfable. The keyring carries an optional ",[941,77021,77022],{},"echoMaskHint",", stored verbatim and handed back to the player as ",[941,77025,77026],{},"ceremony.maskHint",[2217,77028,77030],{"className":2219,"code":77029,"language":2221,"meta":1037,"style":1037},"const db = await createNoydb({\n  store, user: 'vicio', secretMode: 'echo', secret: parts,\n  echoMaskHint: 'first-letters',\n})\n",[941,77031,77032,77048,77089,77105],{"__ignoreMap":1037},[2225,77033,77034,77036,77038,77040,77042,77044,77046],{"class":2227,"line":2228},[2225,77035,2775],{"class":2774},[2225,77037,2778],{"class":2239},[2225,77039,2781],{"class":2235},[2225,77041,2784],{"class":2231},[2225,77043,2744],{"class":2291},[2225,77045,2789],{"class":2239},[2225,77047,4200],{"class":2235},[2225,77049,77050,77052,77054,77056,77058,77060,77063,77065,77067,77070,77072,77074,77076,77078,77080,77082,77084,77087],{"class":2227,"line":1041},[2225,77051,15608],{"class":2239},[2225,77053,2298],{"class":2235},[2225,77055,2795],{"class":2307},[2225,77057,2311],{"class":2235},[2225,77059,2249],{"class":2235},[2225,77061,77062],{"class":2252},"vicio",[2225,77064,2805],{"class":2235},[2225,77066,2298],{"class":2235},[2225,77068,77069],{"class":2307}," secretMode",[2225,77071,2311],{"class":2235},[2225,77073,2249],{"class":2235},[2225,77075,55896],{"class":2252},[2225,77077,2805],{"class":2235},[2225,77079,2298],{"class":2235},[2225,77081,2810],{"class":2307},[2225,77083,2311],{"class":2235},[2225,77085,77086],{"class":2239}," parts",[2225,77088,1707],{"class":2235},[2225,77090,77091,77094,77096,77098,77101,77103],{"class":2227,"line":1038},[2225,77092,77093],{"class":2307},"  echoMaskHint",[2225,77095,2311],{"class":2235},[2225,77097,2249],{"class":2235},[2225,77099,77100],{"class":2252},"first-letters",[2225,77102,2805],{"class":2235},[2225,77104,1707],{"class":2235},[2225,77106,77107,77109],{"class":2227,"line":2288},[2225,77108,2359],{"class":2235},[2225,77110,2362],{"class":2239},[937,77112,77113,77114,77117],{},"The hub attaches ",[950,77115,77116],{},"no meaning"," to the string — it neither validates nor interprets it. That makes the vocabulary yours, so pick a small one and use it consistently across every surface that opens your vaults. A workable set:",[1338,77119,77120,77130],{},[1341,77121,77122],{},[1344,77123,77124,77127],{},[1347,77125,77126],{},"Hint",[1347,77128,77129],{},"What a player renders",[1357,77131,77132,77141,77151],{},[1344,77133,77134,77138],{},[1362,77135,77136],{},[941,77137,77100],{},[1362,77139,77140],{},"the first letter of each word, the rest as dots — enough to recognize, little to steal",[1344,77142,77143,77148],{},[1362,77144,77145],{},[941,77146,77147],{},"full",[1362,77149,77150],{},"the echo in full; for a private device, or when the user asked for it",[1344,77152,77153,77158],{},[1362,77154,77155],{},[941,77156,77157],{},"tap-to-reveal",[1362,77159,77160],{},"masked until the user deliberately reveals it",[937,77162,77163,77164,77167,77168,77170],{},"Two constraints on whatever vocabulary you choose. The hint is ",[950,77165,77166],{},"plaintext keyring metadata",", like the salt — so it must never contain any part of the echo. And it is a ",[1092,77169,39047],{},": a player that does not recognize your vocabulary still has to mask by its own judgment rather than fall back to showing everything.",[1022,77172,77174],{"id":77173},"rule-6-teach-the-warning-and-mean-it","Rule 6 — teach the warning, and mean it",[937,77176,77177],{},"Enrollment is where the user learns what the ceremony is for. One sentence on the screen, and it has to be unambiguous:",[1061,77179,77180],{},[937,77181,77182,77183,77185],{},"If the app ever shows you the wrong middle part — or shows you none at all — ",[950,77184,27604],{},". Do not type the last part. Close it, open the official app yourself, and change your secret.",[937,77187,77188,77189,2462,77192,77195],{},"Then make your own application obey it. When a user does not recognize the echo, the ceremony is over: show the warning, do not call ",[941,77190,77191],{},"complete()",[950,77193,77194],{},"do not"," offer a \"skip this step\" or \"verification unavailable\" affordance. An escape hatch is the only thing that can turn a defeated phishing attempt back into a successful one — and once one application ships one, every phishing page will imitate it.",[937,77197,77198,77199,77202,77203,77207],{},"Rate limiting is on you too: the hub keeps no attempt counter, so repeated ",[941,77200,77201],{},"WrongPromptError","s should trigger your own backoff or lockout (",[1007,77204,77205],{"href":330},[941,77206,15984],{}," covers the ready-made version).",[1022,77209,58394],{"id":58393},[937,77211,77212],{},"The complete enrollment call, with everything above applied:",[2217,77214,77216],{"className":2219,"code":77215,"language":2221,"meta":1037,"style":1037},"import { createNoydb, assertStrongEchoSecret } from '@noy-db\u002Fhub'\nimport { toBrowserIdb } from '@noy-db\u002Fto-browser-idb'\n\nconst parts = {\n  prompt: collected.prompt,   \u002F\u002F three separate inputs, never one field\n  echo: collected.echo,\n  key: collected.key,\n}\n\nassertStrongEchoSecret(parts)   \u002F\u002F surface reason + suggestion in the form, before creating anything\n\nconst db = await createNoydb({\n  store: toBrowserIdb(),\n  user: 'vicio',\n  secretMode: 'echo',\n  secret: parts,\n  echoMaskHint: 'first-letters',\n  validateSecret: true,\n})\n\nconst vault = await db.openVault('acme')   \u002F\u002F the keyring and its echo block are written here\n",[941,77217,77218,77241,77259,77263,77273,77291,77305,77319,77323,77327,77337,77341,77357,77369,77383,77397,77407,77421,77432,77438,77442],{"__ignoreMap":1037},[2225,77219,77220,77222,77224,77226,77228,77231,77233,77235,77237,77239],{"class":2227,"line":2228},[2225,77221,2232],{"class":2231},[2225,77223,2236],{"class":2235},[2225,77225,2744],{"class":2239},[2225,77227,2298],{"class":2235},[2225,77229,77230],{"class":2239}," assertStrongEchoSecret",[2225,77232,2243],{"class":2235},[2225,77234,2246],{"class":2231},[2225,77236,2249],{"class":2235},[2225,77238,1086],{"class":2252},[2225,77240,2256],{"class":2235},[2225,77242,77243,77245,77247,77249,77251,77253,77255,77257],{"class":2227,"line":1041},[2225,77244,2232],{"class":2231},[2225,77246,2236],{"class":2235},[2225,77248,34763],{"class":2239},[2225,77250,2243],{"class":2235},[2225,77252,2246],{"class":2231},[2225,77254,2249],{"class":2235},[2225,77256,9573],{"class":2252},[2225,77258,2256],{"class":2235},[2225,77260,77261],{"class":2227,"line":1038},[2225,77262,2285],{"emptyLinePlaceholder":2284},[2225,77264,77265,77267,77269,77271],{"class":2227,"line":2288},[2225,77266,2775],{"class":2774},[2225,77268,76668],{"class":2239},[2225,77270,2781],{"class":2235},[2225,77272,2301],{"class":2235},[2225,77274,77275,77277,77279,77282,77284,77286,77288],{"class":2227,"line":2304},[2225,77276,76677],{"class":2307},[2225,77278,2311],{"class":2235},[2225,77280,77281],{"class":2239}," collected",[2225,77283,1520],{"class":2235},[2225,77285,47651],{"class":2239},[2225,77287,2298],{"class":2235},[2225,77289,77290],{"class":2763},"   \u002F\u002F three separate inputs, never one field\n",[2225,77292,77293,77295,77297,77299,77301,77303],{"class":2227,"line":2356},[2225,77294,76693],{"class":2307},[2225,77296,2311],{"class":2235},[2225,77298,77281],{"class":2239},[2225,77300,1520],{"class":2235},[2225,77302,55896],{"class":2239},[2225,77304,1707],{"class":2235},[2225,77306,77307,77309,77311,77313,77315,77317],{"class":2227,"line":2830},[2225,77308,69281],{"class":2307},[2225,77310,2311],{"class":2235},[2225,77312,77281],{"class":2239},[2225,77314,1520],{"class":2235},[2225,77316,8364],{"class":2239},[2225,77318,1707],{"class":2235},[2225,77320,77321],{"class":2227,"line":2861},[2225,77322,7351],{"class":2235},[2225,77324,77325],{"class":2227,"line":4246},[2225,77326,2285],{"emptyLinePlaceholder":2284},[2225,77328,77329,77331,77334],{"class":2227,"line":4253},[2225,77330,77002],{"class":2291},[2225,77332,77333],{"class":2239},"(parts)   ",[2225,77335,77336],{"class":2763},"\u002F\u002F surface reason + suggestion in the form, before creating anything\n",[2225,77338,77339],{"class":2227,"line":4259},[2225,77340,2285],{"emptyLinePlaceholder":2284},[2225,77342,77343,77345,77347,77349,77351,77353,77355],{"class":2227,"line":6548},[2225,77344,2775],{"class":2774},[2225,77346,2778],{"class":2239},[2225,77348,2781],{"class":2235},[2225,77350,2784],{"class":2231},[2225,77352,2744],{"class":2291},[2225,77354,2789],{"class":2239},[2225,77356,4200],{"class":2235},[2225,77358,77359,77361,77363,77365,77367],{"class":2227,"line":6576},[2225,77360,15608],{"class":2307},[2225,77362,2311],{"class":2235},[2225,77364,34763],{"class":2291},[2225,77366,4228],{"class":2239},[2225,77368,1707],{"class":2235},[2225,77370,77371,77373,77375,77377,77379,77381],{"class":2227,"line":6582},[2225,77372,4205],{"class":2307},[2225,77374,2311],{"class":2235},[2225,77376,2249],{"class":2235},[2225,77378,77062],{"class":2252},[2225,77380,2805],{"class":2235},[2225,77382,1707],{"class":2235},[2225,77384,77385,77387,77389,77391,77393,77395],{"class":2227,"line":6587},[2225,77386,71905],{"class":2307},[2225,77388,2311],{"class":2235},[2225,77390,2249],{"class":2235},[2225,77392,55896],{"class":2252},[2225,77394,2805],{"class":2235},[2225,77396,1707],{"class":2235},[2225,77398,77399,77401,77403,77405],{"class":2227,"line":7174},[2225,77400,10423],{"class":2307},[2225,77402,2311],{"class":2235},[2225,77404,77086],{"class":2239},[2225,77406,1707],{"class":2235},[2225,77408,77409,77411,77413,77415,77417,77419],{"class":2227,"line":7180},[2225,77410,77093],{"class":2307},[2225,77412,2311],{"class":2235},[2225,77414,2249],{"class":2235},[2225,77416,77100],{"class":2252},[2225,77418,2805],{"class":2235},[2225,77420,1707],{"class":2235},[2225,77422,77423,77426,77428,77430],{"class":2227,"line":7220},[2225,77424,77425],{"class":2307},"  validateSecret",[2225,77427,2311],{"class":2235},[2225,77429,2322],{"class":2321},[2225,77431,1707],{"class":2235},[2225,77433,77434,77436],{"class":2227,"line":7225},[2225,77435,2359],{"class":2235},[2225,77437,2362],{"class":2239},[2225,77439,77440],{"class":2227,"line":7231},[2225,77441,2285],{"emptyLinePlaceholder":2284},[2225,77443,77444,77446,77448,77450,77452,77454,77456,77458,77460,77462,77464,77466,77468],{"class":2227,"line":7267},[2225,77445,2775],{"class":2774},[2225,77447,2835],{"class":2239},[2225,77449,2781],{"class":2235},[2225,77451,2784],{"class":2231},[2225,77453,2842],{"class":2239},[2225,77455,1520],{"class":2235},[2225,77457,2847],{"class":2291},[2225,77459,2789],{"class":2239},[2225,77461,2805],{"class":2235},[2225,77463,6450],{"class":2252},[2225,77465,2805],{"class":2235},[2225,77467,71781],{"class":2239},[2225,77469,77470],{"class":2763},"\u002F\u002F the keyring and its echo block are written here\n",[1022,77472,71414],{"id":18051},[937,77474,77475],{},"Two checks, both worth running once against your own enrollment screen.",[937,77477,77478],{},[950,77479,77480],{},"The vault reveals exactly what you enrolled:",[2217,77482,77484],{"className":2219,"code":77483,"language":2221,"meta":1037,"style":1037},"import { beginEchoUnlock } from '@noy-db\u002Fhub'\n\nconst ceremony = await beginEchoUnlock(store, 'acme', { userId: 'vicio', prompt: parts.prompt })\nconsole.log(ceremony.reveal === parts.echo)   \u002F\u002F → true\nconsole.log(ceremony.maskHint)                \u002F\u002F → 'first-letters'\n\nconst unlocked = await ceremony.complete({ key: parts.key })\n",[941,77485,77486,77505,77509,77563,77591,77609,77613],{"__ignoreMap":1037},[2225,77487,77488,77490,77492,77495,77497,77499,77501,77503],{"class":2227,"line":2228},[2225,77489,2232],{"class":2231},[2225,77491,2236],{"class":2235},[2225,77493,77494],{"class":2239}," beginEchoUnlock",[2225,77496,2243],{"class":2235},[2225,77498,2246],{"class":2231},[2225,77500,2249],{"class":2235},[2225,77502,1086],{"class":2252},[2225,77504,2256],{"class":2235},[2225,77506,77507],{"class":2227,"line":1041},[2225,77508,2285],{"emptyLinePlaceholder":2284},[2225,77510,77511,77513,77516,77518,77520,77522,77524,77526,77528,77530,77532,77534,77536,77538,77540,77542,77544,77546,77548,77550,77552,77554,77556,77559,77561],{"class":2227,"line":1038},[2225,77512,2775],{"class":2774},[2225,77514,77515],{"class":2239}," ceremony ",[2225,77517,2781],{"class":2235},[2225,77519,2784],{"class":2231},[2225,77521,77494],{"class":2291},[2225,77523,39702],{"class":2239},[2225,77525,2298],{"class":2235},[2225,77527,2249],{"class":2235},[2225,77529,6450],{"class":2252},[2225,77531,2805],{"class":2235},[2225,77533,2298],{"class":2235},[2225,77535,2236],{"class":2235},[2225,77537,10668],{"class":2307},[2225,77539,2311],{"class":2235},[2225,77541,2249],{"class":2235},[2225,77543,77062],{"class":2252},[2225,77545,2805],{"class":2235},[2225,77547,2298],{"class":2235},[2225,77549,76947],{"class":2307},[2225,77551,2311],{"class":2235},[2225,77553,77086],{"class":2239},[2225,77555,1520],{"class":2235},[2225,77557,77558],{"class":2239},"prompt ",[2225,77560,2359],{"class":2235},[2225,77562,2362],{"class":2239},[2225,77564,77565,77567,77569,77571,77574,77576,77579,77581,77583,77585,77588],{"class":2227,"line":2288},[2225,77566,28031],{"class":2239},[2225,77568,1520],{"class":2235},[2225,77570,27100],{"class":2291},[2225,77572,77573],{"class":2239},"(ceremony",[2225,77575,1520],{"class":2235},[2225,77577,77578],{"class":2239},"reveal ",[2225,77580,33083],{"class":2235},[2225,77582,77086],{"class":2239},[2225,77584,1520],{"class":2235},[2225,77586,77587],{"class":2239},"echo)   ",[2225,77589,77590],{"class":2763},"\u002F\u002F → true\n",[2225,77592,77593,77595,77597,77599,77601,77603,77606],{"class":2227,"line":2304},[2225,77594,28031],{"class":2239},[2225,77596,1520],{"class":2235},[2225,77598,27100],{"class":2291},[2225,77600,77573],{"class":2239},[2225,77602,1520],{"class":2235},[2225,77604,77605],{"class":2239},"maskHint)                ",[2225,77607,77608],{"class":2763},"\u002F\u002F → 'first-letters'\n",[2225,77610,77611],{"class":2227,"line":2356},[2225,77612,2285],{"emptyLinePlaceholder":2284},[2225,77614,77615,77617,77620,77622,77624,77627,77629,77632,77634,77636,77638,77640,77642,77644,77647,77649],{"class":2227,"line":2830},[2225,77616,2775],{"class":2774},[2225,77618,77619],{"class":2239}," unlocked ",[2225,77621,2781],{"class":2235},[2225,77623,2784],{"class":2231},[2225,77625,77626],{"class":2239}," ceremony",[2225,77628,1520],{"class":2235},[2225,77630,77631],{"class":2291},"complete",[2225,77633,2789],{"class":2239},[2225,77635,2792],{"class":2235},[2225,77637,8153],{"class":2307},[2225,77639,2311],{"class":2235},[2225,77641,77086],{"class":2239},[2225,77643,1520],{"class":2235},[2225,77645,77646],{"class":2239},"key ",[2225,77648,2359],{"class":2235},[2225,77650,2362],{"class":2239},[937,77652,77653,77656],{},[950,77654,77655],{},"No joined string opens it"," — the property the whole design rests on:",[2217,77658,77660],{"className":2219,"code":77659,"language":2221,"meta":1037,"style":1037},"import { EchoCeremonyRequiredError } from '@noy-db\u002Fhub'\n\nconst joined = `${parts.prompt}#${parts.echo}#${parts.key}`\nconst wrong = await createNoydb({ store, user: 'vicio', secret: joined })\n\ntry {\n  await wrong.openVault('acme')\n} catch (err) {\n  console.log(err instanceof EchoCeremonyRequiredError)   \u002F\u002F → true\n}\n",[941,77661,77662,77681,77685,77729,77772,77776,77782,77803,77813,77833],{"__ignoreMap":1037},[2225,77663,77664,77666,77668,77671,77673,77675,77677,77679],{"class":2227,"line":2228},[2225,77665,2232],{"class":2231},[2225,77667,2236],{"class":2235},[2225,77669,77670],{"class":2239}," EchoCeremonyRequiredError",[2225,77672,2243],{"class":2235},[2225,77674,2246],{"class":2231},[2225,77676,2249],{"class":2235},[2225,77678,1086],{"class":2252},[2225,77680,2256],{"class":2235},[2225,77682,77683],{"class":2227,"line":1041},[2225,77684,2285],{"emptyLinePlaceholder":2284},[2225,77686,77687,77689,77692,77694,77696,77699,77701,77703,77705,77707,77709,77711,77713,77715,77717,77719,77721,77723,77725,77727],{"class":2227,"line":1038},[2225,77688,2775],{"class":2774},[2225,77690,77691],{"class":2239}," joined ",[2225,77693,2781],{"class":2235},[2225,77695,8188],{"class":2235},[2225,77697,77698],{"class":2239},"parts",[2225,77700,1520],{"class":2235},[2225,77702,47651],{"class":2239},[2225,77704,2359],{"class":2235},[2225,77706,3096],{"class":2252},[2225,77708,8197],{"class":2235},[2225,77710,77698],{"class":2239},[2225,77712,1520],{"class":2235},[2225,77714,55896],{"class":2239},[2225,77716,2359],{"class":2235},[2225,77718,3096],{"class":2252},[2225,77720,8197],{"class":2235},[2225,77722,77698],{"class":2239},[2225,77724,1520],{"class":2235},[2225,77726,8364],{"class":2239},[2225,77728,8211],{"class":2235},[2225,77730,77731,77733,77736,77738,77740,77742,77744,77746,77748,77750,77752,77754,77756,77758,77760,77762,77764,77766,77768,77770],{"class":2227,"line":2288},[2225,77732,2775],{"class":2774},[2225,77734,77735],{"class":2239}," wrong ",[2225,77737,2781],{"class":2235},[2225,77739,2784],{"class":2231},[2225,77741,2744],{"class":2291},[2225,77743,2789],{"class":2239},[2225,77745,2792],{"class":2235},[2225,77747,7564],{"class":2239},[2225,77749,2298],{"class":2235},[2225,77751,2795],{"class":2307},[2225,77753,2311],{"class":2235},[2225,77755,2249],{"class":2235},[2225,77757,77062],{"class":2252},[2225,77759,2805],{"class":2235},[2225,77761,2298],{"class":2235},[2225,77763,2810],{"class":2307},[2225,77765,2311],{"class":2235},[2225,77767,77691],{"class":2239},[2225,77769,2359],{"class":2235},[2225,77771,2362],{"class":2239},[2225,77773,77774],{"class":2227,"line":2304},[2225,77775,2285],{"emptyLinePlaceholder":2284},[2225,77777,77778,77780],{"class":2227,"line":2356},[2225,77779,7554],{"class":2231},[2225,77781,2301],{"class":2235},[2225,77783,77784,77786,77789,77791,77793,77795,77797,77799,77801],{"class":2227,"line":2830},[2225,77785,7561],{"class":2231},[2225,77787,77788],{"class":2239}," wrong",[2225,77790,1520],{"class":2235},[2225,77792,2847],{"class":2291},[2225,77794,2789],{"class":2307},[2225,77796,2805],{"class":2235},[2225,77798,6450],{"class":2252},[2225,77800,2805],{"class":2235},[2225,77802,2362],{"class":2307},[2225,77804,77805,77807,77809,77811],{"class":2227,"line":2861},[2225,77806,2359],{"class":2235},[2225,77808,7598],{"class":2231},[2225,77810,7601],{"class":2239},[2225,77812,4200],{"class":2235},[2225,77814,77815,77817,77819,77821,77823,77825,77827,77829,77831],{"class":2227,"line":4246},[2225,77816,27095],{"class":2239},[2225,77818,1520],{"class":2235},[2225,77820,27100],{"class":2291},[2225,77822,2789],{"class":2307},[2225,77824,7617],{"class":2239},[2225,77826,19172],{"class":2235},[2225,77828,77670],{"class":6342},[2225,77830,71781],{"class":2307},[2225,77832,77590],{"class":2763},[2225,77834,77835],{"class":2227,"line":4253},[2225,77836,7351],{"class":2235},[937,77838,77839],{},"Any separator behaves the same way, because no single string is equivalent to the three-part derivation. If your unlock screen has a field that would accept that string, it is a phishing flow you shipped yourself — remove it.",[1022,77841,2621],{"id":2620},[1111,77843,77844,77849,77854,77859],{},[1114,77845,77846,77848],{},[1007,77847,844],{"href":55885}," — the full echo API: reveal policies, the ceremony, rotation and migration, pod recipients, errors.",[1114,77850,77851,77853],{},[1007,77852,463],{"href":76804}," — attacker grading, and what echo mode does not hide.",[1114,77855,77856,77858],{},[1007,77857,589],{"href":590}," — how tier 1 composes with passkeys, session policy, and recovery codes.",[1114,77860,77861,77865],{},[1007,77862,77863],{"href":330},[941,77864,15984],{}," — lockout and duress handling around the unlock ceremony.",[2645,77867,77868],{},"html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html pre.shiki code .sfNiH, html code.shiki .sfNiH{--shiki-light:#FF5370;--shiki-default:#FF9CAC;--shiki-dark:#FF9CAC}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}",{"title":1037,"searchDepth":1038,"depth":1038,"links":77870},[77871,77872,77873,77874,77875,77876,77877,77878,77879,77880,77881],{"id":70092,"depth":1041,"text":70093},{"id":58275,"depth":1041,"text":58276},{"id":76632,"depth":1041,"text":76633},{"id":76739,"depth":1041,"text":76740},{"id":76783,"depth":1041,"text":76784},{"id":76814,"depth":1041,"text":76815},{"id":77015,"depth":1041,"text":77016},{"id":77173,"depth":1041,"text":77174},{"id":58393,"depth":1041,"text":58394},{"id":18051,"depth":1041,"text":71414},{"id":2620,"depth":1041,"text":2621},"How to choose the three parts of an echo secret — one memorable sentence, an echo no acquaintance could guess, and the warning users must be taught.",{},{"title":539,"description":77882},"eUnxA46ALL9sIEaisTPfZCU_Whp5JUlFktikgP_qve4",{"id":77887,"title":543,"access":12,"apiToc":929,"body":77888,"category":1042,"description":80334,"extension":1044,"meta":80335,"navigation":1042,"nest":1042,"path":544,"seo":80336,"sinceVersion":1042,"status":1049,"stem":545,"subtitle":1042,"tier":1050,"tocExternal":1042,"__hash__":80337},"docs\u002Frecipes\u002Femail-archive.md",{"type":931,"value":77889,"toc":80325},[77890,77893,77908,77921,77931,77933,77943,78131,78135,78145,78151,78171,78175,78389,78401,78405,78852,78866,78870,78876,79045,80259,80263,80281,80301,80303,80322],[934,77891,543],{"id":77892},"email-archive",[1061,77894,77895],{},[937,77896,77897,77899,77900,77903,77904,58223,77906,1520],{},[950,77898,1500],{}," apps that ingest raw MIME email (",[941,77901,77902],{},".eml",") and need to query metadata, serve attachments, and keep forensic originals — inboxes, archives, compliance systems.\n",[950,77905,58222],{},[941,77907,6775],{},[937,77909,77910,77911,77913,77914,77916,77917,77920],{},"You receive raw MIME emails (",[941,77912,77902],{}," files) and want three things at once:\nquery the metadata (from, to, subject, thread) as structured data, serve\nthe bodies and attachments as files, and keep the original bytes for\nforensic re-parsing. This recipe shows how to lay that out in noy-db —\nan encrypted, offline-first document store where structured data lives\nin queryable ",[950,77915,9136],{}," and binary payloads live in ",[950,77918,77919],{},"blob slots","\nattached to those records.",[937,77922,77923,77924,77927,77928,77930],{},"It assumes you know the basics of\n",[1007,77925,77926],{"href":58262},"vaults and collections"," and uses\nthe ",[1007,77929,72482],{"href":61714}," for attachments.",[1022,77932,58394],{"id":58393},[937,77934,77935,77936,77939,77940,77942],{},"Blobs are an opt-in service: pass ",[941,77937,77938],{},"blobsStrategy: withBlobs()"," to\n",[941,77941,12780],{},". Everything else in this recipe is core.",[2217,77944,77946],{"className":2219,"code":77945,"language":2221,"meta":1037,"style":1037},"import { createNoydb } from '@noy-db\u002Fhub'\nimport { withBlobs } from '@noy-db\u002Fhub\u002Fblobs'\nimport { toFile } from '@noy-db\u002Fto-file'\n\nconst db = await createNoydb({\n  store: toFile({ dir: '.\u002Fmail-data' }),\n  user: 'me',\n  secret: 'user-supplied secret',\n  blobsStrategy: withBlobs(),        \u002F\u002F enables collection.blob(id)\n})\n\nconst vault = await db.openVault('mail')\n",[941,77947,77948,77966,77984,78002,78006,78022,78051,78065,78080,78095,78101,78105],{"__ignoreMap":1037},[2225,77949,77950,77952,77954,77956,77958,77960,77962,77964],{"class":2227,"line":2228},[2225,77951,2232],{"class":2231},[2225,77953,2236],{"class":2235},[2225,77955,2744],{"class":2239},[2225,77957,2243],{"class":2235},[2225,77959,2246],{"class":2231},[2225,77961,2249],{"class":2235},[2225,77963,1086],{"class":2252},[2225,77965,2256],{"class":2235},[2225,77967,77968,77970,77972,77974,77976,77978,77980,77982],{"class":2227,"line":1041},[2225,77969,2232],{"class":2231},[2225,77971,2236],{"class":2235},[2225,77973,58485],{"class":2239},[2225,77975,2243],{"class":2235},[2225,77977,2246],{"class":2231},[2225,77979,2249],{"class":2235},[2225,77981,58494],{"class":2252},[2225,77983,2256],{"class":2235},[2225,77985,77986,77988,77990,77992,77994,77996,77998,78000],{"class":2227,"line":1038},[2225,77987,2232],{"class":2231},[2225,77989,2236],{"class":2235},[2225,77991,15573],{"class":2239},[2225,77993,2243],{"class":2235},[2225,77995,2246],{"class":2231},[2225,77997,2249],{"class":2235},[2225,77999,9551],{"class":2252},[2225,78001,2256],{"class":2235},[2225,78003,78004],{"class":2227,"line":2288},[2225,78005,2285],{"emptyLinePlaceholder":2284},[2225,78007,78008,78010,78012,78014,78016,78018,78020],{"class":2227,"line":2304},[2225,78009,2775],{"class":2774},[2225,78011,2778],{"class":2239},[2225,78013,2781],{"class":2235},[2225,78015,2784],{"class":2231},[2225,78017,2744],{"class":2291},[2225,78019,2789],{"class":2239},[2225,78021,4200],{"class":2235},[2225,78023,78024,78026,78028,78030,78032,78034,78036,78038,78040,78043,78045,78047,78049],{"class":2227,"line":2356},[2225,78025,15608],{"class":2307},[2225,78027,2311],{"class":2235},[2225,78029,15573],{"class":2291},[2225,78031,2789],{"class":2239},[2225,78033,2792],{"class":2235},[2225,78035,15619],{"class":2307},[2225,78037,2311],{"class":2235},[2225,78039,2249],{"class":2235},[2225,78041,78042],{"class":2252},".\u002Fmail-data",[2225,78044,2805],{"class":2235},[2225,78046,2243],{"class":2235},[2225,78048,1381],{"class":2239},[2225,78050,1707],{"class":2235},[2225,78052,78053,78055,78057,78059,78061,78063],{"class":2227,"line":2830},[2225,78054,4205],{"class":2307},[2225,78056,2311],{"class":2235},[2225,78058,2249],{"class":2235},[2225,78060,4212],{"class":2252},[2225,78062,2805],{"class":2235},[2225,78064,1707],{"class":2235},[2225,78066,78067,78069,78071,78073,78076,78078],{"class":2227,"line":2861},[2225,78068,10423],{"class":2307},[2225,78070,2311],{"class":2235},[2225,78072,2249],{"class":2235},[2225,78074,78075],{"class":2252},"user-supplied secret",[2225,78077,2805],{"class":2235},[2225,78079,1707],{"class":2235},[2225,78081,78082,78084,78086,78088,78090,78092],{"class":2227,"line":4246},[2225,78083,58728],{"class":2307},[2225,78085,2311],{"class":2235},[2225,78087,58485],{"class":2291},[2225,78089,4228],{"class":2239},[2225,78091,2298],{"class":2235},[2225,78093,78094],{"class":2763},"        \u002F\u002F enables collection.blob(id)\n",[2225,78096,78097,78099],{"class":2227,"line":4253},[2225,78098,2359],{"class":2235},[2225,78100,2362],{"class":2239},[2225,78102,78103],{"class":2227,"line":4259},[2225,78104,2285],{"emptyLinePlaceholder":2284},[2225,78106,78107,78109,78111,78113,78115,78117,78119,78121,78123,78125,78127,78129],{"class":2227,"line":6548},[2225,78108,2775],{"class":2774},[2225,78110,2835],{"class":2239},[2225,78112,2781],{"class":2235},[2225,78114,2784],{"class":2231},[2225,78116,2842],{"class":2239},[2225,78118,1520],{"class":2235},[2225,78120,2847],{"class":2291},[2225,78122,2789],{"class":2239},[2225,78124,2805],{"class":2235},[2225,78126,57433],{"class":2252},[2225,78128,2805],{"class":2235},[2225,78130,2362],{"class":2239},[1022,78132,78134],{"id":78133},"short-answer","Short answer",[937,78136,78137,78140,78141,78144],{},[950,78138,78139],{},"An email is ONE entity with many facets."," Model it as one ",[941,78142,78143],{},"Email","\nrecord plus N+2 blob slots on that same record:",[2217,78146,78149],{"className":78147,"code":78148,"language":5672},[5670],"emails\u002F\u003Cemail-id>        ← the record (structured fields)\n  └─ blob slots:\n     ├─ 'raw'            ← original .eml (always)\n     ├─ 'body-html'      ← if HTML part exists\n     ├─ 'body-text'      ← if plaintext body is > 10 KB\n     └─ 'att-N-\u003Cfile>'   ← one blob per attachment\n",[941,78150,78148],{"__ignoreMap":1037},[937,78152,78153,78154,6030,78157,78160,78161,66237,78164,78167,78168,78170],{},"Don't split into separate ",[941,78155,78156],{},"emails",[941,78158,78159],{},"attachments"," collections unless\nyou cross ~50 000 emails. The attachment manifest lives on the email\nrecord; the bytes are blob slots on that same record. noy-db's\ncontent-address deduplication (each blob is stored under an ",[941,78162,78163],{},"eTag",[941,78165,78166],{},"HMAC-SHA-256(blob key, plaintext bytes)",") stores the same PDF attached\nto 100 emails as ",[950,78169,11756],{}," blob on disk.",[1022,78172,78174],{"id":78173},"the-decision-matrix-which-part-lives-where","The decision matrix — which part lives where",[1338,78176,78177,78193],{},[1341,78178,78179],{},[1344,78180,78181,78184,78187,78190],{},[1347,78182,78183],{},"Part",[1347,78185,78186],{},"Size",[1347,78188,78189],{"align":9536},"Query-needed?",[1347,78191,78192],{"align":9536},"→ Where",[1357,78194,78195,78220,78245,78264,78282,78301,78320,78339,78357,78373],{},[1344,78196,78197,78211,78214,78216],{},[1362,78198,78199,968,78201,968,78203,968,78206,968,78208],{},[941,78200,2272],{},[941,78202,4879],{},[941,78204,78205],{},"cc",[941,78207,57531],{},[941,78209,78210],{},"receivedAt",[1362,78212,78213],{},"tiny",[1362,78215,14852],{"align":9536},[1362,78217,78218],{"align":9536},[950,78219,24158],{},[1344,78221,78222,78236,78238,78241],{},[1362,78223,78224,968,78227,968,78230,968,78233],{},[941,78225,78226],{},"messageId",[941,78228,78229],{},"threadId",[941,78231,78232],{},"inReplyTo",[941,78234,78235],{},"references",[1362,78237,78213],{},[1362,78239,78240],{"align":9536},"yes (threading)",[1362,78242,78243],{"align":9536},[950,78244,24158],{},[1344,78246,78247,78256,78258,78260],{},[1362,78248,78249,968,78252,78255],{},[941,78250,78251],{},"labels",[941,78253,78254],{},"flags"," (read \u002F starred \u002F archived \u002F replied)",[1362,78257,78213],{},[1362,78259,14852],{"align":9536},[1362,78261,78262],{"align":9536},[950,78263,24158],{},[1344,78265,78266,78272,78275,78278],{},[1362,78267,78268,78271],{},[941,78269,78270],{},"bodyPreview"," (first ~200 chars)",[1362,78273,78274],{},"small",[1362,78276,78277],{"align":9536},"no (display)",[1362,78279,78280],{"align":9536},[950,78281,24158],{},[1344,78283,78284,78290,78293,78296],{},[1362,78285,78286,78289],{},[941,78287,78288],{},"bodyText"," plaintext",[1362,78291,78292],{},"1–50 KB typical",[1362,78294,78295],{"align":9536},"rarely",[1362,78297,78298],{"align":9536},[950,78299,78300],{},"record if \u003C 10 KB, blob if larger",[1344,78302,78303,78308,78311,78313],{},[1362,78304,78305],{},[941,78306,78307],{},"bodyHtml",[1362,78309,78310],{},"often 100 KB+",[1362,78312,78277],{"align":9536},[1362,78314,78315,44999,78317],{"align":9536},[950,78316,133],{},[941,78318,78319],{},"body-html",[1344,78321,78322,78327,78330,78333],{},[1362,78323,78324,78325],{},"Raw ",[941,78326,77902],{},[1362,78328,78329],{},"50 KB – few MB",[1362,78331,78332],{"align":9536},"no (forensic)",[1362,78334,78335,44999,78337],{"align":9536},[950,78336,133],{},[941,78338,30312],{},[1344,78340,78341,78346,78349,78352],{},[1362,78342,78343,78344,1381],{},"Attachments (PDFs, images, nested ",[941,78345,77902],{},[1362,78347,78348],{},"10 KB – 100 MB",[1362,78350,78351],{"align":9536},"no (download)",[1362,78353,78354,78356],{"align":9536},[950,78355,133],{}," slot per attachment",[1344,78358,78359,78365,78367,78369],{},[1362,78360,78361,78364],{},[941,78362,78363],{},"attachments[]"," metadata (filename, mime, size, slot name)",[1362,78366,78213],{},[1362,78368,14852],{"align":9536},[1362,78370,78371],{"align":9536},[950,78372,24158],{},[1344,78374,78375,78381,78383,78385],{},[1362,78376,78377,78380],{},[941,78378,78379],{},"extracted"," (AI summary, topics, action items)",[1362,78382,78274],{},[1362,78384,14852],{"align":9536},[1362,78386,78387],{"align":9536},[950,78388,24158],{},[937,78390,78391,78392,78400],{},"One rule: ",[950,78393,78394,78395,4688,78397,78399],{},"if you ever ",[941,78396,24883],{},[941,78398,41940],{}," on it, put it on the\nrecord",". Otherwise it's a blob. Keep records small so the in-memory\nvault stays fast; blobs lazy-load.",[1022,78402,78404],{"id":78403},"concrete-record-shape","Concrete record shape",[2217,78406,78408],{"className":2219,"code":78407,"language":2221,"meta":1037,"style":1037},"interface Address {\n  email: string\n  name?: string\n}\n\ninterface Email {\n  id: string                      \u002F\u002F ULID — stable identity\n  messageId: string               \u002F\u002F RFC 5322 Message-ID header (dedup)\n  threadId: string                \u002F\u002F derived — see \"Threading\"\n\n  inReplyTo?: string\n  references?: string[]\n\n  from: Address\n  to: Address[]\n  cc?: Address[]\n  bcc?: Address[]\n  subject: string\n  receivedAt: string              \u002F\u002F ISO-8601 — Received: header timestamp\n  sentAt?: string                 \u002F\u002F ISO-8601 — Date: header\n\n  labels?: string[]\n  flags?: {\n    read?: boolean\n    starred?: boolean\n    replied?: boolean\n    archived?: boolean\n  }\n\n  bodyPreview: string             \u002F\u002F first ~200 chars for listing UIs\n  bodyText?: string               \u002F\u002F plaintext ONLY if \u003C 10 KB\n  bodyTextInBlob?: boolean        \u002F\u002F true → fetch blob slot 'body-text'\n  bodyHtmlInBlob?: boolean        \u002F\u002F true → fetch blob slot 'body-html'\n\n  attachments?: Array\u003C{\n    filename: string\n    mimeType: string\n    sizeBytes: number\n    contentId?: string            \u002F\u002F cid:... reference for inline images\n    slotName: string              \u002F\u002F blob slot holding the bytes\n  }>\n\n  extracted?: {\n    summary?: string\n    topics?: string[]\n    sentiment?: number\n    actionItems?: string[]\n    invoiceNumber?: string        \u002F\u002F when the email IS an invoice\n  }\n}\n",[941,78409,78410,78419,78427,78435,78439,78443,78452,78463,78475,78487,78491,78500,78512,78516,78526,78537,78548,78559,78568,78580,78592,78596,78607,78616,78625,78634,78643,78652,78656,78660,78672,78684,78696,78708,78712,78723,78732,78741,78750,78762,78774,78779,78783,78792,78801,78812,78821,78832,78844,78848],{"__ignoreMap":1037},[2225,78411,78412,78414,78417],{"class":2227,"line":2228},[2225,78413,6994],{"class":2774},[2225,78415,78416],{"class":6342}," Address",[2225,78418,2301],{"class":2235},[2225,78420,78421,78423,78425],{"class":2227,"line":1041},[2225,78422,43743],{"class":2307},[2225,78424,2311],{"class":2235},[2225,78426,7014],{"class":6342},[2225,78428,78429,78431,78433],{"class":2227,"line":1038},[2225,78430,7008],{"class":2307},[2225,78432,7011],{"class":2235},[2225,78434,7014],{"class":6342},[2225,78436,78437],{"class":2227,"line":2288},[2225,78438,7351],{"class":2235},[2225,78440,78441],{"class":2227,"line":2304},[2225,78442,2285],{"emptyLinePlaceholder":2284},[2225,78444,78445,78447,78450],{"class":2227,"line":2356},[2225,78446,6994],{"class":2774},[2225,78448,78449],{"class":6342}," Email",[2225,78451,2301],{"class":2235},[2225,78453,78454,78456,78458,78460],{"class":2227,"line":2830},[2225,78455,28371],{"class":2307},[2225,78457,2311],{"class":2235},[2225,78459,6366],{"class":6342},[2225,78461,78462],{"class":2763},"                      \u002F\u002F ULID — stable identity\n",[2225,78464,78465,78468,78470,78472],{"class":2227,"line":2861},[2225,78466,78467],{"class":2307},"  messageId",[2225,78469,2311],{"class":2235},[2225,78471,6366],{"class":6342},[2225,78473,78474],{"class":2763},"               \u002F\u002F RFC 5322 Message-ID header (dedup)\n",[2225,78476,78477,78480,78482,78484],{"class":2227,"line":4246},[2225,78478,78479],{"class":2307},"  threadId",[2225,78481,2311],{"class":2235},[2225,78483,6366],{"class":6342},[2225,78485,78486],{"class":2763},"                \u002F\u002F derived — see \"Threading\"\n",[2225,78488,78489],{"class":2227,"line":4253},[2225,78490,2285],{"emptyLinePlaceholder":2284},[2225,78492,78493,78496,78498],{"class":2227,"line":4259},[2225,78494,78495],{"class":2307},"  inReplyTo",[2225,78497,7011],{"class":2235},[2225,78499,7014],{"class":6342},[2225,78501,78502,78505,78507,78509],{"class":2227,"line":6548},[2225,78503,78504],{"class":2307},"  references",[2225,78506,7011],{"class":2235},[2225,78508,6366],{"class":6342},[2225,78510,78511],{"class":2239},"[]\n",[2225,78513,78514],{"class":2227,"line":6576},[2225,78515,2285],{"emptyLinePlaceholder":2284},[2225,78517,78518,78521,78523],{"class":2227,"line":6582},[2225,78519,78520],{"class":2307},"  from",[2225,78522,2311],{"class":2235},[2225,78524,78525],{"class":6342}," Address\n",[2225,78527,78528,78531,78533,78535],{"class":2227,"line":6587},[2225,78529,78530],{"class":2307},"  to",[2225,78532,2311],{"class":2235},[2225,78534,78416],{"class":6342},[2225,78536,78511],{"class":2239},[2225,78538,78539,78542,78544,78546],{"class":2227,"line":7174},[2225,78540,78541],{"class":2307},"  cc",[2225,78543,7011],{"class":2235},[2225,78545,78416],{"class":6342},[2225,78547,78511],{"class":2239},[2225,78549,78550,78553,78555,78557],{"class":2227,"line":7180},[2225,78551,78552],{"class":2307},"  bcc",[2225,78554,7011],{"class":2235},[2225,78556,78416],{"class":6342},[2225,78558,78511],{"class":2239},[2225,78560,78561,78564,78566],{"class":2227,"line":7220},[2225,78562,78563],{"class":2307},"  subject",[2225,78565,2311],{"class":2235},[2225,78567,7014],{"class":6342},[2225,78569,78570,78573,78575,78577],{"class":2227,"line":7225},[2225,78571,78572],{"class":2307},"  receivedAt",[2225,78574,2311],{"class":2235},[2225,78576,6366],{"class":6342},[2225,78578,78579],{"class":2763},"              \u002F\u002F ISO-8601 — Received: header timestamp\n",[2225,78581,78582,78585,78587,78589],{"class":2227,"line":7231},[2225,78583,78584],{"class":2307},"  sentAt",[2225,78586,7011],{"class":2235},[2225,78588,6366],{"class":6342},[2225,78590,78591],{"class":2763},"                 \u002F\u002F ISO-8601 — Date: header\n",[2225,78593,78594],{"class":2227,"line":7267},[2225,78595,2285],{"emptyLinePlaceholder":2284},[2225,78597,78598,78601,78603,78605],{"class":2227,"line":7272},[2225,78599,78600],{"class":2307},"  labels",[2225,78602,7011],{"class":2235},[2225,78604,6366],{"class":6342},[2225,78606,78511],{"class":2239},[2225,78608,78609,78612,78614],{"class":2227,"line":7278},[2225,78610,78611],{"class":2307},"  flags",[2225,78613,7011],{"class":2235},[2225,78615,2301],{"class":2235},[2225,78617,78618,78621,78623],{"class":2227,"line":7303},[2225,78619,78620],{"class":2307},"    read",[2225,78622,7011],{"class":2235},[2225,78624,11718],{"class":6342},[2225,78626,78627,78630,78632],{"class":2227,"line":7308},[2225,78628,78629],{"class":2307},"    starred",[2225,78631,7011],{"class":2235},[2225,78633,11718],{"class":6342},[2225,78635,78636,78639,78641],{"class":2227,"line":7314},[2225,78637,78638],{"class":2307},"    replied",[2225,78640,7011],{"class":2235},[2225,78642,11718],{"class":6342},[2225,78644,78645,78648,78650],{"class":2227,"line":7348},[2225,78646,78647],{"class":2307},"    archived",[2225,78649,7011],{"class":2235},[2225,78651,11718],{"class":6342},[2225,78653,78654],{"class":2227,"line":8613},[2225,78655,7632],{"class":2235},[2225,78657,78658],{"class":2227,"line":8633},[2225,78659,2285],{"emptyLinePlaceholder":2284},[2225,78661,78662,78665,78667,78669],{"class":2227,"line":8662},[2225,78663,78664],{"class":2307},"  bodyPreview",[2225,78666,2311],{"class":2235},[2225,78668,6366],{"class":6342},[2225,78670,78671],{"class":2763},"             \u002F\u002F first ~200 chars for listing UIs\n",[2225,78673,78674,78677,78679,78681],{"class":2227,"line":8747},[2225,78675,78676],{"class":2307},"  bodyText",[2225,78678,7011],{"class":2235},[2225,78680,6366],{"class":6342},[2225,78682,78683],{"class":2763},"               \u002F\u002F plaintext ONLY if \u003C 10 KB\n",[2225,78685,78686,78689,78691,78693],{"class":2227,"line":8752},[2225,78687,78688],{"class":2307},"  bodyTextInBlob",[2225,78690,7011],{"class":2235},[2225,78692,7723],{"class":6342},[2225,78694,78695],{"class":2763},"        \u002F\u002F true → fetch blob slot 'body-text'\n",[2225,78697,78698,78701,78703,78705],{"class":2227,"line":8768},[2225,78699,78700],{"class":2307},"  bodyHtmlInBlob",[2225,78702,7011],{"class":2235},[2225,78704,7723],{"class":6342},[2225,78706,78707],{"class":2763},"        \u002F\u002F true → fetch blob slot 'body-html'\n",[2225,78709,78710],{"class":2227,"line":8785},[2225,78711,2285],{"emptyLinePlaceholder":2284},[2225,78713,78714,78716,78718,78720],{"class":2227,"line":8815},[2225,78715,33096],{"class":2307},[2225,78717,7011],{"class":2235},[2225,78719,66498],{"class":6342},[2225,78721,78722],{"class":2235},"\u003C{\n",[2225,78724,78725,78728,78730],{"class":2227,"line":8854},[2225,78726,78727],{"class":2307},"    filename",[2225,78729,2311],{"class":2235},[2225,78731,7014],{"class":6342},[2225,78733,78734,78737,78739],{"class":2227,"line":8874},[2225,78735,78736],{"class":2307},"    mimeType",[2225,78738,2311],{"class":2235},[2225,78740,7014],{"class":6342},[2225,78742,78743,78746,78748],{"class":2227,"line":8911},[2225,78744,78745],{"class":2307},"    sizeBytes",[2225,78747,2311],{"class":2235},[2225,78749,12024],{"class":6342},[2225,78751,78752,78755,78757,78759],{"class":2227,"line":8916},[2225,78753,78754],{"class":2307},"    contentId",[2225,78756,7011],{"class":2235},[2225,78758,6366],{"class":6342},[2225,78760,78761],{"class":2763},"            \u002F\u002F cid:... reference for inline images\n",[2225,78763,78764,78767,78769,78771],{"class":2227,"line":8924},[2225,78765,78766],{"class":2307},"    slotName",[2225,78768,2311],{"class":2235},[2225,78770,6366],{"class":6342},[2225,78772,78773],{"class":2763},"              \u002F\u002F blob slot holding the bytes\n",[2225,78775,78776],{"class":2227,"line":8929},[2225,78777,78778],{"class":2235},"  }>\n",[2225,78780,78781],{"class":2227,"line":8949},[2225,78782,2285],{"emptyLinePlaceholder":2284},[2225,78784,78785,78788,78790],{"class":2227,"line":8955},[2225,78786,78787],{"class":2307},"  extracted",[2225,78789,7011],{"class":2235},[2225,78791,2301],{"class":2235},[2225,78793,78794,78797,78799],{"class":2227,"line":8983},[2225,78795,78796],{"class":2307},"    summary",[2225,78798,7011],{"class":2235},[2225,78800,7014],{"class":6342},[2225,78802,78803,78806,78808,78810],{"class":2227,"line":9016},[2225,78804,78805],{"class":2307},"    topics",[2225,78807,7011],{"class":2235},[2225,78809,6366],{"class":6342},[2225,78811,78511],{"class":2239},[2225,78813,78814,78817,78819],{"class":2227,"line":9063},[2225,78815,78816],{"class":2307},"    sentiment",[2225,78818,7011],{"class":2235},[2225,78820,12024],{"class":6342},[2225,78822,78823,78826,78828,78830],{"class":2227,"line":9068},[2225,78824,78825],{"class":2307},"    actionItems",[2225,78827,7011],{"class":2235},[2225,78829,6366],{"class":6342},[2225,78831,78511],{"class":2239},[2225,78833,78834,78837,78839,78841],{"class":2227,"line":9106},[2225,78835,78836],{"class":2307},"    invoiceNumber",[2225,78838,7011],{"class":2235},[2225,78840,6366],{"class":6342},[2225,78842,78843],{"class":2763},"        \u002F\u002F when the email IS an invoice\n",[2225,78845,78846],{"class":2227,"line":9143},[2225,78847,7632],{"class":2235},[2225,78849,78850],{"class":2227,"line":9177},[2225,78851,7351],{"class":2235},[937,78853,78854,78855,78857,78858,78861,78862,957],{},"Note there is no ",[941,78856,78163],{}," on the manifest: the content address is slot\nmetadata, maintained by the blobs service. Read it with\n",[941,78859,78860],{},"emails.blob(id).list()"," when you need it (see\n",[1007,78863,78865],{"href":78864},"\u002Frecipes\u002Femail-archive-threads-revisions#shared-documents-across-a-thread","Shared documents",[1022,78867,78869],{"id":78868},"ingest-flow","Ingest flow",[937,78871,78872,78873,78875],{},"MIME parsing itself is out of noy-db's scope — use any ",[941,78874,77902],{}," parser\nand adapt its output to this minimal shape:",[2217,78877,78879],{"className":2219,"code":78878,"language":2221,"meta":1037,"style":1037},"\u002F\u002F The shape your MIME parser must produce.\ninterface ParsedEmail {\n  headers: Record\u003Cstring, string | undefined>\n  from: Address\n  to: Address[]\n  cc?: Address[]\n  subject: string\n  receivedAt: string              \u002F\u002F ISO-8601, from the top Received: header\n  text?: string\n  html?: string\n  attachments: Array\u003C{\n    filename: string\n    contentType: string\n    size: number\n    contentId?: string\n    bytes: Uint8Array\n  }>\n}\n",[941,78880,78881,78886,78895,78918,78926,78936,78946,78954,78965,78974,78983,78993,79001,79010,79019,79027,79037,79041],{"__ignoreMap":1037},[2225,78882,78883],{"class":2227,"line":2228},[2225,78884,78885],{"class":2763},"\u002F\u002F The shape your MIME parser must produce.\n",[2225,78887,78888,78890,78893],{"class":2227,"line":1041},[2225,78889,6994],{"class":2774},[2225,78891,78892],{"class":6342}," ParsedEmail",[2225,78894,2301],{"class":2235},[2225,78896,78897,78900,78902,78904,78906,78908,78910,78912,78914,78916],{"class":2227,"line":1038},[2225,78898,78899],{"class":2307},"  headers",[2225,78901,2311],{"class":2235},[2225,78903,22527],{"class":6342},[2225,78905,6474],{"class":2235},[2225,78907,7259],{"class":6342},[2225,78909,2298],{"class":2235},[2225,78911,6366],{"class":6342},[2225,78913,7087],{"class":2235},[2225,78915,8483],{"class":6342},[2225,78917,7093],{"class":2235},[2225,78919,78920,78922,78924],{"class":2227,"line":2288},[2225,78921,78520],{"class":2307},[2225,78923,2311],{"class":2235},[2225,78925,78525],{"class":6342},[2225,78927,78928,78930,78932,78934],{"class":2227,"line":2304},[2225,78929,78530],{"class":2307},[2225,78931,2311],{"class":2235},[2225,78933,78416],{"class":6342},[2225,78935,78511],{"class":2239},[2225,78937,78938,78940,78942,78944],{"class":2227,"line":2356},[2225,78939,78541],{"class":2307},[2225,78941,7011],{"class":2235},[2225,78943,78416],{"class":6342},[2225,78945,78511],{"class":2239},[2225,78947,78948,78950,78952],{"class":2227,"line":2830},[2225,78949,78563],{"class":2307},[2225,78951,2311],{"class":2235},[2225,78953,7014],{"class":6342},[2225,78955,78956,78958,78960,78962],{"class":2227,"line":2861},[2225,78957,78572],{"class":2307},[2225,78959,2311],{"class":2235},[2225,78961,6366],{"class":6342},[2225,78963,78964],{"class":2763},"              \u002F\u002F ISO-8601, from the top Received: header\n",[2225,78966,78967,78970,78972],{"class":2227,"line":4246},[2225,78968,78969],{"class":2307},"  text",[2225,78971,7011],{"class":2235},[2225,78973,7014],{"class":6342},[2225,78975,78976,78979,78981],{"class":2227,"line":4253},[2225,78977,78978],{"class":2307},"  html",[2225,78980,7011],{"class":2235},[2225,78982,7014],{"class":6342},[2225,78984,78985,78987,78989,78991],{"class":2227,"line":4259},[2225,78986,33096],{"class":2307},[2225,78988,2311],{"class":2235},[2225,78990,66498],{"class":6342},[2225,78992,78722],{"class":2235},[2225,78994,78995,78997,78999],{"class":2227,"line":6548},[2225,78996,78727],{"class":2307},[2225,78998,2311],{"class":2235},[2225,79000,7014],{"class":6342},[2225,79002,79003,79006,79008],{"class":2227,"line":6576},[2225,79004,79005],{"class":2307},"    contentType",[2225,79007,2311],{"class":2235},[2225,79009,7014],{"class":6342},[2225,79011,79012,79015,79017],{"class":2227,"line":6582},[2225,79013,79014],{"class":2307},"    size",[2225,79016,2311],{"class":2235},[2225,79018,12024],{"class":6342},[2225,79020,79021,79023,79025],{"class":2227,"line":6587},[2225,79022,78754],{"class":2307},[2225,79024,7011],{"class":2235},[2225,79026,7014],{"class":6342},[2225,79028,79029,79032,79034],{"class":2227,"line":7174},[2225,79030,79031],{"class":2307},"    bytes",[2225,79033,2311],{"class":2235},[2225,79035,79036],{"class":6342}," Uint8Array\n",[2225,79038,79039],{"class":2227,"line":7180},[2225,79040,78778],{"class":2235},[2225,79042,79043],{"class":2227,"line":7220},[2225,79044,7351],{"class":2235},[2217,79046,79048],{"className":2219,"code":79047,"language":2221,"meta":1037,"style":1037},"import { readFile } from 'node:fs\u002Fpromises'\nimport { generateULID, type Collection } from '@noy-db\u002Fhub'\nimport { parseEmail } from '.\u002Fyour-eml-parser'   \u002F\u002F MIME → ParsedEmail\n\n\u002F** Filename → safe slot-name fragment. *\u002F\nfunction slug(filename: string): string {\n  return filename.toLowerCase().replace(\u002F[^a-z0-9.]+\u002Fg, '-')\n}\n\nasync function ingestEml(emails: Collection\u003CEmail>, emlBytes: Uint8Array): Promise\u003Cstring> {\n  const parsed = parseEmail(emlBytes)\n  const id = generateULID()\n\n  const attachments = parsed.attachments.map((a, i) => ({\n    filename: a.filename,\n    mimeType: a.contentType,\n    sizeBytes: a.size,\n    contentId: a.contentId,                         \u002F\u002F for cid: inline images\n    slotName: `att-${i}-${slug(a.filename)}`,\n  }))\n\n  const record: Email = {\n    id,\n    messageId: parsed.headers['message-id'] ?? id,\n    threadId: await deriveThreadId(emails, parsed),\n    inReplyTo: parsed.headers['in-reply-to'],\n    references: parsed.headers['references']?.split(\u002F\\s+\u002F),\n    from: parsed.from,\n    to: parsed.to,\n    cc: parsed.cc,\n    subject: parsed.subject,\n    receivedAt: parsed.receivedAt,\n    bodyPreview: (parsed.text ?? '').slice(0, 200),\n    bodyText: parsed.text && parsed.text.length \u003C 10_000 ? parsed.text : undefined,\n    bodyTextInBlob: (parsed.text?.length ?? 0) >= 10_000,\n    bodyHtmlInBlob: !!parsed.html,\n    attachments,\n  }\n  await emails.put(id, record)\n\n  \u002F\u002F Always store the raw .eml — forensic source of truth\n  await emails.blob(id).put('raw', emlBytes, { mimeType: 'message\u002Frfc822' })\n\n  if (record.bodyTextInBlob) {\n    await emails.blob(id).put('body-text', new TextEncoder().encode(parsed.text!))\n  }\n  if (record.bodyHtmlInBlob) {\n    await emails.blob(id).put('body-html', new TextEncoder().encode(parsed.html!))\n  }\n\n  for (const [i, att] of attachments.entries()) {\n    const src = parsed.attachments[i]!\n    await emails.blob(id).put(att.slotName, src.bytes, {\n      mimeType: att.mimeType,\n      filename: att.filename,\n    })\n  }\n\n  return id\n}\n\nconst emails = vault.collection\u003CEmail>('emails')\nconst emlBytes = new Uint8Array(await readFile('.\u002Finbox\u002Fq2-invoice.eml'))\nconst id = await ingestEml(emails, emlBytes)\n\u002F\u002F → '01J1ZC4Q4T9GQ0V4Y3F2M8KDXP'\n",[941,79049,79050,79068,79094,79117,79121,79126,79148,79188,79192,79196,79238,79255,79267,79271,79308,79323,79338,79353,79371,79408,79414,79418,79432,79438,79468,79492,79518,79559,79574,79589,79604,79619,79634,79671,79714,79745,79764,79771,79775,79796,79800,79805,79856,79860,79877,79931,79935,79952,80004,80008,80012,80044,80069,80111,80127,80142,80149,80153,80157,80164,80168,80172,80203,80233,80254],{"__ignoreMap":1037},[2225,79051,79052,79054,79056,79058,79060,79062,79064,79066],{"class":2227,"line":2228},[2225,79053,2232],{"class":2231},[2225,79055,2236],{"class":2235},[2225,79057,60906],{"class":2239},[2225,79059,2243],{"class":2235},[2225,79061,2246],{"class":2231},[2225,79063,2249],{"class":2235},[2225,79065,60915],{"class":2252},[2225,79067,2256],{"class":2235},[2225,79069,79070,79072,79074,79077,79079,79081,79084,79086,79088,79090,79092],{"class":2227,"line":1041},[2225,79071,2232],{"class":2231},[2225,79073,2236],{"class":2235},[2225,79075,79076],{"class":2239}," generateULID",[2225,79078,2298],{"class":2235},[2225,79080,3399],{"class":2231},[2225,79082,79083],{"class":2239}," Collection",[2225,79085,2243],{"class":2235},[2225,79087,2246],{"class":2231},[2225,79089,2249],{"class":2235},[2225,79091,1086],{"class":2252},[2225,79093,2256],{"class":2235},[2225,79095,79096,79098,79100,79103,79105,79107,79109,79112,79114],{"class":2227,"line":1038},[2225,79097,2232],{"class":2231},[2225,79099,2236],{"class":2235},[2225,79101,79102],{"class":2239}," parseEmail",[2225,79104,2243],{"class":2235},[2225,79106,2246],{"class":2231},[2225,79108,2249],{"class":2235},[2225,79110,79111],{"class":2252},".\u002Fyour-eml-parser",[2225,79113,2805],{"class":2235},[2225,79115,79116],{"class":2763},"   \u002F\u002F MIME → ParsedEmail\n",[2225,79118,79119],{"class":2227,"line":2288},[2225,79120,2285],{"emptyLinePlaceholder":2284},[2225,79122,79123],{"class":2227,"line":2304},[2225,79124,79125],{"class":2763},"\u002F** Filename → safe slot-name fragment. *\u002F\n",[2225,79127,79128,79130,79133,79135,79138,79140,79142,79144,79146],{"class":2227,"line":2356},[2225,79129,12641],{"class":2774},[2225,79131,79132],{"class":2291}," slug",[2225,79134,2789],{"class":2235},[2225,79136,79137],{"class":7052},"filename",[2225,79139,2311],{"class":2235},[2225,79141,6366],{"class":6342},[2225,79143,6255],{"class":2235},[2225,79145,6366],{"class":6342},[2225,79147,2301],{"class":2235},[2225,79149,79150,79152,79154,79156,79159,79161,79163,79165,79167,79170,79173,79176,79178,79180,79182,79184,79186],{"class":2227,"line":2830},[2225,79151,8220],{"class":2231},[2225,79153,32095],{"class":2239},[2225,79155,1520],{"class":2235},[2225,79157,79158],{"class":2291},"toLowerCase",[2225,79160,4228],{"class":2307},[2225,79162,1520],{"class":2235},[2225,79164,71260],{"class":2291},[2225,79166,2789],{"class":2307},[2225,79168,79169],{"class":2235},"\u002F[^",[2225,79171,79172],{"class":2252},"a-z0-9.",[2225,79174,79175],{"class":2235},"]+\u002F",[2225,79177,71274],{"class":6527},[2225,79179,2298],{"class":2235},[2225,79181,2249],{"class":2235},[2225,79183,71241],{"class":2252},[2225,79185,2805],{"class":2235},[2225,79187,2362],{"class":2307},[2225,79189,79190],{"class":2227,"line":2861},[2225,79191,7351],{"class":2235},[2225,79193,79194],{"class":2227,"line":4246},[2225,79195,2285],{"emptyLinePlaceholder":2284},[2225,79197,79198,79200,79202,79205,79207,79209,79211,79213,79215,79217,79219,79222,79224,79226,79228,79230,79232,79234,79236],{"class":2227,"line":4253},[2225,79199,70491],{"class":2774},[2225,79201,40856],{"class":2774},[2225,79203,79204],{"class":2291}," ingestEml",[2225,79206,2789],{"class":2235},[2225,79208,78156],{"class":7052},[2225,79210,2311],{"class":2235},[2225,79212,79083],{"class":6342},[2225,79214,6474],{"class":2235},[2225,79216,78143],{"class":6342},[2225,79218,17840],{"class":2235},[2225,79220,79221],{"class":7052}," emlBytes",[2225,79223,2311],{"class":2235},[2225,79225,60935],{"class":6342},[2225,79227,6255],{"class":2235},[2225,79229,7079],{"class":6342},[2225,79231,6474],{"class":2235},[2225,79233,7259],{"class":6342},[2225,79235,6480],{"class":2235},[2225,79237,2301],{"class":2235},[2225,79239,79240,79242,79244,79246,79248,79250,79253],{"class":2227,"line":4259},[2225,79241,8123],{"class":2774},[2225,79243,45683],{"class":2239},[2225,79245,6346],{"class":2235},[2225,79247,79102],{"class":2291},[2225,79249,2789],{"class":2307},[2225,79251,79252],{"class":2239},"emlBytes",[2225,79254,2362],{"class":2307},[2225,79256,79257,79259,79261,79263,79265],{"class":2227,"line":6548},[2225,79258,8123],{"class":2774},[2225,79260,7070],{"class":2239},[2225,79262,6346],{"class":2235},[2225,79264,79076],{"class":2291},[2225,79266,8146],{"class":2307},[2225,79268,79269],{"class":2227,"line":6576},[2225,79270,2285],{"emptyLinePlaceholder":2284},[2225,79272,79273,79275,79278,79280,79282,79284,79286,79288,79290,79292,79294,79296,79298,79300,79302,79304,79306],{"class":2227,"line":6582},[2225,79274,8123],{"class":2774},[2225,79276,79277],{"class":2239}," attachments",[2225,79279,6346],{"class":2235},[2225,79281,45683],{"class":2239},[2225,79283,1520],{"class":2235},[2225,79285,78159],{"class":2239},[2225,79287,1520],{"class":2235},[2225,79289,8244],{"class":2291},[2225,79291,2789],{"class":2307},[2225,79293,2789],{"class":2235},[2225,79295,1007],{"class":7052},[2225,79297,2298],{"class":2235},[2225,79299,43404],{"class":7052},[2225,79301,1381],{"class":2235},[2225,79303,8116],{"class":2774},[2225,79305,953],{"class":2307},[2225,79307,4200],{"class":2235},[2225,79309,79310,79312,79314,79317,79319,79321],{"class":2227,"line":6587},[2225,79311,78727],{"class":2307},[2225,79313,2311],{"class":2235},[2225,79315,79316],{"class":2239}," a",[2225,79318,1520],{"class":2235},[2225,79320,79137],{"class":2239},[2225,79322,1707],{"class":2235},[2225,79324,79325,79327,79329,79331,79333,79336],{"class":2227,"line":7174},[2225,79326,78736],{"class":2307},[2225,79328,2311],{"class":2235},[2225,79330,79316],{"class":2239},[2225,79332,1520],{"class":2235},[2225,79334,79335],{"class":2239},"contentType",[2225,79337,1707],{"class":2235},[2225,79339,79340,79342,79344,79346,79348,79351],{"class":2227,"line":7180},[2225,79341,78745],{"class":2307},[2225,79343,2311],{"class":2235},[2225,79345,79316],{"class":2239},[2225,79347,1520],{"class":2235},[2225,79349,79350],{"class":2239},"size",[2225,79352,1707],{"class":2235},[2225,79354,79355,79357,79359,79361,79363,79366,79368],{"class":2227,"line":7220},[2225,79356,78754],{"class":2307},[2225,79358,2311],{"class":2235},[2225,79360,79316],{"class":2239},[2225,79362,1520],{"class":2235},[2225,79364,79365],{"class":2239},"contentId",[2225,79367,2298],{"class":2235},[2225,79369,79370],{"class":2763},"                         \u002F\u002F for cid: inline images\n",[2225,79372,79373,79375,79377,79379,79382,79384,79387,79389,79391,79393,79396,79399,79401,79404,79406],{"class":2227,"line":7225},[2225,79374,78766],{"class":2307},[2225,79376,2311],{"class":2235},[2225,79378,43789],{"class":2235},[2225,79380,79381],{"class":2252},"att-",[2225,79383,8197],{"class":2235},[2225,79385,79386],{"class":2239},"i",[2225,79388,2359],{"class":2235},[2225,79390,71241],{"class":2252},[2225,79392,8197],{"class":2235},[2225,79394,79395],{"class":2291},"slug",[2225,79397,79398],{"class":2239},"(a",[2225,79400,1520],{"class":2235},[2225,79402,79403],{"class":2239},"filename)",[2225,79405,43799],{"class":2235},[2225,79407,1707],{"class":2235},[2225,79409,79410,79412],{"class":2227,"line":7231},[2225,79411,19143],{"class":2235},[2225,79413,8606],{"class":2307},[2225,79415,79416],{"class":2227,"line":7267},[2225,79417,2285],{"emptyLinePlaceholder":2284},[2225,79419,79420,79422,79424,79426,79428,79430],{"class":2227,"line":7272},[2225,79421,8123],{"class":2774},[2225,79423,24268],{"class":2239},[2225,79425,2311],{"class":2235},[2225,79427,78449],{"class":6342},[2225,79429,6346],{"class":2235},[2225,79431,2301],{"class":2235},[2225,79433,79434,79436],{"class":2227,"line":7278},[2225,79435,60558],{"class":2239},[2225,79437,1707],{"class":2235},[2225,79439,79440,79443,79445,79447,79449,79451,79453,79455,79458,79460,79462,79464,79466],{"class":2227,"line":7303},[2225,79441,79442],{"class":2307},"    messageId",[2225,79444,2311],{"class":2235},[2225,79446,45683],{"class":2239},[2225,79448,1520],{"class":2235},[2225,79450,52729],{"class":2239},[2225,79452,8885],{"class":2307},[2225,79454,2805],{"class":2235},[2225,79456,79457],{"class":2252},"message-id",[2225,79459,2805],{"class":2235},[2225,79461,8890],{"class":2307},[2225,79463,8381],{"class":2235},[2225,79465,7070],{"class":2239},[2225,79467,1707],{"class":2235},[2225,79469,79470,79473,79475,79477,79480,79482,79484,79486,79488,79490],{"class":2227,"line":7308},[2225,79471,79472],{"class":2307},"    threadId",[2225,79474,2311],{"class":2235},[2225,79476,2784],{"class":2231},[2225,79478,79479],{"class":2291}," deriveThreadId",[2225,79481,2789],{"class":2307},[2225,79483,78156],{"class":2239},[2225,79485,2298],{"class":2235},[2225,79487,45683],{"class":2239},[2225,79489,1381],{"class":2307},[2225,79491,1707],{"class":2235},[2225,79493,79494,79497,79499,79501,79503,79505,79507,79509,79512,79514,79516],{"class":2227,"line":7314},[2225,79495,79496],{"class":2307},"    inReplyTo",[2225,79498,2311],{"class":2235},[2225,79500,45683],{"class":2239},[2225,79502,1520],{"class":2235},[2225,79504,52729],{"class":2239},[2225,79506,8885],{"class":2307},[2225,79508,2805],{"class":2235},[2225,79510,79511],{"class":2252},"in-reply-to",[2225,79513,2805],{"class":2235},[2225,79515,8803],{"class":2307},[2225,79517,1707],{"class":2235},[2225,79519,79520,79523,79525,79527,79529,79531,79533,79535,79537,79539,79541,79543,79545,79547,79549,79552,79555,79557],{"class":2227,"line":7348},[2225,79521,79522],{"class":2307},"    references",[2225,79524,2311],{"class":2235},[2225,79526,45683],{"class":2239},[2225,79528,1520],{"class":2235},[2225,79530,52729],{"class":2239},[2225,79532,8885],{"class":2307},[2225,79534,2805],{"class":2235},[2225,79536,78235],{"class":2252},[2225,79538,2805],{"class":2235},[2225,79540,8803],{"class":2307},[2225,79542,8457],{"class":2235},[2225,79544,8841],{"class":2291},[2225,79546,2789],{"class":2307},[2225,79548,1625],{"class":2235},[2225,79550,79551],{"class":2252},"\\s",[2225,79553,79554],{"class":2235},"+\u002F",[2225,79556,1381],{"class":2307},[2225,79558,1707],{"class":2235},[2225,79560,79561,79564,79566,79568,79570,79572],{"class":2227,"line":8613},[2225,79562,79563],{"class":2307},"    from",[2225,79565,2311],{"class":2235},[2225,79567,45683],{"class":2239},[2225,79569,1520],{"class":2235},[2225,79571,2272],{"class":2239},[2225,79573,1707],{"class":2235},[2225,79575,79576,79579,79581,79583,79585,79587],{"class":2227,"line":8633},[2225,79577,79578],{"class":2307},"    to",[2225,79580,2311],{"class":2235},[2225,79582,45683],{"class":2239},[2225,79584,1520],{"class":2235},[2225,79586,4879],{"class":2239},[2225,79588,1707],{"class":2235},[2225,79590,79591,79594,79596,79598,79600,79602],{"class":2227,"line":8662},[2225,79592,79593],{"class":2307},"    cc",[2225,79595,2311],{"class":2235},[2225,79597,45683],{"class":2239},[2225,79599,1520],{"class":2235},[2225,79601,78205],{"class":2239},[2225,79603,1707],{"class":2235},[2225,79605,79606,79609,79611,79613,79615,79617],{"class":2227,"line":8747},[2225,79607,79608],{"class":2307},"    subject",[2225,79610,2311],{"class":2235},[2225,79612,45683],{"class":2239},[2225,79614,1520],{"class":2235},[2225,79616,57531],{"class":2239},[2225,79618,1707],{"class":2235},[2225,79620,79621,79624,79626,79628,79630,79632],{"class":2227,"line":8752},[2225,79622,79623],{"class":2307},"    receivedAt",[2225,79625,2311],{"class":2235},[2225,79627,45683],{"class":2239},[2225,79629,1520],{"class":2235},[2225,79631,78210],{"class":2239},[2225,79633,1707],{"class":2235},[2225,79635,79636,79639,79641,79643,79645,79647,79649,79651,79653,79655,79657,79659,79661,79663,79665,79667,79669],{"class":2227,"line":8768},[2225,79637,79638],{"class":2307},"    bodyPreview",[2225,79640,2311],{"class":2235},[2225,79642,953],{"class":2307},[2225,79644,45654],{"class":2239},[2225,79646,1520],{"class":2235},[2225,79648,5672],{"class":2239},[2225,79650,8239],{"class":2235},[2225,79652,12500],{"class":2235},[2225,79654,1381],{"class":2307},[2225,79656,1520],{"class":2235},[2225,79658,8733],{"class":2291},[2225,79660,2789],{"class":2307},[2225,79662,5402],{"class":6527},[2225,79664,2298],{"class":2235},[2225,79666,40956],{"class":6527},[2225,79668,1381],{"class":2307},[2225,79670,1707],{"class":2235},[2225,79672,79673,79676,79678,79680,79682,79684,79686,79688,79690,79692,79694,79696,79698,79701,79703,79705,79707,79709,79711],{"class":2227,"line":8785},[2225,79674,79675],{"class":2307},"    bodyText",[2225,79677,2311],{"class":2235},[2225,79679,45683],{"class":2239},[2225,79681,1520],{"class":2235},[2225,79683,5672],{"class":2239},[2225,79685,8486],{"class":2235},[2225,79687,45683],{"class":2239},[2225,79689,1520],{"class":2235},[2225,79691,5672],{"class":2239},[2225,79693,1520],{"class":2235},[2225,79695,8742],{"class":2239},[2225,79697,40429],{"class":2235},[2225,79699,79700],{"class":6527}," 10_000",[2225,79702,40953],{"class":2235},[2225,79704,45683],{"class":2239},[2225,79706,1520],{"class":2235},[2225,79708,5672],{"class":2239},[2225,79710,40959],{"class":2235},[2225,79712,79713],{"class":2235}," undefined,\n",[2225,79715,79716,79719,79721,79723,79725,79727,79729,79731,79733,79735,79737,79739,79741,79743],{"class":2227,"line":8815},[2225,79717,79718],{"class":2307},"    bodyTextInBlob",[2225,79720,2311],{"class":2235},[2225,79722,953],{"class":2307},[2225,79724,45654],{"class":2239},[2225,79726,1520],{"class":2235},[2225,79728,5672],{"class":2239},[2225,79730,8457],{"class":2235},[2225,79732,8742],{"class":2239},[2225,79734,8239],{"class":2235},[2225,79736,8464],{"class":6527},[2225,79738,8495],{"class":2307},[2225,79740,26921],{"class":2235},[2225,79742,79700],{"class":6527},[2225,79744,1707],{"class":2235},[2225,79746,79747,79750,79752,79755,79757,79759,79762],{"class":2227,"line":8854},[2225,79748,79749],{"class":2307},"    bodyHtmlInBlob",[2225,79751,2311],{"class":2235},[2225,79753,79754],{"class":2235}," !!",[2225,79756,45654],{"class":2239},[2225,79758,1520],{"class":2235},[2225,79760,79761],{"class":2239},"html",[2225,79763,1707],{"class":2235},[2225,79765,79766,79769],{"class":2227,"line":8874},[2225,79767,79768],{"class":2239},"    attachments",[2225,79770,1707],{"class":2235},[2225,79772,79773],{"class":2227,"line":8911},[2225,79774,7632],{"class":2235},[2225,79776,79777,79779,79782,79784,79786,79788,79790,79792,79794],{"class":2227,"line":8916},[2225,79778,7561],{"class":2231},[2225,79780,79781],{"class":2239}," emails",[2225,79783,1520],{"class":2235},[2225,79785,3161],{"class":2291},[2225,79787,2789],{"class":2307},[2225,79789,6983],{"class":2239},[2225,79791,2298],{"class":2235},[2225,79793,24268],{"class":2239},[2225,79795,2362],{"class":2307},[2225,79797,79798],{"class":2227,"line":8924},[2225,79799,2285],{"emptyLinePlaceholder":2284},[2225,79801,79802],{"class":2227,"line":8929},[2225,79803,79804],{"class":2763},"  \u002F\u002F Always store the raw .eml — forensic source of truth\n",[2225,79806,79807,79809,79811,79813,79815,79817,79819,79821,79823,79825,79827,79829,79831,79833,79835,79837,79839,79841,79843,79845,79847,79850,79852,79854],{"class":2227,"line":8949},[2225,79808,7561],{"class":2231},[2225,79810,79781],{"class":2239},[2225,79812,1520],{"class":2235},[2225,79814,133],{"class":2291},[2225,79816,2789],{"class":2307},[2225,79818,6983],{"class":2239},[2225,79820,1381],{"class":2307},[2225,79822,1520],{"class":2235},[2225,79824,3161],{"class":2291},[2225,79826,2789],{"class":2307},[2225,79828,2805],{"class":2235},[2225,79830,30312],{"class":2252},[2225,79832,2805],{"class":2235},[2225,79834,2298],{"class":2235},[2225,79836,79221],{"class":2239},[2225,79838,2298],{"class":2235},[2225,79840,2236],{"class":2235},[2225,79842,61013],{"class":2307},[2225,79844,2311],{"class":2235},[2225,79846,2249],{"class":2235},[2225,79848,79849],{"class":2252},"message\u002Frfc822",[2225,79851,2805],{"class":2235},[2225,79853,2243],{"class":2235},[2225,79855,2362],{"class":2307},[2225,79857,79858],{"class":2227,"line":8955},[2225,79859,2285],{"emptyLinePlaceholder":2284},[2225,79861,79862,79864,79866,79868,79870,79873,79875],{"class":2227,"line":8983},[2225,79863,7608],{"class":2231},[2225,79865,953],{"class":2307},[2225,79867,24158],{"class":2239},[2225,79869,1520],{"class":2235},[2225,79871,79872],{"class":2239},"bodyTextInBlob",[2225,79874,8495],{"class":2307},[2225,79876,4200],{"class":2235},[2225,79878,79879,79882,79884,79886,79888,79890,79892,79894,79896,79898,79900,79902,79905,79907,79909,79911,79913,79915,79917,79919,79921,79923,79925,79927,79929],{"class":2227,"line":9016},[2225,79880,79881],{"class":2231},"    await",[2225,79883,79781],{"class":2239},[2225,79885,1520],{"class":2235},[2225,79887,133],{"class":2291},[2225,79889,2789],{"class":2307},[2225,79891,6983],{"class":2239},[2225,79893,1381],{"class":2307},[2225,79895,1520],{"class":2235},[2225,79897,3161],{"class":2291},[2225,79899,2789],{"class":2307},[2225,79901,2805],{"class":2235},[2225,79903,79904],{"class":2252},"body-text",[2225,79906,2805],{"class":2235},[2225,79908,2298],{"class":2235},[2225,79910,8130],{"class":2235},[2225,79912,69739],{"class":2291},[2225,79914,4228],{"class":2307},[2225,79916,1520],{"class":2235},[2225,79918,69746],{"class":2291},[2225,79920,2789],{"class":2307},[2225,79922,45654],{"class":2239},[2225,79924,1520],{"class":2235},[2225,79926,5672],{"class":2239},[2225,79928,35134],{"class":2235},[2225,79930,8606],{"class":2307},[2225,79932,79933],{"class":2227,"line":9063},[2225,79934,7632],{"class":2235},[2225,79936,79937,79939,79941,79943,79945,79948,79950],{"class":2227,"line":9068},[2225,79938,7608],{"class":2231},[2225,79940,953],{"class":2307},[2225,79942,24158],{"class":2239},[2225,79944,1520],{"class":2235},[2225,79946,79947],{"class":2239},"bodyHtmlInBlob",[2225,79949,8495],{"class":2307},[2225,79951,4200],{"class":2235},[2225,79953,79954,79956,79958,79960,79962,79964,79966,79968,79970,79972,79974,79976,79978,79980,79982,79984,79986,79988,79990,79992,79994,79996,79998,80000,80002],{"class":2227,"line":9106},[2225,79955,79881],{"class":2231},[2225,79957,79781],{"class":2239},[2225,79959,1520],{"class":2235},[2225,79961,133],{"class":2291},[2225,79963,2789],{"class":2307},[2225,79965,6983],{"class":2239},[2225,79967,1381],{"class":2307},[2225,79969,1520],{"class":2235},[2225,79971,3161],{"class":2291},[2225,79973,2789],{"class":2307},[2225,79975,2805],{"class":2235},[2225,79977,78319],{"class":2252},[2225,79979,2805],{"class":2235},[2225,79981,2298],{"class":2235},[2225,79983,8130],{"class":2235},[2225,79985,69739],{"class":2291},[2225,79987,4228],{"class":2307},[2225,79989,1520],{"class":2235},[2225,79991,69746],{"class":2291},[2225,79993,2789],{"class":2307},[2225,79995,45654],{"class":2239},[2225,79997,1520],{"class":2235},[2225,79999,79761],{"class":2239},[2225,80001,35134],{"class":2235},[2225,80003,8606],{"class":2307},[2225,80005,80006],{"class":2227,"line":9143},[2225,80007,7632],{"class":2235},[2225,80009,80010],{"class":2227,"line":9177},[2225,80011,2285],{"emptyLinePlaceholder":2284},[2225,80013,80014,80017,80019,80021,80023,80025,80027,80030,80032,80034,80036,80038,80040,80042],{"class":2227,"line":9183},[2225,80015,80016],{"class":2231},"  for",[2225,80018,953],{"class":2307},[2225,80020,2775],{"class":2774},[2225,80022,8667],{"class":2235},[2225,80024,79386],{"class":2239},[2225,80026,2298],{"class":2235},[2225,80028,80029],{"class":2239}," att",[2225,80031,8803],{"class":2235},[2225,80033,8806],{"class":2235},[2225,80035,79277],{"class":2239},[2225,80037,1520],{"class":2235},[2225,80039,9095],{"class":2291},[2225,80041,35140],{"class":2307},[2225,80043,4200],{"class":2235},[2225,80045,80046,80049,80052,80054,80056,80058,80060,80062,80064,80066],{"class":2227,"line":9188},[2225,80047,80048],{"class":2774},"    const",[2225,80050,80051],{"class":2239}," src",[2225,80053,6346],{"class":2235},[2225,80055,45683],{"class":2239},[2225,80057,1520],{"class":2235},[2225,80059,78159],{"class":2239},[2225,80061,8885],{"class":2307},[2225,80063,79386],{"class":2239},[2225,80065,8803],{"class":2307},[2225,80067,80068],{"class":2235},"!\n",[2225,80070,80071,80073,80075,80077,80079,80081,80083,80085,80087,80089,80091,80094,80096,80099,80101,80103,80105,80107,80109],{"class":2227,"line":9193},[2225,80072,79881],{"class":2231},[2225,80074,79781],{"class":2239},[2225,80076,1520],{"class":2235},[2225,80078,133],{"class":2291},[2225,80080,2789],{"class":2307},[2225,80082,6983],{"class":2239},[2225,80084,1381],{"class":2307},[2225,80086,1520],{"class":2235},[2225,80088,3161],{"class":2291},[2225,80090,2789],{"class":2307},[2225,80092,80093],{"class":2239},"att",[2225,80095,1520],{"class":2235},[2225,80097,80098],{"class":2239},"slotName",[2225,80100,2298],{"class":2235},[2225,80102,80051],{"class":2239},[2225,80104,1520],{"class":2235},[2225,80106,64492],{"class":2239},[2225,80108,2298],{"class":2235},[2225,80110,2301],{"class":2235},[2225,80112,80113,80116,80118,80120,80122,80125],{"class":2227,"line":9198},[2225,80114,80115],{"class":2307},"      mimeType",[2225,80117,2311],{"class":2235},[2225,80119,80029],{"class":2239},[2225,80121,1520],{"class":2235},[2225,80123,80124],{"class":2239},"mimeType",[2225,80126,1707],{"class":2235},[2225,80128,80129,80132,80134,80136,80138,80140],{"class":2227,"line":9205},[2225,80130,80131],{"class":2307},"      filename",[2225,80133,2311],{"class":2235},[2225,80135,80029],{"class":2239},[2225,80137,1520],{"class":2235},[2225,80139,79137],{"class":2239},[2225,80141,1707],{"class":2235},[2225,80143,80144,80147],{"class":2227,"line":9210},[2225,80145,80146],{"class":2235},"    }",[2225,80148,2362],{"class":2307},[2225,80150,80151],{"class":2227,"line":9270},[2225,80152,7632],{"class":2235},[2225,80154,80155],{"class":2227,"line":9297},[2225,80156,2285],{"emptyLinePlaceholder":2284},[2225,80158,80159,80161],{"class":2227,"line":67030},[2225,80160,8220],{"class":2231},[2225,80162,80163],{"class":2239}," id\n",[2225,80165,80166],{"class":2227,"line":67035},[2225,80167,7351],{"class":2235},[2225,80169,80170],{"class":2227,"line":67097},[2225,80171,2285],{"emptyLinePlaceholder":2284},[2225,80173,80174,80176,80179,80181,80183,80185,80187,80189,80191,80193,80195,80197,80199,80201],{"class":2227,"line":67143},[2225,80175,2775],{"class":2774},[2225,80177,80178],{"class":2239}," emails ",[2225,80180,2781],{"class":2235},[2225,80182,6466],{"class":2239},[2225,80184,1520],{"class":2235},[2225,80186,6471],{"class":2291},[2225,80188,6474],{"class":2235},[2225,80190,78143],{"class":6342},[2225,80192,6480],{"class":2235},[2225,80194,2789],{"class":2239},[2225,80196,2805],{"class":2235},[2225,80198,78156],{"class":2252},[2225,80200,2805],{"class":2235},[2225,80202,2362],{"class":2239},[2225,80204,80205,80207,80210,80212,80214,80216,80218,80220,80222,80224,80226,80229,80231],{"class":2227,"line":67148},[2225,80206,2775],{"class":2774},[2225,80208,80209],{"class":2239}," emlBytes ",[2225,80211,2781],{"class":2235},[2225,80213,8130],{"class":2235},[2225,80215,60935],{"class":2291},[2225,80217,2789],{"class":2239},[2225,80219,6500],{"class":2231},[2225,80221,60906],{"class":2291},[2225,80223,2789],{"class":2239},[2225,80225,2805],{"class":2235},[2225,80227,80228],{"class":2252},".\u002Finbox\u002Fq2-invoice.eml",[2225,80230,2805],{"class":2235},[2225,80232,8606],{"class":2239},[2225,80234,80235,80237,80240,80242,80244,80246,80249,80251],{"class":2227,"line":67159},[2225,80236,2775],{"class":2774},[2225,80238,80239],{"class":2239}," id ",[2225,80241,2781],{"class":2235},[2225,80243,2784],{"class":2231},[2225,80245,79204],{"class":2291},[2225,80247,80248],{"class":2239},"(emails",[2225,80250,2298],{"class":2235},[2225,80252,80253],{"class":2239}," emlBytes)\n",[2225,80255,80256],{"class":2227,"line":67188},[2225,80257,80258],{"class":2763},"\u002F\u002F → '01J1ZC4Q4T9GQ0V4Y3F2M8KDXP'\n",[1022,80260,80262],{"id":80261},"when-to-flip-to-a-secondary-attachments-collection","When to flip to a secondary attachments collection",[937,80264,38872,80265,80268,80269,80272,80273,80276,80277,80280],{},[941,80266,80267],{},"vault.collection\u003CAttachmentIndex>('attachments_index')"," as a\n",[950,80270,80271],{},"manually maintained secondary collection"," (not built with the\n",[1007,80274,739],{"href":80275},"..\u002Fservices\u002Fmaterialized-views"," service — you\nwrite the sync logic yourself, e.g. inside ",[941,80278,80279],{},"ingestEml","), not a source\nof truth, when:",[1234,80282,80283,80289,80295],{},[1114,80284,80285,80288],{},[950,80286,80287],{},"You cross ~50 000 emails"," and scan+filter queries for\n\"find all PDFs\" become slow.",[1114,80290,80291,80294],{},[950,80292,80293],{},"You have retention rules per attachment type"," (e.g. \"medical\nimages purge after 7 years, legal agreements retain indefinitely\")\n— easier to run lifecycle policies on a dedicated collection.",[1114,80296,80297,80300],{},[950,80298,80299],{},"You want attachments searchable independent of email state","\n(e.g. search deleted emails' attachments for compliance).",[1022,80302,2621],{"id":2620},[1111,80304,80305,80310,80316],{},[1114,80306,80307,80309],{},[1007,80308,547],{"href":548}," — the continuation: threading, HTML rendering, and revision tracking.",[1114,80311,80312,80315],{},[1007,80313,80314],{"href":622},"Blobs service"," — the slot\u002Fchunking\u002Fdedup layer this recipe stores attachments in.",[1114,80317,80318,80321],{},[1007,80319,80320],{"href":500},"Recipes overview"," — the composition model when this recipe stops being enough.",[2645,80323,80324],{},"html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sHdIc, html code.shiki .sHdIc{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#EEFFFF;--shiki-default-font-style:italic;--shiki-dark:#BABED8;--shiki-dark-font-style:italic}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}",{"title":1037,"searchDepth":1038,"depth":1038,"links":80326},[80327,80328,80329,80330,80331,80332,80333],{"id":58393,"depth":1041,"text":58394},{"id":78133,"depth":1041,"text":78134},{"id":78173,"depth":1041,"text":78174},{"id":78403,"depth":1041,"text":78404},{"id":78868,"depth":1041,"text":78869},{"id":80261,"depth":1041,"text":80262},{"id":2620,"depth":1041,"text":2621},"Model MIME emails as one queryable record plus blob slots — the record\u002Fblob decision matrix and the ingest flow.",{},{"title":543,"description":80334},"V3VNiaxED7JeHN8x-3ZsiP7eBvThXeFSlVK-W8M0wOk",{"id":80339,"title":547,"access":12,"apiToc":929,"body":80340,"category":1042,"description":83099,"extension":1044,"meta":83100,"navigation":1042,"nest":1042,"path":548,"seo":83101,"sinceVersion":1042,"status":1049,"stem":549,"subtitle":1042,"tier":1050,"tocExternal":1042,"__hash__":83102},"docs\u002Frecipes\u002Femail-archive-threads-revisions.md",{"type":931,"value":80341,"toc":83090},[80342,80345,80367,80373,80377,80389,80838,80841,80959,81328,81332,81354,81866,81904,81921,82011,82044,82048,82064,82077,82081,82094,82432,82520,82523,82527,82542,82549,82729,82735,82759,82766,82770,82782,83053,83056,83058,83087],[934,80343,547],{"id":80344},"email-archive-threads-revisions",[1061,80346,80347],{},[937,80348,80349,80351,80352,80354,80355,58223,80357,80359,80360,80363,80364,80366],{},[950,80350,1500],{}," the same apps as ",[1007,80353,543],{"href":544}," — this page continues that recipe.\n",[950,80356,58222],{},[941,80358,6775],{}," (unchanged), plus the ",[941,80361,80362],{},"diff"," utilities from ",[941,80365,4151],{}," (no service opt-in required).",[937,80368,80369,80370,80372],{},"This page continues the ",[1007,80371,543],{"href":544}," recipe — it builds threading, HTML rendering, thread-level views, and document-revision tracking on top of the record\u002Fblob layout defined there.",[1022,80374,80376],{"id":80375},"threading-without-a-separate-collection","Threading without a separate collection",[937,80378,80379,80381,80382,1261,80385,80388],{},[941,80380,78229],{}," is derived at ingest from ",[941,80383,80384],{},"References",[941,80386,80387],{},"In-Reply-To",". An\nemail that references a parent already in the vault inherits the\nparent's thread; otherwise this email is the thread root.",[2217,80390,80392],{"className":2219,"code":80391,"language":2221,"meta":1037,"style":1037},"import { generateULID, type Collection } from '@noy-db\u002Fhub'\n\nasync function sha256Hex(input: string): Promise\u003Cstring> {\n  const digest = await crypto.subtle.digest('SHA-256', new TextEncoder().encode(input))\n  return Array.from(new Uint8Array(digest), (b) => b.toString(16).padStart(2, '0')).join('')\n}\n\nasync function deriveThreadId(\n  emails: Collection\u003CEmail>,\n  parsed: ParsedEmail,\n): Promise\u003Cstring> {\n  const parents = parsed.headers['references']?.split(\u002F\\s+\u002F) ?? []\n  for (const parentMessageId of parents) {\n    const existing = emails.query()\n      .where('messageId', '==', parentMessageId)\n      .first()\n    if (existing) return existing.threadId\n  }\n  \u002F\u002F New thread — use a messageId hash as a stable id\n  return (await sha256Hex(parsed.headers['message-id'] ?? generateULID())).slice(0, 16)\n}\n",[941,80393,80394,80418,80422,80451,80501,80579,80583,80587,80597,80612,80623,80637,80681,80700,80717,80746,80754,80775,80779,80784,80834],{"__ignoreMap":1037},[2225,80395,80396,80398,80400,80402,80404,80406,80408,80410,80412,80414,80416],{"class":2227,"line":2228},[2225,80397,2232],{"class":2231},[2225,80399,2236],{"class":2235},[2225,80401,79076],{"class":2239},[2225,80403,2298],{"class":2235},[2225,80405,3399],{"class":2231},[2225,80407,79083],{"class":2239},[2225,80409,2243],{"class":2235},[2225,80411,2246],{"class":2231},[2225,80413,2249],{"class":2235},[2225,80415,1086],{"class":2252},[2225,80417,2256],{"class":2235},[2225,80419,80420],{"class":2227,"line":1041},[2225,80421,2285],{"emptyLinePlaceholder":2284},[2225,80423,80424,80426,80428,80431,80433,80435,80437,80439,80441,80443,80445,80447,80449],{"class":2227,"line":1038},[2225,80425,70491],{"class":2774},[2225,80427,40856],{"class":2774},[2225,80429,80430],{"class":2291}," sha256Hex",[2225,80432,2789],{"class":2235},[2225,80434,16450],{"class":7052},[2225,80436,2311],{"class":2235},[2225,80438,6366],{"class":6342},[2225,80440,6255],{"class":2235},[2225,80442,7079],{"class":6342},[2225,80444,6474],{"class":2235},[2225,80446,7259],{"class":6342},[2225,80448,6480],{"class":2235},[2225,80450,2301],{"class":2235},[2225,80452,80453,80455,80458,80460,80462,80465,80467,80470,80472,80475,80477,80479,80481,80483,80485,80487,80489,80491,80493,80495,80497,80499],{"class":2227,"line":2288},[2225,80454,8123],{"class":2774},[2225,80456,80457],{"class":2239}," digest",[2225,80459,6346],{"class":2235},[2225,80461,2784],{"class":2231},[2225,80463,80464],{"class":2239}," crypto",[2225,80466,1520],{"class":2235},[2225,80468,80469],{"class":2239},"subtle",[2225,80471,1520],{"class":2235},[2225,80473,80474],{"class":2291},"digest",[2225,80476,2789],{"class":2307},[2225,80478,2805],{"class":2235},[2225,80480,13766],{"class":2252},[2225,80482,2805],{"class":2235},[2225,80484,2298],{"class":2235},[2225,80486,8130],{"class":2235},[2225,80488,69739],{"class":2291},[2225,80490,4228],{"class":2307},[2225,80492,1520],{"class":2235},[2225,80494,69746],{"class":2291},[2225,80496,2789],{"class":2307},[2225,80498,16450],{"class":2239},[2225,80500,8606],{"class":2307},[2225,80502,80503,80505,80507,80509,80511,80513,80515,80517,80519,80521,80523,80525,80527,80530,80532,80534,80537,80539,80542,80544,80547,80549,80551,80554,80556,80558,80560,80562,80564,80566,80568,80570,80572,80574,80577],{"class":2227,"line":2304},[2225,80504,8220],{"class":2231},[2225,80506,66498],{"class":2239},[2225,80508,1520],{"class":2235},[2225,80510,2272],{"class":2291},[2225,80512,2789],{"class":2307},[2225,80514,70371],{"class":2235},[2225,80516,60935],{"class":2291},[2225,80518,2789],{"class":2307},[2225,80520,80474],{"class":2239},[2225,80522,1381],{"class":2307},[2225,80524,2298],{"class":2235},[2225,80526,953],{"class":2235},[2225,80528,80529],{"class":7052},"b",[2225,80531,1381],{"class":2235},[2225,80533,8116],{"class":2774},[2225,80535,80536],{"class":2239}," b",[2225,80538,1520],{"class":2235},[2225,80540,80541],{"class":2291},"toString",[2225,80543,2789],{"class":2307},[2225,80545,80546],{"class":6527},"16",[2225,80548,1381],{"class":2307},[2225,80550,1520],{"class":2235},[2225,80552,80553],{"class":2291},"padStart",[2225,80555,2789],{"class":2307},[2225,80557,5470],{"class":6527},[2225,80559,2298],{"class":2235},[2225,80561,2249],{"class":2235},[2225,80563,5402],{"class":2252},[2225,80565,2805],{"class":2235},[2225,80567,8454],{"class":2307},[2225,80569,1520],{"class":2235},[2225,80571,63728],{"class":2291},[2225,80573,2789],{"class":2307},[2225,80575,80576],{"class":2235},"''",[2225,80578,2362],{"class":2307},[2225,80580,80581],{"class":2227,"line":2356},[2225,80582,7351],{"class":2235},[2225,80584,80585],{"class":2227,"line":2830},[2225,80586,2285],{"emptyLinePlaceholder":2284},[2225,80588,80589,80591,80593,80595],{"class":2227,"line":2861},[2225,80590,70491],{"class":2774},[2225,80592,40856],{"class":2774},[2225,80594,79479],{"class":2291},[2225,80596,11683],{"class":2235},[2225,80598,80599,80602,80604,80606,80608,80610],{"class":2227,"line":4246},[2225,80600,80601],{"class":7052},"  emails",[2225,80603,2311],{"class":2235},[2225,80605,79083],{"class":6342},[2225,80607,6474],{"class":2235},[2225,80609,78143],{"class":6342},[2225,80611,12006],{"class":2235},[2225,80613,80614,80617,80619,80621],{"class":2227,"line":4253},[2225,80615,80616],{"class":7052},"  parsed",[2225,80618,2311],{"class":2235},[2225,80620,78892],{"class":6342},[2225,80622,1707],{"class":2235},[2225,80624,80625,80627,80629,80631,80633,80635],{"class":2227,"line":4259},[2225,80626,6255],{"class":2235},[2225,80628,7079],{"class":6342},[2225,80630,6474],{"class":2235},[2225,80632,7259],{"class":6342},[2225,80634,6480],{"class":2235},[2225,80636,2301],{"class":2235},[2225,80638,80639,80641,80644,80646,80648,80650,80652,80654,80656,80658,80660,80662,80664,80666,80668,80670,80672,80674,80676,80678],{"class":2227,"line":6548},[2225,80640,8123],{"class":2774},[2225,80642,80643],{"class":2239}," parents",[2225,80645,6346],{"class":2235},[2225,80647,45683],{"class":2239},[2225,80649,1520],{"class":2235},[2225,80651,52729],{"class":2239},[2225,80653,8885],{"class":2307},[2225,80655,2805],{"class":2235},[2225,80657,78235],{"class":2252},[2225,80659,2805],{"class":2235},[2225,80661,8803],{"class":2307},[2225,80663,8457],{"class":2235},[2225,80665,8841],{"class":2291},[2225,80667,2789],{"class":2307},[2225,80669,1625],{"class":2235},[2225,80671,79551],{"class":2252},[2225,80673,79554],{"class":2235},[2225,80675,8495],{"class":2307},[2225,80677,8381],{"class":2235},[2225,80679,80680],{"class":2307}," []\n",[2225,80682,80683,80685,80687,80689,80692,80694,80696,80698],{"class":2227,"line":6576},[2225,80684,80016],{"class":2231},[2225,80686,953],{"class":2307},[2225,80688,2775],{"class":2774},[2225,80690,80691],{"class":2239}," parentMessageId",[2225,80693,8806],{"class":2235},[2225,80695,80643],{"class":2239},[2225,80697,8495],{"class":2307},[2225,80699,4200],{"class":2235},[2225,80701,80702,80704,80707,80709,80711,80713,80715],{"class":2227,"line":6582},[2225,80703,80048],{"class":2774},[2225,80705,80706],{"class":2239}," existing",[2225,80708,6346],{"class":2235},[2225,80710,79781],{"class":2239},[2225,80712,1520],{"class":2235},[2225,80714,5286],{"class":2291},[2225,80716,8146],{"class":2307},[2225,80718,80719,80722,80724,80726,80728,80730,80732,80734,80736,80738,80740,80742,80744],{"class":2227,"line":6587},[2225,80720,80721],{"class":2235},"      .",[2225,80723,3016],{"class":2291},[2225,80725,2789],{"class":2307},[2225,80727,2805],{"class":2235},[2225,80729,78226],{"class":2252},[2225,80731,2805],{"class":2235},[2225,80733,2298],{"class":2235},[2225,80735,2249],{"class":2235},[2225,80737,21901],{"class":2252},[2225,80739,2805],{"class":2235},[2225,80741,2298],{"class":2235},[2225,80743,80691],{"class":2239},[2225,80745,2362],{"class":2307},[2225,80747,80748,80750,80752],{"class":2227,"line":7174},[2225,80749,80721],{"class":2235},[2225,80751,3233],{"class":2291},[2225,80753,8146],{"class":2307},[2225,80755,80756,80759,80761,80764,80766,80768,80770,80772],{"class":2227,"line":7180},[2225,80757,80758],{"class":2231},"    if",[2225,80760,953],{"class":2307},[2225,80762,80763],{"class":2239},"existing",[2225,80765,8495],{"class":2307},[2225,80767,40426],{"class":2231},[2225,80769,80706],{"class":2239},[2225,80771,1520],{"class":2235},[2225,80773,80774],{"class":2239},"threadId\n",[2225,80776,80777],{"class":2227,"line":7220},[2225,80778,7632],{"class":2235},[2225,80780,80781],{"class":2227,"line":7225},[2225,80782,80783],{"class":2763},"  \u002F\u002F New thread — use a messageId hash as a stable id\n",[2225,80785,80786,80788,80790,80792,80794,80796,80798,80800,80802,80804,80806,80808,80810,80812,80814,80816,80819,80821,80823,80825,80827,80829,80832],{"class":2227,"line":7231},[2225,80787,8220],{"class":2231},[2225,80789,953],{"class":2307},[2225,80791,6500],{"class":2231},[2225,80793,80430],{"class":2291},[2225,80795,2789],{"class":2307},[2225,80797,45654],{"class":2239},[2225,80799,1520],{"class":2235},[2225,80801,52729],{"class":2239},[2225,80803,8885],{"class":2307},[2225,80805,2805],{"class":2235},[2225,80807,79457],{"class":2252},[2225,80809,2805],{"class":2235},[2225,80811,8890],{"class":2307},[2225,80813,8381],{"class":2235},[2225,80815,79076],{"class":2291},[2225,80817,80818],{"class":2307},"()))",[2225,80820,1520],{"class":2235},[2225,80822,8733],{"class":2291},[2225,80824,2789],{"class":2307},[2225,80826,5402],{"class":6527},[2225,80828,2298],{"class":2235},[2225,80830,80831],{"class":6527}," 16",[2225,80833,2362],{"class":2307},[2225,80835,80836],{"class":2227,"line":7267},[2225,80837,7351],{"class":2235},[937,80839,80840],{},"Querying a thread is a plain query — synchronous, because the default\neager-mode collection holds the decrypted working set in memory:",[2217,80842,80844],{"className":2219,"code":80843,"language":2221,"meta":1037,"style":1037},"const email = (await emails.get(id))!          \u002F\u002F id from ingestEml above\n\nconst thread = emails.query()\n  .where('threadId', '==', email.threadId)\n  .orderBy('receivedAt', 'asc')\n  .toArray()\n",[941,80845,80846,80873,80877,80894,80926,80951],{"__ignoreMap":1037},[2225,80847,80848,80850,80853,80855,80857,80859,80861,80863,80865,80868,80870],{"class":2227,"line":2228},[2225,80849,2775],{"class":2774},[2225,80851,80852],{"class":2239}," email ",[2225,80854,2781],{"class":2235},[2225,80856,953],{"class":2239},[2225,80858,6500],{"class":2231},[2225,80860,79781],{"class":2239},[2225,80862,1520],{"class":2235},[2225,80864,3158],{"class":2291},[2225,80866,80867],{"class":2239},"(id))",[2225,80869,35134],{"class":2235},[2225,80871,80872],{"class":2763},"          \u002F\u002F id from ingestEml above\n",[2225,80874,80875],{"class":2227,"line":1041},[2225,80876,2285],{"emptyLinePlaceholder":2284},[2225,80878,80879,80881,80884,80886,80888,80890,80892],{"class":2227,"line":1038},[2225,80880,2775],{"class":2774},[2225,80882,80883],{"class":2239}," thread ",[2225,80885,2781],{"class":2235},[2225,80887,79781],{"class":2239},[2225,80889,1520],{"class":2235},[2225,80891,5286],{"class":2291},[2225,80893,8146],{"class":2239},[2225,80895,80896,80898,80900,80902,80904,80906,80908,80910,80912,80914,80916,80918,80921,80923],{"class":2227,"line":2288},[2225,80897,26235],{"class":2235},[2225,80899,3016],{"class":2291},[2225,80901,2789],{"class":2239},[2225,80903,2805],{"class":2235},[2225,80905,78229],{"class":2252},[2225,80907,2805],{"class":2235},[2225,80909,2298],{"class":2235},[2225,80911,2249],{"class":2235},[2225,80913,21901],{"class":2252},[2225,80915,2805],{"class":2235},[2225,80917,2298],{"class":2235},[2225,80919,80920],{"class":2239}," email",[2225,80922,1520],{"class":2235},[2225,80924,80925],{"class":2239},"threadId)\n",[2225,80927,80928,80930,80932,80934,80936,80938,80940,80942,80944,80947,80949],{"class":2227,"line":2304},[2225,80929,26235],{"class":2235},[2225,80931,3019],{"class":2291},[2225,80933,2789],{"class":2239},[2225,80935,2805],{"class":2235},[2225,80937,78210],{"class":2252},[2225,80939,2805],{"class":2235},[2225,80941,2298],{"class":2235},[2225,80943,2249],{"class":2235},[2225,80945,80946],{"class":2252},"asc",[2225,80948,2805],{"class":2235},[2225,80950,2362],{"class":2239},[2225,80952,80953,80955,80957],{"class":2227,"line":2356},[2225,80954,26235],{"class":2235},[2225,80956,3230],{"class":2291},[2225,80958,8146],{"class":2239},[2217,80960,80962],{"className":6604,"code":80961,"language":141,"meta":1037,"style":1037},"[\n  { \"id\": \"01J1ZC4Q4T9GQ0V4Y3F2M8KDXP\", \"subject\": \"Q2 invoice\",\n    \"from\": { \"email\": \"ana@example.com\", \"name\": \"Ana\" },\n    \"receivedAt\": \"2026-04-10T09:12:00Z\", \"threadId\": \"9f2c41d8a0b7e3c6\" },\n  { \"id\": \"01J1ZD7N2W5RQ8V0X4H6T3EBSA\", \"subject\": \"Re: Q2 invoice\",\n    \"from\": { \"email\": \"leo@example.com\", \"name\": \"Leo\" },\n    \"receivedAt\": \"2026-04-10T14:03:00Z\", \"threadId\": \"9f2c41d8a0b7e3c6\" },\n  { \"id\": \"01J1ZF9S6K2TQ4V8Y0B3W7DCRM\", \"subject\": \"Re: Q2 invoice\",\n    \"from\": { \"email\": \"ana@example.com\", \"name\": \"Ana\" },\n    \"receivedAt\": \"2026-04-11T08:40:00Z\", \"threadId\": \"9f2c41d8a0b7e3c6\" }\n]\n",[941,80963,80964,80968,81006,81053,81089,81127,81173,81208,81245,81289,81324],{"__ignoreMap":1037},[2225,80965,80966],{"class":2227,"line":2228},[2225,80967,26349],{"class":2235},[2225,80969,80970,80972,80974,80976,80978,80980,80982,80985,80987,80989,80991,80993,80995,80997,80999,81002,81004],{"class":2227,"line":1041},[2225,80971,26354],{"class":2235},[2225,80973,6614],{"class":2235},[2225,80975,6983],{"class":2774},[2225,80977,6620],{"class":2235},[2225,80979,2311],{"class":2235},[2225,80981,6614],{"class":2235},[2225,80983,80984],{"class":2252},"01J1ZC4Q4T9GQ0V4Y3F2M8KDXP",[2225,80986,6620],{"class":2235},[2225,80988,2298],{"class":2235},[2225,80990,6614],{"class":2235},[2225,80992,57531],{"class":2774},[2225,80994,6620],{"class":2235},[2225,80996,2311],{"class":2235},[2225,80998,6614],{"class":2235},[2225,81000,81001],{"class":2252},"Q2 invoice",[2225,81003,6620],{"class":2235},[2225,81005,1707],{"class":2235},[2225,81007,81008,81010,81012,81014,81016,81018,81020,81023,81025,81027,81029,81032,81034,81036,81038,81040,81042,81044,81046,81049,81051],{"class":2227,"line":1038},[2225,81009,14201],{"class":2235},[2225,81011,2272],{"class":2774},[2225,81013,6620],{"class":2235},[2225,81015,2311],{"class":2235},[2225,81017,2236],{"class":2235},[2225,81019,6614],{"class":2235},[2225,81021,81022],{"class":6342},"email",[2225,81024,6620],{"class":2235},[2225,81026,2311],{"class":2235},[2225,81028,6614],{"class":2235},[2225,81030,81031],{"class":2252},"ana@example.com",[2225,81033,6620],{"class":2235},[2225,81035,2298],{"class":2235},[2225,81037,6614],{"class":2235},[2225,81039,4914],{"class":6342},[2225,81041,6620],{"class":2235},[2225,81043,2311],{"class":2235},[2225,81045,6614],{"class":2235},[2225,81047,81048],{"class":2252},"Ana",[2225,81050,6620],{"class":2235},[2225,81052,2353],{"class":2235},[2225,81054,81055,81057,81059,81061,81063,81065,81068,81070,81072,81074,81076,81078,81080,81082,81085,81087],{"class":2227,"line":2288},[2225,81056,14201],{"class":2235},[2225,81058,78210],{"class":2774},[2225,81060,6620],{"class":2235},[2225,81062,2311],{"class":2235},[2225,81064,6614],{"class":2235},[2225,81066,81067],{"class":2252},"2026-04-10T09:12:00Z",[2225,81069,6620],{"class":2235},[2225,81071,2298],{"class":2235},[2225,81073,6614],{"class":2235},[2225,81075,78229],{"class":2774},[2225,81077,6620],{"class":2235},[2225,81079,2311],{"class":2235},[2225,81081,6614],{"class":2235},[2225,81083,81084],{"class":2252},"9f2c41d8a0b7e3c6",[2225,81086,6620],{"class":2235},[2225,81088,2353],{"class":2235},[2225,81090,81091,81093,81095,81097,81099,81101,81103,81106,81108,81110,81112,81114,81116,81118,81120,81123,81125],{"class":2227,"line":2304},[2225,81092,26354],{"class":2235},[2225,81094,6614],{"class":2235},[2225,81096,6983],{"class":2774},[2225,81098,6620],{"class":2235},[2225,81100,2311],{"class":2235},[2225,81102,6614],{"class":2235},[2225,81104,81105],{"class":2252},"01J1ZD7N2W5RQ8V0X4H6T3EBSA",[2225,81107,6620],{"class":2235},[2225,81109,2298],{"class":2235},[2225,81111,6614],{"class":2235},[2225,81113,57531],{"class":2774},[2225,81115,6620],{"class":2235},[2225,81117,2311],{"class":2235},[2225,81119,6614],{"class":2235},[2225,81121,81122],{"class":2252},"Re: Q2 invoice",[2225,81124,6620],{"class":2235},[2225,81126,1707],{"class":2235},[2225,81128,81129,81131,81133,81135,81137,81139,81141,81143,81145,81147,81149,81152,81154,81156,81158,81160,81162,81164,81166,81169,81171],{"class":2227,"line":2356},[2225,81130,14201],{"class":2235},[2225,81132,2272],{"class":2774},[2225,81134,6620],{"class":2235},[2225,81136,2311],{"class":2235},[2225,81138,2236],{"class":2235},[2225,81140,6614],{"class":2235},[2225,81142,81022],{"class":6342},[2225,81144,6620],{"class":2235},[2225,81146,2311],{"class":2235},[2225,81148,6614],{"class":2235},[2225,81150,81151],{"class":2252},"leo@example.com",[2225,81153,6620],{"class":2235},[2225,81155,2298],{"class":2235},[2225,81157,6614],{"class":2235},[2225,81159,4914],{"class":6342},[2225,81161,6620],{"class":2235},[2225,81163,2311],{"class":2235},[2225,81165,6614],{"class":2235},[2225,81167,81168],{"class":2252},"Leo",[2225,81170,6620],{"class":2235},[2225,81172,2353],{"class":2235},[2225,81174,81175,81177,81179,81181,81183,81185,81188,81190,81192,81194,81196,81198,81200,81202,81204,81206],{"class":2227,"line":2830},[2225,81176,14201],{"class":2235},[2225,81178,78210],{"class":2774},[2225,81180,6620],{"class":2235},[2225,81182,2311],{"class":2235},[2225,81184,6614],{"class":2235},[2225,81186,81187],{"class":2252},"2026-04-10T14:03:00Z",[2225,81189,6620],{"class":2235},[2225,81191,2298],{"class":2235},[2225,81193,6614],{"class":2235},[2225,81195,78229],{"class":2774},[2225,81197,6620],{"class":2235},[2225,81199,2311],{"class":2235},[2225,81201,6614],{"class":2235},[2225,81203,81084],{"class":2252},[2225,81205,6620],{"class":2235},[2225,81207,2353],{"class":2235},[2225,81209,81210,81212,81214,81216,81218,81220,81222,81225,81227,81229,81231,81233,81235,81237,81239,81241,81243],{"class":2227,"line":2861},[2225,81211,26354],{"class":2235},[2225,81213,6614],{"class":2235},[2225,81215,6983],{"class":2774},[2225,81217,6620],{"class":2235},[2225,81219,2311],{"class":2235},[2225,81221,6614],{"class":2235},[2225,81223,81224],{"class":2252},"01J1ZF9S6K2TQ4V8Y0B3W7DCRM",[2225,81226,6620],{"class":2235},[2225,81228,2298],{"class":2235},[2225,81230,6614],{"class":2235},[2225,81232,57531],{"class":2774},[2225,81234,6620],{"class":2235},[2225,81236,2311],{"class":2235},[2225,81238,6614],{"class":2235},[2225,81240,81122],{"class":2252},[2225,81242,6620],{"class":2235},[2225,81244,1707],{"class":2235},[2225,81246,81247,81249,81251,81253,81255,81257,81259,81261,81263,81265,81267,81269,81271,81273,81275,81277,81279,81281,81283,81285,81287],{"class":2227,"line":4246},[2225,81248,14201],{"class":2235},[2225,81250,2272],{"class":2774},[2225,81252,6620],{"class":2235},[2225,81254,2311],{"class":2235},[2225,81256,2236],{"class":2235},[2225,81258,6614],{"class":2235},[2225,81260,81022],{"class":6342},[2225,81262,6620],{"class":2235},[2225,81264,2311],{"class":2235},[2225,81266,6614],{"class":2235},[2225,81268,81031],{"class":2252},[2225,81270,6620],{"class":2235},[2225,81272,2298],{"class":2235},[2225,81274,6614],{"class":2235},[2225,81276,4914],{"class":6342},[2225,81278,6620],{"class":2235},[2225,81280,2311],{"class":2235},[2225,81282,6614],{"class":2235},[2225,81284,81048],{"class":2252},[2225,81286,6620],{"class":2235},[2225,81288,2353],{"class":2235},[2225,81290,81291,81293,81295,81297,81299,81301,81304,81306,81308,81310,81312,81314,81316,81318,81320,81322],{"class":2227,"line":4253},[2225,81292,14201],{"class":2235},[2225,81294,78210],{"class":2774},[2225,81296,6620],{"class":2235},[2225,81298,2311],{"class":2235},[2225,81300,6614],{"class":2235},[2225,81302,81303],{"class":2252},"2026-04-11T08:40:00Z",[2225,81305,6620],{"class":2235},[2225,81307,2298],{"class":2235},[2225,81309,6614],{"class":2235},[2225,81311,78229],{"class":2774},[2225,81313,6620],{"class":2235},[2225,81315,2311],{"class":2235},[2225,81317,6614],{"class":2235},[2225,81319,81084],{"class":2252},[2225,81321,6620],{"class":2235},[2225,81323,6369],{"class":2235},[2225,81325,81326],{"class":2227,"line":4259},[2225,81327,26484],{"class":2235},[1022,81329,81331],{"id":81330},"rendering-html-with-inline-images","Rendering HTML with inline images",[937,81333,81334,81335,81338,81339,81342,81343,81345,81346,81349,81350,81353],{},"Emails embed images via ",[941,81336,81337],{},"\u003Cimg src=\"cid:abc123\">"," where ",[941,81340,81341],{},"abc123"," matches\nan attachment's ",[941,81344,79365],{},". Resolve at render time. ",[941,81347,81348],{},"blob(id).get()","\nreturns ",[941,81351,81352],{},"Uint8Array | null"," — a missing slot is a normal outcome, so\nhandle it:",[2217,81355,81357],{"className":2219,"code":81356,"language":2221,"meta":1037,"style":1037},"import { type Collection } from '@noy-db\u002Fhub'\n\nfunction toBase64(bytes: Uint8Array): string {\n  let bin = ''\n  for (const b of bytes) bin += String.fromCharCode(b)\n  return btoa(bin)\n}\n\nasync function renderHtml(emails: Collection\u003CEmail>, emailId: string): Promise\u003Cstring> {\n  const email = await emails.get(emailId)\n  if (!email) throw new Error(`email ${emailId} not found`)\n\n  const htmlBytes = await emails.blob(emailId).get('body-html')\n  if (!htmlBytes) return email.bodyText ?? email.bodyPreview   \u002F\u002F no HTML part stored\n\n  let html = new TextDecoder().decode(htmlBytes)\n  for (const att of email.attachments ?? []) {\n    if (!att.contentId) continue\n    const bytes = await emails.blob(emailId).get(att.slotName)\n    if (!bytes) continue                                       \u002F\u002F slot missing — leave the cid: ref\n    const dataUrl = `data:${att.mimeType};base64,${toBase64(bytes)}`\n    html = html.replaceAll(`cid:${att.contentId}`, dataUrl)\n  }\n  return html\n}\n",[941,81358,81359,81379,81383,81404,81416,81452,81465,81469,81473,81515,81538,81576,81580,81617,81649,81653,81680,81705,81723,81759,81777,81814,81851,81855,81862],{"__ignoreMap":1037},[2225,81360,81361,81363,81365,81367,81369,81371,81373,81375,81377],{"class":2227,"line":2228},[2225,81362,2232],{"class":2231},[2225,81364,2236],{"class":2235},[2225,81366,3399],{"class":2231},[2225,81368,79083],{"class":2239},[2225,81370,2243],{"class":2235},[2225,81372,2246],{"class":2231},[2225,81374,2249],{"class":2235},[2225,81376,1086],{"class":2252},[2225,81378,2256],{"class":2235},[2225,81380,81381],{"class":2227,"line":1041},[2225,81382,2285],{"emptyLinePlaceholder":2284},[2225,81384,81385,81387,81390,81392,81394,81396,81398,81400,81402],{"class":2227,"line":1038},[2225,81386,12641],{"class":2774},[2225,81388,81389],{"class":2291}," toBase64",[2225,81391,2789],{"class":2235},[2225,81393,64492],{"class":7052},[2225,81395,2311],{"class":2235},[2225,81397,60935],{"class":6342},[2225,81399,6255],{"class":2235},[2225,81401,6366],{"class":6342},[2225,81403,2301],{"class":2235},[2225,81405,81406,81408,81411,81413],{"class":2227,"line":2288},[2225,81407,66632],{"class":2774},[2225,81409,81410],{"class":2239}," bin",[2225,81412,6346],{"class":2235},[2225,81414,81415],{"class":2235}," ''\n",[2225,81417,81418,81420,81422,81424,81426,81428,81430,81432,81435,81438,81441,81443,81446,81448,81450],{"class":2227,"line":2304},[2225,81419,80016],{"class":2231},[2225,81421,953],{"class":2307},[2225,81423,2775],{"class":2774},[2225,81425,80536],{"class":2239},[2225,81427,8806],{"class":2235},[2225,81429,29996],{"class":2239},[2225,81431,8495],{"class":2307},[2225,81433,81434],{"class":2239},"bin",[2225,81436,81437],{"class":2235}," +=",[2225,81439,81440],{"class":2239}," String",[2225,81442,1520],{"class":2235},[2225,81444,81445],{"class":2291},"fromCharCode",[2225,81447,2789],{"class":2307},[2225,81449,80529],{"class":2239},[2225,81451,2362],{"class":2307},[2225,81453,81454,81456,81459,81461,81463],{"class":2227,"line":2356},[2225,81455,8220],{"class":2231},[2225,81457,81458],{"class":2291}," btoa",[2225,81460,2789],{"class":2307},[2225,81462,81434],{"class":2239},[2225,81464,2362],{"class":2307},[2225,81466,81467],{"class":2227,"line":2830},[2225,81468,7351],{"class":2235},[2225,81470,81471],{"class":2227,"line":2861},[2225,81472,2285],{"emptyLinePlaceholder":2284},[2225,81474,81475,81477,81479,81482,81484,81486,81488,81490,81492,81494,81496,81499,81501,81503,81505,81507,81509,81511,81513],{"class":2227,"line":4246},[2225,81476,70491],{"class":2774},[2225,81478,40856],{"class":2774},[2225,81480,81481],{"class":2291}," renderHtml",[2225,81483,2789],{"class":2235},[2225,81485,78156],{"class":7052},[2225,81487,2311],{"class":2235},[2225,81489,79083],{"class":6342},[2225,81491,6474],{"class":2235},[2225,81493,78143],{"class":6342},[2225,81495,17840],{"class":2235},[2225,81497,81498],{"class":7052}," emailId",[2225,81500,2311],{"class":2235},[2225,81502,6366],{"class":6342},[2225,81504,6255],{"class":2235},[2225,81506,7079],{"class":6342},[2225,81508,6474],{"class":2235},[2225,81510,7259],{"class":6342},[2225,81512,6480],{"class":2235},[2225,81514,2301],{"class":2235},[2225,81516,81517,81519,81521,81523,81525,81527,81529,81531,81533,81536],{"class":2227,"line":4253},[2225,81518,8123],{"class":2774},[2225,81520,80920],{"class":2239},[2225,81522,6346],{"class":2235},[2225,81524,2784],{"class":2231},[2225,81526,79781],{"class":2239},[2225,81528,1520],{"class":2235},[2225,81530,3158],{"class":2291},[2225,81532,2789],{"class":2307},[2225,81534,81535],{"class":2239},"emailId",[2225,81537,2362],{"class":2307},[2225,81539,81540,81542,81544,81546,81548,81550,81552,81554,81556,81558,81560,81563,81565,81567,81569,81572,81574],{"class":2227,"line":4259},[2225,81541,7608],{"class":2231},[2225,81543,953],{"class":2307},[2225,81545,35134],{"class":2235},[2225,81547,81022],{"class":2239},[2225,81549,8495],{"class":2307},[2225,81551,46514],{"class":2231},[2225,81553,8130],{"class":2235},[2225,81555,46519],{"class":2291},[2225,81557,2789],{"class":2307},[2225,81559,44476],{"class":2235},[2225,81561,81562],{"class":2252},"email ",[2225,81564,8197],{"class":2235},[2225,81566,81535],{"class":2239},[2225,81568,2359],{"class":2235},[2225,81570,81571],{"class":2252}," not found",[2225,81573,44476],{"class":2235},[2225,81575,2362],{"class":2307},[2225,81577,81578],{"class":2227,"line":6548},[2225,81579,2285],{"emptyLinePlaceholder":2284},[2225,81581,81582,81584,81587,81589,81591,81593,81595,81597,81599,81601,81603,81605,81607,81609,81611,81613,81615],{"class":2227,"line":6576},[2225,81583,8123],{"class":2774},[2225,81585,81586],{"class":2239}," htmlBytes",[2225,81588,6346],{"class":2235},[2225,81590,2784],{"class":2231},[2225,81592,79781],{"class":2239},[2225,81594,1520],{"class":2235},[2225,81596,133],{"class":2291},[2225,81598,2789],{"class":2307},[2225,81600,81535],{"class":2239},[2225,81602,1381],{"class":2307},[2225,81604,1520],{"class":2235},[2225,81606,3158],{"class":2291},[2225,81608,2789],{"class":2307},[2225,81610,2805],{"class":2235},[2225,81612,78319],{"class":2252},[2225,81614,2805],{"class":2235},[2225,81616,2362],{"class":2307},[2225,81618,81619,81621,81623,81625,81628,81630,81632,81634,81636,81638,81640,81642,81644,81646],{"class":2227,"line":6582},[2225,81620,7608],{"class":2231},[2225,81622,953],{"class":2307},[2225,81624,35134],{"class":2235},[2225,81626,81627],{"class":2239},"htmlBytes",[2225,81629,8495],{"class":2307},[2225,81631,40426],{"class":2231},[2225,81633,80920],{"class":2239},[2225,81635,1520],{"class":2235},[2225,81637,78288],{"class":2239},[2225,81639,8239],{"class":2235},[2225,81641,80920],{"class":2239},[2225,81643,1520],{"class":2235},[2225,81645,78270],{"class":2239},[2225,81647,81648],{"class":2763},"   \u002F\u002F no HTML part stored\n",[2225,81650,81651],{"class":2227,"line":6587},[2225,81652,2285],{"emptyLinePlaceholder":2284},[2225,81654,81655,81657,81660,81662,81664,81667,81669,81671,81674,81676,81678],{"class":2227,"line":7174},[2225,81656,66632],{"class":2774},[2225,81658,81659],{"class":2239}," html",[2225,81661,6346],{"class":2235},[2225,81663,8130],{"class":2235},[2225,81665,81666],{"class":2291}," TextDecoder",[2225,81668,4228],{"class":2307},[2225,81670,1520],{"class":2235},[2225,81672,81673],{"class":2291},"decode",[2225,81675,2789],{"class":2307},[2225,81677,81627],{"class":2239},[2225,81679,2362],{"class":2307},[2225,81681,81682,81684,81686,81688,81690,81692,81694,81696,81698,81700,81703],{"class":2227,"line":7180},[2225,81683,80016],{"class":2231},[2225,81685,953],{"class":2307},[2225,81687,2775],{"class":2774},[2225,81689,80029],{"class":2239},[2225,81691,8806],{"class":2235},[2225,81693,80920],{"class":2239},[2225,81695,1520],{"class":2235},[2225,81697,78159],{"class":2239},[2225,81699,8239],{"class":2235},[2225,81701,81702],{"class":2307}," []) ",[2225,81704,4200],{"class":2235},[2225,81706,81707,81709,81711,81713,81715,81717,81719,81721],{"class":2227,"line":7220},[2225,81708,80758],{"class":2231},[2225,81710,953],{"class":2307},[2225,81712,35134],{"class":2235},[2225,81714,80093],{"class":2239},[2225,81716,1520],{"class":2235},[2225,81718,79365],{"class":2239},[2225,81720,8495],{"class":2307},[2225,81722,8871],{"class":2231},[2225,81724,81725,81727,81729,81731,81733,81735,81737,81739,81741,81743,81745,81747,81749,81751,81753,81755,81757],{"class":2227,"line":7225},[2225,81726,80048],{"class":2774},[2225,81728,29996],{"class":2239},[2225,81730,6346],{"class":2235},[2225,81732,2784],{"class":2231},[2225,81734,79781],{"class":2239},[2225,81736,1520],{"class":2235},[2225,81738,133],{"class":2291},[2225,81740,2789],{"class":2307},[2225,81742,81535],{"class":2239},[2225,81744,1381],{"class":2307},[2225,81746,1520],{"class":2235},[2225,81748,3158],{"class":2291},[2225,81750,2789],{"class":2307},[2225,81752,80093],{"class":2239},[2225,81754,1520],{"class":2235},[2225,81756,80098],{"class":2239},[2225,81758,2362],{"class":2307},[2225,81760,81761,81763,81765,81767,81769,81771,81774],{"class":2227,"line":7231},[2225,81762,80758],{"class":2231},[2225,81764,953],{"class":2307},[2225,81766,35134],{"class":2235},[2225,81768,64492],{"class":2239},[2225,81770,8495],{"class":2307},[2225,81772,81773],{"class":2231},"continue",[2225,81775,81776],{"class":2763},"                                       \u002F\u002F slot missing — leave the cid: ref\n",[2225,81778,81779,81781,81784,81786,81788,81791,81793,81795,81797,81799,81801,81804,81806,81809,81812],{"class":2227,"line":7267},[2225,81780,80048],{"class":2774},[2225,81782,81783],{"class":2239}," dataUrl",[2225,81785,6346],{"class":2235},[2225,81787,43789],{"class":2235},[2225,81789,81790],{"class":2252},"data:",[2225,81792,8197],{"class":2235},[2225,81794,80093],{"class":2239},[2225,81796,1520],{"class":2235},[2225,81798,80124],{"class":2239},[2225,81800,2359],{"class":2235},[2225,81802,81803],{"class":2252},";base64,",[2225,81805,8197],{"class":2235},[2225,81807,81808],{"class":2291},"toBase64",[2225,81810,81811],{"class":2239},"(bytes)",[2225,81813,8211],{"class":2235},[2225,81815,81816,81819,81821,81823,81825,81828,81830,81832,81835,81837,81839,81841,81843,81845,81847,81849],{"class":2227,"line":7272},[2225,81817,81818],{"class":2239},"    html",[2225,81820,6346],{"class":2235},[2225,81822,81659],{"class":2239},[2225,81824,1520],{"class":2235},[2225,81826,81827],{"class":2291},"replaceAll",[2225,81829,2789],{"class":2307},[2225,81831,44476],{"class":2235},[2225,81833,81834],{"class":2252},"cid:",[2225,81836,8197],{"class":2235},[2225,81838,80093],{"class":2239},[2225,81840,1520],{"class":2235},[2225,81842,79365],{"class":2239},[2225,81844,43799],{"class":2235},[2225,81846,2298],{"class":2235},[2225,81848,81783],{"class":2239},[2225,81850,2362],{"class":2307},[2225,81852,81853],{"class":2227,"line":7278},[2225,81854,7632],{"class":2235},[2225,81856,81857,81859],{"class":2227,"line":7303},[2225,81858,8220],{"class":2231},[2225,81860,81861],{"class":2239}," html\n",[2225,81863,81864],{"class":2227,"line":7308},[2225,81865,7351],{"class":2235},[2217,81867,81869],{"className":2219,"code":81868,"language":2221,"meta":1037,"style":1037},"const html = await renderHtml(emails, id)     \u002F\u002F id from ingestEml above\n\u002F\u002F → '\u003Chtml>...\u003Cimg src=\"data:image\u002Fpng;base64,iVBORw0KGgo...\">...'\n\u002F\u002F   (the cid:abc123 reference was replaced with an inline data: URL)\n",[941,81870,81871,81894,81899],{"__ignoreMap":1037},[2225,81872,81873,81875,81878,81880,81882,81884,81886,81888,81891],{"class":2227,"line":2228},[2225,81874,2775],{"class":2774},[2225,81876,81877],{"class":2239}," html ",[2225,81879,2781],{"class":2235},[2225,81881,2784],{"class":2231},[2225,81883,81481],{"class":2291},[2225,81885,80248],{"class":2239},[2225,81887,2298],{"class":2235},[2225,81889,81890],{"class":2239}," id)     ",[2225,81892,81893],{"class":2763},"\u002F\u002F id from ingestEml above\n",[2225,81895,81896],{"class":2227,"line":1041},[2225,81897,81898],{"class":2763},"\u002F\u002F → '\u003Chtml>...\u003Cimg src=\"data:image\u002Fpng;base64,iVBORw0KGgo...\">...'\n",[2225,81900,81901],{"class":2227,"line":1038},[2225,81902,81903],{"class":2763},"\u002F\u002F   (the cid:abc123 reference was replaced with an inline data: URL)\n",[937,81905,81906,81907,81910,81911,81913,81914,1261,81917,81920],{},"In a browser consumer, prefer ",[941,81908,81909],{},"emails.blob(id).objectURL(slotName)"," over\nbuilding ",[941,81912,81790],{}," URLs by hand — it decrypts the slot and wraps the bytes\nin a browser object URL sized for ",[941,81915,81916],{},"\u003Cimg src>",[941,81918,81919],{},"\u003Ca href>",", without the\nbase64 blowup of inlining bytes into the HTML string:",[2217,81922,81924],{"className":2219,"code":81923,"language":2221,"meta":1037,"style":1037},"const inline = await emails.blob(id).objectURL('att-0-invoice.pdf')\nif (inline) {\n  imgEl.src = inline.url\n  \u002F\u002F ...later, when the element is unmounted or the URL is no longer shown:\n  inline.revoke()\n}\n",[941,81925,81926,81962,81971,81991,81996,82007],{"__ignoreMap":1037},[2225,81927,81928,81930,81933,81935,81937,81939,81941,81943,81946,81948,81951,81953,81955,81958,81960],{"class":2227,"line":2228},[2225,81929,2775],{"class":2774},[2225,81931,81932],{"class":2239}," inline ",[2225,81934,2781],{"class":2235},[2225,81936,2784],{"class":2231},[2225,81938,79781],{"class":2239},[2225,81940,1520],{"class":2235},[2225,81942,133],{"class":2291},[2225,81944,81945],{"class":2239},"(id)",[2225,81947,1520],{"class":2235},[2225,81949,81950],{"class":2291},"objectURL",[2225,81952,2789],{"class":2239},[2225,81954,2805],{"class":2235},[2225,81956,81957],{"class":2252},"att-0-invoice.pdf",[2225,81959,2805],{"class":2235},[2225,81961,2362],{"class":2239},[2225,81963,81964,81966,81969],{"class":2227,"line":1041},[2225,81965,35129],{"class":2231},[2225,81967,81968],{"class":2239}," (inline) ",[2225,81970,4200],{"class":2235},[2225,81972,81973,81976,81978,81981,81983,81986,81988],{"class":2227,"line":1038},[2225,81974,81975],{"class":2239},"  imgEl",[2225,81977,1520],{"class":2235},[2225,81979,81980],{"class":2239},"src",[2225,81982,6346],{"class":2235},[2225,81984,81985],{"class":2239}," inline",[2225,81987,1520],{"class":2235},[2225,81989,81990],{"class":2239},"url\n",[2225,81992,81993],{"class":2227,"line":2288},[2225,81994,81995],{"class":2763},"  \u002F\u002F ...later, when the element is unmounted or the URL is no longer shown:\n",[2225,81997,81998,82001,82003,82005],{"class":2227,"line":2304},[2225,81999,82000],{"class":2239},"  inline",[2225,82002,1520],{"class":2235},[2225,82004,10653],{"class":2291},[2225,82006,8146],{"class":2307},[2225,82008,82009],{"class":2227,"line":2356},[2225,82010,7351],{"class":2235},[937,82012,82013,2557,82016,82018,82019,82022,82023,82030,82031,82033,82034,82036,82037,82040,82041,82043],{},[941,82014,82015],{},"objectURL()",[941,82017,6651],{}," for a missing slot, or ",[941,82020,82021],{},"{ url, revoke }"," for\nan existing one. ",[950,82024,82025,82026,82029],{},"You must call ",[941,82027,82028],{},"revoke()"," when the URL is no longer\nneeded"," — until then, both the object URL and the decrypted bytes behind\nit stay pinned in memory, which leaks on long-lived pages. For a full\n",[941,82032,40875],{}," with HTTP metadata headers instead of a URL (e.g. to pipe into\na ",[941,82035,52725],{},"-style API), use ",[941,82038,82039],{},"blob(id).response(slotName)",", which returns\n",[941,82042,6651],{}," for a missing slot the same way.",[1022,82045,82047],{"id":82046},"thread-level-view","Thread-level view",[937,82049,82050,82052,82053,82056,82057,1625,82060,82063],{},[941,82051,78229],{}," on the record is the index. The ",[1092,82054,82055],{},"meaning"," is the thread.\nUsers don't think \"those 7 emails on April 12th\"; they think \"the\nthread about the Q2 invoice\". Most UI operations fold across the\nthread: title (first email's subject minus ",[941,82058,82059],{},"Re:",[941,82061,82062],{},"Fwd:","), participants\n(union across every email), time span, unread \u002F starred disjunction,\ndocument set grouped by content address.",[937,82065,82066,82067,82070,82071,82073,82074,82076],{},"Compute these on demand with a query (cheap under ~50 emails per\nthread). For heavier UIs, maintain a ",[941,82068,82069],{},"vault.collection\u003CThread>('threads')","\nas a manually maintained secondary collection, updated as a side-effect\nof every email ",[941,82072,3161],{}," — this is application code you write, not the\n",[1007,82075,739],{"href":80275}," service (that\nservice auto-syncs from a registered query; here the \"query\" is a\nhandful of fields folded across the thread's emails, which doesn't fit\nits query\u002Faggregate shape).",[1022,82078,82080],{"id":82079},"shared-documents-across-a-thread","Shared documents across a thread",[937,82082,82083,82084,82087,82088,82091,82092,2311],{},"The common case: someone attaches a PDF; three replies quote it back.\nBecause blobs are content-addressed (",[941,82085,82086],{},"eTag = HMAC-SHA-256(blob key, plaintext bytes)"," — keyed, so the storage backend cannot recognise known\ncontent), noy-db stores ",[950,82089,82090],{},"one blob"," regardless of how many slots\nreference it. Surface this in the UI by grouping the thread's slots by\n",[941,82093,78163],{},[2217,82095,82097],{"className":2219,"code":82096,"language":2221,"meta":1037,"style":1037},"const threadId = '9f2c41d8a0b7e3c6'            \u002F\u002F the thread the user is viewing\n\nconst thread = emails.query()\n  .where('threadId', '==', threadId)\n  .toArray()\n\nconst byETag = new Map\u003Cstring, { filename: string; emails: string[] }>()\nfor (const email of thread) {\n  for (const slot of await emails.blob(email.id).list()) {\n    if (!slot.name.startsWith('att-')) continue\n    const entry = byETag.get(slot.eTag) ?? { filename: slot.filename, emails: [] }\n    entry.emails.push(email.id)\n    byETag.set(slot.eTag, entry)\n  }\n}\n\nconsole.log(Object.fromEntries(byETag))\n",[941,82098,82099,82117,82121,82137,82164,82172,82176,82219,82236,82274,82304,82354,82378,82401,82405,82409,82413],{"__ignoreMap":1037},[2225,82100,82101,82103,82106,82108,82110,82112,82114],{"class":2227,"line":2228},[2225,82102,2775],{"class":2774},[2225,82104,82105],{"class":2239}," threadId ",[2225,82107,2781],{"class":2235},[2225,82109,2249],{"class":2235},[2225,82111,81084],{"class":2252},[2225,82113,2805],{"class":2235},[2225,82115,82116],{"class":2763},"            \u002F\u002F the thread the user is viewing\n",[2225,82118,82119],{"class":2227,"line":1041},[2225,82120,2285],{"emptyLinePlaceholder":2284},[2225,82122,82123,82125,82127,82129,82131,82133,82135],{"class":2227,"line":1038},[2225,82124,2775],{"class":2774},[2225,82126,80883],{"class":2239},[2225,82128,2781],{"class":2235},[2225,82130,79781],{"class":2239},[2225,82132,1520],{"class":2235},[2225,82134,5286],{"class":2291},[2225,82136,8146],{"class":2239},[2225,82138,82139,82141,82143,82145,82147,82149,82151,82153,82155,82157,82159,82161],{"class":2227,"line":2288},[2225,82140,26235],{"class":2235},[2225,82142,3016],{"class":2291},[2225,82144,2789],{"class":2239},[2225,82146,2805],{"class":2235},[2225,82148,78229],{"class":2252},[2225,82150,2805],{"class":2235},[2225,82152,2298],{"class":2235},[2225,82154,2249],{"class":2235},[2225,82156,21901],{"class":2252},[2225,82158,2805],{"class":2235},[2225,82160,2298],{"class":2235},[2225,82162,82163],{"class":2239}," threadId)\n",[2225,82165,82166,82168,82170],{"class":2227,"line":2304},[2225,82167,26235],{"class":2235},[2225,82169,3230],{"class":2291},[2225,82171,8146],{"class":2239},[2225,82173,82174],{"class":2227,"line":2356},[2225,82175,2285],{"emptyLinePlaceholder":2284},[2225,82177,82178,82180,82183,82185,82187,82189,82191,82193,82195,82197,82199,82201,82203,82205,82207,82209,82211,82214,82217],{"class":2227,"line":2830},[2225,82179,2775],{"class":2774},[2225,82181,82182],{"class":2239}," byETag ",[2225,82184,2781],{"class":2235},[2225,82186,8130],{"class":2235},[2225,82188,8133],{"class":2291},[2225,82190,6474],{"class":2235},[2225,82192,7259],{"class":6342},[2225,82194,2298],{"class":2235},[2225,82196,2236],{"class":2235},[2225,82198,32095],{"class":2307},[2225,82200,2311],{"class":2235},[2225,82202,6366],{"class":6342},[2225,82204,3769],{"class":2235},[2225,82206,79781],{"class":2307},[2225,82208,2311],{"class":2235},[2225,82210,6366],{"class":6342},[2225,82212,82213],{"class":2239},"[] ",[2225,82215,82216],{"class":2235},"}>",[2225,82218,8146],{"class":2239},[2225,82220,82221,82223,82225,82227,82229,82231,82234],{"class":2227,"line":2861},[2225,82222,4646],{"class":2231},[2225,82224,953],{"class":2239},[2225,82226,2775],{"class":2774},[2225,82228,80852],{"class":2239},[2225,82230,27066],{"class":2235},[2225,82232,82233],{"class":2239}," thread) ",[2225,82235,4200],{"class":2235},[2225,82237,82238,82240,82242,82244,82246,82248,82250,82252,82254,82256,82258,82260,82262,82264,82266,82268,82270,82272],{"class":2227,"line":4246},[2225,82239,80016],{"class":2231},[2225,82241,953],{"class":2307},[2225,82243,2775],{"class":2774},[2225,82245,45072],{"class":2239},[2225,82247,8806],{"class":2235},[2225,82249,2784],{"class":2231},[2225,82251,79781],{"class":2239},[2225,82253,1520],{"class":2235},[2225,82255,133],{"class":2291},[2225,82257,2789],{"class":2307},[2225,82259,81022],{"class":2239},[2225,82261,1520],{"class":2235},[2225,82263,6983],{"class":2239},[2225,82265,1381],{"class":2307},[2225,82267,1520],{"class":2235},[2225,82269,3167],{"class":2291},[2225,82271,35140],{"class":2307},[2225,82273,4200],{"class":2235},[2225,82275,82276,82278,82280,82282,82284,82286,82288,82290,82292,82294,82296,82298,82300,82302],{"class":2227,"line":4253},[2225,82277,80758],{"class":2231},[2225,82279,953],{"class":2307},[2225,82281,35134],{"class":2235},[2225,82283,18018],{"class":2239},[2225,82285,1520],{"class":2235},[2225,82287,4914],{"class":2239},[2225,82289,1520],{"class":2235},[2225,82291,8705],{"class":2291},[2225,82293,2789],{"class":2307},[2225,82295,2805],{"class":2235},[2225,82297,79381],{"class":2252},[2225,82299,2805],{"class":2235},[2225,82301,7620],{"class":2307},[2225,82303,8871],{"class":2231},[2225,82305,82306,82308,82310,82312,82315,82317,82319,82321,82323,82325,82327,82329,82331,82333,82335,82337,82339,82341,82343,82345,82347,82349,82352],{"class":2227,"line":4259},[2225,82307,80048],{"class":2774},[2225,82309,45642],{"class":2239},[2225,82311,6346],{"class":2235},[2225,82313,82314],{"class":2239}," byETag",[2225,82316,1520],{"class":2235},[2225,82318,3158],{"class":2291},[2225,82320,2789],{"class":2307},[2225,82322,18018],{"class":2239},[2225,82324,1520],{"class":2235},[2225,82326,78163],{"class":2239},[2225,82328,8495],{"class":2307},[2225,82330,8381],{"class":2235},[2225,82332,2236],{"class":2235},[2225,82334,32095],{"class":2307},[2225,82336,2311],{"class":2235},[2225,82338,45072],{"class":2239},[2225,82340,1520],{"class":2235},[2225,82342,79137],{"class":2239},[2225,82344,2298],{"class":2235},[2225,82346,79781],{"class":2307},[2225,82348,2311],{"class":2235},[2225,82350,82351],{"class":2307}," [] ",[2225,82353,7351],{"class":2235},[2225,82355,82356,82359,82361,82363,82365,82368,82370,82372,82374,82376],{"class":2227,"line":6548},[2225,82357,82358],{"class":2239},"    entry",[2225,82360,1520],{"class":2235},[2225,82362,78156],{"class":2239},[2225,82364,1520],{"class":2235},[2225,82366,82367],{"class":2291},"push",[2225,82369,2789],{"class":2307},[2225,82371,81022],{"class":2239},[2225,82373,1520],{"class":2235},[2225,82375,6983],{"class":2239},[2225,82377,2362],{"class":2307},[2225,82379,82380,82383,82385,82387,82389,82391,82393,82395,82397,82399],{"class":2227,"line":6576},[2225,82381,82382],{"class":2239},"    byETag",[2225,82384,1520],{"class":2235},[2225,82386,8528],{"class":2291},[2225,82388,2789],{"class":2307},[2225,82390,18018],{"class":2239},[2225,82392,1520],{"class":2235},[2225,82394,78163],{"class":2239},[2225,82396,2298],{"class":2235},[2225,82398,45642],{"class":2239},[2225,82400,2362],{"class":2307},[2225,82402,82403],{"class":2227,"line":6582},[2225,82404,7632],{"class":2235},[2225,82406,82407],{"class":2227,"line":6587},[2225,82408,7351],{"class":2235},[2225,82410,82411],{"class":2227,"line":7174},[2225,82412,2285],{"emptyLinePlaceholder":2284},[2225,82414,82415,82417,82419,82421,82424,82426,82429],{"class":2227,"line":7180},[2225,82416,28031],{"class":2239},[2225,82418,1520],{"class":2235},[2225,82420,27100],{"class":2291},[2225,82422,82423],{"class":2239},"(Object",[2225,82425,1520],{"class":2235},[2225,82427,82428],{"class":2291},"fromEntries",[2225,82430,82431],{"class":2239},"(byETag))\n",[2217,82433,82435],{"className":6604,"code":82434,"language":141,"meta":1037,"style":1037},"{\n  \"9f41c2d8ab07e3…\": {\n    \"filename\": \"invoice.pdf\",\n    \"emails\": [\"01J1ZC4Q4T…\", \"01J1ZD7N2W…\", \"01J1ZF9S6K…\"]\n  }\n}\n",[941,82436,82437,82441,82454,82473,82512,82516],{"__ignoreMap":1037},[2225,82438,82439],{"class":2227,"line":2228},[2225,82440,4200],{"class":2235},[2225,82442,82443,82445,82448,82450,82452],{"class":2227,"line":1041},[2225,82444,9368],{"class":2235},[2225,82446,82447],{"class":2774},"9f41c2d8ab07e3…",[2225,82449,6620],{"class":2235},[2225,82451,2311],{"class":2235},[2225,82453,2301],{"class":2235},[2225,82455,82456,82458,82460,82462,82464,82466,82469,82471],{"class":2227,"line":1038},[2225,82457,14201],{"class":2235},[2225,82459,79137],{"class":6342},[2225,82461,6620],{"class":2235},[2225,82463,2311],{"class":2235},[2225,82465,6614],{"class":2235},[2225,82467,82468],{"class":2252},"invoice.pdf",[2225,82470,6620],{"class":2235},[2225,82472,1707],{"class":2235},[2225,82474,82475,82477,82479,82481,82483,82485,82487,82490,82492,82494,82496,82499,82501,82503,82505,82508,82510],{"class":2227,"line":2288},[2225,82476,14201],{"class":2235},[2225,82478,78156],{"class":6342},[2225,82480,6620],{"class":2235},[2225,82482,2311],{"class":2235},[2225,82484,8667],{"class":2235},[2225,82486,6620],{"class":2235},[2225,82488,82489],{"class":2252},"01J1ZC4Q4T…",[2225,82491,6620],{"class":2235},[2225,82493,2298],{"class":2235},[2225,82495,6614],{"class":2235},[2225,82497,82498],{"class":2252},"01J1ZD7N2W…",[2225,82500,6620],{"class":2235},[2225,82502,2298],{"class":2235},[2225,82504,6614],{"class":2235},[2225,82506,82507],{"class":2252},"01J1ZF9S6K…",[2225,82509,6620],{"class":2235},[2225,82511,26484],{"class":2235},[2225,82513,82514],{"class":2227,"line":2304},[2225,82515,7632],{"class":2235},[2225,82517,82518],{"class":2227,"line":2356},[2225,82519,7351],{"class":2235},[937,82521,82522],{},"Render each group once — \"invoice.pdf · appears in 3 emails\" — instead\nof three identical attachment rows.",[1022,82524,82526],{"id":82525},"revision-tracking-different-etags-same-logical-document","Revision tracking — different eTags, same logical document",[937,82528,82529,82530,82533,82534,82537,82538,82541],{},"Harder case: someone sends ",[941,82531,82532],{},"invoice-draft1.pdf",", later replies with\n",[941,82535,82536],{},"invoice-draft2.pdf"," and then ",[941,82539,82540],{},"invoice-final.pdf",". Different bytes,\ndifferent eTags, but conceptually ONE document across three revisions.",[937,82543,82544,82545,82548],{},"Model this with a ",[941,82546,82547],{},"DocumentGroup"," collection — a first-class entity\nwhose identity spans email boundaries:",[2217,82550,82552],{"className":2219,"code":82551,"language":2221,"meta":1037,"style":1037},"interface DocumentGroup {\n  id: string                    \u002F\u002F ULID\n  label: string                 \u002F\u002F human-readable\n  threadId?: string\n  filenameStem?: string         \u002F\u002F common stem used for matching\n  revisions: Array\u003C{\n    eTag: string                \u002F\u002F from blob(id).list() at ingest\n    emailId: string\n    slotName: string\n    filename: string\n    receivedAt: string\n    revisionLabel?: string      \u002F\u002F 'draft', 'draft2', 'final'\n    size: number\n    ocrText?: string            \u002F\u002F extracted at ingest, if OCR ran — enables diffing\n  }>\n  latestETag?: string\n  createdAt: string\n  updatedAt: string\n}\n",[941,82553,82554,82563,82574,82586,82594,82606,82617,82629,82638,82646,82654,82662,82674,82682,82694,82698,82707,82716,82725],{"__ignoreMap":1037},[2225,82555,82556,82558,82561],{"class":2227,"line":2228},[2225,82557,6994],{"class":2774},[2225,82559,82560],{"class":6342}," DocumentGroup",[2225,82562,2301],{"class":2235},[2225,82564,82565,82567,82569,82571],{"class":2227,"line":1041},[2225,82566,28371],{"class":2307},[2225,82568,2311],{"class":2235},[2225,82570,6366],{"class":6342},[2225,82572,82573],{"class":2763},"                    \u002F\u002F ULID\n",[2225,82575,82576,82579,82581,82583],{"class":2227,"line":1038},[2225,82577,82578],{"class":2307},"  label",[2225,82580,2311],{"class":2235},[2225,82582,6366],{"class":6342},[2225,82584,82585],{"class":2763},"                 \u002F\u002F human-readable\n",[2225,82587,82588,82590,82592],{"class":2227,"line":2288},[2225,82589,78479],{"class":2307},[2225,82591,7011],{"class":2235},[2225,82593,7014],{"class":6342},[2225,82595,82596,82599,82601,82603],{"class":2227,"line":2304},[2225,82597,82598],{"class":2307},"  filenameStem",[2225,82600,7011],{"class":2235},[2225,82602,6366],{"class":6342},[2225,82604,82605],{"class":2763},"         \u002F\u002F common stem used for matching\n",[2225,82607,82608,82611,82613,82615],{"class":2227,"line":2356},[2225,82609,82610],{"class":2307},"  revisions",[2225,82612,2311],{"class":2235},[2225,82614,66498],{"class":6342},[2225,82616,78722],{"class":2235},[2225,82618,82619,82622,82624,82626],{"class":2227,"line":2830},[2225,82620,82621],{"class":2307},"    eTag",[2225,82623,2311],{"class":2235},[2225,82625,6366],{"class":6342},[2225,82627,82628],{"class":2763},"                \u002F\u002F from blob(id).list() at ingest\n",[2225,82630,82631,82634,82636],{"class":2227,"line":2861},[2225,82632,82633],{"class":2307},"    emailId",[2225,82635,2311],{"class":2235},[2225,82637,7014],{"class":6342},[2225,82639,82640,82642,82644],{"class":2227,"line":4246},[2225,82641,78766],{"class":2307},[2225,82643,2311],{"class":2235},[2225,82645,7014],{"class":6342},[2225,82647,82648,82650,82652],{"class":2227,"line":4253},[2225,82649,78727],{"class":2307},[2225,82651,2311],{"class":2235},[2225,82653,7014],{"class":6342},[2225,82655,82656,82658,82660],{"class":2227,"line":4259},[2225,82657,79623],{"class":2307},[2225,82659,2311],{"class":2235},[2225,82661,7014],{"class":6342},[2225,82663,82664,82667,82669,82671],{"class":2227,"line":6548},[2225,82665,82666],{"class":2307},"    revisionLabel",[2225,82668,7011],{"class":2235},[2225,82670,6366],{"class":6342},[2225,82672,82673],{"class":2763},"      \u002F\u002F 'draft', 'draft2', 'final'\n",[2225,82675,82676,82678,82680],{"class":2227,"line":6576},[2225,82677,79014],{"class":2307},[2225,82679,2311],{"class":2235},[2225,82681,12024],{"class":6342},[2225,82683,82684,82687,82689,82691],{"class":2227,"line":6582},[2225,82685,82686],{"class":2307},"    ocrText",[2225,82688,7011],{"class":2235},[2225,82690,6366],{"class":6342},[2225,82692,82693],{"class":2763},"            \u002F\u002F extracted at ingest, if OCR ran — enables diffing\n",[2225,82695,82696],{"class":2227,"line":6587},[2225,82697,78778],{"class":2235},[2225,82699,82700,82703,82705],{"class":2227,"line":7174},[2225,82701,82702],{"class":2307},"  latestETag",[2225,82704,7011],{"class":2235},[2225,82706,7014],{"class":6342},[2225,82708,82709,82712,82714],{"class":2227,"line":7180},[2225,82710,82711],{"class":2307},"  createdAt",[2225,82713,2311],{"class":2235},[2225,82715,7014],{"class":6342},[2225,82717,82718,82721,82723],{"class":2227,"line":7220},[2225,82719,82720],{"class":2307},"  updatedAt",[2225,82722,2311],{"class":2235},[2225,82724,7014],{"class":6342},[2225,82726,82727],{"class":2227,"line":7225},[2225,82728,7351],{"class":2235},[937,82730,82731,82734],{},[950,82732,82733],{},"Ingest-time revision detection"," uses three signals:",[1234,82736,82737,82743,82749],{},[1114,82738,82739,82742],{},[950,82740,82741],{},"Byte match"," — is this eTag already in the thread? It's the SAME\nfile (dedup). Reuse the existing DocumentGroup.",[1114,82744,82745,82748],{},[950,82746,82747],{},"Stem match"," — strip version suffixes and extension from the\nfilename; matching stem in the thread → new revision of that group.",[1114,82750,82751,82754,82755,82758],{},[950,82752,82753],{},"No match"," — create a new DocumentGroup. ",[941,82756,82757],{},"filenameStem"," becomes\nthe search key for future revisions.",[937,82760,82761,82762,82765],{},"Optional ",[950,82763,82764],{},"LLM enhancement",": when OCR text is available, feed both\nversions plus the body context into an LLM and ask \"is this a revision\nof that?\". Use as confirmation, not primary signal.",[1022,82767,82769],{"id":82768},"diffing-two-revisions","Diffing two revisions",[937,82771,82772,82773,1261,82775,82778,82779,82781],{},"If OCR text was extracted at ingest, the hub's ",[941,82774,21849],{},[941,82776,82777],{},"formatDiff()","\nutilities compare the two revisions. They are plain functions exported\nfrom the ",[941,82780,4151],{}," subpath — no service opt-in required:",[2217,82783,82785],{"className":2219,"code":82784,"language":2221,"meta":1037,"style":1037},"import { diff, formatDiff } from '@noy-db\u002Fhub\u002Fhistory'\n\nconst groups = vault.collection\u003CDocumentGroup>('document_groups')\nconst groupId = '01J1ZG8B3PV3Q6T9W2K5RD7HCN'   \u002F\u002F the DocumentGroup the user selected\nconst group = await groups.get(groupId)\nif (!group || group.revisions.length \u003C 2) throw new Error('nothing to diff')\n\nconst [prev, curr] = group.revisions.slice(-2)\nconst changes = diff({ text: prev!.ocrText ?? '' }, { text: curr!.ocrText ?? '' })\nconsole.log(formatDiff(changes))\n\u002F\u002F → ~ text: \"Total due: 1,200 EUR (draft)\" → \"Total due: 1,450 EUR\"\n",[941,82786,82787,82811,82815,82847,82866,82887,82937,82941,82977,83032,83048],{"__ignoreMap":1037},[2225,82788,82789,82791,82793,82796,82798,82801,82803,82805,82807,82809],{"class":2227,"line":2228},[2225,82790,2232],{"class":2231},[2225,82792,2236],{"class":2235},[2225,82794,82795],{"class":2239}," diff",[2225,82797,2298],{"class":2235},[2225,82799,82800],{"class":2239}," formatDiff",[2225,82802,2243],{"class":2235},[2225,82804,2246],{"class":2231},[2225,82806,2249],{"class":2235},[2225,82808,4151],{"class":2252},[2225,82810,2256],{"class":2235},[2225,82812,82813],{"class":2227,"line":1041},[2225,82814,2285],{"emptyLinePlaceholder":2284},[2225,82816,82817,82819,82822,82824,82826,82828,82830,82832,82834,82836,82838,82840,82843,82845],{"class":2227,"line":1038},[2225,82818,2775],{"class":2774},[2225,82820,82821],{"class":2239}," groups ",[2225,82823,2781],{"class":2235},[2225,82825,6466],{"class":2239},[2225,82827,1520],{"class":2235},[2225,82829,6471],{"class":2291},[2225,82831,6474],{"class":2235},[2225,82833,82547],{"class":6342},[2225,82835,6480],{"class":2235},[2225,82837,2789],{"class":2239},[2225,82839,2805],{"class":2235},[2225,82841,82842],{"class":2252},"document_groups",[2225,82844,2805],{"class":2235},[2225,82846,2362],{"class":2239},[2225,82848,82849,82851,82854,82856,82858,82861,82863],{"class":2227,"line":2288},[2225,82850,2775],{"class":2774},[2225,82852,82853],{"class":2239}," groupId ",[2225,82855,2781],{"class":2235},[2225,82857,2249],{"class":2235},[2225,82859,82860],{"class":2252},"01J1ZG8B3PV3Q6T9W2K5RD7HCN",[2225,82862,2805],{"class":2235},[2225,82864,82865],{"class":2763},"   \u002F\u002F the DocumentGroup the user selected\n",[2225,82867,82868,82870,82873,82875,82877,82880,82882,82884],{"class":2227,"line":2304},[2225,82869,2775],{"class":2774},[2225,82871,82872],{"class":2239}," group ",[2225,82874,2781],{"class":2235},[2225,82876,2784],{"class":2231},[2225,82878,82879],{"class":2239}," groups",[2225,82881,1520],{"class":2235},[2225,82883,3158],{"class":2291},[2225,82885,82886],{"class":2239},"(groupId)\n",[2225,82888,82889,82891,82893,82895,82898,82901,82904,82906,82909,82911,82914,82916,82918,82920,82922,82924,82926,82928,82930,82933,82935],{"class":2227,"line":2356},[2225,82890,35129],{"class":2231},[2225,82892,953],{"class":2239},[2225,82894,35134],{"class":2235},[2225,82896,82897],{"class":2239},"group ",[2225,82899,82900],{"class":2235},"||",[2225,82902,82903],{"class":2239}," group",[2225,82905,1520],{"class":2235},[2225,82907,82908],{"class":2239},"revisions",[2225,82910,1520],{"class":2235},[2225,82912,82913],{"class":2239},"length ",[2225,82915,6474],{"class":2235},[2225,82917,18961],{"class":6527},[2225,82919,8495],{"class":2239},[2225,82921,46514],{"class":2231},[2225,82923,8130],{"class":2235},[2225,82925,46519],{"class":2291},[2225,82927,2789],{"class":2239},[2225,82929,2805],{"class":2235},[2225,82931,82932],{"class":2252},"nothing to diff",[2225,82934,2805],{"class":2235},[2225,82936,2362],{"class":2239},[2225,82938,82939],{"class":2227,"line":2830},[2225,82940,2285],{"emptyLinePlaceholder":2284},[2225,82942,82943,82945,82947,82950,82952,82955,82957,82959,82961,82963,82965,82967,82969,82971,82973,82975],{"class":2227,"line":2861},[2225,82944,2775],{"class":2774},[2225,82946,8667],{"class":2235},[2225,82948,82949],{"class":2239},"prev",[2225,82951,2298],{"class":2235},[2225,82953,82954],{"class":2239}," curr",[2225,82956,8803],{"class":2235},[2225,82958,6346],{"class":2235},[2225,82960,82903],{"class":2239},[2225,82962,1520],{"class":2235},[2225,82964,82908],{"class":2239},[2225,82966,1520],{"class":2235},[2225,82968,8733],{"class":2291},[2225,82970,2789],{"class":2239},[2225,82972,71241],{"class":2235},[2225,82974,5470],{"class":6527},[2225,82976,2362],{"class":2239},[2225,82978,82979,82981,82984,82986,82988,82990,82992,82995,82997,83000,83003,83006,83008,83010,83012,83014,83016,83018,83020,83022,83024,83026,83028,83030],{"class":2227,"line":4246},[2225,82980,2775],{"class":2774},[2225,82982,82983],{"class":2239}," changes ",[2225,82985,2781],{"class":2235},[2225,82987,82795],{"class":2291},[2225,82989,2789],{"class":2239},[2225,82991,2792],{"class":2235},[2225,82993,82994],{"class":2307}," text",[2225,82996,2311],{"class":2235},[2225,82998,82999],{"class":2239}," prev",[2225,83001,83002],{"class":2235},"!.",[2225,83004,83005],{"class":2239},"ocrText ",[2225,83007,8381],{"class":2235},[2225,83009,12500],{"class":2235},[2225,83011,10965],{"class":2235},[2225,83013,2236],{"class":2235},[2225,83015,82994],{"class":2307},[2225,83017,2311],{"class":2235},[2225,83019,82954],{"class":2239},[2225,83021,83002],{"class":2235},[2225,83023,83005],{"class":2239},[2225,83025,8381],{"class":2235},[2225,83027,12500],{"class":2235},[2225,83029,2243],{"class":2235},[2225,83031,2362],{"class":2239},[2225,83033,83034,83036,83038,83040,83042,83045],{"class":2227,"line":4253},[2225,83035,28031],{"class":2239},[2225,83037,1520],{"class":2235},[2225,83039,27100],{"class":2291},[2225,83041,2789],{"class":2239},[2225,83043,83044],{"class":2291},"formatDiff",[2225,83046,83047],{"class":2239},"(changes))\n",[2225,83049,83050],{"class":2227,"line":4259},[2225,83051,83052],{"class":2763},"\u002F\u002F → ~ text: \"Total due: 1,200 EUR (draft)\" → \"Total due: 1,450 EUR\"\n",[937,83054,83055],{},"For binary-only revisions (PDFs with no OCR), diffing is lossy — ship\na \"visual diff\" link that opens both PDFs side-by-side in a viewer\ninstead.",[1022,83057,2621],{"id":2620},[1111,83059,83060,83065,83076,83081],{},[1114,83061,83062,83064],{},[1007,83063,80314],{"href":61714}," — slots, chunking, dedup, compaction",[1114,83066,83067,964,83070,1261,83072,1261,83074],{},[1007,83068,3216],{"href":83069},"..\u002Fcore\u002Fcollection\u002Fquerying",[941,83071,3016],{},[941,83073,3019],{},[941,83075,3233],{},[1114,83077,83078,83080],{},[1007,83079,65376],{"href":58262}," — the record layer this recipe builds on",[1114,83082,83083,83086],{},[1007,83084,83085],{"href":61702},"History service"," — record-level version history (distinct from the blob revisions modelled here)",[2645,83088,83089],{},"html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .sHdIc, html code.shiki .sHdIc{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#EEFFFF;--shiki-default-font-style:italic;--shiki-dark:#BABED8;--shiki-dark-font-style:italic}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":1037,"searchDepth":1038,"depth":1038,"links":83091},[83092,83093,83094,83095,83096,83097,83098],{"id":80375,"depth":1041,"text":80376},{"id":81330,"depth":1041,"text":81331},{"id":82046,"depth":1041,"text":82047},{"id":82079,"depth":1041,"text":82080},{"id":82525,"depth":1041,"text":82526},{"id":82768,"depth":1041,"text":82769},{"id":2620,"depth":1041,"text":2621},"Threading, inline-image HTML rendering, and cross-revision document diffing on the email-archive record\u002Fblob layout.",{},{"title":547,"description":83099},"7jad-aEVL7oUzTobTFhNuRgfldlh_ZEQkwQ6wYY5Axo",{"id":83104,"title":551,"access":12,"apiToc":929,"body":83105,"category":1042,"description":84206,"extension":1044,"meta":84207,"navigation":1042,"nest":1042,"path":552,"seo":84208,"sinceVersion":1042,"status":1049,"stem":553,"subtitle":1042,"tier":1050,"tocExternal":1042,"__hash__":84209},"docs\u002Frecipes\u002Fexport-portability.md",{"type":931,"value":83106,"toc":84196},[83107,83110,83131,83140,83158,83160,83196,83200,83203,83538,83541,83657,83661,83857,83863,83872,83943,83958,83962,83968,84153,84159,84161,84171,84173,84194],[934,83108,551],{"id":83109},"export-portability",[1061,83111,83112],{},[937,83113,83114,83116,83117,83119,83120,58223,83122,83124,83125,83127,83128,83130],{},[950,83115,1500],{}," anyone answering \"how do I get my data ",[1092,83118,13557],{},"?\" — spreadsheet handoffs, encrypted backups for a recipient, or a member leaving with their slice.\n",[950,83121,58222],{},[941,83123,3197],{}," (grants carry the export capability) + ",[941,83126,21604],{}," (the member-copy path) + the ",[941,83129,1791],{}," format packages you need.",[937,83132,58245,83133,73771],{},[1007,83134,83137],{"href":83135,"rel":83136},"https:\u002F\u002Fgithub.com\u002Fnoy-db\u002Fdocs\u002Fblob\u002Fmain\u002Fshowcases\u002Fsrc\u002Frecipe-export-portability.recipe.test.ts",[3371],[941,83138,83139],{},"showcases\u002Fsrc\u002Frecipe-export-portability.recipe.test.ts",[937,83141,83142,83143,83146,83147,83153,83154,83157],{},"Three different exit doors, for three different trust situations: ",[950,83144,83145],{},"plaintext formats"," (CSV\u002FXLSX) when the destination is a spreadsheet and the leak surface is accepted; the ",[950,83148,83149,83150,83152],{},"encrypted ",[941,83151,1402],{}," bundle"," when the data must travel sealed; and the ",[950,83155,83156],{},"portable member copy"," when a non-owner takes exactly what their keyring can decrypt, re-keyed under their own secret. What makes the first door safe to have at all is the gate in front of it.",[1022,83159,70093],{"id":70092},[1111,83161,83162,83169,83176,83188],{},[1114,83163,83164,1324,83166],{},[950,83165,71604],{},[941,83167,83168],{},"pnpm add @noy-db\u002Fhub @noy-db\u002Fas-csv @noy-db\u002Fas-xlsx @noy-db\u002Fas-noydb",[1114,83170,83171,83175],{},[950,83172,83173],{},[941,83174,10312],{}," — the plaintext-export capability is granted per keyring, per format; without the team engine there is nothing to carry the grant.",[1114,83177,83178,83183,83184,83187],{},[950,83179,83180],{},[941,83181,83182],{},"portabilityStrategy: withPortability()"," on the ",[1092,83185,83186],{},"member's"," instance for the portable-copy path.",[1114,83189,83190,83195],{},[950,83191,5093,83192,83194],{},[941,83193,11422],{}," grant"," for any plaintext format — even the owner has no implicit one (see below).",[1022,83197,83199],{"id":83198},"the-gate-plaintext-export-is-a-granted-capability","The gate: plaintext export is a granted capability",[937,83201,83202],{},"Decrypted data leaving the vault is the one thing zero-knowledge can't protect after the fact — so it's refused by default, for everyone:",[2217,83204,83206],{"className":2219,"code":83205,"language":2221,"meta":1037,"style":1037},"import { createNoydb, ExportCapabilityError } from '@noy-db\u002Fhub'\nimport { withTeam } from '@noy-db\u002Fhub\u002Fteam'\nimport { withFormats } from '@noy-db\u002Fhub\u002Fas'\nimport { asCsv } from '@noy-db\u002Fas-csv'\nimport { toMemory } from '@noy-db\u002Fto-memory'\n\nconst db = await createNoydb({ store: toMemory(), user: 'alice', secret: 'export-recipe-pass-2026', teamStrategy: withTeam(), formatsStrategy: withFormats() })\nconst vault = await db.openVault('firm')\nawait vault.collection('invoices').put('a', { id: 'a', client: 'Globex', amount: 1500 })\n\nawait vault.export(asCsv(), { collections: ['invoices'] })\n\u002F\u002F → rejects: ExportCapabilityError — even the owner has no implicit grant\n\u002F\u002F (`collections` scopes the artefact; what it costs to produce depends on your\n\u002F\u002F  hub version — see \"What `collections` costs\" on the as-* family page.)\n",[941,83207,83208,83231,83249,83268,83286,83304,83308,83383,83409,83481,83485,83523,83528,83533],{"__ignoreMap":1037},[2225,83209,83210,83212,83214,83216,83218,83221,83223,83225,83227,83229],{"class":2227,"line":2228},[2225,83211,2232],{"class":2231},[2225,83213,2236],{"class":2235},[2225,83215,2744],{"class":2239},[2225,83217,2298],{"class":2235},[2225,83219,83220],{"class":2239}," ExportCapabilityError",[2225,83222,2243],{"class":2235},[2225,83224,2246],{"class":2231},[2225,83226,2249],{"class":2235},[2225,83228,1086],{"class":2252},[2225,83230,2256],{"class":2235},[2225,83232,83233,83235,83237,83239,83241,83243,83245,83247],{"class":2227,"line":1041},[2225,83234,2232],{"class":2231},[2225,83236,2236],{"class":2235},[2225,83238,10373],{"class":2239},[2225,83240,2243],{"class":2235},[2225,83242,2246],{"class":2231},[2225,83244,2249],{"class":2235},[2225,83246,4680],{"class":2252},[2225,83248,2256],{"class":2235},[2225,83250,83251,83253,83255,83258,83260,83262,83264,83266],{"class":2227,"line":1038},[2225,83252,2232],{"class":2231},[2225,83254,2236],{"class":2235},[2225,83256,83257],{"class":2239}," withFormats",[2225,83259,2243],{"class":2235},[2225,83261,2246],{"class":2231},[2225,83263,2249],{"class":2235},[2225,83265,31659],{"class":2252},[2225,83267,2256],{"class":2235},[2225,83269,83270,83272,83274,83276,83278,83280,83282,83284],{"class":2227,"line":2288},[2225,83271,2232],{"class":2231},[2225,83273,2236],{"class":2235},[2225,83275,30536],{"class":2239},[2225,83277,2243],{"class":2235},[2225,83279,2246],{"class":2231},[2225,83281,2249],{"class":2235},[2225,83283,30472],{"class":2252},[2225,83285,2256],{"class":2235},[2225,83287,83288,83290,83292,83294,83296,83298,83300,83302],{"class":2227,"line":2304},[2225,83289,2232],{"class":2231},[2225,83291,2236],{"class":2235},[2225,83293,68576],{"class":2239},[2225,83295,2243],{"class":2235},[2225,83297,2246],{"class":2231},[2225,83299,2249],{"class":2235},[2225,83301,9638],{"class":2252},[2225,83303,2256],{"class":2235},[2225,83305,83306],{"class":2227,"line":2356},[2225,83307,2285],{"emptyLinePlaceholder":2284},[2225,83309,83310,83312,83314,83316,83318,83320,83322,83324,83326,83328,83330,83332,83334,83336,83338,83340,83342,83344,83346,83348,83350,83352,83355,83357,83359,83362,83364,83366,83368,83370,83373,83375,83377,83379,83381],{"class":2227,"line":2830},[2225,83311,2775],{"class":2774},[2225,83313,2778],{"class":2239},[2225,83315,2781],{"class":2235},[2225,83317,2784],{"class":2231},[2225,83319,2744],{"class":2291},[2225,83321,2789],{"class":2239},[2225,83323,2792],{"class":2235},[2225,83325,7564],{"class":2307},[2225,83327,2311],{"class":2235},[2225,83329,68576],{"class":2291},[2225,83331,4228],{"class":2239},[2225,83333,2298],{"class":2235},[2225,83335,2795],{"class":2307},[2225,83337,2311],{"class":2235},[2225,83339,2249],{"class":2235},[2225,83341,6408],{"class":2252},[2225,83343,2805],{"class":2235},[2225,83345,2298],{"class":2235},[2225,83347,2810],{"class":2307},[2225,83349,2311],{"class":2235},[2225,83351,2249],{"class":2235},[2225,83353,83354],{"class":2252},"export-recipe-pass-2026",[2225,83356,2805],{"class":2235},[2225,83358,2298],{"class":2235},[2225,83360,83361],{"class":2307}," teamStrategy",[2225,83363,2311],{"class":2235},[2225,83365,10373],{"class":2291},[2225,83367,4228],{"class":2239},[2225,83369,2298],{"class":2235},[2225,83371,83372],{"class":2307}," formatsStrategy",[2225,83374,2311],{"class":2235},[2225,83376,83257],{"class":2291},[2225,83378,19132],{"class":2239},[2225,83380,2359],{"class":2235},[2225,83382,2362],{"class":2239},[2225,83384,83385,83387,83389,83391,83393,83395,83397,83399,83401,83403,83405,83407],{"class":2227,"line":2861},[2225,83386,2775],{"class":2774},[2225,83388,2835],{"class":2239},[2225,83390,2781],{"class":2235},[2225,83392,2784],{"class":2231},[2225,83394,2842],{"class":2239},[2225,83396,1520],{"class":2235},[2225,83398,2847],{"class":2291},[2225,83400,2789],{"class":2239},[2225,83402,2805],{"class":2235},[2225,83404,39709],{"class":2252},[2225,83406,2805],{"class":2235},[2225,83408,2362],{"class":2239},[2225,83410,83411,83413,83415,83417,83419,83421,83423,83425,83427,83429,83431,83433,83435,83437,83439,83441,83443,83445,83447,83449,83451,83453,83455,83457,83459,83461,83463,83466,83468,83470,83472,83474,83477,83479],{"class":2227,"line":4246},[2225,83412,6500],{"class":2231},[2225,83414,6466],{"class":2239},[2225,83416,1520],{"class":2235},[2225,83418,6471],{"class":2291},[2225,83420,2789],{"class":2239},[2225,83422,2805],{"class":2235},[2225,83424,6487],{"class":2252},[2225,83426,2805],{"class":2235},[2225,83428,1381],{"class":2239},[2225,83430,1520],{"class":2235},[2225,83432,3161],{"class":2291},[2225,83434,2789],{"class":2239},[2225,83436,2805],{"class":2235},[2225,83438,1007],{"class":2252},[2225,83440,2805],{"class":2235},[2225,83442,2298],{"class":2235},[2225,83444,2236],{"class":2235},[2225,83446,7070],{"class":2307},[2225,83448,2311],{"class":2235},[2225,83450,2249],{"class":2235},[2225,83452,1007],{"class":2252},[2225,83454,2805],{"class":2235},[2225,83456,2298],{"class":2235},[2225,83458,25871],{"class":2307},[2225,83460,2311],{"class":2235},[2225,83462,2249],{"class":2235},[2225,83464,83465],{"class":2252},"Globex",[2225,83467,2805],{"class":2235},[2225,83469,2298],{"class":2235},[2225,83471,6351],{"class":2307},[2225,83473,2311],{"class":2235},[2225,83475,83476],{"class":6527}," 1500",[2225,83478,2243],{"class":2235},[2225,83480,2362],{"class":2239},[2225,83482,83483],{"class":2227,"line":4253},[2225,83484,2285],{"emptyLinePlaceholder":2284},[2225,83486,83487,83489,83491,83493,83495,83497,83499,83501,83503,83505,83507,83509,83511,83513,83515,83517,83519,83521],{"class":2227,"line":4259},[2225,83488,6500],{"class":2231},[2225,83490,6466],{"class":2239},[2225,83492,1520],{"class":2235},[2225,83494,8080],{"class":2291},[2225,83496,2789],{"class":2239},[2225,83498,30572],{"class":2291},[2225,83500,4228],{"class":2239},[2225,83502,2298],{"class":2235},[2225,83504,2236],{"class":2235},[2225,83506,22066],{"class":2307},[2225,83508,2311],{"class":2235},[2225,83510,8667],{"class":2239},[2225,83512,2805],{"class":2235},[2225,83514,6487],{"class":2252},[2225,83516,2805],{"class":2235},[2225,83518,8890],{"class":2239},[2225,83520,2359],{"class":2235},[2225,83522,2362],{"class":2239},[2225,83524,83525],{"class":2227,"line":6548},[2225,83526,83527],{"class":2763},"\u002F\u002F → rejects: ExportCapabilityError — even the owner has no implicit grant\n",[2225,83529,83530],{"class":2227,"line":6576},[2225,83531,83532],{"class":2763},"\u002F\u002F (`collections` scopes the artefact; what it costs to produce depends on your\n",[2225,83534,83535],{"class":2227,"line":6582},[2225,83536,83537],{"class":2763},"\u002F\u002F  hub version — see \"What `collections` costs\" on the as-* family page.)\n",[937,83539,83540],{},"Grant the capability — per format — and re-open:",[2217,83542,83544],{"className":2219,"code":83543,"language":2221,"meta":1037,"style":1037},"await db.grant('firm', {\n  userId: 'alice', displayName: 'Alice', role: 'owner', secret: 'export-recipe-pass-2026',\n  exportCapability: { plaintext: ['csv', 'xlsx'] },\n})\n",[941,83545,83546,83568,83618,83651],{"__ignoreMap":1037},[2225,83547,83548,83550,83552,83554,83556,83558,83560,83562,83564,83566],{"class":2227,"line":2228},[2225,83549,6500],{"class":2231},[2225,83551,2842],{"class":2239},[2225,83553,1520],{"class":2235},[2225,83555,10493],{"class":2291},[2225,83557,2789],{"class":2239},[2225,83559,2805],{"class":2235},[2225,83561,39709],{"class":2252},[2225,83563,2805],{"class":2235},[2225,83565,2298],{"class":2235},[2225,83567,2301],{"class":2235},[2225,83569,83570,83572,83574,83576,83578,83580,83582,83584,83586,83588,83590,83592,83594,83596,83598,83600,83602,83604,83606,83608,83610,83612,83614,83616],{"class":2227,"line":1041},[2225,83571,10510],{"class":2307},[2225,83573,2311],{"class":2235},[2225,83575,2249],{"class":2235},[2225,83577,6408],{"class":2252},[2225,83579,2805],{"class":2235},[2225,83581,2298],{"class":2235},[2225,83583,75153],{"class":2307},[2225,83585,2311],{"class":2235},[2225,83587,2249],{"class":2235},[2225,83589,10533],{"class":2252},[2225,83591,2805],{"class":2235},[2225,83593,2298],{"class":2235},[2225,83595,34944],{"class":2307},[2225,83597,2311],{"class":2235},[2225,83599,2249],{"class":2235},[2225,83601,9898],{"class":2252},[2225,83603,2805],{"class":2235},[2225,83605,2298],{"class":2235},[2225,83607,2810],{"class":2307},[2225,83609,2311],{"class":2235},[2225,83611,2249],{"class":2235},[2225,83613,83354],{"class":2252},[2225,83615,2805],{"class":2235},[2225,83617,1707],{"class":2235},[2225,83619,83620,83623,83625,83627,83629,83631,83633,83635,83637,83639,83641,83643,83645,83647,83649],{"class":2227,"line":1038},[2225,83621,83622],{"class":2307},"  exportCapability",[2225,83624,2311],{"class":2235},[2225,83626,2236],{"class":2235},[2225,83628,78289],{"class":2307},[2225,83630,2311],{"class":2235},[2225,83632,8667],{"class":2239},[2225,83634,2805],{"class":2235},[2225,83636,137],{"class":2252},[2225,83638,2805],{"class":2235},[2225,83640,2298],{"class":2235},[2225,83642,2249],{"class":2235},[2225,83644,157],{"class":2252},[2225,83646,2805],{"class":2235},[2225,83648,8890],{"class":2239},[2225,83650,19008],{"class":2235},[2225,83652,83653,83655],{"class":2227,"line":2288},[2225,83654,2359],{"class":2235},[2225,83656,2362],{"class":2239},[1022,83658,83660],{"id":83659},"spreadsheet-formats-csv-and-xlsx","Spreadsheet formats — CSV and XLSX",[2217,83662,83664],{"className":2219,"code":83663,"language":2221,"meta":1037,"style":1037},"import { asCsv } from '@noy-db\u002Fas-csv'\nimport { toBytes as toXlsx } from '@noy-db\u002Fas-xlsx'\n\nconst csv = await vault.export(asCsv(), { collections: ['invoices'] })\n\u002F\u002F id,client,amount\n\u002F\u002F a,Globex,1500\n\u002F\u002F b,\"Acme, Inc.\",2400        ← RFC-4180 quoting, ready for any spreadsheet\n\u002F\u002F NB: through hub 0.8.0 `collections` filters the OUTPUT only — every collection\n\u002F\u002F is still read and decrypted. The next release narrows the read too. See\n\u002F\u002F \"What `collections` costs\" on the as-* family page.\n\nconst xlsx = await toXlsx(vault, {\n  sheets: [{ name: 'Invoices', collection: 'invoices' }],\n})\n\u002F\u002F a real Office Open XML workbook — one sheet per collection\n",[941,83665,83666,83684,83707,83711,83755,83760,83765,83770,83775,83780,83785,83789,83808,83846,83852],{"__ignoreMap":1037},[2225,83667,83668,83670,83672,83674,83676,83678,83680,83682],{"class":2227,"line":2228},[2225,83669,2232],{"class":2231},[2225,83671,2236],{"class":2235},[2225,83673,30536],{"class":2239},[2225,83675,2243],{"class":2235},[2225,83677,2246],{"class":2231},[2225,83679,2249],{"class":2235},[2225,83681,30472],{"class":2252},[2225,83683,2256],{"class":2235},[2225,83685,83686,83688,83690,83692,83694,83697,83699,83701,83703,83705],{"class":2227,"line":1041},[2225,83687,2232],{"class":2231},[2225,83689,2236],{"class":2235},[2225,83691,30223],{"class":2239},[2225,83693,2266],{"class":2231},[2225,83695,83696],{"class":2239}," toXlsx",[2225,83698,2243],{"class":2235},[2225,83700,2246],{"class":2231},[2225,83702,2249],{"class":2235},[2225,83704,32397],{"class":2252},[2225,83706,2256],{"class":2235},[2225,83708,83709],{"class":2227,"line":1038},[2225,83710,2285],{"emptyLinePlaceholder":2284},[2225,83712,83713,83715,83717,83719,83721,83723,83725,83727,83729,83731,83733,83735,83737,83739,83741,83743,83745,83747,83749,83751,83753],{"class":2227,"line":2288},[2225,83714,2775],{"class":2774},[2225,83716,30557],{"class":2239},[2225,83718,2781],{"class":2235},[2225,83720,2784],{"class":2231},[2225,83722,6466],{"class":2239},[2225,83724,1520],{"class":2235},[2225,83726,8080],{"class":2291},[2225,83728,2789],{"class":2239},[2225,83730,30572],{"class":2291},[2225,83732,4228],{"class":2239},[2225,83734,2298],{"class":2235},[2225,83736,2236],{"class":2235},[2225,83738,22066],{"class":2307},[2225,83740,2311],{"class":2235},[2225,83742,8667],{"class":2239},[2225,83744,2805],{"class":2235},[2225,83746,6487],{"class":2252},[2225,83748,2805],{"class":2235},[2225,83750,8890],{"class":2239},[2225,83752,2359],{"class":2235},[2225,83754,2362],{"class":2239},[2225,83756,83757],{"class":2227,"line":2304},[2225,83758,83759],{"class":2763},"\u002F\u002F id,client,amount\n",[2225,83761,83762],{"class":2227,"line":2356},[2225,83763,83764],{"class":2763},"\u002F\u002F a,Globex,1500\n",[2225,83766,83767],{"class":2227,"line":2830},[2225,83768,83769],{"class":2763},"\u002F\u002F b,\"Acme, Inc.\",2400        ← RFC-4180 quoting, ready for any spreadsheet\n",[2225,83771,83772],{"class":2227,"line":2861},[2225,83773,83774],{"class":2763},"\u002F\u002F NB: through hub 0.8.0 `collections` filters the OUTPUT only — every collection\n",[2225,83776,83777],{"class":2227,"line":4246},[2225,83778,83779],{"class":2763},"\u002F\u002F is still read and decrypted. The next release narrows the read too. See\n",[2225,83781,83782],{"class":2227,"line":4253},[2225,83783,83784],{"class":2763},"\u002F\u002F \"What `collections` costs\" on the as-* family page.\n",[2225,83786,83787],{"class":2227,"line":4259},[2225,83788,2285],{"emptyLinePlaceholder":2284},[2225,83790,83791,83793,83796,83798,83800,83802,83804,83806],{"class":2227,"line":6548},[2225,83792,2775],{"class":2774},[2225,83794,83795],{"class":2239}," xlsx ",[2225,83797,2781],{"class":2235},[2225,83799,2784],{"class":2231},[2225,83801,83696],{"class":2291},[2225,83803,30266],{"class":2239},[2225,83805,2298],{"class":2235},[2225,83807,2301],{"class":2235},[2225,83809,83810,83812,83814,83816,83818,83820,83822,83824,83826,83828,83830,83832,83834,83836,83838,83840,83842,83844],{"class":2227,"line":6576},[2225,83811,32486],{"class":2307},[2225,83813,2311],{"class":2235},[2225,83815,8667],{"class":2239},[2225,83817,2792],{"class":2235},[2225,83819,8104],{"class":2307},[2225,83821,2311],{"class":2235},[2225,83823,2249],{"class":2235},[2225,83825,32505],{"class":2252},[2225,83827,2805],{"class":2235},[2225,83829,2298],{"class":2235},[2225,83831,7061],{"class":2307},[2225,83833,2311],{"class":2235},[2225,83835,2249],{"class":2235},[2225,83837,6487],{"class":2252},[2225,83839,2805],{"class":2235},[2225,83841,2243],{"class":2235},[2225,83843,8803],{"class":2239},[2225,83845,1707],{"class":2235},[2225,83847,83848,83850],{"class":2227,"line":6582},[2225,83849,2359],{"class":2235},[2225,83851,2362],{"class":2239},[2225,83853,83854],{"class":2227,"line":6587},[2225,83855,83856],{"class":2763},"\u002F\u002F a real Office Open XML workbook — one sheet per collection\n",[1022,83858,83860,83861,83152],{"id":83859},"the-sealed-door-the-noydb-bundle","The sealed door — the ",[941,83862,1402],{},[937,83864,83865,83866,83868,83869,2311],{},"When the data must travel encrypted, export the whole vault as a single ",[941,83867,1402],{}," file. Anyone holding the file learns only its ",[950,83870,83871],{},"minimum-disclosure header",[2217,83873,83875],{"className":2219,"code":83874,"language":2221,"meta":1037,"style":1037},"import { toBytes as toNoydb, peek } from '@noy-db\u002Fas-noydb'\n\nconst bytes = await toNoydb(vault)\nconst header = peek(bytes) \u002F\u002F no secret — this is ALL the file discloses\n\u002F\u002F → { formatVersion, handle, bodyBytes, bodySha256 }\n",[941,83876,83877,83904,83908,83922,83938],{"__ignoreMap":1037},[2225,83878,83879,83881,83883,83885,83887,83890,83892,83894,83896,83898,83900,83902],{"class":2227,"line":2228},[2225,83880,2232],{"class":2231},[2225,83882,2236],{"class":2235},[2225,83884,30223],{"class":2239},[2225,83886,2266],{"class":2231},[2225,83888,83889],{"class":2239}," toNoydb",[2225,83891,2298],{"class":2235},[2225,83893,32024],{"class":2239},[2225,83895,2243],{"class":2235},[2225,83897,2246],{"class":2231},[2225,83899,2249],{"class":2235},[2225,83901,31967],{"class":2252},[2225,83903,2256],{"class":2235},[2225,83905,83906],{"class":2227,"line":1041},[2225,83907,2285],{"emptyLinePlaceholder":2284},[2225,83909,83910,83912,83914,83916,83918,83920],{"class":2227,"line":1038},[2225,83911,2775],{"class":2774},[2225,83913,32469],{"class":2239},[2225,83915,2781],{"class":2235},[2225,83917,2784],{"class":2231},[2225,83919,83889],{"class":2291},[2225,83921,45516],{"class":2239},[2225,83923,83924,83926,83928,83930,83932,83935],{"class":2227,"line":2288},[2225,83925,2775],{"class":2774},[2225,83927,32122],{"class":2239},[2225,83929,2781],{"class":2235},[2225,83931,32024],{"class":2291},[2225,83933,83934],{"class":2239},"(bytes) ",[2225,83936,83937],{"class":2763},"\u002F\u002F no secret — this is ALL the file discloses\n",[2225,83939,83940],{"class":2227,"line":2304},[2225,83941,83942],{"class":2763},"\u002F\u002F → { formatVersion, handle, bodyBytes, bodySha256 }\n",[937,83944,960,83945,83947,83948,83955,83956,23897],{},[941,83946,149],{}," CLI's ",[1007,83949,83950,1261,83953],{"href":214},[941,83951,83952],{},"inspect",[941,83954,18051],{}," work on exactly this header — integrity checks with no key. Re-keyed export to a recipient and whole-file pod stores are covered on the ",[1007,83957,629],{"href":630},[1022,83959,83961],{"id":83960},"the-members-door-a-portable-re-keyed-copy","The member's door — a portable, re-keyed copy",[937,83963,83964,83965,83967],{},"A non-owner takes a ",[941,83966,1402],{}," copy of precisely what their keyring can decrypt — nothing more — re-keyed under a secret they choose. Non-destructive, and by design always allowed:",[2217,83969,83971],{"className":2219,"code":83970,"language":2221,"meta":1037,"style":1037},"import { withPortability } from '@noy-db\u002Fhub\u002Fportability'\n\nconst op = await createNoydb({\n  store, user: 'belle', secret: 'belle-operator-2026',\n  portabilityStrategy: withPortability(),\n})\nconst vault = await op.openVault('firm')\n\nconst bytes = await vault.user.exportMyAccessibleData({\n  reKey: { secret: 'belle-takeaway-pass' },\n})\n\u002F\u002F belle's scope, sealed under belle's new secret; the firm vault is untouched\n",[941,83972,83973,83992,83996,84013,84045,84058,84064,84091,84095,84120,84142,84148],{"__ignoreMap":1037},[2225,83974,83975,83977,83979,83982,83984,83986,83988,83990],{"class":2227,"line":2228},[2225,83976,2232],{"class":2231},[2225,83978,2236],{"class":2235},[2225,83980,83981],{"class":2239}," withPortability",[2225,83983,2243],{"class":2235},[2225,83985,2246],{"class":2231},[2225,83987,2249],{"class":2235},[2225,83989,73238],{"class":2252},[2225,83991,2256],{"class":2235},[2225,83993,83994],{"class":2227,"line":1041},[2225,83995,2285],{"emptyLinePlaceholder":2284},[2225,83997,83998,84000,84003,84005,84007,84009,84011],{"class":2227,"line":1038},[2225,83999,2775],{"class":2774},[2225,84001,84002],{"class":2239}," op ",[2225,84004,2781],{"class":2235},[2225,84006,2784],{"class":2231},[2225,84008,2744],{"class":2291},[2225,84010,2789],{"class":2239},[2225,84012,4200],{"class":2235},[2225,84014,84015,84017,84019,84021,84023,84025,84028,84030,84032,84034,84036,84038,84041,84043],{"class":2227,"line":2288},[2225,84016,15608],{"class":2239},[2225,84018,2298],{"class":2235},[2225,84020,2795],{"class":2307},[2225,84022,2311],{"class":2235},[2225,84024,2249],{"class":2235},[2225,84026,84027],{"class":2252},"belle",[2225,84029,2805],{"class":2235},[2225,84031,2298],{"class":2235},[2225,84033,2810],{"class":2307},[2225,84035,2311],{"class":2235},[2225,84037,2249],{"class":2235},[2225,84039,84040],{"class":2252},"belle-operator-2026",[2225,84042,2805],{"class":2235},[2225,84044,1707],{"class":2235},[2225,84046,84047,84050,84052,84054,84056],{"class":2227,"line":2304},[2225,84048,84049],{"class":2307},"  portabilityStrategy",[2225,84051,2311],{"class":2235},[2225,84053,83981],{"class":2291},[2225,84055,4228],{"class":2239},[2225,84057,1707],{"class":2235},[2225,84059,84060,84062],{"class":2227,"line":2356},[2225,84061,2359],{"class":2235},[2225,84063,2362],{"class":2239},[2225,84065,84066,84068,84070,84072,84074,84077,84079,84081,84083,84085,84087,84089],{"class":2227,"line":2830},[2225,84067,2775],{"class":2774},[2225,84069,2835],{"class":2239},[2225,84071,2781],{"class":2235},[2225,84073,2784],{"class":2231},[2225,84075,84076],{"class":2239}," op",[2225,84078,1520],{"class":2235},[2225,84080,2847],{"class":2291},[2225,84082,2789],{"class":2239},[2225,84084,2805],{"class":2235},[2225,84086,39709],{"class":2252},[2225,84088,2805],{"class":2235},[2225,84090,2362],{"class":2239},[2225,84092,84093],{"class":2227,"line":2861},[2225,84094,2285],{"emptyLinePlaceholder":2284},[2225,84096,84097,84099,84101,84103,84105,84107,84109,84111,84113,84116,84118],{"class":2227,"line":4246},[2225,84098,2775],{"class":2774},[2225,84100,32469],{"class":2239},[2225,84102,2781],{"class":2235},[2225,84104,2784],{"class":2231},[2225,84106,6466],{"class":2239},[2225,84108,1520],{"class":2235},[2225,84110,16225],{"class":2239},[2225,84112,1520],{"class":2235},[2225,84114,84115],{"class":2291},"exportMyAccessibleData",[2225,84117,2789],{"class":2239},[2225,84119,4200],{"class":2235},[2225,84121,84122,84125,84127,84129,84131,84133,84135,84138,84140],{"class":2227,"line":4253},[2225,84123,84124],{"class":2307},"  reKey",[2225,84126,2311],{"class":2235},[2225,84128,2236],{"class":2235},[2225,84130,2810],{"class":2307},[2225,84132,2311],{"class":2235},[2225,84134,2249],{"class":2235},[2225,84136,84137],{"class":2252},"belle-takeaway-pass",[2225,84139,2805],{"class":2235},[2225,84141,2353],{"class":2235},[2225,84143,84144,84146],{"class":2227,"line":4259},[2225,84145,2359],{"class":2235},[2225,84147,2362],{"class":2239},[2225,84149,84150],{"class":2227,"line":6548},[2225,84151,84152],{"class":2763},"\u002F\u002F belle's scope, sealed under belle's new secret; the firm vault is untouched\n",[937,84154,84155,84156,84158],{},"The removal-flavored variants — export-and-erase, export-and-freeze — are the ",[1007,84157,900],{"href":901}," ceremony built on the same surface.",[1022,84160,71414],{"id":18051},[937,84162,84163,84164,84167,84168,84170],{},"Run the paired walkthrough: the gate's refusal, the RFC-4180 quoting, the ",[941,84165,84166],{},"PK"," magic of a real workbook, the header's non-disclosure (",[941,84169,31993],{}," output contains no record content), and the copy's non-destructiveness are each asserted.",[1022,84172,2621],{"id":2620},[1111,84174,84175,84180,84187],{},[1114,84176,84177,84179],{},[1007,84178,30143],{"href":115}," — all nine formats, including JSON\u002FNDJSON\u002FXML\u002FSQL and the ZIP composite.",[1114,84181,84182,1368,84184,84186],{},[1007,84183,800],{"href":801},[1007,84185,900],{"href":901}," — the data-sovereignty pair.",[1114,84188,84189,1169,84191,84193],{},[1007,84190,629],{"href":630},[941,84192,1402],{}," container format in depth.",[2645,84195,45319],{},{"title":1037,"searchDepth":1038,"depth":1038,"links":84197},[84198,84199,84200,84201,84203,84204,84205],{"id":70092,"depth":1041,"text":70093},{"id":83198,"depth":1041,"text":83199},{"id":83659,"depth":1041,"text":83660},{"id":83859,"depth":1041,"text":84202},"The sealed door — the .noydb bundle",{"id":83960,"depth":1041,"text":83961},{"id":18051,"depth":1041,"text":71414},{"id":2620,"depth":1041,"text":2621},"Get data out, three ways: capability-gated CSV\u002FXLSX exports, the sealed .noydb bundle, and a non-owner's re-keyed portable copy.",{},{"title":551,"description":84206},"lL3umGNlMvuZxnT1SbHiB76xEISUvV77jH0z18cXadU",{"id":84211,"title":84212,"access":12,"apiToc":929,"body":84213,"category":1042,"description":84589,"extension":1044,"meta":84590,"navigation":84591,"nest":1042,"path":500,"seo":84592,"sinceVersion":1042,"status":1049,"stem":504,"subtitle":1042,"tier":1050,"tocExternal":1042,"__hash__":84593},"docs\u002Frecipes\u002Findex.md","Recipes & demos",{"type":931,"value":84214,"toc":84586},[84215,84218,84228,84248,84418,84424,84428,84431,84494,84497,84571,84580,84583],[934,84216,84212],{"id":84217},"recipes-demos",[937,84219,84220,84221,84224,84225,84227],{},"Copy-paste-ready starter applications, from a minimalist core-only notebook up to a fully opted-in accounting app. Each recipe is a doc page paired with a runnable test under ",[941,84222,84223],{},"showcases\u002Fsrc\u002F",", so you can see the exact services it opts into and copy its ",[941,84226,72368],{}," setup. The table reads roughly as an upgrade ladder — each recipe opts into more than the one before it.",[937,84229,84230,84231,84234,84235,84237,84238,84240,84241,84243,84244,84247],{},"Every recipe has the same anatomy, so once you've read one you can read them all: an ",[950,84232,84233],{},"Audience\u002FBundle"," header naming every package and ",[941,84236,943],{}," service composed, a ",[950,84239,58394],{}," block with the complete ",[941,84242,72368],{},", a walkthrough of each capability, and (where one exists) the ",[941,84245,84246],{},"showcases\u002Fsrc\u002Frecipe-*"," test that keeps the page honest.",[1338,84249,84250,84260],{},[1341,84251,84252],{},[1344,84253,84254,84257],{},[1347,84255,84256],{},"Recipe",[1347,84258,84259],{},"What it builds",[1357,84261,84262,84274,84283,84292,84301,84310,84319,84328,84337,84346,84355,84364,84373,84382,84391,84400,84409],{},[1344,84263,84264,84268],{},[1362,84265,84266],{},[1007,84267,559],{"href":560},[1362,84269,84270,84271,84273],{},"The minimalist, core-only encrypted notebook — the smallest possible ",[941,84272,72368],{}," with no opt-in services.",[1344,84275,84276,84280],{},[1362,84277,84278],{},[1007,84279,568],{"href":569},[1362,84281,84282],{},"An end-to-end React app: reactive lists and filtered views via hooks, encrypted persistence in IndexedDB, lock\u002Funlock — no state library.",[1344,84284,84285,84289],{},[1362,84286,84287],{},[1007,84288,593],{"href":594},[1362,84290,84291],{},"A per-user encrypted profile-and-preferences document in a shared vault — own-only writes, admin pre-fill at invite, cross-user reads, live bindings.",[1344,84293,84294,84298],{},[1362,84295,84296],{},[1007,84297,543],{"href":544},[1362,84299,84300],{},"MIME emails as one queryable record plus blob slots — ingest, threading, inline images, and document revisions across a thread.",[1344,84302,84303,84307],{},[1362,84304,84305],{},[1007,84306,547],{"href":548},[1362,84308,84309],{},"Threading, inline-image HTML rendering, and cross-revision diffing on the same archive.",[1344,84311,84312,84316],{},[1362,84313,84314],{},[1007,84315,572],{"href":573},[1362,84317,84318],{},"CRDT merge, cross-device push\u002Fpull replication, live presence, and multi-user grants so every device converges on the same result.",[1344,84320,84321,84325],{},[1362,84322,84323],{},[1007,84324,511],{"href":512},[1362,84326,84327],{},"Fast filtering, joins, grouped and streaming aggregates, lazy mode for beyond-memory collections, and hot\u002Fcold multi-store routing.",[1344,84329,84330,84334],{},[1362,84331,84332],{},[1007,84333,507],{"href":508},[1362,84335,84336],{},"An auditable, period-sealing accounting app — version history, closed periods, attachments, localised labels, consent logging, and reports.",[1344,84338,84339,84343],{},[1362,84340,84341],{},[1007,84342,589],{"href":590},[1362,84344,84345],{},"The full unlock lifecycle — secret root of trust, passkey enrollment and daily no-secret unlock, session policy gating sensitive operations, printed recovery codes.",[1344,84347,84348,84352],{},[1362,84349,84350],{},[1007,84351,539],{"href":540},[1362,84353,84354],{},"Choosing the three parts of an anti-phishing tier-1 secret — one memorable sentence, an echo no acquaintance could guess, and the warning to teach the user.",[1344,84356,84357,84361],{},[1362,84358,84359],{},[1007,84360,555],{"href":556},[1362,84362,84363],{},"Federated passwordless tier-2 unlock with the least infrastructure — AWS Cognito runs the sign-in, one tiny key-connector, the on-oidc split-key client.",[1344,84365,84366,84370],{},[1362,84367,84368],{},[1007,84369,527],{"href":528},[1362,84371,84372],{},"Postgres-backed vault, unattended at-env unlock with the mandated Shamir recovery, and hot\u002Fcold placement via routeStore.",[1344,84374,84375,84379],{},[1362,84376,84377],{},[1007,84378,581],{"href":582},[1362,84380,84381],{},"Lexical, semantic, and hybrid retrieval over encrypted records, then permission-scoped LLM tools via in-ai.",[1344,84383,84384,84388],{},[1362,84385,84386],{},[1007,84387,523],{"href":524},[1362,84389,84390],{},"A pure SPA that survives a cleared cache — to-browser-idb for the live vault, a .noydb pod for the backup, and two tiers of getting it to disk.",[1344,84392,84393,84397],{},[1362,84394,84395],{},[1007,84396,551],{"href":552},[1362,84398,84399],{},"Getting data out, three ways — capability-gated CSV\u002FXLSX, the sealed .noydb bundle, and a member's re-keyed portable copy.",[1344,84401,84402,84406],{},[1362,84403,84404],{},[1007,84405,515],{"href":516},[1362,84407,84408],{},"A signed per-field commitment as a QR code, verified fully offline against a published Ed25519 key — six verdict states, revocation included.",[1344,84410,84411,84415],{},[1362,84412,84413],{},[1007,84414,519],{"href":520},[1362,84416,84417],{},"An AWS Lambda that KMS-decrypts a sealed record from S3 and renders a PDF with the attestation QR embedded — gated by signed share links, verifiable offline.",[937,84419,84420,84421,84423],{},"Beyond the recipes, every documented capability has a runnable proof: see ",[1007,84422,576],{"href":577}," for the full suite — its catalog is generated from the files, so the count there is current — and how to execute it locally in one command.",[1022,84425,84427],{"id":84426},"no-recipe-matches-compose-your-own","No recipe matches? Compose your own",[937,84429,84430],{},"Every recipe above is the same four decisions, made in order — make them for your own use case and you have your setup:",[1234,84432,84433,84453,84464,84481],{},[1114,84434,84435,84438,84439,1261,84443,84447,84448,84452],{},[950,84436,84437],{},"Pick a store"," — where ciphertext lives: nothing (in-memory default) for prototypes, ",[1007,84440,84441],{"href":384},[941,84442,7461],{},[1007,84444,84445],{"href":359},[941,84446,9507],{}," for local persistence, or a cloud\u002FSQL backend from the ",[1007,84449,84450,5141],{"href":115},[941,84451,1779],{},". The store choice changes no other code.",[1114,84454,84455,84458,84459,84463],{},[950,84456,84457],{},"Pick an unlock method"," — how the vault opens: the built-in secret, or an ",[1007,84460,84461,15939],{"href":115},[941,84462,1787],{}," (passkeys, OIDC, TOTP, recovery codes…). Methods stack — a vault can enroll several.",[1114,84465,84466,84469,84470,84472,84473,2085,84475,968,84478,84480],{},[950,84467,84468],{},"Opt into services"," — one ",[941,84471,943],{}," factory per capability from the ",[1007,84474,4272],{"href":598},[941,84476,84477],{},"historyStrategy: withHistory()",[941,84479,10312],{},", and so on. Each page states what the factory adds and what happens when you skip it; anything not opted into tree-shakes away.",[1114,84482,84483,13535,84486,84490,84491,84493],{},[950,84484,84485],{},"Wire your framework",[1007,84487,84488,49048],{"href":115},[941,84489,1783],{}," (React hooks, Vue composables, Pinia stores…) if you want reactive integration; plain ",[941,84492,6500],{}," calls if you don't.",[937,84495,84496],{},"The result is always the same shape the recipes' Setup blocks show:",[2217,84498,84500],{"className":2219,"code":84499,"language":2221,"meta":1037,"style":1037},"const db = await createNoydb({\n  store: \u002F* 1 *\u002F, user: 'me', secret: \u002F* 2 *\u002F,\n  historyStrategy: withHistory(), \u002F* 3, one per capability *\u002F\n})\n",[941,84501,84502,84518,84550,84565],{"__ignoreMap":1037},[2225,84503,84504,84506,84508,84510,84512,84514,84516],{"class":2227,"line":2228},[2225,84505,2775],{"class":2774},[2225,84507,2778],{"class":2239},[2225,84509,2781],{"class":2235},[2225,84511,2784],{"class":2231},[2225,84513,2744],{"class":2291},[2225,84515,2789],{"class":2239},[2225,84517,4200],{"class":2235},[2225,84519,84520,84522,84524,84527,84529,84531,84533,84535,84537,84539,84541,84543,84545,84548],{"class":2227,"line":1041},[2225,84521,15608],{"class":2307},[2225,84523,2311],{"class":2235},[2225,84525,84526],{"class":2763}," \u002F* 1 *\u002F",[2225,84528,2298],{"class":2235},[2225,84530,2795],{"class":2307},[2225,84532,2311],{"class":2235},[2225,84534,2249],{"class":2235},[2225,84536,4212],{"class":2252},[2225,84538,2805],{"class":2235},[2225,84540,2298],{"class":2235},[2225,84542,2810],{"class":2307},[2225,84544,2311],{"class":2235},[2225,84546,84547],{"class":2763}," \u002F* 2 *\u002F",[2225,84549,1707],{"class":2235},[2225,84551,84552,84554,84556,84558,84560,84562],{"class":2227,"line":1038},[2225,84553,4221],{"class":2307},[2225,84555,2311],{"class":2235},[2225,84557,4142],{"class":2291},[2225,84559,4228],{"class":2239},[2225,84561,2298],{"class":2235},[2225,84563,84564],{"class":2763}," \u002F* 3, one per capability *\u002F\n",[2225,84566,84567,84569],{"class":2227,"line":2288},[2225,84568,2359],{"class":2235},[2225,84570,2362],{"class":2239},[937,84572,960,84573,84575,84576,84579],{},[1007,84574,1019],{"href":1018}," is the interactive version of these four decisions — tick what your app needs and it writes this block for you, with the ",[941,84577,84578],{},"pnpm add"," line and the doc links to match.",[937,84581,84582],{},"Browse the pages in the sidebar, or start from the recipe closest to your use case and add what's missing.",[2645,84584,84585],{},"html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":1037,"searchDepth":1038,"depth":1038,"links":84587},[84588],{"id":84426,"depth":1041,"text":84427},"Runnable showcases and end-to-end usage recipes.",{},{"title":10,"icon":505},{"title":84212,"description":84589},"P5UOIu9BqaHtVAvMprDAYMjqc7e8QsoHorh2ZKkZj8M",{"id":84595,"title":555,"access":12,"apiToc":929,"body":84596,"category":1042,"description":86385,"extension":1044,"meta":86386,"navigation":1042,"nest":1042,"path":556,"seo":86387,"sinceVersion":1042,"status":1049,"stem":557,"subtitle":1042,"tier":1050,"tocExternal":1042,"__hash__":86388},"docs\u002Frecipes\u002Fpasswordless-oidc.md",{"type":931,"value":84597,"toc":86373},[84598,84601,84623,84649,84659,84663,84680,84770,84782,84784,84844,84846,84916,84918,84927,85066,85070,85087,85138,85141,85145,85171,85760,85769,85773,85785,85940,85949,86016,86033,86168,86170,86189,86274,86277,86342,86344,86370],[934,84599,555],{"id":84600},"passwordless-oidc-aws-cognito",[1061,84602,84603],{},[937,84604,84605,84607,84608,84611,84612,58223,84614,84616,84617,84619,84620],{},[950,84606,1500],{}," an app developer who wants users to sign in ",[950,84609,84610],{},"passwordlessly"," (email one-time-code or a passkey) and open their vault, with the least infrastructure to run yourself.\n",[950,84613,58222],{},[941,84615,44580],{}," (client) + ",[950,84618,11756],{}," key-connector server you host + a managed OIDC provider (AWS Cognito). ",[1092,84621,84622],{},"(★ required)",[937,84624,58245,84625,84632,84633,84636,84637,1625,84639,84641,84642,84645,84646],{},[1007,84626,84629],{"href":84627,"rel":84628},"https:\u002F\u002Fgithub.com\u002Fnoy-db\u002Fdocs\u002Fblob\u002Fmain\u002Fshowcases\u002Fsrc\u002Frecipe-passwordless-oidc.recipe.test.ts",[3371],[941,84630,84631],{},"showcases\u002Fsrc\u002Frecipe-passwordless-oidc.recipe.test.ts"," — the client code below is the same code that runs there. CI can't reach real Cognito, so the test signs a ",[950,84634,84635],{},"Cognito-shaped"," RS256 ID token locally and verifies it against a local JWKS through an in-memory key-connector; the exact same ",[941,84638,44614],{},[941,84640,44625],{}," calls and the same ",[941,84643,84644],{},"verifyIdToken()"," run against real Cognito in production. ",[1092,84647,84648],{},"(★)",[937,84650,84651,84652,84655,84656],{},"Give users a login they never type a vault secret for: they authenticate to AWS Cognito — an emailed one-time code, or a passkey — and that federated identity unlocks their noy-db vault as a ",[950,84653,84654],{},"tier-2"," session. Cognito runs the sign-in ceremony and the account directory so you don't; noy-db stays zero-knowledge because the ID token alone can never decrypt anything. This is the cheapest, least-operations way to get federated passwordless unlock — but be clear with yourself up front: ",[950,84657,84658],{},"it is not zero-server.",[1022,84660,84662],{"id":84661},"the-three-pieces-and-the-honest-part","The three pieces (and the honest part)",[937,84664,84665,84667,84668,84671,84672,84675,84676,84679],{},[941,84666,15969],{}," is a Bitwarden-style ",[950,84669,84670],{},"split-key key-connector"," design: ",[941,84673,84674],{},"KEK = serverHalf ⊕ deviceHalf",". That means passwordless-federated unlock needs ",[950,84677,84678],{},"three"," cooperating pieces, and exactly one of them is a server you must run:",[13905,84681,84682],{"style":13907},[13905,84683,84685,84709,84718,84741,84744],{"style":84684},"display:flex; flex-direction:column; gap:0.6rem; min-width:22rem; line-height:1.4;",[13905,84686,84688,84694],{"style":84687},"border:1px solid var(--ui-border); border-radius:0.5rem; background:var(--ui-bg); padding:0.6rem 1rem;",[13905,84689,84690,84691],{"style":13916},"1 · AWS Cognito ",[2225,84692,84693],{"style":13943},"— the OIDC provider (managed)",[13905,84695,84697,84698,84701,84702,84705,84706,84708],{"style":84696},"font-size:0.8rem; color:var(--ui-text-muted);","Runs the passwordless ceremony (email OTP, or a passkey) and issues an OIDC ",[2225,84699,84700],{"style":13950},"ID token",". noy-db never sees the ceremony — only the resulting token. Proves ",[1092,84703,84704],{},"who",", carries ",[1092,84707,11313],{}," wrap-key.",[13905,84710,84712,84713,6030,84715,1381],{"style":84711},"text-align:center; color:var(--ui-text-muted); font-size:0.7rem;","▼  ID token (RS256, ",[2225,84714,44608],{"style":13950},[2225,84716,84717],{"style":13950},"aud",[13905,84719,84721,84727],{"style":84720},"border:1px solid var(--ui-primary); border-radius:0.5rem; background:var(--ui-bg); padding:0.6rem 1rem;",[13905,84722,84723,84724],{"style":13916},"2 · The key-connector ",[2225,84725,84726],{"style":13943},"— a tiny server YOU host",[13905,84728,84729,84732,84733,8003,84735,84737,84738,84740],{"style":84696},[2225,84730,84731],{"style":13950},"PUT\u002FGET \u002Fkek-fragment",". Verifies the Cognito ID token against the pool's JWKS, then stores\u002Freturns the encrypted ",[2225,84734,38374],{"style":13950},[2225,84736,44608],{"style":13950},". This is the one piece that is not zero-infra. It holds only ",[2225,84739,38374],{"style":13950}," — useless without the device half.",[13905,84742,84743],{"style":84711},"▼  encrypted serverHalf",[13905,84745,84746,84752],{"style":84687},[13905,84747,84748,84749],{"style":13916},"3 · The noy-db client ",[2225,84750,84751],{"style":13943},"— on-oidc",[13905,84753,84754,1261,84756,84758,84759,84762,84763,84765,84766,18477,84768,957],{"style":84696},[2225,84755,44614],{"style":13950},[2225,84757,44625],{"style":13950}," derive the ",[2225,84760,84761],{"style":13950},"deviceHalf"," from a per-device secret (never transmitted) and XOR it with the ",[2225,84764,38374],{"style":13950}," → the KEK → a ",[950,84767,84654],{},[2225,84769,17129],{"style":13950},[937,84771,84772,84773,84778,84779,84781],{},"Neither Cognito nor the key-connector ever holds both halves, so a compromise of either — stolen tokens, or a breached connector — still cannot read the vault. Note that ",[950,84774,84775,84777],{},[941,84776,15969],{}," itself does not verify the ID token signature"," — that is the key-connector's job. The client only reads claims for a fast ",[941,84780,38360],{}," pre-flight.",[1022,84783,70093],{"id":70092},[1111,84785,84786,84796,84802,84823,84832],{},[1114,84787,84788,1324,84790,84793,84794,957],{},[950,84789,71604],{},[941,84791,84792],{},"pnpm add @noy-db\u002Fhub @noy-db\u002Fon-oidc"," (+ a store, e.g. ",[941,84795,9573],{},[1114,84797,84798,84801],{},[950,84799,84800],{},"An AWS Cognito user pool"," with passwordless sign-in and an app client (below). Cognito's free tier covers a generous monthly active-user allowance, and beyond it the per-MAU cost is low — this is the \"cheap managed provider\" choice.",[1114,84803,84804,84807,84808,84810,84811,84813,84814,84816,84817,6793,84821,1520],{},[950,84805,84806],{},"A key-connector server you host"," speaking ",[941,84809,84731],{},". It's small (below), but it ",[1092,84812,3082],{}," a server — if you'd rather run ",[950,84815,11313],{}," server at all, jump to ",[1007,84818,84820],{"href":84819},"#limits-alternative","Limits & alternative",[941,84822,15944],{},[1114,84824,84825,84828,84829,84831],{},[950,84826,84827],{},"A one-time tier-1 bootstrap"," per user (secret, recovery code, or a custodian invite). The OIDC unlock is enrolled ",[1092,84830,2272],{}," an already-unlocked keyring — it adds a passwordless way in, it does not create the vault.",[1114,84833,84834,84840,84841,84843],{},[950,84835,84836,84837,24268],{},"Somewhere to persist the ",[941,84838,84839],{},"OidcEnrollment"," — it is opaque metadata (no key material), so any store or ",[941,84842,41955],{}," works.",[1022,84845,58276],{"id":58275},[1338,84847,84848,84857],{},[1341,84849,84850],{},[1344,84851,84852,84854],{},[1347,84853,70107],{},[1347,84855,84856],{},"Which piece provides it",[1357,84858,84859,84867,84875,84885,84897,84908],{},[1344,84860,84861,84864],{},[1362,84862,84863],{},"Passwordless sign-in (email OTP \u002F passkey)",[1362,84865,84866],{},"AWS Cognito's hosted ceremony",[1344,84868,84869,84872],{},[1362,84870,84871],{},"Federated identity, no password directory of your own",[1362,84873,84874],{},"Cognito user pool + app client",[1344,84876,84877,84880],{},[1362,84878,84879],{},"Vault unlock from that identity, zero-knowledge",[1362,84881,84882,84884],{},[941,84883,15969],{}," split-key + your key-connector",[1344,84886,84887,84890],{},[1362,84888,84889],{},"Read \u002F write the vault after unlock",[1362,84891,84892,84893,953,84895,1381],{},"the returned tier-2 ",[941,84894,11493],{},[941,84896,17129],{},[1344,84898,84899,84902],{},[1362,84900,84901],{},"grant \u002F rotate \u002F enroll-another \u002F open a second vault",[1362,84903,84904,84907],{},[950,84905,84906],{},"Not"," this session — needs a tier-1 unlock",[1344,84909,84910,84913],{},[1362,84911,84912],{},"Recover a lost device",[1362,84914,84915],{},"a recovery profile or a fresh tier-1 re-enrollment — see below",[1022,84917,58394],{"id":58393},[937,84919,84920,84921,84923,84924,84926],{},"The tier-1 bootstrap is an ordinary ",[941,84922,72368],{},". Later sessions swap the secret for the OIDC unlock via the ",[941,84925,9953],{}," seam (last block):",[2217,84928,84930],{"className":2219,"code":84929,"language":2221,"meta":1037,"style":1037},"import { createNoydb } from '@noy-db\u002Fhub'\nimport { toMemory } from '@noy-db\u002Fto-memory' \u002F\u002F swap for toBrowserIdb() in a browser\n\nconst db = await createNoydb({\n  store: toMemory(),\n  user: 'alice',\n  secret: 'correct horse battery staple', \u002F\u002F tier-1 bootstrap, once\n})\nconst vault = await db.openVault('books')\n",[941,84931,84932,84950,84971,84975,84991,85003,85017,85034,85040],{"__ignoreMap":1037},[2225,84933,84934,84936,84938,84940,84942,84944,84946,84948],{"class":2227,"line":2228},[2225,84935,2232],{"class":2231},[2225,84937,2236],{"class":2235},[2225,84939,2744],{"class":2239},[2225,84941,2243],{"class":2235},[2225,84943,2246],{"class":2231},[2225,84945,2249],{"class":2235},[2225,84947,1086],{"class":2252},[2225,84949,2256],{"class":2235},[2225,84951,84952,84954,84956,84958,84960,84962,84964,84966,84968],{"class":2227,"line":1041},[2225,84953,2232],{"class":2231},[2225,84955,2236],{"class":2235},[2225,84957,68576],{"class":2239},[2225,84959,2243],{"class":2235},[2225,84961,2246],{"class":2231},[2225,84963,2249],{"class":2235},[2225,84965,9638],{"class":2252},[2225,84967,2805],{"class":2235},[2225,84969,84970],{"class":2763}," \u002F\u002F swap for toBrowserIdb() in a browser\n",[2225,84972,84973],{"class":2227,"line":1038},[2225,84974,2285],{"emptyLinePlaceholder":2284},[2225,84976,84977,84979,84981,84983,84985,84987,84989],{"class":2227,"line":2288},[2225,84978,2775],{"class":2774},[2225,84980,2778],{"class":2239},[2225,84982,2781],{"class":2235},[2225,84984,2784],{"class":2231},[2225,84986,2744],{"class":2291},[2225,84988,2789],{"class":2239},[2225,84990,4200],{"class":2235},[2225,84992,84993,84995,84997,84999,85001],{"class":2227,"line":2304},[2225,84994,15608],{"class":2307},[2225,84996,2311],{"class":2235},[2225,84998,68576],{"class":2291},[2225,85000,4228],{"class":2239},[2225,85002,1707],{"class":2235},[2225,85004,85005,85007,85009,85011,85013,85015],{"class":2227,"line":2356},[2225,85006,4205],{"class":2307},[2225,85008,2311],{"class":2235},[2225,85010,2249],{"class":2235},[2225,85012,6408],{"class":2252},[2225,85014,2805],{"class":2235},[2225,85016,1707],{"class":2235},[2225,85018,85019,85021,85023,85025,85027,85029,85031],{"class":2227,"line":2830},[2225,85020,10423],{"class":2307},[2225,85022,2311],{"class":2235},[2225,85024,2249],{"class":2235},[2225,85026,40260],{"class":2252},[2225,85028,2805],{"class":2235},[2225,85030,2298],{"class":2235},[2225,85032,85033],{"class":2763}," \u002F\u002F tier-1 bootstrap, once\n",[2225,85035,85036,85038],{"class":2227,"line":2861},[2225,85037,2359],{"class":2235},[2225,85039,2362],{"class":2239},[2225,85041,85042,85044,85046,85048,85050,85052,85054,85056,85058,85060,85062,85064],{"class":2227,"line":4246},[2225,85043,2775],{"class":2774},[2225,85045,2835],{"class":2239},[2225,85047,2781],{"class":2235},[2225,85049,2784],{"class":2231},[2225,85051,2842],{"class":2239},[2225,85053,1520],{"class":2235},[2225,85055,2847],{"class":2291},[2225,85057,2789],{"class":2239},[2225,85059,2805],{"class":2235},[2225,85061,65761],{"class":2252},[2225,85063,2805],{"class":2235},[2225,85065,2362],{"class":2239},[1022,85067,85069],{"id":85068},"cognito-setup-documented-not-ci-tested","Cognito setup (documented, not CI-tested)",[1061,85071,85072],{},[937,85073,85074,85075,85077,85078,85080,85081,85086],{},"These are AWS console\u002FAPI steps, so the showcase does ",[950,85076,1004],{}," execute them — it stands in a Cognito-shaped token. The setting ",[1092,85079,71352],{}," below are accurate; consult the ",[1007,85082,85085],{"href":85083,"rel":85084},"https:\u002F\u002Fdocs.aws.amazon.com\u002Fcognito\u002Flatest\u002Fdeveloperguide\u002Fcognito-user-identity-pools.html",[3371],"Amazon Cognito user-pool docs"," for the current console click-path, which AWS revises often.",[1234,85088,85089,85103,85120],{},[1114,85090,85091,85094,85095,85098,85099,85102],{},[950,85092,85093],{},"Create a user pool."," Choose passwordless sign-in: enable ",[950,85096,85097],{},"email one-time code"," (and, optionally, ",[950,85100,85101],{},"passkey \u002F WebAuthn",") as the sign-in method. This is the ceremony noy-db never sees.",[1114,85104,85105,85108,85109,85112,85113,85115,85116,85119],{},[950,85106,85107],{},"Add an app client."," Its ",[950,85110,85111],{},"client ID"," becomes the ID token's ",[941,85114,84717],{}," claim and your ",[941,85117,85118],{},"config.clientId",". For a public SPA\u002Fnative client, use a client without a secret and the authorization-code + PKCE flow.",[1114,85121,85122,85125,85126,85129,85130,85133,85134,85137],{},[950,85123,85124],{},"Note the issuer."," A pool's OIDC issuer is ",[941,85127,85128],{},"https:\u002F\u002Fcognito-idp.\u003Cregion>.amazonaws.com\u002F\u003CuserPoolId>"," and its JWKS is published at ",[941,85131,85132],{},"${issuer}\u002F.well-known\u002Fjwks.json",". Cognito signs ID tokens with ",[950,85135,85136],{},"RS256",". Your key-connector verifies against that JWKS.",[937,85139,85140],{},"That's the whole managed surface — no password reset flows, no email templates, no account tables to own.",[1022,85142,85144],{"id":85143},"the-key-connector-the-one-server-you-run","The key-connector (the one server you run)",[937,85146,85147,85148,85150,85151,85154,85155,85157,85158,85160,85161,85163,85164,1746,85167,85170],{},"A minimal handler for the two endpoints ",[941,85149,15969],{}," speaks. It ",[950,85152,85153],{},"verifies the Cognito ID token against the pool's JWKS",", then stores\u002Freturns the encrypted ",[941,85156,38374],{}," indexed by the verified ",[941,85159,44608],{}," — never trusting a ",[941,85162,44608],{}," from the request body. This mirrors the real, dependency-free connector fixture the showcase runs (",[941,85165,85166],{},"showcases\u002Fsrc\u002F_oidc.ts",[941,85168,85169],{},"createInMemoryKeyConnector","); the fixture is the same protocol, only in-process.",[2217,85172,85174],{"className":2219,"code":85173,"language":2221,"meta":1037,"style":1037},"\u002F\u002F key-connector — sketch of the one server piece you host.\n\u002F\u002F `verifyCognitoIdToken` fetches the pool's JWKS once, caches it, and checks\n\u002F\u002F signature + iss + aud + exp (RS256). See _oidc.ts::verifyIdToken for the\n\u002F\u002F exact Web-Crypto verification this stands in for.\nimport { verifyCognitoIdToken } from '.\u002Fcognito-verify.js'\n\nconst JWKS_URI = `https:\u002F\u002Fcognito-idp.${REGION}.amazonaws.com\u002F${POOL_ID}\u002F.well-known\u002Fjwks.json`\nconst store = new Map\u003Cstring, { encryptedServerHalf: string; iv: string }>()\n\nasync function handle(req: Request): Promise\u003CResponse> {\n  const idToken = (req.headers.get('authorization') ?? '').replace(\u002F^Bearer\\s+\u002Fi, '')\n  let sub: string\n  try {\n    ;({ sub } = await verifyCognitoIdToken(idToken, { jwksUri: JWKS_URI, issuer: ISSUER, audience: APP_CLIENT_ID }))\n  } catch {\n    return new Response(JSON.stringify({ error: 'invalid_token' }), { status: 401 })\n  }\n\n  if (req.method === 'PUT') {\n    const { encryptedServerHalf, iv } = await req.json()\n    store.set(sub, { encryptedServerHalf, iv }) \u002F\u002F sub from the VERIFIED token, never the body\n    return new Response(JSON.stringify({ ok: true }))\n  }\n  \u002F\u002F GET\n  const frag = store.get(sub)\n  return frag\n    ? new Response(JSON.stringify(frag))\n    : new Response(JSON.stringify({ error: 'not_found' }), { status: 404 })\n}\n",[941,85175,85176,85181,85186,85191,85196,85216,85220,85256,85296,85300,85328,85390,85401,85407,85466,85474,85526,85530,85534,85559,85585,85615,85646,85650,85655,85676,85683,85706,85756],{"__ignoreMap":1037},[2225,85177,85178],{"class":2227,"line":2228},[2225,85179,85180],{"class":2763},"\u002F\u002F key-connector — sketch of the one server piece you host.\n",[2225,85182,85183],{"class":2227,"line":1041},[2225,85184,85185],{"class":2763},"\u002F\u002F `verifyCognitoIdToken` fetches the pool's JWKS once, caches it, and checks\n",[2225,85187,85188],{"class":2227,"line":1038},[2225,85189,85190],{"class":2763},"\u002F\u002F signature + iss + aud + exp (RS256). See _oidc.ts::verifyIdToken for the\n",[2225,85192,85193],{"class":2227,"line":2288},[2225,85194,85195],{"class":2763},"\u002F\u002F exact Web-Crypto verification this stands in for.\n",[2225,85197,85198,85200,85202,85205,85207,85209,85211,85214],{"class":2227,"line":2304},[2225,85199,2232],{"class":2231},[2225,85201,2236],{"class":2235},[2225,85203,85204],{"class":2239}," verifyCognitoIdToken",[2225,85206,2243],{"class":2235},[2225,85208,2246],{"class":2231},[2225,85210,2249],{"class":2235},[2225,85212,85213],{"class":2252},".\u002Fcognito-verify.js",[2225,85215,2256],{"class":2235},[2225,85217,85218],{"class":2227,"line":2356},[2225,85219,2285],{"emptyLinePlaceholder":2284},[2225,85221,85222,85224,85227,85229,85231,85234,85236,85239,85241,85244,85246,85249,85251,85254],{"class":2227,"line":2830},[2225,85223,2775],{"class":2774},[2225,85225,85226],{"class":2239}," JWKS_URI ",[2225,85228,2781],{"class":2235},[2225,85230,43789],{"class":2235},[2225,85232,85233],{"class":2252},"https:\u002F\u002Fcognito-idp.",[2225,85235,8197],{"class":2235},[2225,85237,85238],{"class":2239},"REGION",[2225,85240,2359],{"class":2235},[2225,85242,85243],{"class":2252},".amazonaws.com\u002F",[2225,85245,8197],{"class":2235},[2225,85247,85248],{"class":2239},"POOL_ID",[2225,85250,2359],{"class":2235},[2225,85252,85253],{"class":2252},"\u002F.well-known\u002Fjwks.json",[2225,85255,8659],{"class":2235},[2225,85257,85258,85260,85262,85264,85266,85268,85270,85272,85274,85276,85279,85281,85283,85285,85287,85289,85291,85294],{"class":2227,"line":2861},[2225,85259,2775],{"class":2774},[2225,85261,39676],{"class":2239},[2225,85263,2781],{"class":2235},[2225,85265,8130],{"class":2235},[2225,85267,8133],{"class":2291},[2225,85269,6474],{"class":2235},[2225,85271,7259],{"class":6342},[2225,85273,2298],{"class":2235},[2225,85275,2236],{"class":2235},[2225,85277,85278],{"class":2307}," encryptedServerHalf",[2225,85280,2311],{"class":2235},[2225,85282,6366],{"class":6342},[2225,85284,3769],{"class":2235},[2225,85286,18710],{"class":2307},[2225,85288,2311],{"class":2235},[2225,85290,6366],{"class":6342},[2225,85292,85293],{"class":2235}," }>",[2225,85295,8146],{"class":2239},[2225,85297,85298],{"class":2227,"line":4246},[2225,85299,2285],{"emptyLinePlaceholder":2284},[2225,85301,85302,85304,85306,85308,85310,85312,85314,85316,85318,85320,85322,85324,85326],{"class":2227,"line":4253},[2225,85303,70491],{"class":2774},[2225,85305,40856],{"class":2774},[2225,85307,40913],{"class":2291},[2225,85309,2789],{"class":2235},[2225,85311,40861],{"class":7052},[2225,85313,2311],{"class":2235},[2225,85315,40866],{"class":6342},[2225,85317,6255],{"class":2235},[2225,85319,7079],{"class":6342},[2225,85321,6474],{"class":2235},[2225,85323,40875],{"class":6342},[2225,85325,6480],{"class":2235},[2225,85327,2301],{"class":2235},[2225,85329,85330,85332,85335,85337,85339,85341,85343,85345,85347,85349,85351,85353,85356,85358,85360,85362,85364,85366,85368,85370,85372,85374,85377,85380,85382,85384,85386,85388],{"class":2227,"line":4259},[2225,85331,8123],{"class":2774},[2225,85333,85334],{"class":2239}," idToken",[2225,85336,6346],{"class":2235},[2225,85338,953],{"class":2307},[2225,85340,40861],{"class":2239},[2225,85342,1520],{"class":2235},[2225,85344,52729],{"class":2239},[2225,85346,1520],{"class":2235},[2225,85348,3158],{"class":2291},[2225,85350,2789],{"class":2307},[2225,85352,2805],{"class":2235},[2225,85354,85355],{"class":2252},"authorization",[2225,85357,2805],{"class":2235},[2225,85359,8495],{"class":2307},[2225,85361,8381],{"class":2235},[2225,85363,12500],{"class":2235},[2225,85365,1381],{"class":2307},[2225,85367,1520],{"class":2235},[2225,85369,71260],{"class":2291},[2225,85371,2789],{"class":2307},[2225,85373,1625],{"class":2235},[2225,85375,85376],{"class":2231},"^",[2225,85378,85379],{"class":2252},"Bearer\\s",[2225,85381,79554],{"class":2235},[2225,85383,79386],{"class":6527},[2225,85385,2298],{"class":2235},[2225,85387,12500],{"class":2235},[2225,85389,2362],{"class":2307},[2225,85391,85392,85394,85397,85399],{"class":2227,"line":6548},[2225,85393,66632],{"class":2774},[2225,85395,85396],{"class":2239}," sub",[2225,85398,2311],{"class":2235},[2225,85400,7014],{"class":6342},[2225,85402,85403,85405],{"class":2227,"line":6576},[2225,85404,66644],{"class":2231},[2225,85406,2301],{"class":2235},[2225,85408,85409,85412,85414,85416,85418,85420,85422,85424,85426,85428,85431,85433,85435,85438,85440,85443,85445,85447,85449,85452,85454,85457,85459,85462,85464],{"class":2227,"line":6582},[2225,85410,85411],{"class":2235},"    ;",[2225,85413,2789],{"class":2307},[2225,85415,2792],{"class":2235},[2225,85417,85396],{"class":2239},[2225,85419,2243],{"class":2235},[2225,85421,6346],{"class":2235},[2225,85423,2784],{"class":2231},[2225,85425,85204],{"class":2291},[2225,85427,2789],{"class":2307},[2225,85429,85430],{"class":2239},"idToken",[2225,85432,2298],{"class":2235},[2225,85434,2236],{"class":2235},[2225,85436,85437],{"class":2307}," jwksUri",[2225,85439,2311],{"class":2235},[2225,85441,85442],{"class":2239}," JWKS_URI",[2225,85444,2298],{"class":2235},[2225,85446,46370],{"class":2307},[2225,85448,2311],{"class":2235},[2225,85450,85451],{"class":2239}," ISSUER",[2225,85453,2298],{"class":2235},[2225,85455,85456],{"class":2307}," audience",[2225,85458,2311],{"class":2235},[2225,85460,85461],{"class":2239}," APP_CLIENT_ID",[2225,85463,2243],{"class":2235},[2225,85465,8606],{"class":2307},[2225,85467,85468,85470,85472],{"class":2227,"line":6587},[2225,85469,19143],{"class":2235},[2225,85471,7598],{"class":2231},[2225,85473,2301],{"class":2235},[2225,85475,85476,85478,85480,85482,85484,85486,85488,85491,85493,85495,85498,85500,85502,85505,85507,85509,85511,85513,85515,85517,85519,85522,85524],{"class":2227,"line":7174},[2225,85477,66675],{"class":2231},[2225,85479,8130],{"class":2235},[2225,85481,40927],{"class":2291},[2225,85483,2789],{"class":2307},[2225,85485,50521],{"class":2239},[2225,85487,1520],{"class":2235},[2225,85489,85490],{"class":2291},"stringify",[2225,85492,2789],{"class":2307},[2225,85494,2792],{"class":2235},[2225,85496,85497],{"class":2307}," error",[2225,85499,2311],{"class":2235},[2225,85501,2249],{"class":2235},[2225,85503,85504],{"class":2252},"invalid_token",[2225,85506,2805],{"class":2235},[2225,85508,2243],{"class":2235},[2225,85510,1381],{"class":2307},[2225,85512,2298],{"class":2235},[2225,85514,2236],{"class":2235},[2225,85516,6361],{"class":2307},[2225,85518,2311],{"class":2235},[2225,85520,85521],{"class":6527}," 401",[2225,85523,2243],{"class":2235},[2225,85525,2362],{"class":2307},[2225,85527,85528],{"class":2227,"line":7180},[2225,85529,7632],{"class":2235},[2225,85531,85532],{"class":2227,"line":7220},[2225,85533,2285],{"emptyLinePlaceholder":2284},[2225,85535,85536,85538,85540,85542,85544,85546,85548,85550,85553,85555,85557],{"class":2227,"line":7225},[2225,85537,7608],{"class":2231},[2225,85539,953],{"class":2307},[2225,85541,40861],{"class":2239},[2225,85543,1520],{"class":2235},[2225,85545,17574],{"class":2239},[2225,85547,9025],{"class":2235},[2225,85549,2249],{"class":2235},[2225,85551,85552],{"class":2252},"PUT",[2225,85554,2805],{"class":2235},[2225,85556,8495],{"class":2307},[2225,85558,4200],{"class":2235},[2225,85560,85561,85563,85565,85567,85569,85571,85573,85575,85577,85579,85581,85583],{"class":2227,"line":7231},[2225,85562,80048],{"class":2774},[2225,85564,2236],{"class":2235},[2225,85566,85278],{"class":2239},[2225,85568,2298],{"class":2235},[2225,85570,18710],{"class":2239},[2225,85572,2243],{"class":2235},[2225,85574,6346],{"class":2235},[2225,85576,2784],{"class":2231},[2225,85578,40893],{"class":2239},[2225,85580,1520],{"class":2235},[2225,85582,141],{"class":2291},[2225,85584,8146],{"class":2307},[2225,85586,85587,85590,85592,85594,85596,85598,85600,85602,85604,85606,85608,85610,85612],{"class":2227,"line":7267},[2225,85588,85589],{"class":2239},"    store",[2225,85591,1520],{"class":2235},[2225,85593,8528],{"class":2291},[2225,85595,2789],{"class":2307},[2225,85597,44608],{"class":2239},[2225,85599,2298],{"class":2235},[2225,85601,2236],{"class":2235},[2225,85603,85278],{"class":2239},[2225,85605,2298],{"class":2235},[2225,85607,18710],{"class":2239},[2225,85609,2243],{"class":2235},[2225,85611,8495],{"class":2307},[2225,85613,85614],{"class":2763},"\u002F\u002F sub from the VERIFIED token, never the body\n",[2225,85616,85617,85619,85621,85623,85625,85627,85629,85631,85633,85635,85638,85640,85642,85644],{"class":2227,"line":7272},[2225,85618,66675],{"class":2231},[2225,85620,8130],{"class":2235},[2225,85622,40927],{"class":2291},[2225,85624,2789],{"class":2307},[2225,85626,50521],{"class":2239},[2225,85628,1520],{"class":2235},[2225,85630,85490],{"class":2291},[2225,85632,2789],{"class":2307},[2225,85634,2792],{"class":2235},[2225,85636,85637],{"class":2307}," ok",[2225,85639,2311],{"class":2235},[2225,85641,2322],{"class":2321},[2225,85643,2243],{"class":2235},[2225,85645,8606],{"class":2307},[2225,85647,85648],{"class":2227,"line":7278},[2225,85649,7632],{"class":2235},[2225,85651,85652],{"class":2227,"line":7303},[2225,85653,85654],{"class":2763},"  \u002F\u002F GET\n",[2225,85656,85657,85659,85662,85664,85666,85668,85670,85672,85674],{"class":2227,"line":7308},[2225,85658,8123],{"class":2774},[2225,85660,85661],{"class":2239}," frag",[2225,85663,6346],{"class":2235},[2225,85665,7564],{"class":2239},[2225,85667,1520],{"class":2235},[2225,85669,3158],{"class":2291},[2225,85671,2789],{"class":2307},[2225,85673,44608],{"class":2239},[2225,85675,2362],{"class":2307},[2225,85677,85678,85680],{"class":2227,"line":7314},[2225,85679,8220],{"class":2231},[2225,85681,85682],{"class":2239}," frag\n",[2225,85684,85685,85687,85689,85691,85693,85695,85697,85699,85701,85704],{"class":2227,"line":7348},[2225,85686,48355],{"class":2235},[2225,85688,8130],{"class":2235},[2225,85690,40927],{"class":2291},[2225,85692,2789],{"class":2307},[2225,85694,50521],{"class":2239},[2225,85696,1520],{"class":2235},[2225,85698,85490],{"class":2291},[2225,85700,2789],{"class":2307},[2225,85702,85703],{"class":2239},"frag",[2225,85705,8606],{"class":2307},[2225,85707,85708,85710,85712,85714,85716,85718,85720,85722,85724,85726,85728,85730,85732,85735,85737,85739,85741,85743,85745,85747,85749,85752,85754],{"class":2227,"line":8613},[2225,85709,48364],{"class":2235},[2225,85711,8130],{"class":2235},[2225,85713,40927],{"class":2291},[2225,85715,2789],{"class":2307},[2225,85717,50521],{"class":2239},[2225,85719,1520],{"class":2235},[2225,85721,85490],{"class":2291},[2225,85723,2789],{"class":2307},[2225,85725,2792],{"class":2235},[2225,85727,85497],{"class":2307},[2225,85729,2311],{"class":2235},[2225,85731,2249],{"class":2235},[2225,85733,85734],{"class":2252},"not_found",[2225,85736,2805],{"class":2235},[2225,85738,2243],{"class":2235},[2225,85740,1381],{"class":2307},[2225,85742,2298],{"class":2235},[2225,85744,2236],{"class":2235},[2225,85746,6361],{"class":2307},[2225,85748,2311],{"class":2235},[2225,85750,85751],{"class":6527}," 404",[2225,85753,2243],{"class":2235},[2225,85755,2362],{"class":2307},[2225,85757,85758],{"class":2227,"line":8633},[2225,85759,7351],{"class":2235},[937,85761,85762,85763,85765,85766,85768],{},"The connector holds only ",[941,85764,38374],{},", which is worthless without the device-bound ",[941,85767,84761],{}," — that is why a connector breach does not read the vault.",[1022,85770,85772],{"id":85771},"the-noy-db-client","The noy-db client",[937,85774,85775,85776,1952,85782,85784],{},"Build the Cognito config as a ",[950,85777,85778,85779],{},"plain ",[941,85780,85781],{},"OidcProviderConfig",[941,85783,44660],{}," ships shortcuts for google \u002F apple \u002F line only; every other issuer — Cognito included — is this same public object shape, so nothing here is a workaround:",[2217,85786,85788],{"className":2219,"code":85787,"language":2221,"meta":1037,"style":1037},"import { enrollOidc, unlockOidc, type OidcProviderConfig } from '@noy-db\u002Fon-oidc'\n\nconst config: OidcProviderConfig = {\n  name: 'AWS Cognito',\n  issuer: `https:\u002F\u002Fcognito-idp.${REGION}.amazonaws.com\u002F${POOL_ID}`,\n  clientId: APP_CLIENT_ID, \u002F\u002F the app-client id == the ID token's `aud`\n  scopes: ['openid', 'email'],\n  keyConnectorUrl: 'https:\u002F\u002Fkey-connector.example.com',\n}\n",[941,85789,85790,85820,85824,85838,85853,85880,85893,85921,85936],{"__ignoreMap":1037},[2225,85791,85792,85794,85796,85799,85801,85803,85805,85807,85810,85812,85814,85816,85818],{"class":2227,"line":2228},[2225,85793,2232],{"class":2231},[2225,85795,2236],{"class":2235},[2225,85797,85798],{"class":2239}," enrollOidc",[2225,85800,2298],{"class":2235},[2225,85802,44789],{"class":2239},[2225,85804,2298],{"class":2235},[2225,85806,3399],{"class":2231},[2225,85808,85809],{"class":2239}," OidcProviderConfig",[2225,85811,2243],{"class":2235},[2225,85813,2246],{"class":2231},[2225,85815,2249],{"class":2235},[2225,85817,44580],{"class":2252},[2225,85819,2256],{"class":2235},[2225,85821,85822],{"class":2227,"line":1041},[2225,85823,2285],{"emptyLinePlaceholder":2284},[2225,85825,85826,85828,85830,85832,85834,85836],{"class":2227,"line":1038},[2225,85827,2775],{"class":2774},[2225,85829,36491],{"class":2239},[2225,85831,2311],{"class":2235},[2225,85833,85809],{"class":6342},[2225,85835,6346],{"class":2235},[2225,85837,2301],{"class":2235},[2225,85839,85840,85842,85844,85846,85849,85851],{"class":2227,"line":2288},[2225,85841,7008],{"class":2307},[2225,85843,2311],{"class":2235},[2225,85845,2249],{"class":2235},[2225,85847,85848],{"class":2252},"AWS Cognito",[2225,85850,2805],{"class":2235},[2225,85852,1707],{"class":2235},[2225,85854,85855,85858,85860,85862,85864,85866,85868,85870,85872,85874,85876,85878],{"class":2227,"line":2304},[2225,85856,85857],{"class":2307},"  issuer",[2225,85859,2311],{"class":2235},[2225,85861,43789],{"class":2235},[2225,85863,85233],{"class":2252},[2225,85865,8197],{"class":2235},[2225,85867,85238],{"class":2239},[2225,85869,2359],{"class":2235},[2225,85871,85243],{"class":2252},[2225,85873,8197],{"class":2235},[2225,85875,85248],{"class":2239},[2225,85877,43799],{"class":2235},[2225,85879,1707],{"class":2235},[2225,85881,85882,85884,85886,85888,85890],{"class":2227,"line":2356},[2225,85883,58854],{"class":2307},[2225,85885,2311],{"class":2235},[2225,85887,85461],{"class":2239},[2225,85889,2298],{"class":2235},[2225,85891,85892],{"class":2763}," \u002F\u002F the app-client id == the ID token's `aud`\n",[2225,85894,85895,85898,85900,85902,85904,85907,85909,85911,85913,85915,85917,85919],{"class":2227,"line":2830},[2225,85896,85897],{"class":2307},"  scopes",[2225,85899,2311],{"class":2235},[2225,85901,8667],{"class":2239},[2225,85903,2805],{"class":2235},[2225,85905,85906],{"class":2252},"openid",[2225,85908,2805],{"class":2235},[2225,85910,2298],{"class":2235},[2225,85912,2249],{"class":2235},[2225,85914,81022],{"class":2252},[2225,85916,2805],{"class":2235},[2225,85918,8803],{"class":2239},[2225,85920,1707],{"class":2235},[2225,85922,85923,85926,85928,85930,85932,85934],{"class":2227,"line":2861},[2225,85924,85925],{"class":2307},"  keyConnectorUrl",[2225,85927,2311],{"class":2235},[2225,85929,2249],{"class":2235},[2225,85931,44834],{"class":2252},[2225,85933,2805],{"class":2235},[2225,85935,1707],{"class":2235},[2225,85937,85938],{"class":2227,"line":4246},[2225,85939,7351],{"class":2235},[937,85941,85942,85945,85946,85948],{},[950,85943,85944],{},"Enroll once",", from the tier-1-unlocked keyring, with a fresh Cognito ID token. This derives the device secret, XOR-splits the keyring payload, and PUTs the encrypted ",[941,85947,38374],{}," to your connector:",[2217,85950,85952],{"className":2219,"code":85951,"language":2221,"meta":1037,"style":1037},"\u002F\u002F `idToken` came from the completed Cognito passwordless sign-in.\nconst enrollment = await enrollOidc(await db.team.getKeyring('books'), 'books', config, idToken)\n\u002F\u002F Persist `enrollment` (it's opaque metadata) so later sessions can find the `sub`.\n",[941,85953,85954,85959,86011],{"__ignoreMap":1037},[2225,85955,85956],{"class":2227,"line":2228},[2225,85957,85958],{"class":2763},"\u002F\u002F `idToken` came from the completed Cognito passwordless sign-in.\n",[2225,85960,85961,85963,85965,85967,85969,85971,85973,85975,85977,85979,85981,85983,85985,85987,85989,85991,85993,85995,85997,85999,86001,86003,86005,86007,86009],{"class":2227,"line":1041},[2225,85962,2775],{"class":2774},[2225,85964,46662],{"class":2239},[2225,85966,2781],{"class":2235},[2225,85968,2784],{"class":2231},[2225,85970,85798],{"class":2291},[2225,85972,2789],{"class":2239},[2225,85974,6500],{"class":2231},[2225,85976,2842],{"class":2239},[2225,85978,1520],{"class":2235},[2225,85980,868],{"class":2239},[2225,85982,1520],{"class":2235},[2225,85984,9953],{"class":2291},[2225,85986,2789],{"class":2239},[2225,85988,2805],{"class":2235},[2225,85990,65761],{"class":2252},[2225,85992,2805],{"class":2235},[2225,85994,1381],{"class":2239},[2225,85996,2298],{"class":2235},[2225,85998,2249],{"class":2235},[2225,86000,65761],{"class":2252},[2225,86002,2805],{"class":2235},[2225,86004,2298],{"class":2235},[2225,86006,36491],{"class":2239},[2225,86008,2298],{"class":2235},[2225,86010,44863],{"class":2239},[2225,86012,86013],{"class":2227,"line":1038},[2225,86014,86015],{"class":2763},"\u002F\u002F Persist `enrollment` (it's opaque metadata) so later sessions can find the `sub`.\n",[937,86017,86018,86021,86022,86024,86025,9750,86027,86029,86030,86032],{},[950,86019,86020],{},"Unlock on every later session"," with a ",[1092,86023,70371],{}," Cognito login — no secret typed. Feed the returned keyring straight into ",[941,86026,12780],{},[941,86028,9953],{},", the cold-start seam every ",[941,86031,1787],{}," method satisfies:",[2217,86034,86036],{"className":2219,"code":86035,"language":2221,"meta":1037,"style":1037},"const unlocked = await unlockOidc(enrollment, config, freshIdToken)\n\nconst db = await createNoydb({\n  store: toMemory(), \u002F\u002F the same store that holds the vault\n  user: 'alice',\n  getKeyring: async () => unlocked, \u002F\u002F no `secret` — passwordless from here on\n})\nconst books = await db.openVault('books')\n\u002F\u002F books.collection(...).get(...) \u002F .put(...) all work — this is a real session.\n",[941,86037,86038,86061,86065,86081,86096,86110,86130,86136,86163],{"__ignoreMap":1037},[2225,86039,86040,86042,86044,86046,86048,86050,86052,86054,86056,86058],{"class":2227,"line":2228},[2225,86041,2775],{"class":2774},[2225,86043,77619],{"class":2239},[2225,86045,2781],{"class":2235},[2225,86047,2784],{"class":2231},[2225,86049,44789],{"class":2291},[2225,86051,44854],{"class":2239},[2225,86053,2298],{"class":2235},[2225,86055,36491],{"class":2239},[2225,86057,2298],{"class":2235},[2225,86059,86060],{"class":2239}," freshIdToken)\n",[2225,86062,86063],{"class":2227,"line":1041},[2225,86064,2285],{"emptyLinePlaceholder":2284},[2225,86066,86067,86069,86071,86073,86075,86077,86079],{"class":2227,"line":1038},[2225,86068,2775],{"class":2774},[2225,86070,2778],{"class":2239},[2225,86072,2781],{"class":2235},[2225,86074,2784],{"class":2231},[2225,86076,2744],{"class":2291},[2225,86078,2789],{"class":2239},[2225,86080,4200],{"class":2235},[2225,86082,86083,86085,86087,86089,86091,86093],{"class":2227,"line":2288},[2225,86084,15608],{"class":2307},[2225,86086,2311],{"class":2235},[2225,86088,68576],{"class":2291},[2225,86090,4228],{"class":2239},[2225,86092,2298],{"class":2235},[2225,86094,86095],{"class":2763}," \u002F\u002F the same store that holds the vault\n",[2225,86097,86098,86100,86102,86104,86106,86108],{"class":2227,"line":2304},[2225,86099,4205],{"class":2307},[2225,86101,2311],{"class":2235},[2225,86103,2249],{"class":2235},[2225,86105,6408],{"class":2252},[2225,86107,2805],{"class":2235},[2225,86109,1707],{"class":2235},[2225,86111,86112,86114,86116,86118,86120,86122,86125,86127],{"class":2227,"line":2356},[2225,86113,46719],{"class":2291},[2225,86115,2311],{"class":2235},[2225,86117,38717],{"class":2774},[2225,86119,13130],{"class":2235},[2225,86121,8116],{"class":2774},[2225,86123,86124],{"class":2239}," unlocked",[2225,86126,2298],{"class":2235},[2225,86128,86129],{"class":2763}," \u002F\u002F no `secret` — passwordless from here on\n",[2225,86131,86132,86134],{"class":2227,"line":2830},[2225,86133,2359],{"class":2235},[2225,86135,2362],{"class":2239},[2225,86137,86138,86140,86143,86145,86147,86149,86151,86153,86155,86157,86159,86161],{"class":2227,"line":2861},[2225,86139,2775],{"class":2774},[2225,86141,86142],{"class":2239}," books ",[2225,86144,2781],{"class":2235},[2225,86146,2784],{"class":2231},[2225,86148,2842],{"class":2239},[2225,86150,1520],{"class":2235},[2225,86152,2847],{"class":2291},[2225,86154,2789],{"class":2239},[2225,86156,2805],{"class":2235},[2225,86158,65761],{"class":2252},[2225,86160,2805],{"class":2235},[2225,86162,2362],{"class":2239},[2225,86164,86165],{"class":2227,"line":4246},[2225,86166,86167],{"class":2763},"\u002F\u002F books.collection(...).get(...) \u002F .put(...) all work — this is a real session.\n",[1022,86169,71414],{"id":18051},[937,86171,86172,86173,968,86175,7356,86178,86182,86183,86185,86186,86188],{},"The showcase proves the property, not just the happy path: after ",[941,86174,44625],{},[941,86176,86177],{},"unlocked.kek",[950,86179,86180],{},[941,86181,6651],{}," — the tier-2 signature — yet the session reads a record written under tier 1 and writes a new one. It also asserts the connector received only opaque ciphertext, and that a wrong-issuer, wrong-",[941,86184,84717],{},", expired, or wrong-",[941,86187,44608],{}," token is rejected:",[2217,86190,86192],{"className":2219,"code":86191,"language":2221,"meta":1037,"style":1037},"const unlocked = await unlockOidc(enrollment, config, unlockToken)\nexpect(unlocked.kek).toBeNull()          \u002F\u002F tier-2: no KEK, ever\nexpect(await invoices.get('inv-1')).toBeTruthy() \u002F\u002F …but reads\u002Fwrites work\n",[941,86193,86194,86217,86240],{"__ignoreMap":1037},[2225,86195,86196,86198,86200,86202,86204,86206,86208,86210,86212,86214],{"class":2227,"line":2228},[2225,86197,2775],{"class":2774},[2225,86199,77619],{"class":2239},[2225,86201,2781],{"class":2235},[2225,86203,2784],{"class":2231},[2225,86205,44789],{"class":2291},[2225,86207,44854],{"class":2239},[2225,86209,2298],{"class":2235},[2225,86211,36491],{"class":2239},[2225,86213,2298],{"class":2235},[2225,86215,86216],{"class":2239}," unlockToken)\n",[2225,86218,86219,86222,86225,86227,86230,86232,86235,86237],{"class":2227,"line":1041},[2225,86220,86221],{"class":2291},"expect",[2225,86223,86224],{"class":2239},"(unlocked",[2225,86226,1520],{"class":2235},[2225,86228,86229],{"class":2239},"kek)",[2225,86231,1520],{"class":2235},[2225,86233,86234],{"class":2291},"toBeNull",[2225,86236,39855],{"class":2239},[2225,86238,86239],{"class":2763},"\u002F\u002F tier-2: no KEK, ever\n",[2225,86241,86242,86244,86246,86248,86250,86252,86254,86256,86258,86260,86262,86264,86266,86269,86271],{"class":2227,"line":1038},[2225,86243,86221],{"class":2291},[2225,86245,2789],{"class":2239},[2225,86247,6500],{"class":2231},[2225,86249,6503],{"class":2239},[2225,86251,1520],{"class":2235},[2225,86253,3158],{"class":2291},[2225,86255,2789],{"class":2239},[2225,86257,2805],{"class":2235},[2225,86259,6514],{"class":2252},[2225,86261,2805],{"class":2235},[2225,86263,8454],{"class":2239},[2225,86265,1520],{"class":2235},[2225,86267,86268],{"class":2291},"toBeTruthy",[2225,86270,19132],{"class":2239},[2225,86272,86273],{"class":2763},"\u002F\u002F …but reads\u002Fwrites work\n",[1022,86275,84820],{"id":86276},"limits-alternative",[1111,86278,86279,86294,86321],{},[1114,86280,86281,86284,86285,86287,86288,86290,86291,1520],{},[950,86282,86283],{},"It's a tier-2 unlock, with tier-2 limits."," The OIDC session yields DEKs with ",[941,86286,17129],{},": it can read and write, but cannot ",[941,86289,10493],{},", rotate the secret, enroll another authenticator, or open a second vault — those need a real tier-1 unlock. See ",[1007,86292,44739],{"href":86293},"\u002Fcore\u002Fnoydb\u002Fauthentication#can-a-method-serve-a-different-tier",[1114,86295,86296,15920,86299,86301,86302,86304,86305,86308,86309,968,86312,86314,86315,86317,86318,86320],{},[950,86297,86298],{},"Lose the device half and Cognito alone can't get you back in.",[941,86300,84761],{}," never leaves the device; a wiped browser \u002F new device makes ",[941,86303,44625],{}," fail with ",[941,86306,86307],{},"OidcDeviceSecretNotFoundError"," (or a 404 from the connector). Recovery is a fresh tier-1 re-enrollment — a ",[1007,86310,86311],{"href":322},"paper recovery code",[1007,86313,44750],{"href":326}," share, or a custodian invite — then a new ",[941,86316,44614],{},". Cognito re-proves ",[1092,86319,18128],{},", not your device secret.",[1114,86322,86323,86326,86327,86331,86332,86334,86335,86337,86338,86341],{},[950,86324,86325],{},"Want NO server at all?"," If running even a tiny key-connector is unwelcome, ",[1007,86328,86329],{"href":338},[941,86330,15944],{}," gives a similar login-with-your-device UX using a platform passkey (Touch ID \u002F Windows Hello) with ",[950,86333,11313],{}," key-connector and ",[950,86336,11313],{}," OIDC provider — the wrapping key is derived from the authenticator's PRF output on-device. Choose OIDC when you specifically want ",[1092,86339,86340],{},"federated"," identity (a shared corporate\u002Fsocial login) or central account management; choose WebAuthn when you want the least infrastructure.",[1022,86343,2621],{"id":2620},[1111,86345,86346,86353,86360,86365],{},[1114,86347,86348,86352],{},[1007,86349,86350],{"href":310},[941,86351,15969],{}," — the split-key model and the shipped provider configs, in depth.",[1114,86354,86355,86359],{},[1007,86356,86357],{"href":338},[941,86358,15944],{}," — the no-server passwordless alternative.",[1114,86361,86362,86364],{},[1007,86363,589],{"href":590}," — how OIDC stacks with secret, passkey, session policy, and recovery.",[1114,86366,86367,86369],{},[1007,86368,844],{"href":845}," — the three-tier ladder and what each tier may do.",[2645,86371,86372],{},"html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sHdIc, html code.shiki .sHdIc{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#EEFFFF;--shiki-default-font-style:italic;--shiki-dark:#BABED8;--shiki-dark-font-style:italic}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}html pre.shiki code .sfNiH, html code.shiki .sfNiH{--shiki-light:#FF5370;--shiki-default:#FF9CAC;--shiki-dark:#FF9CAC}",{"title":1037,"searchDepth":1038,"depth":1038,"links":86374},[86375,86376,86377,86378,86379,86380,86381,86382,86383,86384],{"id":84661,"depth":1041,"text":84662},{"id":70092,"depth":1041,"text":70093},{"id":58275,"depth":1041,"text":58276},{"id":58393,"depth":1041,"text":58394},{"id":85068,"depth":1041,"text":85069},{"id":85143,"depth":1041,"text":85144},{"id":85771,"depth":1041,"text":85772},{"id":18051,"depth":1041,"text":71414},{"id":86276,"depth":1041,"text":84820},{"id":2620,"depth":1041,"text":2621},"Passwordless federated tier-2 unlock with the least infrastructure — AWS Cognito, one tiny key-connector, and the on-oidc split-key client.",{},{"title":555,"description":86385},"x5LNGZ7QOMkFGucjn36Qj1gNXNa6U9LrBH40OdTCZag",{"id":86390,"title":559,"access":12,"apiToc":929,"body":86391,"category":1042,"description":87891,"extension":1044,"meta":87892,"navigation":1042,"nest":1042,"path":560,"seo":87893,"sinceVersion":1042,"status":1049,"stem":561,"subtitle":1042,"tier":1050,"tocExternal":1042,"__hash__":87894},"docs\u002Frecipes\u002Fpersonal-notebook.md",{"type":931,"value":86392,"toc":87882},[86393,86396,86406,86415,86418,86422,86456,86460,86468,86551,86567,86570,86600,86604,87217,87223,87392,87395,87399,87410,87505,87510,87514,87520,87800,87804,87857,87859,87879],[934,86394,559],{"id":86395},"personal-notebook",[1061,86397,86398],{},[937,86399,86400,86402,86403,86405],{},[950,86401,1500],{}," single user, single device, local-only — the simplest possible noy-db app: one user's encrypted notes, no server.\n",[950,86404,58222],{}," the always-on core only, about 6,500 lines of library code — no opt-in services (no history, blobs, sync, or aggregates).",[937,86407,58245,86408,58253],{},[1007,86409,86412],{"href":86410,"rel":86411},"https:\u002F\u002Fgithub.com\u002Fnoy-db\u002Fdocs\u002Fblob\u002Fmain\u002Fshowcases\u002Fsrc\u002Frecipe-personal-notebook.recipe.test.ts",[3371],[941,86413,86414],{},"showcases\u002Fsrc\u002Frecipe-personal-notebook.recipe.test.ts",[937,86416,86417],{},"noy-db is an encrypted document store: your records are encrypted inside your app's process before they reach any storage backend, so the backend only ever holds ciphertext. This recipe uses nothing but the core — one store adapter, one vault (an encrypted namespace of collections), one typed collection — to build a personal notebook whose contents stay unreadable to anything that can inspect the browser's IndexedDB.",[1022,86419,86421],{"id":86420},"what-you-get","What you get",[1111,86423,86424,86435,86447,86453],{},[1114,86425,86426,86427,86429,86430,17904,86433,1381],{},"Encrypted document storage in the browser (IndexedDB via ",[941,86428,9573],{},") or on disk (",[941,86431,86432],{},"toFile()",[941,86434,9551],{},[1114,86436,86437,86438,1261,86440,1261,86442,2098,86444,86446],{},"Typed collections, basic queries (",[941,86439,3016],{},[941,86441,3019],{},[941,86443,3022],{},[941,86445,7906],{}," for streaming reads",[1114,86448,86449,86450],{},"A single-owner keyring: your secret derives a key-encryption key (KEK, via PBKDF2-SHA256), which wraps the data-encryption keys (DEKs) that encrypt your records. One user, one role (owner), no key ceremony to manage — see ",[1007,86451,6898],{"href":86452},"..\u002Fcore\u002F06-permissions-and-keyring",[1114,86454,86455],{},"Optional schema validation via any Standard Schema library (Zod, Valibot, ArkType, …)",[1022,86457,86459],{"id":86458},"what-you-dont-get-until-you-opt-in","What you don't get (until you opt in)",[937,86461,86462,86463,86465,86466,6255],{},"Everything beyond the core is a service you opt into by passing its factory as an option to ",[941,86464,2134],{}," (for example ",[941,86467,84477],{},[1338,86469,86470,86480],{},[1341,86471,86472],{},[1344,86473,86474,86477],{},[1347,86475,86476],{},"You don't have",[1347,86478,86479],{},"Opt in via",[1357,86481,86482,86493,86506,86517,86528,86540],{},[1344,86483,86484,86487],{},[1362,86485,86486],{},"Record version history",[1362,86488,86489,17904,86491],{},[941,86490,6719],{},[941,86492,4151],{},[1344,86494,86495,86500],{},[1362,86496,86497,86498],{},"Aggregates \u002F ",[941,86499,27496],{},[1362,86501,86502,17904,86504],{},[941,86503,21926],{},[941,86505,27257],{},[1344,86507,86508,86511],{},[1362,86509,86510],{},"File \u002F blob attachments",[1362,86512,86513,17904,86515],{},[941,86514,6775],{},[941,86516,58494],{},[1344,86518,86519,86522],{},[1362,86520,86521],{},"Multi-locale records",[1362,86523,86524,17904,86526],{},[941,86525,6739],{},[941,86527,58514],{},[1344,86529,86530,86533],{},[1362,86531,86532],{},"Multi-user grant \u002F revoke",[1362,86534,86535,17904,86537,86539],{},[941,86536,3197],{},[941,86538,4680],{}," — the built-in single-owner keyring is enough for solo apps",[1344,86541,86542,86545],{},[1362,86543,86544],{},"Replication to a remote peer",[1362,86546,86547,17904,86549],{},[941,86548,4676],{},[941,86550,4672],{},[937,86552,86553,86554,86557,86558,15706,86561,86563,86564,1520],{},"One thing you ",[1092,86555,86556],{},"do"," get without opting in: joins. ",[941,86559,86560],{},"query().join('clientId', { as: 'client' })",[941,86562,62282],{},"-declared foreign key is part of the always-on core — see ",[1007,86565,715],{"href":86566},"..\u002Fservices\u002Fjoins",[937,86568,86569],{},"Features you have not opted into fail loudly, with a pointer at the subpath to enable:",[2217,86571,86573],{"className":2219,"code":86572,"language":2221,"meta":1037,"style":1037},"await notes.history('note-1')\n\u002F\u002F → throws — the error message points at withHistory() from '@noy-db\u002Fhub\u002Fhistory'\n",[941,86574,86575,86595],{"__ignoreMap":1037},[2225,86576,86577,86579,86581,86583,86585,86587,86589,86591,86593],{"class":2227,"line":2228},[2225,86578,6500],{"class":2231},[2225,86580,42817],{"class":2239},[2225,86582,1520],{"class":2235},[2225,86584,697],{"class":2291},[2225,86586,2789],{"class":2239},[2225,86588,2805],{"class":2235},[2225,86590,42829],{"class":2252},[2225,86592,2805],{"class":2235},[2225,86594,2362],{"class":2239},[2225,86596,86597],{"class":2227,"line":1041},[2225,86598,86599],{"class":2763},"\u002F\u002F → throws — the error message points at withHistory() from '@noy-db\u002Fhub\u002Fhistory'\n",[1022,86601,86603],{"id":86602},"the-whole-app","The whole app",[2217,86605,86607],{"className":2219,"code":86606,"language":2221,"meta":1037,"style":1037},"import { createNoydb } from '@noy-db\u002Fhub'\nimport { toBrowserIdb } from '@noy-db\u002Fto-browser-idb'\n\ninterface Note {\n  id: string\n  title: string\n  body: string\n  createdAt: string\n}\n\nconst db = await createNoydb({\n  store: toBrowserIdb(),\n  user: 'me',\n  secret: 'correct-horse-battery-staple', \u002F\u002F user-supplied secret\n})\n\nconst vault = await db.openVault('notebook')\nconst notes = vault.collection\u003CNote>('notes')\n\n\u002F\u002F Write\nawait notes.put('note-1', {\n  id: 'note-1',\n  title: 'Groceries',\n  body: 'eggs, milk',\n  createdAt: '2026-07-06T09:00:00.000Z',\n})\nawait notes.put('note-2', {\n  id: 'note-2',\n  title: 'Meeting notes',\n  body: 'ship the notebook demo',\n  createdAt: '2026-07-07T14:30:00.000Z',\n})\n\n\u002F\u002F Read by id\nconst one = await notes.get('note-1')\n\u002F\u002F → { id: 'note-1', title: 'Groceries', body: 'eggs, milk', createdAt: '2026-07-06T09:00:00.000Z' }\n\n\u002F\u002F Query — newest first. query() runs synchronously over the\n\u002F\u002F decrypted in-memory cache, so no await is needed.\nconst recent = notes\n  .query()\n  .orderBy('createdAt', 'desc')\n  .limit(10)\n  .toArray()\n\n\u002F\u002F Stream — reads records without loading the whole collection into memory\nfor await (const n of notes.scan()) {\n  console.log(n.title)\n}\n\u002F\u002F → Groceries\n\u002F\u002F → Meeting notes\n\n\u002F\u002F Lifecycle — drops the KEK and DEKs from memory\ndb.close()\n",[941,86608,86609,86627,86645,86649,86658,86666,86675,86684,86692,86696,86700,86716,86728,86742,86759,86765,86769,86796,86827,86831,86836,86858,86872,86887,86902,86917,86923,86946,86960,86975,86990,87005,87011,87015,87020,87047,87052,87056,87061,87066,87078,87086,87111,87124,87132,87136,87141,87165,87184,87188,87193,87198,87202,87207],{"__ignoreMap":1037},[2225,86610,86611,86613,86615,86617,86619,86621,86623,86625],{"class":2227,"line":2228},[2225,86612,2232],{"class":2231},[2225,86614,2236],{"class":2235},[2225,86616,2744],{"class":2239},[2225,86618,2243],{"class":2235},[2225,86620,2246],{"class":2231},[2225,86622,2249],{"class":2235},[2225,86624,1086],{"class":2252},[2225,86626,2256],{"class":2235},[2225,86628,86629,86631,86633,86635,86637,86639,86641,86643],{"class":2227,"line":1041},[2225,86630,2232],{"class":2231},[2225,86632,2236],{"class":2235},[2225,86634,34763],{"class":2239},[2225,86636,2243],{"class":2235},[2225,86638,2246],{"class":2231},[2225,86640,2249],{"class":2235},[2225,86642,9573],{"class":2252},[2225,86644,2256],{"class":2235},[2225,86646,86647],{"class":2227,"line":1038},[2225,86648,2285],{"emptyLinePlaceholder":2284},[2225,86650,86651,86653,86656],{"class":2227,"line":2288},[2225,86652,6994],{"class":2774},[2225,86654,86655],{"class":6342}," Note",[2225,86657,2301],{"class":2235},[2225,86659,86660,86662,86664],{"class":2227,"line":2304},[2225,86661,28371],{"class":2307},[2225,86663,2311],{"class":2235},[2225,86665,7014],{"class":6342},[2225,86667,86668,86671,86673],{"class":2227,"line":2356},[2225,86669,86670],{"class":2307},"  title",[2225,86672,2311],{"class":2235},[2225,86674,7014],{"class":6342},[2225,86676,86677,86680,86682],{"class":2227,"line":2830},[2225,86678,86679],{"class":2307},"  body",[2225,86681,2311],{"class":2235},[2225,86683,7014],{"class":6342},[2225,86685,86686,86688,86690],{"class":2227,"line":2861},[2225,86687,82711],{"class":2307},[2225,86689,2311],{"class":2235},[2225,86691,7014],{"class":6342},[2225,86693,86694],{"class":2227,"line":4246},[2225,86695,7351],{"class":2235},[2225,86697,86698],{"class":2227,"line":4253},[2225,86699,2285],{"emptyLinePlaceholder":2284},[2225,86701,86702,86704,86706,86708,86710,86712,86714],{"class":2227,"line":4259},[2225,86703,2775],{"class":2774},[2225,86705,2778],{"class":2239},[2225,86707,2781],{"class":2235},[2225,86709,2784],{"class":2231},[2225,86711,2744],{"class":2291},[2225,86713,2789],{"class":2239},[2225,86715,4200],{"class":2235},[2225,86717,86718,86720,86722,86724,86726],{"class":2227,"line":6548},[2225,86719,15608],{"class":2307},[2225,86721,2311],{"class":2235},[2225,86723,34763],{"class":2291},[2225,86725,4228],{"class":2239},[2225,86727,1707],{"class":2235},[2225,86729,86730,86732,86734,86736,86738,86740],{"class":2227,"line":6576},[2225,86731,4205],{"class":2307},[2225,86733,2311],{"class":2235},[2225,86735,2249],{"class":2235},[2225,86737,4212],{"class":2252},[2225,86739,2805],{"class":2235},[2225,86741,1707],{"class":2235},[2225,86743,86744,86746,86748,86750,86752,86754,86756],{"class":2227,"line":6582},[2225,86745,10423],{"class":2307},[2225,86747,2311],{"class":2235},[2225,86749,2249],{"class":2235},[2225,86751,10202],{"class":2252},[2225,86753,2805],{"class":2235},[2225,86755,2298],{"class":2235},[2225,86757,86758],{"class":2763}," \u002F\u002F user-supplied secret\n",[2225,86760,86761,86763],{"class":2227,"line":6587},[2225,86762,2359],{"class":2235},[2225,86764,2362],{"class":2239},[2225,86766,86767],{"class":2227,"line":7174},[2225,86768,2285],{"emptyLinePlaceholder":2284},[2225,86770,86771,86773,86775,86777,86779,86781,86783,86785,86787,86789,86792,86794],{"class":2227,"line":7180},[2225,86772,2775],{"class":2774},[2225,86774,2835],{"class":2239},[2225,86776,2781],{"class":2235},[2225,86778,2784],{"class":2231},[2225,86780,2842],{"class":2239},[2225,86782,1520],{"class":2235},[2225,86784,2847],{"class":2291},[2225,86786,2789],{"class":2239},[2225,86788,2805],{"class":2235},[2225,86790,86791],{"class":2252},"notebook",[2225,86793,2805],{"class":2235},[2225,86795,2362],{"class":2239},[2225,86797,86798,86800,86802,86804,86806,86808,86810,86812,86815,86817,86819,86821,86823,86825],{"class":2227,"line":7220},[2225,86799,2775],{"class":2774},[2225,86801,42762],{"class":2239},[2225,86803,2781],{"class":2235},[2225,86805,6466],{"class":2239},[2225,86807,1520],{"class":2235},[2225,86809,6471],{"class":2291},[2225,86811,6474],{"class":2235},[2225,86813,86814],{"class":6342},"Note",[2225,86816,6480],{"class":2235},[2225,86818,2789],{"class":2239},[2225,86820,2805],{"class":2235},[2225,86822,2854],{"class":2252},[2225,86824,2805],{"class":2235},[2225,86826,2362],{"class":2239},[2225,86828,86829],{"class":2227,"line":7225},[2225,86830,2285],{"emptyLinePlaceholder":2284},[2225,86832,86833],{"class":2227,"line":7231},[2225,86834,86835],{"class":2763},"\u002F\u002F Write\n",[2225,86837,86838,86840,86842,86844,86846,86848,86850,86852,86854,86856],{"class":2227,"line":7267},[2225,86839,6500],{"class":2231},[2225,86841,42817],{"class":2239},[2225,86843,1520],{"class":2235},[2225,86845,3161],{"class":2291},[2225,86847,2789],{"class":2239},[2225,86849,2805],{"class":2235},[2225,86851,42829],{"class":2252},[2225,86853,2805],{"class":2235},[2225,86855,2298],{"class":2235},[2225,86857,2301],{"class":2235},[2225,86859,86860,86862,86864,86866,86868,86870],{"class":2227,"line":7272},[2225,86861,28371],{"class":2307},[2225,86863,2311],{"class":2235},[2225,86865,2249],{"class":2235},[2225,86867,42829],{"class":2252},[2225,86869,2805],{"class":2235},[2225,86871,1707],{"class":2235},[2225,86873,86874,86876,86878,86880,86883,86885],{"class":2227,"line":7278},[2225,86875,86670],{"class":2307},[2225,86877,2311],{"class":2235},[2225,86879,2249],{"class":2235},[2225,86881,86882],{"class":2252},"Groceries",[2225,86884,2805],{"class":2235},[2225,86886,1707],{"class":2235},[2225,86888,86889,86891,86893,86895,86898,86900],{"class":2227,"line":7303},[2225,86890,86679],{"class":2307},[2225,86892,2311],{"class":2235},[2225,86894,2249],{"class":2235},[2225,86896,86897],{"class":2252},"eggs, milk",[2225,86899,2805],{"class":2235},[2225,86901,1707],{"class":2235},[2225,86903,86904,86906,86908,86910,86913,86915],{"class":2227,"line":7308},[2225,86905,82711],{"class":2307},[2225,86907,2311],{"class":2235},[2225,86909,2249],{"class":2235},[2225,86911,86912],{"class":2252},"2026-07-06T09:00:00.000Z",[2225,86914,2805],{"class":2235},[2225,86916,1707],{"class":2235},[2225,86918,86919,86921],{"class":2227,"line":7314},[2225,86920,2359],{"class":2235},[2225,86922,2362],{"class":2239},[2225,86924,86925,86927,86929,86931,86933,86935,86937,86940,86942,86944],{"class":2227,"line":7348},[2225,86926,6500],{"class":2231},[2225,86928,42817],{"class":2239},[2225,86930,1520],{"class":2235},[2225,86932,3161],{"class":2291},[2225,86934,2789],{"class":2239},[2225,86936,2805],{"class":2235},[2225,86938,86939],{"class":2252},"note-2",[2225,86941,2805],{"class":2235},[2225,86943,2298],{"class":2235},[2225,86945,2301],{"class":2235},[2225,86947,86948,86950,86952,86954,86956,86958],{"class":2227,"line":8613},[2225,86949,28371],{"class":2307},[2225,86951,2311],{"class":2235},[2225,86953,2249],{"class":2235},[2225,86955,86939],{"class":2252},[2225,86957,2805],{"class":2235},[2225,86959,1707],{"class":2235},[2225,86961,86962,86964,86966,86968,86971,86973],{"class":2227,"line":8633},[2225,86963,86670],{"class":2307},[2225,86965,2311],{"class":2235},[2225,86967,2249],{"class":2235},[2225,86969,86970],{"class":2252},"Meeting notes",[2225,86972,2805],{"class":2235},[2225,86974,1707],{"class":2235},[2225,86976,86977,86979,86981,86983,86986,86988],{"class":2227,"line":8662},[2225,86978,86679],{"class":2307},[2225,86980,2311],{"class":2235},[2225,86982,2249],{"class":2235},[2225,86984,86985],{"class":2252},"ship the notebook demo",[2225,86987,2805],{"class":2235},[2225,86989,1707],{"class":2235},[2225,86991,86992,86994,86996,86998,87001,87003],{"class":2227,"line":8747},[2225,86993,82711],{"class":2307},[2225,86995,2311],{"class":2235},[2225,86997,2249],{"class":2235},[2225,86999,87000],{"class":2252},"2026-07-07T14:30:00.000Z",[2225,87002,2805],{"class":2235},[2225,87004,1707],{"class":2235},[2225,87006,87007,87009],{"class":2227,"line":8752},[2225,87008,2359],{"class":2235},[2225,87010,2362],{"class":2239},[2225,87012,87013],{"class":2227,"line":8768},[2225,87014,2285],{"emptyLinePlaceholder":2284},[2225,87016,87017],{"class":2227,"line":8785},[2225,87018,87019],{"class":2763},"\u002F\u002F Read by id\n",[2225,87021,87022,87024,87027,87029,87031,87033,87035,87037,87039,87041,87043,87045],{"class":2227,"line":8815},[2225,87023,2775],{"class":2774},[2225,87025,87026],{"class":2239}," one ",[2225,87028,2781],{"class":2235},[2225,87030,2784],{"class":2231},[2225,87032,42817],{"class":2239},[2225,87034,1520],{"class":2235},[2225,87036,3158],{"class":2291},[2225,87038,2789],{"class":2239},[2225,87040,2805],{"class":2235},[2225,87042,42829],{"class":2252},[2225,87044,2805],{"class":2235},[2225,87046,2362],{"class":2239},[2225,87048,87049],{"class":2227,"line":8854},[2225,87050,87051],{"class":2763},"\u002F\u002F → { id: 'note-1', title: 'Groceries', body: 'eggs, milk', createdAt: '2026-07-06T09:00:00.000Z' }\n",[2225,87053,87054],{"class":2227,"line":8874},[2225,87055,2285],{"emptyLinePlaceholder":2284},[2225,87057,87058],{"class":2227,"line":8911},[2225,87059,87060],{"class":2763},"\u002F\u002F Query — newest first. query() runs synchronously over the\n",[2225,87062,87063],{"class":2227,"line":8916},[2225,87064,87065],{"class":2763},"\u002F\u002F decrypted in-memory cache, so no await is needed.\n",[2225,87067,87068,87070,87073,87075],{"class":2227,"line":8924},[2225,87069,2775],{"class":2774},[2225,87071,87072],{"class":2239}," recent ",[2225,87074,2781],{"class":2235},[2225,87076,87077],{"class":2239}," notes\n",[2225,87079,87080,87082,87084],{"class":2227,"line":8929},[2225,87081,26235],{"class":2235},[2225,87083,5286],{"class":2291},[2225,87085,8146],{"class":2239},[2225,87087,87088,87090,87092,87094,87096,87099,87101,87103,87105,87107,87109],{"class":2227,"line":8949},[2225,87089,26235],{"class":2235},[2225,87091,3019],{"class":2291},[2225,87093,2789],{"class":2239},[2225,87095,2805],{"class":2235},[2225,87097,87098],{"class":2252},"createdAt",[2225,87100,2805],{"class":2235},[2225,87102,2298],{"class":2235},[2225,87104,2249],{"class":2235},[2225,87106,26314],{"class":2252},[2225,87108,2805],{"class":2235},[2225,87110,2362],{"class":2239},[2225,87112,87113,87115,87117,87119,87122],{"class":2227,"line":8955},[2225,87114,26235],{"class":2235},[2225,87116,3022],{"class":2291},[2225,87118,2789],{"class":2239},[2225,87120,87121],{"class":6527},"10",[2225,87123,2362],{"class":2239},[2225,87125,87126,87128,87130],{"class":2227,"line":8983},[2225,87127,26235],{"class":2235},[2225,87129,3230],{"class":2291},[2225,87131,8146],{"class":2239},[2225,87133,87134],{"class":2227,"line":9016},[2225,87135,2285],{"emptyLinePlaceholder":2284},[2225,87137,87138],{"class":2227,"line":9063},[2225,87139,87140],{"class":2763},"\u002F\u002F Stream — reads records without loading the whole collection into memory\n",[2225,87142,87143,87145,87147,87149,87151,87153,87155,87157,87159,87161,87163],{"class":2227,"line":9068},[2225,87144,4646],{"class":2231},[2225,87146,2784],{"class":2231},[2225,87148,953],{"class":2239},[2225,87150,2775],{"class":2774},[2225,87152,26817],{"class":2239},[2225,87154,27066],{"class":2235},[2225,87156,42817],{"class":2239},[2225,87158,1520],{"class":2235},[2225,87160,3239],{"class":2291},[2225,87162,35140],{"class":2239},[2225,87164,4200],{"class":2235},[2225,87166,87167,87169,87171,87173,87175,87177,87179,87182],{"class":2227,"line":9106},[2225,87168,27095],{"class":2239},[2225,87170,1520],{"class":2235},[2225,87172,27100],{"class":2291},[2225,87174,2789],{"class":2307},[2225,87176,26634],{"class":2239},[2225,87178,1520],{"class":2235},[2225,87180,87181],{"class":2239},"title",[2225,87183,2362],{"class":2307},[2225,87185,87186],{"class":2227,"line":9143},[2225,87187,7351],{"class":2235},[2225,87189,87190],{"class":2227,"line":9177},[2225,87191,87192],{"class":2763},"\u002F\u002F → Groceries\n",[2225,87194,87195],{"class":2227,"line":9183},[2225,87196,87197],{"class":2763},"\u002F\u002F → Meeting notes\n",[2225,87199,87200],{"class":2227,"line":9188},[2225,87201,2285],{"emptyLinePlaceholder":2284},[2225,87203,87204],{"class":2227,"line":9193},[2225,87205,87206],{"class":2763},"\u002F\u002F Lifecycle — drops the KEK and DEKs from memory\n",[2225,87208,87209,87211,87213,87215],{"class":2227,"line":9198},[2225,87210,6590],{"class":2239},[2225,87212,1520],{"class":2235},[2225,87214,6595],{"class":2291},[2225,87216,8146],{"class":2239},[937,87218,960,87219,87222],{},[941,87220,87221],{},"recent"," query returns the decrypted, typed records, newest first:",[2217,87224,87226],{"className":6604,"code":87225,"language":141,"meta":1037,"style":1037},"[\n  {\n    \"id\": \"note-2\",\n    \"title\": \"Meeting notes\",\n    \"body\": \"ship the notebook demo\",\n    \"createdAt\": \"2026-07-07T14:30:00.000Z\"\n  },\n  {\n    \"id\": \"note-1\",\n    \"title\": \"Groceries\",\n    \"body\": \"eggs, milk\",\n    \"createdAt\": \"2026-07-06T09:00:00.000Z\"\n  }\n]\n",[941,87227,87228,87232,87236,87254,87272,87290,87306,87310,87314,87332,87350,87368,87384,87388],{"__ignoreMap":1037},[2225,87229,87230],{"class":2227,"line":2228},[2225,87231,26349],{"class":2235},[2225,87233,87234],{"class":2227,"line":1041},[2225,87235,59879],{"class":2235},[2225,87237,87238,87240,87242,87244,87246,87248,87250,87252],{"class":2227,"line":1038},[2225,87239,14201],{"class":2235},[2225,87241,6983],{"class":2774},[2225,87243,6620],{"class":2235},[2225,87245,2311],{"class":2235},[2225,87247,6614],{"class":2235},[2225,87249,86939],{"class":2252},[2225,87251,6620],{"class":2235},[2225,87253,1707],{"class":2235},[2225,87255,87256,87258,87260,87262,87264,87266,87268,87270],{"class":2227,"line":2288},[2225,87257,14201],{"class":2235},[2225,87259,87181],{"class":2774},[2225,87261,6620],{"class":2235},[2225,87263,2311],{"class":2235},[2225,87265,6614],{"class":2235},[2225,87267,86970],{"class":2252},[2225,87269,6620],{"class":2235},[2225,87271,1707],{"class":2235},[2225,87273,87274,87276,87278,87280,87282,87284,87286,87288],{"class":2227,"line":2304},[2225,87275,14201],{"class":2235},[2225,87277,42850],{"class":2774},[2225,87279,6620],{"class":2235},[2225,87281,2311],{"class":2235},[2225,87283,6614],{"class":2235},[2225,87285,86985],{"class":2252},[2225,87287,6620],{"class":2235},[2225,87289,1707],{"class":2235},[2225,87291,87292,87294,87296,87298,87300,87302,87304],{"class":2227,"line":2356},[2225,87293,14201],{"class":2235},[2225,87295,87098],{"class":2774},[2225,87297,6620],{"class":2235},[2225,87299,2311],{"class":2235},[2225,87301,6614],{"class":2235},[2225,87303,87000],{"class":2252},[2225,87305,9449],{"class":2235},[2225,87307,87308],{"class":2227,"line":2830},[2225,87309,19017],{"class":2235},[2225,87311,87312],{"class":2227,"line":2861},[2225,87313,59879],{"class":2235},[2225,87315,87316,87318,87320,87322,87324,87326,87328,87330],{"class":2227,"line":4246},[2225,87317,14201],{"class":2235},[2225,87319,6983],{"class":2774},[2225,87321,6620],{"class":2235},[2225,87323,2311],{"class":2235},[2225,87325,6614],{"class":2235},[2225,87327,42829],{"class":2252},[2225,87329,6620],{"class":2235},[2225,87331,1707],{"class":2235},[2225,87333,87334,87336,87338,87340,87342,87344,87346,87348],{"class":2227,"line":4253},[2225,87335,14201],{"class":2235},[2225,87337,87181],{"class":2774},[2225,87339,6620],{"class":2235},[2225,87341,2311],{"class":2235},[2225,87343,6614],{"class":2235},[2225,87345,86882],{"class":2252},[2225,87347,6620],{"class":2235},[2225,87349,1707],{"class":2235},[2225,87351,87352,87354,87356,87358,87360,87362,87364,87366],{"class":2227,"line":4259},[2225,87353,14201],{"class":2235},[2225,87355,42850],{"class":2774},[2225,87357,6620],{"class":2235},[2225,87359,2311],{"class":2235},[2225,87361,6614],{"class":2235},[2225,87363,86897],{"class":2252},[2225,87365,6620],{"class":2235},[2225,87367,1707],{"class":2235},[2225,87369,87370,87372,87374,87376,87378,87380,87382],{"class":2227,"line":6548},[2225,87371,14201],{"class":2235},[2225,87373,87098],{"class":2774},[2225,87375,6620],{"class":2235},[2225,87377,2311],{"class":2235},[2225,87379,6614],{"class":2235},[2225,87381,86912],{"class":2252},[2225,87383,9449],{"class":2235},[2225,87385,87386],{"class":2227,"line":6576},[2225,87387,7632],{"class":2235},[2225,87389,87390],{"class":2227,"line":6582},[2225,87391,26484],{"class":2235},[937,87393,87394],{},"That's the entire surface area you need for a personal encrypted notebook.",[1022,87396,87398],{"id":87397},"what-the-store-actually-sees","What the store actually sees",[937,87400,15390,87401,87403,87404,87406,87407,87409],{},[941,87402,1086],{}," before a record reaches the store, so IndexedDB never holds a readable note — only an ",[1092,87405,1659],{},": a small JSON wrapper whose ",[941,87408,1146],{}," field is AES-256-GCM ciphertext and whose other fields are non-sensitive metadata (format marker, version counter, timestamp, encryption IV):",[2217,87411,87413],{"className":6604,"code":87412,"language":141,"meta":1037,"style":1037},"{\n  \"_noydb\": 1,\n  \"_v\": 1,\n  \"_ts\": \"2026-07-06T09:00:00.412Z\",\n  \"_iv\": \"…base64…\",\n  \"_data\": \"…base64 AES-256-GCM ciphertext…\"\n}\n",[941,87414,87415,87419,87433,87447,87466,87484,87501],{"__ignoreMap":1037},[2225,87416,87417],{"class":2227,"line":2228},[2225,87418,4200],{"class":2235},[2225,87420,87421,87423,87425,87427,87429,87431],{"class":2227,"line":1041},[2225,87422,9368],{"class":2235},[2225,87424,1697],{"class":2774},[2225,87426,6620],{"class":2235},[2225,87428,2311],{"class":2235},[2225,87430,9377],{"class":6527},[2225,87432,1707],{"class":2235},[2225,87434,87435,87437,87439,87441,87443,87445],{"class":2227,"line":1038},[2225,87436,9368],{"class":2235},[2225,87438,1700],{"class":2774},[2225,87440,6620],{"class":2235},[2225,87442,2311],{"class":2235},[2225,87444,9377],{"class":6527},[2225,87446,1707],{"class":2235},[2225,87448,87449,87451,87453,87455,87457,87459,87462,87464],{"class":2227,"line":2288},[2225,87450,9368],{"class":2235},[2225,87452,1703],{"class":2774},[2225,87454,6620],{"class":2235},[2225,87456,2311],{"class":2235},[2225,87458,6614],{"class":2235},[2225,87460,87461],{"class":2252},"2026-07-06T09:00:00.412Z",[2225,87463,6620],{"class":2235},[2225,87465,1707],{"class":2235},[2225,87467,87468,87470,87472,87474,87476,87478,87480,87482],{"class":2227,"line":2304},[2225,87469,9368],{"class":2235},[2225,87471,1143],{"class":2774},[2225,87473,6620],{"class":2235},[2225,87475,2311],{"class":2235},[2225,87477,6614],{"class":2235},[2225,87479,9427],{"class":2252},[2225,87481,6620],{"class":2235},[2225,87483,1707],{"class":2235},[2225,87485,87486,87488,87490,87492,87494,87496,87499],{"class":2227,"line":2356},[2225,87487,9368],{"class":2235},[2225,87489,1146],{"class":2774},[2225,87491,6620],{"class":2235},[2225,87493,2311],{"class":2235},[2225,87495,6614],{"class":2235},[2225,87497,87498],{"class":2252},"…base64 AES-256-GCM ciphertext…",[2225,87500,9449],{"class":2235},[2225,87502,87503],{"class":2227,"line":2830},[2225,87504,7351],{"class":2235},[937,87506,46939,87507,87509],{},[1007,87508,21],{"href":60250}," for the full key lifecycle.",[1022,87511,87513],{"id":87512},"schema-validation-optional-lightweight","Schema validation (optional, lightweight)",[937,87515,87516,87517,87519],{},"Attach any Standard Schema validator to the collection. ",[941,87518,20953],{}," then validates before encrypting, and reads validate the decrypted record on the way out — drifted stored data is caught too.",[2217,87521,87523],{"className":2219,"code":87522,"language":2221,"meta":1037,"style":1037},"import { z } from 'zod'\n\nconst NoteSchema = z.object({\n  id: z.string(),\n  title: z.string().min(1).max(200),\n  body: z.string(),\n  createdAt: z.string().datetime(),\n})\n\nconst notes = vault.collection\u003Cz.infer\u003Ctypeof NoteSchema>>('notes', {\n  schema: NoteSchema,\n})\n\nawait notes.put('bad', { id: 'bad', title: '', body: '', createdAt: 'not-a-date' })\n\u002F\u002F → throws SchemaValidationError with the validator's field-level issues\n",[941,87524,87525,87543,87547,87566,87582,87619,87635,87657,87663,87667,87708,87718,87724,87728,87795],{"__ignoreMap":1037},[2225,87526,87527,87529,87531,87533,87535,87537,87539,87541],{"class":2227,"line":2228},[2225,87528,2232],{"class":2231},[2225,87530,2236],{"class":2235},[2225,87532,28314],{"class":2239},[2225,87534,2243],{"class":2235},[2225,87536,2246],{"class":2231},[2225,87538,2249],{"class":2235},[2225,87540,28323],{"class":2252},[2225,87542,2256],{"class":2235},[2225,87544,87545],{"class":2227,"line":1041},[2225,87546,2285],{"emptyLinePlaceholder":2284},[2225,87548,87549,87551,87554,87556,87558,87560,87562,87564],{"class":2227,"line":1038},[2225,87550,2775],{"class":2774},[2225,87552,87553],{"class":2239}," NoteSchema ",[2225,87555,2781],{"class":2235},[2225,87557,28314],{"class":2239},[2225,87559,1520],{"class":2235},[2225,87561,25505],{"class":2291},[2225,87563,2789],{"class":2239},[2225,87565,4200],{"class":2235},[2225,87567,87568,87570,87572,87574,87576,87578,87580],{"class":2227,"line":2288},[2225,87569,28371],{"class":2307},[2225,87571,2311],{"class":2235},[2225,87573,28314],{"class":2239},[2225,87575,1520],{"class":2235},[2225,87577,7259],{"class":2291},[2225,87579,4228],{"class":2239},[2225,87581,1707],{"class":2235},[2225,87583,87584,87586,87588,87590,87592,87594,87596,87598,87600,87602,87604,87606,87608,87610,87612,87615,87617],{"class":2227,"line":2304},[2225,87585,86670],{"class":2307},[2225,87587,2311],{"class":2235},[2225,87589,28314],{"class":2239},[2225,87591,1520],{"class":2235},[2225,87593,7259],{"class":2291},[2225,87595,4228],{"class":2239},[2225,87597,1520],{"class":2235},[2225,87599,28204],{"class":2291},[2225,87601,2789],{"class":2239},[2225,87603,5437],{"class":6527},[2225,87605,1381],{"class":2239},[2225,87607,1520],{"class":2235},[2225,87609,28207],{"class":2291},[2225,87611,2789],{"class":2239},[2225,87613,87614],{"class":6527},"200",[2225,87616,1381],{"class":2239},[2225,87618,1707],{"class":2235},[2225,87620,87621,87623,87625,87627,87629,87631,87633],{"class":2227,"line":2356},[2225,87622,86679],{"class":2307},[2225,87624,2311],{"class":2235},[2225,87626,28314],{"class":2239},[2225,87628,1520],{"class":2235},[2225,87630,7259],{"class":2291},[2225,87632,4228],{"class":2239},[2225,87634,1707],{"class":2235},[2225,87636,87637,87639,87641,87643,87645,87647,87649,87651,87653,87655],{"class":2227,"line":2830},[2225,87638,82711],{"class":2307},[2225,87640,2311],{"class":2235},[2225,87642,28314],{"class":2239},[2225,87644,1520],{"class":2235},[2225,87646,7259],{"class":2291},[2225,87648,4228],{"class":2239},[2225,87650,1520],{"class":2235},[2225,87652,28482],{"class":2291},[2225,87654,4228],{"class":2239},[2225,87656,1707],{"class":2235},[2225,87658,87659,87661],{"class":2227,"line":2861},[2225,87660,2359],{"class":2235},[2225,87662,2362],{"class":2239},[2225,87664,87665],{"class":2227,"line":4246},[2225,87666,2285],{"emptyLinePlaceholder":2284},[2225,87668,87669,87671,87673,87675,87677,87679,87681,87683,87685,87687,87689,87691,87694,87696,87698,87700,87702,87704,87706],{"class":2227,"line":4253},[2225,87670,2775],{"class":2774},[2225,87672,42762],{"class":2239},[2225,87674,2781],{"class":2235},[2225,87676,6466],{"class":2239},[2225,87678,1520],{"class":2235},[2225,87680,6471],{"class":2291},[2225,87682,6474],{"class":2235},[2225,87684,28587],{"class":6342},[2225,87686,1520],{"class":2235},[2225,87688,28592],{"class":6342},[2225,87690,28595],{"class":2235},[2225,87692,87693],{"class":2239}," NoteSchema",[2225,87695,28601],{"class":2235},[2225,87697,2789],{"class":2239},[2225,87699,2805],{"class":2235},[2225,87701,2854],{"class":2252},[2225,87703,2805],{"class":2235},[2225,87705,2298],{"class":2235},[2225,87707,2301],{"class":2235},[2225,87709,87710,87712,87714,87716],{"class":2227,"line":4259},[2225,87711,28618],{"class":2307},[2225,87713,2311],{"class":2235},[2225,87715,87693],{"class":2239},[2225,87717,1707],{"class":2235},[2225,87719,87720,87722],{"class":2227,"line":6548},[2225,87721,2359],{"class":2235},[2225,87723,2362],{"class":2239},[2225,87725,87726],{"class":2227,"line":6576},[2225,87727,2285],{"emptyLinePlaceholder":2284},[2225,87729,87730,87732,87734,87736,87738,87740,87742,87745,87747,87749,87751,87753,87755,87757,87759,87761,87763,87765,87767,87769,87771,87773,87775,87777,87779,87782,87784,87786,87789,87791,87793],{"class":2227,"line":6582},[2225,87731,6500],{"class":2231},[2225,87733,42817],{"class":2239},[2225,87735,1520],{"class":2235},[2225,87737,3161],{"class":2291},[2225,87739,2789],{"class":2239},[2225,87741,2805],{"class":2235},[2225,87743,87744],{"class":2252},"bad",[2225,87746,2805],{"class":2235},[2225,87748,2298],{"class":2235},[2225,87750,2236],{"class":2235},[2225,87752,7070],{"class":2307},[2225,87754,2311],{"class":2235},[2225,87756,2249],{"class":2235},[2225,87758,87744],{"class":2252},[2225,87760,2805],{"class":2235},[2225,87762,2298],{"class":2235},[2225,87764,74169],{"class":2307},[2225,87766,2311],{"class":2235},[2225,87768,12500],{"class":2235},[2225,87770,2298],{"class":2235},[2225,87772,42791],{"class":2307},[2225,87774,2311],{"class":2235},[2225,87776,12500],{"class":2235},[2225,87778,2298],{"class":2235},[2225,87780,87781],{"class":2307}," createdAt",[2225,87783,2311],{"class":2235},[2225,87785,2249],{"class":2235},[2225,87787,87788],{"class":2252},"not-a-date",[2225,87790,2805],{"class":2235},[2225,87792,2243],{"class":2235},[2225,87794,2362],{"class":2239},[2225,87796,87797],{"class":2227,"line":6587},[2225,87798,87799],{"class":2763},"\u002F\u002F → throws SchemaValidationError with the validator's field-level issues\n",[1022,87801,87803],{"id":87802},"when-this-recipe-stops-being-enough","When this recipe stops being enough",[1111,87805,87806,87819,87828,87844],{},[1114,87807,87808,87809,87812,87813,87816,87817,1381],{},"You want to ",[950,87810,87811],{},"see what a note used to say"," → upgrade to ",[1007,87814,58212],{"href":87815},".\u002Faccounting-app"," (adds ",[941,87818,58226],{},[1114,87820,87808,87821,87812,87824,87816,87826,1381],{},[950,87822,87823],{},"attach files",[1007,87825,58212],{"href":87815},[941,87827,58232],{},[1114,87829,87830,87831,87812,87834,87816,87838,6030,87841,1381],{},"You want ",[950,87832,87833],{},"two devices to stay in sync",[1007,87835,87837],{"href":87836},".\u002Frealtime-crdt-app","realtime-crdt-app",[941,87839,87840],{},"withSync",[941,87842,87843],{},"withCrdt",[1114,87845,87830,87846,87812,87849,87816,87852,6030,87855,1381],{},[950,87847,87848],{},"fast queries on a million records",[1007,87850,61770],{"href":87851},".\u002Fanalytics-app",[941,87853,87854],{},"withIndexing",[941,87856,58241],{},[1022,87858,2621],{"id":2620},[1111,87860,87861,87865,87869,87873],{},[1114,87862,87863],{},[1007,87864,3112],{"href":58262},[1114,87866,87867],{},[1007,87868,21],{"href":60250},[1114,87870,87871],{},[1007,87872,6898],{"href":86452},[1114,87874,87875,87878],{},[1007,87876,87877],{"href":6254},"Service catalog"," — every opt-in service, one page each",[2645,87880,87881],{},"html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}",{"title":1037,"searchDepth":1038,"depth":1038,"links":87883},[87884,87885,87886,87887,87888,87889,87890],{"id":86420,"depth":1041,"text":86421},{"id":86458,"depth":1041,"text":86459},{"id":86602,"depth":1041,"text":86603},{"id":87397,"depth":1041,"text":87398},{"id":87512,"depth":1041,"text":87513},{"id":87802,"depth":1041,"text":87803},{"id":2620,"depth":1041,"text":2621},"The minimalist, core-only encrypted notebook — the smallest possible createNoydb({...}) with no opt-in services.",{},{"title":559,"description":87891},"DPWAX-1AhF0YQYsidRLSGlo4-H8TRtGgrSVXW22Vcvk",{"id":87896,"title":563,"access":12,"apiToc":929,"body":87897,"category":1042,"description":88076,"extension":1044,"meta":88077,"navigation":88078,"nest":1042,"path":564,"seo":88079,"sinceVersion":1042,"status":1049,"stem":565,"subtitle":1042,"tier":1050,"tocExternal":1042,"__hash__":88080},"docs\u002Frecipes\u002Fplaygrounds.md",{"type":931,"value":87898,"toc":88070},[87899,87902,87912,87919,87922,87934,87978,87981,87985,87991,88012,88015,88038,88048,88050,88067],[934,87900,563],{"id":87901},"playgrounds",[937,87903,87904,87905,87907,87908,87911],{},"Two guided demos, both running against the published ",[941,87906,5129],{}," packages — pick by how you like to explore. Clone the repo once (",[941,87909,87910],{},"git clone https:\u002F\u002Fgithub.com\u002Fnoy-db\u002Fdocs && cd noy-db-docs && pnpm install","), then:",[1022,87913,87915,87916],{"id":87914},"the-cli-tour-pnpm-demo","The CLI tour — ",[941,87917,87918],{},"pnpm demo",[937,87920,87921],{},"A 7-step interactive walkthrough in your terminal (non-interactive when piped, so it also runs in CI):",[2217,87923,87925],{"className":33763,"code":87924,"language":33765,"meta":1037,"style":1037},"pnpm demo\n",[941,87926,87927],{"__ignoreMap":1037},[2225,87928,87929,87931],{"class":2227,"line":2228},[2225,87930,41169],{"class":6342},[2225,87932,87933],{"class":2252}," demo\n",[1234,87935,87936,87942,87948,87954,87960,87966,87972],{},[1114,87937,87938,87941],{},[950,87939,87940],{},"Encrypted CRUD"," on a file store — including a peek at the ciphertext envelope on disk.",[1114,87943,87944,87947],{},[950,87945,87946],{},"Multi-user access control"," — owner grants an operator (read\u002Fwrite) and a viewer (read-only); the viewer's forbidden write fails live.",[1114,87949,87950,87953],{},[950,87951,87952],{},"Offline → online sync"," — two \"locations\" with separate local stores push\u002Fpull through a shared backend.",[1114,87955,87956,87959],{},[950,87957,87958],{},"Conflict detection"," — the same record edited in both places, resolved by strategy.",[1114,87961,87962,87965],{},[950,87963,87964],{},"Backup & restore"," — encrypted dump, simulated disaster, full restore.",[1114,87967,87968,87971],{},[950,87969,87970],{},"Blob store"," — an attached PDF: encrypted, content-addressed, deduplicated.",[1114,87973,87974,87977],{},[950,87975,87976],{},"Audit history"," — versions, field-level diff, time-travel revert, pruning.",[937,87979,87980],{},"Everything runs in a temp directory and cleans up after itself.",[1022,87982,87984],{"id":87983},"the-nuxt-reference-app","The Nuxt reference app",[937,87986,87987,87988,87990],{},"A minimal but real Nuxt 4 application — module config, bootstrap plugin, ",[941,87989,37712],{}," stores, reactive query-driven pages:",[2217,87992,87994],{"className":33763,"code":87993,"language":33765,"meta":1037,"style":1037},"pnpm --filter @noy-db\u002Fplayground-nuxt dev   # → http:\u002F\u002Flocalhost:3000\n",[941,87995,87996],{"__ignoreMap":1037},[2225,87997,87998,88000,88003,88006,88009],{"class":2227,"line":2228},[2225,87999,41169],{"class":6342},[2225,88001,88002],{"class":2252}," --filter",[2225,88004,88005],{"class":2252}," @noy-db\u002Fplayground-nuxt",[2225,88007,88008],{"class":2252}," dev",[2225,88010,88011],{"class":2763},"   # → http:\u002F\u002Flocalhost:3000\n",[937,88013,88014],{},"Worth trying while it runs:",[1111,88016,88017,88020,88025],{},[1114,88018,88019],{},"Edit the invoice list's filters — the reactive query DSL recomputes as you type.",[1114,88021,88022,88023,957],{},"Open DevTools → Application → IndexedDB — every stored record is a ciphertext envelope (the browser form of ",[1007,88024,924],{"href":925},[1114,88026,960,88027,997,88030,88033,88034,88037],{},[941,88028,88029],{},"\u002Fwebauthn",[941,88031,88032],{},"\u002Foidc"," pages drive real ",[941,88035,88036],{},"navigator.credentials"," and provider redirects — the flows the headless showcases (72–75) cover synthetically.",[937,88039,88040,88041,968,88043,968,88045,88047],{},"Its build doubles as the family's integration test: if this app builds, ",[941,88042,38854],{},[941,88044,37591],{},[941,88046,37588],{},", and the hub compose correctly.",[1022,88049,2621],{"id":2620},[1111,88051,88052,88057,88062],{},[1114,88053,88054,88056],{},[1007,88055,576],{"href":577}," — the full proof suite — every capability as a runnable test, one command.",[1114,88058,88059,88061],{},[1007,88060,36282],{"href":244}," — the module the reference app is built on.",[1114,88063,88064,88066],{},[1007,88065,80320],{"href":500}," — the copy-paste starter applications.",[2645,88068,88069],{},"html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}",{"title":1037,"searchDepth":1038,"depth":1038,"links":88071},[88072,88074,88075],{"id":87914,"depth":1041,"text":88073},"The CLI tour — pnpm demo",{"id":87983,"depth":1041,"text":87984},{"id":2620,"depth":1041,"text":2621},"Two guided, runnable demos: a 7-step interactive CLI tour and a full Nuxt 4 reference app whose build doubles as the family's integration test.",{},{"icon":566},{"title":563,"description":88076},"_3wYoqfgQW1tL4oLyVS9qFIGK_0fv-y1JJgOtx39cc0",{"id":88082,"title":568,"access":12,"apiToc":929,"body":88083,"category":1042,"description":89343,"extension":1044,"meta":89344,"navigation":1042,"nest":1042,"path":569,"seo":89345,"sinceVersion":1042,"status":1049,"stem":570,"subtitle":1042,"tier":1050,"tocExternal":1042,"__hash__":89346},"docs\u002Frecipes\u002Freact-invoices.md",{"type":931,"value":88084,"toc":89331},[88085,88088,88103,88112,88121,88123,88155,88157,88160,88467,88473,88691,88694,88910,88916,88921,89090,89094,89099,89248,89256,89260,89269,89273,89304,89306,89328],[934,88086,568],{"id":88087},"react-invoice-app",[1061,88089,88090],{},[937,88091,88092,88094,88095,58223,88097,88099,88100,88102],{},[950,88093,1500],{}," a React developer who wants an encrypted, offline-first data layer without adding a state library.\n",[950,88096,58222],{},[941,88098,38573],{}," (hooks) + ",[941,88101,9573],{}," (persistence) — no opt-in services required.",[937,88104,88105,88106,88108,88109,88111],{},"An invoice list you can add to, edit, and filter — where every record is encrypted before it touches IndexedDB, and every write re-renders exactly the components that read it. The hooks are built on React 18's ",[941,88107,37530],{},", so noy-db ",[1092,88110,3082],{}," the state layer: no Redux, no context-of-arrays, no cache invalidation.",[937,88113,58245,88114,73771],{},[1007,88115,88118],{"href":88116,"rel":88117},"https:\u002F\u002Fgithub.com\u002Fnoy-db\u002Fdocs\u002Fblob\u002Fmain\u002Fshowcases\u002Fsrc\u002Frecipe-react-invoices.recipe.test.tsx",[3371],[941,88119,88120],{},"showcases\u002Fsrc\u002Frecipe-react-invoices.recipe.test.tsx",[1022,88122,70093],{"id":70092},[1111,88124,88125,88133,88147],{},[1114,88126,88127,88130,88131,1520],{},[950,88128,88129],{},"React 18+"," — the hooks rely on ",[941,88132,37530],{},[1114,88134,88135,19862,88141,88143,88144],{},[950,88136,88137,6030,88139],{},[941,88138,38573],{},[941,88140,1086],{},[941,88142,9573],{}," for persistence): ",[941,88145,88146],{},"pnpm add @noy-db\u002Fhub @noy-db\u002Fin-react @noy-db\u002Fto-browser-idb",[1114,88148,88149,17244,88152,88154],{},[950,88150,88151],{},"Nothing else",[941,88153,943],{}," service is needed for this recipe; reactive lists and queries are core behavior surfaced through the hooks.",[1022,88156,58394],{"id":58393},[937,88158,88159],{},"One typed collection, one provider:",[2217,88161,88163],{"className":38784,"code":88162,"language":38786,"meta":1037,"style":1037},"import { createNoydb } from '@noy-db\u002Fhub'\nimport { toBrowserIdb } from '@noy-db\u002Fto-browser-idb'\nimport { NoydbProvider } from '@noy-db\u002Fin-react'\n\ninterface Invoice {\n  id: string\n  number: string\n  customer: string\n  amount: number\n  status: 'draft' | 'sent' | 'paid'\n}\n\nconst db = await createNoydb({\n  store: toBrowserIdb(),          \u002F\u002F encrypted persistence in IndexedDB\n  user: 'me',\n  secret: 'react-recipe-pass-2026',  \u002F\u002F in a real app: collected at unlock, not hardcoded\n})\nconst vault = await db.openVault('acme')\n\nexport function App() {\n  return (\n    \u003CNoydbProvider db={db}>\n      \u003CInvoiceList \u002F>\n      \u003CBigInvoices \u002F>\n    \u003C\u002FNoydbProvider>\n  )\n}\n",[941,88164,88165,88183,88201,88219,88223,88231,88239,88248,88257,88265,88294,88298,88302,88318,88333,88347,88365,88371,88397,88401,88413,88419,88433,88442,88451,88459,88463],{"__ignoreMap":1037},[2225,88166,88167,88169,88171,88173,88175,88177,88179,88181],{"class":2227,"line":2228},[2225,88168,2232],{"class":2231},[2225,88170,2236],{"class":2235},[2225,88172,2744],{"class":2239},[2225,88174,2243],{"class":2235},[2225,88176,2246],{"class":2231},[2225,88178,2249],{"class":2235},[2225,88180,1086],{"class":2252},[2225,88182,2256],{"class":2235},[2225,88184,88185,88187,88189,88191,88193,88195,88197,88199],{"class":2227,"line":1041},[2225,88186,2232],{"class":2231},[2225,88188,2236],{"class":2235},[2225,88190,34763],{"class":2239},[2225,88192,2243],{"class":2235},[2225,88194,2246],{"class":2231},[2225,88196,2249],{"class":2235},[2225,88198,9573],{"class":2252},[2225,88200,2256],{"class":2235},[2225,88202,88203,88205,88207,88209,88211,88213,88215,88217],{"class":2227,"line":1038},[2225,88204,2232],{"class":2231},[2225,88206,2236],{"class":2235},[2225,88208,40200],{"class":2239},[2225,88210,2243],{"class":2235},[2225,88212,2246],{"class":2231},[2225,88214,2249],{"class":2235},[2225,88216,38573],{"class":2252},[2225,88218,2256],{"class":2235},[2225,88220,88221],{"class":2227,"line":2288},[2225,88222,2285],{"emptyLinePlaceholder":2284},[2225,88224,88225,88227,88229],{"class":2227,"line":2304},[2225,88226,6994],{"class":2774},[2225,88228,6343],{"class":6342},[2225,88230,2301],{"class":2235},[2225,88232,88233,88235,88237],{"class":2227,"line":2356},[2225,88234,28371],{"class":2307},[2225,88236,2311],{"class":2235},[2225,88238,7014],{"class":6342},[2225,88240,88241,88244,88246],{"class":2227,"line":2830},[2225,88242,88243],{"class":2307},"  number",[2225,88245,2311],{"class":2235},[2225,88247,7014],{"class":6342},[2225,88249,88250,88253,88255],{"class":2227,"line":2861},[2225,88251,88252],{"class":2307},"  customer",[2225,88254,2311],{"class":2235},[2225,88256,7014],{"class":6342},[2225,88258,88259,88261,88263],{"class":2227,"line":4246},[2225,88260,28389],{"class":2307},[2225,88262,2311],{"class":2235},[2225,88264,12024],{"class":6342},[2225,88266,88267,88269,88271,88273,88275,88277,88279,88281,88284,88286,88288,88290,88292],{"class":2227,"line":4253},[2225,88268,26171],{"class":2307},[2225,88270,2311],{"class":2235},[2225,88272,2249],{"class":2235},[2225,88274,28429],{"class":2252},[2225,88276,2805],{"class":2235},[2225,88278,7087],{"class":2235},[2225,88280,2249],{"class":2235},[2225,88282,88283],{"class":2252},"sent",[2225,88285,2805],{"class":2235},[2225,88287,7087],{"class":2235},[2225,88289,2249],{"class":2235},[2225,88291,6539],{"class":2252},[2225,88293,2256],{"class":2235},[2225,88295,88296],{"class":2227,"line":4259},[2225,88297,7351],{"class":2235},[2225,88299,88300],{"class":2227,"line":6548},[2225,88301,2285],{"emptyLinePlaceholder":2284},[2225,88303,88304,88306,88308,88310,88312,88314,88316],{"class":2227,"line":6576},[2225,88305,2775],{"class":2774},[2225,88307,2778],{"class":2239},[2225,88309,2781],{"class":2235},[2225,88311,2784],{"class":2231},[2225,88313,2744],{"class":2291},[2225,88315,2789],{"class":2239},[2225,88317,4200],{"class":2235},[2225,88319,88320,88322,88324,88326,88328,88330],{"class":2227,"line":6582},[2225,88321,15608],{"class":2307},[2225,88323,2311],{"class":2235},[2225,88325,34763],{"class":2291},[2225,88327,4228],{"class":2239},[2225,88329,2298],{"class":2235},[2225,88331,88332],{"class":2763},"          \u002F\u002F encrypted persistence in IndexedDB\n",[2225,88334,88335,88337,88339,88341,88343,88345],{"class":2227,"line":6587},[2225,88336,4205],{"class":2307},[2225,88338,2311],{"class":2235},[2225,88340,2249],{"class":2235},[2225,88342,4212],{"class":2252},[2225,88344,2805],{"class":2235},[2225,88346,1707],{"class":2235},[2225,88348,88349,88351,88353,88355,88358,88360,88362],{"class":2227,"line":7174},[2225,88350,10423],{"class":2307},[2225,88352,2311],{"class":2235},[2225,88354,2249],{"class":2235},[2225,88356,88357],{"class":2252},"react-recipe-pass-2026",[2225,88359,2805],{"class":2235},[2225,88361,2298],{"class":2235},[2225,88363,88364],{"class":2763},"  \u002F\u002F in a real app: collected at unlock, not hardcoded\n",[2225,88366,88367,88369],{"class":2227,"line":7180},[2225,88368,2359],{"class":2235},[2225,88370,2362],{"class":2239},[2225,88372,88373,88375,88377,88379,88381,88383,88385,88387,88389,88391,88393,88395],{"class":2227,"line":7220},[2225,88374,2775],{"class":2774},[2225,88376,2835],{"class":2239},[2225,88378,2781],{"class":2235},[2225,88380,2784],{"class":2231},[2225,88382,2842],{"class":2239},[2225,88384,1520],{"class":2235},[2225,88386,2847],{"class":2291},[2225,88388,2789],{"class":2239},[2225,88390,2805],{"class":2235},[2225,88392,6450],{"class":2252},[2225,88394,2805],{"class":2235},[2225,88396,2362],{"class":2239},[2225,88398,88399],{"class":2227,"line":7225},[2225,88400,2285],{"emptyLinePlaceholder":2284},[2225,88402,88403,88405,88407,88409,88411],{"class":2227,"line":7231},[2225,88404,8080],{"class":2231},[2225,88406,40856],{"class":2774},[2225,88408,40277],{"class":2291},[2225,88410,4228],{"class":2235},[2225,88412,2301],{"class":2235},[2225,88414,88415,88417],{"class":2227,"line":7267},[2225,88416,8220],{"class":2231},[2225,88418,40288],{"class":2307},[2225,88420,88421,88423,88425,88427,88429,88431],{"class":2227,"line":7272},[2225,88422,40293],{"class":2235},[2225,88424,40296],{"class":6342},[2225,88426,2842],{"class":2774},[2225,88428,40301],{"class":2235},[2225,88430,6590],{"class":2239},[2225,88432,40306],{"class":2235},[2225,88434,88435,88437,88440],{"class":2227,"line":7278},[2225,88436,40311],{"class":2235},[2225,88438,88439],{"class":6342},"InvoiceList",[2225,88441,40316],{"class":2235},[2225,88443,88444,88446,88449],{"class":2227,"line":7303},[2225,88445,40311],{"class":2235},[2225,88447,88448],{"class":6342},"BigInvoices",[2225,88450,40316],{"class":2235},[2225,88452,88453,88455,88457],{"class":2227,"line":7308},[2225,88454,40321],{"class":2235},[2225,88456,40296],{"class":6342},[2225,88458,7093],{"class":2235},[2225,88460,88461],{"class":2227,"line":7314},[2225,88462,40330],{"class":2307},[2225,88464,88465],{"class":2227,"line":7348},[2225,88466,7351],{"class":2235},[1022,88468,88470,88471],{"id":88469},"a-reactive-list-usecollection","A reactive list — ",[941,88472,37493],{},[2217,88474,88476],{"className":38784,"code":88475,"language":38786,"meta":1037,"style":1037},"import { useCollection } from '@noy-db\u002Fin-react'\n\nfunction InvoiceList() {\n  const { data, loading } = useCollection\u003CInvoice>(vault, 'invoices')\n  if (loading) return \u003Cp>Loading…\u003C\u002Fp>\n  return (\n    \u003Cul>\n      {data.map((inv) => (\n        \u003Cli key={inv.id}>{inv.number} — {inv.customer} — {inv.amount}\u003C\u002Fli>\n      ))}\n    \u003C\u002Ful>\n  )\n}\n",[941,88477,88478,88496,88500,88511,88549,88575,88581,88589,88612,88668,88675,88683,88687],{"__ignoreMap":1037},[2225,88479,88480,88482,88484,88486,88488,88490,88492,88494],{"class":2227,"line":2228},[2225,88481,2232],{"class":2231},[2225,88483,2236],{"class":2235},[2225,88485,38802],{"class":2239},[2225,88487,2243],{"class":2235},[2225,88489,2246],{"class":2231},[2225,88491,2249],{"class":2235},[2225,88493,38573],{"class":2252},[2225,88495,2256],{"class":2235},[2225,88497,88498],{"class":2227,"line":1041},[2225,88499,2285],{"emptyLinePlaceholder":2284},[2225,88501,88502,88504,88507,88509],{"class":2227,"line":1038},[2225,88503,12641],{"class":2774},[2225,88505,88506],{"class":2291}," InvoiceList",[2225,88508,4228],{"class":2235},[2225,88510,2301],{"class":2235},[2225,88512,88513,88515,88517,88519,88521,88523,88525,88527,88529,88531,88533,88535,88537,88539,88541,88543,88545,88547],{"class":2227,"line":2288},[2225,88514,8123],{"class":2774},[2225,88516,2236],{"class":2235},[2225,88518,7330],{"class":2239},[2225,88520,2298],{"class":2235},[2225,88522,40386],{"class":2239},[2225,88524,2243],{"class":2235},[2225,88526,6346],{"class":2235},[2225,88528,38802],{"class":2291},[2225,88530,6474],{"class":2235},[2225,88532,6477],{"class":6342},[2225,88534,6480],{"class":2235},[2225,88536,2789],{"class":2307},[2225,88538,2694],{"class":2239},[2225,88540,2298],{"class":2235},[2225,88542,2249],{"class":2235},[2225,88544,6487],{"class":2252},[2225,88546,2805],{"class":2235},[2225,88548,2362],{"class":2307},[2225,88550,88551,88553,88555,88557,88559,88561,88563,88565,88567,88569,88571,88573],{"class":2227,"line":2304},[2225,88552,7608],{"class":2231},[2225,88554,953],{"class":2307},[2225,88556,40421],{"class":2239},[2225,88558,8495],{"class":2307},[2225,88560,40426],{"class":2231},[2225,88562,40429],{"class":2235},[2225,88564,937],{"class":2307},[2225,88566,6480],{"class":2235},[2225,88568,40436],{"class":2239},[2225,88570,40439],{"class":2235},[2225,88572,937],{"class":2307},[2225,88574,7093],{"class":2235},[2225,88576,88577,88579],{"class":2227,"line":2356},[2225,88578,8220],{"class":2231},[2225,88580,40288],{"class":2307},[2225,88582,88583,88585,88587],{"class":2227,"line":2830},[2225,88584,40293],{"class":2235},[2225,88586,1111],{"class":2307},[2225,88588,7093],{"class":2235},[2225,88590,88591,88594,88596,88598,88600,88602,88604,88606,88608,88610],{"class":2227,"line":2861},[2225,88592,88593],{"class":2235},"      {",[2225,88595,8673],{"class":2239},[2225,88597,1520],{"class":2235},[2225,88599,8244],{"class":2291},[2225,88601,2789],{"class":2239},[2225,88603,2789],{"class":2235},[2225,88605,26995],{"class":7052},[2225,88607,1381],{"class":2235},[2225,88609,8116],{"class":2774},[2225,88611,40288],{"class":2239},[2225,88613,88614,88617,88619,88621,88623,88625,88627,88629,88631,88633,88635,88637,88639,88641,88643,88645,88647,88650,88652,88654,88656,88658,88660,88662,88664,88666],{"class":2227,"line":4246},[2225,88615,88616],{"class":2235},"        \u003C",[2225,88618,1114],{"class":2307},[2225,88620,8153],{"class":2774},[2225,88622,40301],{"class":2235},[2225,88624,26995],{"class":2239},[2225,88626,1520],{"class":2235},[2225,88628,6983],{"class":2239},[2225,88630,40487],{"class":2235},[2225,88632,26995],{"class":2239},[2225,88634,1520],{"class":2235},[2225,88636,20636],{"class":2239},[2225,88638,2359],{"class":2235},[2225,88640,964],{"class":2239},[2225,88642,2792],{"class":2235},[2225,88644,26995],{"class":2239},[2225,88646,1520],{"class":2235},[2225,88648,88649],{"class":2239},"customer",[2225,88651,2359],{"class":2235},[2225,88653,964],{"class":2239},[2225,88655,2792],{"class":2235},[2225,88657,26995],{"class":2239},[2225,88659,1520],{"class":2235},[2225,88661,6617],{"class":2239},[2225,88663,40496],{"class":2235},[2225,88665,1114],{"class":2307},[2225,88667,7093],{"class":2235},[2225,88669,88670,88673],{"class":2227,"line":4253},[2225,88671,88672],{"class":2239},"      ))",[2225,88674,7351],{"class":2235},[2225,88676,88677,88679,88681],{"class":2227,"line":4259},[2225,88678,40321],{"class":2235},[2225,88680,1111],{"class":2307},[2225,88682,7093],{"class":2235},[2225,88684,88685],{"class":2227,"line":6548},[2225,88686,40330],{"class":2307},[2225,88688,88689],{"class":2227,"line":6576},[2225,88690,7351],{"class":2235},[937,88692,88693],{},"Writes are plain collection calls — from a form handler, a keyboard shortcut, or a sync pull; every component reading the collection re-renders:",[2217,88695,88697],{"className":38784,"code":88696,"language":38786,"meta":1037,"style":1037},"const invoices = vault.collection\u003CInvoice>('invoices')\n\n\u002F\u002F add\nawait invoices.put('inv-2', { id: 'inv-2', number: '2026-002', customer: 'Globex', amount: 125000, status: 'draft' })\n\n\u002F\u002F edit is the same put() — the row updates in place\nawait invoices.put('inv-2', { id: 'inv-2', number: '2026-002', customer: 'Globex', amount: 125000, status: 'sent' })\n",[941,88698,88699,88729,88733,88738,88821,88825,88830],{"__ignoreMap":1037},[2225,88700,88701,88703,88705,88707,88709,88711,88713,88715,88717,88719,88721,88723,88725,88727],{"class":2227,"line":2228},[2225,88702,2775],{"class":2774},[2225,88704,6461],{"class":2239},[2225,88706,2781],{"class":2235},[2225,88708,6466],{"class":2239},[2225,88710,1520],{"class":2235},[2225,88712,6471],{"class":2291},[2225,88714,6474],{"class":2235},[2225,88716,6477],{"class":6342},[2225,88718,6480],{"class":2235},[2225,88720,2789],{"class":2239},[2225,88722,2805],{"class":2235},[2225,88724,6487],{"class":2252},[2225,88726,2805],{"class":2235},[2225,88728,2362],{"class":2239},[2225,88730,88731],{"class":2227,"line":1041},[2225,88732,2285],{"emptyLinePlaceholder":2284},[2225,88734,88735],{"class":2227,"line":1038},[2225,88736,88737],{"class":2763},"\u002F\u002F add\n",[2225,88739,88740,88742,88744,88746,88748,88750,88752,88754,88756,88758,88760,88762,88764,88766,88768,88770,88772,88774,88776,88778,88781,88783,88785,88788,88790,88792,88794,88796,88798,88800,88802,88805,88807,88809,88811,88813,88815,88817,88819],{"class":2227,"line":2288},[2225,88741,6500],{"class":2231},[2225,88743,6503],{"class":2239},[2225,88745,1520],{"class":2235},[2225,88747,3161],{"class":2291},[2225,88749,2789],{"class":2239},[2225,88751,2805],{"class":2235},[2225,88753,26080],{"class":2252},[2225,88755,2805],{"class":2235},[2225,88757,2298],{"class":2235},[2225,88759,2236],{"class":2235},[2225,88761,7070],{"class":2307},[2225,88763,2311],{"class":2235},[2225,88765,2249],{"class":2235},[2225,88767,26080],{"class":2252},[2225,88769,2805],{"class":2235},[2225,88771,2298],{"class":2235},[2225,88773,6356],{"class":2307},[2225,88775,2311],{"class":2235},[2225,88777,2249],{"class":2235},[2225,88779,88780],{"class":2252},"2026-002",[2225,88782,2805],{"class":2235},[2225,88784,2298],{"class":2235},[2225,88786,88787],{"class":2307}," customer",[2225,88789,2311],{"class":2235},[2225,88791,2249],{"class":2235},[2225,88793,83465],{"class":2252},[2225,88795,2805],{"class":2235},[2225,88797,2298],{"class":2235},[2225,88799,6351],{"class":2307},[2225,88801,2311],{"class":2235},[2225,88803,88804],{"class":6527}," 125000",[2225,88806,2298],{"class":2235},[2225,88808,6361],{"class":2307},[2225,88810,2311],{"class":2235},[2225,88812,2249],{"class":2235},[2225,88814,28429],{"class":2252},[2225,88816,2805],{"class":2235},[2225,88818,2243],{"class":2235},[2225,88820,2362],{"class":2239},[2225,88822,88823],{"class":2227,"line":2304},[2225,88824,2285],{"emptyLinePlaceholder":2284},[2225,88826,88827],{"class":2227,"line":2356},[2225,88828,88829],{"class":2763},"\u002F\u002F edit is the same put() — the row updates in place\n",[2225,88831,88832,88834,88836,88838,88840,88842,88844,88846,88848,88850,88852,88854,88856,88858,88860,88862,88864,88866,88868,88870,88872,88874,88876,88878,88880,88882,88884,88886,88888,88890,88892,88894,88896,88898,88900,88902,88904,88906,88908],{"class":2227,"line":2830},[2225,88833,6500],{"class":2231},[2225,88835,6503],{"class":2239},[2225,88837,1520],{"class":2235},[2225,88839,3161],{"class":2291},[2225,88841,2789],{"class":2239},[2225,88843,2805],{"class":2235},[2225,88845,26080],{"class":2252},[2225,88847,2805],{"class":2235},[2225,88849,2298],{"class":2235},[2225,88851,2236],{"class":2235},[2225,88853,7070],{"class":2307},[2225,88855,2311],{"class":2235},[2225,88857,2249],{"class":2235},[2225,88859,26080],{"class":2252},[2225,88861,2805],{"class":2235},[2225,88863,2298],{"class":2235},[2225,88865,6356],{"class":2307},[2225,88867,2311],{"class":2235},[2225,88869,2249],{"class":2235},[2225,88871,88780],{"class":2252},[2225,88873,2805],{"class":2235},[2225,88875,2298],{"class":2235},[2225,88877,88787],{"class":2307},[2225,88879,2311],{"class":2235},[2225,88881,2249],{"class":2235},[2225,88883,83465],{"class":2252},[2225,88885,2805],{"class":2235},[2225,88887,2298],{"class":2235},[2225,88889,6351],{"class":2307},[2225,88891,2311],{"class":2235},[2225,88893,88804],{"class":6527},[2225,88895,2298],{"class":2235},[2225,88897,6361],{"class":2307},[2225,88899,2311],{"class":2235},[2225,88901,2249],{"class":2235},[2225,88903,88283],{"class":2252},[2225,88905,2805],{"class":2235},[2225,88907,2243],{"class":2235},[2225,88909,2362],{"class":2239},[1022,88911,88913,88914],{"id":88912},"a-filtered-view-usequery","A filtered view — ",[941,88915,37524],{},[937,88917,88918,88920],{},[941,88919,37524],{}," keeps the result of any query builder reactive — new matching writes update the view:",[2217,88922,88924],{"className":38784,"code":88923,"language":38786,"meta":1037,"style":1037},"import { useQuery } from '@noy-db\u002Fin-react'\n\nfunction BigInvoices() {\n  const { data } = useQuery\u003CInvoice, Invoice[]>(\n    vault, 'invoices',\n    (q) => q.where('amount', '>', 10_000).toArray(),\n  )\n  return \u003Cp>{(data ?? []).length} large invoice(s)\u003C\u002Fp>\n}\n",[941,88925,88926,88944,88948,88959,88987,89002,89049,89053,89086],{"__ignoreMap":1037},[2225,88927,88928,88930,88932,88934,88936,88938,88940,88942],{"class":2227,"line":2228},[2225,88929,2232],{"class":2231},[2225,88931,2236],{"class":2235},[2225,88933,41765],{"class":2239},[2225,88935,2243],{"class":2235},[2225,88937,2246],{"class":2231},[2225,88939,2249],{"class":2235},[2225,88941,38573],{"class":2252},[2225,88943,2256],{"class":2235},[2225,88945,88946],{"class":2227,"line":1041},[2225,88947,2285],{"emptyLinePlaceholder":2284},[2225,88949,88950,88952,88955,88957],{"class":2227,"line":1038},[2225,88951,12641],{"class":2774},[2225,88953,88954],{"class":2291}," BigInvoices",[2225,88956,4228],{"class":2235},[2225,88958,2301],{"class":2235},[2225,88960,88961,88963,88965,88967,88969,88971,88973,88975,88977,88979,88981,88983,88985],{"class":2227,"line":2288},[2225,88962,8123],{"class":2774},[2225,88964,2236],{"class":2235},[2225,88966,7330],{"class":2239},[2225,88968,2243],{"class":2235},[2225,88970,6346],{"class":2235},[2225,88972,41765],{"class":2291},[2225,88974,6474],{"class":2235},[2225,88976,6477],{"class":6342},[2225,88978,2298],{"class":2235},[2225,88980,6343],{"class":6342},[2225,88982,7262],{"class":2307},[2225,88984,6480],{"class":2235},[2225,88986,11683],{"class":2307},[2225,88988,88989,88992,88994,88996,88998,89000],{"class":2227,"line":2304},[2225,88990,88991],{"class":2239},"    vault",[2225,88993,2298],{"class":2235},[2225,88995,2249],{"class":2235},[2225,88997,6487],{"class":2252},[2225,88999,2805],{"class":2235},[2225,89001,1707],{"class":2235},[2225,89003,89004,89007,89009,89011,89013,89015,89017,89019,89021,89023,89025,89027,89029,89031,89033,89035,89037,89039,89041,89043,89045,89047],{"class":2227,"line":2356},[2225,89005,89006],{"class":2235},"    (",[2225,89008,26870],{"class":7052},[2225,89010,1381],{"class":2235},[2225,89012,8116],{"class":2774},[2225,89014,26875],{"class":2239},[2225,89016,1520],{"class":2235},[2225,89018,3016],{"class":2291},[2225,89020,2789],{"class":2307},[2225,89022,2805],{"class":2235},[2225,89024,6617],{"class":2252},[2225,89026,2805],{"class":2235},[2225,89028,2298],{"class":2235},[2225,89030,2249],{"class":2235},[2225,89032,6480],{"class":2252},[2225,89034,2805],{"class":2235},[2225,89036,2298],{"class":2235},[2225,89038,79700],{"class":6527},[2225,89040,1381],{"class":2307},[2225,89042,1520],{"class":2235},[2225,89044,3230],{"class":2291},[2225,89046,4228],{"class":2307},[2225,89048,1707],{"class":2235},[2225,89050,89051],{"class":2227,"line":2830},[2225,89052,40330],{"class":2307},[2225,89054,89055,89057,89059,89061,89063,89066,89068,89071,89073,89075,89077,89080,89082,89084],{"class":2227,"line":2861},[2225,89056,8220],{"class":2231},[2225,89058,40429],{"class":2235},[2225,89060,937],{"class":2307},[2225,89062,40454],{"class":2235},[2225,89064,89065],{"class":2239},"(data ",[2225,89067,8381],{"class":2235},[2225,89069,89070],{"class":2239}," [])",[2225,89072,1520],{"class":2235},[2225,89074,8742],{"class":2239},[2225,89076,2359],{"class":2235},[2225,89078,89079],{"class":2239}," large invoice(s)",[2225,89081,40439],{"class":2235},[2225,89083,937],{"class":2307},[2225,89085,7093],{"class":2235},[2225,89087,89088],{"class":2227,"line":4246},[2225,89089,7351],{"class":2235},[1022,89091,89093],{"id":89092},"lock-and-unlock","Lock and unlock",[937,89095,89096,89098],{},[941,89097,13331],{}," wipes the in-memory keys — the \"lock\". The ciphertext stays in IndexedDB; reopening with the secret restores access:",[2217,89100,89102],{"className":2219,"code":89101,"language":2221,"meta":1037,"style":1037},"db.close() \u002F\u002F lock: keys wiped, store keeps only ciphertext\n\nconst db2 = await createNoydb({ store: toBrowserIdb(), user: 'me', secret: 'react-recipe-pass-2026' })\nconst vault2 = await db2.openVault('acme')\nawait vault2.collection\u003CInvoice>('invoices').get('inv-1')\n\u002F\u002F → { id: 'inv-1', number: '2026-001', customer: 'ABC Trading', ... }\n",[941,89103,89104,89117,89121,89174,89202,89243],{"__ignoreMap":1037},[2225,89105,89106,89108,89110,89112,89114],{"class":2227,"line":2228},[2225,89107,6590],{"class":2239},[2225,89109,1520],{"class":2235},[2225,89111,6595],{"class":2291},[2225,89113,19132],{"class":2239},[2225,89115,89116],{"class":2763},"\u002F\u002F lock: keys wiped, store keeps only ciphertext\n",[2225,89118,89119],{"class":2227,"line":1041},[2225,89120,2285],{"emptyLinePlaceholder":2284},[2225,89122,89123,89125,89128,89130,89132,89134,89136,89138,89140,89142,89144,89146,89148,89150,89152,89154,89156,89158,89160,89162,89164,89166,89168,89170,89172],{"class":2227,"line":1038},[2225,89124,2775],{"class":2774},[2225,89126,89127],{"class":2239}," db2 ",[2225,89129,2781],{"class":2235},[2225,89131,2784],{"class":2231},[2225,89133,2744],{"class":2291},[2225,89135,2789],{"class":2239},[2225,89137,2792],{"class":2235},[2225,89139,7564],{"class":2307},[2225,89141,2311],{"class":2235},[2225,89143,34763],{"class":2291},[2225,89145,4228],{"class":2239},[2225,89147,2298],{"class":2235},[2225,89149,2795],{"class":2307},[2225,89151,2311],{"class":2235},[2225,89153,2249],{"class":2235},[2225,89155,4212],{"class":2252},[2225,89157,2805],{"class":2235},[2225,89159,2298],{"class":2235},[2225,89161,2810],{"class":2307},[2225,89163,2311],{"class":2235},[2225,89165,2249],{"class":2235},[2225,89167,88357],{"class":2252},[2225,89169,2805],{"class":2235},[2225,89171,2243],{"class":2235},[2225,89173,2362],{"class":2239},[2225,89175,89176,89178,89181,89183,89185,89188,89190,89192,89194,89196,89198,89200],{"class":2227,"line":2288},[2225,89177,2775],{"class":2774},[2225,89179,89180],{"class":2239}," vault2 ",[2225,89182,2781],{"class":2235},[2225,89184,2784],{"class":2231},[2225,89186,89187],{"class":2239}," db2",[2225,89189,1520],{"class":2235},[2225,89191,2847],{"class":2291},[2225,89193,2789],{"class":2239},[2225,89195,2805],{"class":2235},[2225,89197,6450],{"class":2252},[2225,89199,2805],{"class":2235},[2225,89201,2362],{"class":2239},[2225,89203,89204,89206,89209,89211,89213,89215,89217,89219,89221,89223,89225,89227,89229,89231,89233,89235,89237,89239,89241],{"class":2227,"line":2304},[2225,89205,6500],{"class":2231},[2225,89207,89208],{"class":2239}," vault2",[2225,89210,1520],{"class":2235},[2225,89212,6471],{"class":2291},[2225,89214,6474],{"class":2235},[2225,89216,6477],{"class":6342},[2225,89218,6480],{"class":2235},[2225,89220,2789],{"class":2239},[2225,89222,2805],{"class":2235},[2225,89224,6487],{"class":2252},[2225,89226,2805],{"class":2235},[2225,89228,1381],{"class":2239},[2225,89230,1520],{"class":2235},[2225,89232,3158],{"class":2291},[2225,89234,2789],{"class":2239},[2225,89236,2805],{"class":2235},[2225,89238,6514],{"class":2252},[2225,89240,2805],{"class":2235},[2225,89242,2362],{"class":2239},[2225,89244,89245],{"class":2227,"line":2356},[2225,89246,89247],{"class":2763},"\u002F\u002F → { id: 'inv-1', number: '2026-001', customer: 'ABC Trading', ... }\n",[937,89249,89250,89251,89255],{},"In a real app the secret comes from an unlock screen — or from a passkey via ",[1007,89252,89253],{"href":338},[941,89254,15944],{}," so there's nothing to type at all.",[1022,89257,89259],{"id":89258},"verify-the-encryption-yourself","Verify the encryption yourself",[937,89261,89262,89263,89265,89266,89268],{},"With the app running, open DevTools → Application → IndexedDB: every stored envelope's ",[941,89264,1146],{}," is opaque ciphertext — the same experiment as ",[1007,89267,924],{"href":925},", in the browser.",[1022,89270,89272],{"id":89271},"where-to-go-from-here","Where to go from here",[1111,89274,89275,89285,89292],{},[1114,89276,89277,89278,89281,89282,1520],{},"Sync across devices: add ",[941,89279,89280],{},"syncStrategy: withSync()"," and a shared store — see the ",[1007,89283,89284],{"href":573},"collaborative app recipe",[1114,89286,89287,89288,27678,89290,1520],{},"History\u002Fundo: ",[941,89289,84477],{},[1007,89291,920],{"href":921},[1114,89293,89294,89295,89300,89301,89303],{},"Next.js: ",[1007,89296,89297],{"href":240},[941,89298,89299],{},"in-nextjs"," re-exports these hooks under ",[941,89302,37605],{}," with App Router server helpers.",[1022,89305,2621],{"id":2620},[1111,89307,89308,89319,89324],{},[1114,89309,89310,89312,89313,968,89315,968,89317,957],{},[1007,89311,37609],{"href":256}," — the full five-hook surface (",[941,89314,37490],{},[941,89316,37519],{},[941,89318,37496],{},[1114,89320,89321,89323],{},[1007,89322,9507],{"href":359}," — the persistence layer and its eviction notes.",[1114,89325,89326,80321],{},[1007,89327,80320],{"href":500},[2645,89329,89330],{},"html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sHdIc, html code.shiki .sHdIc{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#EEFFFF;--shiki-default-font-style:italic;--shiki-dark:#BABED8;--shiki-dark-font-style:italic}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}",{"title":1037,"searchDepth":1038,"depth":1038,"links":89332},[89333,89334,89335,89337,89339,89340,89341,89342],{"id":70092,"depth":1041,"text":70093},{"id":58393,"depth":1041,"text":58394},{"id":88469,"depth":1041,"text":89336},"A reactive list — useCollection",{"id":88912,"depth":1041,"text":89338},"A filtered view — useQuery",{"id":89092,"depth":1041,"text":89093},{"id":89258,"depth":1041,"text":89259},{"id":89271,"depth":1041,"text":89272},{"id":2620,"depth":1041,"text":2621},"An end-to-end React app: reactive lists and filtered views via hooks, encrypted persistence in IndexedDB, and the lock\u002Funlock cycle — no state library.",{},{"title":568,"description":89343},"mgKdt9bCjxcmczp9VfIkfIf0jucA-uWvBQN20qAKhqA",{"id":89348,"title":572,"access":12,"apiToc":929,"body":89349,"category":1042,"description":92310,"extension":1044,"meta":92311,"navigation":1042,"nest":1042,"path":573,"seo":92312,"sinceVersion":1042,"status":1049,"stem":574,"subtitle":1042,"tier":1050,"tocExternal":1042,"__hash__":92313},"docs\u002Frecipes\u002Frealtime-crdt-app.md",{"type":931,"value":89350,"toc":92298},[89351,89354,89376,89402,89415,89417,89508,89521,89523,89529,90011,90018,90022,90040,90414,90431,90442,90446,90449,90922,90932,90936,90947,91121,91232,91242,91246,91254,91518,91524,91532,91548,91552,91559,91735,91758,91761,91881,91891,91895,91905,91918,91974,91986,92090,92155,92161,92189,92191,92239,92241,92295],[934,89352,572],{"id":89353},"collaborative-app",[1061,89355,89356],{},[937,89357,89358,89360,89361,58223,89363,6030,89365,6030,89367,6030,89369,89371,89372,89375],{},[950,89359,1500],{}," collaborative editors, live cursors, shared whiteboards, multi-device personal apps — two or more people editing the same documents at once, with every device converging on the same merged result and the server never seeing a word of the content.\n",[950,89362,58222],{},[941,89364,21950],{},[941,89366,4676],{},[941,89368,3197],{},[941,89370,21321],{},". Live query updates (",[941,89373,89374],{},"collection.query().live()",") are part of the core — no opt-in needed.",[937,89377,89378,61794,89380,89383,89384,1625,89386,89388,89389,61812,89392,89394,89395,997,89398,89401],{},[950,89379,61793],{},[941,89381,89382],{},"showcases\u002Fsrc\u002Frecipe-realtime-crdt.recipe.test.ts"," in the documentation repository covers the Yjs ",[941,89385,42822],{},[941,89387,42885],{}," round-trip, concurrent-edit CRDT convergence via ",[941,89390,89391],{},"Y.applyUpdate",[941,89393,21950],{}," is omitted. Push\u002Fpull replication and team grants (the sections below) are covered separately by ",[941,89396,89397],{},"showcases\u002Fsrc\u002F15-with-sync.showcase.test.ts",[941,89399,89400],{},"showcases\u002Fsrc\u002F06-multi-user.showcase.test.ts",". Presence, conflict-resolution strategies, and session-policy enforcement are not exercised by an automated test in this repository.",[937,89403,89404,89405,1625,89407,89409,89410,89414],{},"noy-db is an encrypted document store: records are encrypted inside your app's process before they reach any storage backend, so the backend only ever holds ciphertext. This recipe adds the collaborative layer on top: CRDTs (conflict-free replicated data types — data structures where concurrent edits merge deterministically instead of overwriting each other), replication between devices via ",[941,89406,56859],{},[941,89408,56862],{},", live presence (\"who else is looking at this?\"), and multi-user access grants. If this is your first noy-db app, read ",[1007,89411,89413],{"href":89412},".\u002Fpersonal-notebook","Recipe 1 — Personal encrypted notebook"," first; this recipe builds on the same core.",[1022,89416,58276],{"id":58275},[1338,89418,89419,89427],{},[1341,89420,89421],{},[1344,89422,89423,89425],{},[1347,89424,21181],{},[1347,89426,58287],{},[1357,89428,89429,89449,89466,89484,89497],{},[1344,89430,89431,89437],{},[1362,89432,89433,17904,89435],{},[941,89434,21950],{},[941,89436,73018],{},[1362,89438,89439,89440,968,89443,4765,89446],{},"Conflict-free merge for collections declared with ",[941,89441,89442],{},"crdt: 'lww-map'",[941,89444,89445],{},"'rga'",[941,89447,89448],{},"'yjs'",[1344,89450,89451,89457],{},[1362,89452,89453,17904,89455],{},[941,89454,4676],{},[941,89456,4672],{},[1362,89458,89459,1261,89461,89463,89464],{},[941,89460,20591],{},[941,89462,20594],{}," replication against a remote store, plus ",[941,89465,21307],{},[1344,89467,89468,89474],{},[1362,89469,89470,17904,89472],{},[941,89471,3197],{},[941,89473,4680],{},[1362,89475,89476,89477,1261,89479,1261,89481],{},"Multi-user access: ",[941,89478,21197],{},[941,89480,21200],{},[941,89482,89483],{},"db.listUsers()",[1344,89485,89486,89492],{},[1362,89487,89488,17904,89490],{},[941,89489,21321],{},[941,89491,62043],{},[1362,89493,89494,89496],{},[941,89495,20690],{}," enforcement: idle and absolute timeouts, lock-on-background",[1344,89498,89499,89505],{},[1362,89500,89501,17904,89503],{},[941,89502,6719],{},[941,89504,4151],{},[1362,89506,89507],{},"(Optional) an audit trail of every change — not used below",[937,89509,89510,89511,89513,89514,968,89516,968,89518,89520],{},"Reactive UI updates need no service at all: ",[941,89512,89374],{}," returns a live query that re-runs on every mutation, and the framework bindings (",[941,89515,38573],{},[941,89517,42079],{},[941,89519,39180],{},", …) build on it.",[1022,89522,58394],{"id":58393},[937,89524,89525,89526,89528],{},"Run this same setup on every device. The local store keeps the app offline-first; the ",[941,89527,864],{}," target is the remote the devices replicate through — it only ever receives ciphertext.",[2217,89530,89532],{"className":2219,"code":89531,"language":2221,"meta":1037,"style":1037},"import { createNoydb } from '@noy-db\u002Fhub'\nimport { withCrdt } from '@noy-db\u002Fhub\u002Fcrdt'\nimport { withSync } from '@noy-db\u002Fhub\u002Fsync'\nimport { withTeam } from '@noy-db\u002Fhub\u002Fteam'\nimport { withSession } from '@noy-db\u002Fhub\u002Fsession'\nimport { toBrowserIdb } from '@noy-db\u002Fto-browser-idb'\nimport { toCloudflareR2 } from '@noy-db\u002Fto-cloudflare-r2'\n\n\u002F\u002F The shared remote — any store adapter works (S3, Postgres, a synced\n\u002F\u002F file directory, …). Cloudflare R2 shown here.\n\u002F\u002F Never put R2 keys in browser code. The callback fetches short-lived\n\u002F\u002F `kind: 'aws'` credentials from your own server, which holds the real keys;\n\u002F\u002F the store re-invokes it near `expiresAt`.\nconst remote = toCloudflareR2({\n  accountId: 'your-cloudflare-account-id',\n  bucket: 'collab-app',\n  credentials: async () => (await fetch('\u002Fapi\u002Fr2-credentials')).json(),\n})\n\nconst db = await createNoydb({\n  store: toBrowserIdb(),                \u002F\u002F local, offline-first cache\n  sync: { store: remote, role: 'sync-peer' }, \u002F\u002F bidirectional push\u002Fpull\n  user: 'alice',\n  secret: 'correct-horse-battery-staple',  \u002F\u002F secret — derives the encryption keys\n\n  crdtStrategy: withCrdt(),\n  syncStrategy: withSync(),\n  teamStrategy: withTeam(),\n  sessionStrategy: withSession(),\n\n  sessionPolicy: {\n    idleTimeoutMs: 15 * 60_000,        \u002F\u002F lock after 15 min idle\n    absoluteTimeoutMs: 8 * 60 * 60_000, \u002F\u002F …and after 8 h regardless\n    lockOnBackground: true,             \u002F\u002F lock when the tab is backgrounded\n  },\n})\n",[941,89533,89534,89552,89571,89590,89608,89626,89644,89663,89667,89672,89677,89682,89687,89692,89706,89722,89737,89776,89782,89786,89802,89817,89849,89863,89880,89884,89897,89910,89922,89934,89938,89946,89964,89987,90001,90005],{"__ignoreMap":1037},[2225,89535,89536,89538,89540,89542,89544,89546,89548,89550],{"class":2227,"line":2228},[2225,89537,2232],{"class":2231},[2225,89539,2236],{"class":2235},[2225,89541,2744],{"class":2239},[2225,89543,2243],{"class":2235},[2225,89545,2246],{"class":2231},[2225,89547,2249],{"class":2235},[2225,89549,1086],{"class":2252},[2225,89551,2256],{"class":2235},[2225,89553,89554,89556,89558,89561,89563,89565,89567,89569],{"class":2227,"line":1041},[2225,89555,2232],{"class":2231},[2225,89557,2236],{"class":2235},[2225,89559,89560],{"class":2239}," withCrdt",[2225,89562,2243],{"class":2235},[2225,89564,2246],{"class":2231},[2225,89566,2249],{"class":2235},[2225,89568,73018],{"class":2252},[2225,89570,2256],{"class":2235},[2225,89572,89573,89575,89577,89580,89582,89584,89586,89588],{"class":2227,"line":1038},[2225,89574,2232],{"class":2231},[2225,89576,2236],{"class":2235},[2225,89578,89579],{"class":2239}," withSync",[2225,89581,2243],{"class":2235},[2225,89583,2246],{"class":2231},[2225,89585,2249],{"class":2235},[2225,89587,4672],{"class":2252},[2225,89589,2256],{"class":2235},[2225,89591,89592,89594,89596,89598,89600,89602,89604,89606],{"class":2227,"line":2288},[2225,89593,2232],{"class":2231},[2225,89595,2236],{"class":2235},[2225,89597,10373],{"class":2239},[2225,89599,2243],{"class":2235},[2225,89601,2246],{"class":2231},[2225,89603,2249],{"class":2235},[2225,89605,4680],{"class":2252},[2225,89607,2256],{"class":2235},[2225,89609,89610,89612,89614,89616,89618,89620,89622,89624],{"class":2227,"line":2304},[2225,89611,2232],{"class":2231},[2225,89613,2236],{"class":2235},[2225,89615,62034],{"class":2239},[2225,89617,2243],{"class":2235},[2225,89619,2246],{"class":2231},[2225,89621,2249],{"class":2235},[2225,89623,62043],{"class":2252},[2225,89625,2256],{"class":2235},[2225,89627,89628,89630,89632,89634,89636,89638,89640,89642],{"class":2227,"line":2356},[2225,89629,2232],{"class":2231},[2225,89631,2236],{"class":2235},[2225,89633,34763],{"class":2239},[2225,89635,2243],{"class":2235},[2225,89637,2246],{"class":2231},[2225,89639,2249],{"class":2235},[2225,89641,9573],{"class":2252},[2225,89643,2256],{"class":2235},[2225,89645,89646,89648,89650,89653,89655,89657,89659,89661],{"class":2227,"line":2830},[2225,89647,2232],{"class":2231},[2225,89649,2236],{"class":2235},[2225,89651,89652],{"class":2239}," toCloudflareR2",[2225,89654,2243],{"class":2235},[2225,89656,2246],{"class":2231},[2225,89658,2249],{"class":2235},[2225,89660,49123],{"class":2252},[2225,89662,2256],{"class":2235},[2225,89664,89665],{"class":2227,"line":2861},[2225,89666,2285],{"emptyLinePlaceholder":2284},[2225,89668,89669],{"class":2227,"line":4246},[2225,89670,89671],{"class":2763},"\u002F\u002F The shared remote — any store adapter works (S3, Postgres, a synced\n",[2225,89673,89674],{"class":2227,"line":4253},[2225,89675,89676],{"class":2763},"\u002F\u002F file directory, …). Cloudflare R2 shown here.\n",[2225,89678,89679],{"class":2227,"line":4259},[2225,89680,89681],{"class":2763},"\u002F\u002F Never put R2 keys in browser code. The callback fetches short-lived\n",[2225,89683,89684],{"class":2227,"line":6548},[2225,89685,89686],{"class":2763},"\u002F\u002F `kind: 'aws'` credentials from your own server, which holds the real keys;\n",[2225,89688,89689],{"class":2227,"line":6576},[2225,89690,89691],{"class":2763},"\u002F\u002F the store re-invokes it near `expiresAt`.\n",[2225,89693,89694,89696,89698,89700,89702,89704],{"class":2227,"line":6582},[2225,89695,2775],{"class":2774},[2225,89697,35196],{"class":2239},[2225,89699,2781],{"class":2235},[2225,89701,89652],{"class":2291},[2225,89703,2789],{"class":2239},[2225,89705,4200],{"class":2235},[2225,89707,89708,89711,89713,89715,89718,89720],{"class":2227,"line":6587},[2225,89709,89710],{"class":2307},"  accountId",[2225,89712,2311],{"class":2235},[2225,89714,2249],{"class":2235},[2225,89716,89717],{"class":2252},"your-cloudflare-account-id",[2225,89719,2805],{"class":2235},[2225,89721,1707],{"class":2235},[2225,89723,89724,89726,89728,89730,89733,89735],{"class":2227,"line":7174},[2225,89725,69265],{"class":2307},[2225,89727,2311],{"class":2235},[2225,89729,2249],{"class":2235},[2225,89731,89732],{"class":2252},"collab-app",[2225,89734,2805],{"class":2235},[2225,89736,1707],{"class":2235},[2225,89738,89739,89742,89744,89746,89748,89750,89752,89754,89757,89759,89761,89764,89766,89768,89770,89772,89774],{"class":2227,"line":7180},[2225,89740,89741],{"class":2291},"  credentials",[2225,89743,2311],{"class":2235},[2225,89745,38717],{"class":2774},[2225,89747,13130],{"class":2235},[2225,89749,8116],{"class":2774},[2225,89751,953],{"class":2239},[2225,89753,6500],{"class":2231},[2225,89755,89756],{"class":2291}," fetch",[2225,89758,2789],{"class":2239},[2225,89760,2805],{"class":2235},[2225,89762,89763],{"class":2252},"\u002Fapi\u002Fr2-credentials",[2225,89765,2805],{"class":2235},[2225,89767,8454],{"class":2239},[2225,89769,1520],{"class":2235},[2225,89771,141],{"class":2291},[2225,89773,4228],{"class":2239},[2225,89775,1707],{"class":2235},[2225,89777,89778,89780],{"class":2227,"line":7220},[2225,89779,2359],{"class":2235},[2225,89781,2362],{"class":2239},[2225,89783,89784],{"class":2227,"line":7225},[2225,89785,2285],{"emptyLinePlaceholder":2284},[2225,89787,89788,89790,89792,89794,89796,89798,89800],{"class":2227,"line":7231},[2225,89789,2775],{"class":2774},[2225,89791,2778],{"class":2239},[2225,89793,2781],{"class":2235},[2225,89795,2784],{"class":2231},[2225,89797,2744],{"class":2291},[2225,89799,2789],{"class":2239},[2225,89801,4200],{"class":2235},[2225,89803,89804,89806,89808,89810,89812,89814],{"class":2227,"line":7267},[2225,89805,15608],{"class":2307},[2225,89807,2311],{"class":2235},[2225,89809,34763],{"class":2291},[2225,89811,4228],{"class":2239},[2225,89813,2298],{"class":2235},[2225,89815,89816],{"class":2763},"                \u002F\u002F local, offline-first cache\n",[2225,89818,89819,89821,89823,89825,89827,89829,89832,89834,89836,89838,89840,89842,89844,89846],{"class":2227,"line":7272},[2225,89820,34919],{"class":2307},[2225,89822,2311],{"class":2235},[2225,89824,2236],{"class":2235},[2225,89826,7564],{"class":2307},[2225,89828,2311],{"class":2235},[2225,89830,89831],{"class":2239}," remote",[2225,89833,2298],{"class":2235},[2225,89835,34944],{"class":2307},[2225,89837,2311],{"class":2235},[2225,89839,2249],{"class":2235},[2225,89841,34951],{"class":2252},[2225,89843,2805],{"class":2235},[2225,89845,10965],{"class":2235},[2225,89847,89848],{"class":2763}," \u002F\u002F bidirectional push\u002Fpull\n",[2225,89850,89851,89853,89855,89857,89859,89861],{"class":2227,"line":7278},[2225,89852,4205],{"class":2307},[2225,89854,2311],{"class":2235},[2225,89856,2249],{"class":2235},[2225,89858,6408],{"class":2252},[2225,89860,2805],{"class":2235},[2225,89862,1707],{"class":2235},[2225,89864,89865,89867,89869,89871,89873,89875,89877],{"class":2227,"line":7303},[2225,89866,10423],{"class":2307},[2225,89868,2311],{"class":2235},[2225,89870,2249],{"class":2235},[2225,89872,10202],{"class":2252},[2225,89874,2805],{"class":2235},[2225,89876,2298],{"class":2235},[2225,89878,89879],{"class":2763},"  \u002F\u002F secret — derives the encryption keys\n",[2225,89881,89882],{"class":2227,"line":7308},[2225,89883,2285],{"emptyLinePlaceholder":2284},[2225,89885,89886,89889,89891,89893,89895],{"class":2227,"line":7314},[2225,89887,89888],{"class":2307},"  crdtStrategy",[2225,89890,2311],{"class":2235},[2225,89892,89560],{"class":2291},[2225,89894,4228],{"class":2239},[2225,89896,1707],{"class":2235},[2225,89898,89899,89902,89904,89906,89908],{"class":2227,"line":7348},[2225,89900,89901],{"class":2307},"  syncStrategy",[2225,89903,2311],{"class":2235},[2225,89905,89579],{"class":2291},[2225,89907,4228],{"class":2239},[2225,89909,1707],{"class":2235},[2225,89911,89912,89914,89916,89918,89920],{"class":2227,"line":8613},[2225,89913,10439],{"class":2307},[2225,89915,2311],{"class":2235},[2225,89917,10373],{"class":2291},[2225,89919,4228],{"class":2239},[2225,89921,1707],{"class":2235},[2225,89923,89924,89926,89928,89930,89932],{"class":2227,"line":8633},[2225,89925,62228],{"class":2307},[2225,89927,2311],{"class":2235},[2225,89929,62034],{"class":2291},[2225,89931,4228],{"class":2239},[2225,89933,1707],{"class":2235},[2225,89935,89936],{"class":2227,"line":8662},[2225,89937,2285],{"emptyLinePlaceholder":2284},[2225,89939,89940,89942,89944],{"class":2227,"line":8747},[2225,89941,62245],{"class":2307},[2225,89943,2311],{"class":2235},[2225,89945,2301],{"class":2235},[2225,89947,89948,89951,89953,89955,89957,89959,89961],{"class":2227,"line":8752},[2225,89949,89950],{"class":2307},"    idleTimeoutMs",[2225,89952,2311],{"class":2235},[2225,89954,45248],{"class":6527},[2225,89956,2263],{"class":2235},[2225,89958,19001],{"class":6527},[2225,89960,2298],{"class":2235},[2225,89962,89963],{"class":2763},"        \u002F\u002F lock after 15 min idle\n",[2225,89965,89966,89969,89971,89974,89976,89978,89980,89982,89984],{"class":2227,"line":8768},[2225,89967,89968],{"class":2307},"    absoluteTimeoutMs",[2225,89970,2311],{"class":2235},[2225,89972,89973],{"class":6527}," 8",[2225,89975,2263],{"class":2235},[2225,89977,44452],{"class":6527},[2225,89979,2263],{"class":2235},[2225,89981,19001],{"class":6527},[2225,89983,2298],{"class":2235},[2225,89985,89986],{"class":2763}," \u002F\u002F …and after 8 h regardless\n",[2225,89988,89989,89992,89994,89996,89998],{"class":2227,"line":8785},[2225,89990,89991],{"class":2307},"    lockOnBackground",[2225,89993,2311],{"class":2235},[2225,89995,2322],{"class":2321},[2225,89997,2298],{"class":2235},[2225,89999,90000],{"class":2763},"             \u002F\u002F lock when the tab is backgrounded\n",[2225,90002,90003],{"class":2227,"line":8815},[2225,90004,19017],{"class":2235},[2225,90006,90007,90009],{"class":2227,"line":8854},[2225,90008,2359],{"class":2235},[2225,90010,2362],{"class":2239},[937,90012,90013,90014,90017],{},"Each service is a tree-shakeable opt-in: if you skip a ",[941,90015,90016],{},"*Strategy"," entry, its code stays out of your bundle, and calling its surface throws an error that names the subpath to import.",[1022,90019,90021],{"id":90020},"define-a-crdt-backed-collection","Define a CRDT-backed collection",[937,90023,90024,90025,90030,90031,1952,90033,90035,90036,90039],{},"For rich-text collaboration, use the ",[1007,90026,90029],{"href":90027,"rel":90028},"https:\u002F\u002Fyjs.dev",[3371],"Yjs"," binding from ",[941,90032,42682],{},[941,90034,37809],{}," wraps a ",[941,90037,90038],{},"crdt: 'yjs'"," collection and stores each document as an encrypted Yjs update — the storage backend sees neither the text nor its structure.",[2217,90041,90043],{"className":2219,"code":90042,"language":2221,"meta":1037,"style":1037},"import { yjsCollection, yText, yMap } from '@noy-db\u002Fin-yjs'\n\nconst vault = await db.openVault('shared')\n\nconst docs = yjsCollection(vault, 'docs', {\n  yFields: { body: yText(), meta: yMap() },\n})\n\nconst doc = await docs.getYDoc('doc-1')   \u002F\u002F fresh empty Y.Doc if the record is new\ndoc.getText('body').insert(0, 'Hello, world')\ndoc.getMap('meta').set('author', 'alice')\nawait docs.putYDoc('doc-1', doc)\n\nconst reloaded = await docs.getYDoc('doc-1')\nconsole.log(reloaded.getText('body').toString())\n\u002F\u002F → \"Hello, world\"\nconsole.log(reloaded.getMap('meta').get('author'))\n\u002F\u002F → \"alice\"\n",[941,90044,90045,90072,90076,90103,90107,90132,90162,90168,90172,90203,90240,90282,90304,90308,90335,90366,90371,90409],{"__ignoreMap":1037},[2225,90046,90047,90049,90051,90053,90055,90057,90059,90062,90064,90066,90068,90070],{"class":2227,"line":2228},[2225,90048,2232],{"class":2231},[2225,90050,2236],{"class":2235},[2225,90052,42736],{"class":2239},[2225,90054,2298],{"class":2235},[2225,90056,42741],{"class":2239},[2225,90058,2298],{"class":2235},[2225,90060,90061],{"class":2239}," yMap",[2225,90063,2243],{"class":2235},[2225,90065,2246],{"class":2231},[2225,90067,2249],{"class":2235},[2225,90069,42682],{"class":2252},[2225,90071,2256],{"class":2235},[2225,90073,90074],{"class":2227,"line":1041},[2225,90075,2285],{"emptyLinePlaceholder":2284},[2225,90077,90078,90080,90082,90084,90086,90088,90090,90092,90094,90096,90099,90101],{"class":2227,"line":1038},[2225,90079,2775],{"class":2774},[2225,90081,2835],{"class":2239},[2225,90083,2781],{"class":2235},[2225,90085,2784],{"class":2231},[2225,90087,2842],{"class":2239},[2225,90089,1520],{"class":2235},[2225,90091,2847],{"class":2291},[2225,90093,2789],{"class":2239},[2225,90095,2805],{"class":2235},[2225,90097,90098],{"class":2252},"shared",[2225,90100,2805],{"class":2235},[2225,90102,2362],{"class":2239},[2225,90104,90105],{"class":2227,"line":2288},[2225,90106,2285],{"emptyLinePlaceholder":2284},[2225,90108,90109,90111,90114,90116,90118,90120,90122,90124,90126,90128,90130],{"class":2227,"line":2304},[2225,90110,2775],{"class":2774},[2225,90112,90113],{"class":2239}," docs ",[2225,90115,2781],{"class":2235},[2225,90117,42736],{"class":2291},[2225,90119,30266],{"class":2239},[2225,90121,2298],{"class":2235},[2225,90123,2249],{"class":2235},[2225,90125,69487],{"class":2252},[2225,90127,2805],{"class":2235},[2225,90129,2298],{"class":2235},[2225,90131,2301],{"class":2235},[2225,90133,90134,90137,90139,90141,90143,90145,90147,90149,90151,90154,90156,90158,90160],{"class":2227,"line":2356},[2225,90135,90136],{"class":2307},"  yFields",[2225,90138,2311],{"class":2235},[2225,90140,2236],{"class":2235},[2225,90142,42791],{"class":2307},[2225,90144,2311],{"class":2235},[2225,90146,42741],{"class":2291},[2225,90148,4228],{"class":2239},[2225,90150,2298],{"class":2235},[2225,90152,90153],{"class":2307}," meta",[2225,90155,2311],{"class":2235},[2225,90157,90061],{"class":2291},[2225,90159,19132],{"class":2239},[2225,90161,19008],{"class":2235},[2225,90163,90164,90166],{"class":2227,"line":2830},[2225,90165,2359],{"class":2235},[2225,90167,2362],{"class":2239},[2225,90169,90170],{"class":2227,"line":2861},[2225,90171,2285],{"emptyLinePlaceholder":2284},[2225,90173,90174,90176,90178,90180,90182,90185,90187,90189,90191,90193,90196,90198,90200],{"class":2227,"line":4246},[2225,90175,2775],{"class":2774},[2225,90177,42810],{"class":2239},[2225,90179,2781],{"class":2235},[2225,90181,2784],{"class":2231},[2225,90183,90184],{"class":2239}," docs",[2225,90186,1520],{"class":2235},[2225,90188,42822],{"class":2291},[2225,90190,2789],{"class":2239},[2225,90192,2805],{"class":2235},[2225,90194,90195],{"class":2252},"doc-1",[2225,90197,2805],{"class":2235},[2225,90199,71781],{"class":2239},[2225,90201,90202],{"class":2763},"\u002F\u002F fresh empty Y.Doc if the record is new\n",[2225,90204,90205,90207,90209,90211,90213,90215,90217,90219,90221,90223,90225,90227,90229,90231,90233,90236,90238],{"class":2227,"line":4253},[2225,90206,42838],{"class":2239},[2225,90208,1520],{"class":2235},[2225,90210,42843],{"class":2291},[2225,90212,2789],{"class":2239},[2225,90214,2805],{"class":2235},[2225,90216,42850],{"class":2252},[2225,90218,2805],{"class":2235},[2225,90220,1381],{"class":2239},[2225,90222,1520],{"class":2235},[2225,90224,42859],{"class":2291},[2225,90226,2789],{"class":2239},[2225,90228,5402],{"class":6527},[2225,90230,2298],{"class":2235},[2225,90232,2249],{"class":2235},[2225,90234,90235],{"class":2252},"Hello, world",[2225,90237,2805],{"class":2235},[2225,90239,2362],{"class":2239},[2225,90241,90242,90244,90246,90249,90251,90253,90255,90257,90259,90261,90263,90265,90267,90270,90272,90274,90276,90278,90280],{"class":2227,"line":4259},[2225,90243,42838],{"class":2239},[2225,90245,1520],{"class":2235},[2225,90247,90248],{"class":2291},"getMap",[2225,90250,2789],{"class":2239},[2225,90252,2805],{"class":2235},[2225,90254,17587],{"class":2252},[2225,90256,2805],{"class":2235},[2225,90258,1381],{"class":2239},[2225,90260,1520],{"class":2235},[2225,90262,8528],{"class":2291},[2225,90264,2789],{"class":2239},[2225,90266,2805],{"class":2235},[2225,90268,90269],{"class":2252},"author",[2225,90271,2805],{"class":2235},[2225,90273,2298],{"class":2235},[2225,90275,2249],{"class":2235},[2225,90277,6408],{"class":2252},[2225,90279,2805],{"class":2235},[2225,90281,2362],{"class":2239},[2225,90283,90284,90286,90288,90290,90292,90294,90296,90298,90300,90302],{"class":2227,"line":6548},[2225,90285,6500],{"class":2231},[2225,90287,90184],{"class":2239},[2225,90289,1520],{"class":2235},[2225,90291,42885],{"class":2291},[2225,90293,2789],{"class":2239},[2225,90295,2805],{"class":2235},[2225,90297,90195],{"class":2252},[2225,90299,2805],{"class":2235},[2225,90301,2298],{"class":2235},[2225,90303,42898],{"class":2239},[2225,90305,90306],{"class":2227,"line":6576},[2225,90307,2285],{"emptyLinePlaceholder":2284},[2225,90309,90310,90312,90315,90317,90319,90321,90323,90325,90327,90329,90331,90333],{"class":2227,"line":6582},[2225,90311,2775],{"class":2774},[2225,90313,90314],{"class":2239}," reloaded ",[2225,90316,2781],{"class":2235},[2225,90318,2784],{"class":2231},[2225,90320,90184],{"class":2239},[2225,90322,1520],{"class":2235},[2225,90324,42822],{"class":2291},[2225,90326,2789],{"class":2239},[2225,90328,2805],{"class":2235},[2225,90330,90195],{"class":2252},[2225,90332,2805],{"class":2235},[2225,90334,2362],{"class":2239},[2225,90336,90337,90339,90341,90343,90346,90348,90350,90352,90354,90356,90358,90360,90362,90364],{"class":2227,"line":6587},[2225,90338,28031],{"class":2239},[2225,90340,1520],{"class":2235},[2225,90342,27100],{"class":2291},[2225,90344,90345],{"class":2239},"(reloaded",[2225,90347,1520],{"class":2235},[2225,90349,42843],{"class":2291},[2225,90351,2789],{"class":2239},[2225,90353,2805],{"class":2235},[2225,90355,42850],{"class":2252},[2225,90357,2805],{"class":2235},[2225,90359,1381],{"class":2239},[2225,90361,1520],{"class":2235},[2225,90363,80541],{"class":2291},[2225,90365,34864],{"class":2239},[2225,90367,90368],{"class":2227,"line":7174},[2225,90369,90370],{"class":2763},"\u002F\u002F → \"Hello, world\"\n",[2225,90372,90373,90375,90377,90379,90381,90383,90385,90387,90389,90391,90393,90395,90397,90399,90401,90403,90405,90407],{"class":2227,"line":7180},[2225,90374,28031],{"class":2239},[2225,90376,1520],{"class":2235},[2225,90378,27100],{"class":2291},[2225,90380,90345],{"class":2239},[2225,90382,1520],{"class":2235},[2225,90384,90248],{"class":2291},[2225,90386,2789],{"class":2239},[2225,90388,2805],{"class":2235},[2225,90390,17587],{"class":2252},[2225,90392,2805],{"class":2235},[2225,90394,1381],{"class":2239},[2225,90396,1520],{"class":2235},[2225,90398,3158],{"class":2291},[2225,90400,2789],{"class":2239},[2225,90402,2805],{"class":2235},[2225,90404,90269],{"class":2252},[2225,90406,2805],{"class":2235},[2225,90408,8606],{"class":2239},[2225,90410,90411],{"class":2227,"line":7220},[2225,90412,90413],{"class":2763},"\u002F\u002F → \"alice\"\n",[937,90415,90416,90417,90419,90420,56825,90423,90425,90426,17904,90428,90430],{},"Without ",[941,90418,21950],{},", the first ",[941,90421,90422],{},"putYDoc()",[941,90424,90038],{}," collection rejects with an error pointing at ",[941,90427,21950],{},[941,90429,73018],{}," — features you have not opted into fail loudly.",[937,90432,90433,90434,90437,90438,90441],{},"For structured (non-rich-text) records, the two other CRDT modes need no extra package: declare ",[941,90435,90436],{},"vault.collection\u003CT>('name', { crdt: 'lww-map' })"," (last-writer-wins per field) or ",[941,90439,90440],{},"{ crdt: 'rga' }"," (replicated growable array).",[1022,90443,90445],{"id":90444},"two-collaborators-edit-concurrently-the-merge-converges","Two collaborators edit concurrently — the merge converges",[937,90447,90448],{},"Two people fork the same document, edit independently, and exchange Yjs update buffers. The CRDT guarantees both end up with the identical merged text — no lost writes, no manual conflict dialog.",[2217,90450,90452],{"className":2219,"code":90451,"language":2221,"meta":1037,"style":1037},"import * as Y from 'yjs'\n\n\u002F\u002F Seed a baseline both collaborators fork from.\nconst baseline = await docs.getYDoc('doc-shared')\nbaseline.getText('body').insert(0, 'Hello')\nawait docs.putYDoc('doc-shared', baseline)\n\n\u002F\u002F Alice — appends.\nconst docA = await docs.getYDoc('doc-shared')\ndocA.getText('body').insert(docA.getText('body').length, ' from Alice')\n\n\u002F\u002F Bob — independently prepends.\nconst docB = await docs.getYDoc('doc-shared')\ndocB.getText('body').insert(0, 'Hi! ')\n\n\u002F\u002F Merge by exchanging update buffers — the CRDT contract.\nY.applyUpdate(docA, Y.encodeStateAsUpdate(docB))\nY.applyUpdate(docB, Y.encodeStateAsUpdate(docA))\n\nconsole.log(docA.getText('body').toString())\n\u002F\u002F → \"Hi! Hello from Alice\"\nconsole.log(docA.getText('body').toString() === docB.getText('body').toString())\n\u002F\u002F → true\n\n\u002F\u002F Persist the merged state — it round-trips through encryption intact.\nawait docs.putYDoc('doc-shared', docA)\n",[941,90453,90454,90473,90477,90482,90510,90548,90571,90575,90580,90607,90662,90666,90671,90698,90736,90740,90745,90770,90792,90796,90826,90831,90886,90890,90894,90899],{"__ignoreMap":1037},[2225,90455,90456,90458,90460,90462,90465,90467,90469,90471],{"class":2227,"line":2228},[2225,90457,2232],{"class":2231},[2225,90459,2263],{"class":2235},[2225,90461,2266],{"class":2231},[2225,90463,90464],{"class":2239}," Y ",[2225,90466,2272],{"class":2231},[2225,90468,2249],{"class":2235},[2225,90470,287],{"class":2252},[2225,90472,2256],{"class":2235},[2225,90474,90475],{"class":2227,"line":1041},[2225,90476,2285],{"emptyLinePlaceholder":2284},[2225,90478,90479],{"class":2227,"line":1038},[2225,90480,90481],{"class":2763},"\u002F\u002F Seed a baseline both collaborators fork from.\n",[2225,90483,90484,90486,90489,90491,90493,90495,90497,90499,90501,90503,90506,90508],{"class":2227,"line":2288},[2225,90485,2775],{"class":2774},[2225,90487,90488],{"class":2239}," baseline ",[2225,90490,2781],{"class":2235},[2225,90492,2784],{"class":2231},[2225,90494,90184],{"class":2239},[2225,90496,1520],{"class":2235},[2225,90498,42822],{"class":2291},[2225,90500,2789],{"class":2239},[2225,90502,2805],{"class":2235},[2225,90504,90505],{"class":2252},"doc-shared",[2225,90507,2805],{"class":2235},[2225,90509,2362],{"class":2239},[2225,90511,90512,90515,90517,90519,90521,90523,90525,90527,90529,90531,90533,90535,90537,90539,90541,90544,90546],{"class":2227,"line":2304},[2225,90513,90514],{"class":2239},"baseline",[2225,90516,1520],{"class":2235},[2225,90518,42843],{"class":2291},[2225,90520,2789],{"class":2239},[2225,90522,2805],{"class":2235},[2225,90524,42850],{"class":2252},[2225,90526,2805],{"class":2235},[2225,90528,1381],{"class":2239},[2225,90530,1520],{"class":2235},[2225,90532,42859],{"class":2291},[2225,90534,2789],{"class":2239},[2225,90536,5402],{"class":6527},[2225,90538,2298],{"class":2235},[2225,90540,2249],{"class":2235},[2225,90542,90543],{"class":2252},"Hello",[2225,90545,2805],{"class":2235},[2225,90547,2362],{"class":2239},[2225,90549,90550,90552,90554,90556,90558,90560,90562,90564,90566,90568],{"class":2227,"line":2356},[2225,90551,6500],{"class":2231},[2225,90553,90184],{"class":2239},[2225,90555,1520],{"class":2235},[2225,90557,42885],{"class":2291},[2225,90559,2789],{"class":2239},[2225,90561,2805],{"class":2235},[2225,90563,90505],{"class":2252},[2225,90565,2805],{"class":2235},[2225,90567,2298],{"class":2235},[2225,90569,90570],{"class":2239}," baseline)\n",[2225,90572,90573],{"class":2227,"line":2830},[2225,90574,2285],{"emptyLinePlaceholder":2284},[2225,90576,90577],{"class":2227,"line":2861},[2225,90578,90579],{"class":2763},"\u002F\u002F Alice — appends.\n",[2225,90581,90582,90584,90587,90589,90591,90593,90595,90597,90599,90601,90603,90605],{"class":2227,"line":4246},[2225,90583,2775],{"class":2774},[2225,90585,90586],{"class":2239}," docA ",[2225,90588,2781],{"class":2235},[2225,90590,2784],{"class":2231},[2225,90592,90184],{"class":2239},[2225,90594,1520],{"class":2235},[2225,90596,42822],{"class":2291},[2225,90598,2789],{"class":2239},[2225,90600,2805],{"class":2235},[2225,90602,90505],{"class":2252},[2225,90604,2805],{"class":2235},[2225,90606,2362],{"class":2239},[2225,90608,90609,90612,90614,90616,90618,90620,90622,90624,90626,90628,90630,90633,90635,90637,90639,90641,90643,90645,90647,90649,90651,90653,90655,90658,90660],{"class":2227,"line":4253},[2225,90610,90611],{"class":2239},"docA",[2225,90613,1520],{"class":2235},[2225,90615,42843],{"class":2291},[2225,90617,2789],{"class":2239},[2225,90619,2805],{"class":2235},[2225,90621,42850],{"class":2252},[2225,90623,2805],{"class":2235},[2225,90625,1381],{"class":2239},[2225,90627,1520],{"class":2235},[2225,90629,42859],{"class":2291},[2225,90631,90632],{"class":2239},"(docA",[2225,90634,1520],{"class":2235},[2225,90636,42843],{"class":2291},[2225,90638,2789],{"class":2239},[2225,90640,2805],{"class":2235},[2225,90642,42850],{"class":2252},[2225,90644,2805],{"class":2235},[2225,90646,1381],{"class":2239},[2225,90648,1520],{"class":2235},[2225,90650,8742],{"class":2239},[2225,90652,2298],{"class":2235},[2225,90654,2249],{"class":2235},[2225,90656,90657],{"class":2252}," from Alice",[2225,90659,2805],{"class":2235},[2225,90661,2362],{"class":2239},[2225,90663,90664],{"class":2227,"line":4259},[2225,90665,2285],{"emptyLinePlaceholder":2284},[2225,90667,90668],{"class":2227,"line":6548},[2225,90669,90670],{"class":2763},"\u002F\u002F Bob — independently prepends.\n",[2225,90672,90673,90675,90678,90680,90682,90684,90686,90688,90690,90692,90694,90696],{"class":2227,"line":6576},[2225,90674,2775],{"class":2774},[2225,90676,90677],{"class":2239}," docB ",[2225,90679,2781],{"class":2235},[2225,90681,2784],{"class":2231},[2225,90683,90184],{"class":2239},[2225,90685,1520],{"class":2235},[2225,90687,42822],{"class":2291},[2225,90689,2789],{"class":2239},[2225,90691,2805],{"class":2235},[2225,90693,90505],{"class":2252},[2225,90695,2805],{"class":2235},[2225,90697,2362],{"class":2239},[2225,90699,90700,90703,90705,90707,90709,90711,90713,90715,90717,90719,90721,90723,90725,90727,90729,90732,90734],{"class":2227,"line":6582},[2225,90701,90702],{"class":2239},"docB",[2225,90704,1520],{"class":2235},[2225,90706,42843],{"class":2291},[2225,90708,2789],{"class":2239},[2225,90710,2805],{"class":2235},[2225,90712,42850],{"class":2252},[2225,90714,2805],{"class":2235},[2225,90716,1381],{"class":2239},[2225,90718,1520],{"class":2235},[2225,90720,42859],{"class":2291},[2225,90722,2789],{"class":2239},[2225,90724,5402],{"class":6527},[2225,90726,2298],{"class":2235},[2225,90728,2249],{"class":2235},[2225,90730,90731],{"class":2252},"Hi! ",[2225,90733,2805],{"class":2235},[2225,90735,2362],{"class":2239},[2225,90737,90738],{"class":2227,"line":6587},[2225,90739,2285],{"emptyLinePlaceholder":2284},[2225,90741,90742],{"class":2227,"line":7174},[2225,90743,90744],{"class":2763},"\u002F\u002F Merge by exchanging update buffers — the CRDT contract.\n",[2225,90746,90747,90750,90752,90755,90757,90759,90762,90764,90767],{"class":2227,"line":7180},[2225,90748,90749],{"class":2239},"Y",[2225,90751,1520],{"class":2235},[2225,90753,90754],{"class":2291},"applyUpdate",[2225,90756,90632],{"class":2239},[2225,90758,2298],{"class":2235},[2225,90760,90761],{"class":2239}," Y",[2225,90763,1520],{"class":2235},[2225,90765,90766],{"class":2291},"encodeStateAsUpdate",[2225,90768,90769],{"class":2239},"(docB))\n",[2225,90771,90772,90774,90776,90778,90781,90783,90785,90787,90789],{"class":2227,"line":7220},[2225,90773,90749],{"class":2239},[2225,90775,1520],{"class":2235},[2225,90777,90754],{"class":2291},[2225,90779,90780],{"class":2239},"(docB",[2225,90782,2298],{"class":2235},[2225,90784,90761],{"class":2239},[2225,90786,1520],{"class":2235},[2225,90788,90766],{"class":2291},[2225,90790,90791],{"class":2239},"(docA))\n",[2225,90793,90794],{"class":2227,"line":7225},[2225,90795,2285],{"emptyLinePlaceholder":2284},[2225,90797,90798,90800,90802,90804,90806,90808,90810,90812,90814,90816,90818,90820,90822,90824],{"class":2227,"line":7231},[2225,90799,28031],{"class":2239},[2225,90801,1520],{"class":2235},[2225,90803,27100],{"class":2291},[2225,90805,90632],{"class":2239},[2225,90807,1520],{"class":2235},[2225,90809,42843],{"class":2291},[2225,90811,2789],{"class":2239},[2225,90813,2805],{"class":2235},[2225,90815,42850],{"class":2252},[2225,90817,2805],{"class":2235},[2225,90819,1381],{"class":2239},[2225,90821,1520],{"class":2235},[2225,90823,80541],{"class":2291},[2225,90825,34864],{"class":2239},[2225,90827,90828],{"class":2227,"line":7267},[2225,90829,90830],{"class":2763},"\u002F\u002F → \"Hi! Hello from Alice\"\n",[2225,90832,90833,90835,90837,90839,90841,90843,90845,90847,90849,90851,90853,90855,90857,90859,90861,90863,90866,90868,90870,90872,90874,90876,90878,90880,90882,90884],{"class":2227,"line":7272},[2225,90834,28031],{"class":2239},[2225,90836,1520],{"class":2235},[2225,90838,27100],{"class":2291},[2225,90840,90632],{"class":2239},[2225,90842,1520],{"class":2235},[2225,90844,42843],{"class":2291},[2225,90846,2789],{"class":2239},[2225,90848,2805],{"class":2235},[2225,90850,42850],{"class":2252},[2225,90852,2805],{"class":2235},[2225,90854,1381],{"class":2239},[2225,90856,1520],{"class":2235},[2225,90858,80541],{"class":2291},[2225,90860,19132],{"class":2239},[2225,90862,33083],{"class":2235},[2225,90864,90865],{"class":2239}," docB",[2225,90867,1520],{"class":2235},[2225,90869,42843],{"class":2291},[2225,90871,2789],{"class":2239},[2225,90873,2805],{"class":2235},[2225,90875,42850],{"class":2252},[2225,90877,2805],{"class":2235},[2225,90879,1381],{"class":2239},[2225,90881,1520],{"class":2235},[2225,90883,80541],{"class":2291},[2225,90885,34864],{"class":2239},[2225,90887,90888],{"class":2227,"line":7278},[2225,90889,77590],{"class":2763},[2225,90891,90892],{"class":2227,"line":7303},[2225,90893,2285],{"emptyLinePlaceholder":2284},[2225,90895,90896],{"class":2227,"line":7308},[2225,90897,90898],{"class":2763},"\u002F\u002F Persist the merged state — it round-trips through encryption intact.\n",[2225,90900,90901,90903,90905,90907,90909,90911,90913,90915,90917,90919],{"class":2227,"line":7314},[2225,90902,6500],{"class":2231},[2225,90904,90184],{"class":2239},[2225,90906,1520],{"class":2235},[2225,90908,42885],{"class":2291},[2225,90910,2789],{"class":2239},[2225,90912,2805],{"class":2235},[2225,90914,90505],{"class":2252},[2225,90916,2805],{"class":2235},[2225,90918,2298],{"class":2235},[2225,90920,90921],{"class":2239}," docA)\n",[937,90923,90924,90927,90928,90931],{},[941,90925,90926],{},"docs.applyUpdate(id, update)"," does the same merge against the ",[1092,90929,90930],{},"stored"," state in one call: it loads the record, applies the incoming update buffer, and persists the merged result.",[1022,90933,90935],{"id":90934},"replicate-between-devices-push-and-pull","Replicate between devices — push and pull",[937,90937,90938,90939,90942,90943,90946],{},"Each device runs the setup above against the same remote. ",[941,90940,90941],{},"db.push(vault)"," uploads locally-changed records; ",[941,90944,90945],{},"db.pull(vault)"," downloads remote changes.",[2217,90948,90950],{"className":2219,"code":90949,"language":2221,"meta":1037,"style":1037},"\u002F\u002F Device A\nconst docA2 = await docs.getYDoc('doc-1')\ndocA2.getText('body').insert(0, 'Edited on device A. ')\nawait docs.putYDoc('doc-1', docA2)\n\n\u002F\u002F put() marks the record dirty on a microtask — let it settle before pushing.\nawait new Promise((resolve) => setTimeout(resolve, 0))\n\nconsole.log(await db.push('shared'))\n\u002F\u002F → { pushed: 1, conflicts: [], errors: [] }\n",[941,90951,90952,90957,90984,91022,91045,91049,91054,91084,91088,91116],{"__ignoreMap":1037},[2225,90953,90954],{"class":2227,"line":2228},[2225,90955,90956],{"class":2763},"\u002F\u002F Device A\n",[2225,90958,90959,90961,90964,90966,90968,90970,90972,90974,90976,90978,90980,90982],{"class":2227,"line":1041},[2225,90960,2775],{"class":2774},[2225,90962,90963],{"class":2239}," docA2 ",[2225,90965,2781],{"class":2235},[2225,90967,2784],{"class":2231},[2225,90969,90184],{"class":2239},[2225,90971,1520],{"class":2235},[2225,90973,42822],{"class":2291},[2225,90975,2789],{"class":2239},[2225,90977,2805],{"class":2235},[2225,90979,90195],{"class":2252},[2225,90981,2805],{"class":2235},[2225,90983,2362],{"class":2239},[2225,90985,90986,90989,90991,90993,90995,90997,90999,91001,91003,91005,91007,91009,91011,91013,91015,91018,91020],{"class":2227,"line":1038},[2225,90987,90988],{"class":2239},"docA2",[2225,90990,1520],{"class":2235},[2225,90992,42843],{"class":2291},[2225,90994,2789],{"class":2239},[2225,90996,2805],{"class":2235},[2225,90998,42850],{"class":2252},[2225,91000,2805],{"class":2235},[2225,91002,1381],{"class":2239},[2225,91004,1520],{"class":2235},[2225,91006,42859],{"class":2291},[2225,91008,2789],{"class":2239},[2225,91010,5402],{"class":6527},[2225,91012,2298],{"class":2235},[2225,91014,2249],{"class":2235},[2225,91016,91017],{"class":2252},"Edited on device A. ",[2225,91019,2805],{"class":2235},[2225,91021,2362],{"class":2239},[2225,91023,91024,91026,91028,91030,91032,91034,91036,91038,91040,91042],{"class":2227,"line":2288},[2225,91025,6500],{"class":2231},[2225,91027,90184],{"class":2239},[2225,91029,1520],{"class":2235},[2225,91031,42885],{"class":2291},[2225,91033,2789],{"class":2239},[2225,91035,2805],{"class":2235},[2225,91037,90195],{"class":2252},[2225,91039,2805],{"class":2235},[2225,91041,2298],{"class":2235},[2225,91043,91044],{"class":2239}," docA2)\n",[2225,91046,91047],{"class":2227,"line":2304},[2225,91048,2285],{"emptyLinePlaceholder":2284},[2225,91050,91051],{"class":2227,"line":2356},[2225,91052,91053],{"class":2763},"\u002F\u002F put() marks the record dirty on a microtask — let it settle before pushing.\n",[2225,91055,91056,91058,91060,91062,91064,91066,91068,91070,91072,91075,91078,91080,91082],{"class":2227,"line":2830},[2225,91057,6500],{"class":2231},[2225,91059,8130],{"class":2235},[2225,91061,7079],{"class":6342},[2225,91063,2789],{"class":2239},[2225,91065,2789],{"class":2235},[2225,91067,54399],{"class":7052},[2225,91069,1381],{"class":2235},[2225,91071,8116],{"class":2774},[2225,91073,91074],{"class":2291}," setTimeout",[2225,91076,91077],{"class":2239},"(resolve",[2225,91079,2298],{"class":2235},[2225,91081,8464],{"class":6527},[2225,91083,8606],{"class":2239},[2225,91085,91086],{"class":2227,"line":2861},[2225,91087,2285],{"emptyLinePlaceholder":2284},[2225,91089,91090,91092,91094,91096,91098,91100,91102,91104,91106,91108,91110,91112,91114],{"class":2227,"line":4246},[2225,91091,28031],{"class":2239},[2225,91093,1520],{"class":2235},[2225,91095,27100],{"class":2291},[2225,91097,2789],{"class":2239},[2225,91099,6500],{"class":2231},[2225,91101,2842],{"class":2239},[2225,91103,1520],{"class":2235},[2225,91105,82367],{"class":2291},[2225,91107,2789],{"class":2239},[2225,91109,2805],{"class":2235},[2225,91111,90098],{"class":2252},[2225,91113,2805],{"class":2235},[2225,91115,8606],{"class":2239},[2225,91117,91118],{"class":2227,"line":4253},[2225,91119,91120],{"class":2763},"\u002F\u002F → { pushed: 1, conflicts: [], errors: [] }\n",[2217,91122,91124],{"className":2219,"code":91123,"language":2221,"meta":1037,"style":1037},"\u002F\u002F Device B (same setup, same remote)\nconsole.log(await db.pull('shared'))\n\u002F\u002F → { pulled: 1, conflicts: [], errors: [] }\n\nconst latest = await docs.getYDoc('doc-1')\nconsole.log(latest.getText('body').toString())\n\u002F\u002F → \"Edited on device A. Hello, world\"\n",[941,91125,91126,91131,91160,91165,91169,91196,91227],{"__ignoreMap":1037},[2225,91127,91128],{"class":2227,"line":2228},[2225,91129,91130],{"class":2763},"\u002F\u002F Device B (same setup, same remote)\n",[2225,91132,91133,91135,91137,91139,91141,91143,91145,91147,91150,91152,91154,91156,91158],{"class":2227,"line":1041},[2225,91134,28031],{"class":2239},[2225,91136,1520],{"class":2235},[2225,91138,27100],{"class":2291},[2225,91140,2789],{"class":2239},[2225,91142,6500],{"class":2231},[2225,91144,2842],{"class":2239},[2225,91146,1520],{"class":2235},[2225,91148,91149],{"class":2291},"pull",[2225,91151,2789],{"class":2239},[2225,91153,2805],{"class":2235},[2225,91155,90098],{"class":2252},[2225,91157,2805],{"class":2235},[2225,91159,8606],{"class":2239},[2225,91161,91162],{"class":2227,"line":1038},[2225,91163,91164],{"class":2763},"\u002F\u002F → { pulled: 1, conflicts: [], errors: [] }\n",[2225,91166,91167],{"class":2227,"line":2288},[2225,91168,2285],{"emptyLinePlaceholder":2284},[2225,91170,91171,91173,91176,91178,91180,91182,91184,91186,91188,91190,91192,91194],{"class":2227,"line":2304},[2225,91172,2775],{"class":2774},[2225,91174,91175],{"class":2239}," latest ",[2225,91177,2781],{"class":2235},[2225,91179,2784],{"class":2231},[2225,91181,90184],{"class":2239},[2225,91183,1520],{"class":2235},[2225,91185,42822],{"class":2291},[2225,91187,2789],{"class":2239},[2225,91189,2805],{"class":2235},[2225,91191,90195],{"class":2252},[2225,91193,2805],{"class":2235},[2225,91195,2362],{"class":2239},[2225,91197,91198,91200,91202,91204,91207,91209,91211,91213,91215,91217,91219,91221,91223,91225],{"class":2227,"line":2356},[2225,91199,28031],{"class":2239},[2225,91201,1520],{"class":2235},[2225,91203,27100],{"class":2291},[2225,91205,91206],{"class":2239},"(latest",[2225,91208,1520],{"class":2235},[2225,91210,42843],{"class":2291},[2225,91212,2789],{"class":2239},[2225,91214,2805],{"class":2235},[2225,91216,42850],{"class":2252},[2225,91218,2805],{"class":2235},[2225,91220,1381],{"class":2239},[2225,91222,1520],{"class":2235},[2225,91224,80541],{"class":2291},[2225,91226,34864],{"class":2239},[2225,91228,91229],{"class":2227,"line":2830},[2225,91230,91231],{"class":2763},"\u002F\u002F → \"Edited on device A. Hello, world\"\n",[937,91233,91234,91235,91237,91238,91241],{},"If both devices changed the ",[1092,91236,4664],{}," record before syncing, the ",[941,91239,91240],{},"conflicts"," array is non-empty and the conflict rules below decide the winner.",[1022,91243,91245],{"id":91244},"live-presence-see-who-else-is-here","Live presence — see who else is here",[937,91247,91248,91250,91251,91253],{},[941,91249,21307],{}," (enabled by ",[941,91252,4676],{},") gives you an encrypted ephemeral channel for awareness data — cursors, selections, \"Bob is typing…\".",[2217,91255,91257],{"className":2219,"code":91256,"language":2221,"meta":1037,"style":1037},"const presence = vault\n  .collection('docs')\n  .presence\u003C{ cursor: { x: number; y: number } }>()\n\n\u002F\u002F Broadcast my cursor (call again whenever it moves).\nawait presence.update({ cursor: { x: 120, y: 340 } })\n\n\u002F\u002F Subscribe to peers — the callback receives all active peers,\n\u002F\u002F excluding yourself and anyone stale (default: silent for 30 s).\nconst unsubscribe = presence.subscribe((peers) => {\n  for (const peer of peers) {\n    console.log(`${peer.userId}: cursor at`, peer.payload.cursor)\n  }\n})\n\u002F\u002F → \"bob: cursor at { x: 512, y: 88 }\"   (fires when bob calls update())\n\n\u002F\u002F When the document view unmounts:\nunsubscribe()\npresence.stop()\n",[941,91258,91259,91270,91286,91324,91328,91333,91375,91379,91384,91389,91417,91437,91477,91481,91487,91492,91496,91501,91508],{"__ignoreMap":1037},[2225,91260,91261,91263,91266,91268],{"class":2227,"line":2228},[2225,91262,2775],{"class":2774},[2225,91264,91265],{"class":2239}," presence ",[2225,91267,2781],{"class":2235},[2225,91269,68845],{"class":2239},[2225,91271,91272,91274,91276,91278,91280,91282,91284],{"class":2227,"line":1041},[2225,91273,26235],{"class":2235},[2225,91275,6471],{"class":2291},[2225,91277,2789],{"class":2239},[2225,91279,2805],{"class":2235},[2225,91281,69487],{"class":2252},[2225,91283,2805],{"class":2235},[2225,91285,2362],{"class":2239},[2225,91287,91288,91290,91293,91295,91298,91300,91302,91305,91307,91309,91311,91314,91316,91318,91320,91322],{"class":2227,"line":1038},[2225,91289,26235],{"class":2235},[2225,91291,91292],{"class":2291},"presence",[2225,91294,17851],{"class":2235},[2225,91296,91297],{"class":2307}," cursor",[2225,91299,2311],{"class":2235},[2225,91301,2236],{"class":2235},[2225,91303,91304],{"class":2307}," x",[2225,91306,2311],{"class":2235},[2225,91308,6356],{"class":6342},[2225,91310,3769],{"class":2235},[2225,91312,91313],{"class":2307}," y",[2225,91315,2311],{"class":2235},[2225,91317,6356],{"class":6342},[2225,91319,2243],{"class":2235},[2225,91321,85293],{"class":2235},[2225,91323,8146],{"class":2239},[2225,91325,91326],{"class":2227,"line":2288},[2225,91327,2285],{"emptyLinePlaceholder":2284},[2225,91329,91330],{"class":2227,"line":2304},[2225,91331,91332],{"class":2763},"\u002F\u002F Broadcast my cursor (call again whenever it moves).\n",[2225,91334,91335,91337,91340,91342,91344,91346,91348,91350,91352,91354,91356,91358,91360,91362,91364,91366,91369,91371,91373],{"class":2227,"line":2356},[2225,91336,6500],{"class":2231},[2225,91338,91339],{"class":2239}," presence",[2225,91341,1520],{"class":2235},[2225,91343,39210],{"class":2291},[2225,91345,2789],{"class":2239},[2225,91347,2792],{"class":2235},[2225,91349,91297],{"class":2307},[2225,91351,2311],{"class":2235},[2225,91353,2236],{"class":2235},[2225,91355,91304],{"class":2307},[2225,91357,2311],{"class":2235},[2225,91359,28675],{"class":6527},[2225,91361,2298],{"class":2235},[2225,91363,91313],{"class":2307},[2225,91365,2311],{"class":2235},[2225,91367,91368],{"class":6527}," 340",[2225,91370,2243],{"class":2235},[2225,91372,2243],{"class":2235},[2225,91374,2362],{"class":2239},[2225,91376,91377],{"class":2227,"line":2830},[2225,91378,2285],{"emptyLinePlaceholder":2284},[2225,91380,91381],{"class":2227,"line":2861},[2225,91382,91383],{"class":2763},"\u002F\u002F Subscribe to peers — the callback receives all active peers,\n",[2225,91385,91386],{"class":2227,"line":4246},[2225,91387,91388],{"class":2763},"\u002F\u002F excluding yourself and anyone stale (default: silent for 30 s).\n",[2225,91390,91391,91393,91396,91398,91400,91402,91404,91406,91408,91411,91413,91415],{"class":2227,"line":4253},[2225,91392,2775],{"class":2774},[2225,91394,91395],{"class":2239}," unsubscribe ",[2225,91397,2781],{"class":2235},[2225,91399,91339],{"class":2239},[2225,91401,1520],{"class":2235},[2225,91403,25042],{"class":2291},[2225,91405,2789],{"class":2239},[2225,91407,2789],{"class":2235},[2225,91409,91410],{"class":7052},"peers",[2225,91412,1381],{"class":2235},[2225,91414,8116],{"class":2774},[2225,91416,2301],{"class":2235},[2225,91418,91419,91421,91423,91425,91428,91430,91433,91435],{"class":2227,"line":4259},[2225,91420,80016],{"class":2231},[2225,91422,953],{"class":2307},[2225,91424,2775],{"class":2774},[2225,91426,91427],{"class":2239}," peer",[2225,91429,8806],{"class":2235},[2225,91431,91432],{"class":2239}," peers",[2225,91434,8495],{"class":2307},[2225,91436,4200],{"class":2235},[2225,91438,91439,91441,91443,91445,91447,91450,91452,91454,91456,91458,91461,91463,91465,91467,91469,91471,91473,91475],{"class":2227,"line":6548},[2225,91440,27912],{"class":2239},[2225,91442,1520],{"class":2235},[2225,91444,27100],{"class":2291},[2225,91446,2789],{"class":2307},[2225,91448,91449],{"class":2235},"`${",[2225,91451,201],{"class":2239},[2225,91453,1520],{"class":2235},[2225,91455,10749],{"class":2239},[2225,91457,2359],{"class":2235},[2225,91459,91460],{"class":2252},": cursor at",[2225,91462,44476],{"class":2235},[2225,91464,2298],{"class":2235},[2225,91466,91427],{"class":2239},[2225,91468,1520],{"class":2235},[2225,91470,66738],{"class":2239},[2225,91472,1520],{"class":2235},[2225,91474,25010],{"class":2239},[2225,91476,2362],{"class":2307},[2225,91478,91479],{"class":2227,"line":6576},[2225,91480,7632],{"class":2235},[2225,91482,91483,91485],{"class":2227,"line":6582},[2225,91484,2359],{"class":2235},[2225,91486,2362],{"class":2239},[2225,91488,91489],{"class":2227,"line":6587},[2225,91490,91491],{"class":2763},"\u002F\u002F → \"bob: cursor at { x: 512, y: 88 }\"   (fires when bob calls update())\n",[2225,91493,91494],{"class":2227,"line":7174},[2225,91495,2285],{"emptyLinePlaceholder":2284},[2225,91497,91498],{"class":2227,"line":7180},[2225,91499,91500],{"class":2763},"\u002F\u002F When the document view unmounts:\n",[2225,91502,91503,91506],{"class":2227,"line":7220},[2225,91504,91505],{"class":2291},"unsubscribe",[2225,91507,8146],{"class":2239},[2225,91509,91510,91512,91514,91516],{"class":2227,"line":7225},[2225,91511,91292],{"class":2239},[2225,91513,1520],{"class":2235},[2225,91515,27604],{"class":2291},[2225,91517,8146],{"class":2239},[937,91519,91520,91521,91523],{},"The presence channel key is derived (via HKDF) from the collection's DEK — the data-encryption key that encrypts the collection's records (see ",[1007,91522,21],{"href":60250}," for the full key hierarchy). Two consequences:",[1111,91525,91526,91529],{},[1114,91527,91528],{},"The remote store never sees which user is doing what — presence payloads are ciphertext to it.",[1114,91530,91531],{},"Revoked users automatically lose presence access on the next DEK rotation.",[937,91533,91534,91535,1261,91538,91541,91542,91544,91545,957],{},"Transport adapts to the store: adapters that implement pub\u002Fsub (",[941,91536,91537],{},"presencePublish",[941,91539,91540],{},"presenceSubscribe",") get real-time delivery; all others fall back to polling a reserved ",[941,91543,6780],{}," collection (default poll interval: 5 s, tunable via ",[941,91546,91547],{},"presence({ pollIntervalMs, staleMs })",[1022,91549,91551],{"id":91550},"granting-access-to-a-collaborator","Granting access to a collaborator",[937,91553,91554,91250,91556,91558],{},[941,91555,21197],{},[941,91557,3197],{},") wraps the vault's keys for a new user, who then unlocks with their own secret.",[2217,91560,91562],{"className":2219,"code":91561,"language":2221,"meta":1037,"style":1037},"await db.grant('shared', {\n  userId: 'bob',\n  displayName: 'Bob',\n  role: 'operator',\n  secret: 'a-secret-bob-will-change',\n  permissions: { docs: 'rw' },   \u002F\u002F per-collection: 'rw' or 'ro'\n})\n\nconsole.log((await db.listUsers('shared')).map((u) => u.userId))\n\u002F\u002F → [\"alice\", \"bob\"]\n",[941,91563,91564,91586,91601,91616,91630,91645,91668,91674,91678,91730],{"__ignoreMap":1037},[2225,91565,91566,91568,91570,91572,91574,91576,91578,91580,91582,91584],{"class":2227,"line":2228},[2225,91567,6500],{"class":2231},[2225,91569,2842],{"class":2239},[2225,91571,1520],{"class":2235},[2225,91573,10493],{"class":2291},[2225,91575,2789],{"class":2239},[2225,91577,2805],{"class":2235},[2225,91579,90098],{"class":2252},[2225,91581,2805],{"class":2235},[2225,91583,2298],{"class":2235},[2225,91585,2301],{"class":2235},[2225,91587,91588,91590,91592,91594,91597,91599],{"class":2227,"line":1041},[2225,91589,10510],{"class":2307},[2225,91591,2311],{"class":2235},[2225,91593,2249],{"class":2235},[2225,91595,91596],{"class":2252},"bob",[2225,91598,2805],{"class":2235},[2225,91600,1707],{"class":2235},[2225,91602,91603,91605,91607,91609,91612,91614],{"class":2227,"line":1038},[2225,91604,10526],{"class":2307},[2225,91606,2311],{"class":2235},[2225,91608,2249],{"class":2235},[2225,91610,91611],{"class":2252},"Bob",[2225,91613,2805],{"class":2235},[2225,91615,1707],{"class":2235},[2225,91617,91618,91620,91622,91624,91626,91628],{"class":2227,"line":2288},[2225,91619,10542],{"class":2307},[2225,91621,2311],{"class":2235},[2225,91623,2249],{"class":2235},[2225,91625,9907],{"class":2252},[2225,91627,2805],{"class":2235},[2225,91629,1707],{"class":2235},[2225,91631,91632,91634,91636,91638,91641,91643],{"class":2227,"line":2304},[2225,91633,10423],{"class":2307},[2225,91635,2311],{"class":2235},[2225,91637,2249],{"class":2235},[2225,91639,91640],{"class":2252},"a-secret-bob-will-change",[2225,91642,2805],{"class":2235},[2225,91644,1707],{"class":2235},[2225,91646,91647,91649,91651,91653,91655,91657,91659,91661,91663,91665],{"class":2227,"line":2356},[2225,91648,10572],{"class":2307},[2225,91650,2311],{"class":2235},[2225,91652,2236],{"class":2235},[2225,91654,90184],{"class":2307},[2225,91656,2311],{"class":2235},[2225,91658,2249],{"class":2235},[2225,91660,10026],{"class":2252},[2225,91662,2805],{"class":2235},[2225,91664,10965],{"class":2235},[2225,91666,91667],{"class":2763},"   \u002F\u002F per-collection: 'rw' or 'ro'\n",[2225,91669,91670,91672],{"class":2227,"line":2830},[2225,91671,2359],{"class":2235},[2225,91673,2362],{"class":2239},[2225,91675,91676],{"class":2227,"line":2861},[2225,91677,2285],{"emptyLinePlaceholder":2284},[2225,91679,91680,91682,91684,91686,91689,91691,91693,91695,91697,91699,91701,91703,91705,91707,91709,91711,91713,91715,91718,91720,91722,91725,91727],{"class":2227,"line":4246},[2225,91681,28031],{"class":2239},[2225,91683,1520],{"class":2235},[2225,91685,27100],{"class":2291},[2225,91687,91688],{"class":2239},"((",[2225,91690,6500],{"class":2231},[2225,91692,2842],{"class":2239},[2225,91694,1520],{"class":2235},[2225,91696,10264],{"class":2291},[2225,91698,2789],{"class":2239},[2225,91700,2805],{"class":2235},[2225,91702,90098],{"class":2252},[2225,91704,2805],{"class":2235},[2225,91706,8454],{"class":2239},[2225,91708,1520],{"class":2235},[2225,91710,8244],{"class":2291},[2225,91712,2789],{"class":2239},[2225,91714,2789],{"class":2235},[2225,91716,91717],{"class":7052},"u",[2225,91719,1381],{"class":2235},[2225,91721,8116],{"class":2774},[2225,91723,91724],{"class":2239}," u",[2225,91726,1520],{"class":2235},[2225,91728,91729],{"class":2239},"userId))\n",[2225,91731,91732],{"class":2227,"line":4253},[2225,91733,91734],{"class":2763},"\u002F\u002F → [\"alice\", \"bob\"]\n",[937,91736,91737,91738,968,91740,968,91742,968,91744,968,91746,968,91748,1952,91750,91752,91753,91755,91756,1520],{},"Roles are ",[941,91739,9898],{},[941,91741,9901],{},[941,91743,9904],{},[941,91745,9907],{},[941,91747,9910],{},[941,91749,9913],{},[941,91751,21197],{}," does not check the new secret's strength by default — it accepts whatever string you pass for ",[941,91754,10695],{},". If your app needs a minimum-strength policy at invite time, validate the secret yourself before calling ",[941,91757,10831],{},[937,91759,91760],{},"Bob unlocks the same vault with his own credentials:",[2217,91762,91764],{"className":2219,"code":91763,"language":2221,"meta":1037,"style":1037},"const bobDb = await createNoydb({\n  store: toBrowserIdb(),\n  sync: { store: remote, role: 'sync-peer' },\n  user: 'bob',\n  secret: 'a-secret-bob-will-change',\n  crdtStrategy: withCrdt(),\n  syncStrategy: withSync(),\n})\n",[941,91765,91766,91783,91795,91823,91837,91851,91863,91875],{"__ignoreMap":1037},[2225,91767,91768,91770,91773,91775,91777,91779,91781],{"class":2227,"line":2228},[2225,91769,2775],{"class":2774},[2225,91771,91772],{"class":2239}," bobDb ",[2225,91774,2781],{"class":2235},[2225,91776,2784],{"class":2231},[2225,91778,2744],{"class":2291},[2225,91780,2789],{"class":2239},[2225,91782,4200],{"class":2235},[2225,91784,91785,91787,91789,91791,91793],{"class":2227,"line":1041},[2225,91786,15608],{"class":2307},[2225,91788,2311],{"class":2235},[2225,91790,34763],{"class":2291},[2225,91792,4228],{"class":2239},[2225,91794,1707],{"class":2235},[2225,91796,91797,91799,91801,91803,91805,91807,91809,91811,91813,91815,91817,91819,91821],{"class":2227,"line":1038},[2225,91798,34919],{"class":2307},[2225,91800,2311],{"class":2235},[2225,91802,2236],{"class":2235},[2225,91804,7564],{"class":2307},[2225,91806,2311],{"class":2235},[2225,91808,89831],{"class":2239},[2225,91810,2298],{"class":2235},[2225,91812,34944],{"class":2307},[2225,91814,2311],{"class":2235},[2225,91816,2249],{"class":2235},[2225,91818,34951],{"class":2252},[2225,91820,2805],{"class":2235},[2225,91822,2353],{"class":2235},[2225,91824,91825,91827,91829,91831,91833,91835],{"class":2227,"line":2288},[2225,91826,4205],{"class":2307},[2225,91828,2311],{"class":2235},[2225,91830,2249],{"class":2235},[2225,91832,91596],{"class":2252},[2225,91834,2805],{"class":2235},[2225,91836,1707],{"class":2235},[2225,91838,91839,91841,91843,91845,91847,91849],{"class":2227,"line":2304},[2225,91840,10423],{"class":2307},[2225,91842,2311],{"class":2235},[2225,91844,2249],{"class":2235},[2225,91846,91640],{"class":2252},[2225,91848,2805],{"class":2235},[2225,91850,1707],{"class":2235},[2225,91852,91853,91855,91857,91859,91861],{"class":2227,"line":2356},[2225,91854,89888],{"class":2307},[2225,91856,2311],{"class":2235},[2225,91858,89560],{"class":2291},[2225,91860,4228],{"class":2239},[2225,91862,1707],{"class":2235},[2225,91864,91865,91867,91869,91871,91873],{"class":2227,"line":2830},[2225,91866,89901],{"class":2307},[2225,91868,2311],{"class":2235},[2225,91870,89579],{"class":2291},[2225,91872,4228],{"class":2239},[2225,91874,1707],{"class":2235},[2225,91876,91877,91879],{"class":2227,"line":2861},[2225,91878,2359],{"class":2235},[2225,91880,2362],{"class":2239},[937,91882,91883,91884,91886,91887,957],{},"Secrets are only one unlock path — the ",[941,91885,9945],{}," packages add WebAuthn, OIDC, magic links, TOTP, and more (see the ",[1007,91888,91890],{"href":91889},"..\u002Ffamilies\u002Fon\u002F","on- family",[1022,91892,91894],{"id":91893},"conflicts-what-wins-when-both-sides-changed","Conflicts — what wins when both sides changed?",[937,91896,91897,91898,91900,91901,91904],{},"Two knobs, at two levels. Both operate on the ",[950,91899,1659],{}," — the single encrypted blob a record is stored as (see ",[1007,91902,6891],{"href":91903},"..\u002Fcore\u002F05-stores","); \"higher version wins\" etc. below means the envelope, not the decrypted fields.",[937,91906,91907,91912,91913,91915,91916,1520],{},[950,91908,91909,91910],{},"Db-level ",[941,91911,20524],{}," (type ",[941,91914,20529],{},") applies to every collection unless overridden. Default: ",[941,91917,20534],{},[1338,91919,91920,91929],{},[1341,91921,91922],{},[1344,91923,91924,91927],{},[1347,91925,91926],{},"Value",[1347,91928,20200],{},[1357,91930,91931,91944,91954,91964],{},[1344,91932,91933,91938],{},[1362,91934,91935,91937],{},[941,91936,20534],{}," (default)",[1362,91939,91940,91941,91943],{},"the envelope with the higher version counter ",[941,91942,1700],{}," wins",[1344,91945,91946,91951],{},[1362,91947,91948],{},[941,91949,91950],{},"'local-wins'",[1362,91952,91953],{},"keep the local envelope",[1344,91955,91956,91961],{},[1362,91957,91958],{},[941,91959,91960],{},"'remote-wins'",[1362,91962,91963],{},"keep the remote envelope",[1344,91965,91966,91971],{},[1362,91967,91968],{},[941,91969,91970],{},"(conflict) => 'local' | 'remote'",[1362,91972,91973],{},"decide per conflict",[937,91975,91976,91912,91982,91985],{},[950,91977,91978,91979],{},"Collection-level ",[941,91980,91981],{},"conflictPolicy",[941,91983,91984],{},"ConflictPolicy\u003CT>",") overrides the db-level strategy for one collection:",[2217,91987,91989],{"className":2219,"code":91988,"language":2221,"meta":1037,"style":1037},"interface Task {\n  id: string\n  title: string\n  done: boolean\n}\n\nconst tasks = vault.collection\u003CTask>('tasks', {\n  conflictPolicy: 'last-writer-wins',\n})\n",[941,91990,91991,92000,92008,92016,92025,92029,92033,92068,92084],{"__ignoreMap":1037},[2225,91992,91993,91995,91998],{"class":2227,"line":2228},[2225,91994,6994],{"class":2774},[2225,91996,91997],{"class":6342}," Task",[2225,91999,2301],{"class":2235},[2225,92001,92002,92004,92006],{"class":2227,"line":1041},[2225,92003,28371],{"class":2307},[2225,92005,2311],{"class":2235},[2225,92007,7014],{"class":6342},[2225,92009,92010,92012,92014],{"class":2227,"line":1038},[2225,92011,86670],{"class":2307},[2225,92013,2311],{"class":2235},[2225,92015,7014],{"class":6342},[2225,92017,92018,92021,92023],{"class":2227,"line":2288},[2225,92019,92020],{"class":2307},"  done",[2225,92022,2311],{"class":2235},[2225,92024,11718],{"class":6342},[2225,92026,92027],{"class":2227,"line":2304},[2225,92028,7351],{"class":2235},[2225,92030,92031],{"class":2227,"line":2356},[2225,92032,2285],{"emptyLinePlaceholder":2284},[2225,92034,92035,92037,92040,92042,92044,92046,92048,92050,92053,92055,92057,92059,92062,92064,92066],{"class":2227,"line":2830},[2225,92036,2775],{"class":2774},[2225,92038,92039],{"class":2239}," tasks ",[2225,92041,2781],{"class":2235},[2225,92043,6466],{"class":2239},[2225,92045,1520],{"class":2235},[2225,92047,6471],{"class":2291},[2225,92049,6474],{"class":2235},[2225,92051,92052],{"class":6342},"Task",[2225,92054,6480],{"class":2235},[2225,92056,2789],{"class":2239},[2225,92058,2805],{"class":2235},[2225,92060,92061],{"class":2252},"tasks",[2225,92063,2805],{"class":2235},[2225,92065,2298],{"class":2235},[2225,92067,2301],{"class":2235},[2225,92069,92070,92073,92075,92077,92080,92082],{"class":2227,"line":2861},[2225,92071,92072],{"class":2307},"  conflictPolicy",[2225,92074,2311],{"class":2235},[2225,92076,2249],{"class":2235},[2225,92078,92079],{"class":2252},"last-writer-wins",[2225,92081,2805],{"class":2235},[2225,92083,1707],{"class":2235},[2225,92085,92086,92088],{"class":2227,"line":4246},[2225,92087,2359],{"class":2235},[2225,92089,2362],{"class":2239},[1338,92091,92092,92100],{},[1341,92093,92094],{},[1344,92095,92096,92098],{},[1347,92097,91926],{},[1347,92099,20200],{},[1357,92101,92102,92114,92127,92145],{},[1344,92103,92104,92109],{},[1362,92105,92106],{},[941,92107,92108],{},"'last-writer-wins'",[1362,92110,92111,92112,91943],{},"higher timestamp ",[941,92113,1703],{},[1344,92115,92116,92121],{},[1362,92117,92118],{},[941,92119,92120],{},"'first-writer-wins'",[1362,92122,92123,92124,92126],{},"lower version ",[941,92125,1700],{}," wins (the earlier write is preserved)",[1344,92128,92129,92134],{},[1362,92130,92131],{},[941,92132,92133],{},"'manual'",[1362,92135,92136,92137,92140,92141,92144],{},"emits a ",[941,92138,92139],{},"sync:conflict"," event with a ",[941,92142,92143],{},"resolve(winner)"," callback — your UI decides",[1344,92146,92147,92152],{},[1362,92148,92149],{},[941,92150,92151],{},"(local, remote) => T",[1362,92153,92154],{},"a pure, synchronous custom merge over the decrypted records",[937,92156,92157,92160],{},[950,92158,92159],{},"CRDT collections"," resolve conflicts by merging instead of picking a winner — with one asymmetry:",[1111,92162,92163,92174],{},[1114,92164,92165,997,92167,92170,92171,92173],{},[941,92166,89442],{},[941,92168,92169],{},"crdt: 'rga'"," merge automatically inside the sync engine when ",[941,92172,56862],{}," hits a conflict. No writes are lost.",[1114,92175,92176,92178,92179,92181,92182,92184,92185,4688,92187,957],{},[941,92177,90038],{}," cannot be merged by the sync engine (the core deliberately does not bundle the ",[941,92180,287],{}," package), so a conflicting envelope falls back to higher-",[941,92183,1700],{},"-wins. Converge in the client instead, by exchanging Yjs updates as shown above (",[941,92186,89391],{},[941,92188,90926],{},[1022,92190,68210],{"id":68209},[1111,92192,92193,92204,92207,92220],{},[1114,92194,92195,92197,92198,92201,92202,1520],{},[941,92196,20953],{}," marks records dirty on a microtask — pushing in the same tick as the write can miss it. Yield once (",[941,92199,92200],{},"await new Promise((r) => setTimeout(r, 0))",") before an immediate ",[941,92203,56859],{},[1114,92205,92206],{},"Yjs merges happen in your app, not in the sync engine — plan for a \"pull, merge, push\" loop on each client (see above).",[1114,92208,92209,92210,92213,92214,92216,92217,92219],{},"Presence is ephemeral by design: peers disappear after ",[941,92211,92212],{},"staleMs"," (default 30 s) without an ",[941,92215,39249],{},". Call ",[941,92218,39249],{}," on a heartbeat if mere page-open should count as present.",[1114,92221,92222,21348,92224,6789,92226,5502,92228,997,92230,1625,92232,21348,92234,92236,92237,1520],{},[941,92223,21197],{},[941,92225,3197],{},[941,92227,10329],{},[941,92229,21307],{},[941,92231,20591],{},[941,92233,20594],{},[941,92235,4676],{}," throw with a pointer at ",[941,92238,4672],{},[1022,92240,2621],{"id":2620},[1111,92242,92243,92249,92255,92260,92265,92270,92280,92290],{},[1114,92244,92245,92248],{},[1007,92246,653],{"href":92247},"..\u002Fservices\u002Fcrdt"," — the CRDT service page (modes, API reference)",[1114,92250,92251,92254],{},[1007,92252,864],{"href":92253},"..\u002Fservices\u002Fsync"," — replication, sync policies, conflict events",[1114,92256,92257,92259],{},[1007,92258,868],{"href":10333}," — grants, roles, revocation, key rotation",[1114,92261,92262,92264],{},[1007,92263,840],{"href":11561}," — session policies and timeouts",[1114,92266,92267,92269],{},[1007,92268,21],{"href":60250}," — the key hierarchy (KEK, keyring, DEK)",[1114,92271,92272,92279],{},[1007,92273,92276],{"href":92274,"rel":92275},"https:\u002F\u002Fgithub.com\u002Fnoy-db\u002Fdocs\u002Fblob\u002Fmain\u002Fshowcases\u002Fsrc\u002F14-with-crdt.showcase.test.ts",[3371],[941,92277,92278],{},"14-with-crdt.showcase.test.ts"," — runnable Yjs interop walkthrough",[1114,92281,92282,92289],{},[1007,92283,92286],{"href":92284,"rel":92285},"https:\u002F\u002Fgithub.com\u002Fnoy-db\u002Fdocs\u002Fblob\u002Fmain\u002Fshowcases\u002Fsrc\u002F15-with-sync.showcase.test.ts",[3371],[941,92287,92288],{},"15-with-sync.showcase.test.ts"," — runnable push\u002Fpull walkthrough",[1114,92291,92292,92294],{},[1007,92293,597],{"href":598}," — the full opt-in service catalog",[2645,92296,92297],{},"html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}html pre.shiki code .sfNiH, html code.shiki .sfNiH{--shiki-light:#FF5370;--shiki-default:#FF9CAC;--shiki-dark:#FF9CAC}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sHdIc, html code.shiki .sHdIc{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#EEFFFF;--shiki-default-font-style:italic;--shiki-dark:#BABED8;--shiki-dark-font-style:italic}",{"title":1037,"searchDepth":1038,"depth":1038,"links":92299},[92300,92301,92302,92303,92304,92305,92306,92307,92308,92309],{"id":58275,"depth":1041,"text":58276},{"id":58393,"depth":1041,"text":58394},{"id":90020,"depth":1041,"text":90021},{"id":90444,"depth":1041,"text":90445},{"id":90934,"depth":1041,"text":90935},{"id":91244,"depth":1041,"text":91245},{"id":91550,"depth":1041,"text":91551},{"id":91893,"depth":1041,"text":91894},{"id":68209,"depth":1041,"text":68210},{"id":2620,"depth":1041,"text":2621},"Wire CRDT merge, cross-device push\u002Fpull replication, live presence, and multi-user grants onto an encrypted store so every device converges on the same result.",{},{"title":572,"description":92310},"f5CaWwSbtfDfD-fgQGo2O7SpdPmE6icMJeGa03sAS_U",{"id":92315,"title":576,"access":12,"apiToc":929,"body":92316,"category":1042,"description":94698,"extension":1044,"meta":94699,"navigation":94700,"nest":1042,"path":577,"seo":94701,"sinceVersion":1042,"status":1049,"stem":578,"subtitle":1042,"tier":1050,"tocExternal":1042,"__hash__":94702},"docs\u002Frecipes\u002Frun-the-showcases.md",{"type":931,"value":92317,"toc":94690},[92318,92321,92331,92335,92378,92397,92400,92425,92427,92430,92434,94423,94427,94668,94670,94687],[934,92319,576],{"id":92320},"run-the-showcases",[937,92322,92323,92324,92327,92328,92330],{},"Every capability documented on this site is backed by a ",[950,92325,92326],{},"showcase"," — a vitest test that exercises the feature end-to-end against the published ",[941,92329,5129],{}," packages and reads top-to-bottom as a tutorial. Each file doubles as test, demo, and copy-paste source. If a claim on these pages matters to your decision, you can run its proof yourself in a couple of minutes.",[1022,92332,92334],{"id":92333},"clone-and-run","Clone and run",[2217,92336,92338],{"className":33763,"code":92337,"language":33765,"meta":1037,"style":1037},"git clone https:\u002F\u002Fgithub.com\u002Fnoy-db\u002Fdocs\ncd noy-db-docs\npnpm install\npnpm --filter @noy-db\u002Fshowcases test\n",[941,92339,92340,92351,92359,92366],{"__ignoreMap":1037},[2225,92341,92342,92345,92348],{"class":2227,"line":2228},[2225,92343,92344],{"class":6342},"git",[2225,92346,92347],{"class":2252}," clone",[2225,92349,92350],{"class":2252}," https:\u002F\u002Fgithub.com\u002Fnoy-db\u002Fdocs\n",[2225,92352,92353,92356],{"class":2227,"line":1041},[2225,92354,92355],{"class":2291},"cd",[2225,92357,92358],{"class":2252}," noy-db-docs\n",[2225,92360,92361,92363],{"class":2227,"line":1038},[2225,92362,41169],{"class":6342},[2225,92364,92365],{"class":2252}," install\n",[2225,92367,92368,92370,92372,92375],{"class":2227,"line":2288},[2225,92369,41169],{"class":6342},[2225,92371,88002],{"class":2252},[2225,92373,92374],{"class":2252}," @noy-db\u002Fshowcases",[2225,92376,92377],{"class":2252}," test\n",[937,92379,92380,92381,92384,92385,92388,92389,92396],{},"The full suite runs green with no credentials and no cloud account — every store and auth method is exercised locally. The handful of ",[950,92382,92383],{},"real-provider"," showcases (AWS, Google\u002FApple\u002FLINE OIDC, SMTP) skip cleanly unless you set their variables in ",[941,92386,92387],{},"showcases\u002F.env","; see ",[1007,92390,92393],{"href":92391,"rel":92392},"https:\u002F\u002Fgithub.com\u002Fnoy-db\u002Fdocs\u002Fblob\u002Fmain\u002Fshowcases\u002FREADME.md",[3371],[941,92394,92395],{},"showcases\u002FREADME.md"," for the list.",[937,92398,92399],{},"Run a single showcase by file:",[2217,92401,92403],{"className":33763,"code":92402,"language":33765,"meta":1037,"style":1037},"cd showcases\npnpm vitest run src\u002F16-with-crdt.showcase.test.ts\n",[941,92404,92405,92412],{"__ignoreMap":1037},[2225,92406,92407,92409],{"class":2227,"line":2228},[2225,92408,92355],{"class":2291},[2225,92410,92411],{"class":2252}," showcases\n",[2225,92413,92414,92416,92419,92422],{"class":2227,"line":1041},[2225,92415,41169],{"class":6342},[2225,92417,92418],{"class":2252}," vitest",[2225,92420,92421],{"class":2252}," run",[2225,92423,92424],{"class":2252}," src\u002F16-with-crdt.showcase.test.ts\n",[1022,92426,30965],{"id":30964},[937,92428,92429],{},"132 files — 117 numbered showcases plus 15 recipe walkthroughs. Numbering is sparse by design (retired numbers are not reused).",[1591,92431,92433],{"id":92432},"numbered-showcases","Numbered showcases",[1338,92435,92436,92448],{},[1341,92437,92438],{},[1344,92439,92440,92442,92445],{},[1347,92441,3096],{},[1347,92443,92444],{},"What it demonstrates",[1347,92446,92447],{},"File",[1357,92449,92450,92467,92483,92500,92516,92533,92550,92566,92582,92598,92613,92629,92646,92663,92678,92692,92708,92728,92744,92760,92777,92794,92811,92828,92845,92862,92879,92896,92913,92930,92947,92963,92980,92997,93014,93031,93048,93064,93081,93098,93115,93132,93149,93166,93183,93200,93217,93233,93250,93266,93282,93299,93318,93335,93352,93369,93386,93403,93420,93437,93454,93471,93488,93505,93522,93539,93556,93573,93590,93607,93624,93641,93660,93677,93694,93711,93728,93745,93762,93779,93796,93813,93829,93846,93863,93879,93896,93913,93930,93947,93964,93981,93998,94015,94032,94049,94066,94083,94100,94117,94134,94151,94168,94185,94202,94219,94236,94253,94270,94287,94304,94321,94338,94355,94372,94389,94406],{},[1344,92451,92452,92455,92458],{},[1362,92453,92454],{},"00",[1362,92456,92457],{},"Hello, vault",[1362,92459,92460],{},[1007,92461,92464],{"href":92462,"rel":92463},"https:\u002F\u002Fgithub.com\u002Fnoy-db\u002Fdocs\u002Fblob\u002Fmain\u002Fshowcases\u002Fsrc\u002F00-hello-vault.showcase.test.ts",[3371],[941,92465,92466],{},"00-hello-vault.showcase.test.ts",[1344,92468,92469,92471,92474],{},[1362,92470,55288],{},[1362,92472,92473],{},"Storage: in-memory",[1362,92475,92476],{},[1007,92477,92480],{"href":92478,"rel":92479},"https:\u002F\u002Fgithub.com\u002Fnoy-db\u002Fdocs\u002Fblob\u002Fmain\u002Fshowcases\u002Fsrc\u002F01-storage-memory.showcase.test.ts",[3371],[941,92481,92482],{},"01-storage-memory.showcase.test.ts",[1344,92484,92485,92488,92491],{},[1362,92486,92487],{},"02",[1362,92489,92490],{},"Storage: JSON files on disk",[1362,92492,92493],{},[1007,92494,92497],{"href":92495,"rel":92496},"https:\u002F\u002Fgithub.com\u002Fnoy-db\u002Fdocs\u002Fblob\u002Fmain\u002Fshowcases\u002Fsrc\u002F02-storage-file.showcase.test.ts",[3371],[941,92498,92499],{},"02-storage-file.showcase.test.ts",[1344,92501,92502,92504,92507],{},[1362,92503,55292],{},[1362,92505,92506],{},"Storage: IndexedDB (browser)",[1362,92508,92509],{},[1007,92510,92513],{"href":92511,"rel":92512},"https:\u002F\u002Fgithub.com\u002Fnoy-db\u002Fdocs\u002Fblob\u002Fmain\u002Fshowcases\u002Fsrc\u002F03-storage-browser-idb.showcase.test.ts",[3371],[941,92514,92515],{},"03-storage-browser-idb.showcase.test.ts",[1344,92517,92518,92521,92524],{},[1362,92519,92520],{},"05",[1362,92522,92523],{},"Storage: routing across two backends",[1362,92525,92526],{},[1007,92527,92530],{"href":92528,"rel":92529},"https:\u002F\u002Fgithub.com\u002Fnoy-db\u002Fdocs\u002Fblob\u002Fmain\u002Fshowcases\u002Fsrc\u002F05-storage-routing.showcase.test.ts",[3371],[941,92531,92532],{},"05-storage-routing.showcase.test.ts",[1344,92534,92535,92538,92541],{},[1362,92536,92537],{},"06",[1362,92539,92540],{},"Multi-user: grant, revoke, role tiers",[1362,92542,92543],{},[1007,92544,92547],{"href":92545,"rel":92546},"https:\u002F\u002Fgithub.com\u002Fnoy-db\u002Fdocs\u002Fblob\u002Fmain\u002Fshowcases\u002Fsrc\u002F06-multi-user.showcase.test.ts",[3371],[941,92548,92549],{},"06-multi-user.showcase.test.ts",[1344,92551,92552,92555,92557],{},[1362,92553,92554],{},"07",[1362,92556,6719],{},[1362,92558,92559],{},[1007,92560,92563],{"href":92561,"rel":92562},"https:\u002F\u002Fgithub.com\u002Fnoy-db\u002Fdocs\u002Fblob\u002Fmain\u002Fshowcases\u002Fsrc\u002F07-with-history.showcase.test.ts",[3371],[941,92564,92565],{},"07-with-history.showcase.test.ts",[1344,92567,92568,92571,92573],{},[1362,92569,92570],{},"08",[1362,92572,6775],{},[1362,92574,92575],{},[1007,92576,92579],{"href":92577,"rel":92578},"https:\u002F\u002Fgithub.com\u002Fnoy-db\u002Fdocs\u002Fblob\u002Fmain\u002Fshowcases\u002Fsrc\u002F08-with-blobs.showcase.test.ts",[3371],[941,92580,92581],{},"08-with-blobs.showcase.test.ts",[1344,92583,92584,92587,92589],{},[1362,92585,92586],{},"09",[1362,92588,6739],{},[1362,92590,92591],{},[1007,92592,92595],{"href":92593,"rel":92594},"https:\u002F\u002Fgithub.com\u002Fnoy-db\u002Fdocs\u002Fblob\u002Fmain\u002Fshowcases\u002Fsrc\u002F09-with-i18n.showcase.test.ts",[3371],[941,92596,92597],{},"09-with-i18n.showcase.test.ts",[1344,92599,92600,92602,92604],{},[1362,92601,87121],{},[1362,92603,21926],{},[1362,92605,92606],{},[1007,92607,92610],{"href":92608,"rel":92609},"https:\u002F\u002Fgithub.com\u002Fnoy-db\u002Fdocs\u002Fblob\u002Fmain\u002Fshowcases\u002Fsrc\u002F10-with-aggregate.showcase.test.ts",[3371],[941,92611,92612],{},"10-with-aggregate.showcase.test.ts",[1344,92614,92615,92618,92620],{},[1362,92616,92617],{},"11",[1362,92619,6766],{},[1362,92621,92622],{},[1007,92623,92626],{"href":92624,"rel":92625},"https:\u002F\u002Fgithub.com\u002Fnoy-db\u002Fdocs\u002Fblob\u002Fmain\u002Fshowcases\u002Fsrc\u002F11-with-indexing.showcase.test.ts",[3371],[941,92627,92628],{},"11-with-indexing.showcase.test.ts",[1344,92630,92631,92634,92637],{},[1362,92632,92633],{},"12",[1362,92635,92636],{},"Joins (always-core today)",[1362,92638,92639],{},[1007,92640,92643],{"href":92641,"rel":92642},"https:\u002F\u002Fgithub.com\u002Fnoy-db\u002Fdocs\u002Fblob\u002Fmain\u002Fshowcases\u002Fsrc\u002F12-with-joins.showcase.test.ts",[3371],[941,92644,92645],{},"12-with-joins.showcase.test.ts",[1344,92647,92648,92651,92654],{},[1362,92649,92650],{},"13",[1362,92652,92653],{},"Live queries (always-core today)",[1362,92655,92656],{},[1007,92657,92660],{"href":92658,"rel":92659},"https:\u002F\u002Fgithub.com\u002Fnoy-db\u002Fdocs\u002Fblob\u002Fmain\u002Fshowcases\u002Fsrc\u002F13-with-live.showcase.test.ts",[3371],[941,92661,92662],{},"13-with-live.showcase.test.ts",[1344,92664,92665,92668,92671],{},[1362,92666,92667],{},"14",[1362,92669,92670],{},"withCrdt() (Yjs interop)",[1362,92672,92673],{},[1007,92674,92676],{"href":92274,"rel":92675},[3371],[941,92677,92278],{},[1344,92679,92680,92683,92685],{},[1362,92681,92682],{},"15",[1362,92684,4676],{},[1362,92686,92687],{},[1007,92688,92690],{"href":92284,"rel":92689},[3371],[941,92691,92288],{},[1344,92693,92694,92696,92699],{},[1362,92695,80546],{},[1362,92697,92698],{},"withShadow() (read-only frames)",[1362,92700,92701],{},[1007,92702,92705],{"href":92703,"rel":92704},"https:\u002F\u002Fgithub.com\u002Fnoy-db\u002Fdocs\u002Fblob\u002Fmain\u002Fshowcases\u002Fsrc\u002F16-with-shadow.showcase.test.ts",[3371],[941,92706,92707],{},"16-with-shadow.showcase.test.ts",[1344,92709,92710,92713,92719],{},[1362,92711,92712],{},"17",[1362,92714,92715,92716,1381],{},"withHistory() time-machine reads (",[941,92717,92718],{},"vault.at(t)",[1362,92720,92721],{},[1007,92722,92725],{"href":92723,"rel":92724},"https:\u002F\u002Fgithub.com\u002Fnoy-db\u002Fdocs\u002Fblob\u002Fmain\u002Fshowcases\u002Fsrc\u002F17-with-history-time-machine.showcase.test.ts",[3371],[941,92726,92727],{},"17-with-history-time-machine.showcase.test.ts",[1344,92729,92730,92733,92735],{},[1362,92731,92732],{},"18",[1362,92734,6757],{},[1362,92736,92737],{},[1007,92738,92741],{"href":92739,"rel":92740},"https:\u002F\u002Fgithub.com\u002Fnoy-db\u002Fdocs\u002Fblob\u002Fmain\u002Fshowcases\u002Fsrc\u002F18-with-periods.showcase.test.ts",[3371],[941,92742,92743],{},"18-with-periods.showcase.test.ts",[1344,92745,92746,92749,92751],{},[1362,92747,92748],{},"19",[1362,92750,6748],{},[1362,92752,92753],{},[1007,92754,92757],{"href":92755,"rel":92756},"https:\u002F\u002Fgithub.com\u002Fnoy-db\u002Fdocs\u002Fblob\u002Fmain\u002Fshowcases\u002Fsrc\u002F19-with-consent.showcase.test.ts",[3371],[941,92758,92759],{},"19-with-consent.showcase.test.ts",[1344,92761,92762,92765,92768],{},[1362,92763,92764],{},"20",[1362,92766,92767],{},"withTransactions() (atomic multi-record ops)",[1362,92769,92770],{},[1007,92771,92774],{"href":92772,"rel":92773},"https:\u002F\u002Fgithub.com\u002Fnoy-db\u002Fdocs\u002Fblob\u002Fmain\u002Fshowcases\u002Fsrc\u002F20-with-transactions.showcase.test.ts",[3371],[941,92775,92776],{},"20-with-transactions.showcase.test.ts",[1344,92778,92779,92782,92785],{},[1362,92780,92781],{},"21",[1362,92783,92784],{},"Bundle (.noydb container format)",[1362,92786,92787],{},[1007,92788,92791],{"href":92789,"rel":92790},"https:\u002F\u002Fgithub.com\u002Fnoy-db\u002Fdocs\u002Fblob\u002Fmain\u002Fshowcases\u002Fsrc\u002F21-with-bundle.showcase.test.ts",[3371],[941,92792,92793],{},"21-with-bundle.showcase.test.ts",[1344,92795,92796,92799,92802],{},[1362,92797,92798],{},"22",[1362,92800,92801],{},"Secret unlock (always-on)",[1362,92803,92804],{},[1007,92805,92808],{"href":92806,"rel":92807},"https:\u002F\u002Fgithub.com\u002Fnoy-db\u002Fdocs\u002Fblob\u002Fmain\u002Fshowcases\u002Fsrc\u002F22-on-secret.showcase.test.ts",[3371],[941,92809,92810],{},"22-on-secret.showcase.test.ts",[1344,92812,92813,92816,92819],{},[1362,92814,92815],{},"23",[1362,92817,92818],{},"WebAuthn passkey unlock",[1362,92820,92821],{},[1007,92822,92825],{"href":92823,"rel":92824},"https:\u002F\u002Fgithub.com\u002Fnoy-db\u002Fdocs\u002Fblob\u002Fmain\u002Fshowcases\u002Fsrc\u002F23-on-webauthn.showcase.test.ts",[3371],[941,92826,92827],{},"23-on-webauthn.showcase.test.ts",[1344,92829,92830,92833,92836],{},[1362,92831,92832],{},"24",[1362,92834,92835],{},"OIDC split-key unlock",[1362,92837,92838],{},[1007,92839,92842],{"href":92840,"rel":92841},"https:\u002F\u002Fgithub.com\u002Fnoy-db\u002Fdocs\u002Fblob\u002Fmain\u002Fshowcases\u002Fsrc\u002F24-on-oidc.showcase.test.ts",[3371],[941,92843,92844],{},"24-on-oidc.showcase.test.ts",[1344,92846,92847,92850,92853],{},[1362,92848,92849],{},"25",[1362,92851,92852],{},"Magic-link delegated access",[1362,92854,92855],{},[1007,92856,92859],{"href":92857,"rel":92858},"https:\u002F\u002Fgithub.com\u002Fnoy-db\u002Fdocs\u002Fblob\u002Fmain\u002Fshowcases\u002Fsrc\u002F25-on-magic-link.showcase.test.ts",[3371],[941,92860,92861],{},"25-on-magic-link.showcase.test.ts",[1344,92863,92864,92867,92870],{},[1362,92865,92866],{},"26",[1362,92868,92869],{},"Printable recovery codes",[1362,92871,92872],{},[1007,92873,92876],{"href":92874,"rel":92875},"https:\u002F\u002Fgithub.com\u002Fnoy-db\u002Fdocs\u002Fblob\u002Fmain\u002Fshowcases\u002Fsrc\u002F26-on-recovery.showcase.test.ts",[3371],[941,92877,92878],{},"26-on-recovery.showcase.test.ts",[1344,92880,92881,92884,92887],{},[1362,92882,92883],{},"27",[1362,92885,92886],{},"Shamir secret sharing for KEK recovery",[1362,92888,92889],{},[1007,92890,92893],{"href":92891,"rel":92892},"https:\u002F\u002Fgithub.com\u002Fnoy-db\u002Fdocs\u002Fblob\u002Fmain\u002Fshowcases\u002Fsrc\u002F27-on-shamir.showcase.test.ts",[3371],[941,92894,92895],{},"27-on-shamir.showcase.test.ts",[1344,92897,92898,92901,92904],{},[1362,92899,92900],{},"28",[1362,92902,92903],{},"TOTP (RFC 6238) second factor",[1362,92905,92906],{},[1007,92907,92910],{"href":92908,"rel":92909},"https:\u002F\u002Fgithub.com\u002Fnoy-db\u002Fdocs\u002Fblob\u002Fmain\u002Fshowcases\u002Fsrc\u002F28-on-totp.showcase.test.ts",[3371],[941,92911,92912],{},"28-on-totp.showcase.test.ts",[1344,92914,92915,92918,92921],{},[1362,92916,92917],{},"29",[1362,92919,92920],{},"Email OTP with transport abstraction",[1362,92922,92923],{},[1007,92924,92927],{"href":92925,"rel":92926},"https:\u002F\u002Fgithub.com\u002Fnoy-db\u002Fdocs\u002Fblob\u002Fmain\u002Fshowcases\u002Fsrc\u002F29-on-email-otp.showcase.test.ts",[3371],[941,92928,92929],{},"29-on-email-otp.showcase.test.ts",[1344,92931,92932,92935,92938],{},[1362,92933,92934],{},"30",[1362,92936,92937],{},"Session-resume PIN",[1362,92939,92940],{},[1007,92941,92944],{"href":92942,"rel":92943},"https:\u002F\u002Fgithub.com\u002Fnoy-db\u002Fdocs\u002Fblob\u002Fmain\u002Fshowcases\u002Fsrc\u002F30-on-pin.showcase.test.ts",[3371],[941,92945,92946],{},"30-on-pin.showcase.test.ts",[1344,92948,92949,92951,92954],{},[1362,92950,55864],{},[1362,92952,92953],{},"Lockout, duress, honeypot",[1362,92955,92956],{},[1007,92957,92960],{"href":92958,"rel":92959},"https:\u002F\u002Fgithub.com\u002Fnoy-db\u002Fdocs\u002Fblob\u002Fmain\u002Fshowcases\u002Fsrc\u002F31-on-threat.showcase.test.ts",[3371],[941,92961,92962],{},"31-on-threat.showcase.test.ts",[1344,92964,92965,92968,92971],{},[1362,92966,92967],{},"32",[1362,92969,92970],{},"as-csv (plaintext export)",[1362,92972,92973],{},[1007,92974,92977],{"href":92975,"rel":92976},"https:\u002F\u002Fgithub.com\u002Fnoy-db\u002Fdocs\u002Fblob\u002Fmain\u002Fshowcases\u002Fsrc\u002F32-as-csv.showcase.test.ts",[3371],[941,92978,92979],{},"32-as-csv.showcase.test.ts",[1344,92981,92982,92985,92988],{},[1362,92983,92984],{},"33",[1362,92986,92987],{},"as-xlsx (Excel multi-sheet export)",[1362,92989,92990],{},[1007,92991,92994],{"href":92992,"rel":92993},"https:\u002F\u002Fgithub.com\u002Fnoy-db\u002Fdocs\u002Fblob\u002Fmain\u002Fshowcases\u002Fsrc\u002F33-as-xlsx.showcase.test.ts",[3371],[941,92995,92996],{},"33-as-xlsx.showcase.test.ts",[1344,92998,92999,93002,93005],{},[1362,93000,93001],{},"34",[1362,93003,93004],{},"as-json (structured multi-collection export)",[1362,93006,93007],{},[1007,93008,93011],{"href":93009,"rel":93010},"https:\u002F\u002Fgithub.com\u002Fnoy-db\u002Fdocs\u002Fblob\u002Fmain\u002Fshowcases\u002Fsrc\u002F34-as-json.showcase.test.ts",[3371],[941,93012,93013],{},"34-as-json.showcase.test.ts",[1344,93015,93016,93019,93022],{},[1362,93017,93018],{},"35",[1362,93020,93021],{},"as-zip (composite records + attachments)",[1362,93023,93024],{},[1007,93025,93028],{"href":93026,"rel":93027},"https:\u002F\u002Fgithub.com\u002Fnoy-db\u002Fdocs\u002Fblob\u002Fmain\u002Fshowcases\u002Fsrc\u002F35-as-zip.showcase.test.ts",[3371],[941,93029,93030],{},"35-as-zip.showcase.test.ts",[1344,93032,93033,93036,93039],{},[1362,93034,93035],{},"36",[1362,93037,93038],{},"as-noydb (encrypted .noydb bundle)",[1362,93040,93041],{},[1007,93042,93045],{"href":93043,"rel":93044},"https:\u002F\u002Fgithub.com\u002Fnoy-db\u002Fdocs\u002Fblob\u002Fmain\u002Fshowcases\u002Fsrc\u002F36-as-noydb.showcase.test.ts",[3371],[941,93046,93047],{},"36-as-noydb.showcase.test.ts",[1344,93049,93050,93052,93055],{},[1362,93051,55625],{},[1362,93053,93054],{},"in-vue (Vue 3 composables)",[1362,93056,93057],{},[1007,93058,93061],{"href":93059,"rel":93060},"https:\u002F\u002Fgithub.com\u002Fnoy-db\u002Fdocs\u002Fblob\u002Fmain\u002Fshowcases\u002Fsrc\u002F37-in-vue.showcase.test.ts",[3371],[941,93062,93063],{},"37-in-vue.showcase.test.ts",[1344,93065,93066,93069,93072],{},[1362,93067,93068],{},"38",[1362,93070,93071],{},"in-pinia (defineNoydbStore)",[1362,93073,93074],{},[1007,93075,93078],{"href":93076,"rel":93077},"https:\u002F\u002Fgithub.com\u002Fnoy-db\u002Fdocs\u002Fblob\u002Fmain\u002Fshowcases\u002Fsrc\u002F38-in-pinia.showcase.test.ts",[3371],[941,93079,93080],{},"38-in-pinia.showcase.test.ts",[1344,93082,93083,93086,93089],{},[1362,93084,93085],{},"39",[1362,93087,93088],{},"in-yjs (Y.Doc-typed collections)",[1362,93090,93091],{},[1007,93092,93095],{"href":93093,"rel":93094},"https:\u002F\u002Fgithub.com\u002Fnoy-db\u002Fdocs\u002Fblob\u002Fmain\u002Fshowcases\u002Fsrc\u002F39-in-yjs.showcase.test.ts",[3371],[941,93096,93097],{},"39-in-yjs.showcase.test.ts",[1344,93099,93100,93103,93106],{},[1362,93101,93102],{},"40",[1362,93104,93105],{},"in-svelte (zero-dep Svelte stores)",[1362,93107,93108],{},[1007,93109,93112],{"href":93110,"rel":93111},"https:\u002F\u002Fgithub.com\u002Fnoy-db\u002Fdocs\u002Fblob\u002Fmain\u002Fshowcases\u002Fsrc\u002F40-in-svelte.showcase.test.ts",[3371],[941,93113,93114],{},"40-in-svelte.showcase.test.ts",[1344,93116,93117,93120,93123],{},[1362,93118,93119],{},"41",[1362,93121,93122],{},"in-react (hooks)",[1362,93124,93125],{},[1007,93126,93129],{"href":93127,"rel":93128},"https:\u002F\u002Fgithub.com\u002Fnoy-db\u002Fdocs\u002Fblob\u002Fmain\u002Fshowcases\u002Fsrc\u002F41-in-react.showcase.test.tsx",[3371],[941,93130,93131],{},"41-in-react.showcase.test.tsx",[1344,93133,93134,93137,93140],{},[1362,93135,93136],{},"42",[1362,93138,93139],{},"in-solid (Signal-based reactivity)",[1362,93141,93142],{},[1007,93143,93146],{"href":93144,"rel":93145},"https:\u002F\u002Fgithub.com\u002Fnoy-db\u002Fdocs\u002Fblob\u002Fmain\u002Fshowcases\u002Fsrc\u002F42-in-solid.showcase.test.ts",[3371],[941,93147,93148],{},"42-in-solid.showcase.test.ts",[1344,93150,93151,93154,93157],{},[1362,93152,93153],{},"43",[1362,93155,93156],{},"in-zustand (StateCreator factory)",[1362,93158,93159],{},[1007,93160,93163],{"href":93161,"rel":93162},"https:\u002F\u002Fgithub.com\u002Fnoy-db\u002Fdocs\u002Fblob\u002Fmain\u002Fshowcases\u002Fsrc\u002F43-in-zustand.showcase.test.ts",[3371],[941,93164,93165],{},"43-in-zustand.showcase.test.ts",[1344,93167,93168,93171,93174],{},[1362,93169,93170],{},"44",[1362,93172,93173],{},"in-tanstack-query (collectionListOptions etc.)",[1362,93175,93176],{},[1007,93177,93180],{"href":93178,"rel":93179},"https:\u002F\u002Fgithub.com\u002Fnoy-db\u002Fdocs\u002Fblob\u002Fmain\u002Fshowcases\u002Fsrc\u002F44-in-tanstack-query.showcase.test.ts",[3371],[941,93181,93182],{},"44-in-tanstack-query.showcase.test.ts",[1344,93184,93185,93188,93191],{},[1362,93186,93187],{},"45",[1362,93189,93190],{},"in-tanstack-table (table-state ↔ Query DSL bridge)",[1362,93192,93193],{},[1007,93194,93197],{"href":93195,"rel":93196},"https:\u002F\u002Fgithub.com\u002Fnoy-db\u002Fdocs\u002Fblob\u002Fmain\u002Fshowcases\u002Fsrc\u002F45-in-tanstack-table.showcase.test.ts",[3371],[941,93198,93199],{},"45-in-tanstack-table.showcase.test.ts",[1344,93201,93202,93205,93208],{},[1362,93203,93204],{},"46",[1362,93206,93207],{},"in-ai (LLM tool-calling adapter)",[1362,93209,93210],{},[1007,93211,93214],{"href":93212,"rel":93213},"https:\u002F\u002Fgithub.com\u002Fnoy-db\u002Fdocs\u002Fblob\u002Fmain\u002Fshowcases\u002Fsrc\u002F46-in-ai.showcase.test.ts",[3371],[941,93215,93216],{},"46-in-ai.showcase.test.ts",[1344,93218,93219,93221,93224],{},[1362,93220,55323],{},[1362,93222,93223],{},"in-rest (HTTP ciphertext relay)",[1362,93225,93226],{},[1007,93227,93230],{"href":93228,"rel":93229},"https:\u002F\u002Fgithub.com\u002Fnoy-db\u002Fdocs\u002Fblob\u002Fmain\u002Fshowcases\u002Fsrc\u002F47-in-rest.showcase.test.ts",[3371],[941,93231,93232],{},"47-in-rest.showcase.test.ts",[1344,93234,93235,93238,93241],{},[1362,93236,93237],{},"48",[1362,93239,93240],{},"in-nextjs (cookie-session helpers)",[1362,93242,93243],{},[1007,93244,93247],{"href":93245,"rel":93246},"https:\u002F\u002Fgithub.com\u002Fnoy-db\u002Fdocs\u002Fblob\u002Fmain\u002Fshowcases\u002Fsrc\u002F48-in-nextjs.showcase.test.ts",[3371],[941,93248,93249],{},"48-in-nextjs.showcase.test.ts",[1344,93251,93252,93254,93257],{},[1362,93253,55628],{},[1362,93255,93256],{},"by-tabs (BroadcastChannel multi-tab transport)",[1362,93258,93259],{},[1007,93260,93263],{"href":93261,"rel":93262},"https:\u002F\u002Fgithub.com\u002Fnoy-db\u002Fdocs\u002Fblob\u002Fmain\u002Fshowcases\u002Fsrc\u002F49-by-tabs.showcase.test.ts",[3371],[941,93264,93265],{},"49-by-tabs.showcase.test.ts",[1344,93267,93268,93270,93273],{},[1362,93269,26329],{},[1362,93271,93272],{},"as-json import (preview + apply with three policies)",[1362,93274,93275],{},[1007,93276,93279],{"href":93277,"rel":93278},"https:\u002F\u002Fgithub.com\u002Fnoy-db\u002Fdocs\u002Fblob\u002Fmain\u002Fshowcases\u002Fsrc\u002F50-as-json-import.showcase.test.ts",[3371],[941,93280,93281],{},"50-as-json-import.showcase.test.ts",[1344,93283,93284,93287,93290],{},[1362,93285,93286],{},"51",[1362,93288,93289],{},"as-blob \u002F as-xml \u002F as-xlsx readers",[1362,93291,93292],{},[1007,93293,93296],{"href":93294,"rel":93295},"https:\u002F\u002Fgithub.com\u002Fnoy-db\u002Fdocs\u002Fblob\u002Fmain\u002Fshowcases\u002Fsrc\u002F51-as-readers-import.showcase.test.ts",[3371],[941,93297,93298],{},"51-as-readers-import.showcase.test.ts",[1344,93300,93301,93304,93309],{},[1362,93302,93303],{},"52",[1362,93305,93306,93308],{},[941,93307,1402],{}," bundle: per-recipient expiry + record filters",[1362,93310,93311],{},[1007,93312,93315],{"href":93313,"rel":93314},"https:\u002F\u002Fgithub.com\u002Fnoy-db\u002Fdocs\u002Fblob\u002Fmain\u002Fshowcases\u002Fsrc\u002F52-as-noydb-bundle-extras.showcase.test.ts",[3371],[941,93316,93317],{},"52-as-noydb-bundle-extras.showcase.test.ts",[1344,93319,93320,93323,93326],{},[1362,93321,93322],{},"55",[1362,93324,93325],{},"Storage: meter (drop-in metrics wrapper)",[1362,93327,93328],{},[1007,93329,93332],{"href":93330,"rel":93331},"https:\u002F\u002Fgithub.com\u002Fnoy-db\u002Fdocs\u002Fblob\u002Fmain\u002Fshowcases\u002Fsrc\u002F55-storage-meter.showcase.test.ts",[3371],[941,93333,93334],{},"55-storage-meter.showcase.test.ts",[1344,93336,93337,93340,93343],{},[1362,93338,93339],{},"56",[1362,93341,93342],{},"Storage: probe (5-axis suitability check)",[1362,93344,93345],{},[1007,93346,93349],{"href":93347,"rel":93348},"https:\u002F\u002Fgithub.com\u002Fnoy-db\u002Fdocs\u002Fblob\u002Fmain\u002Fshowcases\u002Fsrc\u002F56-storage-probe.showcase.test.ts",[3371],[941,93350,93351],{},"56-storage-probe.showcase.test.ts",[1344,93353,93354,93357,93360],{},[1362,93355,93356],{},"70",[1362,93358,93359],{},"User envelope: per-principal profile + preferences",[1362,93361,93362],{},[1007,93363,93366],{"href":93364,"rel":93365},"https:\u002F\u002Fgithub.com\u002Fnoy-db\u002Fdocs\u002Fblob\u002Fmain\u002Fshowcases\u002Fsrc\u002F70-user-envelope.showcase.test.ts",[3371],[941,93367,93368],{},"70-user-envelope.showcase.test.ts",[1344,93370,93371,93374,93377],{},[1362,93372,93373],{},"71",[1362,93375,93376],{},"on-password tier-2 capability matrix",[1362,93378,93379],{},[1007,93380,93383],{"href":93381,"rel":93382},"https:\u002F\u002Fgithub.com\u002Fnoy-db\u002Fdocs\u002Fblob\u002Fmain\u002Fshowcases\u002Fsrc\u002F71-on-password-tier2.showcase.test.ts",[3371],[941,93384,93385],{},"71-on-password-tier2.showcase.test.ts",[1344,93387,93388,93391,93394],{},[1362,93389,93390],{},"72",[1362,93392,93393],{},"on-webauthn real-provider via Playwright virtual authenticator",[1362,93395,93396],{},[1007,93397,93400],{"href":93398,"rel":93399},"https:\u002F\u002Fgithub.com\u002Fnoy-db\u002Fdocs\u002Fblob\u002Fmain\u002Fshowcases\u002Fsrc\u002F72-on-webauthn-virtual.showcase.test.ts",[3371],[941,93401,93402],{},"72-on-webauthn-virtual.showcase.test.ts",[1344,93404,93405,93408,93411],{},[1362,93406,93407],{},"73",[1362,93409,93410],{},"OIDC vault unlock via Sign in with Google (IdP bridge)",[1362,93412,93413],{},[1007,93414,93417],{"href":93415,"rel":93416},"https:\u002F\u002Fgithub.com\u002Fnoy-db\u002Fdocs\u002Fblob\u002Fmain\u002Fshowcases\u002Fsrc\u002F73-on-oidc-google.showcase.test.ts",[3371],[941,93418,93419],{},"73-on-oidc-google.showcase.test.ts",[1344,93421,93422,93425,93428],{},[1362,93423,93424],{},"74",[1362,93426,93427],{},"OIDC vault unlock via Sign in with Apple (IdP bridge)",[1362,93429,93430],{},[1007,93431,93434],{"href":93432,"rel":93433},"https:\u002F\u002Fgithub.com\u002Fnoy-db\u002Fdocs\u002Fblob\u002Fmain\u002Fshowcases\u002Fsrc\u002F74-on-oidc-apple.showcase.test.ts",[3371],[941,93435,93436],{},"74-on-oidc-apple.showcase.test.ts",[1344,93438,93439,93442,93445],{},[1362,93440,93441],{},"75",[1362,93443,93444],{},"OIDC vault unlock via LINE Login (IdP bridge)",[1362,93446,93447],{},[1007,93448,93451],{"href":93449,"rel":93450},"https:\u002F\u002Fgithub.com\u002Fnoy-db\u002Fdocs\u002Fblob\u002Fmain\u002Fshowcases\u002Fsrc\u002F75-on-oidc-line.showcase.test.ts",[3371],[941,93452,93453],{},"75-on-oidc-line.showcase.test.ts",[1344,93455,93456,93459,93462],{},[1362,93457,93458],{},"76",[1362,93460,93461],{},"TOTP (RFC 6238) interop with Google Authenticator",[1362,93463,93464],{},[1007,93465,93468],{"href":93466,"rel":93467},"https:\u002F\u002Fgithub.com\u002Fnoy-db\u002Fdocs\u002Fblob\u002Fmain\u002Fshowcases\u002Fsrc\u002F76-on-totp-google-authenticator.showcase.test.ts",[3371],[941,93469,93470],{},"76-on-totp-google-authenticator.showcase.test.ts",[1344,93472,93473,93476,93479],{},[1362,93474,93475],{},"77",[1362,93477,93478],{},"on-email-otp delivered via real SMTP",[1362,93480,93481],{},[1007,93482,93485],{"href":93483,"rel":93484},"https:\u002F\u002Fgithub.com\u002Fnoy-db\u002Fdocs\u002Fblob\u002Fmain\u002Fshowcases\u002Fsrc\u002F77-on-email-otp-smtp.showcase.test.ts",[3371],[941,93486,93487],{},"77-on-email-otp-smtp.showcase.test.ts",[1344,93489,93490,93493,93496],{},[1362,93491,93492],{},"78",[1362,93494,93495],{},"on-magic-link delivered via real SMTP",[1362,93497,93498],{},[1007,93499,93502],{"href":93500,"rel":93501},"https:\u002F\u002Fgithub.com\u002Fnoy-db\u002Fdocs\u002Fblob\u002Fmain\u002Fshowcases\u002Fsrc\u002F78-on-magic-link-smtp.showcase.test.ts",[3371],[941,93503,93504],{},"78-on-magic-link-smtp.showcase.test.ts",[1344,93506,93507,93510,93513],{},[1362,93508,93509],{},"79",[1362,93511,93512],{},"withGuard (accounting end-to-end)",[1362,93514,93515],{},[1007,93516,93519],{"href":93517,"rel":93518},"https:\u002F\u002Fgithub.com\u002Fnoy-db\u002Fdocs\u002Fblob\u002Fmain\u002Fshowcases\u002Fsrc\u002F79-with-guard.showcase.test.ts",[3371],[941,93520,93521],{},"79-with-guard.showcase.test.ts",[1344,93523,93524,93527,93530],{},[1362,93525,93526],{},"80",[1362,93528,93529],{},"withDerivation (Dim 14)",[1362,93531,93532],{},[1007,93533,93536],{"href":93534,"rel":93535},"https:\u002F\u002Fgithub.com\u002Fnoy-db\u002Fdocs\u002Fblob\u002Fmain\u002Fshowcases\u002Fsrc\u002F80-with-derivation.showcase.test.ts",[3371],[941,93537,93538],{},"80-with-derivation.showcase.test.ts",[1344,93540,93541,93544,93547],{},[1362,93542,93543],{},"81",[1362,93545,93546],{},"withMaterializedView, eager refresh (Dim 14 v2)",[1362,93548,93549],{},[1007,93550,93553],{"href":93551,"rel":93552},"https:\u002F\u002Fgithub.com\u002Fnoy-db\u002Fdocs\u002Fblob\u002Fmain\u002Fshowcases\u002Fsrc\u002F81-with-mv-eager.showcase.test.ts",[3371],[941,93554,93555],{},"81-with-mv-eager.showcase.test.ts",[1344,93557,93558,93561,93564],{},[1362,93559,93560],{},"82",[1362,93562,93563],{},"withMaterializedView, lazy lifecycle (Dim 14 v2)",[1362,93565,93566],{},[1007,93567,93570],{"href":93568,"rel":93569},"https:\u002F\u002Fgithub.com\u002Fnoy-db\u002Fdocs\u002Fblob\u002Fmain\u002Fshowcases\u002Fsrc\u002F82-with-mv-lazy.showcase.test.ts",[3371],[941,93571,93572],{},"82-with-mv-lazy.showcase.test.ts",[1344,93574,93575,93578,93581],{},[1362,93576,93577],{},"83",[1362,93579,93580],{},"withOverlayedView (Dim 14 v2)",[1362,93582,93583],{},[1007,93584,93587],{"href":93585,"rel":93586},"https:\u002F\u002Fgithub.com\u002Fnoy-db\u002Fdocs\u002Fblob\u002Fmain\u002Fshowcases\u002Fsrc\u002F83-with-overlay.showcase.test.ts",[3371],[941,93588,93589],{},"83-with-overlay.showcase.test.ts",[1344,93591,93592,93595,93598],{},[1362,93593,93594],{},"84",[1362,93596,93597],{},"declaredDeterministicPredicates (Dim 14 v2)",[1362,93599,93600],{},[1007,93601,93604],{"href":93602,"rel":93603},"https:\u002F\u002Fgithub.com\u002Fnoy-db\u002Fdocs\u002Fblob\u002Fmain\u002Fshowcases\u002Fsrc\u002F84-with-mv-predicates.showcase.test.ts",[3371],[941,93605,93606],{},"84-with-mv-predicates.showcase.test.ts",[1344,93608,93609,93612,93615],{},[1362,93610,93611],{},"85",[1362,93613,93614],{},"multi-key groupBy",[1362,93616,93617],{},[1007,93618,93621],{"href":93619,"rel":93620},"https:\u002F\u002Fgithub.com\u002Fnoy-db\u002Fdocs\u002Fblob\u002Fmain\u002Fshowcases\u002Fsrc\u002F85-with-multikey-groupby.showcase.test.ts",[3371],[941,93622,93623],{},"85-with-multikey-groupby.showcase.test.ts",[1344,93625,93626,93629,93632],{},[1362,93627,93628],{},"86",[1362,93630,93631],{},"UNION MV (withMaterializedView reads multiple sibling collections)",[1362,93633,93634],{},[1007,93635,93638],{"href":93636,"rel":93637},"https:\u002F\u002Fgithub.com\u002Fnoy-db\u002Fdocs\u002Fblob\u002Fmain\u002Fshowcases\u002Fsrc\u002F86-with-union-mv.showcase.test.ts",[3371],[941,93639,93640],{},"86-with-union-mv.showcase.test.ts",[1344,93642,93643,93646,93651],{},[1362,93644,93645],{},"87",[1362,93647,93648,93650],{},[941,93649,36137],{}," audit pipeline",[1362,93652,93653],{},[1007,93654,93657],{"href":93655,"rel":93656},"https:\u002F\u002Fgithub.com\u002Fnoy-db\u002Fdocs\u002Fblob\u002Fmain\u002Fshowcases\u002Fsrc\u002F87-noydb-describe.showcase.test.ts",[3371],[941,93658,93659],{},"87-noydb-describe.showcase.test.ts",[1344,93661,93662,93665,93668],{},[1362,93663,93664],{},"88",[1362,93666,93667],{},"Transferable partition bundles (extract → adopt → own)",[1362,93669,93670],{},[1007,93671,93674],{"href":93672,"rel":93673},"https:\u002F\u002Fgithub.com\u002Fnoy-db\u002Fdocs\u002Fblob\u002Fmain\u002Fshowcases\u002Fsrc\u002F88-transferable-partition.showcase.test.ts",[3371],[941,93675,93676],{},"88-transferable-partition.showcase.test.ts",[1344,93678,93679,93682,93685],{},[1362,93680,93681],{},"89",[1362,93683,93684],{},"Document attestation: revocation publishing",[1362,93686,93687],{},[1007,93688,93691],{"href":93689,"rel":93690},"https:\u002F\u002Fgithub.com\u002Fnoy-db\u002Fdocs\u002Fblob\u002Fmain\u002Fshowcases\u002Fsrc\u002F89-attestation-revocation.showcase.test.ts",[3371],[941,93692,93693],{},"89-attestation-revocation.showcase.test.ts",[1344,93695,93696,93699,93702],{},[1362,93697,93698],{},"90",[1362,93700,93701],{},"in-devtools (createInspector)",[1362,93703,93704],{},[1007,93705,93708],{"href":93706,"rel":93707},"https:\u002F\u002Fgithub.com\u002Fnoy-db\u002Fdocs\u002Fblob\u002Fmain\u002Fshowcases\u002Fsrc\u002F90-in-devtools.showcase.test.ts",[3371],[941,93709,93710],{},"90-in-devtools.showcase.test.ts",[1344,93712,93713,93716,93719],{},[1362,93714,93715],{},"91",[1362,93717,93718],{},"in-devtools records paging + write monitor",[1362,93720,93721],{},[1007,93722,93725],{"href":93723,"rel":93724},"https:\u002F\u002Fgithub.com\u002Fnoy-db\u002Fdocs\u002Fblob\u002Fmain\u002Fshowcases\u002Fsrc\u002F91-in-devtools-records-monitor.showcase.test.ts",[3371],[941,93726,93727],{},"91-in-devtools-records-monitor.showcase.test.ts",[1344,93729,93730,93733,93736],{},[1362,93731,93732],{},"92",[1362,93734,93735],{},"Cross-join query primitive",[1362,93737,93738],{},[1007,93739,93742],{"href":93740,"rel":93741},"https:\u002F\u002Fgithub.com\u002Fnoy-db\u002Fdocs\u002Fblob\u002Fmain\u002Fshowcases\u002Fsrc\u002F92-with-cross-join.showcase.test.ts",[3371],[941,93743,93744],{},"92-with-cross-join.showcase.test.ts",[1344,93746,93747,93750,93753],{},[1362,93748,93749],{},"93",[1362,93751,93752],{},"Snapshot lifecycle (withSnapshots)",[1362,93754,93755],{},[1007,93756,93759],{"href":93757,"rel":93758},"https:\u002F\u002Fgithub.com\u002Fnoy-db\u002Fdocs\u002Fblob\u002Fmain\u002Fshowcases\u002Fsrc\u002F93-with-snapshots.showcase.test.ts",[3371],[941,93760,93761],{},"93-with-snapshots.showcase.test.ts",[1344,93763,93764,93767,93770],{},[1362,93765,93766],{},"94",[1362,93768,93769],{},"i18n hardening (per-layer onMissing, substitute, script, dictKey parity)",[1362,93771,93772],{},[1007,93773,93776],{"href":93774,"rel":93775},"https:\u002F\u002Fgithub.com\u002Fnoy-db\u002Fdocs\u002Fblob\u002Fmain\u002Fshowcases\u002Fsrc\u002F94-with-i18n-hardening.showcase.test.ts",[3371],[941,93777,93778],{},"94-with-i18n-hardening.showcase.test.ts",[1344,93780,93781,93784,93787],{},[1362,93782,93783],{},"95",[1362,93785,93786],{},"in-pinia reactive i18n",[1362,93788,93789],{},[1007,93790,93793],{"href":93791,"rel":93792},"https:\u002F\u002Fgithub.com\u002Fnoy-db\u002Fdocs\u002Fblob\u002Fmain\u002Fshowcases\u002Fsrc\u002F95-in-pinia-i18n.showcase.test.ts",[3371],[941,93794,93795],{},"95-in-pinia-i18n.showcase.test.ts",[1344,93797,93798,93801,93804],{},[1362,93799,93800],{},"96",[1362,93802,93803],{},"automatic snapshot cadence (rolling auto key)",[1362,93805,93806],{},[1007,93807,93810],{"href":93808,"rel":93809},"https:\u002F\u002Fgithub.com\u002Fnoy-db\u002Fdocs\u002Fblob\u002Fmain\u002Fshowcases\u002Fsrc\u002F96-snapshots-auto-cadence.showcase.test.ts",[3371],[941,93811,93812],{},"96-snapshots-auto-cadence.showcase.test.ts",[1344,93814,93815,93817,93820],{},[1362,93816,55369],{},[1362,93818,93819],{},"AWS credential broker: enroll, rolling credentials, store wiring",[1362,93821,93822],{},[1007,93823,93826],{"href":93824,"rel":93825},"https:\u002F\u002Fgithub.com\u002Fnoy-db\u002Fdocs\u002Fblob\u002Fmain\u002Fshowcases\u002Fsrc\u002F97-broker-aws.showcase.test.ts",[3371],[941,93827,93828],{},"97-broker-aws.showcase.test.ts",[1344,93830,93831,93834,93837],{},[1362,93832,93833],{},"98",[1362,93835,93836],{},"Storage: Google Drive (real provider, appDataFolder + bundle)",[1362,93838,93839],{},[1007,93840,93843],{"href":93841,"rel":93842},"https:\u002F\u002Fgithub.com\u002Fnoy-db\u002Fdocs\u002Fblob\u002Fmain\u002Fshowcases\u002Fsrc\u002F98-storage-drive.showcase.test.ts",[3371],[941,93844,93845],{},"98-storage-drive.showcase.test.ts",[1344,93847,93848,93851,93854],{},[1362,93849,93850],{},"99",[1362,93852,93853],{},"Storage: iCloud Drive (real provider, macOS only)",[1362,93855,93856],{},[1007,93857,93860],{"href":93858,"rel":93859},"https:\u002F\u002Fgithub.com\u002Fnoy-db\u002Fdocs\u002Fblob\u002Fmain\u002Fshowcases\u002Fsrc\u002F99-storage-icloud.showcase.test.ts",[3371],[941,93861,93862],{},"99-storage-icloud.showcase.test.ts",[1344,93864,93865,93867,93870],{},[1362,93866,24908],{},[1362,93868,93869],{},"erasable blobs (per-blob CEK crypto-shred)",[1362,93871,93872],{},[1007,93873,93876],{"href":93874,"rel":93875},"https:\u002F\u002Fgithub.com\u002Fnoy-db\u002Fdocs\u002Fblob\u002Fmain\u002Fshowcases\u002Fsrc\u002F100-with-erasable-blobs.showcase.test.ts",[3371],[941,93877,93878],{},"100-with-erasable-blobs.showcase.test.ts",[1344,93880,93881,93884,93887],{},[1362,93882,93883],{},"101",[1362,93885,93886],{},"transitionGuard (state-machine lifecycle)",[1362,93888,93889],{},[1007,93890,93893],{"href":93891,"rel":93892},"https:\u002F\u002Fgithub.com\u002Fnoy-db\u002Fdocs\u002Fblob\u002Fmain\u002Fshowcases\u002Fsrc\u002F101-transition-guard.showcase.test.ts",[3371],[941,93894,93895],{},"101-transition-guard.showcase.test.ts",[1344,93897,93898,93901,93904],{},[1362,93899,93900],{},"102",[1362,93902,93903],{},"formatted sequences (fiscal numbering)",[1362,93905,93906],{},[1007,93907,93910],{"href":93908,"rel":93909},"https:\u002F\u002Fgithub.com\u002Fnoy-db\u002Fdocs\u002Fblob\u002Fmain\u002Fshowcases\u002Fsrc\u002F102-formatted-sequence.showcase.test.ts",[3371],[941,93911,93912],{},"102-formatted-sequence.showcase.test.ts",[1344,93914,93915,93918,93921],{},[1362,93916,93917],{},"103",[1362,93919,93920],{},"refArray (many-to-many integrity)",[1362,93922,93923],{},[1007,93924,93927],{"href":93925,"rel":93926},"https:\u002F\u002Fgithub.com\u002Fnoy-db\u002Fdocs\u002Fblob\u002Fmain\u002Fshowcases\u002Fsrc\u002F103-ref-array.showcase.test.ts",[3371],[941,93928,93929],{},"103-ref-array.showcase.test.ts",[1344,93931,93932,93935,93938],{},[1362,93933,93934],{},"104",[1362,93936,93937],{},"reverse-denormalization (FK derivation triggers)",[1362,93939,93940],{},[1007,93941,93944],{"href":93942,"rel":93943},"https:\u002F\u002Fgithub.com\u002Fnoy-db\u002Fdocs\u002Fblob\u002Fmain\u002Fshowcases\u002Fsrc\u002F104-reverse-denorm.showcase.test.ts",[3371],[941,93945,93946],{},"104-reverse-denorm.showcase.test.ts",[1344,93948,93949,93952,93955],{},[1362,93950,93951],{},"105",[1362,93953,93954],{},"rollups (aggregate onto a parent field)",[1362,93956,93957],{},[1007,93958,93961],{"href":93959,"rel":93960},"https:\u002F\u002Fgithub.com\u002Fnoy-db\u002Fdocs\u002Fblob\u002Fmain\u002Fshowcases\u002Fsrc\u002F105-rollup.showcase.test.ts",[3371],[941,93962,93963],{},"105-rollup.showcase.test.ts",[1344,93965,93966,93969,93972],{},[1362,93967,93968],{},"106",[1362,93970,93971],{},"in-pinia dynamic vault resolver (federation routing)",[1362,93973,93974],{},[1007,93975,93978],{"href":93976,"rel":93977},"https:\u002F\u002Fgithub.com\u002Fnoy-db\u002Fdocs\u002Fblob\u002Fmain\u002Fshowcases\u002Fsrc\u002F106-in-pinia-dynamic-vault.showcase.test.ts",[3371],[941,93979,93980],{},"106-in-pinia-dynamic-vault.showcase.test.ts",[1344,93982,93983,93986,93989],{},[1362,93984,93985],{},"107",[1362,93987,93988],{},"vault.link (managed many-to-many junction)",[1362,93990,93991],{},[1007,93992,93995],{"href":93993,"rel":93994},"https:\u002F\u002Fgithub.com\u002Fnoy-db\u002Fdocs\u002Fblob\u002Fmain\u002Fshowcases\u002Fsrc\u002F107-vault-link.showcase.test.ts",[3371],[941,93996,93997],{},"107-vault-link.showcase.test.ts",[1344,93999,94000,94003,94006],{},[1362,94001,94002],{},"108",[1362,94004,94005],{},"staticDict() code-provided dictionary (#291)",[1362,94007,94008],{},[1007,94009,94012],{"href":94010,"rel":94011},"https:\u002F\u002Fgithub.com\u002Fnoy-db\u002Fdocs\u002Fblob\u002Fmain\u002Fshowcases\u002Fsrc\u002F108-with-static-dict.showcase.test.ts",[3371],[941,94013,94014],{},"108-with-static-dict.showcase.test.ts",[1344,94016,94017,94020,94023],{},[1362,94018,94019],{},"111",[1362,94021,94022],{},"Scan-mode full-text search (collection.search)",[1362,94024,94025],{},[1007,94026,94029],{"href":94027,"rel":94028},"https:\u002F\u002Fgithub.com\u002Fnoy-db\u002Fdocs\u002Fblob\u002Fmain\u002Fshowcases\u002Fsrc\u002F111-scan-search.showcase.test.ts",[3371],[941,94030,94031],{},"111-scan-search.showcase.test.ts",[1344,94033,94034,94037,94040],{},[1362,94035,94036],{},"112",[1362,94038,94039],{},"debug-plaintext store mode (#413)",[1362,94041,94042],{},[1007,94043,94046],{"href":94044,"rel":94045},"https:\u002F\u002Fgithub.com\u002Fnoy-db\u002Fdocs\u002Fblob\u002Fmain\u002Fshowcases\u002Fsrc\u002F112-debug-plaintext-mode.showcase.test.ts",[3371],[941,94047,94048],{},"112-debug-plaintext-mode.showcase.test.ts",[1344,94050,94051,94054,94057],{},[1362,94052,94053],{},"113",[1362,94055,94056],{},"external (direct-serve) blob fields (#412)",[1362,94058,94059],{},[1007,94060,94063],{"href":94061,"rel":94062},"https:\u002F\u002Fgithub.com\u002Fnoy-db\u002Fdocs\u002Fblob\u002Fmain\u002Fshowcases\u002Fsrc\u002F113-external-blobs.showcase.test.ts",[3371],[941,94064,94065],{},"113-external-blobs.showcase.test.ts",[1344,94067,94068,94071,94074],{},[1362,94069,94070],{},"114",[1362,94072,94073],{},"as-xlsx smart workbook (#414 P1)",[1362,94075,94076],{},[1007,94077,94080],{"href":94078,"rel":94079},"https:\u002F\u002Fgithub.com\u002Fnoy-db\u002Fdocs\u002Fblob\u002Fmain\u002Fshowcases\u002Fsrc\u002F114-as-xlsx-smart.showcase.test.ts",[3371],[941,94081,94082],{},"114-as-xlsx-smart.showcase.test.ts",[1344,94084,94085,94088,94091],{},[1362,94086,94087],{},"115",[1362,94089,94090],{},"as-xlsx global LANG cell (#414 P2)",[1362,94092,94093],{},[1007,94094,94097],{"href":94095,"rel":94096},"https:\u002F\u002Fgithub.com\u002Fnoy-db\u002Fdocs\u002Fblob\u002Fmain\u002Fshowcases\u002Fsrc\u002F115-as-xlsx-lang-cell.showcase.test.ts",[3371],[941,94098,94099],{},"115-as-xlsx-lang-cell.showcase.test.ts",[1344,94101,94102,94105,94108],{},[1362,94103,94104],{},"116",[1362,94106,94107],{},"as-xlsx groupBy summary sheets (#414 P3)",[1362,94109,94110],{},[1007,94111,94114],{"href":94112,"rel":94113},"https:\u002F\u002Fgithub.com\u002Fnoy-db\u002Fdocs\u002Fblob\u002Fmain\u002Fshowcases\u002Fsrc\u002F116-as-xlsx-summary.showcase.test.ts",[3371],[941,94115,94116],{},"116-as-xlsx-summary.showcase.test.ts",[1344,94118,94119,94122,94125],{},[1362,94120,94121],{},"117",[1362,94123,94124],{},"as-xlsx smart import (#414 P4)",[1362,94126,94127],{},[1007,94128,94131],{"href":94129,"rel":94130},"https:\u002F\u002Fgithub.com\u002Fnoy-db\u002Fdocs\u002Fblob\u002Fmain\u002Fshowcases\u002Fsrc\u002F117-as-xlsx-smart-import.showcase.test.ts",[3371],[941,94132,94133],{},"117-as-xlsx-smart-import.showcase.test.ts",[1344,94135,94136,94139,94142],{},[1362,94137,94138],{},"118",[1362,94140,94141],{},"as-xlsx Google-Sheets QUERY dialect (#414 P5)",[1362,94143,94144],{},[1007,94145,94148],{"href":94146,"rel":94147},"https:\u002F\u002Fgithub.com\u002Fnoy-db\u002Fdocs\u002Fblob\u002Fmain\u002Fshowcases\u002Fsrc\u002F118-as-xlsx-sheets-query.showcase.test.ts",[3371],[941,94149,94150],{},"118-as-xlsx-sheets-query.showcase.test.ts",[1344,94152,94153,94156,94159],{},[1362,94154,94155],{},"119",[1362,94157,94158],{},"as-xlsx schema inference (#414 P4 Mode B)",[1362,94160,94161],{},[1007,94162,94165],{"href":94163,"rel":94164},"https:\u002F\u002Fgithub.com\u002Fnoy-db\u002Fdocs\u002Fblob\u002Fmain\u002Fshowcases\u002Fsrc\u002F119-as-xlsx-infer-schema.showcase.test.ts",[3371],[941,94166,94167],{},"119-as-xlsx-infer-schema.showcase.test.ts",[1344,94169,94170,94173,94176],{},[1362,94171,94172],{},"120",[1362,94174,94175],{},"Client-initiated portability + withdrawal (#199)",[1362,94177,94178],{},[1007,94179,94182],{"href":94180,"rel":94181},"https:\u002F\u002Fgithub.com\u002Fnoy-db\u002Fdocs\u002Fblob\u002Fmain\u002Fshowcases\u002Fsrc\u002F120-client-portability-withdrawal.showcase.test.ts",[3371],[941,94183,94184],{},"120-client-portability-withdrawal.showcase.test.ts",[1344,94186,94187,94190,94193],{},[1362,94188,94189],{},"121",[1362,94191,94192],{},"densifyOnWrite + i18n:script-violation event (#435)",[1362,94194,94195],{},[1007,94196,94199],{"href":94197,"rel":94198},"https:\u002F\u002Fgithub.com\u002Fnoy-db\u002Fdocs\u002Fblob\u002Fmain\u002Fshowcases\u002Fsrc\u002F121-with-i18n-densify.showcase.test.ts",[3371],[941,94200,94201],{},"121-with-i18n-densify.showcase.test.ts",[1344,94203,94204,94207,94210],{},[1362,94205,94206],{},"122",[1362,94208,94209],{},"Client-side lexical retrieval (collection.retrieve) — #308 L1",[1362,94211,94212],{},[1007,94213,94216],{"href":94214,"rel":94215},"https:\u002F\u002Fgithub.com\u002Fnoy-db\u002Fdocs\u002Fblob\u002Fmain\u002Fshowcases\u002Fsrc\u002F122-with-retrieve.showcase.test.ts",[3371],[941,94217,94218],{},"122-with-retrieve.showcase.test.ts",[1344,94220,94221,94224,94227],{},[1362,94222,94223],{},"123",[1362,94225,94226],{},"Persisted lexical index (textIndexPersist) — #308 L1.5",[1362,94228,94229],{},[1007,94230,94233],{"href":94231,"rel":94232},"https:\u002F\u002Fgithub.com\u002Fnoy-db\u002Fdocs\u002Fblob\u002Fmain\u002Fshowcases\u002Fsrc\u002F123-persisted-index.showcase.test.ts",[3371],[941,94234,94235],{},"123-persisted-index.showcase.test.ts",[1344,94237,94238,94241,94244],{},[1362,94239,94240],{},"124",[1362,94242,94243],{},"Semantic \u002F vector retrieval (collection.retrieve mode:'semantic' + similarTo) — #308 L2",[1362,94245,94246],{},[1007,94247,94250],{"href":94248,"rel":94249},"https:\u002F\u002Fgithub.com\u002Fnoy-db\u002Fdocs\u002Fblob\u002Fmain\u002Fshowcases\u002Fsrc\u002F124-semantic-retrieve.showcase.test.ts",[3371],[941,94251,94252],{},"124-semantic-retrieve.showcase.test.ts",[1344,94254,94255,94258,94261],{},[1362,94256,94257],{},"125",[1362,94259,94260],{},"Hybrid retrieval (collection.retrieve mode:'hybrid' + fuseRetrieval) — #308 L3",[1362,94262,94263],{},[1007,94264,94267],{"href":94265,"rel":94266},"https:\u002F\u002Fgithub.com\u002Fnoy-db\u002Fdocs\u002Fblob\u002Fmain\u002Fshowcases\u002Fsrc\u002F125-hybrid-retrieve.showcase.test.ts",[3371],[941,94268,94269],{},"125-hybrid-retrieve.showcase.test.ts",[1344,94271,94272,94275,94278],{},[1362,94273,94274],{},"126",[1362,94276,94277],{},"collection.describe() + fieldMeta: two consumers, one source (#483)",[1362,94279,94280],{},[1007,94281,94284],{"href":94282,"rel":94283},"https:\u002F\u002Fgithub.com\u002Fnoy-db\u002Fdocs\u002Fblob\u002Fmain\u002Fshowcases\u002Fsrc\u002F126-describe-field-metadata.showcase.test.ts",[3371],[941,94285,94286],{},"126-describe-field-metadata.showcase.test.ts",[1344,94288,94289,94292,94295],{},[1362,94290,94291],{},"127",[1362,94293,94294],{},"withArchive() cold-storage sweep",[1362,94296,94297],{},[1007,94298,94301],{"href":94299,"rel":94300},"https:\u002F\u002Fgithub.com\u002Fnoy-db\u002Fdocs\u002Fblob\u002Fmain\u002Fshowcases\u002Fsrc\u002F127-with-archive.showcase.test.ts",[3371],[941,94302,94303],{},"127-with-archive.showcase.test.ts",[1344,94305,94306,94309,94312],{},[1362,94307,94308],{},"128",[1362,94310,94311],{},"classified fields (seal, digest, reveal)",[1362,94313,94314],{},[1007,94315,94318],{"href":94316,"rel":94317},"https:\u002F\u002Fgithub.com\u002Fnoy-db\u002Fdocs\u002Fblob\u002Fmain\u002Fshowcases\u002Fsrc\u002F128-with-classified.showcase.test.ts",[3371],[941,94319,94320],{},"128-with-classified.showcase.test.ts",[1344,94322,94323,94326,94329],{},[1362,94324,94325],{},"129",[1362,94327,94328],{},"withCustody() — full operation, no ownership",[1362,94330,94331],{},[1007,94332,94335],{"href":94333,"rel":94334},"https:\u002F\u002Fgithub.com\u002Fnoy-db\u002Fdocs\u002Fblob\u002Fmain\u002Fshowcases\u002Fsrc\u002F129-with-custody.showcase.test.ts",[3371],[941,94336,94337],{},"129-with-custody.showcase.test.ts",[1344,94339,94340,94343,94346],{},[1362,94341,94342],{},"130",[1362,94344,94345],{},"withForget() crypto-shred",[1362,94347,94348],{},[1007,94349,94352],{"href":94350,"rel":94351},"https:\u002F\u002Fgithub.com\u002Fnoy-db\u002Fdocs\u002Fblob\u002Fmain\u002Fshowcases\u002Fsrc\u002F130-with-forget.showcase.test.ts",[3371],[941,94353,94354],{},"130-with-forget.showcase.test.ts",[1344,94356,94357,94360,94363],{},[1362,94358,94359],{},"131",[1362,94361,94362],{},"withLazy() bounded-memory reads",[1362,94364,94365],{},[1007,94366,94369],{"href":94367,"rel":94368},"https:\u002F\u002Fgithub.com\u002Fnoy-db\u002Fdocs\u002Fblob\u002Fmain\u002Fshowcases\u002Fsrc\u002F131-with-lazy.showcase.test.ts",[3371],[941,94370,94371],{},"131-with-lazy.showcase.test.ts",[1344,94373,94374,94377,94380],{},[1362,94375,94376],{},"132",[1362,94378,94379],{},"deferred numbering (gap-free without CAS)",[1362,94381,94382],{},[1007,94383,94386],{"href":94384,"rel":94385},"https:\u002F\u002Fgithub.com\u002Fnoy-db\u002Fdocs\u002Fblob\u002Fmain\u002Fshowcases\u002Fsrc\u002F132-deferred-numbering.showcase.test.ts",[3371],[941,94387,94388],{},"132-deferred-numbering.showcase.test.ts",[1344,94390,94391,94394,94397],{},[1362,94392,94393],{},"133",[1362,94395,94396],{},"withSealedRecord() single-record disclosure",[1362,94398,94399],{},[1007,94400,94403],{"href":94401,"rel":94402},"https:\u002F\u002Fgithub.com\u002Fnoy-db\u002Fdocs\u002Fblob\u002Fmain\u002Fshowcases\u002Fsrc\u002F133-sealed-record.showcase.test.ts",[3371],[941,94404,94405],{},"133-sealed-record.showcase.test.ts",[1344,94407,94408,94411,94414],{},[1362,94409,94410],{},"134",[1362,94412,94413],{},"AWS credential broker rollover: rolling credentials across a session",[1362,94415,94416],{},[1007,94417,94420],{"href":94418,"rel":94419},"https:\u002F\u002Fgithub.com\u002Fnoy-db\u002Fdocs\u002Fblob\u002Fmain\u002Fshowcases\u002Fsrc\u002F134-broker-aws-rollover.showcase.test.ts",[3371],[941,94421,94422],{},"134-broker-aws-rollover.showcase.test.ts",[1591,94424,94426],{"id":94425},"recipe-walkthroughs","Recipe walkthroughs",[1338,94428,94429,94439],{},[1341,94430,94431],{},[1344,94432,94433,94435,94437],{},[1347,94434,3096],{},[1347,94436,92444],{},[1347,94438,92447],{},[1357,94440,94441,94456,94471,94485,94500,94515,94531,94546,94561,94577,94594,94609,94625,94639,94654],{},[1344,94442,94443,94446,94448],{},[1362,94444,94445],{},"·",[1362,94447,69985],{},[1362,94449,94450],{},[1007,94451,94453],{"href":65359,"rel":94452},[3371],[941,94454,94455],{},"recipe-attestation-verifier.recipe.test.ts",[1344,94457,94458,94460,94463],{},[1362,94459,94445],{},[1362,94461,94462],{},"AWS-KMS PDF attestation (generation side, CI-safe slice)",[1362,94464,94465],{},[1007,94466,94468],{"href":68345,"rel":94467},[3371],[941,94469,94470],{},"recipe-aws-kms-pdf-attestation.recipe.test.ts",[1344,94472,94473,94475,94477],{},[1362,94474,94445],{},[1362,94476,523],{},[1362,94478,94479],{},[1007,94480,94482],{"href":70048,"rel":94481},[3371],[941,94483,94484],{},"recipe-browser-durability.recipe.test.ts",[1344,94486,94487,94489,94492],{},[1362,94488,94445],{},[1362,94490,94491],{},"Confidential HR records (record tiers end-to-end)",[1362,94493,94494],{},[1007,94495,94497],{"href":73766,"rel":94496},[3371],[941,94498,94499],{},"recipe-confidential-hr.recipe.test.ts",[1344,94501,94502,94504,94507],{},[1362,94503,94445],{},[1362,94505,94506],{},"Passwordless tier-2 auth via a managed OIDC provider (AWS Cognito)",[1362,94508,94509],{},[1007,94510,94512],{"href":84627,"rel":94511},[3371],[941,94513,94514],{},"recipe-passwordless-oidc.recipe.test.ts",[1344,94516,94517,94519,94522],{},[1362,94518,94445],{},[1362,94520,94521],{},"User preferences (the reference UserEnvelope shape)",[1362,94523,94524],{},[1007,94525,94528],{"href":94526,"rel":94527},"https:\u002F\u002Fgithub.com\u002Fnoy-db\u002Fdocs\u002Fblob\u002Fmain\u002Fshowcases\u002Fsrc\u002Frecipe-user-preferences.recipe.test.ts",[3371],[941,94529,94530],{},"recipe-user-preferences.recipe.test.ts",[1344,94532,94533,94535,94538],{},[1362,94534,5437],{},[1362,94536,94537],{},"Personal encrypted notebook",[1362,94539,94540],{},[1007,94541,94543],{"href":86410,"rel":94542},[3371],[941,94544,94545],{},"recipe-personal-notebook.recipe.test.ts",[1344,94547,94548,94550,94553],{},[1362,94549,5470],{},[1362,94551,94552],{},"Accounting application",[1362,94554,94555],{},[1007,94556,94558],{"href":58248,"rel":94557},[3371],[941,94559,94560],{},"recipe-accounting-app.recipe.test.ts",[1344,94562,94563,94565,94568],{},[1362,94564,5512],{},[1362,94566,94567],{},"Real-time collaborative app",[1362,94569,94570],{},[1007,94571,94574],{"href":94572,"rel":94573},"https:\u002F\u002Fgithub.com\u002Fnoy-db\u002Fdocs\u002Fblob\u002Fmain\u002Fshowcases\u002Fsrc\u002Frecipe-realtime-crdt.recipe.test.ts",[3371],[941,94575,94576],{},"recipe-realtime-crdt.recipe.test.ts",[1344,94578,94579,94582,94585],{},[1362,94580,94581],{},"4",[1362,94583,94584],{},"Analytics-heavy querying",[1362,94586,94587],{},[1007,94588,94591],{"href":94589,"rel":94590},"https:\u002F\u002Fgithub.com\u002Fnoy-db\u002Fdocs\u002Fblob\u002Fmain\u002Fshowcases\u002Fsrc\u002Frecipe-analytics.recipe.test.ts",[3371],[941,94592,94593],{},"recipe-analytics.recipe.test.ts",[1344,94595,94596,94599,94601],{},[1362,94597,94598],{},"8",[1362,94600,568],{},[1362,94602,94603],{},[1007,94604,94606],{"href":88116,"rel":94605},[3371],[941,94607,94608],{},"recipe-react-invoices.recipe.test.tsx",[1344,94610,94611,94614,94616],{},[1362,94612,94613],{},"9",[1362,94615,589],{},[1362,94617,94618],{},[1007,94619,94622],{"href":94620,"rel":94621},"https:\u002F\u002Fgithub.com\u002Fnoy-db\u002Fdocs\u002Fblob\u002Fmain\u002Fshowcases\u002Fsrc\u002Frecipe-unlock-ceremonies.recipe.test.ts",[3371],[941,94623,94624],{},"recipe-unlock-ceremonies.recipe.test.ts",[1344,94626,94627,94629,94631],{},[1362,94628,87121],{},[1362,94630,551],{},[1362,94632,94633],{},[1007,94634,94636],{"href":83135,"rel":94635},[3371],[941,94637,94638],{},"recipe-export-portability.recipe.test.ts",[1344,94640,94641,94643,94645],{},[1362,94642,92617],{},[1362,94644,581],{},[1362,94646,94647],{},[1007,94648,94651],{"href":94649,"rel":94650},"https:\u002F\u002Fgithub.com\u002Fnoy-db\u002Fdocs\u002Fblob\u002Fmain\u002Fshowcases\u002Fsrc\u002Frecipe-search-rag.recipe.test.ts",[3371],[941,94652,94653],{},"recipe-search-rag.recipe.test.ts",[1344,94655,94656,94658,94660],{},[1362,94657,92633],{},[1362,94659,527],{},[1362,94661,94662],{},[1007,94663,94665],{"href":71569,"rel":94664},[3371],[941,94666,94667],{},"recipe-cloud-store.recipe.test.ts",[1022,94669,2621],{"id":2620},[1111,94671,94672,94681],{},[1114,94673,94674,94676,94677,94680],{},[1007,94675,80320],{"href":500}," — the end-to-end application recipes, each paired with a ",[941,94678,94679],{},"recipe-*"," walkthrough above.",[1114,94682,94683,94686],{},[1007,94684,94685],{"href":905},"Start here"," — the four-step golden path, if you haven't run anything yet.",[2645,94688,94689],{},"html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":1037,"searchDepth":1038,"depth":1038,"links":94691},[94692,94693,94697],{"id":92333,"depth":1041,"text":92334},{"id":30964,"depth":1041,"text":30965,"children":94694},[94695,94696],{"id":92432,"depth":1038,"text":92433},{"id":94425,"depth":1038,"text":94426},{"id":2620,"depth":1041,"text":2621},"Clone the docs repo and run the full showcase suite — every feature demonstrated as a vitest test that reads top-to-bottom as a tutorial.",{},{"icon":579},{"title":576,"description":94698},"sjBULy7fSeqfBfdIgDm-m88b3V0n4E7oR-qEoEMZnyg",{"id":94704,"title":581,"access":12,"apiToc":929,"body":94705,"category":1042,"description":95626,"extension":1044,"meta":95627,"navigation":1042,"nest":1042,"path":582,"seo":95628,"sinceVersion":1042,"status":1049,"stem":583,"subtitle":1042,"tier":1050,"tocExternal":1042,"__hash__":95629},"docs\u002Frecipes\u002Fsearch-rag.md",{"type":931,"value":94706,"toc":95618},[94707,94710,94725,94733,94740,94742,94795,94797,95026,95033,95138,95142,95286,95297,95301,95306,95580,95586,95588,95591,95593,95615],[934,94708,581],{"id":94709},"search-rag",[1061,94711,94712],{},[937,94713,94714,94716,94717,58223,94719,94721,94722,94724],{},[950,94715,1500],{}," apps that need \"find it by words or by meaning\" over encrypted records — and, optionally, an LLM answering questions over them.\n",[950,94718,58222],{},[941,94720,22027],{}," (one factory, all three retrieval modes) + ",[941,94723,36762],{}," for the LLM loop.",[937,94726,58245,94727,73771],{},[1007,94728,94730],{"href":94649,"rel":94729},[3371],[941,94731,94732],{},"showcases\u002Fsrc\u002Frecipe-search-rag.recipe.test.ts",[937,94734,94735,94736,94739],{},"Everything on this page happens ",[950,94737,94738],{},"client-side, after decryption",": the index, the vectors, the ranking, and the tool dispatch all run inside the hub. The store holds only ciphertext throughout — search adds no leakage beyond reading the collection.",[1022,94741,70093],{"id":70092},[1111,94743,94744,94757,94767,94786],{},[1114,94745,94746,94751,94752,1261,94754,94756],{},[950,94747,94748],{},[941,94749,94750],{},"searchStrategy: withSearch()"," — one opt-in enables lexical, semantic, and hybrid (see ",[1007,94753,4457],{"href":833},[1007,94755,673],{"href":674},", two pages, one subsystem).",[1114,94758,94759,94764,94765,1520],{},[950,94760,3289,94761,94763],{},[941,94762,22775],{}," allowlist on the collection"," for indexed lexical ",[941,94766,22035],{},[1114,94768,94769,94774,94775,94778,94779,94781,94782,94785],{},[950,94770,5093,94771,94773],{},[941,94772,4452],{}," encoder on the collection"," for semantic\u002Fhybrid — a ",[941,94776,94777],{},"{ dim, model, source, encode }"," object ",[1092,94780,13501],{}," supply. An ",[950,94783,94784],{},"on-device"," model keeps zero-knowledge end-to-end; a hosted embeddings API adds that provider as a party that can read your text — choose deliberately.",[1114,94787,94788,94792,94793,1520],{},[950,94789,94790],{},[941,94791,36762],{}," for the RAG half: ",[941,94794,36765],{},[1022,94796,58394],{"id":58393},[2217,94798,94800],{"className":2219,"code":94799,"language":2221,"meta":1037,"style":1037},"import { createNoydb, withSearch } from '@noy-db\u002Fhub'\nimport { toBrowserIdb } from '@noy-db\u002Fto-browser-idb'\n\nconst db = await createNoydb({\n  store: toBrowserIdb(),\n  user: 'analyst',\n  secret: 'search-rag-pass-2026',\n  searchStrategy: withSearch(),\n})\nconst vault = await db.openVault('firm')\n\nconst notes = vault.collection\u003CNote>('notes', {\n  textIndexes: ['text'],   \u002F\u002F lexical retrieval reads this allowlist\n  embeddings: encoder,     \u002F\u002F your text→vector function (dim\u002Fmodel must match it)\n})\n",[941,94801,94802,94824,94842,94846,94862,94874,94889,94904,94916,94922,94948,94952,94984,95006,95020],{"__ignoreMap":1037},[2225,94803,94804,94806,94808,94810,94812,94814,94816,94818,94820,94822],{"class":2227,"line":2228},[2225,94805,2232],{"class":2231},[2225,94807,2236],{"class":2235},[2225,94809,2744],{"class":2239},[2225,94811,2298],{"class":2235},[2225,94813,73949],{"class":2239},[2225,94815,2243],{"class":2235},[2225,94817,2246],{"class":2231},[2225,94819,2249],{"class":2235},[2225,94821,1086],{"class":2252},[2225,94823,2256],{"class":2235},[2225,94825,94826,94828,94830,94832,94834,94836,94838,94840],{"class":2227,"line":1041},[2225,94827,2232],{"class":2231},[2225,94829,2236],{"class":2235},[2225,94831,34763],{"class":2239},[2225,94833,2243],{"class":2235},[2225,94835,2246],{"class":2231},[2225,94837,2249],{"class":2235},[2225,94839,9573],{"class":2252},[2225,94841,2256],{"class":2235},[2225,94843,94844],{"class":2227,"line":1038},[2225,94845,2285],{"emptyLinePlaceholder":2284},[2225,94847,94848,94850,94852,94854,94856,94858,94860],{"class":2227,"line":2288},[2225,94849,2775],{"class":2774},[2225,94851,2778],{"class":2239},[2225,94853,2781],{"class":2235},[2225,94855,2784],{"class":2231},[2225,94857,2744],{"class":2291},[2225,94859,2789],{"class":2239},[2225,94861,4200],{"class":2235},[2225,94863,94864,94866,94868,94870,94872],{"class":2227,"line":2304},[2225,94865,15608],{"class":2307},[2225,94867,2311],{"class":2235},[2225,94869,34763],{"class":2291},[2225,94871,4228],{"class":2239},[2225,94873,1707],{"class":2235},[2225,94875,94876,94878,94880,94882,94885,94887],{"class":2227,"line":2356},[2225,94877,4205],{"class":2307},[2225,94879,2311],{"class":2235},[2225,94881,2249],{"class":2235},[2225,94883,94884],{"class":2252},"analyst",[2225,94886,2805],{"class":2235},[2225,94888,1707],{"class":2235},[2225,94890,94891,94893,94895,94897,94900,94902],{"class":2227,"line":2830},[2225,94892,10423],{"class":2307},[2225,94894,2311],{"class":2235},[2225,94896,2249],{"class":2235},[2225,94898,94899],{"class":2252},"search-rag-pass-2026",[2225,94901,2805],{"class":2235},[2225,94903,1707],{"class":2235},[2225,94905,94906,94908,94910,94912,94914],{"class":2227,"line":2861},[2225,94907,74331],{"class":2307},[2225,94909,2311],{"class":2235},[2225,94911,73949],{"class":2291},[2225,94913,4228],{"class":2239},[2225,94915,1707],{"class":2235},[2225,94917,94918,94920],{"class":2227,"line":4246},[2225,94919,2359],{"class":2235},[2225,94921,2362],{"class":2239},[2225,94923,94924,94926,94928,94930,94932,94934,94936,94938,94940,94942,94944,94946],{"class":2227,"line":4253},[2225,94925,2775],{"class":2774},[2225,94927,2835],{"class":2239},[2225,94929,2781],{"class":2235},[2225,94931,2784],{"class":2231},[2225,94933,2842],{"class":2239},[2225,94935,1520],{"class":2235},[2225,94937,2847],{"class":2291},[2225,94939,2789],{"class":2239},[2225,94941,2805],{"class":2235},[2225,94943,39709],{"class":2252},[2225,94945,2805],{"class":2235},[2225,94947,2362],{"class":2239},[2225,94949,94950],{"class":2227,"line":4259},[2225,94951,2285],{"emptyLinePlaceholder":2284},[2225,94953,94954,94956,94958,94960,94962,94964,94966,94968,94970,94972,94974,94976,94978,94980,94982],{"class":2227,"line":6548},[2225,94955,2775],{"class":2774},[2225,94957,42762],{"class":2239},[2225,94959,2781],{"class":2235},[2225,94961,6466],{"class":2239},[2225,94963,1520],{"class":2235},[2225,94965,6471],{"class":2291},[2225,94967,6474],{"class":2235},[2225,94969,86814],{"class":6342},[2225,94971,6480],{"class":2235},[2225,94973,2789],{"class":2239},[2225,94975,2805],{"class":2235},[2225,94977,2854],{"class":2252},[2225,94979,2805],{"class":2235},[2225,94981,2298],{"class":2235},[2225,94983,2301],{"class":2235},[2225,94985,94986,94989,94991,94993,94995,94997,94999,95001,95003],{"class":2227,"line":6576},[2225,94987,94988],{"class":2307},"  textIndexes",[2225,94990,2311],{"class":2235},[2225,94992,8667],{"class":2239},[2225,94994,2805],{"class":2235},[2225,94996,5672],{"class":2252},[2225,94998,2805],{"class":2235},[2225,95000,8803],{"class":2239},[2225,95002,2298],{"class":2235},[2225,95004,95005],{"class":2763},"   \u002F\u002F lexical retrieval reads this allowlist\n",[2225,95007,95008,95011,95013,95015,95017],{"class":2227,"line":6582},[2225,95009,95010],{"class":2307},"  embeddings",[2225,95012,2311],{"class":2235},[2225,95014,73899],{"class":2239},[2225,95016,2298],{"class":2235},[2225,95018,95019],{"class":2763},"     \u002F\u002F your text→vector function (dim\u002Fmodel must match it)\n",[2225,95021,95022,95024],{"class":2227,"line":6587},[2225,95023,2359],{"class":2235},[2225,95025,2362],{"class":2239},[937,95027,95028,95029,95032],{},"The walkthrough's ",[941,95030,95031],{},"encoder"," is a deterministic stub (a bag-of-chars hash) so the paired test is reproducible — in an app you plug a real model; the shape is just:",[2217,95034,95036],{"className":2219,"code":95035,"language":2221,"meta":1037,"style":1037},"const encoder = {\n  dim: 384, model: 'your-model-name', source: 'text' as const,\n  encode: async (text: string): Promise\u003CFloat32Array> => yourModel.embed(text),\n}\n",[941,95037,95038,95049,95092,95134],{"__ignoreMap":1037},[2225,95039,95040,95042,95045,95047],{"class":2227,"line":2228},[2225,95041,2775],{"class":2774},[2225,95043,95044],{"class":2239}," encoder ",[2225,95046,2781],{"class":2235},[2225,95048,2301],{"class":2235},[2225,95050,95051,95054,95056,95059,95061,95064,95066,95068,95071,95073,95075,95078,95080,95082,95084,95086,95088,95090],{"class":2227,"line":1041},[2225,95052,95053],{"class":2307},"  dim",[2225,95055,2311],{"class":2235},[2225,95057,95058],{"class":6527}," 384",[2225,95060,2298],{"class":2235},[2225,95062,95063],{"class":2307}," model",[2225,95065,2311],{"class":2235},[2225,95067,2249],{"class":2235},[2225,95069,95070],{"class":2252},"your-model-name",[2225,95072,2805],{"class":2235},[2225,95074,2298],{"class":2235},[2225,95076,95077],{"class":2307}," source",[2225,95079,2311],{"class":2235},[2225,95081,2249],{"class":2235},[2225,95083,5672],{"class":2252},[2225,95085,2805],{"class":2235},[2225,95087,2266],{"class":2231},[2225,95089,8083],{"class":2774},[2225,95091,1707],{"class":2235},[2225,95093,95094,95097,95099,95101,95103,95105,95107,95109,95111,95113,95115,95117,95119,95121,95124,95126,95129,95132],{"class":2227,"line":1038},[2225,95095,95096],{"class":2291},"  encode",[2225,95098,2311],{"class":2235},[2225,95100,38717],{"class":2774},[2225,95102,953],{"class":2235},[2225,95104,5672],{"class":7052},[2225,95106,2311],{"class":2235},[2225,95108,6366],{"class":6342},[2225,95110,6255],{"class":2235},[2225,95112,7079],{"class":6342},[2225,95114,6474],{"class":2235},[2225,95116,75686],{"class":6342},[2225,95118,6480],{"class":2235},[2225,95120,8116],{"class":2774},[2225,95122,95123],{"class":2239}," yourModel",[2225,95125,1520],{"class":2235},[2225,95127,95128],{"class":2291},"embed",[2225,95130,95131],{"class":2239},"(text)",[2225,95133,1707],{"class":2235},[2225,95135,95136],{"class":2227,"line":2288},[2225,95137,7351],{"class":2235},[1022,95139,95141],{"id":95140},"three-retrieval-modes-one-call","Three retrieval modes, one call",[2217,95143,95145],{"className":2219,"code":95144,"language":2221,"meta":1037,"style":1037},"await notes.retrieve('invoice payment', { mode: 'lexical' })\n\u002F\u002F BM25 over the textIndexes fields — matching *words*, ranked, with snippets\n\nawait notes.retrieve('invoice payment', { mode: 'semantic' })\n\u002F\u002F cosine similarity over the encrypted _vec side-cars — matching *meaning*\n\nawait notes.retrieve('invoice payment', { mode: 'hybrid' })\n\u002F\u002F both rankings fused (RRF): a record that appears in both lists rises\n\u002F\u002F above each modality's own leader — usually what an app wants by default\n",[941,95146,95147,95185,95190,95194,95230,95235,95239,95276,95281],{"__ignoreMap":1037},[2225,95148,95149,95151,95153,95155,95157,95159,95161,95164,95166,95168,95170,95172,95174,95176,95179,95181,95183],{"class":2227,"line":2228},[2225,95150,6500],{"class":2231},[2225,95152,42817],{"class":2239},[2225,95154,1520],{"class":2235},[2225,95156,75590],{"class":2291},[2225,95158,2789],{"class":2239},[2225,95160,2805],{"class":2235},[2225,95162,95163],{"class":2252},"invoice payment",[2225,95165,2805],{"class":2235},[2225,95167,2298],{"class":2235},[2225,95169,2236],{"class":2235},[2225,95171,32303],{"class":2307},[2225,95173,2311],{"class":2235},[2225,95175,2249],{"class":2235},[2225,95177,95178],{"class":2252},"lexical",[2225,95180,2805],{"class":2235},[2225,95182,2243],{"class":2235},[2225,95184,2362],{"class":2239},[2225,95186,95187],{"class":2227,"line":1041},[2225,95188,95189],{"class":2763},"\u002F\u002F BM25 over the textIndexes fields — matching *words*, ranked, with snippets\n",[2225,95191,95192],{"class":2227,"line":1038},[2225,95193,2285],{"emptyLinePlaceholder":2284},[2225,95195,95196,95198,95200,95202,95204,95206,95208,95210,95212,95214,95216,95218,95220,95222,95224,95226,95228],{"class":2227,"line":2288},[2225,95197,6500],{"class":2231},[2225,95199,42817],{"class":2239},[2225,95201,1520],{"class":2235},[2225,95203,75590],{"class":2291},[2225,95205,2789],{"class":2239},[2225,95207,2805],{"class":2235},[2225,95209,95163],{"class":2252},[2225,95211,2805],{"class":2235},[2225,95213,2298],{"class":2235},[2225,95215,2236],{"class":2235},[2225,95217,32303],{"class":2307},[2225,95219,2311],{"class":2235},[2225,95221,2249],{"class":2235},[2225,95223,75612],{"class":2252},[2225,95225,2805],{"class":2235},[2225,95227,2243],{"class":2235},[2225,95229,2362],{"class":2239},[2225,95231,95232],{"class":2227,"line":2304},[2225,95233,95234],{"class":2763},"\u002F\u002F cosine similarity over the encrypted _vec side-cars — matching *meaning*\n",[2225,95236,95237],{"class":2227,"line":2356},[2225,95238,2285],{"emptyLinePlaceholder":2284},[2225,95240,95241,95243,95245,95247,95249,95251,95253,95255,95257,95259,95261,95263,95265,95267,95270,95272,95274],{"class":2227,"line":2830},[2225,95242,6500],{"class":2231},[2225,95244,42817],{"class":2239},[2225,95246,1520],{"class":2235},[2225,95248,75590],{"class":2291},[2225,95250,2789],{"class":2239},[2225,95252,2805],{"class":2235},[2225,95254,95163],{"class":2252},[2225,95256,2805],{"class":2235},[2225,95258,2298],{"class":2235},[2225,95260,2236],{"class":2235},[2225,95262,32303],{"class":2307},[2225,95264,2311],{"class":2235},[2225,95266,2249],{"class":2235},[2225,95268,95269],{"class":2252},"hybrid",[2225,95271,2805],{"class":2235},[2225,95273,2243],{"class":2235},[2225,95275,2362],{"class":2239},[2225,95277,95278],{"class":2227,"line":2861},[2225,95279,95280],{"class":2763},"\u002F\u002F both rankings fused (RRF): a record that appears in both lists rises\n",[2225,95282,95283],{"class":2227,"line":4246},[2225,95284,95285],{"class":2763},"\u002F\u002F above each modality's own leader — usually what an app wants by default\n",[937,95287,95288,95289,95292,95293,95296],{},"Zero-setup ",[941,95290,95291],{},"notes.search(field, query)"," also exists for ad-hoc scans with no index at all; the ",[1007,95294,95295],{"href":833},"search page"," compares the paths.",[1022,95298,95300],{"id":95299},"the-rag-half-permission-scoped-tools-for-an-llm","The RAG half — permission-scoped tools for an LLM",[937,95302,95303,95305],{},[941,95304,36762],{}," turns the vault into function-calling tools — one per (collection × operation), in OpenAI \u002F Anthropic \u002F Vercel AI \u002F JSON-Schema shapes — and dispatches the model's calls back. The LLM never touches keys; it sees only the decrypted records your host process chose to expose:",[2217,95307,95309],{"className":2219,"code":95308,"language":2221,"meta":1037,"style":1037},"import { buildToolset, invokeToolCall } from '@noy-db\u002Fin-ai'\n\n\u002F\u002F Hand the model exactly these capabilities — an allowlist, not a firehose:\nconst tools = buildToolset(vault, {\n  format: 'openai',\n  operations: ['list', 'get'],\n  collections: ['notes'],\n})\n\u002F\u002F → [{ function: { name: 'notes_list', … } }, { function: { name: 'notes_get', … } }]\n\n\u002F\u002F The model answers with a tool call; you dispatch it:\nconst result = await invokeToolCall(vault, { name: 'notes_get', args: { id: 'n-2' } })\n\n\u002F\u002F Out-of-allowlist calls are refused at dispatch too — defense in both directions:\nawait invokeToolCall(vault, { name: 'notes_delete', args: { id: 'n-2' } },\n  { allowedOperations: ['list', 'get'] }) \u002F\u002F → rejects\n",[941,95310,95311,95333,95337,95342,95358,95374,95401,95419,95425,95430,95434,95439,95493,95497,95502,95547],{"__ignoreMap":1037},[2225,95312,95313,95315,95317,95319,95321,95323,95325,95327,95329,95331],{"class":2227,"line":2228},[2225,95314,2232],{"class":2231},[2225,95316,2236],{"class":2235},[2225,95318,36836],{"class":2239},[2225,95320,2298],{"class":2235},[2225,95322,36841],{"class":2239},[2225,95324,2243],{"class":2235},[2225,95326,2246],{"class":2231},[2225,95328,2249],{"class":2235},[2225,95330,36762],{"class":2252},[2225,95332,2256],{"class":2235},[2225,95334,95335],{"class":2227,"line":1041},[2225,95336,2285],{"emptyLinePlaceholder":2284},[2225,95338,95339],{"class":2227,"line":1038},[2225,95340,95341],{"class":2763},"\u002F\u002F Hand the model exactly these capabilities — an allowlist, not a firehose:\n",[2225,95343,95344,95346,95348,95350,95352,95354,95356],{"class":2227,"line":2288},[2225,95345,2775],{"class":2774},[2225,95347,36862],{"class":2239},[2225,95349,2781],{"class":2235},[2225,95351,36836],{"class":2291},[2225,95353,30266],{"class":2239},[2225,95355,2298],{"class":2235},[2225,95357,2301],{"class":2235},[2225,95359,95360,95363,95365,95367,95370,95372],{"class":2227,"line":2304},[2225,95361,95362],{"class":2307},"  format",[2225,95364,2311],{"class":2235},[2225,95366,2249],{"class":2235},[2225,95368,95369],{"class":2252},"openai",[2225,95371,2805],{"class":2235},[2225,95373,1707],{"class":2235},[2225,95375,95376,95379,95381,95383,95385,95387,95389,95391,95393,95395,95397,95399],{"class":2227,"line":2356},[2225,95377,95378],{"class":2307},"  operations",[2225,95380,2311],{"class":2235},[2225,95382,8667],{"class":2239},[2225,95384,2805],{"class":2235},[2225,95386,3167],{"class":2252},[2225,95388,2805],{"class":2235},[2225,95390,2298],{"class":2235},[2225,95392,2249],{"class":2235},[2225,95394,3158],{"class":2252},[2225,95396,2805],{"class":2235},[2225,95398,8803],{"class":2239},[2225,95400,1707],{"class":2235},[2225,95402,95403,95405,95407,95409,95411,95413,95415,95417],{"class":2227,"line":2830},[2225,95404,32324],{"class":2307},[2225,95406,2311],{"class":2235},[2225,95408,8667],{"class":2239},[2225,95410,2805],{"class":2235},[2225,95412,2854],{"class":2252},[2225,95414,2805],{"class":2235},[2225,95416,8803],{"class":2239},[2225,95418,1707],{"class":2235},[2225,95420,95421,95423],{"class":2227,"line":2861},[2225,95422,2359],{"class":2235},[2225,95424,2362],{"class":2239},[2225,95426,95427],{"class":2227,"line":4246},[2225,95428,95429],{"class":2763},"\u002F\u002F → [{ function: { name: 'notes_list', … } }, { function: { name: 'notes_get', … } }]\n",[2225,95431,95432],{"class":2227,"line":4253},[2225,95433,2285],{"emptyLinePlaceholder":2284},[2225,95435,95436],{"class":2227,"line":4259},[2225,95437,95438],{"class":2763},"\u002F\u002F The model answers with a tool call; you dispatch it:\n",[2225,95440,95441,95443,95445,95447,95449,95451,95453,95455,95457,95459,95461,95463,95466,95468,95470,95472,95474,95476,95478,95480,95482,95485,95487,95489,95491],{"class":2227,"line":6548},[2225,95442,2775],{"class":2774},[2225,95444,36920],{"class":2239},[2225,95446,2781],{"class":2235},[2225,95448,2784],{"class":2231},[2225,95450,36841],{"class":2291},[2225,95452,30266],{"class":2239},[2225,95454,2298],{"class":2235},[2225,95456,2236],{"class":2235},[2225,95458,8104],{"class":2307},[2225,95460,2311],{"class":2235},[2225,95462,2249],{"class":2235},[2225,95464,95465],{"class":2252},"notes_get",[2225,95467,2805],{"class":2235},[2225,95469,2298],{"class":2235},[2225,95471,36954],{"class":2307},[2225,95473,2311],{"class":2235},[2225,95475,2236],{"class":2235},[2225,95477,7070],{"class":2307},[2225,95479,2311],{"class":2235},[2225,95481,2249],{"class":2235},[2225,95483,95484],{"class":2252},"n-2",[2225,95486,2805],{"class":2235},[2225,95488,2243],{"class":2235},[2225,95490,2243],{"class":2235},[2225,95492,2362],{"class":2239},[2225,95494,95495],{"class":2227,"line":6576},[2225,95496,2285],{"emptyLinePlaceholder":2284},[2225,95498,95499],{"class":2227,"line":6582},[2225,95500,95501],{"class":2763},"\u002F\u002F Out-of-allowlist calls are refused at dispatch too — defense in both directions:\n",[2225,95503,95504,95506,95508,95510,95512,95514,95516,95518,95520,95523,95525,95527,95529,95531,95533,95535,95537,95539,95541,95543,95545],{"class":2227,"line":6587},[2225,95505,6500],{"class":2231},[2225,95507,36841],{"class":2291},[2225,95509,30266],{"class":2239},[2225,95511,2298],{"class":2235},[2225,95513,2236],{"class":2235},[2225,95515,8104],{"class":2307},[2225,95517,2311],{"class":2235},[2225,95519,2249],{"class":2235},[2225,95521,95522],{"class":2252},"notes_delete",[2225,95524,2805],{"class":2235},[2225,95526,2298],{"class":2235},[2225,95528,36954],{"class":2307},[2225,95530,2311],{"class":2235},[2225,95532,2236],{"class":2235},[2225,95534,7070],{"class":2307},[2225,95536,2311],{"class":2235},[2225,95538,2249],{"class":2235},[2225,95540,95484],{"class":2252},[2225,95542,2805],{"class":2235},[2225,95544,2243],{"class":2235},[2225,95546,2353],{"class":2235},[2225,95548,95549,95551,95553,95555,95557,95559,95561,95563,95565,95567,95569,95571,95573,95575,95577],{"class":2227,"line":7174},[2225,95550,26354],{"class":2235},[2225,95552,36967],{"class":2307},[2225,95554,2311],{"class":2235},[2225,95556,8667],{"class":2239},[2225,95558,2805],{"class":2235},[2225,95560,3167],{"class":2252},[2225,95562,2805],{"class":2235},[2225,95564,2298],{"class":2235},[2225,95566,2249],{"class":2235},[2225,95568,3158],{"class":2252},[2225,95570,2805],{"class":2235},[2225,95572,8890],{"class":2239},[2225,95574,2359],{"class":2235},[2225,95576,8495],{"class":2239},[2225,95578,95579],{"class":2763},"\u002F\u002F → rejects\n",[937,95581,95582,95583,95585],{},"The retrieval modes above are the natural retriever for this loop: ",[941,95584,22035],{}," picks the passages, the toolset fetches the records, your prompt assembles the context.",[1022,95587,71414],{"id":18051},[937,95589,95590],{},"Run the paired walkthrough: all three modes return real ranked ids (the fusion never invents rows), the toolset emits exactly the allowlisted tools, dispatch round-trips a record, and an out-of-allowlist operation is refused.",[1022,95592,2621],{"id":2620},[1111,95594,95595,95605,95610],{},[1114,95596,95597,1368,95599,95601,95602,95604],{},[1007,95598,4457],{"href":833},[1007,95600,673],{"href":674}," — the two facets of the one ",[941,95603,22027],{}," subsystem, in depth.",[1114,95606,95607,95609],{},[1007,95608,35645],{"href":224}," — the adapter's full surface.",[1114,95611,95612,95614],{},[1007,95613,463],{"href":464}," — where a hosted encoder or LLM sits relative to the trust boundary.",[2645,95616,95617],{},"html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}html pre.shiki code .sHdIc, html code.shiki .sHdIc{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#EEFFFF;--shiki-default-font-style:italic;--shiki-dark:#BABED8;--shiki-dark-font-style:italic}",{"title":1037,"searchDepth":1038,"depth":1038,"links":95619},[95620,95621,95622,95623,95624,95625],{"id":70092,"depth":1041,"text":70093},{"id":58393,"depth":1041,"text":58394},{"id":95140,"depth":1041,"text":95141},{"id":95299,"depth":1041,"text":95300},{"id":18051,"depth":1041,"text":71414},{"id":2620,"depth":1041,"text":2621},"Retrieval over an encrypted collection — lexical, semantic, and hybrid — then the in-ai toolset handing permission-scoped tools to an LLM loop.",{},{"title":581,"description":95626},"EIIrwnsOwiNfm65vOb7ByjuJ1ch6bQfspYnFwE-bkMc",{"id":95631,"title":585,"access":12,"apiToc":929,"body":95632,"category":1042,"description":96442,"extension":1044,"meta":96443,"navigation":1042,"nest":1042,"path":586,"seo":96444,"sinceVersion":1042,"status":1049,"stem":587,"subtitle":1042,"tier":1050,"tocExternal":1042,"__hash__":96445},"docs\u002Frecipes\u002Fteam-vault-topology.md",{"type":931,"value":95633,"toc":96430},[95634,95637,95654,95669,95672,95674,95681,95683,95686,95690,95696,95915,95921,95986,96002,96017,96030,96034,96042,96056,96095,96115,96129,96133,96144,96159,96162,96166,96172,96198,96205,96209,96215,96224,96273,96279,96281,96296,96300,96381,96391,96393,96428],[934,95635,585],{"id":95636},"team-vault-topology",[1061,95638,95639],{},[937,95640,95641,95643,95644,38633,95647,58223,95649,95651,95652,1520],{},[950,95642,1500],{}," you are building two apps — an admin console and a member app — against one shared team\nvault, and you need to know which composition is supported rather than inferring it from ",[941,95645,95646],{},".d.ts",[950,95648,58222],{},[941,95650,4676],{}," + one local store per instance + at least one ",[941,95653,34695],{},[937,95655,95656,95659,95660,95662,95663,95668],{},[950,95657,95658],{},"One team vault, two apps, three stores."," The live copy sits on the LAN, the durable copy is a cloud\n",[941,95661,34951],{},", and members hold offline replicas. Written against ",[950,95664,95665],{},[941,95666,95667],{},"0.9.0",": every symbol below is on\nthe published surface at that version.",[937,95670,95671],{},"There is no \"topology\" API, and that is the point — a topology is a local store, a list of sync targets,\nand a set of properties you can rely on. What is worth writing down is the properties.",[1022,95673,70093],{"id":70092},[937,95675,95676,95677,95680],{},"None beyond the Bundle line, with one obligation: every instance needs its ",[950,95678,95679],{},"own"," local store. A\nshared local store is not a supported topology.",[1022,95682,58276],{"id":58275},[937,95684,95685],{},"An admin console and a member app that converge on one vault, survive going offline, can be restored\nfrom a pod under a live target, and whose UI can state honestly what has and has not been validated.",[1022,95687,95689],{"id":95688},"setup-composition","Setup — composition",[937,95691,95692,95695],{},[950,95693,95694],{},"One local store per instance, and a list of targets."," Each app instance opens its own local store —\nnever a shared one — and declares where else the data lives:",[2217,95697,95699],{"className":2219,"code":95698,"language":2221,"meta":1037,"style":1037},"import { createNoydb } from '@noy-db\u002Fhub'\nimport { withSync } from '@noy-db\u002Fhub\u002Fsync'\nimport { toBrowserIdb } from '@noy-db\u002Fto-browser-idb'\nimport { toAwsDynamo } from '@noy-db\u002Fto-aws-dynamo'\n\nconst db = createNoydb({\n  store: toBrowserIdb({ name: 'team-vault' }),     \u002F\u002F the LOCAL copy — one per instance\n  syncStrategy: withSync(),                        \u002F\u002F ⛔ required; without it there is no sync at all\n  targets: [\n    { store: toAwsDynamo({ table: 'team' }), role: 'sync-peer', label: 'cloud' },\n  ],\n})\n",[941,95700,95701,95719,95737,95755,95774,95778,95792,95824,95839,95848,95903,95909],{"__ignoreMap":1037},[2225,95702,95703,95705,95707,95709,95711,95713,95715,95717],{"class":2227,"line":2228},[2225,95704,2232],{"class":2231},[2225,95706,2236],{"class":2235},[2225,95708,2744],{"class":2239},[2225,95710,2243],{"class":2235},[2225,95712,2246],{"class":2231},[2225,95714,2249],{"class":2235},[2225,95716,1086],{"class":2252},[2225,95718,2256],{"class":2235},[2225,95720,95721,95723,95725,95727,95729,95731,95733,95735],{"class":2227,"line":1041},[2225,95722,2232],{"class":2231},[2225,95724,2236],{"class":2235},[2225,95726,89579],{"class":2239},[2225,95728,2243],{"class":2235},[2225,95730,2246],{"class":2231},[2225,95732,2249],{"class":2235},[2225,95734,4672],{"class":2252},[2225,95736,2256],{"class":2235},[2225,95738,95739,95741,95743,95745,95747,95749,95751,95753],{"class":2227,"line":1038},[2225,95740,2232],{"class":2231},[2225,95742,2236],{"class":2235},[2225,95744,34763],{"class":2239},[2225,95746,2243],{"class":2235},[2225,95748,2246],{"class":2231},[2225,95750,2249],{"class":2235},[2225,95752,9573],{"class":2252},[2225,95754,2256],{"class":2235},[2225,95756,95757,95759,95761,95764,95766,95768,95770,95772],{"class":2227,"line":2288},[2225,95758,2232],{"class":2231},[2225,95760,2236],{"class":2235},[2225,95762,95763],{"class":2239}," toAwsDynamo",[2225,95765,2243],{"class":2235},[2225,95767,2246],{"class":2231},[2225,95769,2249],{"class":2235},[2225,95771,46865],{"class":2252},[2225,95773,2256],{"class":2235},[2225,95775,95776],{"class":2227,"line":2304},[2225,95777,2285],{"emptyLinePlaceholder":2284},[2225,95779,95780,95782,95784,95786,95788,95790],{"class":2227,"line":2356},[2225,95781,2775],{"class":2774},[2225,95783,2778],{"class":2239},[2225,95785,2781],{"class":2235},[2225,95787,2744],{"class":2291},[2225,95789,2789],{"class":2239},[2225,95791,4200],{"class":2235},[2225,95793,95794,95796,95798,95800,95802,95804,95806,95808,95810,95813,95815,95817,95819,95821],{"class":2227,"line":2830},[2225,95795,15608],{"class":2307},[2225,95797,2311],{"class":2235},[2225,95799,34763],{"class":2291},[2225,95801,2789],{"class":2239},[2225,95803,2792],{"class":2235},[2225,95805,8104],{"class":2307},[2225,95807,2311],{"class":2235},[2225,95809,2249],{"class":2235},[2225,95811,95812],{"class":2252},"team-vault",[2225,95814,2805],{"class":2235},[2225,95816,2243],{"class":2235},[2225,95818,1381],{"class":2239},[2225,95820,2298],{"class":2235},[2225,95822,95823],{"class":2763},"     \u002F\u002F the LOCAL copy — one per instance\n",[2225,95825,95826,95828,95830,95832,95834,95836],{"class":2227,"line":2861},[2225,95827,89901],{"class":2307},[2225,95829,2311],{"class":2235},[2225,95831,89579],{"class":2291},[2225,95833,4228],{"class":2239},[2225,95835,2298],{"class":2235},[2225,95837,95838],{"class":2763},"                        \u002F\u002F ⛔ required; without it there is no sync at all\n",[2225,95840,95841,95844,95846],{"class":2227,"line":4246},[2225,95842,95843],{"class":2307},"  targets",[2225,95845,2311],{"class":2235},[2225,95847,32491],{"class":2239},[2225,95849,95850,95852,95854,95856,95858,95860,95862,95864,95866,95868,95870,95872,95874,95876,95878,95880,95882,95884,95886,95888,95890,95893,95895,95897,95899,95901],{"class":2227,"line":4253},[2225,95851,32496],{"class":2235},[2225,95853,7564],{"class":2307},[2225,95855,2311],{"class":2235},[2225,95857,95763],{"class":2291},[2225,95859,2789],{"class":2239},[2225,95861,2792],{"class":2235},[2225,95863,47030],{"class":2307},[2225,95865,2311],{"class":2235},[2225,95867,2249],{"class":2235},[2225,95869,868],{"class":2252},[2225,95871,2805],{"class":2235},[2225,95873,2243],{"class":2235},[2225,95875,1381],{"class":2239},[2225,95877,2298],{"class":2235},[2225,95879,34944],{"class":2307},[2225,95881,2311],{"class":2235},[2225,95883,2249],{"class":2235},[2225,95885,34951],{"class":2252},[2225,95887,2805],{"class":2235},[2225,95889,2298],{"class":2235},[2225,95891,95892],{"class":2307}," label",[2225,95894,2311],{"class":2235},[2225,95896,2249],{"class":2235},[2225,95898,31099],{"class":2252},[2225,95900,2805],{"class":2235},[2225,95902,2353],{"class":2235},[2225,95904,95905,95907],{"class":2227,"line":4259},[2225,95906,32588],{"class":2239},[2225,95908,1707],{"class":2235},[2225,95910,95911,95913],{"class":2227,"line":6548},[2225,95912,2359],{"class":2235},[2225,95914,2362],{"class":2239},[937,95916,95917,95920],{},[941,95918,95919],{},"SyncTarget.role"," is one of three values, and the role — not the store — decides direction:",[1338,95922,95923,95939],{},[1341,95924,95925],{},[1344,95926,95927,95931,95933,95936],{},[1347,95928,95929],{},[941,95930,16785],{},[1347,95932,31203],{},[1347,95934,95935],{},"Conflict resolution",[1347,95937,95938],{},"Typical use",[1357,95940,95941,95957,95972],{},[1344,95942,95943,95947,95950,95954],{},[1362,95944,95945],{},[941,95946,34951],{},[1362,95948,95949],{},"bidirectional",[1362,95951,95952],{},[941,95953,20529],{},[1362,95955,95956],{},"the cloud live copy",[1344,95958,95959,95963,95966,95969],{},[1362,95960,95961],{},[941,95962,50702],{},[1362,95964,95965],{},"push-only",[1362,95967,95968],{},"n\u002Fa — receives merged",[1362,95970,95971],{},"an S3 or Drive dump",[1344,95973,95974,95978,95981,95983],{},[1362,95975,95976],{},[941,95977,605],{},[1362,95979,95980],{},"push-only, cold",[1362,95982,37998],{},[1362,95984,95985],{},"long-term retention",[937,95987,1649,95988,95994,95995,95998,95999,96001],{},[950,95989,95990,95991,95993],{},"A target with no declared ",[941,95992,796],{}," syncs on explicit calls only."," Automation starts from a\n",[1092,95996,95997],{},"declared"," policy — per target, or instance-level ",[941,96000,20562],{},". A target that appears configured and\nnever syncs on its own is almost always this.",[937,96003,46418,96004,96009,96010,96012,96013,96016],{},[950,96005,96006,96008],{},[941,96007,16057],{}," is a sync TARGET, not a local store."," Using a session-share transport as the local\nstore gives an instance with no durable copy of its own: it works while a peer is connected and loses\nthe vault when that ends. The LAN live store is a real store (",[941,96011,7461],{},", a server-backed adapter); the\npeer transport carries ",[1092,96014,96015],{},"sessions",", not custody.",[937,96018,96019,96022,96023,96025,96026,96029],{},[950,96020,96021],{},"Adopt from day one:"," one local store per instance, ",[941,96024,4676],{},", a declared policy per target, and\n",[941,96027,96028],{},"label"," on every target — it is what DevTools and audit logs print, and an unlabelled target is\nindistinguishable from another in the same list.",[1022,96031,96033],{"id":96032},"restore-under-a-live-target","Restore under a live target",[937,96035,96036,96037,1261,96039,96041],{},"A pod is the portable artefact: ",[941,96038,35759],{},[941,96040,70321],{}," on the root barrel, both available in the\nbrowser.",[937,96043,46418,96044,96049,96050,96052,96053,96055],{},[950,96045,36095,96046,96048],{},[941,96047,95667],{},", a restore was local-only and the next pull reverted it"," — the target still held\nthe newer ",[941,96051,1700],{}," and won. That is fixed, and the fix is a ",[1092,96054,17574],{},", not an import:",[2217,96057,96059],{"className":2219,"code":96058,"language":2221,"meta":1037,"style":1037},"await db.replaceRemote(vault)   \u002F\u002F make the target match this local copy, deliberately\nawait db.realign(vault)         \u002F\u002F repair a corrupted LOCAL envelope from the target's healthy copy\n",[941,96060,96061,96078],{"__ignoreMap":1037},[2225,96062,96063,96065,96067,96069,96072,96075],{"class":2227,"line":2228},[2225,96064,6500],{"class":2231},[2225,96066,2842],{"class":2239},[2225,96068,1520],{"class":2235},[2225,96070,96071],{"class":2291},"replaceRemote",[2225,96073,96074],{"class":2239},"(vault)   ",[2225,96076,96077],{"class":2763},"\u002F\u002F make the target match this local copy, deliberately\n",[2225,96079,96080,96082,96084,96086,96089,96092],{"class":2227,"line":1041},[2225,96081,6500],{"class":2231},[2225,96083,2842],{"class":2239},[2225,96085,1520],{"class":2235},[2225,96087,96088],{"class":2291},"realign",[2225,96090,96091],{"class":2239},"(vault)         ",[2225,96093,96094],{"class":2763},"\u002F\u002F repair a corrupted LOCAL envelope from the target's healthy copy\n",[937,96096,96097,96098,96100,96101,5502,96104,96100,96106,96109,96110,96112,96113,1520],{},"The two point in opposite directions and are not interchangeable. ",[941,96099,96071],{}," asserts ",[1092,96102,96103],{},"this copy is\nthe truth",[941,96105,96088],{},[1092,96107,96108],{},"the target's copy is",". ⚠️ An ordinary ",[941,96111,56862],{}," does neither — it cannot\nrepair a corrupted local record, because the corrupt row still wins on ",[941,96114,1700],{},[937,96116,96117,96120,96121,968,96123,2514,96125,96128],{},[950,96118,96119],{},"What travels in a pod and what does not:"," records and the keyring travel; ",[941,96122,6715],{},[941,96124,6724],{},[941,96126,96127],{},"_sync"," do not. So a restored vault is a working vault without its audit trail, and a UI that offers\n\"restore\" should say which of the two the user is getting.",[1022,96130,96132],{"id":96131},"member-credentials-for-a-cloud-store","Member credentials for a cloud store",[937,96134,48707,96135,953,96140,96143],{},[950,96136,96137,96138],{},"Every role can mint its own cloud-store credentials as of ",[941,96139,95667],{},[941,96141,96142],{},"@noy-db\u002Fhub\u002Fbroker","). Before\nthat, broker was admin-only twice over and members had to reach the cloud through an admin-served\nstore — that interim shape is no longer necessary.",[937,96145,96146,96148,96149,96151,96152,96155,96156,96158],{},[941,96147,10831],{}," mints a per-grantee credential and enrols the member with the host; ",[941,96150,82028],{}," de-registers\nit. ⚠️ ",[950,96153,96154],{},"Existing members need a re-grant"," (or magic-link recovery) to be enrolled — an already-granted\nmember from an earlier version is ",[1092,96157,1004],{}," retroactively enrolled, and the symptom is a member who can\nopen the vault locally and cannot reach the cloud store.",[937,96160,96161],{},"⛔ The admin path is byte-identical, so an admin console that works proves nothing about a member app.\nTest the member path on a member's credential.",[1022,96163,96165],{"id":96164},"same-user-handoff-between-two-apps","Same-user handoff between two apps",[937,96167,96168,96171],{},[950,96169,96170],{},"There is no primitive for this, by doctrine."," Two apps on one device are two clients; a vault does\nnot have a notion of \"the same human in another tab\".",[937,96173,96174,96175,96178,96179,96182,96183,96186,96187,96190,96191,96194,96195,96197],{},"The one-tap route is ",[950,96176,96177],{},"WebAuthn under one registrable domain"," — an admin console at ",[941,96180,96181],{},"admin.example.com","\nand a member app at ",[941,96184,96185],{},"app.example.com"," can share a credential scoped to ",[941,96188,96189],{},"example.com",". ⚠️ That makes the\n",[941,96192,96193],{},"rpId"," choice load-bearing and easy to get wrong: a credential registered under the wrong ",[941,96196,96193],{}," is not\nrecoverable by changing configuration later, because the authenticator bound it at registration.",[937,96199,96200,96201,96204],{},"Anything else — a link, a QR, a share-link — is an ",[1092,96202,96203],{},"onboarding"," flow, not a handoff, and should be\npresented to the user as such.",[1022,96206,96208],{"id":96207},"rules-vs-records-arriving-by-sync","Rules vs records arriving by sync",[937,96210,46418,96211,96214],{},[950,96212,96213],{},"Guards never run on pulled records."," A record that arrives by sync was validated by whoever wrote\nit, not by this instance. So a UI must not claim that everything it shows passed local validation.",[937,96216,30835,96217,96219,96220,96223],{},[941,96218,95667],{}," adds is that refusals are now ",[1092,96221,96222],{},"visible"," rather than silent:",[1111,96225,96226,96240,96251],{},[1114,96227,96228,96229,96232,96233,6023,96236,96239],{},"An arbiter ",[950,96230,96231],{},"replicates its admission refusals",", so the writer finds out at all. A refusal made on\nanother device arrives as ",[941,96234,96235],{},"origin: 'arbiter'",[950,96237,96238],{},"report-only"," — the local record is untouched.",[1114,96241,96242,96243,96246,96247,96250],{},"A dirty record ",[950,96244,96245],{},"re-runs this device's own gates before pushing",", so a record refused on the way out\n(",[941,96248,96249],{},"origin: 'push-recheck'",") stays local and dirty, and pushes itself once it passes again.",[1114,96252,96253,96254,96256,96257,96260,96261,96263,96264,96266,96267,55646,96270,1520],{},"A refusal carries ",[950,96255,31219],{}," timestamps: ",[941,96258,96259],{},"recordAt"," (when it was written) and ",[941,96262,4887],{}," (when it was\njudged). An arbiter judges at ",[1092,96265,91149],{}," time, so a record legitimately written while a period was open\ncan be refused later. With both, a UI can say ",[1092,96268,96269],{},"written then, refused now",[1092,96271,96272],{},"your push was\nwrong",[937,96274,96275,96278],{},[950,96276,96277],{},"So the honest claim is:"," \"validated when written on the device that wrote it, and admitted by the\narbiter as of the last pull\" — not \"valid\".",[1022,96280,71414],{"id":18051},[937,96282,96283,96284,96286,96287,96289,96290,96292,96293,96295],{},"The composition is right when: each instance has its own local store; every target carries a\n",[941,96285,96028],{}," and a declared ",[941,96288,796],{},"; a member (not an admin) credential reaches the cloud store; and a\nrestore followed by a ",[941,96291,56862],{}," does ",[950,96294,1004],{}," revert.",[1022,96297,96299],{"id":96298},"where-the-pieces-live","Where the pieces live",[1338,96301,96302,96310],{},[1341,96303,96304],{},[1344,96305,96306,96308],{},[1347,96307,29658],{},[1347,96309,9530],{},[1357,96311,96312,96325,96339,96348,96358,96372],{},[1344,96313,96314,96317],{},[1362,96315,96316],{},"LAN live store",[1362,96318,96319,96321,96322,96324],{},[941,96320,9551],{},", or a server-backed ",[941,96323,1779],{}," adapter",[1344,96326,96327,96332],{},[1362,96328,96329,96330],{},"Cloud ",[941,96331,34951],{},[1362,96333,96334,968,96336,96338],{},[941,96335,46865],{},[941,96337,54533],{},", …",[1344,96340,96341,96344],{},[1362,96342,96343],{},"Browser local copy",[1362,96345,96346],{},[941,96347,9573],{},[1344,96349,96350,96353],{},[1362,96351,96352],{},"Session share between peers",[1362,96354,96355,96357],{},[941,96356,34655],{}," — a target, never the local store",[1344,96359,96360,96363],{},[1362,96361,96362],{},"Relay for a thin client",[1362,96364,96365,96367,96368,11060,96370],{},[941,96366,40559],{}," — ⚠️ published from ",[941,96369,37439],{},[941,96371,9514],{},[1344,96373,96374,96377],{},[1362,96375,96376],{},"Member credentials",[1362,96378,96379],{},[941,96380,96142],{},[937,96382,1649,96383,96386,96387,96390],{},[950,96384,96385],{},"The daemon is below this layer, not beside it."," It opens and runs pods and binds the store\ncontract; it does not know what a fleet is. Fleet orchestration is ",[941,96388,96389],{},"@noy-db\u002Flobby","'s concern, and a\ndeployment that needs both takes lobby as a dependency rather than teaching the daemon about vaults.",[1022,96392,2621],{"id":2620},[1111,96394,96395,96405,96411,96419],{},[1114,96396,96397,96401,96402,96404],{},[1007,96398,96400],{"href":96399},".\u002Fcloud-store","Cloud store"," — configuring a cloud ",[941,96403,34951],{}," in detail",[1114,96406,96407,96410],{},[1007,96408,523],{"href":96409},".\u002Fbrowser-durability"," — keeping the local copy alive in a browser",[1114,96412,96413,96418],{},[1007,96414,96416],{"href":96415},"..\u002Ffamilies\u002Fby\u002F",[941,96417,34655],{}," — session share, and why it is a target not a store",[1114,96420,96421,96424,96425,96427],{},[1007,96422,29],{"href":96423},"..\u002Farchitecture\u002Fports"," — the store contract a ",[941,96426,1779],{}," adapter binds",[2645,96429,30448],{},{"title":1037,"searchDepth":1038,"depth":1038,"links":96431},[96432,96433,96434,96435,96436,96437,96438,96439,96440,96441],{"id":70092,"depth":1041,"text":70093},{"id":58275,"depth":1041,"text":58276},{"id":95688,"depth":1041,"text":95689},{"id":96032,"depth":1041,"text":96033},{"id":96131,"depth":1041,"text":96132},{"id":96164,"depth":1041,"text":96165},{"id":96207,"depth":1041,"text":96208},{"id":18051,"depth":1041,"text":71414},{"id":96298,"depth":1041,"text":96299},{"id":2620,"depth":1041,"text":2621},"Admin console plus member app on one team vault — LAN live store, cloud sync-peer, offline replicas, and what the UI may honestly claim.",{},{"title":585,"description":96442},"YBXzw_5a9Hhevpz2hnPW1h1W-uvSWwqsULmjuKjbuLE",{"id":96447,"title":589,"access":12,"apiToc":929,"body":96448,"category":1042,"description":97685,"extension":1044,"meta":97686,"navigation":1042,"nest":1042,"path":590,"seo":97687,"sinceVersion":1042,"status":1049,"stem":591,"subtitle":1042,"tier":1050,"tocExternal":1042,"__hash__":97688},"docs\u002Frecipes\u002Funlock-ceremonies.md",{"type":931,"value":96449,"toc":97674},[96450,96453,96471,96477,96486,96488,96530,96534,96537,96673,96677,96690,96835,96842,96846,96863,97012,97018,97022,97036,97336,97347,97351,97357,97560,97564,97572,97622,97626,97645,97647,97671],[934,96451,589],{"id":96452},"unlock-ceremonies",[1061,96454,96455],{},[937,96456,96457,96459,96460,58223,96462,96464,96465,96467,96468,96470],{},[950,96458,1500],{}," an app developer designing how users open the vault — day zero, every day after, and the day everything is lost.\n",[950,96461,58222],{},[941,96463,17049],{}," (passkeys) + ",[941,96466,45343],{}," (printed codes) + ",[941,96469,21321],{}," (re-auth policy).",[937,96472,96473,96474,96476],{},"Unlocking is a ",[1092,96475,17867],{},", not a config option: something the user does, in an order you design. This recipe walks the complete lifecycle — the secret as root of trust, a passkey so nothing is typed day-to-day, a session policy that refuses sensitive operations until fresh proof-of-presence, and recovery codes for the day the passkey and secret are both gone. Methods stack: one vault, several ways in.",[937,96478,58245,96479,96485],{},[1007,96480,96482],{"href":94620,"rel":96481},[3371],[941,96483,96484],{},"showcases\u002Fsrc\u002Frecipe-unlock-ceremonies.recipe.test.ts"," — the code below is the same code that runs there (with a synthetic PRF authenticator standing in for Touch ID; the calls are identical against real hardware).",[1022,96487,70093],{"id":70092},[1111,96489,96490,96497,96507,96524],{},[1114,96491,96492,1324,96494],{},[950,96493,71604],{},[941,96495,96496],{},"pnpm add @noy-db\u002Fhub @noy-db\u002Fon-webauthn @noy-db\u002Fon-recovery",[1114,96498,96499,96504,96505,1520],{},[950,96500,96501],{},[941,96502,96503],{},"sessionStrategy: withSession()"," for the policy step — the enforcer ships in ",[941,96506,62043],{},[1114,96508,96509,96512,96513,96515,96516,96519,96520,33515],{},[950,96510,96511],{},"A browser with WebAuthn"," for passkeys (",[941,96514,88036],{}," + the PRF extension — every UI runs ",[941,96517,96518],{},"isWebAuthnAvailable()"," as the pre-flight). Node-side unlock uses the ",[1007,96521,96522,9789],{"href":115},[941,96523,1139],{},[1114,96525,96526,96529],{},[950,96527,96528],{},"Somewhere to persist the enrollment record"," — it is opaque ciphertext, so any store or plain file works.",[1022,96531,96533],{"id":96532},"day-zero-the-secret-creates-the-vault","Day zero — the secret creates the vault",[937,96535,96536],{},"The secret is the root of trust: it derives the key that wraps everything else.",[2217,96538,96540],{"className":2219,"code":96539,"language":2221,"meta":1037,"style":1037},"import { createNoydb } from '@noy-db\u002Fhub'\nimport { toBrowserIdb } from '@noy-db\u002Fto-browser-idb'\n\nconst db = await createNoydb({\n  store: toBrowserIdb(),\n  user: 'alice',\n  secret: 'correct horse battery staple', \u002F\u002F 4+ diceware words or 12+ chars\n})\nconst vault = await db.openVault('acme')\n",[941,96541,96542,96560,96578,96582,96598,96610,96624,96641,96647],{"__ignoreMap":1037},[2225,96543,96544,96546,96548,96550,96552,96554,96556,96558],{"class":2227,"line":2228},[2225,96545,2232],{"class":2231},[2225,96547,2236],{"class":2235},[2225,96549,2744],{"class":2239},[2225,96551,2243],{"class":2235},[2225,96553,2246],{"class":2231},[2225,96555,2249],{"class":2235},[2225,96557,1086],{"class":2252},[2225,96559,2256],{"class":2235},[2225,96561,96562,96564,96566,96568,96570,96572,96574,96576],{"class":2227,"line":1041},[2225,96563,2232],{"class":2231},[2225,96565,2236],{"class":2235},[2225,96567,34763],{"class":2239},[2225,96569,2243],{"class":2235},[2225,96571,2246],{"class":2231},[2225,96573,2249],{"class":2235},[2225,96575,9573],{"class":2252},[2225,96577,2256],{"class":2235},[2225,96579,96580],{"class":2227,"line":1038},[2225,96581,2285],{"emptyLinePlaceholder":2284},[2225,96583,96584,96586,96588,96590,96592,96594,96596],{"class":2227,"line":2288},[2225,96585,2775],{"class":2774},[2225,96587,2778],{"class":2239},[2225,96589,2781],{"class":2235},[2225,96591,2784],{"class":2231},[2225,96593,2744],{"class":2291},[2225,96595,2789],{"class":2239},[2225,96597,4200],{"class":2235},[2225,96599,96600,96602,96604,96606,96608],{"class":2227,"line":2304},[2225,96601,15608],{"class":2307},[2225,96603,2311],{"class":2235},[2225,96605,34763],{"class":2291},[2225,96607,4228],{"class":2239},[2225,96609,1707],{"class":2235},[2225,96611,96612,96614,96616,96618,96620,96622],{"class":2227,"line":2356},[2225,96613,4205],{"class":2307},[2225,96615,2311],{"class":2235},[2225,96617,2249],{"class":2235},[2225,96619,6408],{"class":2252},[2225,96621,2805],{"class":2235},[2225,96623,1707],{"class":2235},[2225,96625,96626,96628,96630,96632,96634,96636,96638],{"class":2227,"line":2830},[2225,96627,10423],{"class":2307},[2225,96629,2311],{"class":2235},[2225,96631,2249],{"class":2235},[2225,96633,40260],{"class":2252},[2225,96635,2805],{"class":2235},[2225,96637,2298],{"class":2235},[2225,96639,96640],{"class":2763}," \u002F\u002F 4+ diceware words or 12+ chars\n",[2225,96642,96643,96645],{"class":2227,"line":2861},[2225,96644,2359],{"class":2235},[2225,96646,2362],{"class":2239},[2225,96648,96649,96651,96653,96655,96657,96659,96661,96663,96665,96667,96669,96671],{"class":2227,"line":4246},[2225,96650,2775],{"class":2774},[2225,96652,2835],{"class":2239},[2225,96654,2781],{"class":2235},[2225,96656,2784],{"class":2231},[2225,96658,2842],{"class":2239},[2225,96660,1520],{"class":2235},[2225,96662,2847],{"class":2291},[2225,96664,2789],{"class":2239},[2225,96666,2805],{"class":2235},[2225,96668,6450],{"class":2252},[2225,96670,2805],{"class":2235},[2225,96672,2362],{"class":2239},[1022,96674,96676],{"id":96675},"enroll-a-passkey","Enroll a passkey",[937,96678,96679,96681,96682,96685,96686,96689],{},[941,96680,17811],{}," calls ",[941,96683,96684],{},"navigator.credentials.create",", derives a wrapping key from the authenticator's PRF output, and returns an ",[950,96687,96688],{},"enrollment record"," — the keyring wrapped under a key that never left the hardware:",[2217,96691,96693],{"className":2219,"code":96692,"language":2221,"meta":1037,"style":1037},"import { enrollWebAuthn, isWebAuthnAvailable } from '@noy-db\u002Fon-webauthn'\n\nif (isWebAuthnAvailable()) {\n  const keyring = await db.team.getKeyring('acme')\n  const enrollment = await enrollWebAuthn(keyring, 'acme')\n  \u002F\u002F persist it anywhere — it is opaque ciphertext:\n  localStorage.setItem('acme-passkey', JSON.stringify(enrollment))\n}\n",[941,96694,96695,96718,96722,96735,96766,96793,96798,96831],{"__ignoreMap":1037},[2225,96696,96697,96699,96701,96703,96705,96708,96710,96712,96714,96716],{"class":2227,"line":2228},[2225,96698,2232],{"class":2231},[2225,96700,2236],{"class":2235},[2225,96702,46759],{"class":2239},[2225,96704,2298],{"class":2235},[2225,96706,96707],{"class":2239}," isWebAuthnAvailable",[2225,96709,2243],{"class":2235},[2225,96711,2246],{"class":2231},[2225,96713,2249],{"class":2235},[2225,96715,17049],{"class":2252},[2225,96717,2256],{"class":2235},[2225,96719,96720],{"class":2227,"line":1041},[2225,96721,2285],{"emptyLinePlaceholder":2284},[2225,96723,96724,96726,96728,96731,96733],{"class":2227,"line":1038},[2225,96725,35129],{"class":2231},[2225,96727,953],{"class":2239},[2225,96729,96730],{"class":2291},"isWebAuthnAvailable",[2225,96732,35140],{"class":2239},[2225,96734,4200],{"class":2235},[2225,96736,96737,96739,96742,96744,96746,96748,96750,96752,96754,96756,96758,96760,96762,96764],{"class":2227,"line":2288},[2225,96738,8123],{"class":2774},[2225,96740,96741],{"class":2239}," keyring",[2225,96743,6346],{"class":2235},[2225,96745,2784],{"class":2231},[2225,96747,2842],{"class":2239},[2225,96749,1520],{"class":2235},[2225,96751,868],{"class":2239},[2225,96753,1520],{"class":2235},[2225,96755,9953],{"class":2291},[2225,96757,2789],{"class":2307},[2225,96759,2805],{"class":2235},[2225,96761,6450],{"class":2252},[2225,96763,2805],{"class":2235},[2225,96765,2362],{"class":2307},[2225,96767,96768,96770,96773,96775,96777,96779,96781,96783,96785,96787,96789,96791],{"class":2227,"line":2304},[2225,96769,8123],{"class":2774},[2225,96771,96772],{"class":2239}," enrollment",[2225,96774,6346],{"class":2235},[2225,96776,2784],{"class":2231},[2225,96778,46759],{"class":2291},[2225,96780,2789],{"class":2307},[2225,96782,2705],{"class":2239},[2225,96784,2298],{"class":2235},[2225,96786,2249],{"class":2235},[2225,96788,6450],{"class":2252},[2225,96790,2805],{"class":2235},[2225,96792,2362],{"class":2307},[2225,96794,96795],{"class":2227,"line":2356},[2225,96796,96797],{"class":2763},"  \u002F\u002F persist it anywhere — it is opaque ciphertext:\n",[2225,96799,96800,96803,96805,96808,96810,96812,96815,96817,96819,96821,96823,96825,96827,96829],{"class":2227,"line":2830},[2225,96801,96802],{"class":2239},"  localStorage",[2225,96804,1520],{"class":2235},[2225,96806,96807],{"class":2291},"setItem",[2225,96809,2789],{"class":2307},[2225,96811,2805],{"class":2235},[2225,96813,96814],{"class":2252},"acme-passkey",[2225,96816,2805],{"class":2235},[2225,96818,2298],{"class":2235},[2225,96820,15793],{"class":2239},[2225,96822,1520],{"class":2235},[2225,96824,85490],{"class":2291},[2225,96826,2789],{"class":2307},[2225,96828,19714],{"class":2239},[2225,96830,8606],{"class":2307},[2225,96832,96833],{"class":2227,"line":2861},[2225,96834,7351],{"class":2235},[937,96836,96837,96838,96841],{},"The secret still works — enrollment ",[1092,96839,96840],{},"adds"," a way in, it never replaces one.",[1022,96843,96845],{"id":96844},"every-day-after-unlock-with-a-touch","Every day after — unlock with a touch",[937,96847,96848,96681,96851,96854,96855,96857,96858,96860,96861,86032],{},[941,96849,96850],{},"unlockWebAuthn",[941,96852,96853],{},"navigator.credentials.get",", reproduces the wrapping key from the same authenticator, and hands back the full keyring. Feed it to ",[941,96856,12780],{}," through the ",[941,96859,9953],{}," callback — the cold-start contract every ",[941,96862,1787],{},[2217,96864,96866],{"className":2219,"code":96865,"language":2221,"meta":1037,"style":1037},"import { unlockWebAuthn } from '@noy-db\u002Fon-webauthn'\n\nconst enrollment = JSON.parse(localStorage.getItem('acme-passkey')!)\nconst unlocked = await unlockWebAuthn(enrollment) \u002F\u002F ← the touch\n\nconst db = await createNoydb({\n  store: toBrowserIdb(),\n  user: 'alice',\n  getKeyring: async () => unlocked,\n})\n",[941,96867,96868,96886,96890,96926,96944,96948,96964,96976,96990,97006],{"__ignoreMap":1037},[2225,96869,96870,96872,96874,96876,96878,96880,96882,96884],{"class":2227,"line":2228},[2225,96871,2232],{"class":2231},[2225,96873,2236],{"class":2235},[2225,96875,46641],{"class":2239},[2225,96877,2243],{"class":2235},[2225,96879,2246],{"class":2231},[2225,96881,2249],{"class":2235},[2225,96883,17049],{"class":2252},[2225,96885,2256],{"class":2235},[2225,96887,96888],{"class":2227,"line":1041},[2225,96889,2285],{"emptyLinePlaceholder":2284},[2225,96891,96892,96894,96896,96898,96900,96902,96904,96907,96909,96912,96914,96916,96918,96920,96922,96924],{"class":2227,"line":1038},[2225,96893,2775],{"class":2774},[2225,96895,46662],{"class":2239},[2225,96897,2781],{"class":2235},[2225,96899,15793],{"class":2239},[2225,96901,1520],{"class":2235},[2225,96903,15798],{"class":2291},[2225,96905,96906],{"class":2239},"(localStorage",[2225,96908,1520],{"class":2235},[2225,96910,96911],{"class":2291},"getItem",[2225,96913,2789],{"class":2239},[2225,96915,2805],{"class":2235},[2225,96917,96814],{"class":2252},[2225,96919,2805],{"class":2235},[2225,96921,1381],{"class":2239},[2225,96923,35134],{"class":2235},[2225,96925,2362],{"class":2239},[2225,96927,96928,96930,96932,96934,96936,96938,96941],{"class":2227,"line":2288},[2225,96929,2775],{"class":2774},[2225,96931,77619],{"class":2239},[2225,96933,2781],{"class":2235},[2225,96935,2784],{"class":2231},[2225,96937,46641],{"class":2291},[2225,96939,96940],{"class":2239},"(enrollment) ",[2225,96942,96943],{"class":2763},"\u002F\u002F ← the touch\n",[2225,96945,96946],{"class":2227,"line":2304},[2225,96947,2285],{"emptyLinePlaceholder":2284},[2225,96949,96950,96952,96954,96956,96958,96960,96962],{"class":2227,"line":2356},[2225,96951,2775],{"class":2774},[2225,96953,2778],{"class":2239},[2225,96955,2781],{"class":2235},[2225,96957,2784],{"class":2231},[2225,96959,2744],{"class":2291},[2225,96961,2789],{"class":2239},[2225,96963,4200],{"class":2235},[2225,96965,96966,96968,96970,96972,96974],{"class":2227,"line":2830},[2225,96967,15608],{"class":2307},[2225,96969,2311],{"class":2235},[2225,96971,34763],{"class":2291},[2225,96973,4228],{"class":2239},[2225,96975,1707],{"class":2235},[2225,96977,96978,96980,96982,96984,96986,96988],{"class":2227,"line":2861},[2225,96979,4205],{"class":2307},[2225,96981,2311],{"class":2235},[2225,96983,2249],{"class":2235},[2225,96985,6408],{"class":2252},[2225,96987,2805],{"class":2235},[2225,96989,1707],{"class":2235},[2225,96991,96992,96994,96996,96998,97000,97002,97004],{"class":2227,"line":4246},[2225,96993,46719],{"class":2291},[2225,96995,2311],{"class":2235},[2225,96997,38717],{"class":2774},[2225,96999,13130],{"class":2235},[2225,97001,8116],{"class":2774},[2225,97003,86124],{"class":2239},[2225,97005,1707],{"class":2235},[2225,97007,97008,97010],{"class":2227,"line":4253},[2225,97009,2359],{"class":2235},[2225,97011,2362],{"class":2239},[937,97013,97014,97015,97017],{},"No secret in the page, nothing to keylog — the property the ",[1007,97016,13661],{"href":464}," leans on.",[1022,97019,97021],{"id":97020},"gate-sensitive-operations-session-policy","Gate sensitive operations — session policy",[937,97023,97024,97025,97028,97029,97032,97033,97035],{},"The session layer is a ",[1092,97026,97027],{},"policy oracle",": it refuses operations you listed, and ",[950,97030,97031],{},"your app supplies the re-auth act"," — a fresh ",[941,97034,1787],{}," verification — before proceeding:",[2217,97037,97039],{"className":2219,"code":97038,"language":2221,"meta":1037,"style":1037},"import { createSession, createEnforcer } from '@noy-db\u002Fhub\u002Fsession'\nimport { SessionPolicyError } from '@noy-db\u002Fhub'\n\nconst { token, sessionId } = await createSession(keyring, 'acme', { ttlMs: 15 * 60_000 })\n\nconst enforcer = createEnforcer({\n  policy: { idleTimeoutMs: 15 * 60_000, requireReAuthFor: ['grant'] },\n  sessionId,\n  onRevoke: (reason) => showLockScreen(reason), \u002F\u002F 'idle' | 'absolute' | 'background'\n})\n\nenforcer.touch() \u002F\u002F top of every operation the idle timer should track\n\ntry {\n  enforcer.checkOperation('grant')\n} catch (e) {\n  if (e instanceof SessionPolicyError) {\n    await unlockWebAuthn(enrollment) \u002F\u002F fresh proof-of-presence\n    \u002F\u002F …now perform the grant\n  }\n}\n",[941,97040,97041,97065,97084,97088,97138,97142,97157,97194,97201,97227,97233,97237,97252,97256,97262,97282,97292,97308,97323,97328,97332],{"__ignoreMap":1037},[2225,97042,97043,97045,97047,97050,97052,97055,97057,97059,97061,97063],{"class":2227,"line":2228},[2225,97044,2232],{"class":2231},[2225,97046,2236],{"class":2235},[2225,97048,97049],{"class":2239}," createSession",[2225,97051,2298],{"class":2235},[2225,97053,97054],{"class":2239}," createEnforcer",[2225,97056,2243],{"class":2235},[2225,97058,2246],{"class":2231},[2225,97060,2249],{"class":2235},[2225,97062,62043],{"class":2252},[2225,97064,2256],{"class":2235},[2225,97066,97067,97069,97071,97074,97076,97078,97080,97082],{"class":2227,"line":1041},[2225,97068,2232],{"class":2231},[2225,97070,2236],{"class":2235},[2225,97072,97073],{"class":2239}," SessionPolicyError",[2225,97075,2243],{"class":2235},[2225,97077,2246],{"class":2231},[2225,97079,2249],{"class":2235},[2225,97081,1086],{"class":2252},[2225,97083,2256],{"class":2235},[2225,97085,97086],{"class":2227,"line":1038},[2225,97087,2285],{"emptyLinePlaceholder":2284},[2225,97089,97090,97092,97094,97097,97099,97102,97104,97106,97108,97110,97112,97114,97116,97118,97120,97122,97124,97126,97128,97130,97132,97134,97136],{"class":2227,"line":2288},[2225,97091,2775],{"class":2774},[2225,97093,2236],{"class":2235},[2225,97095,97096],{"class":2239}," token",[2225,97098,2298],{"class":2235},[2225,97100,97101],{"class":2239}," sessionId ",[2225,97103,2359],{"class":2235},[2225,97105,6346],{"class":2235},[2225,97107,2784],{"class":2231},[2225,97109,97049],{"class":2291},[2225,97111,45008],{"class":2239},[2225,97113,2298],{"class":2235},[2225,97115,2249],{"class":2235},[2225,97117,6450],{"class":2252},[2225,97119,2805],{"class":2235},[2225,97121,2298],{"class":2235},[2225,97123,2236],{"class":2235},[2225,97125,44437],{"class":2307},[2225,97127,2311],{"class":2235},[2225,97129,45248],{"class":6527},[2225,97131,2263],{"class":2235},[2225,97133,19001],{"class":6527},[2225,97135,2243],{"class":2235},[2225,97137,2362],{"class":2239},[2225,97139,97140],{"class":2227,"line":2304},[2225,97141,2285],{"emptyLinePlaceholder":2284},[2225,97143,97144,97146,97149,97151,97153,97155],{"class":2227,"line":2356},[2225,97145,2775],{"class":2774},[2225,97147,97148],{"class":2239}," enforcer ",[2225,97150,2781],{"class":2235},[2225,97152,97054],{"class":2291},[2225,97154,2789],{"class":2239},[2225,97156,4200],{"class":2235},[2225,97158,97159,97161,97163,97165,97167,97169,97171,97173,97175,97177,97180,97182,97184,97186,97188,97190,97192],{"class":2227,"line":2830},[2225,97160,18899],{"class":2307},[2225,97162,2311],{"class":2235},[2225,97164,2236],{"class":2235},[2225,97166,62252],{"class":2307},[2225,97168,2311],{"class":2235},[2225,97170,45248],{"class":6527},[2225,97172,2263],{"class":2235},[2225,97174,19001],{"class":6527},[2225,97176,2298],{"class":2235},[2225,97178,97179],{"class":2307}," requireReAuthFor",[2225,97181,2311],{"class":2235},[2225,97183,8667],{"class":2239},[2225,97185,2805],{"class":2235},[2225,97187,10493],{"class":2252},[2225,97189,2805],{"class":2235},[2225,97191,8890],{"class":2239},[2225,97193,19008],{"class":2235},[2225,97195,97196,97199],{"class":2227,"line":2861},[2225,97197,97198],{"class":2239},"  sessionId",[2225,97200,1707],{"class":2235},[2225,97202,97203,97206,97208,97210,97212,97214,97216,97219,97222,97224],{"class":2227,"line":4246},[2225,97204,97205],{"class":2291},"  onRevoke",[2225,97207,2311],{"class":2235},[2225,97209,953],{"class":2235},[2225,97211,43668],{"class":7052},[2225,97213,1381],{"class":2235},[2225,97215,8116],{"class":2774},[2225,97217,97218],{"class":2291}," showLockScreen",[2225,97220,97221],{"class":2239},"(reason)",[2225,97223,2298],{"class":2235},[2225,97225,97226],{"class":2763}," \u002F\u002F 'idle' | 'absolute' | 'background'\n",[2225,97228,97229,97231],{"class":2227,"line":4253},[2225,97230,2359],{"class":2235},[2225,97232,2362],{"class":2239},[2225,97234,97235],{"class":2227,"line":4259},[2225,97236,2285],{"emptyLinePlaceholder":2284},[2225,97238,97239,97242,97244,97247,97249],{"class":2227,"line":6548},[2225,97240,97241],{"class":2239},"enforcer",[2225,97243,1520],{"class":2235},[2225,97245,97246],{"class":2291},"touch",[2225,97248,19132],{"class":2239},[2225,97250,97251],{"class":2763},"\u002F\u002F top of every operation the idle timer should track\n",[2225,97253,97254],{"class":2227,"line":6576},[2225,97255,2285],{"emptyLinePlaceholder":2284},[2225,97257,97258,97260],{"class":2227,"line":6582},[2225,97259,7554],{"class":2231},[2225,97261,2301],{"class":2235},[2225,97263,97264,97267,97269,97272,97274,97276,97278,97280],{"class":2227,"line":6587},[2225,97265,97266],{"class":2239},"  enforcer",[2225,97268,1520],{"class":2235},[2225,97270,97271],{"class":2291},"checkOperation",[2225,97273,2789],{"class":2307},[2225,97275,2805],{"class":2235},[2225,97277,10493],{"class":2252},[2225,97279,2805],{"class":2235},[2225,97281,2362],{"class":2307},[2225,97283,97284,97286,97288,97290],{"class":2227,"line":7174},[2225,97285,2359],{"class":2235},[2225,97287,7598],{"class":2231},[2225,97289,19159],{"class":2239},[2225,97291,4200],{"class":2235},[2225,97293,97294,97296,97298,97300,97302,97304,97306],{"class":2227,"line":7180},[2225,97295,7608],{"class":2231},[2225,97297,953],{"class":2307},[2225,97299,12450],{"class":2239},[2225,97301,19172],{"class":2235},[2225,97303,97073],{"class":6342},[2225,97305,8495],{"class":2307},[2225,97307,4200],{"class":2235},[2225,97309,97310,97312,97314,97316,97318,97320],{"class":2227,"line":7220},[2225,97311,79881],{"class":2231},[2225,97313,46641],{"class":2291},[2225,97315,2789],{"class":2307},[2225,97317,19714],{"class":2239},[2225,97319,8495],{"class":2307},[2225,97321,97322],{"class":2763},"\u002F\u002F fresh proof-of-presence\n",[2225,97324,97325],{"class":2227,"line":7225},[2225,97326,97327],{"class":2763},"    \u002F\u002F …now perform the grant\n",[2225,97329,97330],{"class":2227,"line":7231},[2225,97331,7632],{"class":2235},[2225,97333,97334],{"class":2227,"line":7267},[2225,97335,7351],{"class":2235},[937,97337,97338,53922,97340,97342,97343,97346],{},[941,97339,96503],{},[941,97341,12780],{}," enables the machinery; the ",[1007,97344,97345],{"href":841},"session page"," covers liveness, revocation, and the full policy surface.",[1022,97348,97350],{"id":97349},"the-escape-hatch-printed-recovery-codes","The escape hatch — printed recovery codes",[937,97352,97353,97354,97356],{},"For the day the passkey is lost ",[1092,97355,14128],{}," the secret is forgotten: a set of one-time codes, each independently able to unwrap the collection keys. Print them, put them in a drawer:",[2217,97358,97360],{"className":2219,"code":97359,"language":2221,"meta":1037,"style":1037},"import { generateRecoveryCodeSet, parseRecoveryCode } from '@noy-db\u002Fon-recovery'\nimport { unwrapDeksFromPaperEntry } from '@noy-db\u002Fhub'\n\nconst keyring = await db.team.getKeyring('acme')\nconst set = await generateRecoveryCodeSet({ deks: keyring.deks, count: 3 })\n\u002F\u002F set.codes → print these; set.entries → persist alongside the vault\n\n\u002F\u002F Years later, the user types one code from the sheet:\nconst parsed = parseRecoveryCode(typedCode)\nif (parsed.status === 'valid') {\n  const deks = await unwrapDeksFromPaperEntry(matchingEntry, parsed.code)\n  \u002F\u002F the recovered DEKs decrypt everything the originals encrypted\n}\n",[941,97361,97362,97384,97402,97406,97436,97475,97480,97484,97489,97502,97524,97551,97556],{"__ignoreMap":1037},[2225,97363,97364,97366,97368,97370,97372,97374,97376,97378,97380,97382],{"class":2227,"line":2228},[2225,97365,2232],{"class":2231},[2225,97367,2236],{"class":2235},[2225,97369,45440],{"class":2239},[2225,97371,2298],{"class":2235},[2225,97373,45450],{"class":2239},[2225,97375,2243],{"class":2235},[2225,97377,2246],{"class":2231},[2225,97379,2249],{"class":2235},[2225,97381,45343],{"class":2252},[2225,97383,2256],{"class":2235},[2225,97385,97386,97388,97390,97392,97394,97396,97398,97400],{"class":2227,"line":1041},[2225,97387,2232],{"class":2231},[2225,97389,2236],{"class":2235},[2225,97391,45469],{"class":2239},[2225,97393,2243],{"class":2235},[2225,97395,2246],{"class":2231},[2225,97397,2249],{"class":2235},[2225,97399,1086],{"class":2252},[2225,97401,2256],{"class":2235},[2225,97403,97404],{"class":2227,"line":1038},[2225,97405,2285],{"emptyLinePlaceholder":2284},[2225,97407,97408,97410,97412,97414,97416,97418,97420,97422,97424,97426,97428,97430,97432,97434],{"class":2227,"line":2288},[2225,97409,2775],{"class":2774},[2225,97411,44845],{"class":2239},[2225,97413,2781],{"class":2235},[2225,97415,2784],{"class":2231},[2225,97417,2842],{"class":2239},[2225,97419,1520],{"class":2235},[2225,97421,868],{"class":2239},[2225,97423,1520],{"class":2235},[2225,97425,9953],{"class":2291},[2225,97427,2789],{"class":2239},[2225,97429,2805],{"class":2235},[2225,97431,6450],{"class":2252},[2225,97433,2805],{"class":2235},[2225,97435,2362],{"class":2239},[2225,97437,97438,97440,97443,97445,97447,97449,97451,97453,97455,97457,97459,97461,97463,97465,97467,97469,97471,97473],{"class":2227,"line":2304},[2225,97439,2775],{"class":2774},[2225,97441,97442],{"class":2239}," set ",[2225,97444,2781],{"class":2235},[2225,97446,2784],{"class":2231},[2225,97448,45440],{"class":2291},[2225,97450,2789],{"class":2239},[2225,97452,2792],{"class":2235},[2225,97454,45545],{"class":2307},[2225,97456,2311],{"class":2235},[2225,97458,96741],{"class":2239},[2225,97460,1520],{"class":2235},[2225,97462,45372],{"class":2239},[2225,97464,2298],{"class":2235},[2225,97466,19270],{"class":2307},[2225,97468,2311],{"class":2235},[2225,97470,14460],{"class":6527},[2225,97472,2243],{"class":2235},[2225,97474,2362],{"class":2239},[2225,97476,97477],{"class":2227,"line":2356},[2225,97478,97479],{"class":2763},"\u002F\u002F set.codes → print these; set.entries → persist alongside the vault\n",[2225,97481,97482],{"class":2227,"line":2830},[2225,97483,2285],{"emptyLinePlaceholder":2284},[2225,97485,97486],{"class":2227,"line":2861},[2225,97487,97488],{"class":2763},"\u002F\u002F Years later, the user types one code from the sheet:\n",[2225,97490,97491,97493,97495,97497,97499],{"class":2227,"line":4246},[2225,97492,2775],{"class":2774},[2225,97494,45605],{"class":2239},[2225,97496,2781],{"class":2235},[2225,97498,45450],{"class":2291},[2225,97500,97501],{"class":2239},"(typedCode)\n",[2225,97503,97504,97506,97508,97510,97512,97514,97516,97518,97520,97522],{"class":2227,"line":4253},[2225,97505,35129],{"class":2231},[2225,97507,45619],{"class":2239},[2225,97509,1520],{"class":2235},[2225,97511,32542],{"class":2239},[2225,97513,33083],{"class":2235},[2225,97515,2249],{"class":2235},[2225,97517,45389],{"class":2252},[2225,97519,2805],{"class":2235},[2225,97521,8495],{"class":2239},[2225,97523,4200],{"class":2235},[2225,97525,97526,97528,97530,97532,97534,97536,97538,97541,97543,97545,97547,97549],{"class":2227,"line":4259},[2225,97527,8123],{"class":2774},[2225,97529,45545],{"class":2239},[2225,97531,6346],{"class":2235},[2225,97533,2784],{"class":2231},[2225,97535,45469],{"class":2291},[2225,97537,2789],{"class":2307},[2225,97539,97540],{"class":2239},"matchingEntry",[2225,97542,2298],{"class":2235},[2225,97544,45683],{"class":2239},[2225,97546,1520],{"class":2235},[2225,97548,941],{"class":2239},[2225,97550,2362],{"class":2307},[2225,97552,97553],{"class":2227,"line":6548},[2225,97554,97555],{"class":2763},"  \u002F\u002F the recovered DEKs decrypt everything the originals encrypted\n",[2225,97557,97558],{"class":2227,"line":6576},[2225,97559,7351],{"class":2235},[1022,97561,97563],{"id":97562},"variations","Variations",[937,97565,97566,97567,97569,97570,2311],{},"The ceremony's shape stays the same whichever method plays each part — every ",[941,97568,1787],{}," unlock returns the same ",[941,97571,11493],{},[1111,97573,97574,97584,97602,97612],{},[1114,97575,97576,1324,97579,97583],{},[950,97577,97578],{},"SSO instead of passkeys:",[1007,97580,97581],{"href":310},[941,97582,15969],{}," (Google \u002F Apple \u002F LINE proven in showcases 73–75).",[1114,97585,97586,1324,97589,968,97593,968,97597,97601],{},[950,97587,97588],{},"Second factors:",[1007,97590,97591],{"href":334},[941,97592,15974],{},[1007,97594,97595],{"href":302},[941,97596,15979],{},[1007,97598,97599],{"href":318},[941,97600,15958],{}," for session resume.",[1114,97603,97604,1324,97607,97611],{},[950,97605,97606],{},"Social recovery instead of paper:",[1007,97608,97609],{"href":326},[941,97610,15994],{}," splits the recovery secret across trustees.",[1114,97613,97614,1324,97617,97621],{},[950,97615,97616],{},"Duress and lockout:",[1007,97618,97619],{"href":330},[941,97620,15984],{}," wraps any of the above.",[1022,97623,97625],{"id":97624},"when-an-unlock-fails","When an unlock FAILS",[937,97627,97628,97629,97635,97636,97638,97639,97641,97642],{},"⚠️ Before you translate an unlock error into user-facing advice, read ",[1007,97630,97631,97632],{"href":367},"Choosing a store → ",[1092,97633,97634],{},"A store's locality changes what an unlock error means",". The short version: branch on ",[941,97637,48590],{}," first (",[941,97640,48597],{}," is a format transition, not an attack; the absence reasons are unclassifiable), then on whether the store is local or remote — and in every branch, ",[950,97643,97644],{},"say what to preserve before telling anyone to remove anything.",[1022,97646,2621],{"id":2620},[1111,97648,97649,97656,97661,97666],{},[1114,97650,97651,97655],{},[1007,97652,97653,9789],{"href":115},[941,97654,1787],{}," — the full unlock catalog and how methods compare.",[1114,97657,97658,97660],{},[1007,97659,366],{"href":367}," — why the same unlock error needs different advice on a remote store.",[1114,97662,97663,97665],{},[1007,97664,840],{"href":841}," — policies, liveness, revocation in depth.",[1114,97667,97668,97670],{},[1007,97669,463],{"href":464}," — what each method protects against, honestly.",[2645,97672,97673],{},"html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}html pre.shiki code .sHdIc, html code.shiki .sHdIc{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#EEFFFF;--shiki-default-font-style:italic;--shiki-dark:#BABED8;--shiki-dark-font-style:italic}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}",{"title":1037,"searchDepth":1038,"depth":1038,"links":97675},[97676,97677,97678,97679,97680,97681,97682,97683,97684],{"id":70092,"depth":1041,"text":70093},{"id":96532,"depth":1041,"text":96533},{"id":96675,"depth":1041,"text":96676},{"id":96844,"depth":1041,"text":96845},{"id":97020,"depth":1041,"text":97021},{"id":97349,"depth":1041,"text":97350},{"id":97562,"depth":1041,"text":97563},{"id":97624,"depth":1041,"text":97625},{"id":2620,"depth":1041,"text":2621},"The full unlock lifecycle: secret setup, passkey enrollment, daily no-secret unlock, session re-auth gates, and printed recovery codes.",{},{"title":589,"description":97685},"GKsq2iFTTGjw6pmXmyGN0uYR4FGXNf24xfNTFAD4J0w",{"id":97690,"title":593,"access":12,"apiToc":929,"body":97691,"category":1042,"description":99792,"extension":1044,"meta":99793,"navigation":1042,"nest":1042,"path":594,"seo":99794,"sinceVersion":1042,"status":1049,"stem":595,"subtitle":1042,"tier":1050,"tocExternal":1042,"__hash__":99795},"docs\u002Frecipes\u002Fuser-preferences.md",{"type":931,"value":97692,"toc":99780},[97693,97696,97712,97720,97747,97751,97758,97760,97919,97923,98101,98112,98123,98127,98136,98392,98398,98549,98561,98673,98683,98687,98697,98863,98870,98874,98881,99027,99043,99047,99060,99157,99160,99164,99170,99489,99492,99496,99509,99752,99758,99760,99777],[934,97694,593],{"id":97695},"user-preferences",[1061,97697,97698],{},[937,97699,97700,97702,97703,97705,97706,97708,97709,97711],{},[950,97701,1500],{}," multi-user vaults that want each user to get their own encrypted profile-and-preferences document (display name, avatar, locale, theme) that follows them across devices and that only they can write.\n",[950,97704,58222],{}," core (the built-in user envelope, always available) + ",[941,97707,3197],{}," for multi-user grants — optionally ",[941,97710,4676],{}," for cross-device propagation and presence.",[937,97713,58245,97714,58253],{},[1007,97715,97717],{"href":94526,"rel":97716},[3371],[941,97718,97719],{},"showcases\u002Fsrc\u002Frecipe-user-preferences.recipe.test.ts",[937,97721,97722,97723,97725,97726,97728,97729,97732,97733,97736,97737,97740,97741,97743,97744,1520],{},"noy-db stores documents encrypted inside a ",[950,97724,2694],{},", a container that can be shared by several users. For each user, the hub (",[941,97727,1086],{},", the core library) reserves one document called the ",[950,97730,97731],{},"user envelope",": it lives at the storage key ",[941,97734,97735],{},"_users\u002F\u003CkeyringId>",", where the ",[1092,97738,97739],{},"keyringId"," is the user's id in the vault's key registry — one envelope per user. The hub guarantees exactly two things: where the envelope is stored, and that each user can write only their own. What goes ",[1092,97742,1094],{}," the envelope is entirely up to your app. This recipe supplies a reference shape and the read\u002Fwrite patterns around it; the full API is documented in ",[1007,97745,884],{"href":97746},"..\u002Fservices\u002Fuser-envelope",[1022,97748,97750],{"id":97749},"when-to-reach-for-this","When to reach for this",[937,97752,97753,97754,97757],{},"You have a multi-user vault and want each user to get their own profile + preferences (display name, avatar, locale, theme) that follow them across devices via the team\u002Fsync engine. You want admin pre-fill at invite time, you want clean cross-user reads (\"Bob's display name in the team list\"), and you want the hub to enforce that ",[950,97755,97756],{},"only Bob can write Bob's profile"," — even an owner cannot.",[1022,97759,58394],{"id":58393},[2217,97761,97763],{"className":2219,"code":97762,"language":2221,"meta":1037,"style":1037},"import { createNoydb } from '@noy-db\u002Fhub'\nimport { withTeam } from '@noy-db\u002Fhub\u002Fteam'\n\n\u002F\u002F withTeam() enables multi-user vaults (grant\u002Frevoke). vault.user itself\n\u002F\u002F is always available, even in single-user vaults.\nconst db = await createNoydb({\n  teamStrategy: withTeam(),\n  user: 'vicio',\n  secret: 'vicio-pass-2026-strong',\n  \u002F\u002F No `store` → the built-in in-memory store. For a persistent browser\n  \u002F\u002F vault, pass e.g. toBrowserIdb() from @noy-db\u002Fto-browser-idb.\n})\n\nconst vault = await db.openVault('demo')\n",[941,97764,97765,97783,97801,97805,97810,97815,97831,97843,97857,97872,97877,97882,97888,97892],{"__ignoreMap":1037},[2225,97766,97767,97769,97771,97773,97775,97777,97779,97781],{"class":2227,"line":2228},[2225,97768,2232],{"class":2231},[2225,97770,2236],{"class":2235},[2225,97772,2744],{"class":2239},[2225,97774,2243],{"class":2235},[2225,97776,2246],{"class":2231},[2225,97778,2249],{"class":2235},[2225,97780,1086],{"class":2252},[2225,97782,2256],{"class":2235},[2225,97784,97785,97787,97789,97791,97793,97795,97797,97799],{"class":2227,"line":1041},[2225,97786,2232],{"class":2231},[2225,97788,2236],{"class":2235},[2225,97790,10373],{"class":2239},[2225,97792,2243],{"class":2235},[2225,97794,2246],{"class":2231},[2225,97796,2249],{"class":2235},[2225,97798,4680],{"class":2252},[2225,97800,2256],{"class":2235},[2225,97802,97803],{"class":2227,"line":1038},[2225,97804,2285],{"emptyLinePlaceholder":2284},[2225,97806,97807],{"class":2227,"line":2288},[2225,97808,97809],{"class":2763},"\u002F\u002F withTeam() enables multi-user vaults (grant\u002Frevoke). vault.user itself\n",[2225,97811,97812],{"class":2227,"line":2304},[2225,97813,97814],{"class":2763},"\u002F\u002F is always available, even in single-user vaults.\n",[2225,97816,97817,97819,97821,97823,97825,97827,97829],{"class":2227,"line":2356},[2225,97818,2775],{"class":2774},[2225,97820,2778],{"class":2239},[2225,97822,2781],{"class":2235},[2225,97824,2784],{"class":2231},[2225,97826,2744],{"class":2291},[2225,97828,2789],{"class":2239},[2225,97830,4200],{"class":2235},[2225,97832,97833,97835,97837,97839,97841],{"class":2227,"line":2830},[2225,97834,10439],{"class":2307},[2225,97836,2311],{"class":2235},[2225,97838,10373],{"class":2291},[2225,97840,4228],{"class":2239},[2225,97842,1707],{"class":2235},[2225,97844,97845,97847,97849,97851,97853,97855],{"class":2227,"line":2861},[2225,97846,4205],{"class":2307},[2225,97848,2311],{"class":2235},[2225,97850,2249],{"class":2235},[2225,97852,77062],{"class":2252},[2225,97854,2805],{"class":2235},[2225,97856,1707],{"class":2235},[2225,97858,97859,97861,97863,97865,97868,97870],{"class":2227,"line":4246},[2225,97860,10423],{"class":2307},[2225,97862,2311],{"class":2235},[2225,97864,2249],{"class":2235},[2225,97866,97867],{"class":2252},"vicio-pass-2026-strong",[2225,97869,2805],{"class":2235},[2225,97871,1707],{"class":2235},[2225,97873,97874],{"class":2227,"line":4253},[2225,97875,97876],{"class":2763},"  \u002F\u002F No `store` → the built-in in-memory store. For a persistent browser\n",[2225,97878,97879],{"class":2227,"line":4259},[2225,97880,97881],{"class":2763},"  \u002F\u002F vault, pass e.g. toBrowserIdb() from @noy-db\u002Fto-browser-idb.\n",[2225,97883,97884,97886],{"class":2227,"line":6548},[2225,97885,2359],{"class":2235},[2225,97887,2362],{"class":2239},[2225,97889,97890],{"class":2227,"line":6576},[2225,97891,2285],{"emptyLinePlaceholder":2284},[2225,97893,97894,97896,97898,97900,97902,97904,97906,97908,97910,97912,97915,97917],{"class":2227,"line":6582},[2225,97895,2775],{"class":2774},[2225,97897,2835],{"class":2239},[2225,97899,2781],{"class":2235},[2225,97901,2784],{"class":2231},[2225,97903,2842],{"class":2239},[2225,97905,1520],{"class":2235},[2225,97907,2847],{"class":2291},[2225,97909,2789],{"class":2239},[2225,97911,2805],{"class":2235},[2225,97913,97914],{"class":2252},"demo",[2225,97916,2805],{"class":2235},[2225,97918,2362],{"class":2239},[1022,97920,97922],{"id":97921},"reference-shape","Reference shape",[2217,97924,97926],{"className":2219,"code":97925,"language":2221,"meta":1037,"style":1037},"interface UserShape {\n  profile?: {\n    displayName?: string\n    avatarRef?: string  \u002F\u002F ref to a file stored via the blobs service; not a URL\n    locale?: string     \u002F\u002F BCP 47\n    timeZone?: string   \u002F\u002F IANA\n  }\n  preferences?: {\n    theme?: 'light' | 'dark' | 'system'\n    locale?: string     \u002F\u002F UI override; falls back to profile.locale\n  }\n  app?: {\n    \u002F\u002F Per-app extensions. Opaque to hub.\n    signature?: string\n    perClientCurrency?: Record\u003Cstring, string>\n  }\n}\n",[941,97927,97928,97937,97946,97955,97967,97979,97991,97995,98004,98036,98047,98051,98060,98065,98074,98093,98097],{"__ignoreMap":1037},[2225,97929,97930,97932,97935],{"class":2227,"line":2228},[2225,97931,6994],{"class":2774},[2225,97933,97934],{"class":6342}," UserShape",[2225,97936,2301],{"class":2235},[2225,97938,97939,97942,97944],{"class":2227,"line":1041},[2225,97940,97941],{"class":2307},"  profile",[2225,97943,7011],{"class":2235},[2225,97945,2301],{"class":2235},[2225,97947,97948,97951,97953],{"class":2227,"line":1038},[2225,97949,97950],{"class":2307},"    displayName",[2225,97952,7011],{"class":2235},[2225,97954,7014],{"class":6342},[2225,97956,97957,97960,97962,97964],{"class":2227,"line":2288},[2225,97958,97959],{"class":2307},"    avatarRef",[2225,97961,7011],{"class":2235},[2225,97963,6366],{"class":6342},[2225,97965,97966],{"class":2763},"  \u002F\u002F ref to a file stored via the blobs service; not a URL\n",[2225,97968,97969,97972,97974,97976],{"class":2227,"line":2304},[2225,97970,97971],{"class":2307},"    locale",[2225,97973,7011],{"class":2235},[2225,97975,6366],{"class":6342},[2225,97977,97978],{"class":2763},"     \u002F\u002F BCP 47\n",[2225,97980,97981,97984,97986,97988],{"class":2227,"line":2356},[2225,97982,97983],{"class":2307},"    timeZone",[2225,97985,7011],{"class":2235},[2225,97987,6366],{"class":6342},[2225,97989,97990],{"class":2763},"   \u002F\u002F IANA\n",[2225,97992,97993],{"class":2227,"line":2830},[2225,97994,7632],{"class":2235},[2225,97996,97997,98000,98002],{"class":2227,"line":2861},[2225,97998,97999],{"class":2307},"  preferences",[2225,98001,7011],{"class":2235},[2225,98003,2301],{"class":2235},[2225,98005,98006,98009,98011,98013,98016,98018,98020,98022,98025,98027,98029,98031,98034],{"class":2227,"line":4246},[2225,98007,98008],{"class":2307},"    theme",[2225,98010,7011],{"class":2235},[2225,98012,2249],{"class":2235},[2225,98014,98015],{"class":2252},"light",[2225,98017,2805],{"class":2235},[2225,98019,7087],{"class":2235},[2225,98021,2249],{"class":2235},[2225,98023,98024],{"class":2252},"dark",[2225,98026,2805],{"class":2235},[2225,98028,7087],{"class":2235},[2225,98030,2249],{"class":2235},[2225,98032,98033],{"class":2252},"system",[2225,98035,2256],{"class":2235},[2225,98037,98038,98040,98042,98044],{"class":2227,"line":4253},[2225,98039,97971],{"class":2307},[2225,98041,7011],{"class":2235},[2225,98043,6366],{"class":6342},[2225,98045,98046],{"class":2763},"     \u002F\u002F UI override; falls back to profile.locale\n",[2225,98048,98049],{"class":2227,"line":4259},[2225,98050,7632],{"class":2235},[2225,98052,98053,98056,98058],{"class":2227,"line":6548},[2225,98054,98055],{"class":2307},"  app",[2225,98057,7011],{"class":2235},[2225,98059,2301],{"class":2235},[2225,98061,98062],{"class":2227,"line":6576},[2225,98063,98064],{"class":2763},"    \u002F\u002F Per-app extensions. Opaque to hub.\n",[2225,98066,98067,98070,98072],{"class":2227,"line":6582},[2225,98068,98069],{"class":2307},"    signature",[2225,98071,7011],{"class":2235},[2225,98073,7014],{"class":6342},[2225,98075,98076,98079,98081,98083,98085,98087,98089,98091],{"class":2227,"line":6587},[2225,98077,98078],{"class":2307},"    perClientCurrency",[2225,98080,7011],{"class":2235},[2225,98082,22527],{"class":6342},[2225,98084,6474],{"class":2235},[2225,98086,7259],{"class":6342},[2225,98088,2298],{"class":2235},[2225,98090,6366],{"class":6342},[2225,98092,7093],{"class":2235},[2225,98094,98095],{"class":2227,"line":7174},[2225,98096,7632],{"class":2235},[2225,98098,98099],{"class":2227,"line":7180},[2225,98100,7351],{"class":2235},[937,98102,98103,98104,98107,98108,98111],{},"This interface is ",[950,98105,98106],{},"app-defined",". The hub never imports it — it JSON-serializes and encrypts whatever object you hand it. Copy this shape, extend it, contract it, or replace it; the API is generic over ",[941,98109,98110],{},"UserEnvelope\u003CT>",", so your own shape flows through every method.",[937,98113,98114,98115,98118,98119,98122],{},"One practical bound: the JSON-serialized payload is soft-capped at 64 KiB — a larger write throws ",[941,98116,98117],{},"UserEnvelopeOversizedError",". Keep bulky data (avatar images, documents) in the blobs service and store only refs here, as ",[941,98120,98121],{},"avatarRef"," above does.",[1022,98124,98126],{"id":98125},"reading-and-writing-your-own-envelope","Reading and writing your own envelope",[937,98128,98129,98131,98132,98135],{},[941,98130,21609],{}," always targets ",[1092,98133,98134],{},"your"," envelope on writes — there is no method that accepts another user's id on the write path.",[2217,98137,98139],{"className":2219,"code":98138,"language":2221,"meta":1037,"style":1037},"\u002F\u002F First write — full shape. Creates the envelope.\nawait vault.user.updateMe\u003CUserShape>({\n  profile: { displayName: 'Vicio', locale: 'it-IT' },\n  preferences: { theme: 'dark' },\n})\n\n\u002F\u002F updateMe deep-merges: partial patches preserve unmentioned fields.\n\u002F\u002F (Pass null to delete a field; omitted keys are preserved.)\nawait vault.user.updateMe\u003CUserShape>({\n  preferences: { theme: 'light' }, \u002F\u002F profile.* untouched\n})\n\n\u002F\u002F Read your own envelope (never policy-gated).\nconst me = await vault.user.me\u003CUserShape>()\nconsole.log(me?.data.profile?.displayName) \u002F\u002F → 'Vicio'\nconsole.log(me?.data.preferences?.theme)   \u002F\u002F → 'light' (merged, profile kept)\n",[941,98140,98141,98146,98172,98206,98227,98233,98237,98242,98247,98271,98294,98300,98304,98309,98338,98365],{"__ignoreMap":1037},[2225,98142,98143],{"class":2227,"line":2228},[2225,98144,98145],{"class":2763},"\u002F\u002F First write — full shape. Creates the envelope.\n",[2225,98147,98148,98150,98152,98154,98156,98158,98161,98163,98166,98168,98170],{"class":2227,"line":1041},[2225,98149,6500],{"class":2231},[2225,98151,6466],{"class":2239},[2225,98153,1520],{"class":2235},[2225,98155,16225],{"class":2239},[2225,98157,1520],{"class":2235},[2225,98159,98160],{"class":2291},"updateMe",[2225,98162,6474],{"class":2235},[2225,98164,98165],{"class":6342},"UserShape",[2225,98167,6480],{"class":2235},[2225,98169,2789],{"class":2239},[2225,98171,4200],{"class":2235},[2225,98173,98174,98176,98178,98180,98182,98184,98186,98189,98191,98193,98195,98197,98199,98202,98204],{"class":2227,"line":1038},[2225,98175,97941],{"class":2307},[2225,98177,2311],{"class":2235},[2225,98179,2236],{"class":2235},[2225,98181,75153],{"class":2307},[2225,98183,2311],{"class":2235},[2225,98185,2249],{"class":2235},[2225,98187,98188],{"class":2252},"Vicio",[2225,98190,2805],{"class":2235},[2225,98192,2298],{"class":2235},[2225,98194,24003],{"class":2307},[2225,98196,2311],{"class":2235},[2225,98198,2249],{"class":2235},[2225,98200,98201],{"class":2252},"it-IT",[2225,98203,2805],{"class":2235},[2225,98205,2353],{"class":2235},[2225,98207,98208,98210,98212,98214,98217,98219,98221,98223,98225],{"class":2227,"line":2288},[2225,98209,97999],{"class":2307},[2225,98211,2311],{"class":2235},[2225,98213,2236],{"class":2235},[2225,98215,98216],{"class":2307}," theme",[2225,98218,2311],{"class":2235},[2225,98220,2249],{"class":2235},[2225,98222,98024],{"class":2252},[2225,98224,2805],{"class":2235},[2225,98226,2353],{"class":2235},[2225,98228,98229,98231],{"class":2227,"line":2304},[2225,98230,2359],{"class":2235},[2225,98232,2362],{"class":2239},[2225,98234,98235],{"class":2227,"line":2356},[2225,98236,2285],{"emptyLinePlaceholder":2284},[2225,98238,98239],{"class":2227,"line":2830},[2225,98240,98241],{"class":2763},"\u002F\u002F updateMe deep-merges: partial patches preserve unmentioned fields.\n",[2225,98243,98244],{"class":2227,"line":2861},[2225,98245,98246],{"class":2763},"\u002F\u002F (Pass null to delete a field; omitted keys are preserved.)\n",[2225,98248,98249,98251,98253,98255,98257,98259,98261,98263,98265,98267,98269],{"class":2227,"line":4246},[2225,98250,6500],{"class":2231},[2225,98252,6466],{"class":2239},[2225,98254,1520],{"class":2235},[2225,98256,16225],{"class":2239},[2225,98258,1520],{"class":2235},[2225,98260,98160],{"class":2291},[2225,98262,6474],{"class":2235},[2225,98264,98165],{"class":6342},[2225,98266,6480],{"class":2235},[2225,98268,2789],{"class":2239},[2225,98270,4200],{"class":2235},[2225,98272,98273,98275,98277,98279,98281,98283,98285,98287,98289,98291],{"class":2227,"line":4253},[2225,98274,97999],{"class":2307},[2225,98276,2311],{"class":2235},[2225,98278,2236],{"class":2235},[2225,98280,98216],{"class":2307},[2225,98282,2311],{"class":2235},[2225,98284,2249],{"class":2235},[2225,98286,98015],{"class":2252},[2225,98288,2805],{"class":2235},[2225,98290,10965],{"class":2235},[2225,98292,98293],{"class":2763}," \u002F\u002F profile.* untouched\n",[2225,98295,98296,98298],{"class":2227,"line":4259},[2225,98297,2359],{"class":2235},[2225,98299,2362],{"class":2239},[2225,98301,98302],{"class":2227,"line":6548},[2225,98303,2285],{"emptyLinePlaceholder":2284},[2225,98305,98306],{"class":2227,"line":6576},[2225,98307,98308],{"class":2763},"\u002F\u002F Read your own envelope (never policy-gated).\n",[2225,98310,98311,98313,98316,98318,98320,98322,98324,98326,98328,98330,98332,98334,98336],{"class":2227,"line":6582},[2225,98312,2775],{"class":2774},[2225,98314,98315],{"class":2239}," me ",[2225,98317,2781],{"class":2235},[2225,98319,2784],{"class":2231},[2225,98321,6466],{"class":2239},[2225,98323,1520],{"class":2235},[2225,98325,16225],{"class":2239},[2225,98327,1520],{"class":2235},[2225,98329,4212],{"class":2291},[2225,98331,6474],{"class":2235},[2225,98333,98165],{"class":6342},[2225,98335,6480],{"class":2235},[2225,98337,8146],{"class":2239},[2225,98339,98340,98342,98344,98346,98349,98351,98353,98355,98357,98359,98362],{"class":2227,"line":6587},[2225,98341,28031],{"class":2239},[2225,98343,1520],{"class":2235},[2225,98345,27100],{"class":2291},[2225,98347,98348],{"class":2239},"(me",[2225,98350,8457],{"class":2235},[2225,98352,8673],{"class":2239},[2225,98354,1520],{"class":2235},[2225,98356,19861],{"class":2239},[2225,98358,8457],{"class":2235},[2225,98360,98361],{"class":2239},"displayName) ",[2225,98363,98364],{"class":2763},"\u002F\u002F → 'Vicio'\n",[2225,98366,98367,98369,98371,98373,98375,98377,98379,98381,98384,98386,98389],{"class":2227,"line":7174},[2225,98368,28031],{"class":2239},[2225,98370,1520],{"class":2235},[2225,98372,27100],{"class":2291},[2225,98374,98348],{"class":2239},[2225,98376,8457],{"class":2235},[2225,98378,8673],{"class":2239},[2225,98380,1520],{"class":2235},[2225,98382,98383],{"class":2239},"preferences",[2225,98385,8457],{"class":2235},[2225,98387,98388],{"class":2239},"theme)   ",[2225,98390,98391],{"class":2763},"\u002F\u002F → 'light' (merged, profile kept)\n",[937,98393,98394,98395,2311],{},"The full envelope returned by ",[941,98396,98397],{},"me()",[2217,98399,98401],{"className":6604,"code":98400,"language":141,"meta":1037,"style":1037},"{\n  \"keyringId\": \"vicio\",\n  \"data\": {\n    \"profile\": { \"displayName\": \"Vicio\", \"locale\": \"it-IT\" },\n    \"preferences\": { \"theme\": \"light\" }\n  },\n  \"_v\": 2,\n  \"_ts\": \"2026-07-08T09:00:00.000Z\"\n}\n",[941,98402,98403,98407,98425,98437,98481,98510,98514,98528,98545],{"__ignoreMap":1037},[2225,98404,98405],{"class":2227,"line":2228},[2225,98406,4200],{"class":2235},[2225,98408,98409,98411,98413,98415,98417,98419,98421,98423],{"class":2227,"line":1041},[2225,98410,9368],{"class":2235},[2225,98412,97739],{"class":2774},[2225,98414,6620],{"class":2235},[2225,98416,2311],{"class":2235},[2225,98418,6614],{"class":2235},[2225,98420,77062],{"class":2252},[2225,98422,6620],{"class":2235},[2225,98424,1707],{"class":2235},[2225,98426,98427,98429,98431,98433,98435],{"class":2227,"line":1038},[2225,98428,9368],{"class":2235},[2225,98430,8673],{"class":2774},[2225,98432,6620],{"class":2235},[2225,98434,2311],{"class":2235},[2225,98436,2301],{"class":2235},[2225,98438,98439,98441,98443,98445,98447,98449,98451,98453,98455,98457,98459,98461,98463,98465,98467,98469,98471,98473,98475,98477,98479],{"class":2227,"line":2288},[2225,98440,14201],{"class":2235},[2225,98442,19861],{"class":6342},[2225,98444,6620],{"class":2235},[2225,98446,2311],{"class":2235},[2225,98448,2236],{"class":2235},[2225,98450,6614],{"class":2235},[2225,98452,16791],{"class":6527},[2225,98454,6620],{"class":2235},[2225,98456,2311],{"class":2235},[2225,98458,6614],{"class":2235},[2225,98460,98188],{"class":2252},[2225,98462,6620],{"class":2235},[2225,98464,2298],{"class":2235},[2225,98466,6614],{"class":2235},[2225,98468,23074],{"class":6527},[2225,98470,6620],{"class":2235},[2225,98472,2311],{"class":2235},[2225,98474,6614],{"class":2235},[2225,98476,98201],{"class":2252},[2225,98478,6620],{"class":2235},[2225,98480,2353],{"class":2235},[2225,98482,98483,98485,98487,98489,98491,98493,98495,98498,98500,98502,98504,98506,98508],{"class":2227,"line":2304},[2225,98484,14201],{"class":2235},[2225,98486,98383],{"class":6342},[2225,98488,6620],{"class":2235},[2225,98490,2311],{"class":2235},[2225,98492,2236],{"class":2235},[2225,98494,6614],{"class":2235},[2225,98496,98497],{"class":6527},"theme",[2225,98499,6620],{"class":2235},[2225,98501,2311],{"class":2235},[2225,98503,6614],{"class":2235},[2225,98505,98015],{"class":2252},[2225,98507,6620],{"class":2235},[2225,98509,6369],{"class":2235},[2225,98511,98512],{"class":2227,"line":2356},[2225,98513,19017],{"class":2235},[2225,98515,98516,98518,98520,98522,98524,98526],{"class":2227,"line":2830},[2225,98517,9368],{"class":2235},[2225,98519,1700],{"class":2774},[2225,98521,6620],{"class":2235},[2225,98523,2311],{"class":2235},[2225,98525,18961],{"class":6527},[2225,98527,1707],{"class":2235},[2225,98529,98530,98532,98534,98536,98538,98540,98543],{"class":2227,"line":2861},[2225,98531,9368],{"class":2235},[2225,98533,1703],{"class":2774},[2225,98535,6620],{"class":2235},[2225,98537,2311],{"class":2235},[2225,98539,6614],{"class":2235},[2225,98541,98542],{"class":2252},"2026-07-08T09:00:00.000Z",[2225,98544,9449],{"class":2235},[2225,98546,98547],{"class":2227,"line":4246},[2225,98548,7351],{"class":2235},[937,98550,98551,1324,98554,98557,98558,98560],{},[941,98552,98553],{},"setMe",[950,98555,98556],{},"replaces"," instead of merging — use it sparingly; ",[941,98559,98160],{}," is the canonical mutation:",[2217,98562,98564],{"className":2219,"code":98563,"language":2221,"meta":1037,"style":1037},"await vault.user.setMe\u003CUserShape>({\n  profile: { displayName: 'Vicio2' },\n})\n\nconst replaced = await vault.user.me\u003CUserShape>()\nconsole.log(replaced?.data.preferences) \u002F\u002F → undefined (setMe replaced everything)\n",[941,98565,98566,98590,98611,98617,98621,98650],{"__ignoreMap":1037},[2225,98567,98568,98570,98572,98574,98576,98578,98580,98582,98584,98586,98588],{"class":2227,"line":2228},[2225,98569,6500],{"class":2231},[2225,98571,6466],{"class":2239},[2225,98573,1520],{"class":2235},[2225,98575,16225],{"class":2239},[2225,98577,1520],{"class":2235},[2225,98579,98553],{"class":2291},[2225,98581,6474],{"class":2235},[2225,98583,98165],{"class":6342},[2225,98585,6480],{"class":2235},[2225,98587,2789],{"class":2239},[2225,98589,4200],{"class":2235},[2225,98591,98592,98594,98596,98598,98600,98602,98604,98607,98609],{"class":2227,"line":1041},[2225,98593,97941],{"class":2307},[2225,98595,2311],{"class":2235},[2225,98597,2236],{"class":2235},[2225,98599,75153],{"class":2307},[2225,98601,2311],{"class":2235},[2225,98603,2249],{"class":2235},[2225,98605,98606],{"class":2252},"Vicio2",[2225,98608,2805],{"class":2235},[2225,98610,2353],{"class":2235},[2225,98612,98613,98615],{"class":2227,"line":1038},[2225,98614,2359],{"class":2235},[2225,98616,2362],{"class":2239},[2225,98618,98619],{"class":2227,"line":2288},[2225,98620,2285],{"emptyLinePlaceholder":2284},[2225,98622,98623,98625,98628,98630,98632,98634,98636,98638,98640,98642,98644,98646,98648],{"class":2227,"line":2304},[2225,98624,2775],{"class":2774},[2225,98626,98627],{"class":2239}," replaced ",[2225,98629,2781],{"class":2235},[2225,98631,2784],{"class":2231},[2225,98633,6466],{"class":2239},[2225,98635,1520],{"class":2235},[2225,98637,16225],{"class":2239},[2225,98639,1520],{"class":2235},[2225,98641,4212],{"class":2291},[2225,98643,6474],{"class":2235},[2225,98645,98165],{"class":6342},[2225,98647,6480],{"class":2235},[2225,98649,8146],{"class":2239},[2225,98651,98652,98654,98656,98658,98661,98663,98665,98667,98670],{"class":2227,"line":2356},[2225,98653,28031],{"class":2239},[2225,98655,1520],{"class":2235},[2225,98657,27100],{"class":2291},[2225,98659,98660],{"class":2239},"(replaced",[2225,98662,8457],{"class":2235},[2225,98664,8673],{"class":2239},[2225,98666,1520],{"class":2235},[2225,98668,98669],{"class":2239},"preferences) ",[2225,98671,98672],{"class":2763},"\u002F\u002F → undefined (setMe replaced everything)\n",[937,98674,98675,98676,98679,98680,98682],{},"Writes go through the ",[941,98677,98678],{},"edit-own-profile"," policy gate; reads of your own envelope are never gated. See ",[1007,98681,884],{"href":97746}," for the gate defaults.",[1022,98684,98686],{"id":98685},"admin-pre-fill-at-grant-time","Admin pre-fill at grant time",[937,98688,98689,98690,98692,98693,98696],{},"When you invite a teammate with ",[941,98691,21197],{},", you can seed their envelope via ",[941,98694,98695],{},"initialProfile"," so their first sign-in already shows a name and sensible defaults:",[2217,98698,98700],{"className":2219,"code":98699,"language":2221,"meta":1037,"style":1037},"await db.grant('demo', {\n  userId: 'bob',\n  displayName: 'Bob',\n  role: 'viewer',\n  secret: 'bob-pass-2026-strong',\n  initialProfile: {\n    profile: { displayName: 'Bob the Auditor', locale: 'fr-FR' },\n    preferences: { theme: 'dark' },\n  } satisfies UserShape,\n})\n",[941,98701,98702,98724,98738,98752,98766,98781,98790,98825,98846,98857],{"__ignoreMap":1037},[2225,98703,98704,98706,98708,98710,98712,98714,98716,98718,98720,98722],{"class":2227,"line":2228},[2225,98705,6500],{"class":2231},[2225,98707,2842],{"class":2239},[2225,98709,1520],{"class":2235},[2225,98711,10493],{"class":2291},[2225,98713,2789],{"class":2239},[2225,98715,2805],{"class":2235},[2225,98717,97914],{"class":2252},[2225,98719,2805],{"class":2235},[2225,98721,2298],{"class":2235},[2225,98723,2301],{"class":2235},[2225,98725,98726,98728,98730,98732,98734,98736],{"class":2227,"line":1041},[2225,98727,10510],{"class":2307},[2225,98729,2311],{"class":2235},[2225,98731,2249],{"class":2235},[2225,98733,91596],{"class":2252},[2225,98735,2805],{"class":2235},[2225,98737,1707],{"class":2235},[2225,98739,98740,98742,98744,98746,98748,98750],{"class":2227,"line":1038},[2225,98741,10526],{"class":2307},[2225,98743,2311],{"class":2235},[2225,98745,2249],{"class":2235},[2225,98747,91611],{"class":2252},[2225,98749,2805],{"class":2235},[2225,98751,1707],{"class":2235},[2225,98753,98754,98756,98758,98760,98762,98764],{"class":2227,"line":2288},[2225,98755,10542],{"class":2307},[2225,98757,2311],{"class":2235},[2225,98759,2249],{"class":2235},[2225,98761,9910],{"class":2252},[2225,98763,2805],{"class":2235},[2225,98765,1707],{"class":2235},[2225,98767,98768,98770,98772,98774,98777,98779],{"class":2227,"line":2304},[2225,98769,10423],{"class":2307},[2225,98771,2311],{"class":2235},[2225,98773,2249],{"class":2235},[2225,98775,98776],{"class":2252},"bob-pass-2026-strong",[2225,98778,2805],{"class":2235},[2225,98780,1707],{"class":2235},[2225,98782,98783,98786,98788],{"class":2227,"line":2356},[2225,98784,98785],{"class":2307},"  initialProfile",[2225,98787,2311],{"class":2235},[2225,98789,2301],{"class":2235},[2225,98791,98792,98795,98797,98799,98801,98803,98805,98808,98810,98812,98814,98816,98818,98821,98823],{"class":2227,"line":2830},[2225,98793,98794],{"class":2307},"    profile",[2225,98796,2311],{"class":2235},[2225,98798,2236],{"class":2235},[2225,98800,75153],{"class":2307},[2225,98802,2311],{"class":2235},[2225,98804,2249],{"class":2235},[2225,98806,98807],{"class":2252},"Bob the Auditor",[2225,98809,2805],{"class":2235},[2225,98811,2298],{"class":2235},[2225,98813,24003],{"class":2307},[2225,98815,2311],{"class":2235},[2225,98817,2249],{"class":2235},[2225,98819,98820],{"class":2252},"fr-FR",[2225,98822,2805],{"class":2235},[2225,98824,2353],{"class":2235},[2225,98826,98827,98830,98832,98834,98836,98838,98840,98842,98844],{"class":2227,"line":2861},[2225,98828,98829],{"class":2307},"    preferences",[2225,98831,2311],{"class":2235},[2225,98833,2236],{"class":2235},[2225,98835,98216],{"class":2307},[2225,98837,2311],{"class":2235},[2225,98839,2249],{"class":2235},[2225,98841,98024],{"class":2252},[2225,98843,2805],{"class":2235},[2225,98845,2353],{"class":2235},[2225,98847,98848,98850,98853,98855],{"class":2227,"line":4246},[2225,98849,19143],{"class":2235},[2225,98851,98852],{"class":2231}," satisfies",[2225,98854,97934],{"class":6342},[2225,98856,1707],{"class":2235},[2225,98858,98859,98861],{"class":2227,"line":4253},[2225,98860,2359],{"class":2235},[2225,98862,2362],{"class":2239},[937,98864,98865,98866,98869],{},"This is ",[950,98867,98868],{},"bootstrap-only",": once Bob activates and writes his own envelope, the own-only write rule prevents further admin edits. Admins cannot edit teammate profiles after activation — that's structural (no such method exists), not policy-controlled.",[1022,98871,98873],{"id":98872},"reading-teammates-profiles","Reading teammates' profiles",[937,98875,98876,98877,98880],{},"Cross-user reads go through the ",[941,98878,98879],{},"view-team-profiles"," policy gate (any authenticated session passes by default):",[2217,98882,98884],{"className":2219,"code":98883,"language":2221,"meta":1037,"style":1037},"\u002F\u002F Read one teammate by keyringId.\nconst bob = await vault.user.get\u003CUserShape>('bob')\nconsole.log(bob?.data.profile?.displayName) \u002F\u002F → 'Bob the Auditor'\n\n\u002F\u002F List every persisted envelope in the vault (order is store-defined).\nconst everyone = await vault.user.list\u003CUserShape>()\nconsole.log(everyone.map((e) => e.keyringId)) \u002F\u002F → ['vicio', 'bob']\n",[941,98885,98886,98891,98928,98954,98958,98963,98992],{"__ignoreMap":1037},[2225,98887,98888],{"class":2227,"line":2228},[2225,98889,98890],{"class":2763},"\u002F\u002F Read one teammate by keyringId.\n",[2225,98892,98893,98895,98898,98900,98902,98904,98906,98908,98910,98912,98914,98916,98918,98920,98922,98924,98926],{"class":2227,"line":1041},[2225,98894,2775],{"class":2774},[2225,98896,98897],{"class":2239}," bob ",[2225,98899,2781],{"class":2235},[2225,98901,2784],{"class":2231},[2225,98903,6466],{"class":2239},[2225,98905,1520],{"class":2235},[2225,98907,16225],{"class":2239},[2225,98909,1520],{"class":2235},[2225,98911,3158],{"class":2291},[2225,98913,6474],{"class":2235},[2225,98915,98165],{"class":6342},[2225,98917,6480],{"class":2235},[2225,98919,2789],{"class":2239},[2225,98921,2805],{"class":2235},[2225,98923,91596],{"class":2252},[2225,98925,2805],{"class":2235},[2225,98927,2362],{"class":2239},[2225,98929,98930,98932,98934,98936,98939,98941,98943,98945,98947,98949,98951],{"class":2227,"line":1038},[2225,98931,28031],{"class":2239},[2225,98933,1520],{"class":2235},[2225,98935,27100],{"class":2291},[2225,98937,98938],{"class":2239},"(bob",[2225,98940,8457],{"class":2235},[2225,98942,8673],{"class":2239},[2225,98944,1520],{"class":2235},[2225,98946,19861],{"class":2239},[2225,98948,8457],{"class":2235},[2225,98950,98361],{"class":2239},[2225,98952,98953],{"class":2763},"\u002F\u002F → 'Bob the Auditor'\n",[2225,98955,98956],{"class":2227,"line":2288},[2225,98957,2285],{"emptyLinePlaceholder":2284},[2225,98959,98960],{"class":2227,"line":2304},[2225,98961,98962],{"class":2763},"\u002F\u002F List every persisted envelope in the vault (order is store-defined).\n",[2225,98964,98965,98967,98970,98972,98974,98976,98978,98980,98982,98984,98986,98988,98990],{"class":2227,"line":2356},[2225,98966,2775],{"class":2774},[2225,98968,98969],{"class":2239}," everyone ",[2225,98971,2781],{"class":2235},[2225,98973,2784],{"class":2231},[2225,98975,6466],{"class":2239},[2225,98977,1520],{"class":2235},[2225,98979,16225],{"class":2239},[2225,98981,1520],{"class":2235},[2225,98983,3167],{"class":2291},[2225,98985,6474],{"class":2235},[2225,98987,98165],{"class":6342},[2225,98989,6480],{"class":2235},[2225,98991,8146],{"class":2239},[2225,98993,98994,98996,98998,99000,99003,99005,99007,99009,99011,99013,99015,99017,99019,99021,99024],{"class":2227,"line":2830},[2225,98995,28031],{"class":2239},[2225,98997,1520],{"class":2235},[2225,98999,27100],{"class":2291},[2225,99001,99002],{"class":2239},"(everyone",[2225,99004,1520],{"class":2235},[2225,99006,8244],{"class":2291},[2225,99008,2789],{"class":2239},[2225,99010,2789],{"class":2235},[2225,99012,12450],{"class":7052},[2225,99014,1381],{"class":2235},[2225,99016,8116],{"class":2774},[2225,99018,12479],{"class":2239},[2225,99020,1520],{"class":2235},[2225,99022,99023],{"class":2239},"keyringId)) ",[2225,99025,99026],{"class":2763},"\u002F\u002F → ['vicio', 'bob']\n",[937,99028,99029,99030,968,99033,99035,99036,99039,99040,99042],{},"Two privacy behaviors worth knowing: with the gate configured as ",[941,99031,99032],{},"enabled: false",[941,99034,24407],{}," silently returns only your own envelope (",[941,99037,99038],{},"[me]","), while an insufficient session tier throws ",[941,99041,13025],{}," — the first is a deliberate \"nobody sees teammate profiles in this app\" choice, the second means \"authenticate further\".",[1022,99044,99046],{"id":99045},"device-local-state-keep-it-out-of-the-vault","Device-local state — keep it OUT of the vault",[937,99048,99049,99050,99053,99054,99056,99057,99059],{},"Anything stored in the vault syncs across devices. Device-local state (last-opened-collection id, table column widths, scroll position, \"don't show this onboarding tip again\") is by definition ",[950,99051,99052],{},"not cross-device"," — keep it in ",[941,99055,41955],{}," at the app layer, ",[950,99058,1004],{}," in the user envelope.",[2217,99061,99063],{"className":2219,"code":99062,"language":2221,"meta":1037,"style":1037},"\u002F\u002F Cross-device state → vault.user.*\nawait vault.user.updateMe\u003CUserShape>({ preferences: { theme: 'dark' } })\n\n\u002F\u002F This-device state → localStorage\nwindow.localStorage.setItem('lastOpenedCollectionId', 'invoices')\n",[941,99064,99065,99070,99117,99121,99126],{"__ignoreMap":1037},[2225,99066,99067],{"class":2227,"line":2228},[2225,99068,99069],{"class":2763},"\u002F\u002F Cross-device state → vault.user.*\n",[2225,99071,99072,99074,99076,99078,99080,99082,99084,99086,99088,99090,99092,99094,99097,99099,99101,99103,99105,99107,99109,99111,99113,99115],{"class":2227,"line":1041},[2225,99073,6500],{"class":2231},[2225,99075,6466],{"class":2239},[2225,99077,1520],{"class":2235},[2225,99079,16225],{"class":2239},[2225,99081,1520],{"class":2235},[2225,99083,98160],{"class":2291},[2225,99085,6474],{"class":2235},[2225,99087,98165],{"class":6342},[2225,99089,6480],{"class":2235},[2225,99091,2789],{"class":2239},[2225,99093,2792],{"class":2235},[2225,99095,99096],{"class":2307}," preferences",[2225,99098,2311],{"class":2235},[2225,99100,2236],{"class":2235},[2225,99102,98216],{"class":2307},[2225,99104,2311],{"class":2235},[2225,99106,2249],{"class":2235},[2225,99108,98024],{"class":2252},[2225,99110,2805],{"class":2235},[2225,99112,2243],{"class":2235},[2225,99114,2243],{"class":2235},[2225,99116,2362],{"class":2239},[2225,99118,99119],{"class":2227,"line":1038},[2225,99120,2285],{"emptyLinePlaceholder":2284},[2225,99122,99123],{"class":2227,"line":2288},[2225,99124,99125],{"class":2763},"\u002F\u002F This-device state → localStorage\n",[2225,99127,99128,99130,99132,99134,99136,99138,99140,99142,99145,99147,99149,99151,99153,99155],{"class":2227,"line":2304},[2225,99129,12429],{"class":2239},[2225,99131,1520],{"class":2235},[2225,99133,41955],{"class":2239},[2225,99135,1520],{"class":2235},[2225,99137,96807],{"class":2291},[2225,99139,2789],{"class":2239},[2225,99141,2805],{"class":2235},[2225,99143,99144],{"class":2252},"lastOpenedCollectionId",[2225,99146,2805],{"class":2235},[2225,99148,2298],{"class":2235},[2225,99150,2249],{"class":2235},[2225,99152,6487],{"class":2252},[2225,99154,2805],{"class":2235},[2225,99156,2362],{"class":2239},[937,99158,99159],{},"Mixing the two in one envelope creates surprises (\"why did my column widths sync to my phone?\").",[1022,99161,99163],{"id":99162},"reactive-bindings-vue-react-svelte","Reactive bindings (Vue \u002F React \u002F Svelte)",[937,99165,99166,99169],{},[941,99167,99168],{},"vault.user.live(keyringId)"," returns a reactive handle that fires on every local write; wire it into your framework's state primitive:",[2217,99171,99173],{"className":2219,"code":99172,"language":2221,"meta":1037,"style":1037},"\u002F\u002F Vue 3 composable (sketch)\nimport { ref, onUnmounted } from 'vue'\nimport { vault } from '.\u002Fvault'          \u002F\u002F your app's vault module\nimport type { UserShape } from '.\u002Fuser-shape'\n\nexport function useUserEnvelope() {\n  const me = ref\u003CUserShape | null>(null)\n  const live = vault.user.live\u003CUserShape>(vault.userId)\n  \u002F\u002F Initial fetch\n  vault.user.me\u003CUserShape>().then((env) => { me.value = env?.data ?? null })\n  \u002F\u002F Live updates on local writes\n  const unsub = live.subscribe((env) => { me.value = env?.data ?? null })\n  onUnmounted(() => { live.stop(); unsub() })\n  return { me }\n}\n",[941,99174,99175,99180,99203,99225,99246,99250,99263,99290,99325,99330,99390,99395,99444,99475,99485],{"__ignoreMap":1037},[2225,99176,99177],{"class":2227,"line":2228},[2225,99178,99179],{"class":2763},"\u002F\u002F Vue 3 composable (sketch)\n",[2225,99181,99182,99184,99186,99188,99190,99193,99195,99197,99199,99201],{"class":2227,"line":1041},[2225,99183,2232],{"class":2231},[2225,99185,2236],{"class":2235},[2225,99187,62303],{"class":2239},[2225,99189,2298],{"class":2235},[2225,99191,99192],{"class":2239}," onUnmounted",[2225,99194,2243],{"class":2235},[2225,99196,2246],{"class":2231},[2225,99198,2249],{"class":2235},[2225,99200,283],{"class":2252},[2225,99202,2256],{"class":2235},[2225,99204,99205,99207,99209,99211,99213,99215,99217,99220,99222],{"class":2227,"line":1038},[2225,99206,2232],{"class":2231},[2225,99208,2236],{"class":2235},[2225,99210,6466],{"class":2239},[2225,99212,2243],{"class":2235},[2225,99214,2246],{"class":2231},[2225,99216,2249],{"class":2235},[2225,99218,99219],{"class":2252},".\u002Fvault",[2225,99221,2805],{"class":2235},[2225,99223,99224],{"class":2763},"          \u002F\u002F your app's vault module\n",[2225,99226,99227,99229,99231,99233,99235,99237,99239,99241,99244],{"class":2227,"line":2288},[2225,99228,2232],{"class":2231},[2225,99230,3399],{"class":2231},[2225,99232,2236],{"class":2235},[2225,99234,97934],{"class":2239},[2225,99236,2243],{"class":2235},[2225,99238,2246],{"class":2231},[2225,99240,2249],{"class":2235},[2225,99242,99243],{"class":2252},".\u002Fuser-shape",[2225,99245,2256],{"class":2235},[2225,99247,99248],{"class":2227,"line":2304},[2225,99249,2285],{"emptyLinePlaceholder":2284},[2225,99251,99252,99254,99256,99259,99261],{"class":2227,"line":2356},[2225,99253,8080],{"class":2231},[2225,99255,40856],{"class":2774},[2225,99257,99258],{"class":2291}," useUserEnvelope",[2225,99260,4228],{"class":2235},[2225,99262,2301],{"class":2235},[2225,99264,99265,99267,99270,99272,99274,99276,99278,99280,99282,99284,99286,99288],{"class":2227,"line":2830},[2225,99266,8123],{"class":2774},[2225,99268,99269],{"class":2239}," me",[2225,99271,6346],{"class":2235},[2225,99273,62303],{"class":2291},[2225,99275,6474],{"class":2235},[2225,99277,98165],{"class":6342},[2225,99279,7087],{"class":2235},[2225,99281,7090],{"class":6342},[2225,99283,6480],{"class":2235},[2225,99285,2789],{"class":2307},[2225,99287,6651],{"class":2235},[2225,99289,2362],{"class":2307},[2225,99291,99292,99294,99297,99299,99301,99303,99305,99307,99309,99311,99313,99315,99317,99319,99321,99323],{"class":2227,"line":2861},[2225,99293,8123],{"class":2774},[2225,99295,99296],{"class":2239}," live",[2225,99298,6346],{"class":2235},[2225,99300,6466],{"class":2239},[2225,99302,1520],{"class":2235},[2225,99304,16225],{"class":2239},[2225,99306,1520],{"class":2235},[2225,99308,727],{"class":2291},[2225,99310,6474],{"class":2235},[2225,99312,98165],{"class":6342},[2225,99314,6480],{"class":2235},[2225,99316,2789],{"class":2307},[2225,99318,2694],{"class":2239},[2225,99320,1520],{"class":2235},[2225,99322,10749],{"class":2239},[2225,99324,2362],{"class":2307},[2225,99326,99327],{"class":2227,"line":4246},[2225,99328,99329],{"class":2763},"  \u002F\u002F Initial fetch\n",[2225,99331,99332,99334,99336,99338,99340,99342,99344,99346,99348,99350,99352,99355,99357,99359,99361,99363,99365,99367,99369,99371,99373,99375,99378,99380,99382,99384,99386,99388],{"class":2227,"line":4253},[2225,99333,36933],{"class":2239},[2225,99335,1520],{"class":2235},[2225,99337,16225],{"class":2239},[2225,99339,1520],{"class":2235},[2225,99341,4212],{"class":2291},[2225,99343,6474],{"class":2235},[2225,99345,98165],{"class":6342},[2225,99347,6480],{"class":2235},[2225,99349,4228],{"class":2307},[2225,99351,1520],{"class":2235},[2225,99353,99354],{"class":2291},"then",[2225,99356,2789],{"class":2307},[2225,99358,2789],{"class":2235},[2225,99360,183],{"class":7052},[2225,99362,1381],{"class":2235},[2225,99364,8116],{"class":2774},[2225,99366,2236],{"class":2235},[2225,99368,99269],{"class":2239},[2225,99370,1520],{"class":2235},[2225,99372,25201],{"class":2239},[2225,99374,6346],{"class":2235},[2225,99376,99377],{"class":2239}," env",[2225,99379,8457],{"class":2235},[2225,99381,8673],{"class":2239},[2225,99383,8239],{"class":2235},[2225,99385,7090],{"class":2235},[2225,99387,2243],{"class":2235},[2225,99389,2362],{"class":2307},[2225,99391,99392],{"class":2227,"line":4259},[2225,99393,99394],{"class":2763},"  \u002F\u002F Live updates on local writes\n",[2225,99396,99397,99399,99402,99404,99406,99408,99410,99412,99414,99416,99418,99420,99422,99424,99426,99428,99430,99432,99434,99436,99438,99440,99442],{"class":2227,"line":6548},[2225,99398,8123],{"class":2774},[2225,99400,99401],{"class":2239}," unsub",[2225,99403,6346],{"class":2235},[2225,99405,99296],{"class":2239},[2225,99407,1520],{"class":2235},[2225,99409,25042],{"class":2291},[2225,99411,2789],{"class":2307},[2225,99413,2789],{"class":2235},[2225,99415,183],{"class":7052},[2225,99417,1381],{"class":2235},[2225,99419,8116],{"class":2774},[2225,99421,2236],{"class":2235},[2225,99423,99269],{"class":2239},[2225,99425,1520],{"class":2235},[2225,99427,25201],{"class":2239},[2225,99429,6346],{"class":2235},[2225,99431,99377],{"class":2239},[2225,99433,8457],{"class":2235},[2225,99435,8673],{"class":2239},[2225,99437,8239],{"class":2235},[2225,99439,7090],{"class":2235},[2225,99441,2243],{"class":2235},[2225,99443,2362],{"class":2307},[2225,99445,99446,99449,99451,99453,99455,99457,99459,99461,99463,99465,99467,99469,99471,99473],{"class":2227,"line":6576},[2225,99447,99448],{"class":2291},"  onUnmounted",[2225,99450,2789],{"class":2307},[2225,99452,4228],{"class":2235},[2225,99454,8116],{"class":2774},[2225,99456,2236],{"class":2235},[2225,99458,99296],{"class":2239},[2225,99460,1520],{"class":2235},[2225,99462,27604],{"class":2291},[2225,99464,4228],{"class":2307},[2225,99466,3769],{"class":2235},[2225,99468,99401],{"class":2291},[2225,99470,19132],{"class":2307},[2225,99472,2359],{"class":2235},[2225,99474,2362],{"class":2307},[2225,99476,99477,99479,99481,99483],{"class":2227,"line":6582},[2225,99478,8220],{"class":2231},[2225,99480,2236],{"class":2235},[2225,99482,99269],{"class":2239},[2225,99484,6369],{"class":2235},[2225,99486,99487],{"class":2227,"line":6587},[2225,99488,7351],{"class":2235},[937,99490,99491],{},"Subscribers fire on local writes; changes made on another device arrive through the team\u002Fsync engine and surface when the sync layer replays the write through this API.",[1022,99493,99495],{"id":99494},"presence-integration","Presence integration",[937,99497,99498,99499,99501,99502,28806,99505,17904,99507,1520],{},"When announcing presence on a collection (who is online, who is editing what), the app sources ",[941,99500,16791],{}," from its own user envelope and includes it in the presence payload. Presence requires the sync strategy: ",[941,99503,99504],{},"createNoydb({ syncStrategy: withSync(), … })",[941,99506,87840],{},[941,99508,4672],{},[2217,99510,99512],{"className":2219,"code":99511,"language":2221,"meta":1037,"style":1037},"const invoices = vault.collection('invoices')\nconst presence = invoices.presence\u003C{ displayName?: string; editingRecordId?: string }>()\n\nconst me = await vault.user.me\u003CUserShape>()\nawait presence.update({\n  displayName: me?.data.profile?.displayName,\n  editingRecordId: 'invoice-42',\n})\n\n\u002F\u002F Subscribers receive every *other* active peer — your own session is\n\u002F\u002F excluded, and stale entries (no update for 30 s by default) are filtered.\npresence.subscribe((peers) => {\n  \u002F\u002F peers: Array\u003C{ userId, payload, lastSeen }>\n  console.log(peers.map((p) => p.payload.displayName))\n  \u002F\u002F → ['Vicio2']  (as seen from Bob's session; Vicio's own session sees [])\n})\n",[941,99513,99514,99538,99573,99577,99605,99619,99641,99657,99663,99667,99672,99677,99697,99702,99741,99746],{"__ignoreMap":1037},[2225,99515,99516,99518,99520,99522,99524,99526,99528,99530,99532,99534,99536],{"class":2227,"line":2228},[2225,99517,2775],{"class":2774},[2225,99519,6461],{"class":2239},[2225,99521,2781],{"class":2235},[2225,99523,6466],{"class":2239},[2225,99525,1520],{"class":2235},[2225,99527,6471],{"class":2291},[2225,99529,2789],{"class":2239},[2225,99531,2805],{"class":2235},[2225,99533,6487],{"class":2252},[2225,99535,2805],{"class":2235},[2225,99537,2362],{"class":2239},[2225,99539,99540,99542,99544,99546,99548,99550,99552,99554,99556,99558,99560,99562,99565,99567,99569,99571],{"class":2227,"line":1041},[2225,99541,2775],{"class":2774},[2225,99543,91265],{"class":2239},[2225,99545,2781],{"class":2235},[2225,99547,6503],{"class":2239},[2225,99549,1520],{"class":2235},[2225,99551,91292],{"class":2291},[2225,99553,17851],{"class":2235},[2225,99555,75153],{"class":2307},[2225,99557,7011],{"class":2235},[2225,99559,6366],{"class":6342},[2225,99561,3769],{"class":2235},[2225,99563,99564],{"class":2307}," editingRecordId",[2225,99566,7011],{"class":2235},[2225,99568,6366],{"class":6342},[2225,99570,85293],{"class":2235},[2225,99572,8146],{"class":2239},[2225,99574,99575],{"class":2227,"line":1038},[2225,99576,2285],{"emptyLinePlaceholder":2284},[2225,99578,99579,99581,99583,99585,99587,99589,99591,99593,99595,99597,99599,99601,99603],{"class":2227,"line":2288},[2225,99580,2775],{"class":2774},[2225,99582,98315],{"class":2239},[2225,99584,2781],{"class":2235},[2225,99586,2784],{"class":2231},[2225,99588,6466],{"class":2239},[2225,99590,1520],{"class":2235},[2225,99592,16225],{"class":2239},[2225,99594,1520],{"class":2235},[2225,99596,4212],{"class":2291},[2225,99598,6474],{"class":2235},[2225,99600,98165],{"class":6342},[2225,99602,6480],{"class":2235},[2225,99604,8146],{"class":2239},[2225,99606,99607,99609,99611,99613,99615,99617],{"class":2227,"line":2304},[2225,99608,6500],{"class":2231},[2225,99610,91339],{"class":2239},[2225,99612,1520],{"class":2235},[2225,99614,39210],{"class":2291},[2225,99616,2789],{"class":2239},[2225,99618,4200],{"class":2235},[2225,99620,99621,99623,99625,99627,99629,99631,99633,99635,99637,99639],{"class":2227,"line":2356},[2225,99622,10526],{"class":2307},[2225,99624,2311],{"class":2235},[2225,99626,99269],{"class":2239},[2225,99628,8457],{"class":2235},[2225,99630,8673],{"class":2239},[2225,99632,1520],{"class":2235},[2225,99634,19861],{"class":2239},[2225,99636,8457],{"class":2235},[2225,99638,16791],{"class":2239},[2225,99640,1707],{"class":2235},[2225,99642,99643,99646,99648,99650,99653,99655],{"class":2227,"line":2830},[2225,99644,99645],{"class":2307},"  editingRecordId",[2225,99647,2311],{"class":2235},[2225,99649,2249],{"class":2235},[2225,99651,99652],{"class":2252},"invoice-42",[2225,99654,2805],{"class":2235},[2225,99656,1707],{"class":2235},[2225,99658,99659,99661],{"class":2227,"line":2861},[2225,99660,2359],{"class":2235},[2225,99662,2362],{"class":2239},[2225,99664,99665],{"class":2227,"line":4246},[2225,99666,2285],{"emptyLinePlaceholder":2284},[2225,99668,99669],{"class":2227,"line":4253},[2225,99670,99671],{"class":2763},"\u002F\u002F Subscribers receive every *other* active peer — your own session is\n",[2225,99673,99674],{"class":2227,"line":4259},[2225,99675,99676],{"class":2763},"\u002F\u002F excluded, and stale entries (no update for 30 s by default) are filtered.\n",[2225,99678,99679,99681,99683,99685,99687,99689,99691,99693,99695],{"class":2227,"line":6548},[2225,99680,91292],{"class":2239},[2225,99682,1520],{"class":2235},[2225,99684,25042],{"class":2291},[2225,99686,2789],{"class":2239},[2225,99688,2789],{"class":2235},[2225,99690,91410],{"class":7052},[2225,99692,1381],{"class":2235},[2225,99694,8116],{"class":2774},[2225,99696,2301],{"class":2235},[2225,99698,99699],{"class":2227,"line":6576},[2225,99700,99701],{"class":2763},"  \u002F\u002F peers: Array\u003C{ userId, payload, lastSeen }>\n",[2225,99703,99704,99706,99708,99710,99712,99714,99716,99718,99720,99722,99724,99726,99728,99731,99733,99735,99737,99739],{"class":2227,"line":6582},[2225,99705,27095],{"class":2239},[2225,99707,1520],{"class":2235},[2225,99709,27100],{"class":2291},[2225,99711,2789],{"class":2307},[2225,99713,91410],{"class":2239},[2225,99715,1520],{"class":2235},[2225,99717,8244],{"class":2291},[2225,99719,2789],{"class":2307},[2225,99721,2789],{"class":2235},[2225,99723,937],{"class":7052},[2225,99725,1381],{"class":2235},[2225,99727,8116],{"class":2774},[2225,99729,99730],{"class":2239}," p",[2225,99732,1520],{"class":2235},[2225,99734,66738],{"class":2239},[2225,99736,1520],{"class":2235},[2225,99738,16791],{"class":2239},[2225,99740,8606],{"class":2307},[2225,99742,99743],{"class":2227,"line":6587},[2225,99744,99745],{"class":2763},"  \u002F\u002F → ['Vicio2']  (as seen from Bob's session; Vicio's own session sees [])\n",[2225,99747,99748,99750],{"class":2227,"line":7174},[2225,99749,2359],{"class":2235},[2225,99751,2362],{"class":2239},[937,99753,99754,99755,99757],{},"The hub stays payload-agnostic — it does ",[950,99756,1004],{}," introspect the user envelope to populate presence; your app decides what to broadcast.",[1022,99759,2621],{"id":2620},[1111,99761,99762,99767,99772],{},[1114,99763,99764,99766],{},[1007,99765,884],{"href":97746}," — full service reference (API, policy gates, lifecycle, storage shape)",[1114,99768,99769,99771],{},[1007,99770,868],{"href":10333}," — grant\u002Frevoke flows the envelope lifecycle binds to",[1114,99773,99774,99776],{},[1007,99775,864],{"href":92253}," — cross-device propagation and presence",[2645,99778,99779],{},"html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}html pre.shiki code .sHdIc, html code.shiki .sHdIc{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#EEFFFF;--shiki-default-font-style:italic;--shiki-dark:#BABED8;--shiki-dark-font-style:italic}",{"title":1037,"searchDepth":1038,"depth":1038,"links":99781},[99782,99783,99784,99785,99786,99787,99788,99789,99790,99791],{"id":97749,"depth":1041,"text":97750},{"id":58393,"depth":1041,"text":58394},{"id":97921,"depth":1041,"text":97922},{"id":98125,"depth":1041,"text":98126},{"id":98685,"depth":1041,"text":98686},{"id":98872,"depth":1041,"text":98873},{"id":99045,"depth":1041,"text":99046},{"id":99162,"depth":1041,"text":99163},{"id":99494,"depth":1041,"text":99495},{"id":2620,"depth":1041,"text":2621},"Give each user of a shared vault an encrypted profile-and-preferences document — own-only writes, admin pre-fill, cross-user reads, live bindings.",{},{"title":593,"description":99792},"Iery2rFFvHhEk8pNM3oOzHp3Rd_llTzhghEYufDsUrk",{"id":99797,"title":605,"access":12,"apiToc":929,"body":99798,"category":1042,"description":101612,"extension":1044,"meta":101613,"navigation":1042,"nest":1047,"path":606,"seo":101614,"sinceVersion":1042,"status":1049,"stem":607,"subtitle":1042,"tier":1050,"tocExternal":1042,"__hash__":101615},"docs\u002Fservices\u002Farchive.md",{"type":931,"value":99799,"toc":101591},[99800,99802,99805,99807,99814,99844,99846,99861,99865,99873,100277,100286,100288,100292,100306,100574,100682,100685,100744,100754,100823,100827,100830,100895,100899,100905,101010,101013,101019,101044,101052,101080,101086,101096,101141,101147,101177,101180,101223,101228,101302,101307,101336,101342,101348,101393,101399,101403,101422,101426,101449,101451,101547,101549,101588],[934,99801,605],{"id":605},[46871,99803],{":rows":99804},"[{\"label\":\"Subpath\",\"value\":\"exported from `@noy-db\u002Fhub` root (no dedicated subpath)\"},{\"label\":\"Factory\",\"value\":\"`withArchive()`\"},{\"label\":\"Layer\",\"value\":\"`with-fork`\"}]",[1022,99806,29857],{"id":29856},[937,99808,99809,99810,99813],{},"Over years of use, a vault accumulates records you almost never read — closed fiscal years,\nsettled invoices, expired contracts. The archive service moves those records out of your\nprimary store into a second, cheaper ",[950,99811,99812],{},"cold store",", so the working set stays small while\nnothing is deleted.",[937,99815,99816,99817,99819,99820,99822,99823,99826,99827,99829,99830,99832,99833,99835,99836,99839,99840,99843],{},"In noy-db, every record is stored as an ",[950,99818,6955],{}," — an opaque ciphertext blob\nproduced inside the hub before anything reaches a store (see\n",[1007,99821,1516],{"href":66},"). Archival relocates that envelope ",[950,99824,99825],{},"as-is",":\nno re-encryption, no plaintext on the wire, and the cold store — like any\n",[1007,99828,6951],{"href":51}," — never sees your data. You declare ",[1092,99831,53557],{}," records are eligible\nwith a per-collection ",[941,99834,605],{}," policy (typically a business-date test like \"year ≤ 2022\"),\nand an optional ",[941,99837,99838],{},"legalHold"," predicate that retains a record even when it is eligible\n(litigation or audit hold). Archived records disappear from normal reads until you\n",[941,99841,99842],{},"restore()"," them.",[1022,99845,70093],{"id":70092},[1111,99847,99848],{},[1114,99849,99850,964,99853,99855,99856,99858,99859,957],{},[950,99851,99852],{},"A cold target store",[941,99854,21396],{}," takes a second store the archived envelopes move to; any ",[941,99857,3154],{}," works (see ",[1007,99860,6891],{"href":51},[1022,99862,99864],{"id":99863},"opt-in","Opt in",[937,99866,99867,99869,99870,99872],{},[941,99868,21396],{}," designates the cold store; each collection that should\nparticipate declares an ",[941,99871,605],{}," policy when it is opened:",[2217,99874,99876],{"className":2219,"code":99875,"language":2221,"meta":1037,"style":1037},"import { createNoydb, withArchive } from '@noy-db\u002Fhub'\nimport { toFile } from '@noy-db\u002Fto-file'\n\nconst db = await createNoydb({\n  store: toFile({ dir: '.\u002Fdata' }),          \u002F\u002F primary store (hot)\n  user: 'owner',\n  secret: 'correct horse battery staple',\n  archiveStrategy: withArchive({\n    store: toFile({ dir: '.\u002Farchive' }),     \u002F\u002F cold store — any NoydbStore works\n  }),\n})\n\nconst vault = await db.openVault('books')\n\ninterface Invoice {\n  id: string\n  year: number\n  status: 'draft' | 'issued' | 'paid'\n  hold?: boolean\n}\n\nconst invoices = vault.collection\u003CInvoice>('invoices', {\n  archive: {\n    archiveWhen: (r) => r.year \u003C= 2022,        \u002F\u002F eligibility: closed fiscal years\n    legalHold: (r) => r.hold === true,         \u002F\u002F optional: retain despite eligibility\n  },\n})\n",[941,99877,99878,99901,99919,99923,99939,99970,99984,99998,100011,100043,100051,100057,100061,100087,100091,100099,100107,100116,100145,100154,100158,100162,100194,100203,100236,100267,100271],{"__ignoreMap":1037},[2225,99879,99880,99882,99884,99886,99888,99891,99893,99895,99897,99899],{"class":2227,"line":2228},[2225,99881,2232],{"class":2231},[2225,99883,2236],{"class":2235},[2225,99885,2744],{"class":2239},[2225,99887,2298],{"class":2235},[2225,99889,99890],{"class":2239}," withArchive",[2225,99892,2243],{"class":2235},[2225,99894,2246],{"class":2231},[2225,99896,2249],{"class":2235},[2225,99898,1086],{"class":2252},[2225,99900,2256],{"class":2235},[2225,99902,99903,99905,99907,99909,99911,99913,99915,99917],{"class":2227,"line":1041},[2225,99904,2232],{"class":2231},[2225,99906,2236],{"class":2235},[2225,99908,15573],{"class":2239},[2225,99910,2243],{"class":2235},[2225,99912,2246],{"class":2231},[2225,99914,2249],{"class":2235},[2225,99916,9551],{"class":2252},[2225,99918,2256],{"class":2235},[2225,99920,99921],{"class":2227,"line":1038},[2225,99922,2285],{"emptyLinePlaceholder":2284},[2225,99924,99925,99927,99929,99931,99933,99935,99937],{"class":2227,"line":2288},[2225,99926,2775],{"class":2774},[2225,99928,2778],{"class":2239},[2225,99930,2781],{"class":2235},[2225,99932,2784],{"class":2231},[2225,99934,2744],{"class":2291},[2225,99936,2789],{"class":2239},[2225,99938,4200],{"class":2235},[2225,99940,99941,99943,99945,99947,99949,99951,99953,99955,99957,99959,99961,99963,99965,99967],{"class":2227,"line":2304},[2225,99942,15608],{"class":2307},[2225,99944,2311],{"class":2235},[2225,99946,15573],{"class":2291},[2225,99948,2789],{"class":2239},[2225,99950,2792],{"class":2235},[2225,99952,15619],{"class":2307},[2225,99954,2311],{"class":2235},[2225,99956,2249],{"class":2235},[2225,99958,15626],{"class":2252},[2225,99960,2805],{"class":2235},[2225,99962,2243],{"class":2235},[2225,99964,1381],{"class":2239},[2225,99966,2298],{"class":2235},[2225,99968,99969],{"class":2763},"          \u002F\u002F primary store (hot)\n",[2225,99971,99972,99974,99976,99978,99980,99982],{"class":2227,"line":2356},[2225,99973,4205],{"class":2307},[2225,99975,2311],{"class":2235},[2225,99977,2249],{"class":2235},[2225,99979,9898],{"class":2252},[2225,99981,2805],{"class":2235},[2225,99983,1707],{"class":2235},[2225,99985,99986,99988,99990,99992,99994,99996],{"class":2227,"line":2830},[2225,99987,10423],{"class":2307},[2225,99989,2311],{"class":2235},[2225,99991,2249],{"class":2235},[2225,99993,40260],{"class":2252},[2225,99995,2805],{"class":2235},[2225,99997,1707],{"class":2235},[2225,99999,100000,100003,100005,100007,100009],{"class":2227,"line":2861},[2225,100001,100002],{"class":2307},"  archiveStrategy",[2225,100004,2311],{"class":2235},[2225,100006,99890],{"class":2291},[2225,100008,2789],{"class":2239},[2225,100010,4200],{"class":2235},[2225,100012,100013,100015,100017,100019,100021,100023,100025,100027,100029,100032,100034,100036,100038,100040],{"class":2227,"line":4246},[2225,100014,85589],{"class":2307},[2225,100016,2311],{"class":2235},[2225,100018,15573],{"class":2291},[2225,100020,2789],{"class":2239},[2225,100022,2792],{"class":2235},[2225,100024,15619],{"class":2307},[2225,100026,2311],{"class":2235},[2225,100028,2249],{"class":2235},[2225,100030,100031],{"class":2252},".\u002Farchive",[2225,100033,2805],{"class":2235},[2225,100035,2243],{"class":2235},[2225,100037,1381],{"class":2239},[2225,100039,2298],{"class":2235},[2225,100041,100042],{"class":2763},"     \u002F\u002F cold store — any NoydbStore works\n",[2225,100044,100045,100047,100049],{"class":2227,"line":4253},[2225,100046,19143],{"class":2235},[2225,100048,1381],{"class":2239},[2225,100050,1707],{"class":2235},[2225,100052,100053,100055],{"class":2227,"line":4259},[2225,100054,2359],{"class":2235},[2225,100056,2362],{"class":2239},[2225,100058,100059],{"class":2227,"line":6548},[2225,100060,2285],{"emptyLinePlaceholder":2284},[2225,100062,100063,100065,100067,100069,100071,100073,100075,100077,100079,100081,100083,100085],{"class":2227,"line":6576},[2225,100064,2775],{"class":2774},[2225,100066,2835],{"class":2239},[2225,100068,2781],{"class":2235},[2225,100070,2784],{"class":2231},[2225,100072,2842],{"class":2239},[2225,100074,1520],{"class":2235},[2225,100076,2847],{"class":2291},[2225,100078,2789],{"class":2239},[2225,100080,2805],{"class":2235},[2225,100082,65761],{"class":2252},[2225,100084,2805],{"class":2235},[2225,100086,2362],{"class":2239},[2225,100088,100089],{"class":2227,"line":6582},[2225,100090,2285],{"emptyLinePlaceholder":2284},[2225,100092,100093,100095,100097],{"class":2227,"line":6587},[2225,100094,6994],{"class":2774},[2225,100096,6343],{"class":6342},[2225,100098,2301],{"class":2235},[2225,100100,100101,100103,100105],{"class":2227,"line":7174},[2225,100102,28371],{"class":2307},[2225,100104,2311],{"class":2235},[2225,100106,7014],{"class":6342},[2225,100108,100109,100112,100114],{"class":2227,"line":7180},[2225,100110,100111],{"class":2307},"  year",[2225,100113,2311],{"class":2235},[2225,100115,12024],{"class":6342},[2225,100117,100118,100120,100122,100124,100126,100128,100130,100132,100135,100137,100139,100141,100143],{"class":2227,"line":7220},[2225,100119,26171],{"class":2307},[2225,100121,2311],{"class":2235},[2225,100123,2249],{"class":2235},[2225,100125,28429],{"class":2252},[2225,100127,2805],{"class":2235},[2225,100129,7087],{"class":2235},[2225,100131,2249],{"class":2235},[2225,100133,100134],{"class":2252},"issued",[2225,100136,2805],{"class":2235},[2225,100138,7087],{"class":2235},[2225,100140,2249],{"class":2235},[2225,100142,6539],{"class":2252},[2225,100144,2256],{"class":2235},[2225,100146,100147,100150,100152],{"class":2227,"line":7225},[2225,100148,100149],{"class":2307},"  hold",[2225,100151,7011],{"class":2235},[2225,100153,11718],{"class":6342},[2225,100155,100156],{"class":2227,"line":7231},[2225,100157,7351],{"class":2235},[2225,100159,100160],{"class":2227,"line":7267},[2225,100161,2285],{"emptyLinePlaceholder":2284},[2225,100163,100164,100166,100168,100170,100172,100174,100176,100178,100180,100182,100184,100186,100188,100190,100192],{"class":2227,"line":7272},[2225,100165,2775],{"class":2774},[2225,100167,6461],{"class":2239},[2225,100169,2781],{"class":2235},[2225,100171,6466],{"class":2239},[2225,100173,1520],{"class":2235},[2225,100175,6471],{"class":2291},[2225,100177,6474],{"class":2235},[2225,100179,6477],{"class":6342},[2225,100181,6480],{"class":2235},[2225,100183,2789],{"class":2239},[2225,100185,2805],{"class":2235},[2225,100187,6487],{"class":2252},[2225,100189,2805],{"class":2235},[2225,100191,2298],{"class":2235},[2225,100193,2301],{"class":2235},[2225,100195,100196,100199,100201],{"class":2227,"line":7278},[2225,100197,100198],{"class":2307},"  archive",[2225,100200,2311],{"class":2235},[2225,100202,2301],{"class":2235},[2225,100204,100205,100208,100210,100212,100214,100216,100218,100220,100222,100225,100228,100231,100233],{"class":2227,"line":7303},[2225,100206,100207],{"class":2291},"    archiveWhen",[2225,100209,2311],{"class":2235},[2225,100211,953],{"class":2235},[2225,100213,13217],{"class":7052},[2225,100215,1381],{"class":2235},[2225,100217,8116],{"class":2774},[2225,100219,32537],{"class":2239},[2225,100221,1520],{"class":2235},[2225,100223,100224],{"class":2239},"year ",[2225,100226,100227],{"class":2235},"\u003C=",[2225,100229,100230],{"class":6527}," 2022",[2225,100232,2298],{"class":2235},[2225,100234,100235],{"class":2763},"        \u002F\u002F eligibility: closed fiscal years\n",[2225,100237,100238,100241,100243,100245,100247,100249,100251,100253,100255,100258,100260,100262,100264],{"class":2227,"line":7308},[2225,100239,100240],{"class":2291},"    legalHold",[2225,100242,2311],{"class":2235},[2225,100244,953],{"class":2235},[2225,100246,13217],{"class":7052},[2225,100248,1381],{"class":2235},[2225,100250,8116],{"class":2774},[2225,100252,32537],{"class":2239},[2225,100254,1520],{"class":2235},[2225,100256,100257],{"class":2239},"hold ",[2225,100259,33083],{"class":2235},[2225,100261,2322],{"class":2321},[2225,100263,2298],{"class":2235},[2225,100265,100266],{"class":2763},"         \u002F\u002F optional: retain despite eligibility\n",[2225,100268,100269],{"class":2227,"line":7314},[2225,100270,19017],{"class":2235},[2225,100272,100273,100275],{"class":2227,"line":7348},[2225,100274,2359],{"class":2235},[2225,100276,2362],{"class":2239},[937,100278,100279,100280,100282,100283,100285],{},"Any ",[941,100281,3154],{}," can be the cold target — a slower disk path, an object-storage adapter\nfrom the extended ",[941,100284,1779],{}," family, anything that satisfies the store contract.",[1022,100287,29893],{"id":29892},[1591,100289,100291],{"id":100290},"sweep-eligible-records-to-cold-storage","Sweep eligible records to cold storage",[937,100293,100294,100296,100297,100299,100300,19862,100303,100305],{},[941,100295,21401],{}," walks every collection that declared an ",[941,100298,605],{}," policy, evaluates\n",[941,100301,100302],{},"archiveWhen",[941,100304,99838],{},") on each decrypted record, and relocates the eligible\nenvelopes:",[2217,100307,100309],{"className":2219,"code":100308,"language":2221,"meta":1037,"style":1037},"await invoices.put('inv-2020-001', { id: 'inv-2020-001', year: 2020, status: 'paid' })\nawait invoices.put('inv-2021-004', { id: 'inv-2021-004', year: 2021, status: 'paid' })\nawait invoices.put('inv-2021-007', { id: 'inv-2021-007', year: 2021, status: 'paid', hold: true })\nawait invoices.put('inv-2025-002', { id: 'inv-2025-002', year: 2025, status: 'issued' })\n\nconst result = await vault.archive()\n",[941,100310,100311,100370,100428,100494,100552,100556],{"__ignoreMap":1037},[2225,100312,100313,100315,100317,100319,100321,100323,100325,100328,100330,100332,100334,100336,100338,100340,100342,100344,100346,100349,100351,100354,100356,100358,100360,100362,100364,100366,100368],{"class":2227,"line":2228},[2225,100314,6500],{"class":2231},[2225,100316,6503],{"class":2239},[2225,100318,1520],{"class":2235},[2225,100320,3161],{"class":2291},[2225,100322,2789],{"class":2239},[2225,100324,2805],{"class":2235},[2225,100326,100327],{"class":2252},"inv-2020-001",[2225,100329,2805],{"class":2235},[2225,100331,2298],{"class":2235},[2225,100333,2236],{"class":2235},[2225,100335,7070],{"class":2307},[2225,100337,2311],{"class":2235},[2225,100339,2249],{"class":2235},[2225,100341,100327],{"class":2252},[2225,100343,2805],{"class":2235},[2225,100345,2298],{"class":2235},[2225,100347,100348],{"class":2307}," year",[2225,100350,2311],{"class":2235},[2225,100352,100353],{"class":6527}," 2020",[2225,100355,2298],{"class":2235},[2225,100357,6361],{"class":2307},[2225,100359,2311],{"class":2235},[2225,100361,2249],{"class":2235},[2225,100363,6539],{"class":2252},[2225,100365,2805],{"class":2235},[2225,100367,2243],{"class":2235},[2225,100369,2362],{"class":2239},[2225,100371,100372,100374,100376,100378,100380,100382,100384,100387,100389,100391,100393,100395,100397,100399,100401,100403,100405,100407,100409,100412,100414,100416,100418,100420,100422,100424,100426],{"class":2227,"line":1041},[2225,100373,6500],{"class":2231},[2225,100375,6503],{"class":2239},[2225,100377,1520],{"class":2235},[2225,100379,3161],{"class":2291},[2225,100381,2789],{"class":2239},[2225,100383,2805],{"class":2235},[2225,100385,100386],{"class":2252},"inv-2021-004",[2225,100388,2805],{"class":2235},[2225,100390,2298],{"class":2235},[2225,100392,2236],{"class":2235},[2225,100394,7070],{"class":2307},[2225,100396,2311],{"class":2235},[2225,100398,2249],{"class":2235},[2225,100400,100386],{"class":2252},[2225,100402,2805],{"class":2235},[2225,100404,2298],{"class":2235},[2225,100406,100348],{"class":2307},[2225,100408,2311],{"class":2235},[2225,100410,100411],{"class":6527}," 2021",[2225,100413,2298],{"class":2235},[2225,100415,6361],{"class":2307},[2225,100417,2311],{"class":2235},[2225,100419,2249],{"class":2235},[2225,100421,6539],{"class":2252},[2225,100423,2805],{"class":2235},[2225,100425,2243],{"class":2235},[2225,100427,2362],{"class":2239},[2225,100429,100430,100432,100434,100436,100438,100440,100442,100445,100447,100449,100451,100453,100455,100457,100459,100461,100463,100465,100467,100469,100471,100473,100475,100477,100479,100481,100483,100486,100488,100490,100492],{"class":2227,"line":1038},[2225,100431,6500],{"class":2231},[2225,100433,6503],{"class":2239},[2225,100435,1520],{"class":2235},[2225,100437,3161],{"class":2291},[2225,100439,2789],{"class":2239},[2225,100441,2805],{"class":2235},[2225,100443,100444],{"class":2252},"inv-2021-007",[2225,100446,2805],{"class":2235},[2225,100448,2298],{"class":2235},[2225,100450,2236],{"class":2235},[2225,100452,7070],{"class":2307},[2225,100454,2311],{"class":2235},[2225,100456,2249],{"class":2235},[2225,100458,100444],{"class":2252},[2225,100460,2805],{"class":2235},[2225,100462,2298],{"class":2235},[2225,100464,100348],{"class":2307},[2225,100466,2311],{"class":2235},[2225,100468,100411],{"class":6527},[2225,100470,2298],{"class":2235},[2225,100472,6361],{"class":2307},[2225,100474,2311],{"class":2235},[2225,100476,2249],{"class":2235},[2225,100478,6539],{"class":2252},[2225,100480,2805],{"class":2235},[2225,100482,2298],{"class":2235},[2225,100484,100485],{"class":2307}," hold",[2225,100487,2311],{"class":2235},[2225,100489,2322],{"class":2321},[2225,100491,2243],{"class":2235},[2225,100493,2362],{"class":2239},[2225,100495,100496,100498,100500,100502,100504,100506,100508,100511,100513,100515,100517,100519,100521,100523,100525,100527,100529,100531,100533,100536,100538,100540,100542,100544,100546,100548,100550],{"class":2227,"line":2288},[2225,100497,6500],{"class":2231},[2225,100499,6503],{"class":2239},[2225,100501,1520],{"class":2235},[2225,100503,3161],{"class":2291},[2225,100505,2789],{"class":2239},[2225,100507,2805],{"class":2235},[2225,100509,100510],{"class":2252},"inv-2025-002",[2225,100512,2805],{"class":2235},[2225,100514,2298],{"class":2235},[2225,100516,2236],{"class":2235},[2225,100518,7070],{"class":2307},[2225,100520,2311],{"class":2235},[2225,100522,2249],{"class":2235},[2225,100524,100510],{"class":2252},[2225,100526,2805],{"class":2235},[2225,100528,2298],{"class":2235},[2225,100530,100348],{"class":2307},[2225,100532,2311],{"class":2235},[2225,100534,100535],{"class":6527}," 2025",[2225,100537,2298],{"class":2235},[2225,100539,6361],{"class":2307},[2225,100541,2311],{"class":2235},[2225,100543,2249],{"class":2235},[2225,100545,100134],{"class":2252},[2225,100547,2805],{"class":2235},[2225,100549,2243],{"class":2235},[2225,100551,2362],{"class":2239},[2225,100553,100554],{"class":2227,"line":2304},[2225,100555,2285],{"emptyLinePlaceholder":2284},[2225,100557,100558,100560,100562,100564,100566,100568,100570,100572],{"class":2227,"line":2356},[2225,100559,2775],{"class":2774},[2225,100561,36920],{"class":2239},[2225,100563,2781],{"class":2235},[2225,100565,2784],{"class":2231},[2225,100567,6466],{"class":2239},[2225,100569,1520],{"class":2235},[2225,100571,605],{"class":2291},[2225,100573,8146],{"class":2239},[2217,100575,100577],{"className":6604,"code":100576,"language":141,"meta":1037,"style":1037},"{\n  \"archived\": 2,\n  \"held\": 1,\n  \"scanned\": 4,\n  \"byCollection\": { \"invoices\": { \"archived\": 2, \"held\": 1 } }\n}\n",[941,100578,100579,100583,100598,100613,100629,100678],{"__ignoreMap":1037},[2225,100580,100581],{"class":2227,"line":2228},[2225,100582,4200],{"class":2235},[2225,100584,100585,100587,100590,100592,100594,100596],{"class":2227,"line":1041},[2225,100586,9368],{"class":2235},[2225,100588,100589],{"class":2774},"archived",[2225,100591,6620],{"class":2235},[2225,100593,2311],{"class":2235},[2225,100595,18961],{"class":6527},[2225,100597,1707],{"class":2235},[2225,100599,100600,100602,100605,100607,100609,100611],{"class":2227,"line":1038},[2225,100601,9368],{"class":2235},[2225,100603,100604],{"class":2774},"held",[2225,100606,6620],{"class":2235},[2225,100608,2311],{"class":2235},[2225,100610,9377],{"class":6527},[2225,100612,1707],{"class":2235},[2225,100614,100615,100617,100620,100622,100624,100627],{"class":2227,"line":2288},[2225,100616,9368],{"class":2235},[2225,100618,100619],{"class":2774},"scanned",[2225,100621,6620],{"class":2235},[2225,100623,2311],{"class":2235},[2225,100625,100626],{"class":6527}," 4",[2225,100628,1707],{"class":2235},[2225,100630,100631,100633,100636,100638,100640,100642,100644,100646,100648,100650,100652,100654,100656,100658,100660,100662,100664,100666,100668,100670,100672,100674,100676],{"class":2227,"line":2304},[2225,100632,9368],{"class":2235},[2225,100634,100635],{"class":2774},"byCollection",[2225,100637,6620],{"class":2235},[2225,100639,2311],{"class":2235},[2225,100641,2236],{"class":2235},[2225,100643,6614],{"class":2235},[2225,100645,6487],{"class":6342},[2225,100647,6620],{"class":2235},[2225,100649,2311],{"class":2235},[2225,100651,2236],{"class":2235},[2225,100653,6614],{"class":2235},[2225,100655,100589],{"class":6527},[2225,100657,6620],{"class":2235},[2225,100659,2311],{"class":2235},[2225,100661,18961],{"class":6527},[2225,100663,2298],{"class":2235},[2225,100665,6614],{"class":2235},[2225,100667,100604],{"class":6527},[2225,100669,6620],{"class":2235},[2225,100671,2311],{"class":2235},[2225,100673,9377],{"class":6527},[2225,100675,2243],{"class":2235},[2225,100677,6369],{"class":2235},[2225,100679,100680],{"class":2227,"line":2356},[2225,100681,7351],{"class":2235},[937,100683,100684],{},"Archived records are gone from the primary store and invisible to normal reads:",[2217,100686,100688],{"className":2219,"code":100687,"language":2221,"meta":1037,"style":1037},"await invoices.get('inv-2020-001')\n\u002F\u002F → null  (relocated to the cold store)\n\nawait invoices.get('inv-2025-002')\n\u002F\u002F → { id: 'inv-2025-002', year: 2025, status: 'issued' }\n",[941,100689,100690,100710,100715,100719,100739],{"__ignoreMap":1037},[2225,100691,100692,100694,100696,100698,100700,100702,100704,100706,100708],{"class":2227,"line":2228},[2225,100693,6500],{"class":2231},[2225,100695,6503],{"class":2239},[2225,100697,1520],{"class":2235},[2225,100699,3158],{"class":2291},[2225,100701,2789],{"class":2239},[2225,100703,2805],{"class":2235},[2225,100705,100327],{"class":2252},[2225,100707,2805],{"class":2235},[2225,100709,2362],{"class":2239},[2225,100711,100712],{"class":2227,"line":1041},[2225,100713,100714],{"class":2763},"\u002F\u002F → null  (relocated to the cold store)\n",[2225,100716,100717],{"class":2227,"line":1038},[2225,100718,2285],{"emptyLinePlaceholder":2284},[2225,100720,100721,100723,100725,100727,100729,100731,100733,100735,100737],{"class":2227,"line":2288},[2225,100722,6500],{"class":2231},[2225,100724,6503],{"class":2239},[2225,100726,1520],{"class":2235},[2225,100728,3158],{"class":2291},[2225,100730,2789],{"class":2239},[2225,100732,2805],{"class":2235},[2225,100734,100510],{"class":2252},[2225,100736,2805],{"class":2235},[2225,100738,2362],{"class":2239},[2225,100740,100741],{"class":2227,"line":2304},[2225,100742,100743],{"class":2763},"\u002F\u002F → { id: 'inv-2025-002', year: 2025, status: 'issued' }\n",[937,100745,100746,100747,100750,100751,2311],{},"Preview a sweep without moving anything with ",[941,100748,100749],{},"dryRun",", or cap a large sweep with\n",[941,100752,100753],{},"maxArchives",[2217,100755,100757],{"className":2219,"code":100756,"language":2221,"meta":1037,"style":1037},"await vault.archive({ dryRun: true })\n\u002F\u002F → counts what *would* happen; no envelope moves\n\nawait vault.archive({ maxArchives: 500 })\n\u002F\u002F → stops after 500 relocations; run again to continue\n",[941,100758,100759,100784,100789,100793,100818],{"__ignoreMap":1037},[2225,100760,100761,100763,100765,100767,100769,100771,100773,100776,100778,100780,100782],{"class":2227,"line":2228},[2225,100762,6500],{"class":2231},[2225,100764,6466],{"class":2239},[2225,100766,1520],{"class":2235},[2225,100768,605],{"class":2291},[2225,100770,2789],{"class":2239},[2225,100772,2792],{"class":2235},[2225,100774,100775],{"class":2307}," dryRun",[2225,100777,2311],{"class":2235},[2225,100779,2322],{"class":2321},[2225,100781,2243],{"class":2235},[2225,100783,2362],{"class":2239},[2225,100785,100786],{"class":2227,"line":1041},[2225,100787,100788],{"class":2763},"\u002F\u002F → counts what *would* happen; no envelope moves\n",[2225,100790,100791],{"class":2227,"line":1038},[2225,100792,2285],{"emptyLinePlaceholder":2284},[2225,100794,100795,100797,100799,100801,100803,100805,100807,100810,100812,100814,100816],{"class":2227,"line":2288},[2225,100796,6500],{"class":2231},[2225,100798,6466],{"class":2239},[2225,100800,1520],{"class":2235},[2225,100802,605],{"class":2291},[2225,100804,2789],{"class":2239},[2225,100806,2792],{"class":2235},[2225,100808,100809],{"class":2307}," maxArchives",[2225,100811,2311],{"class":2235},[2225,100813,27084],{"class":6527},[2225,100815,2243],{"class":2235},[2225,100817,2362],{"class":2239},[2225,100819,100820],{"class":2227,"line":2304},[2225,100821,100822],{"class":2763},"\u002F\u002F → stops after 500 relocations; run again to continue\n",[1591,100824,100826],{"id":100825},"list-the-archived-set","List the archived set",[937,100828,100829],{},"The cold store's contents are themselves the archived-set index — there is no separate\nmetadata to maintain:",[2217,100831,100833],{"className":2219,"code":100832,"language":2221,"meta":1037,"style":1037},"await vault.listArchived()\n\u002F\u002F → [\n\u002F\u002F     { collection: 'invoices', id: 'inv-2020-001' },\n\u002F\u002F     { collection: 'invoices', id: 'inv-2021-004' },\n\u002F\u002F   ]\n\nawait vault.listArchived('invoices')   \u002F\u002F scope to one collection\n",[941,100834,100835,100848,100853,100858,100863,100868,100872],{"__ignoreMap":1037},[2225,100836,100837,100839,100841,100843,100846],{"class":2227,"line":2228},[2225,100838,6500],{"class":2231},[2225,100840,6466],{"class":2239},[2225,100842,1520],{"class":2235},[2225,100844,100845],{"class":2291},"listArchived",[2225,100847,8146],{"class":2239},[2225,100849,100850],{"class":2227,"line":1041},[2225,100851,100852],{"class":2763},"\u002F\u002F → [\n",[2225,100854,100855],{"class":2227,"line":1038},[2225,100856,100857],{"class":2763},"\u002F\u002F     { collection: 'invoices', id: 'inv-2020-001' },\n",[2225,100859,100860],{"class":2227,"line":2288},[2225,100861,100862],{"class":2763},"\u002F\u002F     { collection: 'invoices', id: 'inv-2021-004' },\n",[2225,100864,100865],{"class":2227,"line":2304},[2225,100866,100867],{"class":2763},"\u002F\u002F   ]\n",[2225,100869,100870],{"class":2227,"line":2356},[2225,100871,2285],{"emptyLinePlaceholder":2284},[2225,100873,100874,100876,100878,100880,100882,100884,100886,100888,100890,100892],{"class":2227,"line":2830},[2225,100875,6500],{"class":2231},[2225,100877,6466],{"class":2239},[2225,100879,1520],{"class":2235},[2225,100881,100845],{"class":2291},[2225,100883,2789],{"class":2239},[2225,100885,2805],{"class":2235},[2225,100887,6487],{"class":2252},[2225,100889,2805],{"class":2235},[2225,100891,71781],{"class":2239},[2225,100893,100894],{"class":2763},"\u002F\u002F scope to one collection\n",[1591,100896,100898],{"id":100897},"restore-a-record","Restore a record",[937,100900,100901,100904],{},[941,100902,100903],{},"vault.restore(collection, id)"," relocates one envelope back to the primary store and\nremoves the cold copy:",[2217,100906,100908],{"className":2219,"code":100907,"language":2221,"meta":1037,"style":1037},"await vault.restore('invoices', 'inv-2020-001')\n\u002F\u002F → true\n\nawait invoices.get('inv-2020-001')\n\u002F\u002F → { id: 'inv-2020-001', year: 2020, status: 'paid' }\n\nawait vault.restore('invoices', 'no-such-id')\n\u002F\u002F → false  (not in the archive)\n",[941,100909,100910,100939,100943,100947,100967,100972,100976,101005],{"__ignoreMap":1037},[2225,100911,100912,100914,100916,100918,100921,100923,100925,100927,100929,100931,100933,100935,100937],{"class":2227,"line":2228},[2225,100913,6500],{"class":2231},[2225,100915,6466],{"class":2239},[2225,100917,1520],{"class":2235},[2225,100919,100920],{"class":2291},"restore",[2225,100922,2789],{"class":2239},[2225,100924,2805],{"class":2235},[2225,100926,6487],{"class":2252},[2225,100928,2805],{"class":2235},[2225,100930,2298],{"class":2235},[2225,100932,2249],{"class":2235},[2225,100934,100327],{"class":2252},[2225,100936,2805],{"class":2235},[2225,100938,2362],{"class":2239},[2225,100940,100941],{"class":2227,"line":1041},[2225,100942,77590],{"class":2763},[2225,100944,100945],{"class":2227,"line":1038},[2225,100946,2285],{"emptyLinePlaceholder":2284},[2225,100948,100949,100951,100953,100955,100957,100959,100961,100963,100965],{"class":2227,"line":2288},[2225,100950,6500],{"class":2231},[2225,100952,6503],{"class":2239},[2225,100954,1520],{"class":2235},[2225,100956,3158],{"class":2291},[2225,100958,2789],{"class":2239},[2225,100960,2805],{"class":2235},[2225,100962,100327],{"class":2252},[2225,100964,2805],{"class":2235},[2225,100966,2362],{"class":2239},[2225,100968,100969],{"class":2227,"line":2304},[2225,100970,100971],{"class":2763},"\u002F\u002F → { id: 'inv-2020-001', year: 2020, status: 'paid' }\n",[2225,100973,100974],{"class":2227,"line":2356},[2225,100975,2285],{"emptyLinePlaceholder":2284},[2225,100977,100978,100980,100982,100984,100986,100988,100990,100992,100994,100996,100998,101001,101003],{"class":2227,"line":2830},[2225,100979,6500],{"class":2231},[2225,100981,6466],{"class":2239},[2225,100983,1520],{"class":2235},[2225,100985,100920],{"class":2291},[2225,100987,2789],{"class":2239},[2225,100989,2805],{"class":2235},[2225,100991,6487],{"class":2252},[2225,100993,2805],{"class":2235},[2225,100995,2298],{"class":2235},[2225,100997,2249],{"class":2235},[2225,100999,101000],{"class":2252},"no-such-id",[2225,101002,2805],{"class":2235},[2225,101004,2362],{"class":2239},[2225,101006,101007],{"class":2227,"line":2861},[2225,101008,101009],{"class":2763},"\u002F\u002F → false  (not in the archive)\n",[1022,101011,101012],{"id":41259},"API",[1591,101014,101016],{"id":101015},"witharchiveopts",[941,101017,101018],{},"withArchive(opts)",[2217,101020,101022],{"className":2219,"code":101021,"language":2221,"meta":1037,"style":1037},"function withArchive(opts: WithArchiveOptions): ArchiveStrategy\n",[941,101023,101024],{"__ignoreMap":1037},[2225,101025,101026,101028,101030,101032,101034,101036,101039,101041],{"class":2227,"line":2228},[2225,101027,12641],{"class":2774},[2225,101029,99890],{"class":2291},[2225,101031,2789],{"class":2235},[2225,101033,8097],{"class":7052},[2225,101035,2311],{"class":2235},[2225,101037,101038],{"class":6342}," WithArchiveOptions",[2225,101040,6255],{"class":2235},[2225,101042,101043],{"class":6342}," ArchiveStrategy\n",[937,101045,101046,101047,77939,101050,1520],{},"Enables record cold-storage archival. Pass the result as ",[941,101048,101049],{},"archiveStrategy",[941,101051,12780],{},[1338,101053,101054,101065],{},[1341,101055,101056],{},[1344,101057,101058,101061,101063],{},[1347,101059,101060],{},"Option",[1347,101062,20194],{},[1347,101064,7834],{},[1357,101066,101067],{},[1344,101068,101069,101073,101077],{},[1362,101070,101071],{},[941,101072,6951],{},[1362,101074,101075],{},[941,101076,3154],{},[1362,101078,101079],{},"The cold store that holds archived record envelopes. Required.",[1591,101081,101083],{"id":101082},"archivepolicyt",[941,101084,101085],{},"ArchivePolicy\u003CT>",[937,101087,101088,101089,101092,101093,101095],{},"Declared in the collection options: ",[941,101090,101091],{},"vault.collection\u003CT>(name, { archive: { … } })",".\nEvaluated when ",[941,101094,21401],{}," runs.",[1338,101097,101098,101109],{},[1341,101099,101100],{},[1344,101101,101102,101105,101107],{},[1347,101103,101104],{},"Field",[1347,101106,20194],{},[1347,101108,7834],{},[1357,101110,101111,101125],{},[1344,101112,101113,101117,101122],{},[1362,101114,101115],{},[941,101116,100302],{},[1362,101118,101119],{},[941,101120,101121],{},"(record: T) => boolean",[1362,101123,101124],{},"Selects records eligible for archival — typically a business-date \u002F period test. Required.",[1344,101126,101127,101131,101135],{},[1362,101128,101129],{},[941,101130,99838],{},[1362,101132,101133],{},[941,101134,101121],{},[1362,101136,101137,101138,101140],{},"Optional. While ",[941,101139,11790],{},", blocks archival of an otherwise-eligible record. Fail-closed: if it throws, the record is retained.",[1591,101142,101144],{"id":101143},"vaultarchiveoptions",[941,101145,101146],{},"vault.archive(options?)",[2217,101148,101150],{"className":2219,"code":101149,"language":2221,"meta":1037,"style":1037},"vault.archive(options?: ArchiveRunOptions): Promise\u003CArchiveResult>\n",[941,101151,101152],{"__ignoreMap":1037},[2225,101153,101154,101156,101158,101160,101163,101165,101168,101170,101172,101175],{"class":2227,"line":2228},[2225,101155,2694],{"class":2239},[2225,101157,1520],{"class":2235},[2225,101159,605],{"class":2291},[2225,101161,101162],{"class":2239},"(options",[2225,101164,7011],{"class":2235},[2225,101166,101167],{"class":2239}," ArchiveRunOptions): ",[2225,101169,11740],{"class":6342},[2225,101171,6474],{"class":2235},[2225,101173,101174],{"class":2239},"ArchiveResult",[2225,101176,7093],{"class":2235},[937,101178,101179],{},"Sweeps eligible records into the cold store.",[1338,101181,101182,101195],{},[1341,101183,101184],{},[1344,101185,101186,101191,101193],{},[1347,101187,101188],{},[941,101189,101190],{},"ArchiveRunOptions",[1347,101192,20194],{},[1347,101194,7834],{},[1357,101196,101197,101210],{},[1344,101198,101199,101203,101207],{},[1362,101200,101201],{},[941,101202,100753],{},[1362,101204,101205],{},[941,101206,20636],{},[1362,101208,101209],{},"Stop the sweep after this many relocations. Omit for unbounded.",[1344,101211,101212,101216,101220],{},[1362,101213,101214],{},[941,101215,100749],{},[1362,101217,101218],{},[941,101219,16861],{},[1362,101221,101222],{},"Count what would happen without relocating anything.",[937,101224,101225,101226,2311],{},"Returns an ",[941,101227,101174],{},[1338,101229,101230,101240],{},[1341,101231,101232],{},[1344,101233,101234,101236,101238],{},[1347,101235,101104],{},[1347,101237,20194],{},[1347,101239,20200],{},[1357,101241,101242,101257,101275,101288],{},[1344,101243,101244,101248,101252],{},[1362,101245,101246],{},[941,101247,100589],{},[1362,101249,101250],{},[941,101251,20636],{},[1362,101253,101254,101255,957],{},"Records relocated to the cold store (or that would be, under ",[941,101256,100749],{},[1344,101258,101259,101263,101267],{},[1362,101260,101261],{},[941,101262,100604],{},[1362,101264,101265],{},[941,101266,20636],{},[1362,101268,101269,101270,101272,101273,1520],{},"Records eligible by ",[941,101271,100302],{}," but retained by a ",[941,101274,99838],{},[1344,101276,101277,101281,101285],{},[1362,101278,101279],{},[941,101280,100619],{},[1362,101282,101283],{},[941,101284,20636],{},[1362,101286,101287],{},"Records evaluated across all policy collections.",[1344,101289,101290,101294,101299],{},[1362,101291,101292],{},[941,101293,100635],{},[1362,101295,101296],{},[941,101297,101298],{},"Record\u003Cstring, { archived: number; held: number }>",[1362,101300,101301],{},"Per-collection breakdown.",[1591,101303,101305],{"id":101304},"vaultrestorecollection-id",[941,101306,100903],{},[2217,101308,101310],{"className":2219,"code":101309,"language":2221,"meta":1037,"style":1037},"vault.restore(collection: string, id: string): Promise\u003Cboolean>\n",[941,101311,101312],{"__ignoreMap":1037},[2225,101313,101314,101316,101318,101320,101323,101325,101328,101330,101332,101334],{"class":2227,"line":2228},[2225,101315,2694],{"class":2239},[2225,101317,1520],{"class":2235},[2225,101319,100920],{"class":2291},[2225,101321,101322],{"class":2239},"(collection: string",[2225,101324,2298],{"class":2235},[2225,101326,101327],{"class":2239}," id: string): ",[2225,101329,11740],{"class":6342},[2225,101331,6474],{"class":2235},[2225,101333,16861],{"class":2239},[2225,101335,7093],{"class":2235},[937,101337,101338,101339,101341],{},"Relocates one archived envelope back to the primary store and deletes the cold copy.\nReturns ",[941,101340,2560],{}," if the record is not in the archive.",[1591,101343,101345],{"id":101344},"vaultlistarchivedcollection",[941,101346,101347],{},"vault.listArchived(collection?)",[2217,101349,101351],{"className":2219,"code":101350,"language":2221,"meta":1037,"style":1037},"vault.listArchived(collection?: string): Promise\u003CArray\u003C{ collection: string; id: string }>>\n",[941,101352,101353],{"__ignoreMap":1037},[2225,101354,101355,101357,101359,101361,101364,101366,101369,101371,101373,101375,101377,101379,101381,101383,101385,101387,101389,101391],{"class":2227,"line":2228},[2225,101356,2694],{"class":2239},[2225,101358,1520],{"class":2235},[2225,101360,100845],{"class":2291},[2225,101362,101363],{"class":2239},"(collection",[2225,101365,7011],{"class":2235},[2225,101367,101368],{"class":2239}," string): ",[2225,101370,11740],{"class":6342},[2225,101372,6474],{"class":2235},[2225,101374,19497],{"class":2239},[2225,101376,17851],{"class":2235},[2225,101378,7061],{"class":6342},[2225,101380,2311],{"class":2235},[2225,101382,6366],{"class":2239},[2225,101384,3769],{"class":2235},[2225,101386,7070],{"class":6342},[2225,101388,2311],{"class":2235},[2225,101390,6366],{"class":2239},[2225,101392,17964],{"class":2235},[937,101394,101395,101396,101398],{},"Lists archived record ids for one collection, or for every collection with an ",[941,101397,605],{},"\npolicy when called without arguments.",[1591,101400,101402],{"id":101401},"exported-types","Exported types",[937,101404,101405,968,101408,968,101411,968,101414,45420,101416,101418,101419,101421],{},[941,101406,101407],{},"WithArchiveOptions",[941,101409,101410],{},"ArchiveStrategy",[941,101412,101413],{},"ArchivePolicy",[941,101415,101174],{},[941,101417,101190],{}," are all importable from the ",[941,101420,1086],{}," root.",[1022,101423,101425],{"id":101424},"behavior-when-not-opted-in","Behavior when NOT opted in",[1111,101427,101428,101434],{},[1114,101429,101430,101431,101433],{},"Collections may still declare an ",[941,101432,605],{}," policy; it is inert until a strategy exists.",[1114,101435,101436,968,101438,2462,101440,101442,101443,101445,101446,1520],{},[941,101437,21401],{},[941,101439,21404],{},[941,101441,21407],{}," throw an ",[941,101444,12126],{}," whose\nmessage points at the fix: ",[941,101447,101448],{},"vault.archive\u002Frestore\u002FlistArchived require `archiveStrategy: withArchive({ store })` in createNoydb",[1022,101450,68210],{"id":68209},[1111,101452,101453,101463,101472,101482,101494,101511,101524],{},[1114,101454,101455,101460,101461,1520],{},[950,101456,101457,101458,1520],{},"Archival bypasses ",[1007,101459,693],{"href":694}," Relocation goes through low-level store\noperations, so a record locked as immutable (e.g. an issued invoice under a WORM guard)\ncan still be archived. This is by design — archival is housekeeping, not a user edit —\nbut it means a guard is not a defense against ",[941,101462,21401],{},[1114,101464,101465,101471],{},[950,101466,101467,101470],{},[1007,101468,101469],{"href":740},"Materialized views"," do not recompute."," Archiving never\nfires view dispatch, so finalized aggregates over a closed period keep their values even\nafter the underlying records move to cold storage.",[1114,101473,101474,1324,101477,2557,101479,101481],{},[950,101475,101476],{},"Archived records vanish from every normal read.",[941,101478,14639],{},[941,101480,6651],{},", and queries\ndo not see them. Restore first if a flow needs the record back.",[1114,101483,101484,101487,101488,101490,101491,101493],{},[950,101485,101486],{},"A sweep decrypts every record in each policy collection"," to evaluate ",[941,101489,100302],{}," on\nplaintext (inside the hub — the stores still only ever see ciphertext). On large\ncollections, bound the work with ",[941,101492,100753],{}," and sweep incrementally.",[1114,101495,101496,101499,101500,101502,101503,101505,101506,101508,101509,1520],{},[950,101497,101498],{},"Fail-closed predicates."," A throwing ",[941,101501,100302],{}," skips the record (it is not\narchived); a throwing ",[941,101504,99838],{}," retains the record (counted as ",[941,101507,100604],{},"). A record that\ncannot be decrypted is skipped and not counted in ",[941,101510,100619],{},[1114,101512,101513,1324,101516,101519,101520,101523],{},[950,101514,101515],{},"The cold store is the only index.",[941,101517,101518],{},"listArchived()"," enumerates the cold store\ndirectly; if you point ",[941,101521,101522],{},"withArchive"," at a different store later, previously archived\nrecords are simply not listed (they remain wherever they were written).",[1114,101525,101526,1324,101529,1261,101531,101533,101534,101537,101538,101540,101541,101544,101545,1520],{},[950,101527,101528],{},"Policies are declared per session, not persisted.",[941,101530,100302],{},[941,101532,99838],{}," are\nfunctions registered when ",[941,101535,101536],{},"vault.collection(name, { archive })"," runs. A sweep — and a\nno-argument ",[941,101539,101518],{}," — only covers collections opened ",[1092,101542,101543],{},"with their policy"," in the\ncurrent session, so open every participating collection before calling ",[941,101546,21401],{},[1022,101548,2621],{"id":2620},[1111,101550,101551,101557,101562,101567,101572,101579],{},[1114,101552,101553,1368,101555],{},[1007,101554,61743],{"href":598},[1007,101556,1019],{"href":1018},[1114,101558,101559,101561],{},[1007,101560,1516],{"href":66}," — what an encrypted envelope is",[1114,101563,101564,101566],{},[1007,101565,16076],{"href":51}," — the store contract the cold target must satisfy",[1114,101568,101569,101571],{},[1007,101570,693],{"href":694}," — write-boundary locks that archival deliberately bypasses",[1114,101573,101574,101576,101577,61726],{},[1007,101575,776],{"href":777}," — fiscal-period seals that pair naturally with year-based ",[941,101578,100302],{},[1114,101580,101581,101582,1520],{},"Proven by showcase ",[1007,101583,101585],{"href":94299,"rel":101584},[3371],[941,101586,101587],{},"127-with-archive",[2645,101589,101590],{},"html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sHdIc, html code.shiki .sHdIc{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#EEFFFF;--shiki-default-font-style:italic;--shiki-dark:#BABED8;--shiki-dark-font-style:italic}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}html pre.shiki code .sfNiH, html code.shiki .sfNiH{--shiki-light:#FF5370;--shiki-default:#FF9CAC;--shiki-dark:#FF9CAC}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":1037,"searchDepth":1038,"depth":1038,"links":101592},[101593,101594,101595,101596,101601,101609,101610,101611],{"id":29856,"depth":1041,"text":29857},{"id":70092,"depth":1041,"text":70093},{"id":99863,"depth":1041,"text":99864},{"id":29892,"depth":1041,"text":29893,"children":101597},[101598,101599,101600],{"id":100290,"depth":1038,"text":100291},{"id":100825,"depth":1038,"text":100826},{"id":100897,"depth":1038,"text":100898},{"id":41259,"depth":1041,"text":101012,"children":101602},[101603,101604,101605,101606,101607,101608],{"id":101015,"depth":1038,"text":101018},{"id":101082,"depth":1038,"text":101085},{"id":101143,"depth":1038,"text":101146},{"id":101304,"depth":1038,"text":100903},{"id":101344,"depth":1038,"text":101347},{"id":101401,"depth":1038,"text":101402},{"id":101424,"depth":1041,"text":101425},{"id":68209,"depth":1041,"text":68210},{"id":2620,"depth":1041,"text":2621},"Cold-storage archival: designate a second store with `withArchive({ store })`, sweep old records out with `vault.archive()`, pull one back with `restore()`.",{},{"title":605,"description":101612},"pB8zsGP3XyMyNFzgRNiCN1I22VURzCtWtEG-YDAuxT4",{"id":101617,"title":609,"access":12,"apiToc":929,"body":101618,"category":1042,"description":103417,"extension":1044,"meta":103418,"navigation":1042,"nest":1047,"path":610,"seo":103419,"sinceVersion":1042,"status":1049,"stem":611,"subtitle":1042,"tier":1050,"tocExternal":1042,"__hash__":103420},"docs\u002Fservices\u002Fattestation.md",{"type":931,"value":101619,"toc":103396},[101620,101622,101625,101627,101646,101664,101666,101685,101687,101700,102047,102064,102066,102070,102223,102229,102257,102261,102267,102400,102561,102589,102593,102599,102661,102761,102766,102880,102886,102890,102896,103008,103013,103028,103033,103036,103117,103121,103127,103219,103223,103250,103269,103271,103283,103285,103358,103360,103393],[934,101621,609],{"id":609},[46871,101623],{":rows":101624},"[{\"label\":\"Subpath\",\"value\":\"`@noy-db\u002Fhub\u002Fattestation`\"},{\"label\":\"Factory\",\"value\":\"`withAttestation()`\"},{\"label\":\"Layer\",\"value\":\"`with-audit`\"}]",[1022,101626,29857],{"id":29856},[937,101628,101629,101630,101632,101633,101635,101636,101639,101640,44332,101642,101645],{},"Attestation lets a vault owner prove that a document — an invoice, a certificate, a\nreceipt — is authentic and unaltered, without running a verification server and without\nrevealing the vault's data. A ",[950,101631,2694],{}," is noy-db's end-to-end-encrypted document store\n(see ",[1007,101634,65376],{"href":48},"); this service adds an\n",[950,101637,101638],{},"issue side"," to it: ",[941,101641,21548],{},[950,101643,101644],{},"per-field commitment"," — one\nsalted SHA-256 hash per declared field, so each hash proves a value without revealing\nit — signs the set with the vault's Ed25519 document-signing key, and packs the result\ninto a compact string you render as a QR code on the document itself.",[937,101647,960,101648,101651,101652,101654,101655,2601,101657,101660,101661,101663],{},[950,101649,101650],{},"verify side"," is the standalone, dependency-free ",[941,101653,35864],{}," package:\nanyone holding the paper recomputes the hashes from the values they can read off it and\nchecks the signature against the issuer's published public key — fully offline, no\nnoy-db, no network. The service also maintains a revocation set (",[941,101656,67832],{},[941,101658,101659],{},"unrevokeAttestation",") and can publish it as a signed ",[950,101662,67815],{}," verifiers can\ncheck against. Like every opt-in service, the issue\u002Frevoke engines are dynamically\nimported and stay out of your bundle until you opt in.",[1022,101665,70093],{"id":70092},[1111,101667,101668,101676],{},[1114,101669,101670,101675],{},[950,101671,5093,101672,101674],{},[941,101673,609],{}," schema on the collection"," — the issuer side signs only fields the collection-level schema declares.",[1114,101677,101678,101681,101682,101684],{},[950,101679,101680],{},"A published Ed25519 public key"," — verifiers check signatures against it, so you must publish it somewhere verifiers can fetch out-of-band (a well-known URL, a QR poster). The verifying party itself needs none of this page: only the zero-dependency ",[941,101683,35864],{}," package, the field schema, and that key.",[1022,101686,99864],{"id":99863},[937,101688,101689,101690,101692,101693,101696,101697,101699],{},"Pass the factory to ",[941,101691,12780],{},", then declare ",[1092,101694,101695],{},"which fields"," are attestable per\ncollection with the ",[941,101698,609],{}," option:",[2217,101701,101703],{"className":2219,"code":101702,"language":2221,"meta":1037,"style":1037},"import { createNoydb } from '@noy-db\u002Fhub'\nimport { withAttestation } from '@noy-db\u002Fhub\u002Fattestation'\n\ninterface Invoice { id: string; invoiceNo: string; total: number; issueDate: string }\n\nconst attestation = {\n  fields: [\n    { path: 'invoiceNo', normalize: 'alnum-upper' as const },\n    { path: 'total',     normalize: 'cents'       as const },\n    { path: 'issueDate', normalize: 'iso-date'    as const },\n  ],\n}\n\nconst db = await createNoydb({\n  user: 'firm',                            \u002F\u002F omitting `store` uses the built-in in-memory store;\n  secret: 'firm-secret-2026',          \u002F\u002F pass to-file \u002F to-postgres \u002F … in production\n  attestationStrategy: withAttestation(),\n})\nconst vault = await db.openVault('books')\nconst invoices = vault.collection\u003CInvoice>('invoices', { attestation })\n",[941,101704,101705,101723,101741,101745,101785,101789,101799,101807,101839,101871,101903,101909,101913,101917,101933,101949,101965,101977,101983,102009],{"__ignoreMap":1037},[2225,101706,101707,101709,101711,101713,101715,101717,101719,101721],{"class":2227,"line":2228},[2225,101708,2232],{"class":2231},[2225,101710,2236],{"class":2235},[2225,101712,2744],{"class":2239},[2225,101714,2243],{"class":2235},[2225,101716,2246],{"class":2231},[2225,101718,2249],{"class":2235},[2225,101720,1086],{"class":2252},[2225,101722,2256],{"class":2235},[2225,101724,101725,101727,101729,101731,101733,101735,101737,101739],{"class":2227,"line":1041},[2225,101726,2232],{"class":2231},[2225,101728,2236],{"class":2235},[2225,101730,65469],{"class":2239},[2225,101732,2243],{"class":2235},[2225,101734,2246],{"class":2231},[2225,101736,2249],{"class":2235},[2225,101738,65350],{"class":2252},[2225,101740,2256],{"class":2235},[2225,101742,101743],{"class":2227,"line":1038},[2225,101744,2285],{"emptyLinePlaceholder":2284},[2225,101746,101747,101749,101751,101753,101755,101757,101759,101761,101763,101765,101767,101769,101771,101773,101775,101777,101779,101781,101783],{"class":2227,"line":2288},[2225,101748,6994],{"class":2774},[2225,101750,6343],{"class":6342},[2225,101752,2236],{"class":2235},[2225,101754,7070],{"class":2307},[2225,101756,2311],{"class":2235},[2225,101758,6366],{"class":6342},[2225,101760,3769],{"class":2235},[2225,101762,27739],{"class":2307},[2225,101764,2311],{"class":2235},[2225,101766,6366],{"class":6342},[2225,101768,3769],{"class":2235},[2225,101770,27452],{"class":2307},[2225,101772,2311],{"class":2235},[2225,101774,6356],{"class":6342},[2225,101776,3769],{"class":2235},[2225,101778,65518],{"class":2307},[2225,101780,2311],{"class":2235},[2225,101782,6366],{"class":6342},[2225,101784,6369],{"class":2235},[2225,101786,101787],{"class":2227,"line":2304},[2225,101788,2285],{"emptyLinePlaceholder":2284},[2225,101790,101791,101793,101795,101797],{"class":2227,"line":2356},[2225,101792,2775],{"class":2774},[2225,101794,65535],{"class":2239},[2225,101796,2781],{"class":2235},[2225,101798,2301],{"class":2235},[2225,101800,101801,101803,101805],{"class":2227,"line":2830},[2225,101802,57522],{"class":2307},[2225,101804,2311],{"class":2235},[2225,101806,32491],{"class":2239},[2225,101808,101809,101811,101813,101815,101817,101819,101821,101823,101825,101827,101829,101831,101833,101835,101837],{"class":2227,"line":2861},[2225,101810,32496],{"class":2235},[2225,101812,53037],{"class":2307},[2225,101814,2311],{"class":2235},[2225,101816,2249],{"class":2235},[2225,101818,27925],{"class":2252},[2225,101820,2805],{"class":2235},[2225,101822,2298],{"class":2235},[2225,101824,65566],{"class":2307},[2225,101826,2311],{"class":2235},[2225,101828,2249],{"class":2235},[2225,101830,65573],{"class":2252},[2225,101832,2805],{"class":2235},[2225,101834,2266],{"class":2231},[2225,101836,8083],{"class":2774},[2225,101838,2353],{"class":2235},[2225,101840,101841,101843,101845,101847,101849,101851,101853,101855,101857,101859,101861,101863,101865,101867,101869],{"class":2227,"line":4246},[2225,101842,32496],{"class":2235},[2225,101844,53037],{"class":2307},[2225,101846,2311],{"class":2235},[2225,101848,2249],{"class":2235},[2225,101850,64200],{"class":2252},[2225,101852,2805],{"class":2235},[2225,101854,2298],{"class":2235},[2225,101856,65600],{"class":2307},[2225,101858,2311],{"class":2235},[2225,101860,2249],{"class":2235},[2225,101862,65607],{"class":2252},[2225,101864,2805],{"class":2235},[2225,101866,65612],{"class":2231},[2225,101868,8083],{"class":2774},[2225,101870,2353],{"class":2235},[2225,101872,101873,101875,101877,101879,101881,101883,101885,101887,101889,101891,101893,101895,101897,101899,101901],{"class":2227,"line":4253},[2225,101874,32496],{"class":2235},[2225,101876,53037],{"class":2307},[2225,101878,2311],{"class":2235},[2225,101880,2249],{"class":2235},[2225,101882,65629],{"class":2252},[2225,101884,2805],{"class":2235},[2225,101886,2298],{"class":2235},[2225,101888,65566],{"class":2307},[2225,101890,2311],{"class":2235},[2225,101892,2249],{"class":2235},[2225,101894,65642],{"class":2252},[2225,101896,2805],{"class":2235},[2225,101898,65647],{"class":2231},[2225,101900,8083],{"class":2774},[2225,101902,2353],{"class":2235},[2225,101904,101905,101907],{"class":2227,"line":4259},[2225,101906,32588],{"class":2239},[2225,101908,1707],{"class":2235},[2225,101910,101911],{"class":2227,"line":6548},[2225,101912,7351],{"class":2235},[2225,101914,101915],{"class":2227,"line":6576},[2225,101916,2285],{"emptyLinePlaceholder":2284},[2225,101918,101919,101921,101923,101925,101927,101929,101931],{"class":2227,"line":6582},[2225,101920,2775],{"class":2774},[2225,101922,2778],{"class":2239},[2225,101924,2781],{"class":2235},[2225,101926,2784],{"class":2231},[2225,101928,2744],{"class":2291},[2225,101930,2789],{"class":2239},[2225,101932,4200],{"class":2235},[2225,101934,101935,101937,101939,101941,101943,101945,101947],{"class":2227,"line":6587},[2225,101936,4205],{"class":2307},[2225,101938,2311],{"class":2235},[2225,101940,2249],{"class":2235},[2225,101942,39709],{"class":2252},[2225,101944,2805],{"class":2235},[2225,101946,2298],{"class":2235},[2225,101948,65698],{"class":2763},[2225,101950,101951,101953,101955,101957,101959,101961,101963],{"class":2227,"line":7174},[2225,101952,10423],{"class":2307},[2225,101954,2311],{"class":2235},[2225,101956,2249],{"class":2235},[2225,101958,65709],{"class":2252},[2225,101960,2805],{"class":2235},[2225,101962,2298],{"class":2235},[2225,101964,65716],{"class":2763},[2225,101966,101967,101969,101971,101973,101975],{"class":2227,"line":7180},[2225,101968,65721],{"class":2307},[2225,101970,2311],{"class":2235},[2225,101972,65469],{"class":2291},[2225,101974,4228],{"class":2239},[2225,101976,1707],{"class":2235},[2225,101978,101979,101981],{"class":2227,"line":7220},[2225,101980,2359],{"class":2235},[2225,101982,2362],{"class":2239},[2225,101984,101985,101987,101989,101991,101993,101995,101997,101999,102001,102003,102005,102007],{"class":2227,"line":7225},[2225,101986,2775],{"class":2774},[2225,101988,2835],{"class":2239},[2225,101990,2781],{"class":2235},[2225,101992,2784],{"class":2231},[2225,101994,2842],{"class":2239},[2225,101996,1520],{"class":2235},[2225,101998,2847],{"class":2291},[2225,102000,2789],{"class":2239},[2225,102002,2805],{"class":2235},[2225,102004,65761],{"class":2252},[2225,102006,2805],{"class":2235},[2225,102008,2362],{"class":2239},[2225,102010,102011,102013,102015,102017,102019,102021,102023,102025,102027,102029,102031,102033,102035,102037,102039,102041,102043,102045],{"class":2227,"line":7231},[2225,102012,2775],{"class":2774},[2225,102014,6461],{"class":2239},[2225,102016,2781],{"class":2235},[2225,102018,6466],{"class":2239},[2225,102020,1520],{"class":2235},[2225,102022,6471],{"class":2291},[2225,102024,6474],{"class":2235},[2225,102026,6477],{"class":6342},[2225,102028,6480],{"class":2235},[2225,102030,2789],{"class":2239},[2225,102032,2805],{"class":2235},[2225,102034,6487],{"class":2252},[2225,102036,2805],{"class":2235},[2225,102038,2298],{"class":2235},[2225,102040,2236],{"class":2235},[2225,102042,65535],{"class":2239},[2225,102044,2359],{"class":2235},[2225,102046,2362],{"class":2239},[937,102048,102049,102050,102053,102054,102056,102057,102059,102060,957],{},"Each field spec pairs a dot-path with a ",[950,102051,102052],{},"normalizer"," — a canonicalization rule applied\nidentically on issue and on verify, so the human-typed ",[941,102055,65428],{}," later matches the\nstored number ",[941,102058,65432],{}," (see the ",[1007,102061,102063],{"href":102062},"#normalize","normalizer table",[1022,102065,29893],{"id":29892},[1591,102067,102069],{"id":102068},"issue-a-qr-credential","Issue a QR credential",[2217,102071,102073],{"className":2219,"code":102072,"language":2221,"meta":1037,"style":1037},"await invoices.put('inv-1', {\n  id: 'inv-1', invoiceNo: 'INV-1042', total: 1234.5, issueDate: '2026-05-29',\n})\n\nconst { docId, qr, keyId, publicKeyB64 } = await vault.issueAttestation('invoices', 'inv-1')\n\u002F\u002F → docId       : '01JZK7Y1Q2M4N8R3T5V7X9ZABC'  — 26-char id of THIS attestation (used for revocation)\n\u002F\u002F → qr          : 'eyJ2IjoxLCJkb2NJZCI6...'     — base64url payload; render it as a QR on the document\n\u002F\u002F → keyId       : 'bd7e8268071ac41f'            — identifies the signing key\n\u002F\u002F → publicKeyB64: 'hpbs5UQivCLgPolFRoj5h1Q1…'   — the matching Ed25519 public key\n",[941,102074,102075,102097,102143,102149,102153,102203,102208,102213,102218],{"__ignoreMap":1037},[2225,102076,102077,102079,102081,102083,102085,102087,102089,102091,102093,102095],{"class":2227,"line":2228},[2225,102078,6500],{"class":2231},[2225,102080,6503],{"class":2239},[2225,102082,1520],{"class":2235},[2225,102084,3161],{"class":2291},[2225,102086,2789],{"class":2239},[2225,102088,2805],{"class":2235},[2225,102090,6514],{"class":2252},[2225,102092,2805],{"class":2235},[2225,102094,2298],{"class":2235},[2225,102096,2301],{"class":2235},[2225,102098,102099,102101,102103,102105,102107,102109,102111,102113,102115,102117,102119,102121,102123,102125,102127,102129,102131,102133,102135,102137,102139,102141],{"class":2227,"line":1041},[2225,102100,28371],{"class":2307},[2225,102102,2311],{"class":2235},[2225,102104,2249],{"class":2235},[2225,102106,6514],{"class":2252},[2225,102108,2805],{"class":2235},[2225,102110,2298],{"class":2235},[2225,102112,27739],{"class":2307},[2225,102114,2311],{"class":2235},[2225,102116,2249],{"class":2235},[2225,102118,65852],{"class":2252},[2225,102120,2805],{"class":2235},[2225,102122,2298],{"class":2235},[2225,102124,27452],{"class":2307},[2225,102126,2311],{"class":2235},[2225,102128,65863],{"class":6527},[2225,102130,2298],{"class":2235},[2225,102132,65518],{"class":2307},[2225,102134,2311],{"class":2235},[2225,102136,2249],{"class":2235},[2225,102138,65874],{"class":2252},[2225,102140,2805],{"class":2235},[2225,102142,1707],{"class":2235},[2225,102144,102145,102147],{"class":2227,"line":1038},[2225,102146,2359],{"class":2235},[2225,102148,2362],{"class":2239},[2225,102150,102151],{"class":2227,"line":2288},[2225,102152,2285],{"emptyLinePlaceholder":2284},[2225,102154,102155,102157,102159,102161,102163,102165,102167,102169,102171,102173,102175,102177,102179,102181,102183,102185,102187,102189,102191,102193,102195,102197,102199,102201],{"class":2227,"line":2304},[2225,102156,2775],{"class":2774},[2225,102158,2236],{"class":2235},[2225,102160,65897],{"class":2239},[2225,102162,2298],{"class":2235},[2225,102164,65902],{"class":2239},[2225,102166,2298],{"class":2235},[2225,102168,66797],{"class":2239},[2225,102170,2298],{"class":2235},[2225,102172,65966],{"class":2239},[2225,102174,2359],{"class":2235},[2225,102176,6346],{"class":2235},[2225,102178,2784],{"class":2231},[2225,102180,6466],{"class":2239},[2225,102182,1520],{"class":2235},[2225,102184,65920],{"class":2291},[2225,102186,2789],{"class":2239},[2225,102188,2805],{"class":2235},[2225,102190,6487],{"class":2252},[2225,102192,2805],{"class":2235},[2225,102194,2298],{"class":2235},[2225,102196,2249],{"class":2235},[2225,102198,6514],{"class":2252},[2225,102200,2805],{"class":2235},[2225,102202,2362],{"class":2239},[2225,102204,102205],{"class":2227,"line":2356},[2225,102206,102207],{"class":2763},"\u002F\u002F → docId       : '01JZK7Y1Q2M4N8R3T5V7X9ZABC'  — 26-char id of THIS attestation (used for revocation)\n",[2225,102209,102210],{"class":2227,"line":2830},[2225,102211,102212],{"class":2763},"\u002F\u002F → qr          : 'eyJ2IjoxLCJkb2NJZCI6...'     — base64url payload; render it as a QR on the document\n",[2225,102214,102215],{"class":2227,"line":2861},[2225,102216,102217],{"class":2763},"\u002F\u002F → keyId       : 'bd7e8268071ac41f'            — identifies the signing key\n",[2225,102219,102220],{"class":2227,"line":4246},[2225,102221,102222],{"class":2763},"\u002F\u002F → publicKeyB64: 'hpbs5UQivCLgPolFRoj5h1Q1…'   — the matching Ed25519 public key\n",[937,102224,102225,102226,102228],{},"The first issuance lazily mints the vault's Ed25519 document-signing keypair and\npersists it encrypted inside the vault; every later issuance reuses it, so ",[941,102227,66012],{},"\nstays stable. Publish the public key wherever verifiers can fetch it once (your\nwebsite, the verifier page itself):",[2217,102230,102232],{"className":2219,"code":102231,"language":2221,"meta":1037,"style":1037},"const pub = await vault.getDocumentSigningPublicKey()\n\u002F\u002F → { keyId: 'bd7e8268071ac41f', publicKeyB64: 'hpbs5UQivCLgPolFRoj5h1Q12OCXFlZkZfff-mEiAPM' }\n",[941,102233,102234,102253],{"__ignoreMap":1037},[2225,102235,102236,102238,102241,102243,102245,102247,102249,102251],{"class":2227,"line":2228},[2225,102237,2775],{"class":2774},[2225,102239,102240],{"class":2239}," pub ",[2225,102242,2781],{"class":2235},[2225,102244,2784],{"class":2231},[2225,102246,6466],{"class":2239},[2225,102248,1520],{"class":2235},[2225,102250,65979],{"class":2291},[2225,102252,8146],{"class":2239},[2225,102254,102255],{"class":2227,"line":1041},[2225,102256,65986],{"class":2763},[1591,102258,102260],{"id":102259},"verify-offline-without-noy-db","Verify — offline, without noy-db",[937,102262,102263,102264,102266],{},"Verification needs only ",[941,102265,35864],{},", the field schema, and the published\npublic key. It recomputes each field hash from the claimed values and checks the\nsignature:",[2217,102268,102270],{"className":2219,"code":102269,"language":2221,"meta":1037,"style":1037},"import { verifyAttestation } from '@noy-db\u002Fattestation'\n\nconst result = await verifyAttestation({\n  qr,                                                    \u002F\u002F scanned from the document\n  claimedFields: { invoiceNo: 'INV-1042', total: 1234.5, issueDate: '2026-05-29' },\n  fieldSchema: attestation,\n  publicKeys: { 'bd7e8268071ac41f': 'hpbs5UQivCLgPolFRoj5h1Q12OCXFlZkZfff-mEiAPM' },\n})\n",[941,102271,102272,102290,102294,102310,102319,102359,102369,102394],{"__ignoreMap":1037},[2225,102273,102274,102276,102278,102280,102282,102284,102286,102288],{"class":2227,"line":2228},[2225,102275,2232],{"class":2231},[2225,102277,2236],{"class":2235},[2225,102279,66272],{"class":2239},[2225,102281,2243],{"class":2235},[2225,102283,2246],{"class":2231},[2225,102285,2249],{"class":2235},[2225,102287,35864],{"class":2252},[2225,102289,2256],{"class":2235},[2225,102291,102292],{"class":2227,"line":1041},[2225,102293,2285],{"emptyLinePlaceholder":2284},[2225,102295,102296,102298,102300,102302,102304,102306,102308],{"class":2227,"line":1038},[2225,102297,2775],{"class":2774},[2225,102299,36920],{"class":2239},[2225,102301,2781],{"class":2235},[2225,102303,2784],{"class":2231},[2225,102305,66272],{"class":2291},[2225,102307,2789],{"class":2239},[2225,102309,4200],{"class":2235},[2225,102311,102312,102314,102316],{"class":2227,"line":2288},[2225,102313,66576],{"class":2239},[2225,102315,2298],{"class":2235},[2225,102317,102318],{"class":2763},"                                                    \u002F\u002F scanned from the document\n",[2225,102320,102321,102323,102325,102327,102329,102331,102333,102335,102337,102339,102341,102343,102345,102347,102349,102351,102353,102355,102357],{"class":2227,"line":2304},[2225,102322,66587],{"class":2307},[2225,102324,2311],{"class":2235},[2225,102326,2236],{"class":2235},[2225,102328,27739],{"class":2307},[2225,102330,2311],{"class":2235},[2225,102332,2249],{"class":2235},[2225,102334,65852],{"class":2252},[2225,102336,2805],{"class":2235},[2225,102338,2298],{"class":2235},[2225,102340,27452],{"class":2307},[2225,102342,2311],{"class":2235},[2225,102344,65863],{"class":6527},[2225,102346,2298],{"class":2235},[2225,102348,65518],{"class":2307},[2225,102350,2311],{"class":2235},[2225,102352,2249],{"class":2235},[2225,102354,65874],{"class":2252},[2225,102356,2805],{"class":2235},[2225,102358,2353],{"class":2235},[2225,102360,102361,102363,102365,102367],{"class":2227,"line":2356},[2225,102362,66357],{"class":2307},[2225,102364,2311],{"class":2235},[2225,102366,67441],{"class":2239},[2225,102368,1707],{"class":2235},[2225,102370,102371,102373,102375,102377,102379,102381,102383,102385,102387,102390,102392],{"class":2227,"line":2830},[2225,102372,66335],{"class":2307},[2225,102374,2311],{"class":2235},[2225,102376,2236],{"class":2235},[2225,102378,2249],{"class":2235},[2225,102380,66154],{"class":2307},[2225,102382,2805],{"class":2235},[2225,102384,2311],{"class":2235},[2225,102386,2249],{"class":2235},[2225,102388,102389],{"class":2252},"hpbs5UQivCLgPolFRoj5h1Q12OCXFlZkZfff-mEiAPM",[2225,102391,2805],{"class":2235},[2225,102393,2353],{"class":2235},[2225,102395,102396,102398],{"class":2227,"line":2861},[2225,102397,2359],{"class":2235},[2225,102399,2362],{"class":2239},[2217,102401,102403],{"className":6604,"code":102402,"language":141,"meta":1037,"style":1037},"{\n  \"valid\": true,\n  \"signatureValid\": true,\n  \"perField\": [\n    { \"path\": \"invoiceNo\", \"match\": true },\n    { \"path\": \"total\", \"match\": true },\n    { \"path\": \"issueDate\", \"match\": true }\n  ],\n  \"revoked\": null\n}\n",[941,102404,102405,102409,102421,102433,102445,102477,102509,102541,102545,102557],{"__ignoreMap":1037},[2225,102406,102407],{"class":2227,"line":2228},[2225,102408,4200],{"class":2235},[2225,102410,102411,102413,102415,102417,102419],{"class":2227,"line":1041},[2225,102412,9368],{"class":2235},[2225,102414,45389],{"class":2774},[2225,102416,6620],{"class":2235},[2225,102418,2311],{"class":2235},[2225,102420,69451],{"class":2235},[2225,102422,102423,102425,102427,102429,102431],{"class":2227,"line":1038},[2225,102424,9368],{"class":2235},[2225,102426,67172],{"class":2774},[2225,102428,6620],{"class":2235},[2225,102430,2311],{"class":2235},[2225,102432,69451],{"class":2235},[2225,102434,102435,102437,102439,102441,102443],{"class":2227,"line":2288},[2225,102436,9368],{"class":2235},[2225,102438,67048],{"class":2774},[2225,102440,6620],{"class":2235},[2225,102442,2311],{"class":2235},[2225,102444,32491],{"class":2235},[2225,102446,102447,102449,102451,102453,102455,102457,102459,102461,102463,102465,102467,102469,102471,102473,102475],{"class":2227,"line":2304},[2225,102448,32496],{"class":2235},[2225,102450,6614],{"class":2235},[2225,102452,52937],{"class":6342},[2225,102454,6620],{"class":2235},[2225,102456,2311],{"class":2235},[2225,102458,6614],{"class":2235},[2225,102460,27925],{"class":2252},[2225,102462,6620],{"class":2235},[2225,102464,2298],{"class":2235},[2225,102466,6614],{"class":2235},[2225,102468,7482],{"class":6342},[2225,102470,6620],{"class":2235},[2225,102472,2311],{"class":2235},[2225,102474,2322],{"class":2235},[2225,102476,2353],{"class":2235},[2225,102478,102479,102481,102483,102485,102487,102489,102491,102493,102495,102497,102499,102501,102503,102505,102507],{"class":2227,"line":2356},[2225,102480,32496],{"class":2235},[2225,102482,6614],{"class":2235},[2225,102484,52937],{"class":6342},[2225,102486,6620],{"class":2235},[2225,102488,2311],{"class":2235},[2225,102490,6614],{"class":2235},[2225,102492,64200],{"class":2252},[2225,102494,6620],{"class":2235},[2225,102496,2298],{"class":2235},[2225,102498,6614],{"class":2235},[2225,102500,7482],{"class":6342},[2225,102502,6620],{"class":2235},[2225,102504,2311],{"class":2235},[2225,102506,2322],{"class":2235},[2225,102508,2353],{"class":2235},[2225,102510,102511,102513,102515,102517,102519,102521,102523,102525,102527,102529,102531,102533,102535,102537,102539],{"class":2227,"line":2830},[2225,102512,32496],{"class":2235},[2225,102514,6614],{"class":2235},[2225,102516,52937],{"class":6342},[2225,102518,6620],{"class":2235},[2225,102520,2311],{"class":2235},[2225,102522,6614],{"class":2235},[2225,102524,65629],{"class":2252},[2225,102526,6620],{"class":2235},[2225,102528,2298],{"class":2235},[2225,102530,6614],{"class":2235},[2225,102532,7482],{"class":6342},[2225,102534,6620],{"class":2235},[2225,102536,2311],{"class":2235},[2225,102538,2322],{"class":2235},[2225,102540,6369],{"class":2235},[2225,102542,102543],{"class":2227,"line":2861},[2225,102544,67686],{"class":2235},[2225,102546,102547,102549,102551,102553,102555],{"class":2227,"line":4246},[2225,102548,9368],{"class":2235},[2225,102550,67228],{"class":2774},[2225,102552,6620],{"class":2235},[2225,102554,2311],{"class":2235},[2225,102556,8384],{"class":2235},[2225,102558,102559],{"class":2227,"line":4253},[2225,102560,7351],{"class":2235},[937,102562,102563,7356,102565,102567,102568,102570,102571,102573,102574,28806,102576,102579,102580,102582,102583,102586,102587,1520],{},[941,102564,67228],{},[941,102566,6651],{}," because no revocation list was supplied. A tampered value flips its\n",[941,102569,67048],{}," entry (and ",[941,102572,45389],{},") to ",[941,102575,2560],{},[941,102577,102578],{},"reason: 'field mismatch'","; an unknown\n",[941,102581,66012],{}," yields ",[941,102584,102585],{},"reason: 'unknown keyId'",". For a complete static-HTML verifier page, see\nthe ",[1007,102588,68371],{"href":516},[1591,102590,102592],{"id":102591},"revoke-and-publish-a-revocation-list","Revoke and publish a revocation list",[937,102594,102595,102596,102598],{},"Revocation is a membership set keyed by ",[941,102597,66086],{},", kept encrypted in the vault and\npublished as a signed list:",[2217,102600,102602],{"className":2219,"code":102601,"language":2221,"meta":1037,"style":1037},"await vault.revokeAttestation(docId)\n\nawait vault.getRevokedDocIds()\n\u002F\u002F → ['01JZK7Y1Q2M4N8R3T5V7X9ZABC']\n\nconst list = await vault.publishRevocationList()\n",[941,102603,102604,102617,102621,102634,102639,102643],{"__ignoreMap":1037},[2225,102605,102606,102608,102610,102612,102614],{"class":2227,"line":2228},[2225,102607,6500],{"class":2231},[2225,102609,6466],{"class":2239},[2225,102611,1520],{"class":2235},[2225,102613,67832],{"class":2291},[2225,102615,102616],{"class":2239},"(docId)\n",[2225,102618,102619],{"class":2227,"line":1041},[2225,102620,2285],{"emptyLinePlaceholder":2284},[2225,102622,102623,102625,102627,102629,102632],{"class":2227,"line":1038},[2225,102624,6500],{"class":2231},[2225,102626,6466],{"class":2239},[2225,102628,1520],{"class":2235},[2225,102630,102631],{"class":2291},"getRevokedDocIds",[2225,102633,8146],{"class":2239},[2225,102635,102636],{"class":2227,"line":2288},[2225,102637,102638],{"class":2763},"\u002F\u002F → ['01JZK7Y1Q2M4N8R3T5V7X9ZABC']\n",[2225,102640,102641],{"class":2227,"line":2304},[2225,102642,2285],{"emptyLinePlaceholder":2284},[2225,102644,102645,102647,102649,102651,102653,102655,102657,102659],{"class":2227,"line":2356},[2225,102646,2775],{"class":2774},[2225,102648,67845],{"class":2239},[2225,102650,2781],{"class":2235},[2225,102652,2784],{"class":2231},[2225,102654,6466],{"class":2239},[2225,102656,1520],{"class":2235},[2225,102658,67856],{"class":2291},[2225,102660,8146],{"class":2239},[2217,102662,102664],{"className":6604,"code":102663,"language":141,"meta":1037,"style":1037},"{\n  \"v\": 1,\n  \"revokedDocIds\": [\"01JZK7Y1Q2M4N8R3T5V7X9ZABC\"],\n  \"asOf\": \"2026-07-08T09:30:00.000Z\",\n  \"keyId\": \"bd7e8268071ac41f\",\n  \"sig\": \"0ghYQP_bd5O6qDth33-Xz08Q7_JbL96z…\"\n}\n",[941,102665,102666,102670,102684,102704,102722,102740,102757],{"__ignoreMap":1037},[2225,102667,102668],{"class":2227,"line":2228},[2225,102669,4200],{"class":2235},[2225,102671,102672,102674,102676,102678,102680,102682],{"class":2227,"line":1041},[2225,102673,9368],{"class":2235},[2225,102675,8160],{"class":2774},[2225,102677,6620],{"class":2235},[2225,102679,2311],{"class":2235},[2225,102681,9377],{"class":6527},[2225,102683,1707],{"class":2235},[2225,102685,102686,102688,102690,102692,102694,102696,102698,102700,102702],{"class":2227,"line":1038},[2225,102687,9368],{"class":2235},[2225,102689,67888],{"class":2774},[2225,102691,6620],{"class":2235},[2225,102693,2311],{"class":2235},[2225,102695,8667],{"class":2235},[2225,102697,6620],{"class":2235},[2225,102699,66095],{"class":2252},[2225,102701,6620],{"class":2235},[2225,102703,66199],{"class":2235},[2225,102705,102706,102708,102710,102712,102714,102716,102718,102720],{"class":2227,"line":2288},[2225,102707,9368],{"class":2235},[2225,102709,67909],{"class":2774},[2225,102711,6620],{"class":2235},[2225,102713,2311],{"class":2235},[2225,102715,6614],{"class":2235},[2225,102717,9408],{"class":2252},[2225,102719,6620],{"class":2235},[2225,102721,1707],{"class":2235},[2225,102723,102724,102726,102728,102730,102732,102734,102736,102738],{"class":2227,"line":2304},[2225,102725,9368],{"class":2235},[2225,102727,66012],{"class":2774},[2225,102729,6620],{"class":2235},[2225,102731,2311],{"class":2235},[2225,102733,6614],{"class":2235},[2225,102735,66154],{"class":2252},[2225,102737,6620],{"class":2235},[2225,102739,1707],{"class":2235},[2225,102741,102742,102744,102746,102748,102750,102752,102755],{"class":2227,"line":2356},[2225,102743,9368],{"class":2235},[2225,102745,66206],{"class":2774},[2225,102747,6620],{"class":2235},[2225,102749,2311],{"class":2235},[2225,102751,6614],{"class":2235},[2225,102753,102754],{"class":2252},"0ghYQP_bd5O6qDth33-Xz08Q7_JbL96z…",[2225,102756,9449],{"class":2235},[2225,102758,102759],{"class":2227,"line":2830},[2225,102760,7351],{"class":2235},[937,102762,102763,102764,2311],{},"Host that JSON anywhere (it contains only opaque ids). Verifiers first check the list's\nown signature, then pass it into ",[941,102765,66243],{},[2217,102767,102769],{"className":2219,"code":102768,"language":2221,"meta":1037,"style":1037},"import { verifyRevocationList, verifyAttestation } from '@noy-db\u002Fattestation'\n\nconst trusted = await verifyRevocationList(list, publicKeyB64)\n\u002F\u002F → true\n\nconst r = await verifyAttestation({ qr, claimedFields, fieldSchema, publicKeys, revocation: { list } })\n\u002F\u002F → { valid: false, signatureValid: true, perField: [...all true], revoked: true, reason: 'revoked' }\n",[941,102770,102771,102793,102797,102818,102822,102826,102875],{"__ignoreMap":1037},[2225,102772,102773,102775,102777,102779,102781,102783,102785,102787,102789,102791],{"class":2227,"line":2228},[2225,102774,2232],{"class":2231},[2225,102776,2236],{"class":2235},[2225,102778,66277],{"class":2239},[2225,102780,2298],{"class":2235},[2225,102782,66272],{"class":2239},[2225,102784,2243],{"class":2235},[2225,102786,2246],{"class":2231},[2225,102788,2249],{"class":2235},[2225,102790,35864],{"class":2252},[2225,102792,2256],{"class":2235},[2225,102794,102795],{"class":2227,"line":1041},[2225,102796,2285],{"emptyLinePlaceholder":2284},[2225,102798,102799,102801,102804,102806,102808,102810,102813,102815],{"class":2227,"line":1038},[2225,102800,2775],{"class":2774},[2225,102802,102803],{"class":2239}," trusted ",[2225,102805,2781],{"class":2235},[2225,102807,2784],{"class":2231},[2225,102809,66277],{"class":2291},[2225,102811,102812],{"class":2239},"(list",[2225,102814,2298],{"class":2235},[2225,102816,102817],{"class":2239}," publicKeyB64)\n",[2225,102819,102820],{"class":2227,"line":2288},[2225,102821,77590],{"class":2763},[2225,102823,102824],{"class":2227,"line":2304},[2225,102825,2285],{"emptyLinePlaceholder":2284},[2225,102827,102828,102830,102832,102834,102836,102838,102840,102842,102844,102846,102849,102851,102854,102856,102859,102861,102863,102865,102867,102869,102871,102873],{"class":2227,"line":2356},[2225,102829,2775],{"class":2774},[2225,102831,27875],{"class":2239},[2225,102833,2781],{"class":2235},[2225,102835,2784],{"class":2231},[2225,102837,66272],{"class":2291},[2225,102839,2789],{"class":2239},[2225,102841,2792],{"class":2235},[2225,102843,65902],{"class":2239},[2225,102845,2298],{"class":2235},[2225,102847,102848],{"class":2239}," claimedFields",[2225,102850,2298],{"class":2235},[2225,102852,102853],{"class":2239}," fieldSchema",[2225,102855,2298],{"class":2235},[2225,102857,102858],{"class":2239}," publicKeys",[2225,102860,2298],{"class":2235},[2225,102862,66995],{"class":2307},[2225,102864,2311],{"class":2235},[2225,102866,2236],{"class":2235},[2225,102868,67845],{"class":2239},[2225,102870,2359],{"class":2235},[2225,102872,2243],{"class":2235},[2225,102874,2362],{"class":2239},[2225,102876,102877],{"class":2227,"line":2830},[2225,102878,102879],{"class":2763},"\u002F\u002F → { valid: false, signatureValid: true, perField: [...all true], revoked: true, reason: 'revoked' }\n",[937,102881,102882,102885],{},[941,102883,102884],{},"vault.unrevokeAttestation(docId)"," removes an id from the set again.",[1022,102887,102889],{"id":102888},"api-reference","API reference",[1591,102891,102893,102894,1381],{"id":102892},"vault-methods-enabled-by-withattestation","Vault methods (enabled by ",[941,102895,21543],{},[1338,102897,102898,102909],{},[1341,102899,102900],{},[1344,102901,102902,102904,102907],{},[1347,102903,7831],{},[1347,102905,102906],{},"Returns",[1347,102908,7834],{},[1357,102910,102911,102928,102942,102962,102975,102989],{},[1344,102912,102913,102918,102922],{},[1362,102914,102915],{},[941,102916,102917],{},"vault.issueAttestation(collection, id)",[1362,102919,102920],{},[941,102921,68478],{},[1362,102923,102924,102925,1520],{},"Mint and sign a per-field commitment for a stored record; ",[950,102926,102927],{},"owner role only",[1344,102929,102930,102934,102939],{},[1362,102931,102932],{},[941,102933,69093],{},[1362,102935,102936],{},[941,102937,102938],{},"{ keyId, publicKeyB64 }",[1362,102940,102941],{},"Read the vault's signing public key. Any role may read an existing key; only the owner may trigger the first-time mint.",[1344,102943,102944,102949,102953],{},[1362,102945,102946],{},[941,102947,102948],{},"vault.revokeAttestation(docId)",[1362,102950,102951],{},[941,102952,7165],{},[1362,102954,102955,102956,102958,102959,102961],{},"Add an issued attestation to the revocation set; owner only. Throws ",[941,102957,66008],{}," if ",[941,102960,66086],{}," was never issued by this vault.",[1344,102963,102964,102968,102972],{},[1362,102965,102966],{},[941,102967,102884],{},[1362,102969,102970],{},[941,102971,7165],{},[1362,102973,102974],{},"Remove an id from the revocation set; owner only.",[1344,102976,102977,102982,102986],{},[1362,102978,102979],{},[941,102980,102981],{},"vault.getRevokedDocIds()",[1362,102983,102984],{},[941,102985,22829],{},[1362,102987,102988],{},"The current revoked ids, sorted.",[1344,102990,102991,102996,103001],{},[1362,102992,102993],{},[941,102994,102995],{},"vault.publishRevocationList()",[1362,102997,102998],{},[941,102999,103000],{},"RevocationList",[1362,103002,103003,103004,103007],{},"Sign the current set as a publishable ",[941,103005,103006],{},"{ v, revokedDocIds, asOf, keyId, sig }"," document; owner only.",[1591,103009,103011],{"id":103010},"attestation-1",[941,103012,609],{},[937,103014,103015,103018,103019,11737,103022,103025,103026,1520],{},[941,103016,103017],{},"vault.collection(name, { attestation })"," registers the field schema\n(",[941,103020,103021],{},"AttestationFieldSchema",[941,103023,103024],{},"{ fields: [{ path, normalize }, …] }",". Issuing against a\ncollection without a declared schema throws ",[941,103027,66008],{},[1591,103029,103031],{"id":103030},"normalize",[941,103032,103030],{},[937,103034,103035],{},"Each declared field's value is canonicalized before hashing — the same rule on issue\nand verify:",[1338,103037,103038,103046],{},[1341,103039,103040],{},[1344,103041,103042,103044],{},[1347,103043,68047],{},[1347,103045,68050],{},[1357,103047,103048,103059,103070,103083,103092,103105],{},[1344,103049,103050,103054],{},[1362,103051,103052],{},[941,103053,68062],{},[1362,103055,103056],{},[941,103057,103058],{},"String(value).trim()",[1344,103060,103061,103067],{},[1362,103062,103063,1261,103065],{},[941,103064,11226],{},[941,103066,68097],{},[1362,103068,103069],{},"trimmed, lower\u002Fupper-cased",[1344,103071,103072,103076],{},[1362,103073,103074],{},[941,103075,65573],{},[1362,103077,103078,103079,1746,103081,1381],{},"strip non-alphanumerics, upper-case (",[941,103080,68120],{},[941,103082,68123],{},[1344,103084,103085,103089],{},[1362,103086,103087],{},[941,103088,68130],{},[1362,103090,103091],{},"keep digits only",[1344,103093,103094,103098],{},[1362,103095,103096],{},[941,103097,65607],{},[1362,103099,103100,103101,1746,103103,1381],{},"amount as integer cents; strings may carry currency symbols\u002Fcommas (",[941,103102,68157],{},[941,103104,68160],{},[1344,103106,103107,103111],{},[1362,103108,103109],{},[941,103110,65642],{},[1362,103112,103113,103114],{},"UTC calendar date, ",[941,103115,103116],{},"YYYY-MM-DD",[1591,103118,103120],{"id":103119},"verifier-surface-re-exported","Verifier surface (re-exported)",[937,103122,103123,103124,103126],{},"The subpath re-exports the pure verifier so issue-side code can verify from one import;\nverifiers that never touch noy-db install ",[941,103125,35864],{}," directly:",[1338,103128,103129,103137],{},[1341,103130,103131],{},[1344,103132,103133,103135],{},[1347,103134,72632],{},[1347,103136,7834],{},[1357,103138,103139,103152,103166,103178,103188,103201],{},[1344,103140,103141,103146],{},[1362,103142,103143],{},[941,103144,103145],{},"verifyAttestation(input)",[1362,103147,103148,103149,1520],{},"Full check: signature + per-field match + optional revocation → ",[941,103150,103151],{},"VerifyResult",[1344,103153,103154,103159],{},[1362,103155,103156],{},[941,103157,103158],{},"decodeQr(s)",[1362,103160,103161,103162,103165],{},"Decode\u002Fvalidate a scanned QR string into a ",[941,103163,103164],{},"QrPayload","; throws on malformed input.",[1344,103167,103168,103173],{},[1362,103169,103170],{},[941,103171,103172],{},"verifyRevocationList(list, publicKeyB64)",[1362,103174,103175,103176,1520],{},"Check a fetched revocation list's own signature → ",[941,103177,16861],{},[1344,103179,103180,103185],{},[1362,103181,103182],{},[941,103183,103184],{},"isRevoked(docId, list)",[1362,103186,103187],{},"Membership test against an already-verified list.",[1344,103189,103190,103195],{},[1362,103191,103192],{},[941,103193,103194],{},"signRevocationList(ids, asOf, keyId, privateKeyPkcs8B64)",[1362,103196,103197,103198,103200],{},"Low-level list signing (what ",[941,103199,67856],{}," uses).",[1344,103202,103203,103206],{},[1362,103204,103205],{},"Types",[1362,103207,103208,968,103210,968,103212,968,103215,968,103217,1520],{},[941,103209,103164],{},[941,103211,103021],{},[941,103213,103214],{},"VerifyInput",[941,103216,103151],{},[941,103218,103000],{},[1591,103220,103222],{"id":103221},"errors-and-constants","Errors and constants",[1111,103224,103225,103233,103241],{},[1114,103226,103227,103229,103230,1520],{},[941,103228,21798],{}," — any of the six vault methods called without\n",[941,103231,103232],{},"attestationStrategy: withAttestation()",[1114,103234,103235,103237,103238,103240],{},[941,103236,66008],{}," — wrong role, missing schema, missing source record or field\nvalue, unknown ",[941,103239,66086],{}," on revoke.",[1114,103242,103243,953,103246,103249],{},[941,103244,103245],{},"ATTESTATIONS_COLLECTION",[941,103247,103248],{},"'_attestations'",") — the internal encrypted collection\nholding the signer, the revocation set, and one index entry per issued attestation.",[937,103251,103252,103253,968,103256,1707,103259,968,103262,968,103265,103268],{},"Lower-level building blocks (",[941,103254,103255],{},"issueAttestationCore",[941,103257,103258],{},"revokeDocCore",[941,103260,103261],{},"unrevokeDocCore",[941,103263,103264],{},"getRevokedDocIdsCore",[941,103266,103267],{},"publishRevocationListCore"," and their\ncontext types) are exported for advanced embedding; typical applications use the vault\nmethods above.",[1022,103270,101425],{"id":101424},[937,103272,103273,103274,103276,103277,103279,103280,103282],{},"All six vault methods throw ",[941,103275,21798],{}," directing you to\n",[941,103278,103232],{},". The collection-level ",[941,103281,609],{}," schema\noption is still accepted (it only registers the schema), so you can declare schemas\nbefore enabling the capability.",[1022,103284,68210],{"id":68209},[1111,103286,103287,103299,103309,103318,103329,103343,103352],{},[1114,103288,103289,103292,103293,103295,103296,103298],{},[950,103290,103291],{},"Owner-only signing."," Issuing, revoking, and the first (key-minting) read of the\npublic key require the ",[941,103294,9898],{}," role — signing is the vault owner's identity\noperation. In a single-user vault the creator is the owner; other\n",[1007,103297,9957],{"href":54}," can read an existing public key (it is not\nsecret) but cannot issue or revoke.",[1114,103300,103301,103304,103305,103308],{},[950,103302,103303],{},"The QR commits values, it does not carry them."," Verifiers must read the claimed\nvalues off the document itself; a hash mismatch only says ",[1092,103306,103307],{},"this field differs",", never\nwhat the original value was.",[1114,103310,103311,103314,103315,103317],{},[950,103312,103313],{},"Trust anchors on key distribution."," Verification is only as strong as how\nverifiers obtained ",[941,103316,69060],{}," — bundle it into the verifier or serve it from a\nchannel the issuer controls.",[1114,103319,103320,1324,103323,103325,103326,103328],{},[950,103321,103322],{},"Verify the revocation list before trusting it.",[941,103324,66243],{}," performs a\nmembership check only; call ",[941,103327,66246],{}," on a fetched list first. A stale\nor truncated list lets a revoked document pass — that freshness risk is the\nverifier's.",[1114,103330,103331,103336,103337,103339,103340,103342],{},[950,103332,103333,103335],{},[941,103334,65642],{}," normalizes to the UTC calendar date."," A local-time ",[941,103338,26712],{}," near midnight\ncan shift by a day; prefer ISO date strings (",[941,103341,68174],{},") on both sides.",[1114,103344,103345,103348,103349,103351],{},[950,103346,103347],{},"Fresh salt per issuance."," Re-issuing the same record yields a different QR and a\nnew ",[941,103350,66086],{},"; two QRs for identical values cannot be correlated, and revocation is\nper-issuance, not per-record.",[1114,103353,103354,103357],{},[950,103355,103356],{},"Issuance hashes the canonical stored record",", independent of any display locale —\nverifiers must claim the stored (raw) values, not a locale-resolved presentation.",[1022,103359,2621],{"id":2620},[1111,103361,103362,103367,103372,103379,103385],{},[1114,103363,103364,103366],{},[1007,103365,69985],{"href":516}," — the complete\nverify-side recipe (static HTML page, six verdict states)",[1114,103368,103369,103371],{},[1007,103370,66039],{"href":520}," — rendering the QR into a\ngenerated PDF server-side",[1114,103373,103374,103376,103377],{},[1007,103375,65376],{"href":48}," ·\n",[1007,103378,14279],{"href":54},[1114,103380,103381,1368,103383],{},[1007,103382,61743],{"href":598},[1007,103384,1019],{"href":1018},[1114,103386,101581,103387,1520],{},[1007,103388,103390],{"href":93689,"rel":103389},[3371],[941,103391,103392],{},"89-attestation-revocation",[2645,103394,103395],{},"html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}",{"title":1037,"searchDepth":1038,"depth":1038,"links":103397},[103398,103399,103400,103401,103406,103414,103415,103416],{"id":29856,"depth":1041,"text":29857},{"id":70092,"depth":1041,"text":70093},{"id":99863,"depth":1041,"text":99864},{"id":29892,"depth":1041,"text":29893,"children":103402},[103403,103404,103405],{"id":102068,"depth":1038,"text":102069},{"id":102259,"depth":1038,"text":102260},{"id":102591,"depth":1038,"text":102592},{"id":102888,"depth":1041,"text":102889,"children":103407},[103408,103410,103411,103412,103413],{"id":102892,"depth":1038,"text":103409},"Vault methods (enabled by withAttestation())",{"id":103010,"depth":1038,"text":609},{"id":103030,"depth":1038,"text":103030},{"id":103119,"depth":1038,"text":103120},{"id":103221,"depth":1038,"text":103222},{"id":101424,"depth":1041,"text":101425},{"id":68209,"depth":1041,"text":68210},{"id":2620,"depth":1041,"text":2621},"Issue signed, per-field commitments for vault records as QR credentials anyone can verify fully offline — plus revocation with a signed, publishable list.",{},{"title":609,"description":103417},"fyh4ECPH09iNG3McAH9iVKBQG-cQuT94UNiLXExBw7E",{"id":103422,"title":613,"access":12,"apiToc":929,"body":103423,"category":1042,"description":104578,"extension":1044,"meta":104579,"navigation":1042,"nest":1047,"path":614,"seo":104580,"sinceVersion":1042,"status":1049,"stem":615,"subtitle":1042,"tier":1050,"tocExternal":1042,"__hash__":104581},"docs\u002Fservices\u002Fauth-introspection.md",{"type":931,"value":103424,"toc":104560},[103425,103427,103430,103432,103441,103444,103465,103468,103498,103504,103506,103521,103694,103709,103711,103715,103904,103910,103919,103923,103931,103978,103984,103988,103995,104127,104133,104144,104147,104188,104190,104197,104278,104293,104299,104302,104365,104371,104375,104440,104442,104518,104520,104557],[934,103426,613],{"id":613},[46871,103428],{":rows":103429},"[{\"label\":\"Subpath\",\"value\":\"`@noy-db\u002Fhub` (root export)\"},{\"label\":\"Type\",\"value\":\"Core feature — `describeAuthConfig()` and `diagramAuthConfig()` are always available.\"}]",[1022,103431,6242],{"id":6241},[937,103433,103434,103436,103437,103440],{},[941,103435,613],{}," answers the question ",[1092,103438,103439],{},"\"how is authentication configured in this\nvault, and what has each user actually enrolled?\""," — as ready-to-display text, so an\nadmin screen, CLI, or audit report never has to re-derive it from raw keyring files.",[937,103442,103443],{},"Two pieces of context, if this is your first noy-db auth page:",[1111,103445,103446,103456],{},[1114,103447,103448,103449,103452,103453,103455],{},"noy-db authenticates users against a ",[950,103450,103451],{},"three-tier model",": tier 1 is the master\nsecret, tier 2 is routine login through enrolled authenticators (passkey, OIDC,\npassword), tier 3 is a PIN for quick resume after idle. See\n",[1007,103454,844],{"href":845}," for the full model.",[1114,103457,103458,103459,103462,103463,1520],{},"Sensitive operations are guarded by ",[950,103460,103461],{},"policy gates"," — per-action rules stored with\nthe vault that declare which tier (and which extra factor proofs) an action requires.\nSee ",[1007,103464,796],{"href":797},[937,103466,103467],{},"The service exposes three read-only surfaces over that model:",[1234,103469,103470,103476,103485],{},[1114,103471,3289,103472,103475],{},[950,103473,103474],{},"vault-wide English summary"," — the configured tiers, phrase-format rules,\nenrolled recovery profiles, and every configured gate, as plain text.",[1114,103477,3289,103478,103481,103482,103484],{},[950,103479,103480],{},"vault-wide Mermaid diagram"," — the same auth graph as Mermaid ",[941,103483,19393],{},"\nsource, ready to render as an SVG.",[1114,103486,3289,103487,103490,103491,103493,103494,103497],{},[950,103488,103489],{},"per-user enrollment view"," — which authenticator slots a given user has\nenrolled. This surface is doubly protected: it is gated by the ",[941,103492,19321],{},"\npolicy gate (",[950,103495,103496],{},"disabled by default"," — calls fail until you enable it), and its\noutput passes through a strict field allowlist so credential material can never\nleak into a rendered string.",[937,103499,103500,103501,103503],{},"It is always on within its layer — there is no ",[941,103502,943],{}," factory to opt into and no\nbundle-size decision to make.",[1022,103505,58394],{"id":58393},[937,103507,103508,103509,103511,103512,968,103514,103516,103517,103520],{},"Nothing to wire for the two vault-wide surfaces. The only opt-in is the\n",[941,103510,19321],{}," gate for the per-user surface: both built-in policy presets\n(",[941,103513,12830],{},[941,103515,12834],{},") ship it as ",[941,103518,103519],{},"{ minTier: 1, enabled: false }",", so\nper-user introspection fails closed until you enable it deliberately:",[2217,103522,103524],{"className":2219,"code":103523,"language":2221,"meta":1037,"style":1037},"import { createNoydb } from '@noy-db\u002Fhub'\nimport { toFile } from '@noy-db\u002Fto-file'\n\nconst db = await createNoydb({\n  store: toFile({ dir: '.\u002Fdata' }),\n  user: 'alice',\n  secret: 'orange river candle mountain silver fox',\n  policy: {\n    gates: {\n      \u002F\u002F Enable per-user introspection for tier-1 (secret-opened) sessions.\n      'view-user-auth': { minTier: 1 },\n    },\n  },\n})\n",[941,103525,103526,103544,103562,103566,103582,103610,103624,103639,103647,103655,103660,103680,103684,103688],{"__ignoreMap":1037},[2225,103527,103528,103530,103532,103534,103536,103538,103540,103542],{"class":2227,"line":2228},[2225,103529,2232],{"class":2231},[2225,103531,2236],{"class":2235},[2225,103533,2744],{"class":2239},[2225,103535,2243],{"class":2235},[2225,103537,2246],{"class":2231},[2225,103539,2249],{"class":2235},[2225,103541,1086],{"class":2252},[2225,103543,2256],{"class":2235},[2225,103545,103546,103548,103550,103552,103554,103556,103558,103560],{"class":2227,"line":1041},[2225,103547,2232],{"class":2231},[2225,103549,2236],{"class":2235},[2225,103551,15573],{"class":2239},[2225,103553,2243],{"class":2235},[2225,103555,2246],{"class":2231},[2225,103557,2249],{"class":2235},[2225,103559,9551],{"class":2252},[2225,103561,2256],{"class":2235},[2225,103563,103564],{"class":2227,"line":1038},[2225,103565,2285],{"emptyLinePlaceholder":2284},[2225,103567,103568,103570,103572,103574,103576,103578,103580],{"class":2227,"line":2288},[2225,103569,2775],{"class":2774},[2225,103571,2778],{"class":2239},[2225,103573,2781],{"class":2235},[2225,103575,2784],{"class":2231},[2225,103577,2744],{"class":2291},[2225,103579,2789],{"class":2239},[2225,103581,4200],{"class":2235},[2225,103583,103584,103586,103588,103590,103592,103594,103596,103598,103600,103602,103604,103606,103608],{"class":2227,"line":2304},[2225,103585,15608],{"class":2307},[2225,103587,2311],{"class":2235},[2225,103589,15573],{"class":2291},[2225,103591,2789],{"class":2239},[2225,103593,2792],{"class":2235},[2225,103595,15619],{"class":2307},[2225,103597,2311],{"class":2235},[2225,103599,2249],{"class":2235},[2225,103601,15626],{"class":2252},[2225,103603,2805],{"class":2235},[2225,103605,2243],{"class":2235},[2225,103607,1381],{"class":2239},[2225,103609,1707],{"class":2235},[2225,103611,103612,103614,103616,103618,103620,103622],{"class":2227,"line":2356},[2225,103613,4205],{"class":2307},[2225,103615,2311],{"class":2235},[2225,103617,2249],{"class":2235},[2225,103619,6408],{"class":2252},[2225,103621,2805],{"class":2235},[2225,103623,1707],{"class":2235},[2225,103625,103626,103628,103630,103632,103635,103637],{"class":2227,"line":2830},[2225,103627,10423],{"class":2307},[2225,103629,2311],{"class":2235},[2225,103631,2249],{"class":2235},[2225,103633,103634],{"class":2252},"orange river candle mountain silver fox",[2225,103636,2805],{"class":2235},[2225,103638,1707],{"class":2235},[2225,103640,103641,103643,103645],{"class":2227,"line":2861},[2225,103642,18899],{"class":2307},[2225,103644,2311],{"class":2235},[2225,103646,2301],{"class":2235},[2225,103648,103649,103651,103653],{"class":2227,"line":4246},[2225,103650,18917],{"class":2307},[2225,103652,2311],{"class":2235},[2225,103654,2301],{"class":2235},[2225,103656,103657],{"class":2227,"line":4253},[2225,103658,103659],{"class":2763},"      \u002F\u002F Enable per-user introspection for tier-1 (secret-opened) sessions.\n",[2225,103661,103662,103664,103666,103668,103670,103672,103674,103676,103678],{"class":2227,"line":4259},[2225,103663,18944],{"class":2235},[2225,103665,19321],{"class":2307},[2225,103667,2805],{"class":2235},[2225,103669,2311],{"class":2235},[2225,103671,2236],{"class":2235},[2225,103673,18956],{"class":2307},[2225,103675,2311],{"class":2235},[2225,103677,9377],{"class":6527},[2225,103679,2353],{"class":2235},[2225,103681,103682],{"class":2227,"line":6548},[2225,103683,8324],{"class":2235},[2225,103685,103686],{"class":2227,"line":6576},[2225,103687,19017],{"class":2235},[2225,103689,103690,103692],{"class":2227,"line":6582},[2225,103691,2359],{"class":2235},[2225,103693,2362],{"class":2239},[937,103695,960,103696,103698,103699,103701,103702,103704,103705,103708],{},[941,103697,796],{}," option is merged gate-by-gate over ",[941,103700,12830],{}," and persisted at\n",[941,103703,12784],{}," on the vault's first open; from then on the on-disk document wins. On\nan existing vault, change it with ",[941,103706,103707],{},"db.updatePolicy(vault, { gates: { 'view-user-auth': { minTier: 1 } } })","\n— the override merges into the current policy, leaving the other gates intact.",[1022,103710,29893],{"id":29892},[1591,103712,103714],{"id":103713},"vault-wide-english-summary","Vault-wide English summary",[2217,103716,103718],{"className":2219,"code":103717,"language":2221,"meta":1037,"style":1037},"import { createNoydb } from '@noy-db\u002Fhub'\nimport { toFile } from '@noy-db\u002Fto-file'\n\nconst db = await createNoydb({\n  store: toFile({ dir: '.\u002Fdata' }),\n  user: 'alice',\n  secret: 'orange river candle mountain silver fox',\n})\nawait db.openVault('acme')\n\nconst summary = await db.team.describeAuthConfig('acme')\nconsole.log(summary)\n",[941,103719,103720,103738,103756,103760,103776,103804,103818,103832,103838,103858,103862,103893],{"__ignoreMap":1037},[2225,103721,103722,103724,103726,103728,103730,103732,103734,103736],{"class":2227,"line":2228},[2225,103723,2232],{"class":2231},[2225,103725,2236],{"class":2235},[2225,103727,2744],{"class":2239},[2225,103729,2243],{"class":2235},[2225,103731,2246],{"class":2231},[2225,103733,2249],{"class":2235},[2225,103735,1086],{"class":2252},[2225,103737,2256],{"class":2235},[2225,103739,103740,103742,103744,103746,103748,103750,103752,103754],{"class":2227,"line":1041},[2225,103741,2232],{"class":2231},[2225,103743,2236],{"class":2235},[2225,103745,15573],{"class":2239},[2225,103747,2243],{"class":2235},[2225,103749,2246],{"class":2231},[2225,103751,2249],{"class":2235},[2225,103753,9551],{"class":2252},[2225,103755,2256],{"class":2235},[2225,103757,103758],{"class":2227,"line":1038},[2225,103759,2285],{"emptyLinePlaceholder":2284},[2225,103761,103762,103764,103766,103768,103770,103772,103774],{"class":2227,"line":2288},[2225,103763,2775],{"class":2774},[2225,103765,2778],{"class":2239},[2225,103767,2781],{"class":2235},[2225,103769,2784],{"class":2231},[2225,103771,2744],{"class":2291},[2225,103773,2789],{"class":2239},[2225,103775,4200],{"class":2235},[2225,103777,103778,103780,103782,103784,103786,103788,103790,103792,103794,103796,103798,103800,103802],{"class":2227,"line":2304},[2225,103779,15608],{"class":2307},[2225,103781,2311],{"class":2235},[2225,103783,15573],{"class":2291},[2225,103785,2789],{"class":2239},[2225,103787,2792],{"class":2235},[2225,103789,15619],{"class":2307},[2225,103791,2311],{"class":2235},[2225,103793,2249],{"class":2235},[2225,103795,15626],{"class":2252},[2225,103797,2805],{"class":2235},[2225,103799,2243],{"class":2235},[2225,103801,1381],{"class":2239},[2225,103803,1707],{"class":2235},[2225,103805,103806,103808,103810,103812,103814,103816],{"class":2227,"line":2356},[2225,103807,4205],{"class":2307},[2225,103809,2311],{"class":2235},[2225,103811,2249],{"class":2235},[2225,103813,6408],{"class":2252},[2225,103815,2805],{"class":2235},[2225,103817,1707],{"class":2235},[2225,103819,103820,103822,103824,103826,103828,103830],{"class":2227,"line":2830},[2225,103821,10423],{"class":2307},[2225,103823,2311],{"class":2235},[2225,103825,2249],{"class":2235},[2225,103827,103634],{"class":2252},[2225,103829,2805],{"class":2235},[2225,103831,1707],{"class":2235},[2225,103833,103834,103836],{"class":2227,"line":2861},[2225,103835,2359],{"class":2235},[2225,103837,2362],{"class":2239},[2225,103839,103840,103842,103844,103846,103848,103850,103852,103854,103856],{"class":2227,"line":4246},[2225,103841,6500],{"class":2231},[2225,103843,2842],{"class":2239},[2225,103845,1520],{"class":2235},[2225,103847,2847],{"class":2291},[2225,103849,2789],{"class":2239},[2225,103851,2805],{"class":2235},[2225,103853,6450],{"class":2252},[2225,103855,2805],{"class":2235},[2225,103857,2362],{"class":2239},[2225,103859,103860],{"class":2227,"line":4253},[2225,103861,2285],{"emptyLinePlaceholder":2284},[2225,103863,103864,103866,103869,103871,103873,103875,103877,103879,103881,103883,103885,103887,103889,103891],{"class":2227,"line":4259},[2225,103865,2775],{"class":2774},[2225,103867,103868],{"class":2239}," summary ",[2225,103870,2781],{"class":2235},[2225,103872,2784],{"class":2231},[2225,103874,2842],{"class":2239},[2225,103876,1520],{"class":2235},[2225,103878,868],{"class":2239},[2225,103880,1520],{"class":2235},[2225,103882,19341],{"class":2291},[2225,103884,2789],{"class":2239},[2225,103886,2805],{"class":2235},[2225,103888,6450],{"class":2252},[2225,103890,2805],{"class":2235},[2225,103892,2362],{"class":2239},[2225,103894,103895,103897,103899,103901],{"class":2227,"line":6548},[2225,103896,28031],{"class":2239},[2225,103898,1520],{"class":2235},[2225,103900,27100],{"class":2291},[2225,103902,103903],{"class":2239},"(summary)\n",[2217,103905,103908],{"className":103906,"code":103907,"language":5672,"meta":1037},[5670],"Vault \"acme\" — three-tier authentication\n\nTier 1 — Secret (master)\n  Phrase format: 6+ words, lowercase letters, ≥3 chars\u002Fword\n  Strength validator: enforced (override available for tests only)\n\nTier 2 — Authenticate (routine login)\n  Allowed methods: WebAuthn (passkey), OIDC, Password\n  Slots per user: unlimited\n\nTier 3 — Unlock (quick resume)\n  Method: PIN (per-app configurable)\n\nRecovery profiles enrolled: none\nManaged-secret mode: off (post-1.0)\n\nSensitive-action gates:\n  rotate-secret — tier 1 + 1× totp|email-otp|recovery|webauthn-roaming|webauthn-platform|password|pin\n  recover-secret — tier 1\n  rotate-unlock — tier 2\n  export-plaintext — tier 1 + 1× totp|email-otp\n  view-user-auth — disabled\n  …one line per configured gate\n",[941,103909,103907],{"__ignoreMap":1037},[937,103911,103912,103913,103915,103916,103918],{},"The summary reflects the policy persisted at ",[941,103914,12784],{},". If no policy document\nexists on disk (a store written outside ",[941,103917,12780],{},"), the summary falls back to the\ndefaults (6+ words, ≥3 chars\u002Fword, no gates listed).",[1591,103920,103922],{"id":103921},"vault-wide-mermaid-diagram","Vault-wide Mermaid diagram",[937,103924,103925,103927,103928,103930],{},[941,103926,19367],{}," returns Mermaid ",[950,103929,22723],{},", not an image — pipe it through\nMermaid (CLI, browser bundle, or a docs renderer) to get the SVG. Each enabled gate\nbecomes a node attached to the tier it requires; disabled gates are omitted.",[2217,103932,103934],{"className":2219,"code":103933,"language":2221,"meta":1037,"style":1037},"const mmd = await db.team.diagramAuthConfig('acme')\nconsole.log(mmd)\n",[941,103935,103936,103967],{"__ignoreMap":1037},[2225,103937,103938,103940,103943,103945,103947,103949,103951,103953,103955,103957,103959,103961,103963,103965],{"class":2227,"line":2228},[2225,103939,2775],{"class":2774},[2225,103941,103942],{"class":2239}," mmd ",[2225,103944,2781],{"class":2235},[2225,103946,2784],{"class":2231},[2225,103948,2842],{"class":2239},[2225,103950,1520],{"class":2235},[2225,103952,868],{"class":2239},[2225,103954,1520],{"class":2235},[2225,103956,19377],{"class":2291},[2225,103958,2789],{"class":2239},[2225,103960,2805],{"class":2235},[2225,103962,6450],{"class":2252},[2225,103964,2805],{"class":2235},[2225,103966,2362],{"class":2239},[2225,103968,103969,103971,103973,103975],{"class":2227,"line":1041},[2225,103970,28031],{"class":2239},[2225,103972,1520],{"class":2235},[2225,103974,27100],{"class":2291},[2225,103976,103977],{"class":2239},"(mmd)\n",[2217,103979,103982],{"className":103980,"code":103981,"language":5672,"meta":1037},[5670],"flowchart TB\n  vault[\"Vault: acme\"]\n  tier1[\"Tier 1\u003Cbr\u002F>Secret\"]\n  tier2[\"Tier 2\u003Cbr\u002F>Multi-slot Authenticate\"]\n  tier3[\"Tier 3\u003Cbr\u002F>PIN \u002F Quick-resume\"]\n  vault --> tier1\n  tier1 --> tier2\n  tier2 --> tier3\n  rotate_secret[\"rotate-secret\u003Cbr\u002F>tier ≥ 1\"]\n  tier1 --> rotate_secret\n  rotate_unlock[\"rotate-unlock\u003Cbr\u002F>tier ≥ 2\"]\n  tier2 --> rotate_unlock\n  …one node + one edge per enabled gate\n",[941,103983,103981],{"__ignoreMap":1037},[1591,103985,103987],{"id":103986},"per-user-enrollment-view","Per-user enrollment view",[937,103989,32137,103990,16238,103992,103994],{},[941,103991,19321],{},[1007,103993,58394],{"href":73791},"). Output is allowlist-sanitized:\nonly the slot id, method, enrollment date, and enrolling tier are rendered — wrapped\nkey material, WebAuthn credential ids, OIDC subject ids, password hashes, and TOTP\nsecrets are dropped at the allowlist boundary, never redacted into placeholders.",[2217,103996,103998],{"className":2219,"code":103997,"language":2221,"meta":1037,"style":1037},"import { PolicyDeniedError } from '@noy-db\u002Fhub'\n\ntry {\n  const view = await db.team.describeUserAuth('acme', 'alice')\n  console.log(view)\n} catch (err) {\n  if (err instanceof PolicyDeniedError) {\n    \u002F\u002F err.gate === 'view-user-auth', err.reason e.g. 'disabled' | 'insufficient-tier'\n  }\n  throw err\n}\n",[941,103999,104000,104018,104022,104028,104067,104082,104092,104108,104113,104117,104123],{"__ignoreMap":1037},[2225,104001,104002,104004,104006,104008,104010,104012,104014,104016],{"class":2227,"line":2228},[2225,104003,2232],{"class":2231},[2225,104005,2236],{"class":2235},[2225,104007,18842],{"class":2239},[2225,104009,2243],{"class":2235},[2225,104011,2246],{"class":2231},[2225,104013,2249],{"class":2235},[2225,104015,1086],{"class":2252},[2225,104017,2256],{"class":2235},[2225,104019,104020],{"class":2227,"line":1041},[2225,104021,2285],{"emptyLinePlaceholder":2284},[2225,104023,104024,104026],{"class":2227,"line":1038},[2225,104025,7554],{"class":2231},[2225,104027,2301],{"class":2235},[2225,104029,104030,104032,104035,104037,104039,104041,104043,104045,104047,104049,104051,104053,104055,104057,104059,104061,104063,104065],{"class":2227,"line":2288},[2225,104031,8123],{"class":2774},[2225,104033,104034],{"class":2239}," view",[2225,104036,6346],{"class":2235},[2225,104038,2784],{"class":2231},[2225,104040,2842],{"class":2239},[2225,104042,1520],{"class":2235},[2225,104044,868],{"class":2239},[2225,104046,1520],{"class":2235},[2225,104048,19417],{"class":2291},[2225,104050,2789],{"class":2307},[2225,104052,2805],{"class":2235},[2225,104054,6450],{"class":2252},[2225,104056,2805],{"class":2235},[2225,104058,2298],{"class":2235},[2225,104060,2249],{"class":2235},[2225,104062,6408],{"class":2252},[2225,104064,2805],{"class":2235},[2225,104066,2362],{"class":2307},[2225,104068,104069,104071,104073,104075,104077,104080],{"class":2227,"line":2304},[2225,104070,27095],{"class":2239},[2225,104072,1520],{"class":2235},[2225,104074,27100],{"class":2291},[2225,104076,2789],{"class":2307},[2225,104078,104079],{"class":2239},"view",[2225,104081,2362],{"class":2307},[2225,104083,104084,104086,104088,104090],{"class":2227,"line":2356},[2225,104085,2359],{"class":2235},[2225,104087,7598],{"class":2231},[2225,104089,7601],{"class":2239},[2225,104091,4200],{"class":2235},[2225,104093,104094,104096,104098,104100,104102,104104,104106],{"class":2227,"line":2830},[2225,104095,7608],{"class":2231},[2225,104097,953],{"class":2307},[2225,104099,7617],{"class":2239},[2225,104101,19172],{"class":2235},[2225,104103,18842],{"class":6342},[2225,104105,8495],{"class":2307},[2225,104107,4200],{"class":2235},[2225,104109,104110],{"class":2227,"line":2861},[2225,104111,104112],{"class":2763},"    \u002F\u002F err.gate === 'view-user-auth', err.reason e.g. 'disabled' | 'insufficient-tier'\n",[2225,104114,104115],{"class":2227,"line":4246},[2225,104116,7632],{"class":2235},[2225,104118,104119,104121],{"class":2227,"line":4253},[2225,104120,7637],{"class":2231},[2225,104122,7640],{"class":2239},[2225,104124,104125],{"class":2227,"line":4259},[2225,104126,7351],{"class":2235},[2217,104128,104131],{"className":104129,"code":104130,"language":5672,"meta":1037},[5670],"User: alice (joined 2026-04-15, role: owner)\n\nTier 2 enrollments:\n  - webauthn (id=webauthn-yubikey-blue, enrolled 2026-04-15, via tier 1)\n",[941,104132,104130],{"__ignoreMap":1037},[937,104134,104135,104136,104139,104140,104143],{},"A user with a keyring but no tier-2 slots renders ",[941,104137,104138],{},"(none enrolled)","; an unknown user\nid returns the ",[950,104141,104142],{},"empty string"," rather than throwing, so the call alone never confirms\nor denies that a user exists.",[937,104145,104146],{},"For owner dashboards there is a bulk variant:",[2217,104148,104150],{"className":2219,"code":104149,"language":2221,"meta":1037,"style":1037},"const all = await db.team.describeAllUsersAuth('acme')\n\u002F\u002F → [{ userId: 'alice', description: 'User: alice (joined 2026-04-15, role: owner)\\n…' }, …]\n",[941,104151,104152,104183],{"__ignoreMap":1037},[2225,104153,104154,104156,104159,104161,104163,104165,104167,104169,104171,104173,104175,104177,104179,104181],{"class":2227,"line":2228},[2225,104155,2775],{"class":2774},[2225,104157,104158],{"class":2239}," all ",[2225,104160,2781],{"class":2235},[2225,104162,2784],{"class":2231},[2225,104164,2842],{"class":2239},[2225,104166,1520],{"class":2235},[2225,104168,868],{"class":2239},[2225,104170,1520],{"class":2235},[2225,104172,19480],{"class":2291},[2225,104174,2789],{"class":2239},[2225,104176,2805],{"class":2235},[2225,104178,6450],{"class":2252},[2225,104180,2805],{"class":2235},[2225,104182,2362],{"class":2239},[2225,104184,104185],{"class":2227,"line":1041},[2225,104186,104187],{"class":2763},"\u002F\u002F → [{ userId: 'alice', description: 'User: alice (joined 2026-04-15, role: owner)\\n…' }, …]\n",[1022,104189,102889],{"id":102888},[1591,104191,104193,104194,104196],{"id":104192},"instance-methods-on-the-noydb-handle","Instance methods (on the ",[941,104195,62],{}," handle)",[1338,104198,104199,104209],{},[1341,104200,104201],{},[1344,104202,104203,104205,104207],{},[1347,104204,7831],{},[1347,104206,102906],{},[1347,104208,7834],{},[1357,104210,104211,104226,104243,104263],{},[1344,104212,104213,104218,104223],{},[1362,104214,104215],{},[941,104216,104217],{},"db.team.describeAuthConfig(vault)",[1362,104219,104220],{},[941,104221,104222],{},"Promise\u003Cstring>",[1362,104224,104225],{},"Vault-wide English summary of the configured auth model. Ungated.",[1344,104227,104228,104233,104237],{},[1362,104229,104230],{},[941,104231,104232],{},"db.team.diagramAuthConfig(vault)",[1362,104234,104235],{},[941,104236,104222],{},[1362,104238,104239,104240,104242],{},"Vault-wide auth graph as Mermaid ",[941,104241,19393],{}," source. Ungated.",[1344,104244,104245,104250,104254],{},[1362,104246,104247],{},[941,104248,104249],{},"db.team.describeUserAuth(vault, userId, factors?)",[1362,104251,104252],{},[941,104253,104222],{},[1362,104255,104256,104257,104259,104260,104262],{},"Per-user enrollment summary. Checks the ",[941,104258,19321],{}," gate first; throws ",[941,104261,13025],{}," on denial. Empty string for unknown users.",[1344,104264,104265,104270,104275],{},[1362,104266,104267],{},[941,104268,104269],{},"db.team.describeAllUsersAuth(vault, factors?)",[1362,104271,104272],{},[941,104273,104274],{},"Promise\u003CArray\u003C{ userId: string; description: string }>>",[1362,104276,104277],{},"Bulk per-user summaries (users with no keyring file are skipped). Same gate check.",[937,104279,104280,104282,104283,1617,104286,104289,104290,104292],{},[941,104281,13051],{}," is an optional ",[941,104284,104285],{},"FactorProofBundle",[941,104287,104288],{},"{ factors?: ReadonlyArray\u003CFactorProof>; sharedDevice?: boolean }",") — only needed\nwhen your ",[941,104291,19321],{}," gate policy demands extra factor proofs beyond the active\nsession tier.",[1591,104294,104296,104297,1381],{"id":104295},"standalone-functions-exported-from-noy-dbhub","Standalone functions (exported from ",[941,104298,1086],{},[937,104300,104301],{},"The same four surfaces are exported as free functions that take a raw store — the\nlow-level building blocks the instance methods delegate to:",[2217,104303,104305],{"className":2219,"code":104304,"language":2221,"meta":1037,"style":1037},"import {\n  describeAuthConfig,     \u002F\u002F (store: NoydbStore, vault: string) => Promise\u003Cstring>\n  diagramAuthConfig,      \u002F\u002F (store: NoydbStore, vault: string) => Promise\u003Cstring>\n  describeUserAuth,       \u002F\u002F (store: NoydbStore, vault: string, userId: string) => Promise\u003Cstring>\n  describeAllUsersAuth,   \u002F\u002F (store: NoydbStore, vault: string) => Promise\u003CArray\u003C{ userId, description }>>\n} from '@noy-db\u002Fhub'\n",[941,104306,104307,104313,104323,104333,104343,104353],{"__ignoreMap":1037},[2225,104308,104309,104311],{"class":2227,"line":2228},[2225,104310,2232],{"class":2231},[2225,104312,2301],{"class":2235},[2225,104314,104315,104318,104320],{"class":2227,"line":1041},[2225,104316,104317],{"class":2239},"  describeAuthConfig",[2225,104319,2298],{"class":2235},[2225,104321,104322],{"class":2763},"     \u002F\u002F (store: NoydbStore, vault: string) => Promise\u003Cstring>\n",[2225,104324,104325,104328,104330],{"class":2227,"line":1038},[2225,104326,104327],{"class":2239},"  diagramAuthConfig",[2225,104329,2298],{"class":2235},[2225,104331,104332],{"class":2763},"      \u002F\u002F (store: NoydbStore, vault: string) => Promise\u003Cstring>\n",[2225,104334,104335,104338,104340],{"class":2227,"line":2288},[2225,104336,104337],{"class":2239},"  describeUserAuth",[2225,104339,2298],{"class":2235},[2225,104341,104342],{"class":2763},"       \u002F\u002F (store: NoydbStore, vault: string, userId: string) => Promise\u003Cstring>\n",[2225,104344,104345,104348,104350],{"class":2227,"line":2304},[2225,104346,104347],{"class":2239},"  describeAllUsersAuth",[2225,104349,2298],{"class":2235},[2225,104351,104352],{"class":2763},"   \u002F\u002F (store: NoydbStore, vault: string) => Promise\u003CArray\u003C{ userId, description }>>\n",[2225,104354,104355,104357,104359,104361,104363],{"class":2227,"line":2356},[2225,104356,2359],{"class":2235},[2225,104358,2246],{"class":2231},[2225,104360,2249],{"class":2235},[2225,104362,1086],{"class":2252},[2225,104364,2256],{"class":2235},[937,104366,104367,104368,104370],{},"They read the vault's policy document (",[941,104369,12784],{},") and keyring headers directly\nfrom the store. Note the security scoping below: the gate check lives on the instance\nmethods, not here.",[1591,104372,104373],{"id":19321},[941,104374,19321],{},[1111,104376,104377,104413],{},[1114,104378,104379,2085,104382,104384,104385,104387,104388,104390,104391,968,104393,1617,104395,968,104398,968,104401,968,104404,1707,104407,46431,104410,104412],{},[950,104380,104381],{},"Gate",[941,104383,19321],{}," is a built-in gate name. Built-in gates with no\nconfigured policy fail closed, and both presets configure it as\n",[941,104386,103519],{}," — so per-user introspection is off until enabled.\nDenials throw ",[941,104389,13025],{}," carrying ",[941,104392,2723],{},[941,104394,43668],{},[941,104396,104397],{},"'disabled'",[941,104399,104400],{},"'insufficient-tier'",[941,104402,104403],{},"'missing-factor'",[941,104405,104406],{},"'stale-proof'",[941,104408,104409],{},"'shared-device-blocked'",[941,104411,45375],{}," gate policy.",[1114,104414,104415,104418,104419,968,104421,968,104423,968,104426,104429,104430,104433,104434,104436,104437,104439],{},[950,104416,104417],{},"Allowlist",": per authenticator slot, exactly four fields can ever reach the\nrendered string: ",[941,104420,6983],{},[941,104422,17574],{},[941,104424,104425],{},"enrolled_at",[941,104427,104428],{},"enrolled_via_tier",". Everything\nelse in the keyring file — ",[941,104431,104432],{},"wrapped_kek",", the method-specific ",[941,104435,17587],{}," blob\n(credential ids, subject ids, hashes, secrets), the KDF ",[941,104438,66106],{},", recovery codes —\nis projected out before rendering. Dropped, not redacted: an off-allowlist field\nleaves no trace in the output.",[1022,104441,68210],{"id":68209},[1111,104443,104444,104464,104477,104486,104492,104503],{},[1114,104445,104446,66018,104449,2601,104452,104455,104456,104458,104459,28806,104461,1520],{},[950,104447,104448],{},"Per-user introspection is off by default.",[941,104450,104451],{},"db.team.describeUserAuth()",[941,104453,104454],{},"db.team.describeAllUsersAuth()"," without enabling ",[941,104457,19321],{}," throws\n",[941,104460,13025],{},[941,104462,104463],{},"reason: 'disabled'",[1114,104465,104466,104469,104470,104472,104473,104476],{},[950,104467,104468],{},"The gate protects the app surface, not the store."," The standalone functions take\na raw ",[941,104471,3154],{}," and perform no gate check — anyone holding the store can already\nread the plaintext keyring headers they summarize. Route application code through\nthe ",[941,104474,104475],{},"db.*"," methods so the policy applies.",[1114,104478,104479,104482,104483,104485],{},[950,104480,104481],{},"Strings, not structures."," All four surfaces return display text (or Mermaid\nsource), formatted for humans. There is no stable machine-readable schema — don't\nparse the summary; read the policy document via the ",[1007,104484,796],{"href":797},"\nAPIs instead.",[1114,104487,104488,104491],{},[950,104489,104490],{},"The diagram is Mermaid source."," You must render it yourself; nothing in noy-db\nproduces the SVG.",[1114,104493,104494,1324,104497,104499,104500,104502],{},[950,104495,104496],{},"Unknown user ⇒ empty string.",[941,104498,19417],{}," deliberately does not throw for\na missing keyring, so probing it does not reveal whether a user id exists. Check\nfor ",[941,104501,80576],{}," before displaying.",[1114,104504,104505,104510,104511,104513,104514,104517],{},[950,104506,104507,104508,1520],{},"The summary mirrors ",[941,104509,12784],{}," On a vault created by ",[941,104512,2134],{}," the\nmerged policy is persisted there at creation; edits made with ",[941,104515,104516],{},"db.updatePolicy()","\nshow up on the next call. A store with no policy document renders the built-in\ndefaults.",[1022,104519,2621],{"id":2620},[1111,104521,104522,104527,104539,104544,104551],{},[1114,104523,104524,104526],{},[1007,104525,844],{"href":845}," — the three-tier authentication model this service describes.",[1114,104528,104529,1169,104531,1261,104533,104536,104537,1520],{},[1007,104530,796],{"href":797},[941,104532,12870],{},[941,104534,104535],{},"GatePolicy"," gate DSL, including ",[941,104538,19321],{},[1114,104540,104541,104543],{},[1007,104542,868],{"href":869}," — multi-user grant\u002Frevoke, whose keyring files feed the per-user view.",[1114,104545,104546,104548,104549,13046],{},[1007,104547,669],{"href":670}," — the vault-wide \"who's on this team\" listing, filtered by\nits own visibility flags rather than the ",[941,104550,19321],{},[1114,104552,104553,1368,104555],{},[1007,104554,61743],{"href":598},[1007,104556,1019],{"href":1018},[2645,104558,104559],{},"html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}",{"title":1037,"searchDepth":1038,"depth":1038,"links":104561},[104562,104563,104564,104569,104576,104577],{"id":6241,"depth":1041,"text":6242},{"id":58393,"depth":1041,"text":58394},{"id":29892,"depth":1041,"text":29893,"children":104565},[104566,104567,104568],{"id":103713,"depth":1038,"text":103714},{"id":103921,"depth":1038,"text":103922},{"id":103986,"depth":1038,"text":103987},{"id":102888,"depth":1041,"text":102889,"children":104570},[104571,104573,104575],{"id":104192,"depth":1038,"text":104572},"Instance methods (on the Noydb handle)",{"id":104295,"depth":1038,"text":104574},"Standalone functions (exported from @noy-db\u002Fhub)",{"id":19321,"depth":1038,"text":19321},{"id":68209,"depth":1041,"text":68210},{"id":2620,"depth":1041,"text":2621},"Read-only views of a vault's authentication setup: a plain-English summary, a three-tier model diagram, and a policy-gated per-user enrollment view.",{},{"title":613,"description":104578},"FVkfhgDsO14lrv8XopQ8N0cbedsrbLKqUCT0B38295g",{"id":104583,"title":617,"access":12,"apiToc":929,"body":104584,"category":1042,"description":107425,"extension":1044,"meta":107426,"navigation":1042,"nest":1047,"path":618,"seo":107427,"sinceVersion":1042,"status":1049,"stem":619,"subtitle":1042,"tier":1050,"tocExternal":1042,"__hash__":107428},"docs\u002Fservices\u002Fauth-landscape.md",{"type":931,"value":104585,"toc":107401},[104586,104588,104595,104598,104602,104618,104628,104632,104701,104716,104720,104729,104793,104797,104885,104891,104894,104898,105017,105021,105163,105167,105338,105342,105348,105458,105462,105486,105585,105589,105599,105652,106012,106016,106139,106143,106177,106330,106334,106340,106494,106498,106508,106611,106615,106669,106673,106814,106816,106820,106898,106905,106923,106949,107052,107058,107080,107086,107090,107093,107140,107144,107304,107306],[934,104587,617],{"id":617},[1061,104589,104590],{},[937,104591,104592],{},[950,104593,104594],{},"Reference map of authentication, unlock, and sealing-key primitives.",[46871,104596],{":rows":104597},"[{\"label\":\"Status\",\"value\":\"reference — this page documents no API of its own; the packages it names are documented in the [`on-*` family catalog](\u002Ffamilies\u002Fon).\"},{\"label\":\"Cluster\",\"value\":\"collaboration-and-auth\"},{\"label\":\"Cross-cuts\",\"value\":\"every `@noy-db\u002Fon-*` package, `@noy-db\u002Fhub` core (KEK derivation, keyring layout, policy gates), and the [`@noy-db\u002Fat-*` sealing-provider family](\u002Ffamilies\u002Fat).\"},{\"label\":\"Companion\",\"value\":\"[session-tiers](\u002Fservices\u002Fsession-tiers) — how the primitives below compose into a session lifecycle.\"}]",[1022,104599,104601],{"id":104600},"what-this-page-is","What this page is",[937,104603,104604,104605,104608,104609,16238,104612,104614,104615,1520],{},"noy-db is a zero-knowledge document store: every record is encrypted on the user's device before it reaches any storage backend. The whole key hierarchy hangs off one root — a ",[950,104606,104607],{},"key-encryption key (KEK)"," derived from a user credential, which wraps the per-collection ",[950,104610,104611],{},"data-encryption keys (DEKs)",[1007,104613,1516],{"href":66},"). \"Login\" therefore always means one concrete thing here: ",[1092,104616,104617],{},"producing the material that unlocks the KEK",[937,104619,104620,104621,104623,104624,104627],{},"This page maps the authentication, unlock, and sealing-key methods in common use, scores each on the dimensions that matter for a zero-knowledge offline-first vault, and names the ",[941,104622,5129],{}," package that covers it — or states plainly that it is deliberately omitted, not covered, or out of scope. The next time someone asks ",[1092,104625,104626],{},"\"can we log in with Cognito \u002F Active Directory \u002F Windows \u002F Touch ID \u002F BankID?\"",", the answer is one table lookup.",[1022,104629,104631],{"id":104630},"three-distinct-functions-get-conflated-as-auth","Three distinct functions get conflated as \"auth\"",[1338,104633,104634,104646],{},[1341,104635,104636],{},[1344,104637,104638,104641,104643],{},[1347,104639,104640],{},"Function",[1347,104642,29857],{},[1347,104644,104645],{},"noy-db family",[1357,104647,104648,104663,104678],{},[1344,104649,104650,104655,104658],{},[1362,104651,104652],{},[950,104653,104654],{},"Authenticator",[1362,104656,104657],{},"Proves the user knows \u002F has \u002F is something. Produces a cryptographic proof or releases a wrap key.",[1362,104659,104660,104662],{},[941,104661,1787],{}," (sections 1–5)",[1344,104664,104665,104670,104673],{},[1362,104666,104667],{},[950,104668,104669],{},"Identity provider (IdP)",[1362,104671,104672],{},"Federates identity. Issues tokens. Does not by itself produce wrap-key material.",[1362,104674,104675,104677],{},[941,104676,15969],{}," bridges (section 6)",[1344,104679,104680,104685,104688],{},[1362,104681,104682],{},[950,104683,104684],{},"Sealing-key store",[1362,104686,104687],{},"Protects a machine-generated secret under OS-account or KMS guarantees. Does not authenticate the user.",[1362,104689,104690,104694,104695,104697,104698,104700],{},[1007,104691,104692],{"href":170},[941,104693,1139],{}," — implementations of the ",[941,104696,14350],{}," interface exported by ",[941,104699,1086],{}," (sections 8–9)",[937,104702,104703,104704,104706,104707,104710,104711,104713,104714,1520],{},"Conflating these is the single most common source of misdesign in this space. A Windows DPAPI sealing provider is ",[950,104705,1004],{}," an authenticator. A hypothetical ",[941,104708,104709],{},"on-google"," would ",[950,104712,1004],{}," be different from ",[941,104715,15969],{},[1022,104717,104719],{"id":104718},"session-tiers-briefly","Session tiers, briefly",[937,104721,104722,104723,104726,104727,6255],{},"Several rows below refer to noy-db's ",[950,104724,104725],{},"session tiers"," — the three-rung privilege ladder that structures unlock (defined in full on ",[1007,104728,844],{"href":845},[1338,104730,104731,104742],{},[1341,104732,104733],{},[1344,104734,104735,104738,104740],{},[1347,104736,104737],{},"Tier",[1347,104739,2970],{},[1347,104741,13698],{},[1357,104743,104744,104763,104780],{},[1344,104745,104746,104750,104760],{},[1362,104747,104748],{},[950,104749,5437],{},[1362,104751,104752,104753,104755,104756,104759],{},"Master secret — the root secret; derives the KEK. Factory-fixed inside ",[941,104754,1086],{},"; there is no ",[941,104757,104758],{},"on-secret"," package.",[1362,104761,104762],{},"hub core",[1344,104764,104765,104769,104772],{},[1362,104766,104767],{},[950,104768,5470],{},[1362,104770,104771],{},"Routine login — multi-slot authenticators, any one of which unlocks the same KEK.",[1362,104773,104774,968,104776,968,104778],{},[941,104775,15944],{},[941,104777,15969],{},[941,104779,15953],{},[1344,104781,104782,104786,104789],{},[1362,104783,104784],{},[950,104785,5512],{},[1362,104787,104788],{},"Quick-resume after idle — re-opens an already-unlocked session within a bounded idle window.",[1362,104790,104791],{},[941,104792,15958],{},[1022,104794,104796],{"id":104795},"dimensions","Dimensions",[1338,104798,104799,104809],{},[1341,104800,104801],{},[1344,104802,104803,104806],{},[1347,104804,104805],{},"Dimension",[1347,104807,104808],{},"What it tests",[1357,104810,104811,104825,104835,104849,104869],{},[1344,104812,104813,104818],{},[1362,104814,104815],{},[950,104816,104817],{},"Online?",[1362,104819,104820,104821,104824],{},"Network required ",[1092,104822,104823],{},"at unlock time",". TOTP needs network only at enrollment, so it scores ❌ here.",[1344,104826,104827,104832],{},[1362,104828,104829],{},[950,104830,104831],{},"Personal device",[1362,104833,104834],{},"Suits a phone\u002Flaptop bound to one user.",[1344,104836,104837,104842],{},[1362,104838,104839],{},[950,104840,104841],{},"Shared device",[1362,104843,104844,104845,104848],{},"Suits a kiosk \u002F clinic terminal \u002F shared family iPad. Implicitly tests ",[1092,104846,104847],{},"credential portability"," — methods that work well on shared hardware are ones where the credential travels with the user.",[1344,104850,104851,104856],{},[1362,104852,104853],{},[950,104854,104855],{},"Factor",[1362,104857,104858,104860,104861,104864,104865,104868],{},[950,104859,12279],{}," = knowledge · ",[950,104862,104863],{},"D"," = possession\u002Fdevice · ",[950,104866,104867],{},"B"," = biometric\u002Finherence. Multi-factor methods list all that apply.",[1344,104870,104871,104876],{},[1362,104872,104873],{},[950,104874,104875],{},"noy-db match",[1362,104877,104878,104879,104881,104882,104884],{},"Existing ",[941,104880,1787],{}," package, ",[941,104883,1139],{}," sealing provider, deliberately omitted (with reason), or not covered.",[937,104886,104887,104890],{},[950,104888,104889],{},"Legend:"," ✅ good fit · ⚠️ works with caveats · ❌ poor fit \u002F breaks model",[104892,104893],"hr",{},[1022,104895,104897],{"id":104896},"_1-knowledge-factors","1. Knowledge factors",[1338,104899,104900,104918],{},[1341,104901,104902],{},[1344,104903,104904,104906,104908,104911,104914,104916],{},[1347,104905,7831],{},[1347,104907,104817],{"align":9536},[1347,104909,104910],{"align":9536},"Personal",[1347,104912,104913],{"align":9536},"Shared",[1347,104915,104855],{"align":9536},[1347,104917,104875],{},[1357,104919,104920,104944,104962,104983,105000],{},[1344,104921,104922,104925,104928,104930,104933,104935],{},[1362,104923,104924],{},"Master secret (Diceware)",[1362,104926,104927],{"align":9536},"❌",[1362,104929,55189],{"align":9536},[1362,104931,104932],{"align":9536},"⚠️ shoulder-surf",[1362,104934,12279],{"align":9536},[1362,104936,104937,104940,104941,104943],{},[950,104938,104939],{},"tier 1, internal to hub"," (no ",[941,104942,1787],{}," package)",[1344,104945,104946,104949,104951,104953,104956,104958],{},[1362,104947,104948],{},"Password (tier 2)",[1362,104950,104927],{"align":9536},[1362,104952,55189],{"align":9536},[1362,104954,104955],{"align":9536},"⚠️",[1362,104957,12279],{"align":9536},[1362,104959,104960],{},[941,104961,15953],{},[1344,104963,104964,104967,104969,104971,104974,104976],{},[1362,104965,104966],{},"Numeric PIN (4–6 digits)",[1362,104968,104927],{"align":9536},[1362,104970,55189],{"align":9536},[1362,104972,104973],{"align":9536},"❌ low entropy",[1362,104975,12279],{"align":9536},[1362,104977,104978,953,104980,1381],{},[941,104979,15958],{},[950,104981,104982],{},"tier 3 only",[1344,104984,104985,104988,104990,104992,104994,104997],{},[1362,104986,104987],{},"Pattern lock (Android-style)",[1362,104989,104927],{"align":9536},[1362,104991,104955],{"align":9536},[1362,104993,104927],{"align":9536},[1362,104995,104996],{"align":9536},"K weak",[1362,104998,104999],{},"❌ deliberately omitted",[1344,105001,105002,105005,105008,105010,105012,105014],{},[1362,105003,105004],{},"Security questions",[1362,105006,105007],{"align":9536},"varies",[1362,105009,104955],{"align":9536},[1362,105011,104927],{"align":9536},[1362,105013,104996],{"align":9536},[1362,105015,105016],{},"❌ deliberately omitted (social-engineerable, low entropy)",[1022,105018,105020],{"id":105019},"_2-possession-software-otp-link","2. Possession — software OTP \u002F link",[1338,105022,105023,105039],{},[1341,105024,105025],{},[1344,105026,105027,105029,105031,105033,105035,105037],{},[1347,105028,7831],{},[1347,105030,104817],{"align":9536},[1347,105032,104910],{"align":9536},[1347,105034,104913],{"align":9536},[1347,105036,104855],{"align":9536},[1347,105038,104875],{},[1357,105040,105041,105060,105076,105094,105112,105129,105148],{},[1344,105042,105043,105046,105049,105051,105054,105056],{},[1362,105044,105045],{},"TOTP (Google Auth, Authy, 1Password)",[1362,105047,105048],{"align":9536},"❌ post-enrol",[1362,105050,55189],{"align":9536},[1362,105052,105053],{"align":9536},"✅ phone-borne",[1362,105055,104863],{"align":9536},[1362,105057,105058],{},[941,105059,15974],{},[1344,105061,105062,105065,105067,105069,105071,105073],{},[1362,105063,105064],{},"HOTP (RFC 4226)",[1362,105066,104927],{"align":9536},[1362,105068,55189],{"align":9536},[1362,105070,55189],{"align":9536},[1362,105072,104863],{"align":9536},[1362,105074,105075],{},"❌ (TOTP supersedes)",[1344,105077,105078,105081,105083,105085,105087,105090],{},[1362,105079,105080],{},"Email OTP",[1362,105082,55189],{"align":9536},[1362,105084,55189],{"align":9536},[1362,105086,55189],{"align":9536},[1362,105088,105089],{"align":9536},"D (mailbox)",[1362,105091,105092],{},[941,105093,15979],{},[1344,105095,105096,105099,105101,105103,105106,105109],{},[1362,105097,105098],{},"SMS OTP",[1362,105100,55189],{"align":9536},[1362,105102,55189],{"align":9536},[1362,105104,105105],{"align":9536},"⚠️ SIM-swap",[1362,105107,105108],{"align":9536},"D weakened",[1362,105110,105111],{},"❌ deliberately omitted (NIST SP 800-63B deprecates)",[1344,105113,105114,105117,105119,105121,105123,105125],{},[1362,105115,105116],{},"Magic link",[1362,105118,55189],{"align":9536},[1362,105120,55189],{"align":9536},[1362,105122,104955],{"align":9536},[1362,105124,105089],{"align":9536},[1362,105126,105127],{},[941,105128,15909],{},[1344,105130,105131,105134,105136,105138,105141,105143],{},[1362,105132,105133],{},"Push notification (Duo, Okta Verify, MS Authenticator number-match)",[1362,105135,55189],{"align":9536},[1362,105137,55189],{"align":9536},[1362,105139,105140],{"align":9536},"✅ goes to personal phone",[1362,105142,104863],{"align":9536},[1362,105144,105145,105146,1381],{},"❌ (vendor; reachable via ",[941,105147,15969],{},[1344,105149,105150,105153,105155,105157,105159,105161],{},[1362,105151,105152],{},"WhatsApp \u002F Telegram \u002F Signal OTP",[1362,105154,55189],{"align":9536},[1362,105156,55189],{"align":9536},[1362,105158,104955],{"align":9536},[1362,105160,104863],{"align":9536},[1362,105162,104927],{},[1022,105164,105166],{"id":105165},"_3-possession-hardware","3. Possession — hardware",[1338,105168,105169,105185],{},[1341,105170,105171],{},[1344,105172,105173,105175,105177,105179,105181,105183],{},[1347,105174,7831],{},[1347,105176,104817],{"align":9536},[1347,105178,104910],{"align":9536},[1347,105180,104913],{"align":9536},[1347,105182,104855],{"align":9536},[1347,105184,104875],{},[1357,105186,105187,105210,105232,105253,105271,105287,105303,105322],{},[1344,105188,105189,105196,105198,105200,105203,105206],{},[1362,105190,105191,105192,105195],{},"FIDO2 \u002F WebAuthn — ",[950,105193,105194],{},"roaming"," (YubiKey, SoloKeys, Titan)",[1362,105197,104927],{"align":9536},[1362,105199,55189],{"align":9536},[1362,105201,105202],{"align":9536},"✅ bring the key",[1362,105204,105205],{"align":9536},"D (+ B if biometric key)",[1362,105207,105208],{},[941,105209,15944],{},[1344,105211,105212,105218,105220,105222,105225,105228],{},[1362,105213,105191,105214,105217],{},[950,105215,105216],{},"platform"," (Hello, Touch\u002FFace ID, Android)",[1362,105219,104927],{"align":9536},[1362,105221,55189],{"align":9536},[1362,105223,105224],{"align":9536},"❌ device-bound",[1362,105226,105227],{"align":9536},"D + B",[1362,105229,105230],{},[941,105231,15944],{},[1344,105233,105234,105237,105240,105242,105245,105248],{},[1362,105235,105236],{},"Passkey — synced (iCloud Keychain, Google Password Manager, 1Password)",[1362,105238,105239],{"align":9536},"✅ at sync",[1362,105241,55189],{"align":9536},[1362,105243,105244],{"align":9536},"⚠️ via QR cross-device",[1362,105246,105247],{"align":9536},"D (synced)",[1362,105249,105250,105252],{},[941,105251,15944],{}," (hybrid transport)",[1344,105254,105255,105258,105260,105262,105265,105268],{},[1362,105256,105257],{},"Smart cards — PIV \u002F CAC \u002F e-Residency",[1362,105259,104927],{"align":9536},[1362,105261,55189],{"align":9536},[1362,105263,105264],{"align":9536},"✅ bring the card",[1362,105266,105267],{"align":9536},"D + K (card PIN)",[1362,105269,105270],{},"❌ not covered",[1344,105272,105273,105276,105278,105280,105282,105284],{},[1362,105274,105275],{},"Hardware OTP tokens (RSA SecurID, classic Yubikey OTP)",[1362,105277,104927],{"align":9536},[1362,105279,55189],{"align":9536},[1362,105281,55189],{"align":9536},[1362,105283,104863],{"align":9536},[1362,105285,105286],{},"❌ (legacy; FIDO2 covers)",[1344,105288,105289,105292,105294,105296,105299,105301],{},[1362,105290,105291],{},"Bluetooth proximity (Apple Watch unlock, Pixel Smart Lock)",[1362,105293,104927],{"align":9536},[1362,105295,55189],{"align":9536},[1362,105297,105298],{"align":9536},"❌ wearable-bound",[1362,105300,104863],{"align":9536},[1362,105302,104927],{},[1344,105304,105305,105308,105310,105313,105316,105318],{},[1362,105306,105307],{},"Recovery codes (printable)",[1362,105309,104927],{"align":9536},[1362,105311,105312],{"align":9536},"✅ paper safe",[1362,105314,105315],{"align":9536},"⚠️ emergency only",[1362,105317,104863],{"align":9536},[1362,105319,105320],{},[941,105321,18152],{},[1344,105323,105324,105327,105329,105331,105333,105336],{},[1362,105325,105326],{},"NFC \u002F contactless ID cards (eID, MyKad, Aadhaar w\u002F NFC)",[1362,105328,104927],{"align":9536},[1362,105330,55189],{"align":9536},[1362,105332,55189],{"align":9536},[1362,105334,105335],{"align":9536},"D + K (PIN)",[1362,105337,104927],{},[1022,105339,105341],{"id":105340},"_4-inherence-biometrics","4. Inherence — biometrics",[937,105343,105344,105345,105347],{},"Biometrics never ",[1092,105346,38486],{}," the secret — they gate access to a local credential. So the noy-db match is always indirect.",[1338,105349,105350,105366],{},[1341,105351,105352],{},[1344,105353,105354,105356,105358,105360,105362,105364],{},[1347,105355,7831],{},[1347,105357,104817],{"align":9536},[1347,105359,104910],{"align":9536},[1347,105361,104913],{"align":9536},[1347,105363,104855],{"align":9536},[1347,105365,104875],{},[1357,105367,105368,105389,105408,105425,105442],{},[1344,105369,105370,105373,105375,105377,105379,105382],{},[1362,105371,105372],{},"Touch ID \u002F Face ID (Apple)",[1362,105374,104927],{"align":9536},[1362,105376,55189],{"align":9536},[1362,105378,104927],{"align":9536},[1362,105380,105381],{"align":9536},"B (gates passkey\u002Fkeychain)",[1362,105383,105384,105386,105387],{},[941,105385,15944],{}," (platform); sealing via ",[941,105388,13497],{},[1344,105390,105391,105394,105396,105398,105400,105403],{},[1362,105392,105393],{},"Windows Hello (PIN\u002Ffingerprint\u002Fface\u002Firis)",[1362,105395,104927],{"align":9536},[1362,105397,55189],{"align":9536},[1362,105399,104927],{"align":9536},[1362,105401,105402],{"align":9536},"B + K",[1362,105404,105405,105407],{},[941,105406,15944],{}," (platform)",[1344,105409,105410,105413,105415,105417,105419,105421],{},[1362,105411,105412],{},"Android Biometric (BiometricPrompt)",[1362,105414,104927],{"align":9536},[1362,105416,55189],{"align":9536},[1362,105418,104927],{"align":9536},[1362,105420,104867],{"align":9536},[1362,105422,105423,105407],{},[941,105424,15944],{},[1344,105426,105427,105430,105432,105434,105437,105440],{},[1362,105428,105429],{},"Voice biometrics (banking IVR)",[1362,105431,55189],{"align":9536},[1362,105433,55189],{"align":9536},[1362,105435,105436],{"align":9536},"⚠️ replay-attackable",[1362,105438,105439],{"align":9536},"B weak",[1362,105441,104927],{},[1344,105443,105444,105447,105449,105451,105454,105456],{},[1362,105445,105446],{},"Iris \u002F vein \u002F palm (industrial \u002F clinical)",[1362,105448,104927],{"align":9536},[1362,105450,55189],{"align":9536},[1362,105452,105453],{"align":9536},"✅ if scanner shared",[1362,105455,104867],{"align":9536},[1362,105457,104927],{},[1022,105459,105461],{"id":105460},"_5-threshold-split-secrets","5. Threshold \u002F split secrets",[937,105463,3289,105464,105466,105467,105469,105470,105472,105473,105475,105476,105479,105480,105482,105483,105485],{},[950,105465,19592],{}," is an enrolled way back into the vault when the secret is lost (full lifecycle on ",[1007,105468,844],{"href":845},"). Four profiles exist in the recovery API: ",[950,105471,19603],{}," (printed single-use codes), ",[950,105474,325],{}," (K-of-N shares held by trustees), ",[950,105477,105478],{},"multi-channel 2-of-3"," (email OTP + device PIN + paper code; any two suffice), and ",[950,105481,19630],{}," (an admin issues a magic-link grant and the user adds one factor). Paper and Shamir are available today; multi-channel and admin-mediated declare their API surface and throw ",[941,105484,16540],{}," when used.",[1338,105487,105488,105504],{},[1341,105489,105490],{},[1344,105491,105492,105494,105496,105498,105500,105502],{},[1347,105493,7831],{},[1347,105495,104817],{"align":9536},[1347,105497,104910],{"align":9536},[1347,105499,104913],{"align":9536},[1347,105501,104855],{"align":9536},[1347,105503,104875],{},[1357,105505,105506,105525,105550,105569],{},[1344,105507,105508,105511,105513,105516,105518,105521],{},[1362,105509,105510],{},"Shamir Secret Sharing (K-of-N)",[1362,105512,104927],{"align":9536},[1362,105514,105515],{"align":9536},"⚠️ coordination",[1362,105517,104955],{"align":9536},[1362,105519,105520],{"align":9536},"D × N",[1362,105522,105523],{},[941,105524,15994],{},[1344,105526,105527,105530,105533,105535,105537,105540],{},[1362,105528,105529],{},"Multi-channel 2-of-3 (the multi-channel recovery profile)",[1362,105531,105532],{"align":9536},"partial",[1362,105534,55189],{"align":9536},[1362,105536,104955],{"align":9536},[1362,105538,105539],{"align":9536},"K + D mix",[1362,105541,105542,105543,6030,105545,6030,105547,105549],{},"composed: ",[941,105544,18152],{},[941,105546,15979],{},[941,105548,15958],{}," (profile not yet implemented)",[1344,105551,105552,105555,105557,105559,105561,105564],{},[1362,105553,105554],{},"Admin-mediated magic-link grant (the admin-mediated recovery profile)",[1362,105556,55189],{"align":9536},[1362,105558,55189],{"align":9536},[1362,105560,55189],{"align":9536},[1362,105562,105563],{"align":9536},"D + admin",[1362,105565,105566,105568],{},[941,105567,15909],{}," + the hub's magic-link grant flow",[1344,105570,105571,105574,105576,105578,105580,105583],{},[1362,105572,105573],{},"MPC \u002F threshold signing (Fireblocks, Web3Auth tssLib)",[1362,105575,55189],{"align":9536},[1362,105577,104955],{"align":9536},[1362,105579,104955],{"align":9536},[1362,105581,105582],{"align":9536},"D × parties",[1362,105584,104927],{},[1022,105586,105588],{"id":105587},"_6-oidc-federated-identity-providers","6. OIDC \u002F federated identity providers",[937,105590,105591,105592,105595,105596,105598],{},"All require ",[950,105593,105594],{},"online at auth time",". All map to ",[941,105597,15969],{}," with different issuer URLs. Differences are ecosystem fit and built-in 2FA strength, not protocol.",[1061,105600,105601],{},[937,105602,105603,105606,105607,105609,105610,1952,105613,105615,105616,105618,105619,105621,105622,105625,105626,105628,105629,105631,105632,968,105635,105638,105639,105641,105642,105648,105649,105651],{},[950,105604,105605],{},"Server-side dependency"," — OIDC is the only ",[941,105608,1787],{}," family that requires ",[950,105611,105612],{},"consumer-operated infrastructure",[941,105614,44580],{}," uses a split-key model: the KEK is XOR-split into a ",[941,105617,84761],{}," (a per-device secret that never leaves the device) and a ",[941,105620,38374],{}," held by a small HTTP service — a ",[1092,105623,105624],{},"key connector"," — that verifies ID tokens against the issuer's JWKS and stores each user's ",[941,105627,38374],{}," indexed by the token's ",[941,105630,44608],{}," claim. The package implements the client side and documents the two-endpoint server contract (",[941,105633,105634],{},"PUT \u002Fkek-fragment",[941,105636,105637],{},"GET \u002Fkek-fragment","); it deliberately does ",[950,105640,1004],{}," ship a reference server, hosted instance, or deploy template. If you have no off-device infrastructure, prefer ",[1007,105643,105646],{"href":105644,"rel":105645},"https:\u002F\u002Fgithub.com\u002Fnoy-db\u002Fon\u002Ftree\u002Fmain\u002Fon-webauthn",[3371],[941,105647,17049],{}," (platform passkey via Touch ID \u002F Face ID \u002F Windows Hello) — the same \"Login with X\" UX without a server, because the platform passkey ",[1092,105650,3082],{}," the device-bound credential.",[1338,105653,105654,105672],{},[1341,105655,105656],{},[1344,105657,105658,105660,105662,105664,105667,105670],{},[1347,105659,34178],{},[1347,105661,104910],{"align":9536},[1347,105663,104913],{"align":9536},[1347,105665,105666],{},"Default factors",[1347,105668,105669],{},"Notes \u002F caveat",[1347,105671,104875],{},[1357,105673,105674,105694,105714,105734,105753,105772,105789,105808,105827,105845,105863,105882,105900,105920,105938,105957,105975,105993],{},[1344,105675,105676,105679,105681,105684,105687,105690],{},[1362,105677,105678],{},"Google Sign-In",[1362,105680,55189],{"align":9536},[1362,105682,105683],{"align":9536},"⚠️ leaves session",[1362,105685,105686],{},"K + optional D (Google Prompt \u002F TOTP \u002F key)",[1362,105688,105689],{},"Consumer + Workspace",[1362,105691,105692],{},[941,105693,15969],{},[1344,105695,105696,105699,105701,105704,105707,105710],{},[1362,105697,105698],{},"Apple Sign-In",[1362,105700,55189],{"align":9536},[1362,105702,105703],{"align":9536},"❌ Apple-bound",[1362,105705,105706],{},"K + B (device) + D",[1362,105708,105709],{},"Anonymizes email",[1362,105711,105712],{},[941,105713,15969],{},[1344,105715,105716,105719,105721,105724,105727,105730],{},[1362,105717,105718],{},"Microsoft \u002F Entra ID",[1362,105720,55189],{"align":9536},[1362,105722,105723],{"align":9536},"✅ enterprise",[1362,105725,105726],{},"K + D (Authenticator)",[1362,105728,105729],{},"Conditional Access",[1362,105731,105732],{},[941,105733,15969],{},[1344,105735,105736,105739,105741,105743,105746,105749],{},[1362,105737,105738],{},"AD FS (modern)",[1362,105740,55189],{"align":9536},[1362,105742,55189],{"align":9536},[1362,105744,105745],{},"K + D",[1362,105747,105748],{},"OIDC since Server 2016",[1362,105750,105751],{},[941,105752,15969],{},[1344,105754,105755,105758,105760,105762,105765,105768],{},[1362,105756,105757],{},"GitHub",[1362,105759,55189],{"align":9536},[1362,105761,104955],{"align":9536},[1362,105763,105764],{},"K + D (TOTP\u002Fpasskey)",[1362,105766,105767],{},"Dev-tooling auth",[1362,105769,105770],{},[941,105771,15969],{},[1344,105773,105774,105777,105779,105781,105783,105785],{},[1362,105775,105776],{},"GitLab",[1362,105778,55189],{"align":9536},[1362,105780,104955],{"align":9536},[1362,105782,105745],{},[1362,105784],{},[1362,105786,105787],{},[941,105788,15969],{},[1344,105790,105791,105794,105796,105798,105801,105804],{},[1362,105792,105793],{},"Okta",[1362,105795,55189],{"align":9536},[1362,105797,105723],{"align":9536},[1362,105799,105800],{},"K + D (Okta Verify push)",[1362,105802,105803],{},"Workforce IdP",[1362,105805,105806],{},[941,105807,15969],{},[1344,105809,105810,105813,105815,105817,105820,105823],{},[1362,105811,105812],{},"Auth0",[1362,105814,55189],{"align":9536},[1362,105816,55189],{"align":9536},[1362,105818,105819],{},"tenant-configurable",[1362,105821,105822],{},"B2C \u002F B2B SaaS IdP",[1362,105824,105825],{},[941,105826,15969],{},[1344,105828,105829,105831,105833,105835,105838,105841],{},[1362,105830,85848],{},[1362,105832,55189],{"align":9536},[1362,105834,104955],{"align":9536},[1362,105836,105837],{},"K + optional TOTP\u002FSMS",[1362,105839,105840],{},"State-machine wrapper is real value but the wrap key still needs split-key shape",[1362,105842,105843],{},[941,105844,15969],{},[1344,105846,105847,105850,105852,105854,105856,105859],{},[1362,105848,105849],{},"Firebase Auth",[1362,105851,55189],{"align":9536},[1362,105853,104955],{"align":9536},[1362,105855,105745],{},[1362,105857,105858],{},"Mobile-first",[1362,105860,105861],{},[941,105862,15969],{},[1344,105864,105865,105868,105870,105872,105875,105878],{},[1362,105866,105867],{},"Keycloak (self-host)",[1362,105869,55189],{"align":9536},[1362,105871,55189],{"align":9536},[1362,105873,105874],{},"configurable",[1362,105876,105877],{},"On-prem IdP",[1362,105879,105880],{},[941,105881,15969],{},[1344,105883,105884,105887,105889,105891,105893,105896],{},[1362,105885,105886],{},"ForgeRock \u002F PingOne",[1362,105888,55189],{"align":9536},[1362,105890,105723],{"align":9536},[1362,105892,105874],{},[1362,105894,105895],{},"Legacy enterprise IAM",[1362,105897,105898],{},[941,105899,15969],{},[1344,105901,105902,105905,105907,105909,105912,105915],{},[1362,105903,105904],{},"Facebook \u002F Meta",[1362,105906,55189],{"align":9536},[1362,105908,104927],{"align":9536},[1362,105910,105911],{},"K + optional D",[1362,105913,105914],{},"Consumer; not for vault",[1362,105916,105917,105919],{},[941,105918,15969],{}," (not recommended)",[1344,105921,105922,105925,105927,105929,105931,105934],{},[1362,105923,105924],{},"LinkedIn \u002F X \u002F Discord \u002F Slack \u002F Notion \u002F Atlassian",[1362,105926,55189],{"align":9536},[1362,105928,104955],{"align":9536},[1362,105930,105745],{},[1362,105932,105933],{},"Work-app + consumer",[1362,105935,105936],{},[941,105937,15969],{},[1344,105939,105940,105943,105945,105947,105950,105953],{},[1362,105941,105942],{},"LINE \u002F Kakao \u002F WeChat \u002F Naver",[1362,105944,55189],{"align":9536},[1362,105946,104955],{"align":9536},[1362,105948,105949],{},"K + D (varies)",[1362,105951,105952],{},"Asia-regional",[1362,105954,105955],{},[941,105956,15969],{},[1344,105958,105959,105962,105964,105966,105968,105971],{},[1362,105960,105961],{},"Authentik \u002F Zitadel \u002F Logto",[1362,105963,55189],{"align":9536},[1362,105965,55189],{"align":9536},[1362,105967,105874],{},[1362,105969,105970],{},"Open-source Auth0\u002FOkta",[1362,105972,105973],{},[941,105974,15969],{},[1344,105976,105977,105980,105982,105984,105986,105989],{},[1362,105978,105979],{},"Clerk \u002F Stytch \u002F WorkOS \u002F Frontegg",[1362,105981,55189],{"align":9536},[1362,105983,55189],{"align":9536},[1362,105985,105007],{},[1362,105987,105988],{},"DevX-IdP-as-SDK",[1362,105990,105991],{},[941,105992,15969],{},[1344,105994,105995,105998,106000,106002,106005,106008],{},[1362,105996,105997],{},"ID.me \u002F Login.gov \u002F GOV.UK Verify",[1362,105999,55189],{"align":9536},[1362,106001,55189],{"align":9536},[1362,106003,106004],{},"K + D + identity proofing",[1362,106006,106007],{},"Government IdP",[1362,106009,106010],{},[941,106011,15969],{},[1022,106013,106015],{"id":106014},"_7-enterprise-directory-protocols","7. Enterprise \u002F directory protocols",[1338,106017,106018,106034],{},[1341,106019,106020],{},[1344,106021,106022,106024,106026,106028,106030,106032],{},[1347,106023,7831],{},[1347,106025,104817],{"align":9536},[1347,106027,104910],{"align":9536},[1347,106029,104913],{"align":9536},[1347,106031,104855],{"align":9536},[1347,106033,104875],{},[1357,106035,106036,106056,106073,106092,106107,106123],{},[1344,106037,106038,106041,106044,106046,106049,106051],{},[1362,106039,106040],{},"LDAP simple\u002FSASL bind (incl AD)",[1362,106042,106043],{"align":9536},"✅ to DC",[1362,106045,104955],{"align":9536},[1362,106047,106048],{"align":9536},"✅ on-prem",[1362,106050,12279],{"align":9536},[1362,106052,106053,106054,1381],{},"❌ not covered (would need the split-key shape, like ",[941,106055,15969],{},[1344,106057,106058,106061,106064,106066,106068,106071],{},[1362,106059,106060],{},"Kerberos \u002F IWA \u002F SPNEGO",[1362,106062,106063],{"align":9536},"✅ KDC then cached",[1362,106065,104955],{"align":9536},[1362,106067,55189],{"align":9536},[1362,106069,106070],{"align":9536},"K → D (TGT)",[1362,106072,104927],{},[1344,106074,106075,106078,106080,106082,106084,106087],{},[1362,106076,106077],{},"SAML 2.0",[1362,106079,55189],{"align":9536},[1362,106081,55189],{"align":9536},[1362,106083,55189],{"align":9536},[1362,106085,106086],{"align":9536},"varies by IdP",[1362,106088,106089,106090],{},"❌ — bridge the IdP to OIDC and use ",[941,106091,15969],{},[1344,106093,106094,106097,106099,106101,106103,106105],{},[1362,106095,106096],{},"RADIUS",[1362,106098,55189],{"align":9536},[1362,106100,104955],{"align":9536},[1362,106102,55189],{"align":9536},[1362,106104,12279],{"align":9536},[1362,106106,104927],{},[1344,106108,106109,106112,106114,106116,106119,106121],{},[1362,106110,106111],{},"SSH cert auth (CA-signed)",[1362,106113,104927],{"align":9536},[1362,106115,55189],{"align":9536},[1362,106117,106118],{"align":9536},"✅ bring key",[1362,106120,104863],{"align":9536},[1362,106122,104927],{},[1344,106124,106125,106128,106130,106132,106135,106137],{},[1362,106126,106127],{},"mTLS client certs",[1362,106129,104927],{"align":9536},[1362,106131,55189],{"align":9536},[1362,106133,106134],{"align":9536},"⚠️ cert provisioning",[1362,106136,104863],{"align":9536},[1362,106138,104927],{},[1022,106140,106142],{"id":106141},"_8-os-keychain-device-local-sealing-not-authenticators","8. OS keychain \u002F device-local sealing — NOT authenticators",[937,106144,106145,106146,106148,106149,106153,106154,106156,106157,106159,106160,106162,106163,106165,106166,13538,106168,106171,106172,957],{},"These don't authenticate the ",[1092,106147,16225],{}," — they protect the vault's root secret under OS-account guarantees. They map to the ",[1007,106150,106151,9789],{"href":170},[941,106152,1139],{},": implementations of the ",[941,106155,14350],{}," interface that ",[941,106158,1086],{}," exports for ",[950,106161,14373],{}," — the vault mode where the secret is machine-generated, sealed by the provider, and never shown to the user (see ",[1007,106164,844],{"href":845},"). One caveat is structural: unlike every other satellite family, an ",[941,106167,1139],{},[950,106169,106170],{},"can decrypt the vault it unseals"," — that is the point, and the safeguard is scoping the host to a dedicated vault (the trust boundary is spelled out on the ",[1007,106173,106174,106176],{"href":170},[941,106175,1139],{}," overview",[1338,106178,106179,106195],{},[1341,106180,106181],{},[1344,106182,106183,106185,106187,106189,106191,106193],{},[1347,106184,9665],{},[1347,106186,104817],{"align":9536},[1347,106188,104910],{"align":9536},[1347,106190,104913],{"align":9536},[1347,106192,104855],{"align":9536},[1347,106194,104875],{},[1357,106196,106197,106214,106231,106246,106261,106276,106292,106312],{},[1344,106198,106199,106202,106204,106206,106209,106212],{},[1362,106200,106201],{},"Windows DPAPI \u002F Credential Manager",[1362,106203,104927],{"align":9536},[1362,106205,55189],{"align":9536},[1362,106207,106208],{"align":9536},"❌ user-bound",[1362,106210,106211],{"align":9536},"D (OS account)",[1362,106213,105270],{},[1344,106215,106216,106219,106221,106223,106225,106227],{},[1362,106217,106218],{},"macOS Keychain Services",[1362,106220,104927],{"align":9536},[1362,106222,55189],{"align":9536},[1362,106224,104927],{"align":9536},[1362,106226,104863],{"align":9536},[1362,106228,106229],{},[941,106230,13497],{},[1344,106232,106233,106236,106238,106240,106242,106244],{},[1362,106234,106235],{},"iOS Keychain (Secure Enclave)",[1362,106237,104927],{"align":9536},[1362,106239,55189],{"align":9536},[1362,106241,104927],{"align":9536},[1362,106243,105227],{"align":9536},[1362,106245,105270],{},[1344,106247,106248,106251,106253,106255,106257,106259],{},[1362,106249,106250],{},"Android Keystore \u002F StrongBox \u002F TEE",[1362,106252,104927],{"align":9536},[1362,106254,55189],{"align":9536},[1362,106256,104927],{"align":9536},[1362,106258,105227],{"align":9536},[1362,106260,105270],{},[1344,106262,106263,106266,106268,106270,106272,106274],{},[1362,106264,106265],{},"Linux Secret Service (libsecret, gnome-keyring, KWallet)",[1362,106267,104927],{"align":9536},[1362,106269,55189],{"align":9536},[1362,106271,104927],{"align":9536},[1362,106273,104863],{"align":9536},[1362,106275,105270],{},[1344,106277,106278,106281,106283,106285,106288,106290],{},[1362,106279,106280],{},"TPM 2.0 (sealed-to-PCR \u002F no-export)",[1362,106282,104927],{"align":9536},[1362,106284,55189],{"align":9536},[1362,106286,106287],{"align":9536},"✅ server",[1362,106289,104863],{"align":9536},[1362,106291,105270],{},[1344,106293,106294,106297,106299,106301,106303,106305],{},[1362,106295,106296],{},"Apple Secure Enclave",[1362,106298,104927],{"align":9536},[1362,106300,55189],{"align":9536},[1362,106302,104927],{"align":9536},[1362,106304,105227],{"align":9536},[1362,106306,106307,106308,1261,106310],{},"indirectly via ",[941,106309,15944],{},[941,106311,13497],{},[1344,106313,106314,106319,106321,106323,106325,106327],{},[1362,106315,106316,106317],{},"Browser CredentialStore \u002F ",[941,106318,88036],{},[1362,106320,104927],{"align":9536},[1362,106322,55189],{"align":9536},[1362,106324,104927],{"align":9536},[1362,106326,104863],{"align":9536},[1362,106328,106329],{},"indirectly via WebAuthn",[1022,106331,106333],{"id":106332},"_9-cloud-centralized-kms-hsm-server-context-sealing","9. Cloud \u002F centralized KMS & HSM — server-context sealing",[937,106335,106336,106337,106339],{},"Same family as section 8 (sealing, not auth) but server-side: ",[941,106338,1139],{}," sealing providers for managed-secret mode in server contexts — a host you control opens a scoped vault to compute on a user's behalf.",[1338,106341,106342,106358],{},[1341,106343,106344],{},[1344,106345,106346,106348,106350,106352,106354,106356],{},[1347,106347,21181],{},[1347,106349,104817],{"align":9536},[1347,106351,104910],{"align":9536},[1347,106353,104913],{"align":9536},[1347,106355,104855],{"align":9536},[1347,106357,104875],{},[1357,106359,106360,106381,106398,106415,106432,106448,106463,106478],{},[1344,106361,106362,106368,106370,106372,106374,106377],{},[1362,106363,106364,106365,106367],{},"Host environment variable (base64 256-bit key, ",[941,106366,33663],{}," by default)",[1362,106369,104927],{"align":9536},[1362,106371,104955],{"align":9536},[1362,106373,106287],{"align":9536},[1362,106375,106376],{"align":9536},"D (host env)",[1362,106378,106379],{},[941,106380,13481],{},[1344,106382,106383,106385,106387,106389,106391,106394],{},[1362,106384,34230],{},[1362,106386,55189],{"align":9536},[1362,106388,104955],{"align":9536},[1362,106390,106287],{"align":9536},[1362,106392,106393],{"align":9536},"D (IAM)",[1362,106395,106396],{},[941,106397,13485],{},[1344,106399,106400,106403,106405,106407,106409,106411],{},[1362,106401,106402],{},"GCP Cloud KMS \u002F Cloud HSM",[1362,106404,55189],{"align":9536},[1362,106406,104955],{"align":9536},[1362,106408,55189],{"align":9536},[1362,106410,104863],{"align":9536},[1362,106412,106413],{},[941,106414,13489],{},[1344,106416,106417,106420,106422,106424,106426,106428],{},[1362,106418,106419],{},"Azure Key Vault \u002F Managed HSM",[1362,106421,55189],{"align":9536},[1362,106423,104955],{"align":9536},[1362,106425,55189],{"align":9536},[1362,106427,104863],{"align":9536},[1362,106429,106430],{},[941,106431,13493],{},[1344,106433,106434,106437,106439,106441,106443,106446],{},[1362,106435,106436],{},"HashiCorp Vault Transit",[1362,106438,55189],{"align":9536},[1362,106440,104955],{"align":9536},[1362,106442,55189],{"align":9536},[1362,106444,106445],{"align":9536},"D (token)",[1362,106447,105270],{},[1344,106449,106450,106453,106455,106457,106459,106461],{},[1362,106451,106452],{},"Cloudflare Keyless SSL \u002F Turnstile",[1362,106454,55189],{"align":9536},[1362,106456,104955],{"align":9536},[1362,106458,55189],{"align":9536},[1362,106460,104863],{"align":9536},[1362,106462,104927],{},[1344,106464,106465,106468,106470,106472,106474,106476],{},[1362,106466,106467],{},"PKCS 11 HSM (Thales, YubiHSM, SoftHSMv2)",[1362,106469,104927],{"align":9536},[1362,106471,104955],{"align":9536},[1362,106473,55189],{"align":9536},[1362,106475,104863],{"align":9536},[1362,106477,105270],{},[1344,106479,106480,106483,106485,106487,106489,106492],{},[1362,106481,106482],{},"AWS Nitro Enclaves \u002F GCP Confidential Space \u002F Azure Confidential",[1362,106484,55189],{"align":9536},[1362,106486,104955],{"align":9536},[1362,106488,55189],{"align":9536},[1362,106490,106491],{"align":9536},"D + attestation",[1362,106493,104927],{},[1022,106495,106497],{"id":106496},"_10-password-managers-personal-vaults","10. Password managers \u002F personal vaults",[937,106499,106500,106501,106503,106504,106507],{},"Out of scope for ",[941,106502,1787],{}," — users compose them upstream by ",[1092,106505,106506],{},"storing their secret there",". Listed for completeness; not a wrap-key target.",[1338,106509,106510,106527],{},[1341,106511,106512],{},[1344,106513,106514,106517,106519,106521,106523,106525],{},[1347,106515,106516],{},"Tool",[1347,106518,104817],{"align":9536},[1347,106520,104910],{"align":9536},[1347,106522,104913],{"align":9536},[1347,106524,104855],{"align":9536},[1347,106526,104875],{},[1357,106528,106529,106546,106562,106577,106594],{},[1344,106530,106531,106534,106537,106539,106541,106544],{},[1362,106532,106533],{},"1Password",[1362,106535,106536],{"align":9536},"⚠️ sync",[1362,106538,55189],{"align":9536},[1362,106540,104927],{"align":9536},[1362,106542,106543],{"align":9536},"K + D + B",[1362,106545,104927],{},[1344,106547,106548,106551,106553,106555,106558,106560],{},[1362,106549,106550],{},"Bitwarden \u002F Vaultwarden",[1362,106552,106536],{"align":9536},[1362,106554,55189],{"align":9536},[1362,106556,106557],{"align":9536},"⚠️ self-host kiosk",[1362,106559,106543],{"align":9536},[1362,106561,104927],{},[1344,106563,106564,106567,106569,106571,106573,106575],{},[1362,106565,106566],{},"Dashlane \u002F LastPass \u002F NordPass \u002F Proton Pass",[1362,106568,106536],{"align":9536},[1362,106570,55189],{"align":9536},[1362,106572,104927],{"align":9536},[1362,106574,106543],{"align":9536},[1362,106576,104927],{},[1344,106578,106579,106582,106584,106586,106588,106591],{},[1362,106580,106581],{},"Apple Passwords \u002F Google Password Manager",[1362,106583,106536],{"align":9536},[1362,106585,55189],{"align":9536},[1362,106587,104927],{"align":9536},[1362,106589,106590],{"align":9536},"K\u002FB (device)",[1362,106592,106593],{},"indirectly via Keychain",[1344,106595,106596,106599,106601,106603,106606,106609],{},[1362,106597,106598],{},"KeePass \u002F KeePassXC (offline)",[1362,106600,104927],{"align":9536},[1362,106602,55189],{"align":9536},[1362,106604,106605],{"align":9536},"⚠️ portable",[1362,106607,106608],{"align":9536},"K + D (key file)",[1362,106610,104927],{},[1022,106612,106614],{"id":106613},"_11-threat-context-layers-modify-dont-replace-the-above","11. Threat \u002F context layers (modify, don't replace, the above)",[1338,106616,106617,106626],{},[1341,106618,106619],{},[1344,106620,106621,106624],{},[1347,106622,106623],{},"Mechanism",[1347,106625,104875],{},[1357,106627,106628,106637,106647,106654,106661],{},[1344,106629,106630,106633],{},[1362,106631,106632],{},"Lockout \u002F duress code \u002F honeypot vault",[1362,106634,106635],{},[941,106636,15984],{},[1344,106638,106639,106642],{},[1362,106640,106641],{},"Risk-based \u002F adaptive auth (Okta Risk, Auth0 Anomaly, Cloudflare Turnstile)",[1362,106643,106644,106645,1381],{},"❌ (provider-side; layer above ",[941,106646,15969],{},[1344,106648,106649,106652],{},[1362,106650,106651],{},"Device attestation (Play Integrity, App Attest, ASWebAuthSession)",[1362,106653,104927],{},[1344,106655,106656,106659],{},[1362,106657,106658],{},"Behavioral biometrics (typing cadence, mouse)",[1362,106660,104927],{},[1344,106662,106663,106666],{},[1362,106664,106665],{},"Geofencing \u002F IP allowlists",[1362,106667,106668],{},"❌ (policy-layer, not auth-layer)",[1022,106670,106672],{"id":106671},"_12-emerging-specialized","12. Emerging \u002F specialized",[1338,106674,106675,106691],{},[1341,106676,106677],{},[1344,106678,106679,106681,106683,106685,106687,106689],{},[1347,106680,7831],{},[1347,106682,104817],{"align":9536},[1347,106684,104910],{"align":9536},[1347,106686,104913],{"align":9536},[1347,106688,104855],{"align":9536},[1347,106690,104875],{},[1357,106692,106693,106711,106735,106751,106767,106783,106799],{},[1344,106694,106695,106698,106700,106702,106705,106707],{},[1362,106696,106697],{},"Passkeys cross-device (BLE hybrid transport, FIDO CTAP 2.2)",[1362,106699,105532],{"align":9536},[1362,106701,55189],{"align":9536},[1362,106703,106704],{"align":9536},"✅ via QR + phone",[1362,106706,105227],{"align":9536},[1362,106708,106709],{},[941,106710,15944],{},[1344,106712,106713,106725,106727,106729,106731,106733],{},[1362,106714,106715,106716,968,106719,968,106722],{},"Decentralized Identifiers — DID\u002FVC (W3C), ",[941,106717,106718],{},"did:key",[941,106720,106721],{},"did:web",[941,106723,106724],{},"did:ion",[1362,106726,105007],{"align":9536},[1362,106728,55189],{"align":9536},[1362,106730,55189],{"align":9536},[1362,106732,104863],{"align":9536},[1362,106734,104927],{},[1344,106736,106737,106740,106743,106745,106747,106749],{},[1362,106738,106739],{},"mDL (ISO\u002FIEC 18013-5 mobile driver license)",[1362,106741,106742],{"align":9536},"❌ BLE",[1362,106744,55189],{"align":9536},[1362,106746,55189],{"align":9536},[1362,106748,105227],{"align":9536},[1362,106750,104927],{},[1344,106752,106753,106756,106758,106760,106762,106765],{},[1362,106754,106755],{},"EUDI Wallet (EU Digital Identity, eIDAS 2)",[1362,106757,105007],{"align":9536},[1362,106759,55189],{"align":9536},[1362,106761,55189],{"align":9536},[1362,106763,106764],{"align":9536},"D + B + K",[1362,106766,104927],{},[1344,106768,106769,106772,106774,106776,106778,106781],{},[1362,106770,106771],{},"BankID (SE\u002FNO\u002FDK\u002FFI) \u002F ItsMe (BE)",[1362,106773,55189],{"align":9536},[1362,106775,55189],{"align":9536},[1362,106777,105053],{"align":9536},[1362,106779,106780],{"align":9536},"D + K + B",[1362,106782,105270],{},[1344,106784,106785,106788,106790,106792,106794,106797],{},[1362,106786,106787],{},"Web3 wallet (SIWE — Sign-In with Ethereum \u002F Solana)",[1362,106789,55189],{"align":9536},[1362,106791,55189],{"align":9536},[1362,106793,104955],{"align":9536},[1362,106795,106796],{"align":9536},"D (priv key) + K (wallet PIN)",[1362,106798,104927],{},[1344,106800,106801,106804,106806,106808,106810,106812],{},[1362,106802,106803],{},"WebOTP API (browser-mediated SMS auto-fill)",[1362,106805,55189],{"align":9536},[1362,106807,55189],{"align":9536},[1362,106809,104955],{"align":9536},[1362,106811,104863],{"align":9536},[1362,106813,104927],{},[104892,106815],{},[1022,106817,106819],{"id":106818},"whats-covered-today","What's covered today",[1111,106821,106822,106828,106849,106872,106881,106887],{},[1114,106823,106824,106827],{},[950,106825,106826],{},"Coverage is strong on offline-first primitives"," (TOTP, WebAuthn, recovery codes, Shamir, PIN, password) — the parts where zero-knowledge actually buys you something. That is deliberate.",[1114,106829,106830,106833,106834,106836,106837,968,106840,4765,106843,106845,106846,106848],{},[950,106831,106832],{},"Coverage is uniform on OIDC."," One ",[941,106835,15969],{}," package handles every provider in section 6, because the protocol — not the brand — is the unit of abstraction. There is no ",[941,106838,106839],{},"on-cognito",[941,106841,106842],{},"on-okta",[941,106844,104709],{},", and none is needed: they are issuer URLs in an ",[941,106847,15969],{}," config.",[1114,106850,106851,15920,106854,106858,106859,106861,106862,106864,106865,968,106867,968,106869,106871],{},[950,106852,106853],{},"Sealing coverage is host-first.",[1007,106855,106856,9789],{"href":170},[941,106857,1139],{}," ships providers for the host environment (",[941,106860,13481],{},"), macOS Keychain (",[941,106863,13497],{},"), and the three major cloud KMSes (",[941,106866,13485],{},[941,106868,13489],{},[941,106870,13493],{},"). There is no provider for Windows DPAPI, the iOS\u002FAndroid keystores, Linux Secret Service, or HashiCorp Vault.",[1114,106873,106874,106877,106878,106880],{},[950,106875,106876],{},"Not covered today:"," on-prem directory bind (LDAP \u002F Kerberos), smart cards (roaming WebAuthn covers most of that ground), standalone push-notification factors (reachable via ",[941,106879,15969],{},"), and regional ID schemes (BankID, EUDI Wallet).",[1114,106882,106883,106886],{},[950,106884,106885],{},"Deliberately omitted:"," SMS OTP, security questions, pattern locks — each breaks the security model in a way stated in its table row.",[1114,106888,106889,106894,106895,106897],{},[950,106890,106891,106893],{},[941,106892,15984],{}," is structurally unique."," It is the only ",[941,106896,1787],{}," package that doesn't authenticate; it modifies how the others authenticate (lockout, duress, honeypot).",[1022,106899,106901,106902,106904],{"id":106900},"why-the-hub-and-the-on-packages-are-separate","Why the hub and the ",[941,106903,1787],{}," packages are separate",[937,106906,106907,106908,1324,106910,106912,106913,106916,106917],{},"When you enroll recovery codes you install ",[941,106909,45343],{},[1092,106911,14128],{}," call ",[941,106914,106915],{},"db.team.enrollRecovery(...)"," on the hub — and the same two-package shape recurs for passwords and PINs. The split is deliberate: ",[950,106918,106919,106920,106922],{},"the hub owns the cryptosystem; the ",[941,106921,1787],{}," package owns the user-facing credential format.",[937,106924,106925,106926,1261,106929,106932,106933,106935,106936,106939,106940,106942,106943,106945,106946,106948],{},"The shared \"wrap a DEK set under a credential-derived key\" primitive — ",[941,106927,106928],{},"mintWrappedDeksBlob",[941,106930,106931],{},"unwrapDeksFromBlob",", exported from ",[941,106934,1086],{}," — derives an AES-GCM key from the credential string via PBKDF2-SHA256 (600,000 iterations) and encrypts the serialized DEK set. It is the same primitive whether the credential is a printed recovery code or a typed password. What differs is what counts as a well-formed ",[950,106937,106938],{},"credential",": a Base32-with-checksum string (",[941,106941,18152],{},"), a validated password (",[941,106944,15953],{},"). (",[941,106947,17062],{}," follows the same pattern but at 100,000 PBKDF2 iterations — a PIN slot's protection window is short and idle-timeout-bounded, and its wire format is deliberately incompatible with the 600K one.)",[1338,106950,106951,106963],{},[1341,106952,106953],{},[1344,106954,106955,106957,106960],{},[1347,106956,29658],{},[1347,106958,106959],{},"Lives in",[1347,106961,106962],{},"Why",[1357,106964,106965,106981,107004,107028,107040],{},[1344,106966,106967,106970,106978],{},[1362,106968,106969],{},"Cryptosystem (PBKDF2-SHA256 600K → AES-GCM wrap of the DEK set)",[1362,106971,106972,953,106974,1261,106976,1381],{},[941,106973,1086],{},[941,106975,106928],{},[941,106977,106931],{},[1362,106979,106980],{},"One audited implementation. Every package calls it. A bug-fix or security review touches one place.",[1344,106982,106983,106994,106998],{},[1362,106984,106985,106986,968,106988,968,106991,1381],{},"On-disk format (",[941,106987,19759],{},[941,106989,106990],{},"KeyringFile.authenticators[]",[941,106992,106993],{},"_meta\u002Frecovery-paper",[1362,106995,106996],{},[941,106997,1086],{},[1362,106999,107000,107001,107003],{},"The hub knows how to read every keyring it ever wrote; the ",[941,107002,1787],{}," packages can come and go without changing the format.",[1344,107005,107006,107021,107025],{},[1362,107007,107008,107009,968,107012,968,107015,968,107018,1381],{},"Orchestration (",[941,107010,107011],{},"db.enrollRecovery",[941,107013,107014],{},"db.recoverSecret",[941,107016,107017],{},"db.enrollAuthenticator",[941,107019,107020],{},"db.unlockViaAuthenticator",[1362,107022,107023],{},[941,107024,1086],{},[1362,107026,107027],{},"These compose multiple primitives and run policy gates. They don't change per credential family.",[1344,107029,107030,107033,107037],{},[1362,107031,107032],{},"User-facing string format (Base32 alphabet + checksum, password validation rules, PIN length policy)",[1362,107034,960,107035,14366],{},[941,107036,1787],{},[1362,107038,107039],{},"Each consumer can swap. An app that needs BIP-39 word lists instead of Base32 can supply its own format without touching the hub.",[1344,107041,107042,107045,107049],{},[1362,107043,107044],{},"Auto-rotation, burn-on-use, atomicity",[1362,107046,107047],{},[941,107048,1086],{},[1362,107050,107051],{},"These are properties of the primitive, not the format.",[937,107053,107054,107055],{},"The classification rule behind the table: ",[950,107056,107057],{},"does the thing produce wrap-key material directly, or does it just shape user input?",[1111,107059,107060,107069],{},[1114,107061,107062,107065,107066,107068],{},[950,107063,107064],{},"Produces wrap-key material directly"," (secret → PBKDF2 → KEK; KEK → AES-KW → DEKs; the wrap-DEKs primitive itself) → ",[950,107067,5188],{},". There is one cryptosystem, and there is one of it.",[1114,107070,107071,107074,107075,107079],{},[950,107072,107073],{},"Shapes user input"," (Base32 codes with checksums, password length policies, 4-digit PINs, WebAuthn ceremonies, OIDC token flows) → ",[950,107076,1415,107077,14366],{},[941,107078,1787],{},". There are many possible shapes; each consumer picks one.",[937,107081,107082,107083,107085],{},"This is also why ",[941,107084,45343],{}," stays a separate small package rather than a hub subpath: keeping the format outside the hub is what lets you swap Base32 for another code format without forking the cryptosystem.",[1022,107087,107089],{"id":107088},"classifying-a-method-that-isnt-in-the-tables","Classifying a method that isn't in the tables",[937,107091,107092],{},"Evaluating an authentication method this map doesn't list? Walk the same checklist the map itself was built with:",[1234,107094,107095,107101,107113,107129],{},[1114,107096,107097,107100],{},[950,107098,107099],{},"Is it an authenticator, an IdP, or a sealing-key store?"," Mis-classification means the wrong package family — and the wrong threat model. See \"Three distinct functions\" above.",[1114,107102,107103,107106,107107,107109,107110,107112],{},[950,107104,107105],{},"Does the protocol already have a generic package?"," A vendor-flavored OIDC provider is an ",[941,107108,15969],{}," config, not a missing package. A vendor-flavored OS keychain is a sealing concern (",[941,107111,1139],{},"), not an authenticator.",[1114,107114,107115,107118,107119,107122,107123,27685,107125,107128],{},[950,107116,107117],{},"Can it carry wrap-key material on its own?"," If not (LDAP bind, Kerberos tickets, Cognito tokens), the only honest shape is ",[950,107120,107121],{},"split-key"," — the same pattern as ",[941,107124,15969],{},[950,107126,107127],{},"gate-only composition"," with a real authenticator.",[1114,107130,107131,107134,107135,2156,107137,107139],{},[950,107132,107133],{},"Does it work on shared devices?"," If only on personal devices, treat that as a hard constraint in your deployment. Tier 3 (",[941,107136,15958],{},[1092,107138,18391],{}," safe on shared workstations regardless.",[1022,107141,107143],{"id":107142},"common-questions-this-map-answers","Common questions this map answers",[1338,107145,107146,107158],{},[1341,107147,107148],{},[1344,107149,107150,107152,107155],{},[1347,107151,35907],{},[1347,107153,107154],{},"Where to look",[1347,107156,107157],{},"One-line answer",[1357,107159,107160,107173,107185,107197,107209,107223,107243,107256,107269,107284,107295],{},[1344,107161,107162,107165,107168],{},[1362,107163,107164],{},"\"Login with Google?\"",[1362,107166,107167],{},"§6",[1362,107169,107170,107172],{},[941,107171,15969],{}," with Google issuer.",[1344,107174,107175,107178,107180],{},[1362,107176,107177],{},"\"Login with Apple?\"",[1362,107179,107167],{},[1362,107181,107182,107184],{},[941,107183,15969],{}," with Apple issuer. Apple-ecosystem only on personal device.",[1344,107186,107187,107190,107192],{},[1362,107188,107189],{},"\"Login with Microsoft \u002F work account?\"",[1362,107191,107167],{},[1362,107193,107194,107196],{},[941,107195,15969],{}," with Entra ID issuer.",[1344,107198,107199,107202,107204],{},[1362,107200,107201],{},"\"Login with AWS Cognito?\"",[1362,107203,107167],{},[1362,107205,107206,107208],{},[941,107207,15969],{}," with the Cognito hosted-UI issuer; state-machine sugar can live as a small helper inside the consumer app.",[1344,107210,107211,107214,107217],{},[1362,107212,107213],{},"\"Login with Active Directory?\"",[1362,107215,107216],{},"§6, §7",[1362,107218,107219,107220,107222],{},"Modern AD FS or Entra → ",[941,107221,15969],{},". Raw on-prem AD (LDAP\u002FKerberos) → not covered; it would need the split-key shape.",[1344,107224,107225,107228,107231],{},[1362,107226,107227],{},"\"Login with Windows \u002F Windows Hello?\"",[1362,107229,107230],{},"§3, §4, §8",[1362,107232,107233,107234,107236,107237,107239,107240,107242],{},"Windows Hello ",[1092,107235,3082],{}," WebAuthn → ",[941,107238,15944],{},". \"Use my Windows account to unlock\" (DPAPI sealing) → not covered; no ",[941,107241,1139],{}," provider for Windows exists.",[1344,107244,107245,107248,107251],{},[1362,107246,107247],{},"\"Login with Touch ID \u002F Face ID?\"",[1362,107249,107250],{},"§4",[1362,107252,107253,107254,1520],{},"Platform passkey via ",[941,107255,15944],{},[1344,107257,107258,107261,107264],{},[1362,107259,107260],{},"\"Login with my YubiKey?\"",[1362,107262,107263],{},"§3",[1362,107265,107266,107267,1520],{},"Roaming WebAuthn via ",[941,107268,15944],{},[1344,107270,107271,107274,107277],{},[1362,107272,107273],{},"\"SMS as a 2FA factor?\"",[1362,107275,107276],{},"§2",[1362,107278,107279,107280,4688,107282,1520],{},"Deliberately omitted. SIM-swap and SS7 attacks make it unfit for vault auth. Use ",[941,107281,15974],{},[941,107283,15979],{},[1344,107285,107286,107289,107292],{},[1362,107287,107288],{},"\"BankID \u002F EUDI Wallet?\"",[1362,107290,107291],{},"§12",[1362,107293,107294],{},"Not covered today.",[1344,107296,107297,107300,107302],{},[1362,107298,107299],{},"\"Decentralized identity (DID\u002FVC, SIWE)?\"",[1362,107301,107291],{},[1362,107303,107294],{},[1022,107305,2621],{"id":2620},[1111,107307,107308,107313,107320,107328,107333,107339],{},[1114,107309,107310,107312],{},[1007,107311,844],{"href":845}," — how the primitives compose into the three-tier session lifecycle and policy gates.",[1114,107314,107315,107319],{},[1007,107316,107317,18167],{"href":296},[941,107318,1787],{}," — factor table and per-package summaries.",[1114,107321,107322,107327],{},[1007,107323,107324,107326],{"href":170},[941,107325,1139],{}," family overview"," — the sealing-provider family: trust boundary, managed-secret mode, per-package summaries.",[1114,107329,107330,107332],{},[1007,107331,1516],{"href":66}," — the key hierarchy (secret → KEK → DEKs) everything on this page ultimately unlocks.",[1114,107334,107335,107338],{},[1007,107336,107337],{"href":54},"Core 06 — Permissions and keyring"," — the keyring: the per-user record of wrapped keys that every method on this page ultimately opens.",[1114,107340,107341,107342,968,107347,968,107353,968,107359,968,107365,968,107371,968,107377,968,107383,968,107389,968,107395],{},"Per-package READMEs: ",[1007,107343,107345],{"href":105644,"rel":107344},[3371],[941,107346,15944],{},[1007,107348,107351],{"href":107349,"rel":107350},"https:\u002F\u002Fgithub.com\u002Fnoy-db\u002Fon\u002Ftree\u002Fmain\u002Fon-oidc",[3371],[941,107352,15969],{},[1007,107354,107357],{"href":107355,"rel":107356},"https:\u002F\u002Fgithub.com\u002Fnoy-db\u002Fon\u002Ftree\u002Fmain\u002Fon-pin",[3371],[941,107358,15958],{},[1007,107360,107363],{"href":107361,"rel":107362},"https:\u002F\u002Fgithub.com\u002Fnoy-db\u002Fon\u002Ftree\u002Fmain\u002Fon-totp",[3371],[941,107364,15974],{},[1007,107366,107369],{"href":107367,"rel":107368},"https:\u002F\u002Fgithub.com\u002Fnoy-db\u002Fon\u002Ftree\u002Fmain\u002Fon-email-otp",[3371],[941,107370,15979],{},[1007,107372,107375],{"href":107373,"rel":107374},"https:\u002F\u002Fgithub.com\u002Fnoy-db\u002Fon\u002Ftree\u002Fmain\u002Fon-recovery",[3371],[941,107376,18152],{},[1007,107378,107381],{"href":107379,"rel":107380},"https:\u002F\u002Fgithub.com\u002Fnoy-db\u002Fon\u002Ftree\u002Fmain\u002Fon-shamir",[3371],[941,107382,15994],{},[1007,107384,107387],{"href":107385,"rel":107386},"https:\u002F\u002Fgithub.com\u002Fnoy-db\u002Fon\u002Ftree\u002Fmain\u002Fon-magic-link",[3371],[941,107388,15909],{},[1007,107390,107393],{"href":107391,"rel":107392},"https:\u002F\u002Fgithub.com\u002Fnoy-db\u002Fon\u002Ftree\u002Fmain\u002Fon-password",[3371],[941,107394,15953],{},[1007,107396,107399],{"href":107397,"rel":107398},"https:\u002F\u002Fgithub.com\u002Fnoy-db\u002Fon\u002Ftree\u002Fmain\u002Fon-threat",[3371],[941,107400,15984],{},{"title":1037,"searchDepth":1038,"depth":1038,"links":107402},[107403,107404,107405,107406,107407,107408,107409,107410,107411,107412,107413,107414,107415,107416,107417,107418,107419,107420,107422,107423,107424],{"id":104600,"depth":1041,"text":104601},{"id":104630,"depth":1041,"text":104631},{"id":104718,"depth":1041,"text":104719},{"id":104795,"depth":1041,"text":104796},{"id":104896,"depth":1041,"text":104897},{"id":105019,"depth":1041,"text":105020},{"id":105165,"depth":1041,"text":105166},{"id":105340,"depth":1041,"text":105341},{"id":105460,"depth":1041,"text":105461},{"id":105587,"depth":1041,"text":105588},{"id":106014,"depth":1041,"text":106015},{"id":106141,"depth":1041,"text":106142},{"id":106332,"depth":1041,"text":106333},{"id":106496,"depth":1041,"text":106497},{"id":106613,"depth":1041,"text":106614},{"id":106671,"depth":1041,"text":106672},{"id":106818,"depth":1041,"text":106819},{"id":106900,"depth":1041,"text":107421},"Why the hub and the on-* packages are separate",{"id":107088,"depth":1041,"text":107089},{"id":107142,"depth":1041,"text":107143},{"id":2620,"depth":1041,"text":2621},"Reference map of authentication, unlock, and sealing-key methods — every common auth primitive scored and matched to its on-* or at-* package.",{},{"title":617,"description":107425},"wfqr5o6YwER81CfZ_rcz0aH-pyBrrnuN26hjtbLHoxk",{"id":107430,"title":621,"access":12,"apiToc":929,"body":107431,"category":1042,"description":111609,"extension":1044,"meta":111610,"navigation":1042,"nest":1047,"path":622,"seo":111611,"sinceVersion":1042,"status":1049,"stem":623,"subtitle":1042,"tier":1050,"tocExternal":1042,"__hash__":111612},"docs\u002Fservices\u002Fblobs.md",{"type":931,"value":107432,"toc":111578},[107433,107435,107438,107440,107463,107485,107487,107517,107519,107726,107728,107732,107739,108003,108148,108183,108193,108197,108384,108402,108406,108416,108568,108703,108741,108747,108758,108800,108821,108827,108862,109040,109053,109057,109084,109089,109194,109440,109444,109454,109567,109580,109679,109828,109841,109894,110093,110100,110276,110278,110286,110618,110644,110650,110755,110761,110825,110831,110837,110981,111005,111010,111029,111105,111111,111129,111207,111213,111227,111305,111312,111314,111337,111339,111504,111506,111575],[934,107434,621],{"id":621},[46871,107436],{":rows":107437},"[{\"label\":\"Subpath\",\"value\":\"`@noy-db\u002Fhub\u002Fblobs`\"},{\"label\":\"Factory\",\"value\":\"`withBlobs()`\"},{\"label\":\"Layer\",\"value\":\"`with-shape`\"}]",[1022,107439,29857],{"id":29856},[937,107441,107442,107443,107445,107446,107448,107449,953,107451,968,107454,968,107457,107460,107461,957],{},"Blobs let you attach binary files — PDFs, images, audio — to the records of a noy-db\n",[950,107444,2694],{}," (the end-to-end-encrypted document store; see\n",[1007,107447,65376],{"href":48},"). Each record carries named\n",[950,107450,60887],{},[941,107452,107453],{},"'receipt'",[941,107455,107456],{},"'avatar'",[941,107458,107459],{},"'contract'",", …), and like everything in noy-db the\nbytes are encrypted client-side before the storage backend ever sees them\n(",[1007,107462,21],{"href":66},[937,107464,107465,107466,107469,107470,107473,107474,953,107477,107480,107481,107484],{},"Storage is ",[950,107467,107468],{},"content-addressed"," — a file is identified by a keyed hash of its bytes\n(",[941,107471,107472],{},"eTag = HMAC-SHA-256(blob key, plaintext)","), so two records attaching the same file\nshare one stored copy, and the backend cannot recognize known content because the hash\nis keyed. Large files are split into chunks (256 KB by default), and each chunk is\nencrypted with AES-256-GCM whose ",[950,107475,107476],{},"AAD",[1092,107478,107479],{},"additional authenticated data"," — a value the\nciphertext is cryptographically bound to) is ",[941,107482,107483],{},"{eTag}:{index}:{count}",": a store that\nreorders, substitutes, or truncates chunks produces ciphertext that fails to decrypt.\nMIME types are sniffed from magic bytes when you don't provide one, compressible\npayloads are gzipped before encryption, and a consumer-scheduled compaction pass\nenforces retention policies. The whole service is ~2,376 LOC that stay out of your\nbundle until you opt in.",[1022,107486,70093],{"id":70092},[1111,107488,107489,107500],{},[1114,107490,107491,964,107494,107496,107497,107499],{},[950,107492,107493],{},"None",[941,107495,6775],{}," alone enables ",[941,107498,21881],{}," slots.",[1114,107501,107502,107505,107506,107508,107509,107511,107512,27678,107515,1520],{},[1092,107503,107504],{},"For erasure flows only:"," crypto-shredding blob content via ",[941,107507,2542],{}," pairs ",[941,107510,84477],{}," (the erasure proof) with ",[941,107513,107514],{},"forgetStrategy: withForget()",[1007,107516,685],{"href":686},[1022,107518,99864],{"id":99863},[2217,107520,107522],{"className":2219,"code":107521,"language":2221,"meta":1037,"style":1037},"import { createNoydb } from '@noy-db\u002Fhub'\nimport { withBlobs } from '@noy-db\u002Fhub\u002Fblobs'\n\nconst db = await createNoydb({\n  user: 'alice',                    \u002F\u002F omitting `store` uses the built-in in-memory store;\n  secret: 'alice-secret-2026',  \u002F\u002F pass to-file \u002F to-browser-idb \u002F … in production\n  blobsStrategy: withBlobs(),\n})\nconst vault = await db.openVault('books')\n\ninterface Invoice { id: string; ref: string; reviewed?: boolean }\nconst invoices = vault.collection\u003CInvoice>('invoices')\n",[941,107523,107524,107542,107560,107564,107580,107597,107615,107627,107633,107659,107663,107696],{"__ignoreMap":1037},[2225,107525,107526,107528,107530,107532,107534,107536,107538,107540],{"class":2227,"line":2228},[2225,107527,2232],{"class":2231},[2225,107529,2236],{"class":2235},[2225,107531,2744],{"class":2239},[2225,107533,2243],{"class":2235},[2225,107535,2246],{"class":2231},[2225,107537,2249],{"class":2235},[2225,107539,1086],{"class":2252},[2225,107541,2256],{"class":2235},[2225,107543,107544,107546,107548,107550,107552,107554,107556,107558],{"class":2227,"line":1041},[2225,107545,2232],{"class":2231},[2225,107547,2236],{"class":2235},[2225,107549,58485],{"class":2239},[2225,107551,2243],{"class":2235},[2225,107553,2246],{"class":2231},[2225,107555,2249],{"class":2235},[2225,107557,58494],{"class":2252},[2225,107559,2256],{"class":2235},[2225,107561,107562],{"class":2227,"line":1038},[2225,107563,2285],{"emptyLinePlaceholder":2284},[2225,107565,107566,107568,107570,107572,107574,107576,107578],{"class":2227,"line":2288},[2225,107567,2775],{"class":2774},[2225,107569,2778],{"class":2239},[2225,107571,2781],{"class":2235},[2225,107573,2784],{"class":2231},[2225,107575,2744],{"class":2291},[2225,107577,2789],{"class":2239},[2225,107579,4200],{"class":2235},[2225,107581,107582,107584,107586,107588,107590,107592,107594],{"class":2227,"line":2304},[2225,107583,4205],{"class":2307},[2225,107585,2311],{"class":2235},[2225,107587,2249],{"class":2235},[2225,107589,6408],{"class":2252},[2225,107591,2805],{"class":2235},[2225,107593,2298],{"class":2235},[2225,107595,107596],{"class":2763},"                    \u002F\u002F omitting `store` uses the built-in in-memory store;\n",[2225,107598,107599,107601,107603,107605,107608,107610,107612],{"class":2227,"line":2356},[2225,107600,10423],{"class":2307},[2225,107602,2311],{"class":2235},[2225,107604,2249],{"class":2235},[2225,107606,107607],{"class":2252},"alice-secret-2026",[2225,107609,2805],{"class":2235},[2225,107611,2298],{"class":2235},[2225,107613,107614],{"class":2763},"  \u002F\u002F pass to-file \u002F to-browser-idb \u002F … in production\n",[2225,107616,107617,107619,107621,107623,107625],{"class":2227,"line":2830},[2225,107618,58728],{"class":2307},[2225,107620,2311],{"class":2235},[2225,107622,58485],{"class":2291},[2225,107624,4228],{"class":2239},[2225,107626,1707],{"class":2235},[2225,107628,107629,107631],{"class":2227,"line":2861},[2225,107630,2359],{"class":2235},[2225,107632,2362],{"class":2239},[2225,107634,107635,107637,107639,107641,107643,107645,107647,107649,107651,107653,107655,107657],{"class":2227,"line":4246},[2225,107636,2775],{"class":2774},[2225,107638,2835],{"class":2239},[2225,107640,2781],{"class":2235},[2225,107642,2784],{"class":2231},[2225,107644,2842],{"class":2239},[2225,107646,1520],{"class":2235},[2225,107648,2847],{"class":2291},[2225,107650,2789],{"class":2239},[2225,107652,2805],{"class":2235},[2225,107654,65761],{"class":2252},[2225,107656,2805],{"class":2235},[2225,107658,2362],{"class":2239},[2225,107660,107661],{"class":2227,"line":4253},[2225,107662,2285],{"emptyLinePlaceholder":2284},[2225,107664,107665,107667,107669,107671,107673,107675,107677,107679,107681,107683,107685,107687,107690,107692,107694],{"class":2227,"line":4259},[2225,107666,6994],{"class":2774},[2225,107668,6343],{"class":6342},[2225,107670,2236],{"class":2235},[2225,107672,7070],{"class":2307},[2225,107674,2311],{"class":2235},[2225,107676,6366],{"class":6342},[2225,107678,3769],{"class":2235},[2225,107680,62303],{"class":2307},[2225,107682,2311],{"class":2235},[2225,107684,6366],{"class":6342},[2225,107686,3769],{"class":2235},[2225,107688,107689],{"class":2307}," reviewed",[2225,107691,7011],{"class":2235},[2225,107693,7723],{"class":6342},[2225,107695,6369],{"class":2235},[2225,107697,107698,107700,107702,107704,107706,107708,107710,107712,107714,107716,107718,107720,107722,107724],{"class":2227,"line":6548},[2225,107699,2775],{"class":2774},[2225,107701,6461],{"class":2239},[2225,107703,2781],{"class":2235},[2225,107705,6466],{"class":2239},[2225,107707,1520],{"class":2235},[2225,107709,6471],{"class":2291},[2225,107711,6474],{"class":2235},[2225,107713,6477],{"class":6342},[2225,107715,6480],{"class":2235},[2225,107717,2789],{"class":2239},[2225,107719,2805],{"class":2235},[2225,107721,6487],{"class":2252},[2225,107723,2805],{"class":2235},[2225,107725,2362],{"class":2239},[1022,107727,29893],{"id":29892},[1591,107729,107731],{"id":107730},"attach-list-read-delete","Attach, list, read, delete",[937,107733,107734,22207,107736,107738],{},[941,107735,21881],{},[941,107737,30183],{}," — the handle for every slot on that record.\nNo I\u002FO happens until you call a method.",[2217,107740,107742],{"className":2219,"code":107741,"language":2221,"meta":1037,"style":1037},"await invoices.put('inv-001', { id: 'inv-001', ref: 'INV-001' })\n\nconst pdfBytes = new Uint8Array(await fetch('\u002Freceipt-2026-06.pdf').then(r => r.arrayBuffer()))\n\nconst blobs = invoices.blob('inv-001')\nawait blobs.put('receipt', pdfBytes, {\n  mimeType: 'application\u002Fpdf',            \u002F\u002F omit to auto-detect from magic bytes\n  filename: 'receipt-2026-06.pdf',        \u002F\u002F display\u002Fdownload name; defaults to the slot name\n})\n\nconst bytes = await blobs.get('receipt')\n\u002F\u002F → Uint8Array(48213) — the decrypted original bytes; null if the slot does not exist\n\nconst slots = await blobs.list()\n",[941,107743,107744,107793,107797,107846,107850,107875,107902,107920,107939,107945,107949,107975,107980,107984],{"__ignoreMap":1037},[2225,107745,107746,107748,107750,107752,107754,107756,107758,107760,107762,107764,107766,107768,107770,107772,107774,107776,107778,107780,107782,107784,107787,107789,107791],{"class":2227,"line":2228},[2225,107747,6500],{"class":2231},[2225,107749,6503],{"class":2239},[2225,107751,1520],{"class":2235},[2225,107753,3161],{"class":2291},[2225,107755,2789],{"class":2239},[2225,107757,2805],{"class":2235},[2225,107759,59410],{"class":2252},[2225,107761,2805],{"class":2235},[2225,107763,2298],{"class":2235},[2225,107765,2236],{"class":2235},[2225,107767,7070],{"class":2307},[2225,107769,2311],{"class":2235},[2225,107771,2249],{"class":2235},[2225,107773,59410],{"class":2252},[2225,107775,2805],{"class":2235},[2225,107777,2298],{"class":2235},[2225,107779,62303],{"class":2307},[2225,107781,2311],{"class":2235},[2225,107783,2249],{"class":2235},[2225,107785,107786],{"class":2252},"INV-001",[2225,107788,2805],{"class":2235},[2225,107790,2243],{"class":2235},[2225,107792,2362],{"class":2239},[2225,107794,107795],{"class":2227,"line":1041},[2225,107796,2285],{"emptyLinePlaceholder":2284},[2225,107798,107799,107801,107803,107805,107807,107809,107811,107813,107815,107817,107819,107822,107824,107826,107828,107830,107832,107834,107836,107838,107840,107843],{"class":2227,"line":1038},[2225,107800,2775],{"class":2774},[2225,107802,60928],{"class":2239},[2225,107804,2781],{"class":2235},[2225,107806,8130],{"class":2235},[2225,107808,60935],{"class":2291},[2225,107810,2789],{"class":2239},[2225,107812,6500],{"class":2231},[2225,107814,89756],{"class":2291},[2225,107816,2789],{"class":2239},[2225,107818,2805],{"class":2235},[2225,107820,107821],{"class":2252},"\u002Freceipt-2026-06.pdf",[2225,107823,2805],{"class":2235},[2225,107825,1381],{"class":2239},[2225,107827,1520],{"class":2235},[2225,107829,99354],{"class":2291},[2225,107831,2789],{"class":2239},[2225,107833,13217],{"class":7052},[2225,107835,8116],{"class":2774},[2225,107837,32537],{"class":2239},[2225,107839,1520],{"class":2235},[2225,107841,107842],{"class":2291},"arrayBuffer",[2225,107844,107845],{"class":2239},"()))\n",[2225,107847,107848],{"class":2227,"line":2288},[2225,107849,2285],{"emptyLinePlaceholder":2284},[2225,107851,107852,107854,107857,107859,107861,107863,107865,107867,107869,107871,107873],{"class":2227,"line":2304},[2225,107853,2775],{"class":2774},[2225,107855,107856],{"class":2239}," blobs ",[2225,107858,2781],{"class":2235},[2225,107860,6503],{"class":2239},[2225,107862,1520],{"class":2235},[2225,107864,133],{"class":2291},[2225,107866,2789],{"class":2239},[2225,107868,2805],{"class":2235},[2225,107870,59410],{"class":2252},[2225,107872,2805],{"class":2235},[2225,107874,2362],{"class":2239},[2225,107876,107877,107879,107881,107883,107885,107887,107889,107892,107894,107896,107898,107900],{"class":2227,"line":2356},[2225,107878,6500],{"class":2231},[2225,107880,48557],{"class":2239},[2225,107882,1520],{"class":2235},[2225,107884,3161],{"class":2291},[2225,107886,2789],{"class":2239},[2225,107888,2805],{"class":2235},[2225,107890,107891],{"class":2252},"receipt",[2225,107893,2805],{"class":2235},[2225,107895,2298],{"class":2235},[2225,107897,61006],{"class":2239},[2225,107899,2298],{"class":2235},[2225,107901,2301],{"class":2235},[2225,107903,107904,107907,107909,107911,107913,107915,107917],{"class":2227,"line":2830},[2225,107905,107906],{"class":2307},"  mimeType",[2225,107908,2311],{"class":2235},[2225,107910,2249],{"class":2235},[2225,107912,61020],{"class":2252},[2225,107914,2805],{"class":2235},[2225,107916,2298],{"class":2235},[2225,107918,107919],{"class":2763},"            \u002F\u002F omit to auto-detect from magic bytes\n",[2225,107921,107922,107925,107927,107929,107932,107934,107936],{"class":2227,"line":2861},[2225,107923,107924],{"class":2307},"  filename",[2225,107926,2311],{"class":2235},[2225,107928,2249],{"class":2235},[2225,107930,107931],{"class":2252},"receipt-2026-06.pdf",[2225,107933,2805],{"class":2235},[2225,107935,2298],{"class":2235},[2225,107937,107938],{"class":2763},"        \u002F\u002F display\u002Fdownload name; defaults to the slot name\n",[2225,107940,107941,107943],{"class":2227,"line":4246},[2225,107942,2359],{"class":2235},[2225,107944,2362],{"class":2239},[2225,107946,107947],{"class":2227,"line":4253},[2225,107948,2285],{"emptyLinePlaceholder":2284},[2225,107950,107951,107953,107955,107957,107959,107961,107963,107965,107967,107969,107971,107973],{"class":2227,"line":4259},[2225,107952,2775],{"class":2774},[2225,107954,32469],{"class":2239},[2225,107956,2781],{"class":2235},[2225,107958,2784],{"class":2231},[2225,107960,48557],{"class":2239},[2225,107962,1520],{"class":2235},[2225,107964,3158],{"class":2291},[2225,107966,2789],{"class":2239},[2225,107968,2805],{"class":2235},[2225,107970,107891],{"class":2252},[2225,107972,2805],{"class":2235},[2225,107974,2362],{"class":2239},[2225,107976,107977],{"class":2227,"line":6548},[2225,107978,107979],{"class":2763},"\u002F\u002F → Uint8Array(48213) — the decrypted original bytes; null if the slot does not exist\n",[2225,107981,107982],{"class":2227,"line":6576},[2225,107983,2285],{"emptyLinePlaceholder":2284},[2225,107985,107986,107988,107991,107993,107995,107997,107999,108001],{"class":2227,"line":6582},[2225,107987,2775],{"class":2774},[2225,107989,107990],{"class":2239}," slots ",[2225,107992,2781],{"class":2235},[2225,107994,2784],{"class":2231},[2225,107996,48557],{"class":2239},[2225,107998,1520],{"class":2235},[2225,108000,3167],{"class":2291},[2225,108002,8146],{"class":2239},[2217,108004,108006],{"className":6604,"code":108005,"language":141,"meta":1037,"style":1037},"[\n  {\n    \"name\": \"receipt\",\n    \"eTag\": \"9f2c41d0e6a8b37c51f0d2e94ab6c8d1…\",\n    \"filename\": \"receipt-2026-06.pdf\",\n    \"size\": 48213,\n    \"mimeType\": \"application\u002Fpdf\",\n    \"uploadedAt\": \"2026-07-08T09:30:00.000Z\",\n    \"uploadedBy\": \"alice\"\n  }\n]\n",[941,108007,108008,108012,108016,108034,108053,108071,108086,108104,108123,108140,108144],{"__ignoreMap":1037},[2225,108009,108010],{"class":2227,"line":2228},[2225,108011,26349],{"class":2235},[2225,108013,108014],{"class":2227,"line":1041},[2225,108015,59879],{"class":2235},[2225,108017,108018,108020,108022,108024,108026,108028,108030,108032],{"class":2227,"line":1038},[2225,108019,14201],{"class":2235},[2225,108021,4914],{"class":2774},[2225,108023,6620],{"class":2235},[2225,108025,2311],{"class":2235},[2225,108027,6614],{"class":2235},[2225,108029,107891],{"class":2252},[2225,108031,6620],{"class":2235},[2225,108033,1707],{"class":2235},[2225,108035,108036,108038,108040,108042,108044,108046,108049,108051],{"class":2227,"line":2288},[2225,108037,14201],{"class":2235},[2225,108039,78163],{"class":2774},[2225,108041,6620],{"class":2235},[2225,108043,2311],{"class":2235},[2225,108045,6614],{"class":2235},[2225,108047,108048],{"class":2252},"9f2c41d0e6a8b37c51f0d2e94ab6c8d1…",[2225,108050,6620],{"class":2235},[2225,108052,1707],{"class":2235},[2225,108054,108055,108057,108059,108061,108063,108065,108067,108069],{"class":2227,"line":2304},[2225,108056,14201],{"class":2235},[2225,108058,79137],{"class":2774},[2225,108060,6620],{"class":2235},[2225,108062,2311],{"class":2235},[2225,108064,6614],{"class":2235},[2225,108066,107931],{"class":2252},[2225,108068,6620],{"class":2235},[2225,108070,1707],{"class":2235},[2225,108072,108073,108075,108077,108079,108081,108084],{"class":2227,"line":2356},[2225,108074,14201],{"class":2235},[2225,108076,79350],{"class":2774},[2225,108078,6620],{"class":2235},[2225,108080,2311],{"class":2235},[2225,108082,108083],{"class":6527}," 48213",[2225,108085,1707],{"class":2235},[2225,108087,108088,108090,108092,108094,108096,108098,108100,108102],{"class":2227,"line":2830},[2225,108089,14201],{"class":2235},[2225,108091,80124],{"class":2774},[2225,108093,6620],{"class":2235},[2225,108095,2311],{"class":2235},[2225,108097,6614],{"class":2235},[2225,108099,61020],{"class":2252},[2225,108101,6620],{"class":2235},[2225,108103,1707],{"class":2235},[2225,108105,108106,108108,108111,108113,108115,108117,108119,108121],{"class":2227,"line":2861},[2225,108107,14201],{"class":2235},[2225,108109,108110],{"class":2774},"uploadedAt",[2225,108112,6620],{"class":2235},[2225,108114,2311],{"class":2235},[2225,108116,6614],{"class":2235},[2225,108118,9408],{"class":2252},[2225,108120,6620],{"class":2235},[2225,108122,1707],{"class":2235},[2225,108124,108125,108127,108130,108132,108134,108136,108138],{"class":2227,"line":4246},[2225,108126,14201],{"class":2235},[2225,108128,108129],{"class":2774},"uploadedBy",[2225,108131,6620],{"class":2235},[2225,108133,2311],{"class":2235},[2225,108135,6614],{"class":2235},[2225,108137,6408],{"class":2252},[2225,108139,9449],{"class":2235},[2225,108141,108142],{"class":2227,"line":4253},[2225,108143,7632],{"class":2235},[2225,108145,108146],{"class":2227,"line":4259},[2225,108147,26484],{"class":2235},[2217,108149,108151],{"className":2219,"code":108150,"language":2221,"meta":1037,"style":1037},"await blobs.delete('receipt')\n\u002F\u002F → void — removes the slot; the underlying chunks are freed once no other\n\u002F\u002F   slot or published version references the same content\n",[941,108152,108153,108173,108178],{"__ignoreMap":1037},[2225,108154,108155,108157,108159,108161,108163,108165,108167,108169,108171],{"class":2227,"line":2228},[2225,108156,6500],{"class":2231},[2225,108158,48557],{"class":2239},[2225,108160,1520],{"class":2235},[2225,108162,3164],{"class":2291},[2225,108164,2789],{"class":2239},[2225,108166,2805],{"class":2235},[2225,108168,107891],{"class":2252},[2225,108170,2805],{"class":2235},[2225,108172,2362],{"class":2239},[2225,108174,108175],{"class":2227,"line":1041},[2225,108176,108177],{"class":2763},"\u002F\u002F → void — removes the slot; the underlying chunks are freed once no other\n",[2225,108179,108180],{"class":2227,"line":1038},[2225,108181,108182],{"class":2763},"\u002F\u002F   slot or published version references the same content\n",[937,108184,108185,108186,997,108189,108192],{},"A record can hold any number of slots (",[941,108187,108188],{},".put('receipt', …)",[941,108190,108191],{},".put('contract', …)","),\nand because storage is content-addressed, uploading the same bytes to two different\nrecords stores the chunks once and bumps a reference count.",[1591,108194,108196],{"id":108195},"serve-to-a-browser","Serve to a browser",[2217,108198,108200],{"className":2219,"code":108199,"language":2221,"meta":1037,"style":1037},"const response = await blobs.response('receipt', { inline: true })\n\u002F\u002F → Response — body streams the decrypted bytes; headers:\n\u002F\u002F   Content-Type: application\u002Fpdf\n\u002F\u002F   Content-Length: 48213\n\u002F\u002F   Content-Disposition: inline; filename=\"receipt-2026-06.pdf\"\n\u002F\u002F   ETag: \"9f2c41d0e6a8b37c51f0d2e94ab6c8d1…\"\n\u002F\u002F   (null if the slot does not exist; omit `inline` for attachment\u002Fdownload)\n\nconst built = await blobs.objectURL('receipt')\n\u002F\u002F → { url: 'blob:https:\u002F\u002Fapp.example\u002F1f2e0c9a-…', revoke: [Function] }\n\u002F\u002F   (null if the slot does not exist)\nconst img = document.querySelector\u003CHTMLImageElement>('#receipt-preview')!\nimg.src = built!.url\n\u002F\u002F later — the caller owns the lifetime:\nbuilt!.revoke()\n",[941,108201,108202,108242,108247,108252,108257,108262,108267,108272,108276,108303,108308,108313,108349,108368,108373],{"__ignoreMap":1037},[2225,108203,108204,108206,108209,108211,108213,108215,108217,108220,108222,108224,108226,108228,108230,108232,108234,108236,108238,108240],{"class":2227,"line":2228},[2225,108205,2775],{"class":2774},[2225,108207,108208],{"class":2239}," response ",[2225,108210,2781],{"class":2235},[2225,108212,2784],{"class":2231},[2225,108214,48557],{"class":2239},[2225,108216,1520],{"class":2235},[2225,108218,108219],{"class":2291},"response",[2225,108221,2789],{"class":2239},[2225,108223,2805],{"class":2235},[2225,108225,107891],{"class":2252},[2225,108227,2805],{"class":2235},[2225,108229,2298],{"class":2235},[2225,108231,2236],{"class":2235},[2225,108233,81985],{"class":2307},[2225,108235,2311],{"class":2235},[2225,108237,2322],{"class":2321},[2225,108239,2243],{"class":2235},[2225,108241,2362],{"class":2239},[2225,108243,108244],{"class":2227,"line":1041},[2225,108245,108246],{"class":2763},"\u002F\u002F → Response — body streams the decrypted bytes; headers:\n",[2225,108248,108249],{"class":2227,"line":1038},[2225,108250,108251],{"class":2763},"\u002F\u002F   Content-Type: application\u002Fpdf\n",[2225,108253,108254],{"class":2227,"line":2288},[2225,108255,108256],{"class":2763},"\u002F\u002F   Content-Length: 48213\n",[2225,108258,108259],{"class":2227,"line":2304},[2225,108260,108261],{"class":2763},"\u002F\u002F   Content-Disposition: inline; filename=\"receipt-2026-06.pdf\"\n",[2225,108263,108264],{"class":2227,"line":2356},[2225,108265,108266],{"class":2763},"\u002F\u002F   ETag: \"9f2c41d0e6a8b37c51f0d2e94ab6c8d1…\"\n",[2225,108268,108269],{"class":2227,"line":2830},[2225,108270,108271],{"class":2763},"\u002F\u002F   (null if the slot does not exist; omit `inline` for attachment\u002Fdownload)\n",[2225,108273,108274],{"class":2227,"line":2861},[2225,108275,2285],{"emptyLinePlaceholder":2284},[2225,108277,108278,108280,108283,108285,108287,108289,108291,108293,108295,108297,108299,108301],{"class":2227,"line":4246},[2225,108279,2775],{"class":2774},[2225,108281,108282],{"class":2239}," built ",[2225,108284,2781],{"class":2235},[2225,108286,2784],{"class":2231},[2225,108288,48557],{"class":2239},[2225,108290,1520],{"class":2235},[2225,108292,81950],{"class":2291},[2225,108294,2789],{"class":2239},[2225,108296,2805],{"class":2235},[2225,108298,107891],{"class":2252},[2225,108300,2805],{"class":2235},[2225,108302,2362],{"class":2239},[2225,108304,108305],{"class":2227,"line":4253},[2225,108306,108307],{"class":2763},"\u002F\u002F → { url: 'blob:https:\u002F\u002Fapp.example\u002F1f2e0c9a-…', revoke: [Function] }\n",[2225,108309,108310],{"class":2227,"line":4259},[2225,108311,108312],{"class":2763},"\u002F\u002F   (null if the slot does not exist)\n",[2225,108314,108315,108317,108320,108322,108324,108326,108329,108331,108334,108336,108338,108340,108343,108345,108347],{"class":2227,"line":6548},[2225,108316,2775],{"class":2774},[2225,108318,108319],{"class":2239}," img ",[2225,108321,2781],{"class":2235},[2225,108323,70407],{"class":2239},[2225,108325,1520],{"class":2235},[2225,108327,108328],{"class":2291},"querySelector",[2225,108330,6474],{"class":2235},[2225,108332,108333],{"class":6342},"HTMLImageElement",[2225,108335,6480],{"class":2235},[2225,108337,2789],{"class":2239},[2225,108339,2805],{"class":2235},[2225,108341,108342],{"class":2252},"#receipt-preview",[2225,108344,2805],{"class":2235},[2225,108346,1381],{"class":2239},[2225,108348,80068],{"class":2235},[2225,108350,108351,108354,108356,108359,108361,108364,108366],{"class":2227,"line":6576},[2225,108352,108353],{"class":2239},"img",[2225,108355,1520],{"class":2235},[2225,108357,108358],{"class":2239},"src ",[2225,108360,2781],{"class":2235},[2225,108362,108363],{"class":2239}," built",[2225,108365,83002],{"class":2235},[2225,108367,81990],{"class":2239},[2225,108369,108370],{"class":2227,"line":6582},[2225,108371,108372],{"class":2763},"\u002F\u002F later — the caller owns the lifetime:\n",[2225,108374,108375,108378,108380,108382],{"class":2227,"line":6587},[2225,108376,108377],{"class":2239},"built",[2225,108379,83002],{"class":2235},[2225,108381,10653],{"class":2291},[2225,108383,8146],{"class":2239},[937,108385,108386,108388,108389,108392,108393,108395,108396,108398,108399,108401],{},[941,108387,82015],{}," throws where ",[941,108390,108391],{},"URL.createObjectURL"," is unavailable (Node without DOM,\nrestricted workers). If you use Vue, the ",[941,108394,37499],{}," composable in ",[941,108397,42079],{},"\nwraps this with auto-revoke on reactive id change and scope dispose, and stays at\n",[941,108400,6651],{}," during SSR.",[1591,108403,108405],{"id":108404},"retention-compaction","Retention & compaction",[937,108407,108408,108409,108411,108412,108415],{},"Declare per-slot policies with the collection's ",[941,108410,22785],{}," option, then run\n",[941,108413,108414],{},"vault.compact()"," whenever your workflow allows — a cron job, a manual \"tidy\" button,\nexport prep. noy-db never runs compaction on its own.",[2217,108417,108419],{"className":2219,"code":108418,"language":2221,"meta":1037,"style":1037},"const invoices = vault.collection\u003CInvoice>('invoices', {\n  blobFields: {\n    receipt: {\n      retainDays: 365,                          \u002F\u002F age-based TTL\n      evictWhen: (rec) => rec.reviewed === true, \u002F\u002F predicate over the decrypted record\n    },\n  },\n})\n\nconst preview = await vault.compact({ dryRun: true })\n",[941,108420,108421,108453,108462,108471,108486,108519,108523,108527,108533,108537],{"__ignoreMap":1037},[2225,108422,108423,108425,108427,108429,108431,108433,108435,108437,108439,108441,108443,108445,108447,108449,108451],{"class":2227,"line":2228},[2225,108424,2775],{"class":2774},[2225,108426,6461],{"class":2239},[2225,108428,2781],{"class":2235},[2225,108430,6466],{"class":2239},[2225,108432,1520],{"class":2235},[2225,108434,6471],{"class":2291},[2225,108436,6474],{"class":2235},[2225,108438,6477],{"class":6342},[2225,108440,6480],{"class":2235},[2225,108442,2789],{"class":2239},[2225,108444,2805],{"class":2235},[2225,108446,6487],{"class":2252},[2225,108448,2805],{"class":2235},[2225,108450,2298],{"class":2235},[2225,108452,2301],{"class":2235},[2225,108454,108455,108458,108460],{"class":2227,"line":1041},[2225,108456,108457],{"class":2307},"  blobFields",[2225,108459,2311],{"class":2235},[2225,108461,2301],{"class":2235},[2225,108463,108464,108467,108469],{"class":2227,"line":1038},[2225,108465,108466],{"class":2307},"    receipt",[2225,108468,2311],{"class":2235},[2225,108470,2301],{"class":2235},[2225,108472,108473,108476,108478,108481,108483],{"class":2227,"line":2288},[2225,108474,108475],{"class":2307},"      retainDays",[2225,108477,2311],{"class":2235},[2225,108479,108480],{"class":6527}," 365",[2225,108482,2298],{"class":2235},[2225,108484,108485],{"class":2763},"                          \u002F\u002F age-based TTL\n",[2225,108487,108488,108491,108493,108495,108498,108500,108502,108505,108507,108510,108512,108514,108516],{"class":2227,"line":2304},[2225,108489,108490],{"class":2291},"      evictWhen",[2225,108492,2311],{"class":2235},[2225,108494,953],{"class":2235},[2225,108496,108497],{"class":7052},"rec",[2225,108499,1381],{"class":2235},[2225,108501,8116],{"class":2774},[2225,108503,108504],{"class":2239}," rec",[2225,108506,1520],{"class":2235},[2225,108508,108509],{"class":2239},"reviewed ",[2225,108511,33083],{"class":2235},[2225,108513,2322],{"class":2321},[2225,108515,2298],{"class":2235},[2225,108517,108518],{"class":2763}," \u002F\u002F predicate over the decrypted record\n",[2225,108520,108521],{"class":2227,"line":2356},[2225,108522,8324],{"class":2235},[2225,108524,108525],{"class":2227,"line":2830},[2225,108526,19017],{"class":2235},[2225,108528,108529,108531],{"class":2227,"line":2861},[2225,108530,2359],{"class":2235},[2225,108532,2362],{"class":2239},[2225,108534,108535],{"class":2227,"line":4246},[2225,108536,2285],{"emptyLinePlaceholder":2284},[2225,108538,108539,108541,108544,108546,108548,108550,108552,108554,108556,108558,108560,108562,108564,108566],{"class":2227,"line":4253},[2225,108540,2775],{"class":2774},[2225,108542,108543],{"class":2239}," preview ",[2225,108545,2781],{"class":2235},[2225,108547,2784],{"class":2231},[2225,108549,6466],{"class":2239},[2225,108551,1520],{"class":2235},[2225,108553,48456],{"class":2291},[2225,108555,2789],{"class":2239},[2225,108557,2792],{"class":2235},[2225,108559,100775],{"class":2307},[2225,108561,2311],{"class":2235},[2225,108563,2322],{"class":2321},[2225,108565,2243],{"class":2235},[2225,108567,2362],{"class":2239},[2217,108569,108571],{"className":6604,"code":108570,"language":141,"meta":1037,"style":1037},"{\n  \"evicted\": 12,\n  \"records\": 480,\n  \"collections\": 1,\n  \"auditEntries\": 0,\n  \"held\": 3,\n  \"byCollection\": { \"invoices\": { \"records\": 480, \"evicted\": 12 } }\n}\n",[941,108572,108573,108577,108593,108608,108622,108637,108651,108699],{"__ignoreMap":1037},[2225,108574,108575],{"class":2227,"line":2228},[2225,108576,4200],{"class":2235},[2225,108578,108579,108581,108584,108586,108588,108591],{"class":2227,"line":1041},[2225,108580,9368],{"class":2235},[2225,108582,108583],{"class":2774},"evicted",[2225,108585,6620],{"class":2235},[2225,108587,2311],{"class":2235},[2225,108589,108590],{"class":6527}," 12",[2225,108592,1707],{"class":2235},[2225,108594,108595,108597,108599,108601,108603,108606],{"class":2227,"line":1038},[2225,108596,9368],{"class":2235},[2225,108598,9136],{"class":2774},[2225,108600,6620],{"class":2235},[2225,108602,2311],{"class":2235},[2225,108604,108605],{"class":6527}," 480",[2225,108607,1707],{"class":2235},[2225,108609,108610,108612,108614,108616,108618,108620],{"class":2227,"line":2288},[2225,108611,9368],{"class":2235},[2225,108613,2698],{"class":2774},[2225,108615,6620],{"class":2235},[2225,108617,2311],{"class":2235},[2225,108619,9377],{"class":6527},[2225,108621,1707],{"class":2235},[2225,108623,108624,108626,108629,108631,108633,108635],{"class":2227,"line":2304},[2225,108625,9368],{"class":2235},[2225,108627,108628],{"class":2774},"auditEntries",[2225,108630,6620],{"class":2235},[2225,108632,2311],{"class":2235},[2225,108634,8464],{"class":6527},[2225,108636,1707],{"class":2235},[2225,108638,108639,108641,108643,108645,108647,108649],{"class":2227,"line":2356},[2225,108640,9368],{"class":2235},[2225,108642,100604],{"class":2774},[2225,108644,6620],{"class":2235},[2225,108646,2311],{"class":2235},[2225,108648,14460],{"class":6527},[2225,108650,1707],{"class":2235},[2225,108652,108653,108655,108657,108659,108661,108663,108665,108667,108669,108671,108673,108675,108677,108679,108681,108683,108685,108687,108689,108691,108693,108695,108697],{"class":2227,"line":2830},[2225,108654,9368],{"class":2235},[2225,108656,100635],{"class":2774},[2225,108658,6620],{"class":2235},[2225,108660,2311],{"class":2235},[2225,108662,2236],{"class":2235},[2225,108664,6614],{"class":2235},[2225,108666,6487],{"class":6342},[2225,108668,6620],{"class":2235},[2225,108670,2311],{"class":2235},[2225,108672,2236],{"class":2235},[2225,108674,6614],{"class":2235},[2225,108676,9136],{"class":6527},[2225,108678,6620],{"class":2235},[2225,108680,2311],{"class":2235},[2225,108682,108605],{"class":6527},[2225,108684,2298],{"class":2235},[2225,108686,6614],{"class":2235},[2225,108688,108583],{"class":6527},[2225,108690,6620],{"class":2235},[2225,108692,2311],{"class":2235},[2225,108694,108590],{"class":6527},[2225,108696,2243],{"class":2235},[2225,108698,6369],{"class":2235},[2225,108700,108701],{"class":2227,"line":2861},[2225,108702,7351],{"class":2235},[937,108704,108705,108706,8495,108708,108710,108711,108713,108714,108717,108718,1261,108721,1261,108724,108727,108728,108730,108731,52185,108733,108736,108737,957],{},"In a real run (no ",[941,108707,100749],{},[941,108709,108628],{}," equals ",[941,108712,108583],{},": every eviction writes one\ntamper-evident entry to the internal ",[941,108715,108716],{},"_blob_eviction_audit"," collection carrying the\nactor, the reason (",[941,108719,108720],{},"'ttl'",[941,108722,108723],{},"'predicate'",[941,108725,108726],{},"'both'","), and the opaque eTag — no\nplaintext. ",[941,108729,100604],{}," counts slots that were due for eviction but kept by a ",[941,108732,99838],{},[941,108734,108735],{},"retainUntil"," floor (see the ",[1007,108738,108740],{"href":108739},"#blobfields-policy","policy table",[1591,108742,108744,108745],{"id":108743},"erasure-blobs-shred-with-forget","Erasure — blobs shred with ",[941,108746,2542],{},[937,108748,108749,108750,108754,108755,108757],{},"Compaction removes slots on a schedule; ",[1007,108751,108752],{"href":686},[941,108753,2542],{}," removes them on a\nright-to-erasure request. When you crypto-shred a record with ",[941,108756,21428],{},", the\nblob content attached to that record is shredded too — each attachment's own content key is\ndestroyed, so its encrypted chunks can never be decrypted again. A blob whose bytes another\nrecord still references (same content-addressed eTag) is retained and counted separately:",[2217,108759,108761],{"className":2219,"code":108760,"language":2221,"meta":1037,"style":1037},"const result = await vault.forget('buyer-123')\n\u002F\u002F result.blobsShredded        → 2   attachments whose content key was destroyed\n\u002F\u002F result.blobsRetainedShared  → 1   attachments kept because another record shares the bytes\n",[941,108762,108763,108790,108795],{"__ignoreMap":1037},[2225,108764,108765,108767,108769,108771,108773,108775,108777,108779,108781,108783,108786,108788],{"class":2227,"line":2228},[2225,108766,2775],{"class":2774},[2225,108768,36920],{"class":2239},[2225,108770,2781],{"class":2235},[2225,108772,2784],{"class":2231},[2225,108774,6466],{"class":2239},[2225,108776,1520],{"class":2235},[2225,108778,685],{"class":2291},[2225,108780,2789],{"class":2239},[2225,108782,2805],{"class":2235},[2225,108784,108785],{"class":2252},"buyer-123",[2225,108787,2805],{"class":2235},[2225,108789,2362],{"class":2239},[2225,108791,108792],{"class":2227,"line":1041},[2225,108793,108794],{"class":2763},"\u002F\u002F result.blobsShredded        → 2   attachments whose content key was destroyed\n",[2225,108796,108797],{"class":2227,"line":1038},[2225,108798,108799],{"class":2763},"\u002F\u002F result.blobsRetainedShared  → 1   attachments kept because another record shares the bytes\n",[937,108801,108802,108803,108805,108806,108808,108809,108811,108812,108815,108816,108818,108819,1520],{},"By ",[950,108804,64897],{},", this shred runs for ",[950,108807,2119],{}," collection in a blob-enabled vault — whether or\nnot the collection declared a ",[941,108810,22785],{}," policy. A record that only ever called\n",[941,108813,108814],{},".blob(id)"," directly, with no ",[941,108817,22785],{}," declaration, still has its blobs shredded on\n",[941,108820,2542],{},[24204,108822,108824,108825],{"id":108823},"scoping-the-scan-with-scopedpurge","Scoping the scan with ",[941,108826,56931],{},[937,108828,10993,108829,968,108832,108835,108836,108838,108839,108841,108842,108844,108845,108848,108849,108852,108853,108855,108856,108858,108859,108861],{},[941,108830,108831],{},"0.3.0-pre.11",[941,108833,108834],{},"withForget({ …, scopedPurge: true })","\nturns that default off: under it, ",[941,108837,2542],{}," scans and shreds blobs ",[950,108840,1320],{}," for collections\nthat declared a ",[941,108843,22785],{}," policy. An undeclared collection's blobs are skipped entirely —\nno shred call and no residue-detection listing pass, which is the performance win the opt-in\nbuys. The skip is reported, never silent: ",[941,108846,108847],{},"ForgetResult.scopedPurgeResidue"," carries a\n",[941,108850,108851],{},"{ reason: 'skipped-undeclared-blob-scan', collection, count }"," entry, where ",[941,108854,3236],{}," is the\nnumber of that collection's records whose blob scan was skipped. Those blobs stay untouched\nuntil you either declare ",[941,108857,22785],{}," for the collection or turn ",[941,108860,56931],{}," back off.",[2217,108863,108865],{"className":2219,"code":108864,"language":2221,"meta":1037,"style":1037},"import { withForget } from '@noy-db\u002Fhub\u002Fforget'\nimport { withHistory } from '@noy-db\u002Fhub\u002Fhistory'\n\nconst db = await createNoydb({\n  user: 'alice',\n  secret: 'alice-secret-2026',\n  blobsStrategy: withBlobs(),\n  historyStrategy: withHistory(),                       \u002F\u002F forget() writes its erasure proof here\n  forgetStrategy: withForget({\n    subjects: { invoices: 'buyerId' },\n    scopedPurge: true,                                   \u002F\u002F only scan blobs on declared collections\n  }),\n})\n",[941,108866,108867,108885,108903,108907,108923,108937,108951,108963,108978,108990,109012,109026,109034],{"__ignoreMap":1037},[2225,108868,108869,108871,108873,108875,108877,108879,108881,108883],{"class":2227,"line":2228},[2225,108870,2232],{"class":2231},[2225,108872,2236],{"class":2235},[2225,108874,74041],{"class":2239},[2225,108876,2243],{"class":2235},[2225,108878,2246],{"class":2231},[2225,108880,2249],{"class":2235},[2225,108882,73761],{"class":2252},[2225,108884,2256],{"class":2235},[2225,108886,108887,108889,108891,108893,108895,108897,108899,108901],{"class":2227,"line":1041},[2225,108888,2232],{"class":2231},[2225,108890,2236],{"class":2235},[2225,108892,4142],{"class":2239},[2225,108894,2243],{"class":2235},[2225,108896,2246],{"class":2231},[2225,108898,2249],{"class":2235},[2225,108900,4151],{"class":2252},[2225,108902,2256],{"class":2235},[2225,108904,108905],{"class":2227,"line":1038},[2225,108906,2285],{"emptyLinePlaceholder":2284},[2225,108908,108909,108911,108913,108915,108917,108919,108921],{"class":2227,"line":2288},[2225,108910,2775],{"class":2774},[2225,108912,2778],{"class":2239},[2225,108914,2781],{"class":2235},[2225,108916,2784],{"class":2231},[2225,108918,2744],{"class":2291},[2225,108920,2789],{"class":2239},[2225,108922,4200],{"class":2235},[2225,108924,108925,108927,108929,108931,108933,108935],{"class":2227,"line":2304},[2225,108926,4205],{"class":2307},[2225,108928,2311],{"class":2235},[2225,108930,2249],{"class":2235},[2225,108932,6408],{"class":2252},[2225,108934,2805],{"class":2235},[2225,108936,1707],{"class":2235},[2225,108938,108939,108941,108943,108945,108947,108949],{"class":2227,"line":2356},[2225,108940,10423],{"class":2307},[2225,108942,2311],{"class":2235},[2225,108944,2249],{"class":2235},[2225,108946,107607],{"class":2252},[2225,108948,2805],{"class":2235},[2225,108950,1707],{"class":2235},[2225,108952,108953,108955,108957,108959,108961],{"class":2227,"line":2830},[2225,108954,58728],{"class":2307},[2225,108956,2311],{"class":2235},[2225,108958,58485],{"class":2291},[2225,108960,4228],{"class":2239},[2225,108962,1707],{"class":2235},[2225,108964,108965,108967,108969,108971,108973,108975],{"class":2227,"line":2861},[2225,108966,4221],{"class":2307},[2225,108968,2311],{"class":2235},[2225,108970,4142],{"class":2291},[2225,108972,4228],{"class":2239},[2225,108974,2298],{"class":2235},[2225,108976,108977],{"class":2763},"                       \u002F\u002F forget() writes its erasure proof here\n",[2225,108979,108980,108982,108984,108986,108988],{"class":2227,"line":4246},[2225,108981,74356],{"class":2307},[2225,108983,2311],{"class":2235},[2225,108985,74041],{"class":2291},[2225,108987,2789],{"class":2239},[2225,108989,4200],{"class":2235},[2225,108991,108992,108995,108997,108999,109001,109003,109005,109008,109010],{"class":2227,"line":4253},[2225,108993,108994],{"class":2307},"    subjects",[2225,108996,2311],{"class":2235},[2225,108998,2236],{"class":2235},[2225,109000,6503],{"class":2307},[2225,109002,2311],{"class":2235},[2225,109004,2249],{"class":2235},[2225,109006,109007],{"class":2252},"buyerId",[2225,109009,2805],{"class":2235},[2225,109011,2353],{"class":2235},[2225,109013,109014,109017,109019,109021,109023],{"class":2227,"line":4259},[2225,109015,109016],{"class":2307},"    scopedPurge",[2225,109018,2311],{"class":2235},[2225,109020,2322],{"class":2321},[2225,109022,2298],{"class":2235},[2225,109024,109025],{"class":2763},"                                   \u002F\u002F only scan blobs on declared collections\n",[2225,109027,109028,109030,109032],{"class":2227,"line":6548},[2225,109029,19143],{"class":2235},[2225,109031,1381],{"class":2239},[2225,109033,1707],{"class":2235},[2225,109035,109036,109038],{"class":2227,"line":6576},[2225,109037,2359],{"class":2235},[2225,109039,2362],{"class":2239},[937,109041,109042,109043,109046,109047,109049,109050,109052],{},"The declaration is read from collections ",[950,109044,109045],{},"opened in the current session",": a collection\nwhose ",[941,109048,22785],{}," config was never instantiated before the ",[941,109051,2542],{}," call counts as\nundeclared and is skipped. Open it (with its config) first.",[1591,109054,109056],{"id":109055},"tiered-blobs-content-follows-the-records-tier","Tiered blobs — content follows the record's tier",[937,109058,10993,109059,109061,109062,1324,109064,997,109066,109068,109069,109072,109073,109076,109077,109080,109081,109083],{},[941,109060,56948],{}," (#724\u002F#741) a collection may declare ",[950,109063,31219],{},[941,109065,22785],{},[941,109067,872],{}," (the\n",[1007,109070,109071],{"href":873},"record-elevation ladder",") — a composition that was previously\nrefused with ",[941,109074,109075],{},"UnsupportedTierCompositionError",". A blob's storage tier equals its ",[950,109078,109079],{},"owning record's","\ntier: its content key, chunk address, slot-map metadata, and published versions are all keyed under\nthat tier's ",[941,109082,15122],{}," DEK, so an elevated record's attachment is unreadable at rest by a tier-0 key\nholder.",[937,109085,109086,109087,1520],{},"Tiered blobs are one face of the record-tier system — the model, the elevation API, and the\ncross-service map live in ",[1007,109088,872],{"href":873},[1111,109090,109091,109131,109145,109180],{},[1114,109092,109093,1324,109096,109098,109099,968,109101,1707,109103,109106,109107,109110,109111,109115,109116,1625,109118,109120,109121,109123,109124,109126,109127,109130],{},[950,109094,109095],{},"Reads are tier-gated at runtime.",[941,109097,25639],{}," is the tier-0 surface: ",[941,109100,14639],{},[941,109102,24407],{},[941,109104,109105],{},"blobInfo()",", and every other accessor treat an ",[950,109108,109109],{},"elevated"," record's blobs as nonexistent, for\neveryone, before any decrypt. The sanctioned cleared path is ",[950,109112,109113],{},[941,109114,73914],{}," — it peeks\nthe owning record's live tier and, for an elevated record, runs the same tier-access check\n",[941,109117,21990],{},[941,109119,73849],{}," use (against both the data collection and the ",[941,109122,15122],{}," collection at that\ntier) before minting any key material, then returns a ",[941,109125,30183],{}," that reads through the gate. An\nungranted caller gets ",[941,109128,109129],{},"TierNotGrantedError"," and no key is minted; owner\u002Fadmin\u002Fcustodian clear\nautomatically.",[1114,109132,109133,1324,109136,109138,109139,109141,109142,109144],{},[950,109134,109135],{},"Re-home on elevate\u002Fdemote is reversible.",[941,109137,21990],{}," re-homes a record's blobs (and published\nversions) under the destination tier's DEK; ",[941,109140,21993],{}," restores tier-0 readability. The move is\njournaled and crash-safe (#746) — a half-moved record heals on the next tier op or blob touch.\n",[941,109143,2542],{}," of an elevated blob-owner crypto-shreds under the record's pre-tombstone tier.",[1114,109146,109147,109152,109153,30200,109156,109159,109160,109162,109163,109166,109167,109169,109170,109176,109177,109179],{},[950,109148,109149,109151],{},[941,109150,73911],{}," — how a shared blob behaves on elevate."," Content-addressed dedup means one\nstored object can back several records. The collection option ",[941,109154,109155],{},"blobTierPolicy?: 'isolate' | 'dedup'",[941,109157,109158],{},"'isolate'",") chooses what happens when one co-owner elevates: ",[941,109161,109158],{}," forks a\nprivate tier-scoped copy so the co-owning tier-0 records are untouched; ",[941,109164,109165],{},"'dedup'"," (#741) leaves the\nshared object in place. Under ",[941,109168,109165],{}," the runtime read gate still hides the object from a tier-0\ncaller, but the shared chunks ",[950,109171,109172,109173,109175],{},"remain decryptable at rest under the flat ",[941,109174,15122],{}," DEK"," — a\ndocumented, accepted residue (the same class as the aggregate-inference channel). Choose ",[941,109178,109165],{},"\nonly when at-rest isolation of shared content is not required.",[1114,109181,109182,109187,109188,109190,109191,109193],{},[950,109183,109184,109186],{},[941,109185,14552],{}," is mandatory."," A tiered collection that declares ",[941,109189,22785],{}," must set\n",[941,109192,14552],{}," — a legacy blob has no per-record key and cannot be tier-isolated, so writing one\nto a tiered collection is refused.",[2217,109195,109197],{"className":2219,"code":109196,"language":2221,"meta":1037,"style":1037},"import { withTiers } from '@noy-db\u002Fhub\u002Ftiers'\n\nconst db = await createNoydb({\n  user: 'alice',\n  secret: 'alice-secret-2026',\n  blobsStrategy: withBlobs(),\n  tiersStrategy: withTiers(),\n})\nconst vault = await db.openVault('books')\n\nconst invoices = vault.collection\u003CInvoice>('invoices', {\n  tiers: [0, 1, 2],\n  perRecordKeys: true,        \u002F\u002F required alongside blobFields on a tiered collection\n  blobTierPolicy: 'isolate',  \u002F\u002F default — fork a private copy of a shared blob on elevate\n  blobFields: { receipt: { retainDays: 365 } },\n})\n",[941,109198,109199,109217,109221,109237,109251,109265,109277,109289,109295,109321,109325,109357,109379,109392,109409,109434],{"__ignoreMap":1037},[2225,109200,109201,109203,109205,109207,109209,109211,109213,109215],{"class":2227,"line":2228},[2225,109202,2232],{"class":2231},[2225,109204,2236],{"class":2235},[2225,109206,73968],{"class":2239},[2225,109208,2243],{"class":2235},[2225,109210,2246],{"class":2231},[2225,109212,2249],{"class":2235},[2225,109214,73343],{"class":2252},[2225,109216,2256],{"class":2235},[2225,109218,109219],{"class":2227,"line":1041},[2225,109220,2285],{"emptyLinePlaceholder":2284},[2225,109222,109223,109225,109227,109229,109231,109233,109235],{"class":2227,"line":1038},[2225,109224,2775],{"class":2774},[2225,109226,2778],{"class":2239},[2225,109228,2781],{"class":2235},[2225,109230,2784],{"class":2231},[2225,109232,2744],{"class":2291},[2225,109234,2789],{"class":2239},[2225,109236,4200],{"class":2235},[2225,109238,109239,109241,109243,109245,109247,109249],{"class":2227,"line":2288},[2225,109240,4205],{"class":2307},[2225,109242,2311],{"class":2235},[2225,109244,2249],{"class":2235},[2225,109246,6408],{"class":2252},[2225,109248,2805],{"class":2235},[2225,109250,1707],{"class":2235},[2225,109252,109253,109255,109257,109259,109261,109263],{"class":2227,"line":2304},[2225,109254,10423],{"class":2307},[2225,109256,2311],{"class":2235},[2225,109258,2249],{"class":2235},[2225,109260,107607],{"class":2252},[2225,109262,2805],{"class":2235},[2225,109264,1707],{"class":2235},[2225,109266,109267,109269,109271,109273,109275],{"class":2227,"line":2356},[2225,109268,58728],{"class":2307},[2225,109270,2311],{"class":2235},[2225,109272,58485],{"class":2291},[2225,109274,4228],{"class":2239},[2225,109276,1707],{"class":2235},[2225,109278,109279,109281,109283,109285,109287],{"class":2227,"line":2830},[2225,109280,74294],{"class":2307},[2225,109282,2311],{"class":2235},[2225,109284,73968],{"class":2291},[2225,109286,4228],{"class":2239},[2225,109288,1707],{"class":2235},[2225,109290,109291,109293],{"class":2227,"line":2861},[2225,109292,2359],{"class":2235},[2225,109294,2362],{"class":2239},[2225,109296,109297,109299,109301,109303,109305,109307,109309,109311,109313,109315,109317,109319],{"class":2227,"line":4246},[2225,109298,2775],{"class":2774},[2225,109300,2835],{"class":2239},[2225,109302,2781],{"class":2235},[2225,109304,2784],{"class":2231},[2225,109306,2842],{"class":2239},[2225,109308,1520],{"class":2235},[2225,109310,2847],{"class":2291},[2225,109312,2789],{"class":2239},[2225,109314,2805],{"class":2235},[2225,109316,65761],{"class":2252},[2225,109318,2805],{"class":2235},[2225,109320,2362],{"class":2239},[2225,109322,109323],{"class":2227,"line":4253},[2225,109324,2285],{"emptyLinePlaceholder":2284},[2225,109326,109327,109329,109331,109333,109335,109337,109339,109341,109343,109345,109347,109349,109351,109353,109355],{"class":2227,"line":4259},[2225,109328,2775],{"class":2774},[2225,109330,6461],{"class":2239},[2225,109332,2781],{"class":2235},[2225,109334,6466],{"class":2239},[2225,109336,1520],{"class":2235},[2225,109338,6471],{"class":2291},[2225,109340,6474],{"class":2235},[2225,109342,6477],{"class":6342},[2225,109344,6480],{"class":2235},[2225,109346,2789],{"class":2239},[2225,109348,2805],{"class":2235},[2225,109350,6487],{"class":2252},[2225,109352,2805],{"class":2235},[2225,109354,2298],{"class":2235},[2225,109356,2301],{"class":2235},[2225,109358,109359,109361,109363,109365,109367,109369,109371,109373,109375,109377],{"class":2227,"line":6548},[2225,109360,75748],{"class":2307},[2225,109362,2311],{"class":2235},[2225,109364,8667],{"class":2239},[2225,109366,5402],{"class":6527},[2225,109368,2298],{"class":2235},[2225,109370,9377],{"class":6527},[2225,109372,2298],{"class":2235},[2225,109374,18961],{"class":6527},[2225,109376,8803],{"class":2239},[2225,109378,1707],{"class":2235},[2225,109380,109381,109383,109385,109387,109389],{"class":2227,"line":6576},[2225,109382,75763],{"class":2307},[2225,109384,2311],{"class":2235},[2225,109386,2322],{"class":2321},[2225,109388,2298],{"class":2235},[2225,109390,109391],{"class":2763},"        \u002F\u002F required alongside blobFields on a tiered collection\n",[2225,109393,109394,109396,109398,109400,109402,109404,109406],{"class":2227,"line":6582},[2225,109395,75774],{"class":2307},[2225,109397,2311],{"class":2235},[2225,109399,2249],{"class":2235},[2225,109401,75781],{"class":2252},[2225,109403,2805],{"class":2235},[2225,109405,2298],{"class":2235},[2225,109407,109408],{"class":2763},"  \u002F\u002F default — fork a private copy of a shared blob on elevate\n",[2225,109410,109411,109413,109415,109417,109419,109421,109423,109426,109428,109430,109432],{"class":2227,"line":6587},[2225,109412,108457],{"class":2307},[2225,109414,2311],{"class":2235},[2225,109416,2236],{"class":2235},[2225,109418,60988],{"class":2307},[2225,109420,2311],{"class":2235},[2225,109422,2236],{"class":2235},[2225,109424,109425],{"class":2307}," retainDays",[2225,109427,2311],{"class":2235},[2225,109429,108480],{"class":6527},[2225,109431,2243],{"class":2235},[2225,109433,2353],{"class":2235},[2225,109435,109436,109438],{"class":2227,"line":7174},[2225,109437,2359],{"class":2235},[2225,109439,2362],{"class":2239},[1591,109441,109443],{"id":109442},"bulk-export","Bulk export",[937,109445,109446,109449,109450,109453],{},[941,109447,109448],{},"vault.exportBlobs(options?)"," is the framework-agnostic primitive: an async-iterable\nhandle over every blob in the vault, decrypting lazily as you pull. It checks the\ncaller's export capability once at handle creation and writes a single audit entry to\n",[941,109451,109452],{},"_export_audit"," before the first yield.",[2217,109455,109457],{"className":2219,"code":109456,"language":2221,"meta":1037,"style":1037},"const handle = vault.exportBlobs({ collections: ['invoices'] })\nfor await (const { blobId, recordRef, bytes, meta } of handle) {\n  \u002F\u002F blobId    → '9f2c41d0e6a8b37c51f0d2e94ab6c8d1…'  (the eTag)\n  \u002F\u002F recordRef → { collection: 'invoices', id: 'inv-001', slot: 'receipt' }\n  \u002F\u002F bytes     → Uint8Array(48213) — decrypted plaintext\n  \u002F\u002F meta      → { size: 48213, filename: 'receipt-2026-06.pdf',\n  \u002F\u002F               mimeType: 'application\u002Fpdf', createdAt: '2026-07-08T09:30:00.000Z' }\n  \u002F\u002F …write `bytes` to your sink: zip stream, S3 multipart, cold storage\n}\n",[941,109458,109459,109496,109533,109538,109543,109548,109553,109558,109563],{"__ignoreMap":1037},[2225,109460,109461,109463,109465,109467,109469,109471,109474,109476,109478,109480,109482,109484,109486,109488,109490,109492,109494],{"class":2227,"line":2228},[2225,109462,2775],{"class":2774},[2225,109464,40799],{"class":2239},[2225,109466,2781],{"class":2235},[2225,109468,6466],{"class":2239},[2225,109470,1520],{"class":2235},[2225,109472,109473],{"class":2291},"exportBlobs",[2225,109475,2789],{"class":2239},[2225,109477,2792],{"class":2235},[2225,109479,22066],{"class":2307},[2225,109481,2311],{"class":2235},[2225,109483,8667],{"class":2239},[2225,109485,2805],{"class":2235},[2225,109487,6487],{"class":2252},[2225,109489,2805],{"class":2235},[2225,109491,8890],{"class":2239},[2225,109493,2359],{"class":2235},[2225,109495,2362],{"class":2239},[2225,109497,109498,109500,109502,109504,109506,109508,109511,109513,109516,109518,109520,109522,109524,109526,109528,109531],{"class":2227,"line":1041},[2225,109499,4646],{"class":2231},[2225,109501,2784],{"class":2231},[2225,109503,953],{"class":2239},[2225,109505,2775],{"class":2774},[2225,109507,2236],{"class":2235},[2225,109509,109510],{"class":2239}," blobId",[2225,109512,2298],{"class":2235},[2225,109514,109515],{"class":2239}," recordRef",[2225,109517,2298],{"class":2235},[2225,109519,29996],{"class":2239},[2225,109521,2298],{"class":2235},[2225,109523,18647],{"class":2239},[2225,109525,2359],{"class":2235},[2225,109527,8806],{"class":2235},[2225,109529,109530],{"class":2239}," handle) ",[2225,109532,4200],{"class":2235},[2225,109534,109535],{"class":2227,"line":1038},[2225,109536,109537],{"class":2763},"  \u002F\u002F blobId    → '9f2c41d0e6a8b37c51f0d2e94ab6c8d1…'  (the eTag)\n",[2225,109539,109540],{"class":2227,"line":2288},[2225,109541,109542],{"class":2763},"  \u002F\u002F recordRef → { collection: 'invoices', id: 'inv-001', slot: 'receipt' }\n",[2225,109544,109545],{"class":2227,"line":2304},[2225,109546,109547],{"class":2763},"  \u002F\u002F bytes     → Uint8Array(48213) — decrypted plaintext\n",[2225,109549,109550],{"class":2227,"line":2356},[2225,109551,109552],{"class":2763},"  \u002F\u002F meta      → { size: 48213, filename: 'receipt-2026-06.pdf',\n",[2225,109554,109555],{"class":2227,"line":2830},[2225,109556,109557],{"class":2763},"  \u002F\u002F               mimeType: 'application\u002Fpdf', createdAt: '2026-07-08T09:30:00.000Z' }\n",[2225,109559,109560],{"class":2227,"line":2861},[2225,109561,109562],{"class":2763},"  \u002F\u002F …write `bytes` to your sink: zip stream, S3 multipart, cold storage\n",[2225,109564,109565],{"class":2227,"line":4246},[2225,109566,7351],{"class":2235},[937,109568,109569,109570,109572,109573,109575,109576,109579],{},"For materializing into a real directory, ",[941,109571,9551],{}," ships\n",[941,109574,49750],{},", which adds filename sanitization, path-escape containment\n(throws ",[941,109577,109578],{},"PathEscapeError","), and collision handling:",[2217,109581,109583],{"className":2219,"code":109582,"language":2221,"meta":1037,"style":1037},"import { exportBlobsToDirectory } from '@noy-db\u002Fto-file'\n\nconst result = await exportBlobsToDirectory(vault, '.\u002Fout', {\n  filenameProfile: 'macos-smb',   \u002F\u002F the most restrictive default\n  onCollision: 'suffix',          \u002F\u002F 'overwrite' | 'fail' | (existing, attempt) => string\n})\n",[941,109584,109585,109604,109608,109635,109654,109673],{"__ignoreMap":1037},[2225,109586,109587,109589,109591,109594,109596,109598,109600,109602],{"class":2227,"line":2228},[2225,109588,2232],{"class":2231},[2225,109590,2236],{"class":2235},[2225,109592,109593],{"class":2239}," exportBlobsToDirectory",[2225,109595,2243],{"class":2235},[2225,109597,2246],{"class":2231},[2225,109599,2249],{"class":2235},[2225,109601,9551],{"class":2252},[2225,109603,2256],{"class":2235},[2225,109605,109606],{"class":2227,"line":1041},[2225,109607,2285],{"emptyLinePlaceholder":2284},[2225,109609,109610,109612,109614,109616,109618,109620,109622,109624,109626,109629,109631,109633],{"class":2227,"line":1038},[2225,109611,2775],{"class":2774},[2225,109613,36920],{"class":2239},[2225,109615,2781],{"class":2235},[2225,109617,2784],{"class":2231},[2225,109619,109593],{"class":2291},[2225,109621,30266],{"class":2239},[2225,109623,2298],{"class":2235},[2225,109625,2249],{"class":2235},[2225,109627,109628],{"class":2252},".\u002Fout",[2225,109630,2805],{"class":2235},[2225,109632,2298],{"class":2235},[2225,109634,2301],{"class":2235},[2225,109636,109637,109640,109642,109644,109647,109649,109651],{"class":2227,"line":2288},[2225,109638,109639],{"class":2307},"  filenameProfile",[2225,109641,2311],{"class":2235},[2225,109643,2249],{"class":2235},[2225,109645,109646],{"class":2252},"macos-smb",[2225,109648,2805],{"class":2235},[2225,109650,2298],{"class":2235},[2225,109652,109653],{"class":2763},"   \u002F\u002F the most restrictive default\n",[2225,109655,109656,109659,109661,109663,109666,109668,109670],{"class":2227,"line":2304},[2225,109657,109658],{"class":2307},"  onCollision",[2225,109660,2311],{"class":2235},[2225,109662,2249],{"class":2235},[2225,109664,109665],{"class":2252},"suffix",[2225,109667,2805],{"class":2235},[2225,109669,2298],{"class":2235},[2225,109671,109672],{"class":2763},"          \u002F\u002F 'overwrite' | 'fail' | (existing, attempt) => string\n",[2225,109674,109675,109677],{"class":2227,"line":2356},[2225,109676,2359],{"class":2235},[2225,109678,2362],{"class":2239},[2217,109680,109682],{"className":6604,"code":109681,"language":141,"meta":1037,"style":1037},"{\n  \"written\": 2,\n  \"bytes\": 96426,\n  \"entries\": [\n    { \"blobId\": \"9f2c41d0e6a8…\", \"path\": \"\u002Fwork\u002Fout\u002Freceipt-2026-06.pdf\" },\n    { \"blobId\": \"4b8e02c7d19f…\", \"path\": \"\u002Fwork\u002Fout\u002Fcontract-signed.pdf\" }\n  ],\n  \"manifestPath\": null\n}\n",[941,109683,109684,109688,109703,109718,109730,109769,109807,109811,109824],{"__ignoreMap":1037},[2225,109685,109686],{"class":2227,"line":2228},[2225,109687,4200],{"class":2235},[2225,109689,109690,109692,109695,109697,109699,109701],{"class":2227,"line":1041},[2225,109691,9368],{"class":2235},[2225,109693,109694],{"class":2774},"written",[2225,109696,6620],{"class":2235},[2225,109698,2311],{"class":2235},[2225,109700,18961],{"class":6527},[2225,109702,1707],{"class":2235},[2225,109704,109705,109707,109709,109711,109713,109716],{"class":2227,"line":1038},[2225,109706,9368],{"class":2235},[2225,109708,64492],{"class":2774},[2225,109710,6620],{"class":2235},[2225,109712,2311],{"class":2235},[2225,109714,109715],{"class":6527}," 96426",[2225,109717,1707],{"class":2235},[2225,109719,109720,109722,109724,109726,109728],{"class":2227,"line":2288},[2225,109721,9368],{"class":2235},[2225,109723,9095],{"class":2774},[2225,109725,6620],{"class":2235},[2225,109727,2311],{"class":2235},[2225,109729,32491],{"class":2235},[2225,109731,109732,109734,109736,109739,109741,109743,109745,109748,109750,109752,109754,109756,109758,109760,109762,109765,109767],{"class":2227,"line":2304},[2225,109733,32496],{"class":2235},[2225,109735,6614],{"class":2235},[2225,109737,109738],{"class":6342},"blobId",[2225,109740,6620],{"class":2235},[2225,109742,2311],{"class":2235},[2225,109744,6614],{"class":2235},[2225,109746,109747],{"class":2252},"9f2c41d0e6a8…",[2225,109749,6620],{"class":2235},[2225,109751,2298],{"class":2235},[2225,109753,6614],{"class":2235},[2225,109755,52937],{"class":6342},[2225,109757,6620],{"class":2235},[2225,109759,2311],{"class":2235},[2225,109761,6614],{"class":2235},[2225,109763,109764],{"class":2252},"\u002Fwork\u002Fout\u002Freceipt-2026-06.pdf",[2225,109766,6620],{"class":2235},[2225,109768,2353],{"class":2235},[2225,109770,109771,109773,109775,109777,109779,109781,109783,109786,109788,109790,109792,109794,109796,109798,109800,109803,109805],{"class":2227,"line":2356},[2225,109772,32496],{"class":2235},[2225,109774,6614],{"class":2235},[2225,109776,109738],{"class":6342},[2225,109778,6620],{"class":2235},[2225,109780,2311],{"class":2235},[2225,109782,6614],{"class":2235},[2225,109784,109785],{"class":2252},"4b8e02c7d19f…",[2225,109787,6620],{"class":2235},[2225,109789,2298],{"class":2235},[2225,109791,6614],{"class":2235},[2225,109793,52937],{"class":6342},[2225,109795,6620],{"class":2235},[2225,109797,2311],{"class":2235},[2225,109799,6614],{"class":2235},[2225,109801,109802],{"class":2252},"\u002Fwork\u002Fout\u002Fcontract-signed.pdf",[2225,109804,6620],{"class":2235},[2225,109806,6369],{"class":2235},[2225,109808,109809],{"class":2227,"line":2830},[2225,109810,67686],{"class":2235},[2225,109812,109813,109815,109818,109820,109822],{"class":2227,"line":2861},[2225,109814,9368],{"class":2235},[2225,109816,109817],{"class":2774},"manifestPath",[2225,109819,6620],{"class":2235},[2225,109821,2311],{"class":2235},[2225,109823,8384],{"class":2235},[2225,109825,109826],{"class":2227,"line":4246},[2225,109827,7351],{"class":2235},[937,109829,960,109830,109833,109834,109837,109838,109840],{},[941,109831,109832],{},"'opaque'"," profile renames every file to ",[941,109835,109836],{},"{blobId}.{ext}"," and writes a sidecar\n",[941,109839,32978],{}," mapping the opaque names back to the originals — useful for handing a\ndirectory to a recipient who should not learn filenames from the listing alone:",[2217,109842,109844],{"className":2219,"code":109843,"language":2221,"meta":1037,"style":1037},"const opaque = await exportBlobsToDirectory(vault, '.\u002Fout', { filenameProfile: 'opaque' })\n\u002F\u002F → opaque.manifestPath === '\u002Fwork\u002Fout\u002Fmanifest.json'\n",[941,109845,109846,109889],{"__ignoreMap":1037},[2225,109847,109848,109850,109853,109855,109857,109859,109861,109863,109865,109867,109869,109871,109873,109876,109878,109880,109883,109885,109887],{"class":2227,"line":2228},[2225,109849,2775],{"class":2774},[2225,109851,109852],{"class":2239}," opaque ",[2225,109854,2781],{"class":2235},[2225,109856,2784],{"class":2231},[2225,109858,109593],{"class":2291},[2225,109860,30266],{"class":2239},[2225,109862,2298],{"class":2235},[2225,109864,2249],{"class":2235},[2225,109866,109628],{"class":2252},[2225,109868,2805],{"class":2235},[2225,109870,2298],{"class":2235},[2225,109872,2236],{"class":2235},[2225,109874,109875],{"class":2307}," filenameProfile",[2225,109877,2311],{"class":2235},[2225,109879,2249],{"class":2235},[2225,109881,109882],{"class":2252},"opaque",[2225,109884,2805],{"class":2235},[2225,109886,2243],{"class":2235},[2225,109888,2362],{"class":2239},[2225,109890,109891],{"class":2227,"line":1041},[2225,109892,109893],{"class":2763},"\u002F\u002F → opaque.manifestPath === '\u002Fwork\u002Fout\u002Fmanifest.json'\n",[2217,109895,109897],{"className":6604,"code":109896,"language":141,"meta":1037,"style":1037},"{\n  \"format\": \"noydb-opaque-export\",\n  \"version\": 1,\n  \"entries\": [\n    {\n      \"opaqueName\": \"9f2c41d0e6a8b37c51f0d2e94ab6c8d1….pdf\",\n      \"originalName\": \"receipt-2026-06.pdf\",\n      \"collection\": \"invoices\",\n      \"recordId\": \"inv-001\",\n      \"slot\": \"receipt\",\n      \"blobId\": \"9f2c41d0e6a8b37c51f0d2e94ab6c8d1…\",\n      \"mimeType\": \"application\u002Fpdf\"\n    }\n  ]\n}\n",[941,109898,109899,109903,109922,109936,109948,109953,109973,109992,110010,110028,110046,110064,110080,110084,110089],{"__ignoreMap":1037},[2225,109900,109901],{"class":2227,"line":2228},[2225,109902,4200],{"class":2235},[2225,109904,109905,109907,109909,109911,109913,109915,109918,109920],{"class":2227,"line":1041},[2225,109906,9368],{"class":2235},[2225,109908,23270],{"class":2774},[2225,109910,6620],{"class":2235},[2225,109912,2311],{"class":2235},[2225,109914,6614],{"class":2235},[2225,109916,109917],{"class":2252},"noydb-opaque-export",[2225,109919,6620],{"class":2235},[2225,109921,1707],{"class":2235},[2225,109923,109924,109926,109928,109930,109932,109934],{"class":2227,"line":1038},[2225,109925,9368],{"class":2235},[2225,109927,52564],{"class":2774},[2225,109929,6620],{"class":2235},[2225,109931,2311],{"class":2235},[2225,109933,9377],{"class":6527},[2225,109935,1707],{"class":2235},[2225,109937,109938,109940,109942,109944,109946],{"class":2227,"line":2288},[2225,109939,9368],{"class":2235},[2225,109941,9095],{"class":2774},[2225,109943,6620],{"class":2235},[2225,109945,2311],{"class":2235},[2225,109947,32491],{"class":2235},[2225,109949,109950],{"class":2227,"line":2304},[2225,109951,109952],{"class":2235},"    {\n",[2225,109954,109955,109957,109960,109962,109964,109966,109969,109971],{"class":2227,"line":2356},[2225,109956,59948],{"class":2235},[2225,109958,109959],{"class":6342},"opaqueName",[2225,109961,6620],{"class":2235},[2225,109963,2311],{"class":2235},[2225,109965,6614],{"class":2235},[2225,109967,109968],{"class":2252},"9f2c41d0e6a8b37c51f0d2e94ab6c8d1….pdf",[2225,109970,6620],{"class":2235},[2225,109972,1707],{"class":2235},[2225,109974,109975,109977,109980,109982,109984,109986,109988,109990],{"class":2227,"line":2830},[2225,109976,59948],{"class":2235},[2225,109978,109979],{"class":6342},"originalName",[2225,109981,6620],{"class":2235},[2225,109983,2311],{"class":2235},[2225,109985,6614],{"class":2235},[2225,109987,107931],{"class":2252},[2225,109989,6620],{"class":2235},[2225,109991,1707],{"class":2235},[2225,109993,109994,109996,109998,110000,110002,110004,110006,110008],{"class":2227,"line":2861},[2225,109995,59948],{"class":2235},[2225,109997,6471],{"class":6342},[2225,109999,6620],{"class":2235},[2225,110001,2311],{"class":2235},[2225,110003,6614],{"class":2235},[2225,110005,6487],{"class":2252},[2225,110007,6620],{"class":2235},[2225,110009,1707],{"class":2235},[2225,110011,110012,110014,110016,110018,110020,110022,110024,110026],{"class":2227,"line":4246},[2225,110013,59948],{"class":2235},[2225,110015,61350],{"class":6342},[2225,110017,6620],{"class":2235},[2225,110019,2311],{"class":2235},[2225,110021,6614],{"class":2235},[2225,110023,59410],{"class":2252},[2225,110025,6620],{"class":2235},[2225,110027,1707],{"class":2235},[2225,110029,110030,110032,110034,110036,110038,110040,110042,110044],{"class":2227,"line":4253},[2225,110031,59948],{"class":2235},[2225,110033,18018],{"class":6342},[2225,110035,6620],{"class":2235},[2225,110037,2311],{"class":2235},[2225,110039,6614],{"class":2235},[2225,110041,107891],{"class":2252},[2225,110043,6620],{"class":2235},[2225,110045,1707],{"class":2235},[2225,110047,110048,110050,110052,110054,110056,110058,110060,110062],{"class":2227,"line":4259},[2225,110049,59948],{"class":2235},[2225,110051,109738],{"class":6342},[2225,110053,6620],{"class":2235},[2225,110055,2311],{"class":2235},[2225,110057,6614],{"class":2235},[2225,110059,108048],{"class":2252},[2225,110061,6620],{"class":2235},[2225,110063,1707],{"class":2235},[2225,110065,110066,110068,110070,110072,110074,110076,110078],{"class":2227,"line":6548},[2225,110067,59948],{"class":2235},[2225,110069,80124],{"class":6342},[2225,110071,6620],{"class":2235},[2225,110073,2311],{"class":2235},[2225,110075,6614],{"class":2235},[2225,110077,61020],{"class":2252},[2225,110079,9449],{"class":2235},[2225,110081,110082],{"class":2227,"line":6576},[2225,110083,60103],{"class":2235},[2225,110085,110086],{"class":2227,"line":6582},[2225,110087,110088],{"class":2235},"  ]\n",[2225,110090,110091],{"class":2227,"line":6587},[2225,110092,7351],{"class":2235},[937,110094,110095,110096,110099],{},"The standalone sanitizer lives in ",[941,110097,110098],{},"@noy-db\u002Fhub\u002Futil"," for adopters writing their own\nsinks:",[2217,110101,110103],{"className":2219,"code":110102,"language":2221,"meta":1037,"style":1037},"import { sanitizeFilename } from '@noy-db\u002Fhub\u002Futil'\n\nsanitizeFilename('report: Q2\u002Ffinal?.pdf', { profile: 'windows' })\n\u002F\u002F → 'report_ Q2_final_.pdf'\n\nsanitizeFilename('CON.txt', { profile: 'windows' })\n\u002F\u002F → '_CON.txt'  (Windows reserved device name defused)\n\nsanitizeFilename('scan-'.repeat(80) + '.pdf', { profile: 'posix', maxBytes: 240 })\n\u002F\u002F → 'scan-scan-…' truncated to 240 bytes on a code-point boundary — headroom for '-1', '-2', … suffixes\n",[941,110104,110105,110124,110128,110161,110166,110170,110201,110206,110210,110271],{"__ignoreMap":1037},[2225,110106,110107,110109,110111,110114,110116,110118,110120,110122],{"class":2227,"line":2228},[2225,110108,2232],{"class":2231},[2225,110110,2236],{"class":2235},[2225,110112,110113],{"class":2239}," sanitizeFilename",[2225,110115,2243],{"class":2235},[2225,110117,2246],{"class":2231},[2225,110119,2249],{"class":2235},[2225,110121,110098],{"class":2252},[2225,110123,2256],{"class":2235},[2225,110125,110126],{"class":2227,"line":1041},[2225,110127,2285],{"emptyLinePlaceholder":2284},[2225,110129,110130,110133,110135,110137,110140,110142,110144,110146,110148,110150,110152,110155,110157,110159],{"class":2227,"line":1038},[2225,110131,110132],{"class":2291},"sanitizeFilename",[2225,110134,2789],{"class":2239},[2225,110136,2805],{"class":2235},[2225,110138,110139],{"class":2252},"report: Q2\u002Ffinal?.pdf",[2225,110141,2805],{"class":2235},[2225,110143,2298],{"class":2235},[2225,110145,2236],{"class":2235},[2225,110147,72016],{"class":2307},[2225,110149,2311],{"class":2235},[2225,110151,2249],{"class":2235},[2225,110153,110154],{"class":2252},"windows",[2225,110156,2805],{"class":2235},[2225,110158,2243],{"class":2235},[2225,110160,2362],{"class":2239},[2225,110162,110163],{"class":2227,"line":2288},[2225,110164,110165],{"class":2763},"\u002F\u002F → 'report_ Q2_final_.pdf'\n",[2225,110167,110168],{"class":2227,"line":2304},[2225,110169,2285],{"emptyLinePlaceholder":2284},[2225,110171,110172,110174,110176,110178,110181,110183,110185,110187,110189,110191,110193,110195,110197,110199],{"class":2227,"line":2356},[2225,110173,110132],{"class":2291},[2225,110175,2789],{"class":2239},[2225,110177,2805],{"class":2235},[2225,110179,110180],{"class":2252},"CON.txt",[2225,110182,2805],{"class":2235},[2225,110184,2298],{"class":2235},[2225,110186,2236],{"class":2235},[2225,110188,72016],{"class":2307},[2225,110190,2311],{"class":2235},[2225,110192,2249],{"class":2235},[2225,110194,110154],{"class":2252},[2225,110196,2805],{"class":2235},[2225,110198,2243],{"class":2235},[2225,110200,2362],{"class":2239},[2225,110202,110203],{"class":2227,"line":2830},[2225,110204,110205],{"class":2763},"\u002F\u002F → '_CON.txt'  (Windows reserved device name defused)\n",[2225,110207,110208],{"class":2227,"line":2861},[2225,110209,2285],{"emptyLinePlaceholder":2284},[2225,110211,110212,110214,110216,110218,110221,110223,110225,110228,110230,110232,110234,110236,110238,110241,110243,110245,110247,110249,110251,110253,110256,110258,110260,110262,110264,110267,110269],{"class":2227,"line":4246},[2225,110213,110132],{"class":2291},[2225,110215,2789],{"class":2239},[2225,110217,2805],{"class":2235},[2225,110219,110220],{"class":2252},"scan-",[2225,110222,2805],{"class":2235},[2225,110224,1520],{"class":2235},[2225,110226,110227],{"class":2291},"repeat",[2225,110229,2789],{"class":2239},[2225,110231,93526],{"class":6527},[2225,110233,8495],{"class":2239},[2225,110235,71297],{"class":2235},[2225,110237,2249],{"class":2235},[2225,110239,110240],{"class":2252},".pdf",[2225,110242,2805],{"class":2235},[2225,110244,2298],{"class":2235},[2225,110246,2236],{"class":2235},[2225,110248,72016],{"class":2307},[2225,110250,2311],{"class":2235},[2225,110252,2249],{"class":2235},[2225,110254,110255],{"class":2252},"posix",[2225,110257,2805],{"class":2235},[2225,110259,2298],{"class":2235},[2225,110261,64566],{"class":2307},[2225,110263,2311],{"class":2235},[2225,110265,110266],{"class":6527}," 240",[2225,110268,2243],{"class":2235},[2225,110270,2362],{"class":2239},[2225,110272,110273],{"class":2227,"line":4253},[2225,110274,110275],{"class":2763},"\u002F\u002F → 'scan-scan-…' truncated to 240 bytes on a code-point boundary — headroom for '-1', '-2', … suffixes\n",[1022,110277,102889],{"id":102888},[1591,110279,110281,110283,110284],{"id":110280},"blobset-from-collectionblobid",[941,110282,30183],{}," — from ",[941,110285,21881],{},[1338,110287,110288,110298],{},[1341,110289,110290],{},[1344,110291,110292,110294,110296],{},[1347,110293,7831],{},[1347,110295,102906],{},[1347,110297,7834],{},[1357,110299,110300,110314,110331,110362,110376,110409,110430,110444,110458,110483,110497,110515,110553,110580,110599],{},[1344,110301,110302,110307,110311],{},[1362,110303,110304],{},[941,110305,110306],{},"put(slotName, data, opts?)",[1362,110308,110309],{},[941,110310,7165],{},[1362,110312,110313],{},"Upload bytes to a named slot. Dedups by keyed eTag; overwriting a slot releases the old content's reference.",[1344,110315,110316,110321,110325],{},[1362,110317,110318],{},[941,110319,110320],{},"get(slotName)",[1362,110322,110323],{},[941,110324,81352],{},[1362,110326,110327,110328,110330],{},"Fetch and decrypt all bytes. ",[941,110329,6651],{}," for a missing slot.",[1344,110332,110333,110337,110342],{},[1362,110334,110335],{},[941,110336,24407],{},[1362,110338,110339],{},[941,110340,110341],{},"SlotInfo[]",[1362,110343,110344,110345,968,110347,968,110349,968,110351,968,110353,968,110356,968,110358,110361],{},"All slot entries (",[941,110346,4914],{},[941,110348,78163],{},[941,110350,79137],{},[941,110352,79350],{},[941,110354,110355],{},"mimeType?",[941,110357,108110],{},[941,110359,110360],{},"uploadedBy?","). Metadata only — no chunk reads.",[1344,110363,110364,110369,110373],{},[1362,110365,110366],{},[941,110367,110368],{},"delete(slotName)",[1362,110370,110371],{},[941,110372,7165],{},[1362,110374,110375],{},"Remove the slot and decrement the blob's reference count.",[1344,110377,110378,110383,110388],{},[1362,110379,110380],{},[941,110381,110382],{},"response(slotName, opts?)",[1362,110384,110385],{},[941,110386,110387],{},"Response | null",[1362,110389,110390,110391,28806,110393,968,110396,968,110399,968,110402,968,110405,110408],{},"Native ",[941,110392,40875],{},[941,110394,110395],{},"Content-Type",[941,110397,110398],{},"Content-Length",[941,110400,110401],{},"Content-Disposition",[941,110403,110404],{},"ETag",[941,110406,110407],{},"Last-Modified"," headers.",[1344,110410,110411,110416,110421],{},[1362,110412,110413],{},[941,110414,110415],{},"objectURL(slotName, opts?)",[1362,110417,110418],{},[941,110419,110420],{},"{ url, revoke } | null",[1362,110422,110423,110424,110427,110428,1520],{},"Decrypt and wrap in a browser ObjectURL. ",[941,110425,110426],{},"opts.mimeType"," overrides the stored type. Caller must call ",[941,110429,82028],{},[1344,110431,110432,110437,110441],{},[1362,110433,110434],{},[941,110435,110436],{},"publish(slotName, label)",[1362,110438,110439],{},[941,110440,7165],{},[1362,110442,110443],{},"Snapshot the current slot content as a named version. The version holds its own reference — the bytes survive later slot overwrites\u002Fdeletes.",[1344,110445,110446,110451,110455],{},[1362,110447,110448],{},[941,110449,110450],{},"getVersion(slotName, label)",[1362,110452,110453],{},[941,110454,81352],{},[1362,110456,110457],{},"Fetch a published version's bytes.",[1344,110459,110460,110465,110470],{},[1362,110461,110462],{},[941,110463,110464],{},"listVersions(slotName)",[1362,110466,110467],{},[941,110468,110469],{},"VersionRecord[]",[1362,110471,110472,110473,968,110475,968,110477,968,110480,957],{},"All published versions (",[941,110474,96028],{},[941,110476,78163],{},[941,110478,110479],{},"publishedAt",[941,110481,110482],{},"publishedBy?",[1344,110484,110485,110490,110494],{},[1362,110486,110487],{},[941,110488,110489],{},"deleteVersion(slotName, label)",[1362,110491,110492],{},[941,110493,7165],{},[1362,110495,110496],{},"Delete a published version and release its reference.",[1344,110498,110499,110504,110508],{},[1362,110500,110501],{},[941,110502,110503],{},"responseVersion(slotName, label, opts?)",[1362,110505,110506],{},[941,110507,110387],{},[1362,110509,110510,110511,110514],{},"Like ",[941,110512,110513],{},"response()"," but reads the published version.",[1344,110516,110517,110522,110527],{},[1362,110518,110519],{},[941,110520,110521],{},"blobInfo(slotName)",[1362,110523,110524],{},[941,110525,110526],{},"BlobObject | null",[1362,110528,110529,110530,968,110532,968,110534,968,110537,968,110540,968,110543,968,110546,968,110548,968,110550,1520],{},"Content metadata: ",[941,110531,78163],{},[941,110533,79350],{},[941,110535,110536],{},"compressedSize",[941,110538,110539],{},"compression",[941,110541,110542],{},"chunkSize",[941,110544,110545],{},"chunkCount",[941,110547,110355],{},[941,110549,87098],{},[941,110551,110552],{},"refCount",[1344,110554,110555,110560,110565],{},[1362,110556,110557],{},[941,110558,110559],{},"atTier()",[1362,110561,110562],{},[941,110563,110564],{},"Promise\u003CBlobSet>",[1362,110566,110567,110568,110570,110571,110573,110574,110576,110577,110579],{},"Cleared-read path to an ",[950,110569,109109],{}," record's blobs (#749). Runs the tier-access check, then returns a ",[941,110572,30183],{}," that reads through the tier gate; ",[941,110575,109129],{}," for an ungranted caller. Plain ",[941,110578,25639],{}," treats an elevated record's blobs as nonexistent.",[1344,110581,110582,110587,110592],{},[1362,110583,110584],{},[941,110585,110586],{},"presignedUrl(slotName, expiresInSeconds?)",[1362,110588,110589],{},[941,110590,110591],{},"string | null",[1362,110593,110594,110595,110598],{},"Presigned URL to the ",[950,110596,110597],{},"ciphertext"," of a single-chunk blob, on stores that support presigning. Default expiry 3600 s.",[1344,110600,110601,110606,110610],{},[1362,110602,110603],{},[941,110604,110605],{},"decryptResponse(slotName, cipherResponse)",[1362,110607,110608],{},[941,110609,110387],{},[1362,110611,110612,110613,110615,110616,1520],{},"Decrypt a ciphertext ",[941,110614,40875],{}," (e.g. fetched from a presigned URL) into a plaintext ",[941,110617,40875],{},[937,110619,110620,110621,1261,110624,2601,110627,110630,110631,1707,110634,968,110637,968,110640,110643],{},"The subpath also exports the MIME helpers ",[941,110622,110623],{},"detectMimeType",[941,110625,110626],{},"detectMagic",[941,110628,110629],{},"isPreCompressed"," and the internal-collection constants (",[941,110632,110633],{},"BLOB_COLLECTION",[941,110635,110636],{},"BLOB_INDEX_COLLECTION",[941,110638,110639],{},"BLOB_CHUNKS_COLLECTION",[941,110641,110642],{},"DEFAULT_CHUNK_SIZE",", …).",[1591,110645,110647],{"id":110646},"blobputoptions",[941,110648,110649],{},"BlobPutOptions",[1338,110651,110652,110664],{},[1341,110653,110654],{},[1344,110655,110656,110658,110660,110662],{},[1347,110657,101060],{},[1347,110659,20194],{},[1347,110661,20197],{},[1347,110663,7834],{},[1357,110665,110666,110682,110701,110723,110739],{},[1344,110667,110668,110672,110676,110679],{},[1362,110669,110670],{},[941,110671,80124],{},[1362,110673,110674],{},[941,110675,7259],{},[1362,110677,110678],{},"auto-detected from magic bytes",[1362,110680,110681],{},"MIME type hint stored on the slot.",[1344,110683,110684,110688,110692,110698],{},[1362,110685,110686],{},[941,110687,110542],{},[1362,110689,110690],{},[941,110691,20636],{},[1362,110693,110694,110697],{},[941,110695,110696],{},"store.maxBlobBytes",", else 256 KB",[1362,110699,110700],{},"Raw bytes per chunk. Explicit value wins over the store's advertised cap.",[1344,110702,110703,110708,110712,110720],{},[1362,110704,110705],{},[941,110706,110707],{},"compress",[1362,110709,110710],{},[941,110711,16861],{},[1362,110713,110714,110716,110717,110719],{},[941,110715,11790],{},"; auto-",[941,110718,2560],{}," for pre-compressed MIME types (JPEG, PNG, ZIP, …)",[1362,110721,110722],{},"Gzip before encrypting.",[1344,110724,110725,110729,110733,110736],{},[1362,110726,110727],{},[941,110728,108129],{},[1362,110730,110731],{},[941,110732,7259],{},[1362,110734,110735],{},"the session user",[1362,110737,110738],{},"User id recorded on the slot.",[1344,110740,110741,110745,110749,110752],{},[1362,110742,110743],{},[941,110744,79137],{},[1362,110746,110747],{},[941,110748,7259],{},[1362,110750,110751],{},"the slot name",[1362,110753,110754],{},"User-visible display\u002Fdownload name.",[1591,110756,110758],{"id":110757},"blobresponseoptions",[941,110759,110760],{},"BlobResponseOptions",[1338,110762,110763,110775],{},[1341,110764,110765],{},[1344,110766,110767,110769,110771,110773],{},[1347,110768,101060],{},[1347,110770,20194],{},[1347,110772,20197],{},[1347,110774,7834],{},[1357,110776,110777,110806],{},[1344,110778,110779,110784,110788,110792],{},[1362,110780,110781],{},[941,110782,110783],{},"inline",[1362,110785,110786],{},[941,110787,16861],{},[1362,110789,110790],{},[941,110791,2560],{},[1362,110793,110794,1746,110796,110799,110800,1746,110802,110805],{},[941,110795,11790],{},[941,110797,110798],{},"Content-Disposition: inline"," (render in tab); ",[941,110801,2560],{},[941,110803,110804],{},"attachment"," (download).",[1344,110807,110808,110812,110816,110819],{},[1362,110809,110810],{},[941,110811,79137],{},[1362,110813,110814],{},[941,110815,7259],{},[1362,110817,110818],{},"the slot's stored filename",[1362,110820,110821,110822,110824],{},"Override the filename in the ",[941,110823,110401],{}," header.",[1591,110826,110828,110830],{"id":110827},"blobfields-policy",[941,110829,22785],{}," policy",[937,110832,110833,110834,1520],{},"Declared per collection: ",[941,110835,110836],{},"vault.collection(name, { blobFields: { [slotName]: policy } })",[1338,110838,110839,110849],{},[1341,110840,110841],{},[1344,110842,110843,110845,110847],{},[1347,110844,14769],{},[1347,110846,20194],{},[1347,110848,7834],{},[1357,110850,110851,110867,110887,110903,110921,110942,110960],{},[1344,110852,110853,110858,110862],{},[1362,110854,110855],{},[941,110856,110857],{},"retainDays",[1362,110859,110860],{},[941,110861,20636],{},[1362,110863,110864,110865,1520],{},"Age-based TTL — a slot uploaded more than N days ago evicts on the next ",[941,110866,108414],{},[1344,110868,110869,110874,110878],{},[1362,110870,110871],{},[941,110872,110873],{},"evictWhen",[1362,110875,110876],{},[941,110877,24825],{},[1362,110879,110880,110881,110883,110884,110886],{},"Evict when the predicate over the decrypted record returns ",[941,110882,11790],{},". A throwing predicate does ",[950,110885,1004],{}," evict (fail-closed).",[1344,110888,110889,110893,110897],{},[1362,110890,110891],{},[941,110892,99838],{},[1362,110894,110895],{},[941,110896,24825],{},[1362,110898,110899,110900,110902],{},"While ",[941,110901,11790],{},", the slot is never evicted, overriding both triggers. A throwing predicate holds the slot.",[1344,110904,110905,110909,110914],{},[1362,110906,110907],{},[941,110908,108735],{},[1362,110910,110911],{},[941,110912,110913],{},"(record) => Date | string | number | null",[1362,110915,110916,110917,110920],{},"Retention floor — no eviction while ",[941,110918,110919],{},"now \u003C retainUntil",". Unparseable or throwing values hold the slot.",[1344,110922,110923,110927,110931],{},[1362,110924,110925],{},[941,110926,20451],{},[1362,110928,110929],{},[941,110930,16861],{},[1362,110932,110933,110934,110937,110938,110941],{},"Store this field's bytes as a single raw ",[950,110935,110936],{},"unencrypted"," object in the vault's object projection (",[941,110939,110940],{},"createNoydb({ objectStore })",") — servable from S3\u002FCDN, outside the zero-knowledge guarantee. The encrypted slot record remains the catalog entry.",[1344,110943,110944,110948,110952],{},[1362,110945,110946],{},[941,110947,12],{},[1362,110949,110950],{},[941,110951,16861],{},[1362,110953,110954,110955,110957,110958,1520],{},"For ",[941,110956,20451],{}," fields: world-readable object instead of presigned-only. Default ",[941,110959,2560],{},[1344,110961,110962,110967,110972],{},[1362,110963,110964],{},[941,110965,110966],{},"backlink",[1362,110968,110969],{},[941,110970,110971],{},"'opaque-token' | 'encrypted' | 'plain' | 'none'",[1362,110973,110954,110974,110976,110977,110980],{},[941,110975,20451],{}," fields: how to stamp a reference back to the record on the object's metadata. Default ",[941,110978,110979],{},"'opaque-token'"," (no name leak).",[937,110982,110983,54824,110986,110989,110990,953,110993,968,110995,968,110997,968,110999,968,111001,1707,111003,957],{},[941,110984,110985],{},"vault.compact(options?)",[941,110987,110988],{},"{ now?, maxEvictions?, dryRun? }"," and returns a\n",[941,110991,110992],{},"CompactionResult",[941,110994,108583],{},[941,110996,9136],{},[941,110998,2698],{},[941,111000,108628],{},[941,111002,100604],{},[941,111004,100635],{},[1591,111006,111008],{"id":111007},"vaultexportblobsoptions",[941,111009,109448],{},[937,111011,101225,111012,964,111015,27493,111018,111021,111022,111025,111026,1520],{},[941,111013,111014],{},"ExportBlobsHandle",[941,111016,111017],{},"AsyncIterable\u003CExportedBlob>",[941,111019,111020],{},"abort()"," and a\nreadonly ",[941,111023,111024],{},"aborted"," flag. Requires the caller's keyring to grant plaintext blob export;\naborting makes the next iteration throw ",[941,111027,111028],{},"ExportBlobsAbortedError",[1338,111030,111031,111041],{},[1341,111032,111033],{},[1344,111034,111035,111037,111039],{},[1347,111036,101060],{},[1347,111038,20194],{},[1347,111040,7834],{},[1357,111042,111043,111057,111071,111088],{},[1344,111044,111045,111049,111054],{},[1362,111046,111047],{},[941,111048,2698],{},[1362,111050,111051],{},[941,111052,111053],{},"readonly string[]",[1362,111055,111056],{},"Allowlist. Omit to export from every collection the caller can read.",[1344,111058,111059,111063,111068],{},[1362,111060,111061],{},[941,111062,3016],{},[1362,111064,111065],{},[941,111066,111067],{},"(record, { collection, id }) => boolean",[1362,111069,111070],{},"Per-record predicate, evaluated before any of that record's chunks are read.",[1344,111072,111073,111078,111082],{},[1362,111074,111075],{},[941,111076,111077],{},"afterBlobId",[1362,111079,111080],{},[941,111081,7259],{},[1362,111083,111084,111085,111087],{},"Resume cursor — skip tuples up to and including this ",[941,111086,109738],{},", for idempotent batch re-runs.",[1344,111089,111090,111094,111099],{},[1362,111091,111092],{},[941,111093,37947],{},[1362,111095,111096],{},[941,111097,111098],{},"AbortSignal",[1362,111100,111101,111102,1520],{},"External abort, honored alongside ",[941,111103,111104],{},"handle.abort()",[1591,111106,111108],{"id":111107},"exportblobstodirectoryvault-targetdir-options",[941,111109,111110],{},"exportBlobsToDirectory(vault, targetDir, options?)",[937,111112,111113,111114,5502,111117,111119,111120,111122,111123,111125,111126,111128],{},"Returns ",[941,111115,111116],{},"{ written, bytes, entries, manifestPath }",[941,111118,109817],{}," is the absolute\npath of ",[941,111121,32978],{}," for the ",[941,111124,109832],{}," profile, ",[941,111127,6651],{}," otherwise.",[1338,111130,111131,111143],{},[1341,111132,111133],{},[1344,111134,111135,111137,111139,111141],{},[1347,111136,101060],{},[1347,111138,20194],{},[1347,111140,20197],{},[1347,111142,7834],{},[1357,111144,111145,111165,111185],{},[1344,111146,111147,111152,111157,111162],{},[1362,111148,111149],{},[941,111150,111151],{},"filenameProfile",[1362,111153,111154],{},[941,111155,111156],{},"FilenameProfile",[1362,111158,111159],{},[941,111160,111161],{},"'macos-smb'",[1362,111163,111164],{},"Sanitization target profile (most restrictive common default).",[1344,111166,111167,111172,111177,111182],{},[1362,111168,111169],{},[941,111170,111171],{},"onCollision",[1362,111173,111174],{},[941,111175,111176],{},"'suffix' | 'overwrite' | 'fail' | (existing, attempt) => string",[1362,111178,111179],{},[941,111180,111181],{},"'suffix'",[1362,111183,111184],{},"How to resolve two blobs sanitizing to the same name.",[1344,111186,111187,111197,111199,111201],{},[1362,111188,111189,1261,111191,1261,111193,1261,111195],{},[941,111190,2698],{},[941,111192,3016],{},[941,111194,111077],{},[941,111196,37947],{},[1362,111198,3610],{},[1362,111200,3610],{},[1362,111202,111203,111204,1520],{},"Forwarded to ",[941,111205,111206],{},"vault.exportBlobs",[1591,111208,111210],{"id":111209},"sanitizefilenamename-options",[941,111211,111212],{},"sanitizeFilename(name, options)",[937,111214,111215,111216,111219,111220,111222,111223,111226],{},"Pure string in \u002F string out; throws ",[941,111217,111218],{},"FilenameSanitizationError"," when the input cannot\nbe made safe (NUL byte, ",[941,111221,7452],{}," traversal segment, empty after normalization, missing\n",[941,111224,111225],{},"opaqueId"," for the opaque profile).",[1338,111228,111229,111239],{},[1341,111230,111231],{},[1344,111232,111233,111235,111237],{},[1347,111234,101060],{},[1347,111236,20194],{},[1347,111238,7834],{},[1357,111240,111241,111255,111285],{},[1344,111242,111243,111247,111252],{},[1362,111244,111245],{},[941,111246,19861],{},[1362,111248,111249],{},[941,111250,111251],{},"'posix' | 'windows' | 'macos-smb' | 'zip' | 'url-path' | 's3-key' | 'opaque'",[1362,111253,111254],{},"Storage-destination profile to defang for.",[1344,111256,111257,111261,111265],{},[1362,111258,111259],{},[941,111260,64488],{},[1362,111262,111263],{},[941,111264,20636],{},[1362,111266,111267,111268,1625,111270,1625,111272,111274,111275,111277,111278,1625,111281,111284],{},"Override the per-profile length cap (255 for ",[941,111269,110255],{},[941,111271,110154],{},[941,111273,165],{},", 240 for ",[941,111276,109646],{},", 1024 for ",[941,111279,111280],{},"url-path",[941,111282,111283],{},"s3-key",") — e.g. leave headroom for collision suffixes.",[1344,111286,111287,111291,111295],{},[1362,111288,111289],{},[941,111290,111225],{},[1362,111292,111293],{},[941,111294,7259],{},[1362,111296,111297,111298,111300,111301,111304],{},"Required for ",[941,111299,109832],{},": the output becomes ",[941,111302,111303],{},"{opaqueId}.{ext}"," (extension kept only if alphanumeric and ≤ 16 bytes).",[937,111306,111307,111308,111311],{},"Always-on transforms across all profiles: NFC normalization, bidi-override stripping\n(defeats ",[941,111309,111310],{},"‮","-style extension spoofing), NUL rejection (hard fail — silent stripping\nwould enable truncation bypass), and trimming of leading\u002Ftrailing whitespace and\ncontrol characters.",[1022,111313,101425],{"id":101424},[1111,111315,111316,111327,111330],{},[1114,111317,111318,111320,111321,6000,111324,111326],{},[941,111319,21881],{}," throws an error pointing you at\n",[941,111322,111323],{},"createNoydb({ blobsStrategy: withBlobs() })",[941,111325,58494],{}," import.",[1114,111328,111329],{},"All blob machinery stays out of the bundle — ~2,376 LOC saved.",[1114,111331,111332,9622,111334,111336],{},[941,111333,108414],{},[941,111335,22785],{}," declared anywhere is a no-op returning zeros.",[1022,111338,68210],{"id":68209},[1111,111340,111341,111349,111363,111378,111388,111402,111415,111423,111455,111480],{},[1114,111342,111343,111348],{},[950,111344,111345,111347],{},[941,111346,110513],{}," is buffered",", not truly streaming — all chunks are loaded into memory\nbefore the body is enqueued. Budget accordingly for very large files.",[1114,111350,111351,111356,111357,111359,111360,111362],{},[950,111352,111353,111355],{},[941,111354,82015],{}," is browser-only"," — it throws where ",[941,111358,108391],{}," is missing —\nand the caller owns ",[941,111361,82028],{},"; an unrevoked URL pins the decrypted bytes for the\nlifetime of the document.",[1114,111364,111365,111368,111369,53922,111371,111373,111374,111377],{},[950,111366,111367],{},"Chunk size resolution",": explicit ",[941,111370,110542],{},[941,111372,20953],{}," > the store's advertised\n",[941,111375,111376],{},"maxBlobBytes"," (e.g. 256 KB for DynamoDB-backed stores) > the 256 KB default.",[1114,111379,111380,111383,111384,111387],{},[950,111381,111382],{},"Mutating a slot publishes new content"," — blobs are content-addressed and immutable;\noverwriting a slot points it at a new eTag and releases the old one. Use\n",[941,111385,111386],{},"publish(slot, label)"," to pin a version that survives later overwrites.",[1114,111389,111390,111393,111394,111396,111397,1625,111399,111401],{},[950,111391,111392],{},"Compaction is consumer-scheduled"," — no background daemon. Policy predicates are\nfail-closed: a throwing ",[941,111395,110873],{}," never evicts, a throwing ",[941,111398,99838],{},[941,111400,108735],{},"\nholds the slot.",[1114,111403,111404,111410,111411,111414],{},[950,111405,111406,111409],{},[941,111407,111408],{},"presignedUrl()"," serves ciphertext",", works only for single-chunk blobs on stores\nthat support presigning, and must be paired with ",[941,111412,111413],{},"decryptResponse()"," (or a service\nworker) client-side.",[1114,111416,111417,111422],{},[950,111418,111419,111421],{},[941,111420,20451],{}," blob fields are unencrypted by design"," — they exist to serve assets\ndirectly from S3\u002FCDN and sit outside the zero-knowledge guarantee. Keep sensitive\nattachments on the default encrypted-chunk path.",[1114,111424,111425,14636,111434,6030,111436,111438,111439,5502,111441,111444,111445,111447,111448,111450,111451,1520],{},[950,111426,111427,111428,2462,111430,111433],{},"Tiered blobs need ",[941,111429,14552],{},[941,111431,111432],{},"blobTierPolicy: 'dedup'"," keeps shared content\nunder the flat DEK.",[941,111435,22785],{},[941,111437,872],{}," collection must set ",[941,111440,14552],{},[941,111442,111443],{},"dedup","\npolicy hides a shared blob from tier-0 callers at the read gate but leaves its chunks\ndecryptable at rest under the flat ",[941,111446,15122],{}," DEK. Use ",[941,111449,109158],{}," (the default) when at-rest\nisolation of shared content matters. See ",[1007,111452,111454],{"href":111453},"#tiered-blobs-content-follows-the-records-tier","Tiered blobs",[1114,111456,111457,41138,111462,111465,111466,111469,111470,11737,111472,111475,111476,111479],{},[950,111458,3289,111459,111461],{},[941,111460,22785],{}," slot is not queryable.",[941,111463,111464],{},"0.3.0-pre.9",", a\ndeclared blob field carries an enforced ",[941,111467,111468],{},"queryable: 'none'"," posture (see\n",[1007,111471,896],{"href":897},[941,111473,111474],{},"query().where()"," on it throws ",[941,111477,111478],{},"FieldNotQueryableError"," instead of\nsilently matching nothing. Query record metadata, not blob slots.",[1114,111481,111482,111487,111488,111490,111491,111493,111494,111497,111498,111503],{},[950,111483,111484,111486],{},[941,111485,2542],{}," shreds blobs on every collection by default"," — not only collections that\ndeclared ",[941,111489,22785],{},". A collection that only ever wrote blobs via ",[941,111492,108814],{}," still has\nthem crypto-shredded on erasure. Opt into ",[941,111495,111496],{},"scopedPurge: true"," (see\n",[1007,111499,108744,111501],{"href":111500},"#erasure-blobs-shred-with-forget",[941,111502,2542],{},") to scan only\ndeclared collections; undeclared ones are then skipped and reported, never silently.",[1022,111505,2621],{"id":2620},[1111,111507,111508,111513,111520,111525,111530,111539,111547,111555,111561,111567],{},[1114,111509,111510,111512],{},[1007,111511,507],{"href":508}," — invoices with attached receipt scans,\nretention, and export in one recipe",[1114,111514,111515,964,111517,111519],{},[1007,111516,629],{"href":630},[941,111518,1402],{}," containers carry blobs alongside records",[1114,111521,111522,111524],{},[1007,111523,697],{"href":698}," — slot metadata is itself versioned envelope data",[1114,111526,111527,111529],{},[1007,111528,808],{"href":809}," — send blob chunks to a separate backend (S3 \u002F R2) while\nrecord metadata stays on the primary store",[1114,111531,111532,111534,111535,111538],{},[1007,111533,812],{"href":813}," — the sibling heavy-field strategy for large\n",[1092,111536,111537],{},"structured\u002Ftext"," fields that still need schema validation and query",[1114,111540,111541,111543,111544,111546],{},[1007,111542,896],{"href":897}," — the field-feature posture a ",[941,111545,22785],{}," declaration carries",[1114,111548,111549,111551,111552,111554],{},[1007,111550,685],{"href":686}," — right-to-erasure crypto-shredding; how attached blobs shred with a record and the ",[941,111553,56931],{}," scan-scoping knob",[1114,111556,111557,1368,111559],{},[1007,111558,6891],{"href":51},[1007,111560,21],{"href":66},[1114,111562,111563,1368,111565],{},[1007,111564,61743],{"href":598},[1007,111566,1019],{"href":1018},[1114,111568,101581,111569,1520],{},[1007,111570,111572],{"href":92577,"rel":111571},[3371],[941,111573,111574],{},"08-with-blobs",[2645,111576,111577],{},"html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sHdIc, html code.shiki .sHdIc{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#EEFFFF;--shiki-default-font-style:italic;--shiki-dark:#BABED8;--shiki-dark-font-style:italic}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}html pre.shiki code .sfNiH, html code.shiki .sfNiH{--shiki-light:#FF5370;--shiki-default:#FF9CAC;--shiki-dark:#FF9CAC}",{"title":1037,"searchDepth":1038,"depth":1038,"links":111579},[111580,111581,111582,111583,111595,111606,111607,111608],{"id":29856,"depth":1041,"text":29857},{"id":70092,"depth":1041,"text":70093},{"id":99863,"depth":1041,"text":99864},{"id":29892,"depth":1041,"text":29893,"children":111584},[111585,111586,111587,111588,111593,111594],{"id":107730,"depth":1038,"text":107731},{"id":108195,"depth":1038,"text":108196},{"id":108404,"depth":1038,"text":108405},{"id":108743,"depth":1038,"text":111589,"children":111590},"Erasure — blobs shred with forget()",[111591],{"id":108823,"depth":2288,"text":111592},"Scoping the scan with scopedPurge",{"id":109055,"depth":1038,"text":109056},{"id":109442,"depth":1038,"text":109443},{"id":102888,"depth":1041,"text":102889,"children":111596},[111597,111599,111600,111601,111603,111604,111605],{"id":110280,"depth":1038,"text":111598},"BlobSet — from collection.blob(id)",{"id":110646,"depth":1038,"text":110649},{"id":110757,"depth":1038,"text":110760},{"id":110827,"depth":1038,"text":111602},"blobFields policy",{"id":111007,"depth":1038,"text":109448},{"id":111107,"depth":1038,"text":111110},{"id":111209,"depth":1038,"text":111212},{"id":101424,"depth":1041,"text":101425},{"id":68209,"depth":1041,"text":68210},{"id":2620,"depth":1041,"text":2621},"Encrypted binary attachments — chunked AES-256-GCM storage with content-addressed dedup, MIME sniffing, retention compaction, and bulk export.",{},{"title":621,"description":111609},"BOqC8zdANiIkr9I-P9v49BIqnHWURQNe2Hn-Zqqrjms",{"id":111614,"title":625,"access":12,"apiToc":929,"body":111615,"category":1042,"description":114857,"extension":1044,"meta":114858,"navigation":1042,"nest":1047,"path":626,"seo":114859,"sinceVersion":108831,"status":1049,"stem":627,"subtitle":114860,"tier":1473,"tocExternal":1042,"__hash__":114861},"docs\u002Fservices\u002Fbroker.md",{"type":931,"value":111616,"toc":114841},[111617,111619,111622,111624,111642,111657,111661,111689,111693,111700,111850,111853,111857,111879,111974,111976,111979,112018,112020,112270,112274,112288,112643,112661,112670,112674,112867,112882,113005,113009,113030,113122,113124,113144,113148,113166,113214,113221,114681,114685,114720,114722,114805,114807,114838],[934,111618,625],{"id":625},[46871,111620],{":rows":111621},"[{\"label\":\"Subpath\",\"value\":\"`@noy-db\u002Fhub\u002Fbroker`\"},{\"label\":\"Factory\",\"value\":\"`withBroker(config)`\"},{\"label\":\"Layer\",\"value\":\"`with-party`\"},{\"label\":\"Cluster\",\"value\":\"G — Collaboration & Auth\"},{\"label\":\"LOC cost\",\"value\":\"~500 (off-bundle when not opted in)\"}]",[1022,111623,29857],{"id":29856},[937,111625,111626,111627,111630,111631,111634,111635,111637,111638,111641],{},"The credential broker lets a client ",[950,111628,111629],{},"secret-bound to a vault"," obtain short-lived, rolling\ncloud-storage credentials (AWS access keys today; a vendor-neutral token shape is reserved for a\nlater slice) ",[950,111632,111633],{},"without ever putting a long-lived secret in the client",". A 32-byte seed is\ngenerated once, encrypted under the vault's own ",[941,111636,15926],{}," collection DEK (data-encryption key),\nand never leaves the device. A ",[950,111639,111640],{},"derived proof key"," — never the seed itself — is registered with\na broker host at enrolment; from then on the client re-derives the same key, signs a challenge the\nhost just issued, and the host mints scoped, minutes-to-an-hour credentials on a verified proof.",[937,111643,111644,111645,111647,111648,111651,111652,111656],{},"Nothing here weakens the zero-knowledge guarantee over document ",[1092,111646,11007],{}," — this broker only gates\n",[950,111649,111650],{},"storage-backend access"," (can this client currently read\u002Fwrite ciphertext at all), not the\nconfidentiality of what's inside it. It is the same trust class as the ",[1007,111653,111654],{"href":170},[941,111655,1139],{},"\nsealing-key family: a host you run participates, but it never sees a secret, KEK, DEK, or\nplaintext.",[1022,111658,111660],{"id":111659},"when-you-need-it","When you need it",[1111,111662,111663,111673,111676],{},[1114,111664,111665,111666,968,111668,968,111670,111672],{},"An app backed by a cloud store (",[941,111667,46865],{},[941,111669,29876],{},[941,111671,29849],{},")\nthat wants per-vault-scoped, auto-rotating credentials instead of one static IAM key baked into\nevery client.",[1114,111674,111675],{},"Revocation without a client-side ceremony: stop re-issuing at the broker and every client's\ncredential goes dead within its TTL.",[1114,111677,111678,111679,111681,111682,11189,111685,111688],{},"Per-role access tiers (",[941,111680,19861],{},", e.g. ",[941,111683,111684],{},"'read'",[941,111686,111687],{},"'admin'",") mapped to distinct STS session\npolicies, enforced by the same proof.",[1022,111690,111692],{"id":111691},"threat-model-what-it-defeats-and-what-it-honestly-does-not","Threat model — what it defeats, and what it honestly does not",[937,111694,111695,111696,111699],{},"Store-access credentials gate ",[950,111697,111698],{},"availability \u002F tamper \u002F deletion"," of ciphertext, never its\nconfidentiality — that rests on the enclave, unchanged. The secret and every key\u002FDEK never\nleave the client; the broker sees only a challenge proof. The stolen-ciphertext story is unchanged\nby any of this.",[1338,111701,111702,111718],{},[1341,111703,111704],{},[1344,111705,111706,111709,111712],{},[1347,111707,111708],{},"Property",[1347,111710,111711],{},"Defeats",[1347,111713,111714,111715,111717],{},"Does ",[950,111716,1004],{}," defeat",[1357,111719,111720,111738,111781,111807,111837],{},[1344,111721,111722,111728,111731],{},[1362,111723,111724,111727],{},[950,111725,111726],{},"Rolling"," (short-lived, rotating)",[1362,111729,111730],{},"A credential lifted from a memory dump, crash log, or devtools session is dead within its TTL (minutes–1 h). Revocation = the broker stops re-issuing; no client-side rotation ceremony.",[1362,111732,111733,111734,111737],{},"Live abuse ",[950,111735,111736],{},"within"," the TTL.",[1344,111739,111740,111745,111762],{},[1362,111741,111742],{},[950,111743,111744],{},"Non-extractable",[1362,111746,111747,111748,111750,111751,111754,111755,1324,111758,111761],{},"An attacker who copies the app bundle, ",[941,111749,41955],{},", or IndexedDB ",[950,111752,111753],{},"at rest"," gets nothing replayable: the steady-state proof key is a WebCrypto ",[941,111756,111757],{},"extractable: false",[941,111759,111760],{},"['sign']"," HMAC key.",[1362,111763,111764,111767,111768,111770,111771,111774,111775,111777,111778,1520],{},[950,111765,111766],{},"A resident XSS attacker while the vault is unlocked"," — they can drive the whole proof flow with the live key, ",[1092,111769,14128],{}," re-run the key derivation from the decrypted seed, whose bytes are re-derivable while unlocked. Non-extractability denies ",[950,111772,111773],{},"capability-cloning"," (reuse of a ",[1092,111776,36368],{}," offline\u002Flater\u002Felsewhere), not live in-page use. It is defense-in-depth against a ",[1092,111779,111780],{},"leaked handle only",[1344,111782,111783,111791,111797],{},[1362,111784,111785,953,111788,1381],{},[950,111786,111787],{},"User-bound",[941,111789,111790],{},"access = f(app config, unlocked keyring)",[1362,111792,111793,111794,111796],{},"App config alone (bundle constants, endpoint URLs) grants zero cloud access — the broker refuses without a proof derivable only from a decrypted ",[941,111795,15926],{}," seed. Per-user STS session tags + a scoped session policy let the cloud side enforce per-vault scoping, so user A's credentials cannot read user B's rows.",[1362,111798,111799,111802,111803,111806],{},[950,111800,111801],{},"A compromised broker host."," It is the credential authority; its compromise mints credentials for anyone. This is outside the zero-knowledge boundary ",[950,111804,111805],{},"by design"," — the broker still never sees secret, KEK, DEK, or plaintext.",[1344,111808,111809,111815,111824],{},[1362,111810,111811,111814],{},[950,111812,111813],{},"Symmetric-verifiable"," (the broker holds the same key it verifies)",[1362,111816,111817,111818,111820,111821,111823],{},"A forged ",[1092,111819,9913],{}," proof grants nothing the broker cannot already grant itself — it ",[950,111822,3082],{}," the credential mint.",[1362,111825,111826,111829,111830,111832,111833,111836],{},[950,111827,111828],{},"A read-only leak of the broker's registered-key store."," Because verification is symmetric HMAC, a registered proof key ",[950,111831,3082],{}," a forgeable credential — ",[1092,111834,111835],{},"unless the host wraps registered keys at rest under a broker-side KMS key"," (mandated below): with KMS-wrap, a read-only DB leak yields only ciphertext the attacker cannot unwrap.",[1344,111838,111839,111845,111848],{},[1362,111840,111841,111844],{},[950,111842,111843],{},"Transparent"," (dev wires the cloud store at setup; the end user never sees it)",[1362,111846,111847],{},"Nothing — this is UX, not security. Listed so nobody mistakes it for a security property.",[1362,111849,3610],{},[937,111851,111852],{},"An asymmetric enrolment (register only a public verifier) would remove the at-rest forgeability\nstructurally, but WebCrypto cannot portably derive a deterministic keypair from the key-derivation\noutput. KMS-wrap is the accepted mitigation for the symmetric-HMAC at-rest exposure — see the\nreference host below.",[1022,111854,111856],{"id":111855},"protocol-enrol-challenge-mint-refresh-rotate","Protocol: enrol → challenge → mint → refresh → rotate",[937,111858,111859,1324,111862,968,111865,111868,111869,111871,111872,111875,111876,111878],{},[950,111860,111861],{},"What the broker learns:",[941,111863,111864],{},"vaultId",[941,111866,111867],{},"brokerId",", a one-way 32-byte derivation output (registered\nonce, inverts to nothing), the requested ",[941,111870,19861],{},", request timing\u002FIP, and the dev-backend\nattestation identity at enrol.\n",[950,111873,111874],{},"What it never learns:"," the secret, any KEK\u002FDEK, collection names, record contents, or the\n",[941,111877,15926],{}," seed itself.",[1234,111880,111881,111912,111922,111947,111960],{},[1114,111882,111883,953,111886,111889,111890,111892,111893,111896,111897,111900,111901,111904,111905,111908,111909,111911],{},[950,111884,111885],{},"Enrol",[941,111887,111888],{},"vault.broker().enroll()",", idempotent). Creates the ",[941,111891,15926],{}," seed if absent (a\ncreate-if-absent compare-and-set — two concurrent enrols converge on one seed, never two),\nderives the proof bits, and ",[941,111894,111895],{},"POST \u002Fenroll","s them plus a dev-backend attestation token. Only after\na 2xx does the local record flip ",[941,111898,111899],{},"registered: true"," — a seed whose ",[941,111902,111903],{},"\u002Fenroll"," POST failed stays\nunregistered, so a later mint fails fast instead of degrading into an opaque proof error.\nRequires owner\u002Fadmin role, and — ",[950,111906,111907],{},"for first-ever seed creation only"," — the vault's KEK. A\nkeyring holding only a DEK (e.g. a PIN quick-resume) can still ",[1092,111910,42401],{}," an already-enrolled seed,\njust not create the first one.",[1114,111913,111914,111917,111918,111921],{},[950,111915,111916],{},"Challenge."," The client ",[941,111919,111920],{},"POST \u002Fchallenge","s; the host mints a random, single-use challenge with\na short (≤ 60 s) TTL and returns it.",[1114,111923,111924,111927,111928,111930,111931,968,111933,111935,111936,111938,111939,111942,111943,111946],{},[950,111925,111926],{},"Mint."," The client re-derives the proof key from the seed, signs a canonical string binding\n",[941,111929,111864],{},", the broker endpoint's origin, ",[941,111932,111867],{},[941,111934,19861],{},", the challenge, and its\n",[941,111937,44339],{},", then ",[941,111940,111941],{},"POST \u002Fcredentials","s the proof. The host ",[950,111944,111945],{},"burns the challenge before doing any\nHMAC work"," (a replay of an already-used challenge is rejected before the MAC is even checked),\nverifies the signature against the unwrapped registered key, and — only then — mints scoped,\nshort-lived cloud credentials.",[1114,111948,111949,1324,111952,111955,111956,111959],{},[950,111950,111951],{},"Refresh \u002F cache.",[941,111953,111954],{},"credentialSource(profile)"," (what a store's credential hook calls) holds a\nsingle-flight, per-profile cache: concurrent callers near expiry await one round-trip; cached\ncredentials are valid until ",[941,111957,111958],{},"max(expiresAt − skewMs, now + minCacheMs)",". Nothing is ever written\nto disk — credentials live only in memory and die with the tab.",[1114,111961,111962,953,111965,111967,111968,111970,111971,111973],{},[950,111963,111964],{},"Rotate",[941,111966,15332],{},"). Quiesces the cache (awaits any in-flight round-trip),\nmints a fresh seed, registers it ",[950,111969,11357],{}," overwriting the local record, and overwrites last.\nThe host must accept ",[950,111972,31219],{}," the old and new registration for a short grace window so an\nin-flight proof computed under the pre-rotation seed still verifies.",[1022,111975,70093],{"id":70092},[937,111977,111978],{},"Four parts must exist together before a brokered credential reaches a store — each is detailed below, all four up front:",[1234,111980,111981,111991,111997,112012],{},[1114,111982,111983,53922,111988,111990],{},[950,111984,111985],{},[941,111986,111987],{},"brokerStrategy: withBroker(config)",[941,111989,12780],{}," (this page's opt-in).",[1114,111992,111993,111996],{},[950,111994,111995],{},"A running broker host"," implementing the four obligations listed under \"The reference broker host\" below — the hub ships the protocol, you run the host.",[1114,111998,111999,33694,112007,1261,112009,112011],{},[950,112000,960,112001,112004,112005,73413],{},[941,112002,112003],{},"credentialSource"," wired into a cloud store's ",[941,112006,48119],{},[941,112008,29876],{},[941,112010,9710],{},") so the short-lived credentials actually reach the SDK.",[1114,112013,112014,112017],{},[950,112015,112016],{},"Owner\u002Fadmin role"," for registration — and, for first-ever seed creation only, the vault's KEK.",[1022,112019,99864],{"id":99863},[2217,112021,112023],{"className":2219,"code":112022,"language":2221,"meta":1037,"style":1037},"import { createNoydb } from '@noy-db\u002Fhub'\nimport { withBroker } from '@noy-db\u002Fhub\u002Fbroker'\nimport { toBrowserIdb } from '@noy-db\u002Fto-browser-idb'\n\nconst db = await createNoydb({\n  store: toBrowserIdb(),\n  user: 'me',\n  secret: 'correct horse battery staple',\n  brokerStrategy: withBroker({\n    brokerId: 'broker-1',\n    endpoint: 'https:\u002F\u002Fbroker.example.com',\n    \u002F\u002F Your app's proof to the host that this enrollment call is\n    \u002F\u002F legitimate — e.g. the session token your backend already issued.\n    attestation: () => mySessionToken,\n  }),\n})\nconst vault = await db.openVault('acme')\nawait vault.broker().enroll()\n",[941,112024,112025,112043,112062,112080,112084,112100,112112,112126,112140,112153,112169,112185,112190,112195,112211,112219,112225,112251],{"__ignoreMap":1037},[2225,112026,112027,112029,112031,112033,112035,112037,112039,112041],{"class":2227,"line":2228},[2225,112028,2232],{"class":2231},[2225,112030,2236],{"class":2235},[2225,112032,2744],{"class":2239},[2225,112034,2243],{"class":2235},[2225,112036,2246],{"class":2231},[2225,112038,2249],{"class":2235},[2225,112040,1086],{"class":2252},[2225,112042,2256],{"class":2235},[2225,112044,112045,112047,112049,112052,112054,112056,112058,112060],{"class":2227,"line":1041},[2225,112046,2232],{"class":2231},[2225,112048,2236],{"class":2235},[2225,112050,112051],{"class":2239}," withBroker",[2225,112053,2243],{"class":2235},[2225,112055,2246],{"class":2231},[2225,112057,2249],{"class":2235},[2225,112059,96142],{"class":2252},[2225,112061,2256],{"class":2235},[2225,112063,112064,112066,112068,112070,112072,112074,112076,112078],{"class":2227,"line":1038},[2225,112065,2232],{"class":2231},[2225,112067,2236],{"class":2235},[2225,112069,34763],{"class":2239},[2225,112071,2243],{"class":2235},[2225,112073,2246],{"class":2231},[2225,112075,2249],{"class":2235},[2225,112077,9573],{"class":2252},[2225,112079,2256],{"class":2235},[2225,112081,112082],{"class":2227,"line":2288},[2225,112083,2285],{"emptyLinePlaceholder":2284},[2225,112085,112086,112088,112090,112092,112094,112096,112098],{"class":2227,"line":2304},[2225,112087,2775],{"class":2774},[2225,112089,2778],{"class":2239},[2225,112091,2781],{"class":2235},[2225,112093,2784],{"class":2231},[2225,112095,2744],{"class":2291},[2225,112097,2789],{"class":2239},[2225,112099,4200],{"class":2235},[2225,112101,112102,112104,112106,112108,112110],{"class":2227,"line":2356},[2225,112103,15608],{"class":2307},[2225,112105,2311],{"class":2235},[2225,112107,34763],{"class":2291},[2225,112109,4228],{"class":2239},[2225,112111,1707],{"class":2235},[2225,112113,112114,112116,112118,112120,112122,112124],{"class":2227,"line":2830},[2225,112115,4205],{"class":2307},[2225,112117,2311],{"class":2235},[2225,112119,2249],{"class":2235},[2225,112121,4212],{"class":2252},[2225,112123,2805],{"class":2235},[2225,112125,1707],{"class":2235},[2225,112127,112128,112130,112132,112134,112136,112138],{"class":2227,"line":2861},[2225,112129,10423],{"class":2307},[2225,112131,2311],{"class":2235},[2225,112133,2249],{"class":2235},[2225,112135,40260],{"class":2252},[2225,112137,2805],{"class":2235},[2225,112139,1707],{"class":2235},[2225,112141,112142,112145,112147,112149,112151],{"class":2227,"line":4246},[2225,112143,112144],{"class":2307},"  brokerStrategy",[2225,112146,2311],{"class":2235},[2225,112148,112051],{"class":2291},[2225,112150,2789],{"class":2239},[2225,112152,4200],{"class":2235},[2225,112154,112155,112158,112160,112162,112165,112167],{"class":2227,"line":4253},[2225,112156,112157],{"class":2307},"    brokerId",[2225,112159,2311],{"class":2235},[2225,112161,2249],{"class":2235},[2225,112163,112164],{"class":2252},"broker-1",[2225,112166,2805],{"class":2235},[2225,112168,1707],{"class":2235},[2225,112170,112171,112174,112176,112178,112181,112183],{"class":2227,"line":4259},[2225,112172,112173],{"class":2307},"    endpoint",[2225,112175,2311],{"class":2235},[2225,112177,2249],{"class":2235},[2225,112179,112180],{"class":2252},"https:\u002F\u002Fbroker.example.com",[2225,112182,2805],{"class":2235},[2225,112184,1707],{"class":2235},[2225,112186,112187],{"class":2227,"line":6548},[2225,112188,112189],{"class":2763},"    \u002F\u002F Your app's proof to the host that this enrollment call is\n",[2225,112191,112192],{"class":2227,"line":6576},[2225,112193,112194],{"class":2763},"    \u002F\u002F legitimate — e.g. the session token your backend already issued.\n",[2225,112196,112197,112200,112202,112204,112206,112209],{"class":2227,"line":6582},[2225,112198,112199],{"class":2291},"    attestation",[2225,112201,2311],{"class":2235},[2225,112203,13130],{"class":2235},[2225,112205,8116],{"class":2774},[2225,112207,112208],{"class":2239}," mySessionToken",[2225,112210,1707],{"class":2235},[2225,112212,112213,112215,112217],{"class":2227,"line":6587},[2225,112214,19143],{"class":2235},[2225,112216,1381],{"class":2239},[2225,112218,1707],{"class":2235},[2225,112220,112221,112223],{"class":2227,"line":7174},[2225,112222,2359],{"class":2235},[2225,112224,2362],{"class":2239},[2225,112226,112227,112229,112231,112233,112235,112237,112239,112241,112243,112245,112247,112249],{"class":2227,"line":7180},[2225,112228,2775],{"class":2774},[2225,112230,2835],{"class":2239},[2225,112232,2781],{"class":2235},[2225,112234,2784],{"class":2231},[2225,112236,2842],{"class":2239},[2225,112238,1520],{"class":2235},[2225,112240,2847],{"class":2291},[2225,112242,2789],{"class":2239},[2225,112244,2805],{"class":2235},[2225,112246,6450],{"class":2252},[2225,112248,2805],{"class":2235},[2225,112250,2362],{"class":2239},[2225,112252,112253,112255,112257,112259,112261,112263,112265,112268],{"class":2227,"line":7220},[2225,112254,6500],{"class":2231},[2225,112256,6466],{"class":2239},[2225,112258,1520],{"class":2235},[2225,112260,625],{"class":2291},[2225,112262,4228],{"class":2239},[2225,112264,1520],{"class":2235},[2225,112266,112267],{"class":2291},"enroll",[2225,112269,8146],{"class":2239},[1022,112271,112273],{"id":112272},"the-whole-flow-end-to-end","The whole flow, end to end",[937,112275,112276,112277,112280,112281,1625,112284,112287],{},"The four parts above, assembled — this is the shape the showcase suite runs in CI (",[941,112278,112279],{},"97-broker-aws",", Tier 1: an in-process reference host built from the published ",[941,112282,112283],{},"issueChallenge()",[941,112285,112286],{},"verifyBrokerProof()"," primitives, real proof crypto, no network):",[2217,112289,112291],{"className":2219,"code":112290,"language":2221,"meta":1037,"style":1037},"import { createNoydb } from '@noy-db\u002Fhub'\nimport { withBroker } from '@noy-db\u002Fhub\u002Fbroker'\nimport { toMemory } from '@noy-db\u002Fto-memory'\nimport { toAwsDynamo } from '@noy-db\u002Fto-aws-dynamo'\n\n\u002F\u002F Part 1 + 4 — opt in, with your attestation:\nconst db = await createNoydb({\n  store: toMemory(),\n  user: 'alice',\n  secret: 'broker-secret-2026',\n  brokerStrategy: withBroker({\n    brokerId: 'broker-1',\n    endpoint: 'https:\u002F\u002Fbroker.example.com', \u002F\u002F Part 2 — your running host\n    attestation: () => mySessionToken,\n  }),\n})\nconst vault = await db.openVault('acme')\n\n\u002F\u002F One-time per device: register this client's proof key with the host.\nawait vault.broker().enroll()\n\n\u002F\u002F Part 3 — the rolling credential source, wired into a cloud store's\n\u002F\u002F `credentials` refresh hook. The SDK re-invokes it near expiry; each\n\u002F\u002F invocation runs the challenge → proof → mint round trip.\nconst source = vault.broker().credentialSource('read')\nconst cloudStore = toAwsDynamo({ table: 'acme-vault-table', credentials: source })\n",[941,112292,112293,112311,112329,112347,112365,112369,112374,112390,112402,112416,112431,112443,112457,112474,112488,112496,112502,112528,112532,112537,112555,112559,112564,112569,112574,112605],{"__ignoreMap":1037},[2225,112294,112295,112297,112299,112301,112303,112305,112307,112309],{"class":2227,"line":2228},[2225,112296,2232],{"class":2231},[2225,112298,2236],{"class":2235},[2225,112300,2744],{"class":2239},[2225,112302,2243],{"class":2235},[2225,112304,2246],{"class":2231},[2225,112306,2249],{"class":2235},[2225,112308,1086],{"class":2252},[2225,112310,2256],{"class":2235},[2225,112312,112313,112315,112317,112319,112321,112323,112325,112327],{"class":2227,"line":1041},[2225,112314,2232],{"class":2231},[2225,112316,2236],{"class":2235},[2225,112318,112051],{"class":2239},[2225,112320,2243],{"class":2235},[2225,112322,2246],{"class":2231},[2225,112324,2249],{"class":2235},[2225,112326,96142],{"class":2252},[2225,112328,2256],{"class":2235},[2225,112330,112331,112333,112335,112337,112339,112341,112343,112345],{"class":2227,"line":1038},[2225,112332,2232],{"class":2231},[2225,112334,2236],{"class":2235},[2225,112336,68576],{"class":2239},[2225,112338,2243],{"class":2235},[2225,112340,2246],{"class":2231},[2225,112342,2249],{"class":2235},[2225,112344,9638],{"class":2252},[2225,112346,2256],{"class":2235},[2225,112348,112349,112351,112353,112355,112357,112359,112361,112363],{"class":2227,"line":2288},[2225,112350,2232],{"class":2231},[2225,112352,2236],{"class":2235},[2225,112354,95763],{"class":2239},[2225,112356,2243],{"class":2235},[2225,112358,2246],{"class":2231},[2225,112360,2249],{"class":2235},[2225,112362,46865],{"class":2252},[2225,112364,2256],{"class":2235},[2225,112366,112367],{"class":2227,"line":2304},[2225,112368,2285],{"emptyLinePlaceholder":2284},[2225,112370,112371],{"class":2227,"line":2356},[2225,112372,112373],{"class":2763},"\u002F\u002F Part 1 + 4 — opt in, with your attestation:\n",[2225,112375,112376,112378,112380,112382,112384,112386,112388],{"class":2227,"line":2830},[2225,112377,2775],{"class":2774},[2225,112379,2778],{"class":2239},[2225,112381,2781],{"class":2235},[2225,112383,2784],{"class":2231},[2225,112385,2744],{"class":2291},[2225,112387,2789],{"class":2239},[2225,112389,4200],{"class":2235},[2225,112391,112392,112394,112396,112398,112400],{"class":2227,"line":2861},[2225,112393,15608],{"class":2307},[2225,112395,2311],{"class":2235},[2225,112397,68576],{"class":2291},[2225,112399,4228],{"class":2239},[2225,112401,1707],{"class":2235},[2225,112403,112404,112406,112408,112410,112412,112414],{"class":2227,"line":4246},[2225,112405,4205],{"class":2307},[2225,112407,2311],{"class":2235},[2225,112409,2249],{"class":2235},[2225,112411,6408],{"class":2252},[2225,112413,2805],{"class":2235},[2225,112415,1707],{"class":2235},[2225,112417,112418,112420,112422,112424,112427,112429],{"class":2227,"line":4253},[2225,112419,10423],{"class":2307},[2225,112421,2311],{"class":2235},[2225,112423,2249],{"class":2235},[2225,112425,112426],{"class":2252},"broker-secret-2026",[2225,112428,2805],{"class":2235},[2225,112430,1707],{"class":2235},[2225,112432,112433,112435,112437,112439,112441],{"class":2227,"line":4259},[2225,112434,112144],{"class":2307},[2225,112436,2311],{"class":2235},[2225,112438,112051],{"class":2291},[2225,112440,2789],{"class":2239},[2225,112442,4200],{"class":2235},[2225,112444,112445,112447,112449,112451,112453,112455],{"class":2227,"line":6548},[2225,112446,112157],{"class":2307},[2225,112448,2311],{"class":2235},[2225,112450,2249],{"class":2235},[2225,112452,112164],{"class":2252},[2225,112454,2805],{"class":2235},[2225,112456,1707],{"class":2235},[2225,112458,112459,112461,112463,112465,112467,112469,112471],{"class":2227,"line":6576},[2225,112460,112173],{"class":2307},[2225,112462,2311],{"class":2235},[2225,112464,2249],{"class":2235},[2225,112466,112180],{"class":2252},[2225,112468,2805],{"class":2235},[2225,112470,2298],{"class":2235},[2225,112472,112473],{"class":2763}," \u002F\u002F Part 2 — your running host\n",[2225,112475,112476,112478,112480,112482,112484,112486],{"class":2227,"line":6582},[2225,112477,112199],{"class":2291},[2225,112479,2311],{"class":2235},[2225,112481,13130],{"class":2235},[2225,112483,8116],{"class":2774},[2225,112485,112208],{"class":2239},[2225,112487,1707],{"class":2235},[2225,112489,112490,112492,112494],{"class":2227,"line":6587},[2225,112491,19143],{"class":2235},[2225,112493,1381],{"class":2239},[2225,112495,1707],{"class":2235},[2225,112497,112498,112500],{"class":2227,"line":7174},[2225,112499,2359],{"class":2235},[2225,112501,2362],{"class":2239},[2225,112503,112504,112506,112508,112510,112512,112514,112516,112518,112520,112522,112524,112526],{"class":2227,"line":7180},[2225,112505,2775],{"class":2774},[2225,112507,2835],{"class":2239},[2225,112509,2781],{"class":2235},[2225,112511,2784],{"class":2231},[2225,112513,2842],{"class":2239},[2225,112515,1520],{"class":2235},[2225,112517,2847],{"class":2291},[2225,112519,2789],{"class":2239},[2225,112521,2805],{"class":2235},[2225,112523,6450],{"class":2252},[2225,112525,2805],{"class":2235},[2225,112527,2362],{"class":2239},[2225,112529,112530],{"class":2227,"line":7220},[2225,112531,2285],{"emptyLinePlaceholder":2284},[2225,112533,112534],{"class":2227,"line":7225},[2225,112535,112536],{"class":2763},"\u002F\u002F One-time per device: register this client's proof key with the host.\n",[2225,112538,112539,112541,112543,112545,112547,112549,112551,112553],{"class":2227,"line":7231},[2225,112540,6500],{"class":2231},[2225,112542,6466],{"class":2239},[2225,112544,1520],{"class":2235},[2225,112546,625],{"class":2291},[2225,112548,4228],{"class":2239},[2225,112550,1520],{"class":2235},[2225,112552,112267],{"class":2291},[2225,112554,8146],{"class":2239},[2225,112556,112557],{"class":2227,"line":7267},[2225,112558,2285],{"emptyLinePlaceholder":2284},[2225,112560,112561],{"class":2227,"line":7272},[2225,112562,112563],{"class":2763},"\u002F\u002F Part 3 — the rolling credential source, wired into a cloud store's\n",[2225,112565,112566],{"class":2227,"line":7278},[2225,112567,112568],{"class":2763},"\u002F\u002F `credentials` refresh hook. The SDK re-invokes it near expiry; each\n",[2225,112570,112571],{"class":2227,"line":7303},[2225,112572,112573],{"class":2763},"\u002F\u002F invocation runs the challenge → proof → mint round trip.\n",[2225,112575,112576,112578,112581,112583,112585,112587,112589,112591,112593,112595,112597,112599,112601,112603],{"class":2227,"line":7308},[2225,112577,2775],{"class":2774},[2225,112579,112580],{"class":2239}," source ",[2225,112582,2781],{"class":2235},[2225,112584,6466],{"class":2239},[2225,112586,1520],{"class":2235},[2225,112588,625],{"class":2291},[2225,112590,4228],{"class":2239},[2225,112592,1520],{"class":2235},[2225,112594,112003],{"class":2291},[2225,112596,2789],{"class":2239},[2225,112598,2805],{"class":2235},[2225,112600,56023],{"class":2252},[2225,112602,2805],{"class":2235},[2225,112604,2362],{"class":2239},[2225,112606,112607,112609,112612,112614,112616,112618,112620,112622,112624,112626,112629,112631,112633,112635,112637,112639,112641],{"class":2227,"line":7314},[2225,112608,2775],{"class":2774},[2225,112610,112611],{"class":2239}," cloudStore ",[2225,112613,2781],{"class":2235},[2225,112615,95763],{"class":2291},[2225,112617,2789],{"class":2239},[2225,112619,2792],{"class":2235},[2225,112621,47030],{"class":2307},[2225,112623,2311],{"class":2235},[2225,112625,2249],{"class":2235},[2225,112627,112628],{"class":2252},"acme-vault-table",[2225,112630,2805],{"class":2235},[2225,112632,2298],{"class":2235},[2225,112634,49291],{"class":2307},[2225,112636,2311],{"class":2235},[2225,112638,112580],{"class":2239},[2225,112640,2359],{"class":2235},[2225,112642,2362],{"class":2239},[937,112644,112645,112646,112649,112650,112652,112653,35026,112655,112657,112658,112660],{},"What the showcase asserts about this exact flow: the first ",[941,112647,112648],{},"source()"," call performs one challenge and one mint through the real HKDF\u002FHMAC proof chain; the minted credentials carry ",[941,112651,46971],{}," and an expiry; the store's SDK client receives ",[941,112654,48119],{},[1092,112656,12641],{}," (rolling, not baked); and a replayed challenge is rejected with 401 ",[950,112659,11357],{}," any proof verification runs (burn-then-verify). Vault records written meanwhile are ordinary encrypted envelopes — the broker changes how the store authenticates, never what it can read.",[937,112662,112663,112664,112666,112667,112669],{},"To run it yourself: ",[1007,112665,576],{"href":577},", file ",[941,112668,93828],{}," — Tier 2 of the same file drives real STS against a deployable CloudFormation stack when you set the gate variables.",[1022,112671,112673],{"id":112672},"client-surface","Client surface",[2217,112675,112677],{"className":2219,"code":112676,"language":2221,"meta":1037,"style":1037},"export interface BrokerConfig {\n  readonly brokerId: string        \u002F\u002F stable id; part of the derivation info tag AND the proof MAC.\n                                    \u002F\u002F MUST be globally-unique \u002F endpoint-derived — reusing one\n                                    \u002F\u002F brokerId across two endpoints enables a cross-endpoint relay.\n  readonly endpoint: string        \u002F\u002F https broker base URL; its origin is bound into the proof MAC\n  readonly attestation?: () => string | Promise\u003Cstring>  \u002F\u002F dev-backend session token for \u002Fenroll\n  readonly fetch?: typeof fetch    \u002F\u002F DI for tests \u002F non-window runtimes\n  readonly skewMs?: number         \u002F\u002F refresh margin, default 60_000\n}\n\nexport interface CredentialBrokerHandle {\n  enroll(): Promise\u003Cvoid>                                     \u002F\u002F generate + persist seed, register\n  rotate(): Promise\u003Cvoid>                                     \u002F\u002F rotate seed + re-register\n  credentialSource(profile?: string): StoreCredentialSource   \u002F\u002F single-flight, cached\n}\n",[941,112678,112679,112690,112704,112709,112714,112728,112755,112772,112786,112790,112794,112805,112824,112842,112863],{"__ignoreMap":1037},[2225,112680,112681,112683,112685,112688],{"class":2227,"line":2228},[2225,112682,8080],{"class":2231},[2225,112684,54003],{"class":2774},[2225,112686,112687],{"class":6342}," BrokerConfig",[2225,112689,2301],{"class":2235},[2225,112691,112692,112694,112697,112699,112701],{"class":2227,"line":1041},[2225,112693,53580],{"class":2774},[2225,112695,112696],{"class":2307}," brokerId",[2225,112698,2311],{"class":2235},[2225,112700,6366],{"class":6342},[2225,112702,112703],{"class":2763},"        \u002F\u002F stable id; part of the derivation info tag AND the proof MAC.\n",[2225,112705,112706],{"class":2227,"line":1038},[2225,112707,112708],{"class":2763},"                                    \u002F\u002F MUST be globally-unique \u002F endpoint-derived — reusing one\n",[2225,112710,112711],{"class":2227,"line":2288},[2225,112712,112713],{"class":2763},"                                    \u002F\u002F brokerId across two endpoints enables a cross-endpoint relay.\n",[2225,112715,112716,112718,112721,112723,112725],{"class":2227,"line":2304},[2225,112717,53580],{"class":2774},[2225,112719,112720],{"class":2307}," endpoint",[2225,112722,2311],{"class":2235},[2225,112724,6366],{"class":6342},[2225,112726,112727],{"class":2763},"        \u002F\u002F https broker base URL; its origin is bound into the proof MAC\n",[2225,112729,112730,112732,112734,112736,112738,112740,112742,112744,112746,112748,112750,112752],{"class":2227,"line":2356},[2225,112731,53580],{"class":2774},[2225,112733,67441],{"class":2307},[2225,112735,7011],{"class":2235},[2225,112737,13130],{"class":2235},[2225,112739,8116],{"class":2774},[2225,112741,6366],{"class":6342},[2225,112743,7087],{"class":2235},[2225,112745,7079],{"class":6342},[2225,112747,6474],{"class":2235},[2225,112749,7259],{"class":6342},[2225,112751,6480],{"class":2235},[2225,112753,112754],{"class":2763},"  \u002F\u002F dev-backend session token for \u002Fenroll\n",[2225,112756,112757,112759,112761,112763,112766,112769],{"class":2227,"line":2830},[2225,112758,53580],{"class":2774},[2225,112760,89756],{"class":2307},[2225,112762,7011],{"class":2235},[2225,112764,112765],{"class":2235}," typeof",[2225,112767,112768],{"class":2239}," fetch    ",[2225,112770,112771],{"class":2763},"\u002F\u002F DI for tests \u002F non-window runtimes\n",[2225,112773,112774,112776,112779,112781,112783],{"class":2227,"line":2861},[2225,112775,53580],{"class":2774},[2225,112777,112778],{"class":2307}," skewMs",[2225,112780,7011],{"class":2235},[2225,112782,6356],{"class":6342},[2225,112784,112785],{"class":2763},"         \u002F\u002F refresh margin, default 60_000\n",[2225,112787,112788],{"class":2227,"line":4246},[2225,112789,7351],{"class":2235},[2225,112791,112792],{"class":2227,"line":4253},[2225,112793,2285],{"emptyLinePlaceholder":2284},[2225,112795,112796,112798,112800,112803],{"class":2227,"line":4259},[2225,112797,8080],{"class":2231},[2225,112799,54003],{"class":2774},[2225,112801,112802],{"class":6342}," CredentialBrokerHandle",[2225,112804,2301],{"class":2235},[2225,112806,112807,112810,112813,112815,112817,112819,112821],{"class":2227,"line":6548},[2225,112808,112809],{"class":2307},"  enroll",[2225,112811,112812],{"class":2235},"():",[2225,112814,7079],{"class":6342},[2225,112816,6474],{"class":2235},[2225,112818,7165],{"class":6342},[2225,112820,6480],{"class":2235},[2225,112822,112823],{"class":2763},"                                     \u002F\u002F generate + persist seed, register\n",[2225,112825,112826,112829,112831,112833,112835,112837,112839],{"class":2227,"line":6576},[2225,112827,112828],{"class":2307},"  rotate",[2225,112830,112812],{"class":2235},[2225,112832,7079],{"class":6342},[2225,112834,6474],{"class":2235},[2225,112836,7165],{"class":6342},[2225,112838,6480],{"class":2235},[2225,112840,112841],{"class":2763},"                                     \u002F\u002F rotate seed + re-register\n",[2225,112843,112844,112847,112849,112851,112853,112855,112857,112860],{"class":2227,"line":6582},[2225,112845,112846],{"class":2307},"  credentialSource",[2225,112848,2789],{"class":2235},[2225,112850,19861],{"class":7052},[2225,112852,7011],{"class":2235},[2225,112854,6366],{"class":6342},[2225,112856,6255],{"class":2235},[2225,112858,112859],{"class":6342}," StoreCredentialSource",[2225,112861,112862],{"class":2763},"   \u002F\u002F single-flight, cached\n",[2225,112864,112865],{"class":2227,"line":6587},[2225,112866,7351],{"class":2235},[937,112868,112869,112871,112872,112875,112876,112878,112879,112881],{},[941,112870,21648],{}," returns the ",[941,112873,112874],{},"CredentialBrokerHandle",". Every ",[941,112877,46974],{}," produces the\nvendor-neutral ",[941,112880,53652],{}," type:",[2217,112883,112885],{"className":2219,"code":112884,"language":2221,"meta":1037,"style":1037},"export type StoreCredentials =\n  | { readonly kind: 'aws'; readonly accessKeyId: string; readonly secretAccessKey: string\n      readonly sessionToken?: string; readonly expiresAt?: string }\n  | { readonly kind: 'token'; readonly token: string; readonly expiresAt?: string } \u002F\u002F a later slice\n",[941,112886,112887,112898,112937,112962],{"__ignoreMap":1037},[2225,112888,112889,112891,112893,112896],{"class":2227,"line":2228},[2225,112890,8080],{"class":2231},[2225,112892,3399],{"class":2774},[2225,112894,112895],{"class":6342}," StoreCredentials",[2225,112897,66396],{"class":2235},[2225,112899,112900,112902,112904,112907,112909,112911,112913,112915,112917,112919,112921,112923,112925,112927,112929,112931,112933,112935],{"class":2227,"line":1041},[2225,112901,66401],{"class":2235},[2225,112903,2236],{"class":2235},[2225,112905,112906],{"class":2774}," readonly",[2225,112908,8285],{"class":2307},[2225,112910,2311],{"class":2235},[2225,112912,2249],{"class":2235},[2225,112914,49315],{"class":2252},[2225,112916,2805],{"class":2235},[2225,112918,3769],{"class":2235},[2225,112920,112906],{"class":2774},[2225,112922,49254],{"class":2307},[2225,112924,2311],{"class":2235},[2225,112926,6366],{"class":6342},[2225,112928,3769],{"class":2235},[2225,112930,112906],{"class":2774},[2225,112932,49326],{"class":2307},[2225,112934,2311],{"class":2235},[2225,112936,7014],{"class":6342},[2225,112938,112939,112942,112945,112947,112949,112951,112953,112956,112958,112960],{"class":2227,"line":1038},[2225,112940,112941],{"class":2774},"      readonly",[2225,112943,112944],{"class":2307}," sessionToken",[2225,112946,7011],{"class":2235},[2225,112948,6366],{"class":6342},[2225,112950,3769],{"class":2235},[2225,112952,112906],{"class":2774},[2225,112954,112955],{"class":2307}," expiresAt",[2225,112957,7011],{"class":2235},[2225,112959,6366],{"class":6342},[2225,112961,6369],{"class":2235},[2225,112963,112964,112966,112968,112970,112972,112974,112976,112978,112980,112982,112984,112986,112988,112990,112992,112994,112996,112998,113000,113002],{"class":2227,"line":2288},[2225,112965,66401],{"class":2235},[2225,112967,2236],{"class":2235},[2225,112969,112906],{"class":2774},[2225,112971,8285],{"class":2307},[2225,112973,2311],{"class":2235},[2225,112975,2249],{"class":2235},[2225,112977,44489],{"class":2252},[2225,112979,2805],{"class":2235},[2225,112981,3769],{"class":2235},[2225,112983,112906],{"class":2774},[2225,112985,97096],{"class":2307},[2225,112987,2311],{"class":2235},[2225,112989,6366],{"class":6342},[2225,112991,3769],{"class":2235},[2225,112993,112906],{"class":2774},[2225,112995,112955],{"class":2307},[2225,112997,7011],{"class":2235},[2225,112999,6366],{"class":6342},[2225,113001,2243],{"class":2235},[2225,113003,113004],{"class":2763}," \u002F\u002F a later slice\n",[1022,113006,113008],{"id":113007},"wiring-into-a-stores-credential-hook","Wiring into a store's credential hook",[937,113010,113011,113012,113014,113015,34689,113017,113019,113020,113023,113024,113026,113027,6255],{},"Wire the resulting ",[941,113013,112003],{}," into a store's rolling-credential hook — e.g.\n",[941,113016,29849],{},[941,113018,48119],{}," option, which feeds the AWS SDK's own ",[941,113021,113022],{},"memoizeIdentityProvider","\n(the SDK owns refresh on the ",[941,113025,46971],{}," arm, re-invoking the provider whenever the returned\nidentity carries an ",[941,113028,113029],{},"expiration: Date",[2217,113031,113033],{"className":2219,"code":113032,"language":2221,"meta":1037,"style":1037},"import { asAwsS3 } from '@noy-db\u002Fas-aws-s3'\n\nconst objects = asAwsS3({\n  bucket: 'acme-assets',\n  credentials: vault.broker().credentialSource('read'),\n})\n",[941,113034,113035,113053,113057,113071,113086,113116],{"__ignoreMap":1037},[2225,113036,113037,113039,113041,113043,113045,113047,113049,113051],{"class":2227,"line":2228},[2225,113038,2232],{"class":2231},[2225,113040,2236],{"class":2235},[2225,113042,29907],{"class":2239},[2225,113044,2243],{"class":2235},[2225,113046,2246],{"class":2231},[2225,113048,2249],{"class":2235},[2225,113050,29849],{"class":2252},[2225,113052,2256],{"class":2235},[2225,113054,113055],{"class":2227,"line":1041},[2225,113056,2285],{"emptyLinePlaceholder":2284},[2225,113058,113059,113061,113063,113065,113067,113069],{"class":2227,"line":1038},[2225,113060,2775],{"class":2774},[2225,113062,29928],{"class":2239},[2225,113064,2781],{"class":2235},[2225,113066,29907],{"class":2291},[2225,113068,2789],{"class":2239},[2225,113070,4200],{"class":2235},[2225,113072,113073,113075,113077,113079,113082,113084],{"class":2227,"line":2288},[2225,113074,69265],{"class":2307},[2225,113076,2311],{"class":2235},[2225,113078,2249],{"class":2235},[2225,113080,113081],{"class":2252},"acme-assets",[2225,113083,2805],{"class":2235},[2225,113085,1707],{"class":2235},[2225,113087,113088,113090,113092,113094,113096,113098,113100,113102,113104,113106,113108,113110,113112,113114],{"class":2227,"line":2304},[2225,113089,89741],{"class":2307},[2225,113091,2311],{"class":2235},[2225,113093,6466],{"class":2239},[2225,113095,1520],{"class":2235},[2225,113097,625],{"class":2291},[2225,113099,4228],{"class":2239},[2225,113101,1520],{"class":2235},[2225,113103,112003],{"class":2291},[2225,113105,2789],{"class":2239},[2225,113107,2805],{"class":2235},[2225,113109,56023],{"class":2252},[2225,113111,2805],{"class":2235},[2225,113113,1381],{"class":2239},[2225,113115,1707],{"class":2235},[2225,113117,113118,113120],{"class":2227,"line":2356},[2225,113119,2359],{"class":2235},[2225,113121,2362],{"class":2239},[1022,113123,101425],{"id":101424},[937,113125,113126,113127,113129,113130,17904,113133,5584,113135,968,113137,45420,113139,113141,113142,1520],{},"Every broker method on the default floor throws ",[941,113128,21778],{}," (its message points at\n",[941,113131,113132],{},"withBroker()",[941,113134,96142],{},[941,113136,111888],{},[941,113138,21654],{},[941,113140,21657],{}," all throw\u002Freject the same way. Zero bytes enter a bundle that never imports\n",[941,113143,96142],{},[1022,113145,113147],{"id":113146},"the-reference-broker-host-four-obligations","The reference broker host — four obligations",[937,113149,113150,113151,66237,113153,113155,113156,47474,113159,113162,113163,113165],{},"A conforming broker host embeds two host-side helpers re-exported from ",[941,113152,96142],{},[941,113154,112283],{}," (nonce mint, TTL-clamped to ",[941,113157,113158],{},"[10 s, 60 s]",[941,113160,113161],{},"verifyBrokerProof(args)","\n(burn-then-verify). Around them, ",[950,113164,2119],{}," conforming host must implement four obligations. Skipping\n(a) or (b) breaks the threat-model candor above; skipping (c) or (d) degrades availability, not\nconfidentiality.",[1111,113167,113168,113178,113191,113205],{},[1114,113169,113170,113173,113174,113177],{},[950,113171,113172],{},"(a) KMS-wrap registered proof keys at rest — mandated, not optional."," The host must store each\nregistered proof key wrapped under a broker-side KMS key and unwrap only in-memory at verify time.\nSymmetric HMAC verification means a read-only leak of an ",[1092,113175,113176],{},"unwrapped"," key store is\ncredential-equivalent — this is the one control that prevents that.",[1114,113179,113180,15920,113183,113186,113187,113190],{},[950,113181,113182],{},"(b) Atomic single-use challenge burn, before any MAC work.",[941,113184,113185],{},"consumeChallenge"," callback\npassed to ",[941,113188,113189],{},"verifyBrokerProof"," must atomically test-and-delete the challenge; a second presentation\nof the same challenge must fail even with a byte-identical, unexpired, otherwise-valid proof.",[1114,113192,113193,113204],{},[950,113194,113195,113196,113199,113200,113203],{},"(c) Rate-limit ",[941,113197,113198],{},"\u002Fcredentials"," per ",[941,113201,113202],{},"(vaultId, brokerId)"," (recommended)."," Bounds the multi-tab\ncredential-amplification surface. App-side hooks + consent audit remain the primary controls; this\nis a defense-in-depth backstop, not a substitute.",[1114,113206,113207,113213],{},[950,113208,113209,113210,113212],{},"(d) Accept old ",[950,113211,14128],{}," new registrations for a short grace window on rotate."," An in-flight proof\ncomputed under the pre-rotation seed (e.g. mid-sync-flush) must still verify; never drop the old\nregistration the instant a new one lands.",[937,113215,113216,113217,113220],{},"The illustrative host below is Lambda\u002FSTS-shaped — swap the KMS\u002FDynamoDB\u002FSTS clients for whatever\nyour infra uses. It issues a challenge, burns it ",[950,113218,113219],{},"once"," before any candidate-key loop, verifies\nagainst a KMS-unwrapped key, and mints via STS, hardened with obligations (a) and (c):",[2217,113222,113224],{"className":2219,"code":113223,"language":2221,"meta":1037,"style":1037},"import { issueChallenge, verifyBrokerProof } from '@noy-db\u002Fhub\u002Fbroker'\nimport { KMSClient, EncryptCommand, DecryptCommand } from '@aws-sdk\u002Fclient-kms'\nimport { STSClient, AssumeRoleCommand } from '@aws-sdk\u002Fclient-sts'\n\u002F\u002F registeredKeys \u002F challenges \u002F rateLimiter: any durable KV with an atomic\n\u002F\u002F delete-if-present (DynamoDB conditional delete, Redis DEL, etc.) works.\n\nconst kms = new KMSClient({})\nconst sts = new STSClient({})\nconst KMS_KEY_ID = process.env.BROKER_KMS_KEY_ID!            \u002F\u002F (a)\nconst keyOf = (vaultId: string, brokerId: string) => `${vaultId}:${brokerId}`\n\n\u002F\u002F ─── POST \u002Fenroll ──────────────────────────────────────────────\nexport async function handleEnroll(req: { vaultId: string; brokerId: string; proofKey: string }, attestation?: string) {\n  if (!attestation || !(await verifyDevBackendSession(attestation))) return { statusCode: 401 }\n  \u002F\u002F (a) Wrap BEFORE it ever touches durable storage. A read-only table leak is\n  \u002F\u002F otherwise credential-equivalent under symmetric HMAC.\n  const { CiphertextBlob } = await kms.send(new EncryptCommand({\n    KeyId: KMS_KEY_ID, Plaintext: Buffer.from(req.proofKey, 'base64'),\n  }))\n  \u002F\u002F (d) APPEND, never overwrite: rotate() registers new before local overwrite,\n  \u002F\u002F so this key must coexist with any prior one for a grace window.\n  await registeredKeys.append(keyOf(req.vaultId, req.brokerId), Buffer.from(CiphertextBlob!).toString('base64'))\n  return { statusCode: 200 }\n}\n\n\u002F\u002F ─── POST \u002Fchallenge ───────────────────────────────────────────\nexport async function handleChallenge() {\n  const { challenge, expiresAt } = issueChallenge()\n  await challenges.put(challenge, { expiresAt })            \u002F\u002F immutable once written\n  return { statusCode: 200, body: { challenge, expiresAt } }\n}\n\n\u002F\u002F ─── POST \u002Fcredentials ─────────────────────────────────────────\nexport async function handleCredentials(req: { vaultId: string; brokerId: string; challenge: string; proof: string; profile?: string }) {\n  if (!(await rateLimiter.allow(keyOf(req.vaultId, req.brokerId)))) return { statusCode: 429 } \u002F\u002F (c)\n\n  \u002F\u002F expiresAt is immutable per challenge once written, so reading it is safe\n  \u002F\u002F without coordination; only the delete below needs to be atomic.\n  const expiresAt = await challenges.peekExpiresAt(req.challenge)\n  const burnedFresh = await challenges.deleteIfPresent(req.challenge)  \u002F\u002F (b) burn ONCE, before any candidate loop\n  if (!burnedFresh || expiresAt === undefined) return { statusCode: 401 }\n\n  const wrappedCandidates = await registeredKeys.allFor(keyOf(req.vaultId, req.brokerId)) \u002F\u002F may hold old+new (d)\n  let ok = false\n  for (const wrapped of wrappedCandidates) {\n    const { Plaintext } = await kms.send(new DecryptCommand({ CiphertextBlob: Buffer.from(wrapped, 'base64') }))\n    ok = await verifyBrokerProof({\n      consumeChallenge: async () => true,                  \u002F\u002F already burned above — trivially fresh\n      registeredProofKey: new Uint8Array(Plaintext!),\n      vaultId: req.vaultId, endpointOrigin: 'https:\u002F\u002Fbroker.example.com', brokerId: req.brokerId,\n      profile: req.profile, challenge: req.challenge, expiresAt, proof: req.proof,\n    })\n    if (ok) break\n  }\n  if (!ok) return { statusCode: 401 }\n\n  const { Credentials } = await sts.send(new AssumeRoleCommand({\n    RoleArn: roleForProfile(req.profile),\n    RoleSessionName: `broker-${req.vaultId}`.slice(0, 64),\n    Tags: [{ Key: 'noydbVault', Value: req.vaultId }],\n    Policy: scopedSessionPolicy(req.vaultId),              \u002F\u002F dynamodb:LeadingKeys \u002F S3 prefix on the vault id\n    DurationSeconds: 3600,\n  }))\n  return {\n    statusCode: 200,\n    body: {\n      kind: 'aws', accessKeyId: Credentials!.AccessKeyId, secretAccessKey: Credentials!.SecretAccessKey,\n      sessionToken: Credentials!.SessionToken, expiresAt: Credentials!.Expiration!.toISOString(),\n    },\n  }\n}\n",[941,113225,113226,113250,113280,113305,113310,113315,113319,113338,113357,113382,113425,113429,113434,113489,113531,113536,113541,113574,113620,113626,113631,113636,113702,113716,113720,113724,113729,113744,113765,113794,113824,113828,113832,113837,113899,113957,113961,113966,113971,113998,114029,114062,114066,114109,114119,114138,114196,114211,114231,114253,114293,114337,114343,114356,114360,114384,114388,114420,114442,114481,114523,114548,114560,114566,114572,114583,114592,114633,114668,114672,114676],{"__ignoreMap":1037},[2225,113227,113228,113230,113232,113235,113237,113240,113242,113244,113246,113248],{"class":2227,"line":2228},[2225,113229,2232],{"class":2231},[2225,113231,2236],{"class":2235},[2225,113233,113234],{"class":2239}," issueChallenge",[2225,113236,2298],{"class":2235},[2225,113238,113239],{"class":2239}," verifyBrokerProof",[2225,113241,2243],{"class":2235},[2225,113243,2246],{"class":2231},[2225,113245,2249],{"class":2235},[2225,113247,96142],{"class":2252},[2225,113249,2256],{"class":2235},[2225,113251,113252,113254,113256,113259,113261,113264,113266,113269,113271,113273,113275,113278],{"class":2227,"line":1041},[2225,113253,2232],{"class":2231},[2225,113255,2236],{"class":2235},[2225,113257,113258],{"class":2239}," KMSClient",[2225,113260,2298],{"class":2235},[2225,113262,113263],{"class":2239}," EncryptCommand",[2225,113265,2298],{"class":2235},[2225,113267,113268],{"class":2239}," DecryptCommand",[2225,113270,2243],{"class":2235},[2225,113272,2246],{"class":2231},[2225,113274,2249],{"class":2235},[2225,113276,113277],{"class":2252},"@aws-sdk\u002Fclient-kms",[2225,113279,2256],{"class":2235},[2225,113281,113282,113284,113286,113289,113291,113294,113296,113298,113300,113303],{"class":2227,"line":1038},[2225,113283,2232],{"class":2231},[2225,113285,2236],{"class":2235},[2225,113287,113288],{"class":2239}," STSClient",[2225,113290,2298],{"class":2235},[2225,113292,113293],{"class":2239}," AssumeRoleCommand",[2225,113295,2243],{"class":2235},[2225,113297,2246],{"class":2231},[2225,113299,2249],{"class":2235},[2225,113301,113302],{"class":2252},"@aws-sdk\u002Fclient-sts",[2225,113304,2256],{"class":2235},[2225,113306,113307],{"class":2227,"line":2288},[2225,113308,113309],{"class":2763},"\u002F\u002F registeredKeys \u002F challenges \u002F rateLimiter: any durable KV with an atomic\n",[2225,113311,113312],{"class":2227,"line":2304},[2225,113313,113314],{"class":2763},"\u002F\u002F delete-if-present (DynamoDB conditional delete, Redis DEL, etc.) works.\n",[2225,113316,113317],{"class":2227,"line":2356},[2225,113318,2285],{"emptyLinePlaceholder":2284},[2225,113320,113321,113323,113326,113328,113330,113332,113334,113336],{"class":2227,"line":2830},[2225,113322,2775],{"class":2774},[2225,113324,113325],{"class":2239}," kms ",[2225,113327,2781],{"class":2235},[2225,113329,8130],{"class":2235},[2225,113331,113258],{"class":2291},[2225,113333,2789],{"class":2239},[2225,113335,9261],{"class":2235},[2225,113337,2362],{"class":2239},[2225,113339,113340,113342,113345,113347,113349,113351,113353,113355],{"class":2227,"line":2861},[2225,113341,2775],{"class":2774},[2225,113343,113344],{"class":2239}," sts ",[2225,113346,2781],{"class":2235},[2225,113348,8130],{"class":2235},[2225,113350,113288],{"class":2291},[2225,113352,2789],{"class":2239},[2225,113354,9261],{"class":2235},[2225,113356,2362],{"class":2239},[2225,113358,113359,113361,113364,113366,113368,113370,113372,113374,113377,113379],{"class":2227,"line":4246},[2225,113360,2775],{"class":2774},[2225,113362,113363],{"class":2239}," KMS_KEY_ID ",[2225,113365,2781],{"class":2235},[2225,113367,36593],{"class":2239},[2225,113369,1520],{"class":2235},[2225,113371,183],{"class":2239},[2225,113373,1520],{"class":2235},[2225,113375,113376],{"class":2239},"BROKER_KMS_KEY_ID",[2225,113378,35134],{"class":2235},[2225,113380,113381],{"class":2763},"            \u002F\u002F (a)\n",[2225,113383,113384,113386,113389,113391,113393,113395,113397,113399,113401,113403,113405,113407,113409,113411,113413,113415,113417,113419,113421,113423],{"class":2227,"line":4253},[2225,113385,2775],{"class":2774},[2225,113387,113388],{"class":2239}," keyOf ",[2225,113390,2781],{"class":2235},[2225,113392,953],{"class":2235},[2225,113394,111864],{"class":7052},[2225,113396,2311],{"class":2235},[2225,113398,6366],{"class":6342},[2225,113400,2298],{"class":2235},[2225,113402,112696],{"class":7052},[2225,113404,2311],{"class":2235},[2225,113406,6366],{"class":6342},[2225,113408,1381],{"class":2235},[2225,113410,8116],{"class":2774},[2225,113412,8188],{"class":2235},[2225,113414,111864],{"class":2239},[2225,113416,2359],{"class":2235},[2225,113418,2311],{"class":2252},[2225,113420,8197],{"class":2235},[2225,113422,111867],{"class":2239},[2225,113424,8211],{"class":2235},[2225,113426,113427],{"class":2227,"line":4259},[2225,113428,2285],{"emptyLinePlaceholder":2284},[2225,113430,113431],{"class":2227,"line":6548},[2225,113432,113433],{"class":2763},"\u002F\u002F ─── POST \u002Fenroll ──────────────────────────────────────────────\n",[2225,113435,113436,113438,113440,113442,113445,113447,113449,113451,113453,113456,113458,113460,113462,113464,113466,113468,113470,113473,113475,113477,113479,113481,113483,113485,113487],{"class":2227,"line":6576},[2225,113437,8080],{"class":2231},[2225,113439,38717],{"class":2774},[2225,113441,40856],{"class":2774},[2225,113443,113444],{"class":2291}," handleEnroll",[2225,113446,2789],{"class":2235},[2225,113448,40861],{"class":7052},[2225,113450,2311],{"class":2235},[2225,113452,2236],{"class":2235},[2225,113454,113455],{"class":2307}," vaultId",[2225,113457,2311],{"class":2235},[2225,113459,6366],{"class":6342},[2225,113461,3769],{"class":2235},[2225,113463,112696],{"class":2307},[2225,113465,2311],{"class":2235},[2225,113467,6366],{"class":6342},[2225,113469,3769],{"class":2235},[2225,113471,113472],{"class":2307}," proofKey",[2225,113474,2311],{"class":2235},[2225,113476,6366],{"class":6342},[2225,113478,10965],{"class":2235},[2225,113480,67441],{"class":7052},[2225,113482,7011],{"class":2235},[2225,113484,6366],{"class":6342},[2225,113486,1381],{"class":2235},[2225,113488,2301],{"class":2235},[2225,113490,113491,113493,113495,113497,113499,113502,113504,113506,113508,113511,113513,113515,113518,113520,113522,113525,113527,113529],{"class":2227,"line":6582},[2225,113492,7608],{"class":2231},[2225,113494,953],{"class":2307},[2225,113496,35134],{"class":2235},[2225,113498,609],{"class":2239},[2225,113500,113501],{"class":2235}," ||",[2225,113503,9032],{"class":2235},[2225,113505,2789],{"class":2307},[2225,113507,6500],{"class":2231},[2225,113509,113510],{"class":2291}," verifyDevBackendSession",[2225,113512,2789],{"class":2307},[2225,113514,609],{"class":2239},[2225,113516,113517],{"class":2307},"))) ",[2225,113519,40426],{"class":2231},[2225,113521,2236],{"class":2235},[2225,113523,113524],{"class":2307}," statusCode",[2225,113526,2311],{"class":2235},[2225,113528,85521],{"class":6527},[2225,113530,6369],{"class":2235},[2225,113532,113533],{"class":2227,"line":6587},[2225,113534,113535],{"class":2763},"  \u002F\u002F (a) Wrap BEFORE it ever touches durable storage. A read-only table leak is\n",[2225,113537,113538],{"class":2227,"line":7174},[2225,113539,113540],{"class":2763},"  \u002F\u002F otherwise credential-equivalent under symmetric HMAC.\n",[2225,113542,113543,113545,113547,113550,113552,113554,113556,113559,113561,113564,113566,113568,113570,113572],{"class":2227,"line":7180},[2225,113544,8123],{"class":2774},[2225,113546,2236],{"class":2235},[2225,113548,113549],{"class":2239}," CiphertextBlob",[2225,113551,2243],{"class":2235},[2225,113553,6346],{"class":2235},[2225,113555,2784],{"class":2231},[2225,113557,113558],{"class":2239}," kms",[2225,113560,1520],{"class":2235},[2225,113562,113563],{"class":2291},"send",[2225,113565,2789],{"class":2307},[2225,113567,70371],{"class":2235},[2225,113569,113263],{"class":2291},[2225,113571,2789],{"class":2307},[2225,113573,4200],{"class":2235},[2225,113575,113576,113579,113581,113584,113586,113589,113591,113594,113596,113598,113600,113602,113604,113607,113609,113611,113614,113616,113618],{"class":2227,"line":7220},[2225,113577,113578],{"class":2307},"    KeyId",[2225,113580,2311],{"class":2235},[2225,113582,113583],{"class":2239}," KMS_KEY_ID",[2225,113585,2298],{"class":2235},[2225,113587,113588],{"class":2307}," Plaintext",[2225,113590,2311],{"class":2235},[2225,113592,113593],{"class":2239}," Buffer",[2225,113595,1520],{"class":2235},[2225,113597,2272],{"class":2291},[2225,113599,2789],{"class":2307},[2225,113601,40861],{"class":2239},[2225,113603,1520],{"class":2235},[2225,113605,113606],{"class":2239},"proofKey",[2225,113608,2298],{"class":2235},[2225,113610,2249],{"class":2235},[2225,113612,113613],{"class":2252},"base64",[2225,113615,2805],{"class":2235},[2225,113617,1381],{"class":2307},[2225,113619,1707],{"class":2235},[2225,113621,113622,113624],{"class":2227,"line":7225},[2225,113623,19143],{"class":2235},[2225,113625,8606],{"class":2307},[2225,113627,113628],{"class":2227,"line":7231},[2225,113629,113630],{"class":2763},"  \u002F\u002F (d) APPEND, never overwrite: rotate() registers new before local overwrite,\n",[2225,113632,113633],{"class":2227,"line":7267},[2225,113634,113635],{"class":2763},"  \u002F\u002F so this key must coexist with any prior one for a grace window.\n",[2225,113637,113638,113640,113643,113645,113648,113650,113653,113655,113657,113659,113661,113663,113665,113667,113669,113671,113673,113675,113677,113679,113681,113684,113686,113688,113690,113692,113694,113696,113698,113700],{"class":2227,"line":7272},[2225,113639,7561],{"class":2231},[2225,113641,113642],{"class":2239}," registeredKeys",[2225,113644,1520],{"class":2235},[2225,113646,113647],{"class":2291},"append",[2225,113649,2789],{"class":2307},[2225,113651,113652],{"class":2291},"keyOf",[2225,113654,2789],{"class":2307},[2225,113656,40861],{"class":2239},[2225,113658,1520],{"class":2235},[2225,113660,111864],{"class":2239},[2225,113662,2298],{"class":2235},[2225,113664,40893],{"class":2239},[2225,113666,1520],{"class":2235},[2225,113668,111867],{"class":2239},[2225,113670,1381],{"class":2307},[2225,113672,2298],{"class":2235},[2225,113674,113593],{"class":2239},[2225,113676,1520],{"class":2235},[2225,113678,2272],{"class":2291},[2225,113680,2789],{"class":2307},[2225,113682,113683],{"class":2239},"CiphertextBlob",[2225,113685,35134],{"class":2235},[2225,113687,1381],{"class":2307},[2225,113689,1520],{"class":2235},[2225,113691,80541],{"class":2291},[2225,113693,2789],{"class":2307},[2225,113695,2805],{"class":2235},[2225,113697,113613],{"class":2252},[2225,113699,2805],{"class":2235},[2225,113701,8606],{"class":2307},[2225,113703,113704,113706,113708,113710,113712,113714],{"class":2227,"line":7278},[2225,113705,8220],{"class":2231},[2225,113707,2236],{"class":2235},[2225,113709,113524],{"class":2307},[2225,113711,2311],{"class":2235},[2225,113713,40956],{"class":6527},[2225,113715,6369],{"class":2235},[2225,113717,113718],{"class":2227,"line":7303},[2225,113719,7351],{"class":2235},[2225,113721,113722],{"class":2227,"line":7308},[2225,113723,2285],{"emptyLinePlaceholder":2284},[2225,113725,113726],{"class":2227,"line":7314},[2225,113727,113728],{"class":2763},"\u002F\u002F ─── POST \u002Fchallenge ───────────────────────────────────────────\n",[2225,113730,113731,113733,113735,113737,113740,113742],{"class":2227,"line":7348},[2225,113732,8080],{"class":2231},[2225,113734,38717],{"class":2774},[2225,113736,40856],{"class":2774},[2225,113738,113739],{"class":2291}," handleChallenge",[2225,113741,4228],{"class":2235},[2225,113743,2301],{"class":2235},[2225,113745,113746,113748,113750,113753,113755,113757,113759,113761,113763],{"class":2227,"line":8613},[2225,113747,8123],{"class":2774},[2225,113749,2236],{"class":2235},[2225,113751,113752],{"class":2239}," challenge",[2225,113754,2298],{"class":2235},[2225,113756,112955],{"class":2239},[2225,113758,2243],{"class":2235},[2225,113760,6346],{"class":2235},[2225,113762,113234],{"class":2291},[2225,113764,8146],{"class":2307},[2225,113766,113767,113769,113772,113774,113776,113778,113781,113783,113785,113787,113789,113791],{"class":2227,"line":8633},[2225,113768,7561],{"class":2231},[2225,113770,113771],{"class":2239}," challenges",[2225,113773,1520],{"class":2235},[2225,113775,3161],{"class":2291},[2225,113777,2789],{"class":2307},[2225,113779,113780],{"class":2239},"challenge",[2225,113782,2298],{"class":2235},[2225,113784,2236],{"class":2235},[2225,113786,112955],{"class":2239},[2225,113788,2243],{"class":2235},[2225,113790,76120],{"class":2307},[2225,113792,113793],{"class":2763},"\u002F\u002F immutable once written\n",[2225,113795,113796,113798,113800,113802,113804,113806,113808,113810,113812,113814,113816,113818,113820,113822],{"class":2227,"line":8662},[2225,113797,8220],{"class":2231},[2225,113799,2236],{"class":2235},[2225,113801,113524],{"class":2307},[2225,113803,2311],{"class":2235},[2225,113805,40956],{"class":6527},[2225,113807,2298],{"class":2235},[2225,113809,42791],{"class":2307},[2225,113811,2311],{"class":2235},[2225,113813,2236],{"class":2235},[2225,113815,113752],{"class":2239},[2225,113817,2298],{"class":2235},[2225,113819,112955],{"class":2239},[2225,113821,2243],{"class":2235},[2225,113823,6369],{"class":2235},[2225,113825,113826],{"class":2227,"line":8747},[2225,113827,7351],{"class":2235},[2225,113829,113830],{"class":2227,"line":8752},[2225,113831,2285],{"emptyLinePlaceholder":2284},[2225,113833,113834],{"class":2227,"line":8768},[2225,113835,113836],{"class":2763},"\u002F\u002F ─── POST \u002Fcredentials ─────────────────────────────────────────\n",[2225,113838,113839,113841,113843,113845,113848,113850,113852,113854,113856,113858,113860,113862,113864,113866,113868,113870,113872,113874,113876,113878,113880,113883,113885,113887,113889,113891,113893,113895,113897],{"class":2227,"line":8785},[2225,113840,8080],{"class":2231},[2225,113842,38717],{"class":2774},[2225,113844,40856],{"class":2774},[2225,113846,113847],{"class":2291}," handleCredentials",[2225,113849,2789],{"class":2235},[2225,113851,40861],{"class":7052},[2225,113853,2311],{"class":2235},[2225,113855,2236],{"class":2235},[2225,113857,113455],{"class":2307},[2225,113859,2311],{"class":2235},[2225,113861,6366],{"class":6342},[2225,113863,3769],{"class":2235},[2225,113865,112696],{"class":2307},[2225,113867,2311],{"class":2235},[2225,113869,6366],{"class":6342},[2225,113871,3769],{"class":2235},[2225,113873,113752],{"class":2307},[2225,113875,2311],{"class":2235},[2225,113877,6366],{"class":6342},[2225,113879,3769],{"class":2235},[2225,113881,113882],{"class":2307}," proof",[2225,113884,2311],{"class":2235},[2225,113886,6366],{"class":6342},[2225,113888,3769],{"class":2235},[2225,113890,72016],{"class":2307},[2225,113892,7011],{"class":2235},[2225,113894,6366],{"class":6342},[2225,113896,43776],{"class":2235},[2225,113898,2301],{"class":2235},[2225,113900,113901,113903,113905,113907,113909,113911,113914,113916,113918,113920,113922,113924,113926,113928,113930,113932,113934,113936,113938,113941,113943,113945,113947,113949,113952,113954],{"class":2227,"line":8815},[2225,113902,7608],{"class":2231},[2225,113904,953],{"class":2307},[2225,113906,35134],{"class":2235},[2225,113908,2789],{"class":2307},[2225,113910,6500],{"class":2231},[2225,113912,113913],{"class":2239}," rateLimiter",[2225,113915,1520],{"class":2235},[2225,113917,34720],{"class":2291},[2225,113919,2789],{"class":2307},[2225,113921,113652],{"class":2291},[2225,113923,2789],{"class":2307},[2225,113925,40861],{"class":2239},[2225,113927,1520],{"class":2235},[2225,113929,111864],{"class":2239},[2225,113931,2298],{"class":2235},[2225,113933,40893],{"class":2239},[2225,113935,1520],{"class":2235},[2225,113937,111867],{"class":2239},[2225,113939,113940],{"class":2307},")))) ",[2225,113942,40426],{"class":2231},[2225,113944,2236],{"class":2235},[2225,113946,113524],{"class":2307},[2225,113948,2311],{"class":2235},[2225,113950,113951],{"class":6527}," 429",[2225,113953,2243],{"class":2235},[2225,113955,113956],{"class":2763}," \u002F\u002F (c)\n",[2225,113958,113959],{"class":2227,"line":8854},[2225,113960,2285],{"emptyLinePlaceholder":2284},[2225,113962,113963],{"class":2227,"line":8874},[2225,113964,113965],{"class":2763},"  \u002F\u002F expiresAt is immutable per challenge once written, so reading it is safe\n",[2225,113967,113968],{"class":2227,"line":8911},[2225,113969,113970],{"class":2763},"  \u002F\u002F without coordination; only the delete below needs to be atomic.\n",[2225,113972,113973,113975,113977,113979,113981,113983,113985,113988,113990,113992,113994,113996],{"class":2227,"line":8916},[2225,113974,8123],{"class":2774},[2225,113976,112955],{"class":2239},[2225,113978,6346],{"class":2235},[2225,113980,2784],{"class":2231},[2225,113982,113771],{"class":2239},[2225,113984,1520],{"class":2235},[2225,113986,113987],{"class":2291},"peekExpiresAt",[2225,113989,2789],{"class":2307},[2225,113991,40861],{"class":2239},[2225,113993,1520],{"class":2235},[2225,113995,113780],{"class":2239},[2225,113997,2362],{"class":2307},[2225,113999,114000,114002,114005,114007,114009,114011,114013,114016,114018,114020,114022,114024,114026],{"class":2227,"line":8924},[2225,114001,8123],{"class":2774},[2225,114003,114004],{"class":2239}," burnedFresh",[2225,114006,6346],{"class":2235},[2225,114008,2784],{"class":2231},[2225,114010,113771],{"class":2239},[2225,114012,1520],{"class":2235},[2225,114014,114015],{"class":2291},"deleteIfPresent",[2225,114017,2789],{"class":2307},[2225,114019,40861],{"class":2239},[2225,114021,1520],{"class":2235},[2225,114023,113780],{"class":2239},[2225,114025,27226],{"class":2307},[2225,114027,114028],{"class":2763},"\u002F\u002F (b) burn ONCE, before any candidate loop\n",[2225,114030,114031,114033,114035,114037,114040,114042,114044,114046,114048,114050,114052,114054,114056,114058,114060],{"class":2227,"line":8929},[2225,114032,7608],{"class":2231},[2225,114034,953],{"class":2307},[2225,114036,35134],{"class":2235},[2225,114038,114039],{"class":2239},"burnedFresh",[2225,114041,113501],{"class":2235},[2225,114043,112955],{"class":2239},[2225,114045,9025],{"class":2235},[2225,114047,8483],{"class":2235},[2225,114049,8495],{"class":2307},[2225,114051,40426],{"class":2231},[2225,114053,2236],{"class":2235},[2225,114055,113524],{"class":2307},[2225,114057,2311],{"class":2235},[2225,114059,85521],{"class":6527},[2225,114061,6369],{"class":2235},[2225,114063,114064],{"class":2227,"line":8949},[2225,114065,2285],{"emptyLinePlaceholder":2284},[2225,114067,114068,114070,114073,114075,114077,114079,114081,114084,114086,114088,114090,114092,114094,114096,114098,114100,114102,114104,114106],{"class":2227,"line":8955},[2225,114069,8123],{"class":2774},[2225,114071,114072],{"class":2239}," wrappedCandidates",[2225,114074,6346],{"class":2235},[2225,114076,2784],{"class":2231},[2225,114078,113642],{"class":2239},[2225,114080,1520],{"class":2235},[2225,114082,114083],{"class":2291},"allFor",[2225,114085,2789],{"class":2307},[2225,114087,113652],{"class":2291},[2225,114089,2789],{"class":2307},[2225,114091,40861],{"class":2239},[2225,114093,1520],{"class":2235},[2225,114095,111864],{"class":2239},[2225,114097,2298],{"class":2235},[2225,114099,40893],{"class":2239},[2225,114101,1520],{"class":2235},[2225,114103,111867],{"class":2239},[2225,114105,7620],{"class":2307},[2225,114107,114108],{"class":2763},"\u002F\u002F may hold old+new (d)\n",[2225,114110,114111,114113,114115,114117],{"class":2227,"line":8983},[2225,114112,66632],{"class":2774},[2225,114114,85637],{"class":2239},[2225,114116,6346],{"class":2235},[2225,114118,70962],{"class":2321},[2225,114120,114121,114123,114125,114127,114130,114132,114134,114136],{"class":2227,"line":9016},[2225,114122,80016],{"class":2231},[2225,114124,953],{"class":2307},[2225,114126,2775],{"class":2774},[2225,114128,114129],{"class":2239}," wrapped",[2225,114131,8806],{"class":2235},[2225,114133,114072],{"class":2239},[2225,114135,8495],{"class":2307},[2225,114137,4200],{"class":2235},[2225,114139,114140,114142,114144,114146,114148,114150,114152,114154,114156,114158,114160,114162,114164,114166,114168,114170,114172,114174,114176,114178,114180,114182,114184,114186,114188,114190,114192,114194],{"class":2227,"line":9063},[2225,114141,80048],{"class":2774},[2225,114143,2236],{"class":2235},[2225,114145,113588],{"class":2239},[2225,114147,2243],{"class":2235},[2225,114149,6346],{"class":2235},[2225,114151,2784],{"class":2231},[2225,114153,113558],{"class":2239},[2225,114155,1520],{"class":2235},[2225,114157,113563],{"class":2291},[2225,114159,2789],{"class":2307},[2225,114161,70371],{"class":2235},[2225,114163,113268],{"class":2291},[2225,114165,2789],{"class":2307},[2225,114167,2792],{"class":2235},[2225,114169,113549],{"class":2307},[2225,114171,2311],{"class":2235},[2225,114173,113593],{"class":2239},[2225,114175,1520],{"class":2235},[2225,114177,2272],{"class":2291},[2225,114179,2789],{"class":2307},[2225,114181,9875],{"class":2239},[2225,114183,2298],{"class":2235},[2225,114185,2249],{"class":2235},[2225,114187,113613],{"class":2252},[2225,114189,2805],{"class":2235},[2225,114191,8495],{"class":2307},[2225,114193,2359],{"class":2235},[2225,114195,8606],{"class":2307},[2225,114197,114198,114201,114203,114205,114207,114209],{"class":2227,"line":9068},[2225,114199,114200],{"class":2239},"    ok",[2225,114202,6346],{"class":2235},[2225,114204,2784],{"class":2231},[2225,114206,113239],{"class":2291},[2225,114208,2789],{"class":2307},[2225,114210,4200],{"class":2235},[2225,114212,114213,114216,114218,114220,114222,114224,114226,114228],{"class":2227,"line":9106},[2225,114214,114215],{"class":2291},"      consumeChallenge",[2225,114217,2311],{"class":2235},[2225,114219,38717],{"class":2774},[2225,114221,13130],{"class":2235},[2225,114223,8116],{"class":2774},[2225,114225,2322],{"class":2321},[2225,114227,2298],{"class":2235},[2225,114229,114230],{"class":2763},"                  \u002F\u002F already burned above — trivially fresh\n",[2225,114232,114233,114236,114238,114240,114242,114244,114247,114249,114251],{"class":2227,"line":9143},[2225,114234,114235],{"class":2307},"      registeredProofKey",[2225,114237,2311],{"class":2235},[2225,114239,8130],{"class":2235},[2225,114241,60935],{"class":2291},[2225,114243,2789],{"class":2307},[2225,114245,114246],{"class":2239},"Plaintext",[2225,114248,35134],{"class":2235},[2225,114250,1381],{"class":2307},[2225,114252,1707],{"class":2235},[2225,114254,114255,114258,114260,114262,114264,114266,114268,114271,114273,114275,114277,114279,114281,114283,114285,114287,114289,114291],{"class":2227,"line":9177},[2225,114256,114257],{"class":2307},"      vaultId",[2225,114259,2311],{"class":2235},[2225,114261,40893],{"class":2239},[2225,114263,1520],{"class":2235},[2225,114265,111864],{"class":2239},[2225,114267,2298],{"class":2235},[2225,114269,114270],{"class":2307}," endpointOrigin",[2225,114272,2311],{"class":2235},[2225,114274,2249],{"class":2235},[2225,114276,112180],{"class":2252},[2225,114278,2805],{"class":2235},[2225,114280,2298],{"class":2235},[2225,114282,112696],{"class":2307},[2225,114284,2311],{"class":2235},[2225,114286,40893],{"class":2239},[2225,114288,1520],{"class":2235},[2225,114290,111867],{"class":2239},[2225,114292,1707],{"class":2235},[2225,114294,114295,114298,114300,114302,114304,114306,114308,114310,114312,114314,114316,114318,114320,114322,114324,114326,114328,114330,114332,114335],{"class":2227,"line":9183},[2225,114296,114297],{"class":2307},"      profile",[2225,114299,2311],{"class":2235},[2225,114301,40893],{"class":2239},[2225,114303,1520],{"class":2235},[2225,114305,19861],{"class":2239},[2225,114307,2298],{"class":2235},[2225,114309,113752],{"class":2307},[2225,114311,2311],{"class":2235},[2225,114313,40893],{"class":2239},[2225,114315,1520],{"class":2235},[2225,114317,113780],{"class":2239},[2225,114319,2298],{"class":2235},[2225,114321,112955],{"class":2239},[2225,114323,2298],{"class":2235},[2225,114325,113882],{"class":2307},[2225,114327,2311],{"class":2235},[2225,114329,40893],{"class":2239},[2225,114331,1520],{"class":2235},[2225,114333,114334],{"class":2239},"proof",[2225,114336,1707],{"class":2235},[2225,114338,114339,114341],{"class":2227,"line":9188},[2225,114340,80146],{"class":2235},[2225,114342,2362],{"class":2307},[2225,114344,114345,114347,114349,114351,114353],{"class":2227,"line":9193},[2225,114346,80758],{"class":2231},[2225,114348,953],{"class":2307},[2225,114350,40950],{"class":2239},[2225,114352,8495],{"class":2307},[2225,114354,114355],{"class":2231},"break\n",[2225,114357,114358],{"class":2227,"line":9198},[2225,114359,7632],{"class":2235},[2225,114361,114362,114364,114366,114368,114370,114372,114374,114376,114378,114380,114382],{"class":2227,"line":9205},[2225,114363,7608],{"class":2231},[2225,114365,953],{"class":2307},[2225,114367,35134],{"class":2235},[2225,114369,40950],{"class":2239},[2225,114371,8495],{"class":2307},[2225,114373,40426],{"class":2231},[2225,114375,2236],{"class":2235},[2225,114377,113524],{"class":2307},[2225,114379,2311],{"class":2235},[2225,114381,85521],{"class":6527},[2225,114383,6369],{"class":2235},[2225,114385,114386],{"class":2227,"line":9210},[2225,114387,2285],{"emptyLinePlaceholder":2284},[2225,114389,114390,114392,114394,114397,114399,114401,114403,114406,114408,114410,114412,114414,114416,114418],{"class":2227,"line":9270},[2225,114391,8123],{"class":2774},[2225,114393,2236],{"class":2235},[2225,114395,114396],{"class":2239}," Credentials",[2225,114398,2243],{"class":2235},[2225,114400,6346],{"class":2235},[2225,114402,2784],{"class":2231},[2225,114404,114405],{"class":2239}," sts",[2225,114407,1520],{"class":2235},[2225,114409,113563],{"class":2291},[2225,114411,2789],{"class":2307},[2225,114413,70371],{"class":2235},[2225,114415,113293],{"class":2291},[2225,114417,2789],{"class":2307},[2225,114419,4200],{"class":2235},[2225,114421,114422,114425,114427,114430,114432,114434,114436,114438,114440],{"class":2227,"line":9297},[2225,114423,114424],{"class":2307},"    RoleArn",[2225,114426,2311],{"class":2235},[2225,114428,114429],{"class":2291}," roleForProfile",[2225,114431,2789],{"class":2307},[2225,114433,40861],{"class":2239},[2225,114435,1520],{"class":2235},[2225,114437,19861],{"class":2239},[2225,114439,1381],{"class":2307},[2225,114441,1707],{"class":2235},[2225,114443,114444,114447,114449,114451,114454,114456,114458,114460,114462,114464,114466,114468,114470,114472,114474,114477,114479],{"class":2227,"line":67030},[2225,114445,114446],{"class":2307},"    RoleSessionName",[2225,114448,2311],{"class":2235},[2225,114450,43789],{"class":2235},[2225,114452,114453],{"class":2252},"broker-",[2225,114455,8197],{"class":2235},[2225,114457,40861],{"class":2239},[2225,114459,1520],{"class":2235},[2225,114461,111864],{"class":2239},[2225,114463,43799],{"class":2235},[2225,114465,1520],{"class":2235},[2225,114467,8733],{"class":2291},[2225,114469,2789],{"class":2307},[2225,114471,5402],{"class":6527},[2225,114473,2298],{"class":2235},[2225,114475,114476],{"class":6527}," 64",[2225,114478,1381],{"class":2307},[2225,114480,1707],{"class":2235},[2225,114482,114483,114486,114488,114490,114492,114495,114497,114499,114502,114504,114506,114509,114511,114513,114515,114517,114519,114521],{"class":2227,"line":67035},[2225,114484,114485],{"class":2307},"    Tags",[2225,114487,2311],{"class":2235},[2225,114489,8667],{"class":2307},[2225,114491,2792],{"class":2235},[2225,114493,114494],{"class":2307}," Key",[2225,114496,2311],{"class":2235},[2225,114498,2249],{"class":2235},[2225,114500,114501],{"class":2252},"noydbVault",[2225,114503,2805],{"class":2235},[2225,114505,2298],{"class":2235},[2225,114507,114508],{"class":2307}," Value",[2225,114510,2311],{"class":2235},[2225,114512,40893],{"class":2239},[2225,114514,1520],{"class":2235},[2225,114516,111864],{"class":2239},[2225,114518,2243],{"class":2235},[2225,114520,8803],{"class":2307},[2225,114522,1707],{"class":2235},[2225,114524,114525,114528,114530,114533,114535,114537,114539,114541,114543,114545],{"class":2227,"line":67097},[2225,114526,114527],{"class":2307},"    Policy",[2225,114529,2311],{"class":2235},[2225,114531,114532],{"class":2291}," scopedSessionPolicy",[2225,114534,2789],{"class":2307},[2225,114536,40861],{"class":2239},[2225,114538,1520],{"class":2235},[2225,114540,111864],{"class":2239},[2225,114542,1381],{"class":2307},[2225,114544,2298],{"class":2235},[2225,114546,114547],{"class":2763},"              \u002F\u002F dynamodb:LeadingKeys \u002F S3 prefix on the vault id\n",[2225,114549,114550,114553,114555,114558],{"class":2227,"line":67143},[2225,114551,114552],{"class":2307},"    DurationSeconds",[2225,114554,2311],{"class":2235},[2225,114556,114557],{"class":6527}," 3600",[2225,114559,1707],{"class":2235},[2225,114561,114562,114564],{"class":2227,"line":67148},[2225,114563,19143],{"class":2235},[2225,114565,8606],{"class":2307},[2225,114567,114568,114570],{"class":2227,"line":67159},[2225,114569,8220],{"class":2231},[2225,114571,2301],{"class":2235},[2225,114573,114574,114577,114579,114581],{"class":2227,"line":67188},[2225,114575,114576],{"class":2307},"    statusCode",[2225,114578,2311],{"class":2235},[2225,114580,40956],{"class":6527},[2225,114582,1707],{"class":2235},[2225,114584,114585,114588,114590],{"class":2227,"line":67215},[2225,114586,114587],{"class":2307},"    body",[2225,114589,2311],{"class":2235},[2225,114591,2301],{"class":2235},[2225,114593,114594,114597,114599,114601,114603,114605,114607,114609,114611,114613,114615,114618,114620,114622,114624,114626,114628,114631],{"class":2227,"line":67247},[2225,114595,114596],{"class":2307},"      kind",[2225,114598,2311],{"class":2235},[2225,114600,2249],{"class":2235},[2225,114602,49315],{"class":2252},[2225,114604,2805],{"class":2235},[2225,114606,2298],{"class":2235},[2225,114608,49254],{"class":2307},[2225,114610,2311],{"class":2235},[2225,114612,114396],{"class":2239},[2225,114614,83002],{"class":2235},[2225,114616,114617],{"class":2239},"AccessKeyId",[2225,114619,2298],{"class":2235},[2225,114621,49326],{"class":2307},[2225,114623,2311],{"class":2235},[2225,114625,114396],{"class":2239},[2225,114627,83002],{"class":2235},[2225,114629,114630],{"class":2239},"SecretAccessKey",[2225,114632,1707],{"class":2235},[2225,114634,114635,114638,114640,114642,114644,114647,114649,114651,114653,114655,114657,114660,114662,114664,114666],{"class":2227,"line":67262},[2225,114636,114637],{"class":2307},"      sessionToken",[2225,114639,2311],{"class":2235},[2225,114641,114396],{"class":2239},[2225,114643,83002],{"class":2235},[2225,114645,114646],{"class":2239},"SessionToken",[2225,114648,2298],{"class":2235},[2225,114650,112955],{"class":2307},[2225,114652,2311],{"class":2235},[2225,114654,114396],{"class":2239},[2225,114656,83002],{"class":2235},[2225,114658,114659],{"class":2239},"Expiration",[2225,114661,83002],{"class":2235},[2225,114663,19129],{"class":2291},[2225,114665,4228],{"class":2307},[2225,114667,1707],{"class":2235},[2225,114669,114670],{"class":2227,"line":67267},[2225,114671,8324],{"class":2235},[2225,114673,114674],{"class":2227,"line":67310},[2225,114675,7632],{"class":2235},[2225,114677,114679],{"class":2227,"line":114678},71,[2225,114680,7351],{"class":2235},[1022,114682,114684],{"id":114683},"pairs-well-with","Pairs well with",[1111,114686,114687,114707],{},[1114,114688,114689,114693,114694,1625,114696,114698,114699,1625,114701,114703,114704,114706],{},[1007,114690,114691],{"href":869},[941,114692,868],{}," — role gating (",[941,114695,9898],{},[941,114697,9901],{}," only for ",[941,114700,112267],{},[941,114702,10859],{},") reuses the\nsame keyring roles ",[941,114705,3197],{}," grants.",[1114,114708,114709,114713,114714,114716,114717,114719],{},[1007,114710,114711],{"href":841},[941,114712,840],{}," — a broker-fronted app typically also gates ",[1092,114715,41270],{}," sessions with\n",[941,114718,21321],{},"; the two are independent (broker gates cloud-store access, session gates\napp-level tokens).",[1022,114721,6825],{"id":6824},[1111,114723,114724,114741,114751,114768,114789,114799],{},[1114,114725,114726,1324,114731,114734,114735,114737,114738,114740],{},[950,114727,114728,114730],{},[941,114729,15926],{}," is not a normal collection.",[941,114732,114733],{},"vault.collection('_broker')"," throws a\nreserved-collection error — the seed is reachable only through ",[941,114736,21648],{},". A granted\nsub-admin keyring (operator, viewer, client) never receives the ",[941,114739,15926],{}," DEK, so it cannot\ndecrypt the seed even with API access to everything else; only owner\u002Fadmin grantees do.",[1114,114742,114743,114746,114747,114750],{},[950,114744,114745],{},"Enrol vs. use."," First-ever seed creation needs the vault's KEK (throws ",[941,114748,114749],{},"BrokerEnrolmentError"," —\n\"re-authenticate\" — on a DEK-only keyring, e.g. mid PIN quick-resume). Using an already-enrolled\nseed to mint credentials needs only the DEK.",[1114,114752,114753,114756,114757,40603,114760,114763,114764,114767],{},[950,114754,114755],{},"Offline \u002F broker-down."," A network failure or an unreachable host surfaces ",[941,114758,114759],{},"NetworkError",[941,114761,114762],{},"credentialSource()","; a rejected proof surfaces ",[941,114765,114766],{},"BrokerProofError",". Both are treated as\noffline-degradable by a syncing store — local writes queue, nothing is lost.",[1114,114769,114770,114778,114779,33320,114781,114783,114784,114786,114787,1520],{},[950,114771,114772,997,114774,114777],{},[941,114773,19861],{},[941,114775,114776],{},"endpointOrigin"," are bound into the MAC."," A proof captured at one ",[941,114780,19861],{},[941,114782,111684],{},") cannot be replayed at another (",[941,114785,111687],{},"); a proof cannot be relayed to a different\nbroker endpoint reusing the same ",[941,114788,111867],{},[1114,114790,114791,114795,114796,114798],{},[950,114792,114793,3476],{},[941,114794,52761],{}," (postgres\u002Fturso\u002Fsupabase\u002Fwebdav) are not wired yet — the\n",[941,114797,53652],{}," type reserves the shape, but no store owns its refresh loop for it today.",[1114,114800,114801,114804],{},[950,114802,114803],{},"Sealed-to-instance delivery"," (encrypting the credential response to a specific device's\nkeypair) is a deferred future slice.",[1022,114806,2621],{"id":2620},[1111,114808,114809,114820,114831],{},[1114,114810,114811,997,114815,114819],{},[1007,114812,114813],{"href":686},[941,114814,685],{},[1007,114816,114817],{"href":865},[941,114818,864],{}," — how offline-degradable writes queue\nwhen the broker or store is unreachable.",[1114,114821,114822,114826,114827,1625,114829,1520],{},[1007,114823,114824],{"href":869},[941,114825,868],{}," — the keyring roles that gate ",[941,114828,112267],{},[941,114830,10859],{},[1114,114832,101581,114833,1520],{},[1007,114834,114836],{"href":93824,"rel":114835},[3371],[941,114837,112279],{},[2645,114839,114840],{},"html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sHdIc, html code.shiki .sHdIc{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#EEFFFF;--shiki-default-font-style:italic;--shiki-dark:#BABED8;--shiki-dark-font-style:italic}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}html pre.shiki code .sfNiH, html code.shiki .sfNiH{--shiki-light:#FF5370;--shiki-default:#FF9CAC;--shiki-dark:#FF9CAC}",{"title":1037,"searchDepth":1038,"depth":1038,"links":114842},[114843,114844,114845,114846,114847,114848,114849,114850,114851,114852,114853,114854,114855,114856],{"id":29856,"depth":1041,"text":29857},{"id":111659,"depth":1041,"text":111660},{"id":111691,"depth":1041,"text":111692},{"id":111855,"depth":1041,"text":111856},{"id":70092,"depth":1041,"text":70093},{"id":99863,"depth":1041,"text":99864},{"id":112272,"depth":1041,"text":112273},{"id":112672,"depth":1041,"text":112673},{"id":113007,"depth":1041,"text":113008},{"id":101424,"depth":1041,"text":101425},{"id":113146,"depth":1041,"text":113147},{"id":114683,"depth":1041,"text":114684},{"id":6824,"depth":1041,"text":6825},{"id":2620,"depth":1041,"text":2621},"The credential broker: a secret-bound client obtains short-lived, auto-rotating cloud-storage credentials without ever holding a long-lived secret.",{},{"title":625,"description":114857},"Secret-bound rolling store credentials — short-lived cloud access with no long-lived secret in the client","Wi7FMH4sOeomfDv2MhIjTdaLgUqcYvBVycQqaHDHDVs",{"id":114863,"title":629,"access":12,"apiToc":929,"body":114864,"category":1042,"description":117119,"extension":1044,"meta":117120,"navigation":1042,"nest":1047,"path":630,"seo":117121,"sinceVersion":1042,"status":1049,"stem":631,"subtitle":1042,"tier":1050,"tocExternal":1042,"__hash__":117122},"docs\u002Fservices\u002Fbundle.md",{"type":931,"value":114865,"toc":117093},[114866,114868,114871,114873,114891,114908,114910,114927,114929,114935,114967,114993,114995,114999,115250,115328,115423,115429,115564,115567,115571,115586,115797,115818,115831,115833,115837,115994,115998,116040,116045,116294,116301,116307,116460,116466,116527,116532,116605,116610,116616,116756,116763,116767,116810,116819,116833,116837,116840,116875,116879,116901,116933,116935,116938,116940,116973,116975,117033,117035,117090],[934,114867,629],{"id":629},[46871,114869],{":rows":114870},"[{\"label\":\"Subpath\",\"value\":\"`@noy-db\u002Fhub\u002Fpod`. The old `@noy-db\u002Fhub\u002Fbundle` alias is **gone from every published version of hub since 0.4.0-pre** — it was last present in 0.3.0, so an import of it fails on both rails today.\"},{\"label\":\"Factory\",\"value\":\"none — direct named imports\"},{\"label\":\"Cluster\",\"value\":\"F — Snapshot & Portability\"},{\"label\":\"LOC cost\",\"value\":\"~846 (off-bundle when not opted in)\"}]",[1022,114872,29857],{"id":29856},[937,114874,114875,114876,114878,114879,114881,114882,114884,114885,114887,114888,114890],{},"This service writes and reads ",[941,114877,1402],{}," files — a single-file, portable container holding a complete (or filtered) backup of a ",[950,114880,2694],{},", noy-db's unit of encrypted storage. ",[941,114883,6820],{}," serialises the vault to a JSON string — records still encrypted, plus the keyring (the wrapped decryption keys) and the audit ledger — and ",[941,114886,31188],{}," wraps that JSON in a compact binary container that is safe to park on untrusted storage (S3, Google Drive, a USB stick, an email attachment), because every record inside is already AES-256-GCM ciphertext. See ",[1007,114889,21],{"href":60250}," for the key model.",[937,114892,114893,114894,114896,114897,114900,114901,114904,114905,114907],{},"The container layout is: a 10-byte fixed prefix (",[941,114895,70502],{}," magic, flags, compression algorithm, header length), then an ",[950,114898,114899],{},"unencrypted JSON header",", then the compressed body (brotli where the runtime supports it, gzip otherwise). The header is ",[1092,114902,114903],{},"minimum-disclosure"," — it carries only what a cloud-listing UI needs before downloading: the format version, the vault's ",[950,114906,36368],{}," (an opaque 26-character ULID identifier, generated once per vault and stable across re-exports), the body's byte count, and the body's SHA-256. The parser rejects any header key outside that allowlist, so nothing about the tenant, the exporter, or export timing leaks through cloud-storage metadata indexing.",[1022,114909,111660],{"id":111659},[1111,114911,114912,114915,114918,114921],{},[1114,114913,114914],{},"Backups to S3 \u002F R2 \u002F Google Drive \u002F Dropbox where you want a single file per vault",[1114,114916,114917],{},"Offline transfer (USB, email) of a complete vault snapshot",[1114,114919,114920],{},"Handing a re-keyed slice of a vault to a recipient with their own secret",[1114,114922,114923,114924,1381],{},"Bundle-as-store: backends that prefer whole-file I\u002FO over per-record operations (",[941,114925,114926],{},"wrapPodStore",[1022,114928,99864],{"id":99863},[937,114930,114931,114932,114934],{},"The service does not use a ",[941,114933,943],{}," strategy factory — it tree-shakes naturally via direct named imports:",[2217,114936,114938],{"className":2219,"code":114937,"language":2221,"meta":1037,"style":1037},"import { writePod, readPod, readPodHeader } from '@noy-db\u002Fhub\u002Fpod'\n",[941,114939,114940],{"__ignoreMap":1037},[2225,114941,114942,114944,114946,114948,114950,114952,114954,114957,114959,114961,114963,114965],{"class":2227,"line":2228},[2225,114943,2232],{"class":2231},[2225,114945,2236],{"class":2235},[2225,114947,70188],{"class":2239},[2225,114949,2298],{"class":2235},[2225,114951,70193],{"class":2239},[2225,114953,2298],{"class":2235},[2225,114955,114956],{"class":2239}," readPodHeader",[2225,114958,2243],{"class":2235},[2225,114960,2246],{"class":2231},[2225,114962,2249],{"class":2235},[2225,114964,70036],{"class":2252},[2225,114966,2256],{"class":2235},[937,114968,114969,114970,968,114972,968,114974,55293,114977,114979,114980,968,114982,968,114984,2462,114986,114988,114989,114992],{},"The former names — ",[941,114971,35759],{},[941,114973,70321],{},[941,114975,114976],{},"readPodHeader",[941,114978,71403],{}," type — remain exported as deprecated aliases of ",[941,114981,35759],{},[941,114983,70321],{},[941,114985,114976],{},[941,114987,71403],{},". New code should use the ",[941,114990,114991],{},"Pod"," names.",[1022,114994,29893],{"id":29892},[1591,114996,114998],{"id":114997},"backup-and-restore-round-trip","Backup and restore round-trip",[2217,115000,115002],{"className":2219,"code":115001,"language":2221,"meta":1037,"style":1037},"import { createNoydb } from '@noy-db\u002Fhub'\nimport { toMemory } from '@noy-db\u002Fto-memory'\nimport { writePod, readPod, readPodHeader } from '@noy-db\u002Fhub\u002Fpod'\n\nconst db = await createNoydb({ store: toMemory(), user: 'alice', secret: 'a-strong-secret' })\nconst vault = await db.openVault('acme')\nawait vault.collection('invoices').put('inv-1', { id: 'inv-1', amount: 100 })\n\n\u002F\u002F 1. Write the container — one Uint8Array, save it anywhere\nconst bytes = await writePod(vault)\n\n\u002F\u002F 2. Inspect the header without decompressing the body (cheap, synchronous)\nconst header = readPodHeader(bytes)\n",[941,115003,115004,115022,115040,115066,115070,115122,115148,115206,115210,115215,115229,115233,115238],{"__ignoreMap":1037},[2225,115005,115006,115008,115010,115012,115014,115016,115018,115020],{"class":2227,"line":2228},[2225,115007,2232],{"class":2231},[2225,115009,2236],{"class":2235},[2225,115011,2744],{"class":2239},[2225,115013,2243],{"class":2235},[2225,115015,2246],{"class":2231},[2225,115017,2249],{"class":2235},[2225,115019,1086],{"class":2252},[2225,115021,2256],{"class":2235},[2225,115023,115024,115026,115028,115030,115032,115034,115036,115038],{"class":2227,"line":1041},[2225,115025,2232],{"class":2231},[2225,115027,2236],{"class":2235},[2225,115029,68576],{"class":2239},[2225,115031,2243],{"class":2235},[2225,115033,2246],{"class":2231},[2225,115035,2249],{"class":2235},[2225,115037,9638],{"class":2252},[2225,115039,2256],{"class":2235},[2225,115041,115042,115044,115046,115048,115050,115052,115054,115056,115058,115060,115062,115064],{"class":2227,"line":1038},[2225,115043,2232],{"class":2231},[2225,115045,2236],{"class":2235},[2225,115047,70188],{"class":2239},[2225,115049,2298],{"class":2235},[2225,115051,70193],{"class":2239},[2225,115053,2298],{"class":2235},[2225,115055,114956],{"class":2239},[2225,115057,2243],{"class":2235},[2225,115059,2246],{"class":2231},[2225,115061,2249],{"class":2235},[2225,115063,70036],{"class":2252},[2225,115065,2256],{"class":2235},[2225,115067,115068],{"class":2227,"line":2288},[2225,115069,2285],{"emptyLinePlaceholder":2284},[2225,115071,115072,115074,115076,115078,115080,115082,115084,115086,115088,115090,115092,115094,115096,115098,115100,115102,115104,115106,115108,115110,115112,115114,115116,115118,115120],{"class":2227,"line":2304},[2225,115073,2775],{"class":2774},[2225,115075,2778],{"class":2239},[2225,115077,2781],{"class":2235},[2225,115079,2784],{"class":2231},[2225,115081,2744],{"class":2291},[2225,115083,2789],{"class":2239},[2225,115085,2792],{"class":2235},[2225,115087,7564],{"class":2307},[2225,115089,2311],{"class":2235},[2225,115091,68576],{"class":2291},[2225,115093,4228],{"class":2239},[2225,115095,2298],{"class":2235},[2225,115097,2795],{"class":2307},[2225,115099,2311],{"class":2235},[2225,115101,2249],{"class":2235},[2225,115103,6408],{"class":2252},[2225,115105,2805],{"class":2235},[2225,115107,2298],{"class":2235},[2225,115109,2810],{"class":2307},[2225,115111,2311],{"class":2235},[2225,115113,2249],{"class":2235},[2225,115115,6421],{"class":2252},[2225,115117,2805],{"class":2235},[2225,115119,2243],{"class":2235},[2225,115121,2362],{"class":2239},[2225,115123,115124,115126,115128,115130,115132,115134,115136,115138,115140,115142,115144,115146],{"class":2227,"line":2356},[2225,115125,2775],{"class":2774},[2225,115127,2835],{"class":2239},[2225,115129,2781],{"class":2235},[2225,115131,2784],{"class":2231},[2225,115133,2842],{"class":2239},[2225,115135,1520],{"class":2235},[2225,115137,2847],{"class":2291},[2225,115139,2789],{"class":2239},[2225,115141,2805],{"class":2235},[2225,115143,6450],{"class":2252},[2225,115145,2805],{"class":2235},[2225,115147,2362],{"class":2239},[2225,115149,115150,115152,115154,115156,115158,115160,115162,115164,115166,115168,115170,115172,115174,115176,115178,115180,115182,115184,115186,115188,115190,115192,115194,115196,115198,115200,115202,115204],{"class":2227,"line":2830},[2225,115151,6500],{"class":2231},[2225,115153,6466],{"class":2239},[2225,115155,1520],{"class":2235},[2225,115157,6471],{"class":2291},[2225,115159,2789],{"class":2239},[2225,115161,2805],{"class":2235},[2225,115163,6487],{"class":2252},[2225,115165,2805],{"class":2235},[2225,115167,1381],{"class":2239},[2225,115169,1520],{"class":2235},[2225,115171,3161],{"class":2291},[2225,115173,2789],{"class":2239},[2225,115175,2805],{"class":2235},[2225,115177,6514],{"class":2252},[2225,115179,2805],{"class":2235},[2225,115181,2298],{"class":2235},[2225,115183,2236],{"class":2235},[2225,115185,7070],{"class":2307},[2225,115187,2311],{"class":2235},[2225,115189,2249],{"class":2235},[2225,115191,6514],{"class":2252},[2225,115193,2805],{"class":2235},[2225,115195,2298],{"class":2235},[2225,115197,6351],{"class":2307},[2225,115199,2311],{"class":2235},[2225,115201,6528],{"class":6527},[2225,115203,2243],{"class":2235},[2225,115205,2362],{"class":2239},[2225,115207,115208],{"class":2227,"line":2861},[2225,115209,2285],{"emptyLinePlaceholder":2284},[2225,115211,115212],{"class":2227,"line":4246},[2225,115213,115214],{"class":2763},"\u002F\u002F 1. Write the container — one Uint8Array, save it anywhere\n",[2225,115216,115217,115219,115221,115223,115225,115227],{"class":2227,"line":4253},[2225,115218,2775],{"class":2774},[2225,115220,32469],{"class":2239},[2225,115222,2781],{"class":2235},[2225,115224,2784],{"class":2231},[2225,115226,70188],{"class":2291},[2225,115228,45516],{"class":2239},[2225,115230,115231],{"class":2227,"line":4259},[2225,115232,2285],{"emptyLinePlaceholder":2284},[2225,115234,115235],{"class":2227,"line":6548},[2225,115236,115237],{"class":2763},"\u002F\u002F 2. Inspect the header without decompressing the body (cheap, synchronous)\n",[2225,115239,115240,115242,115244,115246,115248],{"class":2227,"line":6576},[2225,115241,2775],{"class":2774},[2225,115243,32122],{"class":2239},[2225,115245,2781],{"class":2235},[2225,115247,114956],{"class":2291},[2225,115249,71461],{"class":2239},[2217,115251,115253],{"className":6604,"code":115252,"language":141,"meta":1037,"style":1037},"{\n  \"formatVersion\": 1,\n  \"handle\": \"01J1QDX9Y3Z4A5B6C7D8E9FGHJ\",\n  \"bodyBytes\": 1874,\n  \"bodySha256\": \"9f2ce1b4a7d8036c5e12f9a0b3d47c68e51a2f90c4b7d3e6a8f1049b2c5d7e3a\"\n}\n",[941,115254,115255,115259,115273,115292,115307,115324],{"__ignoreMap":1037},[2225,115256,115257],{"class":2227,"line":2228},[2225,115258,4200],{"class":2235},[2225,115260,115261,115263,115265,115267,115269,115271],{"class":2227,"line":1041},[2225,115262,9368],{"class":2235},[2225,115264,36353],{"class":2774},[2225,115266,6620],{"class":2235},[2225,115268,2311],{"class":2235},[2225,115270,9377],{"class":6527},[2225,115272,1707],{"class":2235},[2225,115274,115275,115277,115279,115281,115283,115285,115288,115290],{"class":2227,"line":1038},[2225,115276,9368],{"class":2235},[2225,115278,36368],{"class":2774},[2225,115280,6620],{"class":2235},[2225,115282,2311],{"class":2235},[2225,115284,6614],{"class":2235},[2225,115286,115287],{"class":2252},"01J1QDX9Y3Z4A5B6C7D8E9FGHJ",[2225,115289,6620],{"class":2235},[2225,115291,1707],{"class":2235},[2225,115293,115294,115296,115298,115300,115302,115305],{"class":2227,"line":2288},[2225,115295,9368],{"class":2235},[2225,115297,36388],{"class":2774},[2225,115299,6620],{"class":2235},[2225,115301,2311],{"class":2235},[2225,115303,115304],{"class":6527}," 1874",[2225,115306,1707],{"class":2235},[2225,115308,115309,115311,115313,115315,115317,115319,115322],{"class":2227,"line":2304},[2225,115310,9368],{"class":2235},[2225,115312,36404],{"class":2774},[2225,115314,6620],{"class":2235},[2225,115316,2311],{"class":2235},[2225,115318,6614],{"class":2235},[2225,115320,115321],{"class":2252},"9f2ce1b4a7d8036c5e12f9a0b3d47c68e51a2f90c4b7d3e6a8f1049b2c5d7e3a",[2225,115323,9449],{"class":2235},[2225,115325,115326],{"class":2227,"line":2356},[2225,115327,7351],{"class":2235},[2217,115329,115331],{"className":2219,"code":115330,"language":2221,"meta":1037,"style":1037},"\u002F\u002F 3. Restore — readPod validates the magic, the header, and the body's\n\u002F\u002F    integrity hash, then decompresses. It takes NO secret: it is a\n\u002F\u002F    pure format layer, and the records inside dumpJson stay ciphertext.\nconst { dumpJson } = await readPod(bytes)\nawait vault.load(dumpJson)\n\nawait vault.collection('invoices').get('inv-1')\n\u002F\u002F → { id: 'inv-1', amount: 100 }\n",[941,115332,115333,115338,115343,115348,115367,115380,115384,115418],{"__ignoreMap":1037},[2225,115334,115335],{"class":2227,"line":2228},[2225,115336,115337],{"class":2763},"\u002F\u002F 3. Restore — readPod validates the magic, the header, and the body's\n",[2225,115339,115340],{"class":2227,"line":1041},[2225,115341,115342],{"class":2763},"\u002F\u002F    integrity hash, then decompresses. It takes NO secret: it is a\n",[2225,115344,115345],{"class":2227,"line":1038},[2225,115346,115347],{"class":2763},"\u002F\u002F    pure format layer, and the records inside dumpJson stay ciphertext.\n",[2225,115349,115350,115352,115354,115357,115359,115361,115363,115365],{"class":2227,"line":2288},[2225,115351,2775],{"class":2774},[2225,115353,2236],{"class":2235},[2225,115355,115356],{"class":2239}," dumpJson ",[2225,115358,2359],{"class":2235},[2225,115360,6346],{"class":2235},[2225,115362,2784],{"class":2231},[2225,115364,70193],{"class":2291},[2225,115366,71461],{"class":2239},[2225,115368,115369,115371,115373,115375,115377],{"class":2227,"line":2304},[2225,115370,6500],{"class":2231},[2225,115372,6466],{"class":2239},[2225,115374,1520],{"class":2235},[2225,115376,22905],{"class":2291},[2225,115378,115379],{"class":2239},"(dumpJson)\n",[2225,115381,115382],{"class":2227,"line":2356},[2225,115383,2285],{"emptyLinePlaceholder":2284},[2225,115385,115386,115388,115390,115392,115394,115396,115398,115400,115402,115404,115406,115408,115410,115412,115414,115416],{"class":2227,"line":2830},[2225,115387,6500],{"class":2231},[2225,115389,6466],{"class":2239},[2225,115391,1520],{"class":2235},[2225,115393,6471],{"class":2291},[2225,115395,2789],{"class":2239},[2225,115397,2805],{"class":2235},[2225,115399,6487],{"class":2252},[2225,115401,2805],{"class":2235},[2225,115403,1381],{"class":2239},[2225,115405,1520],{"class":2235},[2225,115407,3158],{"class":2291},[2225,115409,2789],{"class":2239},[2225,115411,2805],{"class":2235},[2225,115413,6514],{"class":2252},[2225,115415,2805],{"class":2235},[2225,115417,2362],{"class":2239},[2225,115419,115420],{"class":2227,"line":2861},[2225,115421,115422],{"class":2763},"\u002F\u002F → { id: 'inv-1', amount: 100 }\n",[937,115424,115425,115426,115428],{},"For local files, ",[941,115427,9551],{}," ships two thin wrappers over the same primitives:",[2217,115430,115432],{"className":2219,"code":115431,"language":2221,"meta":1037,"style":1037},"import { savePod, loadPod } from '@noy-db\u002Fto-file'\n\nconst handle = await vault.getPodHandle()\nawait savePod(`.\u002Fbundles\u002F${handle}.noydb`, vault)  \u002F\u002F writePod + atomic writeFile\nconst result = await loadPod(`.\u002Fbundles\u002F${handle}.noydb`)  \u002F\u002F readFile + readPod\nresult.header\n\u002F\u002F → { formatVersion: 1, handle: '01J1QDX9Y3Z4A5B6C7D8E9FGHJ', bodyBytes: 1874, bodySha256: '9f2ce1b4a7d8036c5e12f9a0b3d47c68e51a2f90c4b7d3e6a8f1049b2c5d7e3a' }\n\u002F\u002F result.dumpJson is the same vault.dump() JSON ready for vault.load() — see PodReadResult below\n",[941,115433,115434,115458,115462,115481,115512,115545,115554,115559],{"__ignoreMap":1037},[2225,115435,115436,115438,115440,115443,115445,115448,115450,115452,115454,115456],{"class":2227,"line":2228},[2225,115437,2232],{"class":2231},[2225,115439,2236],{"class":2235},[2225,115441,115442],{"class":2239}," savePod",[2225,115444,2298],{"class":2235},[2225,115446,115447],{"class":2239}," loadPod",[2225,115449,2243],{"class":2235},[2225,115451,2246],{"class":2231},[2225,115453,2249],{"class":2235},[2225,115455,9551],{"class":2252},[2225,115457,2256],{"class":2235},[2225,115459,115460],{"class":2227,"line":1041},[2225,115461,2285],{"emptyLinePlaceholder":2284},[2225,115463,115464,115466,115468,115470,115472,115474,115476,115479],{"class":2227,"line":1038},[2225,115465,2775],{"class":2774},[2225,115467,40799],{"class":2239},[2225,115469,2781],{"class":2235},[2225,115471,2784],{"class":2231},[2225,115473,6466],{"class":2239},[2225,115475,1520],{"class":2235},[2225,115477,115478],{"class":2291},"getPodHandle",[2225,115480,8146],{"class":2239},[2225,115482,115483,115485,115487,115489,115491,115494,115496,115498,115500,115502,115504,115506,115509],{"class":2227,"line":2288},[2225,115484,6500],{"class":2231},[2225,115486,115442],{"class":2291},[2225,115488,2789],{"class":2239},[2225,115490,44476],{"class":2235},[2225,115492,115493],{"class":2252},".\u002Fbundles\u002F",[2225,115495,8197],{"class":2235},[2225,115497,36368],{"class":2239},[2225,115499,2359],{"class":2235},[2225,115501,1402],{"class":2252},[2225,115503,44476],{"class":2235},[2225,115505,2298],{"class":2235},[2225,115507,115508],{"class":2239}," vault)  ",[2225,115510,115511],{"class":2763},"\u002F\u002F writePod + atomic writeFile\n",[2225,115513,115514,115516,115518,115520,115522,115524,115526,115528,115530,115532,115534,115536,115538,115540,115542],{"class":2227,"line":2304},[2225,115515,2775],{"class":2774},[2225,115517,36920],{"class":2239},[2225,115519,2781],{"class":2235},[2225,115521,2784],{"class":2231},[2225,115523,115447],{"class":2291},[2225,115525,2789],{"class":2239},[2225,115527,44476],{"class":2235},[2225,115529,115493],{"class":2252},[2225,115531,8197],{"class":2235},[2225,115533,36368],{"class":2239},[2225,115535,2359],{"class":2235},[2225,115537,1402],{"class":2252},[2225,115539,44476],{"class":2235},[2225,115541,27226],{"class":2239},[2225,115543,115544],{"class":2763},"\u002F\u002F readFile + readPod\n",[2225,115546,115547,115549,115551],{"class":2227,"line":2356},[2225,115548,11195],{"class":2239},[2225,115550,1520],{"class":2235},[2225,115552,115553],{"class":2239},"header\n",[2225,115555,115556],{"class":2227,"line":2830},[2225,115557,115558],{"class":2763},"\u002F\u002F → { formatVersion: 1, handle: '01J1QDX9Y3Z4A5B6C7D8E9FGHJ', bodyBytes: 1874, bodySha256: '9f2ce1b4a7d8036c5e12f9a0b3d47c68e51a2f90c4b7d3e6a8f1049b2c5d7e3a' }\n",[2225,115560,115561],{"class":2227,"line":2861},[2225,115562,115563],{"class":2763},"\u002F\u002F result.dumpJson is the same vault.dump() JSON ready for vault.load() — see PodReadResult below\n",[937,115565,115566],{},"Naming files by the handle rather than the vault name avoids leaking metadata at the filesystem layer.",[1591,115568,115570],{"id":115569},"re-keyed-export-to-recipients","Re-keyed export to recipients",[937,115572,115573,115574,115577,115578,115581,115582,115585],{},"By default a bundle inherits the source vault's keyring — right for personal backup-and-restore. To hand a bundle to someone else, pass ",[941,115575,115576],{},"recipients"," (or the single-recipient ",[941,115579,115580],{},"exportSecret"," shorthand): the bundle's keyring map is ",[950,115583,115584],{},"replaced"," with one freshly derived slot per recipient, each sealed with that recipient's own secret. The source credentials never travel.",[2217,115587,115589],{"className":2219,"code":115588,"language":2221,"meta":1037,"style":1037},"const bytes = await writePod(vault, {\n  collections: ['invoices'],                    \u002F\u002F slice: only this collection\n  recipients: [\n    {\n      id: 'auditor',\n      secret: 'auditor-pw-2026',\n      role: 'viewer',\n      permissions: { invoices: 'ro' },\n      expiresAt: '2026-08-07T00:00:00Z',        \u002F\u002F slot refuses to unlock after this\n    },\n  ],\n})\n\nconst { dumpJson } = await readPod(bytes)\nObject.keys(JSON.parse(dumpJson).keyrings)\n\u002F\u002F → ['auditor']   — the source keyring is not in the bundle\n",[941,115590,115591,115609,115630,115639,115643,115659,115675,115690,115711,115730,115734,115740,115746,115750,115768,115792],{"__ignoreMap":1037},[2225,115592,115593,115595,115597,115599,115601,115603,115605,115607],{"class":2227,"line":2228},[2225,115594,2775],{"class":2774},[2225,115596,32469],{"class":2239},[2225,115598,2781],{"class":2235},[2225,115600,2784],{"class":2231},[2225,115602,70188],{"class":2291},[2225,115604,30266],{"class":2239},[2225,115606,2298],{"class":2235},[2225,115608,2301],{"class":2235},[2225,115610,115611,115613,115615,115617,115619,115621,115623,115625,115627],{"class":2227,"line":1041},[2225,115612,32324],{"class":2307},[2225,115614,2311],{"class":2235},[2225,115616,8667],{"class":2239},[2225,115618,2805],{"class":2235},[2225,115620,6487],{"class":2252},[2225,115622,2805],{"class":2235},[2225,115624,8803],{"class":2239},[2225,115626,2298],{"class":2235},[2225,115628,115629],{"class":2763},"                    \u002F\u002F slice: only this collection\n",[2225,115631,115632,115635,115637],{"class":2227,"line":1038},[2225,115633,115634],{"class":2307},"  recipients",[2225,115636,2311],{"class":2235},[2225,115638,32491],{"class":2239},[2225,115640,115641],{"class":2227,"line":2288},[2225,115642,109952],{"class":2235},[2225,115644,115645,115648,115650,115652,115655,115657],{"class":2227,"line":2304},[2225,115646,115647],{"class":2307},"      id",[2225,115649,2311],{"class":2235},[2225,115651,2249],{"class":2235},[2225,115653,115654],{"class":2252},"auditor",[2225,115656,2805],{"class":2235},[2225,115658,1707],{"class":2235},[2225,115660,115661,115664,115666,115668,115671,115673],{"class":2227,"line":2356},[2225,115662,115663],{"class":2307},"      secret",[2225,115665,2311],{"class":2235},[2225,115667,2249],{"class":2235},[2225,115669,115670],{"class":2252},"auditor-pw-2026",[2225,115672,2805],{"class":2235},[2225,115674,1707],{"class":2235},[2225,115676,115677,115680,115682,115684,115686,115688],{"class":2227,"line":2830},[2225,115678,115679],{"class":2307},"      role",[2225,115681,2311],{"class":2235},[2225,115683,2249],{"class":2235},[2225,115685,9910],{"class":2252},[2225,115687,2805],{"class":2235},[2225,115689,1707],{"class":2235},[2225,115691,115692,115695,115697,115699,115701,115703,115705,115707,115709],{"class":2227,"line":2861},[2225,115693,115694],{"class":2307},"      permissions",[2225,115696,2311],{"class":2235},[2225,115698,2236],{"class":2235},[2225,115700,6503],{"class":2307},[2225,115702,2311],{"class":2235},[2225,115704,2249],{"class":2235},[2225,115706,10029],{"class":2252},[2225,115708,2805],{"class":2235},[2225,115710,2353],{"class":2235},[2225,115712,115713,115716,115718,115720,115723,115725,115727],{"class":2227,"line":4246},[2225,115714,115715],{"class":2307},"      expiresAt",[2225,115717,2311],{"class":2235},[2225,115719,2249],{"class":2235},[2225,115721,115722],{"class":2252},"2026-08-07T00:00:00Z",[2225,115724,2805],{"class":2235},[2225,115726,2298],{"class":2235},[2225,115728,115729],{"class":2763},"        \u002F\u002F slot refuses to unlock after this\n",[2225,115731,115732],{"class":2227,"line":4253},[2225,115733,8324],{"class":2235},[2225,115735,115736,115738],{"class":2227,"line":4259},[2225,115737,32588],{"class":2239},[2225,115739,1707],{"class":2235},[2225,115741,115742,115744],{"class":2227,"line":6548},[2225,115743,2359],{"class":2235},[2225,115745,2362],{"class":2239},[2225,115747,115748],{"class":2227,"line":6576},[2225,115749,2285],{"emptyLinePlaceholder":2284},[2225,115751,115752,115754,115756,115758,115760,115762,115764,115766],{"class":2227,"line":6582},[2225,115753,2775],{"class":2774},[2225,115755,2236],{"class":2235},[2225,115757,115356],{"class":2239},[2225,115759,2359],{"class":2235},[2225,115761,6346],{"class":2235},[2225,115763,2784],{"class":2231},[2225,115765,70193],{"class":2291},[2225,115767,71461],{"class":2239},[2225,115769,115770,115773,115775,115777,115780,115782,115784,115787,115789],{"class":2227,"line":6587},[2225,115771,115772],{"class":2239},"Object",[2225,115774,1520],{"class":2235},[2225,115776,8678],{"class":2291},[2225,115778,115779],{"class":2239},"(JSON",[2225,115781,1520],{"class":2235},[2225,115783,15798],{"class":2291},[2225,115785,115786],{"class":2239},"(dumpJson)",[2225,115788,1520],{"class":2235},[2225,115790,115791],{"class":2239},"keyrings)\n",[2225,115793,115794],{"class":2227,"line":7174},[2225,115795,115796],{"class":2763},"\u002F\u002F → ['auditor']   — the source keyring is not in the bundle\n",[937,115798,115799,964,115802,115804,115805,115808,115809,115811,115812,115814,115815,115817],{},[950,115800,115801],{},"A recipient secret must be at least 12 characters",[941,115803,115580],{}," and every string\n",[941,115806,115807],{},"recipients[].secret"," — with no opt-out, refused with ",[941,115810,20752],{}," since hub ",[941,115813,2126],{},". Any\ncharacters qualify: a passphrase, a generated token, a password-manager string. It is deliberately\nnot the owner-phrase rule, because a ",[941,115816,1402],{}," bundle is built to travel.",[937,115819,115820,115821,115823,115824,115826,115827,115830],{},"On the recipient's device, the bundle's keyrings and collections are written to the recipient's own store; ",[941,115822,1749],{}," then unlocks with the recipient's secret, exactly like any other vault. A slot's ",[941,115825,16800],{}," also bound which decryption keys are wrapped into it — a ",[941,115828,115829],{},"{ invoices: 'ro' }"," slot cannot decrypt other collections even though their ciphertext may sit in the bundle.",[1022,115832,101012],{"id":41259},[1591,115834,115836],{"id":115835},"functions","Functions",[1111,115838,115839,115857,115877,115890,115942,115951,115988],{},[1114,115840,115841,1746,115844,115846,115847,997,115849,115851,115852],{},[941,115842,115843],{},"writePod(vault, options?)",[941,115845,70495],{}," — dump the vault, apply re-keying and slice filters, compress, and assemble the container. Throws if ",[941,115848,115580],{},[941,115850,115576],{}," are both set. ",[1092,115853,115854,115855,1381],{},"(deprecated alias: ",[941,115856,35759],{},[1114,115858,115859,1746,115862,115865,115866,115869,115870,115872,115873],{},[941,115860,115861],{},"readPod(bytes, options?)",[941,115863,115864],{},"Promise\u003CPodReadResult>"," — validate magic + header, verify the body length and SHA-256, decompress, and return the original dump JSON. Throws ",[941,115867,115868],{},"PodIntegrityError"," (importable from ",[941,115871,1086],{},") when the body was truncated, padded, or modified — distinct from format errors so you can pattern-match corrupted-in-transit vs malformed-by-producer. ",[1092,115874,115854,115875,1381],{},[941,115876,70321],{},[1114,115878,115879,1746,115882,115885,115886],{},[941,115880,115881],{},"readPodHeader(bytes)",[941,115883,115884],{},"NoydbPodHeader"," — synchronous, header-only read: no body decompression, no integrity verification. Cost is O(prefix + header bytes). ",[1092,115887,115854,115888,1381],{},[941,115889,114976],{},[1114,115891,115892,1261,115895,115898,115899,115901,115902,6030,115905,1261,115908,115911,115912,2878,115915,115918,115919,23423,115922,27493,115924,1261,115927,5584,115930],{},[941,115893,115894],{},"wrapPodStore(store, options?)",[941,115896,115897],{},"createPodStore(impl)"," — convert a whole-file pod store (blob-oriented read\u002Fwrite with optimistic concurrency) into the standard six-method ",[941,115900,3154],{},", for backends like Google Drive, WebDAV, or iCloud Drive that work best with whole-file I\u002FO. Version conflicts re-read, merge last-write-wins per record, and retry (max 3 attempts); flushing is per-mutation by default, or deferred via ",[941,115903,115904],{},"autoFlush: false",[941,115906,115907],{},"store.flush()",[941,115909,115910],{},"store.batch()",". Types: ",[941,115913,115914],{},"WrapPodStoreOptions",[941,115916,115917],{},"autoFlush"," bag) and ",[941,115920,115921],{},"WrappedPodNoydbStore",[941,115923,3154],{},[941,115925,115926],{},"flush()",[941,115928,115929],{},"batch()",[1092,115931,115932,115933,968,115935,968,115938,968,115940,1381],{},"(deprecated aliases: ",[941,115934,114926],{},[941,115936,115937],{},"createPodStore",[941,115939,115914],{},[941,115941,115921],{},[1114,115943,115944,1261,115947,115950],{},[941,115945,115946],{},"generateULID()",[941,115948,115949],{},"isULID(value)"," — the handle format helpers.",[1114,115952,115953,115954,968,115957,968,115960,968,115963,968,115966,968,115969,1261,115972,1261,115975,968,115978,968,115981,55293,115984,115987],{},"Format-level exports for tooling: ",[941,115955,115956],{},"NOYDB_POD_MAGIC",[941,115958,115959],{},"NOYDB_POD_PREFIX_BYTES",[941,115961,115962],{},"NOYDB_POD_FORMAT_VERSION",[941,115964,115965],{},"FLAG_COMPRESSED",[941,115967,115968],{},"FLAG_HAS_INTEGRITY_HASH",[941,115970,115971],{},"COMPRESSION_NONE",[941,115973,115974],{},"COMPRESSION_GZIP",[941,115976,115977],{},"COMPRESSION_BROTLI",[941,115979,115980],{},"validateBundleHeader(parsed)",[941,115982,115983],{},"encodeBundleHeader(header)",[941,115985,115986],{},"CompressionAlgo"," type.",[1114,115989,115990,115993],{},[941,115991,115992],{},"resetBrotliSupportCache()"," — test-only: reset the cached brotli feature detection.",[1591,115995,115997],{"id":115996},"vault-methods-used-with-this-service-always-on-core","Vault methods used with this service (always-on core)",[1111,115999,116000,116007,116016,116028],{},[1114,116001,116002,1746,116004,116006],{},[941,116003,6820],{},[941,116005,104222],{}," — the canonical backup JSON (encrypted records + keyrings + ledger).",[1114,116008,116009,1746,116012,116015],{},[941,116010,116011],{},"vault.load(dumpJson)",[941,116013,116014],{},"Promise\u003Cvoid>"," — restore from a dump; verifies the embedded ledger head when the backup carries one.",[1114,116017,116018,1746,116021,116023,116024,116027],{},[941,116019,116020],{},"vault.getPodHandle()",[941,116022,104222],{}," — the vault's ULID handle, persisted in the vault's ",[941,116025,116026],{},"_meta\u002Fhandle"," document so it survives process restarts and repeat exports.",[1114,116029,116030,1746,116033,116036,116037,116039],{},[941,116031,116032],{},"vault.buildBundleRecipientKeyrings(recipients)",[941,116034,116035],{},"Promise\u003CRecord\u003Cstring, KeyringFile>>"," — build per-recipient keyring files without persisting anything to the source vault. This is the primitive ",[941,116038,31188],{}," uses for its re-keying path; the produced files exist only in the bundle bytes.",[1591,116041,116043],{"id":116042},"writepodoptions",[941,116044,71403],{},[1338,116046,116047,116060],{},[1341,116048,116049],{},[1344,116050,116051,116053,116055,116057],{},[1347,116052,101060],{},[1347,116054,20194],{},[1347,116056,20197],{},[1347,116058,116059],{},"Effect",[1357,116061,116062,116091,116106,116125,116145,116164,116184,116202,116237,116258,116277],{},[1344,116063,116064,116068,116073,116078],{},[1362,116065,116066],{},[941,116067,110539],{},[1362,116069,116070],{},[941,116071,116072],{},"'auto' | 'brotli' | 'gzip' | 'none'",[1362,116074,116075],{},[941,116076,116077],{},"'auto'",[1362,116079,116080,116082,116083,116086,116087,116090],{},[941,116081,116077],{}," tries brotli, falls back to gzip. ",[941,116084,116085],{},"'brotli'"," throws if the runtime lacks support. ",[941,116088,116089],{},"'none'"," is for round-trip testing.",[1344,116092,116093,116097,116101,116103],{},[1362,116094,116095],{},[941,116096,2698],{},[1362,116098,116099],{},[941,116100,111053],{},[1362,116102,59366],{},[1362,116104,116105],{},"Allowlist of user-collection names. Applies to user collections only — keyrings and ledger stay so the bundle remains unlockable.",[1344,116107,116108,116112,116117,116119],{},[1362,116109,116110],{},[941,116111,7883],{},[1362,116113,116114],{},[941,116115,116116],{},"Date | string",[1362,116118,3610],{},[1362,116120,116121,116122,116124],{},"Keep only records whose envelope ",[941,116123,1703],{}," is on\u002Fafter this instant. Operates on the unencrypted envelope timestamp — no decryption needed.",[1344,116126,116127,116131,116136,116138],{},[1362,116128,116129],{},[941,116130,3016],{},[1362,116132,116133],{},[941,116134,116135],{},"(record, { collection, id }) => boolean | Promise\u003Cboolean>",[1362,116137,3610],{},[1362,116139,116140,116141,116144],{},"Plaintext predicate: each record is decrypted with the vault's key to run the test, and survivors keep their ",[950,116142,116143],{},"original ciphertext"," (no re-encryption). Async predicates supported.",[1344,116146,116147,116152,116156,116158],{},[1362,116148,116149],{},[941,116150,116151],{},"tierAtMost",[1362,116153,116154],{},[941,116155,20636],{},[1362,116157,3610],{},[1362,116159,116160,116161,116163],{},"Drop records whose envelope ",[941,116162,1716],{}," is strictly greater. Envelope-level; no decryption needed.",[1344,116165,116166,116170,116174,116176],{},[1362,116167,116168],{},[941,116169,115580],{},[1362,116171,116172],{},[941,116173,7259],{},[1362,116175,3610],{},[1362,116177,116178,116179,116181,116182,1520],{},"Single-recipient re-keying shorthand: one fresh keyring slot sealed with this secret, inheriting the source keyring's userId, role, and permissions. At least 12 characters (since hub ",[941,116180,2126],{},"). Mutually exclusive with ",[941,116183,115576],{},[1344,116185,116186,116190,116195,116197],{},[1362,116187,116188],{},[941,116189,115576],{},[1362,116191,116192],{},[941,116193,116194],{},"readonly PodRecipient[]",[1362,116196,3610],{},[1362,116198,116199,116200,1520],{},"Multi-recipient re-keying (see below). Mutually exclusive with ",[941,116201,115580],{},[1344,116203,116204,116209,116214,116216],{},[1362,116205,116206],{},[941,116207,116208],{},"autoCredentials",[1362,116210,116211],{},[941,116212,116213],{},"{ policy: 'public-by-design', perUser: Record\u003Cstring, AutoCredential> }",[1362,116215,3610],{},[1362,116217,116218,116219,116221,116222,116224,116225,116224,116227,116229,116230,116232,116233,116236],{},"Embed ",[950,116220,2985],{}," per-user credentials (",[941,116223,10695],{}," | ",[941,116226,313],{},[941,116228,317],{},") for auto-unlock. Anyone holding the bytes can read them — for demo data and sample vaults only. The explicit ",[941,116231,796],{}," discriminant is mandatory; a bare ",[941,116234,116235],{},"{ perUser }"," is rejected at write time.",[1344,116238,116239,116244,116251,116253],{},[1362,116240,116241],{},[941,116242,116243],{},"sealedCredentials",[1362,116245,116246,116247,1381],{},"self-target or recipient-target shape (see ",[1007,116248,116250],{"href":116249},"#auto-unlock-credentials","Auto-unlock",[1362,116252,3610],{},[1362,116254,116255,116256,1520],{},"Embed per-user credentials sealed under a sealing-key provider. Mutually exclusive with ",[941,116257,116208],{},[1344,116259,116260,116265,116270,116272],{},[1362,116261,116262],{},[941,116263,116264],{},"autoSecrets",[1362,116266,116267],{},[1092,116268,116269],{},"(deprecated)",[1362,116271,3610],{},[1362,116273,116274,116275,1520],{},"Use ",[941,116276,116208],{},[1344,116278,116279,116284,116288,116290],{},[1362,116280,116281],{},[941,116282,116283],{},"sealedSecrets",[1362,116285,116286],{},[1092,116287,116269],{},[1362,116289,3610],{},[1362,116291,116274,116292,1520],{},[941,116293,116243],{},[937,116295,116296,997,116298,116300],{},[941,116297,2698],{},[941,116299,7883],{}," intersect (AND). With no options, the bundle is a whole-vault snapshot.",[1591,116302,116304],{"id":116303},"podrecipient",[941,116305,116306],{},"PodRecipient",[1338,116308,116309,116321],{},[1341,116310,116311],{},[1344,116312,116313,116315,116317,116319],{},[1347,116314,101104],{},[1347,116316,20194],{},[1347,116318,20197],{},[1347,116320,20200],{},[1357,116322,116323,116338,116353,116370,116387,116404,116422,116440],{},[1344,116324,116325,116329,116333,116335],{},[1362,116326,116327],{},[941,116328,6983],{},[1362,116330,116331],{},[941,116332,7259],{},[1362,116334,45375],{},[1362,116336,116337],{},"User id stamped onto the keyring file in the bundle.",[1344,116339,116340,116344,116348,116350],{},[1362,116341,116342],{},[941,116343,10695],{},[1362,116345,116346],{},[941,116347,7259],{},[1362,116349,45375],{},[1362,116351,116352],{},"What the recipient will type to unlock.",[1344,116354,116355,116359,116363,116367],{},[1362,116356,116357],{},[941,116358,16791],{},[1362,116360,116361],{},[941,116362,7259],{},[1362,116364,116365],{},[941,116366,6983],{},[1362,116368,116369],{},"Optional display name.",[1344,116371,116372,116376,116380,116384],{},[1362,116373,116374],{},[941,116375,16785],{},[1362,116377,116378],{},[941,116379,2970],{},[1362,116381,116382],{},[941,116383,44343],{},[1362,116385,116386],{},"Role on the destination vault.",[1344,116388,116389,116393,116398,116401],{},[1362,116390,116391],{},[941,116392,16800],{},[1362,116394,116395],{},[941,116396,116397],{},"Permissions",[1362,116399,116400],{},"role defaults",[1362,116402,116403],{},"Per-collection permissions; also bounds which DEKs are wrapped into the slot.",[1344,116405,116406,116410,116415,116417],{},[1362,116407,116408],{},[941,116409,11422],{},[1362,116411,116412],{},[941,116413,116414],{},"ExportCapability",[1362,116416,8292],{},[1362,116418,82761,116419,116421],{},[941,116420,1791],{}," export grants on the destination vault.",[1344,116423,116424,116428,116433,116435],{},[1362,116425,116426],{},[941,116427,11438],{},[1362,116429,116430],{},[941,116431,116432],{},"ImportCapability",[1362,116434,8292],{},[1362,116436,82761,116437,116439],{},[941,116438,1791],{}," import grants. Default-closed.",[1344,116441,116442,116446,116451,116453],{},[1362,116443,116444],{},[941,116445,44339],{},[1362,116447,116448,116450],{},[941,116449,7259],{}," (ISO 8601)",[1362,116452,3610],{},[1362,116454,116455,116456,116459],{},"Past this cutoff the slot's keyring refuses to load with ",[941,116457,116458],{},"KeyringExpiredError"," — time-boxed audit access.",[1591,116461,116463],{"id":116462},"readpodoptions",[941,116464,116465],{},"ReadPodOptions",[1338,116467,116468,116480],{},[1341,116469,116470],{},[1344,116471,116472,116474,116476,116478],{},[1347,116473,101060],{},[1347,116475,20194],{},[1347,116477,20197],{},[1347,116479,116059],{},[1357,116481,116482,116506],{},[1344,116483,116484,116489,116494,116496],{},[1362,116485,116486],{},[941,116487,116488],{},"sealingProviders",[1362,116490,116491],{},[941,116492,116493],{},"readonly NoydbSealer[]",[1362,116495,3610],{},[1362,116497,116498,116499,116502,116503,116505],{},"Recipient-side providers used to unseal sealed auto-unlock entries; matched by provider ",[941,116500,116501],{},".id"," against each entry's ",[941,116504,33316],{},". When unset, sealed entries are returned unmodified (base64 sealed bytes).",[1344,116507,116508,116513,116517,116521],{},[1362,116509,116510],{},[941,116511,116512],{},"attemptUnsealAcrossProviders",[1362,116514,116515],{},[941,116516,16861],{},[1362,116518,116519],{},[941,116520,2560],{},[1362,116522,116523,116524,116526],{},"Opt-in trial mode: when an entry's ",[941,116525,33316],{}," matches no provider, try each provider with a matching algorithm. Surfaces extra credential prompts; use deliberately.",[1591,116528,116530],{"id":116529},"podreadresult",[941,116531,71468],{},[1338,116533,116534,116544],{},[1341,116535,116536],{},[1344,116537,116538,116540,116542],{},[1347,116539,101104],{},[1347,116541,20194],{},[1347,116543,20200],{},[1357,116545,116546,116560,116580],{},[1344,116547,116548,116553,116557],{},[1362,116549,116550],{},[941,116551,116552],{},"header",[1362,116554,116555],{},[941,116556,115884],{},[1362,116558,116559],{},"The validated header.",[1344,116561,116562,116567,116571],{},[1362,116563,116564],{},[941,116565,116566],{},"dumpJson",[1362,116568,116569],{},[941,116570,7259],{},[1362,116572,116573,116574,116576,116577,1520],{},"The original ",[941,116575,6820],{}," JSON, ready for ",[941,116578,116579],{},"vault.load()",[1344,116581,116582,116587,116592],{},[1362,116583,116584],{},[941,116585,116586],{},"autoUnlock?",[1362,116588,116589],{},[941,116590,116591],{},"{ kind: 'unsealed' | 'sealed', perUser: Record\u003Cstring, AutoCredential> }",[1362,116593,116594,116595,116598,116599,116602,116603,1520],{},"Present only when the header's ",[941,116596,116597],{},"autoUnlock"," flag is set. ",[941,116600,116601],{},"'sealed'"," entries read without matching providers keep the raw base64 sealed bytes in ",[941,116604,25201],{},[1591,116606,116608],{"id":116607},"noydbpodheader",[941,116609,115884],{},[937,116611,116612],{},[1092,116613,115854,116614,1381],{},[941,116615,115884],{},[1338,116617,116618,116628],{},[1341,116619,116620],{},[1344,116621,116622,116624,116626],{},[1347,116623,101104],{},[1347,116625,20194],{},[1347,116627,20200],{},[1357,116629,116630,116645,116658,116671,116684,116702,116716,116738],{},[1344,116631,116632,116636,116640],{},[1362,116633,116634],{},[941,116635,36353],{},[1362,116637,116638],{},[941,116639,20636],{},[1362,116641,116642,116643,957],{},"Bumped on layout changes (currently ",[941,116644,5437],{},[1344,116646,116647,116651,116655],{},[1362,116648,116649],{},[941,116650,36368],{},[1362,116652,116653],{},[941,116654,7259],{},[1362,116656,116657],{},"The vault's opaque ULID (26-char Crockford base32).",[1344,116659,116660,116664,116668],{},[1362,116661,116662],{},[941,116663,36388],{},[1362,116665,116666],{},[941,116667,20636],{},[1362,116669,116670],{},"Compressed body length — completeness check without decompressing.",[1344,116672,116673,116677,116681],{},[1362,116674,116675],{},[941,116676,36404],{},[1362,116678,116679],{},[941,116680,7259],{},[1362,116682,116683],{},"SHA-256 of the compressed body (lowercase hex) — integrity check without decompressing.",[1344,116685,116686,116691,116696],{},[1362,116687,116688],{},[941,116689,116690],{},"cover?",[1362,116692,116693],{},[941,116694,116695],{},"Cover",[1362,116697,116698,116699,116701],{},"Owner-curated label fields (name, icon, …) — see ",[1007,116700,645],{"href":645},". Untrusted hint; the encrypted body stays the source of truth.",[1344,116703,116704,116708,116713],{},[1362,116705,116706],{},[941,116707,116586],{},[1362,116709,116710],{},[941,116711,116712],{},"'unsealed' | 'sealed'",[1362,116714,116715],{},"Indicator that the body carries auto-unlock credentials — visible pre-download so listing UIs can warn.",[1344,116717,116718,116723,116728],{},[1362,116719,116720],{},[941,116721,116722],{},"bundleKind?",[1362,116724,116725],{},[941,116726,116727],{},"'snapshot' | 'extracted-partition'",[1362,116729,116730,116731,116734,116735,1520],{},"The bundle's lifecycle role; ",[941,116732,116733],{},"'extracted-partition'"," bundles are re-keyed projections awaiting adoption — see ",[1007,116736,14626],{"href":116737},"transferable-partitions",[1344,116739,116740,116745,116750],{},[1362,116741,116742],{},[941,116743,116744],{},"transferSeal?",[1362,116746,116747],{},[941,116748,116749],{},"{ v: 1, alg, sealId }",[1362,116751,116752,116753,1520],{},"Metadata-only indicator, present iff ",[941,116754,116755],{},"bundleKind === 'extracted-partition'",[937,116757,116758,116759,116762],{},"Any other header key causes ",[941,116760,116761],{},"validateBundleHeader"," to throw. Deliberately forbidden: vault\u002Ftenant name, exporter identity, export timestamps, and KDF parameters or salts.",[1022,116764,116766],{"id":116765},"re-keying-pipeline","Re-keying pipeline",[1234,116768,116769,116774,116781,116795,116807],{},[1114,116770,116771,116773],{},[941,116772,6820],{}," produces the canonical backup JSON (records + source keyring + ledger).",[1114,116775,116776,116777,116780],{},"Records in ",[1007,116778,6875],{"href":116779},"..\u002Fglossary\u002Fsatellite-collections"," whose base row is dead are dropped unconditionally, before any other filter.",[1114,116782,20818,116783,36059,116785,116787,116788,116790,116791,116794],{},[941,116784,115576],{},[941,116786,115580],{},") is set, the source keyring is ",[950,116789,115584],{}," with one freshly derived ",[941,116792,116793],{},"KeyringFile"," per recipient: each recipient's key-encryption key is derived from their secret + a fresh salt via PBKDF2-SHA256 (600,000 iterations); the per-collection data-encryption keys (DEKs) are unwrapped from the source keyring once and re-wrapped under each recipient's key with AES-KW.",[1114,116796,116797,116798,116800,116801,968,116803,968,116805,957],{},"Plaintext filters (",[941,116799,3016],{},") run, then metadata-only slice filters (",[941,116802,2698],{},[941,116804,7883],{},[941,116806,116151],{},[1114,116808,116809],{},"The pruned, re-keyed JSON is compressed and wrapped in the container.",[937,116811,116812,116813,116815,116816,116818],{},"Record ciphertext is ",[950,116814,18391],{}," rewritten — re-keying touches only the small wrapped-DEK blobs (32 bytes each), so re-keying a multi-GB bundle takes milliseconds. The privilege-escalation guard from ",[941,116817,10831],{}," is reused: every DEK wrapped into a recipient's slot must come from the source's own DEK set.",[937,116820,116821,116822,116824,116825,116828,116829,116832],{},"After re-keying, the ",[941,116823,115576],{}," array materialises as ",[941,116826,116827],{},"Record\u003CuserId, KeyringFile>"," in the bundle's ",[941,116830,116831],{},"keyrings"," field — structurally identical to a live vault's multi-user keyring map.",[1022,116834,116836],{"id":116835},"auto-unlock-credentials","Auto-unlock credentials",[937,116838,116839],{},"A bundle can carry per-user credentials so the recipient's app can open it without prompting:",[1111,116841,116842,116853],{},[1114,116843,116844,116848,116849,116852],{},[950,116845,116846],{},[941,116847,116208],{}," — plaintext, ",[941,116850,116851],{},"policy: 'public-by-design'"," required. Anyone holding the file can read them; meant for demo data, sample vaults, and onboarding.",[1114,116854,116855,116862,116863,116865,116866,10313,116868,116871,116872,116874],{},[950,116856,116857,28806,116859],{},[941,116858,116243],{},[941,116860,116861],{},"mode: 'self-target'"," — each credential is sealed under a ",[941,116864,14350],{}," you also control on the reading side (same Keychain entry, same KMS account, …). The reader supplies ",[941,116867,116488],{},[941,116869,116870],{},"readPod()","; entries are matched by provider id (",[941,116873,33316],{},") and unsealed transparently.",[1591,116876,116878],{"id":116877},"recipient-target-sealing","Recipient-target sealing",[937,116880,116881,28806,116883,116886,116887,116889,116890,964,116893,116896,116897,116900],{},[941,116882,116243],{},[941,116884,116885],{},"mode: 'recipient-target'"," lets a sender seal credentials for a recipient whose sealing provider the sender does ",[950,116888,1004],{}," hold. The recipient publishes a ",[941,116891,116892],{},"RecipientHint",[941,116894,116895],{},"{ v: 1, pid, alg: 'rsa-oaep-sha256', material: { publicKeyPem } }"," — through any out-of-band channel (email, S3, in-app message). The sender passes, per user, ",[941,116898,116899],{},"{ credential, hint }","; the hub seals each credential against the hint using a hybrid scheme (a fresh AES-256-GCM content key, RSA-OAEP-SHA256-wrapped to the recipient's public key).",[937,116902,116903,116904,3155,116906,6030,116909,116912,116913,116915,116916,997,116918,116920,116921,116923,116924,116926,116927,1520],{},"The sender's provider must implement the ",[941,116905,34334],{},[941,116907,116908],{},"publishRecipientHint()",[941,116910,116911],{},"sealForRecipient()","). Handover-capable providers implement it (for example ",[941,116914,33294],{}," with asymmetric keys); self-only providers such as ",[941,116917,34296],{},[941,116919,33656],{}," do not — the type system rejects them at the call site. The reader side is unchanged: ",[941,116922,33316],{}," dispatch finds the matching local provider, which unseals the hybrid envelope inside the opaque ",[941,116925,55954],{}," bytes. See the ",[1007,116928,116930,116932],{"href":116929},"..\u002Ffamilies\u002Fat\u002F",[941,116931,1139],{}," sealing-provider family",[1022,116934,101425],{"id":101424},[937,116936,116937],{},"Not importing the symbols means they are not in your application bundle — there is nothing to gate.",[1022,116939,114684],{"id":114683},[1111,116941,116942,116949,116959,116966],{},[1114,116943,116944,116948],{},[950,116945,116946],{},[1007,116947,621],{"href":621}," — attached binaries round-trip inside the bundle",[1114,116950,116951,116955,116956,116958],{},[950,116952,116953],{},[1007,116954,697],{"href":697}," — the dump embeds the audit-ledger head; ",[941,116957,116579],{}," verifies the hash chain on restore",[1114,116960,116961,116965],{},[950,116962,116963],{},[1007,116964,856],{"href":856}," — snapshot bytes are produced by this service's writer",[1114,116967,116968,116972],{},[950,116969,116970],{},[1007,116971,808],{"href":808}," — pod stores as one leg of a multi-store setup",[1022,116974,6825],{"id":6824},[1111,116976,116977,116991,117004,117010,117013,117025],{},[1114,116978,116979,116980,116983,116984,116986,116987,116990],{},"Brotli needs ",[941,116981,116982],{},"CompressionStream('br')"," (Node 22+, Chrome 124+, Firefox 122+). ",[941,116985,116077],{}," falls back to gzip; ",[941,116988,116989],{},"compression: 'brotli'"," throws where unsupported. The algorithm is encoded in the prefix byte, so readers handle either transparently.",[1114,116992,116993,116994,116996,116997,116999,117000,117003],{},"The handle is persisted on the vault's own storage (",[941,116995,116026],{},"): repeat exports of the same vault share it, but a ",[950,116998,51264],{}," vault generates its own handle on first export — the dump deliberately excludes ",[941,117001,117002],{},"_meta",", because handle stability is per vault instance, not per content.",[1114,117005,117006,117007,117009],{},"The header is normally well under 1 KB but can reach roughly 256 KB when a ",[941,117008,645],{}," with an inline icon is present — size range requests accordingly.",[1114,117011,117012],{},"The header parser rejects unknown keys — you cannot smuggle custom metadata into the header; put it in a record instead.",[1114,117014,117015,997,117017,117019,117020,968,117022,117024],{},[941,117016,115580],{},[941,117018,115576],{}," are mutually exclusive, as are ",[941,117021,116208],{},[941,117023,116243],{},", and their deprecated predecessors — violations throw at write time.",[1114,117026,117027,117029,117030,117032],{},[941,117028,116870],{}," never decrypts records; restoring re-keyed bundles requires opening the vault with a recipient credential, not ",[941,117031,116579],{}," under the source session.",[1022,117034,2621],{"id":2620},[1111,117036,117037,117042,117050,117055,117060,117075,117082],{},[1114,117038,117039,117041],{},[1007,117040,856],{"href":856}," — checkpoint\u002Frestore built on this container",[1114,117043,117044,964,117046,117049],{},[1007,117045,14626],{"href":116737},[941,117047,117048],{},"extracted-partition"," bundles and adoption",[1114,117051,117052,117054],{},[1007,117053,645],{"href":645}," — the owner-curated header labels",[1114,117056,117057,117059],{},[1007,117058,21],{"href":60250}," — KEK\u002FDEK model, AES-GCM envelopes",[1114,117061,117062,117067,117068,1324,117070,1261,117072,117074],{},[1007,117063,117064,117066],{"href":9786},[941,117065,1779],{}," storage family"," — includes the ",[941,117069,9551],{},[941,117071,49740],{},[941,117073,49743],{}," helpers",[1114,117076,117077,117081],{},[1007,117078,117079,116932],{"href":116929},[941,117080,1139],{}," — sealing keys for auto-unlock",[1114,117083,101581,117084,1520],{},[1007,117085,117087],{"href":92789,"rel":117086},[3371],[941,117088,117089],{},"21-with-bundle",[2645,117091,117092],{},"html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}",{"title":1037,"searchDepth":1038,"depth":1038,"links":117094},[117095,117096,117097,117098,117102,117111,117112,117115,117116,117117,117118],{"id":29856,"depth":1041,"text":29857},{"id":111659,"depth":1041,"text":111660},{"id":99863,"depth":1041,"text":99864},{"id":29892,"depth":1041,"text":29893,"children":117099},[117100,117101],{"id":114997,"depth":1038,"text":114998},{"id":115569,"depth":1038,"text":115570},{"id":41259,"depth":1041,"text":101012,"children":117103},[117104,117105,117106,117107,117108,117109,117110],{"id":115835,"depth":1038,"text":115836},{"id":115996,"depth":1038,"text":115997},{"id":116042,"depth":1038,"text":71403},{"id":116303,"depth":1038,"text":116306},{"id":116462,"depth":1038,"text":116465},{"id":116529,"depth":1038,"text":71468},{"id":116607,"depth":1038,"text":115884},{"id":116765,"depth":1041,"text":116766},{"id":116835,"depth":1041,"text":116836,"children":117113},[117114],{"id":116877,"depth":1038,"text":116878},{"id":101424,"depth":1041,"text":101425},{"id":114683,"depth":1041,"text":114684},{"id":6824,"depth":1041,"text":6825},{"id":2620,"depth":1041,"text":2621},"Write and read .noydb files — a portable single-file vault container with a minimum-disclosure header and optional re-keyed export to recipients.",{},{"title":629,"description":117119},"IdahF3Th6-lr85oYWXV4ndfQkqVzYTXLoVmTYv06IzY",{"id":117124,"title":633,"access":12,"apiToc":929,"body":117125,"category":1042,"description":120792,"extension":1044,"meta":120793,"navigation":1042,"nest":1047,"path":634,"seo":120794,"sinceVersion":1042,"status":1049,"stem":635,"subtitle":1042,"tier":1050,"tocExternal":1042,"__hash__":120795},"docs\u002Fservices\u002Fclassified.md",{"type":931,"value":117126,"toc":120766},[117127,117129,117132,117134,117140,117146,117189,117207,117232,117234,117255,117257,117288,117290,117312,117650,117652,117656,117666,117924,118105,118111,118130,118182,118359,118363,118368,118472,118487,118491,118500,118811,118833,118842,118981,118989,119007,119011,119035,119288,119298,119316,119323,119340,119363,119374,119539,119554,119596,119689,119697,119737,119748,119750,119755,119771,119791,119798,120023,120064,120076,120082,120190,120194,120262,120266,120372,120384,120388,120391,120518,120520,120695,120697,120763],[934,117128,633],{"id":633},[46871,117130],{":rows":117131},"[{\"label\":\"Subpath\",\"value\":\"`@noy-db\u002Fhub\u002Fclassified`\"},{\"label\":\"Factory\",\"value\":\"`withClassified()`\"},{\"label\":\"Layer\",\"value\":\"`with-shape`\"},{\"label\":\"Cluster\",\"value\":\"D — Data Shape\"},{\"label\":\"LOC cost\",\"value\":\"~1,150 (off-bundle when not opted in)\"}]",[1022,117133,29857],{"id":29856},[937,117135,117136,117137,117139],{},"Some fields are more dangerous than the rest of the record: card numbers, passwords,\nbirth dates, security answers. The classified service lets you declare those fields\n",[950,117138,113219],{},", with a behavioral preset, and the hub enforces the whole lifecycle for you:\nvalidation on write, how (or whether) the value is persisted, what a list may show, and\nhow the value can ever come back out.",[937,117141,117142,117143,2311],{},"Every classified field has exactly one ",[950,117144,117145],{},"storage form",[1111,117147,117148,117167,117182],{},[1114,117149,117150,117155,117156,117158,117159,117162,117163,117166],{},[950,117151,117152],{},[941,117153,117154],{},"recoverable"," — the value is stored encrypted and can be read back, but only through\na deliberate, auditable ",[941,117157,22149],{}," call. Normal reads return an opaque ",[950,117160,117161],{},"sealed handle","\n(it logs and serializes as ",[941,117164,117165],{},"\"[sealed]\"",", never the value).",[1114,117168,117169,117174,117175,117177,117178,117181],{},[950,117170,117171],{},[941,117172,117173],{},"digest-only"," — the value itself is never stored; only a salted, key-bound digest\nis. You can ",[1092,117176,18051],{}," a candidate against it (",[941,117179,117180],{},"verify()","), never read it back. This is\nhow passwords and secret answers work.",[1114,117183,117184,117188],{},[950,117185,117186],{},[941,117187,18391],{}," — the value must not be persisted at all (a card's CVC). Writing it throws.",[937,117190,117191,117192,117195,117196,968,117199,117202,117203,117206],{},"Alongside the protected value, presets can materialize ",[950,117193,117194],{},"riders"," — safe derived\ncompanion fields (",[941,117197,117198],{},"cardNumber_last4",[941,117200,117201],{},"email_domain",") stored as plain fields — and\ndeclare a ",[950,117204,117205],{},"list projection"," (mask, omit, or rider) that tells list renderers and\nexporters what to show instead of the value.",[937,117208,10993,117209,117211,117212,117214,117215,117218,117219,117221,117222,117224,117225,117227,117228,117231],{},[941,117210,111464],{},", a classified declaration is also a\n",[1007,117213,896],{"href":897}," field-feature binding carrying an enforced ",[950,117216,117217],{},"security posture"," —\nsealed-at-rest, deterministic-exact-only querying, non-exportable, forgettable. The posture is\nconsulted generically by query, export, and ",[1007,117220,685],{"href":686}," (no classified-specific\nspecial cases), and — through the dependency graph — it is ",[950,117223,9603],{}," by any\n",[1007,117226,637],{"href":638}," field that declares a classified source in its ",[941,117229,117230],{},"deps",": the\nderived value is sealed or redacted too, instead of silently leaking plaintext into an ordinary\nfield.",[1022,117233,111660],{"id":111659},[1111,117235,117236,117239,117242,117245],{},[1114,117237,117238],{},"Storing payment-card data without hand-rolling PCI-style handling (PAN sealed,\nlast4\u002FBIN available for display, CVC refused at the write boundary)",[1114,117240,117241],{},"Login secrets and account-recovery answers: verify a candidate without ever being able\nto read the stored value, including k-of-n \"answer 2 of 3 questions\" challenges",[1114,117243,117244],{},"PII (emails, phone numbers, birth dates) that lists should show masked, with an\naudited path to the full value for support cases",[1114,117246,117247,117248,117251,117252],{},"Looking up ",[1092,117249,117250],{},"which record"," holds a given secret (invite codes, API tokens) without\nstoring the secret — the opt-in equatable blind index and ",[941,117253,117254],{},"findByDigest",[1022,117256,70093],{"id":70092},[1111,117258,117259,117267,117278],{},[1114,117260,117261,117266],{},[950,117262,117263,117265],{},[941,117264,22770],{}," declared on the collection"," — this feature reads per-field declarations; the factory alone does nothing without them.",[1114,117268,117269,117274,117275,117277],{},[1092,117270,110954,117271,117273],{},[941,117272,117254],{}," on equatable digests:"," the collection must be opened with ",[941,117276,55759],{}," (the \"Find by digest\" section below explains the equality-search trade-off).",[1114,117279,117280,117283,117284,27678,117286,1520],{},[1092,117281,117282],{},"For erasure flows:"," pairs with ",[941,117285,107514],{},[1007,117287,685],{"href":686},[1022,117289,99864],{"id":99863},[937,117291,117292,117293,117295,117296,968,117299,968,117301,968,117304,117306,117307,117309,117310,1520],{},"Declaring ",[941,117294,22770],{}," on a collection is always available — write enforcement,\nvalidation, and riders run with no factory. The four read-egress operations\n(",[941,117297,117298],{},"reveal",[941,117300,18051],{},[941,117302,117303],{},"verifyGroup",[941,117305,117254],{},") are tree-shaken behind\n",[941,117308,22007],{},"; without it they throw ",[941,117311,22152],{},[2217,117313,117315],{"className":2219,"code":117314,"language":2221,"meta":1037,"style":1037},"import { createNoydb } from '@noy-db\u002Fhub'\nimport { classified, withClassified } from '@noy-db\u002Fhub\u002Fclassified'\nimport { toMemory } from '@noy-db\u002Fto-memory'\n\nconst db = await createNoydb({\n  store: toMemory(),\n  user: 'owner',\n  secret: 'correct horse battery staple',\n  classifiedStrategy: withClassified(),   \u002F\u002F enables reveal \u002F verify \u002F verifyGroup \u002F findByDigest\n})\n\nconst vault = await db.openVault('crm')\n\nconst customers = vault.collection('customers', {\n  classifiedFields: {\n    \u002F\u002F A group claims several record fields with one preset (differential policy per member):\n    card: classified.creditCard({ pan: 'cardNumber', expiry: 'cardExpiry', cvc: 'cardCvc' }),\n    \u002F\u002F A single-field entry: the key is the record field name.\n    email: classified.email(),\n    dob: classified.birthDate(),\n  },\n})\n",[941,117316,117317,117335,117359,117377,117381,117397,117409,117423,117437,117453,117459,117463,117490,117494,117522,117531,117536,117600,117605,117622,117640,117644],{"__ignoreMap":1037},[2225,117318,117319,117321,117323,117325,117327,117329,117331,117333],{"class":2227,"line":2228},[2225,117320,2232],{"class":2231},[2225,117322,2236],{"class":2235},[2225,117324,2744],{"class":2239},[2225,117326,2243],{"class":2235},[2225,117328,2246],{"class":2231},[2225,117330,2249],{"class":2235},[2225,117332,1086],{"class":2252},[2225,117334,2256],{"class":2235},[2225,117336,117337,117339,117341,117344,117346,117349,117351,117353,117355,117357],{"class":2227,"line":1041},[2225,117338,2232],{"class":2231},[2225,117340,2236],{"class":2235},[2225,117342,117343],{"class":2239}," classified",[2225,117345,2298],{"class":2235},[2225,117347,117348],{"class":2239}," withClassified",[2225,117350,2243],{"class":2235},[2225,117352,2246],{"class":2231},[2225,117354,2249],{"class":2235},[2225,117356,73151],{"class":2252},[2225,117358,2256],{"class":2235},[2225,117360,117361,117363,117365,117367,117369,117371,117373,117375],{"class":2227,"line":1038},[2225,117362,2232],{"class":2231},[2225,117364,2236],{"class":2235},[2225,117366,68576],{"class":2239},[2225,117368,2243],{"class":2235},[2225,117370,2246],{"class":2231},[2225,117372,2249],{"class":2235},[2225,117374,9638],{"class":2252},[2225,117376,2256],{"class":2235},[2225,117378,117379],{"class":2227,"line":2288},[2225,117380,2285],{"emptyLinePlaceholder":2284},[2225,117382,117383,117385,117387,117389,117391,117393,117395],{"class":2227,"line":2304},[2225,117384,2775],{"class":2774},[2225,117386,2778],{"class":2239},[2225,117388,2781],{"class":2235},[2225,117390,2784],{"class":2231},[2225,117392,2744],{"class":2291},[2225,117394,2789],{"class":2239},[2225,117396,4200],{"class":2235},[2225,117398,117399,117401,117403,117405,117407],{"class":2227,"line":2356},[2225,117400,15608],{"class":2307},[2225,117402,2311],{"class":2235},[2225,117404,68576],{"class":2291},[2225,117406,4228],{"class":2239},[2225,117408,1707],{"class":2235},[2225,117410,117411,117413,117415,117417,117419,117421],{"class":2227,"line":2830},[2225,117412,4205],{"class":2307},[2225,117414,2311],{"class":2235},[2225,117416,2249],{"class":2235},[2225,117418,9898],{"class":2252},[2225,117420,2805],{"class":2235},[2225,117422,1707],{"class":2235},[2225,117424,117425,117427,117429,117431,117433,117435],{"class":2227,"line":2861},[2225,117426,10423],{"class":2307},[2225,117428,2311],{"class":2235},[2225,117430,2249],{"class":2235},[2225,117432,40260],{"class":2252},[2225,117434,2805],{"class":2235},[2225,117436,1707],{"class":2235},[2225,117438,117439,117442,117444,117446,117448,117450],{"class":2227,"line":4246},[2225,117440,117441],{"class":2307},"  classifiedStrategy",[2225,117443,2311],{"class":2235},[2225,117445,117348],{"class":2291},[2225,117447,4228],{"class":2239},[2225,117449,2298],{"class":2235},[2225,117451,117452],{"class":2763},"   \u002F\u002F enables reveal \u002F verify \u002F verifyGroup \u002F findByDigest\n",[2225,117454,117455,117457],{"class":2227,"line":4253},[2225,117456,2359],{"class":2235},[2225,117458,2362],{"class":2239},[2225,117460,117461],{"class":2227,"line":4259},[2225,117462,2285],{"emptyLinePlaceholder":2284},[2225,117464,117465,117467,117469,117471,117473,117475,117477,117479,117481,117483,117486,117488],{"class":2227,"line":6548},[2225,117466,2775],{"class":2774},[2225,117468,2835],{"class":2239},[2225,117470,2781],{"class":2235},[2225,117472,2784],{"class":2231},[2225,117474,2842],{"class":2239},[2225,117476,1520],{"class":2235},[2225,117478,2847],{"class":2291},[2225,117480,2789],{"class":2239},[2225,117482,2805],{"class":2235},[2225,117484,117485],{"class":2252},"crm",[2225,117487,2805],{"class":2235},[2225,117489,2362],{"class":2239},[2225,117491,117492],{"class":2227,"line":6576},[2225,117493,2285],{"emptyLinePlaceholder":2284},[2225,117495,117496,117498,117501,117503,117505,117507,117509,117511,117513,117516,117518,117520],{"class":2227,"line":6582},[2225,117497,2775],{"class":2774},[2225,117499,117500],{"class":2239}," customers ",[2225,117502,2781],{"class":2235},[2225,117504,6466],{"class":2239},[2225,117506,1520],{"class":2235},[2225,117508,6471],{"class":2291},[2225,117510,2789],{"class":2239},[2225,117512,2805],{"class":2235},[2225,117514,117515],{"class":2252},"customers",[2225,117517,2805],{"class":2235},[2225,117519,2298],{"class":2235},[2225,117521,2301],{"class":2235},[2225,117523,117524,117527,117529],{"class":2227,"line":6587},[2225,117525,117526],{"class":2307},"  classifiedFields",[2225,117528,2311],{"class":2235},[2225,117530,2301],{"class":2235},[2225,117532,117533],{"class":2227,"line":7174},[2225,117534,117535],{"class":2763},"    \u002F\u002F A group claims several record fields with one preset (differential policy per member):\n",[2225,117537,117538,117541,117543,117545,117547,117550,117552,117554,117557,117559,117561,117564,117566,117568,117571,117573,117575,117578,117580,117582,117585,117587,117589,117592,117594,117596,117598],{"class":2227,"line":7180},[2225,117539,117540],{"class":2307},"    card",[2225,117542,2311],{"class":2235},[2225,117544,117343],{"class":2239},[2225,117546,1520],{"class":2235},[2225,117548,117549],{"class":2291},"creditCard",[2225,117551,2789],{"class":2239},[2225,117553,2792],{"class":2235},[2225,117555,117556],{"class":2307}," pan",[2225,117558,2311],{"class":2235},[2225,117560,2249],{"class":2235},[2225,117562,117563],{"class":2252},"cardNumber",[2225,117565,2805],{"class":2235},[2225,117567,2298],{"class":2235},[2225,117569,117570],{"class":2307}," expiry",[2225,117572,2311],{"class":2235},[2225,117574,2249],{"class":2235},[2225,117576,117577],{"class":2252},"cardExpiry",[2225,117579,2805],{"class":2235},[2225,117581,2298],{"class":2235},[2225,117583,117584],{"class":2307}," cvc",[2225,117586,2311],{"class":2235},[2225,117588,2249],{"class":2235},[2225,117590,117591],{"class":2252},"cardCvc",[2225,117593,2805],{"class":2235},[2225,117595,2243],{"class":2235},[2225,117597,1381],{"class":2239},[2225,117599,1707],{"class":2235},[2225,117601,117602],{"class":2227,"line":7220},[2225,117603,117604],{"class":2763},"    \u002F\u002F A single-field entry: the key is the record field name.\n",[2225,117606,117607,117610,117612,117614,117616,117618,117620],{"class":2227,"line":7225},[2225,117608,117609],{"class":2307},"    email",[2225,117611,2311],{"class":2235},[2225,117613,117343],{"class":2239},[2225,117615,1520],{"class":2235},[2225,117617,81022],{"class":2291},[2225,117619,4228],{"class":2239},[2225,117621,1707],{"class":2235},[2225,117623,117624,117627,117629,117631,117633,117636,117638],{"class":2227,"line":7231},[2225,117625,117626],{"class":2307},"    dob",[2225,117628,2311],{"class":2235},[2225,117630,117343],{"class":2239},[2225,117632,1520],{"class":2235},[2225,117634,117635],{"class":2291},"birthDate",[2225,117637,4228],{"class":2239},[2225,117639,1707],{"class":2235},[2225,117641,117642],{"class":2227,"line":7267},[2225,117643,19017],{"class":2235},[2225,117645,117646,117648],{"class":2227,"line":7272},[2225,117647,2359],{"class":2235},[2225,117649,2362],{"class":2239},[1022,117651,29893],{"id":29892},[1591,117653,117655],{"id":117654},"write-time-enforcement-sealed-reads-riders","Write-time enforcement, sealed reads, riders",[937,117657,117658,117659,117661,117662,117665],{},"On ",[941,117660,20953],{},", the hub rejects ",[941,117663,117664],{},"storage:'never'"," fields, runs the preset validator, and\nmaterializes the riders — all before encryption, inside the trust boundary:",[2217,117667,117669],{"className":2219,"code":117668,"language":2221,"meta":1037,"style":1037},"await customers.put('c1', {\n  holder: 'Nok P.',\n  cardNumber: '4242424242424242',\n  cardExpiry: '12\u002F28',\n  email: 'nok@example.com',\n  dob: '1990-04-12',\n})\n\n\u002F\u002F storage:'never' refuses persistence — validate the CVC at capture, drop it before put():\nawait customers.put('c2', { cardNumber: '4242424242424242', cardCvc: '123' })\n\u002F\u002F → throws ClassifiedNeverStoredError\n\n\u002F\u002F preset validators run on every write:\nawait customers.put('c3', { cardNumber: '1234' })\n\u002F\u002F → throws ClassifiedValidationError: not a Luhn-valid card number\n\nconst record = await customers.get('c1')\n",[941,117670,117671,117694,117710,117726,117742,117757,117773,117779,117783,117788,117838,117843,117847,117852,117889,117894,117898],{"__ignoreMap":1037},[2225,117672,117673,117675,117678,117680,117682,117684,117686,117688,117690,117692],{"class":2227,"line":2228},[2225,117674,6500],{"class":2231},[2225,117676,117677],{"class":2239}," customers",[2225,117679,1520],{"class":2235},[2225,117681,3161],{"class":2291},[2225,117683,2789],{"class":2239},[2225,117685,2805],{"class":2235},[2225,117687,62700],{"class":2252},[2225,117689,2805],{"class":2235},[2225,117691,2298],{"class":2235},[2225,117693,2301],{"class":2235},[2225,117695,117696,117699,117701,117703,117706,117708],{"class":2227,"line":1041},[2225,117697,117698],{"class":2307},"  holder",[2225,117700,2311],{"class":2235},[2225,117702,2249],{"class":2235},[2225,117704,117705],{"class":2252},"Nok P.",[2225,117707,2805],{"class":2235},[2225,117709,1707],{"class":2235},[2225,117711,117712,117715,117717,117719,117722,117724],{"class":2227,"line":1038},[2225,117713,117714],{"class":2307},"  cardNumber",[2225,117716,2311],{"class":2235},[2225,117718,2249],{"class":2235},[2225,117720,117721],{"class":2252},"4242424242424242",[2225,117723,2805],{"class":2235},[2225,117725,1707],{"class":2235},[2225,117727,117728,117731,117733,117735,117738,117740],{"class":2227,"line":2288},[2225,117729,117730],{"class":2307},"  cardExpiry",[2225,117732,2311],{"class":2235},[2225,117734,2249],{"class":2235},[2225,117736,117737],{"class":2252},"12\u002F28",[2225,117739,2805],{"class":2235},[2225,117741,1707],{"class":2235},[2225,117743,117744,117746,117748,117750,117753,117755],{"class":2227,"line":2304},[2225,117745,43743],{"class":2307},[2225,117747,2311],{"class":2235},[2225,117749,2249],{"class":2235},[2225,117751,117752],{"class":2252},"nok@example.com",[2225,117754,2805],{"class":2235},[2225,117756,1707],{"class":2235},[2225,117758,117759,117762,117764,117766,117769,117771],{"class":2227,"line":2356},[2225,117760,117761],{"class":2307},"  dob",[2225,117763,2311],{"class":2235},[2225,117765,2249],{"class":2235},[2225,117767,117768],{"class":2252},"1990-04-12",[2225,117770,2805],{"class":2235},[2225,117772,1707],{"class":2235},[2225,117774,117775,117777],{"class":2227,"line":2830},[2225,117776,2359],{"class":2235},[2225,117778,2362],{"class":2239},[2225,117780,117781],{"class":2227,"line":2861},[2225,117782,2285],{"emptyLinePlaceholder":2284},[2225,117784,117785],{"class":2227,"line":4246},[2225,117786,117787],{"class":2763},"\u002F\u002F storage:'never' refuses persistence — validate the CVC at capture, drop it before put():\n",[2225,117789,117790,117792,117794,117796,117798,117800,117802,117804,117806,117808,117810,117813,117815,117817,117819,117821,117823,117826,117828,117830,117832,117834,117836],{"class":2227,"line":4253},[2225,117791,6500],{"class":2231},[2225,117793,117677],{"class":2239},[2225,117795,1520],{"class":2235},[2225,117797,3161],{"class":2291},[2225,117799,2789],{"class":2239},[2225,117801,2805],{"class":2235},[2225,117803,62762],{"class":2252},[2225,117805,2805],{"class":2235},[2225,117807,2298],{"class":2235},[2225,117809,2236],{"class":2235},[2225,117811,117812],{"class":2307}," cardNumber",[2225,117814,2311],{"class":2235},[2225,117816,2249],{"class":2235},[2225,117818,117721],{"class":2252},[2225,117820,2805],{"class":2235},[2225,117822,2298],{"class":2235},[2225,117824,117825],{"class":2307}," cardCvc",[2225,117827,2311],{"class":2235},[2225,117829,2249],{"class":2235},[2225,117831,94223],{"class":2252},[2225,117833,2805],{"class":2235},[2225,117835,2243],{"class":2235},[2225,117837,2362],{"class":2239},[2225,117839,117840],{"class":2227,"line":4259},[2225,117841,117842],{"class":2763},"\u002F\u002F → throws ClassifiedNeverStoredError\n",[2225,117844,117845],{"class":2227,"line":6548},[2225,117846,2285],{"emptyLinePlaceholder":2284},[2225,117848,117849],{"class":2227,"line":6576},[2225,117850,117851],{"class":2763},"\u002F\u002F preset validators run on every write:\n",[2225,117853,117854,117856,117858,117860,117862,117864,117866,117868,117870,117872,117874,117876,117878,117880,117883,117885,117887],{"class":2227,"line":6582},[2225,117855,6500],{"class":2231},[2225,117857,117677],{"class":2239},[2225,117859,1520],{"class":2235},[2225,117861,3161],{"class":2291},[2225,117863,2789],{"class":2239},[2225,117865,2805],{"class":2235},[2225,117867,62825],{"class":2252},[2225,117869,2805],{"class":2235},[2225,117871,2298],{"class":2235},[2225,117873,2236],{"class":2235},[2225,117875,117812],{"class":2307},[2225,117877,2311],{"class":2235},[2225,117879,2249],{"class":2235},[2225,117881,117882],{"class":2252},"1234",[2225,117884,2805],{"class":2235},[2225,117886,2243],{"class":2235},[2225,117888,2362],{"class":2239},[2225,117890,117891],{"class":2227,"line":6587},[2225,117892,117893],{"class":2763},"\u002F\u002F → throws ClassifiedValidationError: not a Luhn-valid card number\n",[2225,117895,117896],{"class":2227,"line":7174},[2225,117897,2285],{"emptyLinePlaceholder":2284},[2225,117899,117900,117902,117904,117906,117908,117910,117912,117914,117916,117918,117920,117922],{"class":2227,"line":7180},[2225,117901,2775],{"class":2774},[2225,117903,43726],{"class":2239},[2225,117905,2781],{"class":2235},[2225,117907,2784],{"class":2231},[2225,117909,117677],{"class":2239},[2225,117911,1520],{"class":2235},[2225,117913,3158],{"class":2291},[2225,117915,2789],{"class":2239},[2225,117917,2805],{"class":2235},[2225,117919,62700],{"class":2252},[2225,117921,2805],{"class":2235},[2225,117923,2362],{"class":2239},[2217,117925,117927],{"className":6604,"code":117926,"language":141,"meta":1037,"style":1037},"{\n  \"holder\": \"Nok P.\",\n  \"cardNumber\": \"[sealed]\",\n  \"cardExpiry\": \"[sealed]\",\n  \"email\": \"[sealed]\",\n  \"dob\": \"[sealed]\",\n  \"cardNumber_last4\": \"4242\",\n  \"cardNumber_bin\": \"424242\",\n  \"email_domain\": \"example.com\",\n  \"dob_yob\": \"1990\"\n}\n",[941,117928,117929,117933,117952,117971,117989,118007,118026,118045,118065,118083,118101],{"__ignoreMap":1037},[2225,117930,117931],{"class":2227,"line":2228},[2225,117932,4200],{"class":2235},[2225,117934,117935,117937,117940,117942,117944,117946,117948,117950],{"class":2227,"line":1041},[2225,117936,9368],{"class":2235},[2225,117938,117939],{"class":2774},"holder",[2225,117941,6620],{"class":2235},[2225,117943,2311],{"class":2235},[2225,117945,6614],{"class":2235},[2225,117947,117705],{"class":2252},[2225,117949,6620],{"class":2235},[2225,117951,1707],{"class":2235},[2225,117953,117954,117956,117958,117960,117962,117964,117967,117969],{"class":2227,"line":1038},[2225,117955,9368],{"class":2235},[2225,117957,117563],{"class":2774},[2225,117959,6620],{"class":2235},[2225,117961,2311],{"class":2235},[2225,117963,6614],{"class":2235},[2225,117965,117966],{"class":2252},"[sealed]",[2225,117968,6620],{"class":2235},[2225,117970,1707],{"class":2235},[2225,117972,117973,117975,117977,117979,117981,117983,117985,117987],{"class":2227,"line":2288},[2225,117974,9368],{"class":2235},[2225,117976,117577],{"class":2774},[2225,117978,6620],{"class":2235},[2225,117980,2311],{"class":2235},[2225,117982,6614],{"class":2235},[2225,117984,117966],{"class":2252},[2225,117986,6620],{"class":2235},[2225,117988,1707],{"class":2235},[2225,117990,117991,117993,117995,117997,117999,118001,118003,118005],{"class":2227,"line":2304},[2225,117992,9368],{"class":2235},[2225,117994,81022],{"class":2774},[2225,117996,6620],{"class":2235},[2225,117998,2311],{"class":2235},[2225,118000,6614],{"class":2235},[2225,118002,117966],{"class":2252},[2225,118004,6620],{"class":2235},[2225,118006,1707],{"class":2235},[2225,118008,118009,118011,118014,118016,118018,118020,118022,118024],{"class":2227,"line":2356},[2225,118010,9368],{"class":2235},[2225,118012,118013],{"class":2774},"dob",[2225,118015,6620],{"class":2235},[2225,118017,2311],{"class":2235},[2225,118019,6614],{"class":2235},[2225,118021,117966],{"class":2252},[2225,118023,6620],{"class":2235},[2225,118025,1707],{"class":2235},[2225,118027,118028,118030,118032,118034,118036,118038,118041,118043],{"class":2227,"line":2830},[2225,118029,9368],{"class":2235},[2225,118031,117198],{"class":2774},[2225,118033,6620],{"class":2235},[2225,118035,2311],{"class":2235},[2225,118037,6614],{"class":2235},[2225,118039,118040],{"class":2252},"4242",[2225,118042,6620],{"class":2235},[2225,118044,1707],{"class":2235},[2225,118046,118047,118049,118052,118054,118056,118058,118061,118063],{"class":2227,"line":2861},[2225,118048,9368],{"class":2235},[2225,118050,118051],{"class":2774},"cardNumber_bin",[2225,118053,6620],{"class":2235},[2225,118055,2311],{"class":2235},[2225,118057,6614],{"class":2235},[2225,118059,118060],{"class":2252},"424242",[2225,118062,6620],{"class":2235},[2225,118064,1707],{"class":2235},[2225,118066,118067,118069,118071,118073,118075,118077,118079,118081],{"class":2227,"line":4246},[2225,118068,9368],{"class":2235},[2225,118070,117201],{"class":2774},[2225,118072,6620],{"class":2235},[2225,118074,2311],{"class":2235},[2225,118076,6614],{"class":2235},[2225,118078,96189],{"class":2252},[2225,118080,6620],{"class":2235},[2225,118082,1707],{"class":2235},[2225,118084,118085,118087,118090,118092,118094,118096,118099],{"class":2227,"line":4253},[2225,118086,9368],{"class":2235},[2225,118088,118089],{"class":2774},"dob_yob",[2225,118091,6620],{"class":2235},[2225,118093,2311],{"class":2235},[2225,118095,6614],{"class":2235},[2225,118097,118098],{"class":2252},"1990",[2225,118100,9449],{"class":2235},[2225,118102,118103],{"class":2227,"line":4259},[2225,118104,7351],{"class":2235},[937,118106,118107,118108,118110],{},"The recoverable values come back as sealed handles (shown here as their ",[941,118109,117165],{},"\nJSON form); the riders are ordinary plain fields you can display, query, and index.",[937,118112,118113,118114,118116,118117,118119,118120,118123,118124,118126,118127,118129],{},"The declared ",[950,118115,117205],{}," (mask \u002F omit \u002F rider) is a contract, not automatic\nredaction: kernel reads return sealed handles + riders as above, and\n",[941,118118,1380],{}," serializes each field's projection so UI layers know what to\nrender. ",[941,118121,118122],{},"applyListProjection"," (exported from ",[941,118125,1086],{},") is the vetted helper that\napplies it — the ",[941,118128,1791],{}," exporters use the same function:",[2217,118131,118133],{"className":2219,"code":118132,"language":2221,"meta":1037,"style":1037},"import { applyListProjection } from '@noy-db\u002Fhub'\n\nconst row = applyListProjection(customers.describe(), record)\n",[941,118134,118135,118154,118158],{"__ignoreMap":1037},[2225,118136,118137,118139,118141,118144,118146,118148,118150,118152],{"class":2227,"line":2228},[2225,118138,2232],{"class":2231},[2225,118140,2236],{"class":2235},[2225,118142,118143],{"class":2239}," applyListProjection",[2225,118145,2243],{"class":2235},[2225,118147,2246],{"class":2231},[2225,118149,2249],{"class":2235},[2225,118151,1086],{"class":2252},[2225,118153,2256],{"class":2235},[2225,118155,118156],{"class":2227,"line":1041},[2225,118157,2285],{"emptyLinePlaceholder":2284},[2225,118159,118160,118162,118165,118167,118169,118172,118174,118176,118178,118180],{"class":2227,"line":1038},[2225,118161,2775],{"class":2774},[2225,118163,118164],{"class":2239}," row ",[2225,118166,2781],{"class":2235},[2225,118168,118143],{"class":2291},[2225,118170,118171],{"class":2239},"(customers",[2225,118173,1520],{"class":2235},[2225,118175,6153],{"class":2291},[2225,118177,4228],{"class":2239},[2225,118179,2298],{"class":2235},[2225,118181,43833],{"class":2239},[2217,118183,118185],{"className":6604,"code":118184,"language":141,"meta":1037,"style":1037},"{\n  \"holder\": \"Nok P.\",\n  \"cardNumber\": \"•••• 4242\",\n  \"cardExpiry\": \"••\u002F••\",\n  \"email\": \"•••@example.com\",\n  \"dob\": \"1990-••-••\",\n  \"cardNumber_last4\": \"4242\",\n  \"cardNumber_bin\": \"424242\",\n  \"email_domain\": \"example.com\",\n  \"dob_yob\": \"1990\"\n}\n",[941,118186,118187,118191,118209,118228,118247,118266,118285,118303,118321,118339,118355],{"__ignoreMap":1037},[2225,118188,118189],{"class":2227,"line":2228},[2225,118190,4200],{"class":2235},[2225,118192,118193,118195,118197,118199,118201,118203,118205,118207],{"class":2227,"line":1041},[2225,118194,9368],{"class":2235},[2225,118196,117939],{"class":2774},[2225,118198,6620],{"class":2235},[2225,118200,2311],{"class":2235},[2225,118202,6614],{"class":2235},[2225,118204,117705],{"class":2252},[2225,118206,6620],{"class":2235},[2225,118208,1707],{"class":2235},[2225,118210,118211,118213,118215,118217,118219,118221,118224,118226],{"class":2227,"line":1038},[2225,118212,9368],{"class":2235},[2225,118214,117563],{"class":2774},[2225,118216,6620],{"class":2235},[2225,118218,2311],{"class":2235},[2225,118220,6614],{"class":2235},[2225,118222,118223],{"class":2252},"•••• 4242",[2225,118225,6620],{"class":2235},[2225,118227,1707],{"class":2235},[2225,118229,118230,118232,118234,118236,118238,118240,118243,118245],{"class":2227,"line":2288},[2225,118231,9368],{"class":2235},[2225,118233,117577],{"class":2774},[2225,118235,6620],{"class":2235},[2225,118237,2311],{"class":2235},[2225,118239,6614],{"class":2235},[2225,118241,118242],{"class":2252},"••\u002F••",[2225,118244,6620],{"class":2235},[2225,118246,1707],{"class":2235},[2225,118248,118249,118251,118253,118255,118257,118259,118262,118264],{"class":2227,"line":2304},[2225,118250,9368],{"class":2235},[2225,118252,81022],{"class":2774},[2225,118254,6620],{"class":2235},[2225,118256,2311],{"class":2235},[2225,118258,6614],{"class":2235},[2225,118260,118261],{"class":2252},"•••@example.com",[2225,118263,6620],{"class":2235},[2225,118265,1707],{"class":2235},[2225,118267,118268,118270,118272,118274,118276,118278,118281,118283],{"class":2227,"line":2356},[2225,118269,9368],{"class":2235},[2225,118271,118013],{"class":2774},[2225,118273,6620],{"class":2235},[2225,118275,2311],{"class":2235},[2225,118277,6614],{"class":2235},[2225,118279,118280],{"class":2252},"1990-••-••",[2225,118282,6620],{"class":2235},[2225,118284,1707],{"class":2235},[2225,118286,118287,118289,118291,118293,118295,118297,118299,118301],{"class":2227,"line":2830},[2225,118288,9368],{"class":2235},[2225,118290,117198],{"class":2774},[2225,118292,6620],{"class":2235},[2225,118294,2311],{"class":2235},[2225,118296,6614],{"class":2235},[2225,118298,118040],{"class":2252},[2225,118300,6620],{"class":2235},[2225,118302,1707],{"class":2235},[2225,118304,118305,118307,118309,118311,118313,118315,118317,118319],{"class":2227,"line":2861},[2225,118306,9368],{"class":2235},[2225,118308,118051],{"class":2774},[2225,118310,6620],{"class":2235},[2225,118312,2311],{"class":2235},[2225,118314,6614],{"class":2235},[2225,118316,118060],{"class":2252},[2225,118318,6620],{"class":2235},[2225,118320,1707],{"class":2235},[2225,118322,118323,118325,118327,118329,118331,118333,118335,118337],{"class":2227,"line":4246},[2225,118324,9368],{"class":2235},[2225,118326,117201],{"class":2774},[2225,118328,6620],{"class":2235},[2225,118330,2311],{"class":2235},[2225,118332,6614],{"class":2235},[2225,118334,96189],{"class":2252},[2225,118336,6620],{"class":2235},[2225,118338,1707],{"class":2235},[2225,118340,118341,118343,118345,118347,118349,118351,118353],{"class":2227,"line":4253},[2225,118342,9368],{"class":2235},[2225,118344,118089],{"class":2774},[2225,118346,6620],{"class":2235},[2225,118348,2311],{"class":2235},[2225,118350,6614],{"class":2235},[2225,118352,118098],{"class":2252},[2225,118354,9449],{"class":2235},[2225,118356,118357],{"class":2227,"line":4259},[2225,118358,7351],{"class":2235},[1591,118360,118362],{"id":118361},"audited-reveal","Audited reveal",[937,118364,118365,118367],{},[941,118366,22149],{}," is the single deliberate path from a sealed field back to plaintext. It\ndecrypts one field of one record, and refuses everything that has nothing to reveal:",[2217,118369,118371],{"className":2219,"code":118370,"language":2221,"meta":1037,"style":1037},"await customers.reveal('c1', 'cardNumber')   \u002F\u002F → '4242424242424242'\n\nawait customers.reveal('c1', 'cardCvc')      \u002F\u002F rejects: ClassifiedRevealError — storage:'never', nothing stored\nawait customers.reveal('c1', 'nope')         \u002F\u002F rejects: ClassifiedRevealError — field is not classified\n",[941,118372,118373,118404,118408,118439],{"__ignoreMap":1037},[2225,118374,118375,118377,118379,118381,118383,118385,118387,118389,118391,118393,118395,118397,118399,118401],{"class":2227,"line":2228},[2225,118376,6500],{"class":2231},[2225,118378,117677],{"class":2239},[2225,118380,1520],{"class":2235},[2225,118382,117298],{"class":2291},[2225,118384,2789],{"class":2239},[2225,118386,2805],{"class":2235},[2225,118388,62700],{"class":2252},[2225,118390,2805],{"class":2235},[2225,118392,2298],{"class":2235},[2225,118394,2249],{"class":2235},[2225,118396,117563],{"class":2252},[2225,118398,2805],{"class":2235},[2225,118400,71781],{"class":2239},[2225,118402,118403],{"class":2763},"\u002F\u002F → '4242424242424242'\n",[2225,118405,118406],{"class":2227,"line":1041},[2225,118407,2285],{"emptyLinePlaceholder":2284},[2225,118409,118410,118412,118414,118416,118418,118420,118422,118424,118426,118428,118430,118432,118434,118436],{"class":2227,"line":1038},[2225,118411,6500],{"class":2231},[2225,118413,117677],{"class":2239},[2225,118415,1520],{"class":2235},[2225,118417,117298],{"class":2291},[2225,118419,2789],{"class":2239},[2225,118421,2805],{"class":2235},[2225,118423,62700],{"class":2252},[2225,118425,2805],{"class":2235},[2225,118427,2298],{"class":2235},[2225,118429,2249],{"class":2235},[2225,118431,117591],{"class":2252},[2225,118433,2805],{"class":2235},[2225,118435,74756],{"class":2239},[2225,118437,118438],{"class":2763},"\u002F\u002F rejects: ClassifiedRevealError — storage:'never', nothing stored\n",[2225,118440,118441,118443,118445,118447,118449,118451,118453,118455,118457,118459,118461,118464,118466,118469],{"class":2227,"line":2288},[2225,118442,6500],{"class":2231},[2225,118444,117677],{"class":2239},[2225,118446,1520],{"class":2235},[2225,118448,117298],{"class":2291},[2225,118450,2789],{"class":2239},[2225,118452,2805],{"class":2235},[2225,118454,62700],{"class":2252},[2225,118456,2805],{"class":2235},[2225,118458,2298],{"class":2235},[2225,118460,2249],{"class":2235},[2225,118462,118463],{"class":2252},"nope",[2225,118465,2805],{"class":2235},[2225,118467,118468],{"class":2239},")         ",[2225,118470,118471],{"class":2763},"\u002F\u002F rejects: ClassifiedRevealError — field is not classified\n",[937,118473,118474,118475,118477,118478,118480,118481,118483,118484,118486],{},"When the ",[1007,118476,641],{"href":642}," service is enabled, each ",[941,118479,22149],{}," records\nexactly one ",[941,118482,117298],{}," entry in the consent audit trail (and no extra ",[941,118485,3158],{}," entry), so\n\"who looked at the full card number, when, and why\" is answerable.",[1591,118488,118490],{"id":118489},"verify-without-reveal","Verify without reveal",[937,118492,118493,118494,118496,118497,118499],{},"Digest-only fields are a verdict-only oracle: you can ask \"is this candidate the stored\nvalue?\" but never \"what is the stored value?\". Digest-only storage requires an\nencrypted collection with ",[941,118495,73811],{}," — the digest keys derive from the\nper-record key, so ",[1007,118498,685],{"href":686}," shreds them.",[2217,118501,118503],{"className":2219,"code":118502,"language":2221,"meta":1037,"style":1037},"const users = vault.collection('users', {\n  perRecordKeys: true,                      \u002F\u002F required for digest-only fields\n  classifiedFields: {\n    password: classified.password({ minLength: 12, rotateDays: 90, notLastN: 3 }),\n    petName: classified.secretAnswer(),\n    firstSchool: classified.secretAnswer(),\n  },\n})\n\nawait users.put('u1', {\n  password: 'correct-horse-battery',\n  petName: 'Rex',\n  firstSchool: 'Wat Suan Dok',\n})\n\nawait users.verify('u1', 'password', 'correct-horse-battery')   \u002F\u002F → { ok: true }\nawait users.verify('u1', 'password', 'wrong-guess-1234')        \u002F\u002F → { ok: false }\n",[941,118504,118505,118532,118545,118553,118601,118619,118636,118640,118646,118650,118674,118689,118705,118721,118727,118731,118770],{"__ignoreMap":1037},[2225,118506,118507,118509,118512,118514,118516,118518,118520,118522,118524,118526,118528,118530],{"class":2227,"line":2228},[2225,118508,2775],{"class":2774},[2225,118510,118511],{"class":2239}," users ",[2225,118513,2781],{"class":2235},[2225,118515,6466],{"class":2239},[2225,118517,1520],{"class":2235},[2225,118519,6471],{"class":2291},[2225,118521,2789],{"class":2239},[2225,118523,2805],{"class":2235},[2225,118525,16629],{"class":2252},[2225,118527,2805],{"class":2235},[2225,118529,2298],{"class":2235},[2225,118531,2301],{"class":2235},[2225,118533,118534,118536,118538,118540,118542],{"class":2227,"line":1041},[2225,118535,75763],{"class":2307},[2225,118537,2311],{"class":2235},[2225,118539,2322],{"class":2321},[2225,118541,2298],{"class":2235},[2225,118543,118544],{"class":2763},"                      \u002F\u002F required for digest-only fields\n",[2225,118546,118547,118549,118551],{"class":2227,"line":1038},[2225,118548,117526],{"class":2307},[2225,118550,2311],{"class":2235},[2225,118552,2301],{"class":2235},[2225,118554,118555,118558,118560,118562,118564,118566,118568,118570,118573,118575,118577,118579,118582,118584,118586,118588,118591,118593,118595,118597,118599],{"class":2227,"line":2288},[2225,118556,118557],{"class":2307},"    password",[2225,118559,2311],{"class":2235},[2225,118561,117343],{"class":2239},[2225,118563,1520],{"class":2235},[2225,118565,313],{"class":2291},[2225,118567,2789],{"class":2239},[2225,118569,2792],{"class":2235},[2225,118571,118572],{"class":2307}," minLength",[2225,118574,2311],{"class":2235},[2225,118576,108590],{"class":6527},[2225,118578,2298],{"class":2235},[2225,118580,118581],{"class":2307}," rotateDays",[2225,118583,2311],{"class":2235},[2225,118585,65133],{"class":6527},[2225,118587,2298],{"class":2235},[2225,118589,118590],{"class":2307}," notLastN",[2225,118592,2311],{"class":2235},[2225,118594,14460],{"class":6527},[2225,118596,2243],{"class":2235},[2225,118598,1381],{"class":2239},[2225,118600,1707],{"class":2235},[2225,118602,118603,118606,118608,118610,118612,118615,118617],{"class":2227,"line":2304},[2225,118604,118605],{"class":2307},"    petName",[2225,118607,2311],{"class":2235},[2225,118609,117343],{"class":2239},[2225,118611,1520],{"class":2235},[2225,118613,118614],{"class":2291},"secretAnswer",[2225,118616,4228],{"class":2239},[2225,118618,1707],{"class":2235},[2225,118620,118621,118624,118626,118628,118630,118632,118634],{"class":2227,"line":2356},[2225,118622,118623],{"class":2307},"    firstSchool",[2225,118625,2311],{"class":2235},[2225,118627,117343],{"class":2239},[2225,118629,1520],{"class":2235},[2225,118631,118614],{"class":2291},[2225,118633,4228],{"class":2239},[2225,118635,1707],{"class":2235},[2225,118637,118638],{"class":2227,"line":2830},[2225,118639,19017],{"class":2235},[2225,118641,118642,118644],{"class":2227,"line":2861},[2225,118643,2359],{"class":2235},[2225,118645,2362],{"class":2239},[2225,118647,118648],{"class":2227,"line":4246},[2225,118649,2285],{"emptyLinePlaceholder":2284},[2225,118651,118652,118654,118657,118659,118661,118663,118665,118668,118670,118672],{"class":2227,"line":4253},[2225,118653,6500],{"class":2231},[2225,118655,118656],{"class":2239}," users",[2225,118658,1520],{"class":2235},[2225,118660,3161],{"class":2291},[2225,118662,2789],{"class":2239},[2225,118664,2805],{"class":2235},[2225,118666,118667],{"class":2252},"u1",[2225,118669,2805],{"class":2235},[2225,118671,2298],{"class":2235},[2225,118673,2301],{"class":2235},[2225,118675,118676,118678,118680,118682,118685,118687],{"class":2227,"line":4259},[2225,118677,33135],{"class":2307},[2225,118679,2311],{"class":2235},[2225,118681,2249],{"class":2235},[2225,118683,118684],{"class":2252},"correct-horse-battery",[2225,118686,2805],{"class":2235},[2225,118688,1707],{"class":2235},[2225,118690,118691,118694,118696,118698,118701,118703],{"class":2227,"line":6548},[2225,118692,118693],{"class":2307},"  petName",[2225,118695,2311],{"class":2235},[2225,118697,2249],{"class":2235},[2225,118699,118700],{"class":2252},"Rex",[2225,118702,2805],{"class":2235},[2225,118704,1707],{"class":2235},[2225,118706,118707,118710,118712,118714,118717,118719],{"class":2227,"line":6576},[2225,118708,118709],{"class":2307},"  firstSchool",[2225,118711,2311],{"class":2235},[2225,118713,2249],{"class":2235},[2225,118715,118716],{"class":2252},"Wat Suan Dok",[2225,118718,2805],{"class":2235},[2225,118720,1707],{"class":2235},[2225,118722,118723,118725],{"class":2227,"line":6582},[2225,118724,2359],{"class":2235},[2225,118726,2362],{"class":2239},[2225,118728,118729],{"class":2227,"line":6587},[2225,118730,2285],{"emptyLinePlaceholder":2284},[2225,118732,118733,118735,118737,118739,118741,118743,118745,118747,118749,118751,118753,118755,118757,118759,118761,118763,118765,118767],{"class":2227,"line":7174},[2225,118734,6500],{"class":2231},[2225,118736,118656],{"class":2239},[2225,118738,1520],{"class":2235},[2225,118740,18051],{"class":2291},[2225,118742,2789],{"class":2239},[2225,118744,2805],{"class":2235},[2225,118746,118667],{"class":2252},[2225,118748,2805],{"class":2235},[2225,118750,2298],{"class":2235},[2225,118752,2249],{"class":2235},[2225,118754,313],{"class":2252},[2225,118756,2805],{"class":2235},[2225,118758,2298],{"class":2235},[2225,118760,2249],{"class":2235},[2225,118762,118684],{"class":2252},[2225,118764,2805],{"class":2235},[2225,118766,71781],{"class":2239},[2225,118768,118769],{"class":2763},"\u002F\u002F → { ok: true }\n",[2225,118771,118772,118774,118776,118778,118780,118782,118784,118786,118788,118790,118792,118794,118796,118798,118800,118803,118805,118808],{"class":2227,"line":7180},[2225,118773,6500],{"class":2231},[2225,118775,118656],{"class":2239},[2225,118777,1520],{"class":2235},[2225,118779,18051],{"class":2291},[2225,118781,2789],{"class":2239},[2225,118783,2805],{"class":2235},[2225,118785,118667],{"class":2252},[2225,118787,2805],{"class":2235},[2225,118789,2298],{"class":2235},[2225,118791,2249],{"class":2235},[2225,118793,313],{"class":2252},[2225,118795,2805],{"class":2235},[2225,118797,2298],{"class":2235},[2225,118799,2249],{"class":2235},[2225,118801,118802],{"class":2252},"wrong-guess-1234",[2225,118804,2805],{"class":2235},[2225,118806,118807],{"class":2239},")        ",[2225,118809,118810],{"class":2763},"\u002F\u002F → { ok: false }\n",[937,118812,118813,118814,118817,118818,118821,118822,118825,118826,118829,118830,1520],{},"The verdict is bare — a failure carries no detail an attacker could use. When the\npreset declares ",[941,118815,118816],{},"rotateDays",", an ",[941,118819,118820],{},"ok: true"," verdict past the deadline additionally\ncarries ",[941,118823,118824],{},"mustRotate: true"," (never computed for a failed verdict). With ",[941,118827,118828],{},"notLastN",",\nrewriting the field to one of its last N values throws ",[941,118831,118832],{},"ClassifiedRotationError",[937,118834,118835,118836,118838,118839,118841],{},"Secret answers normalize before digesting (casefold, trim, whitespace-collapse), and\n",[941,118837,117303],{}," runs a k-of-n challenge over all ",[941,118840,118614],{}," members in one call:",[2217,118843,118845],{"className":2219,"code":118844,"language":2221,"meta":1037,"style":1037},"await users.verifyGroup('u1', { petName: 'rex', firstSchool: 'nope' }, { min: 1 })\n\u002F\u002F → { passed: true }   ('rex' matches 'Rex' after normalization)\n\nawait users.verifyGroup('u1', { petName: 'rex', firstSchool: 'nope' }, { min: 2 })\n\u002F\u002F → { passed: false }\n",[941,118846,118847,118909,118914,118918,118976],{"__ignoreMap":1037},[2225,118848,118849,118851,118853,118855,118857,118859,118861,118863,118865,118867,118869,118872,118874,118876,118879,118881,118883,118886,118888,118890,118892,118894,118896,118898,118901,118903,118905,118907],{"class":2227,"line":2228},[2225,118850,6500],{"class":2231},[2225,118852,118656],{"class":2239},[2225,118854,1520],{"class":2235},[2225,118856,117303],{"class":2291},[2225,118858,2789],{"class":2239},[2225,118860,2805],{"class":2235},[2225,118862,118667],{"class":2252},[2225,118864,2805],{"class":2235},[2225,118866,2298],{"class":2235},[2225,118868,2236],{"class":2235},[2225,118870,118871],{"class":2307}," petName",[2225,118873,2311],{"class":2235},[2225,118875,2249],{"class":2235},[2225,118877,118878],{"class":2252},"rex",[2225,118880,2805],{"class":2235},[2225,118882,2298],{"class":2235},[2225,118884,118885],{"class":2307}," firstSchool",[2225,118887,2311],{"class":2235},[2225,118889,2249],{"class":2235},[2225,118891,118463],{"class":2252},[2225,118893,2805],{"class":2235},[2225,118895,10965],{"class":2235},[2225,118897,2236],{"class":2235},[2225,118899,118900],{"class":2307}," min",[2225,118902,2311],{"class":2235},[2225,118904,9377],{"class":6527},[2225,118906,2243],{"class":2235},[2225,118908,2362],{"class":2239},[2225,118910,118911],{"class":2227,"line":1041},[2225,118912,118913],{"class":2763},"\u002F\u002F → { passed: true }   ('rex' matches 'Rex' after normalization)\n",[2225,118915,118916],{"class":2227,"line":1038},[2225,118917,2285],{"emptyLinePlaceholder":2284},[2225,118919,118920,118922,118924,118926,118928,118930,118932,118934,118936,118938,118940,118942,118944,118946,118948,118950,118952,118954,118956,118958,118960,118962,118964,118966,118968,118970,118972,118974],{"class":2227,"line":2288},[2225,118921,6500],{"class":2231},[2225,118923,118656],{"class":2239},[2225,118925,1520],{"class":2235},[2225,118927,117303],{"class":2291},[2225,118929,2789],{"class":2239},[2225,118931,2805],{"class":2235},[2225,118933,118667],{"class":2252},[2225,118935,2805],{"class":2235},[2225,118937,2298],{"class":2235},[2225,118939,2236],{"class":2235},[2225,118941,118871],{"class":2307},[2225,118943,2311],{"class":2235},[2225,118945,2249],{"class":2235},[2225,118947,118878],{"class":2252},[2225,118949,2805],{"class":2235},[2225,118951,2298],{"class":2235},[2225,118953,118885],{"class":2307},[2225,118955,2311],{"class":2235},[2225,118957,2249],{"class":2235},[2225,118959,118463],{"class":2252},[2225,118961,2805],{"class":2235},[2225,118963,10965],{"class":2235},[2225,118965,2236],{"class":2235},[2225,118967,118900],{"class":2307},[2225,118969,2311],{"class":2235},[2225,118971,18961],{"class":6527},[2225,118973,2243],{"class":2235},[2225,118975,2362],{"class":2239},[2225,118977,118978],{"class":2227,"line":2304},[2225,118979,118980],{"class":2763},"\u002F\u002F → { passed: false }\n",[937,118982,118983,118985,118986,118988],{},[941,118984,117180],{}," also works on ",[1092,118987,117154],{}," fields — it routes through a normalized\nplaintext comparison inside the hub, so you can confirm \"is this the email we have on\nfile?\" without revealing it.",[937,118990,11526,118991,118993,118994,118996,118997,119000,119001,119003,119004,119006],{},[1007,118992,641],{"href":642}," enabled, every ",[941,118995,117180],{}," and every ",[941,118998,118999],{},"verifyGroup()","\ncall records exactly one ",[941,119002,18051],{}," audit entry — on success ",[950,119005,14128],{}," failure, and one per\ngroup call, never one per member (attempt auditing without a member-count oracle).",[1591,119008,119010],{"id":119009},"find-by-digest-equatable-blind-index","Find by digest (equatable blind index)",[937,119012,119013,119014,119016,119017,119020,119021,119023,119024,119026,119027,1324,119030,119032,119033,2311],{},"By default, equal digest-only values are ",[950,119015,1004],{}," correlatable — each write is salted.\nA field can opt into an ",[950,119018,119019],{},"equatable blind index",": an extra store-visible tag\n(",[941,119022,13654],{},") that is equal exactly when the values are equal, which makes\n",[941,119025,25627],{}," possible — \"which records hold this secret?\" without\nstoring the secret. It is a double door: the field declares ",[941,119028,119029],{},"equatable: true",[1092,119031,14128],{}," the\ncollection must open ",[941,119034,55759],{},[2217,119036,119038],{"className":2219,"code":119037,"language":2221,"meta":1037,"style":1037},"const invites = vault.collection('invites', {\n  perRecordKeys: true,\n  acknowledgeEquatableRisk: true,           \u002F\u002F the collection-level door — see the warning below\n  classifiedFields: {\n    inviteCode: classified.password({ equatable: true }),\n  },\n})\n\nawait invites.put('r1', { inviteCode: 'invite-r1-secret-xyz' })\nawait invites.put('r2', { inviteCode: 'invite-r2-secret-abc' })\n\nawait invites.findByDigest('inviteCode', 'invite-r1-secret-xyz')  \u002F\u002F → ['r1']\nawait invites.findByDigest('inviteCode', 'not-a-real-code-00')    \u002F\u002F → []\n",[941,119039,119040,119068,119078,119092,119100,119130,119134,119140,119144,119183,119220,119224,119256],{"__ignoreMap":1037},[2225,119041,119042,119044,119047,119049,119051,119053,119055,119057,119059,119062,119064,119066],{"class":2227,"line":2228},[2225,119043,2775],{"class":2774},[2225,119045,119046],{"class":2239}," invites ",[2225,119048,2781],{"class":2235},[2225,119050,6466],{"class":2239},[2225,119052,1520],{"class":2235},[2225,119054,6471],{"class":2291},[2225,119056,2789],{"class":2239},[2225,119058,2805],{"class":2235},[2225,119060,119061],{"class":2252},"invites",[2225,119063,2805],{"class":2235},[2225,119065,2298],{"class":2235},[2225,119067,2301],{"class":2235},[2225,119069,119070,119072,119074,119076],{"class":2227,"line":1041},[2225,119071,75763],{"class":2307},[2225,119073,2311],{"class":2235},[2225,119075,2322],{"class":2321},[2225,119077,1707],{"class":2235},[2225,119079,119080,119083,119085,119087,119089],{"class":2227,"line":1038},[2225,119081,119082],{"class":2307},"  acknowledgeEquatableRisk",[2225,119084,2311],{"class":2235},[2225,119086,2322],{"class":2321},[2225,119088,2298],{"class":2235},[2225,119090,119091],{"class":2763},"           \u002F\u002F the collection-level door — see the warning below\n",[2225,119093,119094,119096,119098],{"class":2227,"line":2288},[2225,119095,117526],{"class":2307},[2225,119097,2311],{"class":2235},[2225,119099,2301],{"class":2235},[2225,119101,119102,119105,119107,119109,119111,119113,119115,119117,119120,119122,119124,119126,119128],{"class":2227,"line":2304},[2225,119103,119104],{"class":2307},"    inviteCode",[2225,119106,2311],{"class":2235},[2225,119108,117343],{"class":2239},[2225,119110,1520],{"class":2235},[2225,119112,313],{"class":2291},[2225,119114,2789],{"class":2239},[2225,119116,2792],{"class":2235},[2225,119118,119119],{"class":2307}," equatable",[2225,119121,2311],{"class":2235},[2225,119123,2322],{"class":2321},[2225,119125,2243],{"class":2235},[2225,119127,1381],{"class":2239},[2225,119129,1707],{"class":2235},[2225,119131,119132],{"class":2227,"line":2356},[2225,119133,19017],{"class":2235},[2225,119135,119136,119138],{"class":2227,"line":2830},[2225,119137,2359],{"class":2235},[2225,119139,2362],{"class":2239},[2225,119141,119142],{"class":2227,"line":2861},[2225,119143,2285],{"emptyLinePlaceholder":2284},[2225,119145,119146,119148,119151,119153,119155,119157,119159,119161,119163,119165,119167,119170,119172,119174,119177,119179,119181],{"class":2227,"line":4246},[2225,119147,6500],{"class":2231},[2225,119149,119150],{"class":2239}," invites",[2225,119152,1520],{"class":2235},[2225,119154,3161],{"class":2291},[2225,119156,2789],{"class":2239},[2225,119158,2805],{"class":2235},[2225,119160,75463],{"class":2252},[2225,119162,2805],{"class":2235},[2225,119164,2298],{"class":2235},[2225,119166,2236],{"class":2235},[2225,119168,119169],{"class":2307}," inviteCode",[2225,119171,2311],{"class":2235},[2225,119173,2249],{"class":2235},[2225,119175,119176],{"class":2252},"invite-r1-secret-xyz",[2225,119178,2805],{"class":2235},[2225,119180,2243],{"class":2235},[2225,119182,2362],{"class":2239},[2225,119184,119185,119187,119189,119191,119193,119195,119197,119199,119201,119203,119205,119207,119209,119211,119214,119216,119218],{"class":2227,"line":4253},[2225,119186,6500],{"class":2231},[2225,119188,119150],{"class":2239},[2225,119190,1520],{"class":2235},[2225,119192,3161],{"class":2291},[2225,119194,2789],{"class":2239},[2225,119196,2805],{"class":2235},[2225,119198,75513],{"class":2252},[2225,119200,2805],{"class":2235},[2225,119202,2298],{"class":2235},[2225,119204,2236],{"class":2235},[2225,119206,119169],{"class":2307},[2225,119208,2311],{"class":2235},[2225,119210,2249],{"class":2235},[2225,119212,119213],{"class":2252},"invite-r2-secret-abc",[2225,119215,2805],{"class":2235},[2225,119217,2243],{"class":2235},[2225,119219,2362],{"class":2239},[2225,119221,119222],{"class":2227,"line":4259},[2225,119223,2285],{"emptyLinePlaceholder":2284},[2225,119225,119226,119228,119230,119232,119234,119236,119238,119241,119243,119245,119247,119249,119251,119253],{"class":2227,"line":6548},[2225,119227,6500],{"class":2231},[2225,119229,119150],{"class":2239},[2225,119231,1520],{"class":2235},[2225,119233,117254],{"class":2291},[2225,119235,2789],{"class":2239},[2225,119237,2805],{"class":2235},[2225,119239,119240],{"class":2252},"inviteCode",[2225,119242,2805],{"class":2235},[2225,119244,2298],{"class":2235},[2225,119246,2249],{"class":2235},[2225,119248,119176],{"class":2252},[2225,119250,2805],{"class":2235},[2225,119252,27226],{"class":2239},[2225,119254,119255],{"class":2763},"\u002F\u002F → ['r1']\n",[2225,119257,119258,119260,119262,119264,119266,119268,119270,119272,119274,119276,119278,119281,119283,119285],{"class":2227,"line":6576},[2225,119259,6500],{"class":2231},[2225,119261,119150],{"class":2239},[2225,119263,1520],{"class":2235},[2225,119265,117254],{"class":2291},[2225,119267,2789],{"class":2239},[2225,119269,2805],{"class":2235},[2225,119271,119240],{"class":2252},[2225,119273,2805],{"class":2235},[2225,119275,2298],{"class":2235},[2225,119277,2249],{"class":2235},[2225,119279,119280],{"class":2252},"not-a-real-code-00",[2225,119282,2805],{"class":2235},[2225,119284,28126],{"class":2239},[2225,119286,119287],{"class":2763},"\u002F\u002F → []\n",[937,119289,119290,119291,119293,119294,119297],{},"Every hit is confirmed by a real digest verification before it is returned, so a forged\nor copied tag on another record never matches. After a value rotates, only the current\nvalue is findable. With ",[1007,119292,641],{"href":642}," enabled, one ",[941,119295,119296],{},"find"," sweep entry is\nrecorded per call — never one per hit.",[7646,119299,119300],{"color":10789,"icon":10790},[937,119301,119302,119305,119306,119309,119310,119312,119313,119315],{},[950,119303,119304],{},"The equatable trade, stated plainly."," Equal values produce equal store-visible tags:\nanyone with store access learns ",[1092,119307,119308],{},"which records share the secret and how many share each\nvalue"," — never the value itself. A holder of the collection's data-encryption key can\nadditionally test candidate values offline at PBKDF2-SHA256 speed (600K iterations,\nGPU-friendly): for low-entropy secrets like PINs or secret answers, recovering the\nequality partition is a matter of hours, not years. The iteration count raises the\nprice; it does not make a low-entropy field safe — the real control is the door itself.\nDo not enable ",[941,119311,13650],{}," on low-entropy fields unless a learnable partition is\nacceptable. Backups taken before a ",[941,119314,2542],{}," retain the tags.",[1591,119317,119319,119320,119322],{"id":119318},"forget-sealed-slots-and-the-scopedpurge-knob","Forget — sealed slots and the ",[941,119321,56931],{}," knob",[937,119324,119325,119326,11271,119329,119331,119332,119335,119336,119339],{},"A classified declaration is ",[950,119327,119328],{},"forgettable",[1007,119330,685],{"href":686}," erases a subject's\nclassified data along with the rest of the record: every sealed slot keyed to the shredded\nrecord's per-record key is dropped (",[941,119333,119334],{},"sealedFieldsShredded","), and a legacy slot it cannot shred is\nreported (",[941,119337,119338],{},"sealedResidue",") rather than silently kept. These counts are unchanged from before\nclassified became a via field-feature.",[937,119341,119342,119343,119346,119347,119349,119350,119352,119353,119355,119356,119362],{},"Separately, if a record's key was ever ",[1007,119344,119345],{"href":825},"sealed to a host"," — a\n",[941,119348,15295],{}," host-delivery envelope that lets a granted ",[941,119351,1139],{}," host recover the key — ",[941,119354,2542],{},"\npurges those envelopes too. ",[950,119357,119358,119359,119361],{},"By default this purge is unconditional: it runs on every\ncollection, including one that declares only ",[941,119360,14672],{}," and no classified field"," — because\nany collection can seal a key to a host, and narrowing the purge would silently leave some keys\nrecoverable.",[937,119364,119365,119367,119368,119370,119371,119373],{},[941,119366,56931],{}," opts into the narrower, cheaper behavior. A deployment where most collections never\nseal to a host pays for that broad prefix scan for nothing; set ",[941,119369,111496],{}," on the forget\ncascade to purge ",[941,119372,15295],{}," envelopes only for declaration-backed collections:",[2217,119375,119377],{"className":2219,"code":119376,"language":2221,"meta":1037,"style":1037},"import { withForget } from '@noy-db\u002Fhub\u002Fforget'\n\nconst db = await createNoydb({\n  store: toMemory(),\n  user: 'owner',\n  secret: 'correct horse battery staple',\n  classifiedStrategy: withClassified(),\n  forgetStrategy: withForget({\n    subjects: { contacts: 'subjectId', audit: 'subjectId' },\n    scopedPurge: true,\n  }),\n})\n",[941,119378,119379,119397,119401,119417,119429,119443,119457,119469,119481,119515,119525,119533],{"__ignoreMap":1037},[2225,119380,119381,119383,119385,119387,119389,119391,119393,119395],{"class":2227,"line":2228},[2225,119382,2232],{"class":2231},[2225,119384,2236],{"class":2235},[2225,119386,74041],{"class":2239},[2225,119388,2243],{"class":2235},[2225,119390,2246],{"class":2231},[2225,119392,2249],{"class":2235},[2225,119394,73761],{"class":2252},[2225,119396,2256],{"class":2235},[2225,119398,119399],{"class":2227,"line":1041},[2225,119400,2285],{"emptyLinePlaceholder":2284},[2225,119402,119403,119405,119407,119409,119411,119413,119415],{"class":2227,"line":1038},[2225,119404,2775],{"class":2774},[2225,119406,2778],{"class":2239},[2225,119408,2781],{"class":2235},[2225,119410,2784],{"class":2231},[2225,119412,2744],{"class":2291},[2225,119414,2789],{"class":2239},[2225,119416,4200],{"class":2235},[2225,119418,119419,119421,119423,119425,119427],{"class":2227,"line":2288},[2225,119420,15608],{"class":2307},[2225,119422,2311],{"class":2235},[2225,119424,68576],{"class":2291},[2225,119426,4228],{"class":2239},[2225,119428,1707],{"class":2235},[2225,119430,119431,119433,119435,119437,119439,119441],{"class":2227,"line":2304},[2225,119432,4205],{"class":2307},[2225,119434,2311],{"class":2235},[2225,119436,2249],{"class":2235},[2225,119438,9898],{"class":2252},[2225,119440,2805],{"class":2235},[2225,119442,1707],{"class":2235},[2225,119444,119445,119447,119449,119451,119453,119455],{"class":2227,"line":2356},[2225,119446,10423],{"class":2307},[2225,119448,2311],{"class":2235},[2225,119450,2249],{"class":2235},[2225,119452,40260],{"class":2252},[2225,119454,2805],{"class":2235},[2225,119456,1707],{"class":2235},[2225,119458,119459,119461,119463,119465,119467],{"class":2227,"line":2830},[2225,119460,117441],{"class":2307},[2225,119462,2311],{"class":2235},[2225,119464,117348],{"class":2291},[2225,119466,4228],{"class":2239},[2225,119468,1707],{"class":2235},[2225,119470,119471,119473,119475,119477,119479],{"class":2227,"line":2861},[2225,119472,74356],{"class":2307},[2225,119474,2311],{"class":2235},[2225,119476,74041],{"class":2291},[2225,119478,2789],{"class":2239},[2225,119480,4200],{"class":2235},[2225,119482,119483,119485,119487,119489,119492,119494,119496,119499,119501,119503,119505,119507,119509,119511,119513],{"class":2227,"line":4246},[2225,119484,108994],{"class":2307},[2225,119486,2311],{"class":2235},[2225,119488,2236],{"class":2235},[2225,119490,119491],{"class":2307}," contacts",[2225,119493,2311],{"class":2235},[2225,119495,2249],{"class":2235},[2225,119497,119498],{"class":2252},"subjectId",[2225,119500,2805],{"class":2235},[2225,119502,2298],{"class":2235},[2225,119504,12644],{"class":2307},[2225,119506,2311],{"class":2235},[2225,119508,2249],{"class":2235},[2225,119510,119498],{"class":2252},[2225,119512,2805],{"class":2235},[2225,119514,2353],{"class":2235},[2225,119516,119517,119519,119521,119523],{"class":2227,"line":4253},[2225,119518,109016],{"class":2307},[2225,119520,2311],{"class":2235},[2225,119522,2322],{"class":2321},[2225,119524,1707],{"class":2235},[2225,119526,119527,119529,119531],{"class":2227,"line":4259},[2225,119528,19143],{"class":2235},[2225,119530,1381],{"class":2239},[2225,119532,1707],{"class":2235},[2225,119534,119535,119537],{"class":2227,"line":6548},[2225,119536,2359],{"class":2235},[2225,119538,2362],{"class":2239},[937,119540,15432,119541,119543,119544,119546,119547,119549,119550,119553],{},[941,119542,111496],{},", a collection with a ",[941,119545,22770],{}," binding is ",[950,119548,95997],{}," and its\nenvelopes are purged as before; every other collection is ",[950,119551,119552],{},"undeclared"," — its envelopes are left\nin place and reported, never silently dropped:",[2217,119555,119557],{"className":2219,"code":119556,"language":2221,"meta":1037,"style":1037},"\u002F\u002F contacts declares classifiedFields; audit has only `sensitive: [...]`.\n\u002F\u002F Both had a key sealed to a host for subject-1.\nconst result = await vault.forget('subject-1')\n",[941,119558,119559,119564,119569],{"__ignoreMap":1037},[2225,119560,119561],{"class":2227,"line":2228},[2225,119562,119563],{"class":2763},"\u002F\u002F contacts declares classifiedFields; audit has only `sensitive: [...]`.\n",[2225,119565,119566],{"class":2227,"line":1041},[2225,119567,119568],{"class":2763},"\u002F\u002F Both had a key sealed to a host for subject-1.\n",[2225,119570,119571,119573,119575,119577,119579,119581,119583,119585,119587,119589,119592,119594],{"class":2227,"line":1038},[2225,119572,2775],{"class":2774},[2225,119574,36920],{"class":2239},[2225,119576,2781],{"class":2235},[2225,119578,2784],{"class":2231},[2225,119580,6466],{"class":2239},[2225,119582,1520],{"class":2235},[2225,119584,685],{"class":2291},[2225,119586,2789],{"class":2239},[2225,119588,2805],{"class":2235},[2225,119590,119591],{"class":2252},"subject-1",[2225,119593,2805],{"class":2235},[2225,119595,2362],{"class":2239},[2217,119597,119599],{"className":6604,"code":119598,"language":141,"meta":1037,"style":1037},"{\n  \"sealedCekEnvelopesPurged\": 1,\n  \"scopedPurgeResidue\": [\n    { \"reason\": \"skipped-undeclared-sealed-cek\", \"collection\": \"audit\", \"count\": 1 }\n  ]\n}\n",[941,119600,119601,119605,119620,119632,119681,119685],{"__ignoreMap":1037},[2225,119602,119603],{"class":2227,"line":2228},[2225,119604,4200],{"class":2235},[2225,119606,119607,119609,119612,119614,119616,119618],{"class":2227,"line":1041},[2225,119608,9368],{"class":2235},[2225,119610,119611],{"class":2774},"sealedCekEnvelopesPurged",[2225,119613,6620],{"class":2235},[2225,119615,2311],{"class":2235},[2225,119617,9377],{"class":6527},[2225,119619,1707],{"class":2235},[2225,119621,119622,119624,119626,119628,119630],{"class":2227,"line":1038},[2225,119623,9368],{"class":2235},[2225,119625,56927],{"class":2774},[2225,119627,6620],{"class":2235},[2225,119629,2311],{"class":2235},[2225,119631,32491],{"class":2235},[2225,119633,119634,119636,119638,119640,119642,119644,119646,119648,119650,119652,119654,119656,119658,119660,119662,119665,119667,119669,119671,119673,119675,119677,119679],{"class":2227,"line":2288},[2225,119635,32496],{"class":2235},[2225,119637,6614],{"class":2235},[2225,119639,43668],{"class":6342},[2225,119641,6620],{"class":2235},[2225,119643,2311],{"class":2235},[2225,119645,6614],{"class":2235},[2225,119647,56935],{"class":2252},[2225,119649,6620],{"class":2235},[2225,119651,2298],{"class":2235},[2225,119653,6614],{"class":2235},[2225,119655,6471],{"class":6342},[2225,119657,6620],{"class":2235},[2225,119659,2311],{"class":2235},[2225,119661,6614],{"class":2235},[2225,119663,119664],{"class":2252},"audit",[2225,119666,6620],{"class":2235},[2225,119668,2298],{"class":2235},[2225,119670,6614],{"class":2235},[2225,119672,3236],{"class":6342},[2225,119674,6620],{"class":2235},[2225,119676,2311],{"class":2235},[2225,119678,9377],{"class":6527},[2225,119680,6369],{"class":2235},[2225,119682,119683],{"class":2227,"line":2304},[2225,119684,110088],{"class":2235},[2225,119686,119687],{"class":2227,"line":2356},[2225,119688,7351],{"class":2235},[937,119690,119691,119693,119694,119696],{},[941,119692,3236],{}," is the number of envelopes left in place for that collection, aggregated across the whole\n",[941,119695,2542],{}," call. You are trading the default's completeness for cost: purge fewer prefixes, in\nexchange for reviewing the residue report for everything left out of scope.",[7646,119698,119699],{"color":10789,"icon":10790},[937,119700,119701,119707,119708,119710,119711,119713,119714,119716,119717,119719,119720,119722,119723,119725,119726,119729,119730,119733,119734,119736],{},[950,119702,119703,119704,119706],{},"A bare ",[941,119705,14672],{}," collection is undeclared here."," Sensitive fields without a\n",[941,119709,22770],{}," binding do not bring a collection into ",[941,119712,56931],{}," scope — after ",[941,119715,2542],{},"\nits ",[941,119718,15295],{}," envelope remains, and a host granted that key can still recover it until the\nenvelope is purged another way. Wrap the field in a ",[941,119721,22770],{}," binding to bring the\ncollection into scope, or leave ",[941,119724,56931],{}," off for collections that use only bare ",[941,119727,119728],{},"sensitive",".\nThe declaration signal is also ",[950,119731,119732],{},"session-local",": a declared collection never opened in the\ncurrent session before ",[941,119735,2542],{}," resolves as undeclared, so open your declared collections first.",[937,119738,119739,119741,119742,119744,119745,1520],{},[941,119740,56931],{}," is set once on the forget strategy at ",[941,119743,2134],{}," and applies identically to\nevery vault from that instance — the same per-instance granularity as ",[941,119746,119747],{},"subjects",[1022,119749,102889],{"id":102888},[1591,119751,119753],{"id":119752},"withclassified",[941,119754,22007],{},[2217,119756,119758],{"className":2219,"code":119757,"language":2221,"meta":1037,"style":1037},"function withClassified(): ClassifiedStrategy\n",[941,119759,119760],{"__ignoreMap":1037},[2225,119761,119762,119764,119766,119768],{"class":2227,"line":2228},[2225,119763,12641],{"class":2774},[2225,119765,117348],{"class":2291},[2225,119767,112812],{"class":2235},[2225,119769,119770],{"class":6342}," ClassifiedStrategy\n",[937,119772,119773,119774,119776,119777,119780,119781,1261,119783,1261,119785,1261,119787,66005,119789,1520],{},"Enables the four read-egress operations on collections that declare ",[941,119775,22770],{},".\nPass it to ",[941,119778,119779],{},"createNoydb({ classifiedStrategy })",". Without it, the declaration\u002Fwrite\nside still runs, and ",[941,119782,117298],{},[941,119784,18051],{},[941,119786,117303],{},[941,119788,117254],{},[941,119790,22152],{},[1591,119792,119794,119795,1381],{"id":119793},"the-preset-catalog-classified","The preset catalog (",[941,119796,119797],{},"classified.*",[1338,119799,119800,119818],{},[1341,119801,119802],{},[1344,119803,119804,119807,119809,119812,119815],{},[1347,119805,119806],{},"Preset",[1347,119808,20459],{},[1347,119810,119811],{},"List projection",[1347,119813,119814],{},"Riders",[1347,119816,119817],{},"Write validation",[1357,119819,119820,119836,119861,119882,119905,119932,119959,119983,120005],{},[1344,119821,119822,119827,119829,119831,119833],{},[1362,119823,119824],{},[941,119825,119826],{},"classified.creditCard({ pan, expiry?, cvc? })",[1362,119828,2115],{},[1362,119830,3610],{},[1362,119832,3610],{},[1362,119834,119835],{},"per member ↓",[1344,119837,119838,119841,119845,119851,119858],{},[1362,119839,119840],{},"— PAN member",[1362,119842,119843],{},[941,119844,117154],{},[1362,119846,119847,119848],{},"mask ",[941,119849,119850],{},"'•••• ${last4}'",[1362,119852,119853,968,119856],{},[941,119854,119855],{},"last4",[941,119857,81434],{},[1362,119859,119860],{},"Luhn check",[1344,119862,119863,119866,119870,119875,119877],{},[1362,119864,119865],{},"— expiry member",[1362,119867,119868],{},[941,119869,117154],{},[1362,119871,119847,119872],{},[941,119873,119874],{},"'••\u002F••'",[1362,119876,3610],{},[1362,119878,119879],{},[941,119880,119881],{},"MM\u002FYY",[1344,119883,119884,119887,119891,119894,119896],{},[1362,119885,119886],{},"— CVC member",[1362,119888,119889],{},[941,119890,18391],{},[1362,119892,119893],{},"omit",[1362,119895,3610],{},[1362,119897,119898,119899,119901,119902],{},"none at ",[941,119900,3161],{}," — any presence throws ",[941,119903,119904],{},"ClassifiedNeverStoredError",[1344,119906,119907,119912,119916,119921,119926],{},[1362,119908,119909],{},[941,119910,119911],{},"classified.birthDate()",[1362,119913,119914],{},[941,119915,117154],{},[1362,119917,119847,119918],{},[941,119919,119920],{},"'${yob}-••-••'",[1362,119922,119923],{},[941,119924,119925],{},"yob",[1362,119927,119928,119929],{},"valid ISO ",[941,119930,119931],{},"yyyy-mm-dd",[1344,119933,119934,119939,119943,119948,119953],{},[1362,119935,119936],{},[941,119937,119938],{},"classified.email()",[1362,119940,119941],{},[941,119942,117154],{},[1362,119944,119847,119945],{},[941,119946,119947],{},"'•••@${domain}'",[1362,119949,119950],{},[941,119951,119952],{},"domain",[1362,119954,119955,119956],{},"contains ",[941,119957,119958],{},"@",[1344,119960,119961,119966,119970,119975,119980],{},[1362,119962,119963],{},[941,119964,119965],{},"classified.phone()",[1362,119967,119968],{},[941,119969,117154],{},[1362,119971,119847,119972],{},[941,119973,119974],{},"'•••••${last2}'",[1362,119976,119977],{},[941,119978,119979],{},"last2",[1362,119981,119982],{},"≥ 5 digits",[1344,119984,119985,119990,119994,119996,119998],{},[1362,119986,119987],{},[941,119988,119989],{},"classified.password(opts?)",[1362,119991,119992],{},[941,119993,117173],{},[1362,119995,119893],{},[1362,119997,3610],{},[1362,119999,120000,120001,120004],{},"NFC length ≥ ",[941,120002,120003],{},"minLength"," (default 10)",[1344,120006,120007,120012,120016,120018,120020],{},[1362,120008,120009],{},[941,120010,120011],{},"classified.secretAnswer(opts?)",[1362,120013,120014],{},[941,120015,117173],{},[1362,120017,119893],{},[1362,120019,3610],{},[1362,120021,120022],{},"non-empty after normalization",[937,120024,120025,20001,120028,120031,120032,120035,120036,120038,120039,120042,120043,120046,120047,1952,120050,20001,120053,1952,120055,120057,120058,120060,120061,120063],{},[941,120026,120027],{},"classified.password",[941,120029,120030],{},"minLength?"," (default 10), ",[941,120033,120034],{},"rotateDays?"," (decorate\n",[941,120037,118820],{}," verdicts with ",[941,120040,120041],{},"mustRotate"," after N days), ",[941,120044,120045],{},"notLastN?"," (0–8, refuse reuse of\nthe last N values on rotate; each remembered value costs one 600K PBKDF2 on write),\n",[941,120048,120049],{},"equatable?",[941,120051,120052],{},"classified.secretAnswer",[941,120054,120049],{},[941,120056,118614],{}," members\nare groupable via ",[941,120059,117303],{}," and normalize casefold\u002Ftrim\u002Fwhitespace-collapse;\n",[941,120062,313],{}," normalizes NFC-strict.",[937,120065,120066,120067,33694,120070,120072,120073,1520],{},"Riders become plain companion fields named ",[941,120068,120069],{},"\u003Cfield>_\u003Crider>",[941,120071,117198],{},");\na collision with a declared or computed field throws ",[941,120074,120075],{},"ClassifiedConfigError",[1591,120077,120079,120080,1381],{"id":120078},"collection-operations-require-withclassified","Collection operations (require ",[941,120081,22007],{},[1338,120083,120084,120094],{},[1341,120085,120086],{},[1344,120087,120088,120090,120092],{},[1347,120089,7831],{},[1347,120091,102906],{},[1347,120093,7834],{},[1357,120095,120096,120121,120144,120162],{},[1344,120097,120098,120102,120107],{},[1362,120099,120100],{},[941,120101,25618],{},[1362,120103,120104],{},[941,120105,120106],{},"Promise\u003Cunknown>",[1362,120108,120109,120110,120112,120113,968,120115,120117,120118,957],{},"Audited decrypt of one ",[941,120111,117154],{}," field. Refuses ",[941,120114,18391],{},[941,120116,117173],{},", unknown fields, missing records (",[941,120119,120120],{},"ClassifiedRevealError",[1344,120122,120123,120127,120132],{},[1362,120124,120125],{},[941,120126,25621],{},[1362,120128,120129],{},[941,120130,120131],{},"Promise\u003CClassifiedVerdict>",[1362,120133,120134,120135,120138,120139,120141,120142,1520],{},"Verdict-only check: ",[941,120136,120137],{},"{ ok: boolean, mustRotate?: true }",". Digest compare for ",[941,120140,117173],{},"; normalized in-hub plaintext compare for ",[941,120143,117154],{},[1344,120145,120146,120151,120156],{},[1362,120147,120148],{},[941,120149,120150],{},"verifyGroup(id, answers, { min })",[1362,120152,120153],{},[941,120154,120155],{},"Promise\u003C{ passed: boolean }>",[1362,120157,120158,120159,120161],{},"k-of-n challenge over the collection's ",[941,120160,118614],{}," members.",[1344,120163,120164,120168,120173],{},[1362,120165,120166],{},[941,120167,25627],{},[1362,120169,120170],{},[941,120171,120172],{},"Promise\u003Creadonly string[]>",[1362,120174,120175,120176,120179,120180,6030,120182,120185,120186,120189],{},"Ids whose equatable digest-only field equals ",[941,120177,120178],{},"candidate",". Requires ",[941,120181,119029],{},[941,120183,120184],{},"acknowledgeEquatableRisk: true",". The three mis-declaration cases (not classified \u002F not digest-only \u002F not equatable) throw ",[941,120187,120188],{},"ClassifiedVerifyError"," with one identical message — no field-enumeration oracle.",[1591,120191,120193],{"id":120192},"collection-options","Collection options",[1338,120195,120196,120206],{},[1341,120197,120198],{},[1344,120199,120200,120202,120204],{},[1347,120201,101060],{},[1347,120203,20194],{},[1347,120205,20200],{},[1357,120207,120208,120222,120243],{},[1344,120209,120210,120214,120219],{},[1362,120211,120212],{},[941,120213,22770],{},[1362,120215,120216],{},[941,120217,120218],{},"Record\u003Cstring, ClassifiedEntry>",[1362,120220,120221],{},"Field name → spec, or any key → group (groups claim their member field names).",[1344,120223,120224,120228,120232],{},[1362,120225,120226],{},[941,120227,14552],{},[1362,120229,120230],{},[941,120231,16861],{},[1362,120233,120234,120235,120237,120238,120240,120241,118499],{},"Required ",[941,120236,11790],{}," when any field is ",[941,120239,117173],{}," — digest keys derive from the per-record key so ",[941,120242,2542],{},[1344,120244,120245,120249,120253],{},[1362,120246,120247],{},[941,120248,55759],{},[1362,120250,120251],{},[941,120252,16861],{},[1362,120254,120255,120256,120258,120259,120261],{},"Collection-level door for ",[941,120257,13650],{}," fields. ",[941,120260,11790],{}," with zero equatable members is a harmless no-op.",[1591,120263,120265],{"id":120264},"errors","Errors",[1338,120267,120268,120277],{},[1341,120269,120270],{},[1344,120271,120272,120274],{},[1347,120273,12126],{},[1347,120275,120276],{},"Thrown when",[1357,120278,120279,120290,120311,120321,120334,120345,120360],{},[1344,120280,120281,120285],{},[1362,120282,120283],{},[941,120284,22152],{},[1362,120286,120287,120288,1520],{},"A read-egress op is called without ",[941,120289,22007],{},[1344,120291,120292,120296],{},[1362,120293,120294],{},[941,120295,120075],{},[1362,120297,120298,120299,120301,120302,968,120304,120306,120307,120310],{},"Invalid declaration: duplicate field claim, rider collision, ",[941,120300,117173],{}," without encryption or ",[941,120303,14552],{},[941,120305,13650],{}," on a non-digest field or without the door, combining with CRDT\u002Fconflict resolvers, overlap with ",[941,120308,120309],{},"sensitive[]",", indexed\u002Fembedded digest fields.",[1344,120312,120313,120318],{},[1362,120314,120315],{},[941,120316,120317],{},"ClassifiedValidationError",[1362,120319,120320],{},"A write value fails its preset validator.",[1344,120322,120323,120327],{},[1362,120324,120325],{},[941,120326,119904],{},[1362,120328,3289,120329,120331,120332,1520],{},[941,120330,117664],{}," field is present on ",[941,120333,20953],{},[1344,120335,120336,120340],{},[1362,120337,120338],{},[941,120339,120120],{},[1362,120341,120342,120344],{},[941,120343,22149],{}," on a non-recoverable\u002Funknown field, a missing record, or a never-written field.",[1344,120346,120347,120351],{},[1362,120348,120349],{},[941,120350,120188],{},[1362,120352,120353,1625,120355,1625,120357,120359],{},[941,120354,18051],{},[941,120356,117303],{},[941,120358,117254],{}," caller bugs (unknown field, wrong storage form, no groupable members).",[1344,120361,120362,120366],{},[1362,120363,120364],{},[941,120365,118832],{},[1362,120367,120368,120369,120371],{},"A digest-only rewrite reuses one of the last ",[941,120370,118828],{}," values.",[937,120373,120374,120375,120377,120378,120380,120381,120383],{},"All are exported from ",[941,120376,73151],{},", except ",[941,120379,22152],{},",\nwhich is exported from ",[941,120382,1086],{}," (it belongs to the kernel's strategy seam, not to\nthe opt-in module).",[1591,120385,120387],{"id":120386},"advanced-exports","Advanced exports",[937,120389,120390],{},"For building custom specs and tooling on top of the same machinery:",[1338,120392,120393,120401],{},[1341,120394,120395],{},[1344,120396,120397,120399],{},[1347,120398,72632],{},[1347,120400,7834],{},[1357,120402,120403,120420,120439,120465,120482,120492,120505],{},[1344,120404,120405,120417],{},[1362,120406,120407,968,120410,968,120413,120416],{},[941,120408,120409],{},"ClassifiedFieldSpec",[941,120411,120412],{},"ClassifiedGroup",[941,120414,120415],{},"ClassifiedEntry"," (types)",[1362,120418,120419],{},"The descriptor shapes behind the presets — a raw spec object works anywhere a preset does.",[1344,120421,120422,120436],{},[1362,120423,120424,968,120427,968,120430,968,120433,120416],{},[941,120425,120426],{},"ClassifiedStorage",[941,120428,120429],{},"ClassifiedList",[941,120431,120432],{},"ClassifiedRider",[941,120434,120435],{},"ClassifiedVerdict",[1362,120437,120438],{},"The storage-form, list-projection, rider, and verdict vocabularies.",[1344,120440,120441,120456],{},[1362,120442,120443,968,120446,968,120449,120452,120453],{},[941,120444,120445],{},"ClassifiedStrategy",[941,120447,120448],{},"ClassifiedRevealCtx",[941,120450,120451],{},"ClassifiedVerifyCtx"," (types), ",[941,120454,120455],{},"NO_CLASSIFIED",[1362,120457,120458,120459,120461,120462,120464],{},"The strategy seam ",[941,120460,22007],{}," implements; ",[941,120463,120455],{}," is the throwing default.",[1344,120466,120467,120476],{},[1362,120468,120469,968,120472,120475],{},[941,120470,120471],{},"resolveClassifiedFields(collection, config)",[941,120473,120474],{},"ResolvedClassified"," (type)",[1362,120477,120478,120479,120481],{},"Flattens a ",[941,120480,22770],{}," config into a per-field map + rider computed entries.",[1344,120483,120484,120489],{},[1362,120485,120486],{},[941,120487,120488],{},"enforceClassifiedWrite(record, byField, collection)",[1362,120490,120491],{},"The pure write-time gate (never-rejection + validators).",[1344,120493,120494,120502],{},[1362,120495,120496,968,120499],{},[941,120497,120498],{},"isClassifiedFieldSpec(x)",[941,120500,120501],{},"isClassifiedGroup(x)",[1362,120503,120504],{},"Type guards for descriptor values.",[1344,120506,120507,120512],{},[1362,120508,120509],{},[941,120510,120511],{},"luhnCheck(value)",[1362,120513,120514,120515,120517],{},"The card-number checksum used by the ",[941,120516,117549],{}," preset.",[1022,120519,68210],{"id":68209},[1111,120521,120522,120533,120545,120551,120572,120588,120599,120615,120629,120638,120650],{},[1114,120523,120524,120527,120528,45420,120530,120532],{},[950,120525,120526],{},"Digest operations are deliberately slow."," Every digest write, ",[941,120529,18051],{},[941,120531,117254],{}," target derivation runs a 600K-iteration PBKDF2 — that is the\nbrute-force price tag, by design. Budget for it in hot login paths.",[1114,120534,120535,120540,120541,120544],{},[950,120536,120537,120539],{},[941,120538,117254],{}," scans the collection",": one 600K digest derivation for the\ncandidate (always exactly one, even on an empty collection — so timing cannot\ndistinguish \"no records\" from \"no match\"), one envelope read per record (exactly\n",[941,120542,120543],{},"list + N get"," store calls), and a confirming digest verification per hit —\nO(collection size), not an index lookup in the SQL sense.",[1114,120546,120547,120550],{},[950,120548,120549],{},"No built-in rate limiter."," For low-entropy secrets (PINs, secret answers) the\nenumeration math is on you — add application-side attempt limiting.",[1114,120552,120553,120556,120557,120559,120560,120562,120563,120565,120566,120568,120569,120571],{},[950,120554,120555],{},"Storage forms are mutually exclusive per field"," — a field is ",[941,120558,117173],{}," OR\n",[941,120561,117154],{}," OR ",[941,120564,18391],{},", exactly one; a ",[941,120567,117173],{}," field additionally cannot\nappear in the collection's ",[941,120570,120309],{}," list (the overlap would silently downgrade\nit to recoverable sealing, so it is refused).",[1114,120573,120574,120584,120585,120587],{},[950,120575,120576,997,120578,120580,120581,120583],{},[941,120577,117154],{},[941,120579,117173],{}," fields cannot combine with CRDT modes or a\n",[941,120582,91981],{}," resolver"," — merge paths bypass write enforcement, so the\ncombination is refused at declaration (",[941,120586,18391],{},"-only declarations are exempt).",[1114,120589,120590,120593,120594,120596,120597,1520],{},[950,120591,120592],{},"Digest-only fields cannot be indexed, text-indexed, embedded, deterministic, or a\nforget-subject key",", and require an encrypted collection (",[941,120595,11453],{}," is\nrefused) with ",[941,120598,73811],{},[1114,120600,120601,120608,120609,120611,120612,120614],{},[950,120602,120603,997,120605,120607],{},[941,120604,117154],{},[941,120606,117173],{}," declarations are fixed at first open."," A\ncollection first opened without them cannot gain them later — the sealing and digest\nwrite paths are frozen at construction, so a late declaration would persist values\nunprotected while ",[941,120610,3926],{}," advertises protection; it is refused instead.\n",[941,120613,18391],{},"-only declarations can reconcile onto an existing collection. A\nre-declaration that changes a field's storage form is always refused.",[1114,120616,120617,120620,120621,120623,120624,34689,120626,120628],{},[950,120618,120619],{},"List masking is opt-in at the render layer."," Kernel reads return sealed handles +\nriders; run ",[941,120622,118122],{}," (or consume ",[941,120625,3926],{},[941,120627,633],{}," block) to\nget the declared mask\u002Fomit\u002Frider view.",[1114,120630,120631,120637],{},[950,120632,120633,120634,120636],{},"Writing ",[941,120635,6651],{}," to a digest-only field clears it"," (an explicit clear, not a\nvalidation error).",[1114,120639,120640,120649],{},[950,120641,120642,120643,120645,120646,120648],{},"Pre-",[941,120644,2542],{}," backups retain equatable ",[941,120647,13654],{}," tags"," — the partition of a\nforgotten record's secret can survive in old backups.",[1114,120651,120652,120657,120658,120661,120662,120664,120665,1625,120668,120670,120671,120674,120675,1625,120677,1625,120679,120681,120682,120684,120685,120687,120688,120691,120692,120694],{},[950,120653,120654,120655],{},"Export output changed in ",[941,120656,111464],{}," for the default (non-",[941,120659,120660],{},"redact","-option) path of the\n",[941,120663,1791],{}," exporters: ",[941,120666,120667],{},"vault.exportStream()",[941,120669,23185],{}," now redact a classified field to the\nplain string ",[941,120672,120673],{},"'[sealed]'"," on the record itself, so ",[941,120676,30472],{},[941,120678,30496],{},[941,120680,30493],{}," emit a\nbare ",[941,120683,117966],{}," cell \u002F ",[941,120686,5672],{}," column where they previously emitted JSON-stringified sealed\nhandles (",[941,120689,120690],{},"\"\"\"[sealed]\"\"\""," in CSV, a ",[941,120693,50501],{}," column). The new bytes are the intended ones —\nthe old output was an accidental echo of a live, revealable handle reaching the export stream.",[1022,120696,2621],{"id":2620},[1111,120698,120699,120704,120709,120721,120728,120739,120744,120749,120755],{},[1114,120700,120701,120703],{},[1007,120702,896],{"href":897}," — the field-feature surface and the posture\u002Ftaint machinery classified plugs into",[1114,120705,120706,120708],{},[1007,120707,637],{"href":638}," — how a derived field inherits a classified source's posture",[1114,120710,120711,120713,120714,1261,120716,1261,120718,120720],{},[1007,120712,641],{"href":642}," — the audit trail that ",[941,120715,117298],{},[941,120717,18051],{},[941,120719,119296],{}," operations feed",[1114,120722,120723,120725,120726],{},[1007,120724,685],{"href":686}," — per-record key shredding, the reason digest-only requires ",[941,120727,14552],{},[1114,120729,120730,120735,120736,120738],{},[1007,120731,120732,120733],{"href":678},"Field metadata — ",[941,120734,1380],{}," — where the serialized ",[941,120737,633],{}," projection contract surfaces",[1114,120740,120741,120743],{},[1007,120742,697],{"href":698}," — pairs with classified for versioned, audited records",[1114,120745,120746,120748],{},[1007,120747,1516],{"href":66}," — the envelope model classified fields build on",[1114,120750,120751,1368,120753],{},[1007,120752,61743],{"href":598},[1007,120754,1019],{"href":1018},[1114,120756,101581,120757,1520],{},[1007,120758,120760],{"href":94316,"rel":120759},[3371],[941,120761,120762],{},"128-with-classified",[2645,120764,120765],{},"html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sfNiH, html code.shiki .sfNiH{--shiki-light:#FF5370;--shiki-default:#FF9CAC;--shiki-dark:#FF9CAC}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}",{"title":1037,"searchDepth":1038,"depth":1038,"links":120767},[120768,120769,120770,120771,120772,120780,120790,120791],{"id":29856,"depth":1041,"text":29857},{"id":111659,"depth":1041,"text":111660},{"id":70092,"depth":1041,"text":70093},{"id":99863,"depth":1041,"text":99864},{"id":29892,"depth":1041,"text":29893,"children":120773},[120774,120775,120776,120777,120778],{"id":117654,"depth":1038,"text":117655},{"id":118361,"depth":1038,"text":118362},{"id":118489,"depth":1038,"text":118490},{"id":119009,"depth":1038,"text":119010},{"id":119318,"depth":1038,"text":120779},"Forget — sealed slots and the scopedPurge knob",{"id":102888,"depth":1041,"text":102889,"children":120781},[120782,120783,120785,120787,120788,120789],{"id":119752,"depth":1038,"text":22007},{"id":119793,"depth":1038,"text":120784},"The preset catalog (classified.*)",{"id":120078,"depth":1038,"text":120786},"Collection operations (require withClassified())",{"id":120192,"depth":1038,"text":120193},{"id":120264,"depth":1038,"text":120265},{"id":120386,"depth":1038,"text":120387},{"id":68209,"depth":1041,"text":68210},{"id":2620,"depth":1041,"text":2621},"Classified fields: declare sensitive fields once with a preset — sealed or digested storage, safe projections, audited reveal, and verify-without-reveal.",{},{"title":633,"description":120792},"F8SA0JYJJrKjj92ONaO2bCmfZQ8kDuFHDlhrNVgQ4sc",{"id":120797,"title":637,"access":12,"apiToc":929,"body":120798,"category":1042,"description":124850,"extension":1044,"meta":124851,"navigation":1042,"nest":1047,"path":638,"seo":124852,"sinceVersion":1042,"status":1049,"stem":639,"subtitle":1042,"tier":1050,"tocExternal":1042,"__hash__":124853},"docs\u002Fservices\u002Fcomputed.md",{"type":931,"value":120799,"toc":124826},[120800,120802,120805,120807,120817,120838,120846,120889,120891,120897,121452,121466,121468,121472,121562,121666,121678,121682,121685,121787,121791,121797,122014,122025,122131,122135,122150,122413,122431,122435,122717,122737,123051,123161,123194,123198,123214,123419,123439,123470,123474,123486,123948,123950,123955,123960,124013,124104,124111,124207,124225,124234,124323,124326,124332,124402,124408,124527,124532,124596,124606,124608,124763,124765,124823],[934,120801,637],{"id":637},[46871,120803],{":rows":120804},"[{\"label\":\"Subpath\",\"value\":\"`@noy-db\u002Fhub` (root export)\"},{\"label\":\"Type\",\"value\":\"Core feature — activated per collection by declaring a `computed` map.\"}]",[1022,120806,29857],{"id":29856},[937,120808,120809,120810,120813,120814,120816],{},"A computed field is a value your data model owns instead of your callers: you declare a pure function on the collection (",[941,120811,120812],{},"netAmount = unitPrice × qty","), and every ",[941,120815,20953],{}," evaluates it and stores the result on the record. Callers never supply the field — and if they try, the computed value wins. Because the result is materialized (physically stored, not recalculated on read), a computed field behaves like any other field afterwards: it is encrypted with the record, queryable, indexable, and aggregatable.",[937,120818,120819,120820,120823,120824,120826,120827,120830,120831,120834,120835,120837],{},"Functions run in ",[950,120821,120822],{},"declaration order",", and each one sees the record with all earlier computed fields already filled in — so ",[941,120825,64200],{}," can read ",[941,120828,120829],{},"netAmount",". Evaluation happens ",[950,120832,120833],{},"before schema validation",", which means the schema validates the ",[1092,120836,637],{}," result, not the raw input.",[937,120839,3977,120840,120842,120843,120845],{},[941,120841,943],{}," factory to wire: declaring a ",[941,120844,637],{}," map on a collection is the opt-in. The evaluation engine loads lazily on the first write to a computed-declared collection, so collections without one pay nothing in bundle size.",[937,120847,10993,120848,120850,120851,111497,120854,120856,120857,120860,120861,120863,120864,1952,120867,120869,120870,120872,120873,120876,120877,120882,120883,120888],{},[941,120849,111464],{},", computed is a full ",[950,120852,120853],{},"field feature",[1007,120855,896],{"href":897},"): each entry can carry declared dependencies and a mode —\n",[941,120858,120859],{},"{ fn, deps, mode }"," — and the same declaration composes through the ",[941,120862,3040],{}," spelling,\n",[941,120865,120866],{},"via(computed(fn, { deps, mode }))",[941,120868,117230],{}," names the field(s) ",[941,120871,12070],{}," reads, feeding the\ndependency graph that folds each computed field's effective security posture from its sources;\n",[941,120874,120875],{},"mode"," picks ",[950,120878,120879],{},[941,120880,120881],{},"'materialized'"," (the default — byte-for-byte the write-time compute described\nabove) or ",[950,120884,120885],{},[941,120886,120887],{},"'virtual'"," — computed fresh on every read and never stored.",[1022,120890,58394],{"id":58393},[937,120892,120893,120894,120896],{},"Declare a ",[941,120895,637],{}," map — field name to pure, synchronous function — in the collection config:",[2217,120898,120900],{"className":2219,"code":120899,"language":2221,"meta":1037,"style":1037},"import { z } from 'zod'\nimport { createNoydb } from '@noy-db\u002Fhub'\nimport { toMemory } from '@noy-db\u002Fto-memory'\n\nconst db = await createNoydb({\n  store: toMemory(),\n  user: 'alice',\n  secret: 'a-strong-secret',\n})\n\ntype Line = {\n  id: string\n  unitPrice: number\n  qty: number\n  netAmount?: number\n  taxAmount?: number\n  total?: number\n}\n\nconst vault = await db.openVault('acme')\nconst lines = vault.collection\u003CLine>('lines', {\n  schema: z.object({\n    id: z.string(),\n    unitPrice: z.number(),\n    qty: z.number(),\n    \u002F\u002F computed targets are optional: callers never supply them\n    netAmount: z.number().optional(),\n    taxAmount: z.number().optional(),\n    total: z.number().optional(),\n  }),\n  computed: {\n    netAmount: (r) => (r.unitPrice as number) * (r.qty as number),\n    taxAmount: (r) => (r.netAmount as number) * 0.2,        \u002F\u002F reads the field above\n    total:     (r) => (r.netAmount as number) + (r.taxAmount as number),\n  },\n})\n",[941,120901,120902,120920,120938,120956,120960,120976,120988,121002,121016,121022,121026,121037,121045,121054,121063,121072,121081,121090,121094,121098,121124,121159,121175,121191,121208,121225,121230,121253,121276,121298,121306,121315,121361,121398,121442,121446],{"__ignoreMap":1037},[2225,120903,120904,120906,120908,120910,120912,120914,120916,120918],{"class":2227,"line":2228},[2225,120905,2232],{"class":2231},[2225,120907,2236],{"class":2235},[2225,120909,28314],{"class":2239},[2225,120911,2243],{"class":2235},[2225,120913,2246],{"class":2231},[2225,120915,2249],{"class":2235},[2225,120917,28323],{"class":2252},[2225,120919,2256],{"class":2235},[2225,120921,120922,120924,120926,120928,120930,120932,120934,120936],{"class":2227,"line":1041},[2225,120923,2232],{"class":2231},[2225,120925,2236],{"class":2235},[2225,120927,2744],{"class":2239},[2225,120929,2243],{"class":2235},[2225,120931,2246],{"class":2231},[2225,120933,2249],{"class":2235},[2225,120935,1086],{"class":2252},[2225,120937,2256],{"class":2235},[2225,120939,120940,120942,120944,120946,120948,120950,120952,120954],{"class":2227,"line":1038},[2225,120941,2232],{"class":2231},[2225,120943,2236],{"class":2235},[2225,120945,68576],{"class":2239},[2225,120947,2243],{"class":2235},[2225,120949,2246],{"class":2231},[2225,120951,2249],{"class":2235},[2225,120953,9638],{"class":2252},[2225,120955,2256],{"class":2235},[2225,120957,120958],{"class":2227,"line":2288},[2225,120959,2285],{"emptyLinePlaceholder":2284},[2225,120961,120962,120964,120966,120968,120970,120972,120974],{"class":2227,"line":2304},[2225,120963,2775],{"class":2774},[2225,120965,2778],{"class":2239},[2225,120967,2781],{"class":2235},[2225,120969,2784],{"class":2231},[2225,120971,2744],{"class":2291},[2225,120973,2789],{"class":2239},[2225,120975,4200],{"class":2235},[2225,120977,120978,120980,120982,120984,120986],{"class":2227,"line":2356},[2225,120979,15608],{"class":2307},[2225,120981,2311],{"class":2235},[2225,120983,68576],{"class":2291},[2225,120985,4228],{"class":2239},[2225,120987,1707],{"class":2235},[2225,120989,120990,120992,120994,120996,120998,121000],{"class":2227,"line":2830},[2225,120991,4205],{"class":2307},[2225,120993,2311],{"class":2235},[2225,120995,2249],{"class":2235},[2225,120997,6408],{"class":2252},[2225,120999,2805],{"class":2235},[2225,121001,1707],{"class":2235},[2225,121003,121004,121006,121008,121010,121012,121014],{"class":2227,"line":2861},[2225,121005,10423],{"class":2307},[2225,121007,2311],{"class":2235},[2225,121009,2249],{"class":2235},[2225,121011,6421],{"class":2252},[2225,121013,2805],{"class":2235},[2225,121015,1707],{"class":2235},[2225,121017,121018,121020],{"class":2227,"line":4246},[2225,121019,2359],{"class":2235},[2225,121021,2362],{"class":2239},[2225,121023,121024],{"class":2227,"line":4253},[2225,121025,2285],{"emptyLinePlaceholder":2284},[2225,121027,121028,121030,121033,121035],{"class":2227,"line":4259},[2225,121029,6339],{"class":2774},[2225,121031,121032],{"class":6342}," Line",[2225,121034,6346],{"class":2235},[2225,121036,2301],{"class":2235},[2225,121038,121039,121041,121043],{"class":2227,"line":6548},[2225,121040,28371],{"class":2307},[2225,121042,2311],{"class":2235},[2225,121044,7014],{"class":6342},[2225,121046,121047,121050,121052],{"class":2227,"line":6576},[2225,121048,121049],{"class":2307},"  unitPrice",[2225,121051,2311],{"class":2235},[2225,121053,12024],{"class":6342},[2225,121055,121056,121059,121061],{"class":2227,"line":6582},[2225,121057,121058],{"class":2307},"  qty",[2225,121060,2311],{"class":2235},[2225,121062,12024],{"class":6342},[2225,121064,121065,121068,121070],{"class":2227,"line":6587},[2225,121066,121067],{"class":2307},"  netAmount",[2225,121069,7011],{"class":2235},[2225,121071,12024],{"class":6342},[2225,121073,121074,121077,121079],{"class":2227,"line":7174},[2225,121075,121076],{"class":2307},"  taxAmount",[2225,121078,7011],{"class":2235},[2225,121080,12024],{"class":6342},[2225,121082,121083,121086,121088],{"class":2227,"line":7180},[2225,121084,121085],{"class":2307},"  total",[2225,121087,7011],{"class":2235},[2225,121089,12024],{"class":6342},[2225,121091,121092],{"class":2227,"line":7220},[2225,121093,7351],{"class":2235},[2225,121095,121096],{"class":2227,"line":7225},[2225,121097,2285],{"emptyLinePlaceholder":2284},[2225,121099,121100,121102,121104,121106,121108,121110,121112,121114,121116,121118,121120,121122],{"class":2227,"line":7231},[2225,121101,2775],{"class":2774},[2225,121103,2835],{"class":2239},[2225,121105,2781],{"class":2235},[2225,121107,2784],{"class":2231},[2225,121109,2842],{"class":2239},[2225,121111,1520],{"class":2235},[2225,121113,2847],{"class":2291},[2225,121115,2789],{"class":2239},[2225,121117,2805],{"class":2235},[2225,121119,6450],{"class":2252},[2225,121121,2805],{"class":2235},[2225,121123,2362],{"class":2239},[2225,121125,121126,121128,121131,121133,121135,121137,121139,121141,121144,121146,121148,121150,121153,121155,121157],{"class":2227,"line":7267},[2225,121127,2775],{"class":2774},[2225,121129,121130],{"class":2239}," lines ",[2225,121132,2781],{"class":2235},[2225,121134,6466],{"class":2239},[2225,121136,1520],{"class":2235},[2225,121138,6471],{"class":2291},[2225,121140,6474],{"class":2235},[2225,121142,121143],{"class":6342},"Line",[2225,121145,6480],{"class":2235},[2225,121147,2789],{"class":2239},[2225,121149,2805],{"class":2235},[2225,121151,121152],{"class":2252},"lines",[2225,121154,2805],{"class":2235},[2225,121156,2298],{"class":2235},[2225,121158,2301],{"class":2235},[2225,121160,121161,121163,121165,121167,121169,121171,121173],{"class":2227,"line":7272},[2225,121162,28618],{"class":2307},[2225,121164,2311],{"class":2235},[2225,121166,28314],{"class":2239},[2225,121168,1520],{"class":2235},[2225,121170,25505],{"class":2291},[2225,121172,2789],{"class":2239},[2225,121174,4200],{"class":2235},[2225,121176,121177,121179,121181,121183,121185,121187,121189],{"class":2227,"line":7278},[2225,121178,60558],{"class":2307},[2225,121180,2311],{"class":2235},[2225,121182,28314],{"class":2239},[2225,121184,1520],{"class":2235},[2225,121186,7259],{"class":2291},[2225,121188,4228],{"class":2239},[2225,121190,1707],{"class":2235},[2225,121192,121193,121196,121198,121200,121202,121204,121206],{"class":2227,"line":7303},[2225,121194,121195],{"class":2307},"    unitPrice",[2225,121197,2311],{"class":2235},[2225,121199,28314],{"class":2239},[2225,121201,1520],{"class":2235},[2225,121203,20636],{"class":2291},[2225,121205,4228],{"class":2239},[2225,121207,1707],{"class":2235},[2225,121209,121210,121213,121215,121217,121219,121221,121223],{"class":2227,"line":7308},[2225,121211,121212],{"class":2307},"    qty",[2225,121214,2311],{"class":2235},[2225,121216,28314],{"class":2239},[2225,121218,1520],{"class":2235},[2225,121220,20636],{"class":2291},[2225,121222,4228],{"class":2239},[2225,121224,1707],{"class":2235},[2225,121226,121227],{"class":2227,"line":7314},[2225,121228,121229],{"class":2763},"    \u002F\u002F computed targets are optional: callers never supply them\n",[2225,121231,121232,121235,121237,121239,121241,121243,121245,121247,121249,121251],{"class":2227,"line":7348},[2225,121233,121234],{"class":2307},"    netAmount",[2225,121236,2311],{"class":2235},[2225,121238,28314],{"class":2239},[2225,121240,1520],{"class":2235},[2225,121242,20636],{"class":2291},[2225,121244,4228],{"class":2239},[2225,121246,1520],{"class":2235},[2225,121248,2084],{"class":2291},[2225,121250,4228],{"class":2239},[2225,121252,1707],{"class":2235},[2225,121254,121255,121258,121260,121262,121264,121266,121268,121270,121272,121274],{"class":2227,"line":8613},[2225,121256,121257],{"class":2307},"    taxAmount",[2225,121259,2311],{"class":2235},[2225,121261,28314],{"class":2239},[2225,121263,1520],{"class":2235},[2225,121265,20636],{"class":2291},[2225,121267,4228],{"class":2239},[2225,121269,1520],{"class":2235},[2225,121271,2084],{"class":2291},[2225,121273,4228],{"class":2239},[2225,121275,1707],{"class":2235},[2225,121277,121278,121280,121282,121284,121286,121288,121290,121292,121294,121296],{"class":2227,"line":8633},[2225,121279,64102],{"class":2307},[2225,121281,2311],{"class":2235},[2225,121283,28314],{"class":2239},[2225,121285,1520],{"class":2235},[2225,121287,20636],{"class":2291},[2225,121289,4228],{"class":2239},[2225,121291,1520],{"class":2235},[2225,121293,2084],{"class":2291},[2225,121295,4228],{"class":2239},[2225,121297,1707],{"class":2235},[2225,121299,121300,121302,121304],{"class":2227,"line":8662},[2225,121301,19143],{"class":2235},[2225,121303,1381],{"class":2239},[2225,121305,1707],{"class":2235},[2225,121307,121308,121311,121313],{"class":2227,"line":8747},[2225,121309,121310],{"class":2307},"  computed",[2225,121312,2311],{"class":2235},[2225,121314,2301],{"class":2235},[2225,121316,121317,121319,121321,121323,121325,121327,121329,121332,121334,121337,121339,121341,121343,121346,121348,121350,121353,121355,121357,121359],{"class":2227,"line":8752},[2225,121318,121234],{"class":2291},[2225,121320,2311],{"class":2235},[2225,121322,953],{"class":2235},[2225,121324,13217],{"class":7052},[2225,121326,1381],{"class":2235},[2225,121328,8116],{"class":2774},[2225,121330,121331],{"class":2239}," (r",[2225,121333,1520],{"class":2235},[2225,121335,121336],{"class":2239},"unitPrice ",[2225,121338,4899],{"class":2231},[2225,121340,6356],{"class":6342},[2225,121342,8495],{"class":2239},[2225,121344,121345],{"class":2235},"*",[2225,121347,121331],{"class":2239},[2225,121349,1520],{"class":2235},[2225,121351,121352],{"class":2239},"qty ",[2225,121354,4899],{"class":2231},[2225,121356,6356],{"class":6342},[2225,121358,1381],{"class":2239},[2225,121360,1707],{"class":2235},[2225,121362,121363,121365,121367,121369,121371,121373,121375,121377,121379,121382,121384,121386,121388,121390,121393,121395],{"class":2227,"line":8768},[2225,121364,121257],{"class":2291},[2225,121366,2311],{"class":2235},[2225,121368,953],{"class":2235},[2225,121370,13217],{"class":7052},[2225,121372,1381],{"class":2235},[2225,121374,8116],{"class":2774},[2225,121376,121331],{"class":2239},[2225,121378,1520],{"class":2235},[2225,121380,121381],{"class":2239},"netAmount ",[2225,121383,4899],{"class":2231},[2225,121385,6356],{"class":6342},[2225,121387,8495],{"class":2239},[2225,121389,121345],{"class":2235},[2225,121391,121392],{"class":6527}," 0.2",[2225,121394,2298],{"class":2235},[2225,121396,121397],{"class":2763},"        \u002F\u002F reads the field above\n",[2225,121399,121400,121402,121404,121407,121409,121411,121413,121415,121417,121419,121421,121423,121425,121427,121429,121431,121434,121436,121438,121440],{"class":2227,"line":8785},[2225,121401,64102],{"class":2291},[2225,121403,2311],{"class":2235},[2225,121405,121406],{"class":2235},"     (",[2225,121408,13217],{"class":7052},[2225,121410,1381],{"class":2235},[2225,121412,8116],{"class":2774},[2225,121414,121331],{"class":2239},[2225,121416,1520],{"class":2235},[2225,121418,121381],{"class":2239},[2225,121420,4899],{"class":2231},[2225,121422,6356],{"class":6342},[2225,121424,8495],{"class":2239},[2225,121426,71297],{"class":2235},[2225,121428,121331],{"class":2239},[2225,121430,1520],{"class":2235},[2225,121432,121433],{"class":2239},"taxAmount ",[2225,121435,4899],{"class":2231},[2225,121437,6356],{"class":6342},[2225,121439,1381],{"class":2239},[2225,121441,1707],{"class":2235},[2225,121443,121444],{"class":2227,"line":8815},[2225,121445,19017],{"class":2235},[2225,121447,121448,121450],{"class":2227,"line":8854},[2225,121449,2359],{"class":2235},[2225,121451,2362],{"class":2239},[937,121453,121454,121455,121458,121459,121462,121463,121465],{},"Mark computed targets ",[941,121456,121457],{},".optional()"," in the schema — the user never supplies them — but constrain them as strictly as you like (",[941,121460,121461],{},".nonnegative()",", ranges, …): the schema runs ",[1092,121464,10761],{}," computation and validates the final values.",[1022,121467,29893],{"id":29892},[1591,121469,121471],{"id":121470},"write-a-record-computed-fields-materialize","Write a record — computed fields materialize",[2217,121473,121475],{"className":2219,"code":121474,"language":2221,"meta":1037,"style":1037},"await lines.put('a', { id: 'a', unitPrice: 10, qty: 2 })\n\nconst row = await lines.get('a')\n",[941,121476,121477,121532,121536],{"__ignoreMap":1037},[2225,121478,121479,121481,121484,121486,121488,121490,121492,121494,121496,121498,121500,121502,121504,121506,121508,121510,121512,121515,121517,121519,121521,121524,121526,121528,121530],{"class":2227,"line":2228},[2225,121480,6500],{"class":2231},[2225,121482,121483],{"class":2239}," lines",[2225,121485,1520],{"class":2235},[2225,121487,3161],{"class":2291},[2225,121489,2789],{"class":2239},[2225,121491,2805],{"class":2235},[2225,121493,1007],{"class":2252},[2225,121495,2805],{"class":2235},[2225,121497,2298],{"class":2235},[2225,121499,2236],{"class":2235},[2225,121501,7070],{"class":2307},[2225,121503,2311],{"class":2235},[2225,121505,2249],{"class":2235},[2225,121507,1007],{"class":2252},[2225,121509,2805],{"class":2235},[2225,121511,2298],{"class":2235},[2225,121513,121514],{"class":2307}," unitPrice",[2225,121516,2311],{"class":2235},[2225,121518,45554],{"class":6527},[2225,121520,2298],{"class":2235},[2225,121522,121523],{"class":2307}," qty",[2225,121525,2311],{"class":2235},[2225,121527,18961],{"class":6527},[2225,121529,2243],{"class":2235},[2225,121531,2362],{"class":2239},[2225,121533,121534],{"class":2227,"line":1041},[2225,121535,2285],{"emptyLinePlaceholder":2284},[2225,121537,121538,121540,121542,121544,121546,121548,121550,121552,121554,121556,121558,121560],{"class":2227,"line":1038},[2225,121539,2775],{"class":2774},[2225,121541,118164],{"class":2239},[2225,121543,2781],{"class":2235},[2225,121545,2784],{"class":2231},[2225,121547,121483],{"class":2239},[2225,121549,1520],{"class":2235},[2225,121551,3158],{"class":2291},[2225,121553,2789],{"class":2239},[2225,121555,2805],{"class":2235},[2225,121557,1007],{"class":2252},[2225,121559,2805],{"class":2235},[2225,121561,2362],{"class":2239},[2217,121563,121565],{"className":6604,"code":121564,"language":141,"meta":1037,"style":1037},"{\n  \"id\": \"a\",\n  \"unitPrice\": 10,\n  \"qty\": 2,\n  \"netAmount\": 20,\n  \"taxAmount\": 4,\n  \"total\": 24\n}\n",[941,121566,121567,121571,121589,121604,121619,121634,121649,121662],{"__ignoreMap":1037},[2225,121568,121569],{"class":2227,"line":2228},[2225,121570,4200],{"class":2235},[2225,121572,121573,121575,121577,121579,121581,121583,121585,121587],{"class":2227,"line":1041},[2225,121574,9368],{"class":2235},[2225,121576,6983],{"class":2774},[2225,121578,6620],{"class":2235},[2225,121580,2311],{"class":2235},[2225,121582,6614],{"class":2235},[2225,121584,1007],{"class":2252},[2225,121586,6620],{"class":2235},[2225,121588,1707],{"class":2235},[2225,121590,121591,121593,121596,121598,121600,121602],{"class":2227,"line":1038},[2225,121592,9368],{"class":2235},[2225,121594,121595],{"class":2774},"unitPrice",[2225,121597,6620],{"class":2235},[2225,121599,2311],{"class":2235},[2225,121601,45554],{"class":6527},[2225,121603,1707],{"class":2235},[2225,121605,121606,121608,121611,121613,121615,121617],{"class":2227,"line":2288},[2225,121607,9368],{"class":2235},[2225,121609,121610],{"class":2774},"qty",[2225,121612,6620],{"class":2235},[2225,121614,2311],{"class":2235},[2225,121616,18961],{"class":6527},[2225,121618,1707],{"class":2235},[2225,121620,121621,121623,121625,121627,121629,121632],{"class":2227,"line":2304},[2225,121622,9368],{"class":2235},[2225,121624,120829],{"class":2774},[2225,121626,6620],{"class":2235},[2225,121628,2311],{"class":2235},[2225,121630,121631],{"class":6527}," 20",[2225,121633,1707],{"class":2235},[2225,121635,121636,121638,121641,121643,121645,121647],{"class":2227,"line":2356},[2225,121637,9368],{"class":2235},[2225,121639,121640],{"class":2774},"taxAmount",[2225,121642,6620],{"class":2235},[2225,121644,2311],{"class":2235},[2225,121646,100626],{"class":6527},[2225,121648,1707],{"class":2235},[2225,121650,121651,121653,121655,121657,121659],{"class":2227,"line":2830},[2225,121652,9368],{"class":2235},[2225,121654,64200],{"class":2774},[2225,121656,6620],{"class":2235},[2225,121658,2311],{"class":2235},[2225,121660,121661],{"class":6527}," 24\n",[2225,121663,121664],{"class":2227,"line":2861},[2225,121665,7351],{"class":2235},[937,121667,121668,121669,121671,121672,121674,121675,121677],{},"The three functions ran in declaration order — ",[941,121670,120829],{}," first, then ",[941,121673,121640],{}," reading it, then ",[941,121676,64200],{}," reading both — and the results were stored with the record. Reads are plain reads; nothing is recalculated.",[1591,121679,121681],{"id":121680},"schema-owned-a-user-supplied-value-is-overwritten","Schema-owned: a user-supplied value is overwritten",[937,121683,121684],{},"A computed field belongs to the schema, not the caller. If a write supplies a value for a computed field, the computed result silently replaces it:",[2217,121686,121688],{"className":2219,"code":121687,"language":2221,"meta":1037,"style":1037},"await lines.put('b', { id: 'b', unitPrice: 5, qty: 3, total: 9999 })\n\n;(await lines.get('b'))?.total \u002F\u002F → 18  (never 9999)\n",[941,121689,121690,121751,121755],{"__ignoreMap":1037},[2225,121691,121692,121694,121696,121698,121700,121702,121704,121706,121708,121710,121712,121714,121716,121718,121720,121722,121724,121726,121728,121730,121732,121734,121736,121738,121740,121742,121744,121747,121749],{"class":2227,"line":2228},[2225,121693,6500],{"class":2231},[2225,121695,121483],{"class":2239},[2225,121697,1520],{"class":2235},[2225,121699,3161],{"class":2291},[2225,121701,2789],{"class":2239},[2225,121703,2805],{"class":2235},[2225,121705,80529],{"class":2252},[2225,121707,2805],{"class":2235},[2225,121709,2298],{"class":2235},[2225,121711,2236],{"class":2235},[2225,121713,7070],{"class":2307},[2225,121715,2311],{"class":2235},[2225,121717,2249],{"class":2235},[2225,121719,80529],{"class":2252},[2225,121721,2805],{"class":2235},[2225,121723,2298],{"class":2235},[2225,121725,121514],{"class":2307},[2225,121727,2311],{"class":2235},[2225,121729,18996],{"class":6527},[2225,121731,2298],{"class":2235},[2225,121733,121523],{"class":2307},[2225,121735,2311],{"class":2235},[2225,121737,14460],{"class":6527},[2225,121739,2298],{"class":2235},[2225,121741,27452],{"class":2307},[2225,121743,2311],{"class":2235},[2225,121745,121746],{"class":6527}," 9999",[2225,121748,2243],{"class":2235},[2225,121750,2362],{"class":2239},[2225,121752,121753],{"class":2227,"line":1041},[2225,121754,2285],{"emptyLinePlaceholder":2284},[2225,121756,121757,121759,121761,121763,121765,121767,121769,121771,121773,121775,121777,121779,121781,121784],{"class":2227,"line":1038},[2225,121758,3769],{"class":2235},[2225,121760,2789],{"class":2239},[2225,121762,6500],{"class":2231},[2225,121764,121483],{"class":2239},[2225,121766,1520],{"class":2235},[2225,121768,3158],{"class":2291},[2225,121770,2789],{"class":2239},[2225,121772,2805],{"class":2235},[2225,121774,80529],{"class":2252},[2225,121776,2805],{"class":2235},[2225,121778,8454],{"class":2239},[2225,121780,8457],{"class":2235},[2225,121782,121783],{"class":2239},"total ",[2225,121785,121786],{"class":2763},"\u002F\u002F → 18  (never 9999)\n",[1591,121788,121790],{"id":121789},"the-schema-validates-the-computed-result","The schema validates the computed result",[937,121792,121793,121794,121796],{},"Because computation runs first, schema constraints apply to what the formula produced. A formula that yields an invalid value makes the whole ",[941,121795,20953],{}," reject — nothing is stored:",[2217,121798,121800],{"className":2219,"code":121799,"language":2221,"meta":1037,"style":1037},"const audited = vault.collection('audited', {\n  schema: z.object({\n    id: z.string(),\n    amount: z.number(),\n    fee: z.number().nonnegative(),\n  }),\n  computed: { fee: (r) => (r.amount as number) * -0.01 },\n})\n\nawait audited.put('x', { id: 'x', amount: 100 })\n\u002F\u002F → rejects with a SchemaValidationError: fee = -1 violates nonnegative()\n",[941,121801,121802,121830,121846,121862,121878,121902,121910,121953,121959,121963,122009],{"__ignoreMap":1037},[2225,121803,121804,121806,121809,121811,121813,121815,121817,121819,121821,121824,121826,121828],{"class":2227,"line":2228},[2225,121805,2775],{"class":2774},[2225,121807,121808],{"class":2239}," audited ",[2225,121810,2781],{"class":2235},[2225,121812,6466],{"class":2239},[2225,121814,1520],{"class":2235},[2225,121816,6471],{"class":2291},[2225,121818,2789],{"class":2239},[2225,121820,2805],{"class":2235},[2225,121822,121823],{"class":2252},"audited",[2225,121825,2805],{"class":2235},[2225,121827,2298],{"class":2235},[2225,121829,2301],{"class":2235},[2225,121831,121832,121834,121836,121838,121840,121842,121844],{"class":2227,"line":1041},[2225,121833,28618],{"class":2307},[2225,121835,2311],{"class":2235},[2225,121837,28314],{"class":2239},[2225,121839,1520],{"class":2235},[2225,121841,25505],{"class":2291},[2225,121843,2789],{"class":2239},[2225,121845,4200],{"class":2235},[2225,121847,121848,121850,121852,121854,121856,121858,121860],{"class":2227,"line":1038},[2225,121849,60558],{"class":2307},[2225,121851,2311],{"class":2235},[2225,121853,28314],{"class":2239},[2225,121855,1520],{"class":2235},[2225,121857,7259],{"class":2291},[2225,121859,4228],{"class":2239},[2225,121861,1707],{"class":2235},[2225,121863,121864,121866,121868,121870,121872,121874,121876],{"class":2227,"line":2288},[2225,121865,60651],{"class":2307},[2225,121867,2311],{"class":2235},[2225,121869,28314],{"class":2239},[2225,121871,1520],{"class":2235},[2225,121873,20636],{"class":2291},[2225,121875,4228],{"class":2239},[2225,121877,1707],{"class":2235},[2225,121879,121880,121883,121885,121887,121889,121891,121893,121895,121898,121900],{"class":2227,"line":2304},[2225,121881,121882],{"class":2307},"    fee",[2225,121884,2311],{"class":2235},[2225,121886,28314],{"class":2239},[2225,121888,1520],{"class":2235},[2225,121890,20636],{"class":2291},[2225,121892,4228],{"class":2239},[2225,121894,1520],{"class":2235},[2225,121896,121897],{"class":2291},"nonnegative",[2225,121899,4228],{"class":2239},[2225,121901,1707],{"class":2235},[2225,121903,121904,121906,121908],{"class":2227,"line":2356},[2225,121905,19143],{"class":2235},[2225,121907,1381],{"class":2239},[2225,121909,1707],{"class":2235},[2225,121911,121912,121914,121916,121918,121921,121923,121925,121927,121929,121931,121933,121935,121938,121940,121942,121944,121946,121948,121951],{"class":2227,"line":2830},[2225,121913,121310],{"class":2307},[2225,121915,2311],{"class":2235},[2225,121917,2236],{"class":2235},[2225,121919,121920],{"class":2291}," fee",[2225,121922,2311],{"class":2235},[2225,121924,953],{"class":2235},[2225,121926,13217],{"class":7052},[2225,121928,1381],{"class":2235},[2225,121930,8116],{"class":2774},[2225,121932,121331],{"class":2239},[2225,121934,1520],{"class":2235},[2225,121936,121937],{"class":2239},"amount ",[2225,121939,4899],{"class":2231},[2225,121941,6356],{"class":6342},[2225,121943,8495],{"class":2239},[2225,121945,121345],{"class":2235},[2225,121947,28754],{"class":2235},[2225,121949,121950],{"class":6527},"0.01",[2225,121952,2353],{"class":2235},[2225,121954,121955,121957],{"class":2227,"line":2861},[2225,121956,2359],{"class":2235},[2225,121958,2362],{"class":2239},[2225,121960,121961],{"class":2227,"line":4246},[2225,121962,2285],{"emptyLinePlaceholder":2284},[2225,121964,121965,121967,121970,121972,121974,121976,121978,121981,121983,121985,121987,121989,121991,121993,121995,121997,121999,122001,122003,122005,122007],{"class":2227,"line":4253},[2225,121966,6500],{"class":2231},[2225,121968,121969],{"class":2239}," audited",[2225,121971,1520],{"class":2235},[2225,121973,3161],{"class":2291},[2225,121975,2789],{"class":2239},[2225,121977,2805],{"class":2235},[2225,121979,121980],{"class":2252},"x",[2225,121982,2805],{"class":2235},[2225,121984,2298],{"class":2235},[2225,121986,2236],{"class":2235},[2225,121988,7070],{"class":2307},[2225,121990,2311],{"class":2235},[2225,121992,2249],{"class":2235},[2225,121994,121980],{"class":2252},[2225,121996,2805],{"class":2235},[2225,121998,2298],{"class":2235},[2225,122000,6351],{"class":2307},[2225,122002,2311],{"class":2235},[2225,122004,6528],{"class":6527},[2225,122006,2243],{"class":2235},[2225,122008,2362],{"class":2239},[2225,122010,122011],{"class":2227,"line":4259},[2225,122012,122013],{"class":2763},"\u002F\u002F → rejects with a SchemaValidationError: fee = -1 violates nonnegative()\n",[937,122015,122016,122017,122020,122021,122024],{},"And a formula that ",[1092,122018,122019],{},"throws"," rejects the write with a ",[941,122022,122023],{},"ComputedFieldError"," naming the field and the record:",[2217,122026,122028],{"className":2219,"code":122027,"language":2221,"meta":1037,"style":1037},"computed: { total: () => { throw new Error('boom') } }\n\nawait lines.put('c', { id: 'c', unitPrice: 1, qty: 1 })\n\u002F\u002F → rejects with ComputedFieldError: computed field \"total\" threw for record \"c\": boom\n",[941,122029,122030,122070,122074,122126],{"__ignoreMap":1037},[2225,122031,122032,122034,122036,122038,122040,122042,122044,122046,122048,122051,122053,122055,122057,122059,122062,122064,122066,122068],{"class":2227,"line":2228},[2225,122033,637],{"class":6342},[2225,122035,2311],{"class":2235},[2225,122037,2236],{"class":2235},[2225,122039,27452],{"class":6342},[2225,122041,2311],{"class":2235},[2225,122043,13130],{"class":2235},[2225,122045,8116],{"class":2774},[2225,122047,2236],{"class":2235},[2225,122049,122050],{"class":2231}," throw",[2225,122052,8130],{"class":2235},[2225,122054,46519],{"class":2291},[2225,122056,2789],{"class":2307},[2225,122058,2805],{"class":2235},[2225,122060,122061],{"class":2252},"boom",[2225,122063,2805],{"class":2235},[2225,122065,8495],{"class":2307},[2225,122067,2359],{"class":2235},[2225,122069,6369],{"class":2235},[2225,122071,122072],{"class":2227,"line":1041},[2225,122073,2285],{"emptyLinePlaceholder":2284},[2225,122075,122076,122078,122080,122082,122084,122086,122088,122090,122092,122094,122096,122098,122100,122102,122104,122106,122108,122110,122112,122114,122116,122118,122120,122122,122124],{"class":2227,"line":1038},[2225,122077,6500],{"class":2231},[2225,122079,121483],{"class":2239},[2225,122081,1520],{"class":2235},[2225,122083,3161],{"class":2291},[2225,122085,2789],{"class":2239},[2225,122087,2805],{"class":2235},[2225,122089,8200],{"class":2252},[2225,122091,2805],{"class":2235},[2225,122093,2298],{"class":2235},[2225,122095,2236],{"class":2235},[2225,122097,7070],{"class":2307},[2225,122099,2311],{"class":2235},[2225,122101,2249],{"class":2235},[2225,122103,8200],{"class":2252},[2225,122105,2805],{"class":2235},[2225,122107,2298],{"class":2235},[2225,122109,121514],{"class":2307},[2225,122111,2311],{"class":2235},[2225,122113,9377],{"class":6527},[2225,122115,2298],{"class":2235},[2225,122117,121523],{"class":2307},[2225,122119,2311],{"class":2235},[2225,122121,9377],{"class":6527},[2225,122123,2243],{"class":2235},[2225,122125,2362],{"class":2239},[2225,122127,122128],{"class":2227,"line":2288},[2225,122129,122130],{"class":2763},"\u002F\u002F → rejects with ComputedFieldError: computed field \"total\" threw for record \"c\": boom\n",[1591,122132,122134],{"id":122133},"virtual-mode-computed-on-read-never-stored","Virtual mode — computed on read, never stored",[937,122136,122137,122140,122141,1625,122143,122145,122146,122149],{},[941,122138,122139],{},"mode: 'virtual'"," flips the evaluation to read time: the function runs fresh on every\n",[941,122142,14639],{},[941,122144,24407],{},", and the field is ",[950,122147,122148],{},"never stored"," — it is absent from the encrypted record\nbody entirely:",[2217,122151,122153],{"className":2219,"code":122152,"language":2221,"meta":1037,"style":1037},"import { via, computed } from '@noy-db\u002Fhub'\n\nconst items = vault.collection\u003C{ id: string; amount: number; doubled?: number }>('items', {\n  viaFields: {\n    doubled: via(computed((r) => (r.amount as number) * 2, { deps: ['amount'], mode: 'virtual' })),\n  },\n})\n\nawait items.put('r1', { id: 'r1', amount: 21 })\n;(await items.get('r1'))?.doubled \u002F\u002F → 42 — computed on this read, not persisted\n",[941,122154,122155,122179,122183,122236,122245,122322,122326,122332,122336,122381],{"__ignoreMap":1037},[2225,122156,122157,122159,122161,122164,122166,122169,122171,122173,122175,122177],{"class":2227,"line":2228},[2225,122158,2232],{"class":2231},[2225,122160,2236],{"class":2235},[2225,122162,122163],{"class":2239}," via",[2225,122165,2298],{"class":2235},[2225,122167,122168],{"class":2239}," computed",[2225,122170,2243],{"class":2235},[2225,122172,2246],{"class":2231},[2225,122174,2249],{"class":2235},[2225,122176,1086],{"class":2252},[2225,122178,2256],{"class":2235},[2225,122180,122181],{"class":2227,"line":1041},[2225,122182,2285],{"emptyLinePlaceholder":2284},[2225,122184,122185,122187,122189,122191,122193,122195,122197,122199,122201,122203,122205,122207,122209,122211,122213,122215,122218,122220,122222,122224,122226,122228,122230,122232,122234],{"class":2227,"line":1038},[2225,122186,2775],{"class":2774},[2225,122188,42531],{"class":2239},[2225,122190,2781],{"class":2235},[2225,122192,6466],{"class":2239},[2225,122194,1520],{"class":2235},[2225,122196,6471],{"class":2291},[2225,122198,17851],{"class":2235},[2225,122200,7070],{"class":2307},[2225,122202,2311],{"class":2235},[2225,122204,6366],{"class":6342},[2225,122206,3769],{"class":2235},[2225,122208,6351],{"class":2307},[2225,122210,2311],{"class":2235},[2225,122212,6356],{"class":6342},[2225,122214,3769],{"class":2235},[2225,122216,122217],{"class":2307}," doubled",[2225,122219,7011],{"class":2235},[2225,122221,6356],{"class":6342},[2225,122223,85293],{"class":2235},[2225,122225,2789],{"class":2239},[2225,122227,2805],{"class":2235},[2225,122229,39195],{"class":2252},[2225,122231,2805],{"class":2235},[2225,122233,2298],{"class":2235},[2225,122235,2301],{"class":2235},[2225,122237,122238,122241,122243],{"class":2227,"line":2288},[2225,122239,122240],{"class":2307},"  viaFields",[2225,122242,2311],{"class":2235},[2225,122244,2301],{"class":2235},[2225,122246,122247,122250,122252,122254,122256,122258,122260,122262,122264,122266,122268,122270,122272,122274,122276,122278,122280,122282,122284,122286,122288,122291,122293,122295,122297,122299,122301,122303,122305,122307,122309,122311,122314,122316,122318,122320],{"class":2227,"line":2304},[2225,122248,122249],{"class":2307},"    doubled",[2225,122251,2311],{"class":2235},[2225,122253,122163],{"class":2291},[2225,122255,2789],{"class":2239},[2225,122257,637],{"class":2291},[2225,122259,2789],{"class":2239},[2225,122261,2789],{"class":2235},[2225,122263,13217],{"class":7052},[2225,122265,1381],{"class":2235},[2225,122267,8116],{"class":2774},[2225,122269,121331],{"class":2239},[2225,122271,1520],{"class":2235},[2225,122273,121937],{"class":2239},[2225,122275,4899],{"class":2231},[2225,122277,6356],{"class":6342},[2225,122279,8495],{"class":2239},[2225,122281,121345],{"class":2235},[2225,122283,18961],{"class":6527},[2225,122285,2298],{"class":2235},[2225,122287,2236],{"class":2235},[2225,122289,122290],{"class":2307}," deps",[2225,122292,2311],{"class":2235},[2225,122294,8667],{"class":2239},[2225,122296,2805],{"class":2235},[2225,122298,6617],{"class":2252},[2225,122300,2805],{"class":2235},[2225,122302,8803],{"class":2239},[2225,122304,2298],{"class":2235},[2225,122306,32303],{"class":2307},[2225,122308,2311],{"class":2235},[2225,122310,2249],{"class":2235},[2225,122312,122313],{"class":2252},"virtual",[2225,122315,2805],{"class":2235},[2225,122317,2243],{"class":2235},[2225,122319,8454],{"class":2239},[2225,122321,1707],{"class":2235},[2225,122323,122324],{"class":2227,"line":2356},[2225,122325,19017],{"class":2235},[2225,122327,122328,122330],{"class":2227,"line":2830},[2225,122329,2359],{"class":2235},[2225,122331,2362],{"class":2239},[2225,122333,122334],{"class":2227,"line":2861},[2225,122335,2285],{"emptyLinePlaceholder":2284},[2225,122337,122338,122340,122342,122344,122346,122348,122350,122352,122354,122356,122358,122360,122362,122364,122366,122368,122370,122372,122374,122377,122379],{"class":2227,"line":4246},[2225,122339,6500],{"class":2231},[2225,122341,24980],{"class":2239},[2225,122343,1520],{"class":2235},[2225,122345,3161],{"class":2291},[2225,122347,2789],{"class":2239},[2225,122349,2805],{"class":2235},[2225,122351,75463],{"class":2252},[2225,122353,2805],{"class":2235},[2225,122355,2298],{"class":2235},[2225,122357,2236],{"class":2235},[2225,122359,7070],{"class":2307},[2225,122361,2311],{"class":2235},[2225,122363,2249],{"class":2235},[2225,122365,75463],{"class":2252},[2225,122367,2805],{"class":2235},[2225,122369,2298],{"class":2235},[2225,122371,6351],{"class":2307},[2225,122373,2311],{"class":2235},[2225,122375,122376],{"class":6527}," 21",[2225,122378,2243],{"class":2235},[2225,122380,2362],{"class":2239},[2225,122382,122383,122385,122387,122389,122391,122393,122395,122397,122399,122401,122403,122405,122407,122410],{"class":2227,"line":4253},[2225,122384,3769],{"class":2235},[2225,122386,2789],{"class":2239},[2225,122388,6500],{"class":2231},[2225,122390,24980],{"class":2239},[2225,122392,1520],{"class":2235},[2225,122394,3158],{"class":2291},[2225,122396,2789],{"class":2239},[2225,122398,2805],{"class":2235},[2225,122400,75463],{"class":2252},[2225,122402,2805],{"class":2235},[2225,122404,8454],{"class":2239},[2225,122406,8457],{"class":2235},[2225,122408,122409],{"class":2239},"doubled ",[2225,122411,122412],{"class":2763},"\u002F\u002F → 42 — computed on this read, not persisted\n",[937,122414,122415,122416,104458,122419,122421,122422,122424,122425,122428,122429,1520],{},"A virtual field is unconditionally non-queryable — ",[941,122417,122418],{},"query().where('doubled', …)",[941,122420,111478],{}," — because there is no stored value to match against. The same\n",[941,122423,120859],{}," shape works in the plain ",[941,122426,122427],{},"computed:"," sugar map (a bare function still means\nmaterialized mode with no declared deps). Virtual mode is construction-only: declaring it on a\nlater reconcile call for an already-constructed collection throws ",[941,122430,12881],{},[1591,122432,122434],{"id":122433},"composing-with-money-both-modes-format-the-derived-value","Composing with money — both modes format the derived value",[7646,122436,122438,122457,122711],{"color":10789,"icon":122437},"i-lucide-triangle-alert",[937,122439,122440,122441,122444,122445,22765,122448,5885,122450,5885,122453,122456],{},"**Landed here from ",[941,122442,122443],{},"field \"…\" is declared via both a sugar key and \\","viaFields`",[941,122446,122447],{},"?** The field is listed in **both** ",[941,122449,14128],{},[941,122451,122452],{},". A money-typed computed field is **one** ",[941,122454,122455],{},"entry composing both descriptors — do not also name it under","moneyFields`:",[2217,122458,122460],{"className":2219,"code":122459,"language":2221,"meta":1037,"style":1037},"\u002F\u002F ✗ throws: via(): field \"receiptAmount\" is declared via both a sugar key\n\u002F\u002F   and `viaFields` — declare it in one place only.\nmoneyFields: { receiptAmount: { currency: 'THB' } },\nviaFields:   { receiptAmount: via(computed(\u002F* … *\u002F)) },\n\n\u002F\u002F ✓ one entry, both descriptors\nviaFields: {\n  receiptAmount: via(\n    money({ currency: 'THB' }),\n    computed((r) => (r.kind === 'IV' ? r.paidTotal : r.amount), {\n      mode: 'virtual',\n      deps: ['kind', 'amount', 'paidTotal'],\n    }),\n  ),\n}\n\u002F\u002F receiptAmount '42.50' · receiptAmountFormatted 'THB 42.50' · receiptAmountNumber 42.5\n",[941,122461,122462,122467,122472,122503,122530,122534,122539,122547,122558,122583,122637,122652,122687,122695,122702,122706],{"__ignoreMap":1037},[2225,122463,122464],{"class":2227,"line":2228},[2225,122465,122466],{"class":2763},"\u002F\u002F ✗ throws: via(): field \"receiptAmount\" is declared via both a sugar key\n",[2225,122468,122469],{"class":2227,"line":1041},[2225,122470,122471],{"class":2763},"\u002F\u002F   and `viaFields` — declare it in one place only.\n",[2225,122473,122474,122476,122478,122480,122483,122485,122487,122490,122492,122494,122497,122499,122501],{"class":2227,"line":1038},[2225,122475,22765],{"class":6342},[2225,122477,2311],{"class":2235},[2225,122479,2236],{"class":2235},[2225,122481,122482],{"class":6342}," receiptAmount",[2225,122484,2311],{"class":2235},[2225,122486,2236],{"class":2235},[2225,122488,122489],{"class":6342}," currency",[2225,122491,2311],{"class":2235},[2225,122493,2249],{"class":2235},[2225,122495,122496],{"class":2252},"THB",[2225,122498,2805],{"class":2235},[2225,122500,2243],{"class":2235},[2225,122502,2353],{"class":2235},[2225,122504,122505,122507,122509,122511,122513,122515,122517,122519,122521,122523,122526,122528],{"class":2227,"line":2288},[2225,122506,5885],{"class":6342},[2225,122508,2311],{"class":2235},[2225,122510,58994],{"class":2235},[2225,122512,122482],{"class":6342},[2225,122514,2311],{"class":2235},[2225,122516,122163],{"class":2291},[2225,122518,2789],{"class":2307},[2225,122520,637],{"class":2291},[2225,122522,2789],{"class":2307},[2225,122524,122525],{"class":2763},"\u002F* … *\u002F",[2225,122527,7620],{"class":2307},[2225,122529,19008],{"class":2235},[2225,122531,122532],{"class":2227,"line":2304},[2225,122533,2285],{"emptyLinePlaceholder":2284},[2225,122535,122536],{"class":2227,"line":2356},[2225,122537,122538],{"class":2763},"\u002F\u002F ✓ one entry, both descriptors\n",[2225,122540,122541,122543,122545],{"class":2227,"line":2830},[2225,122542,5885],{"class":6342},[2225,122544,2311],{"class":2235},[2225,122546,2301],{"class":2235},[2225,122548,122549,122552,122554,122556],{"class":2227,"line":2861},[2225,122550,122551],{"class":6342},"  receiptAmount",[2225,122553,2311],{"class":2235},[2225,122555,122163],{"class":2291},[2225,122557,11683],{"class":2307},[2225,122559,122560,122563,122565,122567,122569,122571,122573,122575,122577,122579,122581],{"class":2227,"line":4246},[2225,122561,122562],{"class":2291},"    money",[2225,122564,2789],{"class":2307},[2225,122566,2792],{"class":2235},[2225,122568,122489],{"class":2307},[2225,122570,2311],{"class":2235},[2225,122572,2249],{"class":2235},[2225,122574,122496],{"class":2252},[2225,122576,2805],{"class":2235},[2225,122578,2243],{"class":2235},[2225,122580,1381],{"class":2307},[2225,122582,1707],{"class":2235},[2225,122584,122585,122588,122590,122592,122594,122596,122598,122600,122602,122604,122606,122608,122610,122612,122614,122616,122618,122620,122623,122625,122627,122629,122631,122633,122635],{"class":2227,"line":4253},[2225,122586,122587],{"class":2291},"    computed",[2225,122589,2789],{"class":2307},[2225,122591,2789],{"class":2235},[2225,122593,13217],{"class":7052},[2225,122595,1381],{"class":2235},[2225,122597,8116],{"class":2774},[2225,122599,953],{"class":2307},[2225,122601,13217],{"class":2239},[2225,122603,1520],{"class":2235},[2225,122605,27895],{"class":2239},[2225,122607,9025],{"class":2235},[2225,122609,2249],{"class":2235},[2225,122611,13865],{"class":2252},[2225,122613,2805],{"class":2235},[2225,122615,40953],{"class":2235},[2225,122617,32537],{"class":2239},[2225,122619,1520],{"class":2235},[2225,122621,122622],{"class":2239},"paidTotal",[2225,122624,40959],{"class":2235},[2225,122626,32537],{"class":2239},[2225,122628,1520],{"class":2235},[2225,122630,6617],{"class":2239},[2225,122632,1381],{"class":2307},[2225,122634,2298],{"class":2235},[2225,122636,2301],{"class":2235},[2225,122638,122639,122642,122644,122646,122648,122650],{"class":2227,"line":4259},[2225,122640,122641],{"class":2307},"      mode",[2225,122643,2311],{"class":2235},[2225,122645,2249],{"class":2235},[2225,122647,122313],{"class":2252},[2225,122649,2805],{"class":2235},[2225,122651,1707],{"class":2235},[2225,122653,122654,122657,122659,122661,122663,122665,122667,122669,122671,122673,122675,122677,122679,122681,122683,122685],{"class":2227,"line":6548},[2225,122655,122656],{"class":2307},"      deps",[2225,122658,2311],{"class":2235},[2225,122660,8667],{"class":2307},[2225,122662,2805],{"class":2235},[2225,122664,27895],{"class":2252},[2225,122666,2805],{"class":2235},[2225,122668,2298],{"class":2235},[2225,122670,2249],{"class":2235},[2225,122672,6617],{"class":2252},[2225,122674,2805],{"class":2235},[2225,122676,2298],{"class":2235},[2225,122678,2249],{"class":2235},[2225,122680,122622],{"class":2252},[2225,122682,2805],{"class":2235},[2225,122684,8803],{"class":2307},[2225,122686,1707],{"class":2235},[2225,122688,122689,122691,122693],{"class":2227,"line":6576},[2225,122690,80146],{"class":2235},[2225,122692,1381],{"class":2307},[2225,122694,1707],{"class":2235},[2225,122696,122697,122700],{"class":2227,"line":6582},[2225,122698,122699],{"class":2307},"  )",[2225,122701,1707],{"class":2235},[2225,122703,122704],{"class":2227,"line":6587},[2225,122705,7351],{"class":2235},[2225,122707,122708],{"class":2227,"line":7174},[2225,122709,122710],{"class":2763},"\u002F\u002F receiptAmount '42.50' · receiptAmountFormatted 'THB 42.50' · receiptAmountNumber 42.5\n",[937,122712,122713,122714,122716],{},"Argument order doesn't matter — ",[941,122715,3040],{}," groups by descriptor brand, not position.",[937,122718,122719,122720,122723,122724,122726,122727,122729,122730,122732,122733,122736],{},"Stacking ",[941,122721,122722],{},"money(...)"," onto a computed field money-formats its result in ",[950,122725,31219],{}," modes. In\n",[941,122728,120881],{}," mode (default) the computed value merges into the record before money's\nencode\u002Fdecode\u002Fpresent hooks run, so money covers it exactly like a plain money field. In\n",[941,122731,120887],{}," mode the value is never stored for money to decode, so it is money-formatted at read\ntime instead: the fresh function output is quantized to the field's scale and presented as a\ndecimal string, with a ",[941,122734,122735],{},"\u003Cfield>Formatted"," companion alongside it.",[2217,122738,122740],{"className":2219,"code":122739,"language":2221,"meta":1037,"style":1037},"const priced = vault.collection\u003C{ id: string; base: number; doubledPrice?: number }>('priced', {\n  viaFields: {\n    doubledPrice: via(\n      computed((r) => (r.base as number) * 2, { deps: ['base'], mode: 'virtual' }),\n      money({ currency: 'EUR', scale: 2 }),\n    ),\n  },\n})\n\nawait priced.put('a', { id: 'a', base: 10.5 })\n;(await priced.get('a'))?.doubledPrice          \u002F\u002F → '21.00' — quantized decimal string\n;(await priced.get('a'))?.doubledPriceFormatted \u002F\u002F → defined — locale-dressed companion\n",[941,122741,122742,122798,122806,122817,122885,122920,122927,122931,122937,122941,122987,123019],{"__ignoreMap":1037},[2225,122743,122744,122746,122749,122751,122753,122755,122757,122759,122761,122763,122765,122767,122770,122772,122774,122776,122779,122781,122783,122785,122787,122789,122792,122794,122796],{"class":2227,"line":2228},[2225,122745,2775],{"class":2774},[2225,122747,122748],{"class":2239}," priced ",[2225,122750,2781],{"class":2235},[2225,122752,6466],{"class":2239},[2225,122754,1520],{"class":2235},[2225,122756,6471],{"class":2291},[2225,122758,17851],{"class":2235},[2225,122760,7070],{"class":2307},[2225,122762,2311],{"class":2235},[2225,122764,6366],{"class":6342},[2225,122766,3769],{"class":2235},[2225,122768,122769],{"class":2307}," base",[2225,122771,2311],{"class":2235},[2225,122773,6356],{"class":6342},[2225,122775,3769],{"class":2235},[2225,122777,122778],{"class":2307}," doubledPrice",[2225,122780,7011],{"class":2235},[2225,122782,6356],{"class":6342},[2225,122784,85293],{"class":2235},[2225,122786,2789],{"class":2239},[2225,122788,2805],{"class":2235},[2225,122790,122791],{"class":2252},"priced",[2225,122793,2805],{"class":2235},[2225,122795,2298],{"class":2235},[2225,122797,2301],{"class":2235},[2225,122799,122800,122802,122804],{"class":2227,"line":1041},[2225,122801,122240],{"class":2307},[2225,122803,2311],{"class":2235},[2225,122805,2301],{"class":2235},[2225,122807,122808,122811,122813,122815],{"class":2227,"line":1038},[2225,122809,122810],{"class":2307},"    doubledPrice",[2225,122812,2311],{"class":2235},[2225,122814,122163],{"class":2291},[2225,122816,11683],{"class":2239},[2225,122818,122819,122822,122824,122826,122828,122830,122832,122834,122836,122839,122841,122843,122845,122847,122849,122851,122853,122855,122857,122859,122861,122863,122865,122867,122869,122871,122873,122875,122877,122879,122881,122883],{"class":2227,"line":2288},[2225,122820,122821],{"class":2291},"      computed",[2225,122823,2789],{"class":2239},[2225,122825,2789],{"class":2235},[2225,122827,13217],{"class":7052},[2225,122829,1381],{"class":2235},[2225,122831,8116],{"class":2774},[2225,122833,121331],{"class":2239},[2225,122835,1520],{"class":2235},[2225,122837,122838],{"class":2239},"base ",[2225,122840,4899],{"class":2231},[2225,122842,6356],{"class":6342},[2225,122844,8495],{"class":2239},[2225,122846,121345],{"class":2235},[2225,122848,18961],{"class":6527},[2225,122850,2298],{"class":2235},[2225,122852,2236],{"class":2235},[2225,122854,122290],{"class":2307},[2225,122856,2311],{"class":2235},[2225,122858,8667],{"class":2239},[2225,122860,2805],{"class":2235},[2225,122862,57272],{"class":2252},[2225,122864,2805],{"class":2235},[2225,122866,8803],{"class":2239},[2225,122868,2298],{"class":2235},[2225,122870,32303],{"class":2307},[2225,122872,2311],{"class":2235},[2225,122874,2249],{"class":2235},[2225,122876,122313],{"class":2252},[2225,122878,2805],{"class":2235},[2225,122880,2243],{"class":2235},[2225,122882,1381],{"class":2239},[2225,122884,1707],{"class":2235},[2225,122886,122887,122890,122892,122894,122896,122898,122900,122903,122905,122907,122910,122912,122914,122916,122918],{"class":2227,"line":2304},[2225,122888,122889],{"class":2291},"      money",[2225,122891,2789],{"class":2239},[2225,122893,2792],{"class":2235},[2225,122895,122489],{"class":2307},[2225,122897,2311],{"class":2235},[2225,122899,2249],{"class":2235},[2225,122901,122902],{"class":2252},"EUR",[2225,122904,2805],{"class":2235},[2225,122906,2298],{"class":2235},[2225,122908,122909],{"class":2307}," scale",[2225,122911,2311],{"class":2235},[2225,122913,18961],{"class":6527},[2225,122915,2243],{"class":2235},[2225,122917,1381],{"class":2239},[2225,122919,1707],{"class":2235},[2225,122921,122922,122925],{"class":2227,"line":2356},[2225,122923,122924],{"class":2239},"    )",[2225,122926,1707],{"class":2235},[2225,122928,122929],{"class":2227,"line":2830},[2225,122930,19017],{"class":2235},[2225,122932,122933,122935],{"class":2227,"line":2861},[2225,122934,2359],{"class":2235},[2225,122936,2362],{"class":2239},[2225,122938,122939],{"class":2227,"line":4246},[2225,122940,2285],{"emptyLinePlaceholder":2284},[2225,122942,122943,122945,122948,122950,122952,122954,122956,122958,122960,122962,122964,122966,122968,122970,122972,122974,122976,122978,122980,122983,122985],{"class":2227,"line":4253},[2225,122944,6500],{"class":2231},[2225,122946,122947],{"class":2239}," priced",[2225,122949,1520],{"class":2235},[2225,122951,3161],{"class":2291},[2225,122953,2789],{"class":2239},[2225,122955,2805],{"class":2235},[2225,122957,1007],{"class":2252},[2225,122959,2805],{"class":2235},[2225,122961,2298],{"class":2235},[2225,122963,2236],{"class":2235},[2225,122965,7070],{"class":2307},[2225,122967,2311],{"class":2235},[2225,122969,2249],{"class":2235},[2225,122971,1007],{"class":2252},[2225,122973,2805],{"class":2235},[2225,122975,2298],{"class":2235},[2225,122977,122769],{"class":2307},[2225,122979,2311],{"class":2235},[2225,122981,122982],{"class":6527}," 10.5",[2225,122984,2243],{"class":2235},[2225,122986,2362],{"class":2239},[2225,122988,122989,122991,122993,122995,122997,122999,123001,123003,123005,123007,123009,123011,123013,123016],{"class":2227,"line":4259},[2225,122990,3769],{"class":2235},[2225,122992,2789],{"class":2239},[2225,122994,6500],{"class":2231},[2225,122996,122947],{"class":2239},[2225,122998,1520],{"class":2235},[2225,123000,3158],{"class":2291},[2225,123002,2789],{"class":2239},[2225,123004,2805],{"class":2235},[2225,123006,1007],{"class":2252},[2225,123008,2805],{"class":2235},[2225,123010,8454],{"class":2239},[2225,123012,8457],{"class":2235},[2225,123014,123015],{"class":2239},"doubledPrice          ",[2225,123017,123018],{"class":2763},"\u002F\u002F → '21.00' — quantized decimal string\n",[2225,123020,123021,123023,123025,123027,123029,123031,123033,123035,123037,123039,123041,123043,123045,123048],{"class":2227,"line":6548},[2225,123022,3769],{"class":2235},[2225,123024,2789],{"class":2239},[2225,123026,6500],{"class":2231},[2225,123028,122947],{"class":2239},[2225,123030,1520],{"class":2235},[2225,123032,3158],{"class":2291},[2225,123034,2789],{"class":2239},[2225,123036,2805],{"class":2235},[2225,123038,1007],{"class":2252},[2225,123040,2805],{"class":2235},[2225,123042,8454],{"class":2239},[2225,123044,8457],{"class":2235},[2225,123046,123047],{"class":2239},"doubledPriceFormatted ",[2225,123049,123050],{"class":2763},"\u002F\u002F → defined — locale-dressed companion\n",[7646,123052,123054,123074],{"color":7648,"icon":123053},"i-lucide-bug",[937,123055,123056,1324,123062,123065,123066,123069,123070,123073],{},[950,123057,123058,123059,123061],{},"Asserting on ",[941,123060,122735],{}," in a test?",[941,123063,123064],{},"Intl.NumberFormat"," separates the currency code from\nthe number with a ",[950,123067,123068],{},"non-breaking space (U+00A0)",", not a regular space. Compare against a literal\nyou typed and you get the maddening ",[941,123071,123072],{},"expected 'THB 42.50' to be 'THB 42.50'"," — two strings that\nlook identical and are not. Normalise first:",[2217,123075,123077],{"className":2219,"code":123076,"language":2221,"meta":1037,"style":1037},"const spaces = (s: unknown) => String(s).replace(\u002F\\s\u002Fg, ' ')\nexpect(spaces(rec.receiptAmountFormatted)).toBe('THB 42.50')\n",[941,123078,123079,123128],{"__ignoreMap":1037},[2225,123080,123081,123083,123086,123088,123090,123093,123095,123097,123099,123101,123103,123106,123108,123110,123112,123114,123116,123118,123120,123122,123124,123126],{"class":2227,"line":2228},[2225,123082,2775],{"class":2774},[2225,123084,123085],{"class":2239}," spaces ",[2225,123087,2781],{"class":2235},[2225,123089,953],{"class":2235},[2225,123091,123092],{"class":7052},"s",[2225,123094,2311],{"class":2235},[2225,123096,40717],{"class":6342},[2225,123098,1381],{"class":2235},[2225,123100,8116],{"class":2774},[2225,123102,81440],{"class":2291},[2225,123104,123105],{"class":2239},"(s)",[2225,123107,1520],{"class":2235},[2225,123109,71260],{"class":2291},[2225,123111,2789],{"class":2239},[2225,123113,1625],{"class":2235},[2225,123115,79551],{"class":2252},[2225,123117,1625],{"class":2235},[2225,123119,71274],{"class":6527},[2225,123121,2298],{"class":2235},[2225,123123,2249],{"class":2235},[2225,123125,2249],{"class":2235},[2225,123127,2362],{"class":2239},[2225,123129,123130,123132,123134,123137,123140,123142,123145,123147,123150,123152,123154,123157,123159],{"class":2227,"line":1041},[2225,123131,86221],{"class":2291},[2225,123133,2789],{"class":2239},[2225,123135,123136],{"class":2291},"spaces",[2225,123138,123139],{"class":2239},"(rec",[2225,123141,1520],{"class":2235},[2225,123143,123144],{"class":2239},"receiptAmountFormatted))",[2225,123146,1520],{"class":2235},[2225,123148,123149],{"class":2291},"toBe",[2225,123151,2789],{"class":2239},[2225,123153,2805],{"class":2235},[2225,123155,123156],{"class":2252},"THB 42.50",[2225,123158,2805],{"class":2235},[2225,123160,2362],{"class":2239},[937,123162,123163,123164,123167,123168,123171,123172,123175,123176,964,123179,35055,123182,123184,123185,33694,123187,123190,123191,957],{},"Two constraints on the virtual case: it is ",[950,123165,123166],{},"fixed-currency only"," (a virtual output has no\n",[941,123169,123170],{},"{ amount, currency }"," shape for money's multi-currency mode to parse), and it only quantizes a\nvalue that parses cleanly at the declared scale. A function output with excess precision and no\n",[941,123173,123174],{},"rounding"," declared is left ",[950,123177,123178],{},"raw and untouched",[941,123180,123181],{},"21.005",[941,123183,123181],{},", no error — while a\ndeclared ",[941,123186,123174],{},[941,123188,123189],{},"'half-up'",") rounds it into range (",[941,123192,123193],{},"'21.01'",[1591,123195,123197],{"id":123196},"taint-a-computed-field-inherits-its-sources-security-posture","Taint — a computed field inherits its sources' security posture",[937,123199,123200,123201,123203,123204,123206,123207,123210,123211,123213],{},"A computed field's effective security posture is folded from its ",[941,123202,117230],{},"' postures by the\n",[1007,123205,896],{"href":897}," dependency graph. A ",[950,123208,123209],{},"materialized"," field computed from a\n",[1007,123212,633],{"href":634}," source is sealed at rest, refused in the query DSL, and\nredacted on export — it reads back as a sealed handle, not plaintext:",[2217,123215,123217],{"className":2219,"code":123216,"language":2221,"meta":1037,"style":1037},"const people = vault.collection('people', {\n  classifiedFields: { ssn: classified.email() \u002F* any preset *\u002F },\n  computed: { ssnCopy: { fn: (r) => r.ssn, deps: ['ssn'] } },   \u002F\u002F materialized (default)\n})\nawait people.put('r1', { id: 'r1', ssn: '123-45-6789' })\nconst rec = await people.get('r1')\n\u002F\u002F rec.ssnCopy is a SealedHandle — JSON.stringify(rec.ssnCopy) → '\"[sealed]\"', never the value\n",[941,123218,123219,123247,123273,123331,123337,123387,123414],{"__ignoreMap":1037},[2225,123220,123221,123223,123226,123228,123230,123232,123234,123236,123238,123241,123243,123245],{"class":2227,"line":2228},[2225,123222,2775],{"class":2774},[2225,123224,123225],{"class":2239}," people ",[2225,123227,2781],{"class":2235},[2225,123229,6466],{"class":2239},[2225,123231,1520],{"class":2235},[2225,123233,6471],{"class":2291},[2225,123235,2789],{"class":2239},[2225,123237,2805],{"class":2235},[2225,123239,123240],{"class":2252},"people",[2225,123242,2805],{"class":2235},[2225,123244,2298],{"class":2235},[2225,123246,2301],{"class":2235},[2225,123248,123249,123251,123253,123255,123258,123260,123262,123264,123266,123268,123271],{"class":2227,"line":1041},[2225,123250,117526],{"class":2307},[2225,123252,2311],{"class":2235},[2225,123254,2236],{"class":2235},[2225,123256,123257],{"class":2307}," ssn",[2225,123259,2311],{"class":2235},[2225,123261,117343],{"class":2239},[2225,123263,1520],{"class":2235},[2225,123265,81022],{"class":2291},[2225,123267,19132],{"class":2239},[2225,123269,123270],{"class":2763},"\u002F* any preset *\u002F",[2225,123272,2353],{"class":2235},[2225,123274,123275,123277,123279,123281,123284,123286,123288,123291,123293,123295,123297,123299,123301,123303,123305,123308,123310,123312,123314,123316,123318,123320,123322,123324,123326,123328],{"class":2227,"line":1038},[2225,123276,121310],{"class":2307},[2225,123278,2311],{"class":2235},[2225,123280,2236],{"class":2235},[2225,123282,123283],{"class":2307}," ssnCopy",[2225,123285,2311],{"class":2235},[2225,123287,2236],{"class":2235},[2225,123289,123290],{"class":2291}," fn",[2225,123292,2311],{"class":2235},[2225,123294,953],{"class":2235},[2225,123296,13217],{"class":7052},[2225,123298,1381],{"class":2235},[2225,123300,8116],{"class":2774},[2225,123302,32537],{"class":2239},[2225,123304,1520],{"class":2235},[2225,123306,123307],{"class":2239},"ssn",[2225,123309,2298],{"class":2235},[2225,123311,122290],{"class":2307},[2225,123313,2311],{"class":2235},[2225,123315,8667],{"class":2239},[2225,123317,2805],{"class":2235},[2225,123319,123307],{"class":2252},[2225,123321,2805],{"class":2235},[2225,123323,8890],{"class":2239},[2225,123325,2359],{"class":2235},[2225,123327,10965],{"class":2235},[2225,123329,123330],{"class":2763},"   \u002F\u002F materialized (default)\n",[2225,123332,123333,123335],{"class":2227,"line":2288},[2225,123334,2359],{"class":2235},[2225,123336,2362],{"class":2239},[2225,123338,123339,123341,123344,123346,123348,123350,123352,123354,123356,123358,123360,123362,123364,123366,123368,123370,123372,123374,123376,123378,123381,123383,123385],{"class":2227,"line":2304},[2225,123340,6500],{"class":2231},[2225,123342,123343],{"class":2239}," people",[2225,123345,1520],{"class":2235},[2225,123347,3161],{"class":2291},[2225,123349,2789],{"class":2239},[2225,123351,2805],{"class":2235},[2225,123353,75463],{"class":2252},[2225,123355,2805],{"class":2235},[2225,123357,2298],{"class":2235},[2225,123359,2236],{"class":2235},[2225,123361,7070],{"class":2307},[2225,123363,2311],{"class":2235},[2225,123365,2249],{"class":2235},[2225,123367,75463],{"class":2252},[2225,123369,2805],{"class":2235},[2225,123371,2298],{"class":2235},[2225,123373,123257],{"class":2307},[2225,123375,2311],{"class":2235},[2225,123377,2249],{"class":2235},[2225,123379,123380],{"class":2252},"123-45-6789",[2225,123382,2805],{"class":2235},[2225,123384,2243],{"class":2235},[2225,123386,2362],{"class":2239},[2225,123388,123389,123391,123394,123396,123398,123400,123402,123404,123406,123408,123410,123412],{"class":2227,"line":2356},[2225,123390,2775],{"class":2774},[2225,123392,123393],{"class":2239}," rec ",[2225,123395,2781],{"class":2235},[2225,123397,2784],{"class":2231},[2225,123399,123343],{"class":2239},[2225,123401,1520],{"class":2235},[2225,123403,3158],{"class":2291},[2225,123405,2789],{"class":2239},[2225,123407,2805],{"class":2235},[2225,123409,75463],{"class":2252},[2225,123411,2805],{"class":2235},[2225,123413,2362],{"class":2239},[2225,123415,123416],{"class":2227,"line":2830},[2225,123417,123418],{"class":2763},"\u002F\u002F rec.ssnCopy is a SealedHandle — JSON.stringify(rec.ssnCopy) → '\"[sealed]\"', never the value\n",[937,123420,3289,123421,123423,123424,123426,123427,123429,123430,123432,123433,1625,123435,123438],{},[950,123422,122313],{}," field from a classified source has nothing to seal (it is never stored), so it is\nredacted to the literal ",[941,123425,120673],{}," string on ",[950,123428,2119],{}," read instead. This posture inheritance\nis a deliberate behavior change shipped in ",[941,123431,111464],{},": a computed-from-classified\nconfiguration that previously leaked plaintext into an ordinary field now seals or redacts it.\nWhen a redacted virtual field is also money-formatted, its ",[941,123434,122735],{},[941,123436,123437],{},"\u003Cfield>Number","\ncompanions are stripped along with the base field — the redaction never leaves a pre-redaction\nformatted value behind.",[937,123440,123441,123442,1324,123444,123446,123447,123449,123450,123452,123453,123455,123456,123459,123460,123463,123464,123466,123467,123469],{},"Two guard rails at declare time, on collections that also declare classified fields: a computed\nentry with ",[950,123443,11313],{},[941,123445,117230],{},", or with a ",[941,123448,117230],{}," entry naming an unknown field, throws\n",[941,123451,12881],{}," at construction (an opaque function could otherwise copy sealed plaintext with\nno way for the graph to know). Known limit, deliberately documented: the guard checks that\n",[941,123454,117230],{}," names ",[1092,123457,123458],{},"some"," known field, not that it names the field the function actually reads —\n",[941,123461,123462],{},"deps: ['amount']"," on a function that reads ",[941,123465,123307],{}," still passes and still leaks, because the\ngraph folds from ",[941,123468,6617],{},"'s posture. Declare deps honestly.",[1591,123471,123473],{"id":123472},"computed-money-fields-aggregate-exactly","Computed money fields aggregate exactly",[937,123475,123476,123477,123479,123480,997,123483,6255],{},"Declare the computed target in the collection's ",[941,123478,22765],{}," map and the numeric result is quantized to an exact decimal on store — so sums over it never accumulate float error (see ",[1007,123481,760],{"href":123482},".\u002Fmoney",[1007,123484,4449],{"href":123485},".\u002Freduce",[2217,123487,123489],{"className":2219,"code":123488,"language":2221,"meta":1037,"style":1037},"import { withReduce, sum } from '@noy-db\u002Fhub\u002Freduce'\nimport { money } from '@noy-db\u002Fhub'\n\n\u002F\u002F createNoydb({ ..., reduceStrategy: withReduce() })\nconst sales = vault.collection('sales', {\n  schema: z.object({\n    id: z.string(), unitPrice: z.number(), qty: z.number(),\n    total: z.union([z.number(), z.string()]).optional(),\n  }),\n  computed: { total: (r) => (r.unitPrice as number) * (r.qty as number) },\n  moneyFields: { total: money({ currency: 'EUR', scale: 2 }) },\n})\n\nawait sales.put('a', { id: 'a', unitPrice: 0.10, qty: 1 })\nawait sales.put('b', { id: 'b', unitPrice: 0.20, qty: 1 })\n\nconst agg = await sales.query().aggregate({ total: sum('total') }).run()\n\u002F\u002F → { total: \"0.30\" }   — exact decimal string, not 0.30000000000000004\n",[941,123490,123491,123513,123532,123536,123541,123569,123585,123629,123670,123678,123726,123769,123775,123779,123833,123886,123890,123943],{"__ignoreMap":1037},[2225,123492,123493,123495,123497,123499,123501,123503,123505,123507,123509,123511],{"class":2227,"line":2228},[2225,123494,2232],{"class":2231},[2225,123496,2236],{"class":2235},[2225,123498,27293],{"class":2239},[2225,123500,2298],{"class":2235},[2225,123502,27298],{"class":2239},[2225,123504,2243],{"class":2235},[2225,123506,2246],{"class":2231},[2225,123508,2249],{"class":2235},[2225,123510,27257],{"class":2252},[2225,123512,2256],{"class":2235},[2225,123514,123515,123517,123519,123522,123524,123526,123528,123530],{"class":2227,"line":1041},[2225,123516,2232],{"class":2231},[2225,123518,2236],{"class":2235},[2225,123520,123521],{"class":2239}," money",[2225,123523,2243],{"class":2235},[2225,123525,2246],{"class":2231},[2225,123527,2249],{"class":2235},[2225,123529,1086],{"class":2252},[2225,123531,2256],{"class":2235},[2225,123533,123534],{"class":2227,"line":1038},[2225,123535,2285],{"emptyLinePlaceholder":2284},[2225,123537,123538],{"class":2227,"line":2288},[2225,123539,123540],{"class":2763},"\u002F\u002F createNoydb({ ..., reduceStrategy: withReduce() })\n",[2225,123542,123543,123545,123548,123550,123552,123554,123556,123558,123560,123563,123565,123567],{"class":2227,"line":2304},[2225,123544,2775],{"class":2774},[2225,123546,123547],{"class":2239}," sales ",[2225,123549,2781],{"class":2235},[2225,123551,6466],{"class":2239},[2225,123553,1520],{"class":2235},[2225,123555,6471],{"class":2291},[2225,123557,2789],{"class":2239},[2225,123559,2805],{"class":2235},[2225,123561,123562],{"class":2252},"sales",[2225,123564,2805],{"class":2235},[2225,123566,2298],{"class":2235},[2225,123568,2301],{"class":2235},[2225,123570,123571,123573,123575,123577,123579,123581,123583],{"class":2227,"line":2356},[2225,123572,28618],{"class":2307},[2225,123574,2311],{"class":2235},[2225,123576,28314],{"class":2239},[2225,123578,1520],{"class":2235},[2225,123580,25505],{"class":2291},[2225,123582,2789],{"class":2239},[2225,123584,4200],{"class":2235},[2225,123586,123587,123589,123591,123593,123595,123597,123599,123601,123603,123605,123607,123609,123611,123613,123615,123617,123619,123621,123623,123625,123627],{"class":2227,"line":2830},[2225,123588,60558],{"class":2307},[2225,123590,2311],{"class":2235},[2225,123592,28314],{"class":2239},[2225,123594,1520],{"class":2235},[2225,123596,7259],{"class":2291},[2225,123598,4228],{"class":2239},[2225,123600,2298],{"class":2235},[2225,123602,121514],{"class":2307},[2225,123604,2311],{"class":2235},[2225,123606,28314],{"class":2239},[2225,123608,1520],{"class":2235},[2225,123610,20636],{"class":2291},[2225,123612,4228],{"class":2239},[2225,123614,2298],{"class":2235},[2225,123616,121523],{"class":2307},[2225,123618,2311],{"class":2235},[2225,123620,28314],{"class":2239},[2225,123622,1520],{"class":2235},[2225,123624,20636],{"class":2291},[2225,123626,4228],{"class":2239},[2225,123628,1707],{"class":2235},[2225,123630,123631,123633,123635,123637,123639,123642,123645,123647,123649,123651,123653,123655,123657,123659,123662,123664,123666,123668],{"class":2227,"line":2861},[2225,123632,64102],{"class":2307},[2225,123634,2311],{"class":2235},[2225,123636,28314],{"class":2239},[2225,123638,1520],{"class":2235},[2225,123640,123641],{"class":2291},"union",[2225,123643,123644],{"class":2239},"([z",[2225,123646,1520],{"class":2235},[2225,123648,20636],{"class":2291},[2225,123650,4228],{"class":2239},[2225,123652,2298],{"class":2235},[2225,123654,28314],{"class":2239},[2225,123656,1520],{"class":2235},[2225,123658,7259],{"class":2291},[2225,123660,123661],{"class":2239},"()])",[2225,123663,1520],{"class":2235},[2225,123665,2084],{"class":2291},[2225,123667,4228],{"class":2239},[2225,123669,1707],{"class":2235},[2225,123671,123672,123674,123676],{"class":2227,"line":4246},[2225,123673,19143],{"class":2235},[2225,123675,1381],{"class":2239},[2225,123677,1707],{"class":2235},[2225,123679,123680,123682,123684,123686,123688,123690,123692,123694,123696,123698,123700,123702,123704,123706,123708,123710,123712,123714,123716,123718,123720,123722,123724],{"class":2227,"line":4253},[2225,123681,121310],{"class":2307},[2225,123683,2311],{"class":2235},[2225,123685,2236],{"class":2235},[2225,123687,27452],{"class":2291},[2225,123689,2311],{"class":2235},[2225,123691,953],{"class":2235},[2225,123693,13217],{"class":7052},[2225,123695,1381],{"class":2235},[2225,123697,8116],{"class":2774},[2225,123699,121331],{"class":2239},[2225,123701,1520],{"class":2235},[2225,123703,121336],{"class":2239},[2225,123705,4899],{"class":2231},[2225,123707,6356],{"class":6342},[2225,123709,8495],{"class":2239},[2225,123711,121345],{"class":2235},[2225,123713,121331],{"class":2239},[2225,123715,1520],{"class":2235},[2225,123717,121352],{"class":2239},[2225,123719,4899],{"class":2231},[2225,123721,6356],{"class":6342},[2225,123723,8495],{"class":2239},[2225,123725,19008],{"class":2235},[2225,123727,123728,123731,123733,123735,123737,123739,123741,123743,123745,123747,123749,123751,123753,123755,123757,123759,123761,123763,123765,123767],{"class":2227,"line":4259},[2225,123729,123730],{"class":2307},"  moneyFields",[2225,123732,2311],{"class":2235},[2225,123734,2236],{"class":2235},[2225,123736,27452],{"class":2307},[2225,123738,2311],{"class":2235},[2225,123740,123521],{"class":2291},[2225,123742,2789],{"class":2239},[2225,123744,2792],{"class":2235},[2225,123746,122489],{"class":2307},[2225,123748,2311],{"class":2235},[2225,123750,2249],{"class":2235},[2225,123752,122902],{"class":2252},[2225,123754,2805],{"class":2235},[2225,123756,2298],{"class":2235},[2225,123758,122909],{"class":2307},[2225,123760,2311],{"class":2235},[2225,123762,18961],{"class":6527},[2225,123764,2243],{"class":2235},[2225,123766,8495],{"class":2239},[2225,123768,19008],{"class":2235},[2225,123770,123771,123773],{"class":2227,"line":6548},[2225,123772,2359],{"class":2235},[2225,123774,2362],{"class":2239},[2225,123776,123777],{"class":2227,"line":6576},[2225,123778,2285],{"emptyLinePlaceholder":2284},[2225,123780,123781,123783,123786,123788,123790,123792,123794,123796,123798,123800,123802,123804,123806,123808,123810,123812,123814,123816,123818,123821,123823,123825,123827,123829,123831],{"class":2227,"line":6582},[2225,123782,6500],{"class":2231},[2225,123784,123785],{"class":2239}," sales",[2225,123787,1520],{"class":2235},[2225,123789,3161],{"class":2291},[2225,123791,2789],{"class":2239},[2225,123793,2805],{"class":2235},[2225,123795,1007],{"class":2252},[2225,123797,2805],{"class":2235},[2225,123799,2298],{"class":2235},[2225,123801,2236],{"class":2235},[2225,123803,7070],{"class":2307},[2225,123805,2311],{"class":2235},[2225,123807,2249],{"class":2235},[2225,123809,1007],{"class":2252},[2225,123811,2805],{"class":2235},[2225,123813,2298],{"class":2235},[2225,123815,121514],{"class":2307},[2225,123817,2311],{"class":2235},[2225,123819,123820],{"class":6527}," 0.10",[2225,123822,2298],{"class":2235},[2225,123824,121523],{"class":2307},[2225,123826,2311],{"class":2235},[2225,123828,9377],{"class":6527},[2225,123830,2243],{"class":2235},[2225,123832,2362],{"class":2239},[2225,123834,123835,123837,123839,123841,123843,123845,123847,123849,123851,123853,123855,123857,123859,123861,123863,123865,123867,123869,123871,123874,123876,123878,123880,123882,123884],{"class":2227,"line":6587},[2225,123836,6500],{"class":2231},[2225,123838,123785],{"class":2239},[2225,123840,1520],{"class":2235},[2225,123842,3161],{"class":2291},[2225,123844,2789],{"class":2239},[2225,123846,2805],{"class":2235},[2225,123848,80529],{"class":2252},[2225,123850,2805],{"class":2235},[2225,123852,2298],{"class":2235},[2225,123854,2236],{"class":2235},[2225,123856,7070],{"class":2307},[2225,123858,2311],{"class":2235},[2225,123860,2249],{"class":2235},[2225,123862,80529],{"class":2252},[2225,123864,2805],{"class":2235},[2225,123866,2298],{"class":2235},[2225,123868,121514],{"class":2307},[2225,123870,2311],{"class":2235},[2225,123872,123873],{"class":6527}," 0.20",[2225,123875,2298],{"class":2235},[2225,123877,121523],{"class":2307},[2225,123879,2311],{"class":2235},[2225,123881,9377],{"class":6527},[2225,123883,2243],{"class":2235},[2225,123885,2362],{"class":2239},[2225,123887,123888],{"class":2227,"line":7174},[2225,123889,2285],{"emptyLinePlaceholder":2284},[2225,123891,123892,123894,123897,123899,123901,123903,123905,123907,123909,123911,123913,123915,123917,123919,123921,123923,123925,123927,123929,123931,123933,123935,123937,123939,123941],{"class":2227,"line":7180},[2225,123893,2775],{"class":2774},[2225,123895,123896],{"class":2239}," agg ",[2225,123898,2781],{"class":2235},[2225,123900,2784],{"class":2231},[2225,123902,123785],{"class":2239},[2225,123904,1520],{"class":2235},[2225,123906,5286],{"class":2291},[2225,123908,4228],{"class":2239},[2225,123910,1520],{"class":2235},[2225,123912,4449],{"class":2291},[2225,123914,2789],{"class":2239},[2225,123916,2792],{"class":2235},[2225,123918,27452],{"class":2307},[2225,123920,2311],{"class":2235},[2225,123922,27298],{"class":2291},[2225,123924,2789],{"class":2239},[2225,123926,2805],{"class":2235},[2225,123928,64200],{"class":2252},[2225,123930,2805],{"class":2235},[2225,123932,8495],{"class":2239},[2225,123934,2359],{"class":2235},[2225,123936,1381],{"class":2239},[2225,123938,1520],{"class":2235},[2225,123940,61526],{"class":2291},[2225,123942,8146],{"class":2239},[2225,123944,123945],{"class":2227,"line":7220},[2225,123946,123947],{"class":2763},"\u002F\u002F → { total: \"0.30\" }   — exact decimal string, not 0.30000000000000004\n",[1022,123949,101012],{"id":41259},[937,123951,123952,123953,101421],{},"All exports below come from the ",[941,123954,1086],{},[1591,123956,123958],{"id":123957},"computed-1",[941,123959,637],{},[2217,123961,123963],{"className":2219,"code":123962,"language":2221,"meta":1037,"style":1037},"collection\u003CT>(name, {\n  schema,\n  computed?: ComputedFields\u003CT>,   \u002F\u002F Record\u003Cstring, fn | { fn, deps?, mode? }>\n})\n",[941,123964,123965,123982,123988,124007],{"__ignoreMap":1037},[2225,123966,123967,123969,123971,123973,123975,123978,123980],{"class":2227,"line":2228},[2225,123968,6471],{"class":2291},[2225,123970,6474],{"class":2235},[2225,123972,11968],{"class":6342},[2225,123974,6480],{"class":2235},[2225,123976,123977],{"class":2239},"(name",[2225,123979,2298],{"class":2235},[2225,123981,2301],{"class":2235},[2225,123983,123984,123986],{"class":2227,"line":1041},[2225,123985,28618],{"class":2239},[2225,123987,1707],{"class":2235},[2225,123989,123990,123993,123995,123998,124000,124002,124004],{"class":2227,"line":1038},[2225,123991,123992],{"class":2239},"  computed?",[2225,123994,2311],{"class":2235},[2225,123996,123997],{"class":2239}," ComputedFields",[2225,123999,6474],{"class":2235},[2225,124001,11968],{"class":2239},[2225,124003,17840],{"class":2235},[2225,124005,124006],{"class":2763},"   \u002F\u002F Record\u003Cstring, fn | { fn, deps?, mode? }>\n",[2225,124008,124009,124011],{"class":2227,"line":2288},[2225,124010,2359],{"class":2235},[2225,124012,2362],{"class":2239},[1338,124014,124015,124025],{},[1341,124016,124017],{},[1344,124018,124019,124022],{},[1347,124020,124021],{},"Aspect",[1347,124023,124024],{},"Behavior",[1357,124026,124027,124034,124061,124069,124077,124096],{},[1344,124028,124029,124031],{},[1362,124030,14769],{},[1362,124032,124033],{},"Top-level field name the result is stored under",[1344,124035,124036,124038],{},[1362,124037,91926],{},[1362,124039,124040,124041,124043,124044,17244,124047,124049,124050,124052,124053,124056,124057,124060],{},"Pure, ",[950,124042,25812],{}," function ",[941,124045,124046],{},"(record) => unknown",[941,124048,70491],{},", no I\u002FO — or, since ",[941,124051,111464],{},", an object ",[941,124054,124055],{},"{ fn, deps?: string[], mode?: 'materialized' | 'virtual' }"," (a bare function means ",[941,124058,124059],{},"mode: 'materialized'",", no declared deps)",[1344,124062,124063,124066],{},[1362,124064,124065],{},"Order",[1362,124067,124068],{},"Declaration order of the map's keys; each function sees the record with all earlier computed results injected",[1344,124070,124071,124074],{},[1362,124072,124073],{},"Ownership",[1362,124075,124076],{},"Schema-owned: the computed result overwrites any user-supplied value of the same name",[1344,124078,124079,124082],{},[1362,124080,124081],{},"When",[1362,124083,124084,124086,124087,124089,124090,124092,124093,124095],{},[941,124085,120881],{},": on every ",[941,124088,20953],{},", after write gates, ",[950,124091,11357],{}," schema validation. ",[941,124094,120887],{},": on every read, never stored",[1344,124097,124098,124101],{},[1362,124099,124100],{},"Cost",[1362,124102,124103],{},"The evaluation engine lazy-loads on the first write to a computed-declared collection; zero bundle cost otherwise",[1591,124105,124107,124110],{"id":124106},"computedfn-opts-the-via-composable-descriptor",[941,124108,124109],{},"computed(fn, opts?)"," — the via-composable descriptor",[2217,124112,124114],{"className":2219,"code":124113,"language":2221,"meta":1037,"style":1037},"import { via, computed } from '@noy-db\u002Fhub'\n\nfunction computed(fn: (record) => unknown, opts?: { deps?: readonly string[]; mode?: 'materialized' | 'virtual' }): ComputedDescriptor\n",[941,124115,124116,124138,124142],{"__ignoreMap":1037},[2225,124117,124118,124120,124122,124124,124126,124128,124130,124132,124134,124136],{"class":2227,"line":2228},[2225,124119,2232],{"class":2231},[2225,124121,2236],{"class":2235},[2225,124123,122163],{"class":2239},[2225,124125,2298],{"class":2235},[2225,124127,122168],{"class":2239},[2225,124129,2243],{"class":2235},[2225,124131,2246],{"class":2231},[2225,124133,2249],{"class":2235},[2225,124135,1086],{"class":2252},[2225,124137,2256],{"class":2235},[2225,124139,124140],{"class":2227,"line":1041},[2225,124141,2285],{"emptyLinePlaceholder":2284},[2225,124143,124144,124146,124148,124150,124152,124154,124156,124158,124160,124162,124164,124166,124168,124170,124172,124174,124176,124178,124180,124182,124184,124186,124188,124190,124192,124194,124196,124198,124200,124202,124204],{"class":2227,"line":1038},[2225,124145,12641],{"class":2774},[2225,124147,122168],{"class":2291},[2225,124149,2789],{"class":2235},[2225,124151,12070],{"class":2291},[2225,124153,2311],{"class":2235},[2225,124155,953],{"class":2235},[2225,124157,24158],{"class":7052},[2225,124159,1381],{"class":2235},[2225,124161,8116],{"class":2774},[2225,124163,40717],{"class":6342},[2225,124165,2298],{"class":2235},[2225,124167,8232],{"class":7052},[2225,124169,7011],{"class":2235},[2225,124171,2236],{"class":2235},[2225,124173,122290],{"class":2307},[2225,124175,7011],{"class":2235},[2225,124177,112906],{"class":2774},[2225,124179,6366],{"class":6342},[2225,124181,7262],{"class":2239},[2225,124183,3769],{"class":2235},[2225,124185,32303],{"class":2307},[2225,124187,7011],{"class":2235},[2225,124189,2249],{"class":2235},[2225,124191,123209],{"class":2252},[2225,124193,2805],{"class":2235},[2225,124195,7087],{"class":2235},[2225,124197,2249],{"class":2235},[2225,124199,122313],{"class":2252},[2225,124201,2805],{"class":2235},[2225,124203,8111],{"class":2235},[2225,124205,124206],{"class":6342}," ComputedDescriptor\n",[937,124208,124209,124210,124212,124213,124216,124217,124220,124221,124224],{},"The descriptor form for the ",[941,124211,3040],{}," spelling — ",[941,124214,124215],{},"viaFields: { total: via(computed(fn, { deps: ['subtotal'] })) }"," — identical semantics to the sugar map's object entries (materialized mode is\nbyte-for-byte identical to the plain sugar, pinned by test). ",[941,124218,124219],{},"isComputedDescriptor(x)"," is the\nruntime predicate; the ",[941,124222,124223],{},"ComputedDescriptor"," type is exported alongside.",[1591,124226,124228,1261,124231,120416],{"id":124227},"computedfnt-computedfieldst-types",[941,124229,124230],{},"ComputedFn\u003CT>",[941,124232,124233],{},"ComputedFields\u003CT>",[2217,124235,124237],{"className":2219,"code":124236,"language":2221,"meta":1037,"style":1037},"type ComputedFn\u003CT = Record\u003Cstring, unknown>> = (record: T) => unknown\ntype ComputedFields\u003CT = Record\u003Cstring, unknown>> = Record\u003Cstring, ComputedFn\u003CT>>\n",[941,124238,124239,124281],{"__ignoreMap":1037},[2225,124240,124241,124243,124246,124248,124250,124252,124254,124256,124258,124260,124262,124264,124266,124268,124270,124272,124274,124276,124278],{"class":2227,"line":2228},[2225,124242,6339],{"class":2774},[2225,124244,124245],{"class":6342}," ComputedFn",[2225,124247,6474],{"class":2235},[2225,124249,11968],{"class":6342},[2225,124251,6346],{"class":2235},[2225,124253,22527],{"class":6342},[2225,124255,6474],{"class":2235},[2225,124257,7259],{"class":6342},[2225,124259,2298],{"class":2235},[2225,124261,40717],{"class":6342},[2225,124263,28601],{"class":2235},[2225,124265,6346],{"class":2235},[2225,124267,953],{"class":2235},[2225,124269,24158],{"class":7052},[2225,124271,2311],{"class":2235},[2225,124273,24772],{"class":6342},[2225,124275,1381],{"class":2235},[2225,124277,8116],{"class":2774},[2225,124279,124280],{"class":6342}," unknown\n",[2225,124282,124283,124285,124287,124289,124291,124293,124295,124297,124299,124301,124303,124305,124307,124309,124311,124313,124315,124317,124319,124321],{"class":2227,"line":1041},[2225,124284,6339],{"class":2774},[2225,124286,123997],{"class":6342},[2225,124288,6474],{"class":2235},[2225,124290,11968],{"class":6342},[2225,124292,6346],{"class":2235},[2225,124294,22527],{"class":6342},[2225,124296,6474],{"class":2235},[2225,124298,7259],{"class":6342},[2225,124300,2298],{"class":2235},[2225,124302,40717],{"class":6342},[2225,124304,28601],{"class":2235},[2225,124306,6346],{"class":2235},[2225,124308,22527],{"class":6342},[2225,124310,6474],{"class":2235},[2225,124312,7259],{"class":6342},[2225,124314,2298],{"class":2235},[2225,124316,124245],{"class":6342},[2225,124318,6474],{"class":2235},[2225,124320,11968],{"class":6342},[2225,124322,17688],{"class":2235},[937,124324,124325],{},"The shape of one computed function and of the whole map.",[1591,124327,124329],{"id":124328},"evalcomputedfieldsrecord-computed-id",[941,124330,124331],{},"evalComputedFields(record, computed, id)",[2217,124333,124335],{"className":2219,"code":124334,"language":2221,"meta":1037,"style":1037},"function evalComputedFields\u003CT extends Record\u003Cstring, unknown>>(\n  record: T,\n  computed: ComputedFields,\n  id: string,\n): T\n",[941,124336,124337,124364,124375,124385,124395],{"__ignoreMap":1037},[2225,124338,124339,124341,124344,124346,124348,124351,124353,124355,124357,124359,124361],{"class":2227,"line":2228},[2225,124340,12641],{"class":2774},[2225,124342,124343],{"class":2291}," evalComputedFields",[2225,124345,6474],{"class":2235},[2225,124347,11968],{"class":6342},[2225,124349,124350],{"class":2774}," extends",[2225,124352,22527],{"class":6342},[2225,124354,6474],{"class":2235},[2225,124356,7259],{"class":6342},[2225,124358,2298],{"class":2235},[2225,124360,40717],{"class":6342},[2225,124362,124363],{"class":2235},">>(\n",[2225,124365,124366,124369,124371,124373],{"class":2227,"line":1041},[2225,124367,124368],{"class":7052},"  record",[2225,124370,2311],{"class":2235},[2225,124372,24772],{"class":6342},[2225,124374,1707],{"class":2235},[2225,124376,124377,124379,124381,124383],{"class":2227,"line":1038},[2225,124378,121310],{"class":7052},[2225,124380,2311],{"class":2235},[2225,124382,123997],{"class":6342},[2225,124384,1707],{"class":2235},[2225,124386,124387,124389,124391,124393],{"class":2227,"line":2288},[2225,124388,28371],{"class":7052},[2225,124390,2311],{"class":2235},[2225,124392,6366],{"class":6342},[2225,124394,1707],{"class":2235},[2225,124396,124397,124399],{"class":2227,"line":2304},[2225,124398,6255],{"class":2235},[2225,124400,124401],{"class":6342}," T\n",[937,124403,124404,124405,124407],{},"The pure evaluator the write pipeline uses — exported for testing formulas outside a vault. Returns a shallow clone with every computed field injected in declaration order; the input record is not mutated. Throws ",[941,124406,122023],{}," if any function throws.",[2217,124409,124411],{"className":2219,"code":124410,"language":2221,"meta":1037,"style":1037},"import { evalComputedFields } from '@noy-db\u002Fhub'\n\nevalComputedFields(\n  { unitPrice: 10, qty: 2 },\n  { netAmount: (r) => (r.unitPrice as number) * (r.qty as number) },\n  'a',\n)\n\u002F\u002F → { unitPrice: 10, qty: 2, netAmount: 20 }\n",[941,124412,124413,124431,124435,124442,124462,124507,124518,124522],{"__ignoreMap":1037},[2225,124414,124415,124417,124419,124421,124423,124425,124427,124429],{"class":2227,"line":2228},[2225,124416,2232],{"class":2231},[2225,124418,2236],{"class":2235},[2225,124420,124343],{"class":2239},[2225,124422,2243],{"class":2235},[2225,124424,2246],{"class":2231},[2225,124426,2249],{"class":2235},[2225,124428,1086],{"class":2252},[2225,124430,2256],{"class":2235},[2225,124432,124433],{"class":2227,"line":1041},[2225,124434,2285],{"emptyLinePlaceholder":2284},[2225,124436,124437,124440],{"class":2227,"line":1038},[2225,124438,124439],{"class":2291},"evalComputedFields",[2225,124441,11683],{"class":2239},[2225,124443,124444,124446,124448,124450,124452,124454,124456,124458,124460],{"class":2227,"line":2288},[2225,124445,26354],{"class":2235},[2225,124447,121514],{"class":2307},[2225,124449,2311],{"class":2235},[2225,124451,45554],{"class":6527},[2225,124453,2298],{"class":2235},[2225,124455,121523],{"class":2307},[2225,124457,2311],{"class":2235},[2225,124459,18961],{"class":6527},[2225,124461,2353],{"class":2235},[2225,124463,124464,124466,124469,124471,124473,124475,124477,124479,124481,124483,124485,124487,124489,124491,124493,124495,124497,124499,124501,124503,124505],{"class":2227,"line":2304},[2225,124465,26354],{"class":2235},[2225,124467,124468],{"class":2291}," netAmount",[2225,124470,2311],{"class":2235},[2225,124472,953],{"class":2235},[2225,124474,13217],{"class":7052},[2225,124476,1381],{"class":2235},[2225,124478,8116],{"class":2774},[2225,124480,121331],{"class":2239},[2225,124482,1520],{"class":2235},[2225,124484,121336],{"class":2239},[2225,124486,4899],{"class":2231},[2225,124488,6356],{"class":6342},[2225,124490,8495],{"class":2239},[2225,124492,121345],{"class":2235},[2225,124494,121331],{"class":2239},[2225,124496,1520],{"class":2235},[2225,124498,121352],{"class":2239},[2225,124500,4899],{"class":2231},[2225,124502,6356],{"class":6342},[2225,124504,8495],{"class":2239},[2225,124506,19008],{"class":2235},[2225,124508,124509,124512,124514,124516],{"class":2227,"line":2356},[2225,124510,124511],{"class":2235},"  '",[2225,124513,1007],{"class":2252},[2225,124515,2805],{"class":2235},[2225,124517,1707],{"class":2235},[2225,124519,124520],{"class":2227,"line":2830},[2225,124521,2362],{"class":2239},[2225,124523,124524],{"class":2227,"line":2861},[2225,124525,124526],{"class":2763},"\u002F\u002F → { unitPrice: 10, qty: 2, netAmount: 20 }\n",[1591,124528,124530],{"id":124529},"computedfielderror",[941,124531,122023],{},[2217,124533,124535],{"className":2219,"code":124534,"language":2221,"meta":1037,"style":1037},"class ComputedFieldError extends NoydbError {\n  readonly field: string   \u002F\u002F the computed field whose function threw\n  readonly id: string      \u002F\u002F the record being written\n  readonly cause: unknown  \u002F\u002F the original thrown value\n}\n",[941,124536,124537,124551,124565,124578,124592],{"__ignoreMap":1037},[2225,124538,124539,124541,124544,124546,124549],{"class":2227,"line":2228},[2225,124540,7670],{"class":2774},[2225,124542,124543],{"class":6342}," ComputedFieldError",[2225,124545,124350],{"class":2774},[2225,124547,124548],{"class":6342}," NoydbError",[2225,124550,2301],{"class":2235},[2225,124552,124553,124555,124558,124560,124562],{"class":2227,"line":1041},[2225,124554,53580],{"class":2774},[2225,124556,124557],{"class":2307}," field",[2225,124559,2311],{"class":2235},[2225,124561,6366],{"class":6342},[2225,124563,124564],{"class":2763},"   \u002F\u002F the computed field whose function threw\n",[2225,124566,124567,124569,124571,124573,124575],{"class":2227,"line":1038},[2225,124568,53580],{"class":2774},[2225,124570,7070],{"class":2307},[2225,124572,2311],{"class":2235},[2225,124574,6366],{"class":6342},[2225,124576,124577],{"class":2763},"      \u002F\u002F the record being written\n",[2225,124579,124580,124582,124585,124587,124589],{"class":2227,"line":2288},[2225,124581,53580],{"class":2774},[2225,124583,124584],{"class":2307}," cause",[2225,124586,2311],{"class":2235},[2225,124588,40717],{"class":6342},[2225,124590,124591],{"class":2763},"  \u002F\u002F the original thrown value\n",[2225,124593,124594],{"class":2227,"line":2304},[2225,124595,7351],{"class":2235},[937,124597,124598,124599,124602,124603,124605],{},"Error code ",[941,124600,124601],{},"COMPUTED_FIELD",". Raised when a computed function throws during a write; the ",[941,124604,20953],{}," rejects and nothing is stored.",[1022,124607,68210],{"id":68209},[1111,124609,124610,124626,124638,124658,124669,124683,124692,124698,124710,124733,124751],{},[1114,124611,124612,124615,124616,124618,124619,997,124622,1520],{},[950,124613,124614],{},"Synchronous, single-record only."," A computed function gets exactly one record and must return immediately — no ",[941,124617,6500],{},", no reading other records or collections. Cross-record and reactive derivation belong to ",[1007,124620,665],{"href":124621},".\u002Fderivations",[1007,124623,124625],{"href":124624},".\u002Fmaterialized-views","materialized views",[1114,124627,124628,124634,124635,124637],{},[950,124629,124630,124631,124633],{},"Declare ",[941,124632,117230],{}," for what the function actually reads."," The taint guard validates that each ",[941,124636,117230],{}," entry names a known field, but cannot verify the function body — a wrong-but-known dep still passes construction and folds the wrong posture. On classified-declaring collections, depsless or unknown-dep entries are refused outright.",[1114,124639,124640,124646,124647,124650,124651,124654,124655,124657],{},[950,124641,124642,124643,124645],{},"Deps validate against the same ",[941,124644,2138],{}," call."," Declaring ",[941,124648,124649],{},"classifiedFields: { ssn }"," in one call and a computed field with ",[941,124652,124653],{},"deps: ['ssn']"," in a later, separate call over-refuses with ",[941,124656,12881],{}," (fail-safe) — declare the classified field and its dependent computed field together in one call.",[1114,124659,124660,1324,124663,124665,124666,124668],{},[950,124661,124662],{},"Virtual mode is construction-only.",[941,124664,122139],{}," on a reconcile call for an existing collection throws ",[941,124667,12881],{},"; there is no late-attach door for a never-stored field.",[1114,124670,124671,124674,124675,124678,124679,124682],{},[950,124672,124673],{},"Top-level field names, not nested paths."," The map key becomes a top-level property on the record; a key like ",[941,124676,124677],{},"'a.b'"," creates a literal property named ",[941,124680,124681],{},"a.b",", not a nested one.",[1114,124684,124685,124688,124689,124691],{},[950,124686,124687],{},"Materialized values are not retroactive."," Values are computed when a record is written. Changing a formula does not rewrite existing records; they keep the values computed at their last write until they are written again (",[941,124690,122139],{}," sidesteps this — it always evaluates the current formula on read).",[1114,124693,124694,124697],{},[950,124695,124696],{},"Callers cannot override."," A user-supplied value for a computed field is silently replaced, never merged or rejected. If a caller must be able to set the field sometimes, it should not be computed.",[1114,124699,124700,124703,124704,124706,124707,124709],{},[950,124701,124702],{},"Declaration order is load-bearing."," A function reading a computed field declared ",[1092,124705,10761],{}," it sees the raw (possibly ",[941,124708,18316],{},") input value. Order dependent fields accordingly.",[1114,124711,124712,14636,124715,124717,124718,4688,124721,124724,124725,1625,124727,124729,124730,124732],{},[950,124713,124714],{},"A virtual function can't read another field's presentation.",[941,124716,122139],{}," function runs before label\u002Fformat dressing from features like ",[1007,124719,701],{"href":124720},".\u002Fi18n",[1007,124722,731],{"href":124723},".\u002Flookup"," is applied, so it can read sibling fields' stored values but not their derived ",[941,124726,24050],{},[941,124728,122735],{}," presentation. A same-field money amount is the exception — money quantizes ahead of computed, so it ",[1092,124731,3082],{}," visible.",[1114,124734,124735,1324,124738,124743,124744,997,124747,124750],{},[950,124736,124737],{},"Not user-editable in introspection.",[1007,124739,124741],{"href":124740},".\u002Fintrospection",[941,124742,1380],{}," reports computed fields with ",[941,124745,124746],{},"computed: true",[941,124748,124749],{},"editable: false",", so schema-driven UIs render them read-only.",[1114,124752,124753,124759,124760,124762],{},[950,124754,124755,124758],{},[1007,124756,633],{"href":124757},".\u002Fclassified"," rider companions share the map."," Field classifications that derive companion fields merge into the same computed map and run before your functions (your formulas may read them); declaring a ",[941,124761,637],{}," key that collides with a rider companion is a configuration error at collection setup.",[1022,124764,2621],{"id":2620},[1111,124766,124767,124772,124781,124786,124794,124799,124810,124816],{},[1114,124768,124769,124771],{},[1007,124770,896],{"href":897}," — the field-feature surface, the dependency graph, and posture enforcement",[1114,124773,124774,124777,124778,124780],{},[1007,124775,111],{"href":124776},"..\u002Fcore\u002Fcollection\u002Fschema"," — the collection schema the ",[941,124779,637],{}," map sits beside",[1114,124782,124783,124785],{},[1007,124784,665],{"href":124621}," — cross-record, reactive derivation (rollups, downstream collections)",[1114,124787,124788,124790,124791,124793],{},[1007,124789,739],{"href":124624}," — derived ",[1092,124792,2698],{}," rather than derived fields",[1114,124795,124796,124798],{},[1007,124797,760],{"href":123482}," — declare a computed target as a money field for exact decimal storage",[1114,124800,124801,964,124803,1625,124806,124809],{},[1007,124802,4449],{"href":123485},[941,124804,124805],{},"sum()",[941,124807,124808],{},"groupBy()"," over computed fields",[1114,124811,124812,124815],{},[1007,124813,693],{"href":124814},".\u002Fguards"," — write gates run before computed evaluation and treat computed fields as schema-owned",[1114,124817,124818,124820,124821],{},[1007,124819,711],{"href":124740}," — how computed fields appear in ",[941,124822,1380],{},[2645,124824,124825],{},"html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html pre.shiki code .sHdIc, html code.shiki .sHdIc{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#EEFFFF;--shiki-default-font-style:italic;--shiki-dark:#BABED8;--shiki-dark-font-style:italic}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":1037,"searchDepth":1038,"depth":1038,"links":124827},[124828,124829,124830,124839,124848,124849],{"id":29856,"depth":1041,"text":29857},{"id":58393,"depth":1041,"text":58394},{"id":29892,"depth":1041,"text":29893,"children":124831},[124832,124833,124834,124835,124836,124837,124838],{"id":121470,"depth":1038,"text":121471},{"id":121680,"depth":1038,"text":121681},{"id":121789,"depth":1038,"text":121790},{"id":122133,"depth":1038,"text":122134},{"id":122433,"depth":1038,"text":122434},{"id":123196,"depth":1038,"text":123197},{"id":123472,"depth":1038,"text":123473},{"id":41259,"depth":1041,"text":101012,"children":124840},[124841,124842,124844,124846,124847],{"id":123957,"depth":1038,"text":637},{"id":124106,"depth":1038,"text":124843},"computed(fn, opts?) — the via-composable descriptor",{"id":124227,"depth":1038,"text":124845},"ComputedFn\u003CT> \u002F ComputedFields\u003CT> (types)",{"id":124328,"depth":1038,"text":124331},{"id":124529,"depth":1038,"text":122023},{"id":68209,"depth":1041,"text":68210},{"id":2620,"depth":1041,"text":2621},"Computed scalar fields: derived values declared on the schema, evaluated on every write and stored — or virtual, computed fresh on every read.",{},{"title":637,"description":124850},"wp2t35b8TetXTGzG9NB-YTYXlRM-_hhYxuXaCDS3c2s",{"id":124855,"title":641,"access":12,"apiToc":929,"body":124856,"category":1042,"description":126590,"extension":1044,"meta":126591,"navigation":1042,"nest":1047,"path":642,"seo":126592,"sinceVersion":1042,"status":1049,"stem":643,"subtitle":1042,"tier":1050,"tocExternal":1042,"__hash__":126593},"docs\u002Fservices\u002Fconsent.md",{"type":931,"value":124857,"toc":126570},[124858,124860,124863,124865,124889,124892,124894,124903,124905,124913,125083,125085,125089,125101,125350,125354,125363,125402,125719,125722,125831,125835,125880,125886,125888,125893,125909,125917,125922,125971,125985,125990,126037,126042,126072,126075,126081,126160,126166,126297,126302,126362,126390,126394,126428,126430,126449,126451,126517,126519,126567],[934,124859,641],{"id":641},[46871,124861],{":rows":124862},"[{\"label\":\"Subpath\",\"value\":\"`@noy-db\u002Fhub\u002Fconsent`\"},{\"label\":\"Factory\",\"value\":\"`withConsent()`\"},{\"label\":\"Layer\",\"value\":\"`with-audit`\"},{\"label\":\"Cluster\",\"value\":\"E — Time & Audit\"},{\"label\":\"LOC cost\",\"value\":\"~194 (off-bundle when not opted in)\"}]",[1022,124864,29857],{"id":29856},[937,124866,124867,124868,124871,124872,124875,124876,118996,124879,968,124881,2423,124883,124885,124886,124888],{},"Privacy regulations such as the GDPR require consent to be ",[1092,124869,124870],{},"demonstrable",": you must be able\nto show what was done with personal data, when, by whom, and under which consent. The\nconsent service gives a vault (an encrypted, per-tenant data space — see\n",[1007,124873,124874],{"href":48},"Core 01 — Vault and collections",") exactly that: wrap any\nblock of work in ",[941,124877,124878],{},"vault.withConsent({ purpose, consentHash }, fn)",[941,124880,3158],{},[941,124882,3161],{},[941,124884,3164],{}," inside the block appends one audit entry to the reserved ",[941,124887,6744],{},"\ncollection. Outside a scope, nothing is written — consent logging is opt-in per access,\nby design.",[937,124890,124891],{},"Audit entries are encrypted like any other record, with their own data-encryption key,\nso the storage backend only ever sees ciphertext — it can prove entries exist without\nbeing able to read who accessed what. The whole service costs ~194 lines of code and is\noff-bundle when you don't opt in.",[1022,124893,70093],{"id":70092},[1111,124895,124896],{},[1114,124897,124898,964,124900,124902],{},[950,124899,107493],{},[941,124901,6748],{}," alone enables consent scopes and the audit log.",[1022,124904,99864],{"id":99863},[937,124906,124907,124908,10313,124911,2311],{},"Pass ",[941,124909,124910],{},"consentStrategy: withConsent()",[941,124912,12780],{},[2217,124914,124916],{"className":2219,"code":124915,"language":2221,"meta":1037,"style":1037},"import { createNoydb } from '@noy-db\u002Fhub'\nimport { withConsent } from '@noy-db\u002Fhub\u002Fconsent'\nimport { toMemory } from '@noy-db\u002Fto-memory'\n\nconst db = await createNoydb({\n  store: toMemory(),                          \u002F\u002F any NoydbStore works\n  user: 'alice',\n  secret: 'correct horse battery staple',\n  consentStrategy: withConsent(),\n})\n\nconst vault = await db.openVault('acme')\n",[941,124917,124918,124936,124954,124972,124976,124992,125007,125021,125035,125047,125053,125057],{"__ignoreMap":1037},[2225,124919,124920,124922,124924,124926,124928,124930,124932,124934],{"class":2227,"line":2228},[2225,124921,2232],{"class":2231},[2225,124923,2236],{"class":2235},[2225,124925,2744],{"class":2239},[2225,124927,2243],{"class":2235},[2225,124929,2246],{"class":2231},[2225,124931,2249],{"class":2235},[2225,124933,1086],{"class":2252},[2225,124935,2256],{"class":2235},[2225,124937,124938,124940,124942,124944,124946,124948,124950,124952],{"class":2227,"line":1041},[2225,124939,2232],{"class":2231},[2225,124941,2236],{"class":2235},[2225,124943,58525],{"class":2239},[2225,124945,2243],{"class":2235},[2225,124947,2246],{"class":2231},[2225,124949,2249],{"class":2235},[2225,124951,58534],{"class":2252},[2225,124953,2256],{"class":2235},[2225,124955,124956,124958,124960,124962,124964,124966,124968,124970],{"class":2227,"line":1038},[2225,124957,2232],{"class":2231},[2225,124959,2236],{"class":2235},[2225,124961,68576],{"class":2239},[2225,124963,2243],{"class":2235},[2225,124965,2246],{"class":2231},[2225,124967,2249],{"class":2235},[2225,124969,9638],{"class":2252},[2225,124971,2256],{"class":2235},[2225,124973,124974],{"class":2227,"line":2288},[2225,124975,2285],{"emptyLinePlaceholder":2284},[2225,124977,124978,124980,124982,124984,124986,124988,124990],{"class":2227,"line":2304},[2225,124979,2775],{"class":2774},[2225,124981,2778],{"class":2239},[2225,124983,2781],{"class":2235},[2225,124985,2784],{"class":2231},[2225,124987,2744],{"class":2291},[2225,124989,2789],{"class":2239},[2225,124991,4200],{"class":2235},[2225,124993,124994,124996,124998,125000,125002,125004],{"class":2227,"line":2356},[2225,124995,15608],{"class":2307},[2225,124997,2311],{"class":2235},[2225,124999,68576],{"class":2291},[2225,125001,4228],{"class":2239},[2225,125003,2298],{"class":2235},[2225,125005,125006],{"class":2763},"                          \u002F\u002F any NoydbStore works\n",[2225,125008,125009,125011,125013,125015,125017,125019],{"class":2227,"line":2830},[2225,125010,4205],{"class":2307},[2225,125012,2311],{"class":2235},[2225,125014,2249],{"class":2235},[2225,125016,6408],{"class":2252},[2225,125018,2805],{"class":2235},[2225,125020,1707],{"class":2235},[2225,125022,125023,125025,125027,125029,125031,125033],{"class":2227,"line":2861},[2225,125024,10423],{"class":2307},[2225,125026,2311],{"class":2235},[2225,125028,2249],{"class":2235},[2225,125030,40260],{"class":2252},[2225,125032,2805],{"class":2235},[2225,125034,1707],{"class":2235},[2225,125036,125037,125039,125041,125043,125045],{"class":2227,"line":4246},[2225,125038,58754],{"class":2307},[2225,125040,2311],{"class":2235},[2225,125042,58525],{"class":2291},[2225,125044,4228],{"class":2239},[2225,125046,1707],{"class":2235},[2225,125048,125049,125051],{"class":2227,"line":4253},[2225,125050,2359],{"class":2235},[2225,125052,2362],{"class":2239},[2225,125054,125055],{"class":2227,"line":4259},[2225,125056,2285],{"emptyLinePlaceholder":2284},[2225,125058,125059,125061,125063,125065,125067,125069,125071,125073,125075,125077,125079,125081],{"class":2227,"line":6548},[2225,125060,2775],{"class":2774},[2225,125062,2835],{"class":2239},[2225,125064,2781],{"class":2235},[2225,125066,2784],{"class":2231},[2225,125068,2842],{"class":2239},[2225,125070,1520],{"class":2235},[2225,125072,2847],{"class":2291},[2225,125074,2789],{"class":2239},[2225,125076,2805],{"class":2235},[2225,125078,6450],{"class":2252},[2225,125080,2805],{"class":2235},[2225,125082,2362],{"class":2239},[1022,125084,29893],{"id":29892},[1591,125086,125088],{"id":125087},"log-access-under-a-consent-scope","Log access under a consent scope",[937,125090,125091,125094,125095,125097,125098,125100],{},[941,125092,125093],{},"vault.withConsent(ctx, fn)"," runs ",[941,125096,12070],{}," with the consent context active and returns\nwhatever ",[941,125099,12070],{}," resolves to. Each per-record operation inside the scope writes one entry:",[2217,125102,125104],{"className":2219,"code":125103,"language":2221,"meta":1037,"style":1037},"interface Invoice { amount: number; status: string }\nconst invoices = vault.collection\u003CInvoice>('invoices')\n\nawait invoices.put('inv-1', { amount: 100, status: 'draft' })  \u002F\u002F before the scope — not logged\n\nconst invoice = await vault.withConsent(\n  { purpose: 'quarterly-review', consentHash: '7f3a8b9c' },\n  async () => {\n    await invoices.put('inv-2', { amount: 200, status: 'draft' })\n    return invoices.get('inv-1')\n  },\n)\n\u002F\u002F → { amount: 100, status: 'draft' }   (the callback's resolved value)\n",[941,125105,125106,125130,125160,125164,125211,125215,125234,125263,125273,125317,125337,125341,125345],{"__ignoreMap":1037},[2225,125107,125108,125110,125112,125114,125116,125118,125120,125122,125124,125126,125128],{"class":2227,"line":2228},[2225,125109,6994],{"class":2774},[2225,125111,6343],{"class":6342},[2225,125113,2236],{"class":2235},[2225,125115,6351],{"class":2307},[2225,125117,2311],{"class":2235},[2225,125119,6356],{"class":6342},[2225,125121,3769],{"class":2235},[2225,125123,6361],{"class":2307},[2225,125125,2311],{"class":2235},[2225,125127,6366],{"class":6342},[2225,125129,6369],{"class":2235},[2225,125131,125132,125134,125136,125138,125140,125142,125144,125146,125148,125150,125152,125154,125156,125158],{"class":2227,"line":1041},[2225,125133,2775],{"class":2774},[2225,125135,6461],{"class":2239},[2225,125137,2781],{"class":2235},[2225,125139,6466],{"class":2239},[2225,125141,1520],{"class":2235},[2225,125143,6471],{"class":2291},[2225,125145,6474],{"class":2235},[2225,125147,6477],{"class":6342},[2225,125149,6480],{"class":2235},[2225,125151,2789],{"class":2239},[2225,125153,2805],{"class":2235},[2225,125155,6487],{"class":2252},[2225,125157,2805],{"class":2235},[2225,125159,2362],{"class":2239},[2225,125161,125162],{"class":2227,"line":1038},[2225,125163,2285],{"emptyLinePlaceholder":2284},[2225,125165,125166,125168,125170,125172,125174,125176,125178,125180,125182,125184,125186,125188,125190,125192,125194,125196,125198,125200,125202,125204,125206,125208],{"class":2227,"line":2288},[2225,125167,6500],{"class":2231},[2225,125169,6503],{"class":2239},[2225,125171,1520],{"class":2235},[2225,125173,3161],{"class":2291},[2225,125175,2789],{"class":2239},[2225,125177,2805],{"class":2235},[2225,125179,6514],{"class":2252},[2225,125181,2805],{"class":2235},[2225,125183,2298],{"class":2235},[2225,125185,2236],{"class":2235},[2225,125187,6351],{"class":2307},[2225,125189,2311],{"class":2235},[2225,125191,6528],{"class":6527},[2225,125193,2298],{"class":2235},[2225,125195,6361],{"class":2307},[2225,125197,2311],{"class":2235},[2225,125199,2249],{"class":2235},[2225,125201,28429],{"class":2252},[2225,125203,2805],{"class":2235},[2225,125205,2243],{"class":2235},[2225,125207,27226],{"class":2239},[2225,125209,125210],{"class":2763},"\u002F\u002F before the scope — not logged\n",[2225,125212,125213],{"class":2227,"line":2304},[2225,125214,2285],{"emptyLinePlaceholder":2284},[2225,125216,125217,125219,125222,125224,125226,125228,125230,125232],{"class":2227,"line":2356},[2225,125218,2775],{"class":2774},[2225,125220,125221],{"class":2239}," invoice ",[2225,125223,2781],{"class":2235},[2225,125225,2784],{"class":2231},[2225,125227,6466],{"class":2239},[2225,125229,1520],{"class":2235},[2225,125231,58238],{"class":2291},[2225,125233,11683],{"class":2239},[2225,125235,125236,125238,125240,125242,125244,125246,125248,125250,125252,125254,125256,125259,125261],{"class":2227,"line":2830},[2225,125237,26354],{"class":2235},[2225,125239,61100],{"class":2307},[2225,125241,2311],{"class":2235},[2225,125243,2249],{"class":2235},[2225,125245,61107],{"class":2252},[2225,125247,2805],{"class":2235},[2225,125249,2298],{"class":2235},[2225,125251,61114],{"class":2307},[2225,125253,2311],{"class":2235},[2225,125255,2249],{"class":2235},[2225,125257,125258],{"class":2252},"7f3a8b9c",[2225,125260,2805],{"class":2235},[2225,125262,2353],{"class":2235},[2225,125264,125265,125267,125269,125271],{"class":2227,"line":2861},[2225,125266,61130],{"class":2774},[2225,125268,13130],{"class":2235},[2225,125270,8116],{"class":2774},[2225,125272,2301],{"class":2235},[2225,125274,125275,125277,125279,125281,125283,125285,125287,125289,125291,125293,125295,125297,125299,125301,125303,125305,125307,125309,125311,125313,125315],{"class":2227,"line":4246},[2225,125276,79881],{"class":2231},[2225,125278,6503],{"class":2239},[2225,125280,1520],{"class":2235},[2225,125282,3161],{"class":2291},[2225,125284,2789],{"class":2307},[2225,125286,2805],{"class":2235},[2225,125288,26080],{"class":2252},[2225,125290,2805],{"class":2235},[2225,125292,2298],{"class":2235},[2225,125294,2236],{"class":2235},[2225,125296,6351],{"class":2307},[2225,125298,2311],{"class":2235},[2225,125300,40956],{"class":6527},[2225,125302,2298],{"class":2235},[2225,125304,6361],{"class":2307},[2225,125306,2311],{"class":2235},[2225,125308,2249],{"class":2235},[2225,125310,28429],{"class":2252},[2225,125312,2805],{"class":2235},[2225,125314,2243],{"class":2235},[2225,125316,2362],{"class":2307},[2225,125318,125319,125321,125323,125325,125327,125329,125331,125333,125335],{"class":2227,"line":4253},[2225,125320,66675],{"class":2231},[2225,125322,6503],{"class":2239},[2225,125324,1520],{"class":2235},[2225,125326,3158],{"class":2291},[2225,125328,2789],{"class":2307},[2225,125330,2805],{"class":2235},[2225,125332,6514],{"class":2252},[2225,125334,2805],{"class":2235},[2225,125336,2362],{"class":2307},[2225,125338,125339],{"class":2227,"line":4259},[2225,125340,19017],{"class":2235},[2225,125342,125343],{"class":2227,"line":6548},[2225,125344,2362],{"class":2239},[2225,125346,125347],{"class":2227,"line":6576},[2225,125348,125349],{"class":2763},"\u002F\u002F → { amount: 100, status: 'draft' }   (the callback's resolved value)\n",[1591,125351,125353],{"id":125352},"query-the-audit-log","Query the audit log",[937,125355,125356,125359,125360,125362],{},[941,125357,125358],{},"vault.consentAudit(filter?)"," decrypts and returns the matching entries. Entries carry\nULID ids, so sorting by ",[941,125361,6983],{}," is insertion-order stable:",[2217,125364,125366],{"className":2219,"code":125365,"language":2221,"meta":1037,"style":1037},"const log = await vault.consentAudit({ purpose: 'quarterly-review' })\n",[941,125367,125368],{"__ignoreMap":1037},[2225,125369,125370,125372,125374,125376,125378,125380,125382,125384,125386,125388,125390,125392,125394,125396,125398,125400],{"class":2227,"line":2228},[2225,125371,2775],{"class":2774},[2225,125373,61167],{"class":2239},[2225,125375,2781],{"class":2235},[2225,125377,2784],{"class":2231},[2225,125379,6466],{"class":2239},[2225,125381,1520],{"class":2235},[2225,125383,61178],{"class":2291},[2225,125385,2789],{"class":2239},[2225,125387,2792],{"class":2235},[2225,125389,61100],{"class":2307},[2225,125391,2311],{"class":2235},[2225,125393,2249],{"class":2235},[2225,125395,61107],{"class":2252},[2225,125397,2805],{"class":2235},[2225,125399,2243],{"class":2235},[2225,125401,2362],{"class":2239},[2217,125403,125405],{"className":6604,"code":125404,"language":141,"meta":1037,"style":1037},"[\n  {\n    \"id\": \"01JZGJ3V9GQ2N9W3H4X5T6K7R8\",\n    \"timestamp\": \"2026-07-08T09:14:03.412Z\",\n    \"actor\": \"alice\",\n    \"purpose\": \"quarterly-review\",\n    \"consentHash\": \"7f3a8b9c\",\n    \"op\": \"put\",\n    \"collection\": \"invoices\",\n    \"recordId\": \"inv-2\"\n  },\n  {\n    \"id\": \"01JZGJ3V9SQ0F2B7C8D9E0A1B2\",\n    \"timestamp\": \"2026-07-08T09:14:03.421Z\",\n    \"actor\": \"alice\",\n    \"purpose\": \"quarterly-review\",\n    \"consentHash\": \"7f3a8b9c\",\n    \"op\": \"get\",\n    \"collection\": \"invoices\",\n    \"recordId\": \"inv-1\"\n  }\n]\n",[941,125406,125407,125411,125415,125434,125453,125471,125489,125507,125525,125543,125559,125563,125567,125586,125605,125623,125641,125659,125677,125695,125711,125715],{"__ignoreMap":1037},[2225,125408,125409],{"class":2227,"line":2228},[2225,125410,26349],{"class":2235},[2225,125412,125413],{"class":2227,"line":1041},[2225,125414,59879],{"class":2235},[2225,125416,125417,125419,125421,125423,125425,125427,125430,125432],{"class":2227,"line":1038},[2225,125418,14201],{"class":2235},[2225,125420,6983],{"class":2774},[2225,125422,6620],{"class":2235},[2225,125424,2311],{"class":2235},[2225,125426,6614],{"class":2235},[2225,125428,125429],{"class":2252},"01JZGJ3V9GQ2N9W3H4X5T6K7R8",[2225,125431,6620],{"class":2235},[2225,125433,1707],{"class":2235},[2225,125435,125436,125438,125440,125442,125444,125446,125449,125451],{"class":2227,"line":2288},[2225,125437,14201],{"class":2235},[2225,125439,59900],{"class":2774},[2225,125441,6620],{"class":2235},[2225,125443,2311],{"class":2235},[2225,125445,6614],{"class":2235},[2225,125447,125448],{"class":2252},"2026-07-08T09:14:03.412Z",[2225,125450,6620],{"class":2235},[2225,125452,1707],{"class":2235},[2225,125454,125455,125457,125459,125461,125463,125465,125467,125469],{"class":2227,"line":2304},[2225,125456,14201],{"class":2235},[2225,125458,61256],{"class":2774},[2225,125460,6620],{"class":2235},[2225,125462,2311],{"class":2235},[2225,125464,6614],{"class":2235},[2225,125466,6408],{"class":2252},[2225,125468,6620],{"class":2235},[2225,125470,1707],{"class":2235},[2225,125472,125473,125475,125477,125479,125481,125483,125485,125487],{"class":2227,"line":2356},[2225,125474,14201],{"class":2235},[2225,125476,61275],{"class":2774},[2225,125478,6620],{"class":2235},[2225,125480,2311],{"class":2235},[2225,125482,6614],{"class":2235},[2225,125484,61107],{"class":2252},[2225,125486,6620],{"class":2235},[2225,125488,1707],{"class":2235},[2225,125490,125491,125493,125495,125497,125499,125501,125503,125505],{"class":2227,"line":2830},[2225,125492,14201],{"class":2235},[2225,125494,61294],{"class":2774},[2225,125496,6620],{"class":2235},[2225,125498,2311],{"class":2235},[2225,125500,6614],{"class":2235},[2225,125502,125258],{"class":2252},[2225,125504,6620],{"class":2235},[2225,125506,1707],{"class":2235},[2225,125508,125509,125511,125513,125515,125517,125519,125521,125523],{"class":2227,"line":2861},[2225,125510,14201],{"class":2235},[2225,125512,61313],{"class":2774},[2225,125514,6620],{"class":2235},[2225,125516,2311],{"class":2235},[2225,125518,6614],{"class":2235},[2225,125520,3161],{"class":2252},[2225,125522,6620],{"class":2235},[2225,125524,1707],{"class":2235},[2225,125526,125527,125529,125531,125533,125535,125537,125539,125541],{"class":2227,"line":4246},[2225,125528,14201],{"class":2235},[2225,125530,6471],{"class":2774},[2225,125532,6620],{"class":2235},[2225,125534,2311],{"class":2235},[2225,125536,6614],{"class":2235},[2225,125538,6487],{"class":2252},[2225,125540,6620],{"class":2235},[2225,125542,1707],{"class":2235},[2225,125544,125545,125547,125549,125551,125553,125555,125557],{"class":2227,"line":4253},[2225,125546,14201],{"class":2235},[2225,125548,61350],{"class":2774},[2225,125550,6620],{"class":2235},[2225,125552,2311],{"class":2235},[2225,125554,6614],{"class":2235},[2225,125556,26080],{"class":2252},[2225,125558,9449],{"class":2235},[2225,125560,125561],{"class":2227,"line":4259},[2225,125562,19017],{"class":2235},[2225,125564,125565],{"class":2227,"line":6548},[2225,125566,59879],{"class":2235},[2225,125568,125569,125571,125573,125575,125577,125579,125582,125584],{"class":2227,"line":6576},[2225,125570,14201],{"class":2235},[2225,125572,6983],{"class":2774},[2225,125574,6620],{"class":2235},[2225,125576,2311],{"class":2235},[2225,125578,6614],{"class":2235},[2225,125580,125581],{"class":2252},"01JZGJ3V9SQ0F2B7C8D9E0A1B2",[2225,125583,6620],{"class":2235},[2225,125585,1707],{"class":2235},[2225,125587,125588,125590,125592,125594,125596,125598,125601,125603],{"class":2227,"line":6582},[2225,125589,14201],{"class":2235},[2225,125591,59900],{"class":2774},[2225,125593,6620],{"class":2235},[2225,125595,2311],{"class":2235},[2225,125597,6614],{"class":2235},[2225,125599,125600],{"class":2252},"2026-07-08T09:14:03.421Z",[2225,125602,6620],{"class":2235},[2225,125604,1707],{"class":2235},[2225,125606,125607,125609,125611,125613,125615,125617,125619,125621],{"class":2227,"line":6587},[2225,125608,14201],{"class":2235},[2225,125610,61256],{"class":2774},[2225,125612,6620],{"class":2235},[2225,125614,2311],{"class":2235},[2225,125616,6614],{"class":2235},[2225,125618,6408],{"class":2252},[2225,125620,6620],{"class":2235},[2225,125622,1707],{"class":2235},[2225,125624,125625,125627,125629,125631,125633,125635,125637,125639],{"class":2227,"line":7174},[2225,125626,14201],{"class":2235},[2225,125628,61275],{"class":2774},[2225,125630,6620],{"class":2235},[2225,125632,2311],{"class":2235},[2225,125634,6614],{"class":2235},[2225,125636,61107],{"class":2252},[2225,125638,6620],{"class":2235},[2225,125640,1707],{"class":2235},[2225,125642,125643,125645,125647,125649,125651,125653,125655,125657],{"class":2227,"line":7180},[2225,125644,14201],{"class":2235},[2225,125646,61294],{"class":2774},[2225,125648,6620],{"class":2235},[2225,125650,2311],{"class":2235},[2225,125652,6614],{"class":2235},[2225,125654,125258],{"class":2252},[2225,125656,6620],{"class":2235},[2225,125658,1707],{"class":2235},[2225,125660,125661,125663,125665,125667,125669,125671,125673,125675],{"class":2227,"line":7220},[2225,125662,14201],{"class":2235},[2225,125664,61313],{"class":2774},[2225,125666,6620],{"class":2235},[2225,125668,2311],{"class":2235},[2225,125670,6614],{"class":2235},[2225,125672,3158],{"class":2252},[2225,125674,6620],{"class":2235},[2225,125676,1707],{"class":2235},[2225,125678,125679,125681,125683,125685,125687,125689,125691,125693],{"class":2227,"line":7225},[2225,125680,14201],{"class":2235},[2225,125682,6471],{"class":2774},[2225,125684,6620],{"class":2235},[2225,125686,2311],{"class":2235},[2225,125688,6614],{"class":2235},[2225,125690,6487],{"class":2252},[2225,125692,6620],{"class":2235},[2225,125694,1707],{"class":2235},[2225,125696,125697,125699,125701,125703,125705,125707,125709],{"class":2227,"line":7231},[2225,125698,14201],{"class":2235},[2225,125700,61350],{"class":2774},[2225,125702,6620],{"class":2235},[2225,125704,2311],{"class":2235},[2225,125706,6614],{"class":2235},[2225,125708,6514],{"class":2252},[2225,125710,9449],{"class":2235},[2225,125712,125713],{"class":2227,"line":7267},[2225,125714,7632],{"class":2235},[2225,125716,125717],{"class":2227,"line":7272},[2225,125718,26484],{"class":2235},[937,125720,125721],{},"The filter fields compose; every field is optional:",[2217,125723,125725],{"className":2219,"code":125724,"language":2221,"meta":1037,"style":1037},"await vault.consentAudit({ collection: 'invoices' })\n\u002F\u002F → only entries targeting the 'invoices' collection\n\nawait vault.consentAudit({ actor: 'alice', since: '2026-07-01T00:00:00Z' })\n\u002F\u002F → alice's entries at or after July 1\n\nawait vault.consentAudit()\n\u002F\u002F → every entry\n",[941,125726,125727,125755,125760,125764,125805,125810,125814,125826],{"__ignoreMap":1037},[2225,125728,125729,125731,125733,125735,125737,125739,125741,125743,125745,125747,125749,125751,125753],{"class":2227,"line":2228},[2225,125730,6500],{"class":2231},[2225,125732,6466],{"class":2239},[2225,125734,1520],{"class":2235},[2225,125736,61178],{"class":2291},[2225,125738,2789],{"class":2239},[2225,125740,2792],{"class":2235},[2225,125742,7061],{"class":2307},[2225,125744,2311],{"class":2235},[2225,125746,2249],{"class":2235},[2225,125748,6487],{"class":2252},[2225,125750,2805],{"class":2235},[2225,125752,2243],{"class":2235},[2225,125754,2362],{"class":2239},[2225,125756,125757],{"class":2227,"line":1041},[2225,125758,125759],{"class":2763},"\u002F\u002F → only entries targeting the 'invoices' collection\n",[2225,125761,125762],{"class":2227,"line":1038},[2225,125763,2285],{"emptyLinePlaceholder":2284},[2225,125765,125766,125768,125770,125772,125774,125776,125778,125781,125783,125785,125787,125789,125791,125793,125795,125797,125799,125801,125803],{"class":2227,"line":2288},[2225,125767,6500],{"class":2231},[2225,125769,6466],{"class":2239},[2225,125771,1520],{"class":2235},[2225,125773,61178],{"class":2291},[2225,125775,2789],{"class":2239},[2225,125777,2792],{"class":2235},[2225,125779,125780],{"class":2307}," actor",[2225,125782,2311],{"class":2235},[2225,125784,2249],{"class":2235},[2225,125786,6408],{"class":2252},[2225,125788,2805],{"class":2235},[2225,125790,2298],{"class":2235},[2225,125792,61185],{"class":2307},[2225,125794,2311],{"class":2235},[2225,125796,2249],{"class":2235},[2225,125798,28698],{"class":2252},[2225,125800,2805],{"class":2235},[2225,125802,2243],{"class":2235},[2225,125804,2362],{"class":2239},[2225,125806,125807],{"class":2227,"line":2304},[2225,125808,125809],{"class":2763},"\u002F\u002F → alice's entries at or after July 1\n",[2225,125811,125812],{"class":2227,"line":2356},[2225,125813,2285],{"emptyLinePlaceholder":2284},[2225,125815,125816,125818,125820,125822,125824],{"class":2227,"line":2830},[2225,125817,6500],{"class":2231},[2225,125819,6466],{"class":2239},[2225,125821,1520],{"class":2235},[2225,125823,61178],{"class":2291},[2225,125825,8146],{"class":2239},[2225,125827,125828],{"class":2227,"line":2861},[2225,125829,125830],{"class":2763},"\u002F\u002F → every entry\n",[1591,125832,125834],{"id":125833},"nothing-is-logged-outside-a-scope","Nothing is logged outside a scope",[2217,125836,125838],{"className":2219,"code":125837,"language":2221,"meta":1037,"style":1037},"await invoices.get('inv-1')       \u002F\u002F no active scope\nawait vault.consentAudit()\n\u002F\u002F → the same entries as before — no new ones\n",[941,125839,125840,125863,125875],{"__ignoreMap":1037},[2225,125841,125842,125844,125846,125848,125850,125852,125854,125856,125858,125860],{"class":2227,"line":2228},[2225,125843,6500],{"class":2231},[2225,125845,6503],{"class":2239},[2225,125847,1520],{"class":2235},[2225,125849,3158],{"class":2291},[2225,125851,2789],{"class":2239},[2225,125853,2805],{"class":2235},[2225,125855,6514],{"class":2252},[2225,125857,2805],{"class":2235},[2225,125859,76416],{"class":2239},[2225,125861,125862],{"class":2763},"\u002F\u002F no active scope\n",[2225,125864,125865,125867,125869,125871,125873],{"class":2227,"line":1041},[2225,125866,6500],{"class":2231},[2225,125868,6466],{"class":2239},[2225,125870,1520],{"class":2235},[2225,125872,61178],{"class":2291},[2225,125874,8146],{"class":2239},[2225,125876,125877],{"class":2227,"line":1038},[2225,125878,125879],{"class":2763},"\u002F\u002F → the same entries as before — no new ones\n",[937,125881,125882,125883,125885],{},"The scope is restored when the callback exits, even if it throws: an error inside\n",[941,125884,58238],{}," propagates to the caller, and subsequent operations are unaudited again.",[1022,125887,102889],{"id":102888},[1591,125889,125891],{"id":125890},"withconsent",[941,125892,6748],{},[2217,125894,125896],{"className":2219,"code":125895,"language":2221,"meta":1037,"style":1037},"function withConsent(): ConsentStrategy\n",[941,125897,125898],{"__ignoreMap":1037},[2225,125899,125900,125902,125904,125906],{"class":2227,"line":2228},[2225,125901,12641],{"class":2774},[2225,125903,58525],{"class":2291},[2225,125905,112812],{"class":2235},[2225,125907,125908],{"class":6342}," ConsentStrategy\n",[937,125910,125911,125912,10313,125914,125916],{},"Builds the active consent strategy. Pass it as ",[941,125913,73191],{},[941,125915,12780],{}," — this\nis the only wiring step. Takes no options.",[1591,125918,125920],{"id":125919},"vaultwithconsentctx-fn",[941,125921,125093],{},[2217,125923,125925],{"className":2219,"code":125924,"language":2221,"meta":1037,"style":1037},"vault.withConsent\u003CT>(ctx: ConsentContext, fn: () => Promise\u003CT>): Promise\u003CT>\n",[941,125926,125927],{"__ignoreMap":1037},[2225,125928,125929,125931,125933,125935,125937,125939,125941,125944,125946,125949,125951,125953,125955,125957,125959,125961,125963,125965,125967,125969],{"class":2227,"line":2228},[2225,125930,2694],{"class":2239},[2225,125932,1520],{"class":2235},[2225,125934,58238],{"class":2291},[2225,125936,6474],{"class":2235},[2225,125938,11968],{"class":6342},[2225,125940,6480],{"class":2235},[2225,125942,125943],{"class":2239},"(ctx: ConsentContext",[2225,125945,2298],{"class":2235},[2225,125947,125948],{"class":2239}," fn: ",[2225,125950,4228],{"class":2235},[2225,125952,8116],{"class":2774},[2225,125954,7079],{"class":6342},[2225,125956,6474],{"class":2235},[2225,125958,11968],{"class":2239},[2225,125960,6480],{"class":2235},[2225,125962,11737],{"class":2239},[2225,125964,11740],{"class":6342},[2225,125966,6474],{"class":2235},[2225,125968,11968],{"class":2239},[2225,125970,7093],{"class":2235},[937,125972,125973,125974,125976,125977,125979,125980,125982,125983,20243],{},"Runs ",[941,125975,12070],{}," under a consent scope and returns its resolved value. Every audited operation\ninside ",[941,125978,12070],{}," writes one entry to ",[941,125981,6744],{},". The prior scope is restored on exit,\nincluding when ",[941,125984,12070],{},[937,125986,125987,2311],{},[941,125988,125989],{},"ConsentContext",[1338,125991,125992,126002],{},[1341,125993,125994],{},[1344,125995,125996,125998,126000],{},[1347,125997,101104],{},[1347,125999,20194],{},[1347,126001,7834],{},[1357,126003,126004,126020],{},[1344,126005,126006,126010,126014],{},[1362,126007,126008],{},[941,126009,61275],{},[1362,126011,126012],{},[941,126013,7259],{},[1362,126015,126016,126017,1520],{},"What this access is for. Free-form — your compliance vocabulary. Carried in every entry and queryable via ",[941,126018,126019],{},"consentAudit({ purpose })",[1344,126021,126022,126026,126030],{},[1362,126023,126024],{},[941,126025,61294],{},[1362,126027,126028],{},[941,126029,7259],{},[1362,126031,126032,126033,126036],{},"Hex-encoded sha256 of the consent artefact the actor presented (a signed consent-banner click, an authorization PDF, an ",[941,126034,126035],{},"X-Consent-Hash"," header). Stored as-is in each entry.",[1591,126038,126040],{"id":126039},"vaultconsentauditfilter",[941,126041,125358],{},[2217,126043,126045],{"className":2219,"code":126044,"language":2221,"meta":1037,"style":1037},"vault.consentAudit(filter?: ConsentAuditFilter): Promise\u003CConsentAuditEntry[]>\n",[941,126046,126047],{"__ignoreMap":1037},[2225,126048,126049,126051,126053,126055,126058,126060,126063,126065,126067,126070],{"class":2227,"line":2228},[2225,126050,2694],{"class":2239},[2225,126052,1520],{"class":2235},[2225,126054,61178],{"class":2291},[2225,126056,126057],{"class":2239},"(filter",[2225,126059,7011],{"class":2235},[2225,126061,126062],{"class":2239}," ConsentAuditFilter): ",[2225,126064,11740],{"class":6342},[2225,126066,6474],{"class":2235},[2225,126068,126069],{"class":2239},"ConsentAuditEntry[]",[2225,126071,7093],{"class":2235},[937,126073,126074],{},"Decrypts and returns every audit entry matching the filter (all entries when omitted).",[937,126076,126077,126080],{},[941,126078,126079],{},"ConsentAuditFilter"," — all fields optional, combined with AND:",[1338,126082,126083,126093],{},[1341,126084,126085],{},[1344,126086,126087,126089,126091],{},[1347,126088,101104],{},[1347,126090,20194],{},[1347,126092,7834],{},[1357,126094,126095,126108,126121,126134,126147],{},[1344,126096,126097,126101,126105],{},[1362,126098,126099],{},[941,126100,7883],{},[1362,126102,126103],{},[941,126104,7259],{},[1362,126106,126107],{},"Only entries at or after this ISO timestamp.",[1344,126109,126110,126114,126118],{},[1362,126111,126112],{},[941,126113,75352],{},[1362,126115,126116],{},[941,126117,7259],{},[1362,126119,126120],{},"Only entries at or before this ISO timestamp.",[1344,126122,126123,126127,126131],{},[1362,126124,126125],{},[941,126126,6471],{},[1362,126128,126129],{},[941,126130,7259],{},[1362,126132,126133],{},"Only entries targeting this collection.",[1344,126135,126136,126140,126144],{},[1362,126137,126138],{},[941,126139,61256],{},[1362,126141,126142],{},[941,126143,7259],{},[1362,126145,126146],{},"Only entries written by this actor (the vault user).",[1344,126148,126149,126153,126157],{},[1362,126150,126151],{},[941,126152,61275],{},[1362,126154,126155],{},[941,126156,7259],{},[1362,126158,126159],{},"Only entries with this purpose.",[937,126161,126162,126165],{},[941,126163,126164],{},"ConsentAuditEntry"," — one record per audited operation:",[1338,126167,126168,126178],{},[1341,126169,126170],{},[1344,126171,126172,126174,126176],{},[1347,126173,101104],{},[1347,126175,20194],{},[1347,126177,20200],{},[1357,126179,126180,126193,126206,126221,126236,126250,126264,126277],{},[1344,126181,126182,126186,126190],{},[1362,126183,126184],{},[941,126185,6983],{},[1362,126187,126188],{},[941,126189,7259],{},[1362,126191,126192],{},"ULID — stable insertion-order sort key.",[1344,126194,126195,126199,126203],{},[1362,126196,126197],{},[941,126198,59900],{},[1362,126200,126201],{},[941,126202,7259],{},[1362,126204,126205],{},"ISO timestamp of the operation.",[1344,126207,126208,126212,126216],{},[1362,126209,126210],{},[941,126211,61256],{},[1362,126213,126214],{},[941,126215,7259],{},[1362,126217,960,126218,126220],{},[941,126219,16225],{}," the vault was opened with.",[1344,126222,126223,126227,126231],{},[1362,126224,126225],{},[941,126226,61275],{},[1362,126228,126229],{},[941,126230,7259],{},[1362,126232,126233,126234,1520],{},"From the active ",[941,126235,125989],{},[1344,126237,126238,126242,126246],{},[1362,126239,126240],{},[941,126241,61294],{},[1362,126243,126244],{},[941,126245,7259],{},[1362,126247,126233,126248,1520],{},[941,126249,125989],{},[1344,126251,126252,126256,126261],{},[1362,126253,126254],{},[941,126255,61313],{},[1362,126257,126258],{},[941,126259,126260],{},"ConsentOp",[1362,126262,126263],{},"Which operation ran (see below).",[1344,126265,126266,126270,126274],{},[1362,126267,126268],{},[941,126269,6471],{},[1362,126271,126272],{},[941,126273,7259],{},[1362,126275,126276],{},"Target collection.",[1344,126278,126279,126283,126287],{},[1362,126280,126281],{},[941,126282,61350],{},[1362,126284,126285],{},[941,126286,7259],{},[1362,126288,126289,126290,126293,126294,126296],{},"Target record id — ",[941,126291,126292],{},"'*'"," for whole-collection sweeps (the ",[941,126295,119296],{}," op).",[1591,126298,126300],{"id":126299},"consentop",[941,126301,126260],{},[2217,126303,126305],{"className":2219,"code":126304,"language":2221,"meta":1037,"style":1037},"type ConsentOp = 'get' | 'put' | 'delete' | 'reveal' | 'verify' | 'find'\n",[941,126306,126307],{"__ignoreMap":1037},[2225,126308,126309,126311,126314,126316,126318,126320,126322,126324,126326,126328,126330,126332,126334,126336,126338,126340,126342,126344,126346,126348,126350,126352,126354,126356,126358,126360],{"class":2227,"line":2228},[2225,126310,6339],{"class":2774},[2225,126312,126313],{"class":6342}," ConsentOp",[2225,126315,6346],{"class":2235},[2225,126317,2249],{"class":2235},[2225,126319,3158],{"class":2252},[2225,126321,2805],{"class":2235},[2225,126323,7087],{"class":2235},[2225,126325,2249],{"class":2235},[2225,126327,3161],{"class":2252},[2225,126329,2805],{"class":2235},[2225,126331,7087],{"class":2235},[2225,126333,2249],{"class":2235},[2225,126335,3164],{"class":2252},[2225,126337,2805],{"class":2235},[2225,126339,7087],{"class":2235},[2225,126341,2249],{"class":2235},[2225,126343,117298],{"class":2252},[2225,126345,2805],{"class":2235},[2225,126347,7087],{"class":2235},[2225,126349,2249],{"class":2235},[2225,126351,18051],{"class":2252},[2225,126353,2805],{"class":2235},[2225,126355,7087],{"class":2235},[2225,126357,2249],{"class":2235},[2225,126359,119296],{"class":2252},[2225,126361,2256],{"class":2235},[937,126363,126364,1261,126366,1261,126368,126370,126371,1707,126373,2462,126375,126377,126378,126380,126381,126383,126384,126386,126387,126389],{},[941,126365,3158],{},[941,126367,3161],{},[941,126369,3164],{}," come from ordinary per-record collection operations. ",[941,126372,117298],{},[941,126374,18051],{},[941,126376,119296],{}," are emitted by the ",[1007,126379,633],{"href":634}," service's\nsealed-field operations; ",[941,126382,119296],{}," is a digest-search sweep and records the ",[941,126385,126292],{}," sentinel\nas its ",[941,126388,61350],{}," (never a real id — real ids are ULIDs).",[1591,126391,126393],{"id":126392},"other-exports","Other exports",[1111,126395,126396,126405,126414],{},[1114,126397,126398,126401,126402,1520],{},[941,126399,126400],{},"CONSENT_AUDIT_COLLECTION"," — the reserved collection name, ",[941,126403,126404],{},"'_consent_audit'",[1114,126406,126407,2601,126410,126413],{},[941,126408,126409],{},"writeConsentEntry(adapter, vault, encrypted, entry, getDEK)",[941,126411,126412],{},"loadConsentEntries(adapter, vault, encrypted, getDEK, filter?)"," — the low-level\nfunctions the strategy delegates to. Application code uses the vault methods above;\nthese exist for custom strategy implementations.",[1114,126415,126416,126417,968,126420,968,126422,968,126424,1707,126426,1520],{},"Types: ",[941,126418,126419],{},"ConsentStrategy",[941,126421,125989],{},[941,126423,126260],{},[941,126425,126164],{},[941,126427,126079],{},[1022,126429,101425],{"id":101424},[1111,126431,126432,126440,126446],{},[1114,126433,126434,126436,126437,126439],{},[941,126435,125093],{}," still runs ",[941,126438,12070],{}," and returns its value — but no entries are\nwritten.",[1114,126441,126442,2557,126444,1520],{},[941,126443,21528],{},[941,126445,7262],{},[1114,126447,126448],{},"None of the ~194 LOC ships in your bundle.",[1022,126450,68210],{"id":68209},[1111,126452,126453,126466,126495,126503],{},[1114,126454,126455,126458,126459,126461,126462,126465],{},[950,126456,126457],{},"Single-slot scope per Vault instance."," Two concurrent ",[941,126460,58238],{}," calls on the same\nvault stomp each other's context. Use separate Vault instances — or an external\n",[941,126463,126464],{},"AsyncLocalStorage"," shim — for per-flight scoping.",[1114,126467,126468,126474,126475,968,126477,968,126479,126481,126482,968,126484,1707,126486,126488,126489,126491,126492,126494],{},[950,126469,126470,126471,126473],{},"Bulk ",[941,126472,24407],{}," reads are not logged."," Entries are written by per-record operations\n(",[941,126476,3158],{},[941,126478,3161],{},[941,126480,3164],{},") and by classified-field operations (",[941,126483,117298],{},[941,126485,18051],{},[941,126487,119296],{},"). Reading a whole collection with ",[941,126490,24407],{}," writes no per-record entries; use\nper-record ",[941,126493,14639],{}," inside the scope when each read must be provable.",[1114,126496,126497,126502],{},[950,126498,960,126499,126501],{},[941,126500,61294],{}," is opaque to noy-db."," The system stores only the hash; the consent\nrecord itself (the signed banner click, the authorization document) lives outside the\nvault. A regulator verifies a presented document against the logged hash at audit time —\nthe vault never possesses the document.",[1114,126504,126505,126508,126509,968,126511,51161,126513,16238,126515,957],{},[950,126506,126507],{},"The log is ciphertext at the store."," Entries are encrypted with a dedicated\nconsent-audit key, separate from per-collection keys, so querying the log never unwraps\nindividual collection keys — and the storage backend can never read ",[941,126510,61275],{},[941,126512,61256],{},[941,126514,61294],{},[1007,126516,1516],{"href":66},[1022,126518,2621],{"id":2620},[1111,126520,126521,126527,126532,126544,126554,126559],{},[1114,126522,126523,1368,126525],{},[1007,126524,61743],{"href":598},[1007,126526,1019],{"href":1018},[1114,126528,126529,126531],{},[1007,126530,697],{"href":698}," — hash-chained change ledger; the vault's other audit primitive",[1114,126533,126534,126536,126537,1261,126539,1261,126541,126543],{},[1007,126535,633],{"href":634}," — sealed fields whose ",[941,126538,117298],{},[941,126540,18051],{},[941,126542,119296],{}," operations feed the consent log",[1114,126545,126546,126548,126549,1261,126551,126553],{},[1007,126547,776],{"href":777}," — fiscal-period seals; pair ",[941,126550,7883],{},[941,126552,75352],{}," audit filters with period boundaries",[1114,126555,126556,126558],{},[1007,126557,1516],{"href":66}," — how envelopes and per-collection keys work",[1114,126560,101581,126561,1520],{},[1007,126562,126564],{"href":92755,"rel":126563},[3371],[941,126565,126566],{},"19-with-consent",[2645,126568,126569],{},"html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}",{"title":1037,"searchDepth":1038,"depth":1038,"links":126571},[126572,126573,126574,126575,126580,126587,126588,126589],{"id":29856,"depth":1041,"text":29857},{"id":70092,"depth":1041,"text":70093},{"id":99863,"depth":1041,"text":99864},{"id":29892,"depth":1041,"text":29893,"children":126576},[126577,126578,126579],{"id":125087,"depth":1038,"text":125088},{"id":125352,"depth":1038,"text":125353},{"id":125833,"depth":1038,"text":125834},{"id":102888,"depth":1041,"text":102889,"children":126581},[126582,126583,126584,126585,126586],{"id":125890,"depth":1038,"text":6748},{"id":125919,"depth":1038,"text":125093},{"id":126039,"depth":1038,"text":125358},{"id":126299,"depth":1038,"text":126260},{"id":126392,"depth":1038,"text":126393},{"id":101424,"depth":1041,"text":101425},{"id":68209,"depth":1041,"text":68210},{"id":2620,"depth":1041,"text":2621},"Consent-audit logging for GDPR\u002FPIPL-style compliance: wrap access in `vault.withConsent(...)` and every operation in scope appends one encrypted audit entry.",{},{"title":641,"description":126590},"Gl-BdgGH_sINXJxHG1tkUV_Ur4mjzntMrcKdj34myd8",{"id":126595,"title":645,"access":12,"apiToc":929,"body":126596,"category":1042,"description":128419,"extension":1044,"meta":128420,"navigation":1042,"nest":1047,"path":646,"seo":128421,"sinceVersion":1042,"status":1049,"stem":647,"subtitle":1042,"tier":1050,"tocExternal":1042,"__hash__":128422},"docs\u002Fservices\u002Fcover.md",{"type":931,"value":126597,"toc":128395},[126598,126600,126603,126605,126622,126636,126639,126643,126646,126777,126792,126796,126799,126814,126816,126949,126970,126972,126976,127197,127211,127215,127224,127303,127309,127316,127463,127489,127727,127731,127738,127825,127838,127858,127877,127884,127886,127891,127897,127948,127954,127973,127977,128065,128069,128133,128136,128204,128208,128270,128287,128289,128294,128366,128368,128392],[934,126599,645],{"id":645},[46871,126601],{":rows":126602},"[{\"label\":\"Subpath\",\"value\":\"`@noy-db\u002Fhub` (root export)\"},{\"label\":\"Type\",\"value\":\"Core feature — enabled with the `cover` option on `NoydbOptions`.\"},{\"label\":\"Cross-cuts\",\"value\":\"`@noy-db\u002Fhub` core (vault storage + `.noydb` bundle header format), every storage adapter (reads go through the plain `get`\u002F`put` contract, no adapter-specific code)\"}]",[1022,126604,6242],{"id":6241},[937,126606,126607,126608,126610,126611,126613,126614,126618,126619,126621],{},"noy-db's core guarantee is that storage backends only ever see ciphertext: every record — and by default every label on a ",[950,126609,2694],{}," (noy-db's unit of encrypted storage; see ",[1007,126612,77926],{"href":48},") — is encrypted under a key the owner must first supply. That is great for confidentiality, but it means a ",[1007,126615,126616,83152],{"href":629},[941,126617,1402],{}," dropped on a USB stick, or a list of vaults in a multi-tenant picker, has nothing readable at all: no name, no icon, not even a timestamp. See ",[1007,126620,21],{"href":60250}," for the key model this sits below.",[937,126623,126624,126626,126627,126629,126630,126632,126633,126635],{},[941,126625,645],{}," closes that specific gap with a small, deliberately narrow plaintext label. The developer opts a vault into the feature via ",[941,126628,36470],{},"; the vault's owner then sets a handful of fields — name, description, icon, locale — that stay readable without unlocking the vault or decrypting a ",[941,126631,1402],{}," bundle (see ",[1007,126634,629],{"href":629}," for the container format). Everything else about the vault stays exactly as encrypted as before: the envelope is a label, not a window into the data.",[937,126637,126638],{},"Every published field is one the owner explicitly chose to publish. The envelope deliberately excludes anything an attacker could use to infer activity over time — record counts, user counts, last-accessed timestamps — so opting in never turns into an unintended audit surface.",[1022,126640,126642],{"id":126641},"whats-in-scope","What's in scope",[937,126644,126645],{},"Owner-curated, static fields:",[1338,126647,126648,126658],{},[1341,126649,126650],{},[1344,126651,126652,126654,126656],{},[1347,126653,101104],{},[1347,126655,20194],{},[1347,126657,9542],{},[1357,126659,126660,126678,126693,126717,126732,126746,126759],{},[1344,126661,126662,126666,126671],{},[1362,126663,126664],{},[941,126665,4914],{},[1362,126667,126668],{},[941,126669,126670],{},"string | Record\u003Cstring, string>",[1362,126672,126673,126674,126677],{},"Vault display name. Pass a plain string for a single-locale label, or a locale → string map (",[941,126675,126676],{},"{ en: '…', th: '…' }",") for multi-locale.",[1344,126679,126680,126684,126688],{},[1362,126681,126682],{},[941,126683,59777],{},[1362,126685,126686],{},[941,126687,126670],{},[1362,126689,126690,126691,1520],{},"Free-form one or two sentences. Same single-string \u002F locale-map shape as ",[941,126692,4914],{},[1344,126694,126695,126700,126704],{},[1362,126696,126697],{},[941,126698,126699],{},"icon",[1362,126701,126702],{},[941,126703,7259],{},[1362,126705,126706,126707,126709,126710,4688,126713,126716],{},"Inline ",[941,126708,81790],{}," URL (",[941,126711,126712],{},"data:image\u002Fpng;base64,…",[941,126714,126715],{},"data:image\u002Fsvg+xml;base64,…","). External URLs are not accepted. Capped at 256 KB of encoded data-URL string length by default. PNG and SVG only.",[1344,126718,126719,126723,126726],{},[1362,126720,126721],{},[941,126722,87098],{},[1362,126724,126725],{},"ISO-8601 string",[1362,126727,126728,126729,126731],{},"Set on the first ",[941,126730,13064],{}," write; immutable thereafter — the developer\u002Fowner cannot set it directly.",[1344,126733,126734,126739,126741],{},[1362,126735,126736],{},[941,126737,126738],{},"updatedAt",[1362,126740,126725],{},[1362,126742,126743,126744,124645],{},"Refreshed on every ",[941,126745,13064],{},[1344,126747,126748,126752,126756],{},[1362,126749,126750],{},[941,126751,52564],{},[1362,126753,126754],{},[941,126755,20636],{},[1362,126757,126758],{},"Monotonic counter starting at 1; increments on every successful write. Useful as a cheap cache-invalidation signal.",[1344,126760,126761,126766,126769],{},[1362,126762,126763],{},[941,126764,126765],{},"defaultLocale",[1362,126767,126768],{},"BCP-47 string",[1362,126770,126771,126772,1625,126774,126776],{},"Fallback locale a reader falls back to when the requested locale isn't in a ",[941,126773,4914],{},[941,126775,59777],{}," map.",[937,126778,126779,968,126781,2462,126783,126785,126786,126788,126789,126791],{},[941,126780,87098],{},[941,126782,126738],{},[941,126784,52564],{}," are hub-managed — the owner cannot set them through ",[941,126787,13064],{},"; including them in a schema's ",[941,126790,57285],{}," list is a no-op.",[1022,126793,126795],{"id":126794},"whats-out-of-scope","What's out of scope",[937,126797,126798],{},"These are deliberately absent from the envelope — they would make it an audit surface rather than a label:",[1111,126800,126801,126804,126807],{},[1114,126802,126803],{},"Record counts, per-collection record counts, user counts, user lists.",[1114,126805,126806],{},"Any field that updates on every read or write (last-accessed-at) — that pattern leaks activity over time to anyone who can list the bundle or vault.",[1114,126808,126809,126810,126813],{},"A signature or HMAC over the envelope (see ",[1007,126811,68210],{"href":126812},"#limits-gotchas"," below — v1 is a plaintext, untrusted hint, not a signed claim).",[1022,126815,58394],{"id":58393},[2217,126817,126819],{"className":2219,"code":126818,"language":2221,"meta":1037,"style":1037},"import { createNoydb } from '@noy-db\u002Fhub'\nimport { toMemory } from '@noy-db\u002Fto-memory'\n\nconst db = await createNoydb({\n  store: toMemory(),\n  user: 'alice',\n  secret: 'a-strong-secret',\n  \u002F\u002F shorthand — every field above, default caps:\n  cover: true,\n  \u002F\u002F or narrow what the owner is allowed to set:\n  \u002F\u002F cover: { fields: ['name', 'icon'], maxIconBytes: 64 * 1024 },\n})\n",[941,126820,126821,126839,126857,126861,126877,126889,126903,126917,126922,126933,126938,126943],{"__ignoreMap":1037},[2225,126822,126823,126825,126827,126829,126831,126833,126835,126837],{"class":2227,"line":2228},[2225,126824,2232],{"class":2231},[2225,126826,2236],{"class":2235},[2225,126828,2744],{"class":2239},[2225,126830,2243],{"class":2235},[2225,126832,2246],{"class":2231},[2225,126834,2249],{"class":2235},[2225,126836,1086],{"class":2252},[2225,126838,2256],{"class":2235},[2225,126840,126841,126843,126845,126847,126849,126851,126853,126855],{"class":2227,"line":1041},[2225,126842,2232],{"class":2231},[2225,126844,2236],{"class":2235},[2225,126846,68576],{"class":2239},[2225,126848,2243],{"class":2235},[2225,126850,2246],{"class":2231},[2225,126852,2249],{"class":2235},[2225,126854,9638],{"class":2252},[2225,126856,2256],{"class":2235},[2225,126858,126859],{"class":2227,"line":1038},[2225,126860,2285],{"emptyLinePlaceholder":2284},[2225,126862,126863,126865,126867,126869,126871,126873,126875],{"class":2227,"line":2288},[2225,126864,2775],{"class":2774},[2225,126866,2778],{"class":2239},[2225,126868,2781],{"class":2235},[2225,126870,2784],{"class":2231},[2225,126872,2744],{"class":2291},[2225,126874,2789],{"class":2239},[2225,126876,4200],{"class":2235},[2225,126878,126879,126881,126883,126885,126887],{"class":2227,"line":2304},[2225,126880,15608],{"class":2307},[2225,126882,2311],{"class":2235},[2225,126884,68576],{"class":2291},[2225,126886,4228],{"class":2239},[2225,126888,1707],{"class":2235},[2225,126890,126891,126893,126895,126897,126899,126901],{"class":2227,"line":2356},[2225,126892,4205],{"class":2307},[2225,126894,2311],{"class":2235},[2225,126896,2249],{"class":2235},[2225,126898,6408],{"class":2252},[2225,126900,2805],{"class":2235},[2225,126902,1707],{"class":2235},[2225,126904,126905,126907,126909,126911,126913,126915],{"class":2227,"line":2830},[2225,126906,10423],{"class":2307},[2225,126908,2311],{"class":2235},[2225,126910,2249],{"class":2235},[2225,126912,6421],{"class":2252},[2225,126914,2805],{"class":2235},[2225,126916,1707],{"class":2235},[2225,126918,126919],{"class":2227,"line":2861},[2225,126920,126921],{"class":2763},"  \u002F\u002F shorthand — every field above, default caps:\n",[2225,126923,126924,126927,126929,126931],{"class":2227,"line":4246},[2225,126925,126926],{"class":2307},"  cover",[2225,126928,2311],{"class":2235},[2225,126930,2322],{"class":2321},[2225,126932,1707],{"class":2235},[2225,126934,126935],{"class":2227,"line":4253},[2225,126936,126937],{"class":2763},"  \u002F\u002F or narrow what the owner is allowed to set:\n",[2225,126939,126940],{"class":2227,"line":4259},[2225,126941,126942],{"class":2763},"  \u002F\u002F cover: { fields: ['name', 'icon'], maxIconBytes: 64 * 1024 },\n",[2225,126944,126945,126947],{"class":2227,"line":6548},[2225,126946,2359],{"class":2235},[2225,126948,2362],{"class":2239},[937,126950,126951,126952,126954,126955,1625,126957,126960,126961,126963,126964,6789,126966,115869,126968,957],{},"Off by default. A vault written by a hub without ",[941,126953,645],{}," set carries no envelope: ",[941,126956,13061],{},[941,126958,126959],{},"readCover"," on it return ",[941,126962,18316],{},", and calling ",[941,126965,13064],{},[941,126967,12881],{},[941,126969,1086],{},[1022,126971,29893],{"id":29892},[1591,126973,126975],{"id":126974},"owner-sets-the-label-a-caller-reads-it-back","Owner sets the label, a caller reads it back",[2217,126977,126979],{"className":2219,"code":126978,"language":2221,"meta":1037,"style":1037},"await db.openVault('acme')\n\nconst envelope = await db.setCover('acme', {\n  name: { en: 'Acme 2026 Tax Records', th: 'บันทึกภาษี Acme 2026' },\n  description: 'Q1–Q4 invoices, audit-ready.',\n  icon: 'data:image\u002Fsvg+xml;base64,PHN2ZyB4bWxucz0i…',\n  defaultLocale: 'en',\n})\n\u002F\u002F → { _noydb_public: 1, version: 1, name: { en: '…', th: '…' },\n\u002F\u002F     description: '…', icon: '…', defaultLocale: 'en',\n\u002F\u002F     createdAt: '2026-07-08T12:00:00.000Z', updatedAt: '2026-07-08T12:00:00.000Z' }\n\nconst label = await db.getCover('acme', { locale: 'th' })\n\u002F\u002F → { _noydb_public: 1, version: 1, name: 'บันทึกภาษี Acme 2026',\n\u002F\u002F     description: 'Q1–Q4 invoices, audit-ready.', icon: '…',\n\u002F\u002F     defaultLocale: 'en', createdAt: '…', updatedAt: '…' }\n",[941,126980,126981,127001,127005,127034,127068,127083,127099,127114,127120,127125,127130,127135,127139,127182,127187,127192],{"__ignoreMap":1037},[2225,126982,126983,126985,126987,126989,126991,126993,126995,126997,126999],{"class":2227,"line":2228},[2225,126984,6500],{"class":2231},[2225,126986,2842],{"class":2239},[2225,126988,1520],{"class":2235},[2225,126990,2847],{"class":2291},[2225,126992,2789],{"class":2239},[2225,126994,2805],{"class":2235},[2225,126996,6450],{"class":2252},[2225,126998,2805],{"class":2235},[2225,127000,2362],{"class":2239},[2225,127002,127003],{"class":2227,"line":1041},[2225,127004,2285],{"emptyLinePlaceholder":2284},[2225,127006,127007,127009,127012,127014,127016,127018,127020,127022,127024,127026,127028,127030,127032],{"class":2227,"line":1038},[2225,127008,2775],{"class":2774},[2225,127010,127011],{"class":2239}," envelope ",[2225,127013,2781],{"class":2235},[2225,127015,2784],{"class":2231},[2225,127017,2842],{"class":2239},[2225,127019,1520],{"class":2235},[2225,127021,13064],{"class":2291},[2225,127023,2789],{"class":2239},[2225,127025,2805],{"class":2235},[2225,127027,6450],{"class":2252},[2225,127029,2805],{"class":2235},[2225,127031,2298],{"class":2235},[2225,127033,2301],{"class":2235},[2225,127035,127036,127038,127040,127042,127044,127046,127048,127051,127053,127055,127057,127059,127061,127064,127066],{"class":2227,"line":2288},[2225,127037,7008],{"class":2307},[2225,127039,2311],{"class":2235},[2225,127041,2236],{"class":2235},[2225,127043,58997],{"class":2307},[2225,127045,2311],{"class":2235},[2225,127047,2249],{"class":2235},[2225,127049,127050],{"class":2252},"Acme 2026 Tax Records",[2225,127052,2805],{"class":2235},[2225,127054,2298],{"class":2235},[2225,127056,60621],{"class":2307},[2225,127058,2311],{"class":2235},[2225,127060,2249],{"class":2235},[2225,127062,127063],{"class":2252},"บันทึกภาษี Acme 2026",[2225,127065,2805],{"class":2235},[2225,127067,2353],{"class":2235},[2225,127069,127070,127072,127074,127076,127079,127081],{"class":2227,"line":2304},[2225,127071,58874],{"class":2307},[2225,127073,2311],{"class":2235},[2225,127075,2249],{"class":2235},[2225,127077,127078],{"class":2252},"Q1–Q4 invoices, audit-ready.",[2225,127080,2805],{"class":2235},[2225,127082,1707],{"class":2235},[2225,127084,127085,127088,127090,127092,127095,127097],{"class":2227,"line":2356},[2225,127086,127087],{"class":2307},"  icon",[2225,127089,2311],{"class":2235},[2225,127091,2249],{"class":2235},[2225,127093,127094],{"class":2252},"data:image\u002Fsvg+xml;base64,PHN2ZyB4bWxucz0i…",[2225,127096,2805],{"class":2235},[2225,127098,1707],{"class":2235},[2225,127100,127101,127104,127106,127108,127110,127112],{"class":2227,"line":2830},[2225,127102,127103],{"class":2307},"  defaultLocale",[2225,127105,2311],{"class":2235},[2225,127107,2249],{"class":2235},[2225,127109,59334],{"class":2252},[2225,127111,2805],{"class":2235},[2225,127113,1707],{"class":2235},[2225,127115,127116,127118],{"class":2227,"line":2861},[2225,127117,2359],{"class":2235},[2225,127119,2362],{"class":2239},[2225,127121,127122],{"class":2227,"line":4246},[2225,127123,127124],{"class":2763},"\u002F\u002F → { _noydb_public: 1, version: 1, name: { en: '…', th: '…' },\n",[2225,127126,127127],{"class":2227,"line":4253},[2225,127128,127129],{"class":2763},"\u002F\u002F     description: '…', icon: '…', defaultLocale: 'en',\n",[2225,127131,127132],{"class":2227,"line":4259},[2225,127133,127134],{"class":2763},"\u002F\u002F     createdAt: '2026-07-08T12:00:00.000Z', updatedAt: '2026-07-08T12:00:00.000Z' }\n",[2225,127136,127137],{"class":2227,"line":6548},[2225,127138,2285],{"emptyLinePlaceholder":2284},[2225,127140,127141,127143,127146,127148,127150,127152,127154,127156,127158,127160,127162,127164,127166,127168,127170,127172,127174,127176,127178,127180],{"class":2227,"line":6576},[2225,127142,2775],{"class":2774},[2225,127144,127145],{"class":2239}," label ",[2225,127147,2781],{"class":2235},[2225,127149,2784],{"class":2231},[2225,127151,2842],{"class":2239},[2225,127153,1520],{"class":2235},[2225,127155,13061],{"class":2291},[2225,127157,2789],{"class":2239},[2225,127159,2805],{"class":2235},[2225,127161,6450],{"class":2252},[2225,127163,2805],{"class":2235},[2225,127165,2298],{"class":2235},[2225,127167,2236],{"class":2235},[2225,127169,24003],{"class":2307},[2225,127171,2311],{"class":2235},[2225,127173,2249],{"class":2235},[2225,127175,1347],{"class":2252},[2225,127177,2805],{"class":2235},[2225,127179,2243],{"class":2235},[2225,127181,2362],{"class":2239},[2225,127183,127184],{"class":2227,"line":6582},[2225,127185,127186],{"class":2763},"\u002F\u002F → { _noydb_public: 1, version: 1, name: 'บันทึกภาษี Acme 2026',\n",[2225,127188,127189],{"class":2227,"line":6587},[2225,127190,127191],{"class":2763},"\u002F\u002F     description: 'Q1–Q4 invoices, audit-ready.', icon: '…',\n",[2225,127193,127194],{"class":2227,"line":7174},[2225,127195,127196],{"class":2763},"\u002F\u002F     defaultLocale: 'en', createdAt: '…', updatedAt: '…' }\n",[937,127198,127199,127200,127202,127203,1625,127205,127207,127208,127210],{},"Passing ",[941,127201,23074],{}," resolves ",[941,127204,4914],{},[941,127206,59777],{}," to plain strings for that locale; omitting ",[941,127209,23074],{}," returns the raw map so a multilingual picker can render every translation itself.",[1591,127212,127214],{"id":127213},"reading-without-unlocking-the-vault","Reading without unlocking the vault",[937,127216,127217,127218,127220,127221,127223],{},"The whole point of the envelope is that it works without a secret or an open ",[941,127219,62],{}," session — pass the same ",[941,127222,6951],{}," adapter directly:",[2217,127225,127227],{"className":2219,"code":127226,"language":2221,"meta":1037,"style":1037},"import { readCover } from '@noy-db\u002Fhub\u002Fcover'\n\nconst label = await readCover(store, 'acme', { locale: 'en' })\n\u002F\u002F → { name: 'Acme 2026 Tax Records', description: 'Q1–Q4 invoices, audit-ready.', … }\n\u002F\u002F or `undefined` if the vault has no envelope written\n",[941,127228,127229,127249,127253,127293,127298],{"__ignoreMap":1037},[2225,127230,127231,127233,127235,127238,127240,127242,127244,127247],{"class":2227,"line":2228},[2225,127232,2232],{"class":2231},[2225,127234,2236],{"class":2235},[2225,127236,127237],{"class":2239}," readCover",[2225,127239,2243],{"class":2235},[2225,127241,2246],{"class":2231},[2225,127243,2249],{"class":2235},[2225,127245,127246],{"class":2252},"@noy-db\u002Fhub\u002Fcover",[2225,127248,2256],{"class":2235},[2225,127250,127251],{"class":2227,"line":1041},[2225,127252,2285],{"emptyLinePlaceholder":2284},[2225,127254,127255,127257,127259,127261,127263,127265,127267,127269,127271,127273,127275,127277,127279,127281,127283,127285,127287,127289,127291],{"class":2227,"line":1038},[2225,127256,2775],{"class":2774},[2225,127258,127145],{"class":2239},[2225,127260,2781],{"class":2235},[2225,127262,2784],{"class":2231},[2225,127264,127237],{"class":2291},[2225,127266,39702],{"class":2239},[2225,127268,2298],{"class":2235},[2225,127270,2249],{"class":2235},[2225,127272,6450],{"class":2252},[2225,127274,2805],{"class":2235},[2225,127276,2298],{"class":2235},[2225,127278,2236],{"class":2235},[2225,127280,24003],{"class":2307},[2225,127282,2311],{"class":2235},[2225,127284,2249],{"class":2235},[2225,127286,59334],{"class":2252},[2225,127288,2805],{"class":2235},[2225,127290,2243],{"class":2235},[2225,127292,2362],{"class":2239},[2225,127294,127295],{"class":2227,"line":2288},[2225,127296,127297],{"class":2763},"\u002F\u002F → { name: 'Acme 2026 Tax Records', description: 'Q1–Q4 invoices, audit-ready.', … }\n",[2225,127299,127300],{"class":2227,"line":2304},[2225,127301,127302],{"class":2763},"\u002F\u002F or `undefined` if the vault has no envelope written\n",[1591,127304,127306,127307,83152],{"id":127305},"snapshotting-into-a-noydb-bundle","Snapshotting into a ",[941,127308,1402],{},[937,127310,127311,16238,127313,127315],{},[941,127312,31188],{},[1007,127314,629],{"href":629},") automatically snapshots the source vault's current envelope into the bundle's plaintext header, so a recipient can read the label before decrypting anything:",[2217,127317,127319],{"className":2219,"code":127318,"language":2221,"meta":1037,"style":1037},"import { writePod, readPod, readPodHeader } from '@noy-db\u002Fhub\u002Fpod'\nimport { readPodCover } from '@noy-db\u002Fhub'\n\nconst bytes = await writePod(vault)\n\n\u002F\u002F Pre-decompression, pre-decryption read — cheap, synchronous:\nconst header = readPodHeader(bytes)\nheader.publicEnvelope\n\u002F\u002F → { name: '…', description: '…', … } or undefined\n\u002F\u002F The field keeps its frozen wire name `publicEnvelope` (#799), not `cover`.\n\n\u002F\u002F Equivalent helper when you only care about the label:\nreadPodCover(bytes, { locale: 'en' })\n\u002F\u002F → { name: 'Acme 2026 Tax Records', description: 'Q1–Q4 invoices, audit-ready.', … }\n",[941,127320,127321,127347,127366,127370,127384,127388,127393,127405,127414,127419,127424,127428,127433,127459],{"__ignoreMap":1037},[2225,127322,127323,127325,127327,127329,127331,127333,127335,127337,127339,127341,127343,127345],{"class":2227,"line":2228},[2225,127324,2232],{"class":2231},[2225,127326,2236],{"class":2235},[2225,127328,70188],{"class":2239},[2225,127330,2298],{"class":2235},[2225,127332,70193],{"class":2239},[2225,127334,2298],{"class":2235},[2225,127336,114956],{"class":2239},[2225,127338,2243],{"class":2235},[2225,127340,2246],{"class":2231},[2225,127342,2249],{"class":2235},[2225,127344,70036],{"class":2252},[2225,127346,2256],{"class":2235},[2225,127348,127349,127351,127353,127356,127358,127360,127362,127364],{"class":2227,"line":1041},[2225,127350,2232],{"class":2231},[2225,127352,2236],{"class":2235},[2225,127354,127355],{"class":2239}," readPodCover",[2225,127357,2243],{"class":2235},[2225,127359,2246],{"class":2231},[2225,127361,2249],{"class":2235},[2225,127363,1086],{"class":2252},[2225,127365,2256],{"class":2235},[2225,127367,127368],{"class":2227,"line":1038},[2225,127369,2285],{"emptyLinePlaceholder":2284},[2225,127371,127372,127374,127376,127378,127380,127382],{"class":2227,"line":2288},[2225,127373,2775],{"class":2774},[2225,127375,32469],{"class":2239},[2225,127377,2781],{"class":2235},[2225,127379,2784],{"class":2231},[2225,127381,70188],{"class":2291},[2225,127383,45516],{"class":2239},[2225,127385,127386],{"class":2227,"line":2304},[2225,127387,2285],{"emptyLinePlaceholder":2284},[2225,127389,127390],{"class":2227,"line":2356},[2225,127391,127392],{"class":2763},"\u002F\u002F Pre-decompression, pre-decryption read — cheap, synchronous:\n",[2225,127394,127395,127397,127399,127401,127403],{"class":2227,"line":2830},[2225,127396,2775],{"class":2774},[2225,127398,32122],{"class":2239},[2225,127400,2781],{"class":2235},[2225,127402,114956],{"class":2291},[2225,127404,71461],{"class":2239},[2225,127406,127407,127409,127411],{"class":2227,"line":2861},[2225,127408,116552],{"class":2239},[2225,127410,1520],{"class":2235},[2225,127412,127413],{"class":2239},"publicEnvelope\n",[2225,127415,127416],{"class":2227,"line":4246},[2225,127417,127418],{"class":2763},"\u002F\u002F → { name: '…', description: '…', … } or undefined\n",[2225,127420,127421],{"class":2227,"line":4253},[2225,127422,127423],{"class":2763},"\u002F\u002F The field keeps its frozen wire name `publicEnvelope` (#799), not `cover`.\n",[2225,127425,127426],{"class":2227,"line":4259},[2225,127427,2285],{"emptyLinePlaceholder":2284},[2225,127429,127430],{"class":2227,"line":6548},[2225,127431,127432],{"class":2763},"\u002F\u002F Equivalent helper when you only care about the label:\n",[2225,127434,127435,127438,127441,127443,127445,127447,127449,127451,127453,127455,127457],{"class":2227,"line":6576},[2225,127436,127437],{"class":2291},"readPodCover",[2225,127439,127440],{"class":2239},"(bytes",[2225,127442,2298],{"class":2235},[2225,127444,2236],{"class":2235},[2225,127446,24003],{"class":2307},[2225,127448,2311],{"class":2235},[2225,127450,2249],{"class":2235},[2225,127452,59334],{"class":2252},[2225,127454,2805],{"class":2235},[2225,127456,2243],{"class":2235},[2225,127458,2362],{"class":2239},[2225,127460,127461],{"class":2227,"line":6582},[2225,127462,127297],{"class":2763},[937,127464,127465,127466,127469,127470,127472,127473,964,127476,127478,127479,127482,127483,127485,127486,127488],{},"Whether ",[941,127467,127468],{},"_meta\u002Fcover"," survives ",[941,127471,116011],{}," on the destination vault is ",[950,127474,127475],{},"store-dependent",[941,127477,6820],{}," reads through ",[941,127480,127481],{},"adapter.loadAll(name)",", and individual store adapters choose whether to include underscore-prefixed internal collections in that bulk read. The bundle header snapshot is the portable path: after loading, re-attach the label explicitly from the header (the destination vault needs ",[941,127484,645],{}," enabled in its own ",[941,127487,36470],{}," for this to succeed):",[2217,127490,127492],{"className":2219,"code":127491,"language":2221,"meta":1037,"style":1037},"const result = await readPod(bytes)\nawait vault.load(result.dumpJson)\n\nif (result.header.publicEnvelope) {\n  const { name, description, icon, defaultLocale } = result.header.publicEnvelope\n  await db.setCover(vault.name, {\n    ...(name !== undefined ? { name } : {}),\n    ...(description !== undefined ? { description } : {}),\n    ...(icon !== undefined ? { icon } : {}),\n    ...(defaultLocale !== undefined ? { defaultLocale } : {}),\n  })\n}\n",[941,127493,127494,127508,127526,127530,127547,127583,127605,127633,127661,127689,127717,127723],{"__ignoreMap":1037},[2225,127495,127496,127498,127500,127502,127504,127506],{"class":2227,"line":2228},[2225,127497,2775],{"class":2774},[2225,127499,36920],{"class":2239},[2225,127501,2781],{"class":2235},[2225,127503,2784],{"class":2231},[2225,127505,70193],{"class":2291},[2225,127507,71461],{"class":2239},[2225,127509,127510,127512,127514,127516,127518,127521,127523],{"class":2227,"line":1041},[2225,127511,6500],{"class":2231},[2225,127513,6466],{"class":2239},[2225,127515,1520],{"class":2235},[2225,127517,22905],{"class":2291},[2225,127519,127520],{"class":2239},"(result",[2225,127522,1520],{"class":2235},[2225,127524,127525],{"class":2239},"dumpJson)\n",[2225,127527,127528],{"class":2227,"line":1038},[2225,127529,2285],{"emptyLinePlaceholder":2284},[2225,127531,127532,127534,127536,127538,127540,127542,127545],{"class":2227,"line":2288},[2225,127533,35129],{"class":2231},[2225,127535,43840],{"class":2239},[2225,127537,1520],{"class":2235},[2225,127539,116552],{"class":2239},[2225,127541,1520],{"class":2235},[2225,127543,127544],{"class":2239},"publicEnvelope) ",[2225,127546,4200],{"class":2235},[2225,127548,127549,127551,127553,127555,127557,127559,127561,127564,127566,127569,127571,127573,127575,127577,127579,127581],{"class":2227,"line":2304},[2225,127550,8123],{"class":2774},[2225,127552,2236],{"class":2235},[2225,127554,8104],{"class":2239},[2225,127556,2298],{"class":2235},[2225,127558,19510],{"class":2239},[2225,127560,2298],{"class":2235},[2225,127562,127563],{"class":2239}," icon",[2225,127565,2298],{"class":2235},[2225,127567,127568],{"class":2239}," defaultLocale",[2225,127570,2243],{"class":2235},[2225,127572,6346],{"class":2235},[2225,127574,40906],{"class":2239},[2225,127576,1520],{"class":2235},[2225,127578,116552],{"class":2239},[2225,127580,1520],{"class":2235},[2225,127582,127413],{"class":2239},[2225,127584,127585,127587,127589,127591,127593,127595,127597,127599,127601,127603],{"class":2227,"line":2356},[2225,127586,7561],{"class":2231},[2225,127588,2842],{"class":2239},[2225,127590,1520],{"class":2235},[2225,127592,13064],{"class":2291},[2225,127594,2789],{"class":2307},[2225,127596,2694],{"class":2239},[2225,127598,1520],{"class":2235},[2225,127600,4914],{"class":2239},[2225,127602,2298],{"class":2235},[2225,127604,2301],{"class":2235},[2225,127606,127607,127609,127611,127613,127615,127617,127619,127621,127623,127625,127627,127629,127631],{"class":2227,"line":2830},[2225,127608,18908],{"class":2235},[2225,127610,2789],{"class":2307},[2225,127612,4914],{"class":2239},[2225,127614,8480],{"class":2235},[2225,127616,8483],{"class":2235},[2225,127618,40953],{"class":2235},[2225,127620,2236],{"class":2235},[2225,127622,8104],{"class":2239},[2225,127624,2243],{"class":2235},[2225,127626,40959],{"class":2235},[2225,127628,8896],{"class":2235},[2225,127630,1381],{"class":2307},[2225,127632,1707],{"class":2235},[2225,127634,127635,127637,127639,127641,127643,127645,127647,127649,127651,127653,127655,127657,127659],{"class":2227,"line":2861},[2225,127636,18908],{"class":2235},[2225,127638,2789],{"class":2307},[2225,127640,59777],{"class":2239},[2225,127642,8480],{"class":2235},[2225,127644,8483],{"class":2235},[2225,127646,40953],{"class":2235},[2225,127648,2236],{"class":2235},[2225,127650,19510],{"class":2239},[2225,127652,2243],{"class":2235},[2225,127654,40959],{"class":2235},[2225,127656,8896],{"class":2235},[2225,127658,1381],{"class":2307},[2225,127660,1707],{"class":2235},[2225,127662,127663,127665,127667,127669,127671,127673,127675,127677,127679,127681,127683,127685,127687],{"class":2227,"line":4246},[2225,127664,18908],{"class":2235},[2225,127666,2789],{"class":2307},[2225,127668,126699],{"class":2239},[2225,127670,8480],{"class":2235},[2225,127672,8483],{"class":2235},[2225,127674,40953],{"class":2235},[2225,127676,2236],{"class":2235},[2225,127678,127563],{"class":2239},[2225,127680,2243],{"class":2235},[2225,127682,40959],{"class":2235},[2225,127684,8896],{"class":2235},[2225,127686,1381],{"class":2307},[2225,127688,1707],{"class":2235},[2225,127690,127691,127693,127695,127697,127699,127701,127703,127705,127707,127709,127711,127713,127715],{"class":2227,"line":4253},[2225,127692,18908],{"class":2235},[2225,127694,2789],{"class":2307},[2225,127696,126765],{"class":2239},[2225,127698,8480],{"class":2235},[2225,127700,8483],{"class":2235},[2225,127702,40953],{"class":2235},[2225,127704,2236],{"class":2235},[2225,127706,127568],{"class":2239},[2225,127708,2243],{"class":2235},[2225,127710,40959],{"class":2235},[2225,127712,8896],{"class":2235},[2225,127714,1381],{"class":2307},[2225,127716,1707],{"class":2235},[2225,127718,127719,127721],{"class":2227,"line":4259},[2225,127720,19143],{"class":2235},[2225,127722,2362],{"class":2307},[2225,127724,127725],{"class":2227,"line":6548},[2225,127726,7351],{"class":2235},[1022,127728,127730],{"id":127729},"i18n-integration","i18n integration",[937,127732,127733,997,127735,127737],{},[941,127734,4914],{},[941,127736,59777],{}," accept either a plain string (single-locale) or a locale → string map:",[2217,127739,127741],{"className":2219,"code":127740,"language":2221,"meta":1037,"style":1037},"\u002F\u002F Single-locale\n{ name: 'Acme 2026' }\n\n\u002F\u002F Multi-locale\n{ name: { en: 'Acme 2026', th: 'Acme 2026 (TH)', de: 'Acme 2026 (DE)' } }\n",[941,127742,127743,127748,127765,127769,127774],{"__ignoreMap":1037},[2225,127744,127745],{"class":2227,"line":2228},[2225,127746,127747],{"class":2763},"\u002F\u002F Single-locale\n",[2225,127749,127750,127752,127754,127756,127758,127761,127763],{"class":2227,"line":1041},[2225,127751,2792],{"class":2235},[2225,127753,8104],{"class":6342},[2225,127755,2311],{"class":2235},[2225,127757,2249],{"class":2235},[2225,127759,127760],{"class":2252},"Acme 2026",[2225,127762,2805],{"class":2235},[2225,127764,6369],{"class":2235},[2225,127766,127767],{"class":2227,"line":1038},[2225,127768,2285],{"emptyLinePlaceholder":2284},[2225,127770,127771],{"class":2227,"line":2288},[2225,127772,127773],{"class":2763},"\u002F\u002F Multi-locale\n",[2225,127775,127776,127778,127780,127782,127784,127786,127788,127790,127792,127794,127796,127798,127800,127802,127805,127807,127809,127812,127814,127816,127819,127821,127823],{"class":2227,"line":2304},[2225,127777,2792],{"class":2235},[2225,127779,8104],{"class":6342},[2225,127781,2311],{"class":2235},[2225,127783,2236],{"class":2235},[2225,127785,58997],{"class":6342},[2225,127787,2311],{"class":2235},[2225,127789,2249],{"class":2235},[2225,127791,127760],{"class":2252},[2225,127793,2805],{"class":2235},[2225,127795,2298],{"class":2235},[2225,127797,60621],{"class":6342},[2225,127799,2311],{"class":2235},[2225,127801,2249],{"class":2235},[2225,127803,127804],{"class":2252},"Acme 2026 (TH)",[2225,127806,2805],{"class":2235},[2225,127808,2298],{"class":2235},[2225,127810,127811],{"class":6342}," de",[2225,127813,2311],{"class":2235},[2225,127815,2249],{"class":2235},[2225,127817,127818],{"class":2252},"Acme 2026 (DE)",[2225,127820,2805],{"class":2235},[2225,127822,2243],{"class":2235},[2225,127824,6369],{"class":2235},[937,127826,127827,127828,1625,127830,1625,127832,127834,127835,127837],{},"On the read path, ",[941,127829,126959],{},[941,127831,13061],{},[941,127833,127437],{}," all accept a ",[941,127836,23074],{}," option and resolve through the envelope's own fallback chain:",[1234,127839,127840,127846,127852,127855],{},[1114,127841,127842,127843,127845],{},"Exact ",[941,127844,23074],{}," match.",[1114,127847,127848,127849,127851],{},"The envelope's ",[941,127850,126765],{}," (when set).",[1114,127853,127854],{},"First non-empty translation in the map.",[1114,127856,127857],{},"Empty string (only if every translation is empty).",[937,127859,127860,127861,127866,127867,6789,127869,127872,127873,127876],{},"This is a narrower, purpose-built resolver — not the hub's general-purpose ",[1007,127862,127863],{"href":701},[941,127864,127865],{},"resolveI18nText"," used for record fields. ",[941,127868,127865],{},[941,127870,127871],{},"LocaleNotSpecifiedError"," when no translation matches, because a missing record value is treated as a programming error worth surfacing. Label semantics are looser: a vault picker prefers showing ",[1092,127874,127875],{},"something"," identifiable over a thrown error, so the envelope's resolver never throws.",[937,127878,127879,127880,127883],{},"The schema's ",[941,127881,127882],{},"maxStringChars"," cap (default 200) applies per resolved locale — each translation in a map is bounded independently, not the JSON-stringified union.",[1022,127885,102889],{"id":102888},[937,127887,127888,127889,1520],{},"All names below are exported from ",[941,127890,1086],{},[1591,127892,127894,127896],{"id":127893},"noydb-methods",[941,127895,62],{}," methods",[1111,127898,127899,127930],{},[1114,127900,127901,1746,127904,127907,127908,127910,127911,127913,127914,1625,127916,127918,127919,102958,127921,127923,127924,127926,127927,127929],{},[941,127902,127903],{},"db.setCover(vault, input)",[941,127905,127906],{},"Promise\u003CCover>"," — validate ",[941,127909,16450],{}," against the vault's resolved schema and persist it, merging onto any existing envelope (",[941,127912,87098],{}," preserved, ",[941,127915,126738],{},[941,127917,52564],{}," bumped). Throws ",[941,127920,12881],{},[941,127922,645],{}," isn't enabled in ",[941,127925,36470],{},", or if ",[941,127928,16450],{}," violates the schema (unknown field, oversize icon\u002Fstring, disallowed MIME type).",[1114,127931,127932,1746,127935,127938,127939,127941,127942,127944,127945,127947],{},[941,127933,127934],{},"db.getCover(vault, opts?)",[941,127936,127937],{},"Promise\u003CCover | undefined>"," — read the envelope. Works even when ",[941,127940,645],{}," is not enabled on this hub instance (reads are passive); returns ",[941,127943,18316],{}," when nothing has been written. ",[941,127946,11777],{}," resolves locale-map fields to plain strings.",[1591,127949,127951],{"id":127950},"vaultgetcoveropts",[941,127952,127953],{},"vault.getCover(opts?)",[1111,127955,127956],{},[1114,127957,127958,1746,127960,127962,127963,127966,127967,127969,127970,127972],{},[941,127959,127953],{},[941,127961,127937],{}," — same read as ",[941,127964,127965],{},"db.getCover(vault.name, opts)",", scoped to an already-open ",[941,127968,94],{}," instance. This is what ",[941,127971,31188],{}," calls internally to snapshot the envelope into a bundle header.",[1591,127974,127976],{"id":127975},"standalone-functions-no-unlock-required","Standalone functions (no unlock required)",[1111,127978,127979,127998,128015,128036,128051],{},[1114,127980,127981,1746,127984,127986,127987,1625,127989,127991,127992,127994,127995,1520],{},[941,127982,127983],{},"readCover(store, vault, opts?)",[941,127985,127937],{}," — read directly from a store adapter, no ",[941,127988,62],{},[941,127990,94],{}," instance needed. Resolves ",[941,127993,11777],{}," when given; built on top of ",[941,127996,127997],{},"loadCover",[1114,127999,128000,1746,128003,128005,128006,128008,128009,128011,128012,128014],{},[941,128001,128002],{},"loadCover(store, vault)",[941,128004,127937],{}," — the lower-level read ",[941,128007,126959],{}," is built on: fetches ",[941,128010,127468],{}," from the store and parses it, with no locale resolution. Returns ",[941,128013,18316],{}," for a fresh vault or a corrupted document.",[1114,128016,128017,1746,128020,128022,128023,1625,128026,128029,128030,128032,128033,128035],{},[941,128018,128019],{},"saveCover(store, vault, envelope)",[941,128021,116014],{}," — the lower-level write ",[941,128024,128025],{},"db.setCover",[941,128027,128028],{},"vault.getCover","'s write path is built on: persists a full ",[941,128031,116695],{}," record to ",[941,128034,127468],{}," directly, with no schema validation or field merging.",[1114,128037,128038,1746,128041,128044,128045,128047,128048,128050],{},[941,128039,128040],{},"readPodCover(bytes, opts?)",[941,128042,128043],{},"Cover | undefined"," — read the envelope out of raw ",[941,128046,1402],{}," bundle bytes, without parsing the body. ",[941,128049,11777],{}," behaves the same as above.",[1114,128052,128053,1746,128056,128058,128059,997,128062,128064],{},[941,128054,128055],{},"isCover(x)",[941,128057,16861],{}," — runtime shape check (",[941,128060,128061],{},"_noydb_public === 1",[941,128063,52564],{}," is a number).",[1591,128066,128068],{"id":128067},"schema-helpers","Schema helpers",[1111,128070,128071,128094,128108,128126],{},[1114,128072,128073,1746,128076,128079,128080,128083,128084,128086,128087,128090,128091,128093],{},[941,128074,128075],{},"resolveCoverSchema(schema)",[941,128077,128078],{},"ResolvedCoverSchema | undefined"," — merge a developer-supplied ",[941,128081,128082],{},"CoverSchema"," (or the ",[941,128085,11790],{}," shorthand) onto ",[941,128088,128089],{},"DEFAULT_COVER_SCHEMA",". Returns ",[941,128092,18316],{}," when the feature is off.",[1114,128095,128096,1746,128099,128101,128102,128104,128105,128107],{},[941,128097,128098],{},"validateCoverInput(input, schema)",[941,128100,7165],{}," — the validator ",[941,128103,13064],{}," runs internally; throws ",[941,128106,12881],{}," on the first violation.",[1114,128109,128110,128113,128114,968,128116,968,128118,968,128120,968,128122,968,128124,1520],{},[941,128111,128112],{},"COVER_FIELDS"," — the full list of settable field names: ",[941,128115,4914],{},[941,128117,59777],{},[941,128119,126699],{},[941,128121,87098],{},[941,128123,126738],{},[941,128125,126765],{},[1114,128127,128128,964,128130,1520],{},[941,128129,128089],{},[941,128131,128132],{},"{ fields: COVER_FIELDS, maxIconBytes: 256 * 1024, iconMimeTypes: ['image\u002Fpng', 'image\u002Fsvg+xml'], maxStringChars: 200 }",[1591,128134,103205],{"id":128135},"types",[1111,128137,128138,128149,128161,128169,128188],{},[1114,128139,128140,128142,128143,968,128146,128148],{},[941,128141,116695],{}," — the persisted shape (",[941,128144,128145],{},"_noydb_public: 1",[941,128147,52564],{},", plus the optional fields from the table above).",[1114,128150,128151,964,128154,128156,128157,1625,128159,1520],{},[941,128152,128153],{},"CoverText",[941,128155,126670],{},", the type of ",[941,128158,4914],{},[941,128160,59777],{},[1114,128162,128163,1261,128165,128168],{},[941,128164,128082],{},[941,128166,128167],{},"ResolvedCoverSchema"," — the developer-supplied and resolved-with-defaults schema shapes.",[1114,128170,128171,128174,128175,128177,128178,953,128180,968,128182,968,128184,968,128186,957],{},[941,128172,128173],{},"SetCoverInput"," — the subset of ",[941,128176,116695],{}," an owner can pass to ",[941,128179,13064],{},[941,128181,4914],{},[941,128183,59777],{},[941,128185,126699],{},[941,128187,126765],{},[1114,128189,128190,128193,128194,11737,128197,128200,128201,1520],{},[941,128191,128192],{},"CoverField"," — the union of settable field names (",[941,128195,128196],{},"typeof COVER_FIELDS[number]",[941,128198,128199],{},"'name' | 'description' | 'icon' | 'createdAt' | 'updatedAt' | 'defaultLocale'",". This is the element type of ",[941,128202,128203],{},"CoverSchema.fields",[1591,128205,128207],{"id":128206},"storage-layout","Storage layout",[1338,128209,128210,128221],{},[1341,128211,128212],{},[1344,128213,128214,128216,128218],{},[1347,128215,4295],{},[1347,128217,55509],{},[1347,128219,128220],{},"Format",[1357,128222,128223,128255],{},[1344,128224,128225,128228,128239],{},[1362,128226,128227],{},"Vault, on the store",[1362,128229,128230,128232,128233,968,128236,1381],{},[941,128231,127468],{}," (the record id is exported as ",[941,128234,128235],{},"COVER_RECORD_ID",[941,128237,128238],{},"'cover'",[1362,128240,128241,128242,968,128245,128247,128248,128250,128251,997,128253,1520],{},"Plaintext envelope (",[941,128243,128244],{},"_iv: ''",[941,128246,1146],{}," is the JSON-serialized ",[941,128249,116695],{},") — the same bypass-encryption pattern used by ",[941,128252,116026],{},[941,128254,12784],{},[1344,128256,128257,128262,128267],{},[1362,128258,128259,128261],{},[941,128260,1402],{}," bundle header",[1362,128263,128264],{},[941,128265,128266],{},"header.cover",[1362,128268,128269],{},"Inline JSON object inside the bundle's plaintext, minimum-disclosure header.",[937,128271,128272,128273,1261,128275,1261,128277,1261,128279,5502,128281,128283,128284,128286],{},"The bundle header's allowlist normally carries only ",[941,128274,36353],{},[941,128276,36368],{},[941,128278,36388],{},[941,128280,36404],{},[941,128282,645],{}," is one of a small set of additional, individually-validated keys the parser accepts. Every other unknown key is still rejected at parse time — see ",[1007,128285,629],{"href":629}," for the full allowlist.",[1022,128288,68210],{"id":68209},[937,128290,128291,128293],{},[950,128292,463],{}," — the envelope is a plaintext, untrusted hint, not a signed claim:",[1111,128295,128296,128306,128315,128334,128340,128352],{},[1114,128297,128298,128301,128302,128305],{},[950,128299,128300],{},"Plaintext, untrusted hint."," A store adapter (or anyone with write access to the underlying storage) can modify the envelope. Treat it like an HTTP ",[941,128303,128304],{},"Server:"," header — it helps the user, it is never a security claim. Don't mark the label \"verified\" or \"owner-signed\" in a renderer.",[1114,128307,128308,128311,128312,128314],{},[950,128309,128310],{},"Off by default."," Vaults written by a hub without ",[941,128313,645],{}," enabled carry no envelope at all.",[1114,128316,128317,128320,128321,997,128323,128325,128326,28806,128328,128330,128331,128333],{},[950,128318,128319],{},"Schema-bounded."," If a developer's schema only lists ",[941,128322,4914],{},[941,128324,126699],{},", an owner calling ",[941,128327,13064],{},[941,128329,59777],{}," gets a ",[941,128332,12881],{}," — the allowed-field list caps what can leak.",[1114,128335,128336,128339],{},[950,128337,128338],{},"Existence does not confirm contents."," The envelope can say \"Acme Tax Records\" while the vault body is empty or holds something else entirely; a first-contact UI should not treat the label as a promise about what's inside.",[1114,128341,128342,128345,128346,968,128348,128351],{},[950,128343,128344],{},"Icon surface."," The default 256 KB cap on the encoded data URL (≈ 192 KB decoded) is generous enough for a logo but bounded against smuggling large payloads. The MIME allowlist (",[941,128347,30010],{},[941,128349,128350],{},"image\u002Fsvg+xml",") is checked on write only — SVGs are not sanitized for embedded scripts at the storage layer, so a renderer must treat the icon as untrusted DOM input, not inline it unsanitized.",[1114,128353,128354,14636,128357,128359,128360,128362,128363,128365],{},[950,128355,128356],{},"No new class of leakage.",[941,128358,1402],{}," bundle in cloud storage already exposes its size and ",[941,128361,36404],{}," to anyone who can list the file. ",[941,128364,645],{}," adds owner-curated label fields to that same, already-visible surface — the encrypted body itself remains the source of truth.",[1022,128367,2621],{"id":2620},[1111,128369,128370,128377,128387],{},[1114,128371,128372,1169,128374,128376],{},[1007,128373,629],{"href":629},[941,128375,1402],{}," container format whose header carries the envelope snapshot.",[1114,128378,128379,128381,128382,1625,128384,128386],{},[1007,128380,701],{"href":701}," — the record-level ",[941,128383,20943],{},[941,128385,127865],{}," machinery the envelope's own locale resolver deliberately departs from.",[1114,128388,128389,128391],{},[1007,128390,844],{"href":844}," — the tier-1\u002F2\u002F3 unlock model the envelope sits below (it is readable before tier 1).",[2645,128393,128394],{},"html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html pre.shiki code .sfNiH, html code.shiki .sfNiH{--shiki-light:#FF5370;--shiki-default:#FF9CAC;--shiki-dark:#FF9CAC}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}",{"title":1037,"searchDepth":1038,"depth":1038,"links":128396},[128397,128398,128399,128400,128401,128407,128408,128417,128418],{"id":6241,"depth":1041,"text":6242},{"id":126641,"depth":1041,"text":126642},{"id":126794,"depth":1041,"text":126795},{"id":58393,"depth":1041,"text":58394},{"id":29892,"depth":1041,"text":29893,"children":128402},[128403,128404,128405],{"id":126974,"depth":1038,"text":126975},{"id":127213,"depth":1038,"text":127214},{"id":127305,"depth":1038,"text":128406},"Snapshotting into a .noydb bundle",{"id":127729,"depth":1041,"text":127730},{"id":102888,"depth":1041,"text":102889,"children":128409},[128410,128412,128413,128414,128415,128416],{"id":127893,"depth":1038,"text":128411},"Noydb methods",{"id":127950,"depth":1038,"text":127953},{"id":127975,"depth":1038,"text":127976},{"id":128067,"depth":1038,"text":128068},{"id":128135,"depth":1038,"text":103205},{"id":128206,"depth":1038,"text":128207},{"id":68209,"depth":1041,"text":68210},{"id":2620,"depth":1041,"text":2621},"Owner-curated plaintext metadata (name, description, icon) that stays readable before vault unlock or bundle decryption.",{},{"title":645,"description":128419},"uJda4UmN3FsxD5S2675AFQ9cAmIo1ZABNgE8hK4oU2U",{"id":128424,"title":649,"access":12,"apiToc":929,"body":128425,"category":1042,"description":129047,"extension":1044,"meta":129048,"navigation":1042,"nest":1047,"path":650,"seo":129049,"sinceVersion":1042,"status":1049,"stem":651,"subtitle":1042,"tier":1050,"tocExternal":1042,"__hash__":129050},"docs\u002Fservices\u002Fcoverage.md",{"type":931,"value":128426,"toc":129036},[128427,128429,128432,128441,128452,128495,128502,128504,128511,128518,128539,128542,128544,128572,128576,128685,128706,128718,128722,128765,128776,128784,128800,128810,128814,128822,128942,128950,128986,128990,129012,129014,129033],[934,128428,649],{"id":649},[46871,128430],{":rows":128431},"[{\"label\":\"Subpath\",\"value\":\"`@noy-db\u002Fhub\u002Fcoverage`\"},{\"label\":\"Factory\",\"value\":\"`withCoverage()` → `coverageStrategy`\"},{\"label\":\"Cluster\",\"value\":\"Audit\"}]",[1022,128433,128435,128436],{"id":128434},"️-not-the-same-thing-as-cover","⚠️ Not the same thing as ",[1007,128437,128439],{"href":128438},".\u002Fcover",[941,128440,645],{},[937,128442,128443,128444,1324,128446,997,128448,128451],{},"Hub publishes ",[950,128445,31219],{},[941,128447,127246],{},[941,128449,128450],{},"@noy-db\u002Fhub\u002Fcoverage",", and they\nare unrelated:",[1338,128453,128454,128464],{},[1341,128455,128456],{},[1344,128457,128458,128461],{},[1347,128459,128460],{},"subpath",[1347,128462,128463],{},"what it is",[1357,128465,128466,128482],{},[1344,128467,128468,128475],{},[1362,128469,128470],{},[1007,128471,128472],{"href":128438},[941,128473,128474],{},"\u002Fcover",[1362,128476,128477,128478,128481],{},"owner-curated ",[950,128479,128480],{},"plaintext metadata"," — name, description, icon — readable before unlock",[1344,128483,128484,128489],{},[1362,128485,128486],{},[941,128487,128488],{},"\u002Fcoverage",[1362,128490,128491,128494],{},[950,128492,128493],{},"read accounting"," — how much of a collection a principal has decrypted",[937,128496,128497,128498,1520],{},"The collision is named deliberately rather than renamed away, because renaming a\npublished subpath is a breaking change for everyone already importing it. If you\narrived here looking for the pre-unlock labels on a vault, you want\n",[1007,128499,128500],{"href":128438},[941,128501,645],{},[1022,128503,29857],{"id":29856},[937,128505,128506,128507,128510],{},"Answers a question no access check can: ",[950,128508,128509],{},"this principal is allowed to read the\ncollection — how much of it have they actually read?"," A viewer entitled to any\nsingle record is still worth noticing when they have decrypted 90% of the corpus.",[937,128512,128513,128514,128517],{},"Accounting happens at the ",[950,128515,128516],{},"decrypt point",", and the state is sketches rather\nthan a log:",[1111,128519,128520,128529],{},[1114,128521,5983,128522,113199,128525,128528],{},[950,128523,128524],{},"HyperLogLog",[941,128526,128527],{},"(principal, vault, collection)"," estimates distinct\nrecords ever decrypted — the coverage horizon, which never resets;",[1114,128530,5983,128531,128534,128535,128538],{},[950,128532,128533],{},"Bloom filter"," per window estimates records ",[1092,128536,128537],{},"novel"," to that window.",[937,128540,128541],{},"⭐ Both under-report by construction and never over-report. That direction is the\npoint: an alert that fires late is a missed signal, while one that fires on\nphantom reads destroys trust in the alert.",[1022,128543,70093],{"id":70092},[1111,128545,128546,128559,128569],{},[1114,128547,128548,128554,128555,128558],{},[950,128549,3289,128550,128553],{},[941,128551,128552],{},"corpusSize"," per collection you want a fraction for"," — coverage is\n",[941,128556,128557],{},"distinct \u002F corpusSize"," and the size is declared, not counted (see below). A\ncollection without one still accumulates counts but can never cross a threshold.",[1114,128560,128561,128564,128565,128568],{},[950,128562,128563],{},"Somewhere for the event to go"," — the signal is a ",[941,128566,128567],{},"coverage:threshold","\nemission, so it is only useful if something is listening.",[1114,128570,128571],{},"Nothing else. The sensor never reads the store.",[1022,128573,128575],{"id":128574},"opting-in","Opting in",[2217,128577,128579],{"className":2219,"code":128578,"language":2221,"meta":1037,"style":1037},"import { createNoydb } from '@noy-db\u002Fhub'\nimport { withCoverage } from '@noy-db\u002Fhub\u002Fcoverage'\n\nconst db = await createNoydb({\n  user: 'alice',\n  secret: 'a-real-passphrase-2026',\n  coverageStrategy: withCoverage(),\n})\n",[941,128580,128581,128599,128618,128622,128638,128652,128666,128679],{"__ignoreMap":1037},[2225,128582,128583,128585,128587,128589,128591,128593,128595,128597],{"class":2227,"line":2228},[2225,128584,2232],{"class":2231},[2225,128586,2236],{"class":2235},[2225,128588,2744],{"class":2239},[2225,128590,2243],{"class":2235},[2225,128592,2246],{"class":2231},[2225,128594,2249],{"class":2235},[2225,128596,1086],{"class":2252},[2225,128598,2256],{"class":2235},[2225,128600,128601,128603,128605,128608,128610,128612,128614,128616],{"class":2227,"line":1041},[2225,128602,2232],{"class":2231},[2225,128604,2236],{"class":2235},[2225,128606,128607],{"class":2239}," withCoverage",[2225,128609,2243],{"class":2235},[2225,128611,2246],{"class":2231},[2225,128613,2249],{"class":2235},[2225,128615,128450],{"class":2252},[2225,128617,2256],{"class":2235},[2225,128619,128620],{"class":2227,"line":1038},[2225,128621,2285],{"emptyLinePlaceholder":2284},[2225,128623,128624,128626,128628,128630,128632,128634,128636],{"class":2227,"line":2288},[2225,128625,2775],{"class":2774},[2225,128627,2778],{"class":2239},[2225,128629,2781],{"class":2235},[2225,128631,2784],{"class":2231},[2225,128633,2744],{"class":2291},[2225,128635,2789],{"class":2239},[2225,128637,4200],{"class":2235},[2225,128639,128640,128642,128644,128646,128648,128650],{"class":2227,"line":2304},[2225,128641,4205],{"class":2307},[2225,128643,2311],{"class":2235},[2225,128645,2249],{"class":2235},[2225,128647,6408],{"class":2252},[2225,128649,2805],{"class":2235},[2225,128651,1707],{"class":2235},[2225,128653,128654,128656,128658,128660,128662,128664],{"class":2227,"line":2356},[2225,128655,10423],{"class":2307},[2225,128657,2311],{"class":2235},[2225,128659,2249],{"class":2235},[2225,128661,70278],{"class":2252},[2225,128663,2805],{"class":2235},[2225,128665,1707],{"class":2235},[2225,128667,128668,128671,128673,128675,128677],{"class":2227,"line":2830},[2225,128669,128670],{"class":2307},"  coverageStrategy",[2225,128672,2311],{"class":2235},[2225,128674,128607],{"class":2291},[2225,128676,4228],{"class":2239},[2225,128678,1707],{"class":2235},[2225,128680,128681,128683],{"class":2227,"line":2861},[2225,128682,2359],{"class":2235},[2225,128684,2362],{"class":2239},[937,128686,128687,128690,128691,128694,128695,128698,128699,128702,128703,128705],{},[941,128688,128689],{},"WithCoverageOptions"," tunes the sketches — ",[941,128692,128693],{},"precision"," (HLL registers, default\n14 ⇒ 16 KiB per key), ",[941,128696,128697],{},"bloomBits"," (default 16384), ",[941,128700,128701],{},"bloomHashes"," (default 7) and\n",[941,128704,51302],{}," (the novelty window, default one hour).",[937,128707,46418,128708,128713,128714,128717],{},[950,128709,128710,128712],{},[941,128711,51302],{}," is not the coverage horizon."," The window governs ",[1092,128715,128716],{},"novelty","; the\ncoverage fraction accumulates for all time and never resets. Shortening the\nwindow makes bursts easier to see and does nothing to coverage.",[1022,128719,128721],{"id":128720},"declaring-a-corpus-and-why-it-is-declared","Declaring a corpus, and why it is declared",[2217,128723,128725],{"className":2219,"code":128724,"language":2221,"meta":1037,"style":1037},"\u002F\u002F per collection\n{ corpusSize: 12_000, alertAt: [0.6, 0.9] }\n",[941,128726,128727,128732],{"__ignoreMap":1037},[2225,128728,128729],{"class":2227,"line":2228},[2225,128730,128731],{"class":2763},"\u002F\u002F per collection\n",[2225,128733,128734,128736,128739,128741,128744,128746,128749,128751,128753,128756,128758,128761,128763],{"class":2227,"line":1041},[2225,128735,2792],{"class":2235},[2225,128737,128738],{"class":6342}," corpusSize",[2225,128740,2311],{"class":2235},[2225,128742,128743],{"class":6527}," 12_000",[2225,128745,2298],{"class":2235},[2225,128747,128748],{"class":6342}," alertAt",[2225,128750,2311],{"class":2235},[2225,128752,8667],{"class":2307},[2225,128754,128755],{"class":6527},"0.6",[2225,128757,2298],{"class":2235},[2225,128759,128760],{"class":6527}," 0.9",[2225,128762,8890],{"class":2307},[2225,128764,7351],{"class":2235},[937,128766,128767,128768,2462,128770,128775],{},"Coverage is ",[941,128769,128557],{},[950,128771,128772,128774],{},[941,128773,128552],{}," is declared, not\ncounted"," — two deliberate reasons, both worth keeping:",[1234,128777,128778,128781],{},[1114,128779,128780],{},"Counting means a store read, on a sensor whose whole design is that it never\ntouches the store.",[1114,128782,128783],{},"A corpus size that moves with every write makes an alert threshold\nun-reproducible: the same read pattern would cross 0.9 on Tuesday and not on\nWednesday.",[937,128785,128786,128787,128789,128790,2514,128792,128795,128796,128799],{},"A collection that declares no ",[941,128788,128552],{}," still accumulates ",[941,128791,14691],{},[941,128793,128794],{},"served","; it simply has no fraction, so ",[941,128797,128798],{},"CoverageStats.coverage"," is absent and no\nthreshold can fire.",[937,128801,128802,128803,128806,128807,1520],{},"Each fraction in ",[941,128804,128805],{},"alertAt"," fires ",[950,128808,128809],{},"at most once",[1022,128811,128813],{"id":128812},"the-event","The event",[937,128815,128816,128817,77006,128819,2311],{},"Crossing a threshold emits ",[941,128818,128567],{},[941,128820,128821],{},"CoverageEvent",[2217,128823,128825],{"className":2219,"code":128824,"language":2221,"meta":1037,"style":1037},"interface CoverageEvent {\n  readonly principal: string   \u002F\u002F keyring.userId the reads are attributed to\n  readonly vault: string\n  readonly collection: string\n  readonly novel: number       \u002F\u002F records novel to this window (Bloom-estimated)\n  readonly served: number      \u002F\u002F decrypts served to this principal, all time\n  readonly coverage: number    \u002F\u002F distinct-ever \u002F declared corpus, 0..1 (HLL)\n  readonly window: string      \u002F\u002F ISO-8601 start of the observing window\n  readonly source: string      \u002F\u002F which sensor produced it — 'hub\u002Fcoverage' here\n}\n",[941,128826,128827,128836,128850,128860,128870,128884,128898,128912,128925,128938],{"__ignoreMap":1037},[2225,128828,128829,128831,128834],{"class":2227,"line":2228},[2225,128830,6994],{"class":2774},[2225,128832,128833],{"class":6342}," CoverageEvent",[2225,128835,2301],{"class":2235},[2225,128837,128838,128840,128843,128845,128847],{"class":2227,"line":1041},[2225,128839,53580],{"class":2774},[2225,128841,128842],{"class":2307}," principal",[2225,128844,2311],{"class":2235},[2225,128846,6366],{"class":6342},[2225,128848,128849],{"class":2763},"   \u002F\u002F keyring.userId the reads are attributed to\n",[2225,128851,128852,128854,128856,128858],{"class":2227,"line":1038},[2225,128853,53580],{"class":2774},[2225,128855,6466],{"class":2307},[2225,128857,2311],{"class":2235},[2225,128859,7014],{"class":6342},[2225,128861,128862,128864,128866,128868],{"class":2227,"line":2288},[2225,128863,53580],{"class":2774},[2225,128865,7061],{"class":2307},[2225,128867,2311],{"class":2235},[2225,128869,7014],{"class":6342},[2225,128871,128872,128874,128877,128879,128881],{"class":2227,"line":2304},[2225,128873,53580],{"class":2774},[2225,128875,128876],{"class":2307}," novel",[2225,128878,2311],{"class":2235},[2225,128880,6356],{"class":6342},[2225,128882,128883],{"class":2763},"       \u002F\u002F records novel to this window (Bloom-estimated)\n",[2225,128885,128886,128888,128891,128893,128895],{"class":2227,"line":2356},[2225,128887,53580],{"class":2774},[2225,128889,128890],{"class":2307}," served",[2225,128892,2311],{"class":2235},[2225,128894,6356],{"class":6342},[2225,128896,128897],{"class":2763},"      \u002F\u002F decrypts served to this principal, all time\n",[2225,128899,128900,128902,128905,128907,128909],{"class":2227,"line":2830},[2225,128901,53580],{"class":2774},[2225,128903,128904],{"class":2307}," coverage",[2225,128906,2311],{"class":2235},[2225,128908,6356],{"class":6342},[2225,128910,128911],{"class":2763},"    \u002F\u002F distinct-ever \u002F declared corpus, 0..1 (HLL)\n",[2225,128913,128914,128916,128918,128920,128922],{"class":2227,"line":2861},[2225,128915,53580],{"class":2774},[2225,128917,47415],{"class":2307},[2225,128919,2311],{"class":2235},[2225,128921,6366],{"class":6342},[2225,128923,128924],{"class":2763},"      \u002F\u002F ISO-8601 start of the observing window\n",[2225,128926,128927,128929,128931,128933,128935],{"class":2227,"line":4246},[2225,128928,53580],{"class":2774},[2225,128930,95077],{"class":2307},[2225,128932,2311],{"class":2235},[2225,128934,6366],{"class":6342},[2225,128936,128937],{"class":2763},"      \u002F\u002F which sensor produced it — 'hub\u002Fcoverage' here\n",[2225,128939,128940],{"class":2227,"line":4253},[2225,128941,7351],{"class":2235},[937,128943,48707,128944,128949],{},[950,128945,128946,128948],{},[941,128947,128821],{}," is a published seam, not an internal shape."," Two\nconsequences for anyone building on it:",[1111,128951,128952,128969],{},[1114,128953,128954,128961,128962,1169,128965,128968],{},[950,128955,128956,2993,128958,128960],{},[941,128957,22723],{},[941,128959,7259],{},", never an enum",", and values are shaped\n",[941,128963,128964],{},"\u003Cpublishing package>\u002F\u003Csensor>",[1092,128966,128967],{},"package",", not the repo. An enum would\nmake every new sensor a breaking change to hub.",[1114,128970,128971,1324,128974,1261,128976,2601,128978,1261,128980,128982,128983,128985],{},[950,128972,128973],{},"New sensors add fields; they never redefine",[941,128975,128794],{},[941,128977,128537],{},[941,128979,649],{},[941,128981,12429],{},". Mixed horizons across sensors are by design, so read\n",[941,128984,22723],{}," before comparing two events' numbers.",[1022,128987,128989],{"id":128988},"limits-worth-knowing-before-you-alert-on-it","Limits worth knowing before you alert on it",[1111,128991,128992,128999,129005],{},[1114,128993,128994,128995,128998],{},"The estimates are sketches. Treat a crossing as ",[1092,128996,128997],{},"a threshold was reached",",\nnever as an exact count.",[1114,129000,129001,129002,129004],{},"Accounting is per ",[941,129003,128527],{},". One principal reading\nbroadly across many collections crosses nothing if each collection stays below\nits fraction — alert on the pattern you care about, not only on this one.",[1114,129006,129007,129008,129011],{},"⚠️ An ",[950,129009,129010],{},"eagerly hydrated"," collection decrypts its whole corpus when it loads,\nso a decrypt-point sensor sees full coverage immediately. That is honest\nreporting of what happened, and it is not the signal you wanted; the eager case\nis handled in hub's accounting rather than by ignoring it, but a threshold on\nan eager collection tells you about hydration, not about a reader.",[1022,129013,2621],{"id":2620},[1111,129015,129016,129021,129027],{},[1114,129017,129018,129020],{},[1007,129019,645],{"href":128438}," — the similarly-named, unrelated pre-unlock metadata subpath",[1114,129022,129023,129026],{},[1007,129024,872],{"href":129025},".\u002Ftiers"," — the access controls coverage complements: entitlement versus volume",[1114,129028,129029,129032],{},[1007,129030,844],{"href":129031},".\u002Fsession-tiers"," — the session side of the same question, per operation rather than per corpus",[2645,129034,129035],{},"html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}",{"title":1037,"searchDepth":1038,"depth":1038,"links":129037},[129038,129040,129041,129042,129043,129044,129045,129046],{"id":128434,"depth":1041,"text":129039},"⚠️ Not the same thing as cover",{"id":29856,"depth":1041,"text":29857},{"id":70092,"depth":1041,"text":70093},{"id":128574,"depth":1041,"text":128575},{"id":128720,"depth":1041,"text":128721},{"id":128812,"depth":1041,"text":128813},{"id":128988,"depth":1041,"text":128989},{"id":2620,"depth":1041,"text":2621},"Sketch-based read accounting: how much of a collection one principal has ever decrypted, emitted as a CoverageEvent when a declared fraction is crossed.",{},{"title":649,"description":129047},"6j4p5bpdqvd1rtkkSSKRLEsJuKcuvdjSKwrBOVOkTg4",{"id":129052,"title":653,"access":12,"apiToc":929,"body":129053,"category":1042,"description":132441,"extension":1044,"meta":132442,"navigation":1042,"nest":1047,"path":654,"seo":132443,"sinceVersion":1042,"status":1049,"stem":655,"subtitle":1042,"tier":1050,"tocExternal":1042,"__hash__":132444},"docs\u002Fservices\u002Fcrdt.md",{"type":931,"value":129054,"toc":132417},[129055,129057,129060,129062,129068,129107,129124,129126,129161,129163,129170,129374,129377,129465,129467,129473,129481,129618,129623,129649,129832,129837,130516,130522,130527,130731,130909,130915,130921,130946,131279,131282,131559,131561,131566,131582,131590,131596,131651,131671,131676,131703,131716,131720,131726,131830,131894,131896,132135,132140,132153,132285,132287,132305,132307,132375,132377,132414],[934,129056,653],{"id":653},[46871,129058],{":rows":129059},"[{\"label\":\"Subpath\",\"value\":\"`@noy-db\u002Fhub\u002Fcrdt`\"},{\"label\":\"Factory\",\"value\":\"`withCrdt()`\"},{\"label\":\"Layer\",\"value\":\"`with-commit`\"}]",[1022,129061,29857],{"id":29856},[937,129063,129064,129065,129067],{},"When the same record is edited on two devices while both are offline, an ordinary\nlast-write-wins store silently drops one of the edits. The crdt service replaces the\nrecord body with a ",[950,129066,25699],{}," (conflict-free replicated data type) — a data structure whose\nmerge is deterministic and order-independent — so concurrent edits from two devices\ncombine instead of overwriting each other. You pick a merge shape per collection:",[1111,129069,129070,129086,129093],{},[1114,129071,129072,129076,129077,129079,129080,129082,129083,129085],{},[950,129073,129074],{},[941,129075,21956],{}," — last-writer-wins ",[1092,129078,4741],{},": each field carries its own write\ntimestamp, so device A's edit to ",[941,129081,6631],{}," and device B's edit to ",[941,129084,42850],{}," both survive.",[1114,129087,129088,129092],{},[950,129089,129090],{},[941,129091,21959],{}," — a replicated growable array for ordered lists: elements get stable ids,\nremovals become tombstones, and concurrent inserts\u002Fremovals union cleanly.",[1114,129094,129095,129099,129100,129104,129105,1520],{},[950,129096,129097],{},[941,129098,287],{}," — full ",[1007,129101,90029],{"href":129102,"rel":129103},"https:\u002F\u002Fyjs.org",[3371]," documents (collaborative rich text, maps,\narrays) stored encrypted, via the companion package ",[941,129106,42682],{},[937,129108,129109,129110,129112,129113,129115,129116,129119,129120,129123],{},"The encrypted envelope wraps the CRDT ",[1092,129111,4656],{},", not the resolved record — the storage\nbackend only ever sees ciphertext (see ",[1007,129114,1516],{"href":66},").\n",[941,129117,129118],{},"collection.get(id)"," returns the resolved snapshot; ",[941,129121,129122],{},"collection.getRaw(id)"," returns the\nunderlying state. The whole service costs ~221 lines of code and is off-bundle when you\ndon't opt in.",[1022,129125,70093],{"id":70092},[1111,129127,129128,129139,129150],{},[1114,129129,129130,953,129135,129138],{},[950,129131,3289,129132,129134],{},[941,129133,653],{}," shape declared per collection",[941,129136,129137],{},"'lww-map' | 'rga' | 'yjs'",") — the factory enables the machinery; each collection picks its merge shape.",[1114,129140,129141,129147,129148,1520],{},[1092,129142,129143,129144,129146],{},"For the ",[941,129145,89448],{}," shape:"," the companion package ",[941,129149,42682],{},[1114,129151,129152,1324,129155,129157,129158,129160],{},[1092,129153,129154],{},"For cross-device replication:",[941,129156,89280],{}," plus a shared sync adapter both devices can reach — merging is local, replication is ",[1007,129159,864],{"href":865},"'s job.",[1022,129162,99864],{"id":99863},[937,129164,124907,129165,10313,129168,2311],{},[941,129166,129167],{},"crdtStrategy: withCrdt()",[941,129169,12780],{},[2217,129171,129173],{"className":2219,"code":129172,"language":2221,"meta":1037,"style":1037},"import { createNoydb } from '@noy-db\u002Fhub'\nimport { withCrdt } from '@noy-db\u002Fhub\u002Fcrdt'\nimport { withSync } from '@noy-db\u002Fhub\u002Fsync'\nimport { toMemory } from '@noy-db\u002Fto-memory'\n\nconst db = await createNoydb({\n  store: toMemory(),               \u002F\u002F any NoydbStore works\n  user: 'alice',\n  secret: 'correct horse battery staple',\n  crdtStrategy: withCrdt(),\n  syncStrategy: withSync(),      \u002F\u002F only needed for cross-device replication\n})\n\nconst vault = await db.openVault('travel')\n",[941,129174,129175,129193,129211,129229,129247,129251,129267,129282,129296,129310,129322,129337,129343,129347],{"__ignoreMap":1037},[2225,129176,129177,129179,129181,129183,129185,129187,129189,129191],{"class":2227,"line":2228},[2225,129178,2232],{"class":2231},[2225,129180,2236],{"class":2235},[2225,129182,2744],{"class":2239},[2225,129184,2243],{"class":2235},[2225,129186,2246],{"class":2231},[2225,129188,2249],{"class":2235},[2225,129190,1086],{"class":2252},[2225,129192,2256],{"class":2235},[2225,129194,129195,129197,129199,129201,129203,129205,129207,129209],{"class":2227,"line":1041},[2225,129196,2232],{"class":2231},[2225,129198,2236],{"class":2235},[2225,129200,89560],{"class":2239},[2225,129202,2243],{"class":2235},[2225,129204,2246],{"class":2231},[2225,129206,2249],{"class":2235},[2225,129208,73018],{"class":2252},[2225,129210,2256],{"class":2235},[2225,129212,129213,129215,129217,129219,129221,129223,129225,129227],{"class":2227,"line":1038},[2225,129214,2232],{"class":2231},[2225,129216,2236],{"class":2235},[2225,129218,89579],{"class":2239},[2225,129220,2243],{"class":2235},[2225,129222,2246],{"class":2231},[2225,129224,2249],{"class":2235},[2225,129226,4672],{"class":2252},[2225,129228,2256],{"class":2235},[2225,129230,129231,129233,129235,129237,129239,129241,129243,129245],{"class":2227,"line":2288},[2225,129232,2232],{"class":2231},[2225,129234,2236],{"class":2235},[2225,129236,68576],{"class":2239},[2225,129238,2243],{"class":2235},[2225,129240,2246],{"class":2231},[2225,129242,2249],{"class":2235},[2225,129244,9638],{"class":2252},[2225,129246,2256],{"class":2235},[2225,129248,129249],{"class":2227,"line":2304},[2225,129250,2285],{"emptyLinePlaceholder":2284},[2225,129252,129253,129255,129257,129259,129261,129263,129265],{"class":2227,"line":2356},[2225,129254,2775],{"class":2774},[2225,129256,2778],{"class":2239},[2225,129258,2781],{"class":2235},[2225,129260,2784],{"class":2231},[2225,129262,2744],{"class":2291},[2225,129264,2789],{"class":2239},[2225,129266,4200],{"class":2235},[2225,129268,129269,129271,129273,129275,129277,129279],{"class":2227,"line":2830},[2225,129270,15608],{"class":2307},[2225,129272,2311],{"class":2235},[2225,129274,68576],{"class":2291},[2225,129276,4228],{"class":2239},[2225,129278,2298],{"class":2235},[2225,129280,129281],{"class":2763},"               \u002F\u002F any NoydbStore works\n",[2225,129283,129284,129286,129288,129290,129292,129294],{"class":2227,"line":2861},[2225,129285,4205],{"class":2307},[2225,129287,2311],{"class":2235},[2225,129289,2249],{"class":2235},[2225,129291,6408],{"class":2252},[2225,129293,2805],{"class":2235},[2225,129295,1707],{"class":2235},[2225,129297,129298,129300,129302,129304,129306,129308],{"class":2227,"line":4246},[2225,129299,10423],{"class":2307},[2225,129301,2311],{"class":2235},[2225,129303,2249],{"class":2235},[2225,129305,40260],{"class":2252},[2225,129307,2805],{"class":2235},[2225,129309,1707],{"class":2235},[2225,129311,129312,129314,129316,129318,129320],{"class":2227,"line":4253},[2225,129313,89888],{"class":2307},[2225,129315,2311],{"class":2235},[2225,129317,89560],{"class":2291},[2225,129319,4228],{"class":2239},[2225,129321,1707],{"class":2235},[2225,129323,129324,129326,129328,129330,129332,129334],{"class":2227,"line":4259},[2225,129325,89901],{"class":2307},[2225,129327,2311],{"class":2235},[2225,129329,89579],{"class":2291},[2225,129331,4228],{"class":2239},[2225,129333,2298],{"class":2235},[2225,129335,129336],{"class":2763},"      \u002F\u002F only needed for cross-device replication\n",[2225,129338,129339,129341],{"class":2227,"line":6548},[2225,129340,2359],{"class":2235},[2225,129342,2362],{"class":2239},[2225,129344,129345],{"class":2227,"line":6576},[2225,129346,2285],{"emptyLinePlaceholder":2284},[2225,129348,129349,129351,129353,129355,129357,129359,129361,129363,129365,129367,129370,129372],{"class":2227,"line":6582},[2225,129350,2775],{"class":2774},[2225,129352,2835],{"class":2239},[2225,129354,2781],{"class":2235},[2225,129356,2784],{"class":2231},[2225,129358,2842],{"class":2239},[2225,129360,1520],{"class":2235},[2225,129362,2847],{"class":2291},[2225,129364,2789],{"class":2239},[2225,129366,2805],{"class":2235},[2225,129368,129369],{"class":2252},"travel",[2225,129371,2805],{"class":2235},[2225,129373,2362],{"class":2239},[937,129375,129376],{},"Then declare the mode per collection:",[2217,129378,129380],{"className":2219,"code":129379,"language":2221,"meta":1037,"style":1037},"interface Note { title?: string; body?: string; status?: string }\n\nconst notes = vault.collection\u003CNote>('notes', { crdt: 'lww-map' })\n",[941,129381,129382,129414,129418],{"__ignoreMap":1037},[2225,129383,129384,129386,129388,129390,129392,129394,129396,129398,129400,129402,129404,129406,129408,129410,129412],{"class":2227,"line":2228},[2225,129385,6994],{"class":2774},[2225,129387,86655],{"class":6342},[2225,129389,2236],{"class":2235},[2225,129391,74169],{"class":2307},[2225,129393,7011],{"class":2235},[2225,129395,6366],{"class":6342},[2225,129397,3769],{"class":2235},[2225,129399,42791],{"class":2307},[2225,129401,7011],{"class":2235},[2225,129403,6366],{"class":6342},[2225,129405,3769],{"class":2235},[2225,129407,6361],{"class":2307},[2225,129409,7011],{"class":2235},[2225,129411,6366],{"class":6342},[2225,129413,6369],{"class":2235},[2225,129415,129416],{"class":2227,"line":1041},[2225,129417,2285],{"emptyLinePlaceholder":2284},[2225,129419,129420,129422,129424,129426,129428,129430,129432,129434,129436,129438,129440,129442,129444,129446,129448,129450,129453,129455,129457,129459,129461,129463],{"class":2227,"line":1038},[2225,129421,2775],{"class":2774},[2225,129423,42762],{"class":2239},[2225,129425,2781],{"class":2235},[2225,129427,6466],{"class":2239},[2225,129429,1520],{"class":2235},[2225,129431,6471],{"class":2291},[2225,129433,6474],{"class":2235},[2225,129435,86814],{"class":6342},[2225,129437,6480],{"class":2235},[2225,129439,2789],{"class":2239},[2225,129441,2805],{"class":2235},[2225,129443,2854],{"class":2252},[2225,129445,2805],{"class":2235},[2225,129447,2298],{"class":2235},[2225,129449,2236],{"class":2235},[2225,129451,129452],{"class":2307}," crdt",[2225,129454,2311],{"class":2235},[2225,129456,2249],{"class":2235},[2225,129458,21956],{"class":2252},[2225,129460,2805],{"class":2235},[2225,129462,2243],{"class":2235},[2225,129464,2362],{"class":2239},[1022,129466,29893],{"id":29892},[1591,129468,129470,129471],{"id":129469},"per-field-merge-with-lww-map","Per-field merge with ",[941,129472,21956],{},[937,129474,14370,129475,129477,129478,129480],{},[941,129476,21956],{}," mode every field is an independent last-writer-wins register. A ",[941,129479,3161],{}," may\ncarry only the fields it changes — fields absent from the new record keep their existing\nvalue and timestamp:",[2217,129482,129484],{"className":2219,"code":129483,"language":2221,"meta":1037,"style":1037},"await notes.put('n1', { title: 'Trip to Chiang Mai', body: 'First draft', status: 'draft' })\nawait notes.put('n1', { status: 'reviewed' })   \u002F\u002F partial put — only this field advances\n\nawait notes.get('n1')\n\u002F\u002F → { status: 'reviewed', title: 'Trip to Chiang Mai', body: 'First draft' }\n",[941,129485,129486,129549,129589,129593,129613],{"__ignoreMap":1037},[2225,129487,129488,129490,129492,129494,129496,129498,129500,129503,129505,129507,129509,129511,129513,129515,129518,129520,129522,129524,129526,129528,129531,129533,129535,129537,129539,129541,129543,129545,129547],{"class":2227,"line":2228},[2225,129489,6500],{"class":2231},[2225,129491,42817],{"class":2239},[2225,129493,1520],{"class":2235},[2225,129495,3161],{"class":2291},[2225,129497,2789],{"class":2239},[2225,129499,2805],{"class":2235},[2225,129501,129502],{"class":2252},"n1",[2225,129504,2805],{"class":2235},[2225,129506,2298],{"class":2235},[2225,129508,2236],{"class":2235},[2225,129510,74169],{"class":2307},[2225,129512,2311],{"class":2235},[2225,129514,2249],{"class":2235},[2225,129516,129517],{"class":2252},"Trip to Chiang Mai",[2225,129519,2805],{"class":2235},[2225,129521,2298],{"class":2235},[2225,129523,42791],{"class":2307},[2225,129525,2311],{"class":2235},[2225,129527,2249],{"class":2235},[2225,129529,129530],{"class":2252},"First draft",[2225,129532,2805],{"class":2235},[2225,129534,2298],{"class":2235},[2225,129536,6361],{"class":2307},[2225,129538,2311],{"class":2235},[2225,129540,2249],{"class":2235},[2225,129542,28429],{"class":2252},[2225,129544,2805],{"class":2235},[2225,129546,2243],{"class":2235},[2225,129548,2362],{"class":2239},[2225,129550,129551,129553,129555,129557,129559,129561,129563,129565,129567,129569,129571,129573,129575,129577,129580,129582,129584,129586],{"class":2227,"line":1041},[2225,129552,6500],{"class":2231},[2225,129554,42817],{"class":2239},[2225,129556,1520],{"class":2235},[2225,129558,3161],{"class":2291},[2225,129560,2789],{"class":2239},[2225,129562,2805],{"class":2235},[2225,129564,129502],{"class":2252},[2225,129566,2805],{"class":2235},[2225,129568,2298],{"class":2235},[2225,129570,2236],{"class":2235},[2225,129572,6361],{"class":2307},[2225,129574,2311],{"class":2235},[2225,129576,2249],{"class":2235},[2225,129578,129579],{"class":2252},"reviewed",[2225,129581,2805],{"class":2235},[2225,129583,2243],{"class":2235},[2225,129585,71781],{"class":2239},[2225,129587,129588],{"class":2763},"\u002F\u002F partial put — only this field advances\n",[2225,129590,129591],{"class":2227,"line":1038},[2225,129592,2285],{"emptyLinePlaceholder":2284},[2225,129594,129595,129597,129599,129601,129603,129605,129607,129609,129611],{"class":2227,"line":2288},[2225,129596,6500],{"class":2231},[2225,129598,42817],{"class":2239},[2225,129600,1520],{"class":2235},[2225,129602,3158],{"class":2291},[2225,129604,2789],{"class":2239},[2225,129606,2805],{"class":2235},[2225,129608,129502],{"class":2252},[2225,129610,2805],{"class":2235},[2225,129612,2362],{"class":2239},[2225,129614,129615],{"class":2227,"line":2304},[2225,129616,129617],{"class":2763},"\u002F\u002F → { status: 'reviewed', title: 'Trip to Chiang Mai', body: 'First draft' }\n",[937,129619,129620,129622],{},[941,129621,25702],{}," exposes the stored state — each field with its value and the ISO timestamp\nof its last write:",[2217,129624,129626],{"className":2219,"code":129625,"language":2221,"meta":1037,"style":1037},"await notes.getRaw('n1')\n",[941,129627,129628],{"__ignoreMap":1037},[2225,129629,129630,129632,129634,129636,129639,129641,129643,129645,129647],{"class":2227,"line":2228},[2225,129631,6500],{"class":2231},[2225,129633,42817],{"class":2239},[2225,129635,1520],{"class":2235},[2225,129637,129638],{"class":2291},"getRaw",[2225,129640,2789],{"class":2239},[2225,129642,2805],{"class":2235},[2225,129644,129502],{"class":2252},[2225,129646,2805],{"class":2235},[2225,129648,2362],{"class":2239},[2217,129650,129652],{"className":6604,"code":129651,"language":141,"meta":1037,"style":1037},"{\n  \"_crdt\": \"lww-map\",\n  \"fields\": {\n    \"status\": { \"v\": \"reviewed\",           \"ts\": \"2026-07-08T09:31:12.844Z\" },\n    \"title\":  { \"v\": \"Trip to Chiang Mai\", \"ts\": \"2026-07-08T09:31:12.802Z\" },\n    \"body\":   { \"v\": \"First draft\",        \"ts\": \"2026-07-08T09:31:12.802Z\" }\n  }\n}\n",[941,129653,129654,129658,129677,129689,129735,129780,129824,129828],{"__ignoreMap":1037},[2225,129655,129656],{"class":2227,"line":2228},[2225,129657,4200],{"class":2235},[2225,129659,129660,129662,129665,129667,129669,129671,129673,129675],{"class":2227,"line":1041},[2225,129661,9368],{"class":2235},[2225,129663,129664],{"class":2774},"_crdt",[2225,129666,6620],{"class":2235},[2225,129668,2311],{"class":2235},[2225,129670,6614],{"class":2235},[2225,129672,21956],{"class":2252},[2225,129674,6620],{"class":2235},[2225,129676,1707],{"class":2235},[2225,129678,129679,129681,129683,129685,129687],{"class":2227,"line":1038},[2225,129680,9368],{"class":2235},[2225,129682,57285],{"class":2774},[2225,129684,6620],{"class":2235},[2225,129686,2311],{"class":2235},[2225,129688,2301],{"class":2235},[2225,129690,129691,129693,129695,129697,129699,129701,129703,129705,129707,129709,129711,129713,129715,129717,129720,129722,129724,129726,129728,129731,129733],{"class":2227,"line":2288},[2225,129692,14201],{"class":2235},[2225,129694,6631],{"class":6342},[2225,129696,6620],{"class":2235},[2225,129698,2311],{"class":2235},[2225,129700,2236],{"class":2235},[2225,129702,6614],{"class":2235},[2225,129704,8160],{"class":6527},[2225,129706,6620],{"class":2235},[2225,129708,2311],{"class":2235},[2225,129710,6614],{"class":2235},[2225,129712,129579],{"class":2252},[2225,129714,6620],{"class":2235},[2225,129716,2298],{"class":2235},[2225,129718,129719],{"class":2235},"           \"",[2225,129721,2221],{"class":6527},[2225,129723,6620],{"class":2235},[2225,129725,2311],{"class":2235},[2225,129727,6614],{"class":2235},[2225,129729,129730],{"class":2252},"2026-07-08T09:31:12.844Z",[2225,129732,6620],{"class":2235},[2225,129734,2353],{"class":2235},[2225,129736,129737,129739,129741,129743,129745,129747,129749,129751,129753,129755,129757,129759,129761,129763,129765,129767,129769,129771,129773,129776,129778],{"class":2227,"line":2304},[2225,129738,14201],{"class":2235},[2225,129740,87181],{"class":6342},[2225,129742,6620],{"class":2235},[2225,129744,2311],{"class":2235},[2225,129746,26354],{"class":2235},[2225,129748,6614],{"class":2235},[2225,129750,8160],{"class":6527},[2225,129752,6620],{"class":2235},[2225,129754,2311],{"class":2235},[2225,129756,6614],{"class":2235},[2225,129758,129517],{"class":2252},[2225,129760,6620],{"class":2235},[2225,129762,2298],{"class":2235},[2225,129764,6614],{"class":2235},[2225,129766,2221],{"class":6527},[2225,129768,6620],{"class":2235},[2225,129770,2311],{"class":2235},[2225,129772,6614],{"class":2235},[2225,129774,129775],{"class":2252},"2026-07-08T09:31:12.802Z",[2225,129777,6620],{"class":2235},[2225,129779,2353],{"class":2235},[2225,129781,129782,129784,129786,129788,129790,129792,129794,129796,129798,129800,129802,129804,129806,129808,129810,129812,129814,129816,129818,129820,129822],{"class":2227,"line":2356},[2225,129783,14201],{"class":2235},[2225,129785,42850],{"class":6342},[2225,129787,6620],{"class":2235},[2225,129789,2311],{"class":2235},[2225,129791,58994],{"class":2235},[2225,129793,6614],{"class":2235},[2225,129795,8160],{"class":6527},[2225,129797,6620],{"class":2235},[2225,129799,2311],{"class":2235},[2225,129801,6614],{"class":2235},[2225,129803,129530],{"class":2252},[2225,129805,6620],{"class":2235},[2225,129807,2298],{"class":2235},[2225,129809,60015],{"class":2235},[2225,129811,2221],{"class":6527},[2225,129813,6620],{"class":2235},[2225,129815,2311],{"class":2235},[2225,129817,6614],{"class":2235},[2225,129819,129775],{"class":2252},[2225,129821,6620],{"class":2235},[2225,129823,6369],{"class":2235},[2225,129825,129826],{"class":2227,"line":2830},[2225,129827,7632],{"class":2235},[2225,129829,129830],{"class":2227,"line":2861},[2225,129831,7351],{"class":2235},[937,129833,11526,129834,129836],{},[1007,129835,864],{"href":865}," enabled, the same merge runs automatically when two devices\npush conflicting versions: the collection registers a CRDT conflict resolver with the\nsync engine, and for each field the entry with the higher timestamp wins.",[2217,129838,129840],{"className":2219,"code":129839,"language":2221,"meta":1037,"style":1037},"const remote = toMemory()   \u002F\u002F the shared sync target both devices reach\n\nconst laptop = await createNoydb({\n  store: toMemory(), sync: remote, user: 'alice', encrypt: false,\n  crdtStrategy: withCrdt(), syncStrategy: withSync(),\n})\nconst phone = await createNoydb({\n  store: toMemory(), sync: remote, user: 'alice', encrypt: false,\n  crdtStrategy: withCrdt(), syncStrategy: withSync(),\n})\n\u002F\u002F encrypt: false — testing only, NO privacy guarantee: the stores hold plaintext.\n\u002F\u002F It keeps this two-instance example self-contained; moving keys between real\n\u002F\u002F devices is the team service's job (grants flow over sync).\n\nconst laptopNotes = (await laptop.openVault('travel'))\n  .collection\u003CNote>('notes', { crdt: 'lww-map' })\nconst phoneNotes = (await phone.openVault('travel'))\n  .collection\u003CNote>('notes', { crdt: 'lww-map' })\n\n\u002F\u002F The laptop writes the note and pushes it\nawait laptopNotes.put('n1', { title: 'Trip to Chiang Mai', body: 'First draft', status: 'draft' })\nawait laptop.push('travel')\n\n\u002F\u002F The phone pulls it, goes offline, and edits only `body`\nawait phone.pull('travel')\nawait phoneNotes.put('n1', { body: 'Phone draft — updated on the train' })\n\n\u002F\u002F Meanwhile the laptop — also offline — edits only `status`\nawait laptopNotes.put('n1', { status: 'reviewed' })\n\n\u002F\u002F Both come back online\nawait phone.push('travel')\nawait laptop.push('travel')   \u002F\u002F conflict detected → merged per field\nawait phone.pull('travel')\n\nawait laptopNotes.get('n1')\n\u002F\u002F → { title: 'Trip to Chiang Mai', body: 'Phone draft — updated on the train', status: 'reviewed' }\nawait phoneNotes.get('n1')\n\u002F\u002F → the same record — both devices converged, neither edit was lost\n",[941,129841,129842,129857,129861,129878,129920,129942,129948,129965,130005,130027,130033,130038,130043,130048,130052,130082,130120,130150,130188,130192,130197,130258,130278,130282,130287,130307,130345,130349,130354,130390,130394,130399,130419,130442,130462,130466,130486,130491,130511],{"__ignoreMap":1037},[2225,129843,129844,129846,129848,129850,129852,129854],{"class":2227,"line":2228},[2225,129845,2775],{"class":2774},[2225,129847,35196],{"class":2239},[2225,129849,2781],{"class":2235},[2225,129851,68576],{"class":2291},[2225,129853,39880],{"class":2239},[2225,129855,129856],{"class":2763},"\u002F\u002F the shared sync target both devices reach\n",[2225,129858,129859],{"class":2227,"line":1041},[2225,129860,2285],{"emptyLinePlaceholder":2284},[2225,129862,129863,129865,129868,129870,129872,129874,129876],{"class":2227,"line":1038},[2225,129864,2775],{"class":2774},[2225,129866,129867],{"class":2239}," laptop ",[2225,129869,2781],{"class":2235},[2225,129871,2784],{"class":2231},[2225,129873,2744],{"class":2291},[2225,129875,2789],{"class":2239},[2225,129877,4200],{"class":2235},[2225,129879,129880,129882,129884,129886,129888,129890,129893,129895,129897,129899,129901,129903,129905,129907,129909,129911,129914,129916,129918],{"class":2227,"line":2288},[2225,129881,15608],{"class":2307},[2225,129883,2311],{"class":2235},[2225,129885,68576],{"class":2291},[2225,129887,4228],{"class":2239},[2225,129889,2298],{"class":2235},[2225,129891,129892],{"class":2307}," sync",[2225,129894,2311],{"class":2235},[2225,129896,89831],{"class":2239},[2225,129898,2298],{"class":2235},[2225,129900,2795],{"class":2307},[2225,129902,2311],{"class":2235},[2225,129904,2249],{"class":2235},[2225,129906,6408],{"class":2252},[2225,129908,2805],{"class":2235},[2225,129910,2298],{"class":2235},[2225,129912,129913],{"class":2307}," encrypt",[2225,129915,2311],{"class":2235},[2225,129917,2341],{"class":2321},[2225,129919,1707],{"class":2235},[2225,129921,129922,129924,129926,129928,129930,129932,129934,129936,129938,129940],{"class":2227,"line":2304},[2225,129923,89888],{"class":2307},[2225,129925,2311],{"class":2235},[2225,129927,89560],{"class":2291},[2225,129929,4228],{"class":2239},[2225,129931,2298],{"class":2235},[2225,129933,40020],{"class":2307},[2225,129935,2311],{"class":2235},[2225,129937,89579],{"class":2291},[2225,129939,4228],{"class":2239},[2225,129941,1707],{"class":2235},[2225,129943,129944,129946],{"class":2227,"line":2356},[2225,129945,2359],{"class":2235},[2225,129947,2362],{"class":2239},[2225,129949,129950,129952,129955,129957,129959,129961,129963],{"class":2227,"line":2830},[2225,129951,2775],{"class":2774},[2225,129953,129954],{"class":2239}," phone ",[2225,129956,2781],{"class":2235},[2225,129958,2784],{"class":2231},[2225,129960,2744],{"class":2291},[2225,129962,2789],{"class":2239},[2225,129964,4200],{"class":2235},[2225,129966,129967,129969,129971,129973,129975,129977,129979,129981,129983,129985,129987,129989,129991,129993,129995,129997,129999,130001,130003],{"class":2227,"line":2861},[2225,129968,15608],{"class":2307},[2225,129970,2311],{"class":2235},[2225,129972,68576],{"class":2291},[2225,129974,4228],{"class":2239},[2225,129976,2298],{"class":2235},[2225,129978,129892],{"class":2307},[2225,129980,2311],{"class":2235},[2225,129982,89831],{"class":2239},[2225,129984,2298],{"class":2235},[2225,129986,2795],{"class":2307},[2225,129988,2311],{"class":2235},[2225,129990,2249],{"class":2235},[2225,129992,6408],{"class":2252},[2225,129994,2805],{"class":2235},[2225,129996,2298],{"class":2235},[2225,129998,129913],{"class":2307},[2225,130000,2311],{"class":2235},[2225,130002,2341],{"class":2321},[2225,130004,1707],{"class":2235},[2225,130006,130007,130009,130011,130013,130015,130017,130019,130021,130023,130025],{"class":2227,"line":4246},[2225,130008,89888],{"class":2307},[2225,130010,2311],{"class":2235},[2225,130012,89560],{"class":2291},[2225,130014,4228],{"class":2239},[2225,130016,2298],{"class":2235},[2225,130018,40020],{"class":2307},[2225,130020,2311],{"class":2235},[2225,130022,89579],{"class":2291},[2225,130024,4228],{"class":2239},[2225,130026,1707],{"class":2235},[2225,130028,130029,130031],{"class":2227,"line":4253},[2225,130030,2359],{"class":2235},[2225,130032,2362],{"class":2239},[2225,130034,130035],{"class":2227,"line":4259},[2225,130036,130037],{"class":2763},"\u002F\u002F encrypt: false — testing only, NO privacy guarantee: the stores hold plaintext.\n",[2225,130039,130040],{"class":2227,"line":6548},[2225,130041,130042],{"class":2763},"\u002F\u002F It keeps this two-instance example self-contained; moving keys between real\n",[2225,130044,130045],{"class":2227,"line":6576},[2225,130046,130047],{"class":2763},"\u002F\u002F devices is the team service's job (grants flow over sync).\n",[2225,130049,130050],{"class":2227,"line":6582},[2225,130051,2285],{"emptyLinePlaceholder":2284},[2225,130053,130054,130056,130059,130061,130063,130065,130068,130070,130072,130074,130076,130078,130080],{"class":2227,"line":6587},[2225,130055,2775],{"class":2774},[2225,130057,130058],{"class":2239}," laptopNotes ",[2225,130060,2781],{"class":2235},[2225,130062,953],{"class":2239},[2225,130064,6500],{"class":2231},[2225,130066,130067],{"class":2239}," laptop",[2225,130069,1520],{"class":2235},[2225,130071,2847],{"class":2291},[2225,130073,2789],{"class":2239},[2225,130075,2805],{"class":2235},[2225,130077,129369],{"class":2252},[2225,130079,2805],{"class":2235},[2225,130081,8606],{"class":2239},[2225,130083,130084,130086,130088,130090,130092,130094,130096,130098,130100,130102,130104,130106,130108,130110,130112,130114,130116,130118],{"class":2227,"line":7174},[2225,130085,26235],{"class":2235},[2225,130087,6471],{"class":2291},[2225,130089,6474],{"class":2235},[2225,130091,86814],{"class":6342},[2225,130093,6480],{"class":2235},[2225,130095,2789],{"class":2239},[2225,130097,2805],{"class":2235},[2225,130099,2854],{"class":2252},[2225,130101,2805],{"class":2235},[2225,130103,2298],{"class":2235},[2225,130105,2236],{"class":2235},[2225,130107,129452],{"class":2307},[2225,130109,2311],{"class":2235},[2225,130111,2249],{"class":2235},[2225,130113,21956],{"class":2252},[2225,130115,2805],{"class":2235},[2225,130117,2243],{"class":2235},[2225,130119,2362],{"class":2239},[2225,130121,130122,130124,130127,130129,130131,130133,130136,130138,130140,130142,130144,130146,130148],{"class":2227,"line":7180},[2225,130123,2775],{"class":2774},[2225,130125,130126],{"class":2239}," phoneNotes ",[2225,130128,2781],{"class":2235},[2225,130130,953],{"class":2239},[2225,130132,6500],{"class":2231},[2225,130134,130135],{"class":2239}," phone",[2225,130137,1520],{"class":2235},[2225,130139,2847],{"class":2291},[2225,130141,2789],{"class":2239},[2225,130143,2805],{"class":2235},[2225,130145,129369],{"class":2252},[2225,130147,2805],{"class":2235},[2225,130149,8606],{"class":2239},[2225,130151,130152,130154,130156,130158,130160,130162,130164,130166,130168,130170,130172,130174,130176,130178,130180,130182,130184,130186],{"class":2227,"line":7220},[2225,130153,26235],{"class":2235},[2225,130155,6471],{"class":2291},[2225,130157,6474],{"class":2235},[2225,130159,86814],{"class":6342},[2225,130161,6480],{"class":2235},[2225,130163,2789],{"class":2239},[2225,130165,2805],{"class":2235},[2225,130167,2854],{"class":2252},[2225,130169,2805],{"class":2235},[2225,130171,2298],{"class":2235},[2225,130173,2236],{"class":2235},[2225,130175,129452],{"class":2307},[2225,130177,2311],{"class":2235},[2225,130179,2249],{"class":2235},[2225,130181,21956],{"class":2252},[2225,130183,2805],{"class":2235},[2225,130185,2243],{"class":2235},[2225,130187,2362],{"class":2239},[2225,130189,130190],{"class":2227,"line":7225},[2225,130191,2285],{"emptyLinePlaceholder":2284},[2225,130193,130194],{"class":2227,"line":7231},[2225,130195,130196],{"class":2763},"\u002F\u002F The laptop writes the note and pushes it\n",[2225,130198,130199,130201,130204,130206,130208,130210,130212,130214,130216,130218,130220,130222,130224,130226,130228,130230,130232,130234,130236,130238,130240,130242,130244,130246,130248,130250,130252,130254,130256],{"class":2227,"line":7267},[2225,130200,6500],{"class":2231},[2225,130202,130203],{"class":2239}," laptopNotes",[2225,130205,1520],{"class":2235},[2225,130207,3161],{"class":2291},[2225,130209,2789],{"class":2239},[2225,130211,2805],{"class":2235},[2225,130213,129502],{"class":2252},[2225,130215,2805],{"class":2235},[2225,130217,2298],{"class":2235},[2225,130219,2236],{"class":2235},[2225,130221,74169],{"class":2307},[2225,130223,2311],{"class":2235},[2225,130225,2249],{"class":2235},[2225,130227,129517],{"class":2252},[2225,130229,2805],{"class":2235},[2225,130231,2298],{"class":2235},[2225,130233,42791],{"class":2307},[2225,130235,2311],{"class":2235},[2225,130237,2249],{"class":2235},[2225,130239,129530],{"class":2252},[2225,130241,2805],{"class":2235},[2225,130243,2298],{"class":2235},[2225,130245,6361],{"class":2307},[2225,130247,2311],{"class":2235},[2225,130249,2249],{"class":2235},[2225,130251,28429],{"class":2252},[2225,130253,2805],{"class":2235},[2225,130255,2243],{"class":2235},[2225,130257,2362],{"class":2239},[2225,130259,130260,130262,130264,130266,130268,130270,130272,130274,130276],{"class":2227,"line":7272},[2225,130261,6500],{"class":2231},[2225,130263,130067],{"class":2239},[2225,130265,1520],{"class":2235},[2225,130267,82367],{"class":2291},[2225,130269,2789],{"class":2239},[2225,130271,2805],{"class":2235},[2225,130273,129369],{"class":2252},[2225,130275,2805],{"class":2235},[2225,130277,2362],{"class":2239},[2225,130279,130280],{"class":2227,"line":7278},[2225,130281,2285],{"emptyLinePlaceholder":2284},[2225,130283,130284],{"class":2227,"line":7303},[2225,130285,130286],{"class":2763},"\u002F\u002F The phone pulls it, goes offline, and edits only `body`\n",[2225,130288,130289,130291,130293,130295,130297,130299,130301,130303,130305],{"class":2227,"line":7308},[2225,130290,6500],{"class":2231},[2225,130292,130135],{"class":2239},[2225,130294,1520],{"class":2235},[2225,130296,91149],{"class":2291},[2225,130298,2789],{"class":2239},[2225,130300,2805],{"class":2235},[2225,130302,129369],{"class":2252},[2225,130304,2805],{"class":2235},[2225,130306,2362],{"class":2239},[2225,130308,130309,130311,130314,130316,130318,130320,130322,130324,130326,130328,130330,130332,130334,130336,130339,130341,130343],{"class":2227,"line":7314},[2225,130310,6500],{"class":2231},[2225,130312,130313],{"class":2239}," phoneNotes",[2225,130315,1520],{"class":2235},[2225,130317,3161],{"class":2291},[2225,130319,2789],{"class":2239},[2225,130321,2805],{"class":2235},[2225,130323,129502],{"class":2252},[2225,130325,2805],{"class":2235},[2225,130327,2298],{"class":2235},[2225,130329,2236],{"class":2235},[2225,130331,42791],{"class":2307},[2225,130333,2311],{"class":2235},[2225,130335,2249],{"class":2235},[2225,130337,130338],{"class":2252},"Phone draft — updated on the train",[2225,130340,2805],{"class":2235},[2225,130342,2243],{"class":2235},[2225,130344,2362],{"class":2239},[2225,130346,130347],{"class":2227,"line":7348},[2225,130348,2285],{"emptyLinePlaceholder":2284},[2225,130350,130351],{"class":2227,"line":8613},[2225,130352,130353],{"class":2763},"\u002F\u002F Meanwhile the laptop — also offline — edits only `status`\n",[2225,130355,130356,130358,130360,130362,130364,130366,130368,130370,130372,130374,130376,130378,130380,130382,130384,130386,130388],{"class":2227,"line":8633},[2225,130357,6500],{"class":2231},[2225,130359,130203],{"class":2239},[2225,130361,1520],{"class":2235},[2225,130363,3161],{"class":2291},[2225,130365,2789],{"class":2239},[2225,130367,2805],{"class":2235},[2225,130369,129502],{"class":2252},[2225,130371,2805],{"class":2235},[2225,130373,2298],{"class":2235},[2225,130375,2236],{"class":2235},[2225,130377,6361],{"class":2307},[2225,130379,2311],{"class":2235},[2225,130381,2249],{"class":2235},[2225,130383,129579],{"class":2252},[2225,130385,2805],{"class":2235},[2225,130387,2243],{"class":2235},[2225,130389,2362],{"class":2239},[2225,130391,130392],{"class":2227,"line":8662},[2225,130393,2285],{"emptyLinePlaceholder":2284},[2225,130395,130396],{"class":2227,"line":8747},[2225,130397,130398],{"class":2763},"\u002F\u002F Both come back online\n",[2225,130400,130401,130403,130405,130407,130409,130411,130413,130415,130417],{"class":2227,"line":8752},[2225,130402,6500],{"class":2231},[2225,130404,130135],{"class":2239},[2225,130406,1520],{"class":2235},[2225,130408,82367],{"class":2291},[2225,130410,2789],{"class":2239},[2225,130412,2805],{"class":2235},[2225,130414,129369],{"class":2252},[2225,130416,2805],{"class":2235},[2225,130418,2362],{"class":2239},[2225,130420,130421,130423,130425,130427,130429,130431,130433,130435,130437,130439],{"class":2227,"line":8768},[2225,130422,6500],{"class":2231},[2225,130424,130067],{"class":2239},[2225,130426,1520],{"class":2235},[2225,130428,82367],{"class":2291},[2225,130430,2789],{"class":2239},[2225,130432,2805],{"class":2235},[2225,130434,129369],{"class":2252},[2225,130436,2805],{"class":2235},[2225,130438,71781],{"class":2239},[2225,130440,130441],{"class":2763},"\u002F\u002F conflict detected → merged per field\n",[2225,130443,130444,130446,130448,130450,130452,130454,130456,130458,130460],{"class":2227,"line":8785},[2225,130445,6500],{"class":2231},[2225,130447,130135],{"class":2239},[2225,130449,1520],{"class":2235},[2225,130451,91149],{"class":2291},[2225,130453,2789],{"class":2239},[2225,130455,2805],{"class":2235},[2225,130457,129369],{"class":2252},[2225,130459,2805],{"class":2235},[2225,130461,2362],{"class":2239},[2225,130463,130464],{"class":2227,"line":8815},[2225,130465,2285],{"emptyLinePlaceholder":2284},[2225,130467,130468,130470,130472,130474,130476,130478,130480,130482,130484],{"class":2227,"line":8854},[2225,130469,6500],{"class":2231},[2225,130471,130203],{"class":2239},[2225,130473,1520],{"class":2235},[2225,130475,3158],{"class":2291},[2225,130477,2789],{"class":2239},[2225,130479,2805],{"class":2235},[2225,130481,129502],{"class":2252},[2225,130483,2805],{"class":2235},[2225,130485,2362],{"class":2239},[2225,130487,130488],{"class":2227,"line":8874},[2225,130489,130490],{"class":2763},"\u002F\u002F → { title: 'Trip to Chiang Mai', body: 'Phone draft — updated on the train', status: 'reviewed' }\n",[2225,130492,130493,130495,130497,130499,130501,130503,130505,130507,130509],{"class":2227,"line":8911},[2225,130494,6500],{"class":2231},[2225,130496,130313],{"class":2239},[2225,130498,1520],{"class":2235},[2225,130500,3158],{"class":2291},[2225,130502,2789],{"class":2239},[2225,130504,2805],{"class":2235},[2225,130506,129502],{"class":2252},[2225,130508,2805],{"class":2235},[2225,130510,2362],{"class":2239},[2225,130512,130513],{"class":2227,"line":8916},[2225,130514,130515],{"class":2763},"\u002F\u002F → the same record — both devices converged, neither edit was lost\n",[1591,130517,130519,130520],{"id":130518},"ordered-lists-with-rga","Ordered lists with ",[941,130521,21959],{},[937,130523,14370,130524,130526],{},[941,130525,21959],{}," mode the record is an array. Elements get stable ids (ULIDs) on first insertion;\nwriting the array without an element tombstones it instead of deleting it, so concurrent\nremovals and inserts from two devices union without duplicating or resurrecting items:",[2217,130528,130530],{"className":2219,"code":130529,"language":2221,"meta":1037,"style":1037},"const tags = vault.collection\u003Cstring[]>('tags', { crdt: 'rga' })\n\nawait tags.put('rec1', ['alpha', 'beta', 'gamma'])\nawait tags.put('rec1', ['alpha', 'gamma'])   \u002F\u002F remove 'beta' by writing the list without it\n\nawait tags.get('rec1')\n\u002F\u002F → ['alpha', 'gamma']\n\nawait tags.getRaw('rec1')\n",[941,130531,130532,130582,130586,130636,130678,130682,130702,130707,130711],{"__ignoreMap":1037},[2225,130533,130534,130536,130539,130541,130543,130545,130547,130549,130551,130553,130555,130557,130559,130562,130564,130566,130568,130570,130572,130574,130576,130578,130580],{"class":2227,"line":2228},[2225,130535,2775],{"class":2774},[2225,130537,130538],{"class":2239}," tags ",[2225,130540,2781],{"class":2235},[2225,130542,6466],{"class":2239},[2225,130544,1520],{"class":2235},[2225,130546,6471],{"class":2291},[2225,130548,6474],{"class":2235},[2225,130550,7259],{"class":6342},[2225,130552,7262],{"class":2239},[2225,130554,6480],{"class":2235},[2225,130556,2789],{"class":2239},[2225,130558,2805],{"class":2235},[2225,130560,130561],{"class":2252},"tags",[2225,130563,2805],{"class":2235},[2225,130565,2298],{"class":2235},[2225,130567,2236],{"class":2235},[2225,130569,129452],{"class":2307},[2225,130571,2311],{"class":2235},[2225,130573,2249],{"class":2235},[2225,130575,21959],{"class":2252},[2225,130577,2805],{"class":2235},[2225,130579,2243],{"class":2235},[2225,130581,2362],{"class":2239},[2225,130583,130584],{"class":2227,"line":1041},[2225,130585,2285],{"emptyLinePlaceholder":2284},[2225,130587,130588,130590,130592,130594,130596,130598,130600,130603,130605,130607,130609,130611,130614,130616,130618,130620,130623,130625,130627,130629,130632,130634],{"class":2227,"line":1038},[2225,130589,6500],{"class":2231},[2225,130591,120648],{"class":2239},[2225,130593,1520],{"class":2235},[2225,130595,3161],{"class":2291},[2225,130597,2789],{"class":2239},[2225,130599,2805],{"class":2235},[2225,130601,130602],{"class":2252},"rec1",[2225,130604,2805],{"class":2235},[2225,130606,2298],{"class":2235},[2225,130608,8667],{"class":2239},[2225,130610,2805],{"class":2235},[2225,130612,130613],{"class":2252},"alpha",[2225,130615,2805],{"class":2235},[2225,130617,2298],{"class":2235},[2225,130619,2249],{"class":2235},[2225,130621,130622],{"class":2252},"beta",[2225,130624,2805],{"class":2235},[2225,130626,2298],{"class":2235},[2225,130628,2249],{"class":2235},[2225,130630,130631],{"class":2252},"gamma",[2225,130633,2805],{"class":2235},[2225,130635,10880],{"class":2239},[2225,130637,130638,130640,130642,130644,130646,130648,130650,130652,130654,130656,130658,130660,130662,130664,130666,130668,130670,130672,130675],{"class":2227,"line":2288},[2225,130639,6500],{"class":2231},[2225,130641,120648],{"class":2239},[2225,130643,1520],{"class":2235},[2225,130645,3161],{"class":2291},[2225,130647,2789],{"class":2239},[2225,130649,2805],{"class":2235},[2225,130651,130602],{"class":2252},[2225,130653,2805],{"class":2235},[2225,130655,2298],{"class":2235},[2225,130657,8667],{"class":2239},[2225,130659,2805],{"class":2235},[2225,130661,130613],{"class":2252},[2225,130663,2805],{"class":2235},[2225,130665,2298],{"class":2235},[2225,130667,2249],{"class":2235},[2225,130669,130631],{"class":2252},[2225,130671,2805],{"class":2235},[2225,130673,130674],{"class":2239},"])   ",[2225,130676,130677],{"class":2763},"\u002F\u002F remove 'beta' by writing the list without it\n",[2225,130679,130680],{"class":2227,"line":2304},[2225,130681,2285],{"emptyLinePlaceholder":2284},[2225,130683,130684,130686,130688,130690,130692,130694,130696,130698,130700],{"class":2227,"line":2356},[2225,130685,6500],{"class":2231},[2225,130687,120648],{"class":2239},[2225,130689,1520],{"class":2235},[2225,130691,3158],{"class":2291},[2225,130693,2789],{"class":2239},[2225,130695,2805],{"class":2235},[2225,130697,130602],{"class":2252},[2225,130699,2805],{"class":2235},[2225,130701,2362],{"class":2239},[2225,130703,130704],{"class":2227,"line":2830},[2225,130705,130706],{"class":2763},"\u002F\u002F → ['alpha', 'gamma']\n",[2225,130708,130709],{"class":2227,"line":2861},[2225,130710,2285],{"emptyLinePlaceholder":2284},[2225,130712,130713,130715,130717,130719,130721,130723,130725,130727,130729],{"class":2227,"line":4246},[2225,130714,6500],{"class":2231},[2225,130716,120648],{"class":2239},[2225,130718,1520],{"class":2235},[2225,130720,129638],{"class":2291},[2225,130722,2789],{"class":2239},[2225,130724,2805],{"class":2235},[2225,130726,130602],{"class":2252},[2225,130728,2805],{"class":2235},[2225,130730,2362],{"class":2239},[2217,130732,130734],{"className":6604,"code":130733,"language":141,"meta":1037,"style":1037},"{\n  \"_crdt\": \"rga\",\n  \"items\": [\n    { \"nid\": \"01JZGJ3V9GQ2N9W3H4X5T6K7R8\", \"v\": \"alpha\" },\n    { \"nid\": \"01JZGJ3V9SQ0F2B7C8D9E0A1B2\", \"v\": \"gamma\" },\n    { \"nid\": \"01JZGJ3V9J4D6E8F0G2H4J6K8M\", \"v\": \"beta\" }\n  ],\n  \"tombstones\": [\"01JZGJ3V9J4D6E8F0G2H4J6K8M\"]\n}\n",[941,130735,130736,130740,130758,130770,130807,130843,130880,130884,130905],{"__ignoreMap":1037},[2225,130737,130738],{"class":2227,"line":2228},[2225,130739,4200],{"class":2235},[2225,130741,130742,130744,130746,130748,130750,130752,130754,130756],{"class":2227,"line":1041},[2225,130743,9368],{"class":2235},[2225,130745,129664],{"class":2774},[2225,130747,6620],{"class":2235},[2225,130749,2311],{"class":2235},[2225,130751,6614],{"class":2235},[2225,130753,21959],{"class":2252},[2225,130755,6620],{"class":2235},[2225,130757,1707],{"class":2235},[2225,130759,130760,130762,130764,130766,130768],{"class":2227,"line":1038},[2225,130761,9368],{"class":2235},[2225,130763,39195],{"class":2774},[2225,130765,6620],{"class":2235},[2225,130767,2311],{"class":2235},[2225,130769,32491],{"class":2235},[2225,130771,130772,130774,130776,130779,130781,130783,130785,130787,130789,130791,130793,130795,130797,130799,130801,130803,130805],{"class":2227,"line":2288},[2225,130773,32496],{"class":2235},[2225,130775,6614],{"class":2235},[2225,130777,130778],{"class":6342},"nid",[2225,130780,6620],{"class":2235},[2225,130782,2311],{"class":2235},[2225,130784,6614],{"class":2235},[2225,130786,125429],{"class":2252},[2225,130788,6620],{"class":2235},[2225,130790,2298],{"class":2235},[2225,130792,6614],{"class":2235},[2225,130794,8160],{"class":6342},[2225,130796,6620],{"class":2235},[2225,130798,2311],{"class":2235},[2225,130800,6614],{"class":2235},[2225,130802,130613],{"class":2252},[2225,130804,6620],{"class":2235},[2225,130806,2353],{"class":2235},[2225,130808,130809,130811,130813,130815,130817,130819,130821,130823,130825,130827,130829,130831,130833,130835,130837,130839,130841],{"class":2227,"line":2304},[2225,130810,32496],{"class":2235},[2225,130812,6614],{"class":2235},[2225,130814,130778],{"class":6342},[2225,130816,6620],{"class":2235},[2225,130818,2311],{"class":2235},[2225,130820,6614],{"class":2235},[2225,130822,125581],{"class":2252},[2225,130824,6620],{"class":2235},[2225,130826,2298],{"class":2235},[2225,130828,6614],{"class":2235},[2225,130830,8160],{"class":6342},[2225,130832,6620],{"class":2235},[2225,130834,2311],{"class":2235},[2225,130836,6614],{"class":2235},[2225,130838,130631],{"class":2252},[2225,130840,6620],{"class":2235},[2225,130842,2353],{"class":2235},[2225,130844,130845,130847,130849,130851,130853,130855,130857,130860,130862,130864,130866,130868,130870,130872,130874,130876,130878],{"class":2227,"line":2356},[2225,130846,32496],{"class":2235},[2225,130848,6614],{"class":2235},[2225,130850,130778],{"class":6342},[2225,130852,6620],{"class":2235},[2225,130854,2311],{"class":2235},[2225,130856,6614],{"class":2235},[2225,130858,130859],{"class":2252},"01JZGJ3V9J4D6E8F0G2H4J6K8M",[2225,130861,6620],{"class":2235},[2225,130863,2298],{"class":2235},[2225,130865,6614],{"class":2235},[2225,130867,8160],{"class":6342},[2225,130869,6620],{"class":2235},[2225,130871,2311],{"class":2235},[2225,130873,6614],{"class":2235},[2225,130875,130622],{"class":2252},[2225,130877,6620],{"class":2235},[2225,130879,6369],{"class":2235},[2225,130881,130882],{"class":2227,"line":2830},[2225,130883,67686],{"class":2235},[2225,130885,130886,130888,130891,130893,130895,130897,130899,130901,130903],{"class":2227,"line":2861},[2225,130887,9368],{"class":2235},[2225,130889,130890],{"class":2774},"tombstones",[2225,130892,6620],{"class":2235},[2225,130894,2311],{"class":2235},[2225,130896,8667],{"class":2235},[2225,130898,6620],{"class":2235},[2225,130900,130859],{"class":2252},[2225,130902,6620],{"class":2235},[2225,130904,26484],{"class":2235},[2225,130906,130907],{"class":2227,"line":4246},[2225,130908,7351],{"class":2235},[937,130910,130911,130912,130914],{},"Tombstoned items stay in ",[941,130913,39195],{}," to preserve ordering for cross-device merges; the\nresolved snapshot filters them out. Re-inserting an equal value reuses its existing id,\nso element identity is stable across devices.",[1591,130916,130918,130919],{"id":130917},"collaborative-documents-with-yjs","Collaborative documents with ",[941,130920,287],{},[937,130922,130923,130924,130927,130928,130930,130931,130934,130935,90035,130938,130940,130941,1261,130943,130945],{},"For rich-text and structured collaborative editing, use\n",[1007,130925,90029],{"href":129102,"rel":130926},[3371]," through the companion package ",[941,130929,42682],{}," (requires ",[941,130932,130933],{},"yjs >= 13","\nas a peer dependency). ",[941,130936,130937],{},"yjsCollection",[941,130939,90038],{}," collection with\n",[941,130942,42822],{},[941,130944,42885],{},"; the encrypted envelope stores the encoded Yjs update:",[2217,130947,130949],{"className":2219,"code":130948,"language":2221,"meta":1037,"style":1037},"import * as Y from 'yjs'\nimport { yjsCollection, yText, yMap } from '@noy-db\u002Fin-yjs'\n\nconst docs = yjsCollection(vault, 'docs', {\n  yFields: { body: yText(), meta: yMap() },\n})\n\nconst doc = await docs.getYDoc('doc-1')\ndoc.getText('body').insert(0, 'Hello')\ndoc.getMap('meta').set('author', 'alice')\nawait docs.putYDoc('doc-1', doc)\n\nconst reloaded = await docs.getYDoc('doc-1')\nreloaded.getText('body').toString()    \u002F\u002F → 'Hello'\nreloaded.getMap('meta').get('author')  \u002F\u002F → 'alice'\n",[941,130950,130951,130969,130995,130999,131023,131051,131057,131061,131087,131123,131163,131185,131189,131215,131244],{"__ignoreMap":1037},[2225,130952,130953,130955,130957,130959,130961,130963,130965,130967],{"class":2227,"line":2228},[2225,130954,2232],{"class":2231},[2225,130956,2263],{"class":2235},[2225,130958,2266],{"class":2231},[2225,130960,90464],{"class":2239},[2225,130962,2272],{"class":2231},[2225,130964,2249],{"class":2235},[2225,130966,287],{"class":2252},[2225,130968,2256],{"class":2235},[2225,130970,130971,130973,130975,130977,130979,130981,130983,130985,130987,130989,130991,130993],{"class":2227,"line":1041},[2225,130972,2232],{"class":2231},[2225,130974,2236],{"class":2235},[2225,130976,42736],{"class":2239},[2225,130978,2298],{"class":2235},[2225,130980,42741],{"class":2239},[2225,130982,2298],{"class":2235},[2225,130984,90061],{"class":2239},[2225,130986,2243],{"class":2235},[2225,130988,2246],{"class":2231},[2225,130990,2249],{"class":2235},[2225,130992,42682],{"class":2252},[2225,130994,2256],{"class":2235},[2225,130996,130997],{"class":2227,"line":1038},[2225,130998,2285],{"emptyLinePlaceholder":2284},[2225,131000,131001,131003,131005,131007,131009,131011,131013,131015,131017,131019,131021],{"class":2227,"line":2288},[2225,131002,2775],{"class":2774},[2225,131004,90113],{"class":2239},[2225,131006,2781],{"class":2235},[2225,131008,42736],{"class":2291},[2225,131010,30266],{"class":2239},[2225,131012,2298],{"class":2235},[2225,131014,2249],{"class":2235},[2225,131016,69487],{"class":2252},[2225,131018,2805],{"class":2235},[2225,131020,2298],{"class":2235},[2225,131022,2301],{"class":2235},[2225,131024,131025,131027,131029,131031,131033,131035,131037,131039,131041,131043,131045,131047,131049],{"class":2227,"line":2304},[2225,131026,90136],{"class":2307},[2225,131028,2311],{"class":2235},[2225,131030,2236],{"class":2235},[2225,131032,42791],{"class":2307},[2225,131034,2311],{"class":2235},[2225,131036,42741],{"class":2291},[2225,131038,4228],{"class":2239},[2225,131040,2298],{"class":2235},[2225,131042,90153],{"class":2307},[2225,131044,2311],{"class":2235},[2225,131046,90061],{"class":2291},[2225,131048,19132],{"class":2239},[2225,131050,19008],{"class":2235},[2225,131052,131053,131055],{"class":2227,"line":2356},[2225,131054,2359],{"class":2235},[2225,131056,2362],{"class":2239},[2225,131058,131059],{"class":2227,"line":2830},[2225,131060,2285],{"emptyLinePlaceholder":2284},[2225,131062,131063,131065,131067,131069,131071,131073,131075,131077,131079,131081,131083,131085],{"class":2227,"line":2861},[2225,131064,2775],{"class":2774},[2225,131066,42810],{"class":2239},[2225,131068,2781],{"class":2235},[2225,131070,2784],{"class":2231},[2225,131072,90184],{"class":2239},[2225,131074,1520],{"class":2235},[2225,131076,42822],{"class":2291},[2225,131078,2789],{"class":2239},[2225,131080,2805],{"class":2235},[2225,131082,90195],{"class":2252},[2225,131084,2805],{"class":2235},[2225,131086,2362],{"class":2239},[2225,131088,131089,131091,131093,131095,131097,131099,131101,131103,131105,131107,131109,131111,131113,131115,131117,131119,131121],{"class":2227,"line":4246},[2225,131090,42838],{"class":2239},[2225,131092,1520],{"class":2235},[2225,131094,42843],{"class":2291},[2225,131096,2789],{"class":2239},[2225,131098,2805],{"class":2235},[2225,131100,42850],{"class":2252},[2225,131102,2805],{"class":2235},[2225,131104,1381],{"class":2239},[2225,131106,1520],{"class":2235},[2225,131108,42859],{"class":2291},[2225,131110,2789],{"class":2239},[2225,131112,5402],{"class":6527},[2225,131114,2298],{"class":2235},[2225,131116,2249],{"class":2235},[2225,131118,90543],{"class":2252},[2225,131120,2805],{"class":2235},[2225,131122,2362],{"class":2239},[2225,131124,131125,131127,131129,131131,131133,131135,131137,131139,131141,131143,131145,131147,131149,131151,131153,131155,131157,131159,131161],{"class":2227,"line":4253},[2225,131126,42838],{"class":2239},[2225,131128,1520],{"class":2235},[2225,131130,90248],{"class":2291},[2225,131132,2789],{"class":2239},[2225,131134,2805],{"class":2235},[2225,131136,17587],{"class":2252},[2225,131138,2805],{"class":2235},[2225,131140,1381],{"class":2239},[2225,131142,1520],{"class":2235},[2225,131144,8528],{"class":2291},[2225,131146,2789],{"class":2239},[2225,131148,2805],{"class":2235},[2225,131150,90269],{"class":2252},[2225,131152,2805],{"class":2235},[2225,131154,2298],{"class":2235},[2225,131156,2249],{"class":2235},[2225,131158,6408],{"class":2252},[2225,131160,2805],{"class":2235},[2225,131162,2362],{"class":2239},[2225,131164,131165,131167,131169,131171,131173,131175,131177,131179,131181,131183],{"class":2227,"line":4259},[2225,131166,6500],{"class":2231},[2225,131168,90184],{"class":2239},[2225,131170,1520],{"class":2235},[2225,131172,42885],{"class":2291},[2225,131174,2789],{"class":2239},[2225,131176,2805],{"class":2235},[2225,131178,90195],{"class":2252},[2225,131180,2805],{"class":2235},[2225,131182,2298],{"class":2235},[2225,131184,42898],{"class":2239},[2225,131186,131187],{"class":2227,"line":6548},[2225,131188,2285],{"emptyLinePlaceholder":2284},[2225,131190,131191,131193,131195,131197,131199,131201,131203,131205,131207,131209,131211,131213],{"class":2227,"line":6576},[2225,131192,2775],{"class":2774},[2225,131194,90314],{"class":2239},[2225,131196,2781],{"class":2235},[2225,131198,2784],{"class":2231},[2225,131200,90184],{"class":2239},[2225,131202,1520],{"class":2235},[2225,131204,42822],{"class":2291},[2225,131206,2789],{"class":2239},[2225,131208,2805],{"class":2235},[2225,131210,90195],{"class":2252},[2225,131212,2805],{"class":2235},[2225,131214,2362],{"class":2239},[2225,131216,131217,131220,131222,131224,131226,131228,131230,131232,131234,131236,131238,131241],{"class":2227,"line":6582},[2225,131218,131219],{"class":2239},"reloaded",[2225,131221,1520],{"class":2235},[2225,131223,42843],{"class":2291},[2225,131225,2789],{"class":2239},[2225,131227,2805],{"class":2235},[2225,131229,42850],{"class":2252},[2225,131231,2805],{"class":2235},[2225,131233,1381],{"class":2239},[2225,131235,1520],{"class":2235},[2225,131237,80541],{"class":2291},[2225,131239,131240],{"class":2239},"()    ",[2225,131242,131243],{"class":2763},"\u002F\u002F → 'Hello'\n",[2225,131245,131246,131248,131250,131252,131254,131256,131258,131260,131262,131264,131266,131268,131270,131272,131274,131276],{"class":2227,"line":6587},[2225,131247,131219],{"class":2239},[2225,131249,1520],{"class":2235},[2225,131251,90248],{"class":2291},[2225,131253,2789],{"class":2239},[2225,131255,2805],{"class":2235},[2225,131257,17587],{"class":2252},[2225,131259,2805],{"class":2235},[2225,131261,1381],{"class":2239},[2225,131263,1520],{"class":2235},[2225,131265,3158],{"class":2291},[2225,131267,2789],{"class":2239},[2225,131269,2805],{"class":2235},[2225,131271,90269],{"class":2252},[2225,131273,2805],{"class":2235},[2225,131275,27226],{"class":2239},[2225,131277,131278],{"class":2763},"\u002F\u002F → 'alice'\n",[937,131280,131281],{},"Two collaborators forking from the same baseline converge by exchanging Yjs updates —\nthe standard Yjs contract, preserved through encrypted storage:",[2217,131283,131285],{"className":2219,"code":131284,"language":2221,"meta":1037,"style":1037},"const docA = await docs.getYDoc('doc-1')\ndocA.getText('body').insert(docA.getText('body').length, ' from Alice')\n\nconst docB = await docs.getYDoc('doc-1')\ndocB.getText('body').insert(0, 'Hi! ')\n\nY.applyUpdate(docA, Y.encodeStateAsUpdate(docB))\nY.applyUpdate(docB, Y.encodeStateAsUpdate(docA))\n\ndocA.getText('body').toString()   \u002F\u002F → 'Hi! Hello from Alice'\ndocB.getText('body').toString()   \u002F\u002F → 'Hi! Hello from Alice' — converged\n\nawait docs.putYDoc('doc-1', docA)\n",[941,131286,131287,131313,131365,131369,131395,131431,131435,131455,131475,131479,131506,131533,131537],{"__ignoreMap":1037},[2225,131288,131289,131291,131293,131295,131297,131299,131301,131303,131305,131307,131309,131311],{"class":2227,"line":2228},[2225,131290,2775],{"class":2774},[2225,131292,90586],{"class":2239},[2225,131294,2781],{"class":2235},[2225,131296,2784],{"class":2231},[2225,131298,90184],{"class":2239},[2225,131300,1520],{"class":2235},[2225,131302,42822],{"class":2291},[2225,131304,2789],{"class":2239},[2225,131306,2805],{"class":2235},[2225,131308,90195],{"class":2252},[2225,131310,2805],{"class":2235},[2225,131312,2362],{"class":2239},[2225,131314,131315,131317,131319,131321,131323,131325,131327,131329,131331,131333,131335,131337,131339,131341,131343,131345,131347,131349,131351,131353,131355,131357,131359,131361,131363],{"class":2227,"line":1041},[2225,131316,90611],{"class":2239},[2225,131318,1520],{"class":2235},[2225,131320,42843],{"class":2291},[2225,131322,2789],{"class":2239},[2225,131324,2805],{"class":2235},[2225,131326,42850],{"class":2252},[2225,131328,2805],{"class":2235},[2225,131330,1381],{"class":2239},[2225,131332,1520],{"class":2235},[2225,131334,42859],{"class":2291},[2225,131336,90632],{"class":2239},[2225,131338,1520],{"class":2235},[2225,131340,42843],{"class":2291},[2225,131342,2789],{"class":2239},[2225,131344,2805],{"class":2235},[2225,131346,42850],{"class":2252},[2225,131348,2805],{"class":2235},[2225,131350,1381],{"class":2239},[2225,131352,1520],{"class":2235},[2225,131354,8742],{"class":2239},[2225,131356,2298],{"class":2235},[2225,131358,2249],{"class":2235},[2225,131360,90657],{"class":2252},[2225,131362,2805],{"class":2235},[2225,131364,2362],{"class":2239},[2225,131366,131367],{"class":2227,"line":1038},[2225,131368,2285],{"emptyLinePlaceholder":2284},[2225,131370,131371,131373,131375,131377,131379,131381,131383,131385,131387,131389,131391,131393],{"class":2227,"line":2288},[2225,131372,2775],{"class":2774},[2225,131374,90677],{"class":2239},[2225,131376,2781],{"class":2235},[2225,131378,2784],{"class":2231},[2225,131380,90184],{"class":2239},[2225,131382,1520],{"class":2235},[2225,131384,42822],{"class":2291},[2225,131386,2789],{"class":2239},[2225,131388,2805],{"class":2235},[2225,131390,90195],{"class":2252},[2225,131392,2805],{"class":2235},[2225,131394,2362],{"class":2239},[2225,131396,131397,131399,131401,131403,131405,131407,131409,131411,131413,131415,131417,131419,131421,131423,131425,131427,131429],{"class":2227,"line":2304},[2225,131398,90702],{"class":2239},[2225,131400,1520],{"class":2235},[2225,131402,42843],{"class":2291},[2225,131404,2789],{"class":2239},[2225,131406,2805],{"class":2235},[2225,131408,42850],{"class":2252},[2225,131410,2805],{"class":2235},[2225,131412,1381],{"class":2239},[2225,131414,1520],{"class":2235},[2225,131416,42859],{"class":2291},[2225,131418,2789],{"class":2239},[2225,131420,5402],{"class":6527},[2225,131422,2298],{"class":2235},[2225,131424,2249],{"class":2235},[2225,131426,90731],{"class":2252},[2225,131428,2805],{"class":2235},[2225,131430,2362],{"class":2239},[2225,131432,131433],{"class":2227,"line":2356},[2225,131434,2285],{"emptyLinePlaceholder":2284},[2225,131436,131437,131439,131441,131443,131445,131447,131449,131451,131453],{"class":2227,"line":2830},[2225,131438,90749],{"class":2239},[2225,131440,1520],{"class":2235},[2225,131442,90754],{"class":2291},[2225,131444,90632],{"class":2239},[2225,131446,2298],{"class":2235},[2225,131448,90761],{"class":2239},[2225,131450,1520],{"class":2235},[2225,131452,90766],{"class":2291},[2225,131454,90769],{"class":2239},[2225,131456,131457,131459,131461,131463,131465,131467,131469,131471,131473],{"class":2227,"line":2861},[2225,131458,90749],{"class":2239},[2225,131460,1520],{"class":2235},[2225,131462,90754],{"class":2291},[2225,131464,90780],{"class":2239},[2225,131466,2298],{"class":2235},[2225,131468,90761],{"class":2239},[2225,131470,1520],{"class":2235},[2225,131472,90766],{"class":2291},[2225,131474,90791],{"class":2239},[2225,131476,131477],{"class":2227,"line":4246},[2225,131478,2285],{"emptyLinePlaceholder":2284},[2225,131480,131481,131483,131485,131487,131489,131491,131493,131495,131497,131499,131501,131503],{"class":2227,"line":4253},[2225,131482,90611],{"class":2239},[2225,131484,1520],{"class":2235},[2225,131486,42843],{"class":2291},[2225,131488,2789],{"class":2239},[2225,131490,2805],{"class":2235},[2225,131492,42850],{"class":2252},[2225,131494,2805],{"class":2235},[2225,131496,1381],{"class":2239},[2225,131498,1520],{"class":2235},[2225,131500,80541],{"class":2291},[2225,131502,39880],{"class":2239},[2225,131504,131505],{"class":2763},"\u002F\u002F → 'Hi! Hello from Alice'\n",[2225,131507,131508,131510,131512,131514,131516,131518,131520,131522,131524,131526,131528,131530],{"class":2227,"line":4259},[2225,131509,90702],{"class":2239},[2225,131511,1520],{"class":2235},[2225,131513,42843],{"class":2291},[2225,131515,2789],{"class":2239},[2225,131517,2805],{"class":2235},[2225,131519,42850],{"class":2252},[2225,131521,2805],{"class":2235},[2225,131523,1381],{"class":2239},[2225,131525,1520],{"class":2235},[2225,131527,80541],{"class":2291},[2225,131529,39880],{"class":2239},[2225,131531,131532],{"class":2763},"\u002F\u002F → 'Hi! Hello from Alice' — converged\n",[2225,131534,131535],{"class":2227,"line":6548},[2225,131536,2285],{"emptyLinePlaceholder":2284},[2225,131538,131539,131541,131543,131545,131547,131549,131551,131553,131555,131557],{"class":2227,"line":6576},[2225,131540,6500],{"class":2231},[2225,131542,90184],{"class":2239},[2225,131544,1520],{"class":2235},[2225,131546,42885],{"class":2291},[2225,131548,2789],{"class":2239},[2225,131550,2805],{"class":2235},[2225,131552,90195],{"class":2252},[2225,131554,2805],{"class":2235},[2225,131556,2298],{"class":2235},[2225,131558,90921],{"class":2239},[1022,131560,101012],{"id":41259},[1591,131562,131564],{"id":131563},"withcrdt",[941,131565,21950],{},[2217,131567,131569],{"className":2219,"code":131568,"language":2221,"meta":1037,"style":1037},"function withCrdt(): CrdtStrategy\n",[941,131570,131571],{"__ignoreMap":1037},[2225,131572,131573,131575,131577,131579],{"class":2227,"line":2228},[2225,131574,12641],{"class":2774},[2225,131576,89560],{"class":2291},[2225,131578,112812],{"class":2235},[2225,131580,131581],{"class":6342}," CrdtStrategy\n",[937,131583,131584,131585,10313,131587,131589],{},"Builds the active CRDT strategy. Pass it as ",[941,131586,73015],{},[941,131588,12780],{}," — this is the\nonly wiring step. Takes no options.",[1591,131591,131593],{"id":131592},"vaultcollectionname-crdt",[941,131594,131595],{},"vault.collection(name, { crdt })",[2217,131597,131599],{"className":2219,"code":131598,"language":2221,"meta":1037,"style":1037},"vault.collection\u003CT>(name, { crdt: 'lww-map' | 'rga' | 'yjs' })\n",[941,131600,131601],{"__ignoreMap":1037},[2225,131602,131603,131605,131607,131609,131611,131613,131615,131617,131619,131621,131623,131625,131627,131629,131631,131633,131635,131637,131639,131641,131643,131645,131647,131649],{"class":2227,"line":2228},[2225,131604,2694],{"class":2239},[2225,131606,1520],{"class":2235},[2225,131608,6471],{"class":2291},[2225,131610,6474],{"class":2235},[2225,131612,11968],{"class":6342},[2225,131614,6480],{"class":2235},[2225,131616,123977],{"class":2239},[2225,131618,2298],{"class":2235},[2225,131620,2236],{"class":2235},[2225,131622,129452],{"class":2307},[2225,131624,2311],{"class":2235},[2225,131626,2249],{"class":2235},[2225,131628,21956],{"class":2252},[2225,131630,2805],{"class":2235},[2225,131632,7087],{"class":2235},[2225,131634,2249],{"class":2235},[2225,131636,21959],{"class":2252},[2225,131638,2805],{"class":2235},[2225,131640,7087],{"class":2235},[2225,131642,2249],{"class":2235},[2225,131644,287],{"class":2252},[2225,131646,2805],{"class":2235},[2225,131648,2243],{"class":2235},[2225,131650,2362],{"class":2239},[937,131652,131653,131654,131656,131657,131659,131660,2462,131662,131664,131665,131667,131668,131670],{},"Declares the collection's CRDT mode at construction. ",[941,131655,11968],{}," is the resolved snapshot type:\na record object for ",[941,131658,21956],{},", an array for ",[941,131661,21959],{},[941,131663,7259],{}," (the base64-encoded Yjs\nupdate) for ",[941,131666,287],{},". When ",[1007,131669,864],{"href":865}," is also enabled, the collection registers\na CRDT conflict resolver with the sync engine automatically.",[1591,131672,131674],{"id":131673},"collectiongetrawid",[941,131675,129122],{},[2217,131677,131679],{"className":2219,"code":131678,"language":2221,"meta":1037,"style":1037},"collection.getRaw(id: string): Promise\u003CCrdtState | null>\n",[941,131680,131681],{"__ignoreMap":1037},[2225,131682,131683,131685,131687,131689,131692,131694,131696,131699,131701],{"class":2227,"line":2228},[2225,131684,6471],{"class":2239},[2225,131686,1520],{"class":2235},[2225,131688,129638],{"class":2291},[2225,131690,131691],{"class":2239},"(id: string): ",[2225,131693,11740],{"class":6342},[2225,131695,6474],{"class":2235},[2225,131697,131698],{"class":2239},"CrdtState ",[2225,131700,18307],{"class":2235},[2225,131702,24020],{"class":2235},[937,131704,131705,131706,131708,131709,131711,131712,131715],{},"Returns the raw CRDT state for a record (",[941,131707,6651],{}," if absent). Only available on collections\ndeclared with a ",[941,131710,653],{}," option — on a plain collection it throws. Use it to inspect merge\nstate or to feed external merge tooling; normal reads go through ",[941,131713,131714],{},"get(id)",", which returns\nthe resolved snapshot.",[1591,131717,131719],{"id":131718},"merge-helpers","Merge helpers",[937,131721,131722,131723,131725],{},"The building blocks ",[941,131724,21950],{}," wires in are also exported for advanced use (custom\nstrategies, offline merge tooling):",[1338,131727,131728,131739],{},[1341,131729,131730],{},[1344,131731,131732,131734,131737],{},[1347,131733,72632],{},[1347,131735,131736],{},"Signature",[1347,131738,7834],{},[1357,131740,131741,131759,131774,131807],{},[1344,131742,131743,131748,131753],{},[1362,131744,131745],{},[941,131746,131747],{},"buildLwwMapState",[1362,131749,131750],{},[941,131751,131752],{},"(record, previous, now: string) => LwwMapState",[1362,131754,131755,131756,131758],{},"Fold a new record into an existing per-field state; fields absent from ",[941,131757,24158],{}," are preserved.",[1344,131760,131761,131766,131771],{},[1362,131762,131763],{},[941,131764,131765],{},"buildRgaState",[1362,131767,131768],{},[941,131769,131770],{},"(items, previous, idGen: () => string) => RgaState",[1362,131772,131773],{},"Fold a new array into an existing RGA state; unmatched existing items are tombstoned.",[1344,131775,131776,131781,131786],{},[1362,131777,131778],{},[941,131779,131780],{},"mergeCrdtStates",[1362,131782,131783],{},[941,131784,131785],{},"(local, remote) => CrdtState",[1362,131787,131788,131789,131791,131792,131794,131795,131797,131798,131800,131801,131803,131804,131806],{},"Merge two concurrent states of the same mode. For ",[941,131790,21956],{},", the higher ",[941,131793,2221],{}," wins per field (ties favor ",[941,131796,49766],{},"); for ",[941,131799,21959],{},", items and tombstones union. For ",[941,131802,287],{}," it returns ",[941,131805,49766],{}," unchanged — core cannot merge Yjs bytes.",[1344,131808,131809,131814,131819],{},[1362,131810,131811],{},[941,131812,131813],{},"resolveCrdtSnapshot",[1362,131815,131816],{},[941,131817,131818],{},"(state) => unknown",[1362,131820,131821,131822,131824,131825,131827,131828,1520],{},"State → user-facing snapshot: field map for ",[941,131823,21956],{},", live-item array for ",[941,131826,21959],{},", base64 update string for ",[941,131829,287],{},[2217,131831,131833],{"className":2219,"code":131832,"language":2221,"meta":1037,"style":1037},"import { mergeCrdtStates, resolveCrdtSnapshot } from '@noy-db\u002Fhub\u002Fcrdt'\n\nconst merged = mergeCrdtStates(localState, remoteState)\nresolveCrdtSnapshot(merged)\n\u002F\u002F → the converged record snapshot\n",[941,131834,131835,131859,131863,131882,131889],{"__ignoreMap":1037},[2225,131836,131837,131839,131841,131844,131846,131849,131851,131853,131855,131857],{"class":2227,"line":2228},[2225,131838,2232],{"class":2231},[2225,131840,2236],{"class":2235},[2225,131842,131843],{"class":2239}," mergeCrdtStates",[2225,131845,2298],{"class":2235},[2225,131847,131848],{"class":2239}," resolveCrdtSnapshot",[2225,131850,2243],{"class":2235},[2225,131852,2246],{"class":2231},[2225,131854,2249],{"class":2235},[2225,131856,73018],{"class":2252},[2225,131858,2256],{"class":2235},[2225,131860,131861],{"class":2227,"line":1041},[2225,131862,2285],{"emptyLinePlaceholder":2284},[2225,131864,131865,131867,131870,131872,131874,131877,131879],{"class":2227,"line":1038},[2225,131866,2775],{"class":2774},[2225,131868,131869],{"class":2239}," merged ",[2225,131871,2781],{"class":2235},[2225,131873,131843],{"class":2291},[2225,131875,131876],{"class":2239},"(localState",[2225,131878,2298],{"class":2235},[2225,131880,131881],{"class":2239}," remoteState)\n",[2225,131883,131884,131886],{"class":2227,"line":2288},[2225,131885,131813],{"class":2291},[2225,131887,131888],{"class":2239},"(merged)\n",[2225,131890,131891],{"class":2227,"line":2304},[2225,131892,131893],{"class":2763},"\u002F\u002F → the converged record snapshot\n",[1591,131895,103205],{"id":128135},[2217,131897,131899],{"className":2219,"code":131898,"language":2221,"meta":1037,"style":1037},"type CrdtMode  = 'lww-map' | 'rga' | 'yjs'\ntype CrdtState = LwwMapState | RgaState | YjsState\n\ninterface LwwMapState {\n  _crdt: 'lww-map'\n  fields: Record\u003Cstring, { v: unknown; ts: string }>   \u002F\u002F ts = ISO timestamp of last write\n}\n\ninterface RgaState {\n  _crdt: 'rga'\n  items: Array\u003C{ nid: string; v: unknown }>   \u002F\u002F nid = stable ULID per element\n  tombstones: string[]                        \u002F\u002F nids of removed elements\n}\n\ninterface YjsState {\n  _crdt: 'yjs'\n  update: string   \u002F\u002F base64(Y.encodeStateAsUpdate(doc))\n}\n",[941,131900,131901,131933,131955,131959,131967,131980,132016,132020,132024,132032,132044,132075,132090,132094,132098,132107,132119,132131],{"__ignoreMap":1037},[2225,131902,131903,131905,131908,131911,131913,131915,131917,131919,131921,131923,131925,131927,131929,131931],{"class":2227,"line":2228},[2225,131904,6339],{"class":2774},[2225,131906,131907],{"class":6342}," CrdtMode",[2225,131909,131910],{"class":2235},"  =",[2225,131912,2249],{"class":2235},[2225,131914,21956],{"class":2252},[2225,131916,2805],{"class":2235},[2225,131918,7087],{"class":2235},[2225,131920,2249],{"class":2235},[2225,131922,21959],{"class":2252},[2225,131924,2805],{"class":2235},[2225,131926,7087],{"class":2235},[2225,131928,2249],{"class":2235},[2225,131930,287],{"class":2252},[2225,131932,2256],{"class":2235},[2225,131934,131935,131937,131940,131942,131945,131947,131950,131952],{"class":2227,"line":1041},[2225,131936,6339],{"class":2774},[2225,131938,131939],{"class":6342}," CrdtState",[2225,131941,6346],{"class":2235},[2225,131943,131944],{"class":6342}," LwwMapState",[2225,131946,7087],{"class":2235},[2225,131948,131949],{"class":6342}," RgaState",[2225,131951,7087],{"class":2235},[2225,131953,131954],{"class":6342}," YjsState\n",[2225,131956,131957],{"class":2227,"line":1038},[2225,131958,2285],{"emptyLinePlaceholder":2284},[2225,131960,131961,131963,131965],{"class":2227,"line":2288},[2225,131962,6994],{"class":2774},[2225,131964,131944],{"class":6342},[2225,131966,2301],{"class":2235},[2225,131968,131969,131972,131974,131976,131978],{"class":2227,"line":2304},[2225,131970,131971],{"class":2307},"  _crdt",[2225,131973,2311],{"class":2235},[2225,131975,2249],{"class":2235},[2225,131977,21956],{"class":2252},[2225,131979,2256],{"class":2235},[2225,131981,131982,131984,131986,131988,131990,131992,131994,131996,131998,132000,132002,132004,132007,132009,132011,132013],{"class":2227,"line":2356},[2225,131983,57522],{"class":2307},[2225,131985,2311],{"class":2235},[2225,131987,22527],{"class":6342},[2225,131989,6474],{"class":2235},[2225,131991,7259],{"class":6342},[2225,131993,2298],{"class":2235},[2225,131995,2236],{"class":2235},[2225,131997,8866],{"class":2307},[2225,131999,2311],{"class":2235},[2225,132001,40717],{"class":6342},[2225,132003,3769],{"class":2235},[2225,132005,132006],{"class":2307}," ts",[2225,132008,2311],{"class":2235},[2225,132010,6366],{"class":6342},[2225,132012,85293],{"class":2235},[2225,132014,132015],{"class":2763},"   \u002F\u002F ts = ISO timestamp of last write\n",[2225,132017,132018],{"class":2227,"line":2830},[2225,132019,7351],{"class":2235},[2225,132021,132022],{"class":2227,"line":2861},[2225,132023,2285],{"emptyLinePlaceholder":2284},[2225,132025,132026,132028,132030],{"class":2227,"line":4246},[2225,132027,6994],{"class":2774},[2225,132029,131949],{"class":6342},[2225,132031,2301],{"class":2235},[2225,132033,132034,132036,132038,132040,132042],{"class":2227,"line":4253},[2225,132035,131971],{"class":2307},[2225,132037,2311],{"class":2235},[2225,132039,2249],{"class":2235},[2225,132041,21959],{"class":2252},[2225,132043,2256],{"class":2235},[2225,132045,132046,132049,132051,132053,132055,132058,132060,132062,132064,132066,132068,132070,132072],{"class":2227,"line":4259},[2225,132047,132048],{"class":2307},"  items",[2225,132050,2311],{"class":2235},[2225,132052,66498],{"class":6342},[2225,132054,17851],{"class":2235},[2225,132056,132057],{"class":2307}," nid",[2225,132059,2311],{"class":2235},[2225,132061,6366],{"class":6342},[2225,132063,3769],{"class":2235},[2225,132065,8866],{"class":2307},[2225,132067,2311],{"class":2235},[2225,132069,40717],{"class":6342},[2225,132071,85293],{"class":2235},[2225,132073,132074],{"class":2763},"   \u002F\u002F nid = stable ULID per element\n",[2225,132076,132077,132080,132082,132084,132087],{"class":2227,"line":6548},[2225,132078,132079],{"class":2307},"  tombstones",[2225,132081,2311],{"class":2235},[2225,132083,6366],{"class":6342},[2225,132085,132086],{"class":2239},"[]                        ",[2225,132088,132089],{"class":2763},"\u002F\u002F nids of removed elements\n",[2225,132091,132092],{"class":2227,"line":6576},[2225,132093,7351],{"class":2235},[2225,132095,132096],{"class":2227,"line":6582},[2225,132097,2285],{"emptyLinePlaceholder":2284},[2225,132099,132100,132102,132105],{"class":2227,"line":6587},[2225,132101,6994],{"class":2774},[2225,132103,132104],{"class":6342}," YjsState",[2225,132106,2301],{"class":2235},[2225,132108,132109,132111,132113,132115,132117],{"class":2227,"line":7174},[2225,132110,131971],{"class":2307},[2225,132112,2311],{"class":2235},[2225,132114,2249],{"class":2235},[2225,132116,287],{"class":2252},[2225,132118,2256],{"class":2235},[2225,132120,132121,132124,132126,132128],{"class":2227,"line":7180},[2225,132122,132123],{"class":2307},"  update",[2225,132125,2311],{"class":2235},[2225,132127,6366],{"class":6342},[2225,132129,132130],{"class":2763},"   \u002F\u002F base64(Y.encodeStateAsUpdate(doc))\n",[2225,132132,132133],{"class":2227,"line":7220},[2225,132134,7351],{"class":2235},[1591,132136,132138],{"id":132137},"noy-dbin-yjs",[941,132139,42682],{},[937,132141,132142,132143,132145,132146,132149,132150,132152],{},"The Yjs bridge is a separate package of the ",[941,132144,1270],{}," family (framework\u002Flibrary bindings —\nsee ",[1007,132147,132148],{"href":218},"the in- family","), with ",[941,132151,130933],{}," as a peer dependency:",[1338,132154,132155,132163],{},[1341,132156,132157],{},[1344,132158,132159,132161],{},[1347,132160,72632],{},[1347,132162,7834],{},[1357,132164,132165,132182,132199,132209,132222,132235,132261],{},[1344,132166,132167,132172],{},[1362,132168,132169],{},[941,132170,132171],{},"yjsCollection(vault, name, { yFields })",[1362,132173,132174,132175,132178,132179,132181],{},"Create a ",[941,132176,132177],{},"YjsCollection"," wrapping a ",[941,132180,90038],{}," collection.",[1344,132183,132184,132189],{},[1362,132185,132186],{},[941,132187,132188],{},"YjsCollection.getYDoc(id)",[1362,132190,132191,132192,132194,132195,132198],{},"Decode the stored update into a ",[941,132193,37813],{}," (fresh doc with declared ",[941,132196,132197],{},"yFields"," initialised if the record doesn't exist).",[1344,132200,132201,132206],{},[1362,132202,132203],{},[941,132204,132205],{},"YjsCollection.putYDoc(id, doc)",[1362,132207,132208],{},"Encode the doc's state as a Yjs update and store it encrypted.",[1344,132210,132211,132216],{},[1362,132212,132213],{},[941,132214,132215],{},"YjsCollection.applyUpdate(id, update)",[1362,132217,132218,132219,132221],{},"Read the current doc, apply a raw ",[941,132220,45820],{}," update, persist the merged state.",[1344,132223,132224,132232],{},[1362,132225,132226,1261,132229],{},[941,132227,132228],{},"YjsCollection.delete(id)",[941,132230,132231],{},"has(id)",[1362,132233,132234],{},"Delete \u002F existence check.",[1344,132236,132237,132245],{},[1362,132238,132239,1261,132241,1261,132243],{},[941,132240,42700],{},[941,132242,42703],{},[941,132244,42706],{},[1362,132246,132247,132248,1261,132251,1261,132254,132257,132258,132260],{},"Field descriptors declaring which ",[941,132249,132250],{},"Y.Text",[941,132252,132253],{},"Y.Map",[941,132255,132256],{},"Y.Array"," roots ",[941,132259,42822],{}," initialises.",[1344,132262,132263,132265],{},[1362,132264,103205],{},[1362,132266,132267,968,132270,968,132273,968,132276,968,132279,968,132282,1520],{},[941,132268,132269],{},"YjsCollectionOptions",[941,132271,132272],{},"YjsSnapshot",[941,132274,132275],{},"YFieldDescriptor",[941,132277,132278],{},"YTextDescriptor",[941,132280,132281],{},"YMapDescriptor",[941,132283,132284],{},"YArrayDescriptor",[1022,132286,101425],{"id":101424},[1111,132288,132289,132299,132302],{},[1114,132290,132291,132292,132295,132296,1520],{},"A collection declared with ",[941,132293,132294],{},"crdt: '…'"," throws on its first CRDT operation:\n",[941,132297,132298],{},"CRDT mode requires the CRDT strategy. Import { withCrdt } from \"@noy-db\u002Fhub\u002Fcrdt\" and pass it to createNoydb({ crdtStrategy: withCrdt() })",[1114,132300,132301],{},"The sync engine's CRDT merge path throws the same way when a conflict reaches a CRDT\ncollection.",[1114,132303,132304],{},"Plain (non-CRDT) collections work unchanged, and none of the ~221 LOC ships in your\nbundle.",[1022,132306,68210],{"id":68209},[1111,132308,132309,132323,132336,132344,132359,132365],{},[1114,132310,132311,132316,132317,132319,132320,132322],{},[950,132312,132313,132315],{},[941,132314,21956],{}," timestamps are wall-clock."," Each field's ",[941,132318,2221],{}," is the writing device's ISO\ntimestamp, compared lexicographically; ties favor the local side during a sync merge.\nSignificant clock skew between devices can make the \"wrong\" edit win a field — keep\ndevice clocks sane, or use ",[941,132321,287],{}," mode where merge is clock-independent.",[1114,132324,132325,132330,132331,132333,132334,1520],{},[950,132326,132327,132328,1520],{},"Whole-field granularity in ",[941,132329,21956],{}," Two devices editing the ",[1092,132332,4664],{}," field\nconcurrently still resolve to one winner — per-field LWW merges across fields, not\nwithin one. For character-level text merging use ",[941,132335,287],{},[1114,132337,132338,132343],{},[950,132339,132340,132342],{},[941,132341,21959],{}," matches elements by value."," Existing items are matched to new array elements\nby deep equality, so equal duplicates share identity across writes. Tombstoned items\nare retained in the state to preserve ordering, so heavily-churned lists grow over time.",[1114,132345,132346,132349,132350,132352,132353,4765,132355,132358],{},[950,132347,132348],{},"Core never merges Yjs bytes."," On a sync conflict in ",[941,132351,287],{}," mode, core keeps the\nenvelope with the higher version — true Yjs merging happens in your app by exchanging\nupdates (",[941,132354,89391],{},[941,132356,132357],{},"YjsCollection.applyUpdate","), as shown above.",[1114,132360,132361,132364],{},[950,132362,132363],{},"Yjs state grows with edit history."," The stored update encodes the document's full\nhistory; consider periodically writing a fresh doc for hot documents.",[1114,132366,132367,7509,132372,132374],{},[950,132368,132369,132371],{},[941,132370,130933],{}," is a peer dependency",[941,132373,42682],{}," — your app installs it.",[1022,132376,2621],{"id":2620},[1111,132378,132379,132386,132391,132396,132401,132406],{},[1114,132380,132381,1368,132383],{},[1007,132382,61743],{"href":598},[1007,132384,132385],{"href":573},"Recipe — Real-time collaborative app",[1114,132387,132388,132390],{},[1007,132389,864],{"href":865}," — the replication engine that triggers CRDT merges on push\u002Fpull conflicts",[1114,132392,132393,132395],{},[1007,132394,727],{"href":728}," — reactive subscriptions; CRDT-merged updates surface through live queries",[1114,132397,132398,132400],{},[1007,132399,697],{"href":698}," — CRDT collections snapshot the prior version on every merge write",[1114,132402,132403,132405],{},[1007,132404,1516],{"href":66}," — why the store only ever sees the encrypted CRDT state",[1114,132407,101581,132408,1520],{},[1007,132409,132411],{"href":92274,"rel":132410},[3371],[941,132412,132413],{},"14-with-crdt",[2645,132415,132416],{},"html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}html pre.shiki code .sfNiH, html code.shiki .sfNiH{--shiki-light:#FF5370;--shiki-default:#FF9CAC;--shiki-dark:#FF9CAC}",{"title":1037,"searchDepth":1038,"depth":1038,"links":132418},[132419,132420,132421,132422,132430,132438,132439,132440],{"id":29856,"depth":1041,"text":29857},{"id":70092,"depth":1041,"text":70093},{"id":99863,"depth":1041,"text":99864},{"id":29892,"depth":1041,"text":29893,"children":132423},[132424,132426,132428],{"id":129469,"depth":1038,"text":132425},"Per-field merge with lww-map",{"id":130518,"depth":1038,"text":132427},"Ordered lists with rga",{"id":130917,"depth":1038,"text":132429},"Collaborative documents with yjs",{"id":41259,"depth":1041,"text":101012,"children":132431},[132432,132433,132434,132435,132436,132437],{"id":131563,"depth":1038,"text":21950},{"id":131592,"depth":1038,"text":131595},{"id":131673,"depth":1038,"text":129122},{"id":131718,"depth":1038,"text":131719},{"id":128135,"depth":1038,"text":103205},{"id":132137,"depth":1038,"text":42682},{"id":101424,"depth":1041,"text":101425},{"id":68209,"depth":1041,"text":68210},{"id":2620,"depth":1041,"text":2621},"Conflict-free replicated types: declare `crdt: 'lww-map' | 'rga' | 'yjs'` per collection and concurrent edits merge deterministically.",{},{"title":653,"description":132441},"kwLeyTj0wk8QAnwipMiKCoktLGoxsH2_8_gGO9u1b6s",{"id":132446,"title":657,"access":12,"apiToc":929,"body":132447,"category":1042,"description":135185,"extension":1044,"meta":135186,"navigation":1042,"nest":1047,"path":658,"seo":135187,"sinceVersion":1042,"status":1049,"stem":659,"subtitle":1042,"tier":1050,"tocExternal":1042,"__hash__":135188},"docs\u002Fservices\u002Fcross-join.md",{"type":931,"value":132448,"toc":135168},[132449,132451,132454,132456,132482,132492,132501,132503,132522,132659,132661,132667,133158,133162,133170,133252,133491,133495,133510,133734,133812,133824,133828,133836,133861,134083,134087,134103,134309,134313,134339,134615,134635,134637,134643,134806,134824,134896,134903,134959,134961,134966,135012,135014,135108,135111,135113,135165],[934,132450,657],{"id":657},[46871,132452],{":rows":132453},"[{\"label\":\"Subpath\",\"value\":\"`@noy-db\u002Fhub` (query builder — no separate import)\"},{\"label\":\"Type\",\"value\":\"Core feature — `.crossJoin()` is a clause on every `collection.query()`.\"}]",[1022,132455,29857],{"id":29856},[937,132457,132458,132459,132462,132463,132466,132467,132470,132471,132473,132474,132477,132478,132481],{},"In noy-db, records are encrypted before they reach any storage backend, and queries run ",[950,132460,132461],{},"inside the hub, after decryption"," — the store only ever sees ciphertext. ",[941,132464,132465],{},".crossJoin(target, { as })"," is the query clause that pairs rows ",[1092,132468,132469],{},"across"," two collections in the same ",[1007,132472,2694],{"href":58262}," without any declared foreign key: every left row is combined with every right row (a Cartesian product), and each result row carries the original left fields plus the right row under the alias you choose. An optional ",[941,132475,132476],{},"on:"," filter turns it into a ",[950,132479,132480],{},"lateral"," join — each left row is paired only with the right rows that match it.",[937,132483,132484,132485,132488,132489,132491],{},"You want it for \"every A × every B\" shapes: active workers per pay period, coverage per billing cycle, every product × every region before an aggregation. For lookups that follow a declared reference (invoice → its client), use ",[1007,132486,715],{"href":132487},".\u002Fjoins"," instead — a cross-join is the tool when no ",[941,132490,62282],{}," relationship exists between the two collections.",[937,132493,132494,132495,132497,132498,132500],{},"Cross-join is part of the core query builder — there is no ",[941,132496,943],{}," factory and no bundle cost to opt into. A cost ceiling (default 50,000 result rows) protects you from accidentally materializing a huge product; it throws ",[950,132499,11357],{}," allocating the result.",[1022,132502,58394],{"id":58393},[937,132504,132505,132506,132509,132510,132512,132513,132515,132516,132518,132519,132521],{},"No opt-in is needed for ",[941,132507,132508],{},".crossJoin()"," itself — it is available on every ",[941,132511,25801],{},". The grouped examples below also use the ",[1007,132514,4449],{"href":123485}," service (",[941,132517,27496],{}," + reducers), which ",[1092,132520,3082],{}," opt-in:",[2217,132523,132525],{"className":2219,"code":132524,"language":2221,"meta":1037,"style":1037},"import { createNoydb } from '@noy-db\u002Fhub'\nimport { withReduce } from '@noy-db\u002Fhub\u002Freduce'\nimport { toMemory } from '@noy-db\u002Fto-memory'\n\nconst db = await createNoydb({\n  store: toMemory(),\n  user: 'alice',\n  secret: 'a-strong-secret',\n  reduceStrategy: withReduce(),\n})\n",[941,132526,132527,132545,132563,132581,132585,132601,132613,132627,132641,132653],{"__ignoreMap":1037},[2225,132528,132529,132531,132533,132535,132537,132539,132541,132543],{"class":2227,"line":2228},[2225,132530,2232],{"class":2231},[2225,132532,2236],{"class":2235},[2225,132534,2744],{"class":2239},[2225,132536,2243],{"class":2235},[2225,132538,2246],{"class":2231},[2225,132540,2249],{"class":2235},[2225,132542,1086],{"class":2252},[2225,132544,2256],{"class":2235},[2225,132546,132547,132549,132551,132553,132555,132557,132559,132561],{"class":2227,"line":1041},[2225,132548,2232],{"class":2231},[2225,132550,2236],{"class":2235},[2225,132552,27293],{"class":2239},[2225,132554,2243],{"class":2235},[2225,132556,2246],{"class":2231},[2225,132558,2249],{"class":2235},[2225,132560,27257],{"class":2252},[2225,132562,2256],{"class":2235},[2225,132564,132565,132567,132569,132571,132573,132575,132577,132579],{"class":2227,"line":1038},[2225,132566,2232],{"class":2231},[2225,132568,2236],{"class":2235},[2225,132570,68576],{"class":2239},[2225,132572,2243],{"class":2235},[2225,132574,2246],{"class":2231},[2225,132576,2249],{"class":2235},[2225,132578,9638],{"class":2252},[2225,132580,2256],{"class":2235},[2225,132582,132583],{"class":2227,"line":2288},[2225,132584,2285],{"emptyLinePlaceholder":2284},[2225,132586,132587,132589,132591,132593,132595,132597,132599],{"class":2227,"line":2304},[2225,132588,2775],{"class":2774},[2225,132590,2778],{"class":2239},[2225,132592,2781],{"class":2235},[2225,132594,2784],{"class":2231},[2225,132596,2744],{"class":2291},[2225,132598,2789],{"class":2239},[2225,132600,4200],{"class":2235},[2225,132602,132603,132605,132607,132609,132611],{"class":2227,"line":2356},[2225,132604,15608],{"class":2307},[2225,132606,2311],{"class":2235},[2225,132608,68576],{"class":2291},[2225,132610,4228],{"class":2239},[2225,132612,1707],{"class":2235},[2225,132614,132615,132617,132619,132621,132623,132625],{"class":2227,"line":2830},[2225,132616,4205],{"class":2307},[2225,132618,2311],{"class":2235},[2225,132620,2249],{"class":2235},[2225,132622,6408],{"class":2252},[2225,132624,2805],{"class":2235},[2225,132626,1707],{"class":2235},[2225,132628,132629,132631,132633,132635,132637,132639],{"class":2227,"line":2861},[2225,132630,10423],{"class":2307},[2225,132632,2311],{"class":2235},[2225,132634,2249],{"class":2235},[2225,132636,6421],{"class":2252},[2225,132638,2805],{"class":2235},[2225,132640,1707],{"class":2235},[2225,132642,132643,132645,132647,132649,132651],{"class":2227,"line":4246},[2225,132644,27365],{"class":2307},[2225,132646,2311],{"class":2235},[2225,132648,27293],{"class":2291},[2225,132650,4228],{"class":2239},[2225,132652,1707],{"class":2235},[2225,132654,132655,132657],{"class":2227,"line":4253},[2225,132656,2359],{"class":2235},[2225,132658,2362],{"class":2239},[1022,132660,29893],{"id":29892},[937,132662,132663,132664,132666],{},"The flows below share this setup — a vault with pay periods and workers (no ",[941,132665,62282],{}," between them):",[2217,132668,132670],{"className":2219,"code":132669,"language":2221,"meta":1037,"style":1037},"type Period = { quarter: string; start: string; end: string }\ntype Worker = { name: string; since: string; until: string | null }\n\nconst vault = await db.openVault('acme')\nconst periods = vault.collection\u003CPeriod>('periods')\nconst workers = vault.collection\u003CWorker>('workers')\n\nawait periods.put('q1', { quarter: 'q1', start: '2026-01', end: '2026-03' })\nawait periods.put('q2', { quarter: 'q2', start: '2026-04', end: '2026-06' })\n\nawait workers.put('w1', { name: 'Alice', since: '2025-12', until: null })\nawait workers.put('w2', { name: 'Bob',   since: '2026-01', until: '2026-06' })\nawait workers.put('w3', { name: 'Carol', since: '2026-04', until: null })\n",[941,132671,132672,132710,132750,132754,132780,132812,132845,132849,132913,132975,132979,133038,133100],{"__ignoreMap":1037},[2225,132673,132674,132676,132679,132681,132683,132686,132688,132690,132692,132695,132697,132699,132701,132704,132706,132708],{"class":2227,"line":2228},[2225,132675,6339],{"class":2774},[2225,132677,132678],{"class":6342}," Period",[2225,132680,6346],{"class":2235},[2225,132682,2236],{"class":2235},[2225,132684,132685],{"class":2307}," quarter",[2225,132687,2311],{"class":2235},[2225,132689,6366],{"class":6342},[2225,132691,3769],{"class":2235},[2225,132693,132694],{"class":2307}," start",[2225,132696,2311],{"class":2235},[2225,132698,6366],{"class":6342},[2225,132700,3769],{"class":2235},[2225,132702,132703],{"class":2307}," end",[2225,132705,2311],{"class":2235},[2225,132707,6366],{"class":6342},[2225,132709,6369],{"class":2235},[2225,132711,132712,132714,132717,132719,132721,132723,132725,132727,132729,132731,132733,132735,132737,132740,132742,132744,132746,132748],{"class":2227,"line":1041},[2225,132713,6339],{"class":2774},[2225,132715,132716],{"class":6342}," Worker",[2225,132718,6346],{"class":2235},[2225,132720,2236],{"class":2235},[2225,132722,8104],{"class":2307},[2225,132724,2311],{"class":2235},[2225,132726,6366],{"class":6342},[2225,132728,3769],{"class":2235},[2225,132730,61185],{"class":2307},[2225,132732,2311],{"class":2235},[2225,132734,6366],{"class":6342},[2225,132736,3769],{"class":2235},[2225,132738,132739],{"class":2307}," until",[2225,132741,2311],{"class":2235},[2225,132743,6366],{"class":6342},[2225,132745,7087],{"class":2235},[2225,132747,7090],{"class":6342},[2225,132749,6369],{"class":2235},[2225,132751,132752],{"class":2227,"line":1038},[2225,132753,2285],{"emptyLinePlaceholder":2284},[2225,132755,132756,132758,132760,132762,132764,132766,132768,132770,132772,132774,132776,132778],{"class":2227,"line":2288},[2225,132757,2775],{"class":2774},[2225,132759,2835],{"class":2239},[2225,132761,2781],{"class":2235},[2225,132763,2784],{"class":2231},[2225,132765,2842],{"class":2239},[2225,132767,1520],{"class":2235},[2225,132769,2847],{"class":2291},[2225,132771,2789],{"class":2239},[2225,132773,2805],{"class":2235},[2225,132775,6450],{"class":2252},[2225,132777,2805],{"class":2235},[2225,132779,2362],{"class":2239},[2225,132781,132782,132784,132787,132789,132791,132793,132795,132797,132800,132802,132804,132806,132808,132810],{"class":2227,"line":2304},[2225,132783,2775],{"class":2774},[2225,132785,132786],{"class":2239}," periods ",[2225,132788,2781],{"class":2235},[2225,132790,6466],{"class":2239},[2225,132792,1520],{"class":2235},[2225,132794,6471],{"class":2291},[2225,132796,6474],{"class":2235},[2225,132798,132799],{"class":6342},"Period",[2225,132801,6480],{"class":2235},[2225,132803,2789],{"class":2239},[2225,132805,2805],{"class":2235},[2225,132807,776],{"class":2252},[2225,132809,2805],{"class":2235},[2225,132811,2362],{"class":2239},[2225,132813,132814,132816,132819,132821,132823,132825,132827,132829,132832,132834,132836,132838,132841,132843],{"class":2227,"line":2356},[2225,132815,2775],{"class":2774},[2225,132817,132818],{"class":2239}," workers ",[2225,132820,2781],{"class":2235},[2225,132822,6466],{"class":2239},[2225,132824,1520],{"class":2235},[2225,132826,6471],{"class":2291},[2225,132828,6474],{"class":2235},[2225,132830,132831],{"class":6342},"Worker",[2225,132833,6480],{"class":2235},[2225,132835,2789],{"class":2239},[2225,132837,2805],{"class":2235},[2225,132839,132840],{"class":2252},"workers",[2225,132842,2805],{"class":2235},[2225,132844,2362],{"class":2239},[2225,132846,132847],{"class":2227,"line":2830},[2225,132848,2285],{"emptyLinePlaceholder":2284},[2225,132850,132851,132853,132856,132858,132860,132862,132864,132867,132869,132871,132873,132875,132877,132879,132881,132883,132885,132887,132889,132891,132894,132896,132898,132900,132902,132904,132907,132909,132911],{"class":2227,"line":2861},[2225,132852,6500],{"class":2231},[2225,132854,132855],{"class":2239}," periods",[2225,132857,1520],{"class":2235},[2225,132859,3161],{"class":2291},[2225,132861,2789],{"class":2239},[2225,132863,2805],{"class":2235},[2225,132865,132866],{"class":2252},"q1",[2225,132868,2805],{"class":2235},[2225,132870,2298],{"class":2235},[2225,132872,2236],{"class":2235},[2225,132874,132685],{"class":2307},[2225,132876,2311],{"class":2235},[2225,132878,2249],{"class":2235},[2225,132880,132866],{"class":2252},[2225,132882,2805],{"class":2235},[2225,132884,2298],{"class":2235},[2225,132886,132694],{"class":2307},[2225,132888,2311],{"class":2235},[2225,132890,2249],{"class":2235},[2225,132892,132893],{"class":2252},"2026-01",[2225,132895,2805],{"class":2235},[2225,132897,2298],{"class":2235},[2225,132899,132703],{"class":2307},[2225,132901,2311],{"class":2235},[2225,132903,2249],{"class":2235},[2225,132905,132906],{"class":2252},"2026-03",[2225,132908,2805],{"class":2235},[2225,132910,2243],{"class":2235},[2225,132912,2362],{"class":2239},[2225,132914,132915,132917,132919,132921,132923,132925,132927,132930,132932,132934,132936,132938,132940,132942,132944,132946,132948,132950,132952,132954,132957,132959,132961,132963,132965,132967,132969,132971,132973],{"class":2227,"line":4246},[2225,132916,6500],{"class":2231},[2225,132918,132855],{"class":2239},[2225,132920,1520],{"class":2235},[2225,132922,3161],{"class":2291},[2225,132924,2789],{"class":2239},[2225,132926,2805],{"class":2235},[2225,132928,132929],{"class":2252},"q2",[2225,132931,2805],{"class":2235},[2225,132933,2298],{"class":2235},[2225,132935,2236],{"class":2235},[2225,132937,132685],{"class":2307},[2225,132939,2311],{"class":2235},[2225,132941,2249],{"class":2235},[2225,132943,132929],{"class":2252},[2225,132945,2805],{"class":2235},[2225,132947,2298],{"class":2235},[2225,132949,132694],{"class":2307},[2225,132951,2311],{"class":2235},[2225,132953,2249],{"class":2235},[2225,132955,132956],{"class":2252},"2026-04",[2225,132958,2805],{"class":2235},[2225,132960,2298],{"class":2235},[2225,132962,132703],{"class":2307},[2225,132964,2311],{"class":2235},[2225,132966,2249],{"class":2235},[2225,132968,27015],{"class":2252},[2225,132970,2805],{"class":2235},[2225,132972,2243],{"class":2235},[2225,132974,2362],{"class":2239},[2225,132976,132977],{"class":2227,"line":4253},[2225,132978,2285],{"emptyLinePlaceholder":2284},[2225,132980,132981,132983,132986,132988,132990,132992,132994,132997,132999,133001,133003,133005,133007,133009,133011,133013,133015,133017,133019,133021,133024,133026,133028,133030,133032,133034,133036],{"class":2227,"line":4259},[2225,132982,6500],{"class":2231},[2225,132984,132985],{"class":2239}," workers",[2225,132987,1520],{"class":2235},[2225,132989,3161],{"class":2291},[2225,132991,2789],{"class":2239},[2225,132993,2805],{"class":2235},[2225,132995,132996],{"class":2252},"w1",[2225,132998,2805],{"class":2235},[2225,133000,2298],{"class":2235},[2225,133002,2236],{"class":2235},[2225,133004,8104],{"class":2307},[2225,133006,2311],{"class":2235},[2225,133008,2249],{"class":2235},[2225,133010,10533],{"class":2252},[2225,133012,2805],{"class":2235},[2225,133014,2298],{"class":2235},[2225,133016,61185],{"class":2307},[2225,133018,2311],{"class":2235},[2225,133020,2249],{"class":2235},[2225,133022,133023],{"class":2252},"2025-12",[2225,133025,2805],{"class":2235},[2225,133027,2298],{"class":2235},[2225,133029,132739],{"class":2307},[2225,133031,2311],{"class":2235},[2225,133033,7090],{"class":2235},[2225,133035,2243],{"class":2235},[2225,133037,2362],{"class":2239},[2225,133039,133040,133042,133044,133046,133048,133050,133052,133055,133057,133059,133061,133063,133065,133067,133069,133071,133073,133076,133078,133080,133082,133084,133086,133088,133090,133092,133094,133096,133098],{"class":2227,"line":6548},[2225,133041,6500],{"class":2231},[2225,133043,132985],{"class":2239},[2225,133045,1520],{"class":2235},[2225,133047,3161],{"class":2291},[2225,133049,2789],{"class":2239},[2225,133051,2805],{"class":2235},[2225,133053,133054],{"class":2252},"w2",[2225,133056,2805],{"class":2235},[2225,133058,2298],{"class":2235},[2225,133060,2236],{"class":2235},[2225,133062,8104],{"class":2307},[2225,133064,2311],{"class":2235},[2225,133066,2249],{"class":2235},[2225,133068,91611],{"class":2252},[2225,133070,2805],{"class":2235},[2225,133072,2298],{"class":2235},[2225,133074,133075],{"class":2307},"   since",[2225,133077,2311],{"class":2235},[2225,133079,2249],{"class":2235},[2225,133081,132893],{"class":2252},[2225,133083,2805],{"class":2235},[2225,133085,2298],{"class":2235},[2225,133087,132739],{"class":2307},[2225,133089,2311],{"class":2235},[2225,133091,2249],{"class":2235},[2225,133093,27015],{"class":2252},[2225,133095,2805],{"class":2235},[2225,133097,2243],{"class":2235},[2225,133099,2362],{"class":2239},[2225,133101,133102,133104,133106,133108,133110,133112,133114,133117,133119,133121,133123,133125,133127,133129,133132,133134,133136,133138,133140,133142,133144,133146,133148,133150,133152,133154,133156],{"class":2227,"line":6576},[2225,133103,6500],{"class":2231},[2225,133105,132985],{"class":2239},[2225,133107,1520],{"class":2235},[2225,133109,3161],{"class":2291},[2225,133111,2789],{"class":2239},[2225,133113,2805],{"class":2235},[2225,133115,133116],{"class":2252},"w3",[2225,133118,2805],{"class":2235},[2225,133120,2298],{"class":2235},[2225,133122,2236],{"class":2235},[2225,133124,8104],{"class":2307},[2225,133126,2311],{"class":2235},[2225,133128,2249],{"class":2235},[2225,133130,133131],{"class":2252},"Carol",[2225,133133,2805],{"class":2235},[2225,133135,2298],{"class":2235},[2225,133137,61185],{"class":2307},[2225,133139,2311],{"class":2235},[2225,133141,2249],{"class":2235},[2225,133143,132956],{"class":2252},[2225,133145,2805],{"class":2235},[2225,133147,2298],{"class":2235},[2225,133149,132739],{"class":2307},[2225,133151,2311],{"class":2235},[2225,133153,7090],{"class":2235},[2225,133155,2243],{"class":2235},[2225,133157,2362],{"class":2239},[1591,133159,133161],{"id":133160},"full-cartesian-product","Full Cartesian product",[937,133163,90416,133164,133166,133167,133169],{},[941,133165,132476],{},", every left row is paired with every right row. Each result row is the left record plus the right record under the ",[941,133168,4899],{}," alias:",[2217,133171,133173],{"className":2219,"code":133172,"language":2221,"meta":1037,"style":1037},"const pairs = periods.query()\n  .crossJoin\u003CWorker, 'worker'>('workers', { as: 'worker' })\n  .toArray()\n\u002F\u002F → 6 rows (2 periods × 3 workers), each shaped like:\n",[941,133174,133175,133191,133239,133247],{"__ignoreMap":1037},[2225,133176,133177,133179,133181,133183,133185,133187,133189],{"class":2227,"line":2228},[2225,133178,2775],{"class":2774},[2225,133180,107508],{"class":2239},[2225,133182,2781],{"class":2235},[2225,133184,132855],{"class":2239},[2225,133186,1520],{"class":2235},[2225,133188,5286],{"class":2291},[2225,133190,8146],{"class":2239},[2225,133192,133193,133195,133198,133200,133202,133204,133206,133209,133211,133213,133215,133217,133219,133221,133223,133225,133227,133229,133231,133233,133235,133237],{"class":2227,"line":1041},[2225,133194,26235],{"class":2235},[2225,133196,133197],{"class":2291},"crossJoin",[2225,133199,6474],{"class":2235},[2225,133201,132831],{"class":6342},[2225,133203,2298],{"class":2235},[2225,133205,2249],{"class":2235},[2225,133207,133208],{"class":2252},"worker",[2225,133210,2805],{"class":2235},[2225,133212,6480],{"class":2235},[2225,133214,2789],{"class":2239},[2225,133216,2805],{"class":2235},[2225,133218,132840],{"class":2252},[2225,133220,2805],{"class":2235},[2225,133222,2298],{"class":2235},[2225,133224,2236],{"class":2235},[2225,133226,2266],{"class":2307},[2225,133228,2311],{"class":2235},[2225,133230,2249],{"class":2235},[2225,133232,133208],{"class":2252},[2225,133234,2805],{"class":2235},[2225,133236,2243],{"class":2235},[2225,133238,2362],{"class":2239},[2225,133240,133241,133243,133245],{"class":2227,"line":1038},[2225,133242,26235],{"class":2235},[2225,133244,3230],{"class":2291},[2225,133246,8146],{"class":2239},[2225,133248,133249],{"class":2227,"line":2288},[2225,133250,133251],{"class":2763},"\u002F\u002F → 6 rows (2 periods × 3 workers), each shaped like:\n",[2217,133253,133255],{"className":6604,"code":133254,"language":141,"meta":1037,"style":1037},"[\n  { \"quarter\": \"q1\", \"start\": \"2026-01\", \"end\": \"2026-03\",\n    \"worker\": { \"name\": \"Alice\", \"since\": \"2025-12\", \"until\": null } },\n  { \"quarter\": \"q1\", \"start\": \"2026-01\", \"end\": \"2026-03\",\n    \"worker\": { \"name\": \"Bob\", \"since\": \"2026-01\", \"until\": \"2026-06\" } }\n]\n",[941,133256,133257,133261,133315,133373,133425,133487],{"__ignoreMap":1037},[2225,133258,133259],{"class":2227,"line":2228},[2225,133260,26349],{"class":2235},[2225,133262,133263,133265,133267,133270,133272,133274,133276,133278,133280,133282,133284,133286,133288,133290,133292,133294,133296,133298,133300,133303,133305,133307,133309,133311,133313],{"class":2227,"line":1041},[2225,133264,26354],{"class":2235},[2225,133266,6614],{"class":2235},[2225,133268,133269],{"class":2774},"quarter",[2225,133271,6620],{"class":2235},[2225,133273,2311],{"class":2235},[2225,133275,6614],{"class":2235},[2225,133277,132866],{"class":2252},[2225,133279,6620],{"class":2235},[2225,133281,2298],{"class":2235},[2225,133283,6614],{"class":2235},[2225,133285,906],{"class":2774},[2225,133287,6620],{"class":2235},[2225,133289,2311],{"class":2235},[2225,133291,6614],{"class":2235},[2225,133293,132893],{"class":2252},[2225,133295,6620],{"class":2235},[2225,133297,2298],{"class":2235},[2225,133299,6614],{"class":2235},[2225,133301,133302],{"class":2774},"end",[2225,133304,6620],{"class":2235},[2225,133306,2311],{"class":2235},[2225,133308,6614],{"class":2235},[2225,133310,132906],{"class":2252},[2225,133312,6620],{"class":2235},[2225,133314,1707],{"class":2235},[2225,133316,133317,133319,133321,133323,133325,133327,133329,133331,133333,133335,133337,133339,133341,133343,133345,133347,133349,133351,133353,133355,133357,133359,133361,133363,133365,133367,133369,133371],{"class":2227,"line":1038},[2225,133318,14201],{"class":2235},[2225,133320,133208],{"class":2774},[2225,133322,6620],{"class":2235},[2225,133324,2311],{"class":2235},[2225,133326,2236],{"class":2235},[2225,133328,6614],{"class":2235},[2225,133330,4914],{"class":6342},[2225,133332,6620],{"class":2235},[2225,133334,2311],{"class":2235},[2225,133336,6614],{"class":2235},[2225,133338,10533],{"class":2252},[2225,133340,6620],{"class":2235},[2225,133342,2298],{"class":2235},[2225,133344,6614],{"class":2235},[2225,133346,7883],{"class":6342},[2225,133348,6620],{"class":2235},[2225,133350,2311],{"class":2235},[2225,133352,6614],{"class":2235},[2225,133354,133023],{"class":2252},[2225,133356,6620],{"class":2235},[2225,133358,2298],{"class":2235},[2225,133360,6614],{"class":2235},[2225,133362,75352],{"class":6342},[2225,133364,6620],{"class":2235},[2225,133366,2311],{"class":2235},[2225,133368,7090],{"class":2235},[2225,133370,2243],{"class":2235},[2225,133372,2353],{"class":2235},[2225,133374,133375,133377,133379,133381,133383,133385,133387,133389,133391,133393,133395,133397,133399,133401,133403,133405,133407,133409,133411,133413,133415,133417,133419,133421,133423],{"class":2227,"line":2288},[2225,133376,26354],{"class":2235},[2225,133378,6614],{"class":2235},[2225,133380,133269],{"class":2774},[2225,133382,6620],{"class":2235},[2225,133384,2311],{"class":2235},[2225,133386,6614],{"class":2235},[2225,133388,132866],{"class":2252},[2225,133390,6620],{"class":2235},[2225,133392,2298],{"class":2235},[2225,133394,6614],{"class":2235},[2225,133396,906],{"class":2774},[2225,133398,6620],{"class":2235},[2225,133400,2311],{"class":2235},[2225,133402,6614],{"class":2235},[2225,133404,132893],{"class":2252},[2225,133406,6620],{"class":2235},[2225,133408,2298],{"class":2235},[2225,133410,6614],{"class":2235},[2225,133412,133302],{"class":2774},[2225,133414,6620],{"class":2235},[2225,133416,2311],{"class":2235},[2225,133418,6614],{"class":2235},[2225,133420,132906],{"class":2252},[2225,133422,6620],{"class":2235},[2225,133424,1707],{"class":2235},[2225,133426,133427,133429,133431,133433,133435,133437,133439,133441,133443,133445,133447,133449,133451,133453,133455,133457,133459,133461,133463,133465,133467,133469,133471,133473,133475,133477,133479,133481,133483,133485],{"class":2227,"line":2304},[2225,133428,14201],{"class":2235},[2225,133430,133208],{"class":2774},[2225,133432,6620],{"class":2235},[2225,133434,2311],{"class":2235},[2225,133436,2236],{"class":2235},[2225,133438,6614],{"class":2235},[2225,133440,4914],{"class":6342},[2225,133442,6620],{"class":2235},[2225,133444,2311],{"class":2235},[2225,133446,6614],{"class":2235},[2225,133448,91611],{"class":2252},[2225,133450,6620],{"class":2235},[2225,133452,2298],{"class":2235},[2225,133454,6614],{"class":2235},[2225,133456,7883],{"class":6342},[2225,133458,6620],{"class":2235},[2225,133460,2311],{"class":2235},[2225,133462,6614],{"class":2235},[2225,133464,132893],{"class":2252},[2225,133466,6620],{"class":2235},[2225,133468,2298],{"class":2235},[2225,133470,6614],{"class":2235},[2225,133472,75352],{"class":6342},[2225,133474,6620],{"class":2235},[2225,133476,2311],{"class":2235},[2225,133478,6614],{"class":2235},[2225,133480,27015],{"class":2252},[2225,133482,6620],{"class":2235},[2225,133484,2243],{"class":2235},[2225,133486,6369],{"class":2235},[2225,133488,133489],{"class":2227,"line":2356},[2225,133490,26484],{"class":2235},[1591,133492,133494],{"id":133493},"lateral-join-grouped-aggregate-active-workers-per-period","Lateral join + grouped aggregate — active workers per period",[937,133496,127199,133497,133500,133501,133504,133505,56432,133507,133509],{},[941,133498,133499],{},"on: (left) => (right) => boolean"," filters the right side ",[950,133502,133503],{},"per left row",". Combined with ",[941,133506,27496],{},[941,133508,24913],{}," reducer, this answers \"how many workers were active in each pay period\":",[2217,133511,133513],{"className":2219,"code":133512,"language":2221,"meta":1037,"style":1037},"import { count } from '@noy-db\u002Fhub\u002Freduce'\n\nconst perPeriod = periods.query()\n  .crossJoin\u003CWorker, 'worker'>('workers', {\n    as: 'worker',\n    on: (period) => (worker) =>\n      worker.since \u003C= period.start &&\n      (worker.until === null || worker.until >= period.end),\n  })\n  .groupBy('quarter')\n  .aggregate({ workerCount: count() })\n  .run()\n",[941,133514,133515,133533,133537,133554,133586,133600,133624,133647,133681,133687,133703,133726],{"__ignoreMap":1037},[2225,133516,133517,133519,133521,133523,133525,133527,133529,133531],{"class":2227,"line":2228},[2225,133518,2232],{"class":2231},[2225,133520,2236],{"class":2235},[2225,133522,19270],{"class":2239},[2225,133524,2243],{"class":2235},[2225,133526,2246],{"class":2231},[2225,133528,2249],{"class":2235},[2225,133530,27257],{"class":2252},[2225,133532,2256],{"class":2235},[2225,133534,133535],{"class":2227,"line":1041},[2225,133536,2285],{"emptyLinePlaceholder":2284},[2225,133538,133539,133541,133544,133546,133548,133550,133552],{"class":2227,"line":1038},[2225,133540,2775],{"class":2774},[2225,133542,133543],{"class":2239}," perPeriod ",[2225,133545,2781],{"class":2235},[2225,133547,132855],{"class":2239},[2225,133549,1520],{"class":2235},[2225,133551,5286],{"class":2291},[2225,133553,8146],{"class":2239},[2225,133555,133556,133558,133560,133562,133564,133566,133568,133570,133572,133574,133576,133578,133580,133582,133584],{"class":2227,"line":2288},[2225,133557,26235],{"class":2235},[2225,133559,133197],{"class":2291},[2225,133561,6474],{"class":2235},[2225,133563,132831],{"class":6342},[2225,133565,2298],{"class":2235},[2225,133567,2249],{"class":2235},[2225,133569,133208],{"class":2252},[2225,133571,2805],{"class":2235},[2225,133573,6480],{"class":2235},[2225,133575,2789],{"class":2239},[2225,133577,2805],{"class":2235},[2225,133579,132840],{"class":2252},[2225,133581,2805],{"class":2235},[2225,133583,2298],{"class":2235},[2225,133585,2301],{"class":2235},[2225,133587,133588,133590,133592,133594,133596,133598],{"class":2227,"line":2304},[2225,133589,65647],{"class":2307},[2225,133591,2311],{"class":2235},[2225,133593,2249],{"class":2235},[2225,133595,133208],{"class":2252},[2225,133597,2805],{"class":2235},[2225,133599,1707],{"class":2235},[2225,133601,133602,133605,133607,133609,133612,133614,133616,133618,133620,133622],{"class":2227,"line":2356},[2225,133603,133604],{"class":2291},"    on",[2225,133606,2311],{"class":2235},[2225,133608,953],{"class":2235},[2225,133610,133611],{"class":7052},"period",[2225,133613,1381],{"class":2235},[2225,133615,8116],{"class":2774},[2225,133617,953],{"class":2235},[2225,133619,133208],{"class":7052},[2225,133621,1381],{"class":2235},[2225,133623,71229],{"class":2774},[2225,133625,133626,133629,133631,133634,133636,133639,133641,133644],{"class":2227,"line":2830},[2225,133627,133628],{"class":2239},"      worker",[2225,133630,1520],{"class":2235},[2225,133632,133633],{"class":2239},"since ",[2225,133635,100227],{"class":2235},[2225,133637,133638],{"class":2239}," period",[2225,133640,1520],{"class":2235},[2225,133642,133643],{"class":2239},"start ",[2225,133645,133646],{"class":2235},"&&\n",[2225,133648,133649,133652,133654,133657,133659,133661,133663,133666,133668,133670,133672,133674,133676,133679],{"class":2227,"line":2861},[2225,133650,133651],{"class":2239},"      (worker",[2225,133653,1520],{"class":2235},[2225,133655,133656],{"class":2239},"until ",[2225,133658,33083],{"class":2235},[2225,133660,7090],{"class":2235},[2225,133662,113501],{"class":2235},[2225,133664,133665],{"class":2239}," worker",[2225,133667,1520],{"class":2235},[2225,133669,133656],{"class":2239},[2225,133671,26921],{"class":2235},[2225,133673,133638],{"class":2239},[2225,133675,1520],{"class":2235},[2225,133677,133678],{"class":2239},"end)",[2225,133680,1707],{"class":2235},[2225,133682,133683,133685],{"class":2227,"line":4246},[2225,133684,19143],{"class":2235},[2225,133686,2362],{"class":2239},[2225,133688,133689,133691,133693,133695,133697,133699,133701],{"class":2227,"line":4253},[2225,133690,26235],{"class":2235},[2225,133692,27496],{"class":2291},[2225,133694,2789],{"class":2239},[2225,133696,2805],{"class":2235},[2225,133698,133269],{"class":2252},[2225,133700,2805],{"class":2235},[2225,133702,2362],{"class":2239},[2225,133704,133705,133707,133709,133711,133713,133716,133718,133720,133722,133724],{"class":2227,"line":4259},[2225,133706,26235],{"class":2235},[2225,133708,4449],{"class":2291},[2225,133710,2789],{"class":2239},[2225,133712,2792],{"class":2235},[2225,133714,133715],{"class":2307}," workerCount",[2225,133717,2311],{"class":2235},[2225,133719,19270],{"class":2291},[2225,133721,19132],{"class":2239},[2225,133723,2359],{"class":2235},[2225,133725,2362],{"class":2239},[2225,133727,133728,133730,133732],{"class":2227,"line":6548},[2225,133729,26235],{"class":2235},[2225,133731,61526],{"class":2291},[2225,133733,8146],{"class":2239},[2217,133735,133737],{"className":6604,"code":133736,"language":141,"meta":1037,"style":1037},"[\n  { \"quarter\": \"q1\", \"workerCount\": 2 },\n  { \"quarter\": \"q2\", \"workerCount\": 3 }\n]\n",[941,133738,133739,133743,133776,133808],{"__ignoreMap":1037},[2225,133740,133741],{"class":2227,"line":2228},[2225,133742,26349],{"class":2235},[2225,133744,133745,133747,133749,133751,133753,133755,133757,133759,133761,133763,133765,133768,133770,133772,133774],{"class":2227,"line":1041},[2225,133746,26354],{"class":2235},[2225,133748,6614],{"class":2235},[2225,133750,133269],{"class":2774},[2225,133752,6620],{"class":2235},[2225,133754,2311],{"class":2235},[2225,133756,6614],{"class":2235},[2225,133758,132866],{"class":2252},[2225,133760,6620],{"class":2235},[2225,133762,2298],{"class":2235},[2225,133764,6614],{"class":2235},[2225,133766,133767],{"class":2774},"workerCount",[2225,133769,6620],{"class":2235},[2225,133771,2311],{"class":2235},[2225,133773,18961],{"class":6527},[2225,133775,2353],{"class":2235},[2225,133777,133778,133780,133782,133784,133786,133788,133790,133792,133794,133796,133798,133800,133802,133804,133806],{"class":2227,"line":1038},[2225,133779,26354],{"class":2235},[2225,133781,6614],{"class":2235},[2225,133783,133269],{"class":2774},[2225,133785,6620],{"class":2235},[2225,133787,2311],{"class":2235},[2225,133789,6614],{"class":2235},[2225,133791,132929],{"class":2252},[2225,133793,6620],{"class":2235},[2225,133795,2298],{"class":2235},[2225,133797,6614],{"class":2235},[2225,133799,133767],{"class":2774},[2225,133801,6620],{"class":2235},[2225,133803,2311],{"class":2235},[2225,133805,14460],{"class":6527},[2225,133807,6369],{"class":2235},[2225,133809,133810],{"class":2227,"line":2288},[2225,133811,26484],{"class":2235},[937,133813,960,133814,133816,133817,964,133820,133823],{},[941,133815,132476],{}," filter also has a ",[950,133818,133819],{},"subset form",[941,133821,133822],{},"on: (left) => TTarget[]"," returns the exact right rows to pair with each left row, skipping the per-row scan of the whole right side when you can compute the subset directly.",[1591,133825,133827],{"id":133826},"clause-order-matters","Clause order matters",[937,133829,133830,133831,1625,133833,133835],{},"Cross-join clauses are interleaved with ",[941,133832,3016],{},[941,133834,8686],{}," clauses and processed in declaration order:",[1111,133837,133838,133849],{},[1114,133839,133840,1324,133843,1324,133845,133848],{},[941,133841,133842],{},".where(...)",[950,133844,11357],{},[941,133846,133847],{},".crossJoin(...)"," filters the left side first — fewer left rows means a smaller product, so put left-side filters first whenever you can.",[1114,133850,133851,1324,133853,1324,133855,133857,133858,957],{},[941,133852,133842],{},[950,133854,10761],{},[941,133856,133847],{}," runs on the expanded rows and can reference the aliased right-side fields (",[941,133859,133860],{},"'worker.name'",[2217,133862,133864],{"className":2219,"code":133863,"language":2221,"meta":1037,"style":1037},"\u002F\u002F Left filter first: expands only q1's pairs (3 rows, not 6)\nperiods.query()\n  .where('quarter', '==', 'q1')\n  .crossJoin\u003CWorker, 'worker'>('workers', { as: 'worker' })\n  .toArray()\n\u002F\u002F → 3 rows: q1 × Alice, q1 × Bob, q1 × Carol\n\n\u002F\u002F Alias filter after: expands all 6 pairs, keeps Alice's\nperiods.query()\n  .crossJoin\u003CWorker, 'worker'>('workers', { as: 'worker' })\n  .where('worker.name', '==', 'Alice')\n  .toArray()\n\u002F\u002F → 2 rows: q1 × Alice, q2 × Alice\n",[941,133865,133866,133871,133881,133913,133959,133967,133972,133976,133981,133991,134037,134070,134078],{"__ignoreMap":1037},[2225,133867,133868],{"class":2227,"line":2228},[2225,133869,133870],{"class":2763},"\u002F\u002F Left filter first: expands only q1's pairs (3 rows, not 6)\n",[2225,133872,133873,133875,133877,133879],{"class":2227,"line":1041},[2225,133874,776],{"class":2239},[2225,133876,1520],{"class":2235},[2225,133878,5286],{"class":2291},[2225,133880,8146],{"class":2239},[2225,133882,133883,133885,133887,133889,133891,133893,133895,133897,133899,133901,133903,133905,133907,133909,133911],{"class":2227,"line":1038},[2225,133884,26235],{"class":2235},[2225,133886,3016],{"class":2291},[2225,133888,2789],{"class":2239},[2225,133890,2805],{"class":2235},[2225,133892,133269],{"class":2252},[2225,133894,2805],{"class":2235},[2225,133896,2298],{"class":2235},[2225,133898,2249],{"class":2235},[2225,133900,21901],{"class":2252},[2225,133902,2805],{"class":2235},[2225,133904,2298],{"class":2235},[2225,133906,2249],{"class":2235},[2225,133908,132866],{"class":2252},[2225,133910,2805],{"class":2235},[2225,133912,2362],{"class":2239},[2225,133914,133915,133917,133919,133921,133923,133925,133927,133929,133931,133933,133935,133937,133939,133941,133943,133945,133947,133949,133951,133953,133955,133957],{"class":2227,"line":2288},[2225,133916,26235],{"class":2235},[2225,133918,133197],{"class":2291},[2225,133920,6474],{"class":2235},[2225,133922,132831],{"class":6342},[2225,133924,2298],{"class":2235},[2225,133926,2249],{"class":2235},[2225,133928,133208],{"class":2252},[2225,133930,2805],{"class":2235},[2225,133932,6480],{"class":2235},[2225,133934,2789],{"class":2239},[2225,133936,2805],{"class":2235},[2225,133938,132840],{"class":2252},[2225,133940,2805],{"class":2235},[2225,133942,2298],{"class":2235},[2225,133944,2236],{"class":2235},[2225,133946,2266],{"class":2307},[2225,133948,2311],{"class":2235},[2225,133950,2249],{"class":2235},[2225,133952,133208],{"class":2252},[2225,133954,2805],{"class":2235},[2225,133956,2243],{"class":2235},[2225,133958,2362],{"class":2239},[2225,133960,133961,133963,133965],{"class":2227,"line":2304},[2225,133962,26235],{"class":2235},[2225,133964,3230],{"class":2291},[2225,133966,8146],{"class":2239},[2225,133968,133969],{"class":2227,"line":2356},[2225,133970,133971],{"class":2763},"\u002F\u002F → 3 rows: q1 × Alice, q1 × Bob, q1 × Carol\n",[2225,133973,133974],{"class":2227,"line":2830},[2225,133975,2285],{"emptyLinePlaceholder":2284},[2225,133977,133978],{"class":2227,"line":2861},[2225,133979,133980],{"class":2763},"\u002F\u002F Alias filter after: expands all 6 pairs, keeps Alice's\n",[2225,133982,133983,133985,133987,133989],{"class":2227,"line":4246},[2225,133984,776],{"class":2239},[2225,133986,1520],{"class":2235},[2225,133988,5286],{"class":2291},[2225,133990,8146],{"class":2239},[2225,133992,133993,133995,133997,133999,134001,134003,134005,134007,134009,134011,134013,134015,134017,134019,134021,134023,134025,134027,134029,134031,134033,134035],{"class":2227,"line":4253},[2225,133994,26235],{"class":2235},[2225,133996,133197],{"class":2291},[2225,133998,6474],{"class":2235},[2225,134000,132831],{"class":6342},[2225,134002,2298],{"class":2235},[2225,134004,2249],{"class":2235},[2225,134006,133208],{"class":2252},[2225,134008,2805],{"class":2235},[2225,134010,6480],{"class":2235},[2225,134012,2789],{"class":2239},[2225,134014,2805],{"class":2235},[2225,134016,132840],{"class":2252},[2225,134018,2805],{"class":2235},[2225,134020,2298],{"class":2235},[2225,134022,2236],{"class":2235},[2225,134024,2266],{"class":2307},[2225,134026,2311],{"class":2235},[2225,134028,2249],{"class":2235},[2225,134030,133208],{"class":2252},[2225,134032,2805],{"class":2235},[2225,134034,2243],{"class":2235},[2225,134036,2362],{"class":2239},[2225,134038,134039,134041,134043,134045,134047,134050,134052,134054,134056,134058,134060,134062,134064,134066,134068],{"class":2227,"line":4259},[2225,134040,26235],{"class":2235},[2225,134042,3016],{"class":2291},[2225,134044,2789],{"class":2239},[2225,134046,2805],{"class":2235},[2225,134048,134049],{"class":2252},"worker.name",[2225,134051,2805],{"class":2235},[2225,134053,2298],{"class":2235},[2225,134055,2249],{"class":2235},[2225,134057,21901],{"class":2252},[2225,134059,2805],{"class":2235},[2225,134061,2298],{"class":2235},[2225,134063,2249],{"class":2235},[2225,134065,10533],{"class":2252},[2225,134067,2805],{"class":2235},[2225,134069,2362],{"class":2239},[2225,134071,134072,134074,134076],{"class":2227,"line":6548},[2225,134073,26235],{"class":2235},[2225,134075,3230],{"class":2291},[2225,134077,8146],{"class":2239},[2225,134079,134080],{"class":2227,"line":6576},[2225,134081,134082],{"class":2763},"\u002F\u002F → 2 rows: q1 × Alice, q2 × Alice\n",[1591,134084,134086],{"id":134085},"live-queries","Live queries",[937,134088,134089,134091,134092,134094,134095,134098,134099,134102],{},[941,134090,3027],{}," on a cross-join query subscribes to the left collection ",[950,134093,14128],{}," every cross-join target (deduplicated by collection name). A mutation on either side re-runs the query and notifies subscribers. Always call ",[941,134096,134097],{},"live.stop()"," when finished. See ",[1007,134100,727],{"href":134101},".\u002Flive"," for the reactive primitive itself.",[2217,134104,134106],{"className":2219,"code":134105,"language":2221,"meta":1037,"style":1037},"const live = await periods.query()\n  .crossJoin\u003CWorker, 'worker'>('workers', { as: 'worker' })\n  .live()\n\nlive.subscribe(() => console.log('pairs now:', live.value.length))\n\nawait workers.put('w4', { name: 'Dan', since: '2026-06', until: null })\n\u002F\u002F → \"pairs now: 8\"  (right-side mutation re-fired the query: 2 × 4)\n\nlive.stop()\n",[941,134107,134108,134127,134173,134181,134185,134228,134232,134290,134295,134299],{"__ignoreMap":1037},[2225,134109,134110,134112,134115,134117,134119,134121,134123,134125],{"class":2227,"line":2228},[2225,134111,2775],{"class":2774},[2225,134113,134114],{"class":2239}," live ",[2225,134116,2781],{"class":2235},[2225,134118,2784],{"class":2231},[2225,134120,132855],{"class":2239},[2225,134122,1520],{"class":2235},[2225,134124,5286],{"class":2291},[2225,134126,8146],{"class":2239},[2225,134128,134129,134131,134133,134135,134137,134139,134141,134143,134145,134147,134149,134151,134153,134155,134157,134159,134161,134163,134165,134167,134169,134171],{"class":2227,"line":1041},[2225,134130,26235],{"class":2235},[2225,134132,133197],{"class":2291},[2225,134134,6474],{"class":2235},[2225,134136,132831],{"class":6342},[2225,134138,2298],{"class":2235},[2225,134140,2249],{"class":2235},[2225,134142,133208],{"class":2252},[2225,134144,2805],{"class":2235},[2225,134146,6480],{"class":2235},[2225,134148,2789],{"class":2239},[2225,134150,2805],{"class":2235},[2225,134152,132840],{"class":2252},[2225,134154,2805],{"class":2235},[2225,134156,2298],{"class":2235},[2225,134158,2236],{"class":2235},[2225,134160,2266],{"class":2307},[2225,134162,2311],{"class":2235},[2225,134164,2249],{"class":2235},[2225,134166,133208],{"class":2252},[2225,134168,2805],{"class":2235},[2225,134170,2243],{"class":2235},[2225,134172,2362],{"class":2239},[2225,134174,134175,134177,134179],{"class":2227,"line":1038},[2225,134176,26235],{"class":2235},[2225,134178,727],{"class":2291},[2225,134180,8146],{"class":2239},[2225,134182,134183],{"class":2227,"line":2288},[2225,134184,2285],{"emptyLinePlaceholder":2284},[2225,134186,134187,134189,134191,134193,134195,134197,134199,134202,134204,134206,134208,134210,134213,134215,134217,134219,134221,134223,134225],{"class":2227,"line":2304},[2225,134188,727],{"class":2239},[2225,134190,1520],{"class":2235},[2225,134192,25042],{"class":2291},[2225,134194,2789],{"class":2239},[2225,134196,4228],{"class":2235},[2225,134198,8116],{"class":2774},[2225,134200,134201],{"class":2239}," console",[2225,134203,1520],{"class":2235},[2225,134205,27100],{"class":2291},[2225,134207,2789],{"class":2239},[2225,134209,2805],{"class":2235},[2225,134211,134212],{"class":2252},"pairs now:",[2225,134214,2805],{"class":2235},[2225,134216,2298],{"class":2235},[2225,134218,99296],{"class":2239},[2225,134220,1520],{"class":2235},[2225,134222,25201],{"class":2239},[2225,134224,1520],{"class":2235},[2225,134226,134227],{"class":2239},"length))\n",[2225,134229,134230],{"class":2227,"line":2356},[2225,134231,2285],{"emptyLinePlaceholder":2284},[2225,134233,134234,134236,134238,134240,134242,134244,134246,134249,134251,134253,134255,134257,134259,134261,134264,134266,134268,134270,134272,134274,134276,134278,134280,134282,134284,134286,134288],{"class":2227,"line":2830},[2225,134235,6500],{"class":2231},[2225,134237,132985],{"class":2239},[2225,134239,1520],{"class":2235},[2225,134241,3161],{"class":2291},[2225,134243,2789],{"class":2239},[2225,134245,2805],{"class":2235},[2225,134247,134248],{"class":2252},"w4",[2225,134250,2805],{"class":2235},[2225,134252,2298],{"class":2235},[2225,134254,2236],{"class":2235},[2225,134256,8104],{"class":2307},[2225,134258,2311],{"class":2235},[2225,134260,2249],{"class":2235},[2225,134262,134263],{"class":2252},"Dan",[2225,134265,2805],{"class":2235},[2225,134267,2298],{"class":2235},[2225,134269,61185],{"class":2307},[2225,134271,2311],{"class":2235},[2225,134273,2249],{"class":2235},[2225,134275,27015],{"class":2252},[2225,134277,2805],{"class":2235},[2225,134279,2298],{"class":2235},[2225,134281,132739],{"class":2307},[2225,134283,2311],{"class":2235},[2225,134285,7090],{"class":2235},[2225,134287,2243],{"class":2235},[2225,134289,2362],{"class":2239},[2225,134291,134292],{"class":2227,"line":2861},[2225,134293,134294],{"class":2763},"\u002F\u002F → \"pairs now: 8\"  (right-side mutation re-fired the query: 2 × 4)\n",[2225,134296,134297],{"class":2227,"line":4246},[2225,134298,2285],{"emptyLinePlaceholder":2284},[2225,134300,134301,134303,134305,134307],{"class":2227,"line":4253},[2225,134302,727],{"class":2239},[2225,134304,1520],{"class":2235},[2225,134306,27604],{"class":2291},[2225,134308,8146],{"class":2239},[1591,134310,134312],{"id":134311},"inside-materialized-views","Inside materialized views",[937,134314,134315,134316,34689,134319,134321,134322,134325,134326,1324,134328,134330,134331,134334,134335,134338],{},"A cross-join inside a ",[1007,134317,134318],{"href":124624},"materialized view",[941,134320,24715],{}," callback participates in the view's ",[941,134323,134324],{},"queryHash"," — the fingerprint of the query structure that tells the view when its definition changed and its rows must be refreshed. An ",[950,134327,110783],{},[941,134329,132476],{}," callback cannot be fingerprinted, so it disables that change detection for the cross-join (a console warning says so at build time). Use the named-predicate form instead: declare the predicate in the view's ",[941,134332,134333],{},"predicates"," map (each entry pairs a consumer-stable ",[941,134336,134337],{},"hash"," with the function) and reference it by name:",[2217,134340,134342],{"className":2219,"code":134341,"language":2221,"meta":1037,"style":1037},"\u002F\u002F inside a withMaterializedView definition:\npredicates: {\n  activeInPeriod: {\n    hash: 'activeInPeriod-v1',\n    fn: (row: Period & { worker: Worker }) =>\n      row.worker.since \u003C= row.start &&\n      (row.worker.until === null || row.worker.until >= row.end),\n  },\n},\nquery: (v) =>\n  v.collection\u003CPeriod>('periods').query()\n    .crossJoin\u003CWorker, 'worker'>('workers', {\n      as: 'worker',\n      on: { predicate: 'activeInPeriod' },\n    }),\n",[941,134343,134344,134349,134357,134366,134382,134413,134439,134483,134487,134491,134505,134536,134569,134584,134607],{"__ignoreMap":1037},[2225,134345,134346],{"class":2227,"line":2228},[2225,134347,134348],{"class":2763},"\u002F\u002F inside a withMaterializedView definition:\n",[2225,134350,134351,134353,134355],{"class":2227,"line":1041},[2225,134352,134333],{"class":6342},[2225,134354,2311],{"class":2235},[2225,134356,2301],{"class":2235},[2225,134358,134359,134362,134364],{"class":2227,"line":1038},[2225,134360,134361],{"class":6342},"  activeInPeriod",[2225,134363,2311],{"class":2235},[2225,134365,2301],{"class":2235},[2225,134367,134368,134371,134373,134375,134378,134380],{"class":2227,"line":2288},[2225,134369,134370],{"class":6342},"    hash",[2225,134372,2311],{"class":2235},[2225,134374,2249],{"class":2235},[2225,134376,134377],{"class":2252},"activeInPeriod-v1",[2225,134379,2805],{"class":2235},[2225,134381,1707],{"class":2235},[2225,134383,134384,134387,134389,134391,134394,134396,134398,134401,134403,134405,134407,134409,134411],{"class":2227,"line":2304},[2225,134385,134386],{"class":6342},"    fn",[2225,134388,2311],{"class":2235},[2225,134390,953],{"class":2235},[2225,134392,134393],{"class":7052},"row",[2225,134395,2311],{"class":2235},[2225,134397,132678],{"class":6342},[2225,134399,134400],{"class":2235}," &",[2225,134402,2236],{"class":2235},[2225,134404,133665],{"class":2307},[2225,134406,2311],{"class":2235},[2225,134408,132716],{"class":6342},[2225,134410,43776],{"class":2235},[2225,134412,71229],{"class":2774},[2225,134414,134415,134418,134420,134422,134424,134426,134429,134432,134434,134436],{"class":2227,"line":2356},[2225,134416,134417],{"class":2239},"      row",[2225,134419,1520],{"class":2235},[2225,134421,133208],{"class":2239},[2225,134423,1520],{"class":2235},[2225,134425,7883],{"class":2239},[2225,134427,134428],{"class":2235}," \u003C=",[2225,134430,134431],{"class":2239}," row",[2225,134433,1520],{"class":2235},[2225,134435,906],{"class":2239},[2225,134437,134438],{"class":2235}," &&\n",[2225,134440,134441,134444,134446,134448,134450,134452,134454,134456,134458,134460,134462,134464,134466,134468,134470,134473,134475,134477,134479,134481],{"class":2227,"line":2830},[2225,134442,134443],{"class":2307},"      (",[2225,134445,134393],{"class":2239},[2225,134447,1520],{"class":2235},[2225,134449,133208],{"class":2239},[2225,134451,1520],{"class":2235},[2225,134453,75352],{"class":2239},[2225,134455,9025],{"class":2235},[2225,134457,7090],{"class":2235},[2225,134459,113501],{"class":2235},[2225,134461,134431],{"class":2239},[2225,134463,1520],{"class":2235},[2225,134465,133208],{"class":2239},[2225,134467,1520],{"class":2235},[2225,134469,75352],{"class":2239},[2225,134471,134472],{"class":2235}," >=",[2225,134474,134431],{"class":2239},[2225,134476,1520],{"class":2235},[2225,134478,133302],{"class":2239},[2225,134480,1381],{"class":2307},[2225,134482,1707],{"class":2235},[2225,134484,134485],{"class":2227,"line":2861},[2225,134486,19017],{"class":2235},[2225,134488,134489],{"class":2227,"line":4246},[2225,134490,19008],{"class":2235},[2225,134492,134493,134495,134497,134499,134501,134503],{"class":2227,"line":4253},[2225,134494,5286],{"class":6342},[2225,134496,2311],{"class":2235},[2225,134498,953],{"class":2235},[2225,134500,8160],{"class":7052},[2225,134502,1381],{"class":2235},[2225,134504,71229],{"class":2774},[2225,134506,134507,134510,134512,134514,134516,134518,134520,134522,134524,134526,134528,134530,134532,134534],{"class":2227,"line":4259},[2225,134508,134509],{"class":2239},"  v",[2225,134511,1520],{"class":2235},[2225,134513,6471],{"class":2291},[2225,134515,6474],{"class":2235},[2225,134517,132799],{"class":6342},[2225,134519,6480],{"class":2235},[2225,134521,2789],{"class":2239},[2225,134523,2805],{"class":2235},[2225,134525,776],{"class":2252},[2225,134527,2805],{"class":2235},[2225,134529,1381],{"class":2239},[2225,134531,1520],{"class":2235},[2225,134533,5286],{"class":2291},[2225,134535,8146],{"class":2239},[2225,134537,134538,134541,134543,134545,134547,134549,134551,134553,134555,134557,134559,134561,134563,134565,134567],{"class":2227,"line":6548},[2225,134539,134540],{"class":2235},"    .",[2225,134542,133197],{"class":2291},[2225,134544,6474],{"class":2235},[2225,134546,132831],{"class":6342},[2225,134548,2298],{"class":2235},[2225,134550,2249],{"class":2235},[2225,134552,133208],{"class":2252},[2225,134554,2805],{"class":2235},[2225,134556,6480],{"class":2235},[2225,134558,2789],{"class":2239},[2225,134560,2805],{"class":2235},[2225,134562,132840],{"class":2252},[2225,134564,2805],{"class":2235},[2225,134566,2298],{"class":2235},[2225,134568,2301],{"class":2235},[2225,134570,134571,134574,134576,134578,134580,134582],{"class":2227,"line":6576},[2225,134572,134573],{"class":2307},"      as",[2225,134575,2311],{"class":2235},[2225,134577,2249],{"class":2235},[2225,134579,133208],{"class":2252},[2225,134581,2805],{"class":2235},[2225,134583,1707],{"class":2235},[2225,134585,134586,134589,134591,134593,134596,134598,134600,134603,134605],{"class":2227,"line":6582},[2225,134587,134588],{"class":2307},"      on",[2225,134590,2311],{"class":2235},[2225,134592,2236],{"class":2235},[2225,134594,134595],{"class":2307}," predicate",[2225,134597,2311],{"class":2235},[2225,134599,2249],{"class":2235},[2225,134601,134602],{"class":2252},"activeInPeriod",[2225,134604,2805],{"class":2235},[2225,134606,2353],{"class":2235},[2225,134608,134609,134611,134613],{"class":2227,"line":6587},[2225,134610,80146],{"class":2235},[2225,134612,1381],{"class":2239},[2225,134614,1707],{"class":2235},[937,134616,134617,134618,134621,134622,134624,134625,134627,134628,134631,134632,134634],{},"The predicate function receives the ",[1092,134619,134620],{},"expanded"," row (left fields plus the alias field). Bump the ",[941,134623,134337],{}," whenever the function's semantics change — that folds into the ",[941,134626,134324],{}," and forces a refresh. The ",[941,134629,134630],{},"{ predicate }"," form throws outside a view that declares a ",[941,134633,134333],{}," map, and throws if the name is not registered.",[1022,134636,101012],{"id":41259},[1591,134638,134640],{"id":134639},"querycrossjointarget-opts",[941,134641,134642],{},"query.crossJoin(target, opts)",[2217,134644,134646],{"className":2219,"code":134645,"language":2221,"meta":1037,"style":1037},"crossJoin\u003CTTarget, As extends string>(\n  target: string,\n  opts: {\n    as: As\n    on?: ((left: T) => TTarget[] | ((right: TTarget) => boolean))\n       | { predicate: string }\n    maxRows?: number\n  }\n): Query\u003CT & { [K in As]: TTarget }>\n",[941,134647,134648,134669,134680,134688,134697,134744,134759,134768,134772],{"__ignoreMap":1037},[2225,134649,134650,134652,134654,134657,134659,134662,134664,134666],{"class":2227,"line":2228},[2225,134651,133197],{"class":2291},[2225,134653,6474],{"class":2235},[2225,134655,134656],{"class":6342},"TTarget",[2225,134658,2298],{"class":2235},[2225,134660,134661],{"class":6342}," As",[2225,134663,124350],{"class":2774},[2225,134665,6366],{"class":6342},[2225,134667,134668],{"class":2239},">(\n",[2225,134670,134671,134674,134676,134678],{"class":2227,"line":1041},[2225,134672,134673],{"class":7052},"  target",[2225,134675,2311],{"class":2235},[2225,134677,6366],{"class":6342},[2225,134679,1707],{"class":2235},[2225,134681,134682,134684,134686],{"class":2227,"line":1038},[2225,134683,11695],{"class":7052},[2225,134685,2311],{"class":2235},[2225,134687,2301],{"class":2235},[2225,134689,134690,134692,134694],{"class":2227,"line":2288},[2225,134691,65647],{"class":2307},[2225,134693,2311],{"class":2235},[2225,134695,134696],{"class":6342}," As\n",[2225,134698,134699,134701,134703,134705,134707,134710,134712,134714,134716,134718,134721,134723,134725,134727,134729,134732,134734,134736,134738,134740,134742],{"class":2227,"line":2304},[2225,134700,133604],{"class":2307},[2225,134702,7011],{"class":2235},[2225,134704,953],{"class":2239},[2225,134706,2789],{"class":2235},[2225,134708,134709],{"class":7052},"left",[2225,134711,2311],{"class":2235},[2225,134713,24772],{"class":6342},[2225,134715,1381],{"class":2235},[2225,134717,8116],{"class":2774},[2225,134719,134720],{"class":6342}," TTarget",[2225,134722,82213],{"class":2239},[2225,134724,18307],{"class":2235},[2225,134726,953],{"class":2239},[2225,134728,2789],{"class":2235},[2225,134730,134731],{"class":7052},"right",[2225,134733,2311],{"class":2235},[2225,134735,134720],{"class":6342},[2225,134737,1381],{"class":2235},[2225,134739,8116],{"class":2774},[2225,134741,7723],{"class":6342},[2225,134743,8606],{"class":2239},[2225,134745,134746,134749,134751,134753,134755,134757],{"class":2227,"line":2356},[2225,134747,134748],{"class":2235},"       |",[2225,134750,2236],{"class":2235},[2225,134752,134595],{"class":2307},[2225,134754,2311],{"class":2235},[2225,134756,6366],{"class":6342},[2225,134758,6369],{"class":2235},[2225,134760,134761,134764,134766],{"class":2227,"line":2830},[2225,134762,134763],{"class":2307},"    maxRows",[2225,134765,7011],{"class":2235},[2225,134767,12024],{"class":6342},[2225,134769,134770],{"class":2227,"line":2861},[2225,134771,7632],{"class":2235},[2225,134773,134774,134776,134778,134781,134783,134785,134787,134789,134791,134793,134796,134798,134800,134802,134804],{"class":2227,"line":4246},[2225,134775,1381],{"class":2239},[2225,134777,2311],{"class":2235},[2225,134779,134780],{"class":6342}," Query",[2225,134782,6474],{"class":2235},[2225,134784,11968],{"class":6342},[2225,134786,134400],{"class":2235},[2225,134788,2236],{"class":2235},[2225,134790,8667],{"class":2239},[2225,134792,12279],{"class":6342},[2225,134794,134795],{"class":2235}," in",[2225,134797,134661],{"class":6342},[2225,134799,8803],{"class":2239},[2225,134801,2311],{"class":2235},[2225,134803,134720],{"class":6342},[2225,134805,17861],{"class":2235},[937,134807,134808,134810,134811,134813,134814,134817,134818,134820,134821,134823],{},[941,134809,11968],{}," is the left collection's record type — the query must come from ",[941,134812,25801],{}," (a bare ",[941,134815,134816],{},"new Query(...)"," has no join context and ",[941,134819,132508],{}," throws). Returns a new immutable ",[941,134822,46896],{}," whose row type gains the alias field; the original query is unchanged.",[1338,134825,134826,134838],{},[1341,134827,134828],{},[1344,134829,134830,134832,134834,134836],{},[1347,134831,101060],{},[1347,134833,20194],{},[1347,134835,20197],{},[1347,134837,7834],{},[1357,134839,134840,134855,134872],{},[1344,134841,134842,134846,134850,134852],{},[1362,134843,134844],{},[941,134845,4899],{},[1362,134847,134848],{},[941,134849,7259],{},[1362,134851,45375],{},[1362,134853,134854],{},"Field name the right-side row is attached under.",[1344,134856,134857,134861,134866,134869],{},[1362,134858,134859],{},[941,134860,4883],{},[1362,134862,134863,134864],{},"callback or ",[941,134865,134630],{},[1362,134867,134868],{},"none — full Cartesian product",[1362,134870,134871],{},"Lateral filter; see shapes below.",[1344,134873,134874,134879,134883,134893],{},[1362,134875,134876],{},[941,134877,134878],{},"maxRows",[1362,134880,134881],{},[941,134882,20636],{},[1362,134884,134885,953,134888,106932,134891,1381],{},[941,134886,134887],{},"50_000",[941,134889,134890],{},"DEFAULT_CROSS_JOIN_MAX_ROWS",[941,134892,1086],{},[1362,134894,134895],{},"Per-clause result-row ceiling.",[937,134897,134898],{},[950,134899,134900,134902],{},[941,134901,132476],{}," shapes",[1338,134904,134905,134915],{},[1341,134906,134907],{},[1344,134908,134909,134911,134913],{},[1347,134910,50797],{},[1347,134912,131736],{},[1347,134914,124081],{},[1357,134916,134917,134930,134943],{},[1344,134918,134919,134922,134927],{},[1362,134920,134921],{},"Subset",[1362,134923,134924],{},[941,134925,134926],{},"(left) => TTarget[]",[1362,134928,134929],{},"You can compute each left row's partners directly — most efficient.",[1344,134931,134932,134935,134940],{},[1362,134933,134934],{},"Predicate",[1362,134936,134937],{},[941,134938,134939],{},"(left) => (right) => boolean",[1362,134941,134942],{},"The executor scans the right side per left row and keeps matches.",[1344,134944,134945,134948,134953],{},[1362,134946,134947],{},"Named predicate",[1362,134949,134950],{},[941,134951,134952],{},"{ predicate: 'name' }",[1362,134954,134955,134956,134958],{},"Inside a materialized view — keeps ",[941,134957,134324],{}," change detection working.",[1591,134960,120265],{"id":120264},[937,134962,134963,134964,2311],{},"Both are importable from ",[941,134965,1086],{},[1111,134967,134968,134996],{},[1114,134969,134970,2104,134975,134978,134979,134981,134982,968,134985,134987,134988,134990,134991,134993,134994,1520],{},[950,134971,134972],{},[941,134973,134974],{},"CrossJoinTooLargeError",[941,134976,134977],{},"CROSS_JOIN_TOO_LARGE",") — the product would exceed the ceiling. Thrown ",[1092,134980,11357],{}," the result is allocated. Carries ",[941,134983,134984],{},"target",[941,134986,11192],{}," (the row count that would have been produced), and ",[941,134989,3022],{},". Narrow the left side with ",[941,134992,24883],{}," first, or raise the ceiling with ",[941,134995,65219],{},[1114,134997,134998,2104,135003,135006,135007,997,135009,1520],{},[950,134999,135000],{},[941,135001,135002],{},"CrossJoinSourceUnknownError",[941,135004,135005],{},"CROSS_JOIN_SOURCE_UNKNOWN",") — the target collection is not known in the vault at execution time. Carries ",[941,135008,134984],{},[941,135010,135011],{},"leftCollection",[1022,135013,68210],{"id":68209},[1111,135015,135016,135033,135048,135062,135076,135094],{},[1114,135017,135018,135021,135022,135025,135026,135029,135030,135032],{},[950,135019,135020],{},"Row ceiling, checked before allocation."," For a full Cartesian join, ",[941,135023,135024],{},"leftRows × rightRows"," is compared to the ceiling up front. For the lateral form, the ceiling is charged ",[950,135027,135028],{},"cumulatively"," — it applies to the total post-filter row count across ",[1092,135031,59366],{}," left rows, not per left row.",[1114,135034,135035,135040,135041,135044,135045,135047],{},[950,135036,135037,135039],{},[941,135038,24913],{}," counts expanded rows."," On a cross-join query, ",[941,135042,135043],{},"query.count()"," returns the size of the expanded relation (left × right after ",[941,135046,132476],{},"), not the left-side cardinality.",[1114,135049,135050,135053,135054,135056,135057,27678,135059,1520],{},[950,135051,135052],{},"Same vault only."," Both collections must be open in the same vault, or execution throws ",[941,135055,135002],{},". For reads that span vaults, use ",[941,135058,27688],{},[1007,135060,135061],{"href":83069},"query basics",[1114,135063,135064,135067,135068,135071,135072,135075],{},[950,135065,135066],{},"Plan-only execution is unsupported."," The standalone ",[941,135069,135070],{},"executePlan()"," helper throws on plans containing cross-join clauses — execute through ",[941,135073,135074],{},"collection.query()…toArray()",", which carries the join context.",[1114,135077,135078,135084,135085,135087,135088,16238,135091,957],{},[950,135079,135080,135081,135083],{},"Not usable as a ",[941,135082,75590],{}," scope."," A query containing cross-join clauses cannot be passed as the ",[941,135086,111736],{}," scope of ",[941,135089,135090],{},"collection.retrieve()",[1007,135092,4457],{"href":135093},".\u002Fsearch",[1114,135095,135096,135101,135102,135104,135105,24812],{},[950,135097,126706,135098,135100],{},[941,135099,132476],{}," inside a materialized view"," disables ",[941,135103,134324],{}," change detection for that cross-join and emits a build-time console warning — use ",[941,135106,135107],{},"on: { predicate: 'name' }",[937,135109,135110],{},"Left\u002Fouter cross-join variants and cross-joins over materialized-view virtual collections are not available.",[1022,135112,2621],{"id":2620},[1111,135114,135115,135123,135130,135138,135145,135157],{},[1114,135116,135117,135119,135120,135122],{},[1007,135118,715],{"href":132487}," — FK joins over declared ",[941,135121,62282],{}," relationships (use these when a reference exists)",[1114,135124,135125,964,135127,135129],{},[1007,135126,4449],{"href":123485},[941,135128,27496],{}," + reducers over the expanded rows",[1114,135131,135132,135134,135135,135137],{},[1007,135133,727],{"href":134101}," — the reactive query primitive ",[941,135136,3027],{}," returns",[1114,135139,135140,135142,135143],{},[1007,135141,739],{"href":124624}," — named predicates and ",[941,135144,134324],{},[1114,135146,135147,964,135149,1261,135151,1261,135153,997,135155],{},[1007,135148,3216],{"href":83069},[941,135150,3016],{},[941,135152,8686],{},[941,135154,3019],{},[941,135156,27688],{},[1114,135158,101581,135159,1520],{},[1007,135160,135162],{"href":93740,"rel":135161},[3371],[941,135163,135164],{},"92-with-cross-join",[2645,135166,135167],{},"html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html pre.shiki code .sHdIc, html code.shiki .sHdIc{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#EEFFFF;--shiki-default-font-style:italic;--shiki-dark:#BABED8;--shiki-dark-font-style:italic}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}",{"title":1037,"searchDepth":1038,"depth":1038,"links":135169},[135170,135171,135172,135179,135183,135184],{"id":29856,"depth":1041,"text":29857},{"id":58393,"depth":1041,"text":58394},{"id":29892,"depth":1041,"text":29893,"children":135173},[135174,135175,135176,135177,135178],{"id":133160,"depth":1038,"text":133161},{"id":133493,"depth":1038,"text":133494},{"id":133826,"depth":1038,"text":133827},{"id":134085,"depth":1038,"text":134086},{"id":134311,"depth":1038,"text":134312},{"id":41259,"depth":1041,"text":101012,"children":135180},[135181,135182],{"id":134639,"depth":1038,"text":134642},{"id":120264,"depth":1038,"text":120265},{"id":68209,"depth":1041,"text":68210},{"id":2620,"depth":1041,"text":2621},"Cartesian and lateral cross-joins in the query DSL, with a 50,000-row cost ceiling enforced before allocation.",{},{"title":657,"description":135185},"0mcXwS1Ah5yfVpjBDLGYVplwQMHDndyKFyj0hY9b-v8",{"id":135190,"title":661,"access":12,"apiToc":929,"body":135191,"category":1042,"description":137845,"extension":1044,"meta":137846,"navigation":1042,"nest":1047,"path":662,"seo":137847,"sinceVersion":1042,"status":1049,"stem":663,"subtitle":1042,"tier":1050,"tocExternal":1042,"__hash__":137848},"docs\u002Fservices\u002Fcustody.md",{"type":931,"value":135192,"toc":137823},[135193,135195,135198,135200,135205,135208,135235,135246,135248,135259,135261,135284,135586,135597,135599,135603,135613,135760,135841,135858,135862,135865,136278,136297,136301,136304,136347,136351,136360,136632,136733,136736,136792,136795,136969,136984,136986,136991,137007,137016,137022,137054,137070,137081,137193,137199,137231,137240,137282,137288,137317,137320,137325,137405,137411,137477,137481,137530,137536,137622,137624,137677,137679,137709,137711,137777,137779,137820],[934,135194,661],{"id":661},[46871,135196],{":rows":135197},"[{\"label\":\"Subpath\",\"value\":\"`@noy-db\u002Fhub` (root export — no dedicated subpath)\"},{\"label\":\"Factory\",\"value\":\"`withCustody()`\"},{\"label\":\"Layer\",\"value\":\"`with-party`\"}]",[1022,135199,29857],{"id":29856},[937,135201,135202,135203,957],{},"Suppose a service firm runs a client's data day-to-day, but the client must remain the only\ntrue owner — the firm must never be able to lock the client out, hand access to a third\nparty, or walk away with the data. The custody service encodes that arrangement\ncryptographically inside a vault (an encrypted, per-tenant data space — see\n",[1007,135204,124874],{"href":48},[937,135206,135207],{},"It rests on two pieces:",[1111,135209,135210,135221],{},[1114,135211,135212,135217,135218,135220],{},[950,135213,960,135214,135216],{},[941,135215,9904],{}," role."," A custodian's keyring (the per-user file of wrapped encryption\nkeys — see ",[1007,135219,107337],{"href":54},") carries\nevery collection's data-encryption key, so the custodian reads and writes everything.\nBut it is structurally denied every ownership-level act: it cannot grant or revoke users,\ncannot rotate keys, and cannot extract-and-sever the vault.",[1114,135222,135223,14636,135226,135229,135230,49935,135232,135234],{},[950,135224,135225],{},"The Deed.",[1092,135227,135228],{},"Deed vault"," is one whose owner never authenticates: the owner secret\nis minted machine-side as random bytes and sealed under a ",[941,135231,14350],{},[1092,135233,9913],{}," controls (their KMS, their keychain — never the operating firm's infrastructure).\nThat sealing boundary is the ownership anchor: only the holder of the provider can ever\nre-derive the owner key, and there is deliberately no firm-controlled fallback.",[937,135236,135237,135238,135241,135242,135245],{},"The third piece connects them: ",[941,135239,135240],{},"vault.custody.liberate()"," is the one audited route by which\na custodian becomes an owner — for example at the end of the service contract. Liberation\nmints a ",[1092,135243,135244],{},"distinct new owner"," from the custodian's keys and records the act in the vault's\nlifecycle ledger; it never unseals or impersonates the original sealed owner.",[1022,135247,70093],{"id":70092},[1111,135249,135250],{},[1114,135251,135252,135256,135257,1520],{},[950,135253,135254],{},[941,135255,10312],{}," — custody rides the multi-user keyring engine (grant\u002Frevoke), so the team strategy must be passed alongside ",[941,135258,11541],{},[1022,135260,99864],{"id":99863},[937,135262,124907,135263,10313,135265,135267,135268,2874,135270,997,135273,135276,135277,135280,135281,135283],{},[941,135264,10094],{},[941,135266,12780],{},". The custody gates ",[950,135269,2200],{},[941,135271,135272],{},"grant-custodian",[941,135274,135275],{},"liberate-vault"," are denied unless the vault policy explicitly\nenables them (revocation runs through the shared ",[941,135278,135279],{},"revoke-user"," gate). See\n",[1007,135282,844],{"href":845}," for the policy-gates DSL.",[2217,135285,135287],{"className":2219,"code":135286,"language":2221,"meta":1037,"style":1037},"import { createNoydb, withCustody, withTeam } from '@noy-db\u002Fhub'\nimport { toMemory } from '@noy-db\u002Fto-memory'\n\nconst store = toMemory()  \u002F\u002F any NoydbStore works\n\nconst db = await createNoydb({\n  store,\n  user: 'owner-01',\n  secret: 'owner-secret-long',\n  teamStrategy: withTeam(),        \u002F\u002F multi-user keyrings (grant\u002Frevoke engine)\n  custodyStrategy: withCustody(),  \u002F\u002F the custody surface\n  policy: {\n    gates: {\n      'grant-custodian': { enabled: true, minTier: 1 },\n      'liberate-vault':  { enabled: true, minTier: 1 },\n      'revoke-user':     { enabled: true, minTier: 1 },\n    },\n  },\n})\n\nconst vault = await db.openVault('acme')\n",[941,135288,135289,135316,135334,135338,135353,135357,135373,135379,135394,135409,135424,135440,135448,135456,135485,135513,135542,135546,135550,135556,135560],{"__ignoreMap":1037},[2225,135290,135291,135293,135295,135297,135299,135302,135304,135306,135308,135310,135312,135314],{"class":2227,"line":2228},[2225,135292,2232],{"class":2231},[2225,135294,2236],{"class":2235},[2225,135296,2744],{"class":2239},[2225,135298,2298],{"class":2235},[2225,135300,135301],{"class":2239}," withCustody",[2225,135303,2298],{"class":2235},[2225,135305,10373],{"class":2239},[2225,135307,2243],{"class":2235},[2225,135309,2246],{"class":2231},[2225,135311,2249],{"class":2235},[2225,135313,1086],{"class":2252},[2225,135315,2256],{"class":2235},[2225,135317,135318,135320,135322,135324,135326,135328,135330,135332],{"class":2227,"line":1041},[2225,135319,2232],{"class":2231},[2225,135321,2236],{"class":2235},[2225,135323,68576],{"class":2239},[2225,135325,2243],{"class":2235},[2225,135327,2246],{"class":2231},[2225,135329,2249],{"class":2235},[2225,135331,9638],{"class":2252},[2225,135333,2256],{"class":2235},[2225,135335,135336],{"class":2227,"line":1038},[2225,135337,2285],{"emptyLinePlaceholder":2284},[2225,135339,135340,135342,135344,135346,135348,135350],{"class":2227,"line":2288},[2225,135341,2775],{"class":2774},[2225,135343,39676],{"class":2239},[2225,135345,2781],{"class":2235},[2225,135347,68576],{"class":2291},[2225,135349,6598],{"class":2239},[2225,135351,135352],{"class":2763},"\u002F\u002F any NoydbStore works\n",[2225,135354,135355],{"class":2227,"line":2304},[2225,135356,2285],{"emptyLinePlaceholder":2284},[2225,135358,135359,135361,135363,135365,135367,135369,135371],{"class":2227,"line":2356},[2225,135360,2775],{"class":2774},[2225,135362,2778],{"class":2239},[2225,135364,2781],{"class":2235},[2225,135366,2784],{"class":2231},[2225,135368,2744],{"class":2291},[2225,135370,2789],{"class":2239},[2225,135372,4200],{"class":2235},[2225,135374,135375,135377],{"class":2227,"line":2830},[2225,135376,15608],{"class":2239},[2225,135378,1707],{"class":2235},[2225,135380,135381,135383,135385,135387,135390,135392],{"class":2227,"line":2861},[2225,135382,4205],{"class":2307},[2225,135384,2311],{"class":2235},[2225,135386,2249],{"class":2235},[2225,135388,135389],{"class":2252},"owner-01",[2225,135391,2805],{"class":2235},[2225,135393,1707],{"class":2235},[2225,135395,135396,135398,135400,135402,135405,135407],{"class":2227,"line":4246},[2225,135397,10423],{"class":2307},[2225,135399,2311],{"class":2235},[2225,135401,2249],{"class":2235},[2225,135403,135404],{"class":2252},"owner-secret-long",[2225,135406,2805],{"class":2235},[2225,135408,1707],{"class":2235},[2225,135410,135411,135413,135415,135417,135419,135421],{"class":2227,"line":4253},[2225,135412,10439],{"class":2307},[2225,135414,2311],{"class":2235},[2225,135416,10373],{"class":2291},[2225,135418,4228],{"class":2239},[2225,135420,2298],{"class":2235},[2225,135422,135423],{"class":2763},"        \u002F\u002F multi-user keyrings (grant\u002Frevoke engine)\n",[2225,135425,135426,135429,135431,135433,135435,135437],{"class":2227,"line":4259},[2225,135427,135428],{"class":2307},"  custodyStrategy",[2225,135430,2311],{"class":2235},[2225,135432,135301],{"class":2291},[2225,135434,4228],{"class":2239},[2225,135436,2298],{"class":2235},[2225,135438,135439],{"class":2763},"  \u002F\u002F the custody surface\n",[2225,135441,135442,135444,135446],{"class":2227,"line":6548},[2225,135443,18899],{"class":2307},[2225,135445,2311],{"class":2235},[2225,135447,2301],{"class":2235},[2225,135449,135450,135452,135454],{"class":2227,"line":6576},[2225,135451,18917],{"class":2307},[2225,135453,2311],{"class":2235},[2225,135455,2301],{"class":2235},[2225,135457,135458,135460,135462,135464,135466,135468,135471,135473,135475,135477,135479,135481,135483],{"class":2227,"line":6582},[2225,135459,18944],{"class":2235},[2225,135461,135272],{"class":2307},[2225,135463,2805],{"class":2235},[2225,135465,2311],{"class":2235},[2225,135467,2236],{"class":2235},[2225,135469,135470],{"class":2307}," enabled",[2225,135472,2311],{"class":2235},[2225,135474,2322],{"class":2321},[2225,135476,2298],{"class":2235},[2225,135478,18956],{"class":2307},[2225,135480,2311],{"class":2235},[2225,135482,9377],{"class":6527},[2225,135484,2353],{"class":2235},[2225,135486,135487,135489,135491,135493,135495,135497,135499,135501,135503,135505,135507,135509,135511],{"class":2227,"line":6587},[2225,135488,18944],{"class":2235},[2225,135490,135275],{"class":2307},[2225,135492,2805],{"class":2235},[2225,135494,2311],{"class":2235},[2225,135496,26354],{"class":2235},[2225,135498,135470],{"class":2307},[2225,135500,2311],{"class":2235},[2225,135502,2322],{"class":2321},[2225,135504,2298],{"class":2235},[2225,135506,18956],{"class":2307},[2225,135508,2311],{"class":2235},[2225,135510,9377],{"class":6527},[2225,135512,2353],{"class":2235},[2225,135514,135515,135517,135519,135521,135523,135526,135528,135530,135532,135534,135536,135538,135540],{"class":2227,"line":7174},[2225,135516,18944],{"class":2235},[2225,135518,135279],{"class":2307},[2225,135520,2805],{"class":2235},[2225,135522,2311],{"class":2235},[2225,135524,135525],{"class":2235},"     {",[2225,135527,135470],{"class":2307},[2225,135529,2311],{"class":2235},[2225,135531,2322],{"class":2321},[2225,135533,2298],{"class":2235},[2225,135535,18956],{"class":2307},[2225,135537,2311],{"class":2235},[2225,135539,9377],{"class":6527},[2225,135541,2353],{"class":2235},[2225,135543,135544],{"class":2227,"line":7180},[2225,135545,8324],{"class":2235},[2225,135547,135548],{"class":2227,"line":7220},[2225,135549,19017],{"class":2235},[2225,135551,135552,135554],{"class":2227,"line":7225},[2225,135553,2359],{"class":2235},[2225,135555,2362],{"class":2239},[2225,135557,135558],{"class":2227,"line":7231},[2225,135559,2285],{"emptyLinePlaceholder":2284},[2225,135561,135562,135564,135566,135568,135570,135572,135574,135576,135578,135580,135582,135584],{"class":2227,"line":7267},[2225,135563,2775],{"class":2774},[2225,135565,2835],{"class":2239},[2225,135567,2781],{"class":2235},[2225,135569,2784],{"class":2231},[2225,135571,2842],{"class":2239},[2225,135573,1520],{"class":2235},[2225,135575,2847],{"class":2291},[2225,135577,2789],{"class":2239},[2225,135579,2805],{"class":2235},[2225,135581,6450],{"class":2252},[2225,135583,2805],{"class":2235},[2225,135585,2362],{"class":2239},[937,135587,90416,135588,135590,135591,135593,135594,135596],{},[941,135589,11541],{},", every custody operation throws ",[941,135592,21344],{},". The\nheavy liberation engine is loaded via dynamic import inside ",[941,135595,11541],{},", so none of it\nships in your bundle until you opt in.",[1022,135598,29893],{"id":29892},[1591,135600,135602],{"id":135601},"provision-a-deed-vault-sealed-owner","Provision a Deed vault (sealed owner)",[937,135604,135605,135608,135609,135612],{},[941,135606,135607],{},"createDeedOwner"," is a store-level function (it mints the vault's very first owner, so\nthere is no vault instance yet). It generates a random owner secret, seals it under\nthe client-held provider, writes the owner keyring, and stamps a plaintext ",[941,135610,135611],{},"_meta\u002Fdeed","\nmarker:",[2217,135614,135616],{"className":2219,"code":135615,"language":2221,"meta":1037,"style":1037},"import { createDeedOwner, loadDeedMarker, MemorySealer } from '@noy-db\u002Fhub'\n\n\u002F\u002F Held by the CLIENT — their KMS or keychain, never the operating firm's infra.\n\u002F\u002F MemorySealer is the in-memory stand-in for tests and examples;\n\u002F\u002F production providers ship as separate packages.\nconst sealing = new MemorySealer({ id: 'client-kms' })\n\nawait createDeedOwner(store, 'acme', 'owner-01', sealing)\n\nconst marker = await loadDeedMarker(store, 'acme')\n",[941,135617,135618,135647,135651,135656,135661,135666,135698,135702,135731,135735],{"__ignoreMap":1037},[2225,135619,135620,135622,135624,135627,135629,135632,135634,135637,135639,135641,135643,135645],{"class":2227,"line":2228},[2225,135621,2232],{"class":2231},[2225,135623,2236],{"class":2235},[2225,135625,135626],{"class":2239}," createDeedOwner",[2225,135628,2298],{"class":2235},[2225,135630,135631],{"class":2239}," loadDeedMarker",[2225,135633,2298],{"class":2235},[2225,135635,135636],{"class":2239}," MemorySealer",[2225,135638,2243],{"class":2235},[2225,135640,2246],{"class":2231},[2225,135642,2249],{"class":2235},[2225,135644,1086],{"class":2252},[2225,135646,2256],{"class":2235},[2225,135648,135649],{"class":2227,"line":1041},[2225,135650,2285],{"emptyLinePlaceholder":2284},[2225,135652,135653],{"class":2227,"line":1038},[2225,135654,135655],{"class":2763},"\u002F\u002F Held by the CLIENT — their KMS or keychain, never the operating firm's infra.\n",[2225,135657,135658],{"class":2227,"line":2288},[2225,135659,135660],{"class":2763},"\u002F\u002F MemorySealer is the in-memory stand-in for tests and examples;\n",[2225,135662,135663],{"class":2227,"line":2304},[2225,135664,135665],{"class":2763},"\u002F\u002F production providers ship as separate packages.\n",[2225,135667,135668,135670,135673,135675,135677,135679,135681,135683,135685,135687,135689,135692,135694,135696],{"class":2227,"line":2356},[2225,135669,2775],{"class":2774},[2225,135671,135672],{"class":2239}," sealing ",[2225,135674,2781],{"class":2235},[2225,135676,8130],{"class":2235},[2225,135678,135636],{"class":2291},[2225,135680,2789],{"class":2239},[2225,135682,2792],{"class":2235},[2225,135684,7070],{"class":2307},[2225,135686,2311],{"class":2235},[2225,135688,2249],{"class":2235},[2225,135690,135691],{"class":2252},"client-kms",[2225,135693,2805],{"class":2235},[2225,135695,2243],{"class":2235},[2225,135697,2362],{"class":2239},[2225,135699,135700],{"class":2227,"line":2830},[2225,135701,2285],{"emptyLinePlaceholder":2284},[2225,135703,135704,135706,135708,135710,135712,135714,135716,135718,135720,135722,135724,135726,135728],{"class":2227,"line":2861},[2225,135705,6500],{"class":2231},[2225,135707,135626],{"class":2291},[2225,135709,39702],{"class":2239},[2225,135711,2298],{"class":2235},[2225,135713,2249],{"class":2235},[2225,135715,6450],{"class":2252},[2225,135717,2805],{"class":2235},[2225,135719,2298],{"class":2235},[2225,135721,2249],{"class":2235},[2225,135723,135389],{"class":2252},[2225,135725,2805],{"class":2235},[2225,135727,2298],{"class":2235},[2225,135729,135730],{"class":2239}," sealing)\n",[2225,135732,135733],{"class":2227,"line":4246},[2225,135734,2285],{"emptyLinePlaceholder":2284},[2225,135736,135737,135739,135742,135744,135746,135748,135750,135752,135754,135756,135758],{"class":2227,"line":4253},[2225,135738,2775],{"class":2774},[2225,135740,135741],{"class":2239}," marker ",[2225,135743,2781],{"class":2235},[2225,135745,2784],{"class":2231},[2225,135747,135631],{"class":2291},[2225,135749,39702],{"class":2239},[2225,135751,2298],{"class":2235},[2225,135753,2249],{"class":2235},[2225,135755,6450],{"class":2252},[2225,135757,2805],{"class":2235},[2225,135759,2362],{"class":2239},[2217,135761,135763],{"className":6604,"code":135762,"language":141,"meta":1037,"style":1037},"{\n  \"ownerUserId\": \"owner-01\",\n  \"sealedUnder\": \"client-kms\",\n  \"latent\": true,\n  \"issuedAt\": \"2026-07-08T09:14:03.412Z\"\n}\n",[941,135764,135765,135769,135788,135807,135820,135837],{"__ignoreMap":1037},[2225,135766,135767],{"class":2227,"line":2228},[2225,135768,4200],{"class":2235},[2225,135770,135771,135773,135776,135778,135780,135782,135784,135786],{"class":2227,"line":1041},[2225,135772,9368],{"class":2235},[2225,135774,135775],{"class":2774},"ownerUserId",[2225,135777,6620],{"class":2235},[2225,135779,2311],{"class":2235},[2225,135781,6614],{"class":2235},[2225,135783,135389],{"class":2252},[2225,135785,6620],{"class":2235},[2225,135787,1707],{"class":2235},[2225,135789,135790,135792,135795,135797,135799,135801,135803,135805],{"class":2227,"line":1038},[2225,135791,9368],{"class":2235},[2225,135793,135794],{"class":2774},"sealedUnder",[2225,135796,6620],{"class":2235},[2225,135798,2311],{"class":2235},[2225,135800,6614],{"class":2235},[2225,135802,135691],{"class":2252},[2225,135804,6620],{"class":2235},[2225,135806,1707],{"class":2235},[2225,135808,135809,135811,135814,135816,135818],{"class":2227,"line":2288},[2225,135810,9368],{"class":2235},[2225,135812,135813],{"class":2774},"latent",[2225,135815,6620],{"class":2235},[2225,135817,2311],{"class":2235},[2225,135819,69451],{"class":2235},[2225,135821,135822,135824,135827,135829,135831,135833,135835],{"class":2227,"line":2304},[2225,135823,9368],{"class":2235},[2225,135825,135826],{"class":2774},"issuedAt",[2225,135828,6620],{"class":2235},[2225,135830,2311],{"class":2235},[2225,135832,6614],{"class":2235},[2225,135834,125448],{"class":2252},[2225,135836,9449],{"class":2235},[2225,135838,135839],{"class":2227,"line":2356},[2225,135840,7351],{"class":2235},[937,135842,135843,135844,135846,135847,135849,135850,135852,135853,135855,135856,957],{},"The marker is deliberately ",[950,135845,2985],{},": an auditor (or the custodian) must be able to see\n\"this is a Deed vault, owner is ",[941,135848,135389],{},", sealed under ",[941,135851,135691],{},"\" without any key. It\ncarries no secret material — only the owner ",[1092,135854,106938],{}," is sealed. The latent owner never\ntypes a secret; re-entry goes through the same sealing provider (managed-secret\nmode — see ",[1007,135857,844],{"href":845},[1591,135859,135861],{"id":135860},"grant-a-custodian-full-operation-no-ownership","Grant a custodian — full operation, no ownership",[937,135863,135864],{},"Only the owner can mint a custodian (an admin caller is denied). The custodian receives a\nkeyring wrapping every collection key:",[2217,135866,135868],{"className":2219,"code":135867,"language":2221,"meta":1037,"style":1037},"await vault.custody.grantCustodian({\n  userId: 'firm-01',\n  displayName: 'Operating firm',\n  secret: 'firm-pass-long',\n})\n\n\u002F\u002F The custodian opens the vault and operates every collection…\nconst firmDb = await createNoydb({\n  store, user: 'firm-01', secret: 'firm-pass-long', teamStrategy: withTeam(),\n})\nconst firmVault = await firmDb.openVault('acme')\n\nawait firmVault.collection('invoices').get('inv-001')\n\u002F\u002F → { id: 'inv-001', amount: 5000, status: 'draft' }\nawait firmVault.collection('invoices').put('inv-002', { id: 'inv-002', amount: 1200, status: 'sent' })\n\u002F\u002F → ok — full read\u002Fwrite on every collection\n\n\u002F\u002F …but every ownership-level act is denied:\nawait firmDb.grant('acme', { userId: 'mole-01', displayName: 'Mole', role: 'admin', secret: 'mole-pass-long' })\n\u002F\u002F → throws PermissionDeniedError   (cannot widen the principal set)\nawait firmDb.rotate('acme', [])\n\u002F\u002F → throws PermissionDeniedError   (re-key is an owner-only meta-capability)\n",[941,135869,135870,135889,135904,135919,135934,135940,135944,135949,135966,136006,136012,136040,136044,136079,136084,136156,136161,136165,136170,136245,136250,136273],{"__ignoreMap":1037},[2225,135871,135872,135874,135876,135878,135880,135882,135885,135887],{"class":2227,"line":2228},[2225,135873,6500],{"class":2231},[2225,135875,6466],{"class":2239},[2225,135877,1520],{"class":2235},[2225,135879,661],{"class":2239},[2225,135881,1520],{"class":2235},[2225,135883,135884],{"class":2291},"grantCustodian",[2225,135886,2789],{"class":2239},[2225,135888,4200],{"class":2235},[2225,135890,135891,135893,135895,135897,135900,135902],{"class":2227,"line":1041},[2225,135892,10510],{"class":2307},[2225,135894,2311],{"class":2235},[2225,135896,2249],{"class":2235},[2225,135898,135899],{"class":2252},"firm-01",[2225,135901,2805],{"class":2235},[2225,135903,1707],{"class":2235},[2225,135905,135906,135908,135910,135912,135915,135917],{"class":2227,"line":1038},[2225,135907,10526],{"class":2307},[2225,135909,2311],{"class":2235},[2225,135911,2249],{"class":2235},[2225,135913,135914],{"class":2252},"Operating firm",[2225,135916,2805],{"class":2235},[2225,135918,1707],{"class":2235},[2225,135920,135921,135923,135925,135927,135930,135932],{"class":2227,"line":2288},[2225,135922,10423],{"class":2307},[2225,135924,2311],{"class":2235},[2225,135926,2249],{"class":2235},[2225,135928,135929],{"class":2252},"firm-pass-long",[2225,135931,2805],{"class":2235},[2225,135933,1707],{"class":2235},[2225,135935,135936,135938],{"class":2227,"line":2304},[2225,135937,2359],{"class":2235},[2225,135939,2362],{"class":2239},[2225,135941,135942],{"class":2227,"line":2356},[2225,135943,2285],{"emptyLinePlaceholder":2284},[2225,135945,135946],{"class":2227,"line":2830},[2225,135947,135948],{"class":2763},"\u002F\u002F The custodian opens the vault and operates every collection…\n",[2225,135950,135951,135953,135956,135958,135960,135962,135964],{"class":2227,"line":2861},[2225,135952,2775],{"class":2774},[2225,135954,135955],{"class":2239}," firmDb ",[2225,135957,2781],{"class":2235},[2225,135959,2784],{"class":2231},[2225,135961,2744],{"class":2291},[2225,135963,2789],{"class":2239},[2225,135965,4200],{"class":2235},[2225,135967,135968,135970,135972,135974,135976,135978,135980,135982,135984,135986,135988,135990,135992,135994,135996,135998,136000,136002,136004],{"class":2227,"line":4246},[2225,135969,15608],{"class":2239},[2225,135971,2298],{"class":2235},[2225,135973,2795],{"class":2307},[2225,135975,2311],{"class":2235},[2225,135977,2249],{"class":2235},[2225,135979,135899],{"class":2252},[2225,135981,2805],{"class":2235},[2225,135983,2298],{"class":2235},[2225,135985,2810],{"class":2307},[2225,135987,2311],{"class":2235},[2225,135989,2249],{"class":2235},[2225,135991,135929],{"class":2252},[2225,135993,2805],{"class":2235},[2225,135995,2298],{"class":2235},[2225,135997,83361],{"class":2307},[2225,135999,2311],{"class":2235},[2225,136001,10373],{"class":2291},[2225,136003,4228],{"class":2239},[2225,136005,1707],{"class":2235},[2225,136007,136008,136010],{"class":2227,"line":4253},[2225,136009,2359],{"class":2235},[2225,136011,2362],{"class":2239},[2225,136013,136014,136016,136019,136021,136023,136026,136028,136030,136032,136034,136036,136038],{"class":2227,"line":4259},[2225,136015,2775],{"class":2774},[2225,136017,136018],{"class":2239}," firmVault ",[2225,136020,2781],{"class":2235},[2225,136022,2784],{"class":2231},[2225,136024,136025],{"class":2239}," firmDb",[2225,136027,1520],{"class":2235},[2225,136029,2847],{"class":2291},[2225,136031,2789],{"class":2239},[2225,136033,2805],{"class":2235},[2225,136035,6450],{"class":2252},[2225,136037,2805],{"class":2235},[2225,136039,2362],{"class":2239},[2225,136041,136042],{"class":2227,"line":6548},[2225,136043,2285],{"emptyLinePlaceholder":2284},[2225,136045,136046,136048,136051,136053,136055,136057,136059,136061,136063,136065,136067,136069,136071,136073,136075,136077],{"class":2227,"line":6576},[2225,136047,6500],{"class":2231},[2225,136049,136050],{"class":2239}," firmVault",[2225,136052,1520],{"class":2235},[2225,136054,6471],{"class":2291},[2225,136056,2789],{"class":2239},[2225,136058,2805],{"class":2235},[2225,136060,6487],{"class":2252},[2225,136062,2805],{"class":2235},[2225,136064,1381],{"class":2239},[2225,136066,1520],{"class":2235},[2225,136068,3158],{"class":2291},[2225,136070,2789],{"class":2239},[2225,136072,2805],{"class":2235},[2225,136074,59410],{"class":2252},[2225,136076,2805],{"class":2235},[2225,136078,2362],{"class":2239},[2225,136080,136081],{"class":2227,"line":6582},[2225,136082,136083],{"class":2763},"\u002F\u002F → { id: 'inv-001', amount: 5000, status: 'draft' }\n",[2225,136085,136086,136088,136090,136092,136094,136096,136098,136100,136102,136104,136106,136108,136110,136112,136115,136117,136119,136121,136123,136125,136127,136129,136131,136133,136135,136137,136140,136142,136144,136146,136148,136150,136152,136154],{"class":2227,"line":6587},[2225,136087,6500],{"class":2231},[2225,136089,136050],{"class":2239},[2225,136091,1520],{"class":2235},[2225,136093,6471],{"class":2291},[2225,136095,2789],{"class":2239},[2225,136097,2805],{"class":2235},[2225,136099,6487],{"class":2252},[2225,136101,2805],{"class":2235},[2225,136103,1381],{"class":2239},[2225,136105,1520],{"class":2235},[2225,136107,3161],{"class":2291},[2225,136109,2789],{"class":2239},[2225,136111,2805],{"class":2235},[2225,136113,136114],{"class":2252},"inv-002",[2225,136116,2805],{"class":2235},[2225,136118,2298],{"class":2235},[2225,136120,2236],{"class":2235},[2225,136122,7070],{"class":2307},[2225,136124,2311],{"class":2235},[2225,136126,2249],{"class":2235},[2225,136128,136114],{"class":2252},[2225,136130,2805],{"class":2235},[2225,136132,2298],{"class":2235},[2225,136134,6351],{"class":2307},[2225,136136,2311],{"class":2235},[2225,136138,136139],{"class":6527}," 1200",[2225,136141,2298],{"class":2235},[2225,136143,6361],{"class":2307},[2225,136145,2311],{"class":2235},[2225,136147,2249],{"class":2235},[2225,136149,88283],{"class":2252},[2225,136151,2805],{"class":2235},[2225,136153,2243],{"class":2235},[2225,136155,2362],{"class":2239},[2225,136157,136158],{"class":2227,"line":7174},[2225,136159,136160],{"class":2763},"\u002F\u002F → ok — full read\u002Fwrite on every collection\n",[2225,136162,136163],{"class":2227,"line":7180},[2225,136164,2285],{"emptyLinePlaceholder":2284},[2225,136166,136167],{"class":2227,"line":7220},[2225,136168,136169],{"class":2763},"\u002F\u002F …but every ownership-level act is denied:\n",[2225,136171,136172,136174,136176,136178,136180,136182,136184,136186,136188,136190,136192,136194,136196,136198,136201,136203,136205,136207,136209,136211,136214,136216,136218,136220,136222,136224,136226,136228,136230,136232,136234,136236,136239,136241,136243],{"class":2227,"line":7225},[2225,136173,6500],{"class":2231},[2225,136175,136025],{"class":2239},[2225,136177,1520],{"class":2235},[2225,136179,10493],{"class":2291},[2225,136181,2789],{"class":2239},[2225,136183,2805],{"class":2235},[2225,136185,6450],{"class":2252},[2225,136187,2805],{"class":2235},[2225,136189,2298],{"class":2235},[2225,136191,2236],{"class":2235},[2225,136193,10668],{"class":2307},[2225,136195,2311],{"class":2235},[2225,136197,2249],{"class":2235},[2225,136199,136200],{"class":2252},"mole-01",[2225,136202,2805],{"class":2235},[2225,136204,2298],{"class":2235},[2225,136206,75153],{"class":2307},[2225,136208,2311],{"class":2235},[2225,136210,2249],{"class":2235},[2225,136212,136213],{"class":2252},"Mole",[2225,136215,2805],{"class":2235},[2225,136217,2298],{"class":2235},[2225,136219,34944],{"class":2307},[2225,136221,2311],{"class":2235},[2225,136223,2249],{"class":2235},[2225,136225,9901],{"class":2252},[2225,136227,2805],{"class":2235},[2225,136229,2298],{"class":2235},[2225,136231,2810],{"class":2307},[2225,136233,2311],{"class":2235},[2225,136235,2249],{"class":2235},[2225,136237,136238],{"class":2252},"mole-pass-long",[2225,136240,2805],{"class":2235},[2225,136242,2243],{"class":2235},[2225,136244,2362],{"class":2239},[2225,136246,136247],{"class":2227,"line":7231},[2225,136248,136249],{"class":2763},"\u002F\u002F → throws PermissionDeniedError   (cannot widen the principal set)\n",[2225,136251,136252,136254,136256,136258,136260,136262,136264,136266,136268,136270],{"class":2227,"line":7267},[2225,136253,6500],{"class":2231},[2225,136255,136025],{"class":2239},[2225,136257,1520],{"class":2235},[2225,136259,10859],{"class":2291},[2225,136261,2789],{"class":2239},[2225,136263,2805],{"class":2235},[2225,136265,6450],{"class":2252},[2225,136267,2805],{"class":2235},[2225,136269,2298],{"class":2235},[2225,136271,136272],{"class":2239}," [])\n",[2225,136274,136275],{"class":2227,"line":7272},[2225,136276,136277],{"class":2763},"\u002F\u002F → throws PermissionDeniedError   (re-key is an owner-only meta-capability)\n",[937,136279,136280,136281,6789,136284,136286,136287,136290,136291,136293,136294,136296],{},"A custodian is also denied destructive self-service withdrawal\n(",[941,136282,136283],{},"vault.user.unilateralWithdrawal",[941,136285,11361],{},") and partition extraction\n(extract-and-sever throws ",[941,136288,136289],{},"PartitionExtractionError"," — see\n",[1007,136292,116737],{"href":881},"). The only way a custodian\never gains ownership is the audited ",[941,136295,21231],{}," ceremony below.",[1591,136298,136300],{"id":136299},"revoke-a-custodian","Revoke a custodian",[937,136302,136303],{},"The owner removes the custodian at any time — without the custodian's cooperation:",[2217,136305,136307],{"className":2219,"code":136306,"language":2221,"meta":1037,"style":1037},"await vault.custody.revokeCustodian({ userId: 'firm-01' })\n\u002F\u002F firm-01's keyring is deleted; a fresh open as firm-01 now fails\n",[941,136308,136309,136342],{"__ignoreMap":1037},[2225,136310,136311,136313,136315,136317,136319,136321,136324,136326,136328,136330,136332,136334,136336,136338,136340],{"class":2227,"line":2228},[2225,136312,6500],{"class":2231},[2225,136314,6466],{"class":2239},[2225,136316,1520],{"class":2235},[2225,136318,661],{"class":2239},[2225,136320,1520],{"class":2235},[2225,136322,136323],{"class":2291},"revokeCustodian",[2225,136325,2789],{"class":2239},[2225,136327,2792],{"class":2235},[2225,136329,10668],{"class":2307},[2225,136331,2311],{"class":2235},[2225,136333,2249],{"class":2235},[2225,136335,135899],{"class":2252},[2225,136337,2805],{"class":2235},[2225,136339,2243],{"class":2235},[2225,136341,2362],{"class":2239},[2225,136343,136344],{"class":2227,"line":1041},[2225,136345,136346],{"class":2763},"\u002F\u002F firm-01's keyring is deleted; a fresh open as firm-01 now fails\n",[1591,136348,136350],{"id":136349},"liberate-the-audited-ownership-claim","Liberate — the audited ownership claim",[937,136352,136353,136354,136356,136357,136359],{},"When ownership legitimately passes to the operating party (say, a contractual handover),\nthe ",[950,136355,9904],{}," claims the vault. Liberation is gated (",[941,136358,135275],{},", fail-closed),\ncustodian-only (an owner or admin caller is denied), and audited:",[2217,136361,136363],{"className":2219,"code":136362,"language":2221,"meta":1037,"style":1037},"import { withHistory } from '@noy-db\u002Fhub\u002Fhistory'\n\nconst firmDb = await createNoydb({\n  store, user: 'firm-01', secret: 'firm-pass-long',\n  teamStrategy: withTeam(),\n  custodyStrategy: withCustody(),\n  historyStrategy: withHistory(),   \u002F\u002F so the ceremony lands in the lifecycle ledger\n  policy: { gates: { 'liberate-vault': { enabled: true, minTier: 1 } } },\n})\nconst firmVault = await firmDb.openVault('acme')\n\nconst result = await firmVault.custody.liberate({\n  newOwnerId: 'firm-owner-01',\n  newOwnerSecret: 'firm-owner-pass-long',\n  legalBasis: 'contractual-handover',\n})\n",[941,136364,136365,136383,136387,136403,136433,136445,136457,136472,136517,136523,136549,136553,136578,136594,136610,136626],{"__ignoreMap":1037},[2225,136366,136367,136369,136371,136373,136375,136377,136379,136381],{"class":2227,"line":2228},[2225,136368,2232],{"class":2231},[2225,136370,2236],{"class":2235},[2225,136372,4142],{"class":2239},[2225,136374,2243],{"class":2235},[2225,136376,2246],{"class":2231},[2225,136378,2249],{"class":2235},[2225,136380,4151],{"class":2252},[2225,136382,2256],{"class":2235},[2225,136384,136385],{"class":2227,"line":1041},[2225,136386,2285],{"emptyLinePlaceholder":2284},[2225,136388,136389,136391,136393,136395,136397,136399,136401],{"class":2227,"line":1038},[2225,136390,2775],{"class":2774},[2225,136392,135955],{"class":2239},[2225,136394,2781],{"class":2235},[2225,136396,2784],{"class":2231},[2225,136398,2744],{"class":2291},[2225,136400,2789],{"class":2239},[2225,136402,4200],{"class":2235},[2225,136404,136405,136407,136409,136411,136413,136415,136417,136419,136421,136423,136425,136427,136429,136431],{"class":2227,"line":2288},[2225,136406,15608],{"class":2239},[2225,136408,2298],{"class":2235},[2225,136410,2795],{"class":2307},[2225,136412,2311],{"class":2235},[2225,136414,2249],{"class":2235},[2225,136416,135899],{"class":2252},[2225,136418,2805],{"class":2235},[2225,136420,2298],{"class":2235},[2225,136422,2810],{"class":2307},[2225,136424,2311],{"class":2235},[2225,136426,2249],{"class":2235},[2225,136428,135929],{"class":2252},[2225,136430,2805],{"class":2235},[2225,136432,1707],{"class":2235},[2225,136434,136435,136437,136439,136441,136443],{"class":2227,"line":2304},[2225,136436,10439],{"class":2307},[2225,136438,2311],{"class":2235},[2225,136440,10373],{"class":2291},[2225,136442,4228],{"class":2239},[2225,136444,1707],{"class":2235},[2225,136446,136447,136449,136451,136453,136455],{"class":2227,"line":2356},[2225,136448,135428],{"class":2307},[2225,136450,2311],{"class":2235},[2225,136452,135301],{"class":2291},[2225,136454,4228],{"class":2239},[2225,136456,1707],{"class":2235},[2225,136458,136459,136461,136463,136465,136467,136469],{"class":2227,"line":2830},[2225,136460,4221],{"class":2307},[2225,136462,2311],{"class":2235},[2225,136464,4142],{"class":2291},[2225,136466,4228],{"class":2239},[2225,136468,2298],{"class":2235},[2225,136470,136471],{"class":2763},"   \u002F\u002F so the ceremony lands in the lifecycle ledger\n",[2225,136473,136474,136476,136478,136480,136483,136485,136487,136489,136491,136493,136495,136497,136499,136501,136503,136505,136507,136509,136511,136513,136515],{"class":2227,"line":2861},[2225,136475,18899],{"class":2307},[2225,136477,2311],{"class":2235},[2225,136479,2236],{"class":2235},[2225,136481,136482],{"class":2307}," gates",[2225,136484,2311],{"class":2235},[2225,136486,2236],{"class":2235},[2225,136488,2249],{"class":2235},[2225,136490,135275],{"class":2307},[2225,136492,2805],{"class":2235},[2225,136494,2311],{"class":2235},[2225,136496,2236],{"class":2235},[2225,136498,135470],{"class":2307},[2225,136500,2311],{"class":2235},[2225,136502,2322],{"class":2321},[2225,136504,2298],{"class":2235},[2225,136506,18956],{"class":2307},[2225,136508,2311],{"class":2235},[2225,136510,9377],{"class":6527},[2225,136512,2243],{"class":2235},[2225,136514,2243],{"class":2235},[2225,136516,2353],{"class":2235},[2225,136518,136519,136521],{"class":2227,"line":4246},[2225,136520,2359],{"class":2235},[2225,136522,2362],{"class":2239},[2225,136524,136525,136527,136529,136531,136533,136535,136537,136539,136541,136543,136545,136547],{"class":2227,"line":4253},[2225,136526,2775],{"class":2774},[2225,136528,136018],{"class":2239},[2225,136530,2781],{"class":2235},[2225,136532,2784],{"class":2231},[2225,136534,136025],{"class":2239},[2225,136536,1520],{"class":2235},[2225,136538,2847],{"class":2291},[2225,136540,2789],{"class":2239},[2225,136542,2805],{"class":2235},[2225,136544,6450],{"class":2252},[2225,136546,2805],{"class":2235},[2225,136548,2362],{"class":2239},[2225,136550,136551],{"class":2227,"line":4259},[2225,136552,2285],{"emptyLinePlaceholder":2284},[2225,136554,136555,136557,136559,136561,136563,136565,136567,136569,136571,136574,136576],{"class":2227,"line":6548},[2225,136556,2775],{"class":2774},[2225,136558,36920],{"class":2239},[2225,136560,2781],{"class":2235},[2225,136562,2784],{"class":2231},[2225,136564,136050],{"class":2239},[2225,136566,1520],{"class":2235},[2225,136568,661],{"class":2239},[2225,136570,1520],{"class":2235},[2225,136572,136573],{"class":2291},"liberate",[2225,136575,2789],{"class":2239},[2225,136577,4200],{"class":2235},[2225,136579,136580,136583,136585,136587,136590,136592],{"class":2227,"line":6576},[2225,136581,136582],{"class":2307},"  newOwnerId",[2225,136584,2311],{"class":2235},[2225,136586,2249],{"class":2235},[2225,136588,136589],{"class":2252},"firm-owner-01",[2225,136591,2805],{"class":2235},[2225,136593,1707],{"class":2235},[2225,136595,136596,136599,136601,136603,136606,136608],{"class":2227,"line":6582},[2225,136597,136598],{"class":2307},"  newOwnerSecret",[2225,136600,2311],{"class":2235},[2225,136602,2249],{"class":2235},[2225,136604,136605],{"class":2252},"firm-owner-pass-long",[2225,136607,2805],{"class":2235},[2225,136609,1707],{"class":2235},[2225,136611,136612,136615,136617,136619,136622,136624],{"class":2227,"line":6587},[2225,136613,136614],{"class":2307},"  legalBasis",[2225,136616,2311],{"class":2235},[2225,136618,2249],{"class":2235},[2225,136620,136621],{"class":2252},"contractual-handover",[2225,136623,2805],{"class":2235},[2225,136625,1707],{"class":2235},[2225,136627,136628,136630],{"class":2227,"line":7174},[2225,136629,2359],{"class":2235},[2225,136631,2362],{"class":2239},[2217,136633,136635],{"className":6604,"code":136634,"language":141,"meta":1037,"style":1037},"{\n  \"snapshot\": {\n    \"withdrawalId\": \"01JZGJ3V9GQ2N9W3H4X5T6K7R8\",\n    \"sha256\": \"9f2c41a7c3e8d0b6f5a1e2d3c4b5a6978877665544332211ffeeddccbbaa0099\",\n    \"recordCount\": 3,\n    \"frozenAt\": \"2026-07-08T10:02:41.007Z\"\n  }\n}\n",[941,136636,136637,136641,136653,136672,136692,136707,136725,136729],{"__ignoreMap":1037},[2225,136638,136639],{"class":2227,"line":2228},[2225,136640,4200],{"class":2235},[2225,136642,136643,136645,136647,136649,136651],{"class":2227,"line":1041},[2225,136644,9368],{"class":2235},[2225,136646,8882],{"class":2774},[2225,136648,6620],{"class":2235},[2225,136650,2311],{"class":2235},[2225,136652,2301],{"class":2235},[2225,136654,136655,136657,136660,136662,136664,136666,136668,136670],{"class":2227,"line":1038},[2225,136656,14201],{"class":2235},[2225,136658,136659],{"class":6342},"withdrawalId",[2225,136661,6620],{"class":2235},[2225,136663,2311],{"class":2235},[2225,136665,6614],{"class":2235},[2225,136667,125429],{"class":2252},[2225,136669,6620],{"class":2235},[2225,136671,1707],{"class":2235},[2225,136673,136674,136676,136679,136681,136683,136685,136688,136690],{"class":2227,"line":2288},[2225,136675,14201],{"class":2235},[2225,136677,136678],{"class":6342},"sha256",[2225,136680,6620],{"class":2235},[2225,136682,2311],{"class":2235},[2225,136684,6614],{"class":2235},[2225,136686,136687],{"class":2252},"9f2c41a7c3e8d0b6f5a1e2d3c4b5a6978877665544332211ffeeddccbbaa0099",[2225,136689,6620],{"class":2235},[2225,136691,1707],{"class":2235},[2225,136693,136694,136696,136699,136701,136703,136705],{"class":2227,"line":2304},[2225,136695,14201],{"class":2235},[2225,136697,136698],{"class":6342},"recordCount",[2225,136700,6620],{"class":2235},[2225,136702,2311],{"class":2235},[2225,136704,14460],{"class":6527},[2225,136706,1707],{"class":2235},[2225,136708,136709,136711,136714,136716,136718,136720,136723],{"class":2227,"line":2356},[2225,136710,14201],{"class":2235},[2225,136712,136713],{"class":6342},"frozenAt",[2225,136715,6620],{"class":2235},[2225,136717,2311],{"class":2235},[2225,136719,6614],{"class":2235},[2225,136721,136722],{"class":2252},"2026-07-08T10:02:41.007Z",[2225,136724,9449],{"class":2235},[2225,136726,136727],{"class":2227,"line":2830},[2225,136728,7632],{"class":2235},[2225,136730,136731],{"class":2227,"line":2861},[2225,136732,7351],{"class":2235},[937,136734,136735],{},"The ceremony runs six steps, in order:",[1234,136737,136738,136744,136749,136760,136773,136783],{},[1114,136739,136740,136741,136743],{},"Check the ",[941,136742,135275],{}," gate (fail-closed).",[1114,136745,136746,136747,1520],{},"Verify the caller's role is ",[941,136748,9904],{},[1114,136750,136751,136752,136755,136756,136759],{},"Freeze a hash-pinned ",[950,136753,136754],{},"evidence snapshot"," of every operational collection — the live\nrecords are ",[1092,136757,136758],{},"preserved",", not deleted: liberation transfers operational continuity.",[1114,136761,136762,136763,6793,136766,136769,136770,1520],{},"Mint a ",[950,136764,136765],{},"new owner keyring",[941,136767,136768],{},"newOwnerId",", re-wrapping the custodian's collection\nkeys under a fresh key derived from ",[941,136771,136772],{},"newOwnerSecret",[1114,136774,136775,136776,136779,136780,136782],{},"Append ",[941,136777,136778],{},"liberation-claimed:\u003CnewOwnerId>:\u003ClegalBasis>"," to the lifecycle ledger (skipped\nsilently if ",[1007,136781,697],{"href":698}," is not enabled).",[1114,136784,136785,136786,83183,136789,136791],{},"Stamp ",[941,136787,136788],{},"liberatedAt",[941,136790,135611],{}," marker.",[937,136793,136794],{},"Afterwards the new owner is a fully independent principal and the data is intact:",[2217,136796,136798],{"className":2219,"code":136797,"language":2221,"meta":1037,"style":1037},"const entries = await firmVault.ledger().entries()\n\u002F\u002F → includes { op: 'lifecycle', actor: 'firm-owner-01',\n\u002F\u002F              reason: 'liberation-claimed:firm-owner-01:contractual-handover' }\n\nconst newOwnerDb = await createNoydb({\n  store, user: 'firm-owner-01', secret: 'firm-owner-pass-long', teamStrategy: withTeam(),\n})\nconst newOwnerVault = await newOwnerDb.openVault('acme')\nawait newOwnerVault.collection('invoices').get('inv-001')\n\u002F\u002F → { id: 'inv-001', amount: 5000, status: 'draft' }   (live data preserved)\n",[941,136799,136800,136824,136829,136834,136838,136855,136895,136901,136929,136964],{"__ignoreMap":1037},[2225,136801,136802,136804,136806,136808,136810,136812,136814,136816,136818,136820,136822],{"class":2227,"line":2228},[2225,136803,2775],{"class":2774},[2225,136805,45530],{"class":2239},[2225,136807,2781],{"class":2235},[2225,136809,2784],{"class":2231},[2225,136811,136050],{"class":2239},[2225,136813,1520],{"class":2235},[2225,136815,20923],{"class":2291},[2225,136817,4228],{"class":2239},[2225,136819,1520],{"class":2235},[2225,136821,9095],{"class":2291},[2225,136823,8146],{"class":2239},[2225,136825,136826],{"class":2227,"line":1041},[2225,136827,136828],{"class":2763},"\u002F\u002F → includes { op: 'lifecycle', actor: 'firm-owner-01',\n",[2225,136830,136831],{"class":2227,"line":1038},[2225,136832,136833],{"class":2763},"\u002F\u002F              reason: 'liberation-claimed:firm-owner-01:contractual-handover' }\n",[2225,136835,136836],{"class":2227,"line":2288},[2225,136837,2285],{"emptyLinePlaceholder":2284},[2225,136839,136840,136842,136845,136847,136849,136851,136853],{"class":2227,"line":2304},[2225,136841,2775],{"class":2774},[2225,136843,136844],{"class":2239}," newOwnerDb ",[2225,136846,2781],{"class":2235},[2225,136848,2784],{"class":2231},[2225,136850,2744],{"class":2291},[2225,136852,2789],{"class":2239},[2225,136854,4200],{"class":2235},[2225,136856,136857,136859,136861,136863,136865,136867,136869,136871,136873,136875,136877,136879,136881,136883,136885,136887,136889,136891,136893],{"class":2227,"line":2356},[2225,136858,15608],{"class":2239},[2225,136860,2298],{"class":2235},[2225,136862,2795],{"class":2307},[2225,136864,2311],{"class":2235},[2225,136866,2249],{"class":2235},[2225,136868,136589],{"class":2252},[2225,136870,2805],{"class":2235},[2225,136872,2298],{"class":2235},[2225,136874,2810],{"class":2307},[2225,136876,2311],{"class":2235},[2225,136878,2249],{"class":2235},[2225,136880,136605],{"class":2252},[2225,136882,2805],{"class":2235},[2225,136884,2298],{"class":2235},[2225,136886,83361],{"class":2307},[2225,136888,2311],{"class":2235},[2225,136890,10373],{"class":2291},[2225,136892,4228],{"class":2239},[2225,136894,1707],{"class":2235},[2225,136896,136897,136899],{"class":2227,"line":2830},[2225,136898,2359],{"class":2235},[2225,136900,2362],{"class":2239},[2225,136902,136903,136905,136908,136910,136912,136915,136917,136919,136921,136923,136925,136927],{"class":2227,"line":2861},[2225,136904,2775],{"class":2774},[2225,136906,136907],{"class":2239}," newOwnerVault ",[2225,136909,2781],{"class":2235},[2225,136911,2784],{"class":2231},[2225,136913,136914],{"class":2239}," newOwnerDb",[2225,136916,1520],{"class":2235},[2225,136918,2847],{"class":2291},[2225,136920,2789],{"class":2239},[2225,136922,2805],{"class":2235},[2225,136924,6450],{"class":2252},[2225,136926,2805],{"class":2235},[2225,136928,2362],{"class":2239},[2225,136930,136931,136933,136936,136938,136940,136942,136944,136946,136948,136950,136952,136954,136956,136958,136960,136962],{"class":2227,"line":4246},[2225,136932,6500],{"class":2231},[2225,136934,136935],{"class":2239}," newOwnerVault",[2225,136937,1520],{"class":2235},[2225,136939,6471],{"class":2291},[2225,136941,2789],{"class":2239},[2225,136943,2805],{"class":2235},[2225,136945,6487],{"class":2252},[2225,136947,2805],{"class":2235},[2225,136949,1381],{"class":2239},[2225,136951,1520],{"class":2235},[2225,136953,3158],{"class":2291},[2225,136955,2789],{"class":2239},[2225,136957,2805],{"class":2235},[2225,136959,59410],{"class":2252},[2225,136961,2805],{"class":2235},[2225,136963,2362],{"class":2239},[2225,136965,136966],{"class":2227,"line":4253},[2225,136967,136968],{"class":2763},"\u002F\u002F → { id: 'inv-001', amount: 5000, status: 'draft' }   (live data preserved)\n",[937,136970,136971,136972,136975,136976,136979,136980,136983],{},"The original sealed-owner credential is ",[950,136973,136974],{},"never touched",": its keyring file remains, still\nsealed under the client's provider, now orphaned. Claiming ownership always produces a\n",[1092,136977,136978],{},"different"," principal plus an audit trail — a custodian can never silently ",[1092,136981,136982],{},"become"," the\nlatent owner.",[1022,136985,102889],{"id":102888},[1591,136987,136989],{"id":136988},"withcustody",[941,136990,11541],{},[2217,136992,136994],{"className":2219,"code":136993,"language":2221,"meta":1037,"style":1037},"function withCustody(): CustodyStrategy\n",[941,136995,136996],{"__ignoreMap":1037},[2225,136997,136998,137000,137002,137004],{"class":2227,"line":2228},[2225,136999,12641],{"class":2774},[2225,137001,135301],{"class":2291},[2225,137003,112812],{"class":2235},[2225,137005,137006],{"class":6342}," CustodyStrategy\n",[937,137008,137009,137010,10313,137013,137015],{},"Builds the active custody strategy. Pass it as ",[941,137011,137012],{},"custodyStrategy",[941,137014,12780],{}," — this is\nthe only wiring step. Takes no options. The liberation engine is dynamically imported by\nthe strategy, keeping it out of the base bundle.",[1591,137017,137019],{"id":137018},"vaultcustodygrantcustodianoptions-factors",[941,137020,137021],{},"vault.custody.grantCustodian(options, factors?)",[2217,137023,137025],{"className":2219,"code":137024,"language":2221,"meta":1037,"style":1037},"vault.custody.grantCustodian(options: GrantCustodianOptions, factors?: FactorProofBundle): Promise\u003Cvoid>\n",[941,137026,137027],{"__ignoreMap":1037},[2225,137028,137029,137031,137033,137035,137037,137039,137042,137044,137046,137048,137050,137052],{"class":2227,"line":2228},[2225,137030,2694],{"class":2239},[2225,137032,1520],{"class":2235},[2225,137034,661],{"class":2239},[2225,137036,1520],{"class":2235},[2225,137038,135884],{"class":2291},[2225,137040,137041],{"class":2239},"(options: GrantCustodianOptions",[2225,137043,2298],{"class":2235},[2225,137045,17534],{"class":2239},[2225,137047,7011],{"class":2235},[2225,137049,17539],{"class":2239},[2225,137051,11740],{"class":6342},[2225,137053,13016],{"class":2235},[937,137055,137056,137057,137059,137060,137062,137063,137065,137066,137069],{},"Owner-only: mint a ",[941,137058,9904],{},". Defended in depth — the ",[941,137061,135272],{}," gate (fail-closed)\n",[1092,137064,14128],{}," an explicit owner-role check, so a misconfigured gate can never widen who may grant.\nAlso available db-level as ",[941,137067,137068],{},"db.grantCustodian(vault, options, factors?)","; the vault method\ndelegates to it.",[937,137071,137072,7356,137075,137078,137079,2311],{},[941,137073,137074],{},"GrantCustodianOptions",[941,137076,137077],{},"GrantOptions"," with the role fixed to ",[941,137080,9904],{},[1338,137082,137083,137093],{},[1341,137084,137085],{},[1344,137086,137087,137089,137091],{},[1347,137088,101104],{},[1347,137090,20194],{},[1347,137092,7834],{},[1357,137094,137095,137108,137121,137134,137148,137163,137179],{},[1344,137096,137097,137101,137105],{},[1362,137098,137099],{},[941,137100,10749],{},[1362,137102,137103],{},[941,137104,7259],{},[1362,137106,137107],{},"The new custodian's principal id.",[1344,137109,137110,137114,137118],{},[1362,137111,137112],{},[941,137113,16791],{},[1362,137115,137116],{},[941,137117,7259],{},[1362,137119,137120],{},"Human-readable name stored on the keyring.",[1344,137122,137123,137127,137131],{},[1362,137124,137125],{},[941,137126,10695],{},[1362,137128,137129],{},[941,137130,7259],{},[1362,137132,137133],{},"The custodian's secret (validated against the vault's secret policy).",[1344,137135,137136,137141,137145],{},[1362,137137,137138],{},[941,137139,137140],{},"permissions?",[1362,137142,137143],{},[941,137144,116397],{},[1362,137146,137147],{},"Optional per-collection permission map.",[1344,137149,137150,137158,137160],{},[1362,137151,137152,1261,137155],{},[941,137153,137154],{},"exportCapability?",[941,137156,137157],{},"importCapability?",[1362,137159,3610],{},[1362,137161,137162],{},"Optional format-export\u002Fimport capability grants (role-based defaults when omitted).",[1344,137164,137165,137170,137174],{},[1362,137166,137167],{},[941,137168,137169],{},"allowWeakSecret?",[1362,137171,137172],{},[941,137173,16861],{},[1362,137175,137176,137177,1520],{},"Skip secret-strength validation. Default ",[941,137178,2560],{},[1344,137180,137181,137186,137190],{},[1362,137182,137183],{},[941,137184,137185],{},"initialProfile?",[1362,137187,137188],{},[941,137189,22421],{},[1362,137191,137192],{},"Bootstrap-only user-envelope payload for the new principal.",[1591,137194,137196],{"id":137195},"vaultcustodyrevokecustodianoptions-factors",[941,137197,137198],{},"vault.custody.revokeCustodian(options, factors?)",[2217,137200,137202],{"className":2219,"code":137201,"language":2221,"meta":1037,"style":1037},"vault.custody.revokeCustodian(options: RevokeOptions, factors?: FactorProofBundle): Promise\u003Cvoid>\n",[941,137203,137204],{"__ignoreMap":1037},[2225,137205,137206,137208,137210,137212,137214,137216,137219,137221,137223,137225,137227,137229],{"class":2227,"line":2228},[2225,137207,2694],{"class":2239},[2225,137209,1520],{"class":2235},[2225,137211,661],{"class":2239},[2225,137213,1520],{"class":2235},[2225,137215,136323],{"class":2291},[2225,137217,137218],{"class":2239},"(options: RevokeOptions",[2225,137220,2298],{"class":2235},[2225,137222,17534],{"class":2239},[2225,137224,7011],{"class":2235},[2225,137226,17539],{"class":2239},[2225,137228,11740],{"class":6342},[2225,137230,13016],{"class":2235},[937,137232,137233,137234,137236,137237,1520],{},"Owner-only: remove a custodian. Runs through the shared ",[941,137235,135279],{}," gate plus the same\nexplicit owner-role check — an admin cannot unwind a custodianship. Also available as\n",[941,137238,137239],{},"db.revokeCustodian(vault, options, factors?)",[1338,137241,137242,137252],{},[1341,137243,137244],{},[1344,137245,137246,137248,137250],{},[1347,137247,101104],{},[1347,137249,20194],{},[1347,137251,7834],{},[1357,137253,137254,137267],{},[1344,137255,137256,137260,137264],{},[1362,137257,137258],{},[941,137259,10749],{},[1362,137261,137262],{},[941,137263,7259],{},[1362,137265,137266],{},"The custodian to revoke.",[1344,137268,137269,137274,137279],{},[1362,137270,137271],{},[941,137272,137273],{},"cascade?",[1362,137275,137276],{},[941,137277,137278],{},"'strict' | 'warn'",[1362,137280,137281],{},"Admin-delegation cascade behavior; no effect for custodians (they can never grant, so they have no delegation subtree).",[1591,137283,137285],{"id":137284},"vaultcustodyliberateopts",[941,137286,137287],{},"vault.custody.liberate(opts)",[2217,137289,137291],{"className":2219,"code":137290,"language":2221,"meta":1037,"style":1037},"vault.custody.liberate(opts: LiberateOptions): Promise\u003CLiberateResult>\n",[941,137292,137293],{"__ignoreMap":1037},[2225,137294,137295,137297,137299,137301,137303,137305,137308,137310,137312,137315],{"class":2227,"line":2228},[2225,137296,2694],{"class":2239},[2225,137298,1520],{"class":2235},[2225,137300,661],{"class":2239},[2225,137302,1520],{"class":2235},[2225,137304,136573],{"class":2291},[2225,137306,137307],{"class":2239},"(opts: LiberateOptions): ",[2225,137309,11740],{"class":6342},[2225,137311,6474],{"class":2235},[2225,137313,137314],{"class":2239},"LiberateResult",[2225,137316,7093],{"class":2235},[937,137318,137319],{},"Custodian-only: the audited ownership claim over a Deed vault (ceremony above).",[937,137321,137322,2311],{},[941,137323,137324],{},"LiberateOptions",[1338,137326,137327,137337],{},[1341,137328,137329],{},[1344,137330,137331,137333,137335],{},[1347,137332,101104],{},[1347,137334,20194],{},[1347,137336,7834],{},[1357,137338,137339,137359,137372,137389],{},[1344,137340,137341,137345,137349],{},[1362,137342,137343],{},[941,137344,136768],{},[1362,137346,137347],{},[941,137348,7259],{},[1362,137350,137351,137352,137355,137356,137358],{},"Id of the new owner principal to mint. Must be a ",[950,137353,137354],{},"fresh"," id — a collision with any existing principal throws ",[941,137357,11395],{}," before any side effect.",[1344,137360,137361,137365,137369],{},[1362,137362,137363],{},[941,137364,136772],{},[1362,137366,137367],{},[941,137368,7259],{},[1362,137370,137371],{},"Derives the new owner's key; the collection keys are re-wrapped under it.",[1344,137373,137374,137379,137383],{},[1362,137375,137376],{},[941,137377,137378],{},"legalBasis",[1362,137380,137381],{},[941,137382,7259],{},[1362,137384,137385,137386,957],{},"Recorded in the audit entry (e.g. ",[941,137387,137388],{},"'contractual-handover'",[1344,137390,137391,137396,137400],{},[1362,137392,137393],{},[941,137394,137395],{},"factors?",[1362,137397,137398],{},[941,137399,104285],{},[1362,137401,137402,137403,13046],{},"Proofs for the ",[941,137404,135275],{},[937,137406,137407,137410],{},[941,137408,137409],{},"LiberateResult.snapshot"," is the hash-pinned pre-liberation evidence snapshot:",[1338,137412,137413,137423],{},[1341,137414,137415],{},[1344,137416,137417,137419,137421],{},[1347,137418,101104],{},[1347,137420,20194],{},[1347,137422,20200],{},[1357,137424,137425,137438,137451,137464],{},[1344,137426,137427,137431,137435],{},[1362,137428,137429],{},[941,137430,136659],{},[1362,137432,137433],{},[941,137434,7259],{},[1362,137436,137437],{},"Id of the frozen snapshot.",[1344,137439,137440,137444,137448],{},[1362,137441,137442],{},[941,137443,136678],{},[1362,137445,137446],{},[941,137447,7259],{},[1362,137449,137450],{},"Hex digest pinning the snapshot contents.",[1344,137452,137453,137457,137461],{},[1362,137454,137455],{},[941,137456,136698],{},[1362,137458,137459],{},[941,137460,20636],{},[1362,137462,137463],{},"Records captured.",[1344,137465,137466,137470,137474],{},[1362,137467,137468],{},[941,137469,136713],{},[1362,137471,137472],{},[941,137473,7259],{},[1362,137475,137476],{},"ISO timestamp.",[1591,137478,137480],{"id":137479},"deed-helpers-root-exports","Deed helpers (root exports)",[1111,137482,137483,137499,137511,137519],{},[1114,137484,137485,1746,137488,137491,137492,137494,137495,137498],{},[941,137486,137487],{},"createDeedOwner(store, vault, ownerUserId, sealing)",[941,137489,137490],{},"Promise\u003CUnlockedKeyring>"," —\nprovision a sealed latent owner + the ",[941,137493,135611],{}," marker. Store-level by design: it\nmints the vault's first owner, so there is no vault instance (and no ",[941,137496,137497],{},"vault.custody",")\nyet.",[1114,137500,137501,1746,137504,137507,137508,137510],{},[941,137502,137503],{},"loadDeedMarker(store, vault)",[941,137505,137506],{},"Promise\u003CDeedMarker | null>"," — read the plaintext\nmarker (",[941,137509,6651],{}," for non-Deed vaults).",[1114,137512,137513,1746,137516,1520],{},[941,137514,137515],{},"isDeedVault(store, vault)",[941,137517,137518],{},"Promise\u003Cboolean>",[1114,137520,137521,137524,137525,968,137527,1520],{},[941,137522,137523],{},"DEED_RECORD_ID"," — the reserved marker id under ",[941,137526,117002],{},[941,137528,137529],{},"'deed'",[937,137531,137532,137535],{},[941,137533,137534],{},"DeedMarker"," (plaintext, readable without unlocking):",[1338,137537,137538,137548],{},[1341,137539,137540],{},[1344,137541,137542,137544,137546],{},[1347,137543,101104],{},[1347,137545,20194],{},[1347,137547,20200],{},[1357,137549,137550,137563,137576,137592,137605],{},[1344,137551,137552,137556,137560],{},[1362,137553,137554],{},[941,137555,135775],{},[1362,137557,137558],{},[941,137559,7259],{},[1362,137561,137562],{},"The latent owner principal.",[1344,137564,137565,137569,137573],{},[1362,137566,137567],{},[941,137568,135794],{},[1362,137570,137571],{},[941,137572,7259],{},[1362,137574,137575],{},"Id of the sealing provider anchoring ownership.",[1344,137577,137578,137582,137586],{},[1362,137579,137580],{},[941,137581,135813],{},[1362,137583,137584],{},[941,137585,11790],{},[1362,137587,137588,137589,137591],{},"Always ",[941,137590,11790],{}," for a Deed.",[1344,137593,137594,137598,137602],{},[1362,137595,137596],{},[941,137597,135826],{},[1362,137599,137600],{},[941,137601,7259],{},[1362,137603,137604],{},"ISO timestamp of provisioning.",[1344,137606,137607,137612,137616],{},[1362,137608,137609],{},[941,137610,137611],{},"liberatedAt?",[1362,137613,137614],{},[941,137615,7259],{},[1362,137617,137618,137619,137621],{},"Set by a completed ",[941,137620,21231],{},"; absent until then.",[1591,137623,126393],{"id":126392},[1111,137625,137626,137645,137653,137661],{},[1114,137627,137628,137631,137632,137635,137636,137638,137639,137641,137642,137644],{},[941,137629,137630],{},"liberateVault(vault, opts)"," — the lower-level free function ",[941,137633,137634],{},"vault.custody.liberate","\ndelegates to. It has no ",[941,137637,12780],{}," instance to check opt-in against, so it is\nreachable without ",[941,137640,11541],{}," — but it still enforces the ",[941,137643,135275],{}," gate and\nthe custodian-only role check.",[1114,137646,137647,137650,137651,1520],{},[941,137648,137649],{},"NO_CUSTODY"," — the default no-op strategy; every operation throws\n",[941,137652,21344],{},[1114,137654,137655,137657,137658,137660],{},[941,137656,21344],{}," — thrown by every custody surface when ",[941,137659,11541],{}," was not\npassed.",[1114,137662,126416,137663,968,137666,968,137669,968,137671,1707,137673,968,137675,1520],{},[941,137664,137665],{},"CustodyStrategy",[941,137667,137668],{},"CustodyHost",[941,137670,137074],{},[941,137672,137324],{},[941,137674,137314],{},[941,137676,137534],{},[1022,137678,101425],{"id":101424},[1111,137680,137681,137700,137703],{},[1114,137682,137683,1261,137686,1261,137688,137690,137691,1261,137694,137697,137698,1520],{},[941,137684,137685],{},"vault.custody.grantCustodian",[941,137687,136323],{},[941,137689,136573],{}," — and the db-level\n",[941,137692,137693],{},"db.grantCustodian",[941,137695,137696],{},"db.revokeCustodian"," — all throw ",[941,137699,21344],{},[1114,137701,137702],{},"Everything else on the vault works normally.",[1114,137704,137705,137706,137708],{},"The liberation engine never loads: it is reached only through ",[941,137707,11541],{},"'s dynamic\nimport.",[1022,137710,68210],{"id":68209},[1111,137712,137713,137726,137732,137742,137752,137760,137771],{},[1114,137714,137715,1324,137718,997,137720,137722,137723,137725],{},[950,137716,137717],{},"The custody gates fail closed.",[941,137719,135272],{},[941,137721,135275],{}," are denied\nwhen absent from the vault policy — even for the right role. Enable them explicitly\n(and consider pinning ",[941,137724,17301],{}," \u002F factor requirements for such ownership-level acts).",[1114,137727,137728,137731],{},[950,137729,137730],{},"Role checks are structural, not just policy."," Owner-only (grant\u002Frevoke) and\ncustodian-only (liberate) are enforced by explicit keyring-role checks in addition to\nthe gates, so a permissive gate cannot widen who may act.",[1114,137733,137734,137737,137738,137741],{},[950,137735,137736],{},"Liberation never unseals the original owner."," It mints a distinct principal; the old\nsealed credential stays orphaned in place. If you need the ",[1092,137739,137740],{},"original"," owner back, go\nthrough the client's sealing provider — that is the only re-entry point, by design.",[1114,137743,137744,137747,137748,997,137750,1520],{},[950,137745,137746],{},"Liberation preserves live data."," The evidence snapshot is freeze-only; nothing is\ndelete-closured. For destructive exits, see the withdrawal flows on\n",[1007,137749,884],{"href":885},[1007,137751,900],{"href":901},[1114,137753,137754,137759],{},[950,137755,137756,137758],{},[941,137757,136768],{}," must be fresh."," A collision with any existing principal aborts before\nany side effect — no orphan snapshot, no partial keyring overwrite.",[1114,137761,137762,137767,137768,137770],{},[950,137763,137764,137765,1520],{},"The ledger entry needs ",[1007,137766,697],{"href":698}," Without ",[941,137769,6719],{},", step 5\nof the ceremony is a silent no-op. Enable history on any vault where liberation must be\nprovable after the fact.",[1114,137772,137773,137776],{},[950,137774,137775],{},"The Deed marker is plaintext on purpose."," It is audit metadata (who the latent owner\nis, which provider seals them) and must stay readable without keys; it never carries\nsecret material.",[1022,137778,2621],{"id":2620},[1111,137780,137781,137787,137792,137797,137802,137807,137812],{},[1114,137782,137783,1368,137785],{},[1007,137784,61743],{"href":598},[1007,137786,1019],{"href":1018},[1114,137788,137789,137791],{},[1007,137790,868],{"href":869}," — roles, grant\u002Frevoke\u002Frotate; custody builds on the keyring model",[1114,137793,137794,137796],{},[1007,137795,844],{"href":845}," — the policy-gates DSL and managed-secret mode",[1114,137798,137799,137801],{},[1007,137800,697],{"href":698}," — the lifecycle ledger that records liberation",[1114,137803,137804,137806],{},[1007,137805,116737],{"href":881}," — extract-and-sever, the owner-only capability custodians are denied",[1114,137808,137809,137811],{},[1007,137810,107337],{"href":54}," — keyrings, wrapped keys, roles",[1114,137813,101581,137814,1520],{},[1007,137815,137817],{"href":94333,"rel":137816},[3371],[941,137818,137819],{},"129-with-custody",[2645,137821,137822],{},"html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .sfNiH, html code.shiki .sfNiH{--shiki-light:#FF5370;--shiki-default:#FF9CAC;--shiki-dark:#FF9CAC}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}",{"title":1037,"searchDepth":1038,"depth":1038,"links":137824},[137825,137826,137827,137828,137834,137842,137843,137844],{"id":29856,"depth":1041,"text":29857},{"id":70092,"depth":1041,"text":70093},{"id":99863,"depth":1041,"text":99864},{"id":29892,"depth":1041,"text":29893,"children":137829},[137830,137831,137832,137833],{"id":135601,"depth":1038,"text":135602},{"id":135860,"depth":1038,"text":135861},{"id":136299,"depth":1038,"text":136300},{"id":136349,"depth":1038,"text":136350},{"id":102888,"depth":1041,"text":102889,"children":137835},[137836,137837,137838,137839,137840,137841],{"id":136988,"depth":1038,"text":11541},{"id":137018,"depth":1038,"text":137021},{"id":137195,"depth":1038,"text":137198},{"id":137284,"depth":1038,"text":137287},{"id":137479,"depth":1038,"text":137480},{"id":126392,"depth":1038,"text":126393},{"id":101424,"depth":1041,"text":101425},{"id":68209,"depth":1041,"text":68210},{"id":2620,"depth":1041,"text":2621},"Sovereign custody: `withCustody()` adds a custodian role that operates a vault fully but can never grant, revoke, rotate, or extract — plus `liberate()`.",{},{"title":661,"description":137845},"avXpFTHppB9jKqDmf8_n-F9Vw8SbahJuOvsvi8D8M8A",{"id":137850,"title":665,"access":12,"apiToc":929,"body":137851,"category":1042,"description":141215,"extension":1044,"meta":141216,"navigation":1042,"nest":1047,"path":666,"seo":141217,"sinceVersion":1042,"status":38550,"stem":667,"subtitle":1042,"tier":1050,"tocExternal":1042,"__hash__":141218},"docs\u002Fservices\u002Fderivations.md",{"type":931,"value":137852,"toc":141177},[137853,137855,137883,137886,137888,137901,137907,137909,137933,137935,138335,138339,138349,138558,138560,138567,138580,138931,138953,138956,138961,138973,139148,139151,139155,139171,139475,139551,139557,139584,139680,139711,139717,139726,139851,139873,139886,139892,139908,139912,139923,139961,139965,139978,140036,140052,140058,140068,140225,140284,140309,140313,140325,140477,140480,140511,140516,140520,140540,140558,140572,140576,140595,140600,140609,140647,140659,140665,140672,140718,140723,140726,140846,140865,140869,140875,140881,140883,140889,140928,140930,140966,140968,140994,140996,141000,141006,141010,141019,141023,141067,141071,141079,141119,141121,141174],[934,137854,665],{"id":665},[937,137856,137857,137862,137863,137865,137866,137868,137869,137871,137872,137875,137876,137882],{},[950,137858,137859],{},[941,137860,137861],{},"withDerivation()"," keeps computed data in sync with its source: a deterministic function\nprojects one source record into one or more typed outputs, encrypted the same way as everything\nelse in a ",[1007,137864,2694],{"href":58262},". It is one of three related derived-data\nservices registered on ",[941,137867,12780],{},"; the other two have their own pages —\n",[1007,137870,739],{"href":124624}," persist the result of an entire query as a live\ncollection, and ",[1007,137873,768],{"href":137874},".\u002Foverlay-views"," let an operator override specific rows. All\nthree share one cycle detector (checked once when a vault opens) and one rule for internal cleanup:\nsystem-issued deletes (tombstones, refresh-driven row removal) bypass your own\n",[1007,137877,137878,137881],{"href":124814},[941,137879,137880],{},"onDelete"," guards"," — the write-time rules a collection can register to reject a\ndelete — so housekeeping never deadlocks a rule you wrote for humans.",[46871,137884],{":rows":137885},"[{\"label\":\"Subpath\",\"value\":\"`@noy-db\u002Fhub\u002Fderivations`\"},{\"label\":\"Factory\",\"value\":\"`withDerivation()`\"},{\"label\":\"Layer\",\"value\":\"`with-formula`\"}]",[1022,137887,29857],{"id":29856},[937,137889,137890,137893,137894,137896,137897,137900],{},[941,137891,137892],{},"withDerivation"," lets a vault declare a deterministic function that turns one source record into\none or more typed outputs — for example, a PDF source row that derives extracted metadata and\nsearchable text into two separate collections. The ",[941,137895,14092],{}," function runs on plaintext, after the\nsource collection's ",[1007,137898,137899],{"href":60250},"data-encryption key (DEK)"," is unwrapped, and every\noutput is re-encrypted with the same DEK before it reaches the store — a storage backend that only\never sees ciphertext cannot reconstruct the derivation graph by listing records.",[937,137902,137903,137904,957],{},"Reach for it when you're splitting a large record into faster-to-read projections, keeping side\ndata encrypted under the same key as its source, mixing cheap eager outputs with expensive lazy\nones, or need a bulk recompute path after a strategy change (",[941,137905,137906],{},"vault.deriveAll",[1022,137908,70093],{"id":70092},[1111,137910,137911,137919],{},[1114,137912,137913,137915,137916,137918],{},[950,137914,107493],{}," for same-collection derivations — ",[941,137917,137861],{}," plus the strategy declarations on this page.",[1114,137920,137921,137927,137928,27678,137931,1520],{},[1092,137922,137923,137924,6255],{},"For FK-keyed triggers (",[941,137925,137926],{},"triggerBy"," the trigger field must be indexed, which requires ",[941,137929,137930],{},"indexingStrategy: withIndexing()",[1007,137932,705],{"href":706},[1022,137934,99864],{"id":99863},[2217,137936,137938],{"className":2219,"code":137937,"language":2221,"meta":1037,"style":1037},"import { createNoydb } from '@noy-db\u002Fhub'\nimport { toMemory } from '@noy-db\u002Fto-memory'\nimport { withDerivation } from '@noy-db\u002Fhub\u002Fderivations'\n\nconst pdfDerivation = withDerivation\u003CPdf, { meta: PdfMeta; text: PdfText }>({\n  source: 'pdfs',\n  deterministic: true,\n  outputs: {\n    meta: { shape: 'record', collection: 'pdf-meta' },\n    text: { shape: 'record', collection: 'pdf-text' },\n  },\n  derive: (pdf) => ({\n    meta: { len: pdf.body.length, pages: pdf.pages.length },\n    text: { content: extractText(pdf.body) },\n  }),\n  lifecycle: 'eager', \u002F\u002F or 'lazy'\n})\n\nconst db = await createNoydb({\n  store: toMemory(),\n  user: 'alice',\n  derivationStrategies: [pdfDerivation],\n})\n",[941,137939,137940,137958,137976,137995,137999,138041,138057,138068,138077,138111,138145,138149,138169,138212,138238,138246,138265,138271,138275,138291,138303,138317,138329],{"__ignoreMap":1037},[2225,137941,137942,137944,137946,137948,137950,137952,137954,137956],{"class":2227,"line":2228},[2225,137943,2232],{"class":2231},[2225,137945,2236],{"class":2235},[2225,137947,2744],{"class":2239},[2225,137949,2243],{"class":2235},[2225,137951,2246],{"class":2231},[2225,137953,2249],{"class":2235},[2225,137955,1086],{"class":2252},[2225,137957,2256],{"class":2235},[2225,137959,137960,137962,137964,137966,137968,137970,137972,137974],{"class":2227,"line":1041},[2225,137961,2232],{"class":2231},[2225,137963,2236],{"class":2235},[2225,137965,68576],{"class":2239},[2225,137967,2243],{"class":2235},[2225,137969,2246],{"class":2231},[2225,137971,2249],{"class":2235},[2225,137973,9638],{"class":2252},[2225,137975,2256],{"class":2235},[2225,137977,137978,137980,137982,137985,137987,137989,137991,137993],{"class":2227,"line":1038},[2225,137979,2232],{"class":2231},[2225,137981,2236],{"class":2235},[2225,137983,137984],{"class":2239}," withDerivation",[2225,137986,2243],{"class":2235},[2225,137988,2246],{"class":2231},[2225,137990,2249],{"class":2235},[2225,137992,73041],{"class":2252},[2225,137994,2256],{"class":2235},[2225,137996,137997],{"class":2227,"line":2288},[2225,137998,2285],{"emptyLinePlaceholder":2284},[2225,138000,138001,138003,138006,138008,138010,138012,138015,138017,138019,138021,138023,138026,138028,138030,138032,138035,138037,138039],{"class":2227,"line":2304},[2225,138002,2775],{"class":2774},[2225,138004,138005],{"class":2239}," pdfDerivation ",[2225,138007,2781],{"class":2235},[2225,138009,137984],{"class":2291},[2225,138011,6474],{"class":2235},[2225,138013,138014],{"class":6342},"Pdf",[2225,138016,2298],{"class":2235},[2225,138018,2236],{"class":2235},[2225,138020,90153],{"class":2307},[2225,138022,2311],{"class":2235},[2225,138024,138025],{"class":6342}," PdfMeta",[2225,138027,3769],{"class":2235},[2225,138029,82994],{"class":2307},[2225,138031,2311],{"class":2235},[2225,138033,138034],{"class":6342}," PdfText",[2225,138036,85293],{"class":2235},[2225,138038,2789],{"class":2239},[2225,138040,4200],{"class":2235},[2225,138042,138043,138046,138048,138050,138053,138055],{"class":2227,"line":2356},[2225,138044,138045],{"class":2307},"  source",[2225,138047,2311],{"class":2235},[2225,138049,2249],{"class":2235},[2225,138051,138052],{"class":2252},"pdfs",[2225,138054,2805],{"class":2235},[2225,138056,1707],{"class":2235},[2225,138058,138059,138062,138064,138066],{"class":2227,"line":2830},[2225,138060,138061],{"class":2307},"  deterministic",[2225,138063,2311],{"class":2235},[2225,138065,2322],{"class":2321},[2225,138067,1707],{"class":2235},[2225,138069,138070,138073,138075],{"class":2227,"line":2861},[2225,138071,138072],{"class":2307},"  outputs",[2225,138074,2311],{"class":2235},[2225,138076,2301],{"class":2235},[2225,138078,138079,138081,138083,138085,138088,138090,138092,138094,138096,138098,138100,138102,138104,138107,138109],{"class":2227,"line":4246},[2225,138080,11723],{"class":2307},[2225,138082,2311],{"class":2235},[2225,138084,2236],{"class":2235},[2225,138086,138087],{"class":2307}," shape",[2225,138089,2311],{"class":2235},[2225,138091,2249],{"class":2235},[2225,138093,24158],{"class":2252},[2225,138095,2805],{"class":2235},[2225,138097,2298],{"class":2235},[2225,138099,7061],{"class":2307},[2225,138101,2311],{"class":2235},[2225,138103,2249],{"class":2235},[2225,138105,138106],{"class":2252},"pdf-meta",[2225,138108,2805],{"class":2235},[2225,138110,2353],{"class":2235},[2225,138112,138113,138116,138118,138120,138122,138124,138126,138128,138130,138132,138134,138136,138138,138141,138143],{"class":2227,"line":4253},[2225,138114,138115],{"class":2307},"    text",[2225,138117,2311],{"class":2235},[2225,138119,2236],{"class":2235},[2225,138121,138087],{"class":2307},[2225,138123,2311],{"class":2235},[2225,138125,2249],{"class":2235},[2225,138127,24158],{"class":2252},[2225,138129,2805],{"class":2235},[2225,138131,2298],{"class":2235},[2225,138133,7061],{"class":2307},[2225,138135,2311],{"class":2235},[2225,138137,2249],{"class":2235},[2225,138139,138140],{"class":2252},"pdf-text",[2225,138142,2805],{"class":2235},[2225,138144,2353],{"class":2235},[2225,138146,138147],{"class":2227,"line":4259},[2225,138148,19017],{"class":2235},[2225,138150,138151,138154,138156,138158,138161,138163,138165,138167],{"class":2227,"line":6548},[2225,138152,138153],{"class":2291},"  derive",[2225,138155,2311],{"class":2235},[2225,138157,953],{"class":2235},[2225,138159,138160],{"class":7052},"pdf",[2225,138162,1381],{"class":2235},[2225,138164,8116],{"class":2774},[2225,138166,953],{"class":2239},[2225,138168,4200],{"class":2235},[2225,138170,138171,138173,138175,138177,138180,138182,138184,138186,138188,138190,138192,138194,138197,138199,138201,138203,138206,138208,138210],{"class":2227,"line":6576},[2225,138172,11723],{"class":2307},[2225,138174,2311],{"class":2235},[2225,138176,2236],{"class":2235},[2225,138178,138179],{"class":2307}," len",[2225,138181,2311],{"class":2235},[2225,138183,75942],{"class":2239},[2225,138185,1520],{"class":2235},[2225,138187,42850],{"class":2239},[2225,138189,1520],{"class":2235},[2225,138191,8742],{"class":2239},[2225,138193,2298],{"class":2235},[2225,138195,138196],{"class":2307}," pages",[2225,138198,2311],{"class":2235},[2225,138200,75942],{"class":2239},[2225,138202,1520],{"class":2235},[2225,138204,138205],{"class":2239},"pages",[2225,138207,1520],{"class":2235},[2225,138209,82913],{"class":2239},[2225,138211,19008],{"class":2235},[2225,138213,138214,138216,138218,138220,138223,138225,138228,138231,138233,138236],{"class":2227,"line":6582},[2225,138215,138115],{"class":2307},[2225,138217,2311],{"class":2235},[2225,138219,2236],{"class":2235},[2225,138221,138222],{"class":2307}," content",[2225,138224,2311],{"class":2235},[2225,138226,138227],{"class":2291}," extractText",[2225,138229,138230],{"class":2239},"(pdf",[2225,138232,1520],{"class":2235},[2225,138234,138235],{"class":2239},"body) ",[2225,138237,19008],{"class":2235},[2225,138239,138240,138242,138244],{"class":2227,"line":6587},[2225,138241,19143],{"class":2235},[2225,138243,1381],{"class":2239},[2225,138245,1707],{"class":2235},[2225,138247,138248,138251,138253,138255,138258,138260,138262],{"class":2227,"line":7174},[2225,138249,138250],{"class":2307},"  lifecycle",[2225,138252,2311],{"class":2235},[2225,138254,2249],{"class":2235},[2225,138256,138257],{"class":2252},"eager",[2225,138259,2805],{"class":2235},[2225,138261,2298],{"class":2235},[2225,138263,138264],{"class":2763}," \u002F\u002F or 'lazy'\n",[2225,138266,138267,138269],{"class":2227,"line":7180},[2225,138268,2359],{"class":2235},[2225,138270,2362],{"class":2239},[2225,138272,138273],{"class":2227,"line":7220},[2225,138274,2285],{"emptyLinePlaceholder":2284},[2225,138276,138277,138279,138281,138283,138285,138287,138289],{"class":2227,"line":7225},[2225,138278,2775],{"class":2774},[2225,138280,2778],{"class":2239},[2225,138282,2781],{"class":2235},[2225,138284,2784],{"class":2231},[2225,138286,2744],{"class":2291},[2225,138288,2789],{"class":2239},[2225,138290,4200],{"class":2235},[2225,138292,138293,138295,138297,138299,138301],{"class":2227,"line":7231},[2225,138294,15608],{"class":2307},[2225,138296,2311],{"class":2235},[2225,138298,68576],{"class":2291},[2225,138300,4228],{"class":2239},[2225,138302,1707],{"class":2235},[2225,138304,138305,138307,138309,138311,138313,138315],{"class":2227,"line":7267},[2225,138306,4205],{"class":2307},[2225,138308,2311],{"class":2235},[2225,138310,2249],{"class":2235},[2225,138312,6408],{"class":2252},[2225,138314,2805],{"class":2235},[2225,138316,1707],{"class":2235},[2225,138318,138319,138322,138324,138327],{"class":2227,"line":7272},[2225,138320,138321],{"class":2307},"  derivationStrategies",[2225,138323,2311],{"class":2235},[2225,138325,138326],{"class":2239}," [pdfDerivation]",[2225,138328,1707],{"class":2235},[2225,138330,138331,138333],{"class":2227,"line":7278},[2225,138332,2359],{"class":2235},[2225,138334,2362],{"class":2239},[1022,138336,138338],{"id":138337},"basic-usage","Basic usage",[937,138340,138341,138342,138344,138345,138348],{},"Writing a source record fires ",[941,138343,14092],{}," inside the same transaction (",[941,138346,138347],{},"lifecycle: 'eager'","); the\noutputs are readable immediately through their own collections:",[2217,138350,138352],{"className":2219,"code":138351,"language":2221,"meta":1037,"style":1037},"const vault = await db.openVault('docs')\n\nawait vault.collection\u003CPdf>('pdfs').put('doc-1', {\n  id: 'doc-1',\n  body: 'Annual report body text',\n  pages: 12,\n})\n\nawait vault.collection('pdf-meta').get('doc-1')\n\u002F\u002F → { len: 24, pages: 12, _derivedFrom: { source: 'pdfs', sourceId: 'doc-1', sourceVersion: 1, derivedAt: '2026-05-18T00:00:00.000Z', strategyHash: 'sha256-...' } }\n\nawait vault.collection('pdf-text').get('doc-1')\n\u002F\u002F → { content: 'Annual report body text', _derivedFrom: { source: 'pdfs', sourceId: 'doc-1', sourceVersion: 1, derivedAt: '2026-05-18T00:00:00.000Z', strategyHash: 'sha256-...' } }\n",[941,138353,138354,138380,138384,138426,138440,138455,138466,138472,138476,138510,138515,138519,138553],{"__ignoreMap":1037},[2225,138355,138356,138358,138360,138362,138364,138366,138368,138370,138372,138374,138376,138378],{"class":2227,"line":2228},[2225,138357,2775],{"class":2774},[2225,138359,2835],{"class":2239},[2225,138361,2781],{"class":2235},[2225,138363,2784],{"class":2231},[2225,138365,2842],{"class":2239},[2225,138367,1520],{"class":2235},[2225,138369,2847],{"class":2291},[2225,138371,2789],{"class":2239},[2225,138373,2805],{"class":2235},[2225,138375,69487],{"class":2252},[2225,138377,2805],{"class":2235},[2225,138379,2362],{"class":2239},[2225,138381,138382],{"class":2227,"line":1041},[2225,138383,2285],{"emptyLinePlaceholder":2284},[2225,138385,138386,138388,138390,138392,138394,138396,138398,138400,138402,138404,138406,138408,138410,138412,138414,138416,138418,138420,138422,138424],{"class":2227,"line":1038},[2225,138387,6500],{"class":2231},[2225,138389,6466],{"class":2239},[2225,138391,1520],{"class":2235},[2225,138393,6471],{"class":2291},[2225,138395,6474],{"class":2235},[2225,138397,138014],{"class":6342},[2225,138399,6480],{"class":2235},[2225,138401,2789],{"class":2239},[2225,138403,2805],{"class":2235},[2225,138405,138052],{"class":2252},[2225,138407,2805],{"class":2235},[2225,138409,1381],{"class":2239},[2225,138411,1520],{"class":2235},[2225,138413,3161],{"class":2291},[2225,138415,2789],{"class":2239},[2225,138417,2805],{"class":2235},[2225,138419,90195],{"class":2252},[2225,138421,2805],{"class":2235},[2225,138423,2298],{"class":2235},[2225,138425,2301],{"class":2235},[2225,138427,138428,138430,138432,138434,138436,138438],{"class":2227,"line":2288},[2225,138429,28371],{"class":2307},[2225,138431,2311],{"class":2235},[2225,138433,2249],{"class":2235},[2225,138435,90195],{"class":2252},[2225,138437,2805],{"class":2235},[2225,138439,1707],{"class":2235},[2225,138441,138442,138444,138446,138448,138451,138453],{"class":2227,"line":2304},[2225,138443,86679],{"class":2307},[2225,138445,2311],{"class":2235},[2225,138447,2249],{"class":2235},[2225,138449,138450],{"class":2252},"Annual report body text",[2225,138452,2805],{"class":2235},[2225,138454,1707],{"class":2235},[2225,138456,138457,138460,138462,138464],{"class":2227,"line":2356},[2225,138458,138459],{"class":2307},"  pages",[2225,138461,2311],{"class":2235},[2225,138463,108590],{"class":6527},[2225,138465,1707],{"class":2235},[2225,138467,138468,138470],{"class":2227,"line":2830},[2225,138469,2359],{"class":2235},[2225,138471,2362],{"class":2239},[2225,138473,138474],{"class":2227,"line":2861},[2225,138475,2285],{"emptyLinePlaceholder":2284},[2225,138477,138478,138480,138482,138484,138486,138488,138490,138492,138494,138496,138498,138500,138502,138504,138506,138508],{"class":2227,"line":4246},[2225,138479,6500],{"class":2231},[2225,138481,6466],{"class":2239},[2225,138483,1520],{"class":2235},[2225,138485,6471],{"class":2291},[2225,138487,2789],{"class":2239},[2225,138489,2805],{"class":2235},[2225,138491,138106],{"class":2252},[2225,138493,2805],{"class":2235},[2225,138495,1381],{"class":2239},[2225,138497,1520],{"class":2235},[2225,138499,3158],{"class":2291},[2225,138501,2789],{"class":2239},[2225,138503,2805],{"class":2235},[2225,138505,90195],{"class":2252},[2225,138507,2805],{"class":2235},[2225,138509,2362],{"class":2239},[2225,138511,138512],{"class":2227,"line":4253},[2225,138513,138514],{"class":2763},"\u002F\u002F → { len: 24, pages: 12, _derivedFrom: { source: 'pdfs', sourceId: 'doc-1', sourceVersion: 1, derivedAt: '2026-05-18T00:00:00.000Z', strategyHash: 'sha256-...' } }\n",[2225,138516,138517],{"class":2227,"line":4259},[2225,138518,2285],{"emptyLinePlaceholder":2284},[2225,138520,138521,138523,138525,138527,138529,138531,138533,138535,138537,138539,138541,138543,138545,138547,138549,138551],{"class":2227,"line":6548},[2225,138522,6500],{"class":2231},[2225,138524,6466],{"class":2239},[2225,138526,1520],{"class":2235},[2225,138528,6471],{"class":2291},[2225,138530,2789],{"class":2239},[2225,138532,2805],{"class":2235},[2225,138534,138140],{"class":2252},[2225,138536,2805],{"class":2235},[2225,138538,1381],{"class":2239},[2225,138540,1520],{"class":2235},[2225,138542,3158],{"class":2291},[2225,138544,2789],{"class":2239},[2225,138546,2805],{"class":2235},[2225,138548,90195],{"class":2252},[2225,138550,2805],{"class":2235},[2225,138552,2362],{"class":2239},[2225,138554,138555],{"class":2227,"line":6576},[2225,138556,138557],{"class":2763},"\u002F\u002F → { content: 'Annual report body text', _derivedFrom: { source: 'pdfs', sourceId: 'doc-1', sourceVersion: 1, derivedAt: '2026-05-18T00:00:00.000Z', strategyHash: 'sha256-...' } }\n",[1022,138559,101012],{"id":41259},[1591,138561,138563,138566],{"id":138562},"derivesource-ctx-and-the-read-only-vault-facade",[941,138564,138565],{},"derive(source, ctx)"," and the read-only vault facade",[937,138568,960,138569,138571,138572,138575,138576,138579],{},[941,138570,14092],{}," function receives a second argument: a ",[941,138573,138574],{},"DerivationContext","\ncarrying the same read-only vault facade guards see as ",[941,138577,138578],{},"ctx.vault",". Use it\nto fetch sibling records without denormalising them onto the source row.",[2217,138581,138583],{"className":2219,"code":138582,"language":2221,"meta":1037,"style":1037},"withDerivation\u003CAllocation, { receipt: Receipt }>({\n  source: 'allocations',\n  deterministic: true,\n  outputs: { receipt: { shape: 'record', collection: 'receipts' } },\n  derive: async (alloc, ctx) => {\n    const payment = await ctx.vault.collection\u003CPayment>('payments').get(alloc.paymentId)\n    const bill = await ctx.vault.collection\u003CBill>('bills').get(alloc.billId)\n    return {\n      receipt: {\n        id: alloc.id,\n        paymentId: alloc.paymentId,\n        issuedAt: payment!.paymentDate,\n        clientId: bill!.clientId,\n        appliedAmount: alloc.appliedAmount,\n      },\n    }\n  },\n  lifecycle: 'eager',\n})\n",[941,138584,138585,138610,138625,138635,138675,138699,138752,138806,138812,138821,138837,138852,138868,138883,138899,138903,138907,138911,138925],{"__ignoreMap":1037},[2225,138586,138587,138589,138591,138594,138596,138598,138600,138602,138604,138606,138608],{"class":2227,"line":2228},[2225,138588,137892],{"class":2291},[2225,138590,6474],{"class":2235},[2225,138592,138593],{"class":6342},"Allocation",[2225,138595,2298],{"class":2235},[2225,138597,2236],{"class":2235},[2225,138599,60988],{"class":2307},[2225,138601,2311],{"class":2235},[2225,138603,27802],{"class":6342},[2225,138605,85293],{"class":2235},[2225,138607,2789],{"class":2239},[2225,138609,4200],{"class":2235},[2225,138611,138612,138614,138616,138618,138621,138623],{"class":2227,"line":1041},[2225,138613,138045],{"class":2307},[2225,138615,2311],{"class":2235},[2225,138617,2249],{"class":2235},[2225,138619,138620],{"class":2252},"allocations",[2225,138622,2805],{"class":2235},[2225,138624,1707],{"class":2235},[2225,138626,138627,138629,138631,138633],{"class":2227,"line":1038},[2225,138628,138061],{"class":2307},[2225,138630,2311],{"class":2235},[2225,138632,2322],{"class":2321},[2225,138634,1707],{"class":2235},[2225,138636,138637,138639,138641,138643,138645,138647,138649,138651,138653,138655,138657,138659,138661,138663,138665,138667,138669,138671,138673],{"class":2227,"line":2288},[2225,138638,138072],{"class":2307},[2225,138640,2311],{"class":2235},[2225,138642,2236],{"class":2235},[2225,138644,60988],{"class":2307},[2225,138646,2311],{"class":2235},[2225,138648,2236],{"class":2235},[2225,138650,138087],{"class":2307},[2225,138652,2311],{"class":2235},[2225,138654,2249],{"class":2235},[2225,138656,24158],{"class":2252},[2225,138658,2805],{"class":2235},[2225,138660,2298],{"class":2235},[2225,138662,7061],{"class":2307},[2225,138664,2311],{"class":2235},[2225,138666,2249],{"class":2235},[2225,138668,27844],{"class":2252},[2225,138670,2805],{"class":2235},[2225,138672,2243],{"class":2235},[2225,138674,2353],{"class":2235},[2225,138676,138677,138679,138681,138683,138685,138688,138690,138693,138695,138697],{"class":2227,"line":2304},[2225,138678,138153],{"class":2291},[2225,138680,2311],{"class":2235},[2225,138682,38717],{"class":2774},[2225,138684,953],{"class":2235},[2225,138686,138687],{"class":7052},"alloc",[2225,138689,2298],{"class":2235},[2225,138691,138692],{"class":7052}," ctx",[2225,138694,1381],{"class":2235},[2225,138696,8116],{"class":2774},[2225,138698,2301],{"class":2235},[2225,138700,138701,138703,138706,138708,138710,138712,138714,138716,138718,138720,138722,138725,138727,138729,138731,138733,138735,138737,138739,138741,138743,138745,138747,138750],{"class":2227,"line":2356},[2225,138702,80048],{"class":2774},[2225,138704,138705],{"class":2239}," payment",[2225,138707,6346],{"class":2235},[2225,138709,2784],{"class":2231},[2225,138711,138692],{"class":2239},[2225,138713,1520],{"class":2235},[2225,138715,2694],{"class":2239},[2225,138717,1520],{"class":2235},[2225,138719,6471],{"class":2291},[2225,138721,6474],{"class":2235},[2225,138723,138724],{"class":6342},"Payment",[2225,138726,6480],{"class":2235},[2225,138728,2789],{"class":2307},[2225,138730,2805],{"class":2235},[2225,138732,32579],{"class":2252},[2225,138734,2805],{"class":2235},[2225,138736,1381],{"class":2307},[2225,138738,1520],{"class":2235},[2225,138740,3158],{"class":2291},[2225,138742,2789],{"class":2307},[2225,138744,138687],{"class":2239},[2225,138746,1520],{"class":2235},[2225,138748,138749],{"class":2239},"paymentId",[2225,138751,2362],{"class":2307},[2225,138753,138754,138756,138759,138761,138763,138765,138767,138769,138771,138773,138775,138778,138780,138782,138784,138787,138789,138791,138793,138795,138797,138799,138801,138804],{"class":2227,"line":2830},[2225,138755,80048],{"class":2774},[2225,138757,138758],{"class":2239}," bill",[2225,138760,6346],{"class":2235},[2225,138762,2784],{"class":2231},[2225,138764,138692],{"class":2239},[2225,138766,1520],{"class":2235},[2225,138768,2694],{"class":2239},[2225,138770,1520],{"class":2235},[2225,138772,6471],{"class":2291},[2225,138774,6474],{"class":2235},[2225,138776,138777],{"class":6342},"Bill",[2225,138779,6480],{"class":2235},[2225,138781,2789],{"class":2307},[2225,138783,2805],{"class":2235},[2225,138785,138786],{"class":2252},"bills",[2225,138788,2805],{"class":2235},[2225,138790,1381],{"class":2307},[2225,138792,1520],{"class":2235},[2225,138794,3158],{"class":2291},[2225,138796,2789],{"class":2307},[2225,138798,138687],{"class":2239},[2225,138800,1520],{"class":2235},[2225,138802,138803],{"class":2239},"billId",[2225,138805,2362],{"class":2307},[2225,138807,138808,138810],{"class":2227,"line":2861},[2225,138809,66675],{"class":2231},[2225,138811,2301],{"class":2235},[2225,138813,138814,138817,138819],{"class":2227,"line":4246},[2225,138815,138816],{"class":2307},"      receipt",[2225,138818,2311],{"class":2235},[2225,138820,2301],{"class":2235},[2225,138822,138823,138826,138828,138831,138833,138835],{"class":2227,"line":4253},[2225,138824,138825],{"class":2307},"        id",[2225,138827,2311],{"class":2235},[2225,138829,138830],{"class":2239}," alloc",[2225,138832,1520],{"class":2235},[2225,138834,6983],{"class":2239},[2225,138836,1707],{"class":2235},[2225,138838,138839,138842,138844,138846,138848,138850],{"class":2227,"line":4259},[2225,138840,138841],{"class":2307},"        paymentId",[2225,138843,2311],{"class":2235},[2225,138845,138830],{"class":2239},[2225,138847,1520],{"class":2235},[2225,138849,138749],{"class":2239},[2225,138851,1707],{"class":2235},[2225,138853,138854,138857,138859,138861,138863,138866],{"class":2227,"line":6548},[2225,138855,138856],{"class":2307},"        issuedAt",[2225,138858,2311],{"class":2235},[2225,138860,138705],{"class":2239},[2225,138862,83002],{"class":2235},[2225,138864,138865],{"class":2239},"paymentDate",[2225,138867,1707],{"class":2235},[2225,138869,138870,138873,138875,138877,138879,138881],{"class":2227,"line":6576},[2225,138871,138872],{"class":2307},"        clientId",[2225,138874,2311],{"class":2235},[2225,138876,138758],{"class":2239},[2225,138878,83002],{"class":2235},[2225,138880,59721],{"class":2239},[2225,138882,1707],{"class":2235},[2225,138884,138885,138888,138890,138892,138894,138897],{"class":2227,"line":6582},[2225,138886,138887],{"class":2307},"        appliedAmount",[2225,138889,2311],{"class":2235},[2225,138891,138830],{"class":2239},[2225,138893,1520],{"class":2235},[2225,138895,138896],{"class":2239},"appliedAmount",[2225,138898,1707],{"class":2235},[2225,138900,138901],{"class":2227,"line":6587},[2225,138902,60068],{"class":2235},[2225,138904,138905],{"class":2227,"line":7174},[2225,138906,60103],{"class":2235},[2225,138908,138909],{"class":2227,"line":7180},[2225,138910,19017],{"class":2235},[2225,138912,138913,138915,138917,138919,138921,138923],{"class":2227,"line":7220},[2225,138914,138250],{"class":2307},[2225,138916,2311],{"class":2235},[2225,138918,2249],{"class":2235},[2225,138920,138257],{"class":2252},[2225,138922,2805],{"class":2235},[2225,138924,1707],{"class":2235},[2225,138926,138927,138929],{"class":2227,"line":7225},[2225,138928,2359],{"class":2235},[2225,138930,2362],{"class":2239},[937,138932,138933,138935,138936,968,138939,2462,138942,138945,138946,138949,138950,138952],{},[941,138934,138578],{}," exposes ",[941,138937,138938],{},".get(id)",[941,138940,138941],{},".list()",[941,138943,138944],{},".query()"," — no write\ncapability is reachable from the facade. The strategy hash incorporates\n",[941,138947,138948],{},"derive.toString()",", so the function source pins the inputs; whatever\nsibling reads happen inside ",[941,138951,14092],{}," must be deterministic given the\nsame source record (the consumer's responsibility — the hash does not\nfingerprint sibling-record content).",[937,138954,138955],{},"Available on both lifecycles: the eager dispatch and the lazy\nresolve-on-read paths both pass the same facade.",[1591,138957,138959],{"id":138958},"sources",[941,138960,138958],{},[937,138962,138963,138966,138967,138969,138970,138972],{},[941,138964,138965],{},"sources: ['a', 'b']"," declares sibling collections whose writes re-fire\nthis derivation. A write to any declared sibling re-runs ",[941,138968,14092],{},"\nagainst the PRIMARY ",[941,138971,22723],{}," record at the SAME id (a silent no-op if no\nprimary record exists there). Siblings participate in cycle detection.",[2217,138974,138976],{"className":2219,"code":138975,"language":2221,"meta":1037,"style":1037},"withDerivation\u003CAllocation, { receipt: Receipt }>({\n  source: 'allocations',\n  sources: ['payments', 'bills'],  \u002F\u002F sibling writes re-derive at the same id\n  deterministic: true,\n  outputs: { receipt: { shape: 'record', collection: 'receipts' } },\n  derive: async (alloc, ctx) => ({ \u002F* … *\u002F }),\n  lifecycle: 'eager',\n})\n",[941,138977,138978,139002,139016,139046,139056,139096,139128,139142],{"__ignoreMap":1037},[2225,138979,138980,138982,138984,138986,138988,138990,138992,138994,138996,138998,139000],{"class":2227,"line":2228},[2225,138981,137892],{"class":2291},[2225,138983,6474],{"class":2235},[2225,138985,138593],{"class":6342},[2225,138987,2298],{"class":2235},[2225,138989,2236],{"class":2235},[2225,138991,60988],{"class":2307},[2225,138993,2311],{"class":2235},[2225,138995,27802],{"class":6342},[2225,138997,85293],{"class":2235},[2225,138999,2789],{"class":2239},[2225,139001,4200],{"class":2235},[2225,139003,139004,139006,139008,139010,139012,139014],{"class":2227,"line":1041},[2225,139005,138045],{"class":2307},[2225,139007,2311],{"class":2235},[2225,139009,2249],{"class":2235},[2225,139011,138620],{"class":2252},[2225,139013,2805],{"class":2235},[2225,139015,1707],{"class":2235},[2225,139017,139018,139021,139023,139025,139027,139029,139031,139033,139035,139037,139039,139041,139043],{"class":2227,"line":1038},[2225,139019,139020],{"class":2307},"  sources",[2225,139022,2311],{"class":2235},[2225,139024,8667],{"class":2239},[2225,139026,2805],{"class":2235},[2225,139028,32579],{"class":2252},[2225,139030,2805],{"class":2235},[2225,139032,2298],{"class":2235},[2225,139034,2249],{"class":2235},[2225,139036,138786],{"class":2252},[2225,139038,2805],{"class":2235},[2225,139040,8803],{"class":2239},[2225,139042,2298],{"class":2235},[2225,139044,139045],{"class":2763},"  \u002F\u002F sibling writes re-derive at the same id\n",[2225,139047,139048,139050,139052,139054],{"class":2227,"line":2288},[2225,139049,138061],{"class":2307},[2225,139051,2311],{"class":2235},[2225,139053,2322],{"class":2321},[2225,139055,1707],{"class":2235},[2225,139057,139058,139060,139062,139064,139066,139068,139070,139072,139074,139076,139078,139080,139082,139084,139086,139088,139090,139092,139094],{"class":2227,"line":2304},[2225,139059,138072],{"class":2307},[2225,139061,2311],{"class":2235},[2225,139063,2236],{"class":2235},[2225,139065,60988],{"class":2307},[2225,139067,2311],{"class":2235},[2225,139069,2236],{"class":2235},[2225,139071,138087],{"class":2307},[2225,139073,2311],{"class":2235},[2225,139075,2249],{"class":2235},[2225,139077,24158],{"class":2252},[2225,139079,2805],{"class":2235},[2225,139081,2298],{"class":2235},[2225,139083,7061],{"class":2307},[2225,139085,2311],{"class":2235},[2225,139087,2249],{"class":2235},[2225,139089,27844],{"class":2252},[2225,139091,2805],{"class":2235},[2225,139093,2243],{"class":2235},[2225,139095,2353],{"class":2235},[2225,139097,139098,139100,139102,139104,139106,139108,139110,139112,139114,139116,139118,139120,139122,139124,139126],{"class":2227,"line":2356},[2225,139099,138153],{"class":2291},[2225,139101,2311],{"class":2235},[2225,139103,38717],{"class":2774},[2225,139105,953],{"class":2235},[2225,139107,138687],{"class":7052},[2225,139109,2298],{"class":2235},[2225,139111,138692],{"class":7052},[2225,139113,1381],{"class":2235},[2225,139115,8116],{"class":2774},[2225,139117,953],{"class":2239},[2225,139119,2792],{"class":2235},[2225,139121,10968],{"class":2763},[2225,139123,2243],{"class":2235},[2225,139125,1381],{"class":2239},[2225,139127,1707],{"class":2235},[2225,139129,139130,139132,139134,139136,139138,139140],{"class":2227,"line":2830},[2225,139131,138250],{"class":2307},[2225,139133,2311],{"class":2235},[2225,139135,2249],{"class":2235},[2225,139137,138257],{"class":2252},[2225,139139,2805],{"class":2235},[2225,139141,1707],{"class":2235},[2225,139143,139144,139146],{"class":2227,"line":2861},[2225,139145,2359],{"class":2235},[2225,139147,2362],{"class":2239},[937,139149,139150],{},"This retires the hand-rolled \"poke a sibling\" pattern (writing a no-op\nback to the source to force a re-derive when a referenced record\nchanged).",[1591,139152,139154],{"id":139153},"fk-keyed-triggers-reverse-denormalization","FK-keyed triggers — reverse-denormalization",[937,139156,139157,139160,139161,1952,139164,139166,139167,139170],{},[941,139158,139159],{},"sources[]"," re-fires at the ",[950,139162,139163],{},"same id",[941,139165,137926],{}," re-fires keyed by a\n",[950,139168,139169],{},"foreign key",": a write to a PARENT collection fans out to every source\nrecord whose FK matches the parent's id. The canonical use is keeping a\ndenormalized field on the children in sync with a parent change.",[2217,139172,139174],{"className":2219,"code":139173,"language":2221,"meta":1037,"style":1037},"withDerivation\u003CSale, { self: Sale }>({\n  source: 'sales',\n  deterministic: true,\n  \u002F\u002F A write to buyers re-derives every sale where sale.buyerId === buyer.id.\n  triggerBy: [{ collection: 'buyers', on: 'buyerId' }], \u002F\u002F `on` = the FK ON the source\n  \u002F\u002F Self-write: patch ONLY buyerName back onto the sale (field-level provenance).\n  outputs: { self: { shape: 'record', collection: 'sales', denorm: ['buyerName'] } },\n  derive: async (sale, ctx) => {\n    const b = await ctx.vault.collection\u003CBuyer>('buyers').get(sale.buyerId)\n    return { self: { ...sale, buyerName: b?.companyName ?? null } }\n  },\n  lifecycle: 'eager',\n})\n",[941,139175,139176,139203,139217,139227,139232,139276,139281,139339,139362,139413,139451,139455,139469],{"__ignoreMap":1037},[2225,139177,139178,139180,139182,139185,139187,139189,139192,139194,139197,139199,139201],{"class":2227,"line":2228},[2225,139179,137892],{"class":2291},[2225,139181,6474],{"class":2235},[2225,139183,139184],{"class":6342},"Sale",[2225,139186,2298],{"class":2235},[2225,139188,2236],{"class":2235},[2225,139190,139191],{"class":2307}," self",[2225,139193,2311],{"class":2235},[2225,139195,139196],{"class":6342}," Sale",[2225,139198,85293],{"class":2235},[2225,139200,2789],{"class":2239},[2225,139202,4200],{"class":2235},[2225,139204,139205,139207,139209,139211,139213,139215],{"class":2227,"line":1041},[2225,139206,138045],{"class":2307},[2225,139208,2311],{"class":2235},[2225,139210,2249],{"class":2235},[2225,139212,123562],{"class":2252},[2225,139214,2805],{"class":2235},[2225,139216,1707],{"class":2235},[2225,139218,139219,139221,139223,139225],{"class":2227,"line":1038},[2225,139220,138061],{"class":2307},[2225,139222,2311],{"class":2235},[2225,139224,2322],{"class":2321},[2225,139226,1707],{"class":2235},[2225,139228,139229],{"class":2227,"line":2288},[2225,139230,139231],{"class":2763},"  \u002F\u002F A write to buyers re-derives every sale where sale.buyerId === buyer.id.\n",[2225,139233,139234,139237,139239,139241,139243,139245,139247,139249,139252,139254,139256,139259,139261,139263,139265,139267,139269,139271,139273],{"class":2227,"line":2304},[2225,139235,139236],{"class":2307},"  triggerBy",[2225,139238,2311],{"class":2235},[2225,139240,8667],{"class":2239},[2225,139242,2792],{"class":2235},[2225,139244,7061],{"class":2307},[2225,139246,2311],{"class":2235},[2225,139248,2249],{"class":2235},[2225,139250,139251],{"class":2252},"buyers",[2225,139253,2805],{"class":2235},[2225,139255,2298],{"class":2235},[2225,139257,139258],{"class":2307}," on",[2225,139260,2311],{"class":2235},[2225,139262,2249],{"class":2235},[2225,139264,109007],{"class":2252},[2225,139266,2805],{"class":2235},[2225,139268,2243],{"class":2235},[2225,139270,8803],{"class":2239},[2225,139272,2298],{"class":2235},[2225,139274,139275],{"class":2763}," \u002F\u002F `on` = the FK ON the source\n",[2225,139277,139278],{"class":2227,"line":2356},[2225,139279,139280],{"class":2763},"  \u002F\u002F Self-write: patch ONLY buyerName back onto the sale (field-level provenance).\n",[2225,139282,139283,139285,139287,139289,139291,139293,139295,139297,139299,139301,139303,139305,139307,139309,139311,139313,139315,139317,139319,139322,139324,139326,139328,139331,139333,139335,139337],{"class":2227,"line":2830},[2225,139284,138072],{"class":2307},[2225,139286,2311],{"class":2235},[2225,139288,2236],{"class":2235},[2225,139290,139191],{"class":2307},[2225,139292,2311],{"class":2235},[2225,139294,2236],{"class":2235},[2225,139296,138087],{"class":2307},[2225,139298,2311],{"class":2235},[2225,139300,2249],{"class":2235},[2225,139302,24158],{"class":2252},[2225,139304,2805],{"class":2235},[2225,139306,2298],{"class":2235},[2225,139308,7061],{"class":2307},[2225,139310,2311],{"class":2235},[2225,139312,2249],{"class":2235},[2225,139314,123562],{"class":2252},[2225,139316,2805],{"class":2235},[2225,139318,2298],{"class":2235},[2225,139320,139321],{"class":2307}," denorm",[2225,139323,2311],{"class":2235},[2225,139325,8667],{"class":2239},[2225,139327,2805],{"class":2235},[2225,139329,139330],{"class":2252},"buyerName",[2225,139332,2805],{"class":2235},[2225,139334,8890],{"class":2239},[2225,139336,2359],{"class":2235},[2225,139338,2353],{"class":2235},[2225,139340,139341,139343,139345,139347,139349,139352,139354,139356,139358,139360],{"class":2227,"line":2861},[2225,139342,138153],{"class":2291},[2225,139344,2311],{"class":2235},[2225,139346,38717],{"class":2774},[2225,139348,953],{"class":2235},[2225,139350,139351],{"class":7052},"sale",[2225,139353,2298],{"class":2235},[2225,139355,138692],{"class":7052},[2225,139357,1381],{"class":2235},[2225,139359,8116],{"class":2774},[2225,139361,2301],{"class":2235},[2225,139363,139364,139366,139368,139370,139372,139374,139376,139378,139380,139382,139384,139387,139389,139391,139393,139395,139397,139399,139401,139403,139405,139407,139409,139411],{"class":2227,"line":4246},[2225,139365,80048],{"class":2774},[2225,139367,80536],{"class":2239},[2225,139369,6346],{"class":2235},[2225,139371,2784],{"class":2231},[2225,139373,138692],{"class":2239},[2225,139375,1520],{"class":2235},[2225,139377,2694],{"class":2239},[2225,139379,1520],{"class":2235},[2225,139381,6471],{"class":2291},[2225,139383,6474],{"class":2235},[2225,139385,139386],{"class":6342},"Buyer",[2225,139388,6480],{"class":2235},[2225,139390,2789],{"class":2307},[2225,139392,2805],{"class":2235},[2225,139394,139251],{"class":2252},[2225,139396,2805],{"class":2235},[2225,139398,1381],{"class":2307},[2225,139400,1520],{"class":2235},[2225,139402,3158],{"class":2291},[2225,139404,2789],{"class":2307},[2225,139406,139351],{"class":2239},[2225,139408,1520],{"class":2235},[2225,139410,109007],{"class":2239},[2225,139412,2362],{"class":2307},[2225,139414,139415,139417,139419,139421,139423,139425,139427,139429,139431,139434,139436,139438,139440,139443,139445,139447,139449],{"class":2227,"line":4253},[2225,139416,66675],{"class":2231},[2225,139418,2236],{"class":2235},[2225,139420,139191],{"class":2307},[2225,139422,2311],{"class":2235},[2225,139424,2236],{"class":2235},[2225,139426,67770],{"class":2235},[2225,139428,139351],{"class":2239},[2225,139430,2298],{"class":2235},[2225,139432,139433],{"class":2307}," buyerName",[2225,139435,2311],{"class":2235},[2225,139437,80536],{"class":2239},[2225,139439,8457],{"class":2235},[2225,139441,139442],{"class":2239},"companyName",[2225,139444,8239],{"class":2235},[2225,139446,7090],{"class":2235},[2225,139448,2243],{"class":2235},[2225,139450,6369],{"class":2235},[2225,139452,139453],{"class":2227,"line":4259},[2225,139454,19017],{"class":2235},[2225,139456,139457,139459,139461,139463,139465,139467],{"class":2227,"line":6548},[2225,139458,138250],{"class":2307},[2225,139460,2311],{"class":2235},[2225,139462,2249],{"class":2235},[2225,139464,138257],{"class":2252},[2225,139466,2805],{"class":2235},[2225,139468,1707],{"class":2235},[2225,139470,139471,139473],{"class":2227,"line":6576},[2225,139472,2359],{"class":2235},[2225,139474,2362],{"class":2239},[1111,139476,139477,139511,139538],{},[1114,139478,139479,139485,139486,139488,139489,2993,139491,139494,139495,139498,139499,139502,139503,139506,139507,139510],{},[950,139480,139481,139482,1520],{},"Self-write + ",[941,139483,139484],{},"denorm"," A record output whose ",[941,139487,6471],{}," equals the\n",[941,139490,22723],{},[1092,139492,139493],{},"self-write"," and MUST declare ",[941,139496,139497],{},"denorm: [...]",". Only those\nfields are merged onto the ",[950,139500,139501],{},"raw stored record"," — the rest of the user's\nrecord (and any i18n maps) is never clobbered, and no whole-record\n",[941,139504,139505],{},"_derivedFrom"," tag is added (the record stays user-owned). The self-write\nedge is exempt from cycle detection; a ",[950,139508,139509],{},"value-equality guard"," breaks the\nrecursion: when the patch changes nothing, no write is issued.",[1114,139512,139513,139516,139517,139519,139520,69860,139523,139525,139526,139529,139530,139533,139534,139537],{},[950,139514,139515],{},"Fan-out cost."," The match runs through the FK index when the source\ncollection has an index on ",[941,139518,4883],{}," (declared via ",[941,139521,139522],{},"indexes: [...]",[941,139524,137930],{}," enabled — see ",[1007,139527,705],{"href":139528},".\u002Findexing",");\notherwise it scans the collection (O(N) — fine for small child sets;\nindex the FK to scale). An optional ",[941,139531,139532],{},"maxFanout"," on the trigger entry\n(default 64) throws ",[941,139535,139536],{},"DerivationCapExceededError"," before any write.",[1114,139539,139540,139543,139544,139546,139547,4688,139549,1520],{},[941,139541,139542],{},"triggerBy.collection"," must differ from ",[941,139545,22723],{},". Give it either ",[941,139548,4883],{},[941,139550,7482],{},[24204,139552,139554,139555],{"id":139553},"composite-matches-match","Composite matches — ",[941,139556,7482],{},[937,139558,10993,139559,139562,139563,139566,139567,139569,139570,139573,139574,139577,139578,139580,139581,1520],{},[941,139560,139561],{},"0.7.0-pre.12"," a trigger entry can key on ",[950,139564,139565],{},"several fields at once","\n(noy-db#1249). ",[941,139568,7482],{}," is a list of ",[941,139571,139572],{},"{ from, to }"," pairs, read as a\n",[950,139575,139576],{},"conjunction",": a source record matches when ",[1092,139579,2119],{}," pair satisfies\n",[941,139582,139583],{},"String(source[to]) === String(written[from])",[2217,139585,139587],{"className":2219,"code":139586,"language":2221,"meta":1037,"style":1037},"\u002F\u002F Re-derive every sale for the same buyer AND the same region.\ntriggerBy: [{ collection: 'buyers', match: [{ from: 'id', to: 'buyerId' }, { from: 'region', to: 'region' }] }]\n",[941,139588,139589,139594],{"__ignoreMap":1037},[2225,139590,139591],{"class":2227,"line":2228},[2225,139592,139593],{"class":2763},"\u002F\u002F Re-derive every sale for the same buyer AND the same region.\n",[2225,139595,139596,139598,139600,139602,139604,139606,139608,139610,139612,139614,139616,139618,139620,139622,139624,139626,139628,139630,139632,139634,139636,139638,139640,139642,139644,139646,139648,139650,139652,139654,139656,139658,139660,139662,139664,139666,139668,139670,139672,139674,139676,139678],{"class":2227,"line":1041},[2225,139597,137926],{"class":6342},[2225,139599,2311],{"class":2235},[2225,139601,8667],{"class":2239},[2225,139603,2792],{"class":2235},[2225,139605,7061],{"class":2307},[2225,139607,2311],{"class":2235},[2225,139609,2249],{"class":2235},[2225,139611,139251],{"class":2252},[2225,139613,2805],{"class":2235},[2225,139615,2298],{"class":2235},[2225,139617,66511],{"class":2307},[2225,139619,2311],{"class":2235},[2225,139621,8667],{"class":2239},[2225,139623,2792],{"class":2235},[2225,139625,2246],{"class":2307},[2225,139627,2311],{"class":2235},[2225,139629,2249],{"class":2235},[2225,139631,6983],{"class":2252},[2225,139633,2805],{"class":2235},[2225,139635,2298],{"class":2235},[2225,139637,43769],{"class":2307},[2225,139639,2311],{"class":2235},[2225,139641,2249],{"class":2235},[2225,139643,109007],{"class":2252},[2225,139645,2805],{"class":2235},[2225,139647,10965],{"class":2235},[2225,139649,2236],{"class":2235},[2225,139651,2246],{"class":2307},[2225,139653,2311],{"class":2235},[2225,139655,2249],{"class":2235},[2225,139657,53894],{"class":2252},[2225,139659,2805],{"class":2235},[2225,139661,2298],{"class":2235},[2225,139663,43769],{"class":2307},[2225,139665,2311],{"class":2235},[2225,139667,2249],{"class":2235},[2225,139669,53894],{"class":2252},[2225,139671,2805],{"class":2235},[2225,139673,2243],{"class":2235},[2225,139675,8890],{"class":2239},[2225,139677,2359],{"class":2235},[2225,139679,26484],{"class":2239},[937,139681,139682,139685,139686,139688,139689,139691,139692,139695,139696,1952,139699,139704,139705,139707,139708,139710],{},[941,139683,139684],{},"from: 'id'"," reads the ",[950,139687,109694],{}," record's id, so the ",[941,139690,4883],{}," form above is the\nsingle-pair special case — ",[941,139693,139694],{},"on: 'buyerId'"," normalizes to\n",[941,139697,139698],{},"match: [{ from: 'id', to: 'buyerId' }]",[950,139700,139701,139703],{},[941,139702,4883],{}," is unchanged and still\nsupported","; nothing that uses it needs rewriting. Both sides of a pair are\ncoerced to strings, and a pair whose value is neither ",[941,139706,7259],{}," nor ",[941,139709,20636],{},"\nfails to match rather than throwing.",[24204,139712,139714,139715],{"id":139713},"one-declared-hop-via","One declared hop — ",[941,139716,896],{},[937,139718,10993,139719,139721,139722,139725],{},[941,139720,40730],{}," a match pair may resolve through an ",[950,139723,139724],{},"intermediate collection","\n(noy-db#1277), which makes a relationship expressible where the two collections share no field\nat all:",[2217,139727,139729],{"className":2219,"code":139728,"language":2221,"meta":1037,"style":1037},"\u002F\u002F Bills carry `entityId`; disbursements carry `clientId`. The client record relates them.\nmatch: [\n  { from: 'clientId', to: 'entityId', via: { collection: 'clients', take: 'id', on: 'entityId' } },\n  { from: 'cycle', to: 'cycle' },\n]\n",[941,139730,139731,139736,139744,139818,139847],{"__ignoreMap":1037},[2225,139732,139733],{"class":2227,"line":2228},[2225,139734,139735],{"class":2763},"\u002F\u002F Bills carry `entityId`; disbursements carry `clientId`. The client record relates them.\n",[2225,139737,139738,139740,139742],{"class":2227,"line":1041},[2225,139739,7482],{"class":6342},[2225,139741,2311],{"class":2235},[2225,139743,32491],{"class":2239},[2225,139745,139746,139748,139750,139752,139754,139756,139758,139760,139762,139764,139766,139769,139771,139773,139775,139777,139779,139781,139783,139785,139787,139789,139791,139794,139796,139798,139800,139802,139804,139806,139808,139810,139812,139814,139816],{"class":2227,"line":1038},[2225,139747,26354],{"class":2235},[2225,139749,2246],{"class":2307},[2225,139751,2311],{"class":2235},[2225,139753,2249],{"class":2235},[2225,139755,59721],{"class":2252},[2225,139757,2805],{"class":2235},[2225,139759,2298],{"class":2235},[2225,139761,43769],{"class":2307},[2225,139763,2311],{"class":2235},[2225,139765,2249],{"class":2235},[2225,139767,139768],{"class":2252},"entityId",[2225,139770,2805],{"class":2235},[2225,139772,2298],{"class":2235},[2225,139774,122163],{"class":2307},[2225,139776,2311],{"class":2235},[2225,139778,2236],{"class":2235},[2225,139780,7061],{"class":2307},[2225,139782,2311],{"class":2235},[2225,139784,2249],{"class":2235},[2225,139786,23926],{"class":2252},[2225,139788,2805],{"class":2235},[2225,139790,2298],{"class":2235},[2225,139792,139793],{"class":2307}," take",[2225,139795,2311],{"class":2235},[2225,139797,2249],{"class":2235},[2225,139799,6983],{"class":2252},[2225,139801,2805],{"class":2235},[2225,139803,2298],{"class":2235},[2225,139805,139258],{"class":2307},[2225,139807,2311],{"class":2235},[2225,139809,2249],{"class":2235},[2225,139811,139768],{"class":2252},[2225,139813,2805],{"class":2235},[2225,139815,2243],{"class":2235},[2225,139817,2353],{"class":2235},[2225,139819,139820,139822,139824,139826,139828,139831,139833,139835,139837,139839,139841,139843,139845],{"class":2227,"line":2288},[2225,139821,26354],{"class":2235},[2225,139823,2246],{"class":2307},[2225,139825,2311],{"class":2235},[2225,139827,2249],{"class":2235},[2225,139829,139830],{"class":2252},"cycle",[2225,139832,2805],{"class":2235},[2225,139834,2298],{"class":2235},[2225,139836,43769],{"class":2307},[2225,139838,2311],{"class":2235},[2225,139840,2249],{"class":2235},[2225,139842,139830],{"class":2252},[2225,139844,2805],{"class":2235},[2225,139846,2353],{"class":2235},[2225,139848,139849],{"class":2227,"line":2304},[2225,139850,26484],{"class":2239},[937,139852,139853,139854,139857,139858,139861,139862,139865,139866,139869,139870,1520],{},"A source matches when the intermediate — the record in ",[941,139855,139856],{},"via.collection"," whose ",[941,139859,139860],{},"via.take"," equals\n",[941,139863,139864],{},"written[from]"," — carries a ",[941,139867,139868],{},"via.on"," equal to ",[941,139871,139872],{},"source[to]",[7646,139874,139876],{"color":10789,"icon":139875},"i-lucide-git-branch",[937,139877,139878,139881,139882,139885],{},[950,139879,139880],{},"A write to the INTERMEDIATE collection fires the trigger too",", fanning out on its old ∪ new\nvalue. This is the whole correctness argument rather than a detail: re-pointing an intermediate\nwrites to ",[950,139883,139884],{},"neither"," the trigger collection nor the source collection, so nothing else in the\nsystem can notice, and every source it used to address would be stranded silently. The cheaper\ndesign — resolving the hop only on writes to the trigger collection — passes every test anyone\nwould think to write, and fails only on that one edit.",[937,139887,139888,139889,139891],{},"The alternative to a hop is a denormalised key on one side. That is a second copy of something\nthe vault can already resolve, and a partial backfill goes quiet on exactly the oldest,\nleast-audited rows — the silent staleness ",[941,139890,7482],{}," exists to remove, handed back one layer down.",[937,139893,139894,139895,953,139898,139901,139902,139904,139905,139907],{},"Cost and edges: hop resolution is ",[950,139896,139897],{},"one lookup per written record",[941,139899,139900],{},"take: 'id'"," is a direct\n",[941,139903,3158],{},"), never one per candidate row. A dangling hop matches nothing rather than throwing.\n",[941,139906,139532],{}," caps the hop fan-out as it does the direct form, and the intermediate's edge enters\nthe cycle-detection graph.",[24204,139909,139911],{"id":139910},"when-the-fan-out-fires-updates-and-deletes","When the fan-out fires — updates and deletes",[937,139913,139914,139915,139917,139918,2311],{},"Two behaviours changed with ",[941,139916,7482],{},", and they apply to the ",[950,139919,139920,139922],{},[941,139921,4883],{}," form too",[1111,139924,139925,139939],{},[1114,139926,139927,139930,139931,139934,139935,139938],{},[950,139928,139929],{},"An update fans out on the old match ∪ the new one."," Changing a matched\nfield re-derives the records addressed by the ",[1092,139932,139933],{},"previous"," value as well as the\nnew one, so nothing is left silently stale. ⚠️ ",[950,139936,139937],{},"A write that does not thread\nthe prior record — a sync-applied wave write, a tiers restore — degrades to\nthe new tuple only."," That is the previous behaviour, chosen over guessing at\nan old key.",[1114,139940,139941,139944,139945,139947,139948,139950,139951,139954,139955,139960],{},[950,139942,139943],{},"A delete fans out on the tombstoned record's values."," Deletes previously\nfired no triggers at all. The fan-out runs ",[1092,139946,10761],{}," the delete commits, so a\n",[941,139949,139532],{}," cap or a ",[941,139952,139953],{},"strict: true"," failure ",[950,139956,139957,139958],{},"can surface from ",[941,139959,9467],{}," —\na call site that never used to throw for derivation reasons.",[24204,139962,139964],{"id":139963},"match-targets-are-checked-at-registration","Match targets are checked at registration",[937,139966,139967,139969,139970,1261,139972,139974,139975,139977],{},[941,139968,2138],{}," validates every ",[941,139971,2272],{},[941,139973,4879],{}," against the collection's\ndeclared fields, so a provable typo throws there rather than matching nothing\nforever. Two refinements land in ",[941,139976,39039],{}," (noy-db#1266):",[1338,139979,139980,139988],{},[1341,139981,139982],{},[1344,139983,139984,139986],{},[1347,139985,134984],{},[1347,139987,61655],{},[1357,139989,139990,140004,140020,140028],{},[1344,139991,139992,139999],{},[1362,139993,139994,139995,997,139997],{},"a field declared via ",[941,139996,3040],{},[950,139998,123209],{},[1362,140000,140001,140003],{},[950,140002,11205],{}," — it was previously rejected as an undeclared typo",[1344,140005,140006,140011],{},[1362,140007,5983,140008,140010],{},[941,140009,122139],{}," computed field",[1362,140012,140013,140016,140017],{},[950,140014,140015],{},"refused",", and the error says ",[941,140018,140019],{},"VIRTUAL",[1344,140021,140022,140025],{},[1362,140023,140024],{},"an unknown name, where the field set is enumerable",[1362,140026,140027],{},"refused as a typo",[1344,140029,140030,140033],{},[1362,140031,140032],{},"anything, on a TS-generic collection with no readable field list",[1362,140034,140035],{},"unguarded, by design",[937,140037,140038,140039,140041,140042,1952,140044,140047,140048,140051],{},"A virtual field is evaluated on read and never stored, so a matcher pointed at\none reads every candidate row and matches nothing, permanently — the exact\nfailure the guard exists to prevent, reached ",[1092,140040,1409],{}," it. It is refused rather\nthan supported because matching a virtual field turns an indexed narrow into a\nfull scan; use ",[941,140043,124059],{},[950,140045,140046],{},"The virtual check runs even when the\nfield list is unreadable",", unlike the typo check beside it: ",[1092,140049,140050],{},"\"declared but\nnever stored\""," is provable from the declaration alone.",[1591,140053,140055],{"id":140054},"withrollupoptions",[941,140056,140057],{},"withRollup(options)",[937,140059,140060,140063,140064,140067],{},[941,140061,140062],{},"withRollup"," is the inverse of a join: instead of reading children on\ndemand, the parent carries a ",[950,140065,140066],{},"maintained summary"," folded from its\nchildren by foreign key. It recomputes on child insert, update, AND\ndelete — gap-free.",[2217,140069,140071],{"className":2219,"code":140070,"language":2221,"meta":1037,"style":1037},"import { withRollup } from '@noy-db\u002Fhub\u002Fderivations'\n\nwithRollup\u003CSale, Buyer>({\n  from: 'sales',          \u002F\u002F child collection (the trigger)\n  key: 'buyerId',         \u002F\u002F FK on the child → parent id\n  into: 'buyers',         \u002F\u002F parent collection\n  field: 'revenueByYear', \u002F\u002F field on the parent to maintain\n  compute: (sales) => groupSumByYear(sales, 'total'),\n})\n",[941,140072,140073,140092,140096,140115,140132,140149,140167,140186,140219],{"__ignoreMap":1037},[2225,140074,140075,140077,140079,140082,140084,140086,140088,140090],{"class":2227,"line":2228},[2225,140076,2232],{"class":2231},[2225,140078,2236],{"class":2235},[2225,140080,140081],{"class":2239}," withRollup",[2225,140083,2243],{"class":2235},[2225,140085,2246],{"class":2231},[2225,140087,2249],{"class":2235},[2225,140089,73041],{"class":2252},[2225,140091,2256],{"class":2235},[2225,140093,140094],{"class":2227,"line":1041},[2225,140095,2285],{"emptyLinePlaceholder":2284},[2225,140097,140098,140100,140102,140104,140106,140109,140111,140113],{"class":2227,"line":1038},[2225,140099,140062],{"class":2291},[2225,140101,6474],{"class":2235},[2225,140103,139184],{"class":6342},[2225,140105,2298],{"class":2235},[2225,140107,140108],{"class":6342}," Buyer",[2225,140110,6480],{"class":2235},[2225,140112,2789],{"class":2239},[2225,140114,4200],{"class":2235},[2225,140116,140117,140119,140121,140123,140125,140127,140129],{"class":2227,"line":2288},[2225,140118,78520],{"class":2307},[2225,140120,2311],{"class":2235},[2225,140122,2249],{"class":2235},[2225,140124,123562],{"class":2252},[2225,140126,2805],{"class":2235},[2225,140128,2298],{"class":2235},[2225,140130,140131],{"class":2763},"          \u002F\u002F child collection (the trigger)\n",[2225,140133,140134,140136,140138,140140,140142,140144,140146],{"class":2227,"line":2304},[2225,140135,69281],{"class":2307},[2225,140137,2311],{"class":2235},[2225,140139,2249],{"class":2235},[2225,140141,109007],{"class":2252},[2225,140143,2805],{"class":2235},[2225,140145,2298],{"class":2235},[2225,140147,140148],{"class":2763},"         \u002F\u002F FK on the child → parent id\n",[2225,140150,140151,140154,140156,140158,140160,140162,140164],{"class":2227,"line":2356},[2225,140152,140153],{"class":2307},"  into",[2225,140155,2311],{"class":2235},[2225,140157,2249],{"class":2235},[2225,140159,139251],{"class":2252},[2225,140161,2805],{"class":2235},[2225,140163,2298],{"class":2235},[2225,140165,140166],{"class":2763},"         \u002F\u002F parent collection\n",[2225,140168,140169,140172,140174,140176,140179,140181,140183],{"class":2227,"line":2830},[2225,140170,140171],{"class":2307},"  field",[2225,140173,2311],{"class":2235},[2225,140175,2249],{"class":2235},[2225,140177,140178],{"class":2252},"revenueByYear",[2225,140180,2805],{"class":2235},[2225,140182,2298],{"class":2235},[2225,140184,140185],{"class":2763}," \u002F\u002F field on the parent to maintain\n",[2225,140187,140188,140191,140193,140195,140197,140199,140201,140204,140207,140209,140211,140213,140215,140217],{"class":2227,"line":2861},[2225,140189,140190],{"class":2291},"  compute",[2225,140192,2311],{"class":2235},[2225,140194,953],{"class":2235},[2225,140196,123562],{"class":7052},[2225,140198,1381],{"class":2235},[2225,140200,8116],{"class":2774},[2225,140202,140203],{"class":2291}," groupSumByYear",[2225,140205,140206],{"class":2239},"(sales",[2225,140208,2298],{"class":2235},[2225,140210,2249],{"class":2235},[2225,140212,64200],{"class":2252},[2225,140214,2805],{"class":2235},[2225,140216,1381],{"class":2239},[2225,140218,1707],{"class":2235},[2225,140220,140221,140223],{"class":2227,"line":4246},[2225,140222,2359],{"class":2235},[2225,140224,2362],{"class":2239},[1111,140226,140227,140248,140263,140268,140277],{},[1114,140228,14704,140229,140231,140232,140235,140236,140239,140240,140243,140244,140247],{},[941,140230,2272],{}," write\u002Fdelete the parent at id ",[941,140233,140234],{},"child[key]"," is recomputed:\n",[941,140237,140238],{},"compute(children where child[key] === parentId)"," is patched onto\n",[941,140241,140242],{},"parent[field]",". A parent write also recomputes its own aggregate, so a\n",[950,140245,140246],{},"parent created after its children"," still fills in.",[1114,140249,140250,9515,140253,140255,140256,140258,140259,140262],{},[950,140251,140252],{},"A child that RE-PARENTS recomputes both parents",[941,140254,39039],{},"\n(noy-db#1257). Moving a child from A to B previously recomputed only B and\nleft A's aggregate holding a number that was correct before the move — a\nstale total reads as data rather than as an error, so nothing prompts anyone\nto re-check it. ⚠️ Same qualification as the ",[941,140257,137926],{}," fan-out: ",[950,140260,140261],{},"a write\nthat does not thread a prior record — a sync-applied wave write, a tiers\nrestore — degrades to new-parent-only"," rather than guessing at the old key.",[1114,140264,3325,140265,140267],{},[941,140266,25197],{}," is patched onto the parent's raw record (other fields and\ni18n maps untouched); a value-equality guard suppresses no-op writes.",[1114,140269,140270,140271,140273,140274,140276],{},"The aggregate gathers children through the FK index when ",[941,140272,2272],{}," is\nindexed on ",[941,140275,8364],{},", else a scan.",[1114,140278,140279,140280,140283],{},"Eager-only in this slice. Desugars to a derivation carrying a ",[941,140281,140282],{},"rollup","\nmarker; dispatch handles it without the executor.",[1061,140285,140286],{},[937,140287,140288,140291,140292,140295,140296,140299,140300,140302,140303,140305,140306,140308],{},[950,140289,140290],{},"Why not a materialized view?"," An MV aggregates children into a\n",[1092,140293,140294],{},"parallel"," collection (",[941,140297,140298],{},"buyer-totals.get(id)","); it cannot write the\naggregate onto a field of the parent ",[941,140301,139251],{}," record. ",[941,140304,140062],{}," does.\n",[941,140307,2546],{}," does not auto-re-derive (it bypasses the put path) —\nrecompute explicitly if a forgotten record must drop out of an aggregate.",[1591,140310,140311],{"id":2084},[941,140312,2084],{},[937,140314,140315,140316,140319,140320,57794,140322,140324],{},"Declare an output as ",[941,140317,140318],{},"optional: true"," to let ",[941,140321,14092],{},[941,140323,6651],{}," for\nthat key:",[2217,140326,140328],{"className":2219,"code":140327,"language":2221,"meta":1037,"style":1037},"outputs: {\n  receipt: { shape: 'record', collection: 'receipts', optional: true },\n},\nderive: (alloc) => ({\n  receipt: alloc.servicesNetPortion > 0\n    ? { id: alloc.id, paymentId: alloc.paymentId, appliedAmount: alloc.appliedAmount }\n    : null,\n})\n",[941,140329,140330,140339,140381,140385,140403,140420,140465,140471],{"__ignoreMap":1037},[2225,140331,140332,140335,140337],{"class":2227,"line":2228},[2225,140333,140334],{"class":6342},"outputs",[2225,140336,2311],{"class":2235},[2225,140338,2301],{"class":2235},[2225,140340,140341,140344,140346,140348,140350,140352,140354,140356,140358,140360,140362,140364,140366,140368,140370,140372,140375,140377,140379],{"class":2227,"line":1041},[2225,140342,140343],{"class":6342},"  receipt",[2225,140345,2311],{"class":2235},[2225,140347,2236],{"class":2235},[2225,140349,138087],{"class":6342},[2225,140351,2311],{"class":2235},[2225,140353,2249],{"class":2235},[2225,140355,24158],{"class":2252},[2225,140357,2805],{"class":2235},[2225,140359,2298],{"class":2235},[2225,140361,7061],{"class":6342},[2225,140363,2311],{"class":2235},[2225,140365,2249],{"class":2235},[2225,140367,27844],{"class":2252},[2225,140369,2805],{"class":2235},[2225,140371,2298],{"class":2235},[2225,140373,140374],{"class":6342}," optional",[2225,140376,2311],{"class":2235},[2225,140378,2322],{"class":2321},[2225,140380,2353],{"class":2235},[2225,140382,140383],{"class":2227,"line":1038},[2225,140384,19008],{"class":2235},[2225,140386,140387,140389,140391,140393,140395,140397,140399,140401],{"class":2227,"line":2288},[2225,140388,14092],{"class":6342},[2225,140390,2311],{"class":2235},[2225,140392,953],{"class":2235},[2225,140394,138687],{"class":7052},[2225,140396,1381],{"class":2235},[2225,140398,8116],{"class":2774},[2225,140400,953],{"class":2239},[2225,140402,4200],{"class":2235},[2225,140404,140405,140407,140409,140411,140413,140416,140418],{"class":2227,"line":2304},[2225,140406,140343],{"class":2307},[2225,140408,2311],{"class":2235},[2225,140410,138830],{"class":2239},[2225,140412,1520],{"class":2235},[2225,140414,140415],{"class":2239},"servicesNetPortion ",[2225,140417,6480],{"class":2235},[2225,140419,74216],{"class":6527},[2225,140421,140422,140424,140426,140428,140430,140432,140434,140436,140438,140441,140443,140445,140447,140449,140451,140454,140456,140458,140460,140463],{"class":2227,"line":2356},[2225,140423,48355],{"class":2235},[2225,140425,2236],{"class":2235},[2225,140427,7070],{"class":2307},[2225,140429,2311],{"class":2235},[2225,140431,138830],{"class":2239},[2225,140433,1520],{"class":2235},[2225,140435,6983],{"class":2239},[2225,140437,2298],{"class":2235},[2225,140439,140440],{"class":2307}," paymentId",[2225,140442,2311],{"class":2235},[2225,140444,138830],{"class":2239},[2225,140446,1520],{"class":2235},[2225,140448,138749],{"class":2239},[2225,140450,2298],{"class":2235},[2225,140452,140453],{"class":2307}," appliedAmount",[2225,140455,2311],{"class":2235},[2225,140457,138830],{"class":2239},[2225,140459,1520],{"class":2235},[2225,140461,140462],{"class":2239},"appliedAmount ",[2225,140464,7351],{"class":2235},[2225,140466,140467,140469],{"class":2227,"line":2830},[2225,140468,48364],{"class":2235},[2225,140470,67699],{"class":2235},[2225,140472,140473,140475],{"class":2227,"line":2861},[2225,140474,2359],{"class":2235},[2225,140476,2362],{"class":2239},[937,140478,140479],{},"Semantics:",[1111,140481,140482,140489,140496,140499],{},[1114,140483,140484,36059,140486,140488],{},[941,140485,6651],{},[941,140487,18316],{},") for an optional output → no write fires.",[1114,140490,140491,140492,140495],{},"If a previous derivation emitted an output at this id, it's ",[950,140493,140494],{},"deleted","\n(a tombstone for derived data). The eager path captures the prior\nenvelope on the active transaction context so the delete rolls back\nalongside the source op on transaction failure.",[1114,140497,140498],{},"A never-emitted optional output is a silent no-op.",[1114,140500,140501,140502,140504,140505,140507,140508,1520],{},"Returning ",[941,140503,6651],{}," for a required output (default — no ",[941,140506,2084],{}," flag)\nstill throws ",[941,140509,140510],{},"DerivationOutputShapeError",[937,140512,140513,140514,1520],{},"Same behavior on eager and lazy lifecycles, and through ",[941,140515,22116],{},[24204,140517,140519],{"id":140518},"tombstone-vs-ondelete-composition","Tombstone-vs-onDelete composition",[937,140521,140522,140523,140526,140527,140529,140530,140532,140533,140535,140536,140539],{},"A tombstone is a ",[950,140524,140525],{},"system-internal"," delete: the derivation engine\nrevoking its own prior emission because the source flipped to the\n\"no output\" branch. User ",[941,140528,137880],{}," guards registered on the output\ncollection are ",[950,140531,1004],{}," consulted on tombstones — the same way amendments\nbypass user-facing hooks. If they fired, a consumer registering both\n(a) an ",[941,140534,140318],{}," derivation and (b) an ",[941,140537,140538],{},"onDelete: () => throw","\non the output collection would deadlock: every flip-to-null source\nwrite would block on the user's own append-only rule.",[937,140541,140542,140543,140546,140547,140549,140550,140553,140554,140557],{},"Concretely: if you ship a ",[941,140544,140545],{},"paymentAllocation → receipt"," derivation with\n",[941,140548,140318],{},", AND ",[941,140551,140552],{},"receipts.onDelete: throw"," for legal-document\nimmutability, ",[950,140555,140556],{},"the tombstone bypass keeps both coherent",". Users can\nno longer manually delete a receipt (good), and the system can still\nrevoke its own prior emission when the underlying allocation\nrestructures (also good).",[937,140559,140560,140561,140564,140565,140568,140569,140571],{},"The bypass is scoped to the system-internal delete path\n(",[941,140562,140563],{},"Collection._internalDelete","); a user-initiated ",[941,140566,140567],{},"Collection.delete","\non the same record still fires ",[941,140570,137880],{}," normally.",[1591,140573,140574],{"id":6303},[941,140575,6303],{},[1111,140577,140578,140588],{},[1114,140579,140580,140584,140585,140587],{},[950,140581,140582],{},[941,140583,138257],{}," — derive runs synchronously inside the source-write transaction.\nOutputs are written via the same ",[941,140586,15393],{}," pipeline. Recommended for\nsmall, fast derive functions.",[1114,140589,140590,140594],{},[950,140591,140592],{},[941,140593,719],{}," — source-write marks output ids stale; first read of any output\ntriggers the derive. Recommended when the derive is expensive and most\nsources are written without being read.",[1591,140596,140598],{"id":140597},"strict",[941,140599,140597],{},[937,140601,140602,140603,140606,140607,1520],{},"Default (",[941,140604,140605],{},"strict: false","): per-output failures are isolated. Other outputs\ncommit; the failed output is absent from the store and re-attempts on next\n",[941,140608,137906],{},[7646,140610,140612,140641],{"color":7648,"icon":140611},"i-lucide-refresh-cw",[937,140613,140614,140620,140621,5584,140623,140626,140627,140630,140631,140633,140634,42456,140637,140640],{},[950,140615,140616,140617,140619],{},"On the ",[941,140618,719],{}," lifecycle, a failed non-strict re-derive now keeps the record\nstale"," (noy-db#1258, ",[941,140622,39039],{},[941,140624,140625],{},"resolveStaleOnRead"," consumes the pending\nflag before reading the source — a recursion guard for self-write outputs — and\na non-strict failure used to leave it consumed, so the record was served as\nfresh and ",[950,140628,140629],{},"never retried, permanently",", because nothing would mark it stale\nagain. ",[941,140632,140605],{}," means ",[1092,140635,140636],{},"\"a failed derivation must not break the read\"",[1092,140638,140639],{},"\"report this output as current\"",": the record is still served and the\nwarning still fires, but the flag survives and the next read retries.",[937,140642,140643,140644,140646],{},"The stated cost: a permanently-failing non-strict derivation now re-runs on\n",[950,140645,2119],{}," read of that id rather than once. Louder and more expensive than\nserving a stale value forever, and deliberately so.",[937,140648,140649,140651,140652,140658],{},[941,140650,139953],{},": a single output failure rolls back the source write (only\nwhen wrapped in ",[1007,140653,140655],{"href":140654},".\u002Ftransactions",[941,140656,140657],{},"withTransactions","). Use for outputs\nthat must remain consistent with the source.",[1591,140660,140662],{"id":140661},"vaultderiveallcollection",[941,140663,140664],{},"vault.deriveAll(collection)",[937,140666,140667,140668,140671],{},"Re-derive every record in the named source collection. Useful after a strategy\nchange (the strategy hash mismatch forces a recompute on next visit, but\n",[941,140669,140670],{},"deriveAll"," is the explicit bulk path):",[2217,140673,140675],{"className":2219,"code":140674,"language":2221,"meta":1037,"style":1037},"const { derived, failed } = await vault.deriveAll('pdfs')\n\u002F\u002F → { derived: 42, failed: 0 }\n",[941,140676,140677,140713],{"__ignoreMap":1037},[2225,140678,140679,140681,140683,140686,140688,140691,140693,140695,140697,140699,140701,140703,140705,140707,140709,140711],{"class":2227,"line":2228},[2225,140680,2775],{"class":2774},[2225,140682,2236],{"class":2235},[2225,140684,140685],{"class":2239}," derived",[2225,140687,2298],{"class":2235},[2225,140689,140690],{"class":2239}," failed ",[2225,140692,2359],{"class":2235},[2225,140694,6346],{"class":2235},[2225,140696,2784],{"class":2231},[2225,140698,6466],{"class":2239},[2225,140700,1520],{"class":2235},[2225,140702,140670],{"class":2291},[2225,140704,2789],{"class":2239},[2225,140706,2805],{"class":2235},[2225,140708,138052],{"class":2252},[2225,140710,2805],{"class":2235},[2225,140712,2362],{"class":2239},[2225,140714,140715],{"class":2227,"line":1041},[2225,140716,140717],{"class":2763},"\u002F\u002F → { derived: 42, failed: 0 }\n",[1591,140719,140721],{"id":140720},"_derivedfrom",[941,140722,139505],{},[937,140724,140725],{},"Every derived record carries:",[2217,140727,140729],{"className":6604,"code":140728,"language":141,"meta":1037,"style":1037},"{\n  \"_derivedFrom\": {\n    \"source\": \"pdfs\",\n    \"sourceId\": \"abc\",\n    \"sourceVersion\": 3,\n    \"derivedAt\": \"2026-05-18T00:00:00.000Z\",\n    \"strategyHash\": \"sha256-...\"\n  }\n}\n",[941,140730,140731,140735,140747,140765,140785,140800,140820,140838,140842],{"__ignoreMap":1037},[2225,140732,140733],{"class":2227,"line":2228},[2225,140734,4200],{"class":2235},[2225,140736,140737,140739,140741,140743,140745],{"class":2227,"line":1041},[2225,140738,9368],{"class":2235},[2225,140740,139505],{"class":2774},[2225,140742,6620],{"class":2235},[2225,140744,2311],{"class":2235},[2225,140746,2301],{"class":2235},[2225,140748,140749,140751,140753,140755,140757,140759,140761,140763],{"class":2227,"line":1038},[2225,140750,14201],{"class":2235},[2225,140752,22723],{"class":6342},[2225,140754,6620],{"class":2235},[2225,140756,2311],{"class":2235},[2225,140758,6614],{"class":2235},[2225,140760,138052],{"class":2252},[2225,140762,6620],{"class":2235},[2225,140764,1707],{"class":2235},[2225,140766,140767,140769,140772,140774,140776,140778,140781,140783],{"class":2227,"line":2288},[2225,140768,14201],{"class":2235},[2225,140770,140771],{"class":6342},"sourceId",[2225,140773,6620],{"class":2235},[2225,140775,2311],{"class":2235},[2225,140777,6614],{"class":2235},[2225,140779,140780],{"class":2252},"abc",[2225,140782,6620],{"class":2235},[2225,140784,1707],{"class":2235},[2225,140786,140787,140789,140792,140794,140796,140798],{"class":2227,"line":2304},[2225,140788,14201],{"class":2235},[2225,140790,140791],{"class":6342},"sourceVersion",[2225,140793,6620],{"class":2235},[2225,140795,2311],{"class":2235},[2225,140797,14460],{"class":6527},[2225,140799,1707],{"class":2235},[2225,140801,140802,140804,140807,140809,140811,140813,140816,140818],{"class":2227,"line":2356},[2225,140803,14201],{"class":2235},[2225,140805,140806],{"class":6342},"derivedAt",[2225,140808,6620],{"class":2235},[2225,140810,2311],{"class":2235},[2225,140812,6614],{"class":2235},[2225,140814,140815],{"class":2252},"2026-05-18T00:00:00.000Z",[2225,140817,6620],{"class":2235},[2225,140819,1707],{"class":2235},[2225,140821,140822,140824,140827,140829,140831,140833,140836],{"class":2227,"line":2830},[2225,140823,14201],{"class":2235},[2225,140825,140826],{"class":6342},"strategyHash",[2225,140828,6620],{"class":2235},[2225,140830,2311],{"class":2235},[2225,140832,6614],{"class":2235},[2225,140834,140835],{"class":2252},"sha256-...",[2225,140837,9449],{"class":2235},[2225,140839,140840],{"class":2227,"line":2861},[2225,140841,7632],{"class":2235},[2225,140843,140844],{"class":2227,"line":4246},[2225,140845,7351],{"class":2235},[937,140847,140848,140850,140851,140853,140854,1952,140856,140858,140859,140861,140862,140864],{},[941,140849,140826],{}," (a hash of the source name, the output keys, and the ",[941,140852,14092],{},"\nfunction's own source) is the mechanism for detecting strategy drift: a\nrecord whose hash doesn't match the current strategy is recomputed by\n",[941,140855,137906],{},[941,140857,140791],{}," is stamped ",[941,140860,5402],{}," on records produced by\n",[941,140863,22116],{}," or by lazy resolution — the source record's real version\nisn't threaded through those two paths yet.",[1591,140866,140868],{"id":140867},"composition-order","Composition order",[937,140870,140871,140872,140874],{},"Derivations fire ",[950,140873,10761],{}," the store write and ledger append. A guard that\nblocks a source write also blocks the derivation that would have fired from\nit.",[2217,140876,140879],{"className":140877,"code":140878,"language":5672},[5670],"Collection.put\n  1. Permission check\n  2. GuardRegistry.check\n  3. Encrypt + store.put\n  4. Ledger append\n  5. DerivationRegistry           ← this doc\n",[941,140880,140878],{"__ignoreMap":1037},[1591,140882,120265],{"id":120264},[937,140884,140885,140886,2311],{},"All extend ",[941,140887,140888],{},"NoydbError",[1111,140890,140891,140897,140906,140912,140918],{},[1114,140892,140893,140896],{},[941,140894,140895],{},"DerivationCycleError(path[])"," — graph contains a cycle",[1114,140898,140899,140902,140903],{},[941,140900,140901],{},"DerivationDepthError(limit, attempted)"," — cascade exceeded ",[941,140904,140905],{},"maxDepth",[1114,140907,140908,140911],{},[941,140909,140910],{},"DerivationOutputUnknownError(collection)"," — output collection unknown",[1114,140913,140914,140917],{},[941,140915,140916],{},"DerivationOutputShapeError(outputKey, detail)"," — derive returned wrong shape",[1114,140919,140920,12536,140923,140925,140926],{},[941,140921,140922],{},"DerivationCapExceededError(key, count, max)",[941,140924,137926],{}," fan-out or\narray-shape output exceeded ",[941,140927,139532],{},[1022,140929,101425],{"id":101424},[1111,140931,140932,140938,140947],{},[1114,140933,140934,140937],{},[941,140935,140936],{},"createNoydb({ derivationStrategies: [...] })"," is the only way to register\nderivations; without it, no derive function fires",[1114,140939,140940,140942,140943,140946],{},[941,140941,140664],{}," is a no-op — it returns ",[941,140944,140945],{},"{ derived: 0, failed: 0 }","\nrather than throwing, since there is nothing registered to run",[1114,140948,140949,1261,140952,2601,140955,1261,140958,2601,140960,140962,140963,140965],{},[941,140950,140951],{},"DerivationCycleError",[941,140953,140954],{},"DerivationDepthError",[941,140956,140957],{},"DerivationOutputUnknownError",[941,140959,140510],{},[941,140961,139536],{}," are still importable from\n",[941,140964,73041],{}," but are never thrown by core",[1022,140967,114684],{"id":114683},[1111,140969,140970,140975,140985],{},[1114,140971,140972,140974],{},[1007,140973,693],{"href":124814}," — guards run before encryption; a blocked source write\nshort-circuits the derivation that would have fired",[1114,140976,140977,964,140979,140981,140982,140984],{},[1007,140978,876],{"href":140654},[941,140980,139953],{}," derivations require\n",[941,140983,140657],{}," so output failures can roll back the source write",[1114,140986,140987,140990,140991,140993],{},[1007,140988,697],{"href":140989},".\u002Fhistory"," — every committed derivation output fires its own\nledger entry through the standard ",[941,140992,15393],{}," pipeline",[1022,140995,6825],{"id":6824},[1591,140997,140999],{"id":140998},"zero-knowledge-guarantee","Zero-knowledge guarantee",[937,141001,141002,141003,141005],{},"The derive function executes after DEK unwrap, on plaintext. The store never\nsees plaintext. Outputs are encrypted with the same DEK as the source before\nthey reach the store. ",[941,141004,139505],{}," metadata lives inside the encrypted\npayload, not in the plaintext envelope.",[1591,141007,141009],{"id":141008},"cycle-detection","Cycle detection",[937,141011,141012,141013,132149,141016,141018],{},"Cyclic graphs (A → B → A, self-loops, etc.) are rejected when a vault opens\n(",[941,141014,141015],{},"db.openVault",[941,141017,140951],{},". The graph is the union of\nevery strategy's source + output collection set.",[1591,141020,141022],{"id":141021},"behavior-notes","Behavior notes",[1111,141024,141025,141037,141052],{},[1114,141026,141027,141030,141031,141033,141034,141036],{},[950,141028,141029],{},"Lazy stale-tracking is in-memory only."," A vault close-and-reopen\nloses pending stale flags. Derived records with a matching ",[941,141032,140826],{},"\nare treated as fresh; ",[941,141035,22116],{}," is the explicit recompute path\nafter strategy changes.",[1114,141038,141039,1324,141045,141047,141048,141051],{},[950,141040,141041,141044],{},[941,141042,141043],{},"sourceVersion: 0"," on bulk recomputes.",[941,141046,22116],{}," and the\nlazy-resolution path stamp ",[941,141049,141050],{},"_derivedFrom.sourceVersion = 0"," because the\nenvelope version isn't threaded through those two paths.",[1114,141053,141054,141057,141058,141060,141061,141063,141064,141066],{},[950,141055,141056],{},"Recursion guard."," The dispatch hook in ",[941,141059,15393],{}," skips when the\nincoming record carries a ",[941,141062,139505],{}," field — a defense-in-depth check\nalongside the ",[941,141065,2847],{}," cycle validator.",[1591,141068,141070],{"id":141069},"not-yet-built","Not yet built",[937,141072,141073,141074,1261,141076,141078],{},"The following are outside what ",[941,141075,137892],{},[941,141077,140062],{}," do today —\nthere is no API surface for them yet:",[1111,141080,141081,141084,141089,141097,141104,141107,141110,141113,141116],{},[1114,141082,141083],{},"Cache-tier storage backends as a derivation target",[1114,141085,141086,141087,40856],{},"Built-in derivers (PDF text extraction, image processing, etc.) — you\nbring your own ",[941,141088,14092],{},[1114,141090,141091,141092,997,141094,141096],{},"Scheduled \u002F cron-style refresh (only ",[941,141093,138257],{},[941,141095,719],{}," exist)",[1114,141098,141099,141100,141103],{},"Non-deterministic derivations with persistence — ",[941,141101,141102],{},"deterministic: true","\nis required",[1114,141105,141106],{},"External or sandboxed derivation runtimes",[1114,141108,141109],{},"Publishing a derivation's output to a public CDN",[1114,141111,141112],{},"Streaming materialized views for continuous\u002Funbounded result sets",[1114,141114,141115],{},"Persisting the lazy stale-flag map across a vault close",[1114,141117,141118],{},"Threading the real per-record source version through bulk recompute paths",[1022,141120,2621],{"id":2620},[1111,141122,141123,141128,141133,141138,141146,141151,141159],{},[1114,141124,141125,141127],{},[1007,141126,739],{"href":124624}," — persist an entire query as a live, auto-refreshed collection (sibling derived-data service)",[1114,141129,141130,141132],{},[1007,141131,768],{"href":137874}," — operator row-level overrides on a source or MV (sibling derived-data service)",[1114,141134,141135,141137],{},[1007,141136,693],{"href":124814}," — write-time rules; system-internal deletes from derivations bypass them",[1114,141139,141140,141142,141143,141145],{},[1007,141141,876],{"href":140654}," — required for ",[941,141144,139953],{}," rollback on derivations",[1114,141147,141148,141150],{},[1007,141149,697],{"href":140989}," — every derived write still appends its own ledger entry",[1114,141152,141153,141154],{},"Package tests plus showcase ",[1007,141155,141157],{"href":93534,"rel":141156},[3371],[941,141158,93538],{},[1114,141160,141161,141162,997,141168,1520],{},"Proven by showcases ",[1007,141163,141165],{"href":93534,"rel":141164},[3371],[941,141166,141167],{},"80-with-derivation",[1007,141169,141171],{"href":93942,"rel":141170},[3371],[941,141172,141173],{},"104-reverse-denorm",[2645,141175,141176],{},"html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .sfNiH, html code.shiki .sfNiH{--shiki-light:#FF5370;--shiki-default:#FF9CAC;--shiki-dark:#FF9CAC}html pre.shiki code .sHdIc, html code.shiki .sHdIc{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#EEFFFF;--shiki-default-font-style:italic;--shiki-dark:#BABED8;--shiki-dark-font-style:italic}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}",{"title":1037,"searchDepth":1038,"depth":1038,"links":141178},[141179,141180,141181,141182,141183,141206,141207,141208,141214],{"id":29856,"depth":1041,"text":29857},{"id":70092,"depth":1041,"text":70093},{"id":99863,"depth":1041,"text":99864},{"id":138337,"depth":1041,"text":138338},{"id":41259,"depth":1041,"text":101012,"children":141184},[141185,141187,141188,141196,141197,141200,141201,141202,141203,141204,141205],{"id":138562,"depth":1038,"text":141186},"derive(source, ctx) and the read-only vault facade",{"id":138958,"depth":1038,"text":138958},{"id":139153,"depth":1038,"text":139154,"children":141189},[141190,141192,141194,141195],{"id":139553,"depth":2288,"text":141191},"Composite matches — match",{"id":139713,"depth":2288,"text":141193},"One declared hop — via",{"id":139910,"depth":2288,"text":139911},{"id":139963,"depth":2288,"text":139964},{"id":140054,"depth":1038,"text":140057},{"id":2084,"depth":1038,"text":2084,"children":141198},[141199],{"id":140518,"depth":2288,"text":140519},{"id":6303,"depth":1038,"text":6303},{"id":140597,"depth":1038,"text":140597},{"id":140661,"depth":1038,"text":140664},{"id":140720,"depth":1038,"text":139505},{"id":140867,"depth":1038,"text":140868},{"id":120264,"depth":1038,"text":120265},{"id":101424,"depth":1041,"text":101425},{"id":114683,"depth":1041,"text":114684},{"id":6824,"depth":1041,"text":6825,"children":141209},[141210,141211,141212,141213],{"id":140998,"depth":1038,"text":140999},{"id":141008,"depth":1038,"text":141009},{"id":141021,"depth":1038,"text":141022},{"id":141069,"depth":1038,"text":141070},{"id":2620,"depth":1041,"text":2621},"Deterministic derived data: withDerivation() projects one source record into typed outputs, encrypted under the source's key, with cycle detection at open.",{},{"title":665,"description":141215},"CQ2PAo5iyLwvtQtvutzf_WYwOasI3mRgfeYi0ME8qjU",{"id":141220,"title":669,"access":12,"apiToc":929,"body":141221,"category":1042,"description":142673,"extension":1044,"meta":142674,"navigation":1042,"nest":1047,"path":670,"seo":142675,"sinceVersion":1042,"status":1049,"stem":671,"subtitle":1042,"tier":1050,"tocExternal":1042,"__hash__":142676},"docs\u002Fservices\u002Fdirectory.md",{"type":931,"value":141222,"toc":142655},[141223,141225,141228,141230,141252,141258,141260,141297,141594,141596,141600,141606,141764,141768,141771,141825,141829,141838,141854,142067,142088,142094,142096,142098,142147,142151,142265,142269,142414,142416,142461,142466,142475,142521,142523,142612,142614,142652],[934,141224,669],{"id":669},[46871,141226],{":rows":141227},"[{\"label\":\"Subpath\",\"value\":\"`@noy-db\u002Fhub` (root export)\"},{\"label\":\"Type\",\"value\":\"Core feature — `listUsers` and `listUsersWithEnvelopes` are always available.\"}]",[1022,141229,29857],{"id":29856},[937,141231,141232,141233,141236,141237,141239,141240,23423,141242,141245,141246,141248,141249,141251],{},"Any multi-user vault eventually needs a \"who's on this team\" screen. Directory is the\npair of visibility flags that shape that listing: a vault-level toggle that turns\nenumeration off entirely for non-admins, and a per-user flag that lets an individual\nopt out of showing up in it. Both are checked by ",[941,141234,141235],{},"listUsersWithEnvelopes",", the\njoin helper from the ",[1007,141238,868],{"href":869}," collaboration family that combines each\n",[950,141241,2705],{},[1007,141243,141244],{"href":54},"role-carrying access record",") with\nits decrypted ",[950,141247,97731],{}," (the per-principal profile record described in\n",[1007,141250,884],{"href":885},") — documented in full below, since it is\nwhere both directory flags are enforced.",[937,141253,141254,141255,141257],{},"Neither flag is a security boundary. Both are plain JSON sidecars stored next to the\nvault's other metadata — see ",[1007,141256,68210],{"href":126812}," — so treat them as\nadmin-UI polish, not as a way to hide a principal's existence from someone with direct\nstore access.",[1022,141259,58394],{"id":58393},[937,141261,141262,141263,141265,141266,141268,141269,1625,141272,141274,141275,953,141277,1625,141280,141283,141284,4688,141287,141290,141291,141294,141295,2311],{},"There is nothing to opt into — both flags are part of the always-on core, no ",[941,141264,943],{},"\nstrategy required. The directory toggle is reachable from any ",[941,141267,62],{}," instance\n(",[941,141270,141271],{},"db.getDirectoryEnabled",[941,141273,16868],{},"); the per-user flag is reachable from\nany open ",[941,141276,94],{},[941,141278,141279],{},"vault.user.getMyVisibility",[941,141281,141282],{},"setMyVisibility",") — there is no\n",[941,141285,141286],{},"vault.setDirectoryEnabled",[941,141288,141289],{},"db.user.setMyVisibility"," shortcut on the other object.\nEnumerating ",[1092,141292,141293],{},"more than one"," principal does need a second user, which requires opting\ninto multi-user grants with ",[941,141296,3197],{},[2217,141298,141300],{"className":2219,"code":141299,"language":2221,"meta":1037,"style":1037},"import { createNoydb } from '@noy-db\u002Fhub'\nimport { withTeam } from '@noy-db\u002Fhub\u002Fteam'\nimport { toMemory } from '@noy-db\u002Fto-memory'\n\nconst store = toMemory() \u002F\u002F shared by every session below; swap for to-file \u002F to-postgres in production\n\nconst owner = await createNoydb({\n  store,\n  teamStrategy: withTeam(),\n  user: 'alice',\n  secret: 'alice-pass-2026-strong',\n})\nconst ownerVault = await owner.openVault('demo')\n\nawait owner.grant('demo', {\n  userId: 'bob',\n  displayName: 'Bob',\n  role: 'viewer',\n  secret: 'bob-pass-2026-strong',\n})\n\nawait owner.getDirectoryEnabled('demo')\n\u002F\u002F → true — enabled by default, no _meta\u002Fdirectory document required\n",[941,141301,141302,141320,141338,141356,141360,141375,141379,141396,141402,141414,141428,141443,141449,141477,141481,141503,141517,141531,141545,141559,141565,141569,141589],{"__ignoreMap":1037},[2225,141303,141304,141306,141308,141310,141312,141314,141316,141318],{"class":2227,"line":2228},[2225,141305,2232],{"class":2231},[2225,141307,2236],{"class":2235},[2225,141309,2744],{"class":2239},[2225,141311,2243],{"class":2235},[2225,141313,2246],{"class":2231},[2225,141315,2249],{"class":2235},[2225,141317,1086],{"class":2252},[2225,141319,2256],{"class":2235},[2225,141321,141322,141324,141326,141328,141330,141332,141334,141336],{"class":2227,"line":1041},[2225,141323,2232],{"class":2231},[2225,141325,2236],{"class":2235},[2225,141327,10373],{"class":2239},[2225,141329,2243],{"class":2235},[2225,141331,2246],{"class":2231},[2225,141333,2249],{"class":2235},[2225,141335,4680],{"class":2252},[2225,141337,2256],{"class":2235},[2225,141339,141340,141342,141344,141346,141348,141350,141352,141354],{"class":2227,"line":1038},[2225,141341,2232],{"class":2231},[2225,141343,2236],{"class":2235},[2225,141345,68576],{"class":2239},[2225,141347,2243],{"class":2235},[2225,141349,2246],{"class":2231},[2225,141351,2249],{"class":2235},[2225,141353,9638],{"class":2252},[2225,141355,2256],{"class":2235},[2225,141357,141358],{"class":2227,"line":2288},[2225,141359,2285],{"emptyLinePlaceholder":2284},[2225,141361,141362,141364,141366,141368,141370,141372],{"class":2227,"line":2304},[2225,141363,2775],{"class":2774},[2225,141365,39676],{"class":2239},[2225,141367,2781],{"class":2235},[2225,141369,68576],{"class":2291},[2225,141371,19132],{"class":2239},[2225,141373,141374],{"class":2763},"\u002F\u002F shared by every session below; swap for to-file \u002F to-postgres in production\n",[2225,141376,141377],{"class":2227,"line":2356},[2225,141378,2285],{"emptyLinePlaceholder":2284},[2225,141380,141381,141383,141386,141388,141390,141392,141394],{"class":2227,"line":2830},[2225,141382,2775],{"class":2774},[2225,141384,141385],{"class":2239}," owner ",[2225,141387,2781],{"class":2235},[2225,141389,2784],{"class":2231},[2225,141391,2744],{"class":2291},[2225,141393,2789],{"class":2239},[2225,141395,4200],{"class":2235},[2225,141397,141398,141400],{"class":2227,"line":2861},[2225,141399,15608],{"class":2239},[2225,141401,1707],{"class":2235},[2225,141403,141404,141406,141408,141410,141412],{"class":2227,"line":4246},[2225,141405,10439],{"class":2307},[2225,141407,2311],{"class":2235},[2225,141409,10373],{"class":2291},[2225,141411,4228],{"class":2239},[2225,141413,1707],{"class":2235},[2225,141415,141416,141418,141420,141422,141424,141426],{"class":2227,"line":4253},[2225,141417,4205],{"class":2307},[2225,141419,2311],{"class":2235},[2225,141421,2249],{"class":2235},[2225,141423,6408],{"class":2252},[2225,141425,2805],{"class":2235},[2225,141427,1707],{"class":2235},[2225,141429,141430,141432,141434,141436,141439,141441],{"class":2227,"line":4259},[2225,141431,10423],{"class":2307},[2225,141433,2311],{"class":2235},[2225,141435,2249],{"class":2235},[2225,141437,141438],{"class":2252},"alice-pass-2026-strong",[2225,141440,2805],{"class":2235},[2225,141442,1707],{"class":2235},[2225,141444,141445,141447],{"class":2227,"line":6548},[2225,141446,2359],{"class":2235},[2225,141448,2362],{"class":2239},[2225,141450,141451,141453,141456,141458,141460,141463,141465,141467,141469,141471,141473,141475],{"class":2227,"line":6576},[2225,141452,2775],{"class":2774},[2225,141454,141455],{"class":2239}," ownerVault ",[2225,141457,2781],{"class":2235},[2225,141459,2784],{"class":2231},[2225,141461,141462],{"class":2239}," owner",[2225,141464,1520],{"class":2235},[2225,141466,2847],{"class":2291},[2225,141468,2789],{"class":2239},[2225,141470,2805],{"class":2235},[2225,141472,97914],{"class":2252},[2225,141474,2805],{"class":2235},[2225,141476,2362],{"class":2239},[2225,141478,141479],{"class":2227,"line":6582},[2225,141480,2285],{"emptyLinePlaceholder":2284},[2225,141482,141483,141485,141487,141489,141491,141493,141495,141497,141499,141501],{"class":2227,"line":6587},[2225,141484,6500],{"class":2231},[2225,141486,141462],{"class":2239},[2225,141488,1520],{"class":2235},[2225,141490,10493],{"class":2291},[2225,141492,2789],{"class":2239},[2225,141494,2805],{"class":2235},[2225,141496,97914],{"class":2252},[2225,141498,2805],{"class":2235},[2225,141500,2298],{"class":2235},[2225,141502,2301],{"class":2235},[2225,141504,141505,141507,141509,141511,141513,141515],{"class":2227,"line":7174},[2225,141506,10510],{"class":2307},[2225,141508,2311],{"class":2235},[2225,141510,2249],{"class":2235},[2225,141512,91596],{"class":2252},[2225,141514,2805],{"class":2235},[2225,141516,1707],{"class":2235},[2225,141518,141519,141521,141523,141525,141527,141529],{"class":2227,"line":7180},[2225,141520,10526],{"class":2307},[2225,141522,2311],{"class":2235},[2225,141524,2249],{"class":2235},[2225,141526,91611],{"class":2252},[2225,141528,2805],{"class":2235},[2225,141530,1707],{"class":2235},[2225,141532,141533,141535,141537,141539,141541,141543],{"class":2227,"line":7220},[2225,141534,10542],{"class":2307},[2225,141536,2311],{"class":2235},[2225,141538,2249],{"class":2235},[2225,141540,9910],{"class":2252},[2225,141542,2805],{"class":2235},[2225,141544,1707],{"class":2235},[2225,141546,141547,141549,141551,141553,141555,141557],{"class":2227,"line":7225},[2225,141548,10423],{"class":2307},[2225,141550,2311],{"class":2235},[2225,141552,2249],{"class":2235},[2225,141554,98776],{"class":2252},[2225,141556,2805],{"class":2235},[2225,141558,1707],{"class":2235},[2225,141560,141561,141563],{"class":2227,"line":7231},[2225,141562,2359],{"class":2235},[2225,141564,2362],{"class":2239},[2225,141566,141567],{"class":2227,"line":7267},[2225,141568,2285],{"emptyLinePlaceholder":2284},[2225,141570,141571,141573,141575,141577,141579,141581,141583,141585,141587],{"class":2227,"line":7272},[2225,141572,6500],{"class":2231},[2225,141574,141462],{"class":2239},[2225,141576,1520],{"class":2235},[2225,141578,16852],{"class":2291},[2225,141580,2789],{"class":2239},[2225,141582,2805],{"class":2235},[2225,141584,97914],{"class":2252},[2225,141586,2805],{"class":2235},[2225,141588,2362],{"class":2239},[2225,141590,141591],{"class":2227,"line":7278},[2225,141592,141593],{"class":2763},"\u002F\u002F → true — enabled by default, no _meta\u002Fdirectory document required\n",[1022,141595,29893],{"id":29892},[1591,141597,141599],{"id":141598},"opt-yourself-out-of-the-listing","Opt yourself out of the listing",[937,141601,141602,141603,141605],{},"Own-only — there is no method that takes another user's ",[941,141604,97739],{},", so no caller can\nhide or unhide a different principal:",[2217,141607,141609],{"className":2219,"code":141608,"language":2221,"meta":1037,"style":1037},"const bobSession = await createNoydb({\n  store,\n  teamStrategy: withTeam(),\n  user: 'bob',\n  secret: 'bob-pass-2026-strong',\n})\nconst bobVault = await bobSession.openVault('demo')\n\nawait bobVault.user.setMyVisibility({ hidden: true })\nawait bobVault.user.getMyVisibility()\n\u002F\u002F → { hidden: true }\n",[941,141610,141611,141628,141634,141646,141660,141674,141680,141708,141712,141742,141759],{"__ignoreMap":1037},[2225,141612,141613,141615,141618,141620,141622,141624,141626],{"class":2227,"line":2228},[2225,141614,2775],{"class":2774},[2225,141616,141617],{"class":2239}," bobSession ",[2225,141619,2781],{"class":2235},[2225,141621,2784],{"class":2231},[2225,141623,2744],{"class":2291},[2225,141625,2789],{"class":2239},[2225,141627,4200],{"class":2235},[2225,141629,141630,141632],{"class":2227,"line":1041},[2225,141631,15608],{"class":2239},[2225,141633,1707],{"class":2235},[2225,141635,141636,141638,141640,141642,141644],{"class":2227,"line":1038},[2225,141637,10439],{"class":2307},[2225,141639,2311],{"class":2235},[2225,141641,10373],{"class":2291},[2225,141643,4228],{"class":2239},[2225,141645,1707],{"class":2235},[2225,141647,141648,141650,141652,141654,141656,141658],{"class":2227,"line":2288},[2225,141649,4205],{"class":2307},[2225,141651,2311],{"class":2235},[2225,141653,2249],{"class":2235},[2225,141655,91596],{"class":2252},[2225,141657,2805],{"class":2235},[2225,141659,1707],{"class":2235},[2225,141661,141662,141664,141666,141668,141670,141672],{"class":2227,"line":2304},[2225,141663,10423],{"class":2307},[2225,141665,2311],{"class":2235},[2225,141667,2249],{"class":2235},[2225,141669,98776],{"class":2252},[2225,141671,2805],{"class":2235},[2225,141673,1707],{"class":2235},[2225,141675,141676,141678],{"class":2227,"line":2356},[2225,141677,2359],{"class":2235},[2225,141679,2362],{"class":2239},[2225,141681,141682,141684,141687,141689,141691,141694,141696,141698,141700,141702,141704,141706],{"class":2227,"line":2830},[2225,141683,2775],{"class":2774},[2225,141685,141686],{"class":2239}," bobVault ",[2225,141688,2781],{"class":2235},[2225,141690,2784],{"class":2231},[2225,141692,141693],{"class":2239}," bobSession",[2225,141695,1520],{"class":2235},[2225,141697,2847],{"class":2291},[2225,141699,2789],{"class":2239},[2225,141701,2805],{"class":2235},[2225,141703,97914],{"class":2252},[2225,141705,2805],{"class":2235},[2225,141707,2362],{"class":2239},[2225,141709,141710],{"class":2227,"line":2861},[2225,141711,2285],{"emptyLinePlaceholder":2284},[2225,141713,141714,141716,141719,141721,141723,141725,141727,141729,141731,141734,141736,141738,141740],{"class":2227,"line":4246},[2225,141715,6500],{"class":2231},[2225,141717,141718],{"class":2239}," bobVault",[2225,141720,1520],{"class":2235},[2225,141722,16225],{"class":2239},[2225,141724,1520],{"class":2235},[2225,141726,141282],{"class":2291},[2225,141728,2789],{"class":2239},[2225,141730,2792],{"class":2235},[2225,141732,141733],{"class":2307}," hidden",[2225,141735,2311],{"class":2235},[2225,141737,2322],{"class":2321},[2225,141739,2243],{"class":2235},[2225,141741,2362],{"class":2239},[2225,141743,141744,141746,141748,141750,141752,141754,141757],{"class":2227,"line":4253},[2225,141745,6500],{"class":2231},[2225,141747,141718],{"class":2239},[2225,141749,1520],{"class":2235},[2225,141751,16225],{"class":2239},[2225,141753,1520],{"class":2235},[2225,141755,141756],{"class":2291},"getMyVisibility",[2225,141758,8146],{"class":2239},[2225,141760,141761],{"class":2227,"line":4259},[2225,141762,141763],{"class":2763},"\u002F\u002F → { hidden: true }\n",[1591,141765,141767],{"id":141766},"toggle-the-vault-level-directory","Toggle the vault-level directory",[937,141769,141770],{},"Owner-only:",[2217,141772,141774],{"className":2219,"code":141773,"language":2221,"meta":1037,"style":1037},"await owner.setDirectoryEnabled('demo', false)\nawait owner.getDirectoryEnabled('demo')\n\u002F\u002F → false\n",[941,141775,141776,141800,141820],{"__ignoreMap":1037},[2225,141777,141778,141780,141782,141784,141786,141788,141790,141792,141794,141796,141798],{"class":2227,"line":2228},[2225,141779,6500],{"class":2231},[2225,141781,141462],{"class":2239},[2225,141783,1520],{"class":2235},[2225,141785,16868],{"class":2291},[2225,141787,2789],{"class":2239},[2225,141789,2805],{"class":2235},[2225,141791,97914],{"class":2252},[2225,141793,2805],{"class":2235},[2225,141795,2298],{"class":2235},[2225,141797,2341],{"class":2321},[2225,141799,2362],{"class":2239},[2225,141801,141802,141804,141806,141808,141810,141812,141814,141816,141818],{"class":2227,"line":1041},[2225,141803,6500],{"class":2231},[2225,141805,141462],{"class":2239},[2225,141807,1520],{"class":2235},[2225,141809,16852],{"class":2291},[2225,141811,2789],{"class":2239},[2225,141813,2805],{"class":2235},[2225,141815,97914],{"class":2252},[2225,141817,2805],{"class":2235},[2225,141819,2362],{"class":2239},[2225,141821,141822],{"class":2227,"line":1038},[2225,141823,141824],{"class":2763},"\u002F\u002F → false\n",[1591,141826,141828],{"id":141827},"what-the-flags-do-to-a-listing","What the flags do to a listing",[937,141830,141831,141834,141835,141837],{},[941,141832,141833],{},"listUsersWithEnvelopes(store, vault, userEnvelopeDek, callerRole, options?)"," is the\nread path that enforces both flags together. It joins every granted keyring with its\ndecrypted ",[1007,141836,97731],{"href":885}," and applies, in order: the\nvault-level toggle, then the per-user hide flag.",[937,141839,141840,141841,141844,141845,141847,141848,141850,141851,141853],{},"It takes the vault's ",[941,141842,141843],{},"_users"," collection key as a plain argument rather than an open\n",[941,141846,94],{}," session, and today there is no public ",[941,141849,94],{}," method that returns that key —\nsee ",[1007,141852,68210],{"href":126812},". The snippet below is illustrative of the\ncall shape, not something you can copy-paste and run as-is until a public accessor\nexists:",[2217,141855,141857],{"className":2219,"code":141856,"language":2221,"meta":1037,"style":1037},"import { listUsersWithEnvelopes, DirectoryDisabledError } from '@noy-db\u002Fhub'\n\nawait owner.setDirectoryEnabled('demo', true) \u002F\u002F re-enable after the toggle example above\n\n\u002F\u002F userEnvelopeDek: no public accessor yet — see Limits & gotchas below.\n\u002F\u002F This is the vault's `_users` collection DEK; server-side \u002F admin tooling\n\u002F\u002F that already manages store and key access supplies it directly.\ndeclare const userEnvelopeDek: CryptoKey\n\nconst rows = await listUsersWithEnvelopes(store, 'demo', userEnvelopeDek, 'owner')\n\u002F\u002F rows: [{ user: { userId: 'alice', role: 'owner', ... }, envelope: {...} }]\n\u002F\u002F       (bob is filtered out — hidden: true, caller did not ask for includeHidden)\n\nconst all = await listUsersWithEnvelopes(store, 'demo', userEnvelopeDek, 'owner', {\n  includeHidden: true,\n})\n\u002F\u002F all: [{ user: { userId: 'alice', ... }, envelope: {...} },\n\u002F\u002F       { user: { userId: 'bob', ... },   envelope: {...} }]\n",[941,141858,141859,141883,141887,141914,141918,141923,141928,141933,141948,141952,141988,141993,141998,142002,142040,142051,142057,142062],{"__ignoreMap":1037},[2225,141860,141861,141863,141865,141868,141870,141873,141875,141877,141879,141881],{"class":2227,"line":2228},[2225,141862,2232],{"class":2231},[2225,141864,2236],{"class":2235},[2225,141866,141867],{"class":2239}," listUsersWithEnvelopes",[2225,141869,2298],{"class":2235},[2225,141871,141872],{"class":2239}," DirectoryDisabledError",[2225,141874,2243],{"class":2235},[2225,141876,2246],{"class":2231},[2225,141878,2249],{"class":2235},[2225,141880,1086],{"class":2252},[2225,141882,2256],{"class":2235},[2225,141884,141885],{"class":2227,"line":1041},[2225,141886,2285],{"emptyLinePlaceholder":2284},[2225,141888,141889,141891,141893,141895,141897,141899,141901,141903,141905,141907,141909,141911],{"class":2227,"line":1038},[2225,141890,6500],{"class":2231},[2225,141892,141462],{"class":2239},[2225,141894,1520],{"class":2235},[2225,141896,16868],{"class":2291},[2225,141898,2789],{"class":2239},[2225,141900,2805],{"class":2235},[2225,141902,97914],{"class":2252},[2225,141904,2805],{"class":2235},[2225,141906,2298],{"class":2235},[2225,141908,2322],{"class":2321},[2225,141910,8495],{"class":2239},[2225,141912,141913],{"class":2763},"\u002F\u002F re-enable after the toggle example above\n",[2225,141915,141916],{"class":2227,"line":2288},[2225,141917,2285],{"emptyLinePlaceholder":2284},[2225,141919,141920],{"class":2227,"line":2304},[2225,141921,141922],{"class":2763},"\u002F\u002F userEnvelopeDek: no public accessor yet — see Limits & gotchas below.\n",[2225,141924,141925],{"class":2227,"line":2356},[2225,141926,141927],{"class":2763},"\u002F\u002F This is the vault's `_users` collection DEK; server-side \u002F admin tooling\n",[2225,141929,141930],{"class":2227,"line":2830},[2225,141931,141932],{"class":2763},"\u002F\u002F that already manages store and key access supplies it directly.\n",[2225,141934,141935,141938,141940,141943,141945],{"class":2227,"line":2861},[2225,141936,141937],{"class":2774},"declare",[2225,141939,8083],{"class":2774},[2225,141941,141942],{"class":2239}," userEnvelopeDek",[2225,141944,2311],{"class":2235},[2225,141946,141947],{"class":6342}," CryptoKey\n",[2225,141949,141950],{"class":2227,"line":4246},[2225,141951,2285],{"emptyLinePlaceholder":2284},[2225,141953,141954,141956,141958,141960,141962,141964,141966,141968,141970,141972,141974,141976,141978,141980,141982,141984,141986],{"class":2227,"line":4253},[2225,141955,2775],{"class":2774},[2225,141957,42003],{"class":2239},[2225,141959,2781],{"class":2235},[2225,141961,2784],{"class":2231},[2225,141963,141867],{"class":2291},[2225,141965,39702],{"class":2239},[2225,141967,2298],{"class":2235},[2225,141969,2249],{"class":2235},[2225,141971,97914],{"class":2252},[2225,141973,2805],{"class":2235},[2225,141975,2298],{"class":2235},[2225,141977,141942],{"class":2239},[2225,141979,2298],{"class":2235},[2225,141981,2249],{"class":2235},[2225,141983,9898],{"class":2252},[2225,141985,2805],{"class":2235},[2225,141987,2362],{"class":2239},[2225,141989,141990],{"class":2227,"line":4259},[2225,141991,141992],{"class":2763},"\u002F\u002F rows: [{ user: { userId: 'alice', role: 'owner', ... }, envelope: {...} }]\n",[2225,141994,141995],{"class":2227,"line":6548},[2225,141996,141997],{"class":2763},"\u002F\u002F       (bob is filtered out — hidden: true, caller did not ask for includeHidden)\n",[2225,141999,142000],{"class":2227,"line":6576},[2225,142001,2285],{"emptyLinePlaceholder":2284},[2225,142003,142004,142006,142008,142010,142012,142014,142016,142018,142020,142022,142024,142026,142028,142030,142032,142034,142036,142038],{"class":2227,"line":6582},[2225,142005,2775],{"class":2774},[2225,142007,104158],{"class":2239},[2225,142009,2781],{"class":2235},[2225,142011,2784],{"class":2231},[2225,142013,141867],{"class":2291},[2225,142015,39702],{"class":2239},[2225,142017,2298],{"class":2235},[2225,142019,2249],{"class":2235},[2225,142021,97914],{"class":2252},[2225,142023,2805],{"class":2235},[2225,142025,2298],{"class":2235},[2225,142027,141942],{"class":2239},[2225,142029,2298],{"class":2235},[2225,142031,2249],{"class":2235},[2225,142033,9898],{"class":2252},[2225,142035,2805],{"class":2235},[2225,142037,2298],{"class":2235},[2225,142039,2301],{"class":2235},[2225,142041,142042,142045,142047,142049],{"class":2227,"line":6587},[2225,142043,142044],{"class":2307},"  includeHidden",[2225,142046,2311],{"class":2235},[2225,142048,2322],{"class":2321},[2225,142050,1707],{"class":2235},[2225,142052,142053,142055],{"class":2227,"line":7174},[2225,142054,2359],{"class":2235},[2225,142056,2362],{"class":2239},[2225,142058,142059],{"class":2227,"line":7180},[2225,142060,142061],{"class":2763},"\u002F\u002F all: [{ user: { userId: 'alice', ... }, envelope: {...} },\n",[2225,142063,142064],{"class":2227,"line":7220},[2225,142065,142066],{"class":2763},"\u002F\u002F       { user: { userId: 'bob', ... },   envelope: {...} }]\n",[937,142068,142069,142072,142073,4688,142075,142077,142078,142080,142081,142084,142085,142087],{},[941,142070,142071],{},"includeHidden: true"," itself requires ",[941,142074,9898],{},[941,142076,9901],{},"; any other role passing it\nthrows ",[941,142079,11395],{},". With the directory disabled and ",[941,142082,142083],{},"callerRole: 'viewer'",",\nthe same call throws ",[941,142086,16907],{}," instead:",[2217,142089,142092],{"className":142090,"code":142091,"language":5672},[5670],"Vault \"demo\" has its user directory disabled. Only owners and admins\ncan call listUsersWithEnvelopes() here. Use db.setDirectoryEnabled(vault, true) to re-enable.\n",[941,142093,142091],{"__ignoreMap":1037},[1022,142095,102889],{"id":102888},[1591,142097,103205],{"id":128135},[1338,142099,142100,142110],{},[1341,142101,142102],{},[1344,142103,142104,142106,142108],{},[1347,142105,72632],{},[1347,142107,50797],{},[1347,142109,7834],{},[1357,142111,142112,142129],{},[1344,142113,142114,142119,142124],{},[1362,142115,142116],{},[941,142117,142118],{},"DirectoryConfig",[1362,142120,142121],{},[941,142122,142123],{},"{ enabled: boolean }",[1362,142125,142126,142127,1520],{},"Vault-level directory toggle, persisted at ",[941,142128,16900],{},[1344,142130,142131,142136,142141],{},[1362,142132,142133],{},[941,142134,142135],{},"UserVisibility",[1362,142137,142138],{},[941,142139,142140],{},"{ hidden: boolean }",[1362,142142,142143,142144,1520],{},"Per-user hide flag, persisted at ",[941,142145,142146],{},"_meta\u002Fvisibility\u002F\u003CkeyringId>",[1591,142148,142150],{"id":142149},"directory-toggle","Directory toggle",[1338,142152,142153,142163],{},[1341,142154,142155],{},[1344,142156,142157,142159,142161],{},[1347,142158,72632],{},[1347,142160,131736],{},[1347,142162,7834],{},[1357,142164,142165,142192,142210,142227,142248],{},[1344,142166,142167,142172,142177],{},[1362,142168,142169],{},[941,142170,142171],{},"readDirectoryConfig",[1362,142173,142174],{},[941,142175,142176],{},"(store, vault) => Promise\u003CDirectoryConfig | undefined>",[1362,142178,142179,142180,1952,142182,142184,142185,142188,142189,142191],{},"Read ",[941,142181,16900],{},[941,142183,18316],{}," means no document exists yet — callers treat that as ",[941,142186,142187],{},"{ enabled: true }",". A corrupted document also reads back as ",[941,142190,18316],{}," rather than throwing.",[1344,142193,142194,142199,142204],{},[1362,142195,142196],{},[941,142197,142198],{},"persistDirectoryConfig",[1362,142200,142201],{},[941,142202,142203],{},"(store, vault, config: DirectoryConfig) => Promise\u003Cvoid>",[1362,142205,142206,142207,142209],{},"Write ",[941,142208,16900],{},". No role check — the role gate lives in the wrapper below.",[1344,142211,142212,142217,142221],{},[1362,142213,142214],{},[941,142215,142216],{},"db.getDirectoryEnabled(vault)",[1362,142218,142219],{},[941,142220,137518],{},[1362,142222,142223,142224,142226],{},"Kernel wrapper over ",[941,142225,142171],{},"; no role gate — anyone who can open the vault can read the toggle.",[1344,142228,142229,142234,142238],{},[1362,142230,142231],{},[941,142232,142233],{},"db.setDirectoryEnabled(vault, enabled)",[1362,142235,142236],{},[941,142237,116014],{},[1362,142239,142223,142240,5502,142242,142245,142246,111128],{},[941,142241,142198],{},[950,142243,142244],{},"owner-only",", throws ",[941,142247,11395],{},[1344,142249,142250,142255,142260],{},[1362,142251,142252],{},[941,142253,142254],{},"DIRECTORY_RECORD_ID",[1362,142256,142257],{},[941,142258,142259],{},"'directory'",[1362,142261,960,142262,142264],{},[941,142263,117002],{}," record id the toggle lives at.",[1591,142266,142268],{"id":142267},"per-user-visibility","Per-user visibility",[1338,142270,142271,142281],{},[1341,142272,142273],{},[1344,142274,142275,142277,142279],{},[1347,142276,72632],{},[1347,142278,131736],{},[1347,142280,7834],{},[1357,142282,142283,142304,142321,142342,142357,142374,142395],{},[1344,142284,142285,142290,142295],{},[1362,142286,142287],{},[941,142288,142289],{},"readUserVisibility",[1362,142291,142292],{},[941,142293,142294],{},"(store, vault, keyringId) => Promise\u003CUserVisibility | undefined>",[1362,142296,142179,142297,1952,142299,142184,142301,1520],{},[941,142298,142146],{},[941,142300,18316],{},[941,142302,142303],{},"{ hidden: false }",[1344,142305,142306,142311,142316],{},[1362,142307,142308],{},[941,142309,142310],{},"persistUserVisibility",[1362,142312,142313],{},[941,142314,142315],{},"(store, vault, keyringId, visibility: UserVisibility) => Promise\u003Cvoid>",[1362,142317,142206,142318,142320],{},[941,142319,142146],{},". No ownership check — the own-only rule lives in the wrapper below.",[1344,142322,142323,142328,142333],{},[1362,142324,142325],{},[941,142326,142327],{},"deleteUserVisibility",[1362,142329,142330],{},[941,142331,142332],{},"(store, vault, keyringId) => Promise\u003Cvoid>",[1362,142334,142335,142336,142338,142339,142341],{},"Delete the sidecar. Called by ",[941,142337,82028],{}," so a hidden flag never leaks onto a re-granted ",[941,142340,10749],{},". Idempotent.",[1344,142343,142344,142349,142354],{},[1362,142345,142346],{},[941,142347,142348],{},"vault.user.getMyVisibility()",[1362,142350,142351],{},[941,142352,142353],{},"Promise\u003CUserVisibility>",[1362,142355,142356],{},"Read the caller's own flag.",[1344,142358,142359,142364,142368],{},[1362,142360,142361],{},[941,142362,142363],{},"vault.user.setMyVisibility({ hidden })",[1362,142365,142366],{},[941,142367,116014],{},[1362,142369,142370,142371,142373],{},"Set the caller's own flag. Own-only by construction — no ",[941,142372,97739],{}," parameter exists on this method.",[1344,142375,142376,142381,142386],{},[1362,142377,142378],{},[941,142379,142380],{},"visibilityRecordId(keyringId)",[1362,142382,142383],{},[941,142384,142385],{},"(keyringId: string) => string",[1362,142387,142388,142389,142391,142392,1520],{},"Compose the ",[941,142390,117002],{}," record id: ",[941,142393,142394],{},"`visibility\u002F${keyringId}`",[1344,142396,142397,142402,142407],{},[1362,142398,142399],{},[941,142400,142401],{},"VISIBILITY_RECORD_PREFIX",[1362,142403,142404],{},[941,142405,142406],{},"'visibility\u002F'",[1362,142408,142409,142410,142413],{},"The prefix ",[941,142411,142412],{},"visibilityRecordId"," uses.",[1591,142415,104913],{"id":90098},[1338,142417,142418,142426],{},[1341,142419,142420],{},[1344,142421,142422,142424],{},[1347,142423,72632],{},[1347,142425,7834],{},[1357,142427,142428,142441],{},[1344,142429,142430,142435],{},[1362,142431,142432],{},[941,142433,142434],{},"META_COLLECTION",[1362,142436,142437,142440],{},[941,142438,142439],{},"'_meta'"," — the reserved collection both records live in.",[1344,142442,142443,142447],{},[1362,142444,142445],{},[941,142446,16907],{},[1362,142448,142449,142450,142452,142453,139707,142455,142457,142458,1520],{},"Thrown by ",[941,142451,141235],{}," when the directory is disabled and the caller is neither ",[941,142454,9898],{},[941,142456,9901],{},". Carries ",[941,142459,142460],{},".vault",[1591,142462,142464],{"id":142463},"listuserswithenvelopesstore-vault-userenvelopedek-callerrole-options",[941,142465,141833],{},[937,142467,142468,142469,142471,142472,142474],{},"Exported from ",[941,142470,1086],{}," root; defined in the ",[941,142473,868],{}," collaboration family and\ndocumented here because this is where both directory flags are actually enforced.",[1338,142476,142477,142487],{},[1341,142478,142479],{},[1344,142480,142481,142483,142485],{},[1347,142482,72632],{},[1347,142484,131736],{},[1347,142486,7834],{},[1357,142488,142489],{},[1344,142490,142491,142495,142500],{},[1362,142492,142493],{},[941,142494,141235],{},[1362,142496,142497],{},[941,142498,142499],{},"\u003CT>(store: NoydbStore, vault: string, userEnvelopeDek: CryptoKey, callerRole: Role, options?: { includeHidden?: boolean }) => Promise\u003CArray\u003C{ user: UserInfo; envelope: UserEnvelope\u003CT> | null }>>",[1362,142501,142502,142503,7808,142506,142509,142510,4688,142512,142514,142515,142517,142518,1520],{},"Joins every granted keyring with its decrypted user envelope, applying the vault-level toggle then the per-user hide flag. ",[941,142504,142505],{},"options.includeHidden",[941,142507,142508],{},"callerRole"," to be ",[941,142511,9898],{},[941,142513,9901],{},"; passing it as any other role throws ",[941,142516,11395],{},". A principal with no persisted envelope comes back with ",[941,142519,142520],{},"envelope: null",[1022,142522,68210],{"id":68209},[1111,142524,142525,142549,142570,142581,142598],{},[1114,142526,142527,142534,142535,142537,142538,142540,142541,142543,142544,142546,142547,1520],{},[950,142528,142529,142531,142532,1520],{},[941,142530,141235],{}," takes a raw collection key, not a ",[941,142533,94],{}," It reads\ndirectly against a ",[941,142536,3154],{},", so it needs the vault's ",[941,142539,141843],{}," collection DEK and\nthe caller's role as plain arguments rather than deriving them from an open ",[941,142542,94],{},"\nsession — there is currently no public ",[941,142545,94],{}," method that returns that key. It is\nmeant for server-side \u002F admin tooling that already manages store and key access\nitself, not a one-line call from arbitrary app code holding only a ",[941,142548,94],{},[1114,142550,142551,142554,142555,142557,142558,142560,142561,142563,142564,142566,142567,142569],{},[950,142552,142553],{},"UX flag, not a privacy guarantee."," Both records are plaintext JSON sidecars (the\nenvelope's ",[941,142556,1143],{}," is left empty — see ",[1007,142559,784],{"href":785},"),\nand only gate the ",[941,142562,141235],{}," join. The keyring file at\n",[941,142565,9871],{}," and the encrypted envelope at ",[941,142568,97735],{}," are both still\npresent and listable to anyone with direct store read access. These flags stop an\nadmin UI from accidentally rendering a hidden teammate — they do not hide that the\nprincipal exists from a determined attacker.",[1114,142571,142572,142575,142576,968,142578,142580],{},[950,142573,142574],{},"A missing document is not \"off.\""," Both flags default to the visible\u002Fenabled state\nwhen nothing has been persisted (",[941,142577,142187],{},[941,142579,142303],{},") — there is\nno explicit \"unset\" state to branch on.",[1114,142582,142583,142588,142589,142591,142592,142594,142595,142597],{},[950,142584,142585,142587],{},[941,142586,142071],{}," is role-gated, not caller-gated."," It checks the role you\npass into ",[941,142590,141235],{},", so whatever wires ",[941,142593,142508],{}," from your session\nis the actual security boundary — pass it from the unlocked keyring's ",[941,142596,16785],{},", never\nfrom user input.",[1114,142599,142600,1324,142603,142605,142606,142608,142609,142611],{},[950,142601,142602],{},"Revoke cleans up; recovery does not touch it.",[941,142604,82028],{}," deletes the visibility\nsidecar so it can't leak onto a re-granted ",[941,142607,10749],{},". Peer-recovery (",[941,142610,20072],{},")\nonly rewrites the keyring file, so an existing hidden flag survives a secret\nreset for the same principal.",[1022,142613,2621],{"id":2620},[1111,142615,142616,142624,142629,142634,142639,142646],{},[1114,142617,142618,142620,142621,142623],{},[1007,142619,868],{"href":869}," — grant\u002Frevoke\u002Frotate access and the multi-user collaboration\nfamily ",[941,142622,141235],{}," belongs to",[1114,142625,142626,142628],{},[1007,142627,613],{"href":614}," — the read-only per-user auth\nenrollment view, gated the same admin-facing way as the directory listing",[1114,142630,142631,142633],{},[1007,142632,884],{"href":885}," — the per-principal profile record this\nservice filters",[1114,142635,142636,142638],{},[1007,142637,14279],{"href":54}," — roles referenced above",[1114,142640,142641,1169,142643,142645],{},[1007,142642,784],{"href":785},[941,142644,128244],{}," sidecar pattern both\nflags use",[1114,142647,142648,1368,142650],{},[1007,142649,61743],{"href":598},[1007,142651,1019],{"href":1018},[2645,142653,142654],{},"html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sfNiH, html code.shiki .sfNiH{--shiki-light:#FF5370;--shiki-default:#FF9CAC;--shiki-dark:#FF9CAC}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}",{"title":1037,"searchDepth":1038,"depth":1038,"links":142656},[142657,142658,142659,142664,142671,142672],{"id":29856,"depth":1041,"text":29857},{"id":58393,"depth":1041,"text":58394},{"id":29892,"depth":1041,"text":29893,"children":142660},[142661,142662,142663],{"id":141598,"depth":1038,"text":141599},{"id":141766,"depth":1038,"text":141767},{"id":141827,"depth":1038,"text":141828},{"id":102888,"depth":1041,"text":102889,"children":142665},[142666,142667,142668,142669,142670],{"id":128135,"depth":1038,"text":103205},{"id":142149,"depth":1038,"text":142150},{"id":142267,"depth":1038,"text":142268},{"id":90098,"depth":1038,"text":104913},{"id":142463,"depth":1038,"text":141833},{"id":68209,"depth":1041,"text":68210},{"id":2620,"depth":1041,"text":2621},"Vault-level directory toggle and per-user hide flag that filter who shows up when you enumerate a vault's team — always-on, no with*() opt-in required.",{},{"title":669,"description":142673},"ROxvpIH9A-zEQRkUHZZ9yFOHfPnxz2mGMXsZXKLXYJU",{"id":142678,"title":673,"access":12,"apiToc":929,"body":142679,"category":1042,"description":146713,"extension":1044,"meta":146714,"navigation":1042,"nest":1047,"path":674,"seo":146715,"sinceVersion":1042,"status":38550,"stem":675,"subtitle":146716,"tier":1050,"tocExternal":1042,"__hash__":146717},"docs\u002Fservices\u002Fembeddings.md",{"type":931,"value":142680,"toc":146677},[142681,142684,142687,142690,142731,142733,142757,142771,142808,142811,142843,142845,142896,142898,142916,143197,143235,143241,143262,143291,144124,144138,144140,144147,144195,144434,144446,144575,144662,144672,144678,144793,144800,144803,144865,145026,145040,145046,145220,145227,145251,145298,145462,145484,145493,145499,145520,145621,145710,145735,145753,145760,145777,145883,145889,145896,145948,145950,145956,146042,146051,146057,146139,146144,146202,146209,146215,146248,146258,146262,146278,146295,146301,146317,146354,146406,146410,146427,146437,146462,146468,146479,146481,146624,146626,146674],[934,142682,673],{"id":142683},"search-ai",[46871,142685],{":rows":142686},"[{\"label\":\"Subpath\",\"value\":\"`@noy-db\u002Fhub` (root export — no separate subpath)\"},{\"label\":\"Factory\",\"value\":\"`withSearch()`\"},{\"label\":\"Layer\",\"value\":\"`with-lookup`\"}]",[937,142688,142689],{},"This service is in preview: the API on this page is usable but may still change before it stabilizes.",[7646,142691,142692],{"color":7648,"icon":369},[937,142693,142694,142697,142698,142700,142701,142703,142704,142707,142708,142710,142711,142713,142714,142716,142717,142707,142720,142723,142724,142727,142728,142730],{},[950,142695,142696],{},"Two pages, one subsystem."," This page and ",[1007,142699,832],{"href":833}," are facets of a single retrieval engine behind the same ",[941,142702,22027],{}," factory. ",[950,142705,142706],{},"This page",": matching ",[1092,142709,82055],{}," — semantic retrieval via an ",[941,142712,4452],{}," encoder you configure per collection, ",[941,142715,22038],{},", and hybrid lexical⊕semantic fusion. ",[950,142718,142719],{},"The other page",[1092,142721,142722],{},"words"," — full-text ",[941,142725,142726],{},"search()"," and indexed lexical ",[941,142729,22035],{},". Opting in once enables both, but semantic modes additionally require the encoder (a text→vector function you supply).",[1022,142732,29857],{"id":29856},[937,142734,142735,142738,142739,142741,142742,142744,142745,142747,142748,142750,142751,142753,142754,142756],{},[950,142736,142737],{},"Semantic search"," finds records by ",[1092,142740,82055],{}," instead of exact words: \"overdue invoice\" can match a record that says \"payment past due\" even though no word overlaps. It works through ",[950,142743,4452],{}," — you configure an ",[941,142746,4452],{}," option on a collection with a function that turns text into a vector (a fixed-length array of numbers), call it your ",[950,142749,95031],{},". noy-db runs that encoder over each record as you write it, encrypts the resulting vector under the collection's ",[1007,142752,137899],{"href":66}," — the same per-collection key that encrypts every record body — and stores it in a reserved ",[941,142755,75373],{}," side-car, one per record. To search, noy-db encodes your query the same way and ranks stored vectors by cosine similarity (how closely two vectors point in the same direction) against the query vector, entirely after decrypting in memory — the storage backend never sees a vector or the text it came from.",[937,142758,142759,142760,142763,142764,142767,142768,142770],{},"This page also covers ",[950,142761,142762],{},"hybrid retrieval",": fusing this semantic ranking with the keyword-based ranking from ",[1007,142765,142766],{"href":833},"Search"," into one result list, and the ",[941,142769,111736],{}," option for intersecting a retrieve call with a structured query.",[7646,142772,142774],{"color":7648,"icon":142773},"i-lucide-folder-tree",[937,142775,142776,41138,142779,4724,142781,142783,142784,142787,142788,142791,142792,142794,142795,142797,142798,142800,142801,142805,142806,1520],{},[950,142777,142778],{},"Sidecars are collection-namespaced.",[941,142780,56948],{},[941,142782,75373],{}," sidecar is keyed by\n",[941,142785,142786],{},"\u003Ccollection>\u002F\u003CrecordId>",", not a bare record id — the store bucket stays the literal ",[941,142789,142790],{},"'_vec'",",\nbut the row id is composite. Before this, two collections that happened to share a record id\nshared one sidecar, so a write in one collection could clobber or delete the other's vector, and a\n",[941,142793,22038],{}," \u002F cold semantic ",[941,142796,22035],{}," that met a foreign same-id row threw an uncaught\n",[941,142799,11762],{},". This was never a confidentiality leak — each collection has its own DEK and\nAES-GCM auth-tag verification turns a wrong-key decrypt into a throw, never wrong plaintext — but\nit was an id-collision bug (clobber + crash). See ",[1007,142802,142804],{"href":142803},"#migrating-existing-vectors","migration"," below if\nyou already populated embeddings before ",[941,142807,56948],{},[937,142809,142810],{},"Key properties:",[1111,142812,142813,142823,142831,142837],{},[1114,142814,142815,142818,142819,142822],{},[950,142816,142817],{},"Zero-knowledge"," — the store sees only ciphertext per ",[941,142820,142821],{},"_vec\u002F\u003Cid>"," record; vector values and source text never leave the trusted tier (the part of the stack that holds decryption keys and plaintext).",[1114,142824,142825,1169,142828,142830],{},[950,142826,142827],{},"No bundled model",[941,142829,69746],{}," function is yours to supply, calling any local or remote embedding API. noy-db ships no model.",[1114,142832,142833,142836],{},[950,142834,142835],{},"Brute-force cosine"," — every query scores every stored vector; there is no approximate-nearest-neighbour index (HNSW\u002FIVF) yet, so this is best suited to collections up to roughly the tens of thousands of records.",[1114,142838,142839,142842],{},[950,142840,142841],{},"Encrypted-local only"," — a backend that received plaintext vectors (a managed vector database) could have those vectors inverted back toward the source text, defeating zero-knowledge. That integration path does not exist yet; vectors stay encrypted-local.",[1022,142844,70093],{"id":70092},[1111,142846,142847,142857,142875,142888],{},[1114,142848,142849,142853,142854,142856],{},[950,142850,142851],{},[941,142852,94750],{}," — semantic and hybrid retrieval are part of the same opt-in as lexical ",[1007,142855,4457],{"href":833},"; one factory enables both.",[1114,142858,142859,142864,142865,997,142868,142871,142872,142874],{},[950,142860,5093,142861,142863],{},[941,142862,4452],{}," config on the collection"," — your encoder function plus matching ",[941,142866,142867],{},"dim",[941,142869,142870],{},"model",". The encoder is a part ",[1092,142873,13501],{}," supply; whatever runs inside it sees plaintext, so an on-device encoder keeps the zero-knowledge guarantee end-to-end while a hosted API adds that provider as a reader.",[1114,142876,142877,142882,142883,142885,142886,20243],{},[1092,142878,110954,142879,2311],{},[941,142880,142881],{},"mode: 'hybrid'"," the same ",[941,142884,4452],{}," config must be present, or ",[941,142887,22035],{},[1114,142889,142890,142895],{},[1092,142891,110954,142892,142894],{},[941,142893,111736],{}," intersection:"," eager mode (the default) — lazy collections are unsupported there.",[1022,142897,99864],{"id":99863},[937,142899,142900,142901,142903,142904,142908,142909,10313,142911,101692,142913,142915],{},"Semantic and hybrid retrieval are part of the same opt-in ",[941,142902,4457],{}," capability as lexical ",[1007,142905,142906],{"href":833},[941,142907,22035],{},": pass ",[941,142910,94750],{},[941,142912,2134],{},[941,142914,4452],{}," on the collection you want to search semantically.",[2217,142917,142919],{"className":2219,"code":142918,"language":2221,"meta":1037,"style":1037},"import { createNoydb, withSearch } from '@noy-db\u002Fhub'\n\nconst db = await createNoydb({\n  user: 'alice',\n  secret: 'alice-secret-2026',\n  searchStrategy: withSearch(),\n})\nconst vault = await db.openVault('books')\n\ninterface Doc { id: string; text: string }\n\nconst docs = vault.collection\u003CDoc>('docs', {\n  embeddings: {\n    source: 'text',                                 \u002F\u002F field path(s) to embed\n    encode: async (text) => myEmbeddingApi(text),    \u002F\u002F your encoder — see \"Supplying an encode function\" below\n    dim: 1536,                                       \u002F\u002F expected vector length\n    model: 'text-embedding-3-small',                 \u002F\u002F version tag, checked on every retrieval\n  },\n})\n",[941,142920,142921,142943,142947,142963,142977,142991,143003,143009,143035,143039,143064,143068,143101,143109,143126,143153,143168,143187,143191],{"__ignoreMap":1037},[2225,142922,142923,142925,142927,142929,142931,142933,142935,142937,142939,142941],{"class":2227,"line":2228},[2225,142924,2232],{"class":2231},[2225,142926,2236],{"class":2235},[2225,142928,2744],{"class":2239},[2225,142930,2298],{"class":2235},[2225,142932,73949],{"class":2239},[2225,142934,2243],{"class":2235},[2225,142936,2246],{"class":2231},[2225,142938,2249],{"class":2235},[2225,142940,1086],{"class":2252},[2225,142942,2256],{"class":2235},[2225,142944,142945],{"class":2227,"line":1041},[2225,142946,2285],{"emptyLinePlaceholder":2284},[2225,142948,142949,142951,142953,142955,142957,142959,142961],{"class":2227,"line":1038},[2225,142950,2775],{"class":2774},[2225,142952,2778],{"class":2239},[2225,142954,2781],{"class":2235},[2225,142956,2784],{"class":2231},[2225,142958,2744],{"class":2291},[2225,142960,2789],{"class":2239},[2225,142962,4200],{"class":2235},[2225,142964,142965,142967,142969,142971,142973,142975],{"class":2227,"line":2288},[2225,142966,4205],{"class":2307},[2225,142968,2311],{"class":2235},[2225,142970,2249],{"class":2235},[2225,142972,6408],{"class":2252},[2225,142974,2805],{"class":2235},[2225,142976,1707],{"class":2235},[2225,142978,142979,142981,142983,142985,142987,142989],{"class":2227,"line":2304},[2225,142980,10423],{"class":2307},[2225,142982,2311],{"class":2235},[2225,142984,2249],{"class":2235},[2225,142986,107607],{"class":2252},[2225,142988,2805],{"class":2235},[2225,142990,1707],{"class":2235},[2225,142992,142993,142995,142997,142999,143001],{"class":2227,"line":2356},[2225,142994,74331],{"class":2307},[2225,142996,2311],{"class":2235},[2225,142998,73949],{"class":2291},[2225,143000,4228],{"class":2239},[2225,143002,1707],{"class":2235},[2225,143004,143005,143007],{"class":2227,"line":2830},[2225,143006,2359],{"class":2235},[2225,143008,2362],{"class":2239},[2225,143010,143011,143013,143015,143017,143019,143021,143023,143025,143027,143029,143031,143033],{"class":2227,"line":2861},[2225,143012,2775],{"class":2774},[2225,143014,2835],{"class":2239},[2225,143016,2781],{"class":2235},[2225,143018,2784],{"class":2231},[2225,143020,2842],{"class":2239},[2225,143022,1520],{"class":2235},[2225,143024,2847],{"class":2291},[2225,143026,2789],{"class":2239},[2225,143028,2805],{"class":2235},[2225,143030,65761],{"class":2252},[2225,143032,2805],{"class":2235},[2225,143034,2362],{"class":2239},[2225,143036,143037],{"class":2227,"line":4246},[2225,143038,2285],{"emptyLinePlaceholder":2284},[2225,143040,143041,143043,143046,143048,143050,143052,143054,143056,143058,143060,143062],{"class":2227,"line":4253},[2225,143042,6994],{"class":2774},[2225,143044,143045],{"class":6342}," Doc",[2225,143047,2236],{"class":2235},[2225,143049,7070],{"class":2307},[2225,143051,2311],{"class":2235},[2225,143053,6366],{"class":6342},[2225,143055,3769],{"class":2235},[2225,143057,82994],{"class":2307},[2225,143059,2311],{"class":2235},[2225,143061,6366],{"class":6342},[2225,143063,6369],{"class":2235},[2225,143065,143066],{"class":2227,"line":4259},[2225,143067,2285],{"emptyLinePlaceholder":2284},[2225,143069,143070,143072,143074,143076,143078,143080,143082,143084,143087,143089,143091,143093,143095,143097,143099],{"class":2227,"line":6548},[2225,143071,2775],{"class":2774},[2225,143073,90113],{"class":2239},[2225,143075,2781],{"class":2235},[2225,143077,6466],{"class":2239},[2225,143079,1520],{"class":2235},[2225,143081,6471],{"class":2291},[2225,143083,6474],{"class":2235},[2225,143085,143086],{"class":6342},"Doc",[2225,143088,6480],{"class":2235},[2225,143090,2789],{"class":2239},[2225,143092,2805],{"class":2235},[2225,143094,69487],{"class":2252},[2225,143096,2805],{"class":2235},[2225,143098,2298],{"class":2235},[2225,143100,2301],{"class":2235},[2225,143102,143103,143105,143107],{"class":2227,"line":6576},[2225,143104,95010],{"class":2307},[2225,143106,2311],{"class":2235},[2225,143108,2301],{"class":2235},[2225,143110,143111,143113,143115,143117,143119,143121,143123],{"class":2227,"line":6582},[2225,143112,24110],{"class":2307},[2225,143114,2311],{"class":2235},[2225,143116,2249],{"class":2235},[2225,143118,5672],{"class":2252},[2225,143120,2805],{"class":2235},[2225,143122,2298],{"class":2235},[2225,143124,143125],{"class":2763},"                                 \u002F\u002F field path(s) to embed\n",[2225,143127,143128,143131,143133,143135,143137,143139,143141,143143,143146,143148,143150],{"class":2227,"line":6587},[2225,143129,143130],{"class":2291},"    encode",[2225,143132,2311],{"class":2235},[2225,143134,38717],{"class":2774},[2225,143136,953],{"class":2235},[2225,143138,5672],{"class":7052},[2225,143140,1381],{"class":2235},[2225,143142,8116],{"class":2774},[2225,143144,143145],{"class":2291}," myEmbeddingApi",[2225,143147,95131],{"class":2239},[2225,143149,2298],{"class":2235},[2225,143151,143152],{"class":2763},"    \u002F\u002F your encoder — see \"Supplying an encode function\" below\n",[2225,143154,143155,143158,143160,143163,143165],{"class":2227,"line":7174},[2225,143156,143157],{"class":2307},"    dim",[2225,143159,2311],{"class":2235},[2225,143161,143162],{"class":6527}," 1536",[2225,143164,2298],{"class":2235},[2225,143166,143167],{"class":2763},"                                       \u002F\u002F expected vector length\n",[2225,143169,143170,143173,143175,143177,143180,143182,143184],{"class":2227,"line":7180},[2225,143171,143172],{"class":2307},"    model",[2225,143174,2311],{"class":2235},[2225,143176,2249],{"class":2235},[2225,143178,143179],{"class":2252},"text-embedding-3-small",[2225,143181,2805],{"class":2235},[2225,143183,2298],{"class":2235},[2225,143185,143186],{"class":2763},"                 \u002F\u002F version tag, checked on every retrieval\n",[2225,143188,143189],{"class":2227,"line":7220},[2225,143190,19017],{"class":2235},[2225,143192,143193,143195],{"class":2227,"line":7225},[2225,143194,2359],{"class":2235},[2225,143196,2362],{"class":2239},[1111,143198,143199,143204,143209,143221,143232],{},[1114,143200,143201,143203],{},[941,143202,22723],{}," is a field path or array of field paths; values are joined with a space before encoding.",[1114,143205,143206,143208],{},[941,143207,69746],{}," is async; if it throws, the write that triggered it fails.",[1114,143210,143211,143213,143214,143217,143218,1520],{},[941,143212,142867],{}," is enforced on every write — an ",[941,143215,143216],{},"encode()"," result of the wrong length throws ",[941,143219,143220],{},"EmbeddingDimMismatchError",[1114,143222,143223,143226,143227,997,143229,143231],{},[950,143224,143225],{},"CRDT collections are unsupported",": declaring both ",[941,143228,4452],{},[941,143230,653],{}," on the same collection throws immediately. Use a non-CRDT collection for semantic search.",[1114,143233,143234],{},"A record whose source field is blank or missing yields a near-zero vector — it is stored and retrievable but scores low on every query.",[1591,143236,143238,143239,40856],{"id":143237},"supplying-an-encode-function","Supplying an ",[941,143240,69746],{},[7646,143242,143244],{"color":11278,"icon":143243},"i-lucide-sparkles",[937,143245,143246,143252,143253,143255,143256,143258,143259,143261],{},[950,143247,143248,143249,143251],{},"Why ",[941,143250,69746],{}," is a strategic seam."," An embedding is how modern AI turns ",[1092,143254,82055],{}," into numbers: a model trained on vast amounts of text maps each string to a point in vector space where \"payment past due\" lands near \"overdue invoice\". Embedding quality is one of the fastest-moving parts of AI — so noy-db bundles ",[950,143257,11313],{}," model and makes ",[941,143260,69746],{}," a hook you own. You can drop in a better model the day it ships, match cost, latency, and quality to your app, and (by running it on-device) get AI-grade semantic search without giving up zero-knowledge. The database stays a small, model-agnostic vector store; the intelligence is yours to choose and upgrade.",[937,143263,143264,143265,143267,143268,143270,143271,143273,143274,143276,143277,997,143279,143281,143282,143284,143285,143287,143288,143290],{},"In practice, ",[941,143266,69746],{}," receives the record's ",[941,143269,22723],{}," text and must return a ",[941,143272,75686],{}," of length ",[941,143275,142867],{},"; set the collection's ",[941,143278,142867],{},[941,143280,142870],{}," to match the encoder you pick (each tab notes both). The one zero-knowledge caveat: whatever you call inside ",[941,143283,69746],{}," sees your text as ",[950,143286,2985],{},", so an ",[950,143289,94784],{}," encoder keeps the guarantee end-to-end, while a hosted API adds that provider as a party that can read your data.",[143292,143293,143294,143467,143711,143953],"code-group",{},[2217,143295,143298],{"className":2219,"code":143296,"filename":143297,"language":2221,"meta":1037,"style":1037},"\u002F\u002F On-device, nothing leaves the machine — the zero-knowledge-preserving choice.\nimport { pipeline } from '@xenova\u002Ftransformers'\n\nconst extractor = await pipeline('feature-extraction', 'Xenova\u002Fall-MiniLM-L6-v2')\n\nasync function myEmbeddingApi(text: string): Promise\u003CFloat32Array> {\n  const out = await extractor(text, { pooling: 'mean', normalize: true })\n  return new Float32Array(out.data)\n}\n\u002F\u002F collection config → dim: 384, model: 'all-MiniLM-L6-v2'\n","Local · Transformers.js",[941,143299,143300,143305,143324,143328,143361,143365,143393,143439,143458,143462],{"__ignoreMap":1037},[2225,143301,143302],{"class":2227,"line":2228},[2225,143303,143304],{"class":2763},"\u002F\u002F On-device, nothing leaves the machine — the zero-knowledge-preserving choice.\n",[2225,143306,143307,143309,143311,143313,143315,143317,143319,143322],{"class":2227,"line":1041},[2225,143308,2232],{"class":2231},[2225,143310,2236],{"class":2235},[2225,143312,140993],{"class":2239},[2225,143314,2243],{"class":2235},[2225,143316,2246],{"class":2231},[2225,143318,2249],{"class":2235},[2225,143320,143321],{"class":2252},"@xenova\u002Ftransformers",[2225,143323,2256],{"class":2235},[2225,143325,143326],{"class":2227,"line":1038},[2225,143327,2285],{"emptyLinePlaceholder":2284},[2225,143329,143330,143332,143335,143337,143339,143341,143343,143345,143348,143350,143352,143354,143357,143359],{"class":2227,"line":2288},[2225,143331,2775],{"class":2774},[2225,143333,143334],{"class":2239}," extractor ",[2225,143336,2781],{"class":2235},[2225,143338,2784],{"class":2231},[2225,143340,140993],{"class":2291},[2225,143342,2789],{"class":2239},[2225,143344,2805],{"class":2235},[2225,143346,143347],{"class":2252},"feature-extraction",[2225,143349,2805],{"class":2235},[2225,143351,2298],{"class":2235},[2225,143353,2249],{"class":2235},[2225,143355,143356],{"class":2252},"Xenova\u002Fall-MiniLM-L6-v2",[2225,143358,2805],{"class":2235},[2225,143360,2362],{"class":2239},[2225,143362,143363],{"class":2227,"line":2304},[2225,143364,2285],{"emptyLinePlaceholder":2284},[2225,143366,143367,143369,143371,143373,143375,143377,143379,143381,143383,143385,143387,143389,143391],{"class":2227,"line":2356},[2225,143368,70491],{"class":2774},[2225,143370,40856],{"class":2774},[2225,143372,143145],{"class":2291},[2225,143374,2789],{"class":2235},[2225,143376,5672],{"class":7052},[2225,143378,2311],{"class":2235},[2225,143380,6366],{"class":6342},[2225,143382,6255],{"class":2235},[2225,143384,7079],{"class":6342},[2225,143386,6474],{"class":2235},[2225,143388,75686],{"class":6342},[2225,143390,6480],{"class":2235},[2225,143392,2301],{"class":2235},[2225,143394,143395,143397,143400,143402,143404,143407,143409,143411,143413,143415,143418,143420,143422,143425,143427,143429,143431,143433,143435,143437],{"class":2227,"line":2830},[2225,143396,8123],{"class":2774},[2225,143398,143399],{"class":2239}," out",[2225,143401,6346],{"class":2235},[2225,143403,2784],{"class":2231},[2225,143405,143406],{"class":2291}," extractor",[2225,143408,2789],{"class":2307},[2225,143410,5672],{"class":2239},[2225,143412,2298],{"class":2235},[2225,143414,2236],{"class":2235},[2225,143416,143417],{"class":2307}," pooling",[2225,143419,2311],{"class":2235},[2225,143421,2249],{"class":2235},[2225,143423,143424],{"class":2252},"mean",[2225,143426,2805],{"class":2235},[2225,143428,2298],{"class":2235},[2225,143430,65566],{"class":2307},[2225,143432,2311],{"class":2235},[2225,143434,2322],{"class":2321},[2225,143436,2243],{"class":2235},[2225,143438,2362],{"class":2307},[2225,143440,143441,143443,143445,143448,143450,143452,143454,143456],{"class":2227,"line":2861},[2225,143442,8220],{"class":2231},[2225,143444,8130],{"class":2235},[2225,143446,143447],{"class":2291}," Float32Array",[2225,143449,2789],{"class":2307},[2225,143451,13557],{"class":2239},[2225,143453,1520],{"class":2235},[2225,143455,8673],{"class":2239},[2225,143457,2362],{"class":2307},[2225,143459,143460],{"class":2227,"line":4246},[2225,143461,7351],{"class":2235},[2225,143463,143464],{"class":2227,"line":4253},[2225,143465,143466],{"class":2763},"\u002F\u002F collection config → dim: 384, model: 'all-MiniLM-L6-v2'\n",[2217,143468,143471],{"className":2219,"code":143469,"filename":143470,"language":2221,"meta":1037,"style":1037},"\u002F\u002F Hosted API — sees your text in plaintext. Matches the dim\u002Fmodel in the config above.\nasync function myEmbeddingApi(text: string): Promise\u003CFloat32Array> {\n  const res = await fetch('https:\u002F\u002Fapi.openai.com\u002Fv1\u002Fembeddings', {\n    method: 'POST',\n    headers: {\n      Authorization: `Bearer ${process.env.OPENAI_API_KEY}`,\n      'Content-Type': 'application\u002Fjson',\n    },\n    body: JSON.stringify({ model: 'text-embedding-3-small', input: text }),\n  })\n  const { data } = await res.json()\n  return new Float32Array(data[0].embedding)\n}\n\u002F\u002F collection config → dim: 1536, model: 'text-embedding-3-small'\n","OpenAI",[941,143472,143473,143478,143506,143532,143548,143556,143585,143604,143608,143649,143655,143677,143702,143706],{"__ignoreMap":1037},[2225,143474,143475],{"class":2227,"line":2228},[2225,143476,143477],{"class":2763},"\u002F\u002F Hosted API — sees your text in plaintext. Matches the dim\u002Fmodel in the config above.\n",[2225,143479,143480,143482,143484,143486,143488,143490,143492,143494,143496,143498,143500,143502,143504],{"class":2227,"line":1041},[2225,143481,70491],{"class":2774},[2225,143483,40856],{"class":2774},[2225,143485,143145],{"class":2291},[2225,143487,2789],{"class":2235},[2225,143489,5672],{"class":7052},[2225,143491,2311],{"class":2235},[2225,143493,6366],{"class":6342},[2225,143495,6255],{"class":2235},[2225,143497,7079],{"class":6342},[2225,143499,6474],{"class":2235},[2225,143501,75686],{"class":6342},[2225,143503,6480],{"class":2235},[2225,143505,2301],{"class":2235},[2225,143507,143508,143510,143513,143515,143517,143519,143521,143523,143526,143528,143530],{"class":2227,"line":1038},[2225,143509,8123],{"class":2774},[2225,143511,143512],{"class":2239}," res",[2225,143514,6346],{"class":2235},[2225,143516,2784],{"class":2231},[2225,143518,89756],{"class":2291},[2225,143520,2789],{"class":2307},[2225,143522,2805],{"class":2235},[2225,143524,143525],{"class":2252},"https:\u002F\u002Fapi.openai.com\u002Fv1\u002Fembeddings",[2225,143527,2805],{"class":2235},[2225,143529,2298],{"class":2235},[2225,143531,2301],{"class":2235},[2225,143533,143534,143537,143539,143541,143544,143546],{"class":2227,"line":2288},[2225,143535,143536],{"class":2307},"    method",[2225,143538,2311],{"class":2235},[2225,143540,2249],{"class":2235},[2225,143542,143543],{"class":2252},"POST",[2225,143545,2805],{"class":2235},[2225,143547,1707],{"class":2235},[2225,143549,143550,143552,143554],{"class":2227,"line":2304},[2225,143551,52379],{"class":2307},[2225,143553,2311],{"class":2235},[2225,143555,2301],{"class":2235},[2225,143557,143558,143561,143563,143565,143567,143569,143572,143574,143576,143578,143581,143583],{"class":2227,"line":2356},[2225,143559,143560],{"class":2307},"      Authorization",[2225,143562,2311],{"class":2235},[2225,143564,43789],{"class":2235},[2225,143566,52393],{"class":2252},[2225,143568,8197],{"class":2235},[2225,143570,143571],{"class":2239},"process",[2225,143573,1520],{"class":2235},[2225,143575,183],{"class":2239},[2225,143577,1520],{"class":2235},[2225,143579,143580],{"class":2239},"OPENAI_API_KEY",[2225,143582,43799],{"class":2235},[2225,143584,1707],{"class":2235},[2225,143586,143587,143589,143591,143593,143595,143597,143600,143602],{"class":2227,"line":2830},[2225,143588,18944],{"class":2235},[2225,143590,110395],{"class":2307},[2225,143592,2805],{"class":2235},[2225,143594,2311],{"class":2235},[2225,143596,2249],{"class":2235},[2225,143598,143599],{"class":2252},"application\u002Fjson",[2225,143601,2805],{"class":2235},[2225,143603,1707],{"class":2235},[2225,143605,143606],{"class":2227,"line":2861},[2225,143607,8324],{"class":2235},[2225,143609,143610,143612,143614,143616,143618,143620,143622,143624,143626,143628,143630,143632,143634,143636,143639,143641,143643,143645,143647],{"class":2227,"line":4246},[2225,143611,114587],{"class":2307},[2225,143613,2311],{"class":2235},[2225,143615,15793],{"class":2239},[2225,143617,1520],{"class":2235},[2225,143619,85490],{"class":2291},[2225,143621,2789],{"class":2307},[2225,143623,2792],{"class":2235},[2225,143625,95063],{"class":2307},[2225,143627,2311],{"class":2235},[2225,143629,2249],{"class":2235},[2225,143631,143179],{"class":2252},[2225,143633,2805],{"class":2235},[2225,143635,2298],{"class":2235},[2225,143637,143638],{"class":2307}," input",[2225,143640,2311],{"class":2235},[2225,143642,82994],{"class":2239},[2225,143644,2243],{"class":2235},[2225,143646,1381],{"class":2307},[2225,143648,1707],{"class":2235},[2225,143650,143651,143653],{"class":2227,"line":4253},[2225,143652,19143],{"class":2235},[2225,143654,2362],{"class":2307},[2225,143656,143657,143659,143661,143663,143665,143667,143669,143671,143673,143675],{"class":2227,"line":4259},[2225,143658,8123],{"class":2774},[2225,143660,2236],{"class":2235},[2225,143662,7330],{"class":2239},[2225,143664,2243],{"class":2235},[2225,143666,6346],{"class":2235},[2225,143668,2784],{"class":2231},[2225,143670,143512],{"class":2239},[2225,143672,1520],{"class":2235},[2225,143674,141],{"class":2291},[2225,143676,8146],{"class":2307},[2225,143678,143679,143681,143683,143685,143687,143689,143691,143693,143695,143697,143700],{"class":2227,"line":6548},[2225,143680,8220],{"class":2231},[2225,143682,8130],{"class":2235},[2225,143684,143447],{"class":2291},[2225,143686,2789],{"class":2307},[2225,143688,8673],{"class":2239},[2225,143690,8885],{"class":2307},[2225,143692,5402],{"class":6527},[2225,143694,8803],{"class":2307},[2225,143696,1520],{"class":2235},[2225,143698,143699],{"class":2239},"embedding",[2225,143701,2362],{"class":2307},[2225,143703,143704],{"class":2227,"line":6576},[2225,143705,7351],{"class":2235},[2225,143707,143708],{"class":2227,"line":6582},[2225,143709,143710],{"class":2763},"\u002F\u002F collection config → dim: 1536, model: 'text-embedding-3-small'\n",[2217,143712,143715],{"className":2219,"code":143713,"filename":143714,"language":2221,"meta":1037,"style":1037},"\u002F\u002F Anthropic ships no embeddings endpoint — Claude is generative-only. Its\n\u002F\u002F recommended embeddings provider is Voyage AI. Hosted, so it sees plaintext.\nasync function myEmbeddingApi(text: string): Promise\u003CFloat32Array> {\n  const res = await fetch('https:\u002F\u002Fapi.voyageai.com\u002Fv1\u002Fembeddings', {\n    method: 'POST',\n    headers: {\n      Authorization: `Bearer ${process.env.VOYAGE_API_KEY}`,\n      'Content-Type': 'application\u002Fjson',\n    },\n    body: JSON.stringify({ model: 'voyage-3', input: text }),\n  })\n  const { data } = await res.json()\n  return new Float32Array(data[0].embedding)\n}\n\u002F\u002F collection config → dim: 1024, model: 'voyage-3'\n","Anthropic → Voyage",[941,143716,143717,143722,143727,143755,143780,143794,143802,143829,143847,143851,143892,143898,143920,143944,143948],{"__ignoreMap":1037},[2225,143718,143719],{"class":2227,"line":2228},[2225,143720,143721],{"class":2763},"\u002F\u002F Anthropic ships no embeddings endpoint — Claude is generative-only. Its\n",[2225,143723,143724],{"class":2227,"line":1041},[2225,143725,143726],{"class":2763},"\u002F\u002F recommended embeddings provider is Voyage AI. Hosted, so it sees plaintext.\n",[2225,143728,143729,143731,143733,143735,143737,143739,143741,143743,143745,143747,143749,143751,143753],{"class":2227,"line":1038},[2225,143730,70491],{"class":2774},[2225,143732,40856],{"class":2774},[2225,143734,143145],{"class":2291},[2225,143736,2789],{"class":2235},[2225,143738,5672],{"class":7052},[2225,143740,2311],{"class":2235},[2225,143742,6366],{"class":6342},[2225,143744,6255],{"class":2235},[2225,143746,7079],{"class":6342},[2225,143748,6474],{"class":2235},[2225,143750,75686],{"class":6342},[2225,143752,6480],{"class":2235},[2225,143754,2301],{"class":2235},[2225,143756,143757,143759,143761,143763,143765,143767,143769,143771,143774,143776,143778],{"class":2227,"line":2288},[2225,143758,8123],{"class":2774},[2225,143760,143512],{"class":2239},[2225,143762,6346],{"class":2235},[2225,143764,2784],{"class":2231},[2225,143766,89756],{"class":2291},[2225,143768,2789],{"class":2307},[2225,143770,2805],{"class":2235},[2225,143772,143773],{"class":2252},"https:\u002F\u002Fapi.voyageai.com\u002Fv1\u002Fembeddings",[2225,143775,2805],{"class":2235},[2225,143777,2298],{"class":2235},[2225,143779,2301],{"class":2235},[2225,143781,143782,143784,143786,143788,143790,143792],{"class":2227,"line":2304},[2225,143783,143536],{"class":2307},[2225,143785,2311],{"class":2235},[2225,143787,2249],{"class":2235},[2225,143789,143543],{"class":2252},[2225,143791,2805],{"class":2235},[2225,143793,1707],{"class":2235},[2225,143795,143796,143798,143800],{"class":2227,"line":2356},[2225,143797,52379],{"class":2307},[2225,143799,2311],{"class":2235},[2225,143801,2301],{"class":2235},[2225,143803,143804,143806,143808,143810,143812,143814,143816,143818,143820,143822,143825,143827],{"class":2227,"line":2830},[2225,143805,143560],{"class":2307},[2225,143807,2311],{"class":2235},[2225,143809,43789],{"class":2235},[2225,143811,52393],{"class":2252},[2225,143813,8197],{"class":2235},[2225,143815,143571],{"class":2239},[2225,143817,1520],{"class":2235},[2225,143819,183],{"class":2239},[2225,143821,1520],{"class":2235},[2225,143823,143824],{"class":2239},"VOYAGE_API_KEY",[2225,143826,43799],{"class":2235},[2225,143828,1707],{"class":2235},[2225,143830,143831,143833,143835,143837,143839,143841,143843,143845],{"class":2227,"line":2861},[2225,143832,18944],{"class":2235},[2225,143834,110395],{"class":2307},[2225,143836,2805],{"class":2235},[2225,143838,2311],{"class":2235},[2225,143840,2249],{"class":2235},[2225,143842,143599],{"class":2252},[2225,143844,2805],{"class":2235},[2225,143846,1707],{"class":2235},[2225,143848,143849],{"class":2227,"line":4246},[2225,143850,8324],{"class":2235},[2225,143852,143853,143855,143857,143859,143861,143863,143865,143867,143869,143871,143873,143876,143878,143880,143882,143884,143886,143888,143890],{"class":2227,"line":4253},[2225,143854,114587],{"class":2307},[2225,143856,2311],{"class":2235},[2225,143858,15793],{"class":2239},[2225,143860,1520],{"class":2235},[2225,143862,85490],{"class":2291},[2225,143864,2789],{"class":2307},[2225,143866,2792],{"class":2235},[2225,143868,95063],{"class":2307},[2225,143870,2311],{"class":2235},[2225,143872,2249],{"class":2235},[2225,143874,143875],{"class":2252},"voyage-3",[2225,143877,2805],{"class":2235},[2225,143879,2298],{"class":2235},[2225,143881,143638],{"class":2307},[2225,143883,2311],{"class":2235},[2225,143885,82994],{"class":2239},[2225,143887,2243],{"class":2235},[2225,143889,1381],{"class":2307},[2225,143891,1707],{"class":2235},[2225,143893,143894,143896],{"class":2227,"line":4259},[2225,143895,19143],{"class":2235},[2225,143897,2362],{"class":2307},[2225,143899,143900,143902,143904,143906,143908,143910,143912,143914,143916,143918],{"class":2227,"line":6548},[2225,143901,8123],{"class":2774},[2225,143903,2236],{"class":2235},[2225,143905,7330],{"class":2239},[2225,143907,2243],{"class":2235},[2225,143909,6346],{"class":2235},[2225,143911,2784],{"class":2231},[2225,143913,143512],{"class":2239},[2225,143915,1520],{"class":2235},[2225,143917,141],{"class":2291},[2225,143919,8146],{"class":2307},[2225,143921,143922,143924,143926,143928,143930,143932,143934,143936,143938,143940,143942],{"class":2227,"line":6576},[2225,143923,8220],{"class":2231},[2225,143925,8130],{"class":2235},[2225,143927,143447],{"class":2291},[2225,143929,2789],{"class":2307},[2225,143931,8673],{"class":2239},[2225,143933,8885],{"class":2307},[2225,143935,5402],{"class":6527},[2225,143937,8803],{"class":2307},[2225,143939,1520],{"class":2235},[2225,143941,143699],{"class":2239},[2225,143943,2362],{"class":2307},[2225,143945,143946],{"class":2227,"line":6582},[2225,143947,7351],{"class":2235},[2225,143949,143950],{"class":2227,"line":6587},[2225,143951,143952],{"class":2763},"\u002F\u002F collection config → dim: 1024, model: 'voyage-3'\n",[2217,143954,143957],{"className":2219,"code":143955,"filename":143956,"language":2221,"meta":1037,"style":1037},"\u002F\u002F A local Ollama server — on-device, larger models than run in-browser.\nasync function myEmbeddingApi(text: string): Promise\u003CFloat32Array> {\n  const res = await fetch('http:\u002F\u002Flocalhost:11434\u002Fapi\u002Fembeddings', {\n    method: 'POST',\n    body: JSON.stringify({ model: 'nomic-embed-text', prompt: text }),\n  })\n  const { embedding } = await res.json()\n  return new Float32Array(embedding)\n}\n\u002F\u002F collection config → dim: 768, model: 'nomic-embed-text'\n","Local · Ollama",[941,143958,143959,143964,143992,144017,144031,144072,144078,144101,144115,144119],{"__ignoreMap":1037},[2225,143960,143961],{"class":2227,"line":2228},[2225,143962,143963],{"class":2763},"\u002F\u002F A local Ollama server — on-device, larger models than run in-browser.\n",[2225,143965,143966,143968,143970,143972,143974,143976,143978,143980,143982,143984,143986,143988,143990],{"class":2227,"line":1041},[2225,143967,70491],{"class":2774},[2225,143969,40856],{"class":2774},[2225,143971,143145],{"class":2291},[2225,143973,2789],{"class":2235},[2225,143975,5672],{"class":7052},[2225,143977,2311],{"class":2235},[2225,143979,6366],{"class":6342},[2225,143981,6255],{"class":2235},[2225,143983,7079],{"class":6342},[2225,143985,6474],{"class":2235},[2225,143987,75686],{"class":6342},[2225,143989,6480],{"class":2235},[2225,143991,2301],{"class":2235},[2225,143993,143994,143996,143998,144000,144002,144004,144006,144008,144011,144013,144015],{"class":2227,"line":1038},[2225,143995,8123],{"class":2774},[2225,143997,143512],{"class":2239},[2225,143999,6346],{"class":2235},[2225,144001,2784],{"class":2231},[2225,144003,89756],{"class":2291},[2225,144005,2789],{"class":2307},[2225,144007,2805],{"class":2235},[2225,144009,144010],{"class":2252},"http:\u002F\u002Flocalhost:11434\u002Fapi\u002Fembeddings",[2225,144012,2805],{"class":2235},[2225,144014,2298],{"class":2235},[2225,144016,2301],{"class":2235},[2225,144018,144019,144021,144023,144025,144027,144029],{"class":2227,"line":2288},[2225,144020,143536],{"class":2307},[2225,144022,2311],{"class":2235},[2225,144024,2249],{"class":2235},[2225,144026,143543],{"class":2252},[2225,144028,2805],{"class":2235},[2225,144030,1707],{"class":2235},[2225,144032,144033,144035,144037,144039,144041,144043,144045,144047,144049,144051,144053,144056,144058,144060,144062,144064,144066,144068,144070],{"class":2227,"line":2304},[2225,144034,114587],{"class":2307},[2225,144036,2311],{"class":2235},[2225,144038,15793],{"class":2239},[2225,144040,1520],{"class":2235},[2225,144042,85490],{"class":2291},[2225,144044,2789],{"class":2307},[2225,144046,2792],{"class":2235},[2225,144048,95063],{"class":2307},[2225,144050,2311],{"class":2235},[2225,144052,2249],{"class":2235},[2225,144054,144055],{"class":2252},"nomic-embed-text",[2225,144057,2805],{"class":2235},[2225,144059,2298],{"class":2235},[2225,144061,76947],{"class":2307},[2225,144063,2311],{"class":2235},[2225,144065,82994],{"class":2239},[2225,144067,2243],{"class":2235},[2225,144069,1381],{"class":2307},[2225,144071,1707],{"class":2235},[2225,144073,144074,144076],{"class":2227,"line":2356},[2225,144075,19143],{"class":2235},[2225,144077,2362],{"class":2307},[2225,144079,144080,144082,144084,144087,144089,144091,144093,144095,144097,144099],{"class":2227,"line":2830},[2225,144081,8123],{"class":2774},[2225,144083,2236],{"class":2235},[2225,144085,144086],{"class":2239}," embedding",[2225,144088,2243],{"class":2235},[2225,144090,6346],{"class":2235},[2225,144092,2784],{"class":2231},[2225,144094,143512],{"class":2239},[2225,144096,1520],{"class":2235},[2225,144098,141],{"class":2291},[2225,144100,8146],{"class":2307},[2225,144102,144103,144105,144107,144109,144111,144113],{"class":2227,"line":2861},[2225,144104,8220],{"class":2231},[2225,144106,8130],{"class":2235},[2225,144108,143447],{"class":2291},[2225,144110,2789],{"class":2307},[2225,144112,143699],{"class":2239},[2225,144114,2362],{"class":2307},[2225,144116,144117],{"class":2227,"line":4246},[2225,144118,7351],{"class":2235},[2225,144120,144121],{"class":2227,"line":4253},[2225,144122,144123],{"class":2763},"\u002F\u002F collection config → dim: 768, model: 'nomic-embed-text'\n",[937,144125,90416,144126,144128,144129,968,144131,144134,144135,144137],{},[941,144127,94750],{},", every method below (",[941,144130,75590],{},[941,144132,144133],{},"similarTo",", and the write-time embedding compute) throws ",[941,144136,22160],{}," telling you to opt in.",[1022,144139,29893],{"id":29892},[1591,144141,144143,144144],{"id":144142},"semantic-retrieval-retrievequery-mode-semantic","Semantic retrieval — ",[941,144145,144146],{},"retrieve(query, { mode: 'semantic' })",[2217,144148,144150],{"className":2219,"code":144149,"language":2221,"meta":1037,"style":1037},"const hits = await docs.retrieve('overdue invoice', { mode: 'semantic' })\n",[941,144151,144152],{"__ignoreMap":1037},[2225,144153,144154,144156,144158,144160,144162,144164,144166,144168,144170,144172,144175,144177,144179,144181,144183,144185,144187,144189,144191,144193],{"class":2227,"line":2228},[2225,144155,2775],{"class":2774},[2225,144157,75579],{"class":2239},[2225,144159,2781],{"class":2235},[2225,144161,2784],{"class":2231},[2225,144163,90184],{"class":2239},[2225,144165,1520],{"class":2235},[2225,144167,75590],{"class":2291},[2225,144169,2789],{"class":2239},[2225,144171,2805],{"class":2235},[2225,144173,144174],{"class":2252},"overdue invoice",[2225,144176,2805],{"class":2235},[2225,144178,2298],{"class":2235},[2225,144180,2236],{"class":2235},[2225,144182,32303],{"class":2307},[2225,144184,2311],{"class":2235},[2225,144186,2249],{"class":2235},[2225,144188,75612],{"class":2252},[2225,144190,2805],{"class":2235},[2225,144192,2243],{"class":2235},[2225,144194,2362],{"class":2239},[2217,144196,144198],{"className":6604,"code":144197,"language":141,"meta":1037,"style":1037},"[\n  { \"id\": \"inv-0231\", \"score\": 0.912, \"rank\": 1, \"field\": \"(vector)\", \"snippet\": \"\" },\n  { \"id\": \"inv-0198\", \"score\": 0.887, \"rank\": 2, \"field\": \"(vector)\", \"snippet\": \"\" },\n  { \"id\": \"inv-0104\", \"score\": 0.845, \"rank\": 3, \"field\": \"(vector)\", \"snippet\": \"\" }\n]\n",[941,144199,144200,144204,144282,144356,144430],{"__ignoreMap":1037},[2225,144201,144202],{"class":2227,"line":2228},[2225,144203,26349],{"class":2235},[2225,144205,144206,144208,144210,144212,144214,144216,144218,144221,144223,144225,144227,144230,144232,144234,144237,144239,144241,144244,144246,144248,144250,144252,144254,144256,144258,144260,144262,144265,144267,144269,144271,144274,144276,144278,144280],{"class":2227,"line":1041},[2225,144207,26354],{"class":2235},[2225,144209,6614],{"class":2235},[2225,144211,6983],{"class":2774},[2225,144213,6620],{"class":2235},[2225,144215,2311],{"class":2235},[2225,144217,6614],{"class":2235},[2225,144219,144220],{"class":2252},"inv-0231",[2225,144222,6620],{"class":2235},[2225,144224,2298],{"class":2235},[2225,144226,6614],{"class":2235},[2225,144228,144229],{"class":2774},"score",[2225,144231,6620],{"class":2235},[2225,144233,2311],{"class":2235},[2225,144235,144236],{"class":6527}," 0.912",[2225,144238,2298],{"class":2235},[2225,144240,6614],{"class":2235},[2225,144242,144243],{"class":2774},"rank",[2225,144245,6620],{"class":2235},[2225,144247,2311],{"class":2235},[2225,144249,9377],{"class":6527},[2225,144251,2298],{"class":2235},[2225,144253,6614],{"class":2235},[2225,144255,25197],{"class":2774},[2225,144257,6620],{"class":2235},[2225,144259,2311],{"class":2235},[2225,144261,6614],{"class":2235},[2225,144263,144264],{"class":2252},"(vector)",[2225,144266,6620],{"class":2235},[2225,144268,2298],{"class":2235},[2225,144270,6614],{"class":2235},[2225,144272,144273],{"class":2774},"snippet",[2225,144275,6620],{"class":2235},[2225,144277,2311],{"class":2235},[2225,144279,60469],{"class":2235},[2225,144281,2353],{"class":2235},[2225,144283,144284,144286,144288,144290,144292,144294,144296,144299,144301,144303,144305,144307,144309,144311,144314,144316,144318,144320,144322,144324,144326,144328,144330,144332,144334,144336,144338,144340,144342,144344,144346,144348,144350,144352,144354],{"class":2227,"line":1038},[2225,144285,26354],{"class":2235},[2225,144287,6614],{"class":2235},[2225,144289,6983],{"class":2774},[2225,144291,6620],{"class":2235},[2225,144293,2311],{"class":2235},[2225,144295,6614],{"class":2235},[2225,144297,144298],{"class":2252},"inv-0198",[2225,144300,6620],{"class":2235},[2225,144302,2298],{"class":2235},[2225,144304,6614],{"class":2235},[2225,144306,144229],{"class":2774},[2225,144308,6620],{"class":2235},[2225,144310,2311],{"class":2235},[2225,144312,144313],{"class":6527}," 0.887",[2225,144315,2298],{"class":2235},[2225,144317,6614],{"class":2235},[2225,144319,144243],{"class":2774},[2225,144321,6620],{"class":2235},[2225,144323,2311],{"class":2235},[2225,144325,18961],{"class":6527},[2225,144327,2298],{"class":2235},[2225,144329,6614],{"class":2235},[2225,144331,25197],{"class":2774},[2225,144333,6620],{"class":2235},[2225,144335,2311],{"class":2235},[2225,144337,6614],{"class":2235},[2225,144339,144264],{"class":2252},[2225,144341,6620],{"class":2235},[2225,144343,2298],{"class":2235},[2225,144345,6614],{"class":2235},[2225,144347,144273],{"class":2774},[2225,144349,6620],{"class":2235},[2225,144351,2311],{"class":2235},[2225,144353,60469],{"class":2235},[2225,144355,2353],{"class":2235},[2225,144357,144358,144360,144362,144364,144366,144368,144370,144373,144375,144377,144379,144381,144383,144385,144388,144390,144392,144394,144396,144398,144400,144402,144404,144406,144408,144410,144412,144414,144416,144418,144420,144422,144424,144426,144428],{"class":2227,"line":2288},[2225,144359,26354],{"class":2235},[2225,144361,6614],{"class":2235},[2225,144363,6983],{"class":2774},[2225,144365,6620],{"class":2235},[2225,144367,2311],{"class":2235},[2225,144369,6614],{"class":2235},[2225,144371,144372],{"class":2252},"inv-0104",[2225,144374,6620],{"class":2235},[2225,144376,2298],{"class":2235},[2225,144378,6614],{"class":2235},[2225,144380,144229],{"class":2774},[2225,144382,6620],{"class":2235},[2225,144384,2311],{"class":2235},[2225,144386,144387],{"class":6527}," 0.845",[2225,144389,2298],{"class":2235},[2225,144391,6614],{"class":2235},[2225,144393,144243],{"class":2774},[2225,144395,6620],{"class":2235},[2225,144397,2311],{"class":2235},[2225,144399,14460],{"class":6527},[2225,144401,2298],{"class":2235},[2225,144403,6614],{"class":2235},[2225,144405,25197],{"class":2774},[2225,144407,6620],{"class":2235},[2225,144409,2311],{"class":2235},[2225,144411,6614],{"class":2235},[2225,144413,144264],{"class":2252},[2225,144415,6620],{"class":2235},[2225,144417,2298],{"class":2235},[2225,144419,6614],{"class":2235},[2225,144421,144273],{"class":2774},[2225,144423,6620],{"class":2235},[2225,144425,2311],{"class":2235},[2225,144427,60469],{"class":2235},[2225,144429,6369],{"class":2235},[2225,144431,144432],{"class":2227,"line":2304},[2225,144433,26484],{"class":2235},[937,144435,144436,144438,144439,144441,144442,144445],{},[941,144437,144229],{}," is cosine similarity (higher is closer). ",[941,144440,25197],{}," is always the literal string ",[941,144443,144444],{},"'(vector)'"," for a semantic hit — there's no matched field span to report, unlike a lexical hit. Options:",[2217,144447,144449],{"className":2219,"code":144448,"language":2221,"meta":1037,"style":1037},"\u002F\u002F Limit to top 5 (defaults to 10 when omitted):\nconst top5 = await docs.retrieve('overdue invoice', { mode: 'semantic', limit: 5 })\n\u002F\u002F → same shape as the `hits` array above, truncated to at most 5 entries\n\n\u002F\u002F Only hits above a cosine-similarity floor:\nconst strong = await docs.retrieve('quarterly report', { mode: 'semantic', minScore: 0.8 })\n",[941,144450,144451,144456,144507,144512,144516,144521],{"__ignoreMap":1037},[2225,144452,144453],{"class":2227,"line":2228},[2225,144454,144455],{"class":2763},"\u002F\u002F Limit to top 5 (defaults to 10 when omitted):\n",[2225,144457,144458,144460,144463,144465,144467,144469,144471,144473,144475,144477,144479,144481,144483,144485,144487,144489,144491,144493,144495,144497,144499,144501,144503,144505],{"class":2227,"line":1041},[2225,144459,2775],{"class":2774},[2225,144461,144462],{"class":2239}," top5 ",[2225,144464,2781],{"class":2235},[2225,144466,2784],{"class":2231},[2225,144468,90184],{"class":2239},[2225,144470,1520],{"class":2235},[2225,144472,75590],{"class":2291},[2225,144474,2789],{"class":2239},[2225,144476,2805],{"class":2235},[2225,144478,144174],{"class":2252},[2225,144480,2805],{"class":2235},[2225,144482,2298],{"class":2235},[2225,144484,2236],{"class":2235},[2225,144486,32303],{"class":2307},[2225,144488,2311],{"class":2235},[2225,144490,2249],{"class":2235},[2225,144492,75612],{"class":2252},[2225,144494,2805],{"class":2235},[2225,144496,2298],{"class":2235},[2225,144498,37205],{"class":2307},[2225,144500,2311],{"class":2235},[2225,144502,18996],{"class":6527},[2225,144504,2243],{"class":2235},[2225,144506,2362],{"class":2239},[2225,144508,144509],{"class":2227,"line":1038},[2225,144510,144511],{"class":2763},"\u002F\u002F → same shape as the `hits` array above, truncated to at most 5 entries\n",[2225,144513,144514],{"class":2227,"line":2288},[2225,144515,2285],{"emptyLinePlaceholder":2284},[2225,144517,144518],{"class":2227,"line":2304},[2225,144519,144520],{"class":2763},"\u002F\u002F Only hits above a cosine-similarity floor:\n",[2225,144522,144523,144525,144528,144530,144532,144534,144536,144538,144540,144542,144545,144547,144549,144551,144553,144555,144557,144559,144561,144563,144566,144568,144571,144573],{"class":2227,"line":2356},[2225,144524,2775],{"class":2774},[2225,144526,144527],{"class":2239}," strong ",[2225,144529,2781],{"class":2235},[2225,144531,2784],{"class":2231},[2225,144533,90184],{"class":2239},[2225,144535,1520],{"class":2235},[2225,144537,75590],{"class":2291},[2225,144539,2789],{"class":2239},[2225,144541,2805],{"class":2235},[2225,144543,144544],{"class":2252},"quarterly report",[2225,144546,2805],{"class":2235},[2225,144548,2298],{"class":2235},[2225,144550,2236],{"class":2235},[2225,144552,32303],{"class":2307},[2225,144554,2311],{"class":2235},[2225,144556,2249],{"class":2235},[2225,144558,75612],{"class":2252},[2225,144560,2805],{"class":2235},[2225,144562,2298],{"class":2235},[2225,144564,144565],{"class":2307}," minScore",[2225,144567,2311],{"class":2235},[2225,144569,144570],{"class":6527}," 0.8",[2225,144572,2243],{"class":2235},[2225,144574,2362],{"class":2239},[2217,144576,144578],{"className":6604,"code":144577,"language":141,"meta":1037,"style":1037},"[\n  { \"id\": \"rpt-Q2-2026\", \"score\": 0.913, \"rank\": 1, \"field\": \"(vector)\", \"snippet\": \"\" }\n]\n",[941,144579,144580,144584,144658],{"__ignoreMap":1037},[2225,144581,144582],{"class":2227,"line":2228},[2225,144583,26349],{"class":2235},[2225,144585,144586,144588,144590,144592,144594,144596,144598,144601,144603,144605,144607,144609,144611,144613,144616,144618,144620,144622,144624,144626,144628,144630,144632,144634,144636,144638,144640,144642,144644,144646,144648,144650,144652,144654,144656],{"class":2227,"line":1041},[2225,144587,26354],{"class":2235},[2225,144589,6614],{"class":2235},[2225,144591,6983],{"class":2774},[2225,144593,6620],{"class":2235},[2225,144595,2311],{"class":2235},[2225,144597,6614],{"class":2235},[2225,144599,144600],{"class":2252},"rpt-Q2-2026",[2225,144602,6620],{"class":2235},[2225,144604,2298],{"class":2235},[2225,144606,6614],{"class":2235},[2225,144608,144229],{"class":2774},[2225,144610,6620],{"class":2235},[2225,144612,2311],{"class":2235},[2225,144614,144615],{"class":6527}," 0.913",[2225,144617,2298],{"class":2235},[2225,144619,6614],{"class":2235},[2225,144621,144243],{"class":2774},[2225,144623,6620],{"class":2235},[2225,144625,2311],{"class":2235},[2225,144627,9377],{"class":6527},[2225,144629,2298],{"class":2235},[2225,144631,6614],{"class":2235},[2225,144633,25197],{"class":2774},[2225,144635,6620],{"class":2235},[2225,144637,2311],{"class":2235},[2225,144639,6614],{"class":2235},[2225,144641,144264],{"class":2252},[2225,144643,6620],{"class":2235},[2225,144645,2298],{"class":2235},[2225,144647,6614],{"class":2235},[2225,144649,144273],{"class":2774},[2225,144651,6620],{"class":2235},[2225,144653,2311],{"class":2235},[2225,144655,60469],{"class":2235},[2225,144657,6369],{"class":2235},[2225,144659,144660],{"class":2227,"line":1038},[2225,144661,26484],{"class":2235},[937,144663,144664,144665,144668,144669,144671],{},"Hits with ",[941,144666,144667],{},"score \u003C 0.8"," are dropped entirely, not just ranked lower — that's why ",[941,144670,950],{}," can come back shorter than an unfiltered call even when more records exist.",[1591,144673,144675,138087],{"id":144674},"retrievehitt-shape",[941,144676,144677],{},"RetrieveHit\u003CT>",[2217,144679,144681],{"className":2219,"code":144680,"language":2221,"meta":1037,"style":1037},"interface RetrieveHit\u003CT> {\n  readonly id: string\n  readonly score: number    \u002F\u002F cosine similarity for semantic hits; RRF score for hybrid hits (see below)\n  readonly rank: number     \u002F\u002F 1-based\n  readonly field: string    \u002F\u002F '(vector)' for a semantic-only hit\n  readonly snippet: string  \u002F\u002F '' for a semantic-only hit — there's no text span to extract\n  readonly locale?: string  \u002F\u002F set only on lexical hits sourced from an i18n\u002FdictKey field\n  readonly record?: T       \u002F\u002F present only when includeRecord: true\n}\n",[941,144682,144683,144698,144708,144722,144736,144749,144763,144776,144789],{"__ignoreMap":1037},[2225,144684,144685,144687,144690,144692,144694,144696],{"class":2227,"line":2228},[2225,144686,6994],{"class":2774},[2225,144688,144689],{"class":6342}," RetrieveHit",[2225,144691,6474],{"class":2235},[2225,144693,11968],{"class":6342},[2225,144695,6480],{"class":2235},[2225,144697,2301],{"class":2235},[2225,144699,144700,144702,144704,144706],{"class":2227,"line":1041},[2225,144701,53580],{"class":2774},[2225,144703,7070],{"class":2307},[2225,144705,2311],{"class":2235},[2225,144707,7014],{"class":6342},[2225,144709,144710,144712,144715,144717,144719],{"class":2227,"line":1038},[2225,144711,53580],{"class":2774},[2225,144713,144714],{"class":2307}," score",[2225,144716,2311],{"class":2235},[2225,144718,6356],{"class":6342},[2225,144720,144721],{"class":2763},"    \u002F\u002F cosine similarity for semantic hits; RRF score for hybrid hits (see below)\n",[2225,144723,144724,144726,144729,144731,144733],{"class":2227,"line":2288},[2225,144725,53580],{"class":2774},[2225,144727,144728],{"class":2307}," rank",[2225,144730,2311],{"class":2235},[2225,144732,6356],{"class":6342},[2225,144734,144735],{"class":2763},"     \u002F\u002F 1-based\n",[2225,144737,144738,144740,144742,144744,144746],{"class":2227,"line":2304},[2225,144739,53580],{"class":2774},[2225,144741,124557],{"class":2307},[2225,144743,2311],{"class":2235},[2225,144745,6366],{"class":6342},[2225,144747,144748],{"class":2763},"    \u002F\u002F '(vector)' for a semantic-only hit\n",[2225,144750,144751,144753,144756,144758,144760],{"class":2227,"line":2356},[2225,144752,53580],{"class":2774},[2225,144754,144755],{"class":2307}," snippet",[2225,144757,2311],{"class":2235},[2225,144759,6366],{"class":6342},[2225,144761,144762],{"class":2763},"  \u002F\u002F '' for a semantic-only hit — there's no text span to extract\n",[2225,144764,144765,144767,144769,144771,144773],{"class":2227,"line":2830},[2225,144766,53580],{"class":2774},[2225,144768,24003],{"class":2307},[2225,144770,7011],{"class":2235},[2225,144772,6366],{"class":6342},[2225,144774,144775],{"class":2763},"  \u002F\u002F set only on lexical hits sourced from an i18n\u002FdictKey field\n",[2225,144777,144778,144780,144782,144784,144786],{"class":2227,"line":2861},[2225,144779,53580],{"class":2774},[2225,144781,24268],{"class":2307},[2225,144783,7011],{"class":2235},[2225,144785,24772],{"class":6342},[2225,144787,144788],{"class":2763},"       \u002F\u002F present only when includeRecord: true\n",[2225,144790,144791],{"class":2227,"line":4246},[2225,144792,7351],{"class":2235},[1591,144794,144796,144799],{"id":144795},"collectionsimilartovector-opts-bring-your-own-vector",[941,144797,144798],{},"collection.similarTo(vector, opts?)"," — bring your own vector",[937,144801,144802],{},"Use this when you've already encoded the query yourself (or you're comparing against a vector that didn't come from a text query at all).",[2217,144804,144806],{"className":2219,"code":144805,"language":2221,"meta":1037,"style":1037},"const qVec = await myEncoder('quarterly report')\nconst hits = await docs.similarTo(qVec, { k: 10 })\n",[941,144807,144808,144832],{"__ignoreMap":1037},[2225,144809,144810,144812,144815,144817,144819,144822,144824,144826,144828,144830],{"class":2227,"line":2228},[2225,144811,2775],{"class":2774},[2225,144813,144814],{"class":2239}," qVec ",[2225,144816,2781],{"class":2235},[2225,144818,2784],{"class":2231},[2225,144820,144821],{"class":2291}," myEncoder",[2225,144823,2789],{"class":2239},[2225,144825,2805],{"class":2235},[2225,144827,144544],{"class":2252},[2225,144829,2805],{"class":2235},[2225,144831,2362],{"class":2239},[2225,144833,144834,144836,144838,144840,144842,144844,144846,144848,144851,144853,144855,144857,144859,144861,144863],{"class":2227,"line":1041},[2225,144835,2775],{"class":2774},[2225,144837,75579],{"class":2239},[2225,144839,2781],{"class":2235},[2225,144841,2784],{"class":2231},[2225,144843,90184],{"class":2239},[2225,144845,1520],{"class":2235},[2225,144847,144133],{"class":2291},[2225,144849,144850],{"class":2239},"(qVec",[2225,144852,2298],{"class":2235},[2225,144854,2236],{"class":2235},[2225,144856,8700],{"class":2307},[2225,144858,2311],{"class":2235},[2225,144860,45554],{"class":6527},[2225,144862,2243],{"class":2235},[2225,144864,2362],{"class":2239},[2217,144866,144868],{"className":6604,"code":144867,"language":141,"meta":1037,"style":1037},"[\n  { \"id\": \"rep-Q1\", \"score\": 0.934, \"rank\": 1, \"field\": \"(vector)\", \"snippet\": \"\" },\n  { \"id\": \"rep-Q4-prior\", \"score\": 0.79, \"rank\": 2, \"field\": \"(vector)\", \"snippet\": \"\" }\n]\n",[941,144869,144870,144874,144948,145022],{"__ignoreMap":1037},[2225,144871,144872],{"class":2227,"line":2228},[2225,144873,26349],{"class":2235},[2225,144875,144876,144878,144880,144882,144884,144886,144888,144891,144893,144895,144897,144899,144901,144903,144906,144908,144910,144912,144914,144916,144918,144920,144922,144924,144926,144928,144930,144932,144934,144936,144938,144940,144942,144944,144946],{"class":2227,"line":1041},[2225,144877,26354],{"class":2235},[2225,144879,6614],{"class":2235},[2225,144881,6983],{"class":2774},[2225,144883,6620],{"class":2235},[2225,144885,2311],{"class":2235},[2225,144887,6614],{"class":2235},[2225,144889,144890],{"class":2252},"rep-Q1",[2225,144892,6620],{"class":2235},[2225,144894,2298],{"class":2235},[2225,144896,6614],{"class":2235},[2225,144898,144229],{"class":2774},[2225,144900,6620],{"class":2235},[2225,144902,2311],{"class":2235},[2225,144904,144905],{"class":6527}," 0.934",[2225,144907,2298],{"class":2235},[2225,144909,6614],{"class":2235},[2225,144911,144243],{"class":2774},[2225,144913,6620],{"class":2235},[2225,144915,2311],{"class":2235},[2225,144917,9377],{"class":6527},[2225,144919,2298],{"class":2235},[2225,144921,6614],{"class":2235},[2225,144923,25197],{"class":2774},[2225,144925,6620],{"class":2235},[2225,144927,2311],{"class":2235},[2225,144929,6614],{"class":2235},[2225,144931,144264],{"class":2252},[2225,144933,6620],{"class":2235},[2225,144935,2298],{"class":2235},[2225,144937,6614],{"class":2235},[2225,144939,144273],{"class":2774},[2225,144941,6620],{"class":2235},[2225,144943,2311],{"class":2235},[2225,144945,60469],{"class":2235},[2225,144947,2353],{"class":2235},[2225,144949,144950,144952,144954,144956,144958,144960,144962,144965,144967,144969,144971,144973,144975,144977,144980,144982,144984,144986,144988,144990,144992,144994,144996,144998,145000,145002,145004,145006,145008,145010,145012,145014,145016,145018,145020],{"class":2227,"line":1038},[2225,144951,26354],{"class":2235},[2225,144953,6614],{"class":2235},[2225,144955,6983],{"class":2774},[2225,144957,6620],{"class":2235},[2225,144959,2311],{"class":2235},[2225,144961,6614],{"class":2235},[2225,144963,144964],{"class":2252},"rep-Q4-prior",[2225,144966,6620],{"class":2235},[2225,144968,2298],{"class":2235},[2225,144970,6614],{"class":2235},[2225,144972,144229],{"class":2774},[2225,144974,6620],{"class":2235},[2225,144976,2311],{"class":2235},[2225,144978,144979],{"class":6527}," 0.79",[2225,144981,2298],{"class":2235},[2225,144983,6614],{"class":2235},[2225,144985,144243],{"class":2774},[2225,144987,6620],{"class":2235},[2225,144989,2311],{"class":2235},[2225,144991,18961],{"class":6527},[2225,144993,2298],{"class":2235},[2225,144995,6614],{"class":2235},[2225,144997,25197],{"class":2774},[2225,144999,6620],{"class":2235},[2225,145001,2311],{"class":2235},[2225,145003,6614],{"class":2235},[2225,145005,144264],{"class":2252},[2225,145007,6620],{"class":2235},[2225,145009,2298],{"class":2235},[2225,145011,6614],{"class":2235},[2225,145013,144273],{"class":2774},[2225,145015,6620],{"class":2235},[2225,145017,2311],{"class":2235},[2225,145019,60469],{"class":2235},[2225,145021,6369],{"class":2235},[2225,145023,145024],{"class":2227,"line":2288},[2225,145025,26484],{"class":2235},[937,145027,145028,145029,30200,145031,145033,145034,968,145037,1520],{},"Options: ",[941,145030,8693],{},[941,145032,87121],{},", how many hits to return), ",[941,145035,145036],{},"minScore",[941,145038,145039],{},"includeRecord",[937,145041,145042,145045],{},[941,145043,145044],{},"query().similarTo().where()"," chaining — semantic search and a structured filter in one fluent expression — does not exist yet. Intersect two result sets manually instead:",[2217,145047,145049],{"className":2219,"code":145048,"language":2221,"meta":1037,"style":1037},"const vecHits = await docs.similarTo(qVec, { k: 20 })\nconst active = new Set((await docs.query().where('status', '==', 'active').run()).map((r) => r.id))\nconst filtered = vecHits.filter((h) => active.has(h.id))\n\u002F\u002F → the same RetrieveHit objects from vecHits, minus any whose id isn't in `active`;\n\u002F\u002F   rank\u002Fscore are left as similarTo() computed them, not re-numbered\n",[941,145050,145051,145084,145169,145210,145215],{"__ignoreMap":1037},[2225,145052,145053,145055,145058,145060,145062,145064,145066,145068,145070,145072,145074,145076,145078,145080,145082],{"class":2227,"line":2228},[2225,145054,2775],{"class":2774},[2225,145056,145057],{"class":2239}," vecHits ",[2225,145059,2781],{"class":2235},[2225,145061,2784],{"class":2231},[2225,145063,90184],{"class":2239},[2225,145065,1520],{"class":2235},[2225,145067,144133],{"class":2291},[2225,145069,144850],{"class":2239},[2225,145071,2298],{"class":2235},[2225,145073,2236],{"class":2235},[2225,145075,8700],{"class":2307},[2225,145077,2311],{"class":2235},[2225,145079,121631],{"class":6527},[2225,145081,2243],{"class":2235},[2225,145083,2362],{"class":2239},[2225,145085,145086,145088,145091,145093,145095,145098,145100,145102,145104,145106,145108,145110,145112,145114,145116,145118,145120,145122,145124,145126,145128,145130,145132,145134,145137,145139,145141,145143,145145,145148,145150,145152,145154,145156,145158,145160,145162,145164,145166],{"class":2227,"line":1041},[2225,145087,2775],{"class":2774},[2225,145089,145090],{"class":2239}," active ",[2225,145092,2781],{"class":2235},[2225,145094,8130],{"class":2235},[2225,145096,145097],{"class":2291}," Set",[2225,145099,91688],{"class":2239},[2225,145101,6500],{"class":2231},[2225,145103,90184],{"class":2239},[2225,145105,1520],{"class":2235},[2225,145107,5286],{"class":2291},[2225,145109,4228],{"class":2239},[2225,145111,1520],{"class":2235},[2225,145113,3016],{"class":2291},[2225,145115,2789],{"class":2239},[2225,145117,2805],{"class":2235},[2225,145119,6631],{"class":2252},[2225,145121,2805],{"class":2235},[2225,145123,2298],{"class":2235},[2225,145125,2249],{"class":2235},[2225,145127,21901],{"class":2252},[2225,145129,2805],{"class":2235},[2225,145131,2298],{"class":2235},[2225,145133,2249],{"class":2235},[2225,145135,145136],{"class":2252},"active",[2225,145138,2805],{"class":2235},[2225,145140,1381],{"class":2239},[2225,145142,1520],{"class":2235},[2225,145144,61526],{"class":2291},[2225,145146,145147],{"class":2239},"())",[2225,145149,1520],{"class":2235},[2225,145151,8244],{"class":2291},[2225,145153,2789],{"class":2239},[2225,145155,2789],{"class":2235},[2225,145157,13217],{"class":7052},[2225,145159,1381],{"class":2235},[2225,145161,8116],{"class":2774},[2225,145163,32537],{"class":2239},[2225,145165,1520],{"class":2235},[2225,145167,145168],{"class":2239},"id))\n",[2225,145170,145171,145173,145176,145178,145181,145183,145185,145187,145189,145191,145193,145195,145198,145200,145203,145206,145208],{"class":2227,"line":1038},[2225,145172,2775],{"class":2774},[2225,145174,145175],{"class":2239}," filtered ",[2225,145177,2781],{"class":2235},[2225,145179,145180],{"class":2239}," vecHits",[2225,145182,1520],{"class":2235},[2225,145184,8686],{"class":2291},[2225,145186,2789],{"class":2239},[2225,145188,2789],{"class":2235},[2225,145190,75634],{"class":7052},[2225,145192,1381],{"class":2235},[2225,145194,8116],{"class":2774},[2225,145196,145197],{"class":2239}," active",[2225,145199,1520],{"class":2235},[2225,145201,145202],{"class":2291},"has",[2225,145204,145205],{"class":2239},"(h",[2225,145207,1520],{"class":2235},[2225,145209,145168],{"class":2239},[2225,145211,145212],{"class":2227,"line":2288},[2225,145213,145214],{"class":2763},"\u002F\u002F → the same RetrieveHit objects from vecHits, minus any whose id isn't in `active`;\n",[2225,145216,145217],{"class":2227,"line":2304},[2225,145218,145219],{"class":2763},"\u002F\u002F   rank\u002Fscore are left as similarTo() computed them, not re-numbered\n",[1591,145221,145223,145224],{"id":145222},"hybrid-retrieval-retrievequery-mode-hybrid","Hybrid retrieval — ",[941,145225,145226],{},"retrieve(query, { mode: 'hybrid' })",[937,145228,145229,145231,145232,34689,145234,145236,145237,145240,145241,145244,145245,26623,145247,145250],{},[941,145230,142881],{}," fuses the lexical ranking from ",[1007,145233,142766],{"href":833},[941,145235,22035],{}," with the semantic ranking above, using ",[950,145238,145239],{},"Reciprocal Rank Fusion (RRF)",": run both rankings, then score each id by summing ",[941,145242,145243],{},"1 \u002F (k + rank)"," across every list it appears in (",[941,145246,8693],{},[941,145248,145249],{},"60","), sort descending by that sum, and re-assign 1-based rank. A document that appears in both lists earns a contribution from each and typically outranks a document that only appears in one — even one that ranked higher within its own list. That's the benefit of hybrid retrieval: exact-token matches and semantic-proximity matches combine without needing to normalize BM25 scores against cosine scores.",[2217,145252,145254],{"className":2219,"code":145253,"language":2221,"meta":1037,"style":1037},"const hits = await docs.retrieve('overdue invoice', { mode: 'hybrid' })\n",[941,145255,145256],{"__ignoreMap":1037},[2225,145257,145258,145260,145262,145264,145266,145268,145270,145272,145274,145276,145278,145280,145282,145284,145286,145288,145290,145292,145294,145296],{"class":2227,"line":2228},[2225,145259,2775],{"class":2774},[2225,145261,75579],{"class":2239},[2225,145263,2781],{"class":2235},[2225,145265,2784],{"class":2231},[2225,145267,90184],{"class":2239},[2225,145269,1520],{"class":2235},[2225,145271,75590],{"class":2291},[2225,145273,2789],{"class":2239},[2225,145275,2805],{"class":2235},[2225,145277,144174],{"class":2252},[2225,145279,2805],{"class":2235},[2225,145281,2298],{"class":2235},[2225,145283,2236],{"class":2235},[2225,145285,32303],{"class":2307},[2225,145287,2311],{"class":2235},[2225,145289,2249],{"class":2235},[2225,145291,95269],{"class":2252},[2225,145293,2805],{"class":2235},[2225,145295,2243],{"class":2235},[2225,145297,2362],{"class":2239},[2217,145299,145301],{"className":6604,"code":145300,"language":141,"meta":1037,"style":1037},"[\n  { \"id\": \"inv-0231\", \"score\": 0.0328, \"rank\": 1, \"field\": \"title\", \"snippet\": \"…$4,200 overdue invoice for…\" },\n  { \"id\": \"inv-0104\", \"score\": 0.0164, \"rank\": 2, \"field\": \"(vector)\", \"snippet\": \"\" }\n]\n",[941,145302,145303,145307,145385,145458],{"__ignoreMap":1037},[2225,145304,145305],{"class":2227,"line":2228},[2225,145306,26349],{"class":2235},[2225,145308,145309,145311,145313,145315,145317,145319,145321,145323,145325,145327,145329,145331,145333,145335,145338,145340,145342,145344,145346,145348,145350,145352,145354,145356,145358,145360,145362,145364,145366,145368,145370,145372,145374,145376,145378,145381,145383],{"class":2227,"line":1041},[2225,145310,26354],{"class":2235},[2225,145312,6614],{"class":2235},[2225,145314,6983],{"class":2774},[2225,145316,6620],{"class":2235},[2225,145318,2311],{"class":2235},[2225,145320,6614],{"class":2235},[2225,145322,144220],{"class":2252},[2225,145324,6620],{"class":2235},[2225,145326,2298],{"class":2235},[2225,145328,6614],{"class":2235},[2225,145330,144229],{"class":2774},[2225,145332,6620],{"class":2235},[2225,145334,2311],{"class":2235},[2225,145336,145337],{"class":6527}," 0.0328",[2225,145339,2298],{"class":2235},[2225,145341,6614],{"class":2235},[2225,145343,144243],{"class":2774},[2225,145345,6620],{"class":2235},[2225,145347,2311],{"class":2235},[2225,145349,9377],{"class":6527},[2225,145351,2298],{"class":2235},[2225,145353,6614],{"class":2235},[2225,145355,25197],{"class":2774},[2225,145357,6620],{"class":2235},[2225,145359,2311],{"class":2235},[2225,145361,6614],{"class":2235},[2225,145363,87181],{"class":2252},[2225,145365,6620],{"class":2235},[2225,145367,2298],{"class":2235},[2225,145369,6614],{"class":2235},[2225,145371,144273],{"class":2774},[2225,145373,6620],{"class":2235},[2225,145375,2311],{"class":2235},[2225,145377,6614],{"class":2235},[2225,145379,145380],{"class":2252},"…$4,200 overdue invoice for…",[2225,145382,6620],{"class":2235},[2225,145384,2353],{"class":2235},[2225,145386,145387,145389,145391,145393,145395,145397,145399,145401,145403,145405,145407,145409,145411,145413,145416,145418,145420,145422,145424,145426,145428,145430,145432,145434,145436,145438,145440,145442,145444,145446,145448,145450,145452,145454,145456],{"class":2227,"line":1038},[2225,145388,26354],{"class":2235},[2225,145390,6614],{"class":2235},[2225,145392,6983],{"class":2774},[2225,145394,6620],{"class":2235},[2225,145396,2311],{"class":2235},[2225,145398,6614],{"class":2235},[2225,145400,144372],{"class":2252},[2225,145402,6620],{"class":2235},[2225,145404,2298],{"class":2235},[2225,145406,6614],{"class":2235},[2225,145408,144229],{"class":2774},[2225,145410,6620],{"class":2235},[2225,145412,2311],{"class":2235},[2225,145414,145415],{"class":6527}," 0.0164",[2225,145417,2298],{"class":2235},[2225,145419,6614],{"class":2235},[2225,145421,144243],{"class":2774},[2225,145423,6620],{"class":2235},[2225,145425,2311],{"class":2235},[2225,145427,18961],{"class":6527},[2225,145429,2298],{"class":2235},[2225,145431,6614],{"class":2235},[2225,145433,25197],{"class":2774},[2225,145435,6620],{"class":2235},[2225,145437,2311],{"class":2235},[2225,145439,6614],{"class":2235},[2225,145441,144264],{"class":2252},[2225,145443,6620],{"class":2235},[2225,145445,2298],{"class":2235},[2225,145447,6614],{"class":2235},[2225,145449,144273],{"class":2774},[2225,145451,6620],{"class":2235},[2225,145453,2311],{"class":2235},[2225,145455,60469],{"class":2235},[2225,145457,6369],{"class":2235},[2225,145459,145460],{"class":2227,"line":2288},[2225,145461,26484],{"class":2235},[937,145463,145464,145466,145467,145470,145471,1625,145473,1625,145475,145477,145478,145480,145481,145483],{},[941,145465,144229],{}," here is the RRF score, not BM25 or cosine — with two fused lists it tops out at ",[941,145468,145469],{},"2\u002F61 ≈ 0.033",", so don't compare it to the scores from a plain lexical or semantic call. When the same id has a hit in both lists, the lexical hit's ",[941,145472,25197],{},[941,145474,144273],{},[941,145476,23074],{}," win (they're more informative than the ",[941,145479,144444],{}," placeholder); ",[941,145482,24158],{}," is taken from whichever hit carries it.",[937,145485,145486,145487,145489,145490,145492],{},"Requires ",[941,145488,4452],{}," configured on the collection — without it, ",[941,145491,145226],{}," throws. Hybrid retrieval adds no new store artifacts: it's pure in-memory computation over the lexical index, the vector set, and the eager cache, so it carries the same zero-knowledge guarantee as the lexical and semantic paths individually.",[1591,145494,145496,145498],{"id":145495},"within-retrieve-intersected-with-a-structured-query",[941,145497,111736],{}," — retrieve intersected with a structured query",[937,145500,145501,145503,145504,145506,145507,145510,145511,968,145514,968,145517,957],{},[941,145502,111736],{}," intersects a ",[941,145505,22035],{}," result with a ",[941,145508,145509],{},"collection.query().where(...)"," predicate, and works with every mode (",[941,145512,145513],{},"'lexical'",[941,145515,145516],{},"'semantic'",[941,145518,145519],{},"'hybrid'",[2217,145521,145523],{"className":2219,"code":145522,"language":2221,"meta":1037,"style":1037},"const hits = await docs.retrieve('overdue invoice', {\n  mode: 'hybrid',\n  within: docs.query().where('status', '==', 'open'),\n})\n",[941,145524,145525,145553,145568,145615],{"__ignoreMap":1037},[2225,145526,145527,145529,145531,145533,145535,145537,145539,145541,145543,145545,145547,145549,145551],{"class":2227,"line":2228},[2225,145528,2775],{"class":2774},[2225,145530,75579],{"class":2239},[2225,145532,2781],{"class":2235},[2225,145534,2784],{"class":2231},[2225,145536,90184],{"class":2239},[2225,145538,1520],{"class":2235},[2225,145540,75590],{"class":2291},[2225,145542,2789],{"class":2239},[2225,145544,2805],{"class":2235},[2225,145546,144174],{"class":2252},[2225,145548,2805],{"class":2235},[2225,145550,2298],{"class":2235},[2225,145552,2301],{"class":2235},[2225,145554,145555,145558,145560,145562,145564,145566],{"class":2227,"line":1041},[2225,145556,145557],{"class":2307},"  mode",[2225,145559,2311],{"class":2235},[2225,145561,2249],{"class":2235},[2225,145563,95269],{"class":2252},[2225,145565,2805],{"class":2235},[2225,145567,1707],{"class":2235},[2225,145569,145570,145573,145575,145577,145579,145581,145583,145585,145587,145589,145591,145593,145595,145597,145599,145601,145603,145605,145607,145609,145611,145613],{"class":2227,"line":1038},[2225,145571,145572],{"class":2307},"  within",[2225,145574,2311],{"class":2235},[2225,145576,90184],{"class":2239},[2225,145578,1520],{"class":2235},[2225,145580,5286],{"class":2291},[2225,145582,4228],{"class":2239},[2225,145584,1520],{"class":2235},[2225,145586,3016],{"class":2291},[2225,145588,2789],{"class":2239},[2225,145590,2805],{"class":2235},[2225,145592,6631],{"class":2252},[2225,145594,2805],{"class":2235},[2225,145596,2298],{"class":2235},[2225,145598,2249],{"class":2235},[2225,145600,21901],{"class":2252},[2225,145602,2805],{"class":2235},[2225,145604,2298],{"class":2235},[2225,145606,2249],{"class":2235},[2225,145608,26178],{"class":2252},[2225,145610,2805],{"class":2235},[2225,145612,1381],{"class":2239},[2225,145614,1707],{"class":2235},[2225,145616,145617,145619],{"class":2227,"line":2288},[2225,145618,2359],{"class":2235},[2225,145620,2362],{"class":2239},[2217,145622,145624],{"className":6604,"code":145623,"language":141,"meta":1037,"style":1037},"[\n  { \"id\": \"inv-0231\", \"score\": 0.0328, \"rank\": 1, \"field\": \"title\", \"snippet\": \"…$4,200 overdue invoice for…\" }\n]\n",[941,145625,145626,145630,145706],{"__ignoreMap":1037},[2225,145627,145628],{"class":2227,"line":2228},[2225,145629,26349],{"class":2235},[2225,145631,145632,145634,145636,145638,145640,145642,145644,145646,145648,145650,145652,145654,145656,145658,145660,145662,145664,145666,145668,145670,145672,145674,145676,145678,145680,145682,145684,145686,145688,145690,145692,145694,145696,145698,145700,145702,145704],{"class":2227,"line":1041},[2225,145633,26354],{"class":2235},[2225,145635,6614],{"class":2235},[2225,145637,6983],{"class":2774},[2225,145639,6620],{"class":2235},[2225,145641,2311],{"class":2235},[2225,145643,6614],{"class":2235},[2225,145645,144220],{"class":2252},[2225,145647,6620],{"class":2235},[2225,145649,2298],{"class":2235},[2225,145651,6614],{"class":2235},[2225,145653,144229],{"class":2774},[2225,145655,6620],{"class":2235},[2225,145657,2311],{"class":2235},[2225,145659,145337],{"class":6527},[2225,145661,2298],{"class":2235},[2225,145663,6614],{"class":2235},[2225,145665,144243],{"class":2774},[2225,145667,6620],{"class":2235},[2225,145669,2311],{"class":2235},[2225,145671,9377],{"class":6527},[2225,145673,2298],{"class":2235},[2225,145675,6614],{"class":2235},[2225,145677,25197],{"class":2774},[2225,145679,6620],{"class":2235},[2225,145681,2311],{"class":2235},[2225,145683,6614],{"class":2235},[2225,145685,87181],{"class":2252},[2225,145687,6620],{"class":2235},[2225,145689,2298],{"class":2235},[2225,145691,6614],{"class":2235},[2225,145693,144273],{"class":2774},[2225,145695,6620],{"class":2235},[2225,145697,2311],{"class":2235},[2225,145699,6614],{"class":2235},[2225,145701,145380],{"class":2252},[2225,145703,6620],{"class":2235},[2225,145705,6369],{"class":2235},[2225,145707,145708],{"class":2227,"line":1038},[2225,145709,26484],{"class":2235},[937,145711,145712,145714,145715,145717,145718,968,145720,968,145722,968,145724,145726,145727,1952,145729,145731,145732,145734],{},[941,145713,111736],{}," matches ids by reference identity against the same in-memory snapshot ",[941,145716,22035],{}," already reads — no extra store reads. It requires eager mode (the default). Supported query operators: ",[941,145719,3016],{},[941,145721,8686],{},[941,145723,14128],{},[941,145725,7493],{},", and projection ",[941,145728,61808],{},[941,145730,133197],{}," is unsupported inside ",[941,145733,111736],{}," (it produces new row objects that break id recovery) and throws a clear error if used.",[937,145736,145737,145739,145740,145742,145743,145745,145746,145749,145750,145752],{},[941,145738,3022],{}," is applied per-mode, ",[950,145741,11357],{}," the ",[941,145744,111736],{}," intersection — so ",[941,145747,145748],{},"{ limit: 10, within }"," can return fewer than 10 hits even if more matching records exist beyond that cutoff. If the ",[941,145751,111736],{}," query matches nothing, the result is an empty array.",[1591,145754,145756,145759],{"id":145755},"fuseretrievallists-opts-the-fusion-primitive-standalone",[941,145757,145758],{},"fuseRetrieval(lists, opts?)"," — the fusion primitive standalone",[937,145761,145762,145765,145766,145769,145770,145773,145774,145776],{},[941,145763,145764],{},"retrieve({ mode: 'hybrid' })"," calls this internally with ",[941,145767,145768],{},"[lexicalHits, semanticHits]",", but it's also exported directly for merging any set of pre-ranked ",[941,145771,145772],{},"RetrieveHit"," lists — for example, combining ",[941,145775,22035],{}," results from multiple vaults.",[2217,145778,145780],{"className":2219,"code":145779,"language":2221,"meta":1037,"style":1037},"import { fuseRetrieval } from '@noy-db\u002Fhub'\n\u002F\u002F also available from '@noy-db\u002Fhub\u002Fcargo'\n\nconst fused = fuseRetrieval([lexicalHits, semanticHits])\n\u002F\u002F → same shape as the mode: 'hybrid' result above — RRF-scored, re-ranked 1-based\n\nconst top5 = fuseRetrieval([listA, listB], { k: 60, limit: 5 })\n\u002F\u002F → up to 5 hits, sorted by descending RRF score\n",[941,145781,145782,145801,145806,145810,145829,145834,145838,145878],{"__ignoreMap":1037},[2225,145783,145784,145786,145788,145791,145793,145795,145797,145799],{"class":2227,"line":2228},[2225,145785,2232],{"class":2231},[2225,145787,2236],{"class":2235},[2225,145789,145790],{"class":2239}," fuseRetrieval",[2225,145792,2243],{"class":2235},[2225,145794,2246],{"class":2231},[2225,145796,2249],{"class":2235},[2225,145798,1086],{"class":2252},[2225,145800,2256],{"class":2235},[2225,145802,145803],{"class":2227,"line":1041},[2225,145804,145805],{"class":2763},"\u002F\u002F also available from '@noy-db\u002Fhub\u002Fcargo'\n",[2225,145807,145808],{"class":2227,"line":1038},[2225,145809,2285],{"emptyLinePlaceholder":2284},[2225,145811,145812,145814,145817,145819,145821,145824,145826],{"class":2227,"line":2288},[2225,145813,2775],{"class":2774},[2225,145815,145816],{"class":2239}," fused ",[2225,145818,2781],{"class":2235},[2225,145820,145790],{"class":2291},[2225,145822,145823],{"class":2239},"([lexicalHits",[2225,145825,2298],{"class":2235},[2225,145827,145828],{"class":2239}," semanticHits])\n",[2225,145830,145831],{"class":2227,"line":2304},[2225,145832,145833],{"class":2763},"\u002F\u002F → same shape as the mode: 'hybrid' result above — RRF-scored, re-ranked 1-based\n",[2225,145835,145836],{"class":2227,"line":2356},[2225,145837,2285],{"emptyLinePlaceholder":2284},[2225,145839,145840,145842,145844,145846,145848,145851,145853,145856,145858,145860,145862,145864,145866,145868,145870,145872,145874,145876],{"class":2227,"line":2830},[2225,145841,2775],{"class":2774},[2225,145843,144462],{"class":2239},[2225,145845,2781],{"class":2235},[2225,145847,145790],{"class":2291},[2225,145849,145850],{"class":2239},"([listA",[2225,145852,2298],{"class":2235},[2225,145854,145855],{"class":2239}," listB]",[2225,145857,2298],{"class":2235},[2225,145859,2236],{"class":2235},[2225,145861,8700],{"class":2307},[2225,145863,2311],{"class":2235},[2225,145865,44452],{"class":6527},[2225,145867,2298],{"class":2235},[2225,145869,37205],{"class":2307},[2225,145871,2311],{"class":2235},[2225,145873,18996],{"class":6527},[2225,145875,2243],{"class":2235},[2225,145877,2362],{"class":2239},[2225,145879,145880],{"class":2227,"line":2861},[2225,145881,145882],{"class":2763},"\u002F\u002F → up to 5 hits, sorted by descending RRF score\n",[937,145884,145885,145888],{},[941,145886,145887],{},"fuseRetrieval"," is pure — no I\u002FO, no store access — and arity-generic: it accepts two or more lists, so it works equally for fusing modalities (lexical + semantic) or fusing per-source result sets from an external caller.",[937,145890,145891],{},[950,145892,145893,2311],{},[941,145894,145895],{},"FuseOptions",[1338,145897,145898,145908],{},[1341,145899,145900],{},[1344,145901,145902,145904,145906],{},[1347,145903,101060],{},[1347,145905,20197],{},[1347,145907,20200],{},[1357,145909,145910,145924,145937],{},[1344,145911,145912,145916,145921],{},[1362,145913,145914],{},[941,145915,21111],{},[1362,145917,145918],{},[941,145919,145920],{},"'rrf'",[1362,145922,145923],{},"The only supported strategy today",[1344,145925,145926,145930,145934],{},[1362,145927,145928],{},[941,145929,8693],{},[1362,145931,145932],{},[941,145933,145249],{},[1362,145935,145936],{},"RRF constant; larger flattens the rank weighting",[1344,145938,145939,145943,145945],{},[1362,145940,145941],{},[941,145942,3022],{},[1362,145944,3610],{},[1362,145946,145947],{},"Truncate the fused output to the top N",[1022,145949,102889],{"id":102888},[1591,145951,145953],{"id":145952},"embeddingdescriptor",[941,145954,145955],{},"EmbeddingDescriptor",[2217,145957,145959],{"className":2219,"code":145958,"language":2221,"meta":1037,"style":1037},"interface EmbeddingDescriptor {\n  readonly source: string | readonly string[]\n  readonly encode: (text: string) => Promise\u003CFloat32Array>\n  readonly dim: number\n  readonly model: string\n}\n",[941,145960,145961,145970,145988,146017,146028,146038],{"__ignoreMap":1037},[2225,145962,145963,145965,145968],{"class":2227,"line":2228},[2225,145964,6994],{"class":2774},[2225,145966,145967],{"class":6342}," EmbeddingDescriptor",[2225,145969,2301],{"class":2235},[2225,145971,145972,145974,145976,145978,145980,145982,145984,145986],{"class":2227,"line":1041},[2225,145973,53580],{"class":2774},[2225,145975,95077],{"class":2307},[2225,145977,2311],{"class":2235},[2225,145979,6366],{"class":6342},[2225,145981,7087],{"class":2235},[2225,145983,112906],{"class":2774},[2225,145985,6366],{"class":6342},[2225,145987,78511],{"class":2239},[2225,145989,145990,145992,145995,145997,145999,146001,146003,146005,146007,146009,146011,146013,146015],{"class":2227,"line":1038},[2225,145991,53580],{"class":2774},[2225,145993,145994],{"class":2307}," encode",[2225,145996,2311],{"class":2235},[2225,145998,953],{"class":2235},[2225,146000,5672],{"class":7052},[2225,146002,2311],{"class":2235},[2225,146004,6366],{"class":6342},[2225,146006,1381],{"class":2235},[2225,146008,8116],{"class":2774},[2225,146010,7079],{"class":6342},[2225,146012,6474],{"class":2235},[2225,146014,75686],{"class":6342},[2225,146016,7093],{"class":2235},[2225,146018,146019,146021,146024,146026],{"class":2227,"line":2288},[2225,146020,53580],{"class":2774},[2225,146022,146023],{"class":2307}," dim",[2225,146025,2311],{"class":2235},[2225,146027,12024],{"class":6342},[2225,146029,146030,146032,146034,146036],{"class":2227,"line":2304},[2225,146031,53580],{"class":2774},[2225,146033,95063],{"class":2307},[2225,146035,2311],{"class":2235},[2225,146037,7014],{"class":6342},[2225,146039,146040],{"class":2227,"line":2356},[2225,146041,7351],{"class":2235},[937,146043,146044,146045,146047,146048,957],{},"Passed as the ",[941,146046,4452],{}," collection option (see ",[1007,146049,99864],{"href":146050},"#opt-in",[1591,146052,146054],{"id":146053},"retrieveoptionst",[941,146055,146056],{},"RetrieveOptions\u003CT>",[1338,146058,146059,146069],{},[1341,146060,146061],{},[1344,146062,146063,146065,146067],{},[1347,146064,101060],{},[1347,146066,20194],{},[1347,146068,20200],{},[1357,146070,146071,146087,146100,146113,146126],{},[1344,146072,146073,146077,146082],{},[1362,146074,146075],{},[941,146076,120875],{},[1362,146078,146079],{},[941,146080,146081],{},"'lexical' | 'semantic' | 'hybrid'",[1362,146083,146084,146085],{},"Retrieval strategy; defaults to ",[941,146086,145513],{},[1344,146088,146089,146093,146097],{},[1362,146090,146091],{},[941,146092,3022],{},[1362,146094,146095],{},[941,146096,20636],{},[1362,146098,146099],{},"Max hits to return",[1344,146101,146102,146106,146110],{},[1362,146103,146104],{},[941,146105,145036],{},[1362,146107,146108],{},[941,146109,20636],{},[1362,146111,146112],{},"Semantic-mode-only cosine floor",[1344,146114,146115,146119,146123],{},[1362,146116,146117],{},[941,146118,145039],{},[1362,146120,146121],{},[941,146122,16861],{},[1362,146124,146125],{},"Attach the decrypted record to each hit",[1344,146127,146128,146132,146136],{},[1362,146129,146130],{},[941,146131,111736],{},[1362,146133,146134],{},[941,146135,24785],{},[1362,146137,146138],{},"Intersect hits with a structured query (see above)",[1591,146140,146142],{"id":146141},"collectionsimilartovector-opts",[941,146143,144798],{},[2217,146145,146147],{"className":2219,"code":146146,"language":2221,"meta":1037,"style":1037},"similarTo(vector: Float32Array, opts?: { k?: number; minScore?: number; includeRecord?: boolean }): Promise\u003CRetrieveHit\u003CT>[]>\n",[941,146148,146149],{"__ignoreMap":1037},[2225,146150,146151,146153,146156,146158,146160,146162,146164,146167,146169,146172,146174,146177,146179,146182,146184,146186,146188,146190,146192,146194,146196,146198,146200],{"class":2227,"line":2228},[2225,146152,144133],{"class":2291},[2225,146154,146155],{"class":2239},"(vector: Float32Array",[2225,146157,2298],{"class":2235},[2225,146159,8232],{"class":2239},[2225,146161,7011],{"class":2235},[2225,146163,2236],{"class":2235},[2225,146165,146166],{"class":2239}," k?",[2225,146168,2311],{"class":2235},[2225,146170,146171],{"class":2239}," number; minScore",[2225,146173,7011],{"class":2235},[2225,146175,146176],{"class":2239}," number; includeRecord",[2225,146178,7011],{"class":2235},[2225,146180,146181],{"class":2239}," boolean ",[2225,146183,2359],{"class":2235},[2225,146185,11737],{"class":2239},[2225,146187,11740],{"class":6342},[2225,146189,6474],{"class":2235},[2225,146191,145772],{"class":2239},[2225,146193,6474],{"class":2235},[2225,146195,11968],{"class":2239},[2225,146197,6480],{"class":2235},[2225,146199,7262],{"class":2239},[2225,146201,7093],{"class":2235},[937,146203,146204,146205,26623,146207,1520],{},"Raw-vector kNN over the decrypted, in-memory vector set. ",[941,146206,8693],{},[941,146208,87121],{},[1591,146210,146212],{"id":146211},"collectionrebuildembeddings",[941,146213,146214],{},"collection.rebuildEmbeddings()",[2217,146216,146218],{"className":2219,"code":146217,"language":2221,"meta":1037,"style":1037},"rebuildEmbeddings(): Promise\u003C{ rebuilt: number; skipped: number }>\n",[941,146219,146220],{"__ignoreMap":1037},[2225,146221,146222,146224,146226,146228,146230,146233,146235,146237,146239,146242,146244,146246],{"class":2227,"line":2228},[2225,146223,75672],{"class":2291},[2225,146225,12809],{"class":2239},[2225,146227,11740],{"class":6342},[2225,146229,17851],{"class":2235},[2225,146231,146232],{"class":6342}," rebuilt",[2225,146234,2311],{"class":2235},[2225,146236,6356],{"class":2239},[2225,146238,3769],{"class":2235},[2225,146240,146241],{"class":6342}," skipped",[2225,146243,2311],{"class":2235},[2225,146245,6356],{"class":2239},[2225,146247,17861],{"class":2235},[937,146249,146250,146251,146253,146254,1520],{},"Force-re-derives every eligible record's ",[941,146252,75373],{}," sidecar in one idempotent, resumable pass. Skips\n(never refuses) elevated records, tombstones, and delete-markers. See\n",[1007,146255,146257],{"href":146256},"#rebuilding-vectors-rebuildembeddings","Rebuilding vectors",[1022,146259,146261],{"id":146260},"model-version-guard","Model-version guard",[937,146263,146264,146265,146267,146268,146270,146271,146273,146274,146277],{},"Every stored vector carries the ",[941,146266,142870],{}," string from the descriptor at the time it was written. On retrieval, if any stored vector's ",[941,146269,142870],{}," differs from the current descriptor's ",[941,146272,142870],{},", noy-db throws ",[941,146275,146276],{},"EmbeddingModelMismatchError"," — changing encoders silently would otherwise make cosine similarity meaningless across mismatched vector spaces.",[937,146279,146280,146281,146285,146286,146288,146289,146291,146292,146294],{},"Recovery: re-derive vectors across a whole collection in one pass with\n",[1007,146282,146283],{"href":146256},[941,146284,25605],{}," (below), or re-derive one at a time\nby re-putting the affected records (each ",[941,146287,20953],{}," triggers a fresh ",[941,146290,143216],{}," and a new ",[941,146293,75373],{},"\nwrite).",[1022,146296,146298,146299],{"id":146297},"rebuilding-vectors-rebuildembeddings","Rebuilding vectors — ",[941,146300,25605],{},[937,146302,146303,146305,146306,146308,146309,146311,146312,146314,146315,957],{},[941,146304,146214],{}," force-re-derives every eligible record's ",[941,146307,75373],{}," sidecar in one\npass, without waiting for organic writes. It walks every live record, calls your ",[941,146310,143216],{}," on each,\nand re-persists the sidecar. Introduced in ",[941,146313,56948],{}," (#788) as the opt-in bulk closer for the\nrecall gap the collection-namespacing change left open (see ",[1007,146316,142804],{"href":142803},[2217,146318,146320],{"className":2219,"code":146319,"language":2221,"meta":1037,"style":1037},"const { rebuilt, skipped } = await docs.rebuildEmbeddings()\n\u002F\u002F → { rebuilt: 128, skipped: 3 }\n",[941,146321,146322,146349],{"__ignoreMap":1037},[2225,146323,146324,146326,146328,146330,146332,146335,146337,146339,146341,146343,146345,146347],{"class":2227,"line":2228},[2225,146325,2775],{"class":2774},[2225,146327,2236],{"class":2235},[2225,146329,146232],{"class":2239},[2225,146331,2298],{"class":2235},[2225,146333,146334],{"class":2239}," skipped ",[2225,146336,2359],{"class":2235},[2225,146338,6346],{"class":2235},[2225,146340,2784],{"class":2231},[2225,146342,90184],{"class":2239},[2225,146344,1520],{"class":2235},[2225,146346,75672],{"class":2291},[2225,146348,8146],{"class":2239},[2225,146350,146351],{"class":2227,"line":1041},[2225,146352,146353],{"class":2763},"\u002F\u002F → { rebuilt: 128, skipped: 3 }\n",[1111,146355,146356,146362,146383],{},[1114,146357,146358,146361],{},[950,146359,146360],{},"Opt-in and idempotent."," Each id's re-derive is independent, so it is safe to re-run: a partial\nfailure (e.g. a store error mid-walk) leaves the already-rebuilt sidecars in place, and re-running\ncompletes the rest.",[1114,146363,146364,146367,146368,146371,146372,146374,146375,146377,146378,146380,146381,1520],{},[950,146365,146366],{},"Elevated records are skipped, not refused."," A record elevated above tier 0 is ",[1092,146369,146370],{},"supposed"," to\nhave no ",[941,146373,75373],{}," sidecar — re-deriving one would write searchable plaintext-derived data above\ntier 0 — so each elevated record is counted in ",[941,146376,75655],{}," and the walk continues. Tombstones,\ndelete-marker rows, and a raw read racing a concurrent delete all decode to ",[941,146379,6651],{}," and are\nlikewise skipped. This mirrors the tier-0 read gate described on\n",[1007,146382,872],{"href":56971},[1114,146384,146385,146388,146389,146391,146392,146395,146396,146398,146399,6789,146401,146403,146404,1520],{},[950,146386,146387],{},"Gated like every other search method."," A collection that never declared ",[941,146390,4452],{}," returns\n",[941,146393,146394],{},"{ rebuilt: 0, skipped: 0 }"," without touching the strategy; a collection that declared\n",[941,146397,4452],{}," but was created without ",[941,146400,94750],{},[941,146402,22160],{},",\nmatching ",[941,146405,20953],{},[1022,146407,146409],{"id":146408},"migrating-existing-vectors","Migrating existing vectors",[937,146411,146412,146413,146415,146416,146419,146420,146422,146423,146426],{},"The collection-namespacing change (",[941,146414,56948],{},", #726) has ",[950,146417,146418],{},"no dual-read fallback"," — a legacy\nbare-id ",[941,146421,142821],{}," row is irreducibly ambiguous about which collection it belongs to once ids\ncollide, so reading one would be unsound. Instead the change ",[950,146424,146425],{},"fails safe toward not-found",": a\nlegacy sidecar becomes unreachable ciphertext residue until its owning record is rewritten, never\nsurfacing under the wrong record.",[937,146428,146429,146430,146432,146433,146436],{},"If you populated embeddings before ",[941,146431,56948],{},", expect a ",[950,146434,146435],{},"recall gap"," for\nun-rewritten records until you close it one of two ways:",[1111,146438,146439,146451],{},[1114,146440,146441,1324,146444,146447,146448,146450],{},[950,146442,146443],{},"Organic self-heal.",[941,146445,146446],{},"embedOnWrite"," re-derives and re-persists the sidecar on every ",[941,146449,20953],{},", so\nany record written after upgrading heals for free — sidecars are a pure function of live plaintext\nplus model, always re-derivable.",[1114,146452,146453,146456,146457,146461],{},[950,146454,146455],{},"Bulk close."," Call ",[1007,146458,146459],{"href":146256},[941,146460,25605],{}," once per\ncollection to re-derive every live record's sidecar immediately rather than waiting for writes.",[1022,146463,146465,146467],{"id":146464},"forget-teardown",[941,146466,2542],{}," teardown",[937,146469,146470,146472,146473,146475,146476,146478],{},[941,146471,76197],{}," crypto-shreds matching records and also drops each affected record's ",[941,146474,142821],{}," side-car (best-effort, with any failures reported as residue on the returned ",[941,146477,76446],{},", the same pattern used for the lexical and structured indexes). The in-memory vector set is marked dirty so the next retrieval reloads from the store without the forgotten vectors. This matters because a vector can be inverted back toward its source text (embedding inversion), so it's treated as sensitive as the record itself and must not survive an erasure.",[1022,146480,68210],{"id":68209},[1338,146482,146483,146493],{},[1341,146484,146485],{},[1344,146486,146487,146490],{},[1347,146488,146489],{},"Situation",[1347,146491,146492],{},"Behaviour",[1357,146494,146495,146507,146525,146539,146553,146565,146579,146591,146599,146612],{},[1344,146496,146497,146502],{},[1362,146498,146499,146501],{},[941,146500,143216],{}," returns the wrong length",[1362,146503,146504,146506],{},[941,146505,143220],{}," at write time",[1344,146508,146509,146517],{},[1362,146510,146511,146512,146514,146515],{},"Stored vector's ",[941,146513,142870],{}," ≠ descriptor's ",[941,146516,142870],{},[1362,146518,146519,146521,146522,146524],{},[941,146520,146276],{}," on retrieval; re-derive via ",[941,146523,25605],{}," or re-put affected records",[1344,146526,146527,146533],{},[1362,146528,146529,146530,146532],{},"Legacy bare-id ",[941,146531,75373],{}," sidecar after the namespacing change",[1362,146534,146535,146536,146538],{},"Unreachable residue (fails safe to not-found) until the record is rewritten or ",[941,146537,25605],{}," runs",[1344,146540,146541,146546],{},[1362,146542,146543,146544,36491],{},"Collection has no ",[941,146545,4452],{},[1362,146547,146548,997,146550,122050],{},[941,146549,22038],{},[941,146551,146552],{},"retrieve({ mode: 'semantic' })",[1344,146554,146555,146560],{},[1362,146556,146543,146557,146559],{},[941,146558,4452],{}," config, hybrid mode",[1362,146561,146562,146564],{},[941,146563,145764],{}," throws",[1344,146566,146567,146574],{},[1362,146568,146569,146571,146572],{},[941,146570,94750],{}," not passed to ",[941,146573,2134],{},[1362,146575,146576,146577],{},"Every method on this page throws ",[941,146578,22160],{},[1344,146580,146581,146584],{},[1362,146582,146583],{},"Vector set marked dirty after a write",[1362,146585,146586,146587,1625,146589,24561],{},"Rebuilt lazily on the next ",[941,146588,75590],{},[941,146590,144133],{},[1344,146592,146593,146596],{},[1362,146594,146595],{},"Collection scale",[1362,146597,146598],{},"Cosine scoring is brute-force (no HNSW\u002FIVF); comfortable up to roughly tens of thousands of vectors",[1344,146600,146601,146609],{},[1362,146602,146603,6030,146606,146608],{},[941,146604,146605],{},"crdt: true",[941,146607,4452],{}," on the same collection",[1362,146610,146611],{},"Throws at collection construction",[1344,146613,146614,146621],{},[1362,146615,146616,53129,146618,146620],{},[941,146617,133197],{},[941,146619,111736],{}," query",[1362,146622,146623],{},"Throws — cross-join rows break id recovery",[1022,146625,2621],{"id":2620},[1111,146627,146628,146643,146651,146660],{},[1114,146629,146630,146632,146633,1625,146636,146639,146640,146642],{},[1007,146631,142766],{"href":833}," — the lexical (",[941,146634,146635],{},"L0",[941,146637,146638],{},"L1",") retrieval this page's hybrid mode fuses with, and the ",[941,146641,22035],{}," options shared across all three modes.",[1114,146644,146645,146647,146648,146650],{},[1007,146646,21],{"href":66}," — the DEK\u002Fkeyring model that encrypts every ",[941,146649,75373],{}," side-car.",[1114,146652,146653,146656,146657,146659],{},[1007,146654,146655],{"href":686},"Forget"," — the erasure path whose teardown this page's ",[941,146658,2542],{}," section describes.",[1114,146661,141161,146662,997,146668,1520],{},[1007,146663,146665],{"href":94248,"rel":146664},[3371],[941,146666,146667],{},"124-semantic-retrieve",[1007,146669,146671],{"href":94265,"rel":146670},[3371],[941,146672,146673],{},"125-hybrid-retrieve",[2645,146675,146676],{},"html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html pre.shiki code .sHdIc, html code.shiki .sHdIc{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#EEFFFF;--shiki-default-font-style:italic;--shiki-dark:#BABED8;--shiki-dark-font-style:italic}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sfNiH, html code.shiki .sfNiH{--shiki-light:#FF5370;--shiki-default:#FF9CAC;--shiki-dark:#FF9CAC}",{"title":1037,"searchDepth":1038,"depth":1038,"links":146678},[146679,146680,146681,146685,146699,146705,146706,146708,146709,146711,146712],{"id":29856,"depth":1041,"text":29857},{"id":70092,"depth":1041,"text":70093},{"id":99863,"depth":1041,"text":99864,"children":146682},[146683],{"id":143237,"depth":1038,"text":146684},"Supplying an encode function",{"id":29892,"depth":1041,"text":29893,"children":146686},[146687,146689,146691,146693,146695,146697],{"id":144142,"depth":1038,"text":146688},"Semantic retrieval — retrieve(query, { mode: 'semantic' })",{"id":144674,"depth":1038,"text":146690},"RetrieveHit\u003CT> shape",{"id":144795,"depth":1038,"text":146692},"collection.similarTo(vector, opts?) — bring your own vector",{"id":145222,"depth":1038,"text":146694},"Hybrid retrieval — retrieve(query, { mode: 'hybrid' })",{"id":145495,"depth":1038,"text":146696},"within — retrieve intersected with a structured query",{"id":145755,"depth":1038,"text":146698},"fuseRetrieval(lists, opts?) — the fusion primitive standalone",{"id":102888,"depth":1041,"text":102889,"children":146700},[146701,146702,146703,146704],{"id":145952,"depth":1038,"text":145955},{"id":146053,"depth":1038,"text":146056},{"id":146141,"depth":1038,"text":144798},{"id":146211,"depth":1038,"text":146214},{"id":146260,"depth":1041,"text":146261},{"id":146297,"depth":1041,"text":146707},"Rebuilding vectors — rebuildEmbeddings()",{"id":146408,"depth":1041,"text":146409},{"id":146464,"depth":1041,"text":146710},"forget() teardown",{"id":68209,"depth":1041,"text":68210},{"id":2620,"depth":1041,"text":2621},"Semantic and hybrid retrieval via `withSearch()`: an `embeddings` encoder per collection, `retrieve()`\u002F`similarTo()` by meaning, and lexical⊕semantic fusion.",{},{"title":673,"description":146713},"semantic and hybrid retrieval","Bt9qy3D6oDeE5_U4-P4DjSTe0fk35IwGmFbZDvx-pL4",{"id":146719,"title":146720,"access":12,"apiToc":929,"body":146721,"category":1042,"description":150361,"extension":1044,"meta":150362,"navigation":150363,"nest":1047,"path":678,"seo":150364,"sinceVersion":1042,"status":38550,"stem":679,"subtitle":150365,"tier":1050,"tocExternal":1042,"__hash__":150366},"docs\u002Fservices\u002Ffield-metadata.md","Field metadata — describe()",{"type":931,"value":146722,"toc":150334},[146723,146726,146729,146731,146733,146739,146750,146779,146781,146785,146835,146858,146861,146901,146903,146908,146913,147441,147447,147655,147657,147661,147664,147670,147766,147778,147780,147784,147789,147811,147822,148686,149004,149009,149011,149015,149022,149040,149042,149046,149325,149327,149333,149542,149551,149618,149635,149637,149649,149654,149660,149670,149676,149687,149824,149855,149861,149867,149878,149880,149890,149902,150051,150059,150061,150065,150073,150079,150099,150109,150115,150118,150128,150137,150143,150179,150182,150256,150258,150260,150331],[934,146724,146720],{"id":146725},"field-metadata-describe",[46871,146727],{":rows":146728},"[{\"label\":\"Subpath\",\"value\":\"`@noy-db\u002Fhub` (root export) — the seam `@noy-db\u002Fui` binds to — see [API reference](#public-api-surface-noy-dbhub)\"},{\"label\":\"Type\",\"value\":\"Core feature — `describe()` and `toJSONSchema()` are methods on the always-loaded `Collection` class; `fieldMeta` and `meta` are plain collection\u002Fvault options.\"}]",[104892,146730],{},[1022,146732,6242],{"id":6241},[937,146734,146735,146736,146738],{},"Every consumer that renders or exports a ",[1007,146737,6471],{"href":48}," —\na table header, an export pipeline, an API serializer, an AI agent building\ncontext — needs to know each field's display label, unit, and sensitivity.\nWithout a shared source, every consumer ends up duplicating that\nlabel\u002Funit\u002Fsensitivity logic, and the copies drift.",[937,146740,146741,146743,146744,139857,146746,146749],{},[941,146742,1380],{}," solves this by returning a normalised\n",[941,146745,25317],{},[941,146747,146748],{},"fields: DescribedField[]"," array merges every\nper-field config channel into a single consumer-neutral record. Any consumer\nreads from this one source instead of maintaining its own.",[937,146751,146752,1324,146755,146758,146759,968,146761,968,146764,968,146767,968,146770,968,146772,1707,146775,146778],{},[950,146753,146754],{},"Descriptive, never prescriptive.",[941,146756,146757],{},"DescribedField"," carries meaning\n(",[941,146760,96028],{},[941,146762,146763],{},"semanticType",[941,146765,146766],{},"unit",[941,146768,146769],{},"sensitivity",[941,146771,4449],{},[941,146773,146774],{},"aliases",[941,146776,146777],{},"displayFor",") — not layout, not styling, not locale selection. Those stay\napp-side.",[104892,146780],{},[1022,146782,146784],{"id":146783},"sync-vs-async","Sync vs async",[1338,146786,146787,146800],{},[1341,146788,146789],{},[1344,146790,146791,146794,146797],{},[1347,146792,146793],{},"Call",[1347,146795,146796],{},"What it returns",[1347,146798,146799],{},"When to use",[1357,146801,146802,146816],{},[1344,146803,146804,146808,146813],{},[1362,146805,146806],{},[941,146807,1380],{},[1362,146809,146810,146812],{},[941,146811,25317],{}," (sync, immediate)",[1362,146814,146815],{},"Table headers, export spec, any render-time use",[1344,146817,146818,146823,146828],{},[1362,146819,146820],{},[941,146821,146822],{},"collection.describe(opts)",[1362,146824,146825],{},[941,146826,146827],{},"Promise\u003CCollectionDescription>",[1362,146829,146830,146831,146834],{},"Need validator-derived exact types, zod-4 ",[941,146832,146833],{},".meta()",", or dynamic-dict label resolution",[937,146836,146837,146838,146840,146841,146844,146845,1625,146848,146851,146852,146855,146856,957],{},"The sync path is zero-store-I\u002FO: no ",[941,146839,6500],{},", no store reads, no decryption.\nIt derives field types from the in-memory config (money→",[941,146842,146843],{},"'number'",",\nref→",[941,146846,146847],{},"'string'",[941,146849,146850],{},"'array'",", dictKey→",[941,146853,146854],{},"'enum'",", others→",[941,146857,13088],{},[937,146859,146860],{},"The async path adds:",[1111,146862,146863,146881,146887],{},[1114,146864,146865,146866,1625,146868,1625,146870,9750,146873,146876,146877,146880],{},"Validator-derived ",[941,146867,6339],{},[941,146869,2084],{},[941,146871,146872],{},"constraints",[941,146874,146875],{},"deriveZodFields","\n(lazy ",[941,146878,146879],{},"import('zod')"," — never a static dep).",[1114,146882,146883,146884,146886],{},"Zod-4 ",[941,146885,146833],{}," key extraction (label, description, semanticType, …).",[1114,146888,146889,146890,146893,146894,146897,146898,1520],{},"Dynamic-dict label resolution: ",[941,146891,146892],{},"{ resolveDictLabels: true }"," calls\n",[941,146895,146896],{},"vault.dictionary(name).list()"," for each dynamic dictKey field and populates\n",[941,146899,146900],{},"dict.values[].label",[104892,146902],{},[1022,146904,960,146906,35230],{"id":146905},"the-fieldmeta-channel",[941,146907,25333],{},[937,146909,146910,146912],{},[941,146911,25333],{}," is the primary, validator-agnostic authoring surface. It is\ndeclared as a collection option:",[2217,146914,146916],{"className":2219,"code":146915,"language":2221,"meta":1037,"style":1037},"import { createNoydb, money, dictKey, ref } from '@noy-db\u002Fhub'\nimport { toMemory } from '@noy-db\u002Fto-memory'\n\nconst db = await createNoydb({ store: toMemory(), user: 'alice', secret: 'a-strong-secret' })\nconst vault = await db.openVault('ledger')\n\nconst saleStatus = dictKey('saleStatus', ['draft', 'to_verify', 'paid'] as const)\n\ntype Sale = { id: string; saleDate: string; total: number; status: string; buyerId: string; notes?: string }\n\nconst sales = vault.collection\u003CSale>('sales', {\n  moneyFields:   { total: money({ currency: 'EUR' }) },\n  dictKeyFields: { status: saleStatus },\n  refs:          { buyerId: ref('buyers') },\n  fieldMeta: {\n    saleDate: { label: 'Date' },\n    total:    { label: 'Total (€)', unit: '€' },\n    buyerId:  { label: 'Buyer', sensitivity: 'pii', displayFor: 'buyerName' },\n    notes:    { label: 'Internal notes', sensitivity: 'secret' },\n  },\n})\n",[941,146917,146918,146948,146966,146970,147022,147048,147052,147107,147111,147171,147175,147207,147241,147257,147284,147293,147314,147349,147397,147431,147435],{"__ignoreMap":1037},[2225,146919,146920,146922,146924,146926,146928,146930,146932,146934,146936,146938,146940,146942,146944,146946],{"class":2227,"line":2228},[2225,146921,2232],{"class":2231},[2225,146923,2236],{"class":2235},[2225,146925,2744],{"class":2239},[2225,146927,2298],{"class":2235},[2225,146929,123521],{"class":2239},[2225,146931,2298],{"class":2235},[2225,146933,58814],{"class":2239},[2225,146935,2298],{"class":2235},[2225,146937,62303],{"class":2239},[2225,146939,2243],{"class":2235},[2225,146941,2246],{"class":2231},[2225,146943,2249],{"class":2235},[2225,146945,1086],{"class":2252},[2225,146947,2256],{"class":2235},[2225,146949,146950,146952,146954,146956,146958,146960,146962,146964],{"class":2227,"line":1041},[2225,146951,2232],{"class":2231},[2225,146953,2236],{"class":2235},[2225,146955,68576],{"class":2239},[2225,146957,2243],{"class":2235},[2225,146959,2246],{"class":2231},[2225,146961,2249],{"class":2235},[2225,146963,9638],{"class":2252},[2225,146965,2256],{"class":2235},[2225,146967,146968],{"class":2227,"line":1038},[2225,146969,2285],{"emptyLinePlaceholder":2284},[2225,146971,146972,146974,146976,146978,146980,146982,146984,146986,146988,146990,146992,146994,146996,146998,147000,147002,147004,147006,147008,147010,147012,147014,147016,147018,147020],{"class":2227,"line":2288},[2225,146973,2775],{"class":2774},[2225,146975,2778],{"class":2239},[2225,146977,2781],{"class":2235},[2225,146979,2784],{"class":2231},[2225,146981,2744],{"class":2291},[2225,146983,2789],{"class":2239},[2225,146985,2792],{"class":2235},[2225,146987,7564],{"class":2307},[2225,146989,2311],{"class":2235},[2225,146991,68576],{"class":2291},[2225,146993,4228],{"class":2239},[2225,146995,2298],{"class":2235},[2225,146997,2795],{"class":2307},[2225,146999,2311],{"class":2235},[2225,147001,2249],{"class":2235},[2225,147003,6408],{"class":2252},[2225,147005,2805],{"class":2235},[2225,147007,2298],{"class":2235},[2225,147009,2810],{"class":2307},[2225,147011,2311],{"class":2235},[2225,147013,2249],{"class":2235},[2225,147015,6421],{"class":2252},[2225,147017,2805],{"class":2235},[2225,147019,2243],{"class":2235},[2225,147021,2362],{"class":2239},[2225,147023,147024,147026,147028,147030,147032,147034,147036,147038,147040,147042,147044,147046],{"class":2227,"line":2304},[2225,147025,2775],{"class":2774},[2225,147027,2835],{"class":2239},[2225,147029,2781],{"class":2235},[2225,147031,2784],{"class":2231},[2225,147033,2842],{"class":2239},[2225,147035,1520],{"class":2235},[2225,147037,2847],{"class":2291},[2225,147039,2789],{"class":2239},[2225,147041,2805],{"class":2235},[2225,147043,20923],{"class":2252},[2225,147045,2805],{"class":2235},[2225,147047,2362],{"class":2239},[2225,147049,147050],{"class":2227,"line":2356},[2225,147051,2285],{"emptyLinePlaceholder":2284},[2225,147053,147054,147056,147059,147061,147063,147065,147067,147070,147072,147074,147076,147078,147080,147082,147084,147086,147089,147091,147093,147095,147097,147099,147101,147103,147105],{"class":2227,"line":2830},[2225,147055,2775],{"class":2774},[2225,147057,147058],{"class":2239}," saleStatus ",[2225,147060,2781],{"class":2235},[2225,147062,58814],{"class":2291},[2225,147064,2789],{"class":2239},[2225,147066,2805],{"class":2235},[2225,147068,147069],{"class":2252},"saleStatus",[2225,147071,2805],{"class":2235},[2225,147073,2298],{"class":2235},[2225,147075,8667],{"class":2239},[2225,147077,2805],{"class":2235},[2225,147079,28429],{"class":2252},[2225,147081,2805],{"class":2235},[2225,147083,2298],{"class":2235},[2225,147085,2249],{"class":2235},[2225,147087,147088],{"class":2252},"to_verify",[2225,147090,2805],{"class":2235},[2225,147092,2298],{"class":2235},[2225,147094,2249],{"class":2235},[2225,147096,6539],{"class":2252},[2225,147098,2805],{"class":2235},[2225,147100,8890],{"class":2239},[2225,147102,4899],{"class":2231},[2225,147104,8083],{"class":2774},[2225,147106,2362],{"class":2239},[2225,147108,147109],{"class":2227,"line":2861},[2225,147110,2285],{"emptyLinePlaceholder":2284},[2225,147112,147113,147115,147117,147119,147121,147123,147125,147127,147129,147132,147134,147136,147138,147140,147142,147144,147146,147148,147150,147152,147154,147157,147159,147161,147163,147165,147167,147169],{"class":2227,"line":4246},[2225,147114,6339],{"class":2774},[2225,147116,139196],{"class":6342},[2225,147118,6346],{"class":2235},[2225,147120,2236],{"class":2235},[2225,147122,7070],{"class":2307},[2225,147124,2311],{"class":2235},[2225,147126,6366],{"class":6342},[2225,147128,3769],{"class":2235},[2225,147130,147131],{"class":2307}," saleDate",[2225,147133,2311],{"class":2235},[2225,147135,6366],{"class":6342},[2225,147137,3769],{"class":2235},[2225,147139,27452],{"class":2307},[2225,147141,2311],{"class":2235},[2225,147143,6356],{"class":6342},[2225,147145,3769],{"class":2235},[2225,147147,6361],{"class":2307},[2225,147149,2311],{"class":2235},[2225,147151,6366],{"class":6342},[2225,147153,3769],{"class":2235},[2225,147155,147156],{"class":2307}," buyerId",[2225,147158,2311],{"class":2235},[2225,147160,6366],{"class":6342},[2225,147162,3769],{"class":2235},[2225,147164,42817],{"class":2307},[2225,147166,7011],{"class":2235},[2225,147168,6366],{"class":6342},[2225,147170,6369],{"class":2235},[2225,147172,147173],{"class":2227,"line":4253},[2225,147174,2285],{"emptyLinePlaceholder":2284},[2225,147176,147177,147179,147181,147183,147185,147187,147189,147191,147193,147195,147197,147199,147201,147203,147205],{"class":2227,"line":4259},[2225,147178,2775],{"class":2774},[2225,147180,123547],{"class":2239},[2225,147182,2781],{"class":2235},[2225,147184,6466],{"class":2239},[2225,147186,1520],{"class":2235},[2225,147188,6471],{"class":2291},[2225,147190,6474],{"class":2235},[2225,147192,139184],{"class":6342},[2225,147194,6480],{"class":2235},[2225,147196,2789],{"class":2239},[2225,147198,2805],{"class":2235},[2225,147200,123562],{"class":2252},[2225,147202,2805],{"class":2235},[2225,147204,2298],{"class":2235},[2225,147206,2301],{"class":2235},[2225,147208,147209,147211,147213,147215,147217,147219,147221,147223,147225,147227,147229,147231,147233,147235,147237,147239],{"class":2227,"line":6548},[2225,147210,123730],{"class":2307},[2225,147212,2311],{"class":2235},[2225,147214,58994],{"class":2235},[2225,147216,27452],{"class":2307},[2225,147218,2311],{"class":2235},[2225,147220,123521],{"class":2291},[2225,147222,2789],{"class":2239},[2225,147224,2792],{"class":2235},[2225,147226,122489],{"class":2307},[2225,147228,2311],{"class":2235},[2225,147230,2249],{"class":2235},[2225,147232,122902],{"class":2252},[2225,147234,2805],{"class":2235},[2225,147236,2243],{"class":2235},[2225,147238,8495],{"class":2239},[2225,147240,19008],{"class":2235},[2225,147242,147243,147245,147247,147249,147251,147253,147255],{"class":2227,"line":6576},[2225,147244,59231],{"class":2307},[2225,147246,2311],{"class":2235},[2225,147248,2236],{"class":2235},[2225,147250,6361],{"class":2307},[2225,147252,2311],{"class":2235},[2225,147254,147058],{"class":2239},[2225,147256,19008],{"class":2235},[2225,147258,147259,147261,147263,147266,147268,147270,147272,147274,147276,147278,147280,147282],{"class":2227,"line":6582},[2225,147260,62642],{"class":2307},[2225,147262,2311],{"class":2235},[2225,147264,147265],{"class":2235},"          {",[2225,147267,147156],{"class":2307},[2225,147269,2311],{"class":2235},[2225,147271,62303],{"class":2291},[2225,147273,2789],{"class":2239},[2225,147275,2805],{"class":2235},[2225,147277,139251],{"class":2252},[2225,147279,2805],{"class":2235},[2225,147281,8495],{"class":2239},[2225,147283,19008],{"class":2235},[2225,147285,147286,147289,147291],{"class":2227,"line":6587},[2225,147287,147288],{"class":2307},"  fieldMeta",[2225,147290,2311],{"class":2235},[2225,147292,2301],{"class":2235},[2225,147294,147295,147298,147300,147302,147304,147306,147308,147310,147312],{"class":2227,"line":7174},[2225,147296,147297],{"class":2307},"    saleDate",[2225,147299,2311],{"class":2235},[2225,147301,2236],{"class":2235},[2225,147303,95892],{"class":2307},[2225,147305,2311],{"class":2235},[2225,147307,2249],{"class":2235},[2225,147309,26712],{"class":2252},[2225,147311,2805],{"class":2235},[2225,147313,2353],{"class":2235},[2225,147315,147316,147318,147320,147322,147324,147326,147328,147331,147333,147335,147338,147340,147342,147345,147347],{"class":2227,"line":7180},[2225,147317,64102],{"class":2307},[2225,147319,2311],{"class":2235},[2225,147321,32496],{"class":2235},[2225,147323,95892],{"class":2307},[2225,147325,2311],{"class":2235},[2225,147327,2249],{"class":2235},[2225,147329,147330],{"class":2252},"Total (€)",[2225,147332,2805],{"class":2235},[2225,147334,2298],{"class":2235},[2225,147336,147337],{"class":2307}," unit",[2225,147339,2311],{"class":2235},[2225,147341,2249],{"class":2235},[2225,147343,147344],{"class":2252},"€",[2225,147346,2805],{"class":2235},[2225,147348,2353],{"class":2235},[2225,147350,147351,147354,147356,147358,147360,147362,147364,147366,147368,147370,147373,147375,147377,147380,147382,147384,147387,147389,147391,147393,147395],{"class":2227,"line":7220},[2225,147352,147353],{"class":2307},"    buyerId",[2225,147355,2311],{"class":2235},[2225,147357,26354],{"class":2235},[2225,147359,95892],{"class":2307},[2225,147361,2311],{"class":2235},[2225,147363,2249],{"class":2235},[2225,147365,139386],{"class":2252},[2225,147367,2805],{"class":2235},[2225,147369,2298],{"class":2235},[2225,147371,147372],{"class":2307}," sensitivity",[2225,147374,2311],{"class":2235},[2225,147376,2249],{"class":2235},[2225,147378,147379],{"class":2252},"pii",[2225,147381,2805],{"class":2235},[2225,147383,2298],{"class":2235},[2225,147385,147386],{"class":2307}," displayFor",[2225,147388,2311],{"class":2235},[2225,147390,2249],{"class":2235},[2225,147392,139330],{"class":2252},[2225,147394,2805],{"class":2235},[2225,147396,2353],{"class":2235},[2225,147398,147399,147402,147404,147406,147408,147410,147412,147415,147417,147419,147421,147423,147425,147427,147429],{"class":2227,"line":7225},[2225,147400,147401],{"class":2307},"    notes",[2225,147403,2311],{"class":2235},[2225,147405,32496],{"class":2235},[2225,147407,95892],{"class":2307},[2225,147409,2311],{"class":2235},[2225,147411,2249],{"class":2235},[2225,147413,147414],{"class":2252},"Internal notes",[2225,147416,2805],{"class":2235},[2225,147418,2298],{"class":2235},[2225,147420,147372],{"class":2307},[2225,147422,2311],{"class":2235},[2225,147424,2249],{"class":2235},[2225,147426,10695],{"class":2252},[2225,147428,2805],{"class":2235},[2225,147430,2353],{"class":2235},[2225,147432,147433],{"class":2227,"line":7231},[2225,147434,19017],{"class":2235},[2225,147436,147437,147439],{"class":2227,"line":7267},[2225,147438,2359],{"class":2235},[2225,147440,2362],{"class":2239},[937,147442,147443,147446],{},[941,147444,147445],{},"FieldMeta"," members:",[1338,147448,147449,147459],{},[1341,147450,147451],{},[1344,147452,147453,147455,147457],{},[1347,147454,101104],{},[1347,147456,20194],{},[1347,147458,7834],{},[1357,147460,147461,147475,147489,147515,147537,147551,147565,147579,147598,147618,147640],{},[1344,147462,147463,147467,147472],{},[1362,147464,147465],{},[941,147466,96028],{},[1362,147468,147469,147471],{},[941,147470,7259],{}," (required)",[1362,147473,147474],{},"Human display name",[1344,147476,147477,147481,147486],{},[1362,147478,147479],{},[941,147480,59777],{},[1362,147482,147483],{},[941,147484,147485],{},"string?",[1362,147487,147488],{},"Longer tooltip \u002F context",[1344,147490,147491,147495,147500],{},[1362,147492,147493],{},[941,147494,146763],{},[1362,147496,147497],{},[941,147498,147499],{},"SemanticType?",[1362,147501,147502,147503,968,147506,968,147509,968,147512,96338],{},"Domain hint: ",[941,147504,147505],{},"'date'",[941,147507,147508],{},"'currency'",[941,147510,147511],{},"'email'",[941,147513,147514],{},"'vat'",[1344,147516,147517,147521,147525],{},[1362,147518,147519],{},[941,147520,146766],{},[1362,147522,147523],{},[941,147524,147485],{},[1362,147526,147527,147528,968,147531,968,147534,96338],{},"Display unit: ",[941,147529,147530],{},"'€'",[941,147532,147533],{},"'%'",[941,147535,147536],{},"'kg'",[1344,147538,147539,147543,147548],{},[1362,147540,147541],{},[941,147542,146769],{},[1362,147544,147545],{},[941,147546,147547],{},"'public' | 'pii' | 'secret'",[1362,147549,147550],{},"Data-classification for redaction\u002Fmasking",[1344,147552,147553,147557,147562],{},[1362,147554,147555],{},[941,147556,4449],{},[1362,147558,147559],{},[941,147560,147561],{},"'sum' | 'count' | 'distinct' | 'none'",[1362,147563,147564],{},"Default aggregation",[1344,147566,147567,147571,147576],{},[1362,147568,147569],{},[941,147570,146774],{},[1362,147572,147573],{},[941,147574,147575],{},"readonly string[]?",[1362,147577,147578],{},"Canonical search synonyms",[1344,147580,147581,147585,147589],{},[1362,147582,147583],{},[941,147584,146777],{},[1362,147586,147587],{},[941,147588,147485],{},[1362,147590,147591,147592,1746,147595],{},"Entity pairing: ",[941,147593,147594],{},"'buyerId'",[941,147596,147597],{},"displayFor: 'buyerName'",[1344,147599,147600,147605,147609],{},[1362,147601,147602],{},[941,147603,147604],{},"widget",[1362,147606,147607],{},[941,147608,147485],{},[1362,147610,147611,147612,147614,147615,147617],{},"Overrides the auto-derived widget hint (see the ",[941,147613,147604],{}," override in \"",[941,147616,3926],{}," enhancements\" below)",[1344,147619,147620,147624,147628],{},[1362,147621,147622],{},[941,147623,2115],{},[1362,147625,147626],{},[941,147627,147485],{},[1362,147629,147630,147631,968,147634,5584,147637,147639],{},"Card\u002Fsection grouping hint for detail & form layouts (e.g. ",[941,147632,147633],{},"'Identity'",[941,147635,147636],{},"'Amounts'",[941,147638,3926],{}," still emits fields alphabetically — grouping is a hint for the consumer to apply",[1344,147641,147642,147647,147652],{},[1362,147643,147644],{},[941,147645,147646],{},"order",[1362,147648,147649],{},[941,147650,147651],{},"number?",[1362,147653,147654],{},"Relative ordering hint within (and across) groups; lower renders first",[104892,147656],{},[1022,147658,147660],{"id":147659},"merge-precedence","Merge precedence",[937,147662,147663],{},"For each field, three tiers of metadata are merged in priority order:",[2217,147665,147668],{"className":147666,"code":147667,"language":5672},[5670],"channel (fieldMeta) > zod-4 .meta() (async path) > inferred-from-config\n",[941,147669,147667],{"__ignoreMap":1037},[1234,147671,147672,147682,147720],{},[1114,147673,147674,147677,147678,147681],{},[950,147675,147676],{},"channel"," — values from ",[941,147679,147680],{},"collection({ fieldMeta: { … } })",". Wins over\neverything. This is where the app author declares intent.",[1114,147683,147684,147689,147690,968,147692,968,147694,968,147696,968,147698,968,147700,1707,147702,968,147704,968,147706,968,147708,147710,147711,147713,147714,147716,147717,147719],{},[950,147685,147686,147687],{},"zod-4 ",[941,147688,146833],{}," — async path only. Keys recognized:\n",[941,147691,96028],{},[941,147693,59777],{},[941,147695,146766],{},[941,147697,146763],{},[941,147699,146769],{},[941,147701,4449],{},[941,147703,146774],{},[941,147705,146777],{},[941,147707,2115],{},[941,147709,147646],{},". (",[941,147712,147604],{}," is channel-only — zod\n",[941,147715,146833],{}," cannot set it.) Unknown ",[941,147718,146833],{}," keys are silently ignored.",[1114,147721,147722,147725,147726],{},[950,147723,147724],{},"inferred-from-config"," — automatically derived from the collection's other\nconfig:\n",[1111,147727,147728,147741,147756],{},[1114,147729,147730,1746,147732,968,147735,968,147738],{},[941,147731,22765],{},[941,147733,147734],{},"semanticType:'currency'",[941,147736,147737],{},"aggregate:'sum'",[941,147739,147740],{},"type:'number'",[1114,147742,147743,1746,147745,968,147748,36059,147751,6793,147753,1381],{},[941,147744,22753],{},[941,147746,147747],{},"semanticType:'entity'",[941,147749,147750],{},"type:'string'",[941,147752,146850],{},[941,147754,147755],{},"refArray",[1114,147757,147758,1746,147760,968,147763],{},[941,147759,22759],{},[941,147761,147762],{},"type:'enum'",[941,147764,147765],{},"dict: { name, static, values[] }",[937,147767,147768,147769,968,147771,968,147774,147777],{},"Structural extras (",[941,147770,760],{},[941,147772,147773],{},"dict",[941,147775,147776],{},"ref"," blocks) always come from config, not\nfrom the channel.",[104892,147779],{},[1022,147781,147783],{"id":147782},"two-consumer-pattern","Two-consumer pattern",[937,147785,54460,147786,147788],{},[941,147787,3926],{}," once; hand the result to every consumer:",[2217,147790,147792],{"className":2219,"code":147791,"language":2221,"meta":1037,"style":1037},"const d = sales.describe()\n",[941,147793,147794],{"__ignoreMap":1037},[2225,147795,147796,147798,147801,147803,147805,147807,147809],{"class":2227,"line":2228},[2225,147797,2775],{"class":2774},[2225,147799,147800],{"class":2239}," d ",[2225,147802,2781],{"class":2235},[2225,147804,123785],{"class":2239},[2225,147806,1520],{"class":2235},[2225,147808,6153],{"class":2291},[2225,147810,8146],{"class":2239},[937,147812,147813,953,147816,147818,147819,147821],{},[941,147814,147815],{},"d",[941,147817,25317],{},", using the ",[941,147820,123562],{}," collection declared above):",[2217,147823,147825],{"className":6604,"code":147824,"language":141,"meta":1037,"style":1037},"{\n  \"collection\": \"sales\",\n  \"fields\": [\n    { \"key\": \"buyerId\", \"type\": \"string\", \"optional\": false, \"label\": \"Buyer\",\n      \"semanticType\": \"entity\", \"sensitivity\": \"pii\", \"displayFor\": \"buyerName\",\n      \"ref\": { \"target\": \"buyers\", \"mode\": \"strict\" }, \"widget\": \"ref-select\", \"editable\": true },\n    { \"key\": \"notes\", \"type\": \"unknown\", \"optional\": false, \"label\": \"Internal notes\",\n      \"sensitivity\": \"secret\", \"widget\": \"text\", \"editable\": true },\n    { \"key\": \"saleDate\", \"type\": \"unknown\", \"optional\": false, \"label\": \"Date\",\n      \"widget\": \"text\", \"editable\": true },\n    { \"key\": \"status\", \"type\": \"enum\", \"optional\": false, \"label\": \"Status\",\n      \"dict\": { \"name\": \"saleStatus\", \"static\": false,\n        \"values\": [{ \"value\": \"draft\" }, { \"value\": \"to_verify\" }, { \"value\": \"paid\" }] },\n      \"widget\": \"select\", \"editable\": true },\n    { \"key\": \"total\", \"type\": \"number\", \"optional\": false, \"label\": \"Total (€)\", \"unit\": \"€\",\n      \"semanticType\": \"currency\", \"aggregate\": \"sum\",\n      \"money\": { \"mode\": \"fixed\", \"currency\": \"EUR\", \"scale\": 2 }, \"widget\": \"money\", \"editable\": true }\n  ],\n  \"meta\": { \"label\": \"Sales\" }\n}\n",[941,147826,147827,147831,147849,147861,147924,147975,148049,148111,148157,148220,148250,148312,148351,148420,148450,148528,148563,148649,148653,148682],{"__ignoreMap":1037},[2225,147828,147829],{"class":2227,"line":2228},[2225,147830,4200],{"class":2235},[2225,147832,147833,147835,147837,147839,147841,147843,147845,147847],{"class":2227,"line":1041},[2225,147834,9368],{"class":2235},[2225,147836,6471],{"class":2774},[2225,147838,6620],{"class":2235},[2225,147840,2311],{"class":2235},[2225,147842,6614],{"class":2235},[2225,147844,123562],{"class":2252},[2225,147846,6620],{"class":2235},[2225,147848,1707],{"class":2235},[2225,147850,147851,147853,147855,147857,147859],{"class":2227,"line":1038},[2225,147852,9368],{"class":2235},[2225,147854,57285],{"class":2774},[2225,147856,6620],{"class":2235},[2225,147858,2311],{"class":2235},[2225,147860,32491],{"class":2235},[2225,147862,147863,147865,147867,147869,147871,147873,147875,147877,147879,147881,147883,147885,147887,147889,147891,147893,147895,147897,147899,147901,147903,147905,147908,147910,147912,147914,147916,147918,147920,147922],{"class":2227,"line":2288},[2225,147864,32496],{"class":2235},[2225,147866,6614],{"class":2235},[2225,147868,8364],{"class":6342},[2225,147870,6620],{"class":2235},[2225,147872,2311],{"class":2235},[2225,147874,6614],{"class":2235},[2225,147876,109007],{"class":2252},[2225,147878,6620],{"class":2235},[2225,147880,2298],{"class":2235},[2225,147882,6614],{"class":2235},[2225,147884,6339],{"class":6342},[2225,147886,6620],{"class":2235},[2225,147888,2311],{"class":2235},[2225,147890,6614],{"class":2235},[2225,147892,7259],{"class":2252},[2225,147894,6620],{"class":2235},[2225,147896,2298],{"class":2235},[2225,147898,6614],{"class":2235},[2225,147900,2084],{"class":6342},[2225,147902,6620],{"class":2235},[2225,147904,2311],{"class":2235},[2225,147906,147907],{"class":2235}," false,",[2225,147909,6614],{"class":2235},[2225,147911,96028],{"class":6342},[2225,147913,6620],{"class":2235},[2225,147915,2311],{"class":2235},[2225,147917,6614],{"class":2235},[2225,147919,139386],{"class":2252},[2225,147921,6620],{"class":2235},[2225,147923,1707],{"class":2235},[2225,147925,147926,147928,147930,147932,147934,147936,147939,147941,147943,147945,147947,147949,147951,147953,147955,147957,147959,147961,147963,147965,147967,147969,147971,147973],{"class":2227,"line":2304},[2225,147927,59948],{"class":2235},[2225,147929,146763],{"class":6342},[2225,147931,6620],{"class":2235},[2225,147933,2311],{"class":2235},[2225,147935,6614],{"class":2235},[2225,147937,147938],{"class":2252},"entity",[2225,147940,6620],{"class":2235},[2225,147942,2298],{"class":2235},[2225,147944,6614],{"class":2235},[2225,147946,146769],{"class":6342},[2225,147948,6620],{"class":2235},[2225,147950,2311],{"class":2235},[2225,147952,6614],{"class":2235},[2225,147954,147379],{"class":2252},[2225,147956,6620],{"class":2235},[2225,147958,2298],{"class":2235},[2225,147960,6614],{"class":2235},[2225,147962,146777],{"class":6342},[2225,147964,6620],{"class":2235},[2225,147966,2311],{"class":2235},[2225,147968,6614],{"class":2235},[2225,147970,139330],{"class":2252},[2225,147972,6620],{"class":2235},[2225,147974,1707],{"class":2235},[2225,147976,147977,147979,147981,147983,147985,147987,147989,147991,147993,147995,147997,147999,148001,148003,148005,148007,148009,148011,148013,148015,148017,148019,148021,148023,148025,148027,148029,148032,148034,148036,148038,148041,148043,148045,148047],{"class":2227,"line":2356},[2225,147978,59948],{"class":2235},[2225,147980,147776],{"class":6342},[2225,147982,6620],{"class":2235},[2225,147984,2311],{"class":2235},[2225,147986,2236],{"class":2235},[2225,147988,6614],{"class":2235},[2225,147990,134984],{"class":6527},[2225,147992,6620],{"class":2235},[2225,147994,2311],{"class":2235},[2225,147996,6614],{"class":2235},[2225,147998,139251],{"class":2252},[2225,148000,6620],{"class":2235},[2225,148002,2298],{"class":2235},[2225,148004,6614],{"class":2235},[2225,148006,120875],{"class":6527},[2225,148008,6620],{"class":2235},[2225,148010,2311],{"class":2235},[2225,148012,6614],{"class":2235},[2225,148014,140597],{"class":2252},[2225,148016,6620],{"class":2235},[2225,148018,10965],{"class":2235},[2225,148020,6614],{"class":2235},[2225,148022,147604],{"class":6342},[2225,148024,6620],{"class":2235},[2225,148026,2311],{"class":2235},[2225,148028,6614],{"class":2235},[2225,148030,148031],{"class":2252},"ref-select",[2225,148033,6620],{"class":2235},[2225,148035,2298],{"class":2235},[2225,148037,6614],{"class":2235},[2225,148039,148040],{"class":6342},"editable",[2225,148042,6620],{"class":2235},[2225,148044,2311],{"class":2235},[2225,148046,2322],{"class":2235},[2225,148048,2353],{"class":2235},[2225,148050,148051,148053,148055,148057,148059,148061,148063,148065,148067,148069,148071,148073,148075,148077,148079,148081,148083,148085,148087,148089,148091,148093,148095,148097,148099,148101,148103,148105,148107,148109],{"class":2227,"line":2830},[2225,148052,32496],{"class":2235},[2225,148054,6614],{"class":2235},[2225,148056,8364],{"class":6342},[2225,148058,6620],{"class":2235},[2225,148060,2311],{"class":2235},[2225,148062,6614],{"class":2235},[2225,148064,2854],{"class":2252},[2225,148066,6620],{"class":2235},[2225,148068,2298],{"class":2235},[2225,148070,6614],{"class":2235},[2225,148072,6339],{"class":6342},[2225,148074,6620],{"class":2235},[2225,148076,2311],{"class":2235},[2225,148078,6614],{"class":2235},[2225,148080,22421],{"class":2252},[2225,148082,6620],{"class":2235},[2225,148084,2298],{"class":2235},[2225,148086,6614],{"class":2235},[2225,148088,2084],{"class":6342},[2225,148090,6620],{"class":2235},[2225,148092,2311],{"class":2235},[2225,148094,147907],{"class":2235},[2225,148096,6614],{"class":2235},[2225,148098,96028],{"class":6342},[2225,148100,6620],{"class":2235},[2225,148102,2311],{"class":2235},[2225,148104,6614],{"class":2235},[2225,148106,147414],{"class":2252},[2225,148108,6620],{"class":2235},[2225,148110,1707],{"class":2235},[2225,148112,148113,148115,148117,148119,148121,148123,148125,148127,148129,148131,148133,148135,148137,148139,148141,148143,148145,148147,148149,148151,148153,148155],{"class":2227,"line":2861},[2225,148114,59948],{"class":2235},[2225,148116,146769],{"class":6342},[2225,148118,6620],{"class":2235},[2225,148120,2311],{"class":2235},[2225,148122,6614],{"class":2235},[2225,148124,10695],{"class":2252},[2225,148126,6620],{"class":2235},[2225,148128,2298],{"class":2235},[2225,148130,6614],{"class":2235},[2225,148132,147604],{"class":6342},[2225,148134,6620],{"class":2235},[2225,148136,2311],{"class":2235},[2225,148138,6614],{"class":2235},[2225,148140,5672],{"class":2252},[2225,148142,6620],{"class":2235},[2225,148144,2298],{"class":2235},[2225,148146,6614],{"class":2235},[2225,148148,148040],{"class":6342},[2225,148150,6620],{"class":2235},[2225,148152,2311],{"class":2235},[2225,148154,2322],{"class":2235},[2225,148156,2353],{"class":2235},[2225,148158,148159,148161,148163,148165,148167,148169,148171,148174,148176,148178,148180,148182,148184,148186,148188,148190,148192,148194,148196,148198,148200,148202,148204,148206,148208,148210,148212,148214,148216,148218],{"class":2227,"line":4246},[2225,148160,32496],{"class":2235},[2225,148162,6614],{"class":2235},[2225,148164,8364],{"class":6342},[2225,148166,6620],{"class":2235},[2225,148168,2311],{"class":2235},[2225,148170,6614],{"class":2235},[2225,148172,148173],{"class":2252},"saleDate",[2225,148175,6620],{"class":2235},[2225,148177,2298],{"class":2235},[2225,148179,6614],{"class":2235},[2225,148181,6339],{"class":6342},[2225,148183,6620],{"class":2235},[2225,148185,2311],{"class":2235},[2225,148187,6614],{"class":2235},[2225,148189,22421],{"class":2252},[2225,148191,6620],{"class":2235},[2225,148193,2298],{"class":2235},[2225,148195,6614],{"class":2235},[2225,148197,2084],{"class":6342},[2225,148199,6620],{"class":2235},[2225,148201,2311],{"class":2235},[2225,148203,147907],{"class":2235},[2225,148205,6614],{"class":2235},[2225,148207,96028],{"class":6342},[2225,148209,6620],{"class":2235},[2225,148211,2311],{"class":2235},[2225,148213,6614],{"class":2235},[2225,148215,26712],{"class":2252},[2225,148217,6620],{"class":2235},[2225,148219,1707],{"class":2235},[2225,148221,148222,148224,148226,148228,148230,148232,148234,148236,148238,148240,148242,148244,148246,148248],{"class":2227,"line":4253},[2225,148223,59948],{"class":2235},[2225,148225,147604],{"class":6342},[2225,148227,6620],{"class":2235},[2225,148229,2311],{"class":2235},[2225,148231,6614],{"class":2235},[2225,148233,5672],{"class":2252},[2225,148235,6620],{"class":2235},[2225,148237,2298],{"class":2235},[2225,148239,6614],{"class":2235},[2225,148241,148040],{"class":6342},[2225,148243,6620],{"class":2235},[2225,148245,2311],{"class":2235},[2225,148247,2322],{"class":2235},[2225,148249,2353],{"class":2235},[2225,148251,148252,148254,148256,148258,148260,148262,148264,148266,148268,148270,148272,148274,148276,148278,148280,148282,148284,148286,148288,148290,148292,148294,148296,148298,148300,148302,148304,148306,148308,148310],{"class":2227,"line":4259},[2225,148253,32496],{"class":2235},[2225,148255,6614],{"class":2235},[2225,148257,8364],{"class":6342},[2225,148259,6620],{"class":2235},[2225,148261,2311],{"class":2235},[2225,148263,6614],{"class":2235},[2225,148265,6631],{"class":2252},[2225,148267,6620],{"class":2235},[2225,148269,2298],{"class":2235},[2225,148271,6614],{"class":2235},[2225,148273,6339],{"class":6342},[2225,148275,6620],{"class":2235},[2225,148277,2311],{"class":2235},[2225,148279,6614],{"class":2235},[2225,148281,28421],{"class":2252},[2225,148283,6620],{"class":2235},[2225,148285,2298],{"class":2235},[2225,148287,6614],{"class":2235},[2225,148289,2084],{"class":6342},[2225,148291,6620],{"class":2235},[2225,148293,2311],{"class":2235},[2225,148295,147907],{"class":2235},[2225,148297,6614],{"class":2235},[2225,148299,96028],{"class":6342},[2225,148301,6620],{"class":2235},[2225,148303,2311],{"class":2235},[2225,148305,6614],{"class":2235},[2225,148307,48731],{"class":2252},[2225,148309,6620],{"class":2235},[2225,148311,1707],{"class":2235},[2225,148313,148314,148316,148318,148320,148322,148324,148326,148328,148330,148332,148334,148336,148338,148340,148342,148344,148346,148348],{"class":2227,"line":6548},[2225,148315,59948],{"class":2235},[2225,148317,147773],{"class":6342},[2225,148319,6620],{"class":2235},[2225,148321,2311],{"class":2235},[2225,148323,2236],{"class":2235},[2225,148325,6614],{"class":2235},[2225,148327,4914],{"class":6527},[2225,148329,6620],{"class":2235},[2225,148331,2311],{"class":2235},[2225,148333,6614],{"class":2235},[2225,148335,147069],{"class":2252},[2225,148337,6620],{"class":2235},[2225,148339,2298],{"class":2235},[2225,148341,6614],{"class":2235},[2225,148343,8315],{"class":6527},[2225,148345,6620],{"class":2235},[2225,148347,2311],{"class":2235},[2225,148349,148350],{"class":2235}," false,\n",[2225,148352,148353,148355,148358,148360,148362,148365,148367,148369,148371,148373,148375,148377,148379,148381,148383,148385,148387,148389,148391,148393,148395,148397,148399,148401,148403,148405,148407,148409,148411,148413,148415,148418],{"class":2227,"line":6576},[2225,148354,60015],{"class":2235},[2225,148356,148357],{"class":6527},"values",[2225,148359,6620],{"class":2235},[2225,148361,2311],{"class":2235},[2225,148363,148364],{"class":2235}," [{",[2225,148366,6614],{"class":2235},[2225,148368,25201],{"class":2307},[2225,148370,6620],{"class":2235},[2225,148372,2311],{"class":2235},[2225,148374,6614],{"class":2235},[2225,148376,28429],{"class":2252},[2225,148378,6620],{"class":2235},[2225,148380,10965],{"class":2235},[2225,148382,2236],{"class":2235},[2225,148384,6614],{"class":2235},[2225,148386,25201],{"class":2307},[2225,148388,6620],{"class":2235},[2225,148390,2311],{"class":2235},[2225,148392,6614],{"class":2235},[2225,148394,147088],{"class":2252},[2225,148396,6620],{"class":2235},[2225,148398,10965],{"class":2235},[2225,148400,2236],{"class":2235},[2225,148402,6614],{"class":2235},[2225,148404,25201],{"class":2307},[2225,148406,6620],{"class":2235},[2225,148408,2311],{"class":2235},[2225,148410,6614],{"class":2235},[2225,148412,6539],{"class":2252},[2225,148414,6620],{"class":2235},[2225,148416,148417],{"class":2235}," }]",[2225,148419,2353],{"class":2235},[2225,148421,148422,148424,148426,148428,148430,148432,148434,148436,148438,148440,148442,148444,148446,148448],{"class":2227,"line":6582},[2225,148423,59948],{"class":2235},[2225,148425,147604],{"class":6342},[2225,148427,6620],{"class":2235},[2225,148429,2311],{"class":2235},[2225,148431,6614],{"class":2235},[2225,148433,51703],{"class":2252},[2225,148435,6620],{"class":2235},[2225,148437,2298],{"class":2235},[2225,148439,6614],{"class":2235},[2225,148441,148040],{"class":6342},[2225,148443,6620],{"class":2235},[2225,148445,2311],{"class":2235},[2225,148447,2322],{"class":2235},[2225,148449,2353],{"class":2235},[2225,148451,148452,148454,148456,148458,148460,148462,148464,148466,148468,148470,148472,148474,148476,148478,148480,148482,148484,148486,148488,148490,148492,148494,148496,148498,148500,148502,148504,148506,148508,148510,148512,148514,148516,148518,148520,148522,148524,148526],{"class":2227,"line":6587},[2225,148453,32496],{"class":2235},[2225,148455,6614],{"class":2235},[2225,148457,8364],{"class":6342},[2225,148459,6620],{"class":2235},[2225,148461,2311],{"class":2235},[2225,148463,6614],{"class":2235},[2225,148465,64200],{"class":2252},[2225,148467,6620],{"class":2235},[2225,148469,2298],{"class":2235},[2225,148471,6614],{"class":2235},[2225,148473,6339],{"class":6342},[2225,148475,6620],{"class":2235},[2225,148477,2311],{"class":2235},[2225,148479,6614],{"class":2235},[2225,148481,20636],{"class":2252},[2225,148483,6620],{"class":2235},[2225,148485,2298],{"class":2235},[2225,148487,6614],{"class":2235},[2225,148489,2084],{"class":6342},[2225,148491,6620],{"class":2235},[2225,148493,2311],{"class":2235},[2225,148495,147907],{"class":2235},[2225,148497,6614],{"class":2235},[2225,148499,96028],{"class":6342},[2225,148501,6620],{"class":2235},[2225,148503,2311],{"class":2235},[2225,148505,6614],{"class":2235},[2225,148507,147330],{"class":2252},[2225,148509,6620],{"class":2235},[2225,148511,2298],{"class":2235},[2225,148513,6614],{"class":2235},[2225,148515,146766],{"class":6342},[2225,148517,6620],{"class":2235},[2225,148519,2311],{"class":2235},[2225,148521,6614],{"class":2235},[2225,148523,147344],{"class":2252},[2225,148525,6620],{"class":2235},[2225,148527,1707],{"class":2235},[2225,148529,148530,148532,148534,148536,148538,148540,148543,148545,148547,148549,148551,148553,148555,148557,148559,148561],{"class":2227,"line":7174},[2225,148531,59948],{"class":2235},[2225,148533,146763],{"class":6342},[2225,148535,6620],{"class":2235},[2225,148537,2311],{"class":2235},[2225,148539,6614],{"class":2235},[2225,148541,148542],{"class":2252},"currency",[2225,148544,6620],{"class":2235},[2225,148546,2298],{"class":2235},[2225,148548,6614],{"class":2235},[2225,148550,4449],{"class":6342},[2225,148552,6620],{"class":2235},[2225,148554,2311],{"class":2235},[2225,148556,6614],{"class":2235},[2225,148558,28196],{"class":2252},[2225,148560,6620],{"class":2235},[2225,148562,1707],{"class":2235},[2225,148564,148565,148567,148569,148571,148573,148575,148577,148579,148581,148583,148585,148588,148590,148592,148594,148596,148598,148600,148602,148604,148606,148608,148610,148613,148615,148617,148619,148621,148623,148625,148627,148629,148631,148633,148635,148637,148639,148641,148643,148645,148647],{"class":2227,"line":7180},[2225,148566,59948],{"class":2235},[2225,148568,760],{"class":6342},[2225,148570,6620],{"class":2235},[2225,148572,2311],{"class":2235},[2225,148574,2236],{"class":2235},[2225,148576,6614],{"class":2235},[2225,148578,120875],{"class":6527},[2225,148580,6620],{"class":2235},[2225,148582,2311],{"class":2235},[2225,148584,6614],{"class":2235},[2225,148586,148587],{"class":2252},"fixed",[2225,148589,6620],{"class":2235},[2225,148591,2298],{"class":2235},[2225,148593,6614],{"class":2235},[2225,148595,148542],{"class":6527},[2225,148597,6620],{"class":2235},[2225,148599,2311],{"class":2235},[2225,148601,6614],{"class":2235},[2225,148603,122902],{"class":2252},[2225,148605,6620],{"class":2235},[2225,148607,2298],{"class":2235},[2225,148609,6614],{"class":2235},[2225,148611,148612],{"class":6527},"scale",[2225,148614,6620],{"class":2235},[2225,148616,2311],{"class":2235},[2225,148618,18961],{"class":6527},[2225,148620,10965],{"class":2235},[2225,148622,6614],{"class":2235},[2225,148624,147604],{"class":6342},[2225,148626,6620],{"class":2235},[2225,148628,2311],{"class":2235},[2225,148630,6614],{"class":2235},[2225,148632,760],{"class":2252},[2225,148634,6620],{"class":2235},[2225,148636,2298],{"class":2235},[2225,148638,6614],{"class":2235},[2225,148640,148040],{"class":6342},[2225,148642,6620],{"class":2235},[2225,148644,2311],{"class":2235},[2225,148646,2322],{"class":2235},[2225,148648,6369],{"class":2235},[2225,148650,148651],{"class":2227,"line":7220},[2225,148652,67686],{"class":2235},[2225,148654,148655,148657,148659,148661,148663,148665,148667,148669,148671,148673,148675,148678,148680],{"class":2227,"line":7225},[2225,148656,9368],{"class":2235},[2225,148658,17587],{"class":2774},[2225,148660,6620],{"class":2235},[2225,148662,2311],{"class":2235},[2225,148664,2236],{"class":2235},[2225,148666,6614],{"class":2235},[2225,148668,96028],{"class":6342},[2225,148670,6620],{"class":2235},[2225,148672,2311],{"class":2235},[2225,148674,6614],{"class":2235},[2225,148676,148677],{"class":2252},"Sales",[2225,148679,6620],{"class":2235},[2225,148681,6369],{"class":2235},[2225,148683,148684],{"class":2227,"line":7231},[2225,148685,7351],{"class":2235},[2217,148687,148689],{"className":2219,"code":148688,"language":2221,"meta":1037,"style":1037},"\u002F\u002F Consumer A — table header row\nconst headers = d.fields.map((f) => f.label)\n\u002F\u002F → ['Buyer', 'Internal notes', 'Date', 'Status', 'Total (€)']\n\n\u002F\u002F Consumer B — export column spec (flag PII\u002Fsecret for redaction)\nconst exportSpec = d.fields.map((f) => ({\n  key:      f.key,\n  label:    f.label,\n  redacted: f.sensitivity !== undefined && f.sensitivity !== 'public',\n}))\n\u002F\u002F → [{ key:'buyerId', label:'Buyer', redacted:true },\n\u002F\u002F    { key:'notes', label:'Internal notes', redacted:true },\n\u002F\u002F    { key:'saleDate', label:'Date', redacted:false },\n\u002F\u002F    { key:'status', label:'Status', redacted:false },\n\u002F\u002F    { key:'total', label:'Total (€)', redacted:false }]\n\n\u002F\u002F Consumer C — AI agent context (structured field list, no raw records)\nconst agentContext = d.fields.map((f) => ({\n  key:  f.key,\n  type: f.type,\n  ...(f.sensitivity === 'secret' ? { omit: true } : {}),\n}))\n\u002F\u002F → [{ key:'buyerId', type:'string' }, { key:'notes', type:'unknown', omit:true },\n\u002F\u002F    { key:'saleDate', type:'unknown' }, { key:'status', type:'enum' }, { key:'total', type:'number' }]\n",[941,148690,148691,148696,148733,148738,148742,148747,148780,148795,148810,148846,148852,148857,148862,148867,148872,148877,148881,148886,148919,148934,148948,148988,148994,148999],{"__ignoreMap":1037},[2225,148692,148693],{"class":2227,"line":2228},[2225,148694,148695],{"class":2763},"\u002F\u002F Consumer A — table header row\n",[2225,148697,148698,148700,148703,148705,148708,148710,148712,148714,148716,148718,148720,148722,148724,148726,148728,148730],{"class":2227,"line":1041},[2225,148699,2775],{"class":2774},[2225,148701,148702],{"class":2239}," headers ",[2225,148704,2781],{"class":2235},[2225,148706,148707],{"class":2239}," d",[2225,148709,1520],{"class":2235},[2225,148711,57285],{"class":2239},[2225,148713,1520],{"class":2235},[2225,148715,8244],{"class":2291},[2225,148717,2789],{"class":2239},[2225,148719,2789],{"class":2235},[2225,148721,67059],{"class":7052},[2225,148723,1381],{"class":2235},[2225,148725,8116],{"class":2774},[2225,148727,67074],{"class":2239},[2225,148729,1520],{"class":2235},[2225,148731,148732],{"class":2239},"label)\n",[2225,148734,148735],{"class":2227,"line":1038},[2225,148736,148737],{"class":2763},"\u002F\u002F → ['Buyer', 'Internal notes', 'Date', 'Status', 'Total (€)']\n",[2225,148739,148740],{"class":2227,"line":2288},[2225,148741,2285],{"emptyLinePlaceholder":2284},[2225,148743,148744],{"class":2227,"line":2304},[2225,148745,148746],{"class":2763},"\u002F\u002F Consumer B — export column spec (flag PII\u002Fsecret for redaction)\n",[2225,148748,148749,148751,148754,148756,148758,148760,148762,148764,148766,148768,148770,148772,148774,148776,148778],{"class":2227,"line":2356},[2225,148750,2775],{"class":2774},[2225,148752,148753],{"class":2239}," exportSpec ",[2225,148755,2781],{"class":2235},[2225,148757,148707],{"class":2239},[2225,148759,1520],{"class":2235},[2225,148761,57285],{"class":2239},[2225,148763,1520],{"class":2235},[2225,148765,8244],{"class":2291},[2225,148767,2789],{"class":2239},[2225,148769,2789],{"class":2235},[2225,148771,67059],{"class":7052},[2225,148773,1381],{"class":2235},[2225,148775,8116],{"class":2774},[2225,148777,953],{"class":2239},[2225,148779,4200],{"class":2235},[2225,148781,148782,148784,148786,148789,148791,148793],{"class":2227,"line":2830},[2225,148783,69281],{"class":2307},[2225,148785,2311],{"class":2235},[2225,148787,148788],{"class":2239},"      f",[2225,148790,1520],{"class":2235},[2225,148792,8364],{"class":2239},[2225,148794,1707],{"class":2235},[2225,148796,148797,148799,148801,148804,148806,148808],{"class":2227,"line":2861},[2225,148798,82578],{"class":2307},[2225,148800,2311],{"class":2235},[2225,148802,148803],{"class":2239},"    f",[2225,148805,1520],{"class":2235},[2225,148807,96028],{"class":2239},[2225,148809,1707],{"class":2235},[2225,148811,148812,148815,148817,148819,148821,148824,148826,148828,148830,148832,148834,148836,148838,148840,148842,148844],{"class":2227,"line":4246},[2225,148813,148814],{"class":2307},"  redacted",[2225,148816,2311],{"class":2235},[2225,148818,67074],{"class":2239},[2225,148820,1520],{"class":2235},[2225,148822,148823],{"class":2239},"sensitivity ",[2225,148825,32545],{"class":2235},[2225,148827,8483],{"class":2235},[2225,148829,8486],{"class":2235},[2225,148831,67074],{"class":2239},[2225,148833,1520],{"class":2235},[2225,148835,148823],{"class":2239},[2225,148837,32545],{"class":2235},[2225,148839,2249],{"class":2235},[2225,148841,12],{"class":2252},[2225,148843,2805],{"class":2235},[2225,148845,1707],{"class":2235},[2225,148847,148848,148850],{"class":2227,"line":4253},[2225,148849,2359],{"class":2235},[2225,148851,8606],{"class":2239},[2225,148853,148854],{"class":2227,"line":4259},[2225,148855,148856],{"class":2763},"\u002F\u002F → [{ key:'buyerId', label:'Buyer', redacted:true },\n",[2225,148858,148859],{"class":2227,"line":6548},[2225,148860,148861],{"class":2763},"\u002F\u002F    { key:'notes', label:'Internal notes', redacted:true },\n",[2225,148863,148864],{"class":2227,"line":6576},[2225,148865,148866],{"class":2763},"\u002F\u002F    { key:'saleDate', label:'Date', redacted:false },\n",[2225,148868,148869],{"class":2227,"line":6582},[2225,148870,148871],{"class":2763},"\u002F\u002F    { key:'status', label:'Status', redacted:false },\n",[2225,148873,148874],{"class":2227,"line":6587},[2225,148875,148876],{"class":2763},"\u002F\u002F    { key:'total', label:'Total (€)', redacted:false }]\n",[2225,148878,148879],{"class":2227,"line":7174},[2225,148880,2285],{"emptyLinePlaceholder":2284},[2225,148882,148883],{"class":2227,"line":7180},[2225,148884,148885],{"class":2763},"\u002F\u002F Consumer C — AI agent context (structured field list, no raw records)\n",[2225,148887,148888,148890,148893,148895,148897,148899,148901,148903,148905,148907,148909,148911,148913,148915,148917],{"class":2227,"line":7220},[2225,148889,2775],{"class":2774},[2225,148891,148892],{"class":2239}," agentContext ",[2225,148894,2781],{"class":2235},[2225,148896,148707],{"class":2239},[2225,148898,1520],{"class":2235},[2225,148900,57285],{"class":2239},[2225,148902,1520],{"class":2235},[2225,148904,8244],{"class":2291},[2225,148906,2789],{"class":2239},[2225,148908,2789],{"class":2235},[2225,148910,67059],{"class":7052},[2225,148912,1381],{"class":2235},[2225,148914,8116],{"class":2774},[2225,148916,953],{"class":2239},[2225,148918,4200],{"class":2235},[2225,148920,148921,148923,148925,148928,148930,148932],{"class":2227,"line":7225},[2225,148922,69281],{"class":2307},[2225,148924,2311],{"class":2235},[2225,148926,148927],{"class":2239},"  f",[2225,148929,1520],{"class":2235},[2225,148931,8364],{"class":2239},[2225,148933,1707],{"class":2235},[2225,148935,148936,148938,148940,148942,148944,148946],{"class":2227,"line":7231},[2225,148937,66284],{"class":2307},[2225,148939,2311],{"class":2235},[2225,148941,67074],{"class":2239},[2225,148943,1520],{"class":2235},[2225,148945,6339],{"class":2239},[2225,148947,1707],{"class":2235},[2225,148949,148950,148952,148955,148957,148959,148961,148963,148965,148967,148969,148971,148974,148976,148978,148980,148982,148984,148986],{"class":2227,"line":7267},[2225,148951,59585],{"class":2235},[2225,148953,148954],{"class":2239},"(f",[2225,148956,1520],{"class":2235},[2225,148958,148823],{"class":2239},[2225,148960,33083],{"class":2235},[2225,148962,2249],{"class":2235},[2225,148964,10695],{"class":2252},[2225,148966,2805],{"class":2235},[2225,148968,40953],{"class":2235},[2225,148970,2236],{"class":2235},[2225,148972,148973],{"class":2307}," omit",[2225,148975,2311],{"class":2235},[2225,148977,2322],{"class":2321},[2225,148979,2243],{"class":2235},[2225,148981,40959],{"class":2235},[2225,148983,8896],{"class":2235},[2225,148985,1381],{"class":2239},[2225,148987,1707],{"class":2235},[2225,148989,148990,148992],{"class":2227,"line":7272},[2225,148991,2359],{"class":2235},[2225,148993,8606],{"class":2239},[2225,148995,148996],{"class":2227,"line":7278},[2225,148997,148998],{"class":2763},"\u002F\u002F → [{ key:'buyerId', type:'string' }, { key:'notes', type:'unknown', omit:true },\n",[2225,149000,149001],{"class":2227,"line":7303},[2225,149002,149003],{"class":2763},"\u002F\u002F    { key:'saleDate', type:'unknown' }, { key:'status', type:'enum' }, { key:'total', type:'number' }]\n",[937,149005,149006,149008],{},[941,149007,25317],{}," is a frozen plain object — pass it freely.",[104892,149010],{},[1022,149012,149014],{"id":149013},"scope-boundary","Scope boundary",[937,149016,149017,7356,149019,2311],{},[941,149018,3926],{},[950,149020,149021],{},"descriptive, not prescriptive",[1111,149023,149024,149027,149034,149037],{},[1114,149025,149026],{},"Layout order → app-side (describe() returns alphabetical by key)",[1114,149028,149029,149030,149033],{},"Locale selection → app-side (staticDict can surface one locale via\n",[941,149031,149032],{},"displayLocale","; the choice belongs to the caller)",[1114,149035,149036],{},"Rendering \u002F formatting → app-side",[1114,149038,149039],{},"Validation \u002F write enforcement → schema \u002F guards, not describe()",[104892,149041],{},[1022,149043,149045],{"id":149044},"key-invariants","Key invariants",[1111,149047,149048,149054,149062,149074,149084],{},[1114,149049,149050,149053],{},[950,149051,149052],{},"Sync = zero store I\u002FO."," The sync overload reads only in-memory config\nassembled at collection registration time. It never calls any store method.",[1114,149055,149056,149059,149060,1520],{},[950,149057,149058],{},"Descriptive not prescriptive."," No layout, styling, or active-locale\nselection in ",[941,149061,146757],{},[1114,149063,149064,149067,149068,149070,149071,149073],{},[950,149065,149066],{},"Merge precedence is fixed",": channel > zod-4 ",[941,149069,146833],{}," > inferred.\nApplication code can rely on ",[941,149072,25333],{}," always winning.",[1114,149075,149076,149079,149080,149083],{},[950,149077,149078],{},"Validator-agnostic."," Works without zod; non-zod validators get structural\ntype inference from config only (sync path) or an empty ",[941,149081,149082],{},"zodFields"," map\n(async path) — no error.",[1114,149085,149086,149095,149096,149098,149099,149102,149103,149167,149170,149173,149174,149176,149177,149179,149180,149182,149183,2085,149186,149188,149189,149191,149192,1169,149194,149196,149197,149199,149200,149202,149203,149206,149207,149209,149212,149213,149215,149216,149219,149220,149223,149224,149226,149227,53922,149229,964,149232,2514,149235,149238,149239,149296,149298,149299,5502,149302,149304,149305,14388,149309,149311,149314,149315,149318,149319,149321,149322,149324],{},[950,149087,149088,149089,149092,149093,1520],{},"fieldMeta key guard — and since ",[941,149090,149091],{},"0.7.0-pre.14"," it usually fires from ",[941,149094,2138],{},"\nAn unknown ",[941,149097,25333],{}," key throws ",[941,149100,149101],{},"FieldMetaUnknownFieldError"," (noy-db#1253).",[7646,149104,149105,149140,149146],{"color":10789,"icon":39006},[937,149106,149107,149112,149113,149117,149118,149126,149127,149129,149130,149133,149134,55646,149137],{},[950,149108,149109,149110,1520],{},"Breaking if you catch around ",[941,149111,3926],{}," For a validator whose fields read synchronously —\na plain Zod object and most real schemas — the error now arrives from ",[950,149114,149115],{},[941,149116,2138],{},".\nThe consequence is the part that bites: ",[950,149119,5983,149120,149122,149123,149125],{},[941,149121,7554],{}," wrapping only the ",[941,149124,3926],{}," call no longer\ncatches it at all",", so the error escapes the handler written for it. A test suite that\nconstructs the collection outside its ",[941,149128,7554],{}," starts ",[1092,149131,149132],{},"erroring"," rather than failing with a clear\nmessage, which reads as ",[1092,149135,149136],{},"\"the release broke our tests\"",[1092,149138,149139],{},"\"the guard moved.\"",[937,149141,149142,149143,149145],{},"Catch around the ",[941,149144,2138],{}," call — or around both, and report which surface rejected,\nwhich turns the relocation into a changed label rather than a break.",[937,149147,149148,149149,149151,149152,149155,149156,968,149158,149160,149161,149163,149164,1520],{},"Measured on the published ",[941,149150,149091],{},": with ",[941,149153,149154],{},"fieldMeta: { amonut: … }"," against a schema\ndeclaring ",[941,149157,6617],{},[941,149159,2138],{}," throws and ",[941,149162,3926],{}," is never reached; the correct\nkey still carries ",[941,149165,149166],{},"sensitivity: 'pii'",[149168,149169],"br",{},[950,149171,149172],{},"The describe-time check is kept, not moved",", because knowability has three tiers rather than\ntwo: a name declared in the config itself needs no schema at all; a validator whose fields read\nsynchronously is checkable at registration; fields that exist only after async derivation are\ncheckable only at ",[941,149175,3926],{},". Hoisting the check outright would have moved it earlier for some\ncollections and removed it for others. The sync path used to skip the\ncheck, which was silent in ",[950,149178,31219],{}," directions on a PDPA surface: the typo'd key produced a\nphantom field carrying the ",[941,149181,147379],{}," marking, and the real field went out undescribed. Measured on\n",[941,149184,149185],{},"0.7.0-pre.11",[941,149187,149154],{}," against a schema declaring ",[941,149190,6617],{}," throws from\n",[941,149193,3926],{},[941,149195,22369],{}," call itself does not — while the correct key carries\n",[941,149198,149166],{}," onto the described field.",[149168,149201],{},"The check still stays ",[950,149204,149205],{},"silent"," when no validator is attached, and that is deliberate: a\ncollection typed only by its TypeScript generic has real fields the hub cannot enumerate, so\nrefusing there would reject correct code.",[149168,149208],{},[950,149210,149211],{},"It also sees through schema wrappers",", on both Zod majors, since ",[941,149214,39039],{},"\n(noy-db#1262 — ",[941,149217,149218],{},"pre.12"," covered refinements only). The rule is ",[1092,149221,149222],{},"follow the output side",": these\nkeys describe the ",[950,149225,45654],{}," record, so a wrapper matters only if it changes the parsed shape.\nMeasured against the published ",[941,149228,149091],{},[950,149230,149231],{},"both majors",[941,149233,149234],{},"zod@3.25.76",[941,149236,149237],{},"zod@4.4.3",", identical results:",[1338,149240,149241,149252],{},[1341,149242,149243],{},[1344,149244,149245,149247],{},[1347,149246,23934],{},[1347,149248,149249,149250,8153],{},"typo'd ",[941,149251,25333],{},[1357,149253,149254,149263,149275,149284],{},[1344,149255,149256,149261],{},[1362,149257,149258],{},[941,149259,149260],{},"z.object({ … })",[1362,149262,122019],{},[1344,149264,149265,149273],{},[1362,149266,149267,1261,149270],{},[941,149268,149269],{},".refine(…)",[941,149271,149272],{},".superRefine(…)",[1362,149274,122019],{},[1344,149276,149277,149282],{},[1362,149278,149279],{},[941,149280,149281],{},"z.preprocess(fn, z.object({ … }))",[1362,149283,122019],{},[1344,149285,149286,149291],{},[1362,149287,149288],{},[941,149289,149290],{},".transform(…)",[1362,149292,149293],{},[950,149294,149295],{},"silent, deliberately",[149168,149297],{},"(Both majors install cleanly from ",[941,149300,149301],{},"0.7.0-pre.15",[941,149303,149091],{}," alone refuses Zod 3 at install\n— see ",[1007,149306,149308],{"href":149307},".\u002Fpersisted-schemas#limits-gotchas","persisted schemas → limits",[149168,149310],{},[941,149312,149313],{},".transform()"," replaces the output, so the inner keys would describe a record that is never\nstored. It and ",[941,149316,149317],{},"preprocess"," are one string apart and mean opposite things — ",[941,149320,149317],{},"\nrewrites the ",[1092,149323,16450],{}," and then parses with the inner schema, so the parsed keys are the inner\nschema's.",[104892,149326],{},[1022,149328,149330,149331,1381],{"id":149329},"public-api-surface-noy-dbhub","Public API surface (",[941,149332,1086],{},[2217,149334,149336],{"className":2219,"code":149335,"language":2221,"meta":1037,"style":1037},"import { createNoydb, money, staticDict, ref } from '@noy-db\u002Fhub'\nimport type { FieldMeta, SemanticType, CollectionDescription, DescribedField, DescribeOptions } from '@noy-db\u002Fhub'\n\n\u002F\u002F collection option\nconst coll = vault.collection('name', {\n  fieldMeta: Record\u003Cstring, FieldMeta>\n})\n\n\u002F\u002F sync\nconst d: CollectionDescription = coll.describe()\n\n\u002F\u002F async (validator types + dynamic dict labels)\nconst d2: CollectionDescription = await coll.describe({ resolveDictLabels: true })\n",[941,149337,149338,149369,149409,149413,149418,149445,149461,149467,149471,149476,149497,149501,149506],{"__ignoreMap":1037},[2225,149339,149340,149342,149344,149346,149348,149350,149352,149355,149357,149359,149361,149363,149365,149367],{"class":2227,"line":2228},[2225,149341,2232],{"class":2231},[2225,149343,2236],{"class":2235},[2225,149345,2744],{"class":2239},[2225,149347,2298],{"class":2235},[2225,149349,123521],{"class":2239},[2225,149351,2298],{"class":2235},[2225,149353,149354],{"class":2239}," staticDict",[2225,149356,2298],{"class":2235},[2225,149358,62303],{"class":2239},[2225,149360,2243],{"class":2235},[2225,149362,2246],{"class":2231},[2225,149364,2249],{"class":2235},[2225,149366,1086],{"class":2252},[2225,149368,2256],{"class":2235},[2225,149370,149371,149373,149375,149377,149379,149381,149384,149386,149389,149391,149394,149396,149399,149401,149403,149405,149407],{"class":2227,"line":1041},[2225,149372,2232],{"class":2231},[2225,149374,3399],{"class":2231},[2225,149376,2236],{"class":2235},[2225,149378,25440],{"class":2239},[2225,149380,2298],{"class":2235},[2225,149382,149383],{"class":2239}," SemanticType",[2225,149385,2298],{"class":2235},[2225,149387,149388],{"class":2239}," CollectionDescription",[2225,149390,2298],{"class":2235},[2225,149392,149393],{"class":2239}," DescribedField",[2225,149395,2298],{"class":2235},[2225,149397,149398],{"class":2239}," DescribeOptions",[2225,149400,2243],{"class":2235},[2225,149402,2246],{"class":2231},[2225,149404,2249],{"class":2235},[2225,149406,1086],{"class":2252},[2225,149408,2256],{"class":2235},[2225,149410,149411],{"class":2227,"line":1038},[2225,149412,2285],{"emptyLinePlaceholder":2284},[2225,149414,149415],{"class":2227,"line":2288},[2225,149416,149417],{"class":2763},"\u002F\u002F collection option\n",[2225,149419,149420,149422,149425,149427,149429,149431,149433,149435,149437,149439,149441,149443],{"class":2227,"line":2304},[2225,149421,2775],{"class":2774},[2225,149423,149424],{"class":2239}," coll ",[2225,149426,2781],{"class":2235},[2225,149428,6466],{"class":2239},[2225,149430,1520],{"class":2235},[2225,149432,6471],{"class":2291},[2225,149434,2789],{"class":2239},[2225,149436,2805],{"class":2235},[2225,149438,4914],{"class":2252},[2225,149440,2805],{"class":2235},[2225,149442,2298],{"class":2235},[2225,149444,2301],{"class":2235},[2225,149446,149447,149449,149451,149453,149455,149457,149459],{"class":2227,"line":2356},[2225,149448,147288],{"class":2307},[2225,149450,2311],{"class":2235},[2225,149452,22527],{"class":2239},[2225,149454,6474],{"class":2235},[2225,149456,7259],{"class":2239},[2225,149458,2298],{"class":2235},[2225,149460,22536],{"class":2239},[2225,149462,149463,149465],{"class":2227,"line":2830},[2225,149464,2359],{"class":2235},[2225,149466,2362],{"class":2239},[2225,149468,149469],{"class":2227,"line":2861},[2225,149470,2285],{"emptyLinePlaceholder":2284},[2225,149472,149473],{"class":2227,"line":4246},[2225,149474,149475],{"class":2763},"\u002F\u002F sync\n",[2225,149477,149478,149480,149482,149484,149486,149488,149491,149493,149495],{"class":2227,"line":4253},[2225,149479,2775],{"class":2774},[2225,149481,148707],{"class":2239},[2225,149483,2311],{"class":2235},[2225,149485,149388],{"class":6342},[2225,149487,6346],{"class":2235},[2225,149489,149490],{"class":2239}," coll",[2225,149492,1520],{"class":2235},[2225,149494,6153],{"class":2291},[2225,149496,8146],{"class":2239},[2225,149498,149499],{"class":2227,"line":4259},[2225,149500,2285],{"emptyLinePlaceholder":2284},[2225,149502,149503],{"class":2227,"line":6548},[2225,149504,149505],{"class":2763},"\u002F\u002F async (validator types + dynamic dict labels)\n",[2225,149507,149508,149510,149513,149515,149517,149519,149521,149523,149525,149527,149529,149531,149534,149536,149538,149540],{"class":2227,"line":6576},[2225,149509,2775],{"class":2774},[2225,149511,149512],{"class":2239}," d2",[2225,149514,2311],{"class":2235},[2225,149516,149388],{"class":6342},[2225,149518,6346],{"class":2235},[2225,149520,2784],{"class":2231},[2225,149522,149490],{"class":2239},[2225,149524,1520],{"class":2235},[2225,149526,6153],{"class":2291},[2225,149528,2789],{"class":2239},[2225,149530,2792],{"class":2235},[2225,149532,149533],{"class":2307}," resolveDictLabels",[2225,149535,2311],{"class":2235},[2225,149537,2322],{"class":2321},[2225,149539,2243],{"class":2235},[2225,149541,2362],{"class":2239},[937,149543,149544,149545,149547,149548,149550],{},"A consumer that only needs the output ",[1092,149546,128135],{}," imports them from ",[941,149549,1086],{}," — they are\ntype-only, so they cost nothing at runtime:",[2217,149552,149554],{"className":2219,"code":149553,"language":2221,"meta":1037,"style":1037},"import type {\n  CollectionDescription,\n  DescribedField,\n  DescribeOptions,\n  CollectionMeta,\n  FieldMeta,\n  SemanticType,\n} from '@noy-db\u002Fhub'\n",[941,149555,149556,149564,149571,149578,149585,149592,149599,149606],{"__ignoreMap":1037},[2225,149557,149558,149560,149562],{"class":2227,"line":2228},[2225,149559,2232],{"class":2231},[2225,149561,3399],{"class":2231},[2225,149563,2301],{"class":2235},[2225,149565,149566,149569],{"class":2227,"line":1041},[2225,149567,149568],{"class":2239},"  CollectionDescription",[2225,149570,1707],{"class":2235},[2225,149572,149573,149576],{"class":2227,"line":1038},[2225,149574,149575],{"class":2239},"  DescribedField",[2225,149577,1707],{"class":2235},[2225,149579,149580,149583],{"class":2227,"line":2288},[2225,149581,149582],{"class":2239},"  DescribeOptions",[2225,149584,1707],{"class":2235},[2225,149586,149587,149590],{"class":2227,"line":2304},[2225,149588,149589],{"class":2239},"  CollectionMeta",[2225,149591,1707],{"class":2235},[2225,149593,149594,149597],{"class":2227,"line":2356},[2225,149595,149596],{"class":2239},"  FieldMeta",[2225,149598,1707],{"class":2235},[2225,149600,149601,149604],{"class":2227,"line":2830},[2225,149602,149603],{"class":2239},"  SemanticType",[2225,149605,1707],{"class":2235},[2225,149607,149608,149610,149612,149614,149616],{"class":2227,"line":2861},[2225,149609,2359],{"class":2235},[2225,149611,2246],{"class":2231},[2225,149613,2249],{"class":2235},[2225,149615,1086],{"class":2252},[2225,149617,2256],{"class":2235},[937,149619,149620,149621,997,149623,149625,149626,149629,149630,27678,149633,1520],{},"This is the same surface ",[941,149622,3936],{},[941,149624,56225],{}," bind to. They once came from a\nnarrow ",[941,149627,149628],{},"@noy-db\u002Fhub\u002Fui"," seam, but that subpath had no importers and was pruned in\n",[1007,149631,3372],{"href":3369,"rel":149632},[3371],[1007,149634,29],{"href":30},[104892,149636],{},[1022,149638,149640,149642,149643,968,149645,968,149647],{"id":149639},"describe-enhancements-i18n-widget-editable",[941,149641,3926],{}," enhancements — ",[941,149644,701],{},[941,149646,147604],{},[941,149648,148040],{},[937,149650,149651,149652,2311],{},"The metadata ladder build added three fields to ",[941,149653,146757],{},[1591,149655,149657],{"id":149656},"i18n-locales-readonly-string-densify-boolean",[941,149658,149659],{},"i18n?: { locales?: readonly string[]; densify?: boolean }",[937,149661,149662,149663,149665,149666,149669],{},"Present on fields declared with ",[941,149664,58358],{},". Exposes the configured locale set\nand the ",[941,149667,149668],{},"densifyOnWrite"," flag so consumers know the field is multi-locale.",[1591,149671,149673],{"id":149672},"widget-string",[941,149674,149675],{},"widget?: string",[937,149677,149678,149679,6030,149681,149683,149684,2311],{},"Derived from ",[941,149680,146763],{},[941,149682,6339],{},", overridable via ",[941,149685,149686],{},"fieldMeta.widget",[1338,149688,149689,149701],{},[1341,149690,149691],{},[1344,149692,149693,149696],{},[1347,149694,149695],{},"Condition",[1347,149697,149698,149699],{},"Derived ",[941,149700,147604],{},[1357,149702,149703,149717,149730,149743,149755,149767,149781,149792,149804,149815],{},[1344,149704,149705,149713],{},[1362,149706,149707,4688,149710],{},[941,149708,149709],{},"semanticType: 'date'",[941,149711,149712],{},"'datetime'",[1362,149714,149715],{},[941,149716,147505],{},[1344,149718,149719,149725],{},[1362,149720,149721,149724],{},[941,149722,149723],{},"semanticType: 'currency'"," (money)",[1362,149726,149727],{},[941,149728,149729],{},"'money'",[1344,149731,149732,149738],{},[1362,149733,149734,149737],{},[941,149735,149736],{},"semanticType: 'entity'"," (ref)",[1362,149739,149740],{},[941,149741,149742],{},"'ref-select'",[1344,149744,149745,149750],{},[1362,149746,149747,149749],{},[941,149748,147773],{}," block present",[1362,149751,149752],{},[941,149753,149754],{},"'select'",[1344,149756,149757,149762],{},[1362,149758,149759],{},[941,149760,149761],{},"type: 'boolean'",[1362,149763,149764],{},[941,149765,149766],{},"'checkbox'",[1344,149768,149769,149777],{},[1362,149770,149771,149774,149775,1381],{},[941,149772,149773],{},"type: 'number'"," (no matching ",[941,149776,146763],{},[1362,149778,149779],{},[941,149780,146843],{},[1344,149782,149783,149788],{},[1362,149784,149785],{},[941,149786,149787],{},"semanticType: 'percent'",[1362,149789,149790],{},[941,149791,146843],{},[1344,149793,149794,149799],{},[1362,149795,149796],{},[941,149797,149798],{},"semanticType: 'url'",[1362,149800,149801],{},[941,149802,149803],{},"'url'",[1344,149805,149806,149811],{},[1362,149807,149808],{},[941,149809,149810],{},"semanticType: 'email'",[1362,149812,149813],{},[941,149814,147511],{},[1344,149816,149817,149819],{},[1362,149818,46145],{},[1362,149820,149821],{},[941,149822,149823],{},"'text'",[937,149825,149826,149827,149829,149830,149832,149833,149835,149836,149838,149839,149842,149843,52561,149845,149847,149848,149850,149851,11060,149853,1520],{},"Priority: ",[941,149828,149686],{}," override > ",[941,149831,146763],{}," > ",[941,149834,147773],{}," presence >\n",[941,149837,6339],{},". A plain ",[941,149840,149841],{},"z.number()"," field with no ",[941,149844,146763],{},[941,149846,146833],{},", and\nno money\u002Fdict config resolves to ",[941,149849,149773],{}," and therefore widget\n",[941,149852,146843],{},[941,149854,149823],{},[937,149856,149857,149858,1520],{},"Override: ",[941,149859,149860],{},"fieldMeta: { amount: { label: 'Amount', widget: 'currency-input' } }",[1591,149862,149864],{"id":149863},"editable-boolean",[941,149865,149866],{},"editable: boolean",[937,149868,149869,149871,149872,149874,149875,149877],{},[941,149870,2560],{}," for computed fields, the ",[941,149873,6983],{}," field, and provenance-stamped fields;\n",[941,149876,11790],{}," otherwise. Data editors use this to render read-only cells without\nknowing the collection's internals.",[104892,149879],{},[1022,149881,149883,149886,149887,149889],{"id":149882},"dumpschema-collection-level-config-block",[941,149884,149885],{},"dumpSchema()"," collection-level ",[941,149888,66864],{}," block",[937,149891,149892,149895,149896,149898,149899,149901],{},[941,149893,149894],{},"CollectionDescriptor.config"," surfaces the ",[950,149897,727],{}," collection's configuration\noptions for structural-audit tooling. It is populated by ",[941,149900,149885],{}," when the\ncollection is live-declared; omitted for bundle-reconstructed collections where\noptions aren't available.",[2217,149903,149905],{"className":2219,"code":149904,"language":2221,"meta":1037,"style":1037},"config?: {\n  textIndexes?: readonly string[]\n  textIndexPersist?: boolean\n  embeddings?: { source: string | readonly string[]; dim: number; model?: string }\n  i18nFields?: readonly string[]\n  crdt?: string\n  provenance?: boolean\n  archive?: boolean          \u002F\u002F presence flag (the predicate is code)\n  tiers?: readonly number[]\n  tierMode?: string\n  perRecordKeys?: boolean\n  history?: boolean          \u002F\u002F presence flag\n  schemaUpdate?: readonly string[]  \u002F\u002F strategy names\n}\n",[941,149906,149907,149915,149924,149933,149958,149966,149975,149984,149996,150005,150014,150022,150034,150047],{"__ignoreMap":1037},[2225,149908,149909,149911,149913],{"class":2227,"line":2228},[2225,149910,66864],{"class":2239},[2225,149912,7011],{"class":2235},[2225,149914,2301],{"class":2235},[2225,149916,149917,149920,149922],{"class":2227,"line":1041},[2225,149918,149919],{"class":2239},"  textIndexes?",[2225,149921,2311],{"class":2235},[2225,149923,22490],{"class":2239},[2225,149925,149926,149929,149931],{"class":2227,"line":1038},[2225,149927,149928],{"class":2239},"  textIndexPersist",[2225,149930,7011],{"class":2235},[2225,149932,11718],{"class":2239},[2225,149934,149935,149937,149939,149941,149943,149945,149947,149949,149952,149954,149956],{"class":2227,"line":2288},[2225,149936,95010],{"class":2239},[2225,149938,7011],{"class":2235},[2225,149940,2236],{"class":2235},[2225,149942,95077],{"class":2307},[2225,149944,2311],{"class":2235},[2225,149946,19923],{"class":2239},[2225,149948,18307],{"class":2235},[2225,149950,149951],{"class":2239}," readonly string[]; dim: number; model",[2225,149953,7011],{"class":2235},[2225,149955,19923],{"class":2239},[2225,149957,7351],{"class":2235},[2225,149959,149960,149962,149964],{"class":2227,"line":2304},[2225,149961,59305],{"class":2239},[2225,149963,7011],{"class":2235},[2225,149965,22490],{"class":2239},[2225,149967,149968,149971,149973],{"class":2227,"line":2356},[2225,149969,149970],{"class":2239},"  crdt",[2225,149972,7011],{"class":2235},[2225,149974,7014],{"class":2239},[2225,149976,149977,149980,149982],{"class":2227,"line":2830},[2225,149978,149979],{"class":2239},"  provenance",[2225,149981,7011],{"class":2235},[2225,149983,11718],{"class":2239},[2225,149985,149986,149988,149990,149993],{"class":2227,"line":2861},[2225,149987,100198],{"class":2239},[2225,149989,7011],{"class":2235},[2225,149991,149992],{"class":2239}," boolean          ",[2225,149994,149995],{"class":2763},"\u002F\u002F presence flag (the predicate is code)\n",[2225,149997,149998,150000,150002],{"class":2227,"line":4246},[2225,149999,75748],{"class":2239},[2225,150001,7011],{"class":2235},[2225,150003,150004],{"class":2239}," readonly number[]\n",[2225,150006,150007,150010,150012],{"class":2227,"line":4253},[2225,150008,150009],{"class":2239},"  tierMode",[2225,150011,7011],{"class":2235},[2225,150013,7014],{"class":2239},[2225,150015,150016,150018,150020],{"class":2227,"line":4259},[2225,150017,75763],{"class":2239},[2225,150019,7011],{"class":2235},[2225,150021,11718],{"class":2239},[2225,150023,150024,150027,150029,150031],{"class":2227,"line":6548},[2225,150025,150026],{"class":2239},"  history",[2225,150028,7011],{"class":2235},[2225,150030,149992],{"class":2239},[2225,150032,150033],{"class":2763},"\u002F\u002F presence flag\n",[2225,150035,150036,150039,150041,150044],{"class":2227,"line":6576},[2225,150037,150038],{"class":2239},"  schemaUpdate",[2225,150040,7011],{"class":2235},[2225,150042,150043],{"class":2239}," readonly string[]  ",[2225,150045,150046],{"class":2763},"\u002F\u002F strategy names\n",[2225,150048,150049],{"class":2227,"line":6582},[2225,150050,7351],{"class":2235},[937,150052,150053,1324,150056,150058],{},[950,150054,150055],{},"Function-valued options surface as booleans (presence).",[941,150057,91981],{}," is\nconsumed at construction and has no retained state to surface.",[104892,150060],{},[1022,150062,150064],{"id":150063},"devtools-schema-view","Devtools schema view",[1591,150066,150068,1324,150070],{"id":150067},"in-devtools-snapshot",[941,150069,36706],{},[941,150071,150072],{},"snapshot()",[937,150074,150075,150078],{},[941,150076,150077],{},"InspectorCollection"," carries all three rung outputs:",[1111,150080,150081,150087,150093],{},[1114,150082,150083,150086],{},[941,150084,150085],{},"meta?: CollectionMeta"," — collection-level label\u002Fdescription",[1114,150088,150089,150092],{},[941,150090,150091],{},"described?: readonly DescribedField[]"," — rich field list (label\u002Ftype\u002Fwidget\u002Fsensitivity\u002Fi18n\u002Feditable)",[1114,150094,150095,150098],{},[941,150096,150097],{},"config?: CollectionConfig"," — structural config strip",[937,150100,150101,150104,150105,150108],{},[941,150102,150103],{},"InspectorSnapshot"," gains ",[941,150106,150107],{},"meta?: VaultMeta"," for the vault-level label.",[1591,150110,150112,150113,1381],{"id":150111},"terminal-ui-noy-dbin-devtools-tui","Terminal UI (",[941,150114,36195],{},[937,150116,150117],{},"The structure view renders the metadata ladder compactly:",[937,150119,150120,150123,150124,150127],{},[950,150121,150122],{},"VaultList"," — shows ",[941,150125,150126],{},"meta.label (vaultId)"," for the active vault when a label\nis declared; falls back to the vault id.",[937,150129,150130,150123,150133,150136],{},[950,150131,150132],{},"CollectionList",[941,150134,150135],{},"meta.label (name)"," for each collection when a label\nis declared; falls back to the collection name.",[937,150138,150139,150142],{},[950,150140,150141],{},"DetailPane"," (drilled schema tab):",[1111,150144,150145,150153,150163,150173],{},[1114,150146,150147,150148,150150,150151],{},"Heading: ",[941,150149,150135],{}," or just ",[941,150152,4914],{},[1114,150154,150155,150156,150159,150160],{},"If ",[941,150157,150158],{},"described"," is present: one line per field — ",[941,150161,150162],{},"Label  (key: type)  [pii]  [i18n]  [ro]  \u003Cwidget>",[1114,150164,150155,150165,150167,150168,17904,150171],{},[941,150166,150158],{}," is absent (no live describe): raw ",[941,150169,150170],{},"key: type",[941,150172,57285],{},[1114,150174,150175,150176],{},"Config strip (dimmed): ",[941,150177,150178],{},"config: idx:2  emb:1536d  i18n:3  crdt:lww  provenance  archive",[937,150180,150181],{},"Markers used:",[1338,150183,150184,150193],{},[1341,150185,150186],{},[1344,150187,150188,150191],{},[1347,150189,150190],{},"Marker",[1347,150192,20200],{},[1357,150194,150195,150206,150218,150229,150240],{},[1344,150196,150197,150202],{},[1362,150198,150199],{},[941,150200,150201],{},"[pii]",[1362,150203,150204],{},[941,150205,149166],{},[1344,150207,150208,150213],{},[1362,150209,150210],{},[941,150211,150212],{},"[secret]",[1362,150214,150215],{},[941,150216,150217],{},"sensitivity: 'secret'",[1344,150219,150220,150225],{},[1362,150221,150222],{},[941,150223,150224],{},"[i18n]",[1362,150226,150227,149749],{},[941,150228,701],{},[1344,150230,150231,150236],{},[1362,150232,150233],{},[941,150234,150235],{},"[ro]",[1362,150237,150238],{},[941,150239,124749],{},[1344,150241,150242,150247],{},[1362,150243,150244],{},[941,150245,150246],{},"\u003Cwidget>",[1362,150248,150249,150250,4688,150253],{},"widget hint, e.g. ",[941,150251,150252],{},"\u003Cmoney>",[941,150254,150255],{},"\u003Cref-select>",[104892,150257],{},[1022,150259,2621],{"id":2620},[1111,150261,150262,150272,150282,150294,150308,150313,150318,150323],{},[1114,150263,150264,150266,150267,150269,150270],{},[1007,150265,633],{"href":634}," — the serialized ",[941,150268,633],{}," projection contract that surfaces alongside ",[941,150271,146757],{},[1114,150273,150274,150276,150277,150279,150280],{},[1007,150275,637],{"href":638}," — computed fields, which ",[941,150278,3926],{}," marks ",[941,150281,124749],{},[1114,150283,150284,964,150286,150288,150289,150291,150292,149889],{},[1007,150285,653],{"href":654},[941,150287,58358],{}," fields, which ",[941,150290,3926],{}," surfaces via the ",[941,150293,701],{},[1114,150295,150296,150298,150299,968,150301,150303,150304,150307],{},[1007,150297,4449],{"href":805}," — the query-DSL reducers (",[941,150300,28196],{},[941,150302,3236],{},", …) that ",[941,150305,150306],{},"DescribedField.aggregate"," names as a display hint",[1114,150309,150310,150312],{},[1007,150311,743],{"href":744}," — collection + vault-level meta rungs above field meta",[1114,150314,150315,150317],{},[1007,150316,747],{"href":748}," — toJSONSchema(), dictKey inline labels, devtools PII masking",[1114,150319,150320,150322],{},[1007,150321,124874],{"href":48}," — what a collection is and how it's opened",[1114,150324,101581,150325,1520],{},[1007,150326,150328],{"href":94282,"rel":150327},[3371],[941,150329,150330],{},"126-describe-field-metadata",[2645,150332,150333],{},"html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html pre.shiki code .sHdIc, html code.shiki .sHdIc{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#EEFFFF;--shiki-default-font-style:italic;--shiki-dark:#BABED8;--shiki-dark-font-style:italic}html pre.shiki code .sfNiH, html code.shiki .sfNiH{--shiki-light:#FF5370;--shiki-default:#FF9CAC;--shiki-dark:#FF9CAC}",{"title":1037,"searchDepth":1038,"depth":1038,"links":150335},[150336,150337,150338,150340,150341,150342,150343,150344,150346,150352,150354,150360],{"id":6241,"depth":1041,"text":6242},{"id":146783,"depth":1041,"text":146784},{"id":146905,"depth":1041,"text":150339},"The fieldMeta channel",{"id":147659,"depth":1041,"text":147660},{"id":147782,"depth":1041,"text":147783},{"id":149013,"depth":1041,"text":149014},{"id":149044,"depth":1041,"text":149045},{"id":149329,"depth":1041,"text":150345},"Public API surface (@noy-db\u002Fhub)",{"id":149639,"depth":1041,"text":150347,"children":150348},"describe() enhancements — i18n, widget, editable",[150349,150350,150351],{"id":149656,"depth":1038,"text":149659},{"id":149672,"depth":1038,"text":149675},{"id":149863,"depth":1038,"text":149866},{"id":149882,"depth":1041,"text":150353},"dumpSchema() collection-level config block",{"id":150063,"depth":1041,"text":150064,"children":150355},[150356,150358],{"id":150067,"depth":1038,"text":150357},"in-devtools snapshot()",{"id":150111,"depth":1038,"text":150359},"Terminal UI (@noy-db\u002Fin-devtools-tui)",{"id":2620,"depth":1041,"text":2621},"How collection.describe() merges fieldMeta, collection\u002Fvault meta, and inferred config into one normalized contract for UI, export, and AI consumers.",{},{"title":677},{"title":146720,"description":150361},"collection.describe() + fieldMeta + metadata ladder","Yy_MvnoqPsAGN2TDkR50Ksna4BSdz8sjOLrwN3WRitI",{"id":150368,"title":681,"access":12,"apiToc":929,"body":150369,"category":1042,"description":151690,"extension":1044,"meta":151691,"navigation":1042,"nest":2635,"path":682,"seo":151692,"sinceVersion":1042,"status":1049,"stem":683,"subtitle":1042,"tier":6922,"tocExternal":1042,"__hash__":151693},"docs\u002Fservices\u002Fforeign-refs.md",{"type":931,"value":150370,"toc":151679},[150371,150374,150377,150379,150397,150405,150409,150419,150614,150632,150717,150735,150738,150759,150765,150775,150947,150956,150990,150998,151002,151007,151036,151183,151187,151205,151500,151574,151609,151611,151638,151640,151676],[934,150372,681],{"id":150373},"foreign-refs",[46871,150375],{":rows":150376},"[{\"label\":\"Subpath\",\"value\":\"`@noy-db\u002Fhub` (root export — `ref` \u002F `refArray` \u002F `vault.link`)\"},{\"label\":\"Factory\",\"value\":\"— always-on kernel; no `with*()` to opt into\"},{\"label\":\"Layer\",\"value\":\"`with-lookup`\"},{\"label\":\"Kind\",\"value\":\"reference — the always-on relational primitives\"},{\"label\":\"Source\",\"value\":\"`kernel\u002Frefs.ts` (managed link sets: `with-shape\u002Flinks\u002F`)\"}]",[1022,150378,6242],{"id":6241},[937,150380,116776,150381,150383,150384,150387,150388,150391,150392,1069,150394,1520],{},[1007,150382,2698],{"href":58262}," often point at each other. ",[950,150385,150386],{},"Refs"," are foreign-key style references between collections, declared as metadata. They power delete-time integrity enforcement (strict \u002F warn \u002F cascade), the ",[941,150389,150390],{},"vault.checkIntegrity()"," audit, and ",[941,150393,61808],{},[1007,150395,150396],{"href":83069},"query DSL",[937,150398,150399,150400,150402,150403,1520],{},"For validating the ",[1092,150401,76731],{}," of each record, see ",[1007,150404,111],{"href":124776},[1022,150406,150408],{"id":150407},"declaring-refs","Declaring refs",[937,150410,150411,150412,150414,150415,150418],{},"A collection declares its references in the ",[941,150413,22753],{}," option. Each key is a top-level field on the record; each value is a ",[941,150416,150417],{},"ref(target, mode)"," descriptor naming the target collection:",[2217,150420,150422],{"className":2219,"code":150421,"language":2221,"meta":1037,"style":1037},"import { ref } from '@noy-db\u002Fhub'\n\nconst clients  = vault.collection\u003CClient>('clients')\nconst invoices = vault.collection\u003CInvoice>('invoices', {\n  refs: {\n    clientId:   ref('clients'),            \u002F\u002F default mode: 'strict'\n    categoryId: ref('categories', 'warn'),\n    parentId:   ref('invoices', 'cascade'), \u002F\u002F self-reference is allowed\n  },\n})\n",[941,150423,150424,150442,150446,150477,150509,150517,150541,150572,150604,150608],{"__ignoreMap":1037},[2225,150425,150426,150428,150430,150432,150434,150436,150438,150440],{"class":2227,"line":2228},[2225,150427,2232],{"class":2231},[2225,150429,2236],{"class":2235},[2225,150431,62303],{"class":2239},[2225,150433,2243],{"class":2235},[2225,150435,2246],{"class":2231},[2225,150437,2249],{"class":2235},[2225,150439,1086],{"class":2252},[2225,150441,2256],{"class":2235},[2225,150443,150444],{"class":2227,"line":1041},[2225,150445,2285],{"emptyLinePlaceholder":2284},[2225,150447,150448,150450,150453,150455,150457,150459,150461,150463,150465,150467,150469,150471,150473,150475],{"class":2227,"line":1038},[2225,150449,2775],{"class":2774},[2225,150451,150452],{"class":2239}," clients  ",[2225,150454,2781],{"class":2235},[2225,150456,6466],{"class":2239},[2225,150458,1520],{"class":2235},[2225,150460,6471],{"class":2291},[2225,150462,6474],{"class":2235},[2225,150464,6673],{"class":6342},[2225,150466,6480],{"class":2235},[2225,150468,2789],{"class":2239},[2225,150470,2805],{"class":2235},[2225,150472,23926],{"class":2252},[2225,150474,2805],{"class":2235},[2225,150476,2362],{"class":2239},[2225,150478,150479,150481,150483,150485,150487,150489,150491,150493,150495,150497,150499,150501,150503,150505,150507],{"class":2227,"line":2288},[2225,150480,2775],{"class":2774},[2225,150482,6461],{"class":2239},[2225,150484,2781],{"class":2235},[2225,150486,6466],{"class":2239},[2225,150488,1520],{"class":2235},[2225,150490,6471],{"class":2291},[2225,150492,6474],{"class":2235},[2225,150494,6477],{"class":6342},[2225,150496,6480],{"class":2235},[2225,150498,2789],{"class":2239},[2225,150500,2805],{"class":2235},[2225,150502,6487],{"class":2252},[2225,150504,2805],{"class":2235},[2225,150506,2298],{"class":2235},[2225,150508,2301],{"class":2235},[2225,150510,150511,150513,150515],{"class":2227,"line":2304},[2225,150512,62642],{"class":2307},[2225,150514,2311],{"class":2235},[2225,150516,2301],{"class":2235},[2225,150518,150519,150521,150523,150526,150528,150530,150532,150534,150536,150538],{"class":2227,"line":2356},[2225,150520,60573],{"class":2307},[2225,150522,2311],{"class":2235},[2225,150524,150525],{"class":2291},"   ref",[2225,150527,2789],{"class":2239},[2225,150529,2805],{"class":2235},[2225,150531,23926],{"class":2252},[2225,150533,2805],{"class":2235},[2225,150535,1381],{"class":2239},[2225,150537,2298],{"class":2235},[2225,150539,150540],{"class":2763},"            \u002F\u002F default mode: 'strict'\n",[2225,150542,150543,150546,150548,150550,150552,150554,150557,150559,150561,150563,150566,150568,150570],{"class":2227,"line":2830},[2225,150544,150545],{"class":2307},"    categoryId",[2225,150547,2311],{"class":2235},[2225,150549,62303],{"class":2291},[2225,150551,2789],{"class":2239},[2225,150553,2805],{"class":2235},[2225,150555,150556],{"class":2252},"categories",[2225,150558,2805],{"class":2235},[2225,150560,2298],{"class":2235},[2225,150562,2249],{"class":2235},[2225,150564,150565],{"class":2252},"warn",[2225,150567,2805],{"class":2235},[2225,150569,1381],{"class":2239},[2225,150571,1707],{"class":2235},[2225,150573,150574,150577,150579,150581,150583,150585,150587,150589,150591,150593,150595,150597,150599,150601],{"class":2227,"line":2861},[2225,150575,150576],{"class":2307},"    parentId",[2225,150578,2311],{"class":2235},[2225,150580,150525],{"class":2291},[2225,150582,2789],{"class":2239},[2225,150584,2805],{"class":2235},[2225,150586,6487],{"class":2252},[2225,150588,2805],{"class":2235},[2225,150590,2298],{"class":2235},[2225,150592,2249],{"class":2235},[2225,150594,10753],{"class":2252},[2225,150596,2805],{"class":2235},[2225,150598,1381],{"class":2239},[2225,150600,2298],{"class":2235},[2225,150602,150603],{"class":2763}," \u002F\u002F self-reference is allowed\n",[2225,150605,150606],{"class":2227,"line":4246},[2225,150607,19017],{"class":2235},[2225,150609,150610,150612],{"class":2227,"line":4253},[2225,150611,2359],{"class":2235},[2225,150613,2362],{"class":2239},[937,150615,150616,150617,81338,150620,7356,150623,150626,150627,1261,150629,150631],{},"The signature is ",[941,150618,150619],{},"ref(target: string, mode: RefMode = 'strict')",[941,150621,150622],{},"RefMode",[941,150624,150625],{},"'strict' | 'warn' | 'cascade'",". A ",[941,150628,6651],{},[941,150630,18316],{}," field value means \"no reference\" and is always allowed, in every mode.",[1338,150633,150634,150655],{},[1341,150635,150636],{},[1344,150637,150638,150641,150646,150649],{},[1347,150639,150640],{},"Mode",[1347,150642,117658,150643,150645],{},[941,150644,20953],{}," with missing target",[1347,150647,150648],{},"On delete of the target",[1347,150650,150651,150652,150654],{},"On dangling ",[941,150653,61808],{}," read",[1357,150656,150657,150679,150699],{},[1344,150658,150659,150664,150669,150674],{},[1362,150660,150661,91937],{},[941,150662,150663],{},"'strict'",[1362,150665,16810,150666],{},[941,150667,150668],{},"RefIntegrityError",[1362,150670,16810,150671,150673],{},[941,150672,150668],{}," while referencing records exist",[1362,150675,16810,150676],{},[941,150677,150678],{},"DanglingReferenceError",[1344,150680,150681,150685,150688,150693],{},[1362,150682,150683],{},[941,150684,51655],{},[1362,150686,150687],{},"Succeeds",[1362,150689,150690,150691],{},"Succeeds — orphans reported by ",[941,150692,150390],{},[1362,150694,150695,150696,150698],{},"Joined value is ",[941,150697,6651],{},"; one-shot console warning",[1344,150700,150701,150706,150709,150712],{},[1362,150702,150703],{},[941,150704,150705],{},"'cascade'",[1362,150707,150708],{},"Succeeds (unchecked, like warn)",[1362,150710,150711],{},"Deletes every referencing record (cycle-safe — mutual cascades terminate)",[1362,150713,150695,150714,150716],{},[941,150715,6651],{},", silently",[937,150718,150719,150721,150722,968,150724,968,150726,968,150728,968,150731,150734],{},[941,150720,150668],{}," carries structured fields — ",[941,150723,6471],{},[941,150725,6983],{},[941,150727,25197],{},[941,150729,150730],{},"refTo",[941,150732,150733],{},"refId"," — so a UI can render \"client X cannot be deleted because invoices 1, 2 and 3 reference it\" instead of a bare string.",[937,150736,150737],{},"Refs are used by:",[1111,150739,150740,150747,150752],{},[1114,150741,150742,150744,150745],{},[941,150743,27677],{}," — resolve the reference into an attached record; see ",[1007,150746,715],{"href":132487},[1114,150748,150749,150751],{},[941,150750,150390],{}," — full ref-graph audit (below)",[1114,150753,150754,150755,150758],{},"delete-time enforcement — every ",[941,150756,150757],{},"Collection.delete()"," checks inbound refs before removing the record",[1591,150760,150762,150763,1381],{"id":150761},"array-refs-refarray","Array refs (",[941,150764,147755],{},[937,150766,150767,150770,150771,150774],{},[941,150768,150769],{},"refArray(target, mode)"," gives a field holding an ",[950,150772,150773],{},"array of ids"," the same integrity semantics, applied per element — a lightweight many-to-many (order↔product, post↔tag):",[2217,150776,150778],{"className":2219,"code":150777,"language":2221,"meta":1037,"style":1037},"import { refArray } from '@noy-db\u002Fhub'\n\nconst orders = vault.collection\u003COrder>('orders', {\n  refs: { productIds: refArray('products', 'warn') },\n})\n\nawait orders.put('o1', { id: 'o1', productIds: ['p1', 'p2'] })\n",[941,150779,150780,150799,150803,150837,150873,150879,150883],{"__ignoreMap":1037},[2225,150781,150782,150784,150786,150789,150791,150793,150795,150797],{"class":2227,"line":2228},[2225,150783,2232],{"class":2231},[2225,150785,2236],{"class":2235},[2225,150787,150788],{"class":2239}," refArray",[2225,150790,2243],{"class":2235},[2225,150792,2246],{"class":2231},[2225,150794,2249],{"class":2235},[2225,150796,1086],{"class":2252},[2225,150798,2256],{"class":2235},[2225,150800,150801],{"class":2227,"line":1041},[2225,150802,2285],{"emptyLinePlaceholder":2284},[2225,150804,150805,150807,150810,150812,150814,150816,150818,150820,150822,150824,150826,150828,150831,150833,150835],{"class":2227,"line":1038},[2225,150806,2775],{"class":2774},[2225,150808,150809],{"class":2239}," orders ",[2225,150811,2781],{"class":2235},[2225,150813,6466],{"class":2239},[2225,150815,1520],{"class":2235},[2225,150817,6471],{"class":2291},[2225,150819,6474],{"class":2235},[2225,150821,124065],{"class":6342},[2225,150823,6480],{"class":2235},[2225,150825,2789],{"class":2239},[2225,150827,2805],{"class":2235},[2225,150829,150830],{"class":2252},"orders",[2225,150832,2805],{"class":2235},[2225,150834,2298],{"class":2235},[2225,150836,2301],{"class":2235},[2225,150838,150839,150841,150843,150845,150848,150850,150852,150854,150856,150859,150861,150863,150865,150867,150869,150871],{"class":2227,"line":2288},[2225,150840,62642],{"class":2307},[2225,150842,2311],{"class":2235},[2225,150844,2236],{"class":2235},[2225,150846,150847],{"class":2307}," productIds",[2225,150849,2311],{"class":2235},[2225,150851,150788],{"class":2291},[2225,150853,2789],{"class":2239},[2225,150855,2805],{"class":2235},[2225,150857,150858],{"class":2252},"products",[2225,150860,2805],{"class":2235},[2225,150862,2298],{"class":2235},[2225,150864,2249],{"class":2235},[2225,150866,150565],{"class":2252},[2225,150868,2805],{"class":2235},[2225,150870,8495],{"class":2239},[2225,150872,19008],{"class":2235},[2225,150874,150875,150877],{"class":2227,"line":2304},[2225,150876,2359],{"class":2235},[2225,150878,2362],{"class":2239},[2225,150880,150881],{"class":2227,"line":2356},[2225,150882,2285],{"emptyLinePlaceholder":2284},[2225,150884,150885,150887,150890,150892,150894,150896,150898,150901,150903,150905,150907,150909,150911,150913,150915,150917,150919,150921,150923,150925,150927,150930,150932,150934,150936,150939,150941,150943,150945],{"class":2227,"line":2830},[2225,150886,6500],{"class":2231},[2225,150888,150889],{"class":2239}," orders",[2225,150891,1520],{"class":2235},[2225,150893,3161],{"class":2291},[2225,150895,2789],{"class":2239},[2225,150897,2805],{"class":2235},[2225,150899,150900],{"class":2252},"o1",[2225,150902,2805],{"class":2235},[2225,150904,2298],{"class":2235},[2225,150906,2236],{"class":2235},[2225,150908,7070],{"class":2307},[2225,150910,2311],{"class":2235},[2225,150912,2249],{"class":2235},[2225,150914,150900],{"class":2252},[2225,150916,2805],{"class":2235},[2225,150918,2298],{"class":2235},[2225,150920,150847],{"class":2307},[2225,150922,2311],{"class":2235},[2225,150924,8667],{"class":2239},[2225,150926,2805],{"class":2235},[2225,150928,150929],{"class":2252},"p1",[2225,150931,2805],{"class":2235},[2225,150933,2298],{"class":2235},[2225,150935,2249],{"class":2235},[2225,150937,150938],{"class":2252},"p2",[2225,150940,2805],{"class":2235},[2225,150942,8890],{"class":2239},[2225,150944,2359],{"class":2235},[2225,150946,2362],{"class":2239},[937,150948,150949,150950,2085,150952,150955],{},"The signature mirrors ",[941,150951,147776],{},[941,150953,150954],{},"refArray(target: string, mode: RefMode = 'strict')",". Per-element semantics:",[1111,150957,150958,150971,150982],{},[1114,150959,150960,964,150962,150964,150965,150967,150968,150970],{},[950,150961,140597],{},[941,150963,20953],{}," rejects if ",[950,150966,11932],{}," element's target is missing (the error's ",[941,150969,150733],{}," is the offending element); deleting a target is blocked while any record's array still contains its id.",[1114,150972,150973,150975,150976,150978,150979,1520],{},[950,150974,150565],{}," — both succeed; ",[941,150977,150390],{}," reports ",[950,150980,150981],{},"one violation per dangling element",[1114,150983,150984,150986,150987,150989],{},[950,150985,10753],{}," — deleting a target deletes every record whose array contains its id (cycle-safe, like scalar cascade); ",[941,150988,20953],{}," is unchecked.",[937,150991,150992,150993,1261,150995,150997],{},"An empty array or a ",[941,150994,6651],{},[941,150996,18316],{}," field is allowed (no links). Non-array values, or elements that are not strings\u002Fnumbers, are integrity violations.",[1591,150999,151001],{"id":151000},"auditing-the-ref-graph","Auditing the ref graph",[937,151003,151004,151006],{},[941,151005,150390],{}," walks every collection with declared refs (and every managed link set) and returns the full violation list instead of throwing:",[2217,151008,151010],{"className":2219,"code":151009,"language":2221,"meta":1037,"style":1037},"const { violations } = await vault.checkIntegrity()\n",[941,151011,151012],{"__ignoreMap":1037},[2225,151013,151014,151016,151018,151021,151023,151025,151027,151029,151031,151034],{"class":2227,"line":2228},[2225,151015,2775],{"class":2774},[2225,151017,2236],{"class":2235},[2225,151019,151020],{"class":2239}," violations ",[2225,151022,2359],{"class":2235},[2225,151024,6346],{"class":2235},[2225,151026,2784],{"class":2231},[2225,151028,6466],{"class":2239},[2225,151030,1520],{"class":2235},[2225,151032,151033],{"class":2291},"checkIntegrity",[2225,151035,8146],{"class":2239},[2217,151037,151039],{"className":6604,"code":151038,"language":141,"meta":1037,"style":1037},"{\n  \"violations\": [\n    {\n      \"collection\": \"invoices\",\n      \"id\": \"inv-7\",\n      \"field\": \"categoryId\",\n      \"refTo\": \"categories\",\n      \"refId\": \"cat-99\",\n      \"mode\": \"warn\"\n    }\n  ]\n}\n",[941,151040,151041,151045,151058,151062,151080,151099,151118,151136,151155,151171,151175,151179],{"__ignoreMap":1037},[2225,151042,151043],{"class":2227,"line":2228},[2225,151044,4200],{"class":2235},[2225,151046,151047,151049,151052,151054,151056],{"class":2227,"line":1041},[2225,151048,9368],{"class":2235},[2225,151050,151051],{"class":2774},"violations",[2225,151053,6620],{"class":2235},[2225,151055,2311],{"class":2235},[2225,151057,32491],{"class":2235},[2225,151059,151060],{"class":2227,"line":1038},[2225,151061,109952],{"class":2235},[2225,151063,151064,151066,151068,151070,151072,151074,151076,151078],{"class":2227,"line":2288},[2225,151065,59948],{"class":2235},[2225,151067,6471],{"class":6342},[2225,151069,6620],{"class":2235},[2225,151071,2311],{"class":2235},[2225,151073,6614],{"class":2235},[2225,151075,6487],{"class":2252},[2225,151077,6620],{"class":2235},[2225,151079,1707],{"class":2235},[2225,151081,151082,151084,151086,151088,151090,151092,151095,151097],{"class":2227,"line":2304},[2225,151083,59948],{"class":2235},[2225,151085,6983],{"class":6342},[2225,151087,6620],{"class":2235},[2225,151089,2311],{"class":2235},[2225,151091,6614],{"class":2235},[2225,151093,151094],{"class":2252},"inv-7",[2225,151096,6620],{"class":2235},[2225,151098,1707],{"class":2235},[2225,151100,151101,151103,151105,151107,151109,151111,151114,151116],{"class":2227,"line":2356},[2225,151102,59948],{"class":2235},[2225,151104,25197],{"class":6342},[2225,151106,6620],{"class":2235},[2225,151108,2311],{"class":2235},[2225,151110,6614],{"class":2235},[2225,151112,151113],{"class":2252},"categoryId",[2225,151115,6620],{"class":2235},[2225,151117,1707],{"class":2235},[2225,151119,151120,151122,151124,151126,151128,151130,151132,151134],{"class":2227,"line":2830},[2225,151121,59948],{"class":2235},[2225,151123,150730],{"class":6342},[2225,151125,6620],{"class":2235},[2225,151127,2311],{"class":2235},[2225,151129,6614],{"class":2235},[2225,151131,150556],{"class":2252},[2225,151133,6620],{"class":2235},[2225,151135,1707],{"class":2235},[2225,151137,151138,151140,151142,151144,151146,151148,151151,151153],{"class":2227,"line":2861},[2225,151139,59948],{"class":2235},[2225,151141,150733],{"class":6342},[2225,151143,6620],{"class":2235},[2225,151145,2311],{"class":2235},[2225,151147,6614],{"class":2235},[2225,151149,151150],{"class":2252},"cat-99",[2225,151152,6620],{"class":2235},[2225,151154,1707],{"class":2235},[2225,151156,151157,151159,151161,151163,151165,151167,151169],{"class":2227,"line":4246},[2225,151158,59948],{"class":2235},[2225,151160,120875],{"class":6342},[2225,151162,6620],{"class":2235},[2225,151164,2311],{"class":2235},[2225,151166,6614],{"class":2235},[2225,151168,150565],{"class":2252},[2225,151170,9449],{"class":2235},[2225,151172,151173],{"class":2227,"line":4253},[2225,151174,60103],{"class":2235},[2225,151176,151177],{"class":2227,"line":4259},[2225,151178,110088],{"class":2235},[2225,151180,151181],{"class":2227,"line":6548},[2225,151182,7351],{"class":2235},[1022,151184,151186],{"id":151185},"managed-many-to-many-links","Managed many-to-many links",[937,151188,151189,151190,1952,151193,151196,151197,151200,151201,151204],{},"For a full M:N relationship — queryable from both sides, with per-link metadata — declare a ",[950,151191,151192],{},"managed junction",[941,151194,151195],{},"vault.link(name, spec)"," registers a dedicated encrypted ",[941,151198,151199],{},"_links_\u003Cname>"," collection between two endpoint collections; ",[941,151202,151203],{},"vault.links(name)"," returns the handle that operates it:",[2217,151206,151208],{"className":2219,"code":151207,"language":2221,"meta":1037,"style":1037},"import { ref } from '@noy-db\u002Fhub'\n\nvault.link('saleLineLinks', {\n  a: ref('saleLines'),   \u002F\u002F or the plain string 'saleLines'\n  b: ref('purchaseLines'),\n  onDelete: 'cascade',   \u002F\u002F default\n})\n\nconst links = vault.links('saleLineLinks')\nawait links.connect('s1', 'p1', { matchedQty: 6 }) \u002F\u002F validates both endpoints exist\nawait links.of('s1')        \u002F\u002F → [{ a: 's1', b: 'p1', meta: { matchedQty: 6 } }]\nawait links.has('s1', 'p1') \u002F\u002F → true\nawait links.disconnect('s1', 'p1') \u002F\u002F idempotent — no-op if absent\nawait links.list()          \u002F\u002F → all rows in the set\n",[941,151209,151210,151228,151232,151253,151278,151300,151318,151324,151328,151353,151400,151423,151453,151485],{"__ignoreMap":1037},[2225,151211,151212,151214,151216,151218,151220,151222,151224,151226],{"class":2227,"line":2228},[2225,151213,2232],{"class":2231},[2225,151215,2236],{"class":2235},[2225,151217,62303],{"class":2239},[2225,151219,2243],{"class":2235},[2225,151221,2246],{"class":2231},[2225,151223,2249],{"class":2235},[2225,151225,1086],{"class":2252},[2225,151227,2256],{"class":2235},[2225,151229,151230],{"class":2227,"line":1041},[2225,151231,2285],{"emptyLinePlaceholder":2284},[2225,151233,151234,151236,151238,151240,151242,151244,151247,151249,151251],{"class":2227,"line":1038},[2225,151235,2694],{"class":2239},[2225,151237,1520],{"class":2235},[2225,151239,44484],{"class":2291},[2225,151241,2789],{"class":2239},[2225,151243,2805],{"class":2235},[2225,151245,151246],{"class":2252},"saleLineLinks",[2225,151248,2805],{"class":2235},[2225,151250,2298],{"class":2235},[2225,151252,2301],{"class":2235},[2225,151254,151255,151258,151260,151262,151264,151266,151269,151271,151273,151275],{"class":2227,"line":2288},[2225,151256,151257],{"class":2307},"  a",[2225,151259,2311],{"class":2235},[2225,151261,62303],{"class":2291},[2225,151263,2789],{"class":2239},[2225,151265,2805],{"class":2235},[2225,151267,151268],{"class":2252},"saleLines",[2225,151270,2805],{"class":2235},[2225,151272,1381],{"class":2239},[2225,151274,2298],{"class":2235},[2225,151276,151277],{"class":2763},"   \u002F\u002F or the plain string 'saleLines'\n",[2225,151279,151280,151283,151285,151287,151289,151291,151294,151296,151298],{"class":2227,"line":2304},[2225,151281,151282],{"class":2307},"  b",[2225,151284,2311],{"class":2235},[2225,151286,62303],{"class":2291},[2225,151288,2789],{"class":2239},[2225,151290,2805],{"class":2235},[2225,151292,151293],{"class":2252},"purchaseLines",[2225,151295,2805],{"class":2235},[2225,151297,1381],{"class":2239},[2225,151299,1707],{"class":2235},[2225,151301,151302,151305,151307,151309,151311,151313,151315],{"class":2227,"line":2356},[2225,151303,151304],{"class":2307},"  onDelete",[2225,151306,2311],{"class":2235},[2225,151308,2249],{"class":2235},[2225,151310,10753],{"class":2252},[2225,151312,2805],{"class":2235},[2225,151314,2298],{"class":2235},[2225,151316,151317],{"class":2763},"   \u002F\u002F default\n",[2225,151319,151320,151322],{"class":2227,"line":2830},[2225,151321,2359],{"class":2235},[2225,151323,2362],{"class":2239},[2225,151325,151326],{"class":2227,"line":2861},[2225,151327,2285],{"emptyLinePlaceholder":2284},[2225,151329,151330,151332,151335,151337,151339,151341,151343,151345,151347,151349,151351],{"class":2227,"line":4246},[2225,151331,2775],{"class":2774},[2225,151333,151334],{"class":2239}," links ",[2225,151336,2781],{"class":2235},[2225,151338,6466],{"class":2239},[2225,151340,1520],{"class":2235},[2225,151342,723],{"class":2291},[2225,151344,2789],{"class":2239},[2225,151346,2805],{"class":2235},[2225,151348,151246],{"class":2252},[2225,151350,2805],{"class":2235},[2225,151352,2362],{"class":2239},[2225,151354,151355,151357,151360,151362,151364,151366,151368,151371,151373,151375,151377,151379,151381,151383,151385,151388,151390,151393,151395,151397],{"class":2227,"line":4253},[2225,151356,6500],{"class":2231},[2225,151358,151359],{"class":2239}," links",[2225,151361,1520],{"class":2235},[2225,151363,54379],{"class":2291},[2225,151365,2789],{"class":2239},[2225,151367,2805],{"class":2235},[2225,151369,151370],{"class":2252},"s1",[2225,151372,2805],{"class":2235},[2225,151374,2298],{"class":2235},[2225,151376,2249],{"class":2235},[2225,151378,150929],{"class":2252},[2225,151380,2805],{"class":2235},[2225,151382,2298],{"class":2235},[2225,151384,2236],{"class":2235},[2225,151386,151387],{"class":2307}," matchedQty",[2225,151389,2311],{"class":2235},[2225,151391,151392],{"class":6527}," 6",[2225,151394,2243],{"class":2235},[2225,151396,8495],{"class":2239},[2225,151398,151399],{"class":2763},"\u002F\u002F validates both endpoints exist\n",[2225,151401,151402,151404,151406,151408,151410,151412,151414,151416,151418,151420],{"class":2227,"line":4259},[2225,151403,6500],{"class":2231},[2225,151405,151359],{"class":2239},[2225,151407,1520],{"class":2235},[2225,151409,27066],{"class":2291},[2225,151411,2789],{"class":2239},[2225,151413,2805],{"class":2235},[2225,151415,151370],{"class":2252},[2225,151417,2805],{"class":2235},[2225,151419,118807],{"class":2239},[2225,151421,151422],{"class":2763},"\u002F\u002F → [{ a: 's1', b: 'p1', meta: { matchedQty: 6 } }]\n",[2225,151424,151425,151427,151429,151431,151433,151435,151437,151439,151441,151443,151445,151447,151449,151451],{"class":2227,"line":6548},[2225,151426,6500],{"class":2231},[2225,151428,151359],{"class":2239},[2225,151430,1520],{"class":2235},[2225,151432,145202],{"class":2291},[2225,151434,2789],{"class":2239},[2225,151436,2805],{"class":2235},[2225,151438,151370],{"class":2252},[2225,151440,2805],{"class":2235},[2225,151442,2298],{"class":2235},[2225,151444,2249],{"class":2235},[2225,151446,150929],{"class":2252},[2225,151448,2805],{"class":2235},[2225,151450,8495],{"class":2239},[2225,151452,77590],{"class":2763},[2225,151454,151455,151457,151459,151461,151464,151466,151468,151470,151472,151474,151476,151478,151480,151482],{"class":2227,"line":6576},[2225,151456,6500],{"class":2231},[2225,151458,151359],{"class":2239},[2225,151460,1520],{"class":2235},[2225,151462,151463],{"class":2291},"disconnect",[2225,151465,2789],{"class":2239},[2225,151467,2805],{"class":2235},[2225,151469,151370],{"class":2252},[2225,151471,2805],{"class":2235},[2225,151473,2298],{"class":2235},[2225,151475,2249],{"class":2235},[2225,151477,150929],{"class":2252},[2225,151479,2805],{"class":2235},[2225,151481,8495],{"class":2239},[2225,151483,151484],{"class":2763},"\u002F\u002F idempotent — no-op if absent\n",[2225,151486,151487,151489,151491,151493,151495,151497],{"class":2227,"line":6582},[2225,151488,6500],{"class":2231},[2225,151490,151359],{"class":2239},[2225,151492,1520],{"class":2235},[2225,151494,3167],{"class":2291},[2225,151496,39855],{"class":2239},[2225,151498,151499],{"class":2763},"\u002F\u002F → all rows in the set\n",[1111,151501,151502,151537,151561,151564],{},[1114,151503,151504,964,151507,151509,151510,151512,151513,1069,151515,151517,151518,25198,151521,151524,151525,1261,151527,151529,151530,151533,151534,151536],{},[950,151505,151506],{},"Slot-typed",[941,151508,1007],{}," is an id in the ",[941,151511,1007],{}," collection, ",[941,151514,80529],{},[941,151516,80529],{}," collection (the two may be the same collection for self-links); ",[941,151519,151520],{},"of(id)",[950,151522,151523],{},"either"," slot. ",[941,151526,1007],{},[941,151528,80529],{}," accept a plain collection name or a ",[941,151531,151532],{},"ref(target)"," descriptor (only its ",[941,151535,134984],{}," is used — link sets manage their own integrity).",[1114,151538,151539,151543,151544,151546,151547,151549,151550,6789,151552,151555,151556,151558,151559,1520],{},[950,151540,151541],{},[941,151542,137880],{}," governs what happens to link rows when an endpoint ",[1092,151545,24158],{}," is deleted: ",[941,151548,150705],{}," (default) removes the touching link rows; ",[941,151551,150663],{},[941,151553,151554],{},"LinkIntegrityError"," and blocks the delete; ",[941,151557,51655],{}," leaves orphan rows, surfaced by ",[941,151560,150390],{},[1114,151562,151563],{},"Re-declaring the same link with identical spec is a no-op; a conflicting re-declaration throws.",[1114,151565,151566,151567,151569,151570,151573],{},"Rows are encrypted under their own ",[941,151568,151199],{}," data-encryption key, like any other collection; the name is reserved — ",[941,151571,151572],{},"vault.collection('_links_anything')"," is rejected.",[937,151575,151576,151583,151584,151589,151590,151592,151593,151596,151597,151599,151600,1261,151602,151604,151605,151608],{},[950,151577,151578,11189,151580,1520],{},[941,151579,147755],{},[941,151581,151582],{},"vault.link"," Reach for ",[1007,151585,151587],{"href":151586},"#array-refs-refarray",[941,151588,147755],{}," (an id-array on one record) for simple tag-like sets. Reach for ",[941,151591,151582],{}," when the links themselves are ",[1092,151594,151595],{},"entities"," — queryable from both sides, annotatable with metadata, cascading. There is no query-DSL join ",[1092,151598,1409],{}," a link set: read link rows with ",[941,151601,151520],{},[941,151603,24407],{}," and fetch the endpoint records from their collections. See ",[1007,151606,723],{"href":151607},".\u002Flinks"," for the managed link-set API in depth.",[1022,151610,28863],{"id":28862},[1111,151612,151613,151628],{},[1114,151614,151615,151616,151619,151620,6789,151622,151625,151626,1520],{},"Refs are ",[950,151617,151618],{},"intra-vault",". A target name containing ",[941,151621,1625],{},[941,151623,151624],{},"RefScopeError"," at declaration time; for cross-vault reads use ",[941,151627,27688],{},[1114,151629,151630,151631,151634,151635,151637],{},"Ref fields are ",[950,151632,151633],{},"top-level only"," — dotted paths are not supported. Target names cannot be empty or start with ",[941,151636,6702],{}," (reserved for internal collections).",[1022,151639,2621],{"id":2620},[1111,151641,151642,151647,151651,151656,151663,151668],{},[1114,151643,151644,151646],{},[1007,151645,111],{"href":124776}," — Standard Schema validation on collections",[1114,151648,151649],{},[1007,151650,28891],{"href":58262},[1114,151652,151653],{},[1007,151654,151655],{"href":83069},"Core 04 — Collection (Querying)",[1114,151657,151658,964,151660,151662],{},[1007,151659,715],{"href":132487},[941,151661,61808],{}," over declared refs",[1114,151664,151665,151667],{},[1007,151666,723],{"href":151607}," — managed link sets in depth",[1114,151669,101581,151670,1520],{},[1007,151671,151673],{"href":93925,"rel":151672},[3371],[941,151674,151675],{},"103-ref-array",[2645,151677,151678],{},"html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}",{"title":1037,"searchDepth":1038,"depth":1038,"links":151680},[151681,151682,151687,151688,151689],{"id":6241,"depth":1041,"text":6242},{"id":150407,"depth":1041,"text":150408,"children":151683},[151684,151686],{"id":150761,"depth":1038,"text":151685},"Array refs (refArray)",{"id":151000,"depth":1038,"text":151001},{"id":151185,"depth":1041,"text":151186},{"id":28862,"depth":1041,"text":28863},{"id":2620,"depth":1041,"text":2621},"Foreign-key references between collections: `ref()`\u002F`refArray()` with strict\u002Fwarn\u002Fcascade integrity and the `checkIntegrity()` audit. Always-on kernel.",{},{"title":681,"description":151690},"JoLoBmm5XpemdBlU0VvI26kHRBDunlQu7hJ2EfCFUfw",{"id":151695,"title":685,"access":12,"apiToc":929,"body":151696,"category":1042,"description":153981,"extension":1044,"meta":153982,"navigation":1042,"nest":1047,"path":686,"seo":153983,"sinceVersion":1042,"status":1049,"stem":687,"subtitle":1042,"tier":1050,"tocExternal":1042,"__hash__":153984},"docs\u002Fservices\u002Fforget.md",{"type":931,"value":151697,"toc":153961},[151698,151700,151703,151705,151717,151730,151732,151759,151761,151774,152000,152012,152014,152018,152026,152243,152682,152685,152714,152727,152731,152734,152847,152853,152868,152876,152932,152949,153075,153094,153098,153107,153137,153139,153145,153170,153178,153183,153247,153252,153276,153289,153299,153303,153729,153734,153757,153763,153765,153810,153812,153885,153887,153958],[934,151699,685],{"id":685},[46871,151701],{":rows":151702},"[{\"label\":\"Subpath\",\"value\":\"`@noy-db\u002Fhub\u002Fforget`\"},{\"label\":\"Factory\",\"value\":\"`withForget()`\"},{\"label\":\"Layer\",\"value\":\"`with-audit`\"}]",[1022,151704,29857],{"id":29856},[937,151706,151707,151708,151711,151712,3731,151714,151716],{},"Privacy laws such as the GDPR give a person the right to have their data erased on request.\nThe forget service implements that as ",[950,151709,151710],{},"crypto-shredding",": instead of hunting down and\ndeleting every copy of a record (impossible once backups exist), each record body is\nencrypted under its own per-record content-encryption key (a ",[950,151713,13876],{},[1007,151715,1516],{"href":66}," — and \"erasing\" a record means destroying that\nkey's ciphertext so the ciphertext body can never be decrypted again, anywhere it was\ncopied.",[937,151718,151719,151720,151723,151724,151726,151727,151729],{},"You declare which field identifies the data subject on each collection you want covered\n(",[941,151721,151722],{},"{ invoices: 'buyerId' }","). From then on, noy-db maintains an encrypted index mapping each\nsubject to every record that names them. Calling ",[941,151725,21428],{}," looks that index\nup and, for every matching record, rewrites the live envelope and all of its history to an\nempty tombstone — so the body becomes permanently unreadable while the collection's key and\nevery other record stay intact — then appends one entry to the change ledger (see\n",[1007,151728,697],{"href":698},") proving the erasure happened without retaining the subject's\ndata.",[1022,151731,70093],{"id":70092},[1111,151733,151734,151745,151753],{},[1114,151735,151736,964,151740,151742,151743,1520],{},[950,151737,151738],{},[941,151739,84477],{},[941,151741,2542],{}," writes its erasure proof into the change ledger, so history must be enabled alongside ",[941,151744,73758],{},[1114,151746,151747,151752],{},[950,151748,3289,151749,151751],{},[941,151750,119747],{}," declaration in the factory"," — mapping each covered collection to its subject field.",[1114,151754,151755,151758],{},[1092,151756,151757],{},"Coverage note:"," a collection's classified\u002Fblob fields count as covered only if declared in the same session's config (detailed under the coverage rules below).",[1022,151760,99864],{"id":99863},[937,151762,124907,151763,10313,151766,1952,151768,151770,151771,151773],{},[941,151764,151765],{},"forgetStrategy: withForget({ subjects: {...} })",[941,151767,12780],{},[941,151769,2542],{},"\nalso needs the change ledger for its erasure proof, so ",[941,151772,84477],{}," must\nbe configured too:",[2217,151775,151777],{"className":2219,"code":151776,"language":2221,"meta":1037,"style":1037},"import { createNoydb } from '@noy-db\u002Fhub'\nimport { withForget } from '@noy-db\u002Fhub\u002Fforget'\nimport { withHistory } from '@noy-db\u002Fhub\u002Fhistory'\nimport { toMemory } from '@noy-db\u002Fto-memory'\n\nconst db = await createNoydb({\n  store: toMemory(),                          \u002F\u002F any NoydbStore works\n  user: 'alice',\n  secret: 'correct horse battery staple',\n  historyStrategy: withHistory(),            \u002F\u002F required — forget() writes its proof here\n  forgetStrategy: withForget({ subjects: { invoices: 'buyerId' } }),\n})\n\nconst vault = await db.openVault('acme')\n",[941,151778,151779,151797,151815,151833,151851,151855,151871,151885,151899,151913,151928,151964,151970,151974],{"__ignoreMap":1037},[2225,151780,151781,151783,151785,151787,151789,151791,151793,151795],{"class":2227,"line":2228},[2225,151782,2232],{"class":2231},[2225,151784,2236],{"class":2235},[2225,151786,2744],{"class":2239},[2225,151788,2243],{"class":2235},[2225,151790,2246],{"class":2231},[2225,151792,2249],{"class":2235},[2225,151794,1086],{"class":2252},[2225,151796,2256],{"class":2235},[2225,151798,151799,151801,151803,151805,151807,151809,151811,151813],{"class":2227,"line":1041},[2225,151800,2232],{"class":2231},[2225,151802,2236],{"class":2235},[2225,151804,74041],{"class":2239},[2225,151806,2243],{"class":2235},[2225,151808,2246],{"class":2231},[2225,151810,2249],{"class":2235},[2225,151812,73761],{"class":2252},[2225,151814,2256],{"class":2235},[2225,151816,151817,151819,151821,151823,151825,151827,151829,151831],{"class":2227,"line":1038},[2225,151818,2232],{"class":2231},[2225,151820,2236],{"class":2235},[2225,151822,4142],{"class":2239},[2225,151824,2243],{"class":2235},[2225,151826,2246],{"class":2231},[2225,151828,2249],{"class":2235},[2225,151830,4151],{"class":2252},[2225,151832,2256],{"class":2235},[2225,151834,151835,151837,151839,151841,151843,151845,151847,151849],{"class":2227,"line":2288},[2225,151836,2232],{"class":2231},[2225,151838,2236],{"class":2235},[2225,151840,68576],{"class":2239},[2225,151842,2243],{"class":2235},[2225,151844,2246],{"class":2231},[2225,151846,2249],{"class":2235},[2225,151848,9638],{"class":2252},[2225,151850,2256],{"class":2235},[2225,151852,151853],{"class":2227,"line":2304},[2225,151854,2285],{"emptyLinePlaceholder":2284},[2225,151856,151857,151859,151861,151863,151865,151867,151869],{"class":2227,"line":2356},[2225,151858,2775],{"class":2774},[2225,151860,2778],{"class":2239},[2225,151862,2781],{"class":2235},[2225,151864,2784],{"class":2231},[2225,151866,2744],{"class":2291},[2225,151868,2789],{"class":2239},[2225,151870,4200],{"class":2235},[2225,151872,151873,151875,151877,151879,151881,151883],{"class":2227,"line":2830},[2225,151874,15608],{"class":2307},[2225,151876,2311],{"class":2235},[2225,151878,68576],{"class":2291},[2225,151880,4228],{"class":2239},[2225,151882,2298],{"class":2235},[2225,151884,125006],{"class":2763},[2225,151886,151887,151889,151891,151893,151895,151897],{"class":2227,"line":2861},[2225,151888,4205],{"class":2307},[2225,151890,2311],{"class":2235},[2225,151892,2249],{"class":2235},[2225,151894,6408],{"class":2252},[2225,151896,2805],{"class":2235},[2225,151898,1707],{"class":2235},[2225,151900,151901,151903,151905,151907,151909,151911],{"class":2227,"line":4246},[2225,151902,10423],{"class":2307},[2225,151904,2311],{"class":2235},[2225,151906,2249],{"class":2235},[2225,151908,40260],{"class":2252},[2225,151910,2805],{"class":2235},[2225,151912,1707],{"class":2235},[2225,151914,151915,151917,151919,151921,151923,151925],{"class":2227,"line":4253},[2225,151916,4221],{"class":2307},[2225,151918,2311],{"class":2235},[2225,151920,4142],{"class":2291},[2225,151922,4228],{"class":2239},[2225,151924,2298],{"class":2235},[2225,151926,151927],{"class":2763},"            \u002F\u002F required — forget() writes its proof here\n",[2225,151929,151930,151932,151934,151936,151938,151940,151942,151944,151946,151948,151950,151952,151954,151956,151958,151960,151962],{"class":2227,"line":4259},[2225,151931,74356],{"class":2307},[2225,151933,2311],{"class":2235},[2225,151935,74041],{"class":2291},[2225,151937,2789],{"class":2239},[2225,151939,2792],{"class":2235},[2225,151941,74367],{"class":2307},[2225,151943,2311],{"class":2235},[2225,151945,2236],{"class":2235},[2225,151947,6503],{"class":2307},[2225,151949,2311],{"class":2235},[2225,151951,2249],{"class":2235},[2225,151953,109007],{"class":2252},[2225,151955,2805],{"class":2235},[2225,151957,2243],{"class":2235},[2225,151959,2243],{"class":2235},[2225,151961,1381],{"class":2239},[2225,151963,1707],{"class":2235},[2225,151965,151966,151968],{"class":2227,"line":6548},[2225,151967,2359],{"class":2235},[2225,151969,2362],{"class":2239},[2225,151971,151972],{"class":2227,"line":6576},[2225,151973,2285],{"emptyLinePlaceholder":2284},[2225,151975,151976,151978,151980,151982,151984,151986,151988,151990,151992,151994,151996,151998],{"class":2227,"line":6582},[2225,151977,2775],{"class":2774},[2225,151979,2835],{"class":2239},[2225,151981,2781],{"class":2235},[2225,151983,2784],{"class":2231},[2225,151985,2842],{"class":2239},[2225,151987,1520],{"class":2235},[2225,151989,2847],{"class":2291},[2225,151991,2789],{"class":2239},[2225,151993,2805],{"class":2235},[2225,151995,6450],{"class":2252},[2225,151997,2805],{"class":2235},[2225,151999,2362],{"class":2239},[937,152001,152002,152003,152005,152006,152008,152009,152011],{},"Declaring a collection in ",[941,152004,119747],{}," also forces ",[941,152007,73811],{}," for it — a shred can\nonly guarantee erasure of a body keyed off its own CEK, so adopting ",[941,152010,685],{}," on a collection\nopts it into per-record keys automatically.",[1022,152013,29893],{"id":29892},[1591,152015,152017],{"id":152016},"erase-a-subject","Erase a subject",[937,152019,30792,152020,152022,152023,152025],{},[941,152021,3161],{}," on a declared collection updates the encrypted subject index behind the scenes.\n",[941,152024,21428],{}," finds and shreds everything indexed under that subject:",[2217,152027,152029],{"className":2219,"code":152028,"language":2221,"meta":1037,"style":1037},"interface Invoice { id: string; buyerId: string; amount: number }\nconst invoices = vault.collection\u003CInvoice>('invoices')\n\nawait invoices.put('inv-1', { id: 'inv-1', buyerId: 'buyer-123', amount: 100 })\nawait invoices.put('inv-1', { id: 'inv-1', buyerId: 'buyer-123', amount: 150 }) \u002F\u002F 2nd version → history\n\nconst result = await vault.forget('buyer-123')\n",[941,152030,152031,152063,152093,152097,152153,152213,152217],{"__ignoreMap":1037},[2225,152032,152033,152035,152037,152039,152041,152043,152045,152047,152049,152051,152053,152055,152057,152059,152061],{"class":2227,"line":2228},[2225,152034,6994],{"class":2774},[2225,152036,6343],{"class":6342},[2225,152038,2236],{"class":2235},[2225,152040,7070],{"class":2307},[2225,152042,2311],{"class":2235},[2225,152044,6366],{"class":6342},[2225,152046,3769],{"class":2235},[2225,152048,147156],{"class":2307},[2225,152050,2311],{"class":2235},[2225,152052,6366],{"class":6342},[2225,152054,3769],{"class":2235},[2225,152056,6351],{"class":2307},[2225,152058,2311],{"class":2235},[2225,152060,6356],{"class":6342},[2225,152062,6369],{"class":2235},[2225,152064,152065,152067,152069,152071,152073,152075,152077,152079,152081,152083,152085,152087,152089,152091],{"class":2227,"line":1041},[2225,152066,2775],{"class":2774},[2225,152068,6461],{"class":2239},[2225,152070,2781],{"class":2235},[2225,152072,6466],{"class":2239},[2225,152074,1520],{"class":2235},[2225,152076,6471],{"class":2291},[2225,152078,6474],{"class":2235},[2225,152080,6477],{"class":6342},[2225,152082,6480],{"class":2235},[2225,152084,2789],{"class":2239},[2225,152086,2805],{"class":2235},[2225,152088,6487],{"class":2252},[2225,152090,2805],{"class":2235},[2225,152092,2362],{"class":2239},[2225,152094,152095],{"class":2227,"line":1038},[2225,152096,2285],{"emptyLinePlaceholder":2284},[2225,152098,152099,152101,152103,152105,152107,152109,152111,152113,152115,152117,152119,152121,152123,152125,152127,152129,152131,152133,152135,152137,152139,152141,152143,152145,152147,152149,152151],{"class":2227,"line":2288},[2225,152100,6500],{"class":2231},[2225,152102,6503],{"class":2239},[2225,152104,1520],{"class":2235},[2225,152106,3161],{"class":2291},[2225,152108,2789],{"class":2239},[2225,152110,2805],{"class":2235},[2225,152112,6514],{"class":2252},[2225,152114,2805],{"class":2235},[2225,152116,2298],{"class":2235},[2225,152118,2236],{"class":2235},[2225,152120,7070],{"class":2307},[2225,152122,2311],{"class":2235},[2225,152124,2249],{"class":2235},[2225,152126,6514],{"class":2252},[2225,152128,2805],{"class":2235},[2225,152130,2298],{"class":2235},[2225,152132,147156],{"class":2307},[2225,152134,2311],{"class":2235},[2225,152136,2249],{"class":2235},[2225,152138,108785],{"class":2252},[2225,152140,2805],{"class":2235},[2225,152142,2298],{"class":2235},[2225,152144,6351],{"class":2307},[2225,152146,2311],{"class":2235},[2225,152148,6528],{"class":6527},[2225,152150,2243],{"class":2235},[2225,152152,2362],{"class":2239},[2225,152154,152155,152157,152159,152161,152163,152165,152167,152169,152171,152173,152175,152177,152179,152181,152183,152185,152187,152189,152191,152193,152195,152197,152199,152201,152203,152206,152208,152210],{"class":2227,"line":2304},[2225,152156,6500],{"class":2231},[2225,152158,6503],{"class":2239},[2225,152160,1520],{"class":2235},[2225,152162,3161],{"class":2291},[2225,152164,2789],{"class":2239},[2225,152166,2805],{"class":2235},[2225,152168,6514],{"class":2252},[2225,152170,2805],{"class":2235},[2225,152172,2298],{"class":2235},[2225,152174,2236],{"class":2235},[2225,152176,7070],{"class":2307},[2225,152178,2311],{"class":2235},[2225,152180,2249],{"class":2235},[2225,152182,6514],{"class":2252},[2225,152184,2805],{"class":2235},[2225,152186,2298],{"class":2235},[2225,152188,147156],{"class":2307},[2225,152190,2311],{"class":2235},[2225,152192,2249],{"class":2235},[2225,152194,108785],{"class":2252},[2225,152196,2805],{"class":2235},[2225,152198,2298],{"class":2235},[2225,152200,6351],{"class":2307},[2225,152202,2311],{"class":2235},[2225,152204,152205],{"class":6527}," 150",[2225,152207,2243],{"class":2235},[2225,152209,8495],{"class":2239},[2225,152211,152212],{"class":2763},"\u002F\u002F 2nd version → history\n",[2225,152214,152215],{"class":2227,"line":2356},[2225,152216,2285],{"emptyLinePlaceholder":2284},[2225,152218,152219,152221,152223,152225,152227,152229,152231,152233,152235,152237,152239,152241],{"class":2227,"line":2830},[2225,152220,2775],{"class":2774},[2225,152222,36920],{"class":2239},[2225,152224,2781],{"class":2235},[2225,152226,2784],{"class":2231},[2225,152228,6466],{"class":2239},[2225,152230,1520],{"class":2235},[2225,152232,685],{"class":2291},[2225,152234,2789],{"class":2239},[2225,152236,2805],{"class":2235},[2225,152238,108785],{"class":2252},[2225,152240,2805],{"class":2235},[2225,152242,2362],{"class":2239},[2217,152244,152246],{"className":6604,"code":152245,"language":141,"meta":1037,"style":1037},"{\n  \"subject\": \"buyer-123\",\n  \"recordsShredded\": 1,\n  \"historyVersionsShredded\": 1,\n  \"collections\": [\"invoices\"],\n  \"unmigratedRecords\": [],\n  \"blobsShredded\": 0,\n  \"blobsRetainedShared\": 0,\n  \"blobResidueCollections\": [],\n  \"indexPostingsPurged\": 0,\n  \"indexResidue\": [],\n  \"sealedFieldsShredded\": 0,\n  \"sealedCekEnvelopesPurged\": 0,\n  \"sealedCekResidue\": [],\n  \"sealedResidue\": [],\n  \"scopedPurgeResidue\": [],\n  \"derivedRecordsErased\": 0,\n  \"derivedAggregatesRecomputed\": 0,\n  \"derivedResidueFrozen\": [],\n  \"derivedResidueUndecodable\": [],\n  \"derivedResidueDeclined\": [],\n  \"lookupReferencesCascaded\": 0,\n  \"lookupReferencesNullified\": 0,\n  \"lookupReferencesResidue\": [],\n  \"ledgerEntry\": { \"op\": \"forget\", \"collection\": \"\", \"id\": \"\", \"version\": 0, \"payloadHash\": \"\u003Csha256 of subject id>\", \"…\": \"…\" }\n}\n",[941,152247,152248,152252,152270,152285,152300,152320,152334,152349,152364,152377,152392,152405,152419,152433,152446,152458,152470,152485,152500,152513,152525,152537,152552,152567,152580,152678],{"__ignoreMap":1037},[2225,152249,152250],{"class":2227,"line":2228},[2225,152251,4200],{"class":2235},[2225,152253,152254,152256,152258,152260,152262,152264,152266,152268],{"class":2227,"line":1041},[2225,152255,9368],{"class":2235},[2225,152257,57531],{"class":2774},[2225,152259,6620],{"class":2235},[2225,152261,2311],{"class":2235},[2225,152263,6614],{"class":2235},[2225,152265,108785],{"class":2252},[2225,152267,6620],{"class":2235},[2225,152269,1707],{"class":2235},[2225,152271,152272,152274,152277,152279,152281,152283],{"class":2227,"line":1038},[2225,152273,9368],{"class":2235},[2225,152275,152276],{"class":2774},"recordsShredded",[2225,152278,6620],{"class":2235},[2225,152280,2311],{"class":2235},[2225,152282,9377],{"class":6527},[2225,152284,1707],{"class":2235},[2225,152286,152287,152289,152292,152294,152296,152298],{"class":2227,"line":2288},[2225,152288,9368],{"class":2235},[2225,152290,152291],{"class":2774},"historyVersionsShredded",[2225,152293,6620],{"class":2235},[2225,152295,2311],{"class":2235},[2225,152297,9377],{"class":6527},[2225,152299,1707],{"class":2235},[2225,152301,152302,152304,152306,152308,152310,152312,152314,152316,152318],{"class":2227,"line":2304},[2225,152303,9368],{"class":2235},[2225,152305,2698],{"class":2774},[2225,152307,6620],{"class":2235},[2225,152309,2311],{"class":2235},[2225,152311,8667],{"class":2235},[2225,152313,6620],{"class":2235},[2225,152315,6487],{"class":2252},[2225,152317,6620],{"class":2235},[2225,152319,66199],{"class":2235},[2225,152321,152322,152324,152327,152329,152331],{"class":2227,"line":2356},[2225,152323,9368],{"class":2235},[2225,152325,152326],{"class":2774},"unmigratedRecords",[2225,152328,6620],{"class":2235},[2225,152330,2311],{"class":2235},[2225,152332,152333],{"class":2235}," [],\n",[2225,152335,152336,152338,152341,152343,152345,152347],{"class":2227,"line":2830},[2225,152337,9368],{"class":2235},[2225,152339,152340],{"class":2774},"blobsShredded",[2225,152342,6620],{"class":2235},[2225,152344,2311],{"class":2235},[2225,152346,8464],{"class":6527},[2225,152348,1707],{"class":2235},[2225,152350,152351,152353,152356,152358,152360,152362],{"class":2227,"line":2861},[2225,152352,9368],{"class":2235},[2225,152354,152355],{"class":2774},"blobsRetainedShared",[2225,152357,6620],{"class":2235},[2225,152359,2311],{"class":2235},[2225,152361,8464],{"class":6527},[2225,152363,1707],{"class":2235},[2225,152365,152366,152368,152371,152373,152375],{"class":2227,"line":4246},[2225,152367,9368],{"class":2235},[2225,152369,152370],{"class":2774},"blobResidueCollections",[2225,152372,6620],{"class":2235},[2225,152374,2311],{"class":2235},[2225,152376,152333],{"class":2235},[2225,152378,152379,152381,152384,152386,152388,152390],{"class":2227,"line":4253},[2225,152380,9368],{"class":2235},[2225,152382,152383],{"class":2774},"indexPostingsPurged",[2225,152385,6620],{"class":2235},[2225,152387,2311],{"class":2235},[2225,152389,8464],{"class":6527},[2225,152391,1707],{"class":2235},[2225,152393,152394,152396,152399,152401,152403],{"class":2227,"line":4259},[2225,152395,9368],{"class":2235},[2225,152397,152398],{"class":2774},"indexResidue",[2225,152400,6620],{"class":2235},[2225,152402,2311],{"class":2235},[2225,152404,152333],{"class":2235},[2225,152406,152407,152409,152411,152413,152415,152417],{"class":2227,"line":6548},[2225,152408,9368],{"class":2235},[2225,152410,119334],{"class":2774},[2225,152412,6620],{"class":2235},[2225,152414,2311],{"class":2235},[2225,152416,8464],{"class":6527},[2225,152418,1707],{"class":2235},[2225,152420,152421,152423,152425,152427,152429,152431],{"class":2227,"line":6576},[2225,152422,9368],{"class":2235},[2225,152424,119611],{"class":2774},[2225,152426,6620],{"class":2235},[2225,152428,2311],{"class":2235},[2225,152430,8464],{"class":6527},[2225,152432,1707],{"class":2235},[2225,152434,152435,152437,152440,152442,152444],{"class":2227,"line":6582},[2225,152436,9368],{"class":2235},[2225,152438,152439],{"class":2774},"sealedCekResidue",[2225,152441,6620],{"class":2235},[2225,152443,2311],{"class":2235},[2225,152445,152333],{"class":2235},[2225,152447,152448,152450,152452,152454,152456],{"class":2227,"line":6587},[2225,152449,9368],{"class":2235},[2225,152451,119338],{"class":2774},[2225,152453,6620],{"class":2235},[2225,152455,2311],{"class":2235},[2225,152457,152333],{"class":2235},[2225,152459,152460,152462,152464,152466,152468],{"class":2227,"line":7174},[2225,152461,9368],{"class":2235},[2225,152463,56927],{"class":2774},[2225,152465,6620],{"class":2235},[2225,152467,2311],{"class":2235},[2225,152469,152333],{"class":2235},[2225,152471,152472,152474,152477,152479,152481,152483],{"class":2227,"line":7180},[2225,152473,9368],{"class":2235},[2225,152475,152476],{"class":2774},"derivedRecordsErased",[2225,152478,6620],{"class":2235},[2225,152480,2311],{"class":2235},[2225,152482,8464],{"class":6527},[2225,152484,1707],{"class":2235},[2225,152486,152487,152489,152492,152494,152496,152498],{"class":2227,"line":7220},[2225,152488,9368],{"class":2235},[2225,152490,152491],{"class":2774},"derivedAggregatesRecomputed",[2225,152493,6620],{"class":2235},[2225,152495,2311],{"class":2235},[2225,152497,8464],{"class":6527},[2225,152499,1707],{"class":2235},[2225,152501,152502,152504,152507,152509,152511],{"class":2227,"line":7225},[2225,152503,9368],{"class":2235},[2225,152505,152506],{"class":2774},"derivedResidueFrozen",[2225,152508,6620],{"class":2235},[2225,152510,2311],{"class":2235},[2225,152512,152333],{"class":2235},[2225,152514,152515,152517,152519,152521,152523],{"class":2227,"line":7231},[2225,152516,9368],{"class":2235},[2225,152518,56955],{"class":2774},[2225,152520,6620],{"class":2235},[2225,152522,2311],{"class":2235},[2225,152524,152333],{"class":2235},[2225,152526,152527,152529,152531,152533,152535],{"class":2227,"line":7267},[2225,152528,9368],{"class":2235},[2225,152530,56963],{"class":2774},[2225,152532,6620],{"class":2235},[2225,152534,2311],{"class":2235},[2225,152536,152333],{"class":2235},[2225,152538,152539,152541,152544,152546,152548,152550],{"class":2227,"line":7272},[2225,152540,9368],{"class":2235},[2225,152542,152543],{"class":2774},"lookupReferencesCascaded",[2225,152545,6620],{"class":2235},[2225,152547,2311],{"class":2235},[2225,152549,8464],{"class":6527},[2225,152551,1707],{"class":2235},[2225,152553,152554,152556,152559,152561,152563,152565],{"class":2227,"line":7278},[2225,152555,9368],{"class":2235},[2225,152557,152558],{"class":2774},"lookupReferencesNullified",[2225,152560,6620],{"class":2235},[2225,152562,2311],{"class":2235},[2225,152564,8464],{"class":6527},[2225,152566,1707],{"class":2235},[2225,152568,152569,152571,152574,152576,152578],{"class":2227,"line":7303},[2225,152570,9368],{"class":2235},[2225,152572,152573],{"class":2774},"lookupReferencesResidue",[2225,152575,6620],{"class":2235},[2225,152577,2311],{"class":2235},[2225,152579,152333],{"class":2235},[2225,152581,152582,152584,152587,152589,152591,152593,152595,152597,152599,152601,152603,152605,152607,152609,152611,152613,152615,152617,152619,152621,152623,152625,152627,152629,152631,152633,152635,152637,152639,152641,152643,152645,152647,152649,152651,152653,152655,152658,152660,152662,152664,152666,152668,152670,152672,152674,152676],{"class":2227,"line":7308},[2225,152583,9368],{"class":2235},[2225,152585,152586],{"class":2774},"ledgerEntry",[2225,152588,6620],{"class":2235},[2225,152590,2311],{"class":2235},[2225,152592,2236],{"class":2235},[2225,152594,6614],{"class":2235},[2225,152596,61313],{"class":6342},[2225,152598,6620],{"class":2235},[2225,152600,2311],{"class":2235},[2225,152602,6614],{"class":2235},[2225,152604,685],{"class":2252},[2225,152606,6620],{"class":2235},[2225,152608,2298],{"class":2235},[2225,152610,6614],{"class":2235},[2225,152612,6471],{"class":6342},[2225,152614,6620],{"class":2235},[2225,152616,2311],{"class":2235},[2225,152618,60469],{"class":2235},[2225,152620,2298],{"class":2235},[2225,152622,6614],{"class":2235},[2225,152624,6983],{"class":6342},[2225,152626,6620],{"class":2235},[2225,152628,2311],{"class":2235},[2225,152630,60469],{"class":2235},[2225,152632,2298],{"class":2235},[2225,152634,6614],{"class":2235},[2225,152636,52564],{"class":6342},[2225,152638,6620],{"class":2235},[2225,152640,2311],{"class":2235},[2225,152642,8464],{"class":6527},[2225,152644,2298],{"class":2235},[2225,152646,6614],{"class":2235},[2225,152648,22982],{"class":6342},[2225,152650,6620],{"class":2235},[2225,152652,2311],{"class":2235},[2225,152654,6614],{"class":2235},[2225,152656,152657],{"class":2252},"\u003Csha256 of subject id>",[2225,152659,6620],{"class":2235},[2225,152661,2298],{"class":2235},[2225,152663,6614],{"class":2235},[2225,152665,14475],{"class":6342},[2225,152667,6620],{"class":2235},[2225,152669,2311],{"class":2235},[2225,152671,6614],{"class":2235},[2225,152673,14475],{"class":2252},[2225,152675,6620],{"class":2235},[2225,152677,6369],{"class":2235},[2225,152679,152680],{"class":2227,"line":7314},[2225,152681,7351],{"class":2235},[937,152683,152684],{},"Once shredded, the record reads back empty rather than throwing:",[2217,152686,152688],{"className":2219,"code":152687,"language":2221,"meta":1037,"style":1037},"await invoices.get('inv-1')\n\u002F\u002F → null\n",[941,152689,152690,152710],{"__ignoreMap":1037},[2225,152691,152692,152694,152696,152698,152700,152702,152704,152706,152708],{"class":2227,"line":2228},[2225,152693,6500],{"class":2231},[2225,152695,6503],{"class":2239},[2225,152697,1520],{"class":2235},[2225,152699,3158],{"class":2291},[2225,152701,2789],{"class":2239},[2225,152703,2805],{"class":2235},[2225,152705,6514],{"class":2252},[2225,152707,2805],{"class":2235},[2225,152709,2362],{"class":2239},[2225,152711,152712],{"class":2227,"line":1041},[2225,152713,76419],{"class":2763},[937,152715,152716,152717,152720,152721,152723,152724,152726],{},"A second ",[941,152718,152719],{},"forget('buyer-123')"," call is a no-op: ",[941,152722,152276],{}," comes back ",[941,152725,5402],{}," and nothing\nthrows.",[1591,152728,152730],{"id":152729},"the-erasure-propagates-sync-derived-copies-references","The erasure propagates — sync, derived copies, references",[937,152732,152733],{},"Three fan-outs run beyond the record's own body, history, and side-cars:",[1111,152735,152736,152753,152806],{},[1114,152737,152738,152740,152741,152743,152744,152746,152747,152749,152750,1520],{},[950,152739,55147],{}," (since ",[941,152742,9471],{},"). On a synced vault, the tombstones enter the sync\ndirty log, so the shred travels on the next ",[941,152745,56859],{},". Sync treats a forget tombstone as\n",[950,152748,56895],{},": a peer's live edit can never overwrite it, in either direction, and conflict\nresolvers are never consulted against one — see\n",[1007,152751,152752],{"href":56898},"sync → erasures are terminal",[1114,152754,152755,152740,152758,152760,152761,968,152763,33507,152765,152767,152768,152770,152771,953,152774,152776,152777,152780,152781,152783,152784,152786,152787,152789,152790,152793,152794,152796,152797,152799,152800,152802,152803,1520],{},[950,152756,152757],{},"Derived copies",[941,152759,111464],{},"). If the vault uses\n",[1007,152762,665],{"href":666},[1007,152764,124625],{"href":740},[1007,152766,637],{"href":638}," fields, ",[941,152769,2542],{}," asks the field-feature dependency graph for the\nforgotten record's derived artifacts and fans out: record-grain artifacts (MV rows, derivation\nrows, same-id derived copies) are ",[950,152772,152773],{},"erased",[941,152775,152476],{},"); aggregate-grain rollups\nare ",[950,152778,152779],{},"recomputed"," without the forgotten contribution (",[941,152782,152491],{},") — or, when\nthe rollup's target sits in a period frozen by ",[1007,152785,776],{"href":777},", the recompute is\nskipped and audited instead (",[941,152788,152506],{},"), while the subject's own record is still\nfully shredded. A materialized-view output row that resists erasure is surfaced with ",[950,152791,152792],{},"audit\ncandor"," rather than silently skipped, and the reason is split (since ",[941,152795,56948],{},", #785):\n",[941,152798,56955],{}," when the row's ownership stamp couldn't be decoded (ownership\nunconfirmed — e.g. an output elevated above tier 0), versus ",[941,152801,56963],{}," when the row\ndecoded and matched but the tier gate declined its erasure (ownership confirmed, a live copy\ndeliberately retained). Both reasons are entries in the tier map on\n",[1007,152804,872],{"href":152805},"\u002Fservices\u002Ftiers#tiers-across-the-system",[1114,152807,152808,152740,152811,152813,152814,152816,152817,152819,152820,152823,152824,152826,152827,1324,152830,152833,152834,1625,152836,152839,152840,1261,152842,953,152844,152846],{},[950,152809,152810],{},"Lookup references",[941,152812,111464],{},"). If the subject's record is a backing row of a\ndeclared ",[1007,152815,731],{"href":732}," field, the field's ",[941,152818,137880],{}," semantics apply: ",[941,152821,152822],{},"restrict","\n(the default) refuses the whole ",[941,152825,2542],{}," call with ",[941,152828,152829],{},"DictKeyInUseError",[950,152831,152832],{},"before any shred","\nwhile references still exist; ",[941,152835,10753],{},[941,152837,152838],{},"nullify"," propagate after the shred and are reported on\n",[941,152841,152543],{},[941,152843,152558],{},[941,152845,152573],{}," names any\npropagation skipped because a reference's compare-key could not be resolved — always empty in\nthe ordinary case, never silent when non-empty).",[1591,152848,152850,152851],{"id":152849},"scope-the-purge-with-scopedpurge","Scope the purge with ",[941,152852,56931],{},[937,152854,152855,152856,152858,152859,152861,152862,152864,152865,152867],{},"Two of the fan-outs above scan ",[950,152857,2119],{}," collection by default — declared in ",[941,152860,119747],{}," or not:\nthe sealed-CEK host-delivery envelope purge (",[941,152863,119611],{},") and the blob shred\n(",[941,152866,152340],{},"). That makes an erasure complete even for a sealed value or blob attached to a\ncollection you never declared as a subject — at the cost of a full scan.",[937,152869,152870,152871,152873,152874,2311],{},"Opt into a narrower scan with ",[941,152872,111496],{},", a sibling of ",[941,152875,119747],{},[2217,152877,152879],{"className":2219,"code":152878,"language":2221,"meta":1037,"style":1037},"forgetStrategy: withForget({\n  subjects: { invoices: 'buyerId' },\n  scopedPurge: true,\n})\n",[941,152880,152881,152894,152915,152926],{"__ignoreMap":1037},[2225,152882,152883,152886,152888,152890,152892],{"class":2227,"line":2228},[2225,152884,152885],{"class":6342},"forgetStrategy",[2225,152887,2311],{"class":2235},[2225,152889,74041],{"class":2291},[2225,152891,2789],{"class":2239},[2225,152893,4200],{"class":2235},[2225,152895,152896,152899,152901,152903,152905,152907,152909,152911,152913],{"class":2227,"line":1041},[2225,152897,152898],{"class":2307},"  subjects",[2225,152900,2311],{"class":2235},[2225,152902,2236],{"class":2235},[2225,152904,6503],{"class":2307},[2225,152906,2311],{"class":2235},[2225,152908,2249],{"class":2235},[2225,152910,109007],{"class":2252},[2225,152912,2805],{"class":2235},[2225,152914,2353],{"class":2235},[2225,152916,152917,152920,152922,152924],{"class":2227,"line":1038},[2225,152918,152919],{"class":2307},"  scopedPurge",[2225,152921,2311],{"class":2235},[2225,152923,2322],{"class":2321},[2225,152925,1707],{"class":2235},[2225,152927,152928,152930],{"class":2227,"line":2288},[2225,152929,2359],{"class":2235},[2225,152931,2362],{"class":2239},[937,152933,15432,152934,152936,152937,152939,152940,152942,152943,152946,152947,2311],{},[941,152935,56931],{},", the sealed-CEK purge is limited to collections that declared\n",[941,152938,22770],{},", and the blob scan to collections that declared ",[941,152941,22785],{},". A collection that\ndeclared neither is ",[950,152944,152945],{},"skipped, never silently"," — each skip is reported on\n",[941,152948,108847],{},[2217,152950,152952],{"className":6604,"code":152951,"language":141,"meta":1037,"style":1037},"{\n  \"scopedPurgeResidue\": [\n    { \"reason\": \"skipped-undeclared-sealed-cek\", \"collection\": \"audit_log\", \"count\": 3 },\n    { \"reason\": \"skipped-undeclared-blob-scan\", \"collection\": \"attachments\", \"count\": 12 }\n  ]\n}\n",[941,152953,152954,152958,152970,153019,153067,153071],{"__ignoreMap":1037},[2225,152955,152956],{"class":2227,"line":2228},[2225,152957,4200],{"class":2235},[2225,152959,152960,152962,152964,152966,152968],{"class":2227,"line":1041},[2225,152961,9368],{"class":2235},[2225,152963,56927],{"class":2774},[2225,152965,6620],{"class":2235},[2225,152967,2311],{"class":2235},[2225,152969,32491],{"class":2235},[2225,152971,152972,152974,152976,152978,152980,152982,152984,152986,152988,152990,152992,152994,152996,152998,153000,153003,153005,153007,153009,153011,153013,153015,153017],{"class":2227,"line":1038},[2225,152973,32496],{"class":2235},[2225,152975,6614],{"class":2235},[2225,152977,43668],{"class":6342},[2225,152979,6620],{"class":2235},[2225,152981,2311],{"class":2235},[2225,152983,6614],{"class":2235},[2225,152985,56935],{"class":2252},[2225,152987,6620],{"class":2235},[2225,152989,2298],{"class":2235},[2225,152991,6614],{"class":2235},[2225,152993,6471],{"class":6342},[2225,152995,6620],{"class":2235},[2225,152997,2311],{"class":2235},[2225,152999,6614],{"class":2235},[2225,153001,153002],{"class":2252},"audit_log",[2225,153004,6620],{"class":2235},[2225,153006,2298],{"class":2235},[2225,153008,6614],{"class":2235},[2225,153010,3236],{"class":6342},[2225,153012,6620],{"class":2235},[2225,153014,2311],{"class":2235},[2225,153016,14460],{"class":6527},[2225,153018,2353],{"class":2235},[2225,153020,153021,153023,153025,153027,153029,153031,153033,153035,153037,153039,153041,153043,153045,153047,153049,153051,153053,153055,153057,153059,153061,153063,153065],{"class":2227,"line":2288},[2225,153022,32496],{"class":2235},[2225,153024,6614],{"class":2235},[2225,153026,43668],{"class":6342},[2225,153028,6620],{"class":2235},[2225,153030,2311],{"class":2235},[2225,153032,6614],{"class":2235},[2225,153034,56938],{"class":2252},[2225,153036,6620],{"class":2235},[2225,153038,2298],{"class":2235},[2225,153040,6614],{"class":2235},[2225,153042,6471],{"class":6342},[2225,153044,6620],{"class":2235},[2225,153046,2311],{"class":2235},[2225,153048,6614],{"class":2235},[2225,153050,78159],{"class":2252},[2225,153052,6620],{"class":2235},[2225,153054,2298],{"class":2235},[2225,153056,6614],{"class":2235},[2225,153058,3236],{"class":6342},[2225,153060,6620],{"class":2235},[2225,153062,2311],{"class":2235},[2225,153064,108590],{"class":6527},[2225,153066,6369],{"class":2235},[2225,153068,153069],{"class":2227,"line":2304},[2225,153070,110088],{"class":2235},[2225,153072,153073],{"class":2227,"line":2356},[2225,153074,7351],{"class":2235},[937,153076,153077,153079,153080,153082,153083,153085,153086,1261,153088,153090,153091,153093],{},[941,153078,3236],{}," aggregates the sealed-CEK envelopes left unpurged (or, for a blob scan, the records whose\nblobs went unscanned) for that collection. Leave ",[941,153081,56931],{}," off — the default — for the\nunconditional, always-purged behavior. The declaration signal is ",[950,153084,119732],{},": a collection\ncounts as \"declared\" only if its ",[941,153087,22770],{},[941,153089,22785],{}," are declared in the session\nrunning ",[941,153092,2542],{},", so run the erasure from a session that has loaded the full schema.",[1591,153095,153097],{"id":153096},"recover-the-subject-index-after-a-lost-write","Recover the subject index after a lost write",[937,153099,153100,153101,153103,153104,153106],{},"The subject index is read-modify-write with no compare-and-swap, so two concurrent writers\nracing on the ",[1092,153102,4664],{}," subject can lose one ref (last write wins). ",[941,153105,21432],{},"\nrecovers by rescanning every declared collection from the canonical records:",[2217,153108,153110],{"className":2219,"code":153109,"language":2221,"meta":1037,"style":1037},"const reindexed = await vault.rebuildSubjectIndex()\n\u002F\u002F → 2   (2 distinct subjects found across the declared collections)\n",[941,153111,153112,153132],{"__ignoreMap":1037},[2225,153113,153114,153116,153119,153121,153123,153125,153127,153130],{"class":2227,"line":2228},[2225,153115,2775],{"class":2774},[2225,153117,153118],{"class":2239}," reindexed ",[2225,153120,2781],{"class":2235},[2225,153122,2784],{"class":2231},[2225,153124,6466],{"class":2239},[2225,153126,1520],{"class":2235},[2225,153128,153129],{"class":2291},"rebuildSubjectIndex",[2225,153131,8146],{"class":2239},[2225,153133,153134],{"class":2227,"line":1041},[2225,153135,153136],{"class":2763},"\u002F\u002F → 2   (2 distinct subjects found across the declared collections)\n",[1022,153138,102889],{"id":102888},[1591,153140,153142],{"id":153141},"withforgetopts",[941,153143,153144],{},"withForget(opts)",[2217,153146,153148],{"className":2219,"code":153147,"language":2221,"meta":1037,"style":1037},"function withForget(opts: SubjectDeclaration): ForgetStrategy\n",[941,153149,153150],{"__ignoreMap":1037},[2225,153151,153152,153154,153156,153158,153160,153162,153165,153167],{"class":2227,"line":2228},[2225,153153,12641],{"class":2774},[2225,153155,74041],{"class":2291},[2225,153157,2789],{"class":2235},[2225,153159,8097],{"class":7052},[2225,153161,2311],{"class":2235},[2225,153163,153164],{"class":6342}," SubjectDeclaration",[2225,153166,6255],{"class":2235},[2225,153168,153169],{"class":6342}," ForgetStrategy\n",[937,153171,153172,153173,77939,153175,153177],{},"Builds the active forget strategy from a subject declaration. Pass it as ",[941,153174,152885],{},[941,153176,12780],{}," — this is the only wiring step.",[937,153179,153180,2311],{},[941,153181,153182],{},"SubjectDeclaration",[1338,153184,153185,153195],{},[1341,153186,153187],{},[1344,153188,153189,153191,153193],{},[1347,153190,101104],{},[1347,153192,20194],{},[1347,153194,7834],{},[1357,153196,153197,153217],{},[1344,153198,153199,153203,153208],{},[1362,153200,153201],{},[941,153202,119747],{},[1362,153204,153205],{},[941,153206,153207],{},"Record\u003Cstring, string>",[1362,153209,153210,153211,153214,153215,56028],{},"Maps a collection name to the record field (dotted path supported, e.g. ",[941,153212,153213],{},"'billing.buyerId'",") that identifies the data subject. Declaring a collection here forces ",[941,153216,73811],{},[1344,153218,153219,153223,153230],{},[1362,153220,153221],{},[941,153222,56931],{},[1362,153224,153225,153227,153228,1381],{},[941,153226,16861],{}," (optional, default ",[941,153229,2560],{},[1362,153231,20818,153232,153234,153235,1261,153237,153239,153240,153242,153243,1520],{},[941,153233,11790],{},", limits the sealed-CEK envelope purge and the blob scan to collections that declared ",[941,153236,22770],{},[941,153238,22785],{},"; undeclared collections are skipped and reported on ",[941,153241,108847],{},". Left off, both purges scan every collection unconditionally. See ",[1007,153244,153246],{"href":153245},"#scope-the-purge-with-scopedpurge","Scope the purge",[1591,153248,153250],{"id":153249},"vaultforgetsubjectid",[941,153251,21428],{},[2217,153253,153255],{"className":2219,"code":153254,"language":2221,"meta":1037,"style":1037},"vault.forget(subjectId: string): Promise\u003CForgetResult>\n",[941,153256,153257],{"__ignoreMap":1037},[2225,153258,153259,153261,153263,153265,153268,153270,153272,153274],{"class":2227,"line":2228},[2225,153260,2694],{"class":2239},[2225,153262,1520],{"class":2235},[2225,153264,685],{"class":2291},[2225,153266,153267],{"class":2239},"(subjectId: string): ",[2225,153269,11740],{"class":6342},[2225,153271,6474],{"class":2235},[2225,153273,76446],{"class":2239},[2225,153275,7093],{"class":2235},[937,153277,153278,153279,153281,153282,153285,153286,153288],{},"Looks up every record referencing ",[941,153280,119498],{}," in the encrypted subject index, tombstones\neach one's live body and full history, purges its index postings \u002F sealed-field \u002F blob\nside-cars, removes it from the subject index, and appends one ",[941,153283,153284],{},"op:'forget'"," entry to the\nledger (",[941,153287,22982],{}," is the SHA-256 of the subject id, so the chain proves the subject\nexisted and was erased without recording the subject itself).",[937,153290,16810,153291,102958,153293,153295,153296,153298],{},[941,153292,21773],{},[941,153294,152885],{}," was never set (or was left at\nthe default disabled sentinel). Throws if ",[941,153297,20172],{}," was not configured — the ledger\nentry is how the erasure is proven.",[937,153300,153301,2311],{},[941,153302,76446],{},[1338,153304,153305,153315],{},[1341,153306,153307],{},[1344,153308,153309,153311,153313],{},[1347,153310,101104],{},[1347,153312,20194],{},[1347,153314,20200],{},[1357,153316,153317,153330,153343,153356,153369,153385,153398,153411,153427,153440,153459,153475,153497,153512,153530,153555,153570,153585,153602,153630,153654,153675,153692,153712],{},[1344,153318,153319,153323,153327],{},[1362,153320,153321],{},[941,153322,57531],{},[1362,153324,153325],{},[941,153326,7259],{},[1362,153328,153329],{},"The subject id passed in, echoed back.",[1344,153331,153332,153336,153340],{},[1362,153333,153334],{},[941,153335,152276],{},[1362,153337,153338],{},[941,153339,20636],{},[1362,153341,153342],{},"Live records rewritten to a tombstone.",[1344,153344,153345,153349,153353],{},[1362,153346,153347],{},[941,153348,152291],{},[1362,153350,153351],{},[941,153352,20636],{},[1362,153354,153355],{},"History envelopes tombstoned across all shredded records.",[1344,153357,153358,153362,153366],{},[1362,153359,153360],{},[941,153361,2698],{},[1362,153363,153364],{},[941,153365,111053],{},[1362,153367,153368],{},"Distinct collections that had at least one record shredded.",[1344,153370,153371,153375,153379],{},[1362,153372,153373],{},[941,153374,152326],{},[1362,153376,153377],{},[941,153378,111053],{},[1362,153380,153381,153384],{},[941,153382,153383],{},"collection:id"," pairs shredded while their body was still keyed off the shared collection key (not yet migrated to a per-record CEK) — tombstoned, but pre-shred ciphertext that leaked into a backup before migration may still be decryptable. Migrate the record and re-forget to close the gap.",[1344,153386,153387,153391,153395],{},[1362,153388,153389],{},[941,153390,152340],{},[1362,153392,153393],{},[941,153394,20636],{},[1362,153396,153397],{},"Erasable blob attachments crypto-shredded (their own key destroyed).",[1344,153399,153400,153404,153408],{},[1362,153401,153402],{},[941,153403,152355],{},[1362,153405,153406],{},[941,153407,20636],{},[1362,153409,153410],{},"Erasable blobs kept because another record still references the same content.",[1344,153412,153413,153417,153421],{},[1362,153414,153415],{},[941,153416,152370],{},[1362,153418,153419],{},[941,153420,111053],{},[1362,153422,153423,153424,153426],{},"Collections with blobs that could ",[950,153425,1004],{}," be shredded — legacy blobs with no per-blob key, or a session with the blob service not loaded.",[1344,153428,153429,153433,153437],{},[1362,153430,153431],{},[941,153432,152383],{},[1362,153434,153435],{},[941,153436,20636],{},[1362,153438,153439],{},"Persisted lookup-index side-cars deleted for the shredded records.",[1344,153441,153442,153446,153450],{},[1362,153443,153444],{},[941,153445,152398],{},[1362,153447,153448],{},[941,153449,111053],{},[1362,153451,153452,153455,153456,153458],{},[941,153453,153454],{},"collection:id:field"," entries whose index side-car could ",[950,153457,1004],{}," be deleted — still leaks the indexed value.",[1344,153460,153461,153465,153469],{},[1362,153462,153463],{},[941,153464,119334],{},[1362,153466,153467],{},[941,153468,20636],{},[1362,153470,153471,153472,153474],{},"Sealed-field slots (see ",[1007,153473,633],{"href":634},") dropped because they were keyed off the per-record CEK.",[1344,153476,153477,153481,153485],{},[1362,153478,153479],{},[941,153480,119611],{},[1362,153482,153483],{},[941,153484,20636],{},[1362,153486,153487,153490,153491,153493,153494,153496],{},[1007,153488,153489],{"href":825},"Sealed-record"," host-delivery envelopes deleted — a record sealed to an ",[941,153492,1139],{}," host persists its raw CEK there, and ",[941,153495,2542],{}," destroys it too.",[1344,153498,153499,153503,153507],{},[1362,153500,153501],{},[941,153502,152439],{},[1362,153504,153505],{},[941,153506,111053],{},[1362,153508,153509,153511],{},[941,153510,153383],{}," pairs whose sealed-record host-delivery envelope purge failed.",[1344,153513,153514,153518,153522],{},[1362,153515,153516],{},[941,153517,119338],{},[1362,153519,153520],{},[941,153521,111053],{},[1362,153523,153524,153526,153527,153529],{},[941,153525,153454],{}," sealed slots that were keyed off the shared collection key (legacy) and so were ",[950,153528,1004],{}," crypto-shredded by the tombstone.",[1344,153531,153532,153536,153541],{},[1362,153533,153534],{},[941,153535,56927],{},[1362,153537,153538],{},[941,153539,153540],{},"readonly ScopedPurgeResidueNotice[]",[1362,153542,15432,153543,153545,153546,153549,153550,153552,153553,957],{},[941,153544,56931],{},", one entry per undeclared collection skipped by the sealed-CEK purge or blob scan: ",[941,153547,153548],{},"{ reason: 'skipped-undeclared-sealed-cek' | 'skipped-undeclared-blob-scan', collection, count }",". Empty when ",[941,153551,56931],{}," is off (since ",[941,153554,108831],{},[1344,153556,153557,153561,153565],{},[1362,153558,153559],{},[941,153560,152476],{},[1362,153562,153563],{},[941,153564,20636],{},[1362,153566,153567,153568,957],{},"Record-grain derived artifacts erased — materialized-view rows, derivation rows, same-id derived copies (since ",[941,153569,111464],{},[1344,153571,153572,153576,153580],{},[1362,153573,153574],{},[941,153575,152491],{},[1362,153577,153578],{},[941,153579,20636],{},[1362,153581,153582,153583,957],{},"Aggregate-grain rollup targets recomputed without the forgotten contribution (since ",[941,153584,111464],{},[1344,153586,153587,153591,153595],{},[1362,153588,153589],{},[941,153590,152506],{},[1362,153592,153593],{},[941,153594,111053],{},[1362,153596,153597,153599,153600,957],{},[941,153598,153383],{}," derived writes skipped because the target period is frozen — the aggregate temporarily retains the forgotten contribution, audited (since ",[941,153601,111464],{},[1344,153603,153604,153608,153612],{},[1362,153605,153606],{},[941,153607,56955],{},[1362,153609,153610],{},[941,153611,111053],{},[1362,153613,153614,153616,153617,153620,153621,153623,153624,153626,153627,153629],{},[941,153615,153383],{}," materialized-view output rows that survived erasure because their ",[941,153618,153619],{},"_materializedFrom"," ownership stamp could ",[950,153622,1004],{}," be decoded under the collection's default DEK (e.g. elevated above tier 0) — ownership ",[950,153625,56959],{},". Surfaced, never silently skipped (since ",[941,153628,56948],{},", #785).",[1344,153631,153632,153636,153640],{},[1362,153633,153634],{},[941,153635,56963],{},[1362,153637,153638],{},[941,153639,111053],{},[1362,153641,153642,153644,153645,153648,153649,153651,153652,153629],{},[941,153643,153383],{}," MV output rows that ",[950,153646,153647],{},"did"," decode and ownership-stamp-match, but whose erasure was declined by the tier-elevation gate — ownership ",[950,153650,56967],{},", a live tier-holder-decryptable copy deliberately retained (since ",[941,153653,56948],{},[1344,153655,153656,153660,153664],{},[1362,153657,153658],{},[941,153659,152543],{},[1362,153661,153662],{},[941,153663,20636],{},[1362,153665,153666,153667,153669,153670,153672,153673,957],{},"Referencing records tombstoned via a ",[1007,153668,731],{"href":732}," field's ",[941,153671,10753],{}," reference semantics (since ",[941,153674,111464],{},[1344,153676,153677,153681,153685],{},[1362,153678,153679],{},[941,153680,152558],{},[1362,153682,153683],{},[941,153684,20636],{},[1362,153686,153687,153688,153672,153690,957],{},"Referencing fields cleared via a lookup field's ",[941,153689,152838],{},[941,153691,111464],{},[1344,153693,153694,153698,153702],{},[1362,153695,153696],{},[941,153697,152573],{},[1362,153699,153700],{},[941,153701,111053],{},[1362,153703,153704,153705,1625,153707,153709,153710,957],{},"Lookup reference edges whose ",[941,153706,10753],{},[941,153708,152838],{}," propagation was skipped because the compare-key could not be resolved even from the live pre-shred backing row — reported, never silent (since ",[941,153711,111464],{},[1344,153713,153714,153718,153723],{},[1362,153715,153716],{},[941,153717,152586],{},[1362,153719,153720],{},[941,153721,153722],{},"LedgerEntry",[1362,153724,153725,153726,153728],{},"The single ",[941,153727,153284],{}," entry appended for this erasure.",[1591,153730,153732],{"id":153731},"vaultrebuildsubjectindex",[941,153733,21432],{},[2217,153735,153737],{"className":2219,"code":153736,"language":2221,"meta":1037,"style":1037},"vault.rebuildSubjectIndex(): Promise\u003Cnumber>\n",[941,153738,153739],{"__ignoreMap":1037},[2225,153740,153741,153743,153745,153747,153749,153751,153753,153755],{"class":2227,"line":2228},[2225,153742,2694],{"class":2239},[2225,153744,1520],{"class":2235},[2225,153746,153129],{"class":2291},[2225,153748,12809],{"class":2239},[2225,153750,11740],{"class":6342},[2225,153752,6474],{"class":2235},[2225,153754,20636],{"class":2239},[2225,153756,7093],{"class":2235},[937,153758,153759,153760,153762],{},"Drops and rebuilds the encrypted subject index from the canonical records across every\ndeclared collection. Returns the number of distinct subjects re-indexed. Tombstoned\n(already-shredded) records contribute nothing — their body is gone. Throws\n",[941,153761,21773],{}," under the disabled sentinel.",[1591,153764,126393],{"id":126392},[1111,153766,153767,153787,153795],{},[1114,153768,153769,153772,153773,153776,153777,2514,153779,10326,153781,153783,153784,153786],{},[941,153770,153771],{},"NO_FORGET"," — the disabled sentinel (",[941,153774,153775],{},"{ subjects: {} }","). Under it, ",[941,153778,2546],{},[941,153780,21432],{},[941,153782,21773],{},", no write hooks\nregister, and no collection is forced into ",[941,153785,14552],{}," — non-adopters pay nothing.",[1114,153788,153789,126401,153792,1520],{},[941,153790,153791],{},"SUBJECT_INDEX_COLLECTION",[941,153793,153794],{},"'_subject_index'",[1114,153796,126416,153797,968,153799,968,153802,968,153804,968,153807,1520],{},[941,153798,153182],{},[941,153800,153801],{},"ForgetStrategy",[941,153803,76446],{},[941,153805,153806],{},"ScopedPurgeResidueNotice",[941,153808,153809],{},"SubjectRef",[1022,153811,68210],{"id":68209},[1111,153813,153814,153826,153835,153844,153852,153863],{},[1114,153815,153816,1324,153820,153822,153823,153825],{},[950,153817,145486,153818,1520],{},[941,153819,6719],{},[941,153821,2542],{}," writes its erasure proof to the ledger; without a\nconfigured ",[941,153824,20172],{}," it throws instead of shredding.",[1114,153827,153828,153831,153832,153834],{},[950,153829,153830],{},"Single-writer subject index."," Index maintenance is read-modify-write with no\ncompare-and-swap — it assumes noy-db's normal single-process write model. Two writers\nracing on the same subject can lose a ref; ",[941,153833,21432],{}," recovers it from\nthe canonical records.",[1114,153836,153837,153840,153841,153843],{},[950,153838,153839],{},"Legacy (un-migrated) bodies are a completeness gap, not a failure."," A record whose body\nwas never migrated to a per-record CEK is still tombstoned, but its pre-shred ciphertext —\nif it reached a backup before migration — stays decryptable under the collection key. It's\nreported in ",[941,153842,152326],{},", not silently swallowed.",[1114,153845,153846,66018,153849,153851],{},[950,153847,153848],{},"Idempotent.",[941,153850,2542],{}," again for an already-erased subject shreds nothing and\ndoes not throw.",[1114,153853,153854,153859,153860,153862],{},[950,153855,153856,153857,2298],{},"A shredded record reads as ",[941,153858,6651],{}," not as a tamper error — ",[941,153861,14639],{}," on a tombstoned id\nbehaves like the record never existed.",[1114,153864,153865,153868,153869,153871,153872,153874,153875,104458,153877,153879,153880,1625,153882,153884],{},[950,153866,153867],{},"A referenced backing row refuses erasure first."," When the subject's record backs a declared\n",[1007,153870,731],{"href":732}," field with the default ",[941,153873,152822],{}," semantics, ",[941,153876,2542],{},[941,153878,152829],{}," before shredding anything — retire or re-point the referencing records\nfirst, or declare ",[941,153881,10753],{},[941,153883,152838],{}," on the lookup field.",[1022,153886,2621],{"id":2620},[1111,153888,153889,153895,153900,153908,153913,153923,153935,153940,153945,153950],{},[1114,153890,153891,1368,153893],{},[1007,153892,61743],{"href":598},[1007,153894,1019],{"href":1018},[1114,153896,153897,153899],{},[1007,153898,1516],{"href":66}," — per-record CEKs and the crypto-shred mechanism in full",[1114,153901,153902,153904,153905,153907],{},[1007,153903,697],{"href":698}," — the change ledger ",[941,153906,2542],{}," writes its erasure proof to",[1114,153909,153910,153912],{},[1007,153911,621],{"href":622}," — blob attachments and how their content keys are shredded",[1114,153914,153915,153917,153918,1261,153920,153922],{},[1007,153916,633],{"href":634}," — sealed fields and how ",[941,153919,119334],{},[941,153921,119338],{}," are classified",[1114,153924,153925,1169,153927,153929,153930,1261,153932,153934],{},[1007,153926,824],{"href":825},[941,153928,1139],{}," host delivery envelopes ",[941,153931,119611],{},[941,153933,152439],{}," refer to",[1114,153936,153937,153939],{},[1007,153938,864],{"href":865}," — how the shred propagates to peers, and why a tombstone is terminal",[1114,153941,153942,153944],{},[1007,153943,731],{"href":732}," — reference semantics that gate or propagate an erasure of a backing row",[1114,153946,153947,153949],{},[1007,153948,637],{"href":638}," — the dependency graph behind the derived-residue fan-out",[1114,153951,101581,153952,1520],{},[1007,153953,153955],{"href":94350,"rel":153954},[3371],[941,153956,153957],{},"130-with-forget",[2645,153959,153960],{},"html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}html pre.shiki code .sfNiH, html code.shiki .sfNiH{--shiki-light:#FF5370;--shiki-default:#FF9CAC;--shiki-dark:#FF9CAC}html pre.shiki code .sHdIc, html code.shiki .sHdIc{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#EEFFFF;--shiki-default-font-style:italic;--shiki-dark:#BABED8;--shiki-dark-font-style:italic}",{"title":1037,"searchDepth":1038,"depth":1038,"links":153962},[153963,153964,153965,153966,153973,153979,153980],{"id":29856,"depth":1041,"text":29857},{"id":70092,"depth":1041,"text":70093},{"id":99863,"depth":1041,"text":99864},{"id":29892,"depth":1041,"text":29893,"children":153967},[153968,153969,153970,153972],{"id":152016,"depth":1038,"text":152017},{"id":152729,"depth":1038,"text":152730},{"id":152849,"depth":1038,"text":153971},"Scope the purge with scopedPurge",{"id":153096,"depth":1038,"text":153097},{"id":102888,"depth":1041,"text":102889,"children":153974},[153975,153976,153977,153978],{"id":153141,"depth":1038,"text":153144},{"id":153249,"depth":1038,"text":21428},{"id":153731,"depth":1038,"text":21432},{"id":126392,"depth":1038,"text":126393},{"id":68209,"depth":1041,"text":68210},{"id":2620,"depth":1041,"text":2621},"GDPR-style right-to-erasure: `vault.forget(subjectId)` crypto-shreds every matching record — live body, history, side-cars — leaving a provable ledger entry.",{},{"title":685,"description":153981},"35sy89kpvgBoBHWYrVUaCfTCbHNfcixeXQHxiTIPcIM",{"id":153986,"title":689,"access":12,"apiToc":929,"body":153987,"category":1042,"description":154214,"extension":1044,"meta":154215,"navigation":1042,"nest":1047,"path":690,"seo":154216,"sinceVersion":1042,"status":1049,"stem":691,"subtitle":1042,"tier":1050,"tocExternal":1042,"__hash__":154217},"docs\u002Fservices\u002Fgovernance.md",{"type":931,"value":153988,"toc":154208},[153989,153991,153998,154001,154003,154017,154020,154024,154031,154050,154053,154066,154070,154073,154175,154177],[934,153990,689],{"id":689},[1061,153992,153993],{},[937,153994,153995],{},[950,153996,153997],{},"Personal-data governance across GDPR and PDPA — told once, then mapped.",[46871,153999],{":rows":154000},"[{\"label\":\"Status\",\"value\":\"reference — this page documents no API of its own; each right it names is implemented on the linked service page.\"},{\"label\":\"Cluster\",\"value\":\"time-and-audit\"},{\"label\":\"Cross-cuts\",\"value\":\"the [Enclave](\u002Fcore\u002Fnoydb\u002Fenclave) key hierarchy, the [forget](\u002Fservices\u002Fforget) erasure service, and the [consent](\u002Fservices\u002Fconsent) \u002F [portability](\u002Fservices\u002Fportability) \u002F [withdrawal](\u002Fservices\u002Fwithdrawal) rights services.\"},{\"label\":\"Companion\",\"value\":\"[deletion](\u002Fglossary\u002Fdeletion) — the vocabulary (erase vs. delete vs. tombstone) this page uses precisely.\"}]",[1022,154002,104601],{"id":104600},[937,154004,154005,154006,154009,154010,154013,154014,154016],{},"Data-protection regimes — the EU's ",[950,154007,154008],{},"GDPR"," and Thailand's ",[950,154011,154012],{},"PDPA"," among them — grant a\ndata subject a set of rights over their personal data: to have it erased, to see the record of\nwhat was consented to, to receive a portable copy, and to withdraw from further processing. This\npage tells the compliance story ",[950,154015,113219],{}," — how a zero-knowledge, encrypt-before-store vault\nsatisfies those rights structurally rather than by policy — and then maps each right to the\nservice page that implements it. It documents no API of its own.",[937,154018,154019],{},"The two regimes are named deliberately. GDPR (Articles 15–20 in particular) and PDPA share the\nsame core rights vocabulary; where an obligation is regime-specific it is called out on the\nrelevant service page rather than duplicated here.",[1022,154021,154023],{"id":154022},"crypto-shred-how-erasure-actually-happens","Crypto-shred: how erasure actually happens",[937,154025,154026,154027,154030],{},"In an encrypt-before-store design there is no privileged server that can reach into a backend and\nscrub a row — the store only ever holds ciphertext, and the keys live in the client's memory. So\nerasure is implemented cryptographically: destroy the key a record was encrypted under and the\nciphertext left behind is unrecoverable noise. This is ",[950,154028,154029],{},"crypto-shred",", and it is what makes the\nright to erasure enforceable against untrusted storage — an S3 bucket, a synced replica, a backup\ntape — that you cannot reach in to edit.",[937,154032,154033,154034,154037,154038,154042,154043,154046,154047,154049],{},"The unit that makes this per-subject rather than all-or-nothing is the ",[950,154035,154036],{},"per-record content key\n(CEK)",": with per-record keys enabled, each record body is encrypted under its own key, itself\nwrapped under the collection key, so destroying one record's wrapped CEK renders only that record\nundecryptable while every other record stays readable (the mechanism is described in full on\n",[1007,154039,154041],{"href":154040},"\u002Fcore\u002Fnoydb\u002Fenclave#per-record-keys-cek","Enclave → Per-record keys","). Erasing a\nsubject then means shredding the wrapped keys of exactly that subject's records and rewriting each\nenvelope as a ",[950,154044,154045],{},"tombstone"," — ciphertext and wrapped key stripped — so the record reads as\npermanently gone rather than merely hidden. The ",[1007,154048,685],{"href":686}," service performs this\ncascade and emits a tamper-evident ledger proof of it.",[937,154051,154052],{},"To find a subject's records without decrypting the whole vault, forget maintains a reserved index,\nand that index is itself built to leak as little as possible:",[1061,154054,154055],{},[937,154056,154057,154058,154061,154062,154065],{},"A reserved ",[941,154059,154060],{},"_subject_index"," collection (own DEK) maps each subject to its ",[941,154063,154064],{},"[{collection, id}]","\nrefs. The entry id is an HMAC-SHA256 of the subject id keyed by the index DEK — so someone with\nstore access cannot dictionary-test whether a known subject is present — and the entry body is\nencrypted and length-padded so the ciphertext does not reveal the record count.",[1022,154067,154069],{"id":154068},"mapping-the-rights","Mapping the rights",[937,154071,154072],{},"Each data-subject right maps to one implementing surface. Follow the link for the API, the\nguarantees, and the gotchas; this table is only the index.",[1338,154074,154075,154088],{},[1341,154076,154077],{},[1344,154078,154079,154082,154085],{},[1347,154080,154081],{},"Obligation (GDPR \u002F PDPA)",[1347,154083,154084],{},"noy-db mechanism",[1347,154086,154087],{},"Where it's documented",[1357,154089,154090,154105,154119,154133,154147,154161],{},[1344,154091,154092,154098,154101],{},[1362,154093,154094,154097],{},[950,154095,154096],{},"Right to erasure"," (\"right to be forgotten\")",[1362,154099,154100],{},"Crypto-shred cascade over a subject's records, with a ledger proof of erasure",[1362,154102,154103],{},[1007,154104,685],{"href":686},[1344,154106,154107,154112,154115],{},[1362,154108,154109],{},[950,154110,154111],{},"Record of consent",[1362,154113,154114],{},"Per-operation consent audit trail written while a consent scope is active",[1362,154116,154117],{},[1007,154118,641],{"href":642},[1344,154120,154121,154126,154129],{},[1362,154122,154123],{},[950,154124,154125],{},"Right to data portability",[1362,154127,154128],{},"Export of a subject's accessible data as a portable artefact",[1362,154130,154131],{},[1007,154132,800],{"href":801},[1344,154134,154135,154140,154143],{},[1362,154136,154137],{},[950,154138,154139],{},"Right to withdraw \u002F object",[1362,154141,154142],{},"Unilateral or approval-gated withdrawal of a member from a vault",[1362,154144,154145],{},[1007,154146,900],{"href":901},[1344,154148,154149,154153,154156],{},[1362,154150,154151],{},[950,154152,487],{},[1362,154154,154155],{},"Precise terms — erase vs. delete vs. tombstone vs. crypto-shred",[1362,154157,154158],{},[1007,154159,154160],{"href":488},"deletion",[1344,154162,154163,154168,154171],{},[1362,154164,154165],{},[950,154166,154167],{},"What is protected, and from whom",[1362,154169,154170],{},"The vault's threat model: what the store can and cannot observe",[1362,154172,154173],{},[1007,154174,13661],{"href":464},[1022,154176,2621],{"id":2620},[1111,154178,154179,154184,154193,154198,154203],{},[1114,154180,154181,154183],{},[1007,154182,685],{"href":686}," — the erasure cascade and its ledger proof.",[1114,154185,154186,1368,154188,1368,154190,154192],{},[1007,154187,641],{"href":642},[1007,154189,800],{"href":801},[1007,154191,900],{"href":901}," — the remaining rights services.",[1114,154194,154195,154197],{},[1007,154196,21],{"href":66}," — the per-record CEK and tombstone mechanics crypto-shred relies on.",[1114,154199,154200,154202],{},[1007,154201,154160],{"href":488}," — the deletion vocabulary used precisely above.",[1114,154204,154205,154207],{},[1007,154206,13661],{"href":464}," — what a storage backend can and cannot learn.",{"title":1037,"searchDepth":1038,"depth":1038,"links":154209},[154210,154211,154212,154213],{"id":104600,"depth":1041,"text":104601},{"id":154022,"depth":1041,"text":154023},{"id":154068,"depth":1041,"text":154069},{"id":2620,"depth":1041,"text":2621},"Reference map for personal-data governance under GDPR and PDPA — crypto-shred as the erasure mechanism, and where each data-subject right is implemented.",{},{"title":689,"description":154214},"OPcmLkCHYqifQpUE-n2QsRjgNX5NT7qskaYfPsMXwgk",{"id":154219,"title":693,"access":12,"apiToc":929,"body":154220,"category":1042,"description":158382,"extension":1044,"meta":158383,"navigation":1042,"nest":1047,"path":694,"seo":158384,"sinceVersion":1042,"status":1049,"stem":695,"subtitle":1042,"tier":1050,"tocExternal":1042,"__hash__":158385},"docs\u002Fservices\u002Fguards.md",{"type":931,"value":154221,"toc":158350},[154222,154224,154227,154229,154251,154263,154265,154285,154287,154316,154318,154914,154916,154920,154935,155039,155043,155061,155368,155372,155396,155601,155612,155614,155617,155703,155723,155729,155735,155775,155779,155791,156220,156237,156246,156275,156618,156624,156630,156646,157041,157109,157113,157119,157121,157134,157140,157142,157150,157187,157189,157228,157230,157255,157257,157345,157347,157361,157367,157463,157500,157960,157970,157974,158250,158257,158261,158312,158314,158347],[934,154223,693],{"id":693},[46871,154225],{":rows":154226},"[{\"label\":\"Subpath\",\"value\":\"`@noy-db\u002Fhub\u002Fguards`\"},{\"label\":\"Factory\",\"value\":\"`withGuard()`\"},{\"label\":\"Layer\",\"value\":\"`with-audit`\"},{\"label\":\"Cluster\",\"value\":\"E — Time & Audit\"},{\"label\":\"LOC cost\",\"value\":\"~700 (off-bundle when not opted in)\"}]",[1022,154228,29857],{"id":29856},[937,154230,154231,154233,154234,154237,154238,154240,154241,154244,154245,154247,154248,1520],{},[941,154232,693],{}," lets you say \"this record can't be written (or deleted) unless a\nbusiness rule allows it\" — a per-record ",[950,154235,154236],{},"lock",", a per-field ",[950,154239,56994],{},", and\na controlled ",[950,154242,154243],{},"amendment"," path for the rare case where a locked record must\nlegitimately change. It's the layer above a single permission bit: permissions\nsay ",[1092,154246,84704],{}," can write a collection, guards say ",[1092,154249,154250],{},"whether this specific write is\nallowed right now",[937,154252,154253,154254,154257,154258,154262],{},"A guard's callbacks run on the decrypted record — after the\n",[1007,154255,154256],{"href":60250},"per-collection encryption key"," is unwrapped, before\nthe plaintext is re-encrypted for storage, and before any\n",[1007,154259,154261],{"href":154260},".\u002Fperiods","period close"," is checked — the storage backend never sees\nplaintext either way.",[1022,154264,111660],{"id":111659},[1111,154266,154267,154270,154279,154282],{},[1114,154268,154269],{},"Accounting \u002F financial books where issued invoices must not change",[1114,154271,154272,154273,154275,154276,1381],{},"Workflows that freeze specific fields after a status transition (e.g.\n",[941,154274,6617],{}," immutable once ",[941,154277,154278],{},"status === 'paid'",[1114,154280,154281],{},"Audit-tracked corrections that must preserve an invariant (totals,\nbalanced double-entry pairs)",[1114,154283,154284],{},"Any domain where a single permission bit (\"can write\") is too coarse",[1022,154286,70093],{"id":70092},[1111,154288,154289,154298],{},[1114,154290,154291,953,154294,154297],{},[950,154292,154293],{},"Guard declarations per collection",[941,154295,154296],{},"guardStrategies",") — the factory enables enforcement; each collection declares its own rules.",[1114,154299,154300,1324,154303,154306,154307,968,154309,4765,154311,1324,154313,154315],{},[1092,154301,154302],{},"For amendment mode:",[941,154304,154305],{},"transactionsStrategy: withTransactions()"," is required, plus ",[941,154308,9901],{},[941,154310,9898],{},[941,154312,9904],{},[1007,154314,16785],{"href":54}," on every vault the amendment touches.",[1022,154317,99864],{"id":99863},[2217,154319,154321],{"className":2219,"code":154320,"language":2221,"meta":1037,"style":1037},"import { createNoydb } from '@noy-db\u002Fhub'\nimport { toMemory } from '@noy-db\u002Fto-memory'\nimport { withGuard, RecordLockedError, InvariantError } from '@noy-db\u002Fhub\u002Fguards'\nimport { withTransactions } from '@noy-db\u002Fhub\u002Ftransactions'\n\nconst disbursementGuard = withGuard\u003CDisbursement>({\n  collection: 'disbursements',\n  check: async (incoming, { vault }) => {\n    const inv = await vault.collection\u003CInvoice>('invoices').get(incoming.invoiceId)\n    if (inv?.status === 'issued') {\n      throw new RecordLockedError('disbursements', incoming.id, 'invoice is issued')\n    }\n  },\n  amendment: {\n    roles: ['admin', 'owner'],\n    invariant: (changes) => {\n      const sum = (s: 'before' | 'after') =>\n        changes.reduce((t, c) => t + ((c[s])?.amount ?? 0), 0)\n      if (sum('before') !== sum('after')) {\n        throw new InvariantError('total must be preserved')\n      }\n    },\n  },\n})\n\nconst db = await createNoydb({\n  store: toMemory(),\n  user: 'alice',\n  guardStrategies: [disbursementGuard],\n  transactionsStrategy: withTransactions(), \u002F\u002F required for amendment mode\n})\n\nconst vault = await db.openVault('books')\n",[941,154322,154323,154341,154359,154388,154408,154412,154434,154449,154475,154522,154546,154583,154587,154591,154600,154627,154645,154677,154733,154767,154786,154790,154794,154798,154804,154808,154824,154836,154850,154862,154878,154884,154888],{"__ignoreMap":1037},[2225,154324,154325,154327,154329,154331,154333,154335,154337,154339],{"class":2227,"line":2228},[2225,154326,2232],{"class":2231},[2225,154328,2236],{"class":2235},[2225,154330,2744],{"class":2239},[2225,154332,2243],{"class":2235},[2225,154334,2246],{"class":2231},[2225,154336,2249],{"class":2235},[2225,154338,1086],{"class":2252},[2225,154340,2256],{"class":2235},[2225,154342,154343,154345,154347,154349,154351,154353,154355,154357],{"class":2227,"line":1041},[2225,154344,2232],{"class":2231},[2225,154346,2236],{"class":2235},[2225,154348,68576],{"class":2239},[2225,154350,2243],{"class":2235},[2225,154352,2246],{"class":2231},[2225,154354,2249],{"class":2235},[2225,154356,9638],{"class":2252},[2225,154358,2256],{"class":2235},[2225,154360,154361,154363,154365,154368,154370,154373,154375,154378,154380,154382,154384,154386],{"class":2227,"line":1038},[2225,154362,2232],{"class":2231},[2225,154364,2236],{"class":2235},[2225,154366,154367],{"class":2239}," withGuard",[2225,154369,2298],{"class":2235},[2225,154371,154372],{"class":2239}," RecordLockedError",[2225,154374,2298],{"class":2235},[2225,154376,154377],{"class":2239}," InvariantError",[2225,154379,2243],{"class":2235},[2225,154381,2246],{"class":2231},[2225,154383,2249],{"class":2235},[2225,154385,73216],{"class":2252},[2225,154387,2256],{"class":2235},[2225,154389,154390,154392,154394,154397,154399,154401,154403,154406],{"class":2227,"line":2288},[2225,154391,2232],{"class":2231},[2225,154393,2236],{"class":2235},[2225,154395,154396],{"class":2239}," withTransactions",[2225,154398,2243],{"class":2235},[2225,154400,2246],{"class":2231},[2225,154402,2249],{"class":2235},[2225,154404,154405],{"class":2252},"@noy-db\u002Fhub\u002Ftransactions",[2225,154407,2256],{"class":2235},[2225,154409,154410],{"class":2227,"line":2304},[2225,154411,2285],{"emptyLinePlaceholder":2284},[2225,154413,154414,154416,154419,154421,154423,154425,154428,154430,154432],{"class":2227,"line":2356},[2225,154415,2775],{"class":2774},[2225,154417,154418],{"class":2239}," disbursementGuard ",[2225,154420,2781],{"class":2235},[2225,154422,154367],{"class":2291},[2225,154424,6474],{"class":2235},[2225,154426,154427],{"class":6342},"Disbursement",[2225,154429,6480],{"class":2235},[2225,154431,2789],{"class":2239},[2225,154433,4200],{"class":2235},[2225,154435,154436,154438,154440,154442,154445,154447],{"class":2227,"line":2830},[2225,154437,30275],{"class":2307},[2225,154439,2311],{"class":2235},[2225,154441,2249],{"class":2235},[2225,154443,154444],{"class":2252},"disbursements",[2225,154446,2805],{"class":2235},[2225,154448,1707],{"class":2235},[2225,154450,154451,154454,154456,154458,154460,154463,154465,154467,154469,154471,154473],{"class":2227,"line":2861},[2225,154452,154453],{"class":2291},"  check",[2225,154455,2311],{"class":2235},[2225,154457,38717],{"class":2774},[2225,154459,953],{"class":2235},[2225,154461,154462],{"class":7052},"incoming",[2225,154464,2298],{"class":2235},[2225,154466,2236],{"class":2235},[2225,154468,6466],{"class":7052},[2225,154470,43776],{"class":2235},[2225,154472,8116],{"class":2774},[2225,154474,2301],{"class":2235},[2225,154476,154477,154479,154481,154483,154485,154487,154489,154491,154493,154495,154497,154499,154501,154503,154505,154507,154509,154511,154513,154515,154517,154520],{"class":2227,"line":4246},[2225,154478,80048],{"class":2774},[2225,154480,27000],{"class":2239},[2225,154482,6346],{"class":2235},[2225,154484,2784],{"class":2231},[2225,154486,6466],{"class":2239},[2225,154488,1520],{"class":2235},[2225,154490,6471],{"class":2291},[2225,154492,6474],{"class":2235},[2225,154494,6477],{"class":6342},[2225,154496,6480],{"class":2235},[2225,154498,2789],{"class":2307},[2225,154500,2805],{"class":2235},[2225,154502,6487],{"class":2252},[2225,154504,2805],{"class":2235},[2225,154506,1381],{"class":2307},[2225,154508,1520],{"class":2235},[2225,154510,3158],{"class":2291},[2225,154512,2789],{"class":2307},[2225,154514,154462],{"class":2239},[2225,154516,1520],{"class":2235},[2225,154518,154519],{"class":2239},"invoiceId",[2225,154521,2362],{"class":2307},[2225,154523,154524,154526,154528,154530,154532,154534,154536,154538,154540,154542,154544],{"class":2227,"line":4253},[2225,154525,80758],{"class":2231},[2225,154527,953],{"class":2307},[2225,154529,26995],{"class":2239},[2225,154531,8457],{"class":2235},[2225,154533,6631],{"class":2239},[2225,154535,9025],{"class":2235},[2225,154537,2249],{"class":2235},[2225,154539,100134],{"class":2252},[2225,154541,2805],{"class":2235},[2225,154543,8495],{"class":2307},[2225,154545,4200],{"class":2235},[2225,154547,154548,154551,154553,154555,154557,154559,154561,154563,154565,154568,154570,154572,154574,154576,154579,154581],{"class":2227,"line":4259},[2225,154549,154550],{"class":2231},"      throw",[2225,154552,8130],{"class":2235},[2225,154554,154372],{"class":2291},[2225,154556,2789],{"class":2307},[2225,154558,2805],{"class":2235},[2225,154560,154444],{"class":2252},[2225,154562,2805],{"class":2235},[2225,154564,2298],{"class":2235},[2225,154566,154567],{"class":2239}," incoming",[2225,154569,1520],{"class":2235},[2225,154571,6983],{"class":2239},[2225,154573,2298],{"class":2235},[2225,154575,2249],{"class":2235},[2225,154577,154578],{"class":2252},"invoice is issued",[2225,154580,2805],{"class":2235},[2225,154582,2362],{"class":2307},[2225,154584,154585],{"class":2227,"line":6548},[2225,154586,60103],{"class":2235},[2225,154588,154589],{"class":2227,"line":6576},[2225,154590,19017],{"class":2235},[2225,154592,154593,154596,154598],{"class":2227,"line":6582},[2225,154594,154595],{"class":2307},"  amendment",[2225,154597,2311],{"class":2235},[2225,154599,2301],{"class":2235},[2225,154601,154602,154605,154607,154609,154611,154613,154615,154617,154619,154621,154623,154625],{"class":2227,"line":6587},[2225,154603,154604],{"class":2307},"    roles",[2225,154606,2311],{"class":2235},[2225,154608,8667],{"class":2239},[2225,154610,2805],{"class":2235},[2225,154612,9901],{"class":2252},[2225,154614,2805],{"class":2235},[2225,154616,2298],{"class":2235},[2225,154618,2249],{"class":2235},[2225,154620,9898],{"class":2252},[2225,154622,2805],{"class":2235},[2225,154624,8803],{"class":2239},[2225,154626,1707],{"class":2235},[2225,154628,154629,154632,154634,154636,154639,154641,154643],{"class":2227,"line":7174},[2225,154630,154631],{"class":2291},"    invariant",[2225,154633,2311],{"class":2235},[2225,154635,953],{"class":2235},[2225,154637,154638],{"class":7052},"changes",[2225,154640,1381],{"class":2235},[2225,154642,8116],{"class":2774},[2225,154644,2301],{"class":2235},[2225,154646,154647,154649,154651,154653,154655,154657,154659,154661,154663,154665,154667,154669,154671,154673,154675],{"class":2227,"line":7180},[2225,154648,8424],{"class":2774},[2225,154650,27298],{"class":2239},[2225,154652,6346],{"class":2235},[2225,154654,953],{"class":2235},[2225,154656,123092],{"class":7052},[2225,154658,2311],{"class":2235},[2225,154660,2249],{"class":2235},[2225,154662,11357],{"class":2252},[2225,154664,2805],{"class":2235},[2225,154666,7087],{"class":2235},[2225,154668,2249],{"class":2235},[2225,154670,10761],{"class":2252},[2225,154672,2805],{"class":2235},[2225,154674,1381],{"class":2235},[2225,154676,71229],{"class":2774},[2225,154678,154679,154682,154684,154686,154688,154690,154692,154694,154696,154698,154700,154703,154706,154709,154711,154713,154715,154717,154719,154721,154723,154725,154727,154729,154731],{"class":2227,"line":7220},[2225,154680,154681],{"class":2239},"        changes",[2225,154683,1520],{"class":2235},[2225,154685,804],{"class":2291},[2225,154687,2789],{"class":2307},[2225,154689,2789],{"class":2235},[2225,154691,13267],{"class":7052},[2225,154693,2298],{"class":2235},[2225,154695,8169],{"class":7052},[2225,154697,1381],{"class":2235},[2225,154699,8116],{"class":2774},[2225,154701,154702],{"class":2239}," t",[2225,154704,154705],{"class":2235}," +",[2225,154707,154708],{"class":2307}," ((",[2225,154710,8200],{"class":2239},[2225,154712,8885],{"class":2307},[2225,154714,123092],{"class":2239},[2225,154716,28459],{"class":2307},[2225,154718,8457],{"class":2235},[2225,154720,6617],{"class":2239},[2225,154722,8239],{"class":2235},[2225,154724,8464],{"class":6527},[2225,154726,1381],{"class":2307},[2225,154728,2298],{"class":2235},[2225,154730,8464],{"class":6527},[2225,154732,2362],{"class":2307},[2225,154734,154735,154737,154739,154741,154743,154745,154747,154749,154751,154753,154755,154757,154759,154761,154763,154765],{"class":2227,"line":7225},[2225,154736,8472],{"class":2231},[2225,154738,953],{"class":2307},[2225,154740,28196],{"class":2291},[2225,154742,2789],{"class":2307},[2225,154744,2805],{"class":2235},[2225,154746,11357],{"class":2252},[2225,154748,2805],{"class":2235},[2225,154750,8495],{"class":2307},[2225,154752,32545],{"class":2235},[2225,154754,27298],{"class":2291},[2225,154756,2789],{"class":2307},[2225,154758,2805],{"class":2235},[2225,154760,10761],{"class":2252},[2225,154762,2805],{"class":2235},[2225,154764,7620],{"class":2307},[2225,154766,4200],{"class":2235},[2225,154768,154769,154771,154773,154775,154777,154779,154782,154784],{"class":2227,"line":7231},[2225,154770,8502],{"class":2231},[2225,154772,8130],{"class":2235},[2225,154774,154377],{"class":2291},[2225,154776,2789],{"class":2307},[2225,154778,2805],{"class":2235},[2225,154780,154781],{"class":2252},"total must be preserved",[2225,154783,2805],{"class":2235},[2225,154785,2362],{"class":2307},[2225,154787,154788],{"class":2227,"line":7267},[2225,154789,8518],{"class":2235},[2225,154791,154792],{"class":2227,"line":7272},[2225,154793,8324],{"class":2235},[2225,154795,154796],{"class":2227,"line":7278},[2225,154797,19017],{"class":2235},[2225,154799,154800,154802],{"class":2227,"line":7303},[2225,154801,2359],{"class":2235},[2225,154803,2362],{"class":2239},[2225,154805,154806],{"class":2227,"line":7308},[2225,154807,2285],{"emptyLinePlaceholder":2284},[2225,154809,154810,154812,154814,154816,154818,154820,154822],{"class":2227,"line":7314},[2225,154811,2775],{"class":2774},[2225,154813,2778],{"class":2239},[2225,154815,2781],{"class":2235},[2225,154817,2784],{"class":2231},[2225,154819,2744],{"class":2291},[2225,154821,2789],{"class":2239},[2225,154823,4200],{"class":2235},[2225,154825,154826,154828,154830,154832,154834],{"class":2227,"line":7348},[2225,154827,15608],{"class":2307},[2225,154829,2311],{"class":2235},[2225,154831,68576],{"class":2291},[2225,154833,4228],{"class":2239},[2225,154835,1707],{"class":2235},[2225,154837,154838,154840,154842,154844,154846,154848],{"class":2227,"line":8613},[2225,154839,4205],{"class":2307},[2225,154841,2311],{"class":2235},[2225,154843,2249],{"class":2235},[2225,154845,6408],{"class":2252},[2225,154847,2805],{"class":2235},[2225,154849,1707],{"class":2235},[2225,154851,154852,154855,154857,154860],{"class":2227,"line":8633},[2225,154853,154854],{"class":2307},"  guardStrategies",[2225,154856,2311],{"class":2235},[2225,154858,154859],{"class":2239}," [disbursementGuard]",[2225,154861,1707],{"class":2235},[2225,154863,154864,154867,154869,154871,154873,154875],{"class":2227,"line":8662},[2225,154865,154866],{"class":2307},"  transactionsStrategy",[2225,154868,2311],{"class":2235},[2225,154870,154396],{"class":2291},[2225,154872,4228],{"class":2239},[2225,154874,2298],{"class":2235},[2225,154876,154877],{"class":2763}," \u002F\u002F required for amendment mode\n",[2225,154879,154880,154882],{"class":2227,"line":8747},[2225,154881,2359],{"class":2235},[2225,154883,2362],{"class":2239},[2225,154885,154886],{"class":2227,"line":8752},[2225,154887,2285],{"emptyLinePlaceholder":2284},[2225,154889,154890,154892,154894,154896,154898,154900,154902,154904,154906,154908,154910,154912],{"class":2227,"line":8768},[2225,154891,2775],{"class":2774},[2225,154893,2835],{"class":2239},[2225,154895,2781],{"class":2235},[2225,154897,2784],{"class":2231},[2225,154899,2842],{"class":2239},[2225,154901,1520],{"class":2235},[2225,154903,2847],{"class":2291},[2225,154905,2789],{"class":2239},[2225,154907,2805],{"class":2235},[2225,154909,65761],{"class":2252},[2225,154911,2805],{"class":2235},[2225,154913,2362],{"class":2239},[1022,154915,29893],{"id":29892},[1591,154917,154919],{"id":154918},"a-locked-write-is-rejected","A locked write is rejected",[937,154921,960,154922,154925,154926,154928,154929,154931,154932,154934],{},[941,154923,154924],{},"check"," callback above runs on every ",[941,154927,20953],{},". If ",[941,154930,6514],{}," is already\n",[941,154933,100134],{},", the disbursement is locked and the write throws before it ever\nreaches encryption or storage:",[2217,154936,154938],{"className":2219,"code":154937,"language":2221,"meta":1037,"style":1037},"await vault.collection\u003CDisbursement>('disbursements').put('d1', {\n  id: 'd1', invoiceId: 'inv-1', amount: 100,\n})\n\u002F\u002F → throws RecordLockedError:\n\u002F\u002F \"Cannot modify disbursements\u002Fd1 — locked by guard: invoice is issued.\n\u002F\u002F  Use withTransactions({ amendment: true, reason }) with admin\u002Fowner role to override.\"\n",[941,154939,154940,154983,155018,155024,155029,155034],{"__ignoreMap":1037},[2225,154941,154942,154944,154946,154948,154950,154952,154954,154956,154958,154960,154962,154964,154966,154968,154970,154972,154974,154977,154979,154981],{"class":2227,"line":2228},[2225,154943,6500],{"class":2231},[2225,154945,6466],{"class":2239},[2225,154947,1520],{"class":2235},[2225,154949,6471],{"class":2291},[2225,154951,6474],{"class":2235},[2225,154953,154427],{"class":6342},[2225,154955,6480],{"class":2235},[2225,154957,2789],{"class":2239},[2225,154959,2805],{"class":2235},[2225,154961,154444],{"class":2252},[2225,154963,2805],{"class":2235},[2225,154965,1381],{"class":2239},[2225,154967,1520],{"class":2235},[2225,154969,3161],{"class":2291},[2225,154971,2789],{"class":2239},[2225,154973,2805],{"class":2235},[2225,154975,154976],{"class":2252},"d1",[2225,154978,2805],{"class":2235},[2225,154980,2298],{"class":2235},[2225,154982,2301],{"class":2235},[2225,154984,154985,154987,154989,154991,154993,154995,154997,155000,155002,155004,155006,155008,155010,155012,155014,155016],{"class":2227,"line":1041},[2225,154986,28371],{"class":2307},[2225,154988,2311],{"class":2235},[2225,154990,2249],{"class":2235},[2225,154992,154976],{"class":2252},[2225,154994,2805],{"class":2235},[2225,154996,2298],{"class":2235},[2225,154998,154999],{"class":2307}," invoiceId",[2225,155001,2311],{"class":2235},[2225,155003,2249],{"class":2235},[2225,155005,6514],{"class":2252},[2225,155007,2805],{"class":2235},[2225,155009,2298],{"class":2235},[2225,155011,6351],{"class":2307},[2225,155013,2311],{"class":2235},[2225,155015,6528],{"class":6527},[2225,155017,1707],{"class":2235},[2225,155019,155020,155022],{"class":2227,"line":1038},[2225,155021,2359],{"class":2235},[2225,155023,2362],{"class":2239},[2225,155025,155026],{"class":2227,"line":2288},[2225,155027,155028],{"class":2763},"\u002F\u002F → throws RecordLockedError:\n",[2225,155030,155031],{"class":2227,"line":2304},[2225,155032,155033],{"class":2763},"\u002F\u002F \"Cannot modify disbursements\u002Fd1 — locked by guard: invoice is issued.\n",[2225,155035,155036],{"class":2227,"line":2356},[2225,155037,155038],{"class":2763},"\u002F\u002F  Use withTransactions({ amendment: true, reason }) with admin\u002Fowner role to override.\"\n",[1591,155040,155042],{"id":155041},"a-frozen-field-is-rejected","A frozen field is rejected",[937,155044,155045,155048,155049,155051,155052,155054,155055,155058,155059,6255],{},[941,155046,155047],{},"frozenFields"," locks specific fields once a condition on the existing\nrecord becomes true — here, ",[941,155050,6617],{}," can no longer change once an invoice\nis ",[941,155053,6539],{},". Registered the same way as ",[941,155056,155057],{},"disbursementGuard"," above (add it to\n",[941,155060,154296],{},[2217,155062,155064],{"className":2219,"code":155063,"language":2221,"meta":1037,"style":1037},"import { withGuard, FieldFrozenError } from '@noy-db\u002Fhub\u002Fguards'\n\nconst invoiceGuard = withGuard\u003CInvoice>({\n  collection: 'invoices',\n  frozenFields: {\n    when: (existing) => existing.status === 'paid',\n    fields: ['amount'],\n  },\n})\n\nawait vault.collection\u003CInvoice>('invoices').put('inv-2', { id: 'inv-2', status: 'paid', amount: 500 })\nawait vault.collection\u003CInvoice>('invoices').put('inv-2', { id: 'inv-2', status: 'paid', amount: 600 })\n\u002F\u002F → throws FieldFrozenError:\n\u002F\u002F \"Cannot change frozen field(s) on invoices\u002Finv-2: amount.\n\u002F\u002F  Use withTransactions({ amendment: true, reason }) with admin\u002Fowner role to override.\"\n",[941,155065,155066,155089,155093,155114,155128,155137,155168,155187,155191,155197,155201,155277,155354,155359,155364],{"__ignoreMap":1037},[2225,155067,155068,155070,155072,155074,155076,155079,155081,155083,155085,155087],{"class":2227,"line":2228},[2225,155069,2232],{"class":2231},[2225,155071,2236],{"class":2235},[2225,155073,154367],{"class":2239},[2225,155075,2298],{"class":2235},[2225,155077,155078],{"class":2239}," FieldFrozenError",[2225,155080,2243],{"class":2235},[2225,155082,2246],{"class":2231},[2225,155084,2249],{"class":2235},[2225,155086,73216],{"class":2252},[2225,155088,2256],{"class":2235},[2225,155090,155091],{"class":2227,"line":1041},[2225,155092,2285],{"emptyLinePlaceholder":2284},[2225,155094,155095,155097,155100,155102,155104,155106,155108,155110,155112],{"class":2227,"line":1038},[2225,155096,2775],{"class":2774},[2225,155098,155099],{"class":2239}," invoiceGuard ",[2225,155101,2781],{"class":2235},[2225,155103,154367],{"class":2291},[2225,155105,6474],{"class":2235},[2225,155107,6477],{"class":6342},[2225,155109,6480],{"class":2235},[2225,155111,2789],{"class":2239},[2225,155113,4200],{"class":2235},[2225,155115,155116,155118,155120,155122,155124,155126],{"class":2227,"line":2288},[2225,155117,30275],{"class":2307},[2225,155119,2311],{"class":2235},[2225,155121,2249],{"class":2235},[2225,155123,6487],{"class":2252},[2225,155125,2805],{"class":2235},[2225,155127,1707],{"class":2235},[2225,155129,155130,155133,155135],{"class":2227,"line":2304},[2225,155131,155132],{"class":2307},"  frozenFields",[2225,155134,2311],{"class":2235},[2225,155136,2301],{"class":2235},[2225,155138,155139,155142,155144,155146,155148,155150,155152,155154,155156,155158,155160,155162,155164,155166],{"class":2227,"line":2356},[2225,155140,155141],{"class":2291},"    when",[2225,155143,2311],{"class":2235},[2225,155145,953],{"class":2235},[2225,155147,80763],{"class":7052},[2225,155149,1381],{"class":2235},[2225,155151,8116],{"class":2774},[2225,155153,80706],{"class":2239},[2225,155155,1520],{"class":2235},[2225,155157,32542],{"class":2239},[2225,155159,33083],{"class":2235},[2225,155161,2249],{"class":2235},[2225,155163,6539],{"class":2252},[2225,155165,2805],{"class":2235},[2225,155167,1707],{"class":2235},[2225,155169,155170,155173,155175,155177,155179,155181,155183,155185],{"class":2227,"line":2830},[2225,155171,155172],{"class":2307},"    fields",[2225,155174,2311],{"class":2235},[2225,155176,8667],{"class":2239},[2225,155178,2805],{"class":2235},[2225,155180,6617],{"class":2252},[2225,155182,2805],{"class":2235},[2225,155184,8803],{"class":2239},[2225,155186,1707],{"class":2235},[2225,155188,155189],{"class":2227,"line":2861},[2225,155190,19017],{"class":2235},[2225,155192,155193,155195],{"class":2227,"line":4246},[2225,155194,2359],{"class":2235},[2225,155196,2362],{"class":2239},[2225,155198,155199],{"class":2227,"line":4253},[2225,155200,2285],{"emptyLinePlaceholder":2284},[2225,155202,155203,155205,155207,155209,155211,155213,155215,155217,155219,155221,155223,155225,155227,155229,155231,155233,155235,155237,155239,155241,155243,155245,155247,155249,155251,155253,155255,155257,155259,155261,155263,155265,155267,155269,155271,155273,155275],{"class":2227,"line":4259},[2225,155204,6500],{"class":2231},[2225,155206,6466],{"class":2239},[2225,155208,1520],{"class":2235},[2225,155210,6471],{"class":2291},[2225,155212,6474],{"class":2235},[2225,155214,6477],{"class":6342},[2225,155216,6480],{"class":2235},[2225,155218,2789],{"class":2239},[2225,155220,2805],{"class":2235},[2225,155222,6487],{"class":2252},[2225,155224,2805],{"class":2235},[2225,155226,1381],{"class":2239},[2225,155228,1520],{"class":2235},[2225,155230,3161],{"class":2291},[2225,155232,2789],{"class":2239},[2225,155234,2805],{"class":2235},[2225,155236,26080],{"class":2252},[2225,155238,2805],{"class":2235},[2225,155240,2298],{"class":2235},[2225,155242,2236],{"class":2235},[2225,155244,7070],{"class":2307},[2225,155246,2311],{"class":2235},[2225,155248,2249],{"class":2235},[2225,155250,26080],{"class":2252},[2225,155252,2805],{"class":2235},[2225,155254,2298],{"class":2235},[2225,155256,6361],{"class":2307},[2225,155258,2311],{"class":2235},[2225,155260,2249],{"class":2235},[2225,155262,6539],{"class":2252},[2225,155264,2805],{"class":2235},[2225,155266,2298],{"class":2235},[2225,155268,6351],{"class":2307},[2225,155270,2311],{"class":2235},[2225,155272,27084],{"class":6527},[2225,155274,2243],{"class":2235},[2225,155276,2362],{"class":2239},[2225,155278,155279,155281,155283,155285,155287,155289,155291,155293,155295,155297,155299,155301,155303,155305,155307,155309,155311,155313,155315,155317,155319,155321,155323,155325,155327,155329,155331,155333,155335,155337,155339,155341,155343,155345,155347,155350,155352],{"class":2227,"line":6548},[2225,155280,6500],{"class":2231},[2225,155282,6466],{"class":2239},[2225,155284,1520],{"class":2235},[2225,155286,6471],{"class":2291},[2225,155288,6474],{"class":2235},[2225,155290,6477],{"class":6342},[2225,155292,6480],{"class":2235},[2225,155294,2789],{"class":2239},[2225,155296,2805],{"class":2235},[2225,155298,6487],{"class":2252},[2225,155300,2805],{"class":2235},[2225,155302,1381],{"class":2239},[2225,155304,1520],{"class":2235},[2225,155306,3161],{"class":2291},[2225,155308,2789],{"class":2239},[2225,155310,2805],{"class":2235},[2225,155312,26080],{"class":2252},[2225,155314,2805],{"class":2235},[2225,155316,2298],{"class":2235},[2225,155318,2236],{"class":2235},[2225,155320,7070],{"class":2307},[2225,155322,2311],{"class":2235},[2225,155324,2249],{"class":2235},[2225,155326,26080],{"class":2252},[2225,155328,2805],{"class":2235},[2225,155330,2298],{"class":2235},[2225,155332,6361],{"class":2307},[2225,155334,2311],{"class":2235},[2225,155336,2249],{"class":2235},[2225,155338,6539],{"class":2252},[2225,155340,2805],{"class":2235},[2225,155342,2298],{"class":2235},[2225,155344,6351],{"class":2307},[2225,155346,2311],{"class":2235},[2225,155348,155349],{"class":6527}," 600",[2225,155351,2243],{"class":2235},[2225,155353,2362],{"class":2239},[2225,155355,155356],{"class":2227,"line":6576},[2225,155357,155358],{"class":2763},"\u002F\u002F → throws FieldFrozenError:\n",[2225,155360,155361],{"class":2227,"line":6582},[2225,155362,155363],{"class":2763},"\u002F\u002F \"Cannot change frozen field(s) on invoices\u002Finv-2: amount.\n",[2225,155365,155366],{"class":2227,"line":6587},[2225,155367,155038],{"class":2763},[1591,155369,155371],{"id":155370},"an-authorized-amendment-overrides-the-lock","An authorized amendment overrides the lock",[937,155373,155374,155375,997,155377,155379,155380,155383,155384,155386,155387,17871,155390,968,155392,33507,155394,2311],{},"Both ",[941,155376,154924],{},[941,155378,155047],{}," are skipped inside an amendment\ntransaction — ",[941,155381,155382],{},"db.transaction({ amendment: true, reason }, fn)",", available\nonce ",[941,155385,21246],{}," is opted in. The caller's ",[1007,155388,155389],{"href":86452},"keyring\nrole",[941,155391,9901],{},[941,155393,9898],{},[941,155395,9904],{},[2217,155397,155399],{"className":2219,"code":155398,"language":2221,"meta":1037,"style":1037},"await db.transaction({ amendment: true, reason: 'correct posted amount' }, async (tx) => {\n  tx.vault('books').collection\u003CInvoice>('invoices').put('inv-2', {\n    id: 'inv-2', status: 'paid', amount: 600,\n  })\n})\n\nawait vault.collection\u003CInvoice>('invoices').get('inv-2')\n\u002F\u002F → { id: 'inv-2', status: 'paid', amount: 600 }\n",[941,155400,155401,155451,155506,155540,155546,155552,155556,155596],{"__ignoreMap":1037},[2225,155402,155403,155405,155407,155409,155412,155414,155416,155419,155421,155423,155425,155428,155430,155432,155435,155437,155439,155441,155443,155445,155447,155449],{"class":2227,"line":2228},[2225,155404,6500],{"class":2231},[2225,155406,2842],{"class":2239},[2225,155408,1520],{"class":2235},[2225,155410,155411],{"class":2291},"transaction",[2225,155413,2789],{"class":2239},[2225,155415,2792],{"class":2235},[2225,155417,155418],{"class":2307}," amendment",[2225,155420,2311],{"class":2235},[2225,155422,2322],{"class":2321},[2225,155424,2298],{"class":2235},[2225,155426,155427],{"class":2307}," reason",[2225,155429,2311],{"class":2235},[2225,155431,2249],{"class":2235},[2225,155433,155434],{"class":2252},"correct posted amount",[2225,155436,2805],{"class":2235},[2225,155438,10965],{"class":2235},[2225,155440,38717],{"class":2774},[2225,155442,953],{"class":2235},[2225,155444,4399],{"class":7052},[2225,155446,1381],{"class":2235},[2225,155448,8116],{"class":2774},[2225,155450,2301],{"class":2235},[2225,155452,155453,155456,155458,155460,155462,155464,155466,155468,155470,155472,155474,155476,155478,155480,155482,155484,155486,155488,155490,155492,155494,155496,155498,155500,155502,155504],{"class":2227,"line":1041},[2225,155454,155455],{"class":2239},"  tx",[2225,155457,1520],{"class":2235},[2225,155459,2694],{"class":2291},[2225,155461,2789],{"class":2307},[2225,155463,2805],{"class":2235},[2225,155465,65761],{"class":2252},[2225,155467,2805],{"class":2235},[2225,155469,1381],{"class":2307},[2225,155471,1520],{"class":2235},[2225,155473,6471],{"class":2291},[2225,155475,6474],{"class":2235},[2225,155477,6477],{"class":6342},[2225,155479,6480],{"class":2235},[2225,155481,2789],{"class":2307},[2225,155483,2805],{"class":2235},[2225,155485,6487],{"class":2252},[2225,155487,2805],{"class":2235},[2225,155489,1381],{"class":2307},[2225,155491,1520],{"class":2235},[2225,155493,3161],{"class":2291},[2225,155495,2789],{"class":2307},[2225,155497,2805],{"class":2235},[2225,155499,26080],{"class":2252},[2225,155501,2805],{"class":2235},[2225,155503,2298],{"class":2235},[2225,155505,2301],{"class":2235},[2225,155507,155508,155510,155512,155514,155516,155518,155520,155522,155524,155526,155528,155530,155532,155534,155536,155538],{"class":2227,"line":1038},[2225,155509,60558],{"class":2307},[2225,155511,2311],{"class":2235},[2225,155513,2249],{"class":2235},[2225,155515,26080],{"class":2252},[2225,155517,2805],{"class":2235},[2225,155519,2298],{"class":2235},[2225,155521,6361],{"class":2307},[2225,155523,2311],{"class":2235},[2225,155525,2249],{"class":2235},[2225,155527,6539],{"class":2252},[2225,155529,2805],{"class":2235},[2225,155531,2298],{"class":2235},[2225,155533,6351],{"class":2307},[2225,155535,2311],{"class":2235},[2225,155537,155349],{"class":6527},[2225,155539,1707],{"class":2235},[2225,155541,155542,155544],{"class":2227,"line":2288},[2225,155543,19143],{"class":2235},[2225,155545,2362],{"class":2307},[2225,155547,155548,155550],{"class":2227,"line":2304},[2225,155549,2359],{"class":2235},[2225,155551,2362],{"class":2239},[2225,155553,155554],{"class":2227,"line":2356},[2225,155555,2285],{"emptyLinePlaceholder":2284},[2225,155557,155558,155560,155562,155564,155566,155568,155570,155572,155574,155576,155578,155580,155582,155584,155586,155588,155590,155592,155594],{"class":2227,"line":2830},[2225,155559,6500],{"class":2231},[2225,155561,6466],{"class":2239},[2225,155563,1520],{"class":2235},[2225,155565,6471],{"class":2291},[2225,155567,6474],{"class":2235},[2225,155569,6477],{"class":6342},[2225,155571,6480],{"class":2235},[2225,155573,2789],{"class":2239},[2225,155575,2805],{"class":2235},[2225,155577,6487],{"class":2252},[2225,155579,2805],{"class":2235},[2225,155581,1381],{"class":2239},[2225,155583,1520],{"class":2235},[2225,155585,3158],{"class":2291},[2225,155587,2789],{"class":2239},[2225,155589,2805],{"class":2235},[2225,155591,26080],{"class":2252},[2225,155593,2805],{"class":2235},[2225,155595,2362],{"class":2239},[2225,155597,155598],{"class":2227,"line":2861},[2225,155599,155600],{"class":2763},"\u002F\u002F → { id: 'inv-2', status: 'paid', amount: 600 }\n",[937,155602,155603,155604,155607,155608,13528],{},"If a guard declares an ",[941,155605,155606],{},"amendment.invariant",", it still runs at commit over\nthe whole change-set and can reject the amendment — see ",[1007,155609,155611],{"href":155610},"#amendment-flow","Amendment\nflow",[1022,155613,101012],{"id":41259},[937,155615,155616],{},"Four primitives, one strategy:",[1338,155618,155619,155631],{},[1341,155620,155621],{},[1344,155622,155623,155625,155628],{},[1347,155624,13698],{},[1347,155626,155627],{},"When it fires",[1347,155629,155630],{},"Throws",[1357,155632,155633,155650,155665,155685],{},[1344,155634,155635,155639,155644],{},[1362,155636,155637],{},[941,155638,154924],{},[1362,155640,30792,155641,155643],{},[941,155642,20953],{}," (skipped in amendment mode)",[1362,155645,155646,155647],{},"Anything; conventionally ",[941,155648,155649],{},"RecordLockedError",[1344,155651,155652,155656,155661],{},[1362,155653,155654],{},[941,155655,137880],{},[1362,155657,30792,155658,155660],{},[941,155659,9467],{}," of an existing record (skipped in amendment mode); skipped for delete-of-absent",[1362,155662,155646,155663],{},[941,155664,155649],{},[1344,155666,155667,155671,155679],{},[1362,155668,155669],{},[941,155670,155047],{},[1362,155672,30792,155673,22876,155675,155678],{},[941,155674,20953],{},[941,155676,155677],{},"when(existing)"," becomes true",[1362,155680,155681,155684],{},[941,155682,155683],{},"FieldFrozenError"," listing changed frozen fields",[1344,155686,155687,155691,155697],{},[1362,155688,155689],{},[941,155690,154243],{},[1362,155692,155693,155694],{},"Inside ",[941,155695,155696],{},"withTransactions({ amendment: true, reason })",[1362,155698,155699,155702],{},[941,155700,155701],{},"InvariantError"," if the rule fails",[937,155704,155705,155707,155708,155710,155711,155714,155715,41721,155717,155714,155720,1520],{},[941,155706,154924],{}," is put-only — ",[941,155709,137880],{}," is the dedicated delete-time hook. The\nargument shapes mirror each other but the semantics are explicit:\n",[941,155712,155713],{},"check(incoming, ctx)"," validates a record being ",[950,155716,109694],{},[941,155718,155719],{},"onDelete(existing, ctx)",[950,155721,155722],{},"removed",[1591,155724,155726,155728],{"id":155725},"ondelete-bypass-paths",[941,155727,137880],{}," bypass paths",[937,155730,155731,155732,155734],{},"Two paths skip the ",[941,155733,137880],{}," callback. Both are by design:",[1234,155736,155737,155756],{},[1114,155738,155739,953,155742,155745,155746,155748,155749,155751,155752,155755],{},[950,155740,155741],{},"Amendment transactions",[941,155743,155744],{},"db.transaction({ amendment: true })",")\nfor an authorized role — amendments are the generic unlock primitive,\nconsistent with how ",[941,155747,155047],{}," lets staged writes through. The\n",[941,155750,155606],{}," block (if declared) DOES still see the\n",[941,155753,155754],{},"{ before, after: null }"," change pair and can reject the delete at\ncommit time.",[1114,155757,155758,964,155761,155763,155764,155767,155768,155771,155772,155774],{},[950,155759,155760],{},"System-internal deletes",[1007,155762,665],{"href":124621}," tombstones\nand ",[1007,155765,155766],{"href":124624},"materialized-view"," refresh deletes route\nthrough an internal-only delete path that bypasses every user-facing\nguard. Housekeeping writes are not user-initiated and would otherwise\ntrip user invariants registered against output collections. ",[950,155769,155770],{},"If\nthe internal delete fires while an amendment window is open","\n(e.g. an admin amendment edits a source row → triggers a\nderivation cascade → cascades a tombstone), the change pair IS\npushed onto the amendment's change-set and surfaces to\n",[941,155773,155606],{},". This keeps the \"truly unconditional\" paired\npattern below honest.",[1591,155776,155778],{"id":155777},"truly-unconditional-delete-block-pair-the-two-hooks","Truly unconditional delete-block — pair the two hooks",[937,155780,155781,155784,155785,155787,155788,155790],{},[941,155782,155783],{},"onDelete: () => { throw }"," alone is NOT unconditional. An admin\namendment can still bypass it. For legal-document immutability rules\n(e.g. Thai Revenue Code §86: receipts are append-only forever) pair\n",[941,155786,137880],{}," with an ",[941,155789,155606],{}," that re-throws on any\ndelete-shaped change:",[2217,155792,155794],{"className":2219,"code":155793,"language":2221,"meta":1037,"style":1037},"withGuard\u003CReceipt>({\n  collection: 'receipts',\n  onDelete: () => {\n    throw new RecordLockedError('receipts', '', 'receipts are append-only')\n  },\n  amendment: {\n    roles: ['admin', 'owner'],\n    invariant: (changes) => {\n      for (const c of changes) {\n        if (c.before !== null && c.after === null) {\n          throw new RecordLockedError('receipts', '', 'amendment cannot delete')\n        }\n      }\n    },\n  },\n})\n\n\u002F\u002F Ordinary delete — blocked by `onDelete` before it ever reaches an amendment:\nawait vault.collection\u003CReceipt>('receipts').delete('r1')\n\u002F\u002F → throws RecordLockedError: \"Cannot modify receipts\u002F — locked by guard:\n\u002F\u002F   receipts are append-only. Use withTransactions({ amendment: true, reason })\n\u002F\u002F   with admin\u002Fowner role to override.\"\n\n\u002F\u002F Admin amendment attempting the same delete — blocked by the invariant instead:\nawait db.transaction({ amendment: true, reason: 'cleanup' }, async (tx) => {\n  tx.vault('books').collection\u003CReceipt>('receipts').delete('r1')\n})\n\u002F\u002F → throws InvariantError: \"Cannot modify receipts\u002F — locked by guard:\n\u002F\u002F   amendment cannot delete. Use withTransactions({ amendment: true, reason })\n\u002F\u002F   with admin\u002Fowner role to override.\"\n",[941,155795,155796,155811,155825,155837,155869,155873,155881,155907,155923,155942,155974,156006,156010,156014,156018,156022,156028,156032,156037,156077,156082,156087,156092,156096,156101,156148,156200,156206,156211,156216],{"__ignoreMap":1037},[2225,155797,155798,155801,155803,155805,155807,155809],{"class":2227,"line":2228},[2225,155799,155800],{"class":2291},"withGuard",[2225,155802,6474],{"class":2235},[2225,155804,27835],{"class":6342},[2225,155806,6480],{"class":2235},[2225,155808,2789],{"class":2239},[2225,155810,4200],{"class":2235},[2225,155812,155813,155815,155817,155819,155821,155823],{"class":2227,"line":1041},[2225,155814,30275],{"class":2307},[2225,155816,2311],{"class":2235},[2225,155818,2249],{"class":2235},[2225,155820,27844],{"class":2252},[2225,155822,2805],{"class":2235},[2225,155824,1707],{"class":2235},[2225,155826,155827,155829,155831,155833,155835],{"class":2227,"line":1038},[2225,155828,151304],{"class":2291},[2225,155830,2311],{"class":2235},[2225,155832,13130],{"class":2235},[2225,155834,8116],{"class":2774},[2225,155836,2301],{"class":2235},[2225,155838,155839,155842,155844,155846,155848,155850,155852,155854,155856,155858,155860,155862,155865,155867],{"class":2227,"line":2288},[2225,155840,155841],{"class":2231},"    throw",[2225,155843,8130],{"class":2235},[2225,155845,154372],{"class":2291},[2225,155847,2789],{"class":2307},[2225,155849,2805],{"class":2235},[2225,155851,27844],{"class":2252},[2225,155853,2805],{"class":2235},[2225,155855,2298],{"class":2235},[2225,155857,12500],{"class":2235},[2225,155859,2298],{"class":2235},[2225,155861,2249],{"class":2235},[2225,155863,155864],{"class":2252},"receipts are append-only",[2225,155866,2805],{"class":2235},[2225,155868,2362],{"class":2307},[2225,155870,155871],{"class":2227,"line":2304},[2225,155872,19017],{"class":2235},[2225,155874,155875,155877,155879],{"class":2227,"line":2356},[2225,155876,154595],{"class":2307},[2225,155878,2311],{"class":2235},[2225,155880,2301],{"class":2235},[2225,155882,155883,155885,155887,155889,155891,155893,155895,155897,155899,155901,155903,155905],{"class":2227,"line":2830},[2225,155884,154604],{"class":2307},[2225,155886,2311],{"class":2235},[2225,155888,8667],{"class":2239},[2225,155890,2805],{"class":2235},[2225,155892,9901],{"class":2252},[2225,155894,2805],{"class":2235},[2225,155896,2298],{"class":2235},[2225,155898,2249],{"class":2235},[2225,155900,9898],{"class":2252},[2225,155902,2805],{"class":2235},[2225,155904,8803],{"class":2239},[2225,155906,1707],{"class":2235},[2225,155908,155909,155911,155913,155915,155917,155919,155921],{"class":2227,"line":2861},[2225,155910,154631],{"class":2291},[2225,155912,2311],{"class":2235},[2225,155914,953],{"class":2235},[2225,155916,154638],{"class":7052},[2225,155918,1381],{"class":2235},[2225,155920,8116],{"class":2774},[2225,155922,2301],{"class":2235},[2225,155924,155925,155927,155929,155931,155933,155935,155938,155940],{"class":2227,"line":4246},[2225,155926,8788],{"class":2231},[2225,155928,953],{"class":2307},[2225,155930,2775],{"class":2774},[2225,155932,8169],{"class":2239},[2225,155934,8806],{"class":2235},[2225,155936,155937],{"class":2239}," changes",[2225,155939,8495],{"class":2307},[2225,155941,4200],{"class":2235},[2225,155943,155944,155946,155948,155950,155952,155954,155956,155958,155960,155962,155964,155966,155968,155970,155972],{"class":2227,"line":4253},[2225,155945,8857],{"class":2231},[2225,155947,953],{"class":2307},[2225,155949,8200],{"class":2239},[2225,155951,1520],{"class":2235},[2225,155953,11357],{"class":2239},[2225,155955,8480],{"class":2235},[2225,155957,7090],{"class":2235},[2225,155959,8486],{"class":2235},[2225,155961,8169],{"class":2239},[2225,155963,1520],{"class":2235},[2225,155965,10761],{"class":2239},[2225,155967,9025],{"class":2235},[2225,155969,7090],{"class":2235},[2225,155971,8495],{"class":2307},[2225,155973,4200],{"class":2235},[2225,155975,155976,155979,155981,155983,155985,155987,155989,155991,155993,155995,155997,155999,156002,156004],{"class":2227,"line":4259},[2225,155977,155978],{"class":2231},"          throw",[2225,155980,8130],{"class":2235},[2225,155982,154372],{"class":2291},[2225,155984,2789],{"class":2307},[2225,155986,2805],{"class":2235},[2225,155988,27844],{"class":2252},[2225,155990,2805],{"class":2235},[2225,155992,2298],{"class":2235},[2225,155994,12500],{"class":2235},[2225,155996,2298],{"class":2235},[2225,155998,2249],{"class":2235},[2225,156000,156001],{"class":2252},"amendment cannot delete",[2225,156003,2805],{"class":2235},[2225,156005,2362],{"class":2307},[2225,156007,156008],{"class":2227,"line":6548},[2225,156009,9180],{"class":2235},[2225,156011,156012],{"class":2227,"line":6576},[2225,156013,8518],{"class":2235},[2225,156015,156016],{"class":2227,"line":6582},[2225,156017,8324],{"class":2235},[2225,156019,156020],{"class":2227,"line":6587},[2225,156021,19017],{"class":2235},[2225,156023,156024,156026],{"class":2227,"line":7174},[2225,156025,2359],{"class":2235},[2225,156027,2362],{"class":2239},[2225,156029,156030],{"class":2227,"line":7180},[2225,156031,2285],{"emptyLinePlaceholder":2284},[2225,156033,156034],{"class":2227,"line":7220},[2225,156035,156036],{"class":2763},"\u002F\u002F Ordinary delete — blocked by `onDelete` before it ever reaches an amendment:\n",[2225,156038,156039,156041,156043,156045,156047,156049,156051,156053,156055,156057,156059,156061,156063,156065,156067,156069,156071,156073,156075],{"class":2227,"line":7225},[2225,156040,6500],{"class":2231},[2225,156042,6466],{"class":2239},[2225,156044,1520],{"class":2235},[2225,156046,6471],{"class":2291},[2225,156048,6474],{"class":2235},[2225,156050,27835],{"class":6342},[2225,156052,6480],{"class":2235},[2225,156054,2789],{"class":2239},[2225,156056,2805],{"class":2235},[2225,156058,27844],{"class":2252},[2225,156060,2805],{"class":2235},[2225,156062,1381],{"class":2239},[2225,156064,1520],{"class":2235},[2225,156066,3164],{"class":2291},[2225,156068,2789],{"class":2239},[2225,156070,2805],{"class":2235},[2225,156072,75463],{"class":2252},[2225,156074,2805],{"class":2235},[2225,156076,2362],{"class":2239},[2225,156078,156079],{"class":2227,"line":7231},[2225,156080,156081],{"class":2763},"\u002F\u002F → throws RecordLockedError: \"Cannot modify receipts\u002F — locked by guard:\n",[2225,156083,156084],{"class":2227,"line":7267},[2225,156085,156086],{"class":2763},"\u002F\u002F   receipts are append-only. Use withTransactions({ amendment: true, reason })\n",[2225,156088,156089],{"class":2227,"line":7272},[2225,156090,156091],{"class":2763},"\u002F\u002F   with admin\u002Fowner role to override.\"\n",[2225,156093,156094],{"class":2227,"line":7278},[2225,156095,2285],{"emptyLinePlaceholder":2284},[2225,156097,156098],{"class":2227,"line":7303},[2225,156099,156100],{"class":2763},"\u002F\u002F Admin amendment attempting the same delete — blocked by the invariant instead:\n",[2225,156102,156103,156105,156107,156109,156111,156113,156115,156117,156119,156121,156123,156125,156127,156129,156132,156134,156136,156138,156140,156142,156144,156146],{"class":2227,"line":7308},[2225,156104,6500],{"class":2231},[2225,156106,2842],{"class":2239},[2225,156108,1520],{"class":2235},[2225,156110,155411],{"class":2291},[2225,156112,2789],{"class":2239},[2225,156114,2792],{"class":2235},[2225,156116,155418],{"class":2307},[2225,156118,2311],{"class":2235},[2225,156120,2322],{"class":2321},[2225,156122,2298],{"class":2235},[2225,156124,155427],{"class":2307},[2225,156126,2311],{"class":2235},[2225,156128,2249],{"class":2235},[2225,156130,156131],{"class":2252},"cleanup",[2225,156133,2805],{"class":2235},[2225,156135,10965],{"class":2235},[2225,156137,38717],{"class":2774},[2225,156139,953],{"class":2235},[2225,156141,4399],{"class":7052},[2225,156143,1381],{"class":2235},[2225,156145,8116],{"class":2774},[2225,156147,2301],{"class":2235},[2225,156149,156150,156152,156154,156156,156158,156160,156162,156164,156166,156168,156170,156172,156174,156176,156178,156180,156182,156184,156186,156188,156190,156192,156194,156196,156198],{"class":2227,"line":7314},[2225,156151,155455],{"class":2239},[2225,156153,1520],{"class":2235},[2225,156155,2694],{"class":2291},[2225,156157,2789],{"class":2307},[2225,156159,2805],{"class":2235},[2225,156161,65761],{"class":2252},[2225,156163,2805],{"class":2235},[2225,156165,1381],{"class":2307},[2225,156167,1520],{"class":2235},[2225,156169,6471],{"class":2291},[2225,156171,6474],{"class":2235},[2225,156173,27835],{"class":6342},[2225,156175,6480],{"class":2235},[2225,156177,2789],{"class":2307},[2225,156179,2805],{"class":2235},[2225,156181,27844],{"class":2252},[2225,156183,2805],{"class":2235},[2225,156185,1381],{"class":2307},[2225,156187,1520],{"class":2235},[2225,156189,3164],{"class":2291},[2225,156191,2789],{"class":2307},[2225,156193,2805],{"class":2235},[2225,156195,75463],{"class":2252},[2225,156197,2805],{"class":2235},[2225,156199,2362],{"class":2307},[2225,156201,156202,156204],{"class":2227,"line":7348},[2225,156203,2359],{"class":2235},[2225,156205,2362],{"class":2239},[2225,156207,156208],{"class":2227,"line":8613},[2225,156209,156210],{"class":2763},"\u002F\u002F → throws InvariantError: \"Cannot modify receipts\u002F — locked by guard:\n",[2225,156212,156213],{"class":2227,"line":8633},[2225,156214,156215],{"class":2763},"\u002F\u002F   amendment cannot delete. Use withTransactions({ amendment: true, reason })\n",[2225,156217,156218],{"class":2227,"line":8662},[2225,156219,156091],{"class":2763},[937,156221,156222,156223,156225,156226,156229,156230,156232,156233,156236],{},"The thrown ",[941,156224,155649],{}," inside ",[941,156227,156228],{},"invariant"," is wrapped in\n",[941,156231,155701],{}," by ",[941,156234,156235],{},"GuardExecutor.runInvariant"," (the message survives);\nthe staged delete rolls back, the record stays.",[1591,156238,156240,997,156243],{"id":156239},"immutableguard-and-amendmentinvariant",[941,156241,156242],{},"immutableGuard",[941,156244,156245],{},"amendmentInvariant",[937,156247,156248,156250,156251,156254,156255,156258,156259,156261,156262,156264,156265,156267,156268,156271,156272,1520],{},[941,156249,156242],{}," packages the append-only pattern above into one helper.\nGive it either ",[941,156252,156253],{},"after: (record) => boolean"," — the record becomes immutable\nonce the predicate turns true — or ",[941,156256,156257],{},"appendOnly: true",", shorthand for\n\"immutable from creation\" (they're mutually exclusive; omitting both throws\n",[941,156260,12881],{},"). By default its amendment override is an empty-allow (so\nan authorized role can amend freely) — backward compatible with a hand-rolled\n",[941,156263,155800],{},". Pass the optional ",[941,156266,156245],{}," to keep a rule inviolable\n",[950,156269,156270],{},"even inside an amendment tx","; it has the same signature as\n",[941,156273,156274],{},"GuardStrategy.amendment.invariant",[2217,156276,156278],{"className":2219,"code":156277,"language":2221,"meta":1037,"style":1037},"import { immutableGuard, RecordLockedError } from '@noy-db\u002Fhub\u002Fguards'\n\nconst receiptGuard = immutableGuard\u003CReceipt>({\n  collection: 'receipts',\n  appendOnly: true, \u002F\u002F immutable from creation — no `after` needed\n  amendmentInvariant: (changes) => {\n    for (const c of changes) {\n      if (c.before !== null && c.after === null) {\n        throw new RecordLockedError('receipts', '', 'amendment cannot delete')\n      }\n    }\n  },\n})\n\nawait vault.collection\u003CReceipt>('receipts').put('r1', { id: 'r1', total: 500 })\nawait vault.collection\u003CReceipt>('receipts').put('r1', { id: 'r1', total: 600 })\n\u002F\u002F → throws RecordLockedError: \"Cannot modify receipts\u002Fr1 — locked by guard:\n\u002F\u002F   append-only collection. Use withTransactions({ amendment: true, reason })\n\u002F\u002F   with admin\u002Fowner role to override.\"\n",[941,156279,156280,156303,156307,156328,156342,156356,156373,156392,156424,156454,156458,156462,156466,156472,156476,156540,156604,156609,156614],{"__ignoreMap":1037},[2225,156281,156282,156284,156286,156289,156291,156293,156295,156297,156299,156301],{"class":2227,"line":2228},[2225,156283,2232],{"class":2231},[2225,156285,2236],{"class":2235},[2225,156287,156288],{"class":2239}," immutableGuard",[2225,156290,2298],{"class":2235},[2225,156292,154372],{"class":2239},[2225,156294,2243],{"class":2235},[2225,156296,2246],{"class":2231},[2225,156298,2249],{"class":2235},[2225,156300,73216],{"class":2252},[2225,156302,2256],{"class":2235},[2225,156304,156305],{"class":2227,"line":1041},[2225,156306,2285],{"emptyLinePlaceholder":2284},[2225,156308,156309,156311,156314,156316,156318,156320,156322,156324,156326],{"class":2227,"line":1038},[2225,156310,2775],{"class":2774},[2225,156312,156313],{"class":2239}," receiptGuard ",[2225,156315,2781],{"class":2235},[2225,156317,156288],{"class":2291},[2225,156319,6474],{"class":2235},[2225,156321,27835],{"class":6342},[2225,156323,6480],{"class":2235},[2225,156325,2789],{"class":2239},[2225,156327,4200],{"class":2235},[2225,156329,156330,156332,156334,156336,156338,156340],{"class":2227,"line":2288},[2225,156331,30275],{"class":2307},[2225,156333,2311],{"class":2235},[2225,156335,2249],{"class":2235},[2225,156337,27844],{"class":2252},[2225,156339,2805],{"class":2235},[2225,156341,1707],{"class":2235},[2225,156343,156344,156347,156349,156351,156353],{"class":2227,"line":2304},[2225,156345,156346],{"class":2307},"  appendOnly",[2225,156348,2311],{"class":2235},[2225,156350,2322],{"class":2321},[2225,156352,2298],{"class":2235},[2225,156354,156355],{"class":2763}," \u002F\u002F immutable from creation — no `after` needed\n",[2225,156357,156358,156361,156363,156365,156367,156369,156371],{"class":2227,"line":2356},[2225,156359,156360],{"class":2291},"  amendmentInvariant",[2225,156362,2311],{"class":2235},[2225,156364,953],{"class":2235},[2225,156366,154638],{"class":7052},[2225,156368,1381],{"class":2235},[2225,156370,8116],{"class":2774},[2225,156372,2301],{"class":2235},[2225,156374,156375,156378,156380,156382,156384,156386,156388,156390],{"class":2227,"line":2830},[2225,156376,156377],{"class":2231},"    for",[2225,156379,953],{"class":2307},[2225,156381,2775],{"class":2774},[2225,156383,8169],{"class":2239},[2225,156385,8806],{"class":2235},[2225,156387,155937],{"class":2239},[2225,156389,8495],{"class":2307},[2225,156391,4200],{"class":2235},[2225,156393,156394,156396,156398,156400,156402,156404,156406,156408,156410,156412,156414,156416,156418,156420,156422],{"class":2227,"line":2861},[2225,156395,8472],{"class":2231},[2225,156397,953],{"class":2307},[2225,156399,8200],{"class":2239},[2225,156401,1520],{"class":2235},[2225,156403,11357],{"class":2239},[2225,156405,8480],{"class":2235},[2225,156407,7090],{"class":2235},[2225,156409,8486],{"class":2235},[2225,156411,8169],{"class":2239},[2225,156413,1520],{"class":2235},[2225,156415,10761],{"class":2239},[2225,156417,9025],{"class":2235},[2225,156419,7090],{"class":2235},[2225,156421,8495],{"class":2307},[2225,156423,4200],{"class":2235},[2225,156425,156426,156428,156430,156432,156434,156436,156438,156440,156442,156444,156446,156448,156450,156452],{"class":2227,"line":4246},[2225,156427,8502],{"class":2231},[2225,156429,8130],{"class":2235},[2225,156431,154372],{"class":2291},[2225,156433,2789],{"class":2307},[2225,156435,2805],{"class":2235},[2225,156437,27844],{"class":2252},[2225,156439,2805],{"class":2235},[2225,156441,2298],{"class":2235},[2225,156443,12500],{"class":2235},[2225,156445,2298],{"class":2235},[2225,156447,2249],{"class":2235},[2225,156449,156001],{"class":2252},[2225,156451,2805],{"class":2235},[2225,156453,2362],{"class":2307},[2225,156455,156456],{"class":2227,"line":4253},[2225,156457,8518],{"class":2235},[2225,156459,156460],{"class":2227,"line":4259},[2225,156461,60103],{"class":2235},[2225,156463,156464],{"class":2227,"line":6548},[2225,156465,19017],{"class":2235},[2225,156467,156468,156470],{"class":2227,"line":6576},[2225,156469,2359],{"class":2235},[2225,156471,2362],{"class":2239},[2225,156473,156474],{"class":2227,"line":6582},[2225,156475,2285],{"emptyLinePlaceholder":2284},[2225,156477,156478,156480,156482,156484,156486,156488,156490,156492,156494,156496,156498,156500,156502,156504,156506,156508,156510,156512,156514,156516,156518,156520,156522,156524,156526,156528,156530,156532,156534,156536,156538],{"class":2227,"line":6587},[2225,156479,6500],{"class":2231},[2225,156481,6466],{"class":2239},[2225,156483,1520],{"class":2235},[2225,156485,6471],{"class":2291},[2225,156487,6474],{"class":2235},[2225,156489,27835],{"class":6342},[2225,156491,6480],{"class":2235},[2225,156493,2789],{"class":2239},[2225,156495,2805],{"class":2235},[2225,156497,27844],{"class":2252},[2225,156499,2805],{"class":2235},[2225,156501,1381],{"class":2239},[2225,156503,1520],{"class":2235},[2225,156505,3161],{"class":2291},[2225,156507,2789],{"class":2239},[2225,156509,2805],{"class":2235},[2225,156511,75463],{"class":2252},[2225,156513,2805],{"class":2235},[2225,156515,2298],{"class":2235},[2225,156517,2236],{"class":2235},[2225,156519,7070],{"class":2307},[2225,156521,2311],{"class":2235},[2225,156523,2249],{"class":2235},[2225,156525,75463],{"class":2252},[2225,156527,2805],{"class":2235},[2225,156529,2298],{"class":2235},[2225,156531,27452],{"class":2307},[2225,156533,2311],{"class":2235},[2225,156535,27084],{"class":6527},[2225,156537,2243],{"class":2235},[2225,156539,2362],{"class":2239},[2225,156541,156542,156544,156546,156548,156550,156552,156554,156556,156558,156560,156562,156564,156566,156568,156570,156572,156574,156576,156578,156580,156582,156584,156586,156588,156590,156592,156594,156596,156598,156600,156602],{"class":2227,"line":7174},[2225,156543,6500],{"class":2231},[2225,156545,6466],{"class":2239},[2225,156547,1520],{"class":2235},[2225,156549,6471],{"class":2291},[2225,156551,6474],{"class":2235},[2225,156553,27835],{"class":6342},[2225,156555,6480],{"class":2235},[2225,156557,2789],{"class":2239},[2225,156559,2805],{"class":2235},[2225,156561,27844],{"class":2252},[2225,156563,2805],{"class":2235},[2225,156565,1381],{"class":2239},[2225,156567,1520],{"class":2235},[2225,156569,3161],{"class":2291},[2225,156571,2789],{"class":2239},[2225,156573,2805],{"class":2235},[2225,156575,75463],{"class":2252},[2225,156577,2805],{"class":2235},[2225,156579,2298],{"class":2235},[2225,156581,2236],{"class":2235},[2225,156583,7070],{"class":2307},[2225,156585,2311],{"class":2235},[2225,156587,2249],{"class":2235},[2225,156589,75463],{"class":2252},[2225,156591,2805],{"class":2235},[2225,156593,2298],{"class":2235},[2225,156595,27452],{"class":2307},[2225,156597,2311],{"class":2235},[2225,156599,155349],{"class":6527},[2225,156601,2243],{"class":2235},[2225,156603,2362],{"class":2239},[2225,156605,156606],{"class":2227,"line":7180},[2225,156607,156608],{"class":2763},"\u002F\u002F → throws RecordLockedError: \"Cannot modify receipts\u002Fr1 — locked by guard:\n",[2225,156610,156611],{"class":2227,"line":7220},[2225,156612,156613],{"class":2763},"\u002F\u002F   append-only collection. Use withTransactions({ amendment: true, reason })\n",[2225,156615,156616],{"class":2227,"line":7225},[2225,156617,156091],{"class":2763},[937,156619,156620,156621,156623],{},"Omitting ",[941,156622,156245],{}," leaves the empty-allow override in place.",[1591,156625,156627],{"id":156626},"transitionguardopts",[941,156628,156629],{},"transitionGuard(opts)",[937,156631,156632,156635,156636,156638,156639,156642,156643,156645],{},[941,156633,156634],{},"transitionGuard"," generalizes ",[941,156637,156242],{}," from \"any state → locked\"\nto an arbitrary state graph. Declare the allowed arcs once; every write\nthat moves the lifecycle field along an undeclared edge throws\n",[941,156640,156641],{},"IllegalTransitionError",". It's sugar over ",[941,156644,155800],{}," (no new write-path\ncode) and inherits the same ledgered amendment override.",[2217,156647,156649],{"className":2219,"code":156648,"language":2221,"meta":1037,"style":1037},"transitionGuard\u003CSale>({\n  collection: 'sales', field: 'status',\n  transitions: {                          \u002F\u002F absence of an arc = forbidden\n    draft: ['to_verify', 'cancelled'],\n    to_verify: ['proforma', 'draft', 'cancelled'],\n    proforma: ['invoiced', 'cancelled'],\n    invoiced: ['paid'], paid: [], cancelled: [],\n  },\n  initial: ['draft', 'to_verify'],        \u002F\u002F allowed status on insert (default: any)\n  allowIdempotent: true,                  \u002F\u002F same→same write allowed (default true)\n})\n\nawait vault.collection\u003CSale>('sales').put('s1', { id: 's1', status: 'invoiced' })\nawait vault.collection\u003CSale>('sales').put('s1', { id: 's1', status: 'draft' })\n\u002F\u002F → throws IllegalTransitionError: 'Cannot transition sales\u002Fs1 from \"invoiced\"\n\u002F\u002F   to \"draft\" — not a declared arc. Use withTransactions({ amendment: true,\n\u002F\u002F   reason }) with admin\u002Fowner role to override.'\n",[941,156650,156651,156665,156691,156703,156731,156767,156795,156832,156836,156866,156880,156886,156890,156958,157026,157031,157036],{"__ignoreMap":1037},[2225,156652,156653,156655,156657,156659,156661,156663],{"class":2227,"line":2228},[2225,156654,156634],{"class":2291},[2225,156656,6474],{"class":2235},[2225,156658,139184],{"class":6342},[2225,156660,6480],{"class":2235},[2225,156662,2789],{"class":2239},[2225,156664,4200],{"class":2235},[2225,156666,156667,156669,156671,156673,156675,156677,156679,156681,156683,156685,156687,156689],{"class":2227,"line":1041},[2225,156668,30275],{"class":2307},[2225,156670,2311],{"class":2235},[2225,156672,2249],{"class":2235},[2225,156674,123562],{"class":2252},[2225,156676,2805],{"class":2235},[2225,156678,2298],{"class":2235},[2225,156680,124557],{"class":2307},[2225,156682,2311],{"class":2235},[2225,156684,2249],{"class":2235},[2225,156686,6631],{"class":2252},[2225,156688,2805],{"class":2235},[2225,156690,1707],{"class":2235},[2225,156692,156693,156696,156698,156700],{"class":2227,"line":1038},[2225,156694,156695],{"class":2307},"  transitions",[2225,156697,2311],{"class":2235},[2225,156699,2236],{"class":2235},[2225,156701,156702],{"class":2763},"                          \u002F\u002F absence of an arc = forbidden\n",[2225,156704,156705,156708,156710,156712,156714,156716,156718,156720,156722,156725,156727,156729],{"class":2227,"line":2288},[2225,156706,156707],{"class":2307},"    draft",[2225,156709,2311],{"class":2235},[2225,156711,8667],{"class":2239},[2225,156713,2805],{"class":2235},[2225,156715,147088],{"class":2252},[2225,156717,2805],{"class":2235},[2225,156719,2298],{"class":2235},[2225,156721,2249],{"class":2235},[2225,156723,156724],{"class":2252},"cancelled",[2225,156726,2805],{"class":2235},[2225,156728,8803],{"class":2239},[2225,156730,1707],{"class":2235},[2225,156732,156733,156736,156738,156740,156742,156745,156747,156749,156751,156753,156755,156757,156759,156761,156763,156765],{"class":2227,"line":2304},[2225,156734,156735],{"class":2307},"    to_verify",[2225,156737,2311],{"class":2235},[2225,156739,8667],{"class":2239},[2225,156741,2805],{"class":2235},[2225,156743,156744],{"class":2252},"proforma",[2225,156746,2805],{"class":2235},[2225,156748,2298],{"class":2235},[2225,156750,2249],{"class":2235},[2225,156752,28429],{"class":2252},[2225,156754,2805],{"class":2235},[2225,156756,2298],{"class":2235},[2225,156758,2249],{"class":2235},[2225,156760,156724],{"class":2252},[2225,156762,2805],{"class":2235},[2225,156764,8803],{"class":2239},[2225,156766,1707],{"class":2235},[2225,156768,156769,156772,156774,156776,156778,156781,156783,156785,156787,156789,156791,156793],{"class":2227,"line":2356},[2225,156770,156771],{"class":2307},"    proforma",[2225,156773,2311],{"class":2235},[2225,156775,8667],{"class":2239},[2225,156777,2805],{"class":2235},[2225,156779,156780],{"class":2252},"invoiced",[2225,156782,2805],{"class":2235},[2225,156784,2298],{"class":2235},[2225,156786,2249],{"class":2235},[2225,156788,156724],{"class":2252},[2225,156790,2805],{"class":2235},[2225,156792,8803],{"class":2239},[2225,156794,1707],{"class":2235},[2225,156796,156797,156800,156802,156804,156806,156808,156810,156812,156814,156817,156819,156821,156823,156826,156828,156830],{"class":2227,"line":2830},[2225,156798,156799],{"class":2307},"    invoiced",[2225,156801,2311],{"class":2235},[2225,156803,8667],{"class":2239},[2225,156805,2805],{"class":2235},[2225,156807,6539],{"class":2252},[2225,156809,2805],{"class":2235},[2225,156811,8803],{"class":2239},[2225,156813,2298],{"class":2235},[2225,156815,156816],{"class":2307}," paid",[2225,156818,2311],{"class":2235},[2225,156820,66698],{"class":2239},[2225,156822,2298],{"class":2235},[2225,156824,156825],{"class":2307}," cancelled",[2225,156827,2311],{"class":2235},[2225,156829,66698],{"class":2239},[2225,156831,1707],{"class":2235},[2225,156833,156834],{"class":2227,"line":2861},[2225,156835,19017],{"class":2235},[2225,156837,156838,156841,156843,156845,156847,156849,156851,156853,156855,156857,156859,156861,156863],{"class":2227,"line":4246},[2225,156839,156840],{"class":2307},"  initial",[2225,156842,2311],{"class":2235},[2225,156844,8667],{"class":2239},[2225,156846,2805],{"class":2235},[2225,156848,28429],{"class":2252},[2225,156850,2805],{"class":2235},[2225,156852,2298],{"class":2235},[2225,156854,2249],{"class":2235},[2225,156856,147088],{"class":2252},[2225,156858,2805],{"class":2235},[2225,156860,8803],{"class":2239},[2225,156862,2298],{"class":2235},[2225,156864,156865],{"class":2763},"        \u002F\u002F allowed status on insert (default: any)\n",[2225,156867,156868,156871,156873,156875,156877],{"class":2227,"line":4253},[2225,156869,156870],{"class":2307},"  allowIdempotent",[2225,156872,2311],{"class":2235},[2225,156874,2322],{"class":2321},[2225,156876,2298],{"class":2235},[2225,156878,156879],{"class":2763},"                  \u002F\u002F same→same write allowed (default true)\n",[2225,156881,156882,156884],{"class":2227,"line":4259},[2225,156883,2359],{"class":2235},[2225,156885,2362],{"class":2239},[2225,156887,156888],{"class":2227,"line":6548},[2225,156889,2285],{"emptyLinePlaceholder":2284},[2225,156891,156892,156894,156896,156898,156900,156902,156904,156906,156908,156910,156912,156914,156916,156918,156920,156922,156924,156926,156928,156930,156932,156934,156936,156938,156940,156942,156944,156946,156948,156950,156952,156954,156956],{"class":2227,"line":6576},[2225,156893,6500],{"class":2231},[2225,156895,6466],{"class":2239},[2225,156897,1520],{"class":2235},[2225,156899,6471],{"class":2291},[2225,156901,6474],{"class":2235},[2225,156903,139184],{"class":6342},[2225,156905,6480],{"class":2235},[2225,156907,2789],{"class":2239},[2225,156909,2805],{"class":2235},[2225,156911,123562],{"class":2252},[2225,156913,2805],{"class":2235},[2225,156915,1381],{"class":2239},[2225,156917,1520],{"class":2235},[2225,156919,3161],{"class":2291},[2225,156921,2789],{"class":2239},[2225,156923,2805],{"class":2235},[2225,156925,151370],{"class":2252},[2225,156927,2805],{"class":2235},[2225,156929,2298],{"class":2235},[2225,156931,2236],{"class":2235},[2225,156933,7070],{"class":2307},[2225,156935,2311],{"class":2235},[2225,156937,2249],{"class":2235},[2225,156939,151370],{"class":2252},[2225,156941,2805],{"class":2235},[2225,156943,2298],{"class":2235},[2225,156945,6361],{"class":2307},[2225,156947,2311],{"class":2235},[2225,156949,2249],{"class":2235},[2225,156951,156780],{"class":2252},[2225,156953,2805],{"class":2235},[2225,156955,2243],{"class":2235},[2225,156957,2362],{"class":2239},[2225,156959,156960,156962,156964,156966,156968,156970,156972,156974,156976,156978,156980,156982,156984,156986,156988,156990,156992,156994,156996,156998,157000,157002,157004,157006,157008,157010,157012,157014,157016,157018,157020,157022,157024],{"class":2227,"line":6582},[2225,156961,6500],{"class":2231},[2225,156963,6466],{"class":2239},[2225,156965,1520],{"class":2235},[2225,156967,6471],{"class":2291},[2225,156969,6474],{"class":2235},[2225,156971,139184],{"class":6342},[2225,156973,6480],{"class":2235},[2225,156975,2789],{"class":2239},[2225,156977,2805],{"class":2235},[2225,156979,123562],{"class":2252},[2225,156981,2805],{"class":2235},[2225,156983,1381],{"class":2239},[2225,156985,1520],{"class":2235},[2225,156987,3161],{"class":2291},[2225,156989,2789],{"class":2239},[2225,156991,2805],{"class":2235},[2225,156993,151370],{"class":2252},[2225,156995,2805],{"class":2235},[2225,156997,2298],{"class":2235},[2225,156999,2236],{"class":2235},[2225,157001,7070],{"class":2307},[2225,157003,2311],{"class":2235},[2225,157005,2249],{"class":2235},[2225,157007,151370],{"class":2252},[2225,157009,2805],{"class":2235},[2225,157011,2298],{"class":2235},[2225,157013,6361],{"class":2307},[2225,157015,2311],{"class":2235},[2225,157017,2249],{"class":2235},[2225,157019,28429],{"class":2252},[2225,157021,2805],{"class":2235},[2225,157023,2243],{"class":2235},[2225,157025,2362],{"class":2239},[2225,157027,157028],{"class":2227,"line":6587},[2225,157029,157030],{"class":2763},"\u002F\u002F → throws IllegalTransitionError: 'Cannot transition sales\u002Fs1 from \"invoiced\"\n",[2225,157032,157033],{"class":2227,"line":7174},[2225,157034,157035],{"class":2763},"\u002F\u002F   to \"draft\" — not a declared arc. Use withTransactions({ amendment: true,\n",[2225,157037,157038],{"class":2227,"line":7180},[2225,157039,157040],{"class":2763},"\u002F\u002F   reason }) with admin\u002Fowner role to override.'\n",[1111,157042,157043,157065,157082,157095],{},[1114,157044,157045,953,157048,11737,157051,157054,157055,157058,157059,157061,157062,1520],{},[950,157046,157047],{},"Insert",[941,157049,157050],{},"existing === null",[941,157052,157053],{},"incoming[field]"," must be in ",[941,157056,157057],{},"initial","\n(any value when ",[941,157060,157057],{}," is omitted); a violation reports ",[941,157063,157064],{},"from: '(none)'",[1114,157066,157067,157070,157071,157074,157075,157078,157079,157081],{},[950,157068,157069],{},"Update",": the arc ",[941,157072,157073],{},"(existing[field] → incoming[field])"," must be a\ndeclared edge, else ",[941,157076,157077],{},"IllegalTransitionError(collection, id, from, to)",".\nA state absent from the map (or mapped to ",[941,157080,7262],{},") is terminal.",[1114,157083,157084,157087,157088,157091,157092,157094],{},[950,157085,157086],{},"Idempotent",": a same-state write passes when ",[941,157089,157090],{},"allowIdempotent"," (default\n",[941,157093,11790],{},"), so a put that changes other fields without moving state is fine.",[1114,157096,157097,157100,157101,157103,157104,157106,157107,957],{},[950,157098,157099],{},"Override",": an ",[941,157102,154243],{}," tx by an authorized role skips the check and\nis ledgered — ",[941,157105,156242],{}," is the WORM special case (every state → ",[941,157108,7262],{},[1591,157110,157112],{"id":157111},"amendment-flow","Amendment flow",[2217,157114,157117],{"className":157115,"code":157116,"language":5672},[5670],"db.transaction({ amendment: true, reason }, async tx => {\n  tx.vault('books').collection('lines').put(...)\n  tx.vault('books').collection('lines').put(...)\n})\n\n  → 1. Validate reason (ValidationError if missing\u002Fempty)\n  → 2. First tx.vault(name): role check (admin | owner | custodian) — AmendmentForbiddenError fail-fast\n  → 3. beginAmendment() on the touched vault's guard registry\n  → 4. Writes buffered — check + frozenFields skipped; each write records\n       (before, after) plus (id, vBefore, vAfter)\n  → 5. Invariant runs at commit, over the full change-set — a throw rolls\n       back every staged write as InvariantError\n  → 6. Audit entry appended to the ledger (op: 'amendment') if a\n       history strategy is configured\n",[941,157118,157116],{"__ignoreMap":1037},[1591,157120,140868],{"id":140867},[937,157122,157123,125094,157125,997,157128,157130,157131,157133],{},[941,157124,155800],{},[950,157126,157127],{},"before encryption",[950,157129,11357],{}," the period-close check,\nas one stage in the ",[941,157132,15393],{}," write pipeline:",[2217,157135,157138],{"className":157136,"code":157137,"language":5672},[5670],"Collection.put\n  1. Permission check        — can this user write this collection at all?\n  2. GuardRegistry.check     ← this service\n  3. Period-close check      — is the record's business date in a closed period?\n  4. Schema validation\n  5. i18n auto-translate\n  6. Ref enforcement\n  7. Encrypt + store.put     — plaintext never reaches this line's argument\n  8. Ledger append\n",[941,157139,157137],{"__ignoreMap":1037},[1591,157141,120265],{"id":120264},[937,157143,157144,157145,157147,157148,2311],{},"Five error classes, all extending ",[941,157146,140888],{}," and re-exported from\n",[941,157149,73216],{},[1111,157151,157152,157162,157168,157175,157181],{},[1114,157153,157154,964,157157,4688,157159,157161],{},[941,157155,157156],{},"RecordLockedError(collection, id, reason)",[941,157158,154924],{},[941,157160,137880],{}," threw",[1114,157163,157164,157167],{},[941,157165,157166],{},"FieldFrozenError(collection, id, fields[])"," — a frozen field changed",[1114,157169,157170,964,157172,157174],{},[941,157171,157077],{},[941,157173,156634],{}," rejected an undeclared arc",[1114,157176,157177,157180],{},[941,157178,157179],{},"InvariantError(message)"," — an amendment invariant rejected the change-set",[1114,157182,157183,157186],{},[941,157184,157185],{},"AmendmentForbiddenError(userId, role)"," — caller's role can't open an amendment",[1022,157188,101425],{"id":101424},[1111,157190,157191,157197,157213],{},[1114,157192,157193,157196],{},[941,157194,157195],{},"createNoydb({ guardStrategies: [...] })"," is the only way to register guards;\nwithout it, no guard fires for any collection",[1114,157198,157199,157200,157203,157204,6789,157206,157208,157209,157212],{},"Amendment mode also needs at least one guard registered: opening\n",[941,157201,157202],{},"db.transaction({ amendment: true, reason })"," on a vault with no\n",[941,157205,154296],{},[941,157207,12881],{}," as soon as the body calls\n",[941,157210,157211],{},"tx.vault(name)"," — fail-fast, before any write runs. If guards ARE\nregistered but a touched collection has none of its own, writes to that\ncollection still commit inside the amendment; they just don't contribute\nto any invariant",[1114,157214,157215,1261,157217,1261,157219,2601,157221,1261,157223,140962,157226,140965],{},[941,157216,155649],{},[941,157218,155683],{},[941,157220,156641],{},[941,157222,155701],{},[941,157224,157225],{},"AmendmentForbiddenError",[941,157227,73216],{},[1022,157229,114684],{"id":114683},[1111,157231,157232,157239,157246],{},[1114,157233,157234,157236,157237],{},[950,157235,876],{}," — amendment mode is layered on ",[941,157238,140657],{},[1114,157240,157241,157243,157244,1381],{},[950,157242,697],{}," — successful amendments append a multi-record summary entry to\nthe hash-chained ledger (per-record entries always fire from ",[941,157245,15393],{},[1114,157247,157248,157250,157251,157254],{},[950,157249,776],{}," — guards run before the period seal, so a lock can preempt a\n",[941,157252,157253],{},"PeriodClosedError"," with a more specific reason",[1022,157256,6825],{"id":6824},[1111,157258,157259,157272,157288,157296,157302,157322,157330],{},[1114,157260,157261,157264,157265,157268,157269,157271],{},[950,157262,157263],{},"Guards see the pre-schema-validation incoming record."," If a schema\nvalidator coerces values (trims strings, applies defaults), the guard\nobserves the raw user-supplied shape; the stored shape is post-coercion.\nGuards should only consume fields declared in ",[941,157266,157267],{},"frozenFields.fields"," or\nread via ",[941,157270,138578],{}," — those paths are stable across schema transforms.",[1114,157273,157274,1324,157279,157281,157282,157284,157285,157287],{},[950,157275,157276,157278],{},[941,157277,154924],{}," never fires on delete.",[941,157280,155713],{}," runs only on\n",[941,157283,3161],{}," (insert + update); ",[941,157286,155719],{}," is the dedicated\ndelete-time hook, with the currently-persisted record as its argument.",[1114,157289,157290,157295],{},[950,157291,157292,157294],{},[941,157293,3164],{}," of a non-existent record is a no-op."," Guards are not consulted\nin that case; the call returns without error.",[1114,157297,157298,157301],{},[950,157299,157300],{},"Multiple guards on the same collection"," run in registration order;\nfirst throw wins (short-circuits remaining guards).",[1114,157303,157304,157307,157308,157311,157312,157315,157316,968,157318,4765,157320,1520],{},[950,157305,157306],{},"Amendment authorization is declarative-plus-enforced."," The\n",[941,157309,157310],{},"amendment.roles"," field on a guard is documentation for readers of the\nconfig — the actual check is the caller's\n",[1007,157313,157314],{"href":86452},"keyring role",", which must be\n",[941,157317,9901],{},[941,157319,9898],{},[941,157321,9904],{},[1114,157323,157324,157329],{},[950,157325,157326,157327,1520],{},"Amendment audit append is conditional on ",[941,157328,20172],{}," Vaults\nwithout history still enforce amendment rules (role check + invariant +\nrollback) but skip the multi-record summary entry.",[1114,157331,157332,157341,157342,157344],{},[950,157333,157334,997,157336,157340],{},[941,157335,22965],{},[1007,157337,157338],{"href":140989},[941,157339,58310],{}," skip\namendment entries"," — amendment ledger entries carry no ",[941,157343,14571],{},"\nof their own, so both the backup integrity check and point-in-time reads\ntreat them as opaque audit summaries rather than per-record state.",[1591,157346,140999],{"id":140998},[937,157348,157349,157350,157353,157354,157357,157358,157360],{},"Guard functions run on plaintext, after the ",[1007,157351,157352],{"href":60250},"encryption key is\nunwrapped",", inside the client — never inside the\nstorage backend. The store sees only ciphertext envelopes. The\n",[941,157355,157356],{},"ReadOnlyVaultFacade"," passed as ",[941,157359,138578],{}," decrypts on access; no\nplaintext ever reaches the store.",[1591,157362,157364,157366],{"id":157363},"readonlyvaultfacade-surface",[941,157365,157356],{}," surface",[2217,157368,157370],{"className":2219,"code":157369,"language":2221,"meta":1037,"style":1037},"ctx.vault.collection\u003CT>(name).get(id)    \u002F\u002F single record\nctx.vault.collection\u003CT>(name).list()     \u002F\u002F every record (decrypts all)\nctx.vault.collection\u003CT>(name).query()    \u002F\u002F chainable read-only builder\n",[941,157371,157372,157404,157434],{"__ignoreMap":1037},[2225,157373,157374,157377,157379,157381,157383,157385,157387,157389,157391,157394,157396,157398,157401],{"class":2227,"line":2228},[2225,157375,157376],{"class":2239},"ctx",[2225,157378,1520],{"class":2235},[2225,157380,2694],{"class":2239},[2225,157382,1520],{"class":2235},[2225,157384,6471],{"class":2291},[2225,157386,6474],{"class":2235},[2225,157388,11968],{"class":6342},[2225,157390,6480],{"class":2235},[2225,157392,157393],{"class":2239},"(name)",[2225,157395,1520],{"class":2235},[2225,157397,3158],{"class":2291},[2225,157399,157400],{"class":2239},"(id)    ",[2225,157402,157403],{"class":2763},"\u002F\u002F single record\n",[2225,157405,157406,157408,157410,157412,157414,157416,157418,157420,157422,157424,157426,157428,157431],{"class":2227,"line":1041},[2225,157407,157376],{"class":2239},[2225,157409,1520],{"class":2235},[2225,157411,2694],{"class":2239},[2225,157413,1520],{"class":2235},[2225,157415,6471],{"class":2291},[2225,157417,6474],{"class":2235},[2225,157419,11968],{"class":6342},[2225,157421,6480],{"class":2235},[2225,157423,157393],{"class":2239},[2225,157425,1520],{"class":2235},[2225,157427,3167],{"class":2291},[2225,157429,157430],{"class":2239},"()     ",[2225,157432,157433],{"class":2763},"\u002F\u002F every record (decrypts all)\n",[2225,157435,157436,157438,157440,157442,157444,157446,157448,157450,157452,157454,157456,157458,157460],{"class":2227,"line":1038},[2225,157437,157376],{"class":2239},[2225,157439,1520],{"class":2235},[2225,157441,2694],{"class":2239},[2225,157443,1520],{"class":2235},[2225,157445,6471],{"class":2291},[2225,157447,6474],{"class":2235},[2225,157449,11968],{"class":6342},[2225,157451,6480],{"class":2235},[2225,157453,157393],{"class":2239},[2225,157455,1520],{"class":2235},[2225,157457,5286],{"class":2291},[2225,157459,131240],{"class":2239},[2225,157461,157462],{"class":2763},"\u002F\u002F chainable read-only builder\n",[937,157464,157465,157467,157468,157470,157471,968,157473,968,157475,968,157477,1707,157479,968,157481,157483,157484,1261,157487,157490,157491,157493,157494,12101,157497,157499],{},[941,157466,24715],{}," returns the same ",[941,157469,24785],{}," builder used elsewhere in the\nlibrary. Its terminals (",[941,157472,3230],{},[941,157474,3233],{},[941,157476,3236],{},[941,157478,4449],{},[941,157480,27496],{},[941,157482,727],{},") are read-only — there is no ",[941,157485,157486],{},".update()",[941,157488,157489],{},".delete()","\non a ",[941,157492,46896],{},". Prefer ",[941,157495,157496],{},".query().aggregate({ ... })",[941,157498,138941],{}," +\nmanual reduce when enforcing Σ-style invariants: only the records the\npredicate touches get materialised, and the aggregate path is the same\none used by the rest of the library.",[2217,157501,157503],{"className":2219,"code":157502,"language":2221,"meta":1037,"style":1037},"\u002F\u002F Σ-over-siblings invariant — payment-allocation sum must not exceed payment\nimport { withGuard, InvariantError } from '@noy-db\u002Fhub\u002Fguards'\nimport { sum } from '@noy-db\u002Fhub\u002Freduce'\n\nconst allocationGuard = withGuard\u003CAllocation>({\n  collection: 'allocations',\n  check: async (incoming, { vault, existing }) => {\n    const payment = await vault.collection\u003CPayment>('payments').get(incoming.paymentId)\n    const { total } = await vault\n      .collection\u003CAllocation>('allocations')\n      .query()\n      .where('paymentId', '==', incoming.paymentId)\n      .aggregate({ total: sum('appliedAmount') })\n      .run()\n    const otherTotal = total - (existing?.appliedAmount ?? 0)\n    if (otherTotal + incoming.appliedAmount > payment.amount) {\n      throw new InvariantError('allocation total would exceed payment amount')\n    }\n  },\n})\n\nawait vault.collection\u003CAllocation>('allocations').put('a1', {\n  id: 'a1', paymentId: 'p1', appliedAmount: 1_000,\n})\n\u002F\u002F → throws InvariantError: \"allocation total would exceed payment amount\"\n",[941,157504,157505,157510,157532,157550,157554,157575,157589,157617,157663,157679,157701,157709,157741,157771,157779,157806,157835,157854,157858,157862,157868,157872,157915,157949,157955],{"__ignoreMap":1037},[2225,157506,157507],{"class":2227,"line":2228},[2225,157508,157509],{"class":2763},"\u002F\u002F Σ-over-siblings invariant — payment-allocation sum must not exceed payment\n",[2225,157511,157512,157514,157516,157518,157520,157522,157524,157526,157528,157530],{"class":2227,"line":1041},[2225,157513,2232],{"class":2231},[2225,157515,2236],{"class":2235},[2225,157517,154367],{"class":2239},[2225,157519,2298],{"class":2235},[2225,157521,154377],{"class":2239},[2225,157523,2243],{"class":2235},[2225,157525,2246],{"class":2231},[2225,157527,2249],{"class":2235},[2225,157529,73216],{"class":2252},[2225,157531,2256],{"class":2235},[2225,157533,157534,157536,157538,157540,157542,157544,157546,157548],{"class":2227,"line":1038},[2225,157535,2232],{"class":2231},[2225,157537,2236],{"class":2235},[2225,157539,27298],{"class":2239},[2225,157541,2243],{"class":2235},[2225,157543,2246],{"class":2231},[2225,157545,2249],{"class":2235},[2225,157547,27257],{"class":2252},[2225,157549,2256],{"class":2235},[2225,157551,157552],{"class":2227,"line":2288},[2225,157553,2285],{"emptyLinePlaceholder":2284},[2225,157555,157556,157558,157561,157563,157565,157567,157569,157571,157573],{"class":2227,"line":2304},[2225,157557,2775],{"class":2774},[2225,157559,157560],{"class":2239}," allocationGuard ",[2225,157562,2781],{"class":2235},[2225,157564,154367],{"class":2291},[2225,157566,6474],{"class":2235},[2225,157568,138593],{"class":6342},[2225,157570,6480],{"class":2235},[2225,157572,2789],{"class":2239},[2225,157574,4200],{"class":2235},[2225,157576,157577,157579,157581,157583,157585,157587],{"class":2227,"line":2356},[2225,157578,30275],{"class":2307},[2225,157580,2311],{"class":2235},[2225,157582,2249],{"class":2235},[2225,157584,138620],{"class":2252},[2225,157586,2805],{"class":2235},[2225,157588,1707],{"class":2235},[2225,157590,157591,157593,157595,157597,157599,157601,157603,157605,157607,157609,157611,157613,157615],{"class":2227,"line":2830},[2225,157592,154453],{"class":2291},[2225,157594,2311],{"class":2235},[2225,157596,38717],{"class":2774},[2225,157598,953],{"class":2235},[2225,157600,154462],{"class":7052},[2225,157602,2298],{"class":2235},[2225,157604,2236],{"class":2235},[2225,157606,6466],{"class":7052},[2225,157608,2298],{"class":2235},[2225,157610,80706],{"class":7052},[2225,157612,43776],{"class":2235},[2225,157614,8116],{"class":2774},[2225,157616,2301],{"class":2235},[2225,157618,157619,157621,157623,157625,157627,157629,157631,157633,157635,157637,157639,157641,157643,157645,157647,157649,157651,157653,157655,157657,157659,157661],{"class":2227,"line":2861},[2225,157620,80048],{"class":2774},[2225,157622,138705],{"class":2239},[2225,157624,6346],{"class":2235},[2225,157626,2784],{"class":2231},[2225,157628,6466],{"class":2239},[2225,157630,1520],{"class":2235},[2225,157632,6471],{"class":2291},[2225,157634,6474],{"class":2235},[2225,157636,138724],{"class":6342},[2225,157638,6480],{"class":2235},[2225,157640,2789],{"class":2307},[2225,157642,2805],{"class":2235},[2225,157644,32579],{"class":2252},[2225,157646,2805],{"class":2235},[2225,157648,1381],{"class":2307},[2225,157650,1520],{"class":2235},[2225,157652,3158],{"class":2291},[2225,157654,2789],{"class":2307},[2225,157656,154462],{"class":2239},[2225,157658,1520],{"class":2235},[2225,157660,138749],{"class":2239},[2225,157662,2362],{"class":2307},[2225,157664,157665,157667,157669,157671,157673,157675,157677],{"class":2227,"line":4246},[2225,157666,80048],{"class":2774},[2225,157668,2236],{"class":2235},[2225,157670,27452],{"class":2239},[2225,157672,2243],{"class":2235},[2225,157674,6346],{"class":2235},[2225,157676,2784],{"class":2231},[2225,157678,68845],{"class":2239},[2225,157680,157681,157683,157685,157687,157689,157691,157693,157695,157697,157699],{"class":2227,"line":4253},[2225,157682,80721],{"class":2235},[2225,157684,6471],{"class":2291},[2225,157686,6474],{"class":2235},[2225,157688,138593],{"class":6342},[2225,157690,6480],{"class":2235},[2225,157692,2789],{"class":2307},[2225,157694,2805],{"class":2235},[2225,157696,138620],{"class":2252},[2225,157698,2805],{"class":2235},[2225,157700,2362],{"class":2307},[2225,157702,157703,157705,157707],{"class":2227,"line":4259},[2225,157704,80721],{"class":2235},[2225,157706,5286],{"class":2291},[2225,157708,8146],{"class":2307},[2225,157710,157711,157713,157715,157717,157719,157721,157723,157725,157727,157729,157731,157733,157735,157737,157739],{"class":2227,"line":6548},[2225,157712,80721],{"class":2235},[2225,157714,3016],{"class":2291},[2225,157716,2789],{"class":2307},[2225,157718,2805],{"class":2235},[2225,157720,138749],{"class":2252},[2225,157722,2805],{"class":2235},[2225,157724,2298],{"class":2235},[2225,157726,2249],{"class":2235},[2225,157728,21901],{"class":2252},[2225,157730,2805],{"class":2235},[2225,157732,2298],{"class":2235},[2225,157734,154567],{"class":2239},[2225,157736,1520],{"class":2235},[2225,157738,138749],{"class":2239},[2225,157740,2362],{"class":2307},[2225,157742,157743,157745,157747,157749,157751,157753,157755,157757,157759,157761,157763,157765,157767,157769],{"class":2227,"line":6576},[2225,157744,80721],{"class":2235},[2225,157746,4449],{"class":2291},[2225,157748,2789],{"class":2307},[2225,157750,2792],{"class":2235},[2225,157752,27452],{"class":2307},[2225,157754,2311],{"class":2235},[2225,157756,27298],{"class":2291},[2225,157758,2789],{"class":2307},[2225,157760,2805],{"class":2235},[2225,157762,138896],{"class":2252},[2225,157764,2805],{"class":2235},[2225,157766,8495],{"class":2307},[2225,157768,2359],{"class":2235},[2225,157770,2362],{"class":2307},[2225,157772,157773,157775,157777],{"class":2227,"line":6582},[2225,157774,80721],{"class":2235},[2225,157776,61526],{"class":2291},[2225,157778,8146],{"class":2307},[2225,157780,157781,157783,157786,157788,157790,157792,157794,157796,157798,157800,157802,157804],{"class":2227,"line":6587},[2225,157782,80048],{"class":2774},[2225,157784,157785],{"class":2239}," otherTotal",[2225,157787,6346],{"class":2235},[2225,157789,27452],{"class":2239},[2225,157791,28754],{"class":2235},[2225,157793,953],{"class":2307},[2225,157795,80763],{"class":2239},[2225,157797,8457],{"class":2235},[2225,157799,138896],{"class":2239},[2225,157801,8239],{"class":2235},[2225,157803,8464],{"class":6527},[2225,157805,2362],{"class":2307},[2225,157807,157808,157810,157812,157815,157817,157819,157821,157823,157825,157827,157829,157831,157833],{"class":2227,"line":7174},[2225,157809,80758],{"class":2231},[2225,157811,953],{"class":2307},[2225,157813,157814],{"class":2239},"otherTotal",[2225,157816,154705],{"class":2235},[2225,157818,154567],{"class":2239},[2225,157820,1520],{"class":2235},[2225,157822,138896],{"class":2239},[2225,157824,36500],{"class":2235},[2225,157826,138705],{"class":2239},[2225,157828,1520],{"class":2235},[2225,157830,6617],{"class":2239},[2225,157832,8495],{"class":2307},[2225,157834,4200],{"class":2235},[2225,157836,157837,157839,157841,157843,157845,157847,157850,157852],{"class":2227,"line":7180},[2225,157838,154550],{"class":2231},[2225,157840,8130],{"class":2235},[2225,157842,154377],{"class":2291},[2225,157844,2789],{"class":2307},[2225,157846,2805],{"class":2235},[2225,157848,157849],{"class":2252},"allocation total would exceed payment amount",[2225,157851,2805],{"class":2235},[2225,157853,2362],{"class":2307},[2225,157855,157856],{"class":2227,"line":7220},[2225,157857,60103],{"class":2235},[2225,157859,157860],{"class":2227,"line":7225},[2225,157861,19017],{"class":2235},[2225,157863,157864,157866],{"class":2227,"line":7231},[2225,157865,2359],{"class":2235},[2225,157867,2362],{"class":2239},[2225,157869,157870],{"class":2227,"line":7267},[2225,157871,2285],{"emptyLinePlaceholder":2284},[2225,157873,157874,157876,157878,157880,157882,157884,157886,157888,157890,157892,157894,157896,157898,157900,157902,157904,157906,157909,157911,157913],{"class":2227,"line":7272},[2225,157875,6500],{"class":2231},[2225,157877,6466],{"class":2239},[2225,157879,1520],{"class":2235},[2225,157881,6471],{"class":2291},[2225,157883,6474],{"class":2235},[2225,157885,138593],{"class":6342},[2225,157887,6480],{"class":2235},[2225,157889,2789],{"class":2239},[2225,157891,2805],{"class":2235},[2225,157893,138620],{"class":2252},[2225,157895,2805],{"class":2235},[2225,157897,1381],{"class":2239},[2225,157899,1520],{"class":2235},[2225,157901,3161],{"class":2291},[2225,157903,2789],{"class":2239},[2225,157905,2805],{"class":2235},[2225,157907,157908],{"class":2252},"a1",[2225,157910,2805],{"class":2235},[2225,157912,2298],{"class":2235},[2225,157914,2301],{"class":2235},[2225,157916,157917,157919,157921,157923,157925,157927,157929,157931,157933,157935,157937,157939,157941,157943,157945,157947],{"class":2227,"line":7278},[2225,157918,28371],{"class":2307},[2225,157920,2311],{"class":2235},[2225,157922,2249],{"class":2235},[2225,157924,157908],{"class":2252},[2225,157926,2805],{"class":2235},[2225,157928,2298],{"class":2235},[2225,157930,140440],{"class":2307},[2225,157932,2311],{"class":2235},[2225,157934,2249],{"class":2235},[2225,157936,150929],{"class":2252},[2225,157938,2805],{"class":2235},[2225,157940,2298],{"class":2235},[2225,157942,140453],{"class":2307},[2225,157944,2311],{"class":2235},[2225,157946,60656],{"class":6527},[2225,157948,1707],{"class":2235},[2225,157950,157951,157953],{"class":2227,"line":7303},[2225,157952,2359],{"class":2235},[2225,157954,2362],{"class":2239},[2225,157956,157957],{"class":2227,"line":7308},[2225,157958,157959],{"class":2763},"\u002F\u002F → throws InvariantError: \"allocation total would exceed payment amount\"\n",[937,157961,157962,157963,157966,157967,157969],{},"Aggregates require the ",[941,157964,157965],{},"reduceStrategy: withReduce()"," opt-in (the\nsame opt-in everything else in the query DSL respects) — ",[941,157968,24715],{},"\nitself is always present on the facade.",[1591,157971,157973],{"id":157972},"audit-entry-shape","Audit-entry shape",[2217,157975,157977],{"className":2219,"code":157976,"language":2221,"meta":1037,"style":1037},"{\n  op: 'amendment',\n  actor: 'alice',\n  ts: '2026-05-18T...',\n  collection: '',  \u002F\u002F amendment is multi-record; collection\u002Fid are empty\n  id: '',\n  version: 0,\n  payloadHash: '',  \u002F\u002F per-record entries carry real hashes\n  amendment: {\n    reason: 'correct split between travel and meals',\n    role: 'admin',\n    changes: [\n      { collection: 'disbursements', id: 'd1', vBefore: 2, vAfter: 3 },\n      { collection: 'disbursements', id: 'd2', vBefore: 1, vAfter: 2 },\n    ],\n    invariantsPassed: ['disbursements'],\n  },\n}\n",[941,157978,157979,157983,157998,158013,158029,158042,158052,158063,158077,158085,158101,158116,158125,158171,158216,158223,158242,158246],{"__ignoreMap":1037},[2225,157980,157981],{"class":2227,"line":2228},[2225,157982,4200],{"class":2235},[2225,157984,157985,157988,157990,157992,157994,157996],{"class":2227,"line":1041},[2225,157986,157987],{"class":6342},"  op",[2225,157989,2311],{"class":2235},[2225,157991,2249],{"class":2235},[2225,157993,154243],{"class":2252},[2225,157995,2805],{"class":2235},[2225,157997,1707],{"class":2235},[2225,157999,158000,158003,158005,158007,158009,158011],{"class":2227,"line":1038},[2225,158001,158002],{"class":6342},"  actor",[2225,158004,2311],{"class":2235},[2225,158006,2249],{"class":2235},[2225,158008,6408],{"class":2252},[2225,158010,2805],{"class":2235},[2225,158012,1707],{"class":2235},[2225,158014,158015,158018,158020,158022,158025,158027],{"class":2227,"line":2288},[2225,158016,158017],{"class":6342},"  ts",[2225,158019,2311],{"class":2235},[2225,158021,2249],{"class":2235},[2225,158023,158024],{"class":2252},"2026-05-18T...",[2225,158026,2805],{"class":2235},[2225,158028,1707],{"class":2235},[2225,158030,158031,158033,158035,158037,158039],{"class":2227,"line":2304},[2225,158032,30275],{"class":6342},[2225,158034,2311],{"class":2235},[2225,158036,12500],{"class":2235},[2225,158038,2298],{"class":2235},[2225,158040,158041],{"class":2763},"  \u002F\u002F amendment is multi-record; collection\u002Fid are empty\n",[2225,158043,158044,158046,158048,158050],{"class":2227,"line":2356},[2225,158045,28371],{"class":6342},[2225,158047,2311],{"class":2235},[2225,158049,12500],{"class":2235},[2225,158051,1707],{"class":2235},[2225,158053,158054,158057,158059,158061],{"class":2227,"line":2830},[2225,158055,158056],{"class":6342},"  version",[2225,158058,2311],{"class":2235},[2225,158060,8464],{"class":6527},[2225,158062,1707],{"class":2235},[2225,158064,158065,158068,158070,158072,158074],{"class":2227,"line":2861},[2225,158066,158067],{"class":6342},"  payloadHash",[2225,158069,2311],{"class":2235},[2225,158071,12500],{"class":2235},[2225,158073,2298],{"class":2235},[2225,158075,158076],{"class":2763},"  \u002F\u002F per-record entries carry real hashes\n",[2225,158078,158079,158081,158083],{"class":2227,"line":4246},[2225,158080,154595],{"class":6342},[2225,158082,2311],{"class":2235},[2225,158084,2301],{"class":2235},[2225,158086,158087,158090,158092,158094,158097,158099],{"class":2227,"line":4253},[2225,158088,158089],{"class":6342},"    reason",[2225,158091,2311],{"class":2235},[2225,158093,2249],{"class":2235},[2225,158095,158096],{"class":2252},"correct split between travel and meals",[2225,158098,2805],{"class":2235},[2225,158100,1707],{"class":2235},[2225,158102,158103,158106,158108,158110,158112,158114],{"class":2227,"line":4259},[2225,158104,158105],{"class":6342},"    role",[2225,158107,2311],{"class":2235},[2225,158109,2249],{"class":2235},[2225,158111,9901],{"class":2252},[2225,158113,2805],{"class":2235},[2225,158115,1707],{"class":2235},[2225,158117,158118,158121,158123],{"class":2227,"line":6548},[2225,158119,158120],{"class":6342},"    changes",[2225,158122,2311],{"class":2235},[2225,158124,32491],{"class":2307},[2225,158126,158127,158129,158131,158133,158135,158137,158139,158141,158143,158145,158147,158149,158151,158153,158156,158158,158160,158162,158165,158167,158169],{"class":2227,"line":6576},[2225,158128,88593],{"class":2235},[2225,158130,7061],{"class":2307},[2225,158132,2311],{"class":2235},[2225,158134,2249],{"class":2235},[2225,158136,154444],{"class":2252},[2225,158138,2805],{"class":2235},[2225,158140,2298],{"class":2235},[2225,158142,7070],{"class":2307},[2225,158144,2311],{"class":2235},[2225,158146,2249],{"class":2235},[2225,158148,154976],{"class":2252},[2225,158150,2805],{"class":2235},[2225,158152,2298],{"class":2235},[2225,158154,158155],{"class":2307}," vBefore",[2225,158157,2311],{"class":2235},[2225,158159,18961],{"class":6527},[2225,158161,2298],{"class":2235},[2225,158163,158164],{"class":2307}," vAfter",[2225,158166,2311],{"class":2235},[2225,158168,14460],{"class":6527},[2225,158170,2353],{"class":2235},[2225,158172,158173,158175,158177,158179,158181,158183,158185,158187,158189,158191,158193,158196,158198,158200,158202,158204,158206,158208,158210,158212,158214],{"class":2227,"line":6582},[2225,158174,88593],{"class":2235},[2225,158176,7061],{"class":2307},[2225,158178,2311],{"class":2235},[2225,158180,2249],{"class":2235},[2225,158182,154444],{"class":2252},[2225,158184,2805],{"class":2235},[2225,158186,2298],{"class":2235},[2225,158188,7070],{"class":2307},[2225,158190,2311],{"class":2235},[2225,158192,2249],{"class":2235},[2225,158194,158195],{"class":2252},"d2",[2225,158197,2805],{"class":2235},[2225,158199,2298],{"class":2235},[2225,158201,158155],{"class":2307},[2225,158203,2311],{"class":2235},[2225,158205,9377],{"class":6527},[2225,158207,2298],{"class":2235},[2225,158209,158164],{"class":2307},[2225,158211,2311],{"class":2235},[2225,158213,18961],{"class":6527},[2225,158215,2353],{"class":2235},[2225,158217,158218,158221],{"class":2227,"line":6587},[2225,158219,158220],{"class":2307},"    ]",[2225,158222,1707],{"class":2235},[2225,158224,158225,158228,158230,158232,158234,158236,158238,158240],{"class":2227,"line":7174},[2225,158226,158227],{"class":6342},"    invariantsPassed",[2225,158229,2311],{"class":2235},[2225,158231,8667],{"class":2307},[2225,158233,2805],{"class":2235},[2225,158235,154444],{"class":2252},[2225,158237,2805],{"class":2235},[2225,158239,8803],{"class":2307},[2225,158241,1707],{"class":2235},[2225,158243,158244],{"class":2227,"line":7180},[2225,158245,19017],{"class":2235},[2225,158247,158248],{"class":2227,"line":7220},[2225,158249,7351],{"class":2235},[937,158251,158252,158253,158256],{},"Visible via ",[941,158254,158255],{},"vault.ledger().entries()"," like every other ledger entry.",[1591,158258,158260],{"id":158259},"not-supported","Not supported",[1111,158262,158263,158269,158278,158294,158304],{},[1114,158264,158265,158268],{},[950,158266,158267],{},"Per-field amendment."," An amendment opens for a whole record write —\nthere's no way to amend only specific frozen fields while leaving the\nrest of a locked record's rule enforced.",[1114,158270,158271,158274,158275,158277],{},[950,158272,158273],{},"Time-limited amendment windows."," An amendment transaction has no\nexpiry of its own; it's an ordinary transaction with the ",[941,158276,154243],{}," flag\nset, open for as long as the transaction body runs.",[1114,158279,158280,158283,158284,1261,158286,1261,158288,158290,158291,158293],{},[950,158281,158282],{},"Cross-vault guards."," A guard's ",[941,158285,154924],{},[941,158287,137880],{},[941,158289,156228],{}," reads\nonly its own vault through ",[941,158292,138578],{}," — it cannot read a different\nvault's collections.",[1114,158295,158296,158303],{},[950,158297,158298,158299,1261,158301,1520],{},"Guards on ",[941,158300,3170],{},[941,158302,3173],{}," Bulk load\u002Fsave bypasses the guard\npipeline entirely.",[1114,158305,158306,1324,158309,158311],{},[950,158307,158308],{},"Per-guard role lists.",[941,158310,157310],{}," on a guard is documentation\nfor the reader — actual authorization is the single vault-wide keyring\nrole check described above, not a per-guard list.",[1022,158313,2621],{"id":2620},[1111,158315,158316,158323,158328,158333],{},[1114,158317,158318,158320,158321],{},[1007,158319,876],{"href":140654}," — amendment mode's underlying ",[941,158322,47889],{},[1114,158324,158325,158327],{},[1007,158326,697],{"href":140989}," — the ledger that amendment entries append to",[1114,158329,158330,158332],{},[1007,158331,776],{"href":154260}," — the period-close check guards run before",[1114,158334,141161,158335,997,158341,1520],{},[1007,158336,158338],{"href":93517,"rel":158337},[3371],[941,158339,158340],{},"79-with-guard",[1007,158342,158344],{"href":93891,"rel":158343},[3371],[941,158345,158346],{},"101-transition-guard",[2645,158348,158349],{},"html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .sHdIc, html code.shiki .sHdIc{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#EEFFFF;--shiki-default-font-style:italic;--shiki-dark:#BABED8;--shiki-dark-font-style:italic}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sfNiH, html code.shiki .sfNiH{--shiki-light:#FF5370;--shiki-default:#FF9CAC;--shiki-dark:#FF9CAC}",{"title":1037,"searchDepth":1038,"depth":1038,"links":158351},[158352,158353,158354,158355,158356,158361,158372,158373,158374,158381],{"id":29856,"depth":1041,"text":29857},{"id":111659,"depth":1041,"text":111660},{"id":70092,"depth":1041,"text":70093},{"id":99863,"depth":1041,"text":99864},{"id":29892,"depth":1041,"text":29893,"children":158357},[158358,158359,158360],{"id":154918,"depth":1038,"text":154919},{"id":155041,"depth":1038,"text":155042},{"id":155370,"depth":1038,"text":155371},{"id":41259,"depth":1041,"text":101012,"children":158362},[158363,158365,158366,158368,158369,158370,158371],{"id":155725,"depth":1038,"text":158364},"onDelete bypass paths",{"id":155777,"depth":1038,"text":155778},{"id":156239,"depth":1038,"text":158367},"immutableGuard and amendmentInvariant",{"id":156626,"depth":1038,"text":156629},{"id":157111,"depth":1038,"text":157112},{"id":140867,"depth":1038,"text":140868},{"id":120264,"depth":1038,"text":120265},{"id":101424,"depth":1041,"text":101425},{"id":114683,"depth":1041,"text":114684},{"id":6824,"depth":1041,"text":6825,"children":158375},[158376,158377,158379,158380],{"id":140998,"depth":1038,"text":140999},{"id":157363,"depth":1038,"text":158378},"ReadOnlyVaultFacade surface",{"id":157972,"depth":1038,"text":157973},{"id":158259,"depth":1038,"text":158260},{"id":2620,"depth":1041,"text":2621},"Per-record locks, per-field freezes, and an audited amendment override for business rules stricter than a plain write permission.",{},{"title":693,"description":158382},"2KjkET4NCxgH_B44i7vRP6ki2Q8zzpuxASP-fr_TkyI",{"id":158387,"title":697,"access":12,"apiToc":929,"body":158388,"category":1042,"description":160853,"extension":1044,"meta":160854,"navigation":1042,"nest":1047,"path":698,"seo":160855,"sinceVersion":1042,"status":1049,"stem":699,"subtitle":1042,"tier":1050,"tocExternal":1042,"__hash__":160856},"docs\u002Fservices\u002Fhistory.md",{"type":931,"value":158389,"toc":160825},[158390,158392,158395,158397,158425,158427,158449,158451,158471,158473,158609,158612,158699,158703,158717,158820,158846,158849,158853,158862,158905,158919,158945,158947,158951,159187,159378,159383,159514,159518,159527,159556,160026,160060,160070,160074,160082,160166,160173,160177,160189,160261,160275,160303,160305,160311,160443,160448,160539,160541,160577,160581,160584,160668,160670,160704,160706,160737,160739,160777,160779,160822],[934,158391,697],{"id":697},[46871,158393],{":rows":158394},"[{\"label\":\"Subpath\",\"value\":\"`@noy-db\u002Fhub\u002Fhistory`\"},{\"label\":\"Factory\",\"value\":\"`withHistory()`\"},{\"label\":\"Layer\",\"value\":\"`with-commit`\"},{\"label\":\"Cluster\",\"value\":\"B — Write & Mutate\"},{\"label\":\"LOC cost\",\"value\":\"~1,880 (off-bundle when not opted in)\"}]",[1022,158396,29857],{"id":29856},[937,158398,158399,158400,158402,158403,158405,158406,1261,158408,158410,158411,158413,158414,158416,158417,158419,158420,158422,158423,1520],{},"This service keeps a full audit trail of every write to a ",[950,158401,2694],{}," — noy-db's\nend-to-end-encrypted document store (see ",[1007,158404,65376],{"href":48},").\nOnce enabled, every successful ",[941,158407,3161],{},[941,158409,3164],{}," archives a complete encrypted snapshot of the\nrecord's ",[1092,158412,139933],{}," state and appends an entry to a hash-chained, tamper-evident ",[950,158415,20923],{}," — a\nsequential log where each entry embeds the hash of the entry before it, so editing, deleting, or\nreordering a past entry breaks the chain and is detectable by ",[941,158418,117180],{},". On top of that ledger the\nservice adds JSON Patch–based deltas (compact diffs between consecutive versions), typed\nfield-level diffing, one-call revert, and ",[941,158421,58310],{}," — a read-only view of the vault as\nit stood at any past instant. Because it's opt-in, none of the ~1,880 lines behind it enter your\napplication bundle unless you import ",[941,158424,58226],{},[1022,158426,111660],{"id":111659},[1111,158428,158429,158432,158435,158440,158446],{},[1114,158430,158431],{},"Compliance \u002F audit trails — \"who changed this record, and when\"",[1114,158433,158434],{},"Undo \u002F revert UX",[1114,158436,158437,158438],{},"\"What did this record look like on date X\" — point-in-time reads via ",[941,158439,21862],{},[1114,158441,158442,158443,158445],{},"Tamper detection — ",[941,158444,58313],{}," walks the ledger and cross-checks it against\nthe stored data",[1114,158447,158448],{},"Diff visualisation between two versions of a record",[1022,158450,70093],{"id":70092},[1111,158452,158453],{},[1114,158454,158455,964,158457,107496,158459,968,158462,968,158464,968,158466,2462,158468,158470],{},[950,158456,107493],{},[941,158458,6719],{},[941,158460,158461],{},"history()",[941,158463,21843],{},[941,158465,21849],{},[941,158467,21846],{},[941,158469,21862],{}," on every collection.",[1022,158472,99864],{"id":99863},[2217,158474,158476],{"className":2219,"code":158475,"language":2221,"meta":1037,"style":1037},"import { createNoydb } from '@noy-db\u002Fhub'\nimport { withHistory } from '@noy-db\u002Fhub\u002Fhistory'\n\nconst db = await createNoydb({\n  user: 'firm',\n  secret: 'firm-secret-2026',   \u002F\u002F omit `store` to use the built-in in-memory store\n  historyStrategy: withHistory(),\n})\nconst vault = await db.openVault('books')\n",[941,158477,158478,158496,158514,158518,158534,158548,158565,158577,158583],{"__ignoreMap":1037},[2225,158479,158480,158482,158484,158486,158488,158490,158492,158494],{"class":2227,"line":2228},[2225,158481,2232],{"class":2231},[2225,158483,2236],{"class":2235},[2225,158485,2744],{"class":2239},[2225,158487,2243],{"class":2235},[2225,158489,2246],{"class":2231},[2225,158491,2249],{"class":2235},[2225,158493,1086],{"class":2252},[2225,158495,2256],{"class":2235},[2225,158497,158498,158500,158502,158504,158506,158508,158510,158512],{"class":2227,"line":1041},[2225,158499,2232],{"class":2231},[2225,158501,2236],{"class":2235},[2225,158503,4142],{"class":2239},[2225,158505,2243],{"class":2235},[2225,158507,2246],{"class":2231},[2225,158509,2249],{"class":2235},[2225,158511,4151],{"class":2252},[2225,158513,2256],{"class":2235},[2225,158515,158516],{"class":2227,"line":1038},[2225,158517,2285],{"emptyLinePlaceholder":2284},[2225,158519,158520,158522,158524,158526,158528,158530,158532],{"class":2227,"line":2288},[2225,158521,2775],{"class":2774},[2225,158523,2778],{"class":2239},[2225,158525,2781],{"class":2235},[2225,158527,2784],{"class":2231},[2225,158529,2744],{"class":2291},[2225,158531,2789],{"class":2239},[2225,158533,4200],{"class":2235},[2225,158535,158536,158538,158540,158542,158544,158546],{"class":2227,"line":2304},[2225,158537,4205],{"class":2307},[2225,158539,2311],{"class":2235},[2225,158541,2249],{"class":2235},[2225,158543,39709],{"class":2252},[2225,158545,2805],{"class":2235},[2225,158547,1707],{"class":2235},[2225,158549,158550,158552,158554,158556,158558,158560,158562],{"class":2227,"line":2356},[2225,158551,10423],{"class":2307},[2225,158553,2311],{"class":2235},[2225,158555,2249],{"class":2235},[2225,158557,65709],{"class":2252},[2225,158559,2805],{"class":2235},[2225,158561,2298],{"class":2235},[2225,158563,158564],{"class":2763},"   \u002F\u002F omit `store` to use the built-in in-memory store\n",[2225,158566,158567,158569,158571,158573,158575],{"class":2227,"line":2830},[2225,158568,4221],{"class":2307},[2225,158570,2311],{"class":2235},[2225,158572,4142],{"class":2291},[2225,158574,4228],{"class":2239},[2225,158576,1707],{"class":2235},[2225,158578,158579,158581],{"class":2227,"line":2861},[2225,158580,2359],{"class":2235},[2225,158582,2362],{"class":2239},[2225,158584,158585,158587,158589,158591,158593,158595,158597,158599,158601,158603,158605,158607],{"class":2227,"line":4246},[2225,158586,2775],{"class":2774},[2225,158588,2835],{"class":2239},[2225,158590,2781],{"class":2235},[2225,158592,2784],{"class":2231},[2225,158594,2842],{"class":2239},[2225,158596,1520],{"class":2235},[2225,158598,2847],{"class":2291},[2225,158600,2789],{"class":2239},[2225,158602,2805],{"class":2235},[2225,158604,65761],{"class":2252},[2225,158606,2805],{"class":2235},[2225,158608,2362],{"class":2239},[937,158610,158611],{},"Vault-wide retention caps how many snapshots pile up per record:",[2217,158613,158615],{"className":2219,"code":158614,"language":2221,"meta":1037,"style":1037},"const db = await createNoydb({\n  user: 'firm',\n  secret: 'firm-secret-2026',\n  historyStrategy: withHistory(),\n  history: { maxVersions: 50 },   \u002F\u002F oldest snapshot pruned once a record exceeds 50 versions\n})\n",[941,158616,158617,158633,158647,158661,158673,158693],{"__ignoreMap":1037},[2225,158618,158619,158621,158623,158625,158627,158629,158631],{"class":2227,"line":2228},[2225,158620,2775],{"class":2774},[2225,158622,2778],{"class":2239},[2225,158624,2781],{"class":2235},[2225,158626,2784],{"class":2231},[2225,158628,2744],{"class":2291},[2225,158630,2789],{"class":2239},[2225,158632,4200],{"class":2235},[2225,158634,158635,158637,158639,158641,158643,158645],{"class":2227,"line":1041},[2225,158636,4205],{"class":2307},[2225,158638,2311],{"class":2235},[2225,158640,2249],{"class":2235},[2225,158642,39709],{"class":2252},[2225,158644,2805],{"class":2235},[2225,158646,1707],{"class":2235},[2225,158648,158649,158651,158653,158655,158657,158659],{"class":2227,"line":1038},[2225,158650,10423],{"class":2307},[2225,158652,2311],{"class":2235},[2225,158654,2249],{"class":2235},[2225,158656,65709],{"class":2252},[2225,158658,2805],{"class":2235},[2225,158660,1707],{"class":2235},[2225,158662,158663,158665,158667,158669,158671],{"class":2227,"line":2288},[2225,158664,4221],{"class":2307},[2225,158666,2311],{"class":2235},[2225,158668,4142],{"class":2291},[2225,158670,4228],{"class":2239},[2225,158672,1707],{"class":2235},[2225,158674,158675,158677,158679,158681,158684,158686,158688,158690],{"class":2227,"line":2304},[2225,158676,150026],{"class":2307},[2225,158678,2311],{"class":2235},[2225,158680,2236],{"class":2235},[2225,158682,158683],{"class":2307}," maxVersions",[2225,158685,2311],{"class":2235},[2225,158687,37210],{"class":6527},[2225,158689,10965],{"class":2235},[2225,158691,158692],{"class":2763},"   \u002F\u002F oldest snapshot pruned once a record exceeds 50 versions\n",[2225,158694,158695,158697],{"class":2227,"line":2356},[2225,158696,2359],{"class":2235},[2225,158698,2362],{"class":2239},[1591,158700,158702],{"id":158701},"per-collection-scoping","Per-collection scoping",[937,158704,158705,158706,158708,158709,158711,158712,77939,158714,158716],{},"By default ",[941,158707,6719],{}," is all-or-nothing: every collection gets per-record snapshots ",[950,158710,14128],{},"\nappends to the vault-wide tamper ledger. Pass a per-collection ",[941,158713,22798],{},[941,158715,2138],{}," to override the vault-wide config for that collection only (wholesale, not\nmerged):",[2217,158718,158720],{"className":2219,"code":158719,"language":2221,"meta":1037,"style":1037},"\u002F\u002F Filed legal records — full audit (snapshots + tamper ledger). This is the default.\nconst receipts = vault.collection('receipts')\n\n\u002F\u002F High-churn operational collection — no snapshots, no ledger entries.\nconst scratch = vault.collection('scratch', {\n  historyConfig: { enabled: false, ledger: false },\n})\n",[941,158721,158722,158727,158751,158755,158760,158788,158814],{"__ignoreMap":1037},[2225,158723,158724],{"class":2227,"line":2228},[2225,158725,158726],{"class":2763},"\u002F\u002F Filed legal records — full audit (snapshots + tamper ledger). This is the default.\n",[2225,158728,158729,158731,158733,158735,158737,158739,158741,158743,158745,158747,158749],{"class":2227,"line":1041},[2225,158730,2775],{"class":2774},[2225,158732,27822],{"class":2239},[2225,158734,2781],{"class":2235},[2225,158736,6466],{"class":2239},[2225,158738,1520],{"class":2235},[2225,158740,6471],{"class":2291},[2225,158742,2789],{"class":2239},[2225,158744,2805],{"class":2235},[2225,158746,27844],{"class":2252},[2225,158748,2805],{"class":2235},[2225,158750,2362],{"class":2239},[2225,158752,158753],{"class":2227,"line":1038},[2225,158754,2285],{"emptyLinePlaceholder":2284},[2225,158756,158757],{"class":2227,"line":2288},[2225,158758,158759],{"class":2763},"\u002F\u002F High-churn operational collection — no snapshots, no ledger entries.\n",[2225,158761,158762,158764,158767,158769,158771,158773,158775,158777,158779,158782,158784,158786],{"class":2227,"line":2304},[2225,158763,2775],{"class":2774},[2225,158765,158766],{"class":2239}," scratch ",[2225,158768,2781],{"class":2235},[2225,158770,6466],{"class":2239},[2225,158772,1520],{"class":2235},[2225,158774,6471],{"class":2291},[2225,158776,2789],{"class":2239},[2225,158778,2805],{"class":2235},[2225,158780,158781],{"class":2252},"scratch",[2225,158783,2805],{"class":2235},[2225,158785,2298],{"class":2235},[2225,158787,2301],{"class":2235},[2225,158789,158790,158793,158795,158797,158799,158801,158803,158805,158808,158810,158812],{"class":2227,"line":2356},[2225,158791,158792],{"class":2307},"  historyConfig",[2225,158794,2311],{"class":2235},[2225,158796,2236],{"class":2235},[2225,158798,135470],{"class":2307},[2225,158800,2311],{"class":2235},[2225,158802,2341],{"class":2321},[2225,158804,2298],{"class":2235},[2225,158806,158807],{"class":2307}," ledger",[2225,158809,2311],{"class":2235},[2225,158811,2341],{"class":2321},[2225,158813,2353],{"class":2235},[2225,158815,158816,158818],{"class":2227,"line":2830},[2225,158817,2359],{"class":2235},[2225,158819,2362],{"class":2239},[1111,158821,158822,158827],{},[1114,158823,158824,158826],{},[941,158825,99032],{}," — suppress per-record snapshots for this collection.",[1114,158828,158829,158832,158833,158835,158836,158839,158840,158842,158843,158845],{},[941,158830,158831],{},"ledger: false"," — exclude this collection's writes from the hash-chained tamper ledger. Its\nputs\u002Fdeletes leave ",[950,158834,11313],{}," ledger entry; the chain stays valid (",[941,158837,158838],{},"vault.ledger().verify()"," still\npasses) — it simply never receives those entries. Independent of ",[941,158841,16929],{},". No effect when\n",[941,158844,6719],{}," is not active (there is no ledger).",[937,158847,158848],{},"This lets you confine snapshots + tamper-evidence to the few collections where they carry legal\nweight, without paying snapshot + ledger-entry-per-write across operational \u002F derived collections\nvault-wide.",[24204,158850,158852],{"id":158851},"what-each-half-actually-costs","What each half actually costs",[937,158854,158855,158858,158859,158861],{},[950,158856,158857],{},"The hash chain is the entire cost of history; per-record snapshots are effectively free.","\nMeasured upstream on the primitive write path — no guards, MVs or derivations; sequential ",[941,158860,20953],{},";\nin-memory store; N = 3000 (noy-db#1248):",[1338,158863,158864,158874],{},[1341,158865,158866],{},[1344,158867,158868,158871],{},[1347,158869,158870],{},"configuration",[1347,158872,158873],{},"relative cost",[1357,158875,158876,158886,158896],{},[1344,158877,158878,158881],{},[1362,158879,158880],{},"snapshots only",[1362,158882,158883],{},[950,158884,158885],{},"1.00×",[1344,158887,158888,158891],{},[1362,158889,158890],{},"ledger only",[1362,158892,158893],{},[950,158894,158895],{},"2.35×",[1344,158897,158898,158900],{},[1362,158899,31219],{},[1362,158901,158902],{},[950,158903,158904],{},"2.41×",[937,158906,158907,158908,158911,158912,158915,158916,158918],{},"The cause is structural, not an inefficiency to optimize away: an entry's ",[941,158909,158910],{},"prevHash"," depends on the\ncurrent head, so appends are ",[950,158913,158914],{},"inherently serialized"," — one head read, one delta encryption and one\nCAS-put per record operation. That serialization ",[1092,158917,3082],{}," the tamper-evidence property.",[7646,158920,158922,158928],{"color":7648,"icon":158921},"i-lucide-gauge",[937,158923,158924,158927],{},[950,158925,158926],{},"The ratio travels; the absolute numbers do not."," Those figures come from an in-memory store and\nunderstate the felt cost. On browser IndexedDB the ledger adds a whole extra encrypted store write\nto a path whose base operation is already milliseconds — treat ~2.4× as the portable number and the\nin-memory microseconds as a floor.",[937,158929,158930,158931,13535,158934,158937,158938,158941,158942,158944],{},"A single human-paced write pays a difference nobody perceives. What pays visibly is a ",[950,158932,158933],{},"one-click\nbatch",[1092,158935,158936],{},"approve all",", a CSV import, a period prefill — and a write that fans out through\nderivations and materialized views pays once ",[950,158939,158940],{},"per resulting stored write",". That is what\n",[941,158943,158831],{}," is for on a high-churn collection.",[1022,158946,29893],{"id":29892},[1591,158948,158950],{"id":158949},"version-snapshots-diff-and-revert","Version snapshots, diff, and revert",[2217,158952,158954],{"className":2219,"code":158953,"language":2221,"meta":1037,"style":1037},"interface Invoice { amount: number; status: string }\nconst invoices = vault.collection\u003CInvoice>('invoices')\n\nawait invoices.put('inv-1', { amount: 1000, status: 'draft' })   \u002F\u002F → version 1\nawait invoices.put('inv-1', { amount: 2000, status: 'sent' })    \u002F\u002F → version 2\nawait invoices.put('inv-1', { amount: 2000, status: 'paid' })    \u002F\u002F → version 3 (current)\n\nconst versions = await invoices.history('inv-1')\n",[941,158955,158956,158980,159010,159014,159061,159109,159156,159160],{"__ignoreMap":1037},[2225,158957,158958,158960,158962,158964,158966,158968,158970,158972,158974,158976,158978],{"class":2227,"line":2228},[2225,158959,6994],{"class":2774},[2225,158961,6343],{"class":6342},[2225,158963,2236],{"class":2235},[2225,158965,6351],{"class":2307},[2225,158967,2311],{"class":2235},[2225,158969,6356],{"class":6342},[2225,158971,3769],{"class":2235},[2225,158973,6361],{"class":2307},[2225,158975,2311],{"class":2235},[2225,158977,6366],{"class":6342},[2225,158979,6369],{"class":2235},[2225,158981,158982,158984,158986,158988,158990,158992,158994,158996,158998,159000,159002,159004,159006,159008],{"class":2227,"line":1041},[2225,158983,2775],{"class":2774},[2225,158985,6461],{"class":2239},[2225,158987,2781],{"class":2235},[2225,158989,6466],{"class":2239},[2225,158991,1520],{"class":2235},[2225,158993,6471],{"class":2291},[2225,158995,6474],{"class":2235},[2225,158997,6477],{"class":6342},[2225,158999,6480],{"class":2235},[2225,159001,2789],{"class":2239},[2225,159003,2805],{"class":2235},[2225,159005,6487],{"class":2252},[2225,159007,2805],{"class":2235},[2225,159009,2362],{"class":2239},[2225,159011,159012],{"class":2227,"line":1038},[2225,159013,2285],{"emptyLinePlaceholder":2284},[2225,159015,159016,159018,159020,159022,159024,159026,159028,159030,159032,159034,159036,159038,159040,159042,159044,159046,159048,159050,159052,159054,159056,159058],{"class":2227,"line":2288},[2225,159017,6500],{"class":2231},[2225,159019,6503],{"class":2239},[2225,159021,1520],{"class":2235},[2225,159023,3161],{"class":2291},[2225,159025,2789],{"class":2239},[2225,159027,2805],{"class":2235},[2225,159029,6514],{"class":2252},[2225,159031,2805],{"class":2235},[2225,159033,2298],{"class":2235},[2225,159035,2236],{"class":2235},[2225,159037,6351],{"class":2307},[2225,159039,2311],{"class":2235},[2225,159041,26290],{"class":6527},[2225,159043,2298],{"class":2235},[2225,159045,6361],{"class":2307},[2225,159047,2311],{"class":2235},[2225,159049,2249],{"class":2235},[2225,159051,28429],{"class":2252},[2225,159053,2805],{"class":2235},[2225,159055,2243],{"class":2235},[2225,159057,71781],{"class":2239},[2225,159059,159060],{"class":2763},"\u002F\u002F → version 1\n",[2225,159062,159063,159065,159067,159069,159071,159073,159075,159077,159079,159081,159083,159085,159087,159090,159092,159094,159096,159098,159100,159102,159104,159106],{"class":2227,"line":2304},[2225,159064,6500],{"class":2231},[2225,159066,6503],{"class":2239},[2225,159068,1520],{"class":2235},[2225,159070,3161],{"class":2291},[2225,159072,2789],{"class":2239},[2225,159074,2805],{"class":2235},[2225,159076,6514],{"class":2252},[2225,159078,2805],{"class":2235},[2225,159080,2298],{"class":2235},[2225,159082,2236],{"class":2235},[2225,159084,6351],{"class":2307},[2225,159086,2311],{"class":2235},[2225,159088,159089],{"class":6527}," 2000",[2225,159091,2298],{"class":2235},[2225,159093,6361],{"class":2307},[2225,159095,2311],{"class":2235},[2225,159097,2249],{"class":2235},[2225,159099,88283],{"class":2252},[2225,159101,2805],{"class":2235},[2225,159103,2243],{"class":2235},[2225,159105,28126],{"class":2239},[2225,159107,159108],{"class":2763},"\u002F\u002F → version 2\n",[2225,159110,159111,159113,159115,159117,159119,159121,159123,159125,159127,159129,159131,159133,159135,159137,159139,159141,159143,159145,159147,159149,159151,159153],{"class":2227,"line":2356},[2225,159112,6500],{"class":2231},[2225,159114,6503],{"class":2239},[2225,159116,1520],{"class":2235},[2225,159118,3161],{"class":2291},[2225,159120,2789],{"class":2239},[2225,159122,2805],{"class":2235},[2225,159124,6514],{"class":2252},[2225,159126,2805],{"class":2235},[2225,159128,2298],{"class":2235},[2225,159130,2236],{"class":2235},[2225,159132,6351],{"class":2307},[2225,159134,2311],{"class":2235},[2225,159136,159089],{"class":6527},[2225,159138,2298],{"class":2235},[2225,159140,6361],{"class":2307},[2225,159142,2311],{"class":2235},[2225,159144,2249],{"class":2235},[2225,159146,6539],{"class":2252},[2225,159148,2805],{"class":2235},[2225,159150,2243],{"class":2235},[2225,159152,28126],{"class":2239},[2225,159154,159155],{"class":2763},"\u002F\u002F → version 3 (current)\n",[2225,159157,159158],{"class":2227,"line":2830},[2225,159159,2285],{"emptyLinePlaceholder":2284},[2225,159161,159162,159164,159167,159169,159171,159173,159175,159177,159179,159181,159183,159185],{"class":2227,"line":2861},[2225,159163,2775],{"class":2774},[2225,159165,159166],{"class":2239}," versions ",[2225,159168,2781],{"class":2235},[2225,159170,2784],{"class":2231},[2225,159172,6503],{"class":2239},[2225,159174,1520],{"class":2235},[2225,159176,697],{"class":2291},[2225,159178,2789],{"class":2239},[2225,159180,2805],{"class":2235},[2225,159182,6514],{"class":2252},[2225,159184,2805],{"class":2235},[2225,159186,2362],{"class":2239},[2217,159188,159190],{"className":6604,"code":159189,"language":141,"meta":1037,"style":1037},"[\n  { \"version\": 2, \"timestamp\": \"2026-06-01T10:02:00.000Z\", \"userId\": \"firm\", \"record\": { \"amount\": 2000, \"status\": \"sent\" } },\n  { \"version\": 1, \"timestamp\": \"2026-06-01T10:01:00.000Z\", \"userId\": \"firm\", \"record\": { \"amount\": 1000, \"status\": \"draft\" } }\n]\n",[941,159191,159192,159196,159285,159374],{"__ignoreMap":1037},[2225,159193,159194],{"class":2227,"line":2228},[2225,159195,26349],{"class":2235},[2225,159197,159198,159200,159202,159204,159206,159208,159210,159212,159214,159216,159218,159220,159222,159225,159227,159229,159231,159233,159235,159237,159239,159241,159243,159245,159247,159249,159251,159253,159255,159257,159259,159261,159263,159265,159267,159269,159271,159273,159275,159277,159279,159281,159283],{"class":2227,"line":1041},[2225,159199,26354],{"class":2235},[2225,159201,6614],{"class":2235},[2225,159203,52564],{"class":2774},[2225,159205,6620],{"class":2235},[2225,159207,2311],{"class":2235},[2225,159209,18961],{"class":6527},[2225,159211,2298],{"class":2235},[2225,159213,6614],{"class":2235},[2225,159215,59900],{"class":2774},[2225,159217,6620],{"class":2235},[2225,159219,2311],{"class":2235},[2225,159221,6614],{"class":2235},[2225,159223,159224],{"class":2252},"2026-06-01T10:02:00.000Z",[2225,159226,6620],{"class":2235},[2225,159228,2298],{"class":2235},[2225,159230,6614],{"class":2235},[2225,159232,10749],{"class":2774},[2225,159234,6620],{"class":2235},[2225,159236,2311],{"class":2235},[2225,159238,6614],{"class":2235},[2225,159240,39709],{"class":2252},[2225,159242,6620],{"class":2235},[2225,159244,2298],{"class":2235},[2225,159246,6614],{"class":2235},[2225,159248,24158],{"class":2774},[2225,159250,6620],{"class":2235},[2225,159252,2311],{"class":2235},[2225,159254,2236],{"class":2235},[2225,159256,6614],{"class":2235},[2225,159258,6617],{"class":6342},[2225,159260,6620],{"class":2235},[2225,159262,2311],{"class":2235},[2225,159264,159089],{"class":6527},[2225,159266,2298],{"class":2235},[2225,159268,6614],{"class":2235},[2225,159270,6631],{"class":6342},[2225,159272,6620],{"class":2235},[2225,159274,2311],{"class":2235},[2225,159276,6614],{"class":2235},[2225,159278,88283],{"class":2252},[2225,159280,6620],{"class":2235},[2225,159282,2243],{"class":2235},[2225,159284,2353],{"class":2235},[2225,159286,159287,159289,159291,159293,159295,159297,159299,159301,159303,159305,159307,159309,159311,159314,159316,159318,159320,159322,159324,159326,159328,159330,159332,159334,159336,159338,159340,159342,159344,159346,159348,159350,159352,159354,159356,159358,159360,159362,159364,159366,159368,159370,159372],{"class":2227,"line":1038},[2225,159288,26354],{"class":2235},[2225,159290,6614],{"class":2235},[2225,159292,52564],{"class":2774},[2225,159294,6620],{"class":2235},[2225,159296,2311],{"class":2235},[2225,159298,9377],{"class":6527},[2225,159300,2298],{"class":2235},[2225,159302,6614],{"class":2235},[2225,159304,59900],{"class":2774},[2225,159306,6620],{"class":2235},[2225,159308,2311],{"class":2235},[2225,159310,6614],{"class":2235},[2225,159312,159313],{"class":2252},"2026-06-01T10:01:00.000Z",[2225,159315,6620],{"class":2235},[2225,159317,2298],{"class":2235},[2225,159319,6614],{"class":2235},[2225,159321,10749],{"class":2774},[2225,159323,6620],{"class":2235},[2225,159325,2311],{"class":2235},[2225,159327,6614],{"class":2235},[2225,159329,39709],{"class":2252},[2225,159331,6620],{"class":2235},[2225,159333,2298],{"class":2235},[2225,159335,6614],{"class":2235},[2225,159337,24158],{"class":2774},[2225,159339,6620],{"class":2235},[2225,159341,2311],{"class":2235},[2225,159343,2236],{"class":2235},[2225,159345,6614],{"class":2235},[2225,159347,6617],{"class":6342},[2225,159349,6620],{"class":2235},[2225,159351,2311],{"class":2235},[2225,159353,26290],{"class":6527},[2225,159355,2298],{"class":2235},[2225,159357,6614],{"class":2235},[2225,159359,6631],{"class":6342},[2225,159361,6620],{"class":2235},[2225,159363,2311],{"class":2235},[2225,159365,6614],{"class":2235},[2225,159367,28429],{"class":2252},[2225,159369,6620],{"class":2235},[2225,159371,2243],{"class":2235},[2225,159373,6369],{"class":2235},[2225,159375,159376],{"class":2227,"line":2288},[2225,159377,26484],{"class":2235},[937,159379,159380,159382],{},[941,159381,158461],{}," never includes the current live version — only prior snapshots, newest first. Load one\ndirectly, diff it against the current record, or revert to it:",[2217,159384,159386],{"className":2219,"code":159385,"language":2221,"meta":1037,"style":1037},"const v1 = await invoices.getVersion('inv-1', 1)\n\u002F\u002F → { amount: 1000, status: 'draft' }\n\nconst changes = await invoices.diff('inv-1', 1)   \u002F\u002F versionB omitted → diffs against the current record\n\u002F\u002F → [\n\u002F\u002F     { path: 'amount', type: 'changed', from: 1000, to: 2000 },\n\u002F\u002F     { path: 'status', type: 'changed', from: 'draft', to: 'paid' }\n\u002F\u002F   ]\n\nawait invoices.revert('inv-1', 1)\n\u002F\u002F → writes { amount: 1000, status: 'draft' } back as version 4; the current record is v1's content again\n",[941,159387,159388,159420,159425,159429,159462,159466,159471,159476,159480,159484,159509],{"__ignoreMap":1037},[2225,159389,159390,159392,159395,159397,159399,159401,159403,159406,159408,159410,159412,159414,159416,159418],{"class":2227,"line":2228},[2225,159391,2775],{"class":2774},[2225,159393,159394],{"class":2239}," v1 ",[2225,159396,2781],{"class":2235},[2225,159398,2784],{"class":2231},[2225,159400,6503],{"class":2239},[2225,159402,1520],{"class":2235},[2225,159404,159405],{"class":2291},"getVersion",[2225,159407,2789],{"class":2239},[2225,159409,2805],{"class":2235},[2225,159411,6514],{"class":2252},[2225,159413,2805],{"class":2235},[2225,159415,2298],{"class":2235},[2225,159417,9377],{"class":6527},[2225,159419,2362],{"class":2239},[2225,159421,159422],{"class":2227,"line":1041},[2225,159423,159424],{"class":2763},"\u002F\u002F → { amount: 1000, status: 'draft' }\n",[2225,159426,159427],{"class":2227,"line":1038},[2225,159428,2285],{"emptyLinePlaceholder":2284},[2225,159430,159431,159433,159435,159437,159439,159441,159443,159445,159447,159449,159451,159453,159455,159457,159459],{"class":2227,"line":2288},[2225,159432,2775],{"class":2774},[2225,159434,82983],{"class":2239},[2225,159436,2781],{"class":2235},[2225,159438,2784],{"class":2231},[2225,159440,6503],{"class":2239},[2225,159442,1520],{"class":2235},[2225,159444,80362],{"class":2291},[2225,159446,2789],{"class":2239},[2225,159448,2805],{"class":2235},[2225,159450,6514],{"class":2252},[2225,159452,2805],{"class":2235},[2225,159454,2298],{"class":2235},[2225,159456,9377],{"class":6527},[2225,159458,71781],{"class":2239},[2225,159460,159461],{"class":2763},"\u002F\u002F versionB omitted → diffs against the current record\n",[2225,159463,159464],{"class":2227,"line":2304},[2225,159465,100852],{"class":2763},[2225,159467,159468],{"class":2227,"line":2356},[2225,159469,159470],{"class":2763},"\u002F\u002F     { path: 'amount', type: 'changed', from: 1000, to: 2000 },\n",[2225,159472,159473],{"class":2227,"line":2830},[2225,159474,159475],{"class":2763},"\u002F\u002F     { path: 'status', type: 'changed', from: 'draft', to: 'paid' }\n",[2225,159477,159478],{"class":2227,"line":2861},[2225,159479,100867],{"class":2763},[2225,159481,159482],{"class":2227,"line":4246},[2225,159483,2285],{"emptyLinePlaceholder":2284},[2225,159485,159486,159488,159490,159492,159495,159497,159499,159501,159503,159505,159507],{"class":2227,"line":4253},[2225,159487,6500],{"class":2231},[2225,159489,6503],{"class":2239},[2225,159491,1520],{"class":2235},[2225,159493,159494],{"class":2291},"revert",[2225,159496,2789],{"class":2239},[2225,159498,2805],{"class":2235},[2225,159500,6514],{"class":2252},[2225,159502,2805],{"class":2235},[2225,159504,2298],{"class":2235},[2225,159506,9377],{"class":6527},[2225,159508,2362],{"class":2239},[2225,159510,159511],{"class":2227,"line":4259},[2225,159512,159513],{"class":2763},"\u002F\u002F → writes { amount: 1000, status: 'draft' } back as version 4; the current record is v1's content again\n",[1591,159515,159517],{"id":159516},"the-tamper-evident-ledger","The tamper-evident ledger",[937,159519,159520,159521,159523,159524,159526],{},"Every write to a collection with ",[941,159522,20923],{}," enabled also appends an entry to ",[941,159525,21859],{}," — a\nhash chain where each entry embeds the hash of the entry before it:",[2217,159528,159530],{"className":2219,"code":159529,"language":2221,"meta":1037,"style":1037},"const entries = await vault.ledger().entries()\n",[941,159531,159532],{"__ignoreMap":1037},[2225,159533,159534,159536,159538,159540,159542,159544,159546,159548,159550,159552,159554],{"class":2227,"line":2228},[2225,159535,2775],{"class":2774},[2225,159537,45530],{"class":2239},[2225,159539,2781],{"class":2235},[2225,159541,2784],{"class":2231},[2225,159543,6466],{"class":2239},[2225,159545,1520],{"class":2235},[2225,159547,20923],{"class":2291},[2225,159549,4228],{"class":2239},[2225,159551,1520],{"class":2235},[2225,159553,9095],{"class":2291},[2225,159555,8146],{"class":2239},[2217,159557,159559],{"className":6604,"code":159558,"language":141,"meta":1037,"style":1037},"[\n  { \"index\": 0, \"prevHash\": \"\", \"op\": \"put\", \"collection\": \"invoices\", \"id\": \"inv-1\", \"version\": 1, \"ts\": \"2026-06-01T10:01:00.000Z\", \"actor\": \"firm\", \"payloadHash\": \"3fa2c1…\" },\n  { \"index\": 1, \"prevHash\": \"9e1b7a…\", \"op\": \"put\", \"collection\": \"invoices\", \"id\": \"inv-1\", \"version\": 2, \"ts\": \"2026-06-01T10:02:00.000Z\", \"actor\": \"firm\", \"payloadHash\": \"8bd004…\", \"deltaHash\": \"c771ee…\" },\n  { \"index\": 2, \"prevHash\": \"51af90…\", \"op\": \"put\", \"collection\": \"invoices\", \"id\": \"inv-1\", \"version\": 3, \"ts\": \"2026-06-01T10:03:00.000Z\", \"actor\": \"firm\", \"payloadHash\": \"0a55dd…\", \"deltaHash\": \"f42b31…\" }\n]\n",[941,159560,159561,159565,159702,159862,160022],{"__ignoreMap":1037},[2225,159562,159563],{"class":2227,"line":2228},[2225,159564,26349],{"class":2235},[2225,159566,159567,159569,159571,159573,159575,159577,159579,159581,159583,159585,159587,159589,159591,159593,159595,159597,159599,159601,159603,159605,159607,159609,159611,159613,159615,159617,159619,159621,159623,159625,159627,159629,159631,159633,159635,159637,159639,159641,159643,159645,159647,159649,159651,159653,159655,159657,159659,159661,159663,159665,159667,159669,159671,159673,159675,159677,159679,159681,159683,159685,159687,159689,159691,159693,159695,159698,159700],{"class":2227,"line":1041},[2225,159568,26354],{"class":2235},[2225,159570,6614],{"class":2235},[2225,159572,15112],{"class":2774},[2225,159574,6620],{"class":2235},[2225,159576,2311],{"class":2235},[2225,159578,8464],{"class":6527},[2225,159580,2298],{"class":2235},[2225,159582,6614],{"class":2235},[2225,159584,158910],{"class":2774},[2225,159586,6620],{"class":2235},[2225,159588,2311],{"class":2235},[2225,159590,60469],{"class":2235},[2225,159592,2298],{"class":2235},[2225,159594,6614],{"class":2235},[2225,159596,61313],{"class":2774},[2225,159598,6620],{"class":2235},[2225,159600,2311],{"class":2235},[2225,159602,6614],{"class":2235},[2225,159604,3161],{"class":2252},[2225,159606,6620],{"class":2235},[2225,159608,2298],{"class":2235},[2225,159610,6614],{"class":2235},[2225,159612,6471],{"class":2774},[2225,159614,6620],{"class":2235},[2225,159616,2311],{"class":2235},[2225,159618,6614],{"class":2235},[2225,159620,6487],{"class":2252},[2225,159622,6620],{"class":2235},[2225,159624,2298],{"class":2235},[2225,159626,6614],{"class":2235},[2225,159628,6983],{"class":2774},[2225,159630,6620],{"class":2235},[2225,159632,2311],{"class":2235},[2225,159634,6614],{"class":2235},[2225,159636,6514],{"class":2252},[2225,159638,6620],{"class":2235},[2225,159640,2298],{"class":2235},[2225,159642,6614],{"class":2235},[2225,159644,52564],{"class":2774},[2225,159646,6620],{"class":2235},[2225,159648,2311],{"class":2235},[2225,159650,9377],{"class":6527},[2225,159652,2298],{"class":2235},[2225,159654,6614],{"class":2235},[2225,159656,2221],{"class":2774},[2225,159658,6620],{"class":2235},[2225,159660,2311],{"class":2235},[2225,159662,6614],{"class":2235},[2225,159664,159313],{"class":2252},[2225,159666,6620],{"class":2235},[2225,159668,2298],{"class":2235},[2225,159670,6614],{"class":2235},[2225,159672,61256],{"class":2774},[2225,159674,6620],{"class":2235},[2225,159676,2311],{"class":2235},[2225,159678,6614],{"class":2235},[2225,159680,39709],{"class":2252},[2225,159682,6620],{"class":2235},[2225,159684,2298],{"class":2235},[2225,159686,6614],{"class":2235},[2225,159688,22982],{"class":2774},[2225,159690,6620],{"class":2235},[2225,159692,2311],{"class":2235},[2225,159694,6614],{"class":2235},[2225,159696,159697],{"class":2252},"3fa2c1…",[2225,159699,6620],{"class":2235},[2225,159701,2353],{"class":2235},[2225,159703,159704,159706,159708,159710,159712,159714,159716,159718,159720,159722,159724,159726,159728,159731,159733,159735,159737,159739,159741,159743,159745,159747,159749,159751,159753,159755,159757,159759,159761,159763,159765,159767,159769,159771,159773,159775,159777,159779,159781,159783,159785,159787,159789,159791,159793,159795,159797,159799,159801,159803,159805,159807,159809,159811,159813,159815,159817,159819,159821,159823,159825,159827,159829,159831,159833,159835,159837,159840,159842,159844,159846,159849,159851,159853,159855,159858,159860],{"class":2227,"line":1038},[2225,159705,26354],{"class":2235},[2225,159707,6614],{"class":2235},[2225,159709,15112],{"class":2774},[2225,159711,6620],{"class":2235},[2225,159713,2311],{"class":2235},[2225,159715,9377],{"class":6527},[2225,159717,2298],{"class":2235},[2225,159719,6614],{"class":2235},[2225,159721,158910],{"class":2774},[2225,159723,6620],{"class":2235},[2225,159725,2311],{"class":2235},[2225,159727,6614],{"class":2235},[2225,159729,159730],{"class":2252},"9e1b7a…",[2225,159732,6620],{"class":2235},[2225,159734,2298],{"class":2235},[2225,159736,6614],{"class":2235},[2225,159738,61313],{"class":2774},[2225,159740,6620],{"class":2235},[2225,159742,2311],{"class":2235},[2225,159744,6614],{"class":2235},[2225,159746,3161],{"class":2252},[2225,159748,6620],{"class":2235},[2225,159750,2298],{"class":2235},[2225,159752,6614],{"class":2235},[2225,159754,6471],{"class":2774},[2225,159756,6620],{"class":2235},[2225,159758,2311],{"class":2235},[2225,159760,6614],{"class":2235},[2225,159762,6487],{"class":2252},[2225,159764,6620],{"class":2235},[2225,159766,2298],{"class":2235},[2225,159768,6614],{"class":2235},[2225,159770,6983],{"class":2774},[2225,159772,6620],{"class":2235},[2225,159774,2311],{"class":2235},[2225,159776,6614],{"class":2235},[2225,159778,6514],{"class":2252},[2225,159780,6620],{"class":2235},[2225,159782,2298],{"class":2235},[2225,159784,6614],{"class":2235},[2225,159786,52564],{"class":2774},[2225,159788,6620],{"class":2235},[2225,159790,2311],{"class":2235},[2225,159792,18961],{"class":6527},[2225,159794,2298],{"class":2235},[2225,159796,6614],{"class":2235},[2225,159798,2221],{"class":2774},[2225,159800,6620],{"class":2235},[2225,159802,2311],{"class":2235},[2225,159804,6614],{"class":2235},[2225,159806,159224],{"class":2252},[2225,159808,6620],{"class":2235},[2225,159810,2298],{"class":2235},[2225,159812,6614],{"class":2235},[2225,159814,61256],{"class":2774},[2225,159816,6620],{"class":2235},[2225,159818,2311],{"class":2235},[2225,159820,6614],{"class":2235},[2225,159822,39709],{"class":2252},[2225,159824,6620],{"class":2235},[2225,159826,2298],{"class":2235},[2225,159828,6614],{"class":2235},[2225,159830,22982],{"class":2774},[2225,159832,6620],{"class":2235},[2225,159834,2311],{"class":2235},[2225,159836,6614],{"class":2235},[2225,159838,159839],{"class":2252},"8bd004…",[2225,159841,6620],{"class":2235},[2225,159843,2298],{"class":2235},[2225,159845,6614],{"class":2235},[2225,159847,159848],{"class":2774},"deltaHash",[2225,159850,6620],{"class":2235},[2225,159852,2311],{"class":2235},[2225,159854,6614],{"class":2235},[2225,159856,159857],{"class":2252},"c771ee…",[2225,159859,6620],{"class":2235},[2225,159861,2353],{"class":2235},[2225,159863,159864,159866,159868,159870,159872,159874,159876,159878,159880,159882,159884,159886,159888,159891,159893,159895,159897,159899,159901,159903,159905,159907,159909,159911,159913,159915,159917,159919,159921,159923,159925,159927,159929,159931,159933,159935,159937,159939,159941,159943,159945,159947,159949,159951,159953,159955,159957,159959,159961,159963,159965,159968,159970,159972,159974,159976,159978,159980,159982,159984,159986,159988,159990,159992,159994,159996,159998,160001,160003,160005,160007,160009,160011,160013,160015,160018,160020],{"class":2227,"line":2288},[2225,159865,26354],{"class":2235},[2225,159867,6614],{"class":2235},[2225,159869,15112],{"class":2774},[2225,159871,6620],{"class":2235},[2225,159873,2311],{"class":2235},[2225,159875,18961],{"class":6527},[2225,159877,2298],{"class":2235},[2225,159879,6614],{"class":2235},[2225,159881,158910],{"class":2774},[2225,159883,6620],{"class":2235},[2225,159885,2311],{"class":2235},[2225,159887,6614],{"class":2235},[2225,159889,159890],{"class":2252},"51af90…",[2225,159892,6620],{"class":2235},[2225,159894,2298],{"class":2235},[2225,159896,6614],{"class":2235},[2225,159898,61313],{"class":2774},[2225,159900,6620],{"class":2235},[2225,159902,2311],{"class":2235},[2225,159904,6614],{"class":2235},[2225,159906,3161],{"class":2252},[2225,159908,6620],{"class":2235},[2225,159910,2298],{"class":2235},[2225,159912,6614],{"class":2235},[2225,159914,6471],{"class":2774},[2225,159916,6620],{"class":2235},[2225,159918,2311],{"class":2235},[2225,159920,6614],{"class":2235},[2225,159922,6487],{"class":2252},[2225,159924,6620],{"class":2235},[2225,159926,2298],{"class":2235},[2225,159928,6614],{"class":2235},[2225,159930,6983],{"class":2774},[2225,159932,6620],{"class":2235},[2225,159934,2311],{"class":2235},[2225,159936,6614],{"class":2235},[2225,159938,6514],{"class":2252},[2225,159940,6620],{"class":2235},[2225,159942,2298],{"class":2235},[2225,159944,6614],{"class":2235},[2225,159946,52564],{"class":2774},[2225,159948,6620],{"class":2235},[2225,159950,2311],{"class":2235},[2225,159952,14460],{"class":6527},[2225,159954,2298],{"class":2235},[2225,159956,6614],{"class":2235},[2225,159958,2221],{"class":2774},[2225,159960,6620],{"class":2235},[2225,159962,2311],{"class":2235},[2225,159964,6614],{"class":2235},[2225,159966,159967],{"class":2252},"2026-06-01T10:03:00.000Z",[2225,159969,6620],{"class":2235},[2225,159971,2298],{"class":2235},[2225,159973,6614],{"class":2235},[2225,159975,61256],{"class":2774},[2225,159977,6620],{"class":2235},[2225,159979,2311],{"class":2235},[2225,159981,6614],{"class":2235},[2225,159983,39709],{"class":2252},[2225,159985,6620],{"class":2235},[2225,159987,2298],{"class":2235},[2225,159989,6614],{"class":2235},[2225,159991,22982],{"class":2774},[2225,159993,6620],{"class":2235},[2225,159995,2311],{"class":2235},[2225,159997,6614],{"class":2235},[2225,159999,160000],{"class":2252},"0a55dd…",[2225,160002,6620],{"class":2235},[2225,160004,2298],{"class":2235},[2225,160006,6614],{"class":2235},[2225,160008,159848],{"class":2774},[2225,160010,6620],{"class":2235},[2225,160012,2311],{"class":2235},[2225,160014,6614],{"class":2235},[2225,160016,160017],{"class":2252},"f42b31…",[2225,160019,6620],{"class":2235},[2225,160021,6369],{"class":2235},[2225,160023,160024],{"class":2227,"line":2304},[2225,160025,26484],{"class":2235},[2217,160027,160029],{"className":2219,"code":160028,"language":2221,"meta":1037,"style":1037},"const result = await vault.ledger().verify()\n\u002F\u002F → { ok: true, head: '51af90…', length: 3 }\n",[941,160030,160031,160055],{"__ignoreMap":1037},[2225,160032,160033,160035,160037,160039,160041,160043,160045,160047,160049,160051,160053],{"class":2227,"line":2228},[2225,160034,2775],{"class":2774},[2225,160036,36920],{"class":2239},[2225,160038,2781],{"class":2235},[2225,160040,2784],{"class":2231},[2225,160042,6466],{"class":2239},[2225,160044,1520],{"class":2235},[2225,160046,20923],{"class":2291},[2225,160048,4228],{"class":2239},[2225,160050,1520],{"class":2235},[2225,160052,18051],{"class":2291},[2225,160054,8146],{"class":2239},[2225,160056,160057],{"class":2227,"line":1041},[2225,160058,160059],{"class":2763},"\u002F\u002F → { ok: true, head: '51af90…', length: 3 }\n",[937,160061,160062,160063,160066,160067,160069],{},"A broken link — an entry edited, reordered, or deleted out from under the chain — flips this to\n",[941,160064,160065],{},"{ ok: false, divergedAt, expected, actual }",", pinpointing the first entry whose recorded\n",[941,160068,158910],{}," no longer matches the recomputed hash of its predecessor.",[1591,160071,160073],{"id":160072},"point-in-time-reads","Point-in-time reads",[937,160075,160076,160078,160079,160081],{},[941,160077,58310],{}," reconstructs the vault as it stood at a past instant, using the ledger to\nresolve which put\u002Fdelete was live at that moment and the ",[941,160080,6715],{}," snapshots to supply the\nrecord's content:",[2217,160083,160085],{"className":2219,"code":160084,"language":2221,"meta":1037,"style":1037},"const asOf = vault.at('2026-06-01T10:01:30.000Z')\nconst snapshot = await asOf.collection\u003CInvoice>('invoices').get('inv-1')\n\u002F\u002F → { amount: 1000, status: 'draft' }   — v1, the state right after the first put\n",[941,160086,160087,160113,160161],{"__ignoreMap":1037},[2225,160088,160089,160091,160094,160096,160098,160100,160102,160104,160106,160109,160111],{"class":2227,"line":2228},[2225,160090,2775],{"class":2774},[2225,160092,160093],{"class":2239}," asOf ",[2225,160095,2781],{"class":2235},[2225,160097,6466],{"class":2239},[2225,160099,1520],{"class":2235},[2225,160101,4887],{"class":2291},[2225,160103,2789],{"class":2239},[2225,160105,2805],{"class":2235},[2225,160107,160108],{"class":2252},"2026-06-01T10:01:30.000Z",[2225,160110,2805],{"class":2235},[2225,160112,2362],{"class":2239},[2225,160114,160115,160117,160120,160122,160124,160127,160129,160131,160133,160135,160137,160139,160141,160143,160145,160147,160149,160151,160153,160155,160157,160159],{"class":2227,"line":1041},[2225,160116,2775],{"class":2774},[2225,160118,160119],{"class":2239}," snapshot ",[2225,160121,2781],{"class":2235},[2225,160123,2784],{"class":2231},[2225,160125,160126],{"class":2239}," asOf",[2225,160128,1520],{"class":2235},[2225,160130,6471],{"class":2291},[2225,160132,6474],{"class":2235},[2225,160134,6477],{"class":6342},[2225,160136,6480],{"class":2235},[2225,160138,2789],{"class":2239},[2225,160140,2805],{"class":2235},[2225,160142,6487],{"class":2252},[2225,160144,2805],{"class":2235},[2225,160146,1381],{"class":2239},[2225,160148,1520],{"class":2235},[2225,160150,3158],{"class":2291},[2225,160152,2789],{"class":2239},[2225,160154,2805],{"class":2235},[2225,160156,6514],{"class":2252},[2225,160158,2805],{"class":2235},[2225,160160,2362],{"class":2239},[2225,160162,160163],{"class":2227,"line":1038},[2225,160164,160165],{"class":2763},"\u002F\u002F → { amount: 1000, status: 'draft' }   — v1, the state right after the first put\n",[937,160167,160168,160170,160171,1520],{},[941,160169,67909],{}," is read-only: every write method on the view it returns throws ",[941,160172,60214],{},[1591,160174,160176],{"id":160175},"retention-and-backup-integrity","Retention and backup integrity",[937,160178,160179,997,160182,160185,160186,160188],{},[941,160180,160181],{},"pruneRecordHistory",[941,160183,160184],{},"clearHistory"," delete old snapshots explicitly, on top of any\n",[941,160187,20919],{}," cap set at opt-in:",[2217,160190,160192],{"className":2219,"code":160191,"language":2221,"meta":1037,"style":1037},"await invoices.pruneRecordHistory('inv-1', { keepVersions: 1 })\n\u002F\u002F → 1   (older snapshots beyond the 1 most recent were deleted)\n\nawait invoices.clearHistory('inv-1')\n\u002F\u002F → 1   (the remaining snapshot for inv-1 was deleted)\n",[941,160193,160194,160227,160232,160236,160256],{"__ignoreMap":1037},[2225,160195,160196,160198,160200,160202,160204,160206,160208,160210,160212,160214,160216,160219,160221,160223,160225],{"class":2227,"line":2228},[2225,160197,6500],{"class":2231},[2225,160199,6503],{"class":2239},[2225,160201,1520],{"class":2235},[2225,160203,160181],{"class":2291},[2225,160205,2789],{"class":2239},[2225,160207,2805],{"class":2235},[2225,160209,6514],{"class":2252},[2225,160211,2805],{"class":2235},[2225,160213,2298],{"class":2235},[2225,160215,2236],{"class":2235},[2225,160217,160218],{"class":2307}," keepVersions",[2225,160220,2311],{"class":2235},[2225,160222,9377],{"class":6527},[2225,160224,2243],{"class":2235},[2225,160226,2362],{"class":2239},[2225,160228,160229],{"class":2227,"line":1041},[2225,160230,160231],{"class":2763},"\u002F\u002F → 1   (older snapshots beyond the 1 most recent were deleted)\n",[2225,160233,160234],{"class":2227,"line":1038},[2225,160235,2285],{"emptyLinePlaceholder":2284},[2225,160237,160238,160240,160242,160244,160246,160248,160250,160252,160254],{"class":2227,"line":2288},[2225,160239,6500],{"class":2231},[2225,160241,6503],{"class":2239},[2225,160243,1520],{"class":2235},[2225,160245,160184],{"class":2291},[2225,160247,2789],{"class":2239},[2225,160249,2805],{"class":2235},[2225,160251,6514],{"class":2252},[2225,160253,2805],{"class":2235},[2225,160255,2362],{"class":2239},[2225,160257,160258],{"class":2227,"line":2304},[2225,160259,160260],{"class":2763},"\u002F\u002F → 1   (the remaining snapshot for inv-1 was deleted)\n",[937,160262,160263,160265,160266,160269,160270,1324,160272,160274],{},[941,160264,58313],{}," runs the same chain check as ",[941,160267,160268],{},"ledger().verify()",", plus a\ncross-check that every current record's ciphertext hash still matches what the ledger recorded for\nit — the check a restore path (e.g. loading a ",[941,160271,1402],{},[1007,160273,629],{"href":629},") should run before\ntrusting the data:",[2217,160276,160278],{"className":2219,"code":160277,"language":2221,"meta":1037,"style":1037},"const integrity = await vault.verifyBackupIntegrity()\n\u002F\u002F → { ok: true, head: '51af90…', length: 3 }\n",[941,160279,160280,160299],{"__ignoreMap":1037},[2225,160281,160282,160284,160287,160289,160291,160293,160295,160297],{"class":2227,"line":2228},[2225,160283,2775],{"class":2774},[2225,160285,160286],{"class":2239}," integrity ",[2225,160288,2781],{"class":2235},[2225,160290,2784],{"class":2231},[2225,160292,6466],{"class":2239},[2225,160294,1520],{"class":2235},[2225,160296,22965],{"class":2291},[2225,160298,8146],{"class":2239},[2225,160300,160301],{"class":2227,"line":1041},[2225,160302,160059],{"class":2763},[1022,160304,102889],{"id":102888},[1591,160306,160308,160309,1381],{"id":160307},"collection-methods-enabled-by-withhistory","Collection methods (enabled by ",[941,160310,6719],{},[1338,160312,160313,160323],{},[1341,160314,160315],{},[1344,160316,160317,160319,160321],{},[1347,160318,7831],{},[1347,160320,102906],{},[1347,160322,7834],{},[1357,160324,160325,160346,160361,160378,160401,160427],{},[1344,160326,160327,160332,160337],{},[1362,160328,160329],{},[941,160330,160331],{},"collection.history(id, options?)",[1362,160333,160334],{},[941,160335,160336],{},"Promise\u003CHistoryEntry\u003CT>[]>",[1362,160338,160339,160340,2085,160342,160345],{},"List a record's prior versions, newest first. ",[941,160341,16362],{},[941,160343,160344],{},"{ from?, to?, limit? }"," — ISO date filters plus a max count.",[1344,160347,160348,160353,160358],{},[1362,160349,160350],{},[941,160351,160352],{},"collection.getVersion(id, version)",[1362,160354,160355],{},[941,160356,160357],{},"Promise\u003CT | null>",[1362,160359,160360],{},"Load one specific past version's content.",[1344,160362,160363,160368,160372],{},[1362,160364,160365],{},[941,160366,160367],{},"collection.revert(id, version)",[1362,160369,160370],{},[941,160371,116014],{},[1362,160373,160374,160375,160377],{},"Re-",[941,160376,3161],{}," a past version's content as a brand-new version.",[1344,160379,160380,160385,160390],{},[1362,160381,160382],{},[941,160383,160384],{},"collection.diff(id, versionA, versionB?)",[1362,160386,160387],{},[941,160388,160389],{},"Promise\u003CDiffEntry[]>",[1362,160391,160392,160393,160396,160397,160400],{},"Field-level diff between two versions; omit ",[941,160394,160395],{},"versionB"," to diff against the current record. ",[941,160398,160399],{},"versionA: 0"," means \"before creation.\"",[1344,160402,160403,160408,160413],{},[1362,160404,160405],{},[941,160406,160407],{},"collection.pruneRecordHistory(id?, options)",[1362,160409,160410],{},[941,160411,160412],{},"Promise\u003Cnumber>",[1362,160414,160415,160416,160419,160420,160423,160424,160426],{},"Delete snapshots by retention rule — ",[941,160417,160418],{},"{ keepVersions? }"," and\u002For ",[941,160421,160422],{},"{ beforeDate? }","; omit ",[941,160425,6983],{}," to prune every record in the collection. Returns the count deleted.",[1344,160428,160429,160434,160438],{},[1362,160430,160431],{},[941,160432,160433],{},"collection.clearHistory(id?)",[1362,160435,160436],{},[941,160437,160412],{},[1362,160439,160440,160441,23003],{},"Delete all snapshots for one record, or for the whole collection when ",[941,160442,6983],{},[1591,160444,102893,160446,1381],{"id":160445},"vault-methods-enabled-by-withhistory",[941,160447,6719],{},[1338,160449,160450,160460],{},[1341,160451,160452],{},[1344,160453,160454,160456,160458],{},[1347,160455,7831],{},[1347,160457,102906],{},[1347,160459,7834],{},[1357,160461,160462,160489,160518],{},[1344,160463,160464,160468,160473],{},[1362,160465,160466],{},[941,160467,21859],{},[1362,160469,160470],{},[941,160471,160472],{},"LedgerStore",[1362,160474,160475,160476,968,160479,968,160482,160485,160486,160488],{},"The vault's hash-chained audit log. Read via ",[941,160477,160478],{},".entries({ from?, to? })",[941,160480,160481],{},".head()",[941,160483,160484],{},".verify()","; throws if ",[941,160487,6719],{}," isn't opted in.",[1344,160490,160491,160495,160500],{},[1362,160492,160493],{},[941,160494,58310],{},[1362,160496,160497],{},[941,160498,160499],{},"VaultInstant",[1362,160501,160502,160503,1952,160505,22207,160508,28806,160511,1261,160513,160515,160516,1520],{},"A read-only view of the vault at a past ISO-8601 timestamp or ",[941,160504,26712],{},[941,160506,160507],{},".collection(name)",[941,160509,160510],{},"CollectionInstant",[941,160512,138938],{},[941,160514,138941],{},"; every write method throws ",[941,160517,60214],{},[1344,160519,160520,160524,160529],{},[1362,160521,160522],{},[941,160523,58313],{},[1362,160525,160526],{},[941,160527,160528],{},"Promise\u003CVerifyBackupResult>",[1362,160530,160531,160532,160534,160535,160538],{},"Ledger chain check plus a data-envelope cross-check. ",[941,160533,22986],{}," on success; ",[941,160536,160537],{},"{ ok: false, kind: 'chain' | 'data', message, ... }"," pinpointing the divergence otherwise.",[1591,160540,103205],{"id":128135},[1111,160542,160543,160551,160559],{},[1114,160544,160545,964,160548],{},[941,160546,160547],{},"HistoryEntry\u003CT>",[941,160549,160550],{},"{ version: number, timestamp: string, userId: string, record: T }",[1114,160552,160553,964,160556],{},[941,160554,160555],{},"DiffEntry",[941,160557,160558],{},"{ path: string, type: 'added' | 'removed' | 'changed', from?: unknown, to?: unknown }",[1114,160560,160561,964,160563,160566,160567,10313,160569,160571,160572,10313,160574,160576],{},[941,160562,20909],{},[941,160564,160565],{},"{ enabled?: boolean, maxVersions?: number, ledger?: boolean }",", passed as\n",[941,160568,697],{},[941,160570,2134],{}," (vault-wide) or ",[941,160573,22798],{},[941,160575,2138],{}," (per-collection\noverride)",[1591,160578,160580],{"id":160579},"lower-level-exports-subpath-barrel","Lower-level exports (subpath barrel)",[937,160582,160583],{},"Most applications only need the methods above; these are exported for tooling that verifies or\nreplays the chain independently:",[1111,160585,160586,160601,160610,160625,160633,160652],{},[1114,160587,160588,1261,160591,160594,160595,160598,160599,957],{},[941,160589,160590],{},"diff(a, b)",[941,160592,160593],{},"formatDiff(entries)"," — the pure diff engine and a human-readable formatter\n(",[941,160596,160597],{},"collection.diff()"," wraps ",[941,160600,21849],{},[1114,160602,160603,1261,160606,160609],{},[941,160604,160605],{},"computePatch(from, to)",[941,160607,160608],{},"applyPatch(state, patch)"," — the JSON Patch algebra the ledger uses to\nstore and replay deltas.",[1114,160611,160612,1261,160615,1261,160618,160621,160622,160624],{},[941,160613,160614],{},"canonicalJson(value)",[941,160616,160617],{},"sha256Hex(text)",[941,160619,160620],{},"hashEntry(entry)"," — the exact hashing recipe behind\nthe ledger's ",[941,160623,158910],{}," chain.",[1114,160626,160627,160630,160631,1520],{},[941,160628,160629],{},"envelopePayloadHash(envelope)"," — hashes an encrypted envelope's ciphertext the same way the\nledger does, for cross-checking a record against its ",[941,160632,22982],{},[1114,160634,160635,968,160637,968,160640,968,160642,968,160645,968,160648,160651],{},[941,160636,153722],{},[941,160638,160639],{},"AppendInput",[941,160641,103151],{},[941,160643,160644],{},"JsonPatch",[941,160646,160647],{},"JsonPatchOp",[941,160649,160650],{},"ChangeType"," —\nsupporting types.",[1114,160653,160654,953,160657,160660,160661,953,160664,160667],{},[941,160655,160656],{},"LEDGER_COLLECTION",[941,160658,160659],{},"'_ledger'",") \u002F ",[941,160662,160663],{},"LEDGER_DELTAS_COLLECTION",[941,160665,160666],{},"'_ledger_deltas'",") — the internal\ncollection names, for tooling that inspects a raw dump.",[1022,160669,101425],{"id":101424},[1111,160671,160672,160679,160690,160696],{},[1114,160673,160674,1261,160676,160678],{},[941,160675,3161],{},[941,160677,3164],{}," succeed but no snapshot or ledger entry is written",[1114,160680,160681,968,160683,968,160685,160687,160688],{},[941,160682,21840],{},[941,160684,21843],{},[941,160686,21849],{}," throw with a pointer to ",[941,160689,4151],{},[1114,160691,160692,968,160694,122050],{},[941,160693,21859],{},[941,160695,21862],{},[1114,160697,160698,2557,160700,160703],{},[941,160699,58313],{},[941,160701,160702],{},"{ ok: true, head: '', length: 0 }"," trivially (no chain to\ndiverge from)",[1022,160705,114684],{"id":114683},[1111,160707,160708,160715,160727],{},[1114,160709,160710,160714],{},[950,160711,160712],{},[1007,160713,776],{"href":776}," — closing a period writes a normal ledger entry, chaining period\nboundaries into the same tamper-evident sequence as record puts\u002Fdeletes",[1114,160716,160717,964,160721,160723,160724,160726],{},[950,160718,160719],{},[1007,160720,848],{"href":848},[941,160722,21505],{}," is the live-state companion to ",[941,160725,58310],{},":\nsame read-only contract (writes throw), but it reads the current vault instead of a past instant",[1114,160728,160729,160733,160734,160736],{},[950,160730,160731],{},[1007,160732,641],{"href":641}," — keeps its own separate per-access audit trail (",[941,160735,6744],{},"),\nindependent of this service's hash-chained ledger",[1022,160738,6825],{"id":6824},[1111,160740,160741,160760,160766],{},[1114,160742,160743,160744,7356,160747,160749,160750,160753,160754,160756,160757,160759],{},"Ledger writes are multi-writer safe on any store whose ",[941,160745,160746],{},"capabilities.casAtomic",[941,160748,11790],{}," (most\nbuilt-in and cloud\u002FSQL stores — check the store's own docs for its value) via an optimistic-CAS\nretry loop on the chain head. Up to 8 retries with exponential backoff + jitter; throws\n",[941,160751,160752],{},"LedgerContentionError"," on exhaustion. Stores with ",[941,160755,47869],{}," (the built-in ",[941,160758,7461],{},"\nstore and a handful of filesystem\u002Fremote-fs stores) remain best-effort under contention — pair\nthem with an advisory lock or single-writer discipline.",[1114,160761,160762,160763,160765],{},"Time-machine accuracy is bounded by retention: pruned versions are unrecoverable, and queries\nbefore the oldest retained snapshot return ",[941,160764,6651],{}," even for records that existed.",[1114,160767,160768,160770,160771,160773,160774,160776],{},[941,160769,117180],{}," detects tampering ",[1092,160772,111736],{}," the ledger's own chain; it does not by itself detect data\ncollections edited without a matching ledger entry — that cross-check is what\n",[941,160775,22875],{}," adds.",[1022,160778,2621],{"id":2620},[1111,160780,160781,160786,160795,160802,160808],{},[1114,160782,160783,160785],{},[1007,160784,65376],{"href":48}," — the vault\u002Fcollection model this service\nextends",[1114,160787,160788,964,160790,160792,160793],{},[1007,160789,629],{"href":629},[941,160791,1402],{}," backups embed the ledger head; loading one runs the same integrity\ncheck as ",[941,160794,22875],{},[1114,160796,160797,1368,160799,160801],{},[1007,160798,776],{"href":776},[1007,160800,848],{"href":848}," — related services built on this one",[1114,160803,160804,1368,160806],{},[1007,160805,61743],{"href":598},[1007,160807,1019],{"href":1018},[1114,160809,141161,160810,997,160816,1520],{},[1007,160811,160813],{"href":92561,"rel":160812},[3371],[941,160814,160815],{},"07-with-history",[1007,160817,160819],{"href":92723,"rel":160818},[3371],[941,160820,160821],{},"17-with-history-time-machine",[2645,160823,160824],{},"html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}html pre.shiki code .sfNiH, html code.shiki .sfNiH{--shiki-light:#FF5370;--shiki-default:#FF9CAC;--shiki-dark:#FF9CAC}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}",{"title":1037,"searchDepth":1038,"depth":1038,"links":160826},[160827,160828,160829,160830,160835,160841,160849,160850,160851,160852],{"id":29856,"depth":1041,"text":29857},{"id":111659,"depth":1041,"text":111660},{"id":70092,"depth":1041,"text":70093},{"id":99863,"depth":1041,"text":99864,"children":160831},[160832],{"id":158701,"depth":1038,"text":158702,"children":160833},[160834],{"id":158851,"depth":2288,"text":158852},{"id":29892,"depth":1041,"text":29893,"children":160836},[160837,160838,160839,160840],{"id":158949,"depth":1038,"text":158950},{"id":159516,"depth":1038,"text":159517},{"id":160072,"depth":1038,"text":160073},{"id":160175,"depth":1038,"text":160176},{"id":102888,"depth":1041,"text":102889,"children":160842},[160843,160845,160847,160848],{"id":160307,"depth":1038,"text":160844},"Collection methods (enabled by withHistory())",{"id":160445,"depth":1038,"text":160846},"Vault methods (enabled by withHistory())",{"id":128135,"depth":1038,"text":103205},{"id":160579,"depth":1038,"text":160580},{"id":101424,"depth":1041,"text":101425},{"id":114683,"depth":1041,"text":114684},{"id":6824,"depth":1041,"text":6825},{"id":2620,"depth":1041,"text":2621},"Per-record version snapshots, a hash-chained tamper-evident ledger, JSON Patch deltas, point-in-time reads via `vault.at()`, and backup verification.",{},{"title":697,"description":160853},"EIal3qGFyoPRFOikO2Zx7NfFbRcNnhGPgDtSmQjJc2I",{"id":160858,"title":701,"access":12,"apiToc":929,"body":160859,"category":1042,"description":164024,"extension":1044,"meta":164025,"navigation":1042,"nest":1047,"path":702,"seo":164026,"sinceVersion":1042,"status":1049,"stem":703,"subtitle":1042,"tier":1050,"tocExternal":1042,"__hash__":164027},"docs\u002Fservices\u002Fi18n.md",{"type":931,"value":160860,"toc":164009},[160861,160863,160866,160868,160905,160907,160929,160931,160940,160942,161073,161075,161083,161658,161682,161684,161734,161736,161760,161764,161777,161788,162090,162101,162242,162256,162316,162345,162499,162598,162655,162677,162694,162717,162720,162814,162820,162861,162873,162898,162908,162924,163060,163088,163123,163221,163268,163300,163304,163325,163720,163725,163824,163831,163835,163889,163899,163901,163916,163918,163947,163949,164006],[934,160862,701],{"id":701},[46871,160864],{":rows":160865},"[{\"label\":\"Subpath\",\"value\":\"`@noy-db\u002Fhub\u002Fi18n`\"},{\"label\":\"Factory\",\"value\":\"`withI18n()`\"},{\"label\":\"Layer\",\"value\":\"`with-shape`\"},{\"label\":\"Cluster\",\"value\":\"D — Data Shape\"},{\"label\":\"LOC cost\",\"value\":\"~854 (off-bundle when not opted in)\"}]",[1022,160867,29857],{"id":29856},[937,160869,160870,160872,160873,160875,160876,160880,160881,968,160883,160886,160887,12536,160891,160893,160894,1625,160896,160898,160899,160901,160902,160904],{},[941,160871,701],{}," lets a ",[1007,160874,6471],{"href":48}," store one value per record and return the right locale's version of it on read, without your application code branching on language anywhere. It covers two complementary patterns: ",[950,160877,160878],{},[941,160879,21456],{}," — a stable code (",[941,160882,58792],{},[941,160884,160885],{},"'draft'",") that resolves against a shared, per-vault label dictionary — for enum-like fields where the set of values is small and known; and ",[950,160888,160889],{},[941,160890,20943],{},[941,160892,58799],{}," map stored directly on the record — for free-form translated content (descriptions, notes). Locale resolution happens after decryption, at read time, so the record on disk always holds every locale you wrote; only the value handed back from ",[941,160895,14639],{},[941,160897,24407],{}," changes shape. The label dictionary itself is stored as an encrypted collection inside the vault, and is versioned\u002Faudit-logged like any other collection when the history service (",[941,160900,6719],{},") is also opted in — that's not a property of ",[941,160903,701],{}," alone.",[1022,160906,111660],{"id":111659},[1111,160908,160909,160912,160923,160926],{},[1114,160910,160911],{},"Apps that ship in more than one language",[1114,160913,160914,160915,968,160917,968,160919,160922],{},"Enum-like fields (",[941,160916,6631],{},[941,160918,58354],{},[941,160920,160921],{},"country",") where labels differ by locale",[1114,160924,160925],{},"Per-record translatable content (descriptions, comments, free-form notes)",[1114,160927,160928],{},"Auto-translate hooks for missing locale fills",[1022,160930,70093],{"id":70092},[1111,160932,160933],{},[1114,160934,160935,964,160937,160939],{},[950,160936,107493],{},[941,160938,6739],{}," plus the per-collection dictionary declarations shown below; no other strategy or package is involved.",[1022,160941,99864],{"id":99863},[2217,160943,160945],{"className":2219,"code":160944,"language":2221,"meta":1037,"style":1037},"import { createNoydb, dictKey, i18nText } from '@noy-db\u002Fhub'\nimport { toMemory } from '@noy-db\u002Fto-memory'\nimport { withI18n } from '@noy-db\u002Fhub\u002Fi18n'\n\nconst db = await createNoydb({\n  store: toMemory(),\n  user: 'alice',\n  i18nStrategy: withI18n(),\n})\n",[941,160946,160947,160973,160991,161009,161013,161029,161041,161055,161067],{"__ignoreMap":1037},[2225,160948,160949,160951,160953,160955,160957,160959,160961,160963,160965,160967,160969,160971],{"class":2227,"line":2228},[2225,160950,2232],{"class":2231},[2225,160952,2236],{"class":2235},[2225,160954,2744],{"class":2239},[2225,160956,2298],{"class":2235},[2225,160958,58814],{"class":2239},[2225,160960,2298],{"class":2235},[2225,160962,58819],{"class":2239},[2225,160964,2243],{"class":2235},[2225,160966,2246],{"class":2231},[2225,160968,2249],{"class":2235},[2225,160970,1086],{"class":2252},[2225,160972,2256],{"class":2235},[2225,160974,160975,160977,160979,160981,160983,160985,160987,160989],{"class":2227,"line":1041},[2225,160976,2232],{"class":2231},[2225,160978,2236],{"class":2235},[2225,160980,68576],{"class":2239},[2225,160982,2243],{"class":2235},[2225,160984,2246],{"class":2231},[2225,160986,2249],{"class":2235},[2225,160988,9638],{"class":2252},[2225,160990,2256],{"class":2235},[2225,160992,160993,160995,160997,160999,161001,161003,161005,161007],{"class":2227,"line":1038},[2225,160994,2232],{"class":2231},[2225,160996,2236],{"class":2235},[2225,160998,58505],{"class":2239},[2225,161000,2243],{"class":2235},[2225,161002,2246],{"class":2231},[2225,161004,2249],{"class":2235},[2225,161006,58514],{"class":2252},[2225,161008,2256],{"class":2235},[2225,161010,161011],{"class":2227,"line":2288},[2225,161012,2285],{"emptyLinePlaceholder":2284},[2225,161014,161015,161017,161019,161021,161023,161025,161027],{"class":2227,"line":2304},[2225,161016,2775],{"class":2774},[2225,161018,2778],{"class":2239},[2225,161020,2781],{"class":2235},[2225,161022,2784],{"class":2231},[2225,161024,2744],{"class":2291},[2225,161026,2789],{"class":2239},[2225,161028,4200],{"class":2235},[2225,161030,161031,161033,161035,161037,161039],{"class":2227,"line":2356},[2225,161032,15608],{"class":2307},[2225,161034,2311],{"class":2235},[2225,161036,68576],{"class":2291},[2225,161038,4228],{"class":2239},[2225,161040,1707],{"class":2235},[2225,161042,161043,161045,161047,161049,161051,161053],{"class":2227,"line":2830},[2225,161044,4205],{"class":2307},[2225,161046,2311],{"class":2235},[2225,161048,2249],{"class":2235},[2225,161050,6408],{"class":2252},[2225,161052,2805],{"class":2235},[2225,161054,1707],{"class":2235},[2225,161056,161057,161059,161061,161063,161065],{"class":2227,"line":2861},[2225,161058,58741],{"class":2307},[2225,161060,2311],{"class":2235},[2225,161062,58505],{"class":2291},[2225,161064,4228],{"class":2239},[2225,161066,1707],{"class":2235},[2225,161068,161069,161071],{"class":2227,"line":4246},[2225,161070,2359],{"class":2235},[2225,161072,2362],{"class":2239},[1022,161074,29893],{"id":29892},[937,161076,161077,161078,1625,161080,161082],{},"Seed the shared label dictionary, declare a collection's ",[941,161079,21456],{},[941,161081,20943],{}," fields, then\nread the same record back at different locales:",[2217,161084,161086],{"className":2219,"code":161085,"language":2221,"meta":1037,"style":1037},"const vault = await db.openVault('firm')\nawait vault.dictionary('status').putAll({\n  draft: { en: 'Draft', th: 'ฉบับร่าง' },\n  paid:  { en: 'Paid',  th: 'ชำระแล้ว' },\n})\n\ninterface Invoice {\n  id: string\n  status: string\n  description: Record\u003Cstring, string>\n}\n\nconst invoices = vault.collection\u003CInvoice>('invoices', {\n  dictKeyFields: {\n    status: dictKey('status', ['draft', 'paid'] as const),\n  },\n  i18nFields: {\n    description: i18nText({ languages: ['en', 'th'], required: 'all' }),\n  },\n})\n\nawait invoices.put('inv-1', {\n  id: 'inv-1',\n  status: 'paid',\n  description: { en: 'Consulting hours', th: 'ค่าที่ปรึกษา' },\n})\n\nawait invoices.get('inv-1', { locale: 'th' })\n\u002F\u002F → { id: 'inv-1', status: 'paid', statusLabel: 'ชำระแล้ว', description: 'ค่าที่ปรึกษา' }\n\nawait invoices.get('inv-1', { locale: 'en' })\n\u002F\u002F → { id: 'inv-1', status: 'paid', statusLabel: 'Paid', description: 'Consulting hours' }\n\nawait invoices.get('inv-1', { locale: 'raw' })\n\u002F\u002F → { id: 'inv-1', status: 'paid', description: { en: 'Consulting hours', th: 'ค่าที่ปรึกษา' } }\n\u002F\u002F 'raw' skips resolution: dictKey fields get no \u003Cfield>Label, i18nText fields keep their full locale map\n",[941,161087,161088,161114,161142,161174,161206,161212,161216,161224,161232,161240,161258,161262,161266,161298,161306,161350,161354,161362,161414,161418,161424,161428,161450,161464,161478,161512,161518,161522,161558,161563,161567,161603,161608,161612,161648,161653],{"__ignoreMap":1037},[2225,161089,161090,161092,161094,161096,161098,161100,161102,161104,161106,161108,161110,161112],{"class":2227,"line":2228},[2225,161091,2775],{"class":2774},[2225,161093,2835],{"class":2239},[2225,161095,2781],{"class":2235},[2225,161097,2784],{"class":2231},[2225,161099,2842],{"class":2239},[2225,161101,1520],{"class":2235},[2225,161103,2847],{"class":2291},[2225,161105,2789],{"class":2239},[2225,161107,2805],{"class":2235},[2225,161109,39709],{"class":2252},[2225,161111,2805],{"class":2235},[2225,161113,2362],{"class":2239},[2225,161115,161116,161118,161120,161122,161124,161126,161128,161130,161132,161134,161136,161138,161140],{"class":2227,"line":1041},[2225,161117,6500],{"class":2231},[2225,161119,6466],{"class":2239},[2225,161121,1520],{"class":2235},[2225,161123,58965],{"class":2291},[2225,161125,2789],{"class":2239},[2225,161127,2805],{"class":2235},[2225,161129,6631],{"class":2252},[2225,161131,2805],{"class":2235},[2225,161133,1381],{"class":2239},[2225,161135,1520],{"class":2235},[2225,161137,58980],{"class":2291},[2225,161139,2789],{"class":2239},[2225,161141,4200],{"class":2235},[2225,161143,161144,161146,161148,161150,161152,161154,161156,161158,161160,161162,161164,161166,161168,161170,161172],{"class":2227,"line":1038},[2225,161145,58989],{"class":2307},[2225,161147,2311],{"class":2235},[2225,161149,2236],{"class":2235},[2225,161151,58997],{"class":2307},[2225,161153,2311],{"class":2235},[2225,161155,2249],{"class":2235},[2225,161157,59004],{"class":2252},[2225,161159,2805],{"class":2235},[2225,161161,2298],{"class":2235},[2225,161163,60621],{"class":2307},[2225,161165,2311],{"class":2235},[2225,161167,2249],{"class":2235},[2225,161169,59018],{"class":2252},[2225,161171,2805],{"class":2235},[2225,161173,2353],{"class":2235},[2225,161175,161176,161178,161180,161182,161184,161186,161188,161190,161192,161194,161196,161198,161200,161202,161204],{"class":2227,"line":2288},[2225,161177,59091],{"class":2307},[2225,161179,2311],{"class":2235},[2225,161181,26354],{"class":2235},[2225,161183,58997],{"class":2307},[2225,161185,2311],{"class":2235},[2225,161187,2249],{"class":2235},[2225,161189,59104],{"class":2252},[2225,161191,2805],{"class":2235},[2225,161193,2298],{"class":2235},[2225,161195,59159],{"class":2307},[2225,161197,2311],{"class":2235},[2225,161199,2249],{"class":2235},[2225,161201,59117],{"class":2252},[2225,161203,2805],{"class":2235},[2225,161205,2353],{"class":2235},[2225,161207,161208,161210],{"class":2227,"line":2304},[2225,161209,2359],{"class":2235},[2225,161211,2362],{"class":2239},[2225,161213,161214],{"class":2227,"line":2356},[2225,161215,2285],{"emptyLinePlaceholder":2284},[2225,161217,161218,161220,161222],{"class":2227,"line":2830},[2225,161219,6994],{"class":2774},[2225,161221,6343],{"class":6342},[2225,161223,2301],{"class":2235},[2225,161225,161226,161228,161230],{"class":2227,"line":2861},[2225,161227,28371],{"class":2307},[2225,161229,2311],{"class":2235},[2225,161231,7014],{"class":6342},[2225,161233,161234,161236,161238],{"class":2227,"line":4246},[2225,161235,26171],{"class":2307},[2225,161237,2311],{"class":2235},[2225,161239,7014],{"class":6342},[2225,161241,161242,161244,161246,161248,161250,161252,161254,161256],{"class":2227,"line":4253},[2225,161243,58874],{"class":2307},[2225,161245,2311],{"class":2235},[2225,161247,22527],{"class":6342},[2225,161249,6474],{"class":2235},[2225,161251,7259],{"class":6342},[2225,161253,2298],{"class":2235},[2225,161255,6366],{"class":6342},[2225,161257,7093],{"class":2235},[2225,161259,161260],{"class":2227,"line":4259},[2225,161261,7351],{"class":2235},[2225,161263,161264],{"class":2227,"line":6548},[2225,161265,2285],{"emptyLinePlaceholder":2284},[2225,161267,161268,161270,161272,161274,161276,161278,161280,161282,161284,161286,161288,161290,161292,161294,161296],{"class":2227,"line":6576},[2225,161269,2775],{"class":2774},[2225,161271,6461],{"class":2239},[2225,161273,2781],{"class":2235},[2225,161275,6466],{"class":2239},[2225,161277,1520],{"class":2235},[2225,161279,6471],{"class":2291},[2225,161281,6474],{"class":2235},[2225,161283,6477],{"class":6342},[2225,161285,6480],{"class":2235},[2225,161287,2789],{"class":2239},[2225,161289,2805],{"class":2235},[2225,161291,6487],{"class":2252},[2225,161293,2805],{"class":2235},[2225,161295,2298],{"class":2235},[2225,161297,2301],{"class":2235},[2225,161299,161300,161302,161304],{"class":2227,"line":6582},[2225,161301,59231],{"class":2307},[2225,161303,2311],{"class":2235},[2225,161305,2301],{"class":2235},[2225,161307,161308,161310,161312,161314,161316,161318,161320,161322,161324,161326,161328,161330,161332,161334,161336,161338,161340,161342,161344,161346,161348],{"class":2227,"line":6587},[2225,161309,59240],{"class":2307},[2225,161311,2311],{"class":2235},[2225,161313,58814],{"class":2291},[2225,161315,2789],{"class":2239},[2225,161317,2805],{"class":2235},[2225,161319,6631],{"class":2252},[2225,161321,2805],{"class":2235},[2225,161323,2298],{"class":2235},[2225,161325,8667],{"class":2239},[2225,161327,2805],{"class":2235},[2225,161329,28429],{"class":2252},[2225,161331,2805],{"class":2235},[2225,161333,2298],{"class":2235},[2225,161335,2249],{"class":2235},[2225,161337,6539],{"class":2252},[2225,161339,2805],{"class":2235},[2225,161341,8890],{"class":2239},[2225,161343,4899],{"class":2231},[2225,161345,8083],{"class":2774},[2225,161347,1381],{"class":2239},[2225,161349,1707],{"class":2235},[2225,161351,161352],{"class":2227,"line":7174},[2225,161353,19017],{"class":2235},[2225,161355,161356,161358,161360],{"class":2227,"line":7180},[2225,161357,59305],{"class":2307},[2225,161359,2311],{"class":2235},[2225,161361,2301],{"class":2235},[2225,161363,161364,161366,161368,161370,161372,161374,161376,161378,161380,161382,161384,161386,161388,161390,161392,161394,161396,161398,161400,161402,161404,161406,161408,161410,161412],{"class":2227,"line":7220},[2225,161365,59314],{"class":2307},[2225,161367,2311],{"class":2235},[2225,161369,58819],{"class":2291},[2225,161371,2789],{"class":2239},[2225,161373,2792],{"class":2235},[2225,161375,59325],{"class":2307},[2225,161377,2311],{"class":2235},[2225,161379,8667],{"class":2239},[2225,161381,2805],{"class":2235},[2225,161383,59334],{"class":2252},[2225,161385,2805],{"class":2235},[2225,161387,2298],{"class":2235},[2225,161389,2249],{"class":2235},[2225,161391,1347],{"class":2252},[2225,161393,2805],{"class":2235},[2225,161395,8803],{"class":2239},[2225,161397,2298],{"class":2235},[2225,161399,8299],{"class":2307},[2225,161401,2311],{"class":2235},[2225,161403,2249],{"class":2235},[2225,161405,59366],{"class":2252},[2225,161407,2805],{"class":2235},[2225,161409,2243],{"class":2235},[2225,161411,1381],{"class":2239},[2225,161413,1707],{"class":2235},[2225,161415,161416],{"class":2227,"line":7225},[2225,161417,19017],{"class":2235},[2225,161419,161420,161422],{"class":2227,"line":7231},[2225,161421,2359],{"class":2235},[2225,161423,2362],{"class":2239},[2225,161425,161426],{"class":2227,"line":7267},[2225,161427,2285],{"emptyLinePlaceholder":2284},[2225,161429,161430,161432,161434,161436,161438,161440,161442,161444,161446,161448],{"class":2227,"line":7272},[2225,161431,6500],{"class":2231},[2225,161433,6503],{"class":2239},[2225,161435,1520],{"class":2235},[2225,161437,3161],{"class":2291},[2225,161439,2789],{"class":2239},[2225,161441,2805],{"class":2235},[2225,161443,6514],{"class":2252},[2225,161445,2805],{"class":2235},[2225,161447,2298],{"class":2235},[2225,161449,2301],{"class":2235},[2225,161451,161452,161454,161456,161458,161460,161462],{"class":2227,"line":7278},[2225,161453,28371],{"class":2307},[2225,161455,2311],{"class":2235},[2225,161457,2249],{"class":2235},[2225,161459,6514],{"class":2252},[2225,161461,2805],{"class":2235},[2225,161463,1707],{"class":2235},[2225,161465,161466,161468,161470,161472,161474,161476],{"class":2227,"line":7303},[2225,161467,26171],{"class":2307},[2225,161469,2311],{"class":2235},[2225,161471,2249],{"class":2235},[2225,161473,6539],{"class":2252},[2225,161475,2805],{"class":2235},[2225,161477,1707],{"class":2235},[2225,161479,161480,161482,161484,161486,161488,161490,161492,161495,161497,161499,161501,161503,161505,161508,161510],{"class":2227,"line":7308},[2225,161481,58874],{"class":2307},[2225,161483,2311],{"class":2235},[2225,161485,2236],{"class":2235},[2225,161487,58997],{"class":2307},[2225,161489,2311],{"class":2235},[2225,161491,2249],{"class":2235},[2225,161493,161494],{"class":2252},"Consulting hours",[2225,161496,2805],{"class":2235},[2225,161498,2298],{"class":2235},[2225,161500,60621],{"class":2307},[2225,161502,2311],{"class":2235},[2225,161504,2249],{"class":2235},[2225,161506,161507],{"class":2252},"ค่าที่ปรึกษา",[2225,161509,2805],{"class":2235},[2225,161511,2353],{"class":2235},[2225,161513,161514,161516],{"class":2227,"line":7314},[2225,161515,2359],{"class":2235},[2225,161517,2362],{"class":2239},[2225,161519,161520],{"class":2227,"line":7348},[2225,161521,2285],{"emptyLinePlaceholder":2284},[2225,161523,161524,161526,161528,161530,161532,161534,161536,161538,161540,161542,161544,161546,161548,161550,161552,161554,161556],{"class":2227,"line":8613},[2225,161525,6500],{"class":2231},[2225,161527,6503],{"class":2239},[2225,161529,1520],{"class":2235},[2225,161531,3158],{"class":2291},[2225,161533,2789],{"class":2239},[2225,161535,2805],{"class":2235},[2225,161537,6514],{"class":2252},[2225,161539,2805],{"class":2235},[2225,161541,2298],{"class":2235},[2225,161543,2236],{"class":2235},[2225,161545,24003],{"class":2307},[2225,161547,2311],{"class":2235},[2225,161549,2249],{"class":2235},[2225,161551,1347],{"class":2252},[2225,161553,2805],{"class":2235},[2225,161555,2243],{"class":2235},[2225,161557,2362],{"class":2239},[2225,161559,161560],{"class":2227,"line":8633},[2225,161561,161562],{"class":2763},"\u002F\u002F → { id: 'inv-1', status: 'paid', statusLabel: 'ชำระแล้ว', description: 'ค่าที่ปรึกษา' }\n",[2225,161564,161565],{"class":2227,"line":8662},[2225,161566,2285],{"emptyLinePlaceholder":2284},[2225,161568,161569,161571,161573,161575,161577,161579,161581,161583,161585,161587,161589,161591,161593,161595,161597,161599,161601],{"class":2227,"line":8747},[2225,161570,6500],{"class":2231},[2225,161572,6503],{"class":2239},[2225,161574,1520],{"class":2235},[2225,161576,3158],{"class":2291},[2225,161578,2789],{"class":2239},[2225,161580,2805],{"class":2235},[2225,161582,6514],{"class":2252},[2225,161584,2805],{"class":2235},[2225,161586,2298],{"class":2235},[2225,161588,2236],{"class":2235},[2225,161590,24003],{"class":2307},[2225,161592,2311],{"class":2235},[2225,161594,2249],{"class":2235},[2225,161596,59334],{"class":2252},[2225,161598,2805],{"class":2235},[2225,161600,2243],{"class":2235},[2225,161602,2362],{"class":2239},[2225,161604,161605],{"class":2227,"line":8752},[2225,161606,161607],{"class":2763},"\u002F\u002F → { id: 'inv-1', status: 'paid', statusLabel: 'Paid', description: 'Consulting hours' }\n",[2225,161609,161610],{"class":2227,"line":8768},[2225,161611,2285],{"emptyLinePlaceholder":2284},[2225,161613,161614,161616,161618,161620,161622,161624,161626,161628,161630,161632,161634,161636,161638,161640,161642,161644,161646],{"class":2227,"line":8785},[2225,161615,6500],{"class":2231},[2225,161617,6503],{"class":2239},[2225,161619,1520],{"class":2235},[2225,161621,3158],{"class":2291},[2225,161623,2789],{"class":2239},[2225,161625,2805],{"class":2235},[2225,161627,6514],{"class":2252},[2225,161629,2805],{"class":2235},[2225,161631,2298],{"class":2235},[2225,161633,2236],{"class":2235},[2225,161635,24003],{"class":2307},[2225,161637,2311],{"class":2235},[2225,161639,2249],{"class":2235},[2225,161641,30312],{"class":2252},[2225,161643,2805],{"class":2235},[2225,161645,2243],{"class":2235},[2225,161647,2362],{"class":2239},[2225,161649,161650],{"class":2227,"line":8815},[2225,161651,161652],{"class":2763},"\u002F\u002F → { id: 'inv-1', status: 'paid', description: { en: 'Consulting hours', th: 'ค่าที่ปรึกษา' } }\n",[2225,161654,161655],{"class":2227,"line":8854},[2225,161656,161657],{"class":2763},"\u002F\u002F 'raw' skips resolution: dictKey fields get no \u003Cfield>Label, i18nText fields keep their full locale map\n",[937,161659,161660,161661,161663,161664,161666,161667,1625,161669,161671,161672,161674,161675,161677,161678,59636,161680,1520],{},"The stored record never changes — ",[941,161662,6631],{}," is always the code, ",[941,161665,59777],{}," is always the full locale map. Only the shape of what ",[941,161668,14639],{},[941,161670,24407],{}," return depends on the ",[941,161673,23074],{}," you pass: a resolved string for ",[941,161676,59777],{},", plus a ",[941,161679,24050],{},[941,161681,21456],{},[1022,161683,101012],{"id":41259},[1111,161685,161686,161705,161714,161720],{},[1114,161687,161688,1261,161691,1261,161694,1261,161697,1261,161700,1261,161702],{},[941,161689,161690],{},"vault.dictionary(name).putAll(...)",[941,161692,161693],{},"put(...)",[941,161695,161696],{},"get(...)",[941,161698,161699],{},"delete(...)",[941,161701,24407],{},[941,161703,161704],{},"rename(...)",[1114,161706,161707,997,161710,161713],{},[941,161708,161709],{},"dictKey(name, keys)",[941,161711,161712],{},"i18nText({ languages, required })"," field descriptors",[1114,161715,161716,161719],{},[941,161717,161718],{},"resolveI18nText(value, locale, fallback?)"," helper — resolve a single value",[1114,161721,161722,161723,53922,161726,1261,161729,1261,161731,161733],{},"Per-call ",[941,161724,161725],{},"{ locale, fallback }",[941,161727,161728],{},"collection.get()",[941,161730,24407],{},[941,161732,24715],{}," reads",[1022,161735,101425],{"id":101424},[1111,161737,161738,161744,161753],{},[1114,161739,161740,161743],{},[941,161741,161742],{},"applyI18nLocale"," (under the hood) returns the record unchanged — locale resolution is silently skipped",[1114,161745,161746,161749,161750,161752],{},[941,161747,161748],{},"validateI18nTextValue"," throws when a collection declared ",[941,161751,22756],{}," (misconfiguration: declared but didn't opt in)",[1114,161754,161755,161757,161758],{},[941,161756,21460],{}," throws with a pointer to ",[941,161759,58514],{},[1022,161761,161763],{"id":161762},"hardening-per-layer-policy-substitute-script-dictkey-parity","Hardening (per-layer policy, substitute, script, dictKey parity)",[937,161765,161766,161767,1261,161769,161771,161772,161774,161775,1520],{},"Optional, opt-in extensions on ",[941,161768,58358],{},[941,161770,58348],{},". Fields that\ndon't set them behave exactly as before (zero breaking change) — a plain\n",[941,161773,161712],{}," with no extra options resolves and\nvalidates exactly as shown in ",[1007,161776,72860],{"href":146050},[937,161778,161779,161780,161782,161783,161785,161786,59639],{},"Here is what the record on disk and the value returned from ",[941,161781,14639],{}," look\nlike as each option kicks in, using a ",[941,161784,2854],{}," collection with a hardened\n",[941,161787,42850],{},[2217,161789,161791],{"className":2219,"code":161790,"language":2221,"meta":1037,"style":1037},"interface Note { id: string; body: Record\u003Cstring, string> }\n\nconst notes = vault.collection\u003CNote>('notes', {\n  i18nFields: {\n    body: i18nText({\n      languages: ['th', 'en'], required: 'any',\n      substitute: ['en', 'th', 'any'],\n      onMissing: 'substitute',\n      smartSubstitute: true,\n      script: 'auto',\n      densifyOnWrite: true,\n    }),\n  },\n})\n\nawait notes.put('n1', { id: 'n1', body: { en: 'Site visit scheduled' } })\n",[941,161792,161793,161827,161831,161863,161871,161883,161922,161957,161973,161984,162000,162011,162019,162023,162029,162033],{"__ignoreMap":1037},[2225,161794,161795,161797,161799,161801,161803,161805,161807,161809,161811,161813,161815,161817,161819,161821,161823,161825],{"class":2227,"line":2228},[2225,161796,6994],{"class":2774},[2225,161798,86655],{"class":6342},[2225,161800,2236],{"class":2235},[2225,161802,7070],{"class":2307},[2225,161804,2311],{"class":2235},[2225,161806,6366],{"class":6342},[2225,161808,3769],{"class":2235},[2225,161810,42791],{"class":2307},[2225,161812,2311],{"class":2235},[2225,161814,22527],{"class":6342},[2225,161816,6474],{"class":2235},[2225,161818,7259],{"class":6342},[2225,161820,2298],{"class":2235},[2225,161822,6366],{"class":6342},[2225,161824,6480],{"class":2235},[2225,161826,6369],{"class":2235},[2225,161828,161829],{"class":2227,"line":1041},[2225,161830,2285],{"emptyLinePlaceholder":2284},[2225,161832,161833,161835,161837,161839,161841,161843,161845,161847,161849,161851,161853,161855,161857,161859,161861],{"class":2227,"line":1038},[2225,161834,2775],{"class":2774},[2225,161836,42762],{"class":2239},[2225,161838,2781],{"class":2235},[2225,161840,6466],{"class":2239},[2225,161842,1520],{"class":2235},[2225,161844,6471],{"class":2291},[2225,161846,6474],{"class":2235},[2225,161848,86814],{"class":6342},[2225,161850,6480],{"class":2235},[2225,161852,2789],{"class":2239},[2225,161854,2805],{"class":2235},[2225,161856,2854],{"class":2252},[2225,161858,2805],{"class":2235},[2225,161860,2298],{"class":2235},[2225,161862,2301],{"class":2235},[2225,161864,161865,161867,161869],{"class":2227,"line":2288},[2225,161866,59305],{"class":2307},[2225,161868,2311],{"class":2235},[2225,161870,2301],{"class":2235},[2225,161872,161873,161875,161877,161879,161881],{"class":2227,"line":2304},[2225,161874,114587],{"class":2307},[2225,161876,2311],{"class":2235},[2225,161878,58819],{"class":2291},[2225,161880,2789],{"class":2239},[2225,161882,4200],{"class":2235},[2225,161884,161885,161888,161890,161892,161894,161896,161898,161900,161902,161904,161906,161908,161910,161912,161914,161916,161918,161920],{"class":2227,"line":2356},[2225,161886,161887],{"class":2307},"      languages",[2225,161889,2311],{"class":2235},[2225,161891,8667],{"class":2239},[2225,161893,2805],{"class":2235},[2225,161895,1347],{"class":2252},[2225,161897,2805],{"class":2235},[2225,161899,2298],{"class":2235},[2225,161901,2249],{"class":2235},[2225,161903,59334],{"class":2252},[2225,161905,2805],{"class":2235},[2225,161907,8803],{"class":2239},[2225,161909,2298],{"class":2235},[2225,161911,8299],{"class":2307},[2225,161913,2311],{"class":2235},[2225,161915,2249],{"class":2235},[2225,161917,11932],{"class":2252},[2225,161919,2805],{"class":2235},[2225,161921,1707],{"class":2235},[2225,161923,161924,161927,161929,161931,161933,161935,161937,161939,161941,161943,161945,161947,161949,161951,161953,161955],{"class":2227,"line":2830},[2225,161925,161926],{"class":2307},"      substitute",[2225,161928,2311],{"class":2235},[2225,161930,8667],{"class":2239},[2225,161932,2805],{"class":2235},[2225,161934,59334],{"class":2252},[2225,161936,2805],{"class":2235},[2225,161938,2298],{"class":2235},[2225,161940,2249],{"class":2235},[2225,161942,1347],{"class":2252},[2225,161944,2805],{"class":2235},[2225,161946,2298],{"class":2235},[2225,161948,2249],{"class":2235},[2225,161950,11932],{"class":2252},[2225,161952,2805],{"class":2235},[2225,161954,8803],{"class":2239},[2225,161956,1707],{"class":2235},[2225,161958,161959,161962,161964,161966,161969,161971],{"class":2227,"line":2861},[2225,161960,161961],{"class":2307},"      onMissing",[2225,161963,2311],{"class":2235},[2225,161965,2249],{"class":2235},[2225,161967,161968],{"class":2252},"substitute",[2225,161970,2805],{"class":2235},[2225,161972,1707],{"class":2235},[2225,161974,161975,161978,161980,161982],{"class":2227,"line":4246},[2225,161976,161977],{"class":2307},"      smartSubstitute",[2225,161979,2311],{"class":2235},[2225,161981,2322],{"class":2321},[2225,161983,1707],{"class":2235},[2225,161985,161986,161989,161991,161993,161996,161998],{"class":2227,"line":4253},[2225,161987,161988],{"class":2307},"      script",[2225,161990,2311],{"class":2235},[2225,161992,2249],{"class":2235},[2225,161994,161995],{"class":2252},"auto",[2225,161997,2805],{"class":2235},[2225,161999,1707],{"class":2235},[2225,162001,162002,162005,162007,162009],{"class":2227,"line":4259},[2225,162003,162004],{"class":2307},"      densifyOnWrite",[2225,162006,2311],{"class":2235},[2225,162008,2322],{"class":2321},[2225,162010,1707],{"class":2235},[2225,162012,162013,162015,162017],{"class":2227,"line":6548},[2225,162014,80146],{"class":2235},[2225,162016,1381],{"class":2239},[2225,162018,1707],{"class":2235},[2225,162020,162021],{"class":2227,"line":6576},[2225,162022,19017],{"class":2235},[2225,162024,162025,162027],{"class":2227,"line":6582},[2225,162026,2359],{"class":2235},[2225,162028,2362],{"class":2239},[2225,162030,162031],{"class":2227,"line":6587},[2225,162032,2285],{"emptyLinePlaceholder":2284},[2225,162034,162035,162037,162039,162041,162043,162045,162047,162049,162051,162053,162055,162057,162059,162061,162063,162065,162067,162069,162071,162073,162075,162077,162079,162082,162084,162086,162088],{"class":2227,"line":7174},[2225,162036,6500],{"class":2231},[2225,162038,42817],{"class":2239},[2225,162040,1520],{"class":2235},[2225,162042,3161],{"class":2291},[2225,162044,2789],{"class":2239},[2225,162046,2805],{"class":2235},[2225,162048,129502],{"class":2252},[2225,162050,2805],{"class":2235},[2225,162052,2298],{"class":2235},[2225,162054,2236],{"class":2235},[2225,162056,7070],{"class":2307},[2225,162058,2311],{"class":2235},[2225,162060,2249],{"class":2235},[2225,162062,129502],{"class":2252},[2225,162064,2805],{"class":2235},[2225,162066,2298],{"class":2235},[2225,162068,42791],{"class":2307},[2225,162070,2311],{"class":2235},[2225,162072,2236],{"class":2235},[2225,162074,58997],{"class":2307},[2225,162076,2311],{"class":2235},[2225,162078,2249],{"class":2235},[2225,162080,162081],{"class":2252},"Site visit scheduled",[2225,162083,2805],{"class":2235},[2225,162085,2243],{"class":2235},[2225,162087,2243],{"class":2235},[2225,162089,2362],{"class":2239},[937,162091,3325,162092,162094,162095,162097,162098,162100],{},[941,162093,59334],{}," was authored, but ",[941,162096,149668],{}," fills the empty ",[941,162099,1347],{}," slot at\nwrite time from the substitute chain, so the stored record is already dense:",[2217,162102,162104],{"className":2219,"code":162103,"language":2221,"meta":1037,"style":1037},"await notes.get('n1', { locale: 'th' })\n\u002F\u002F → { id: 'n1', body: 'Site visit scheduled' }\n\u002F\u002F th was densify-filled at write time, so this resolves directly —\n\u002F\u002F no substitute walk needed on read\n\nawait notes.get('n1', { locale: 'raw' })\n\u002F\u002F → { id: 'n1', body: { en: 'Site visit scheduled', th: 'Site visit scheduled' } }\n\u002F\u002F the full locale map as stored — densify made both slots non-empty\n\nawait notes.i18nProvenance('n1')\n\u002F\u002F → { body: ['th'] }\n\u002F\u002F th was auto-filled; en is the only authored slot\n",[941,162105,162106,162142,162147,162152,162157,162161,162197,162202,162207,162211,162232,162237],{"__ignoreMap":1037},[2225,162107,162108,162110,162112,162114,162116,162118,162120,162122,162124,162126,162128,162130,162132,162134,162136,162138,162140],{"class":2227,"line":2228},[2225,162109,6500],{"class":2231},[2225,162111,42817],{"class":2239},[2225,162113,1520],{"class":2235},[2225,162115,3158],{"class":2291},[2225,162117,2789],{"class":2239},[2225,162119,2805],{"class":2235},[2225,162121,129502],{"class":2252},[2225,162123,2805],{"class":2235},[2225,162125,2298],{"class":2235},[2225,162127,2236],{"class":2235},[2225,162129,24003],{"class":2307},[2225,162131,2311],{"class":2235},[2225,162133,2249],{"class":2235},[2225,162135,1347],{"class":2252},[2225,162137,2805],{"class":2235},[2225,162139,2243],{"class":2235},[2225,162141,2362],{"class":2239},[2225,162143,162144],{"class":2227,"line":1041},[2225,162145,162146],{"class":2763},"\u002F\u002F → { id: 'n1', body: 'Site visit scheduled' }\n",[2225,162148,162149],{"class":2227,"line":1038},[2225,162150,162151],{"class":2763},"\u002F\u002F th was densify-filled at write time, so this resolves directly —\n",[2225,162153,162154],{"class":2227,"line":2288},[2225,162155,162156],{"class":2763},"\u002F\u002F no substitute walk needed on read\n",[2225,162158,162159],{"class":2227,"line":2304},[2225,162160,2285],{"emptyLinePlaceholder":2284},[2225,162162,162163,162165,162167,162169,162171,162173,162175,162177,162179,162181,162183,162185,162187,162189,162191,162193,162195],{"class":2227,"line":2356},[2225,162164,6500],{"class":2231},[2225,162166,42817],{"class":2239},[2225,162168,1520],{"class":2235},[2225,162170,3158],{"class":2291},[2225,162172,2789],{"class":2239},[2225,162174,2805],{"class":2235},[2225,162176,129502],{"class":2252},[2225,162178,2805],{"class":2235},[2225,162180,2298],{"class":2235},[2225,162182,2236],{"class":2235},[2225,162184,24003],{"class":2307},[2225,162186,2311],{"class":2235},[2225,162188,2249],{"class":2235},[2225,162190,30312],{"class":2252},[2225,162192,2805],{"class":2235},[2225,162194,2243],{"class":2235},[2225,162196,2362],{"class":2239},[2225,162198,162199],{"class":2227,"line":2830},[2225,162200,162201],{"class":2763},"\u002F\u002F → { id: 'n1', body: { en: 'Site visit scheduled', th: 'Site visit scheduled' } }\n",[2225,162203,162204],{"class":2227,"line":2861},[2225,162205,162206],{"class":2763},"\u002F\u002F the full locale map as stored — densify made both slots non-empty\n",[2225,162208,162209],{"class":2227,"line":4246},[2225,162210,2285],{"emptyLinePlaceholder":2284},[2225,162212,162213,162215,162217,162219,162222,162224,162226,162228,162230],{"class":2227,"line":4253},[2225,162214,6500],{"class":2231},[2225,162216,42817],{"class":2239},[2225,162218,1520],{"class":2235},[2225,162220,162221],{"class":2291},"i18nProvenance",[2225,162223,2789],{"class":2239},[2225,162225,2805],{"class":2235},[2225,162227,129502],{"class":2252},[2225,162229,2805],{"class":2235},[2225,162231,2362],{"class":2239},[2225,162233,162234],{"class":2227,"line":4259},[2225,162235,162236],{"class":2763},"\u002F\u002F → { body: ['th'] }\n",[2225,162238,162239],{"class":2227,"line":6548},[2225,162240,162241],{"class":2763},"\u002F\u002F th was auto-filled; en is the only authored slot\n",[937,162243,162244,162245,968,162247,968,162249,162251,162252,162255],{},"Every read (",[941,162246,3158],{},[941,162248,3167],{},[941,162250,30312],{}," included) strips the internal fill-tracking\nmarker — ",[941,162253,162254],{},"i18nProvenance()"," is the only way to see which locales were filled\nrather than authored.",[937,162257,162258,162266,162267,162269,162270,162273,162274,162276,162277,1884,162279,4765,162282,162285,162286,162288,162289,1625,162291,1625,162294,1625,162296,1625,162299,1625,162302,162304,162305,162307,162308,162310,162311,162313,162314,1520],{},[950,162259,162260,6030,162263,162265],{},[941,162261,162262],{},"onMissing",[941,162264,161968],{}," — missing-locale policy."," When a field is\nresolved to an absent locale, ",[941,162268,162262],{}," decides: ",[941,162271,162272],{},"'substitute'"," (walk\nthe declared ",[941,162275,161968],{}," chain, then ",[941,162278,6651],{},[941,162280,162281],{},"'null'",[941,162283,162284],{},"'throw'","\n(the default — today's behavior). ",[941,162287,162262],{}," may be a scalar or a\nper-layer map (",[941,162290,56023],{},[941,162292,162293],{},"guard",[941,162295,63728],{},[941,162297,162298],{},"mv",[941,162300,162301],{},"derivation",[941,162303,8080],{},"); the\n",[941,162306,162293],{}," layer defaults to lenient ",[941,162309,162272],{},". A caller-supplied\n",[941,162312,41462],{}," at read time overrides the declared ",[941,162315,161968],{},[937,162317,162318,1324,162321,162324,162325,162327,162328,162331,162332,162334,162335,162338,162339,162342,162343,1520],{},[950,162319,162320],{},"Smart substitute.",[941,162322,162323],{},"smartSubstitute: true"," makes the ",[941,162326,162272],{},"\nfallback ",[1092,162329,162330],{},"script-aware",": after the explicit ",[941,162333,161968],{}," chain misses, it picks\nthe available locale whose primary script is ",[950,162336,162337],{},"nearest"," the target — same\nscript first, then ",[950,162340,162341],{},"Latin"," (broadly readable), then any other — instead of an\narbitrary first-non-empty value. So a missing Thai label falls back to another\nThai (or a Latin) translation rather than, say, an Arabic one. Default ",[941,162344,2560],{},[2217,162346,162348],{"className":2219,"code":162347,"language":2221,"meta":1037,"style":1037},"i18nText({\n  languages: ['th', 'en'], required: 'any',\n  substitute: ['en', 'th', 'any'],\n  onMissing: { read: 'substitute', mv: 'throw', derivation: 'null' },\n  smartSubstitute: true,   \u002F\u002F nearest-script fallback\n})\n",[941,162349,162350,162358,162397,162432,162479,162493],{"__ignoreMap":1037},[2225,162351,162352,162354,162356],{"class":2227,"line":2228},[2225,162353,20943],{"class":2291},[2225,162355,2789],{"class":2239},[2225,162357,4200],{"class":2235},[2225,162359,162360,162363,162365,162367,162369,162371,162373,162375,162377,162379,162381,162383,162385,162387,162389,162391,162393,162395],{"class":2227,"line":1041},[2225,162361,162362],{"class":2307},"  languages",[2225,162364,2311],{"class":2235},[2225,162366,8667],{"class":2239},[2225,162368,2805],{"class":2235},[2225,162370,1347],{"class":2252},[2225,162372,2805],{"class":2235},[2225,162374,2298],{"class":2235},[2225,162376,2249],{"class":2235},[2225,162378,59334],{"class":2252},[2225,162380,2805],{"class":2235},[2225,162382,8803],{"class":2239},[2225,162384,2298],{"class":2235},[2225,162386,8299],{"class":2307},[2225,162388,2311],{"class":2235},[2225,162390,2249],{"class":2235},[2225,162392,11932],{"class":2252},[2225,162394,2805],{"class":2235},[2225,162396,1707],{"class":2235},[2225,162398,162399,162402,162404,162406,162408,162410,162412,162414,162416,162418,162420,162422,162424,162426,162428,162430],{"class":2227,"line":1038},[2225,162400,162401],{"class":2307},"  substitute",[2225,162403,2311],{"class":2235},[2225,162405,8667],{"class":2239},[2225,162407,2805],{"class":2235},[2225,162409,59334],{"class":2252},[2225,162411,2805],{"class":2235},[2225,162413,2298],{"class":2235},[2225,162415,2249],{"class":2235},[2225,162417,1347],{"class":2252},[2225,162419,2805],{"class":2235},[2225,162421,2298],{"class":2235},[2225,162423,2249],{"class":2235},[2225,162425,11932],{"class":2252},[2225,162427,2805],{"class":2235},[2225,162429,8803],{"class":2239},[2225,162431,1707],{"class":2235},[2225,162433,162434,162437,162439,162441,162443,162445,162447,162449,162451,162453,162456,162458,162460,162462,162464,162466,162469,162471,162473,162475,162477],{"class":2227,"line":2288},[2225,162435,162436],{"class":2307},"  onMissing",[2225,162438,2311],{"class":2235},[2225,162440,2236],{"class":2235},[2225,162442,150654],{"class":2307},[2225,162444,2311],{"class":2235},[2225,162446,2249],{"class":2235},[2225,162448,161968],{"class":2252},[2225,162450,2805],{"class":2235},[2225,162452,2298],{"class":2235},[2225,162454,162455],{"class":2307}," mv",[2225,162457,2311],{"class":2235},[2225,162459,2249],{"class":2235},[2225,162461,46514],{"class":2252},[2225,162463,2805],{"class":2235},[2225,162465,2298],{"class":2235},[2225,162467,162468],{"class":2307}," derivation",[2225,162470,2311],{"class":2235},[2225,162472,2249],{"class":2235},[2225,162474,6651],{"class":2252},[2225,162476,2805],{"class":2235},[2225,162478,2353],{"class":2235},[2225,162480,162481,162484,162486,162488,162490],{"class":2227,"line":2304},[2225,162482,162483],{"class":2307},"  smartSubstitute",[2225,162485,2311],{"class":2235},[2225,162487,2322],{"class":2321},[2225,162489,2298],{"class":2235},[2225,162491,162492],{"class":2763},"   \u002F\u002F nearest-script fallback\n",[2225,162494,162495,162497],{"class":2227,"line":2356},[2225,162496,2359],{"class":2235},[2225,162498,2362],{"class":2239},[1061,162500,162501],{},[937,162502,162503,1324,162506,953,162508,1625,162510,1884,162512,968,162514,1707,162516,968,162518,968,162520,162522,162523,162525,162526,162528,162529,162531,162532,162537,162538,964,162541,162544,162545,162548,162549,162552,162553,162556,162557,162559,162560,162562,162563,162566,162567,162570,162571,162574,162575,162579,162580,162583,162584,162588,162589,1625,162591,162594,162595,162597],{},[950,162504,162505],{},"Current wiring (ALL layers):",[941,162507,56023],{},[941,162509,3158],{},[941,162511,3167],{},[941,162513,162293],{},[941,162515,162301],{},[941,162517,162298],{},[941,162519,63728],{},[941,162521,8080],{}," are all enforced — guard \u002F derivation ",[941,162524,138578],{}," reads\nresolve under their own layer policy (",[941,162527,162293],{}," defaults to the lenient\n",[941,162530,162272],{},"). The ",[950,162533,162534,162536],{},[941,162535,162298],{}," layer"," fires for materialized views that declare\n",[941,162539,162540],{},"{ i18nLocale, i18nFields }",[950,162542,162543],{},"UNION"," (group-key i18n fields resolve before\nthe unified-row bucketing) and ",[950,162546,162547],{},"query-form"," (resolved in\n",[941,162550,162551],{},"GroupedReduction.run"," before ",[941,162554,162555],{},"groupAndReduce","); grouping a raw i18n field\nwithout a locale throws ",[941,162558,127871],{}," (group by a ",[941,162561,21456],{},"\u002F\n",[941,162564,162565],{},"staticDict"," code instead, and resolve the label at read). i18n fields merely\n",[1092,162568,162569],{},"carried through"," an MV stay raw — declare them on the ",[950,162572,162573],{},"output collection","\nand they resolve per-reader at read time. The ",[950,162576,162577,162536],{},[941,162578,63728],{}," resolves a\njoined right-side i18n field to the query locale —\n",[941,162581,162582],{},"query().join(...).toArray({ locale })"," (or the vault default; raw when\nlocale-less). The ",[950,162585,162586,162536],{},[941,162587,8080],{}," fires for\n",[941,162590,31685],{},[941,162592,162593],{},"exportJSON({ resolveLabels })"," — records collapse to the export\nlocale (i18nText to the string, dict ",[941,162596,24050],{},"s resolved), with the\ndictionary snapshot omitted.",[937,162599,162600,162608,162609,162612,162613,162616,162617,162619,162620,162623,162624,162626,162627,1625,162630,1625,162633,162636,162637,162640,162641,162644,162645,162648,162649,162651,162652,1520],{},[950,162601,162602,162603,968,162605,957],{},"Script enforcement (",[941,162604,42479],{},[941,162606,162607],{},"onScriptViolation"," Write-time\nper-locale validation. ",[941,162610,162611],{},"script: 'auto'"," infers allowed Unicode scripts\nwith ",[950,162614,162615],{},"asymmetric Latin tolerance",": non-Latin locales also allow\n",[941,162618,162341],{}," (so ",[941,162621,162622],{},"'9\u002F9 อาคาร TCM ถนนรัชดาภิเษก'"," passes), while Latin locales\nreject other scripts (so Thai text in an ",[941,162625,59334],{}," slot is rejected).\n",[941,162628,162629],{},"Common",[941,162631,162632],{},"Inherited",[941,162634,162635],{},"Mark"," (digits, combining marks) are always\nallowed. Violations ",[941,162638,162639],{},"'reject'"," (default, ",[941,162642,162643],{},"ScriptViolationError","),\n",[941,162646,162647],{},"'filter'"," (strip), or ",[941,162650,51655],{},". Tighten a field with an explicit set:\n",[941,162653,162654],{},"script: { th: ['Thai'] }",[937,162656,162657,162662,162663,20954,162666,162668,162669,162672,162673,162676],{},[950,162658,162659,162661],{},[941,162660,149668],{}," — eager slot fill at write time."," With\n",[941,162664,162665],{},"densifyOnWrite: true",[941,162667,20943],{}," field, every empty declared-language\nslot is filled from the field's substitute chain ",[950,162670,162671],{},"at write time"," — using\n",[941,162674,162675],{},"substitute ?? ['any']"," (first non-empty value when no explicit chain is\ndeclared) — so the stored locale map is always dense and downstream layers\n(join, MV, derivation, export) never hit a hole.",[937,162678,162679,162680,162683,162684,953,162687,1707,162689,968,162691,162693],{},"Which slots were auto-filled is recorded in an internal ",[941,162681,162682],{},"_i18nFilled"," marker\nthat is encrypted with the record and ",[950,162685,162686],{},"stripped from all reads",[941,162688,3158],{},[941,162690,3167],{},[941,162692,30312],{},"). The only sanctioned way to inspect it is:",[2217,162695,162697],{"className":2219,"code":162696,"language":2221,"meta":1037,"style":1037},"await collection.i18nProvenance(id)\n\u002F\u002F → e.g. { name: ['en'] }  — or undefined when nothing was filled\n",[941,162698,162699,162712],{"__ignoreMap":1037},[2225,162700,162701,162703,162705,162707,162709],{"class":2227,"line":2228},[2225,162702,6500],{"class":2231},[2225,162704,7061],{"class":2239},[2225,162706,1520],{"class":2235},[2225,162708,162221],{"class":2291},[2225,162710,162711],{"class":2239},"(id)\n",[2225,162713,162714],{"class":2227,"line":1041},[2225,162715,162716],{"class":2763},"\u002F\u002F → e.g. { name: ['en'] }  — or undefined when nothing was filled\n",[937,162718,162719],{},"Declaring the field:",[2217,162721,162723],{"className":2219,"code":162722,"language":2221,"meta":1037,"style":1037},"i18nText({\n  languages: ['th', 'en'], required: 'any',\n  substitute: ['th', 'any'],\n  densifyOnWrite: true,\n})\n",[941,162724,162725,162733,162771,162797,162808],{"__ignoreMap":1037},[2225,162726,162727,162729,162731],{"class":2227,"line":2228},[2225,162728,20943],{"class":2291},[2225,162730,2789],{"class":2239},[2225,162732,4200],{"class":2235},[2225,162734,162735,162737,162739,162741,162743,162745,162747,162749,162751,162753,162755,162757,162759,162761,162763,162765,162767,162769],{"class":2227,"line":1041},[2225,162736,162362],{"class":2307},[2225,162738,2311],{"class":2235},[2225,162740,8667],{"class":2239},[2225,162742,2805],{"class":2235},[2225,162744,1347],{"class":2252},[2225,162746,2805],{"class":2235},[2225,162748,2298],{"class":2235},[2225,162750,2249],{"class":2235},[2225,162752,59334],{"class":2252},[2225,162754,2805],{"class":2235},[2225,162756,8803],{"class":2239},[2225,162758,2298],{"class":2235},[2225,162760,8299],{"class":2307},[2225,162762,2311],{"class":2235},[2225,162764,2249],{"class":2235},[2225,162766,11932],{"class":2252},[2225,162768,2805],{"class":2235},[2225,162770,1707],{"class":2235},[2225,162772,162773,162775,162777,162779,162781,162783,162785,162787,162789,162791,162793,162795],{"class":2227,"line":1038},[2225,162774,162401],{"class":2307},[2225,162776,2311],{"class":2235},[2225,162778,8667],{"class":2239},[2225,162780,2805],{"class":2235},[2225,162782,1347],{"class":2252},[2225,162784,2805],{"class":2235},[2225,162786,2298],{"class":2235},[2225,162788,2249],{"class":2235},[2225,162790,11932],{"class":2252},[2225,162792,2805],{"class":2235},[2225,162794,8803],{"class":2239},[2225,162796,1707],{"class":2235},[2225,162798,162799,162802,162804,162806],{"class":2227,"line":2288},[2225,162800,162801],{"class":2307},"  densifyOnWrite",[2225,162803,2311],{"class":2235},[2225,162805,2322],{"class":2321},[2225,162807,1707],{"class":2235},[2225,162809,162810,162812],{"class":2227,"line":2304},[2225,162811,2359],{"class":2235},[2225,162813,2362],{"class":2239},[937,162815,162816,162819],{},[950,162817,162818],{},"Write sequence"," (for a densify-enabled field):",[1234,162821,162822,162825,162840,162850,162855,162858],{},[1114,162823,162824],{},"Auto-translate (existing)",[1114,162826,162827,162830,162831,968,162834,4765,162837],{},[941,162828,162829],{},"computeExemptFills"," — reads the prior record's marker; classifies each\nlocale slot as ",[1092,162832,162833],{},"authored",[1092,162835,162836],{},"exempt round-tripped fill",[1092,162838,162839],{},"cleared fill",[1114,162841,162842,162843,162846,162847,162849],{},"Script enforcement — checks ",[950,162844,162845],{},"authored slots only","; unchanged\nround-tripped fills are exempt (so Thai text in an ",[941,162848,59334],{}," fill slot does not\ntrigger a Latin-locale script rejection on a second write)",[1114,162851,162852,162853],{},"Required-validation — authored floor only; fills do not satisfy ",[941,162854,45375],{},[1114,162856,162857],{},"Densify — fill empty slots, record marker, refresh changed-source fills",[1114,162859,162860],{},"Encrypt and persist",[937,162862,162863,162866,162867,162869,162870,162872],{},[950,162864,162865],{},"Refresh and no-clobber."," Densify re-runs from the current authored slots\non every write, so correcting a source value automatically refreshes its\nderived slots (e.g. fixing ",[941,162868,1347],{}," re-densifies ",[941,162871,59334],{},"). If a user authors a real\nvalue into a formerly-filled slot the marker for that locale is cleared and\nthe value is treated as authored from that point on. A value byte-identical to\nthe existing fill remains classified as a fill (value-equality provenance\nlimitation).",[937,162874,162875,1324,162878,162880,162881,162883,162886,162887,162889,162890,162892,162893,162895,162897],{},[950,162876,162877],{},"Mutual exclusion.",[941,162879,162665],{}," combined with an ",[950,162882,1450],{},[941,162884,162885],{},"onMissing: 'throw'"," — scalar or any per-layer value — is a configuration\nerror (densify fills every hole, making a throw policy unreachable). Declaring\n",[941,162888,149668],{}," without an explicit ",[941,162891,162262],{}," is fine; the ",[1092,162894,64897],{},[941,162896,162284],{}," simply never fires.",[937,162899,162900,162903,162904,162907],{},[950,162901,162902],{},"Limitation."," Supports single-leaf i18n fields only. Array-wildcard paths\n(",[941,162905,162906],{},"contacts[].name",") are skipped, mirroring auto-translate.",[937,162909,162910,15920,162915,997,162917,162919,162920,162923],{},[950,162911,162912,162914],{},[941,162913,12172],{}," event channel.",[941,162916,51655],{},[941,162918,162647],{},"\nscript-enforcement modes collect ",[941,162921,162922],{},"ScriptWarning[]"," objects; previously those\nwarnings were silently discarded. The typed event channel surfaces them:",[2217,162925,162927],{"className":2219,"code":162926,"language":2221,"meta":1037,"style":1037},"\u002F\u002F Payload shape (ScriptWarning = { field, locale, expected, sample })\ninterface NoydbEventMap {\n  'i18n:script-violation': {\n    vault: string; collection: string; id: string\n    mode: 'warn' | 'filter'\n    warning: ScriptWarning\n  }\n}\n\ndb.on('i18n:script-violation', (e) => {\n  \u002F\u002F audit; e.mode === 'filter' means characters were dropped from e.warning.sample\n})\n",[941,162928,162929,162934,162942,162954,162978,162999,163009,163013,163017,163021,163049,163054],{"__ignoreMap":1037},[2225,162930,162931],{"class":2227,"line":2228},[2225,162932,162933],{"class":2763},"\u002F\u002F Payload shape (ScriptWarning = { field, locale, expected, sample })\n",[2225,162935,162936,162938,162940],{"class":2227,"line":1041},[2225,162937,6994],{"class":2774},[2225,162939,12274],{"class":6342},[2225,162941,2301],{"class":2235},[2225,162943,162944,162946,162948,162950,162952],{"class":2227,"line":1038},[2225,162945,124511],{"class":2235},[2225,162947,12172],{"class":2252},[2225,162949,2805],{"class":2235},[2225,162951,2311],{"class":2235},[2225,162953,2301],{"class":2235},[2225,162955,162956,162958,162960,162962,162964,162966,162968,162970,162972,162974,162976],{"class":2227,"line":2288},[2225,162957,88991],{"class":2307},[2225,162959,2311],{"class":2235},[2225,162961,6366],{"class":6342},[2225,162963,3769],{"class":2235},[2225,162965,7061],{"class":2307},[2225,162967,2311],{"class":2235},[2225,162969,6366],{"class":6342},[2225,162971,3769],{"class":2235},[2225,162973,7070],{"class":2307},[2225,162975,2311],{"class":2235},[2225,162977,7014],{"class":6342},[2225,162979,162980,162983,162985,162987,162989,162991,162993,162995,162997],{"class":2227,"line":2304},[2225,162981,162982],{"class":2307},"    mode",[2225,162984,2311],{"class":2235},[2225,162986,2249],{"class":2235},[2225,162988,150565],{"class":2252},[2225,162990,2805],{"class":2235},[2225,162992,7087],{"class":2235},[2225,162994,2249],{"class":2235},[2225,162996,8686],{"class":2252},[2225,162998,2256],{"class":2235},[2225,163000,163001,163004,163006],{"class":2227,"line":2356},[2225,163002,163003],{"class":2307},"    warning",[2225,163005,2311],{"class":2235},[2225,163007,163008],{"class":6342}," ScriptWarning\n",[2225,163010,163011],{"class":2227,"line":2830},[2225,163012,7632],{"class":2235},[2225,163014,163015],{"class":2227,"line":2861},[2225,163016,7351],{"class":2235},[2225,163018,163019],{"class":2227,"line":4246},[2225,163020,2285],{"emptyLinePlaceholder":2284},[2225,163022,163023,163025,163027,163029,163031,163033,163035,163037,163039,163041,163043,163045,163047],{"class":2227,"line":4253},[2225,163024,6590],{"class":2239},[2225,163026,1520],{"class":2235},[2225,163028,4883],{"class":2291},[2225,163030,2789],{"class":2239},[2225,163032,2805],{"class":2235},[2225,163034,12172],{"class":2252},[2225,163036,2805],{"class":2235},[2225,163038,2298],{"class":2235},[2225,163040,953],{"class":2235},[2225,163042,12450],{"class":7052},[2225,163044,1381],{"class":2235},[2225,163046,8116],{"class":2774},[2225,163048,2301],{"class":2235},[2225,163050,163051],{"class":2227,"line":4259},[2225,163052,163053],{"class":2763},"  \u002F\u002F audit; e.mode === 'filter' means characters were dropped from e.warning.sample\n",[2225,163055,163056,163058],{"class":2227,"line":6548},[2225,163057,2359],{"class":2235},[2225,163059,2362],{"class":2239},[937,163061,163062,163063,163065,163066,163068,163069,163071,163072,6789,163074,163076,163077,163080,163081,1625,163084,163087],{},"One event is emitted per warning per write. The event fires for ",[941,163064,51655],{},"\n(value stored as-is) and ",[941,163067,162647],{}," (disallowed characters stripped — the\nevent is the ",[950,163070,1320],{}," signal that stored data was mutated by script enforcement).\n",[941,163073,162639],{},[941,163075,162643],{}," and emits nothing (the caller sees\nthe error directly). No new subscription surface — the existing\n",[941,163078,163079],{},"NoydbEventEmitter"," API (",[941,163082,163083],{},"db.on",[941,163085,163086],{},"db.off",") is used.",[937,163089,163090,163096,163097,163099,163100,163103,163104,163106,163107,163110,163111,163114,163115,163118,163119,163122],{},[950,163091,163092,163095],{},[941,163093,163094],{},"I18nMap\u003CLangs, Required>"," type helper."," Types the stored locale-map\nfrom the ",[941,163098,45375],{}," mode so the compiler forces you to handle an absent\noptional locale (",[941,163101,163102],{},"string | undefined",") instead of silently reading\n",[941,163105,18316],{}," — the right fix for bare ",[941,163108,163109],{},".th"," access at raw-reading\n(identity\u002Fguard\u002Fexport) layers. ",[941,163112,163113],{},"'all'"," (default) → every locale\nrequired; ",[941,163116,163117],{},"'any'"," → every locale optional; ",[941,163120,163121],{},"readonly L[]"," → listed\nrequired, rest optional.",[2217,163124,163126],{"className":2219,"code":163125,"language":2221,"meta":1037,"style":1037},"type Lang = 'th' | 'en'\ninterface Contact {\n  name: I18nMap\u003CLang, 'any'>        \u002F\u002F { th?: string; en?: string }\n  legalName: I18nMap\u003CLang, ['th']>  \u002F\u002F { th: string; en?: string }\n}\n",[941,163127,163128,163151,163160,163187,163217],{"__ignoreMap":1037},[2225,163129,163130,163132,163135,163137,163139,163141,163143,163145,163147,163149],{"class":2227,"line":2228},[2225,163131,6339],{"class":2774},[2225,163133,163134],{"class":6342}," Lang",[2225,163136,6346],{"class":2235},[2225,163138,2249],{"class":2235},[2225,163140,1347],{"class":2252},[2225,163142,2805],{"class":2235},[2225,163144,7087],{"class":2235},[2225,163146,2249],{"class":2235},[2225,163148,59334],{"class":2252},[2225,163150,2256],{"class":2235},[2225,163152,163153,163155,163158],{"class":2227,"line":1041},[2225,163154,6994],{"class":2774},[2225,163156,163157],{"class":6342}," Contact",[2225,163159,2301],{"class":2235},[2225,163161,163162,163164,163166,163169,163171,163174,163176,163178,163180,163182,163184],{"class":2227,"line":1038},[2225,163163,7008],{"class":2307},[2225,163165,2311],{"class":2235},[2225,163167,163168],{"class":6342}," I18nMap",[2225,163170,6474],{"class":2235},[2225,163172,163173],{"class":6342},"Lang",[2225,163175,2298],{"class":2235},[2225,163177,2249],{"class":2235},[2225,163179,11932],{"class":2252},[2225,163181,2805],{"class":2235},[2225,163183,6480],{"class":2235},[2225,163185,163186],{"class":2763},"        \u002F\u002F { th?: string; en?: string }\n",[2225,163188,163189,163192,163194,163196,163198,163200,163202,163204,163206,163208,163210,163212,163214],{"class":2227,"line":2288},[2225,163190,163191],{"class":2307},"  legalName",[2225,163193,2311],{"class":2235},[2225,163195,163168],{"class":6342},[2225,163197,6474],{"class":2235},[2225,163199,163173],{"class":6342},[2225,163201,2298],{"class":2235},[2225,163203,8667],{"class":2239},[2225,163205,2805],{"class":2235},[2225,163207,1347],{"class":2252},[2225,163209,2805],{"class":2235},[2225,163211,8803],{"class":2239},[2225,163213,6480],{"class":2235},[2225,163215,163216],{"class":2763},"  \u002F\u002F { th: string; en?: string }\n",[2225,163218,163219],{"class":2227,"line":2304},[2225,163220,7351],{"class":2235},[937,163222,163223,1324,163226,163228,163229,1625,163231,163233,163234,163236,163237,163240,163241,163244,163245,1617,163248,163251,163252,163255,163256,163258,163259,1625,163262,1746,163265,957],{},[950,163224,163225],{},"dictKey parity.",[941,163227,58348],{}," accepts the same ",[941,163230,162262],{},[941,163232,161968],{},"\n(default stays ",[941,163235,162281],{}," = legacy omit-on-miss). An ",[950,163238,163239],{},"array-of-keys","\nfield resolves to ",[941,163242,163243],{},"[{ key, label }]"," pair objects; a ",[950,163246,163247],{},"wildcard path",[941,163249,163250],{},"contacts[].title",") adds a per-element sibling ",[941,163253,163254],{},"\u003Cleaf>Label",". The\nstable key is preserved through resolution, so identity operations\n(",[941,163257,27496],{},", dedup) bind to the key even when labels collapse across\nlocales (",[941,163260,163261],{},"Mr.",[941,163263,163264],{},"Ms.",[941,163266,163267],{},"คุณ",[937,163269,163270,163273,163274,163277,163278,163281,163282,1625,163284,163286,163287,163290,163291,1324,163293,163295,163296,163299],{},[950,163271,163272],{},"Label sort."," Identity binding stays on the code, but when you want\nresults ",[1092,163275,163276],{},"ordered"," by the human label, opt in per-call:\n",[941,163279,163280],{},"query().orderBy('status', 'asc', { by: 'label' })"," sorts a ",[941,163283,21456],{},[941,163285,162565],{},"\nfield by its resolved label at the query locale (",[941,163288,163289],{},"toArray({ locale })",", or a\n",[941,163292,162565],{},[941,163294,149032],{}," when locale-less), falling back to the code when no\nlabel resolves. The default ",[941,163297,163298],{},"orderBy('status')"," still sorts by the stored code.",[1022,163301,163303],{"id":163302},"staticdict-code-provided-dictionary","staticDict (code-provided dictionary)",[937,163305,163306,163309,163310,6793,163312,163315,163316,163318,163319,163321,163322,163324],{},[941,163307,163308],{},"staticDict(name, table, opts?)"," is a sibling to ",[941,163311,21456],{},[950,163313,163314],{},"closed,\ndefined-in-code, identical-across-vaults"," enums (honorific, civil-status,\ngender, religion, status…). The labels live in the in-code ",[941,163317,1338],{},"; the record\nstores only the stable ",[950,163320,941],{},". Drop it into the same ",[941,163323,22759],{}," slot:",[2217,163326,163328],{"className":2219,"code":163327,"language":2221,"meta":1037,"style":1037},"interface Worker { id: string; civilStatus: string }\n\nconst workers = vault.collection\u003CWorker>('workers', {\n  dictKeyFields: {\n    department:  dictKey('department', ['ops', 'fin'] as const),  \u002F\u002F per-vault, editable\n    civilStatus: staticDict('civilStatus', {                      \u002F\u002F code-defined\n      adultMale:   { th: 'นาย',    en: 'Mr'  },\n      adultFemale: { th: 'นาง',    en: 'Mrs' },\n    }, { displayLocale: 'th' }),\n  },\n})\n\nawait workers.put('w1', { id: 'w1', civilStatus: 'adultMale' }) \u002F\u002F stores only the code\n\nawait workers.get('w1')\n\u002F\u002F → { id: 'w1', civilStatus: 'adultMale', civilStatusLabel: 'นาย' }\n\u002F\u002F locale-less read: civilStatusLabel still resolves via displayLocale\n\nawait workers.get('w1', { locale: 'en' })\n\u002F\u002F → { id: 'w1', civilStatus: 'adultMale', civilStatusLabel: 'Mr' }\n\u002F\u002F an active locale wins over displayLocale\n",[941,163329,163330,163355,163359,163391,163399,163451,163476,163511,163546,163570,163574,163580,163584,163636,163640,163660,163665,163670,163674,163710,163715],{"__ignoreMap":1037},[2225,163331,163332,163334,163336,163338,163340,163342,163344,163346,163349,163351,163353],{"class":2227,"line":2228},[2225,163333,6994],{"class":2774},[2225,163335,132716],{"class":6342},[2225,163337,2236],{"class":2235},[2225,163339,7070],{"class":2307},[2225,163341,2311],{"class":2235},[2225,163343,6366],{"class":6342},[2225,163345,3769],{"class":2235},[2225,163347,163348],{"class":2307}," civilStatus",[2225,163350,2311],{"class":2235},[2225,163352,6366],{"class":6342},[2225,163354,6369],{"class":2235},[2225,163356,163357],{"class":2227,"line":1041},[2225,163358,2285],{"emptyLinePlaceholder":2284},[2225,163360,163361,163363,163365,163367,163369,163371,163373,163375,163377,163379,163381,163383,163385,163387,163389],{"class":2227,"line":1038},[2225,163362,2775],{"class":2774},[2225,163364,132818],{"class":2239},[2225,163366,2781],{"class":2235},[2225,163368,6466],{"class":2239},[2225,163370,1520],{"class":2235},[2225,163372,6471],{"class":2291},[2225,163374,6474],{"class":2235},[2225,163376,132831],{"class":6342},[2225,163378,6480],{"class":2235},[2225,163380,2789],{"class":2239},[2225,163382,2805],{"class":2235},[2225,163384,132840],{"class":2252},[2225,163386,2805],{"class":2235},[2225,163388,2298],{"class":2235},[2225,163390,2301],{"class":2235},[2225,163392,163393,163395,163397],{"class":2227,"line":2288},[2225,163394,59231],{"class":2307},[2225,163396,2311],{"class":2235},[2225,163398,2301],{"class":2235},[2225,163400,163401,163404,163406,163409,163411,163413,163416,163418,163420,163422,163424,163427,163429,163431,163433,163436,163438,163440,163442,163444,163446,163448],{"class":2227,"line":2304},[2225,163402,163403],{"class":2307},"    department",[2225,163405,2311],{"class":2235},[2225,163407,163408],{"class":2291},"  dictKey",[2225,163410,2789],{"class":2239},[2225,163412,2805],{"class":2235},[2225,163414,163415],{"class":2252},"department",[2225,163417,2805],{"class":2235},[2225,163419,2298],{"class":2235},[2225,163421,8667],{"class":2239},[2225,163423,2805],{"class":2235},[2225,163425,163426],{"class":2252},"ops",[2225,163428,2805],{"class":2235},[2225,163430,2298],{"class":2235},[2225,163432,2249],{"class":2235},[2225,163434,163435],{"class":2252},"fin",[2225,163437,2805],{"class":2235},[2225,163439,8890],{"class":2239},[2225,163441,4899],{"class":2231},[2225,163443,8083],{"class":2774},[2225,163445,1381],{"class":2239},[2225,163447,2298],{"class":2235},[2225,163449,163450],{"class":2763},"  \u002F\u002F per-vault, editable\n",[2225,163452,163453,163456,163458,163460,163462,163464,163467,163469,163471,163473],{"class":2227,"line":2356},[2225,163454,163455],{"class":2307},"    civilStatus",[2225,163457,2311],{"class":2235},[2225,163459,149354],{"class":2291},[2225,163461,2789],{"class":2239},[2225,163463,2805],{"class":2235},[2225,163465,163466],{"class":2252},"civilStatus",[2225,163468,2805],{"class":2235},[2225,163470,2298],{"class":2235},[2225,163472,2236],{"class":2235},[2225,163474,163475],{"class":2763},"                      \u002F\u002F code-defined\n",[2225,163477,163478,163481,163483,163485,163487,163489,163491,163494,163496,163498,163500,163502,163504,163507,163509],{"class":2227,"line":2830},[2225,163479,163480],{"class":2307},"      adultMale",[2225,163482,2311],{"class":2235},[2225,163484,58994],{"class":2235},[2225,163486,60621],{"class":2307},[2225,163488,2311],{"class":2235},[2225,163490,2249],{"class":2235},[2225,163492,163493],{"class":2252},"นาย",[2225,163495,2805],{"class":2235},[2225,163497,2298],{"class":2235},[2225,163499,59472],{"class":2307},[2225,163501,2311],{"class":2235},[2225,163503,2249],{"class":2235},[2225,163505,163506],{"class":2252},"Mr",[2225,163508,2805],{"class":2235},[2225,163510,19017],{"class":2235},[2225,163512,163513,163516,163518,163520,163522,163524,163526,163529,163531,163533,163535,163537,163539,163542,163544],{"class":2227,"line":2861},[2225,163514,163515],{"class":2307},"      adultFemale",[2225,163517,2311],{"class":2235},[2225,163519,2236],{"class":2235},[2225,163521,60621],{"class":2307},[2225,163523,2311],{"class":2235},[2225,163525,2249],{"class":2235},[2225,163527,163528],{"class":2252},"นาง",[2225,163530,2805],{"class":2235},[2225,163532,2298],{"class":2235},[2225,163534,59472],{"class":2307},[2225,163536,2311],{"class":2235},[2225,163538,2249],{"class":2235},[2225,163540,163541],{"class":2252},"Mrs",[2225,163543,2805],{"class":2235},[2225,163545,2353],{"class":2235},[2225,163547,163548,163551,163553,163556,163558,163560,163562,163564,163566,163568],{"class":2227,"line":4246},[2225,163549,163550],{"class":2235},"    },",[2225,163552,2236],{"class":2235},[2225,163554,163555],{"class":2307}," displayLocale",[2225,163557,2311],{"class":2235},[2225,163559,2249],{"class":2235},[2225,163561,1347],{"class":2252},[2225,163563,2805],{"class":2235},[2225,163565,2243],{"class":2235},[2225,163567,1381],{"class":2239},[2225,163569,1707],{"class":2235},[2225,163571,163572],{"class":2227,"line":4253},[2225,163573,19017],{"class":2235},[2225,163575,163576,163578],{"class":2227,"line":4259},[2225,163577,2359],{"class":2235},[2225,163579,2362],{"class":2239},[2225,163581,163582],{"class":2227,"line":6548},[2225,163583,2285],{"emptyLinePlaceholder":2284},[2225,163585,163586,163588,163590,163592,163594,163596,163598,163600,163602,163604,163606,163608,163610,163612,163614,163616,163618,163620,163622,163624,163627,163629,163631,163633],{"class":2227,"line":6576},[2225,163587,6500],{"class":2231},[2225,163589,132985],{"class":2239},[2225,163591,1520],{"class":2235},[2225,163593,3161],{"class":2291},[2225,163595,2789],{"class":2239},[2225,163597,2805],{"class":2235},[2225,163599,132996],{"class":2252},[2225,163601,2805],{"class":2235},[2225,163603,2298],{"class":2235},[2225,163605,2236],{"class":2235},[2225,163607,7070],{"class":2307},[2225,163609,2311],{"class":2235},[2225,163611,2249],{"class":2235},[2225,163613,132996],{"class":2252},[2225,163615,2805],{"class":2235},[2225,163617,2298],{"class":2235},[2225,163619,163348],{"class":2307},[2225,163621,2311],{"class":2235},[2225,163623,2249],{"class":2235},[2225,163625,163626],{"class":2252},"adultMale",[2225,163628,2805],{"class":2235},[2225,163630,2243],{"class":2235},[2225,163632,8495],{"class":2239},[2225,163634,163635],{"class":2763},"\u002F\u002F stores only the code\n",[2225,163637,163638],{"class":2227,"line":6582},[2225,163639,2285],{"emptyLinePlaceholder":2284},[2225,163641,163642,163644,163646,163648,163650,163652,163654,163656,163658],{"class":2227,"line":6587},[2225,163643,6500],{"class":2231},[2225,163645,132985],{"class":2239},[2225,163647,1520],{"class":2235},[2225,163649,3158],{"class":2291},[2225,163651,2789],{"class":2239},[2225,163653,2805],{"class":2235},[2225,163655,132996],{"class":2252},[2225,163657,2805],{"class":2235},[2225,163659,2362],{"class":2239},[2225,163661,163662],{"class":2227,"line":7174},[2225,163663,163664],{"class":2763},"\u002F\u002F → { id: 'w1', civilStatus: 'adultMale', civilStatusLabel: 'นาย' }\n",[2225,163666,163667],{"class":2227,"line":7180},[2225,163668,163669],{"class":2763},"\u002F\u002F locale-less read: civilStatusLabel still resolves via displayLocale\n",[2225,163671,163672],{"class":2227,"line":7220},[2225,163673,2285],{"emptyLinePlaceholder":2284},[2225,163675,163676,163678,163680,163682,163684,163686,163688,163690,163692,163694,163696,163698,163700,163702,163704,163706,163708],{"class":2227,"line":7225},[2225,163677,6500],{"class":2231},[2225,163679,132985],{"class":2239},[2225,163681,1520],{"class":2235},[2225,163683,3158],{"class":2291},[2225,163685,2789],{"class":2239},[2225,163687,2805],{"class":2235},[2225,163689,132996],{"class":2252},[2225,163691,2805],{"class":2235},[2225,163693,2298],{"class":2235},[2225,163695,2236],{"class":2235},[2225,163697,24003],{"class":2307},[2225,163699,2311],{"class":2235},[2225,163701,2249],{"class":2235},[2225,163703,59334],{"class":2252},[2225,163705,2805],{"class":2235},[2225,163707,2243],{"class":2235},[2225,163709,2362],{"class":2239},[2225,163711,163712],{"class":2227,"line":7231},[2225,163713,163714],{"class":2763},"\u002F\u002F → { id: 'w1', civilStatus: 'adultMale', civilStatusLabel: 'Mr' }\n",[2225,163716,163717],{"class":2227,"line":7267},[2225,163718,163719],{"class":2763},"\u002F\u002F an active locale wins over displayLocale\n",[937,163721,163722,163723,2311],{},"How it differs from ",[941,163724,21456],{},[1111,163726,163727,163740,163781,163793,163808],{},[1114,163728,163729,997,163734,163739],{},[950,163730,163731,163732,7061],{},"No ",[941,163733,6796],{},[950,163735,46960,163736],{},[941,163737,163738],{},"rename()"," — labels are code constants,\nso there is no per-vault encrypted copy and no O(records) migration. A label\nchange is a code deploy.",[1114,163741,163742,163745,163746,52758,163748,163750,163751,163754,163755,163757,163758,163760,163761,163763,163764,163766,163767,1625,163769,163771,163772,163774,163775,163777,163778,163780],{},[950,163743,163744],{},"Hybrid resolution."," With a configured ",[941,163747,149032],{},[941,163749,24050],{},"\nresolves even under a ",[950,163752,163753],{},"locale-less"," read — the property a locale-less app\nneeds and ",[941,163756,21456],{}," cannot provide. Without ",[941,163759,149032],{}," it behaves like\n",[941,163762,21456],{}," (no label until a locale is active). When a locale ",[1092,163765,3082],{}," active, it\nhonors ",[941,163768,162262],{},[941,163770,161968],{}," exactly like ",[941,163773,21456],{},". The locale gate is\notherwise unchanged: an ",[941,163776,20943],{},"-only or plain-",[941,163779,21456],{}," collection still\nreturns the raw record on a locale-less read.",[1114,163782,163783,1324,163786,104458,163789,163792],{},[950,163784,163785],{},"Read-only by construction.",[941,163787,163788],{},"vault.dictionary(staticName)",[941,163790,163791],{},"StaticDictReadonlyError"," — a static dict has no mutation surface.",[1114,163794,163795,163798,163799,104458,163801,163804,163805,957],{},[950,163796,163797],{},"Put-time code validation."," A stored code outside ",[941,163800,1338],{},[941,163802,163803],{},"UnknownDictCodeError"," (opt out per descriptor with ",[941,163806,163807],{},"{ validateCodes: false }",[1114,163809,163810,1324,163813,163816,163817,163820,163821,163823],{},[950,163811,163812],{},"Query parity.",[941,163814,163815],{},"groupBy(field)"," buckets by the stable code; the\ncode-table-backed source resolves ",[941,163818,163819],{},"{ by: 'label' }"," at the active locale, or\n",[941,163822,149032],{}," when the query is locale-less.",[937,163825,163826,163827,163830],{},"Migrating from a userland helper needs ",[950,163828,163829],{},"zero record change"," — the record\nalready stores the code; only the field descriptor changes.",[1022,163832,163834],{"id":163833},"relation-to-the-lookup-feature","Relation to the lookup feature",[937,163836,10993,163837,968,163839,997,163841,163844,163845,163847,163848,163850,163851,163853,163854,163856,163857,163859,163860,163862,163863,1625,163866,1625,163869,163872,163873,1625,163875,1625,163877,163879,163880,163882,163883,163885,163886,163888],{},[941,163838,111464],{},[941,163840,58348],{},[941,163842,163843],{},"staticDict()"," are\n",[950,163846,146774],{}," of the ",[1007,163849,731],{"href":732}," feature's descriptor shape — ",[941,163852,58348],{}," matching\nits reserved-dictionary tier, ",[941,163855,163843],{}," its static tier. This is not a deprecation: both\nspellings stay fully supported, keep compiling onto the i18n binding, and their stored\nenvelopes, ",[941,163858,3926],{}," output, and ",[941,163861,61808],{}," dressing are byte-identical to their native\nequivalents. What the native ",[941,163864,163865],{},"lookup()",[941,163867,163868],{},"enumOf()",[941,163870,163871],{},"dict()"," spellings add on top — alternate-key\nnormalization, closed-vocabulary write refusal, a first-class collection (\"matrix\") tier, and\n",[941,163874,152822],{},[941,163876,10753],{},[941,163878,152838],{}," reference semantics on delete\u002Fforget — is exclusive to them; see\n",[1007,163881,731],{"href":732},". Reserved ",[941,163884,6796],{}," dictionaries themselves now also replicate through\n",[1007,163887,864],{"href":865},", with dictionary deletes traveling as delete markers.",[937,163890,163891,163892,163894,163895,163898],{},"Both declarations can also be spelled through the ",[1007,163893,896],{"href":897}," composer\n(",[941,163896,163897],{},"viaFields: { status: via(dictKey('status', ['paid'] as const)) }",") with identical internals.",[1022,163900,114684],{"id":114683},[1111,163902,163903,163911],{},[1114,163904,163905,964,163907,163910],{},[950,163906,4449],{},[941,163908,163909],{},"groupBy(dictKeyField)"," resolves labels in the result on render",[1114,163912,163913,163915],{},[950,163914,697],{}," — locale changes are versioned",[1022,163917,6825],{"id":6824},[1111,163919,163920,163930,163941],{},[1114,163921,163922,163923,163926,163927],{},"Required locales declared in ",[941,163924,163925],{},"i18nText({ required: 'all' })"," are enforced on put — missing translations throw ",[941,163928,163929],{},"MissingTranslationError",[1114,163931,163932,163934,163935,163937,163938,163940],{},[941,163933,116077],{}," translate fields call out to the ",[941,163936,13335],{}," callback set at ",[941,163939,12780],{},"-time",[1114,163942,163943,163944,163946],{},"Dictionary ",[941,163945,163738],{}," rewrites every collection record that points at the old key — O(records) per rename",[1022,163948,2621],{"id":2620},[1111,163950,163951,163961,163966,163971,163981,163988,163992],{},[1114,163952,163953,163955,163956,1625,163958,163960],{},[1007,163954,731],{"href":732}," — the reference-field feature ",[941,163957,58348],{},[941,163959,163843],{}," alias onto",[1114,163962,163963,163965],{},[1007,163964,896],{"href":897}," — the field-feature composer shared by all field declarations",[1114,163967,163968,163970],{},[1007,163969,65376],{"href":48}," — the vault\u002Fcollection model this service builds on",[1114,163972,163973,163975,163976,1625,163978,163980],{},[1007,163974,507],{"href":508}," — an invoicing app using ",[941,163977,21456],{},[941,163979,20943],{}," end to end",[1114,163982,163983,163985,163986],{},[1007,163984,697],{"href":698}," — versions dictionary and record changes when opted in alongside ",[941,163987,701],{},[1114,163989,163990,61744],{},[1007,163991,597],{"href":598},[1114,163993,141161,163994,997,164000,1520],{},[1007,163995,163997],{"href":92593,"rel":163996},[3371],[941,163998,163999],{},"09-with-i18n",[1007,164001,164003],{"href":93774,"rel":164002},[3371],[941,164004,164005],{},"94-with-i18n-hardening",[2645,164007,164008],{},"html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html pre.shiki code .sfNiH, html code.shiki .sfNiH{--shiki-light:#FF5370;--shiki-default:#FF9CAC;--shiki-dark:#FF9CAC}html pre.shiki code .sHdIc, html code.shiki .sHdIc{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#EEFFFF;--shiki-default-font-style:italic;--shiki-dark:#BABED8;--shiki-dark-font-style:italic}",{"title":1037,"searchDepth":1038,"depth":1038,"links":164010},[164011,164012,164013,164014,164015,164016,164017,164018,164019,164020,164021,164022,164023],{"id":29856,"depth":1041,"text":29857},{"id":111659,"depth":1041,"text":111660},{"id":70092,"depth":1041,"text":70093},{"id":99863,"depth":1041,"text":99864},{"id":29892,"depth":1041,"text":29893},{"id":41259,"depth":1041,"text":101012},{"id":101424,"depth":1041,"text":101425},{"id":161762,"depth":1041,"text":161763},{"id":163302,"depth":1041,"text":163303},{"id":163833,"depth":1041,"text":163834},{"id":114683,"depth":1041,"text":114684},{"id":6824,"depth":1041,"text":6825},{"id":2620,"depth":1041,"text":2621},"Multi-locale records via a shared label dictionary (dictKey) or per-record translated text (i18nText), resolved to the caller's locale at read time.",{},{"title":701,"description":164024},"3xcKhDUKEvDFx94HDJn48fqg-aj5kVDiosLwbdHVLj8",{"id":928,"title":597,"access":12,"apiToc":929,"body":164029,"category":1042,"description":1043,"extension":1044,"meta":164090,"navigation":164091,"nest":1047,"path":598,"seo":164092,"sinceVersion":1042,"status":1049,"stem":602,"subtitle":1042,"tier":1050,"tocExternal":1042,"__hash__":1051},{"type":931,"value":164030,"toc":164087},[164031,164033,164043,164075,164079,164081,164085],[934,164032,597],{"id":599},[937,164034,939,164035,944,164037,948,164039,953,164041,957],{},[941,164036,943],{},[941,164038,947],{},[950,164040,952],{},[941,164042,956],{},[937,164044,960,164045,964,164047,968,164049,968,164051,968,164053,968,164055,968,164057,968,164059,968,164061,968,164063,993,164065,997,164067,1001,164069,1005,164071,1012],{},[950,164046,963],{},[941,164048,967],{},[941,164050,971],{},[941,164052,974],{},[941,164054,977],{},[941,164056,980],{},[941,164058,983],{},[941,164060,986],{},[941,164062,989],{},[941,164064,992],{},[941,164066,996],{},[941,164068,1000],{},[950,164070,1004],{},[1007,164072,164073],{"href":897},[941,164074,1011],{},[937,164076,1015,164077,1020],{},[1007,164078,1019],{"href":1018},[1022,164080,1025],{"id":1024},[937,164082,1028,164083,1032],{},[941,164084,1031],{},[1034,164086],{},{"title":1037,"searchDepth":1038,"depth":1038,"links":164088},[164089],{"id":1024,"depth":1041,"text":1025},{},{"title":10,"icon":603},{"title":597,"description":1043},{"id":164094,"title":705,"access":12,"apiToc":929,"body":164095,"category":1042,"description":165453,"extension":1044,"meta":165454,"navigation":1042,"nest":1047,"path":706,"seo":165455,"sinceVersion":1042,"status":1049,"stem":707,"subtitle":1042,"tier":1050,"tocExternal":1042,"__hash__":165456},"docs\u002Fservices\u002Findexing.md",{"type":931,"value":164096,"toc":165435},[164097,164099,164102,164104,164132,164135,164168,164178,164180,164207,164209,164236,164238,164361,164364,164441,164450,164452,164463,164619,164632,164716,164727,164741,164906,164908,164913,164929,164942,164947,164965,164994,165062,165077,165083,165098,165101,165107,165189,165204,165209,165212,165268,165279,165281,165311,165313,165337,165339,165388,165390,165432],[934,164098,705],{"id":705},[46871,164100],{":rows":164101},"[{\"label\":\"Subpath\",\"value\":\"`@noy-db\u002Fhub\u002Findexing`\"},{\"label\":\"Factory\",\"value\":\"`withIndexing()`\"},{\"label\":\"Layer\",\"value\":\"`with-lookup`\"},{\"label\":\"Cluster\",\"value\":\"A — Read & Query\"},{\"label\":\"LOC cost\",\"value\":\"~886 (off-bundle when not opted in)\"}]",[1022,164103,29857],{"id":29856},[937,164105,3289,164106,164108,164109,1625,164111,164113,164114,164116,164117,164119,164120,164123,164124,968,164126,164128,164129,164131],{},[1007,164107,6471],{"href":58262}," is a set of encrypted records in a noy-db vault, and ",[941,164110,24715],{},[941,164112,24807],{}," are its chainable, in-hub read builders — ",[941,164115,26648],{}," filters records (including ",[941,164118,4891],{},"-list membership tests), and ",[941,164121,164122],{},".orderBy(field)"," sorts them, all running after decryption, never against the raw store. The indexing service adds a secondary-index layer under those builders: a fast lookup structure keyed by one or more record fields, so ",[941,164125,24883],{},[941,164127,4891],{},"-list filters, and ",[941,164130,41940],{}," don't have to touch every record. Without an index, a query is a linear scan over the collection's in-memory cache (eager mode) or a walk of the backing store (lazy mode); with an index declared on the field, the same query becomes an O(1) hash lookup or an O(log n) sorted-range scan.",[937,164133,164134],{},"Where the index lives depends on the collection's hydration mode:",[1111,164136,164137,164151],{},[1114,164138,164139,953,164142,164145,164146,1625,164148,164150],{},[950,164140,164141],{},"Eager-mode collections",[941,164143,164144],{},"prefetch: true",", the default) hold the index as an in-memory mirror. It's built from the cache on load and updated incrementally on every ",[941,164147,3161],{},[941,164149,3164],{},"; it never touches the store, and rebuilds itself from the record cache on restart.",[1114,164152,164153,953,164156,164158,164159,1324,164161,164164,164165,164167],{},[950,164154,164155],{},"Lazy-mode collections",[941,164157,6836],{},") can't keep the whole collection in memory, so the index has to be durable: each indexed field gets one ",[941,164160,61864],{},[950,164162,164163],{},"side-car"," per record — an extra encrypted entry, stored next to the main record, holding just that field's value. ",[941,164166,24807],{}," reads these side-cars to resolve a candidate set of record ids without decrypting the whole collection.",[937,164169,164170,164171,6000,164174,164177],{},"Because side-car writes are a separate store call from the main record write, they can drift out of sync if the store is ever modified out-of-band (a restored backup, a manual edit). ",[941,164172,164173],{},"reconcileIndex()",[941,164175,164176],{},"reconcileOnOpen"," option exist to detect and repair that drift — see Usage below.",[1022,164179,111660],{"id":111659},[1111,164181,164182,164185,164194,164199],{},[1114,164183,164184],{},"A collection with more than ~5,000 records",[1114,164186,164187,164188,968,164190,968,164192,1381],{},"Queries that filter by stable fields (",[941,164189,6631],{},[941,164191,53894],{},[941,164193,59721],{},[1114,164195,164196,164198],{},[941,164197,3019],{}," on a hot field",[1114,164200,164201,164202,164204,164205,1381],{},"Lazy-mode collections (memory-bounded; ",[941,164203,24715],{}," won't work without an index — use ",[941,164206,24807],{},[1022,164208,70093],{"id":70092},[1111,164210,164211,164222],{},[1114,164212,164213,164215,164216,164218,164219,164221],{},[950,164214,107493],{}," standalone — ",[941,164217,6766],{}," plus per-collection ",[941,164220,21907],{}," declarations accelerate eager queries.",[1114,164223,164224,1324,164229,164231,164232,27678,164234,1520],{},[1092,164225,110954,164226,164228],{},[941,164227,24807],{}," on a lazy collection:",[941,164230,64481],{}," must also be passed, and every field the query touches must be declared in ",[941,164233,21907],{},[1007,164235,719],{"href":720},[1022,164237,99864],{"id":99863},[2217,164239,164241],{"className":2219,"code":164240,"language":2221,"meta":1037,"style":1037},"import { createNoydb } from '@noy-db\u002Fhub'\nimport { toMemory } from '@noy-db\u002Fto-memory'\nimport { withIndexing } from '@noy-db\u002Fhub\u002Findexing'\n\nconst db = await createNoydb({\n  store: toMemory(),\n  user: 'alice',\n  indexingStrategy: withIndexing(),\n})\n",[941,164242,164243,164261,164279,164297,164301,164317,164329,164343,164355],{"__ignoreMap":1037},[2225,164244,164245,164247,164249,164251,164253,164255,164257,164259],{"class":2227,"line":2228},[2225,164246,2232],{"class":2231},[2225,164248,2236],{"class":2235},[2225,164250,2744],{"class":2239},[2225,164252,2243],{"class":2235},[2225,164254,2246],{"class":2231},[2225,164256,2249],{"class":2235},[2225,164258,1086],{"class":2252},[2225,164260,2256],{"class":2235},[2225,164262,164263,164265,164267,164269,164271,164273,164275,164277],{"class":2227,"line":1041},[2225,164264,2232],{"class":2231},[2225,164266,2236],{"class":2235},[2225,164268,68576],{"class":2239},[2225,164270,2243],{"class":2235},[2225,164272,2246],{"class":2231},[2225,164274,2249],{"class":2235},[2225,164276,9638],{"class":2252},[2225,164278,2256],{"class":2235},[2225,164280,164281,164283,164285,164287,164289,164291,164293,164295],{"class":2227,"line":1038},[2225,164282,2232],{"class":2231},[2225,164284,2236],{"class":2235},[2225,164286,4165],{"class":2239},[2225,164288,2243],{"class":2235},[2225,164290,2246],{"class":2231},[2225,164292,2249],{"class":2235},[2225,164294,4174],{"class":2252},[2225,164296,2256],{"class":2235},[2225,164298,164299],{"class":2227,"line":2288},[2225,164300,2285],{"emptyLinePlaceholder":2284},[2225,164302,164303,164305,164307,164309,164311,164313,164315],{"class":2227,"line":2304},[2225,164304,2775],{"class":2774},[2225,164306,2778],{"class":2239},[2225,164308,2781],{"class":2235},[2225,164310,2784],{"class":2231},[2225,164312,2744],{"class":2291},[2225,164314,2789],{"class":2239},[2225,164316,4200],{"class":2235},[2225,164318,164319,164321,164323,164325,164327],{"class":2227,"line":2356},[2225,164320,15608],{"class":2307},[2225,164322,2311],{"class":2235},[2225,164324,68576],{"class":2291},[2225,164326,4228],{"class":2239},[2225,164328,1707],{"class":2235},[2225,164330,164331,164333,164335,164337,164339,164341],{"class":2227,"line":2830},[2225,164332,4205],{"class":2307},[2225,164334,2311],{"class":2235},[2225,164336,2249],{"class":2235},[2225,164338,6408],{"class":2252},[2225,164340,2805],{"class":2235},[2225,164342,1707],{"class":2235},[2225,164344,164345,164347,164349,164351,164353],{"class":2227,"line":2861},[2225,164346,4235],{"class":2307},[2225,164348,2311],{"class":2235},[2225,164350,4165],{"class":2291},[2225,164352,4228],{"class":2239},[2225,164354,1707],{"class":2235},[2225,164356,164357,164359],{"class":2227,"line":4246},[2225,164358,2359],{"class":2235},[2225,164360,2362],{"class":2239},[937,164362,164363],{},"Declare which fields to index per collection:",[2217,164365,164367],{"className":2219,"code":164366,"language":2221,"meta":1037,"style":1037},"const invoices = vault.collection\u003CInvoice>('invoices', {\n  indexes: ['clientId', 'status', 'date'],\n})\n",[941,164368,164369,164401,164435],{"__ignoreMap":1037},[2225,164370,164371,164373,164375,164377,164379,164381,164383,164385,164387,164389,164391,164393,164395,164397,164399],{"class":2227,"line":2228},[2225,164372,2775],{"class":2774},[2225,164374,6461],{"class":2239},[2225,164376,2781],{"class":2235},[2225,164378,6466],{"class":2239},[2225,164380,1520],{"class":2235},[2225,164382,6471],{"class":2291},[2225,164384,6474],{"class":2235},[2225,164386,6477],{"class":6342},[2225,164388,6480],{"class":2235},[2225,164390,2789],{"class":2239},[2225,164392,2805],{"class":2235},[2225,164394,6487],{"class":2252},[2225,164396,2805],{"class":2235},[2225,164398,2298],{"class":2235},[2225,164400,2301],{"class":2235},[2225,164402,164403,164405,164407,164409,164411,164413,164415,164417,164419,164421,164423,164425,164427,164429,164431,164433],{"class":2227,"line":1041},[2225,164404,62599],{"class":2307},[2225,164406,2311],{"class":2235},[2225,164408,8667],{"class":2239},[2225,164410,2805],{"class":2235},[2225,164412,59721],{"class":2252},[2225,164414,2805],{"class":2235},[2225,164416,2298],{"class":2235},[2225,164418,2249],{"class":2235},[2225,164420,6631],{"class":2252},[2225,164422,2805],{"class":2235},[2225,164424,2298],{"class":2235},[2225,164426,2249],{"class":2235},[2225,164428,26305],{"class":2252},[2225,164430,2805],{"class":2235},[2225,164432,8803],{"class":2239},[2225,164434,1707],{"class":2235},[2225,164436,164437,164439],{"class":2227,"line":1038},[2225,164438,2359],{"class":2235},[2225,164440,2362],{"class":2239},[937,164442,164443,164445,164446,164449],{},[941,164444,21907],{}," accepts a field name (single-field index), or ",[941,164447,164448],{},"{ fields: [...] }"," \u002F a plain array (a composite index over the field tuple, in order).",[1022,164451,29893],{"id":29892},[937,164453,164454,1324,164457,164459,164460,164462],{},[950,164455,164456],{},"Backfill after a bulk import.",[941,164458,25686],{}," throws away whatever index state exists and rebuilds it from the current records — the in-memory mirror in eager mode, every ",[941,164461,61864],{}," side-car in lazy mode. Use it after adding a new indexed field to a collection that already holds data, or to recover from widespread drift:",[2217,164464,164466],{"className":2219,"code":164465,"language":2221,"meta":1037,"style":1037},"\u002F\u002F 10,000 invoices were imported before `indexes: ['clientId']` was declared.\nawait invoices.rebuildIndexes()\n\u002F\u002F → resolves once every side-car (lazy mode) or the in-memory mirror\n\u002F\u002F   (eager mode) reflects the current records\n\nconst paid = await invoices.lazyQuery()\n  .where('clientId', '==', 'c-42')\n  .orderBy('date', 'desc')\n  .limit(20)\n  .toArray()\n\u002F\u002F → [\n\u002F\u002F     { id: 'inv-118', clientId: 'c-42', date: '2026-06-30', amount: 4200, status: 'paid' },\n\u002F\u002F     { id: 'inv-091', clientId: 'c-42', date: '2026-05-14', amount: 1800, status: 'paid' },\n\u002F\u002F     …\n\u002F\u002F   ]\n",[941,164467,164468,164473,164486,164491,164496,164500,164519,164552,164576,164588,164596,164600,164605,164610,164615],{"__ignoreMap":1037},[2225,164469,164470],{"class":2227,"line":2228},[2225,164471,164472],{"class":2763},"\u002F\u002F 10,000 invoices were imported before `indexes: ['clientId']` was declared.\n",[2225,164474,164475,164477,164479,164481,164484],{"class":2227,"line":1041},[2225,164476,6500],{"class":2231},[2225,164478,6503],{"class":2239},[2225,164480,1520],{"class":2235},[2225,164482,164483],{"class":2291},"rebuildIndexes",[2225,164485,8146],{"class":2239},[2225,164487,164488],{"class":2227,"line":1038},[2225,164489,164490],{"class":2763},"\u002F\u002F → resolves once every side-car (lazy mode) or the in-memory mirror\n",[2225,164492,164493],{"class":2227,"line":2288},[2225,164494,164495],{"class":2763},"\u002F\u002F   (eager mode) reflects the current records\n",[2225,164497,164498],{"class":2227,"line":2304},[2225,164499,2285],{"emptyLinePlaceholder":2284},[2225,164501,164502,164504,164507,164509,164511,164513,164515,164517],{"class":2227,"line":2356},[2225,164503,2775],{"class":2774},[2225,164505,164506],{"class":2239}," paid ",[2225,164508,2781],{"class":2235},[2225,164510,2784],{"class":2231},[2225,164512,6503],{"class":2239},[2225,164514,1520],{"class":2235},[2225,164516,64696],{"class":2291},[2225,164518,8146],{"class":2239},[2225,164520,164521,164523,164525,164527,164529,164531,164533,164535,164537,164539,164541,164543,164545,164548,164550],{"class":2227,"line":2830},[2225,164522,26235],{"class":2235},[2225,164524,3016],{"class":2291},[2225,164526,2789],{"class":2239},[2225,164528,2805],{"class":2235},[2225,164530,59721],{"class":2252},[2225,164532,2805],{"class":2235},[2225,164534,2298],{"class":2235},[2225,164536,2249],{"class":2235},[2225,164538,21901],{"class":2252},[2225,164540,2805],{"class":2235},[2225,164542,2298],{"class":2235},[2225,164544,2249],{"class":2235},[2225,164546,164547],{"class":2252},"c-42",[2225,164549,2805],{"class":2235},[2225,164551,2362],{"class":2239},[2225,164553,164554,164556,164558,164560,164562,164564,164566,164568,164570,164572,164574],{"class":2227,"line":2861},[2225,164555,26235],{"class":2235},[2225,164557,3019],{"class":2291},[2225,164559,2789],{"class":2239},[2225,164561,2805],{"class":2235},[2225,164563,26305],{"class":2252},[2225,164565,2805],{"class":2235},[2225,164567,2298],{"class":2235},[2225,164569,2249],{"class":2235},[2225,164571,26314],{"class":2252},[2225,164573,2805],{"class":2235},[2225,164575,2362],{"class":2239},[2225,164577,164578,164580,164582,164584,164586],{"class":2227,"line":4246},[2225,164579,26235],{"class":2235},[2225,164581,3022],{"class":2291},[2225,164583,2789],{"class":2239},[2225,164585,92764],{"class":6527},[2225,164587,2362],{"class":2239},[2225,164589,164590,164592,164594],{"class":2227,"line":4253},[2225,164591,26235],{"class":2235},[2225,164593,3230],{"class":2291},[2225,164595,8146],{"class":2239},[2225,164597,164598],{"class":2227,"line":4259},[2225,164599,100852],{"class":2763},[2225,164601,164602],{"class":2227,"line":6548},[2225,164603,164604],{"class":2763},"\u002F\u002F     { id: 'inv-118', clientId: 'c-42', date: '2026-06-30', amount: 4200, status: 'paid' },\n",[2225,164606,164607],{"class":2227,"line":6576},[2225,164608,164609],{"class":2763},"\u002F\u002F     { id: 'inv-091', clientId: 'c-42', date: '2026-05-14', amount: 1800, status: 'paid' },\n",[2225,164611,164612],{"class":2227,"line":6582},[2225,164613,164614],{"class":2763},"\u002F\u002F     …\n",[2225,164616,164617],{"class":2227,"line":6587},[2225,164618,100867],{"class":2763},[937,164620,164621,1324,164624,164627,164628,164631],{},[950,164622,164623],{},"Detect drift without repairing it.",[941,164625,164626],{},"reconcileIndex(field, { dryRun: true })"," compares every ",[941,164629,164630],{},"_idx\u002F\u003Cfield>\u002F*"," side-car against the live records and reports what's out of sync, without writing anything:",[2217,164633,164635],{"className":2219,"code":164634,"language":2221,"meta":1037,"style":1037},"const report = await invoices.reconcileIndex('clientId', { dryRun: true })\n\u002F\u002F → {\n\u002F\u002F     field: 'clientId',\n\u002F\u002F     missing: ['inv-042'],              \u002F\u002F indexable, but no side-car exists\n\u002F\u002F     stale: ['_idx\u002FclientId\u002Finv-999'],  \u002F\u002F side-car points at a vanished\u002Fchanged record\n\u002F\u002F     applied: 0                         \u002F\u002F dryRun: true never writes\n\u002F\u002F   }\n",[941,164636,164637,164677,164682,164687,164695,164703,164711],{"__ignoreMap":1037},[2225,164638,164639,164641,164644,164646,164648,164650,164652,164655,164657,164659,164661,164663,164665,164667,164669,164671,164673,164675],{"class":2227,"line":2228},[2225,164640,2775],{"class":2774},[2225,164642,164643],{"class":2239}," report ",[2225,164645,2781],{"class":2235},[2225,164647,2784],{"class":2231},[2225,164649,6503],{"class":2239},[2225,164651,1520],{"class":2235},[2225,164653,164654],{"class":2291},"reconcileIndex",[2225,164656,2789],{"class":2239},[2225,164658,2805],{"class":2235},[2225,164660,59721],{"class":2252},[2225,164662,2805],{"class":2235},[2225,164664,2298],{"class":2235},[2225,164666,2236],{"class":2235},[2225,164668,100775],{"class":2307},[2225,164670,2311],{"class":2235},[2225,164672,2322],{"class":2321},[2225,164674,2243],{"class":2235},[2225,164676,2362],{"class":2239},[2225,164678,164679],{"class":2227,"line":1041},[2225,164680,164681],{"class":2763},"\u002F\u002F → {\n",[2225,164683,164684],{"class":2227,"line":1038},[2225,164685,164686],{"class":2763},"\u002F\u002F     field: 'clientId',\n",[2225,164688,164689,164692],{"class":2227,"line":2288},[2225,164690,164691],{"class":2763},"\u002F\u002F     missing: ['inv-042'],",[2225,164693,164694],{"class":2763},"              \u002F\u002F indexable, but no side-car exists\n",[2225,164696,164697,164700],{"class":2227,"line":2304},[2225,164698,164699],{"class":2763},"\u002F\u002F     stale: ['_idx\u002FclientId\u002Finv-999'],",[2225,164701,164702],{"class":2763},"  \u002F\u002F side-car points at a vanished\u002Fchanged record\n",[2225,164704,164705,164708],{"class":2227,"line":2356},[2225,164706,164707],{"class":2763},"\u002F\u002F     applied: 0",[2225,164709,164710],{"class":2763},"                         \u002F\u002F dryRun: true never writes\n",[2225,164712,164713],{"class":2227,"line":2830},[2225,164714,164715],{"class":2763},"\u002F\u002F   }\n",[937,164717,164718,164719,164722,164723,164726],{},"Call it again with ",[941,164720,164721],{},"{ dryRun: false }"," (the default) to write the fixes; ",[941,164724,164725],{},"applied"," then reports how many writes actually happened.",[937,164728,164729,164732,164733,164735,164736,1625,164738,164740],{},[950,164730,164731],{},"Auto-reconcile on open."," Rather than calling ",[941,164734,164173],{}," by hand, a lazy-mode collection can run it automatically the first time its persisted-index mirror loads (its first ",[941,164737,24807],{},[941,164739,24715],{}," call after the vault opens):",[2217,164742,164744],{"className":2219,"code":164743,"language":2221,"meta":1037,"style":1037},"const invoices = vault.collection\u003CInvoice>('invoices', {\n  prefetch: false,\n  cache: { maxRecords: 1000 },\n  indexes: ['clientId'],\n  reconcileOnOpen: 'auto',\n})\n\ndb.on('index:reconciled', (e) => {\n  console.log(e)\n  \u002F\u002F → { vault: 'acme', collection: 'invoices', field: 'clientId',\n  \u002F\u002F     missing: [], stale: ['_idx\u002FclientId\u002Finv-999'], applied: 1, skipped: false }\n})\n",[941,164745,164746,164778,164788,164804,164822,164837,164843,164847,164876,164890,164895,164900],{"__ignoreMap":1037},[2225,164747,164748,164750,164752,164754,164756,164758,164760,164762,164764,164766,164768,164770,164772,164774,164776],{"class":2227,"line":2228},[2225,164749,2775],{"class":2774},[2225,164751,6461],{"class":2239},[2225,164753,2781],{"class":2235},[2225,164755,6466],{"class":2239},[2225,164757,1520],{"class":2235},[2225,164759,6471],{"class":2291},[2225,164761,6474],{"class":2235},[2225,164763,6477],{"class":6342},[2225,164765,6480],{"class":2235},[2225,164767,2789],{"class":2239},[2225,164769,2805],{"class":2235},[2225,164771,6487],{"class":2252},[2225,164773,2805],{"class":2235},[2225,164775,2298],{"class":2235},[2225,164777,2301],{"class":2235},[2225,164779,164780,164782,164784,164786],{"class":2227,"line":1041},[2225,164781,64535],{"class":2307},[2225,164783,2311],{"class":2235},[2225,164785,2341],{"class":2321},[2225,164787,1707],{"class":2235},[2225,164789,164790,164792,164794,164796,164798,164800,164802],{"class":2227,"line":1038},[2225,164791,64549],{"class":2307},[2225,164793,2311],{"class":2235},[2225,164795,2236],{"class":2235},[2225,164797,64556],{"class":2307},[2225,164799,2311],{"class":2235},[2225,164801,26290],{"class":6527},[2225,164803,2353],{"class":2235},[2225,164805,164806,164808,164810,164812,164814,164816,164818,164820],{"class":2227,"line":2288},[2225,164807,62599],{"class":2307},[2225,164809,2311],{"class":2235},[2225,164811,8667],{"class":2239},[2225,164813,2805],{"class":2235},[2225,164815,59721],{"class":2252},[2225,164817,2805],{"class":2235},[2225,164819,8803],{"class":2239},[2225,164821,1707],{"class":2235},[2225,164823,164824,164827,164829,164831,164833,164835],{"class":2227,"line":2304},[2225,164825,164826],{"class":2307},"  reconcileOnOpen",[2225,164828,2311],{"class":2235},[2225,164830,2249],{"class":2235},[2225,164832,161995],{"class":2252},[2225,164834,2805],{"class":2235},[2225,164836,1707],{"class":2235},[2225,164838,164839,164841],{"class":2227,"line":2356},[2225,164840,2359],{"class":2235},[2225,164842,2362],{"class":2239},[2225,164844,164845],{"class":2227,"line":2830},[2225,164846,2285],{"emptyLinePlaceholder":2284},[2225,164848,164849,164851,164853,164855,164857,164859,164862,164864,164866,164868,164870,164872,164874],{"class":2227,"line":2861},[2225,164850,6590],{"class":2239},[2225,164852,1520],{"class":2235},[2225,164854,4883],{"class":2291},[2225,164856,2789],{"class":2239},[2225,164858,2805],{"class":2235},[2225,164860,164861],{"class":2252},"index:reconciled",[2225,164863,2805],{"class":2235},[2225,164865,2298],{"class":2235},[2225,164867,953],{"class":2235},[2225,164869,12450],{"class":7052},[2225,164871,1381],{"class":2235},[2225,164873,8116],{"class":2774},[2225,164875,2301],{"class":2235},[2225,164877,164878,164880,164882,164884,164886,164888],{"class":2227,"line":4246},[2225,164879,27095],{"class":2239},[2225,164881,1520],{"class":2235},[2225,164883,27100],{"class":2291},[2225,164885,2789],{"class":2307},[2225,164887,12450],{"class":2239},[2225,164889,2362],{"class":2307},[2225,164891,164892],{"class":2227,"line":4253},[2225,164893,164894],{"class":2763},"  \u002F\u002F → { vault: 'acme', collection: 'invoices', field: 'clientId',\n",[2225,164896,164897],{"class":2227,"line":4259},[2225,164898,164899],{"class":2763},"  \u002F\u002F     missing: [], stale: ['_idx\u002FclientId\u002Finv-999'], applied: 1, skipped: false }\n",[2225,164901,164902,164904],{"class":2227,"line":6548},[2225,164903,2359],{"class":2235},[2225,164905,2362],{"class":2239},[1022,164907,102889],{"id":102888},[1591,164909,164911],{"id":164910},"withindexing",[941,164912,6766],{},[2217,164914,164916],{"className":2219,"code":164915,"language":2221,"meta":1037,"style":1037},"function withIndexing(): IndexStrategy\n",[941,164917,164918],{"__ignoreMap":1037},[2225,164919,164920,164922,164924,164926],{"class":2227,"line":2228},[2225,164921,12641],{"class":2774},[2225,164923,4165],{"class":2291},[2225,164925,112812],{"class":2235},[2225,164927,164928],{"class":6342}," IndexStrategy\n",[937,164930,164931,164932,164934,164935,164938,164939,164941],{},"Builds the strategy object that lights up indexing for a ",[941,164933,62],{}," instance. Pass it to ",[941,164936,164937],{},"createNoydb({ indexingStrategy })"," — see Opt-in above. With no ",[941,164940,72880],{},", every collection behaves as described in \"Behavior when NOT opted in\" below.",[1591,164943,164945],{"id":164944},"collectionlazyquery",[941,164946,27522],{},[2217,164948,164950],{"className":2219,"code":164949,"language":2221,"meta":1037,"style":1037},"lazyQuery(): LazyQuery\u003CT>\n",[941,164951,164952],{"__ignoreMap":1037},[2225,164953,164954,164956,164959,164961,164963],{"class":2227,"line":2228},[2225,164955,64696],{"class":2291},[2225,164957,164958],{"class":2239},"(): LazyQuery",[2225,164960,6474],{"class":2235},[2225,164962,11968],{"class":2239},[2225,164964,7093],{"class":2235},[937,164966,164967,164968,140295,164970,164972,164973,4688,164975,164978,164979,164981,164982,968,164985,2462,164988,164990,164991,164993],{},"Builds a chainable, indexed read against a ",[950,164969,27503],{},[941,164971,6836],{},"). Every field touched by ",[941,164974,133842],{},[941,164976,164977],{},".orderBy(...)"," must be declared in ",[941,164980,21907],{},". Terminals are ",[941,164983,164984],{},".toArray()",[941,164986,164987],{},".first()",[941,164989,65247],{},", all ",[941,164992,11740],{},"-returning since candidate records are decrypted from the store on demand.",[2217,164995,164997],{"className":2219,"code":164996,"language":2221,"meta":1037,"style":1037},"const row = await invoices.lazyQuery()\n  .where('clientId', '==', 'c-42')\n  .first()\n\u002F\u002F → { id: 'inv-118', clientId: 'c-42', date: '2026-06-30', amount: 4200, status: 'paid' }\n",[941,164998,164999,165017,165049,165057],{"__ignoreMap":1037},[2225,165000,165001,165003,165005,165007,165009,165011,165013,165015],{"class":2227,"line":2228},[2225,165002,2775],{"class":2774},[2225,165004,118164],{"class":2239},[2225,165006,2781],{"class":2235},[2225,165008,2784],{"class":2231},[2225,165010,6503],{"class":2239},[2225,165012,1520],{"class":2235},[2225,165014,64696],{"class":2291},[2225,165016,8146],{"class":2239},[2225,165018,165019,165021,165023,165025,165027,165029,165031,165033,165035,165037,165039,165041,165043,165045,165047],{"class":2227,"line":1041},[2225,165020,26235],{"class":2235},[2225,165022,3016],{"class":2291},[2225,165024,2789],{"class":2239},[2225,165026,2805],{"class":2235},[2225,165028,59721],{"class":2252},[2225,165030,2805],{"class":2235},[2225,165032,2298],{"class":2235},[2225,165034,2249],{"class":2235},[2225,165036,21901],{"class":2252},[2225,165038,2805],{"class":2235},[2225,165040,2298],{"class":2235},[2225,165042,2249],{"class":2235},[2225,165044,164547],{"class":2252},[2225,165046,2805],{"class":2235},[2225,165048,2362],{"class":2239},[2225,165050,165051,165053,165055],{"class":2227,"line":1038},[2225,165052,26235],{"class":2235},[2225,165054,3233],{"class":2291},[2225,165056,8146],{"class":2239},[2225,165058,165059],{"class":2227,"line":2288},[2225,165060,165061],{"class":2763},"\u002F\u002F → { id: 'inv-118', clientId: 'c-42', date: '2026-06-30', amount: 4200, status: 'paid' }\n",[937,165063,16810,165064,102958,165067,1625,165069,1625,165071,165073,165074,165076],{},[941,165065,165066],{},"IndexRequiredError",[941,165068,164984],{},[941,165070,164987],{},[941,165072,65247],{}," is called with a filtered or ordered field that isn't declared in ",[941,165075,21907],{},"; throws synchronously (before any request) if the collection is eager-mode, or if no index is declared at all.",[1591,165078,165080],{"id":165079},"collectionrebuildindexes",[941,165081,165082],{},"collection.rebuildIndexes()",[2217,165084,165086],{"className":2219,"code":165085,"language":2221,"meta":1037,"style":1037},"rebuildIndexes(): Promise\u003Cvoid>\n",[941,165087,165088],{"__ignoreMap":1037},[2225,165089,165090,165092,165094,165096],{"class":2227,"line":2228},[2225,165091,164483],{"class":2291},[2225,165093,12809],{"class":2239},[2225,165095,11740],{"class":6342},[2225,165097,13016],{"class":2235},[937,165099,165100],{},"Rebuilds every declared index from scratch — not incremental. See the backfill example under Usage above.",[1591,165102,165104],{"id":165103},"collectionreconcileindexfield-opts",[941,165105,165106],{},"collection.reconcileIndex(field, opts?)",[2217,165108,165110],{"className":2219,"code":165109,"language":2221,"meta":1037,"style":1037},"reconcileIndex(\n  field: string,\n  opts?: { dryRun?: boolean },\n): Promise\u003C{ field: string; missing: string[]; stale: string[]; applied: number }>\n",[941,165111,165112,165118,165125,165142],{"__ignoreMap":1037},[2225,165113,165114,165116],{"class":2227,"line":2228},[2225,165115,164654],{"class":2291},[2225,165117,11683],{"class":2239},[2225,165119,165120,165123],{"class":2227,"line":1041},[2225,165121,165122],{"class":2239},"  field: string",[2225,165124,1707],{"class":2235},[2225,165126,165127,165129,165131,165133,165136,165138,165140],{"class":2227,"line":1038},[2225,165128,11695],{"class":2239},[2225,165130,7011],{"class":2235},[2225,165132,2236],{"class":2235},[2225,165134,165135],{"class":2239}," dryRun?",[2225,165137,2311],{"class":2235},[2225,165139,146181],{"class":2239},[2225,165141,19008],{"class":2235},[2225,165143,165144,165146,165148,165150,165152,165154,165156,165158,165161,165163,165165,165167,165169,165172,165174,165176,165178,165180,165183,165185,165187],{"class":2227,"line":2288},[2225,165145,11737],{"class":2239},[2225,165147,11740],{"class":6342},[2225,165149,17851],{"class":2235},[2225,165151,124557],{"class":6342},[2225,165153,2311],{"class":2235},[2225,165155,6366],{"class":2239},[2225,165157,3769],{"class":2235},[2225,165159,165160],{"class":6342}," missing",[2225,165162,2311],{"class":2235},[2225,165164,6366],{"class":2239},[2225,165166,7262],{"class":2307},[2225,165168,3769],{"class":2235},[2225,165170,165171],{"class":6342}," stale",[2225,165173,2311],{"class":2235},[2225,165175,6366],{"class":2239},[2225,165177,7262],{"class":2307},[2225,165179,3769],{"class":2235},[2225,165181,165182],{"class":6342}," applied",[2225,165184,2311],{"class":2235},[2225,165186,6356],{"class":2239},[2225,165188,17861],{"class":2235},[937,165190,165191,165192,165194,165195,165198,165199,165201,165202,1520],{},"Lazy mode only. Compares the ",[941,165193,164630],{}," side-cars for one declared field against the live records and reports (and, unless ",[941,165196,165197],{},"dryRun: true",", repairs) the drift — see the dry-run example under Usage above. Throws in eager mode (there's no durable side-car to drift), and throws if ",[941,165200,25197],{}," isn't declared in ",[941,165203,21907],{},[1591,165205,165207],{"id":165206},"reconcileonopen",[941,165208,164176],{},[937,165210,165211],{},"Auto-reconcile policy for a lazy-mode collection's persisted indexes, evaluated the first time its index mirror loads.",[1338,165213,165214,165222],{},[1341,165215,165216],{},[1344,165217,165218,165220],{},[1347,165219,91926],{},[1347,165221,124024],{},[1357,165223,165224,165237,165252],{},[1344,165225,165226,165231],{},[1362,165227,165228,91937],{},[941,165229,165230],{},"'off'",[1362,165232,165233,165234,165236],{},"No implicit work. Call ",[941,165235,25689],{}," yourself when you want a report.",[1344,165238,165239,165244],{},[1362,165240,165241],{},[941,165242,165243],{},"'dry-run'",[1362,165245,125973,165246,165248,165249,165251],{},[941,165247,164626],{}," for every declared field and emits ",[941,165250,164861],{}," per field with the diff. Nothing is written.",[1344,165253,165254,165258],{},[1362,165255,165256],{},[941,165257,116077],{},[1362,165259,165260,165261,165263,165264,165267],{},"Same walk as ",[941,165262,165243],{},", but with ",[941,165265,165266],{},"dryRun: false"," — drift is repaired in place and the fix count is on the event.",[937,165269,116274,165270,165272,165273,165275,165276,165278],{},[941,165271,116077],{}," for unattended long-lived processes (servers, workers); leave it ",[941,165274,165230],{}," (or use ",[941,165277,165243],{},") for human-operated apps and surface a manual \"rebuild indexes\" action instead.",[1022,165280,101425],{"id":101424},[1111,165282,165283,165293,165301,165306],{},[1114,165284,165285,165286,165289,165290,165292],{},"Eager-mode collections: ",[941,165287,165288],{},"where('field', '==', value)"," falls back to a linear scan; ",[941,165291,139522],{}," is ignored",[1114,165294,165295,165296,165298,165299],{},"Lazy-mode collections: ",[941,165297,24807],{}," throws, pointing at ",[941,165300,4174],{},[1114,165302,165303,165305],{},[941,165304,25686],{}," is a no-op — there is no index state to rebuild",[1114,165307,165308,165310],{},[941,165309,164173],{}," always throws: in eager mode because reconcile only applies to durable side-cars; in lazy mode because indexing isn't enabled",[1022,165312,114684],{"id":114683},[1111,165314,165315,165323,165330],{},[1114,165316,165317,165319,165320,165322],{},[950,165318,715],{}," (always-core, no opt-in needed) — the join planner already gets O(1) per-row lookups from the collection's own primary-key cache, independent of whether ",[941,165321,6766],{}," is enabled; this service doesn't change join performance",[1114,165324,165325,964,165327,165329],{},[950,165326,4449],{},[941,165328,27496],{}," dispatches through the index when present",[1114,165331,165332,165334,165335,7061],{},[950,165333,719],{}," — required for any non-trivial query against a ",[941,165336,6836],{},[1022,165338,6825],{"id":6824},[1111,165340,165341,165378],{},[1114,165342,165343,165344,165346,165347,1625,165349,165351,165352,165355,165356,165359,165360,5691,165362,104390,165365,1625,165367,1625,165369,1625,165371,165374,165375,165377],{},"Side-car writes happen ",[950,165345,10761],{}," the main record write succeeds, as a separate store call — they are not part of the same transaction. If a side-car ",[941,165348,3161],{},[941,165350,3164],{}," fails, the main record write still succeeded; the collection emits an ",[941,165353,165354],{},"index:write-partial"," event (",[941,165357,165358],{},"{ vault, collection, id, action, error }",", where ",[941,165361,12123],{},[941,165363,165364],{},"IndexWriteFailureError",[941,165366,61350],{},[941,165368,25197],{},[941,165370,61313],{},[941,165372,165373],{},"cause",") instead of throwing. ",[941,165376,25689],{}," finds and repairs what that event flagged.",[1114,165379,165380,165381,165384,165385,165387],{},"Side-car drift can also occur if the store is modified out-of-band (a restored backup, a manual edit). Use ",[941,165382,165383],{},"reconcileOnOpen: 'auto'"," for unattended servers, ",[941,165386,165243],{}," for human-operated apps that surface a \"rebuild\" button.",[1022,165389,2621],{"id":2620},[1111,165391,165392,165401,165407,165419,165424],{},[1114,165393,165394,1169,165396,1625,165398,165400],{},[1007,165395,3216],{"href":83069},[941,165397,3016],{},[941,165399,3019],{}," pipeline indexing accelerates",[1114,165402,165403,964,165405,165329],{},[1007,165404,4449],{"href":123485},[941,165406,27496],{},[1114,165408,165409,165412,165413,165415,165416,165418],{},[1007,165410,719],{"href":165411},".\u002Flazy"," — bounded-memory ",[941,165414,6836],{}," collections, which ",[941,165417,24807],{}," requires an index for",[1114,165420,165421,165423],{},[1007,165422,772],{"href":773}," — planned query scoping by time range\u002Fregion\u002Ftenant; a different way to avoid scanning the whole collection than a secondary index",[1114,165425,101581,165426,1520],{},[1007,165427,165429],{"href":92624,"rel":165428},[3371],[941,165430,165431],{},"11-with-indexing",[2645,165433,165434],{},"html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}html pre.shiki code .sfNiH, html code.shiki .sfNiH{--shiki-light:#FF5370;--shiki-default:#FF9CAC;--shiki-dark:#FF9CAC}html pre.shiki code .sHdIc, html code.shiki .sHdIc{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#EEFFFF;--shiki-default-font-style:italic;--shiki-dark:#BABED8;--shiki-dark-font-style:italic}",{"title":1037,"searchDepth":1038,"depth":1038,"links":165436},[165437,165438,165439,165440,165441,165442,165449,165450,165451,165452],{"id":29856,"depth":1041,"text":29857},{"id":111659,"depth":1041,"text":111660},{"id":70092,"depth":1041,"text":70093},{"id":99863,"depth":1041,"text":99864},{"id":29892,"depth":1041,"text":29893},{"id":102888,"depth":1041,"text":102889,"children":165443},[165444,165445,165446,165447,165448],{"id":164910,"depth":1038,"text":6766},{"id":164944,"depth":1038,"text":27522},{"id":165079,"depth":1038,"text":165082},{"id":165103,"depth":1038,"text":165106},{"id":165206,"depth":1038,"text":164176},{"id":101424,"depth":1041,"text":101425},{"id":114683,"depth":1041,"text":114684},{"id":6824,"depth":1041,"text":6825},{"id":2620,"depth":1041,"text":2621},"Opt-in secondary indexing: declare `indexes: [...]` with `withIndexing()` and `where`\u002F`orderBy`\u002F`lazyQuery()` resolve through a fast lookup instead of a scan.",{},{"title":705,"description":165453},"CvBXEoHRzHxjd20dO0PQA0BVb5eYRi0o9Dv0ac00q-A",{"id":165458,"title":711,"access":12,"apiToc":929,"body":165459,"category":1042,"description":168254,"extension":1044,"meta":168255,"navigation":1042,"nest":1047,"path":712,"seo":168256,"sinceVersion":1042,"status":1049,"stem":713,"subtitle":1042,"tier":1050,"tocExternal":1042,"__hash__":168257},"docs\u002Fservices\u002Fintrospection.md",{"type":931,"value":165460,"toc":168239},[165461,165463,165466,165468,165477,165480,165508,165519,165528,165530,165544,165677,165679,165683,166184,166824,166846,166850,166911,167034,167045,167049,167058,167094,167314,167325,167327,167333,167346,167351,167414,167418,167610,167615,167747,167752,167876,167894,167911,167919,167927,167935,167940,167946,168036,168038,168159,168161,168236],[934,165462,711],{"id":711},[46871,165464],{":rows":165465},"[{\"label\":\"Subpath\",\"value\":\"`@noy-db\u002Fhub` (root export)\"},{\"label\":\"Type\",\"value\":\"Core feature — `dumpSchema()` and `introspect()` are methods on the always-loaded `Vault` class.\"}]",[1022,165467,6242],{"id":6241},[937,165469,3289,165470,165472,165473,165476],{},[1007,165471,2694],{"href":48}," is a self-contained, encrypted namespace of collections — but once an app has more than a couple of collections, \"what does this vault actually contain?\" stops being something you can answer by reading source. ",[941,165474,165475],{},"vault.dumpSchema()"," answers it programmatically: it returns a structured snapshot — every collection's fields (with type info and where that type info came from), foreign-key references, materialized\u002Foverlay views, derivations, and which optional services are switched on — as one plain JavaScript object.",[937,165478,165479],{},"Two related surfaces live here:",[1111,165481,165482,165493],{},[1114,165483,165484,165488,165489,165492],{},[950,165485,165486],{},[941,165487,165475],{}," — the full snapshot described above. With ",[941,165490,165491],{},"{ withStats: true }"," it additionally walks every collection's stored records to compute per-collection record counts, byte totals, and oldest\u002Fnewest timestamps.",[1114,165494,165495,165500,165501,165504,165505,165507],{},[950,165496,165497],{},[941,165498,165499],{},"vault.introspect()"," — a much cheaper read: collection names + doc counts, configured guards, materialized views, schema-update strategies, and the ",[1092,165502,165503],{},"currently unlocked user's"," own grants. One ",[941,165506,24407],{}," call per collection, no decryption, no field data.",[937,165509,960,165510,165512,165513,165515,165516,165518],{},[941,165511,36137],{}," command (formerly ",[941,165514,36126],{},") builds its human-readable audit output entirely from a ",[941,165517,149885],{}," snapshot; application code can call the same method directly for admin dashboards, migration tooling, or CI schema-drift checks.",[937,165520,165521,165522,165524,165525,165527],{},"Both methods are always available on an opened vault — there is no ",[941,165523,943],{}," strategy to opt into, and the walker behind ",[941,165526,149885],{}," is loaded lazily on first call, so it costs nothing in your bundle until you actually call it.",[1022,165529,58394],{"id":58393},[937,165531,165532,165533,997,165535,165538,165539,165541,165542,2311],{},"Nothing to wire. ",[941,165534,149885],{},[941,165536,165537],{},"introspect()"," are plain methods on every ",[941,165540,94],{}," you get back from ",[941,165543,1749],{},[2217,165545,165547],{"className":2219,"code":165546,"language":2221,"meta":1037,"style":1037},"import { createNoydb } from '@noy-db\u002Fhub'\nimport { toMemory } from '@noy-db\u002Fto-memory'\n\nconst db = await createNoydb({\n  store: toMemory(),\n  user: 'alice',\n  secret: 'orange river candle mountain silver fox',\n})\nconst vault = await db.openVault('acme')\n",[941,165548,165549,165567,165585,165589,165605,165617,165631,165645,165651],{"__ignoreMap":1037},[2225,165550,165551,165553,165555,165557,165559,165561,165563,165565],{"class":2227,"line":2228},[2225,165552,2232],{"class":2231},[2225,165554,2236],{"class":2235},[2225,165556,2744],{"class":2239},[2225,165558,2243],{"class":2235},[2225,165560,2246],{"class":2231},[2225,165562,2249],{"class":2235},[2225,165564,1086],{"class":2252},[2225,165566,2256],{"class":2235},[2225,165568,165569,165571,165573,165575,165577,165579,165581,165583],{"class":2227,"line":1041},[2225,165570,2232],{"class":2231},[2225,165572,2236],{"class":2235},[2225,165574,68576],{"class":2239},[2225,165576,2243],{"class":2235},[2225,165578,2246],{"class":2231},[2225,165580,2249],{"class":2235},[2225,165582,9638],{"class":2252},[2225,165584,2256],{"class":2235},[2225,165586,165587],{"class":2227,"line":1038},[2225,165588,2285],{"emptyLinePlaceholder":2284},[2225,165590,165591,165593,165595,165597,165599,165601,165603],{"class":2227,"line":2288},[2225,165592,2775],{"class":2774},[2225,165594,2778],{"class":2239},[2225,165596,2781],{"class":2235},[2225,165598,2784],{"class":2231},[2225,165600,2744],{"class":2291},[2225,165602,2789],{"class":2239},[2225,165604,4200],{"class":2235},[2225,165606,165607,165609,165611,165613,165615],{"class":2227,"line":2304},[2225,165608,15608],{"class":2307},[2225,165610,2311],{"class":2235},[2225,165612,68576],{"class":2291},[2225,165614,4228],{"class":2239},[2225,165616,1707],{"class":2235},[2225,165618,165619,165621,165623,165625,165627,165629],{"class":2227,"line":2356},[2225,165620,4205],{"class":2307},[2225,165622,2311],{"class":2235},[2225,165624,2249],{"class":2235},[2225,165626,6408],{"class":2252},[2225,165628,2805],{"class":2235},[2225,165630,1707],{"class":2235},[2225,165632,165633,165635,165637,165639,165641,165643],{"class":2227,"line":2830},[2225,165634,10423],{"class":2307},[2225,165636,2311],{"class":2235},[2225,165638,2249],{"class":2235},[2225,165640,103634],{"class":2252},[2225,165642,2805],{"class":2235},[2225,165644,1707],{"class":2235},[2225,165646,165647,165649],{"class":2227,"line":2861},[2225,165648,2359],{"class":2235},[2225,165650,2362],{"class":2239},[2225,165652,165653,165655,165657,165659,165661,165663,165665,165667,165669,165671,165673,165675],{"class":2227,"line":4246},[2225,165654,2775],{"class":2774},[2225,165656,2835],{"class":2239},[2225,165658,2781],{"class":2235},[2225,165660,2784],{"class":2231},[2225,165662,2842],{"class":2239},[2225,165664,1520],{"class":2235},[2225,165666,2847],{"class":2291},[2225,165668,2789],{"class":2239},[2225,165670,2805],{"class":2235},[2225,165672,6450],{"class":2252},[2225,165674,2805],{"class":2235},[2225,165676,2362],{"class":2239},[1022,165678,29893],{"id":29892},[1591,165680,165682],{"id":165681},"full-snapshot","Full snapshot",[2217,165684,165686],{"className":2219,"code":165685,"language":2221,"meta":1037,"style":1037},"import { createNoydb } from '@noy-db\u002Fhub'\nimport { toMemory } from '@noy-db\u002Fto-memory'\nimport { z } from 'zod'\n\nconst db = await createNoydb({\n  store: toMemory(),\n  user: 'alice',\n  secret: 'orange river candle mountain silver fox',\n})\nconst vault = await db.openVault('acme')\n\nvault.collection('clients', {\n  schema: z.object({ id: z.string(), name: z.string() }),\n})\nvault.collection('invoices', {\n  schema: z.object({ id: z.string(), amount: z.number(), status: z.enum(['draft', 'paid']) }),\n})\nawait vault.collection('clients').put('c1', { id: 'c1', name: 'Acme Co' })\nawait vault.collection('invoices').put('i1', { id: 'i1', amount: 100, status: 'draft' })\n\nconst snap = await vault.dumpSchema()\nconsole.log(snap)\n",[941,165687,165688,165706,165724,165742,165746,165762,165774,165788,165802,165808,165834,165838,165858,165906,165912,165932,166010,166016,166079,166149,166153,166173],{"__ignoreMap":1037},[2225,165689,165690,165692,165694,165696,165698,165700,165702,165704],{"class":2227,"line":2228},[2225,165691,2232],{"class":2231},[2225,165693,2236],{"class":2235},[2225,165695,2744],{"class":2239},[2225,165697,2243],{"class":2235},[2225,165699,2246],{"class":2231},[2225,165701,2249],{"class":2235},[2225,165703,1086],{"class":2252},[2225,165705,2256],{"class":2235},[2225,165707,165708,165710,165712,165714,165716,165718,165720,165722],{"class":2227,"line":1041},[2225,165709,2232],{"class":2231},[2225,165711,2236],{"class":2235},[2225,165713,68576],{"class":2239},[2225,165715,2243],{"class":2235},[2225,165717,2246],{"class":2231},[2225,165719,2249],{"class":2235},[2225,165721,9638],{"class":2252},[2225,165723,2256],{"class":2235},[2225,165725,165726,165728,165730,165732,165734,165736,165738,165740],{"class":2227,"line":1038},[2225,165727,2232],{"class":2231},[2225,165729,2236],{"class":2235},[2225,165731,28314],{"class":2239},[2225,165733,2243],{"class":2235},[2225,165735,2246],{"class":2231},[2225,165737,2249],{"class":2235},[2225,165739,28323],{"class":2252},[2225,165741,2256],{"class":2235},[2225,165743,165744],{"class":2227,"line":2288},[2225,165745,2285],{"emptyLinePlaceholder":2284},[2225,165747,165748,165750,165752,165754,165756,165758,165760],{"class":2227,"line":2304},[2225,165749,2775],{"class":2774},[2225,165751,2778],{"class":2239},[2225,165753,2781],{"class":2235},[2225,165755,2784],{"class":2231},[2225,165757,2744],{"class":2291},[2225,165759,2789],{"class":2239},[2225,165761,4200],{"class":2235},[2225,165763,165764,165766,165768,165770,165772],{"class":2227,"line":2356},[2225,165765,15608],{"class":2307},[2225,165767,2311],{"class":2235},[2225,165769,68576],{"class":2291},[2225,165771,4228],{"class":2239},[2225,165773,1707],{"class":2235},[2225,165775,165776,165778,165780,165782,165784,165786],{"class":2227,"line":2830},[2225,165777,4205],{"class":2307},[2225,165779,2311],{"class":2235},[2225,165781,2249],{"class":2235},[2225,165783,6408],{"class":2252},[2225,165785,2805],{"class":2235},[2225,165787,1707],{"class":2235},[2225,165789,165790,165792,165794,165796,165798,165800],{"class":2227,"line":2861},[2225,165791,10423],{"class":2307},[2225,165793,2311],{"class":2235},[2225,165795,2249],{"class":2235},[2225,165797,103634],{"class":2252},[2225,165799,2805],{"class":2235},[2225,165801,1707],{"class":2235},[2225,165803,165804,165806],{"class":2227,"line":4246},[2225,165805,2359],{"class":2235},[2225,165807,2362],{"class":2239},[2225,165809,165810,165812,165814,165816,165818,165820,165822,165824,165826,165828,165830,165832],{"class":2227,"line":4253},[2225,165811,2775],{"class":2774},[2225,165813,2835],{"class":2239},[2225,165815,2781],{"class":2235},[2225,165817,2784],{"class":2231},[2225,165819,2842],{"class":2239},[2225,165821,1520],{"class":2235},[2225,165823,2847],{"class":2291},[2225,165825,2789],{"class":2239},[2225,165827,2805],{"class":2235},[2225,165829,6450],{"class":2252},[2225,165831,2805],{"class":2235},[2225,165833,2362],{"class":2239},[2225,165835,165836],{"class":2227,"line":4259},[2225,165837,2285],{"emptyLinePlaceholder":2284},[2225,165839,165840,165842,165844,165846,165848,165850,165852,165854,165856],{"class":2227,"line":6548},[2225,165841,2694],{"class":2239},[2225,165843,1520],{"class":2235},[2225,165845,6471],{"class":2291},[2225,165847,2789],{"class":2239},[2225,165849,2805],{"class":2235},[2225,165851,23926],{"class":2252},[2225,165853,2805],{"class":2235},[2225,165855,2298],{"class":2235},[2225,165857,2301],{"class":2235},[2225,165859,165860,165862,165864,165866,165868,165870,165872,165874,165876,165878,165880,165882,165884,165886,165888,165890,165892,165894,165896,165898,165900,165902,165904],{"class":2227,"line":6576},[2225,165861,28618],{"class":2307},[2225,165863,2311],{"class":2235},[2225,165865,28314],{"class":2239},[2225,165867,1520],{"class":2235},[2225,165869,25505],{"class":2291},[2225,165871,2789],{"class":2239},[2225,165873,2792],{"class":2235},[2225,165875,7070],{"class":2307},[2225,165877,2311],{"class":2235},[2225,165879,28314],{"class":2239},[2225,165881,1520],{"class":2235},[2225,165883,7259],{"class":2291},[2225,165885,4228],{"class":2239},[2225,165887,2298],{"class":2235},[2225,165889,8104],{"class":2307},[2225,165891,2311],{"class":2235},[2225,165893,28314],{"class":2239},[2225,165895,1520],{"class":2235},[2225,165897,7259],{"class":2291},[2225,165899,19132],{"class":2239},[2225,165901,2359],{"class":2235},[2225,165903,1381],{"class":2239},[2225,165905,1707],{"class":2235},[2225,165907,165908,165910],{"class":2227,"line":6582},[2225,165909,2359],{"class":2235},[2225,165911,2362],{"class":2239},[2225,165913,165914,165916,165918,165920,165922,165924,165926,165928,165930],{"class":2227,"line":6587},[2225,165915,2694],{"class":2239},[2225,165917,1520],{"class":2235},[2225,165919,6471],{"class":2291},[2225,165921,2789],{"class":2239},[2225,165923,2805],{"class":2235},[2225,165925,6487],{"class":2252},[2225,165927,2805],{"class":2235},[2225,165929,2298],{"class":2235},[2225,165931,2301],{"class":2235},[2225,165933,165934,165936,165938,165940,165942,165944,165946,165948,165950,165952,165954,165956,165958,165960,165962,165964,165966,165968,165970,165972,165974,165976,165978,165980,165982,165984,165986,165988,165990,165992,165994,165996,165998,166000,166002,166004,166006,166008],{"class":2227,"line":7174},[2225,165935,28618],{"class":2307},[2225,165937,2311],{"class":2235},[2225,165939,28314],{"class":2239},[2225,165941,1520],{"class":2235},[2225,165943,25505],{"class":2291},[2225,165945,2789],{"class":2239},[2225,165947,2792],{"class":2235},[2225,165949,7070],{"class":2307},[2225,165951,2311],{"class":2235},[2225,165953,28314],{"class":2239},[2225,165955,1520],{"class":2235},[2225,165957,7259],{"class":2291},[2225,165959,4228],{"class":2239},[2225,165961,2298],{"class":2235},[2225,165963,6351],{"class":2307},[2225,165965,2311],{"class":2235},[2225,165967,28314],{"class":2239},[2225,165969,1520],{"class":2235},[2225,165971,20636],{"class":2291},[2225,165973,4228],{"class":2239},[2225,165975,2298],{"class":2235},[2225,165977,6361],{"class":2307},[2225,165979,2311],{"class":2235},[2225,165981,28314],{"class":2239},[2225,165983,1520],{"class":2235},[2225,165985,28421],{"class":2291},[2225,165987,28424],{"class":2239},[2225,165989,2805],{"class":2235},[2225,165991,28429],{"class":2252},[2225,165993,2805],{"class":2235},[2225,165995,2298],{"class":2235},[2225,165997,2249],{"class":2235},[2225,165999,6539],{"class":2252},[2225,166001,2805],{"class":2235},[2225,166003,75901],{"class":2239},[2225,166005,2359],{"class":2235},[2225,166007,1381],{"class":2239},[2225,166009,1707],{"class":2235},[2225,166011,166012,166014],{"class":2227,"line":7180},[2225,166013,2359],{"class":2235},[2225,166015,2362],{"class":2239},[2225,166017,166018,166020,166022,166024,166026,166028,166030,166032,166034,166036,166038,166040,166042,166044,166046,166048,166050,166052,166054,166056,166058,166060,166062,166064,166066,166068,166070,166073,166075,166077],{"class":2227,"line":7220},[2225,166019,6500],{"class":2231},[2225,166021,6466],{"class":2239},[2225,166023,1520],{"class":2235},[2225,166025,6471],{"class":2291},[2225,166027,2789],{"class":2239},[2225,166029,2805],{"class":2235},[2225,166031,23926],{"class":2252},[2225,166033,2805],{"class":2235},[2225,166035,1381],{"class":2239},[2225,166037,1520],{"class":2235},[2225,166039,3161],{"class":2291},[2225,166041,2789],{"class":2239},[2225,166043,2805],{"class":2235},[2225,166045,62700],{"class":2252},[2225,166047,2805],{"class":2235},[2225,166049,2298],{"class":2235},[2225,166051,2236],{"class":2235},[2225,166053,7070],{"class":2307},[2225,166055,2311],{"class":2235},[2225,166057,2249],{"class":2235},[2225,166059,62700],{"class":2252},[2225,166061,2805],{"class":2235},[2225,166063,2298],{"class":2235},[2225,166065,8104],{"class":2307},[2225,166067,2311],{"class":2235},[2225,166069,2249],{"class":2235},[2225,166071,166072],{"class":2252},"Acme Co",[2225,166074,2805],{"class":2235},[2225,166076,2243],{"class":2235},[2225,166078,2362],{"class":2239},[2225,166080,166081,166083,166085,166087,166089,166091,166093,166095,166097,166099,166101,166103,166105,166107,166109,166111,166113,166115,166117,166119,166121,166123,166125,166127,166129,166131,166133,166135,166137,166139,166141,166143,166145,166147],{"class":2227,"line":7225},[2225,166082,6500],{"class":2231},[2225,166084,6466],{"class":2239},[2225,166086,1520],{"class":2235},[2225,166088,6471],{"class":2291},[2225,166090,2789],{"class":2239},[2225,166092,2805],{"class":2235},[2225,166094,6487],{"class":2252},[2225,166096,2805],{"class":2235},[2225,166098,1381],{"class":2239},[2225,166100,1520],{"class":2235},[2225,166102,3161],{"class":2291},[2225,166104,2789],{"class":2239},[2225,166106,2805],{"class":2235},[2225,166108,62891],{"class":2252},[2225,166110,2805],{"class":2235},[2225,166112,2298],{"class":2235},[2225,166114,2236],{"class":2235},[2225,166116,7070],{"class":2307},[2225,166118,2311],{"class":2235},[2225,166120,2249],{"class":2235},[2225,166122,62891],{"class":2252},[2225,166124,2805],{"class":2235},[2225,166126,2298],{"class":2235},[2225,166128,6351],{"class":2307},[2225,166130,2311],{"class":2235},[2225,166132,6528],{"class":6527},[2225,166134,2298],{"class":2235},[2225,166136,6361],{"class":2307},[2225,166138,2311],{"class":2235},[2225,166140,2249],{"class":2235},[2225,166142,28429],{"class":2252},[2225,166144,2805],{"class":2235},[2225,166146,2243],{"class":2235},[2225,166148,2362],{"class":2239},[2225,166150,166151],{"class":2227,"line":7231},[2225,166152,2285],{"emptyLinePlaceholder":2284},[2225,166154,166155,166157,166160,166162,166164,166166,166168,166171],{"class":2227,"line":7267},[2225,166156,2775],{"class":2774},[2225,166158,166159],{"class":2239}," snap ",[2225,166161,2781],{"class":2235},[2225,166163,2784],{"class":2231},[2225,166165,6466],{"class":2239},[2225,166167,1520],{"class":2235},[2225,166169,166170],{"class":2291},"dumpSchema",[2225,166172,8146],{"class":2239},[2225,166174,166175,166177,166179,166181],{"class":2227,"line":7272},[2225,166176,28031],{"class":2239},[2225,166178,1520],{"class":2235},[2225,166180,27100],{"class":2291},[2225,166182,166183],{"class":2239},"(snap)\n",[2217,166185,166187],{"className":6604,"code":166186,"language":141,"meta":1037,"style":1037},"{\n  \"_noydb_snapshot\": 1,\n  \"vault\": \"acme\",\n  \"emittedAt\": \"2026-07-08T12:00:00.000Z\",\n  \"subsystems\": { \"guards\": false, \"derivations\": false, \"materializedViews\": false, \"overlayViews\": false },\n  \"collections\": {\n    \"clients\": {\n      \"fields\": {\n        \"id\": { \"type\": \"string\", \"source\": \"live-validator\" },\n        \"name\": { \"type\": \"string\", \"source\": \"live-validator\" }\n      },\n      \"indexes\": [],\n      \"refs\": {},\n      \"validator\": { \"kind\": \"Zod\", \"source\": \"live-validator\" }\n    },\n    \"invoices\": {\n      \"fields\": {\n        \"id\": { \"type\": \"string\", \"source\": \"live-validator\" },\n        \"amount\": { \"type\": \"number\", \"source\": \"live-validator\" },\n        \"status\": { \"type\": \"enum\", \"source\": \"live-validator\", \"constraints\": { \"values\": [\"draft\", \"paid\"] } }\n      },\n      \"indexes\": [],\n      \"refs\": {},\n      \"validator\": { \"kind\": \"Zod\", \"source\": \"live-validator\" }\n    }\n  },\n  \"materializedViews\": {},\n  \"overlayViews\": {},\n  \"derivations\": {}\n}\n",[941,166188,166189,166193,166208,166226,166246,166303,166315,166327,166339,166385,166429,166433,166445,166458,166504,166508,166520,166532,166576,166620,166704,166708,166720,166732,166776,166780,166784,166796,166808,166820],{"__ignoreMap":1037},[2225,166190,166191],{"class":2227,"line":2228},[2225,166192,4200],{"class":2235},[2225,166194,166195,166197,166200,166202,166204,166206],{"class":2227,"line":1041},[2225,166196,9368],{"class":2235},[2225,166198,166199],{"class":2774},"_noydb_snapshot",[2225,166201,6620],{"class":2235},[2225,166203,2311],{"class":2235},[2225,166205,9377],{"class":6527},[2225,166207,1707],{"class":2235},[2225,166209,166210,166212,166214,166216,166218,166220,166222,166224],{"class":2227,"line":1038},[2225,166211,9368],{"class":2235},[2225,166213,2694],{"class":2774},[2225,166215,6620],{"class":2235},[2225,166217,2311],{"class":2235},[2225,166219,6614],{"class":2235},[2225,166221,6450],{"class":2252},[2225,166223,6620],{"class":2235},[2225,166225,1707],{"class":2235},[2225,166227,166228,166230,166233,166235,166237,166239,166242,166244],{"class":2227,"line":2288},[2225,166229,9368],{"class":2235},[2225,166231,166232],{"class":2774},"emittedAt",[2225,166234,6620],{"class":2235},[2225,166236,2311],{"class":2235},[2225,166238,6614],{"class":2235},[2225,166240,166241],{"class":2252},"2026-07-08T12:00:00.000Z",[2225,166243,6620],{"class":2235},[2225,166245,1707],{"class":2235},[2225,166247,166248,166250,166253,166255,166257,166259,166261,166263,166265,166267,166269,166271,166273,166275,166277,166279,166281,166284,166286,166288,166290,166292,166295,166297,166299,166301],{"class":2227,"line":2304},[2225,166249,9368],{"class":2235},[2225,166251,166252],{"class":2774},"subsystems",[2225,166254,6620],{"class":2235},[2225,166256,2311],{"class":2235},[2225,166258,2236],{"class":2235},[2225,166260,6614],{"class":2235},[2225,166262,693],{"class":6342},[2225,166264,6620],{"class":2235},[2225,166266,2311],{"class":2235},[2225,166268,147907],{"class":2235},[2225,166270,6614],{"class":2235},[2225,166272,665],{"class":6342},[2225,166274,6620],{"class":2235},[2225,166276,2311],{"class":2235},[2225,166278,147907],{"class":2235},[2225,166280,6614],{"class":2235},[2225,166282,166283],{"class":6342},"materializedViews",[2225,166285,6620],{"class":2235},[2225,166287,2311],{"class":2235},[2225,166289,147907],{"class":2235},[2225,166291,6614],{"class":2235},[2225,166293,166294],{"class":6342},"overlayViews",[2225,166296,6620],{"class":2235},[2225,166298,2311],{"class":2235},[2225,166300,2341],{"class":2235},[2225,166302,2353],{"class":2235},[2225,166304,166305,166307,166309,166311,166313],{"class":2227,"line":2356},[2225,166306,9368],{"class":2235},[2225,166308,2698],{"class":2774},[2225,166310,6620],{"class":2235},[2225,166312,2311],{"class":2235},[2225,166314,2301],{"class":2235},[2225,166316,166317,166319,166321,166323,166325],{"class":2227,"line":2830},[2225,166318,14201],{"class":2235},[2225,166320,23926],{"class":6342},[2225,166322,6620],{"class":2235},[2225,166324,2311],{"class":2235},[2225,166326,2301],{"class":2235},[2225,166328,166329,166331,166333,166335,166337],{"class":2227,"line":2861},[2225,166330,59948],{"class":2235},[2225,166332,57285],{"class":6527},[2225,166334,6620],{"class":2235},[2225,166336,2311],{"class":2235},[2225,166338,2301],{"class":2235},[2225,166340,166341,166343,166345,166347,166349,166351,166353,166356,166358,166360,166362,166364,166366,166368,166370,166372,166374,166376,166378,166381,166383],{"class":2227,"line":4246},[2225,166342,60015],{"class":2235},[2225,166344,6983],{"class":2307},[2225,166346,6620],{"class":2235},[2225,166348,2311],{"class":2235},[2225,166350,2236],{"class":2235},[2225,166352,6614],{"class":2235},[2225,166354,6339],{"class":166355},"su27w",[2225,166357,6620],{"class":2235},[2225,166359,2311],{"class":2235},[2225,166361,6614],{"class":2235},[2225,166363,7259],{"class":2252},[2225,166365,6620],{"class":2235},[2225,166367,2298],{"class":2235},[2225,166369,6614],{"class":2235},[2225,166371,22723],{"class":166355},[2225,166373,6620],{"class":2235},[2225,166375,2311],{"class":2235},[2225,166377,6614],{"class":2235},[2225,166379,166380],{"class":2252},"live-validator",[2225,166382,6620],{"class":2235},[2225,166384,2353],{"class":2235},[2225,166386,166387,166389,166391,166393,166395,166397,166399,166401,166403,166405,166407,166409,166411,166413,166415,166417,166419,166421,166423,166425,166427],{"class":2227,"line":4253},[2225,166388,60015],{"class":2235},[2225,166390,4914],{"class":2307},[2225,166392,6620],{"class":2235},[2225,166394,2311],{"class":2235},[2225,166396,2236],{"class":2235},[2225,166398,6614],{"class":2235},[2225,166400,6339],{"class":166355},[2225,166402,6620],{"class":2235},[2225,166404,2311],{"class":2235},[2225,166406,6614],{"class":2235},[2225,166408,7259],{"class":2252},[2225,166410,6620],{"class":2235},[2225,166412,2298],{"class":2235},[2225,166414,6614],{"class":2235},[2225,166416,22723],{"class":166355},[2225,166418,6620],{"class":2235},[2225,166420,2311],{"class":2235},[2225,166422,6614],{"class":2235},[2225,166424,166380],{"class":2252},[2225,166426,6620],{"class":2235},[2225,166428,6369],{"class":2235},[2225,166430,166431],{"class":2227,"line":4259},[2225,166432,60068],{"class":2235},[2225,166434,166435,166437,166439,166441,166443],{"class":2227,"line":6548},[2225,166436,59948],{"class":2235},[2225,166438,21907],{"class":6527},[2225,166440,6620],{"class":2235},[2225,166442,2311],{"class":2235},[2225,166444,152333],{"class":2235},[2225,166446,166447,166449,166451,166453,166455],{"class":2227,"line":6576},[2225,166448,59948],{"class":2235},[2225,166450,22753],{"class":6527},[2225,166452,6620],{"class":2235},[2225,166454,2311],{"class":2235},[2225,166456,166457],{"class":2235}," {},\n",[2225,166459,166460,166462,166465,166467,166469,166471,166473,166475,166477,166479,166481,166484,166486,166488,166490,166492,166494,166496,166498,166500,166502],{"class":2227,"line":6582},[2225,166461,59948],{"class":2235},[2225,166463,166464],{"class":6527},"validator",[2225,166466,6620],{"class":2235},[2225,166468,2311],{"class":2235},[2225,166470,2236],{"class":2235},[2225,166472,6614],{"class":2235},[2225,166474,27895],{"class":2307},[2225,166476,6620],{"class":2235},[2225,166478,2311],{"class":2235},[2225,166480,6614],{"class":2235},[2225,166482,166483],{"class":2252},"Zod",[2225,166485,6620],{"class":2235},[2225,166487,2298],{"class":2235},[2225,166489,6614],{"class":2235},[2225,166491,22723],{"class":2307},[2225,166493,6620],{"class":2235},[2225,166495,2311],{"class":2235},[2225,166497,6614],{"class":2235},[2225,166499,166380],{"class":2252},[2225,166501,6620],{"class":2235},[2225,166503,6369],{"class":2235},[2225,166505,166506],{"class":2227,"line":6587},[2225,166507,8324],{"class":2235},[2225,166509,166510,166512,166514,166516,166518],{"class":2227,"line":7174},[2225,166511,14201],{"class":2235},[2225,166513,6487],{"class":6342},[2225,166515,6620],{"class":2235},[2225,166517,2311],{"class":2235},[2225,166519,2301],{"class":2235},[2225,166521,166522,166524,166526,166528,166530],{"class":2227,"line":7180},[2225,166523,59948],{"class":2235},[2225,166525,57285],{"class":6527},[2225,166527,6620],{"class":2235},[2225,166529,2311],{"class":2235},[2225,166531,2301],{"class":2235},[2225,166533,166534,166536,166538,166540,166542,166544,166546,166548,166550,166552,166554,166556,166558,166560,166562,166564,166566,166568,166570,166572,166574],{"class":2227,"line":7220},[2225,166535,60015],{"class":2235},[2225,166537,6983],{"class":2307},[2225,166539,6620],{"class":2235},[2225,166541,2311],{"class":2235},[2225,166543,2236],{"class":2235},[2225,166545,6614],{"class":2235},[2225,166547,6339],{"class":166355},[2225,166549,6620],{"class":2235},[2225,166551,2311],{"class":2235},[2225,166553,6614],{"class":2235},[2225,166555,7259],{"class":2252},[2225,166557,6620],{"class":2235},[2225,166559,2298],{"class":2235},[2225,166561,6614],{"class":2235},[2225,166563,22723],{"class":166355},[2225,166565,6620],{"class":2235},[2225,166567,2311],{"class":2235},[2225,166569,6614],{"class":2235},[2225,166571,166380],{"class":2252},[2225,166573,6620],{"class":2235},[2225,166575,2353],{"class":2235},[2225,166577,166578,166580,166582,166584,166586,166588,166590,166592,166594,166596,166598,166600,166602,166604,166606,166608,166610,166612,166614,166616,166618],{"class":2227,"line":7225},[2225,166579,60015],{"class":2235},[2225,166581,6617],{"class":2307},[2225,166583,6620],{"class":2235},[2225,166585,2311],{"class":2235},[2225,166587,2236],{"class":2235},[2225,166589,6614],{"class":2235},[2225,166591,6339],{"class":166355},[2225,166593,6620],{"class":2235},[2225,166595,2311],{"class":2235},[2225,166597,6614],{"class":2235},[2225,166599,20636],{"class":2252},[2225,166601,6620],{"class":2235},[2225,166603,2298],{"class":2235},[2225,166605,6614],{"class":2235},[2225,166607,22723],{"class":166355},[2225,166609,6620],{"class":2235},[2225,166611,2311],{"class":2235},[2225,166613,6614],{"class":2235},[2225,166615,166380],{"class":2252},[2225,166617,6620],{"class":2235},[2225,166619,2353],{"class":2235},[2225,166621,166622,166624,166626,166628,166630,166632,166634,166636,166638,166640,166642,166644,166646,166648,166650,166652,166654,166656,166658,166660,166662,166664,166666,166668,166670,166672,166674,166676,166678,166680,166682,166684,166686,166688,166690,166692,166694,166696,166698,166700,166702],{"class":2227,"line":7231},[2225,166623,60015],{"class":2235},[2225,166625,6631],{"class":2307},[2225,166627,6620],{"class":2235},[2225,166629,2311],{"class":2235},[2225,166631,2236],{"class":2235},[2225,166633,6614],{"class":2235},[2225,166635,6339],{"class":166355},[2225,166637,6620],{"class":2235},[2225,166639,2311],{"class":2235},[2225,166641,6614],{"class":2235},[2225,166643,28421],{"class":2252},[2225,166645,6620],{"class":2235},[2225,166647,2298],{"class":2235},[2225,166649,6614],{"class":2235},[2225,166651,22723],{"class":166355},[2225,166653,6620],{"class":2235},[2225,166655,2311],{"class":2235},[2225,166657,6614],{"class":2235},[2225,166659,166380],{"class":2252},[2225,166661,6620],{"class":2235},[2225,166663,2298],{"class":2235},[2225,166665,6614],{"class":2235},[2225,166667,146872],{"class":166355},[2225,166669,6620],{"class":2235},[2225,166671,2311],{"class":2235},[2225,166673,2236],{"class":2235},[2225,166675,6614],{"class":2235},[2225,166677,148357],{"class":2291},[2225,166679,6620],{"class":2235},[2225,166681,2311],{"class":2235},[2225,166683,8667],{"class":2235},[2225,166685,6620],{"class":2235},[2225,166687,28429],{"class":2252},[2225,166689,6620],{"class":2235},[2225,166691,2298],{"class":2235},[2225,166693,6614],{"class":2235},[2225,166695,6539],{"class":2252},[2225,166697,6620],{"class":2235},[2225,166699,8803],{"class":2235},[2225,166701,2243],{"class":2235},[2225,166703,6369],{"class":2235},[2225,166705,166706],{"class":2227,"line":7267},[2225,166707,60068],{"class":2235},[2225,166709,166710,166712,166714,166716,166718],{"class":2227,"line":7272},[2225,166711,59948],{"class":2235},[2225,166713,21907],{"class":6527},[2225,166715,6620],{"class":2235},[2225,166717,2311],{"class":2235},[2225,166719,152333],{"class":2235},[2225,166721,166722,166724,166726,166728,166730],{"class":2227,"line":7278},[2225,166723,59948],{"class":2235},[2225,166725,22753],{"class":6527},[2225,166727,6620],{"class":2235},[2225,166729,2311],{"class":2235},[2225,166731,166457],{"class":2235},[2225,166733,166734,166736,166738,166740,166742,166744,166746,166748,166750,166752,166754,166756,166758,166760,166762,166764,166766,166768,166770,166772,166774],{"class":2227,"line":7303},[2225,166735,59948],{"class":2235},[2225,166737,166464],{"class":6527},[2225,166739,6620],{"class":2235},[2225,166741,2311],{"class":2235},[2225,166743,2236],{"class":2235},[2225,166745,6614],{"class":2235},[2225,166747,27895],{"class":2307},[2225,166749,6620],{"class":2235},[2225,166751,2311],{"class":2235},[2225,166753,6614],{"class":2235},[2225,166755,166483],{"class":2252},[2225,166757,6620],{"class":2235},[2225,166759,2298],{"class":2235},[2225,166761,6614],{"class":2235},[2225,166763,22723],{"class":2307},[2225,166765,6620],{"class":2235},[2225,166767,2311],{"class":2235},[2225,166769,6614],{"class":2235},[2225,166771,166380],{"class":2252},[2225,166773,6620],{"class":2235},[2225,166775,6369],{"class":2235},[2225,166777,166778],{"class":2227,"line":7308},[2225,166779,60103],{"class":2235},[2225,166781,166782],{"class":2227,"line":7314},[2225,166783,19017],{"class":2235},[2225,166785,166786,166788,166790,166792,166794],{"class":2227,"line":7348},[2225,166787,9368],{"class":2235},[2225,166789,166283],{"class":2774},[2225,166791,6620],{"class":2235},[2225,166793,2311],{"class":2235},[2225,166795,166457],{"class":2235},[2225,166797,166798,166800,166802,166804,166806],{"class":2227,"line":8613},[2225,166799,9368],{"class":2235},[2225,166801,166294],{"class":2774},[2225,166803,6620],{"class":2235},[2225,166805,2311],{"class":2235},[2225,166807,166457],{"class":2235},[2225,166809,166810,166812,166814,166816,166818],{"class":2227,"line":8633},[2225,166811,9368],{"class":2235},[2225,166813,665],{"class":2774},[2225,166815,6620],{"class":2235},[2225,166817,2311],{"class":2235},[2225,166819,8782],{"class":2235},[2225,166821,166822],{"class":2227,"line":8662},[2225,166823,7351],{"class":2235},[937,166825,166826,997,166828,166830,166831,166834,166835,166838,166839,166842,166843,1520],{},[941,166827,23926],{},[941,166829,6487],{}," were declared with a live Zod schema but no ",[1007,166832,166833],{"href":781},"persisted schema",", so every field reports ",[941,166836,166837],{},"source: \"live-validator\""," — derived in-process from the attached validator. Fields validated by a persisted ",[941,166840,166841],{},"_schemas\u002F\u003Ccollection>"," envelope instead report ",[941,166844,166845],{},"source: \"persisted\"",[1591,166847,166849],{"id":166848},"with-recordbyte-stats","With record\u002Fbyte stats",[2217,166851,166853],{"className":2219,"code":166852,"language":2221,"meta":1037,"style":1037},"const withStats = await vault.dumpSchema({ withStats: true })\nconsole.log(withStats.collections.invoices?.stats)\n",[941,166854,166855,166887],{"__ignoreMap":1037},[2225,166856,166857,166859,166862,166864,166866,166868,166870,166872,166874,166876,166879,166881,166883,166885],{"class":2227,"line":2228},[2225,166858,2775],{"class":2774},[2225,166860,166861],{"class":2239}," withStats ",[2225,166863,2781],{"class":2235},[2225,166865,2784],{"class":2231},[2225,166867,6466],{"class":2239},[2225,166869,1520],{"class":2235},[2225,166871,166170],{"class":2291},[2225,166873,2789],{"class":2239},[2225,166875,2792],{"class":2235},[2225,166877,166878],{"class":2307}," withStats",[2225,166880,2311],{"class":2235},[2225,166882,2322],{"class":2321},[2225,166884,2243],{"class":2235},[2225,166886,2362],{"class":2239},[2225,166888,166889,166891,166893,166895,166898,166900,166902,166904,166906,166908],{"class":2227,"line":1041},[2225,166890,28031],{"class":2239},[2225,166892,1520],{"class":2235},[2225,166894,27100],{"class":2291},[2225,166896,166897],{"class":2239},"(withStats",[2225,166899,1520],{"class":2235},[2225,166901,2698],{"class":2239},[2225,166903,1520],{"class":2235},[2225,166905,6487],{"class":2239},[2225,166907,8457],{"class":2235},[2225,166909,166910],{"class":2239},"stats)\n",[2217,166912,166914],{"className":6604,"code":166913,"language":141,"meta":1037,"style":1037},"{\n  \"records\": 1,\n  \"bytes\": 87,\n  \"bytesAvg\": 87,\n  \"bytesMin\": 87,\n  \"bytesMax\": 87,\n  \"oldest\": \"2026-07-08T12:00:00.000Z\",\n  \"newest\": \"2026-07-08T12:00:00.000Z\"\n}\n",[941,166915,166916,166920,166934,166949,166964,166979,166994,167013,167030],{"__ignoreMap":1037},[2225,166917,166918],{"class":2227,"line":2228},[2225,166919,4200],{"class":2235},[2225,166921,166922,166924,166926,166928,166930,166932],{"class":2227,"line":1041},[2225,166923,9368],{"class":2235},[2225,166925,9136],{"class":2774},[2225,166927,6620],{"class":2235},[2225,166929,2311],{"class":2235},[2225,166931,9377],{"class":6527},[2225,166933,1707],{"class":2235},[2225,166935,166936,166938,166940,166942,166944,166947],{"class":2227,"line":1038},[2225,166937,9368],{"class":2235},[2225,166939,64492],{"class":2774},[2225,166941,6620],{"class":2235},[2225,166943,2311],{"class":2235},[2225,166945,166946],{"class":6527}," 87",[2225,166948,1707],{"class":2235},[2225,166950,166951,166953,166956,166958,166960,166962],{"class":2227,"line":2288},[2225,166952,9368],{"class":2235},[2225,166954,166955],{"class":2774},"bytesAvg",[2225,166957,6620],{"class":2235},[2225,166959,2311],{"class":2235},[2225,166961,166946],{"class":6527},[2225,166963,1707],{"class":2235},[2225,166965,166966,166968,166971,166973,166975,166977],{"class":2227,"line":2304},[2225,166967,9368],{"class":2235},[2225,166969,166970],{"class":2774},"bytesMin",[2225,166972,6620],{"class":2235},[2225,166974,2311],{"class":2235},[2225,166976,166946],{"class":6527},[2225,166978,1707],{"class":2235},[2225,166980,166981,166983,166986,166988,166990,166992],{"class":2227,"line":2356},[2225,166982,9368],{"class":2235},[2225,166984,166985],{"class":2774},"bytesMax",[2225,166987,6620],{"class":2235},[2225,166989,2311],{"class":2235},[2225,166991,166946],{"class":6527},[2225,166993,1707],{"class":2235},[2225,166995,166996,166998,167001,167003,167005,167007,167009,167011],{"class":2227,"line":2830},[2225,166997,9368],{"class":2235},[2225,166999,167000],{"class":2774},"oldest",[2225,167002,6620],{"class":2235},[2225,167004,2311],{"class":2235},[2225,167006,6614],{"class":2235},[2225,167008,166241],{"class":2252},[2225,167010,6620],{"class":2235},[2225,167012,1707],{"class":2235},[2225,167014,167015,167017,167020,167022,167024,167026,167028],{"class":2227,"line":2861},[2225,167016,9368],{"class":2235},[2225,167018,167019],{"class":2774},"newest",[2225,167021,6620],{"class":2235},[2225,167023,2311],{"class":2235},[2225,167025,6614],{"class":2235},[2225,167027,166241],{"class":2252},[2225,167029,9449],{"class":2235},[2225,167031,167032],{"class":2227,"line":4246},[2225,167033,7351],{"class":2235},[937,167035,167036,167037,1625,167039,167041,167042,167044],{},"Byte counts are illustrative — they measure the encrypted envelope, so exact numbers depend on your data. ",[941,167038,167000],{},[941,167040,167019],{}," come from each envelope's ",[941,167043,1703],{},"; both are the empty string for an empty collection.",[1591,167046,167048],{"id":167047},"lightweight-read","Lightweight read",[937,167050,167051,167052,167054,167055,167057],{},"For a cheap \"what's in this vault, and what can I see\" check, skip ",[941,167053,149885],{}," and call ",[941,167056,165537],{}," — no decryption, no field data:",[2217,167059,167061],{"className":2219,"code":167060,"language":2221,"meta":1037,"style":1037},"const overview = await vault.introspect()\nconsole.log(overview)\n",[941,167062,167063,167083],{"__ignoreMap":1037},[2225,167064,167065,167067,167070,167072,167074,167076,167078,167081],{"class":2227,"line":2228},[2225,167066,2775],{"class":2774},[2225,167068,167069],{"class":2239}," overview ",[2225,167071,2781],{"class":2235},[2225,167073,2784],{"class":2231},[2225,167075,6466],{"class":2239},[2225,167077,1520],{"class":2235},[2225,167079,167080],{"class":2291},"introspect",[2225,167082,8146],{"class":2239},[2225,167084,167085,167087,167089,167091],{"class":2227,"line":1041},[2225,167086,28031],{"class":2239},[2225,167088,1520],{"class":2235},[2225,167090,27100],{"class":2291},[2225,167092,167093],{"class":2239},"(overview)\n",[2217,167095,167097],{"className":6604,"code":167096,"language":141,"meta":1037,"style":1037},"{\n  \"collections\": [\n    { \"name\": \"clients\", \"docCount\": 1 },\n    { \"name\": \"invoices\", \"docCount\": 1 }\n  ],\n  \"guards\": [],\n  \"materializedViews\": [],\n  \"schemaUpdate\": [],\n  \"grants\": [\n    { \"collection\": \"clients\", \"permission\": \"rw\" },\n    { \"collection\": \"invoices\", \"permission\": \"rw\" }\n  ]\n}\n",[941,167098,167099,167103,167115,167148,167180,167184,167196,167208,167220,167233,167270,167306,167310],{"__ignoreMap":1037},[2225,167100,167101],{"class":2227,"line":2228},[2225,167102,4200],{"class":2235},[2225,167104,167105,167107,167109,167111,167113],{"class":2227,"line":1041},[2225,167106,9368],{"class":2235},[2225,167108,2698],{"class":2774},[2225,167110,6620],{"class":2235},[2225,167112,2311],{"class":2235},[2225,167114,32491],{"class":2235},[2225,167116,167117,167119,167121,167123,167125,167127,167129,167131,167133,167135,167137,167140,167142,167144,167146],{"class":2227,"line":1038},[2225,167118,32496],{"class":2235},[2225,167120,6614],{"class":2235},[2225,167122,4914],{"class":6342},[2225,167124,6620],{"class":2235},[2225,167126,2311],{"class":2235},[2225,167128,6614],{"class":2235},[2225,167130,23926],{"class":2252},[2225,167132,6620],{"class":2235},[2225,167134,2298],{"class":2235},[2225,167136,6614],{"class":2235},[2225,167138,167139],{"class":6342},"docCount",[2225,167141,6620],{"class":2235},[2225,167143,2311],{"class":2235},[2225,167145,9377],{"class":6527},[2225,167147,2353],{"class":2235},[2225,167149,167150,167152,167154,167156,167158,167160,167162,167164,167166,167168,167170,167172,167174,167176,167178],{"class":2227,"line":2288},[2225,167151,32496],{"class":2235},[2225,167153,6614],{"class":2235},[2225,167155,4914],{"class":6342},[2225,167157,6620],{"class":2235},[2225,167159,2311],{"class":2235},[2225,167161,6614],{"class":2235},[2225,167163,6487],{"class":2252},[2225,167165,6620],{"class":2235},[2225,167167,2298],{"class":2235},[2225,167169,6614],{"class":2235},[2225,167171,167139],{"class":6342},[2225,167173,6620],{"class":2235},[2225,167175,2311],{"class":2235},[2225,167177,9377],{"class":6527},[2225,167179,6369],{"class":2235},[2225,167181,167182],{"class":2227,"line":2304},[2225,167183,67686],{"class":2235},[2225,167185,167186,167188,167190,167192,167194],{"class":2227,"line":2356},[2225,167187,9368],{"class":2235},[2225,167189,693],{"class":2774},[2225,167191,6620],{"class":2235},[2225,167193,2311],{"class":2235},[2225,167195,152333],{"class":2235},[2225,167197,167198,167200,167202,167204,167206],{"class":2227,"line":2830},[2225,167199,9368],{"class":2235},[2225,167201,166283],{"class":2774},[2225,167203,6620],{"class":2235},[2225,167205,2311],{"class":2235},[2225,167207,152333],{"class":2235},[2225,167209,167210,167212,167214,167216,167218],{"class":2227,"line":2861},[2225,167211,9368],{"class":2235},[2225,167213,22793],{"class":2774},[2225,167215,6620],{"class":2235},[2225,167217,2311],{"class":2235},[2225,167219,152333],{"class":2235},[2225,167221,167222,167224,167227,167229,167231],{"class":2227,"line":4246},[2225,167223,9368],{"class":2235},[2225,167225,167226],{"class":2774},"grants",[2225,167228,6620],{"class":2235},[2225,167230,2311],{"class":2235},[2225,167232,32491],{"class":2235},[2225,167234,167235,167237,167239,167241,167243,167245,167247,167249,167251,167253,167255,167258,167260,167262,167264,167266,167268],{"class":2227,"line":4253},[2225,167236,32496],{"class":2235},[2225,167238,6614],{"class":2235},[2225,167240,6471],{"class":6342},[2225,167242,6620],{"class":2235},[2225,167244,2311],{"class":2235},[2225,167246,6614],{"class":2235},[2225,167248,23926],{"class":2252},[2225,167250,6620],{"class":2235},[2225,167252,2298],{"class":2235},[2225,167254,6614],{"class":2235},[2225,167256,167257],{"class":6342},"permission",[2225,167259,6620],{"class":2235},[2225,167261,2311],{"class":2235},[2225,167263,6614],{"class":2235},[2225,167265,10026],{"class":2252},[2225,167267,6620],{"class":2235},[2225,167269,2353],{"class":2235},[2225,167271,167272,167274,167276,167278,167280,167282,167284,167286,167288,167290,167292,167294,167296,167298,167300,167302,167304],{"class":2227,"line":4259},[2225,167273,32496],{"class":2235},[2225,167275,6614],{"class":2235},[2225,167277,6471],{"class":6342},[2225,167279,6620],{"class":2235},[2225,167281,2311],{"class":2235},[2225,167283,6614],{"class":2235},[2225,167285,6487],{"class":2252},[2225,167287,6620],{"class":2235},[2225,167289,2298],{"class":2235},[2225,167291,6614],{"class":2235},[2225,167293,167257],{"class":6342},[2225,167295,6620],{"class":2235},[2225,167297,2311],{"class":2235},[2225,167299,6614],{"class":2235},[2225,167301,10026],{"class":2252},[2225,167303,6620],{"class":2235},[2225,167305,6369],{"class":2235},[2225,167307,167308],{"class":2227,"line":6548},[2225,167309,110088],{"class":2235},[2225,167311,167312],{"class":2227,"line":6576},[2225,167313,7351],{"class":2235},[937,167315,167316,167318,167319,167322,167323,957],{},[941,167317,167226],{}," reflects what the ",[1092,167320,167321],{},"unlocked user"," can actually access — the collections they hold a decryption key for, at the permission level from their keyring (owners\u002Fadmins default to ",[941,167324,11380],{},[1022,167326,102889],{"id":102888},[1591,167328,167330],{"id":167329},"vaultdumpschemaopts",[941,167331,167332],{},"vault.dumpSchema(opts?)",[937,167334,167335,167338,167339,167342,167343,1520],{},[941,167336,167337],{},"dumpSchema(opts?: DumpSchemaOptions): Promise\u003CVaultSchemaSnapshot>"," — walks the vault's collections (and, with ",[941,167340,167341],{},"withStats",", its stored records) and returns a ",[941,167344,167345],{},"VaultSchemaSnapshot",[937,167347,167348,2311],{},[941,167349,167350],{},"DumpSchemaOptions",[1338,167352,167353,167365],{},[1341,167354,167355],{},[1344,167356,167357,167359,167361,167363],{},[1347,167358,101060],{},[1347,167360,20194],{},[1347,167362,20197],{},[1347,167364,7834],{},[1357,167366,167367,167394],{},[1344,167368,167369,167373,167377,167381],{},[1362,167370,167371],{},[941,167372,167341],{},[1362,167374,167375],{},[941,167376,16861],{},[1362,167378,167379],{},[941,167380,2560],{},[1362,167382,167383,167384,167387,167388,167391,167392,1520],{},"Also compute per-collection ",[941,167385,167386],{},"stats"," (record count, byte totals, oldest\u002Fnewest) and an ",[941,167389,167390],{},"internal"," block for non-empty internal collections. Reads every record — see ",[1007,167393,27628],{"href":126812},[1344,167395,167396,167401,167405,167409],{},[1362,167397,167398],{},[941,167399,167400],{},"sampleSize",[1362,167402,167403],{},[941,167404,20636],{},[1362,167406,167407],{},[941,167408,26329],{},[1362,167410,167411,167412,1520],{},"Reserved for a future records-sampling fallback for collections with no schema. Currently has no effect — see ",[1007,167413,27628],{"href":126812},[937,167415,167416,2311],{},[941,167417,167345],{},[1338,167419,167420,167430],{},[1341,167421,167422],{},[1344,167423,167424,167426,167428],{},[1347,167425,101104],{},[1347,167427,20194],{},[1347,167429,9542],{},[1357,167431,167432,167445,167458,167471,167493,167507,167526,167540,167553,167566,167579],{},[1344,167433,167434,167438,167442],{},[1362,167435,167436],{},[941,167437,166199],{},[1362,167439,167440],{},[941,167441,5437],{},[1362,167443,167444],{},"Snapshot format version.",[1344,167446,167447,167451,167455],{},[1362,167448,167449],{},[941,167450,2694],{},[1362,167452,167453],{},[941,167454,7259],{},[1362,167456,167457],{},"The vault's name.",[1344,167459,167460,167464,167468],{},[1362,167461,167462],{},[941,167463,166232],{},[1362,167465,167466],{},[941,167467,7259],{},[1362,167469,167470],{},"ISO-8601 timestamp of the call.",[1344,167472,167473,167477,167482],{},[1362,167474,167475],{},[941,167476,166252],{},[1362,167478,167479],{},[941,167480,167481],{},"Record\u003Cstring, boolean>",[1362,167483,167484,167485,968,167487,968,167489,968,167491,1520],{},"Opt-in matrix: ",[941,167486,693],{},[941,167488,665],{},[941,167490,166283],{},[941,167492,166294],{},[1344,167494,167495,167500,167504],{},[1362,167496,167497],{},[941,167498,167499],{},"aclRoles?",[1362,167501,167502],{},[941,167503,22829],{},[1362,167505,167506],{},"Reserved for a future ACL-role listing; not populated by the current walker.",[1344,167508,167509,167514,167518],{},[1362,167510,167511],{},[941,167512,167513],{},"meta?",[1362,167515,167516],{},[941,167517,11817],{},[1362,167519,167520,167521,167523,167524,1520],{},"Present only when the vault was opened with a ",[941,167522,17587],{}," option — see ",[1007,167525,14900],{"href":678},[1344,167527,167528,167532,167537],{},[1362,167529,167530],{},[941,167531,2698],{},[1362,167533,167534],{},[941,167535,167536],{},"Record\u003Cstring, CollectionDescriptor>",[1362,167538,167539],{},"Every non-internal collection, alphabetical by name.",[1344,167541,167542,167546,167551],{},[1362,167543,167544],{},[941,167545,166283],{},[1362,167547,167548],{},[941,167549,167550],{},"Record\u003Cstring, MaterializedViewDescriptor>",[1362,167552],{},[1344,167554,167555,167559,167564],{},[1362,167556,167557],{},[941,167558,166294],{},[1362,167560,167561],{},[941,167562,167563],{},"Record\u003Cstring, OverlayViewDescriptor>",[1362,167565],{},[1344,167567,167568,167572,167577],{},[1362,167569,167570],{},[941,167571,665],{},[1362,167573,167574],{},[941,167575,167576],{},"Record\u003Cstring, DerivationDescriptor>",[1362,167578],{},[1344,167580,167581,167586,167591],{},[1362,167582,167583],{},[941,167584,167585],{},"internal?",[1362,167587,167588],{},[941,167589,167590],{},"Record\u003Cstring, InternalCollectionStats>",[1362,167592,167593,167594,167597,167598,968,167600,968,167602,968,167604,968,167606,167609],{},"Only with ",[941,167595,167596],{},"withStats: true",", and only for internal collections (",[941,167599,6709],{},[941,167601,6724],{},[941,167603,117002],{},[941,167605,35623],{},[941,167607,167608],{},"_deltas",") that have at least one record.",[937,167611,167612,2311],{},[941,167613,167614],{},"CollectionDescriptor",[1338,167616,167617,167627],{},[1341,167618,167619],{},[1344,167620,167621,167623,167625],{},[1347,167622,101104],{},[1347,167624,20194],{},[1347,167626,9542],{},[1357,167628,167629,167643,167661,167677,167692,167708,167724],{},[1344,167630,167631,167635,167640],{},[1362,167632,167633],{},[941,167634,57285],{},[1362,167636,167637],{},[941,167638,167639],{},"Record\u003Cstring, FieldDescriptor>",[1362,167641,167642],{},"Empty object when no schema info could be found.",[1344,167644,167645,167649,167654],{},[1362,167646,167647],{},[941,167648,21907],{},[1362,167650,167651],{},[941,167652,167653],{},"Array\u003C{ fields: string[]; unique?: boolean }>",[1362,167655,167656,167657,3178,167659,1520],{},"Reserved — currently always ",[941,167658,7262],{},[1007,167660,27628],{"href":126812},[1344,167662,167663,167667,167672],{},[1362,167664,167665],{},[941,167666,22753],{},[1362,167668,167669],{},[941,167670,167671],{},"Record\u003Cstring, { target: string; mode: 'strict' | 'warn' | 'cascade' }>",[1362,167673,167674,167675,9954],{},"Foreign-key declarations from the collection's ",[941,167676,22753],{},[1344,167678,167679,167684,167689],{},[1362,167680,167681],{},[941,167682,167683],{},"validator?",[1362,167685,167686],{},[941,167687,167688],{},"{ kind: 'Zod' | 'Valibot' | 'ArkType' | 'Effect' | 'Unknown'; source: 'persisted' | 'live-validator' }",[1362,167690,167691],{},"Present only when a schema was found.",[1344,167693,167694,167699,167704],{},[1362,167695,167696],{},[941,167697,167698],{},"stats?",[1362,167700,167701],{},[941,167702,167703],{},"CollectionStats",[1362,167705,167593,167706,1520],{},[941,167707,167596],{},[1344,167709,167710,167714,167719],{},[1362,167711,167712],{},[941,167713,167513],{},[1362,167715,167716],{},[941,167717,167718],{},"CollectionMeta",[1362,167720,167721,167722,9954],{},"Only when the collection was declared with a ",[941,167723,17587],{},[1344,167725,167726,167731,167736],{},[1362,167727,167728],{},[941,167729,167730],{},"config?",[1362,167732,167733],{},[941,167734,167735],{},"CollectionConfig",[1362,167737,167738,167739,1520],{},"Only for live-declared collections; see the full field list in ",[1007,167740,167742,167743,149886,167745,149889],{"href":167741},"\u002Fservices\u002Ffield-metadata#dumpschema-collection-level-config-block","field-metadata → ",[941,167744,149885],{},[941,167746,66864],{},[937,167748,167749,2311],{},[941,167750,167751],{},"FieldDescriptor",[1338,167753,167754,167764],{},[1341,167755,167756],{},[1344,167757,167758,167760,167762],{},[1347,167759,101104],{},[1347,167761,20194],{},[1347,167763,9542],{},[1357,167765,167766,167782,167805,167843,167859],{},[1344,167767,167768,167772,167776],{},[1362,167769,167770],{},[941,167771,6339],{},[1362,167773,167774],{},[941,167775,7259],{},[1362,167777,167778,167779,1520],{},"One of ",[941,167780,167781],{},"'string' | 'number' | 'boolean' | 'enum' | 'object' | 'array' | 'null' | 'opaque'",[1344,167783,167784,167788,167793],{},[1362,167785,167786],{},[941,167787,22723],{},[1362,167789,167790],{},[941,167791,167792],{},"FieldSource",[1362,167794,167795,27678,167798,167800,167801,167804],{},[941,167796,167797],{},"'persisted' | 'live-validator' | 'sampled' | 'unknown'",[1007,167799,27628],{"href":126812}," for why ",[941,167802,167803],{},"'sampled'"," never actually appears today.",[1344,167806,167807,167812,167817],{},[1362,167808,167809],{},[941,167810,167811],{},"constraints?",[1362,167813,167814],{},[941,167815,167816],{},"Record\u003Cstring, unknown>",[1362,167818,167819,968,167821,968,167824,968,167826,968,167828,968,167831,968,167834,968,167837,4765,167840,167842],{},[941,167820,120003],{},[941,167822,167823],{},"maxLength",[941,167825,16382],{},[941,167827,23270],{},[941,167829,167830],{},"minimum",[941,167832,167833],{},"maximum",[941,167835,167836],{},"gt",[941,167838,167839],{},"lt",[941,167841,148357],{}," (enum\u002Fliteral members).",[1344,167844,167845,167850,167854],{},[1362,167846,167847],{},[941,167848,167849],{},"optional?",[1362,167851,167852],{},[941,167853,16861],{},[1362,167855,167856,167858],{},[941,167857,11790],{}," when the field is not required.",[1344,167860,167861,167866,167870],{},[1362,167862,167863],{},[941,167864,167865],{},"references?",[1362,167867,167868],{},[941,167869,7259],{},[1362,167871,167872,167875],{},[941,167873,167874],{},"\"\u003Ccollection>.id\""," when the field is declared as a ref.",[937,167877,167878,2085,167880,167883,167884,167886,167887,1625,167889,953,167891,167893],{},[941,167879,167703],{},[941,167881,167882],{},"{ records, bytes, bytesAvg, bytesMin, bytesMax, oldest, newest }"," — all ",[941,167885,20636],{}," except ",[941,167888,167000],{},[941,167890,167019],{},[941,167892,7259],{},", ISO-8601, empty when the collection has no records).",[937,167895,167896,2085,167899,964,167902,167904,167905,33694,167908,957],{},[941,167897,167898],{},"MaterializedViewDescriptor",[941,167900,167901],{},"{ sources: string[], groupBy?: string[], aggregate?: Record\u003Cstring, string>, refresh: string, stats?: CollectionStats }",[941,167903,4449],{}," values are summarized as ",[941,167906,167907],{},"\"op(field)\"",[941,167909,167910],{},"\"sum(amount)\"",[937,167912,167913,2085,167916,1520],{},[941,167914,167915],{},"OverlayViewDescriptor",[941,167917,167918],{},"{ base: string, overlay: string }",[937,167920,167921,2085,167924,1520],{},[941,167922,167923],{},"DerivationDescriptor",[941,167925,167926],{},"{ source: string, outputs: string[] }",[937,167928,167929,2085,167932,1520],{},[941,167930,167931],{},"InternalCollectionStats",[941,167933,167934],{},"{ records: number, bytes: number }",[1591,167936,167938],{"id":167937},"vaultintrospect",[941,167939,165499],{},[937,167941,167942,167945],{},[941,167943,167944],{},"introspect(): Promise\u003CSchemaIntrospection>"," — no options.",[1338,167947,167948,167958],{},[1341,167949,167950],{},[1344,167951,167952,167954,167956],{},[1347,167953,101104],{},[1347,167955,20194],{},[1347,167957,9542],{},[1357,167959,167960,167974,167991,168005,168022],{},[1344,167961,167962,167966,167971],{},[1362,167963,167964],{},[941,167965,2698],{},[1362,167967,167968],{},[941,167969,167970],{},"Array\u003C{ name: string; docCount: number }>",[1362,167972,167973],{},"Union of collections registered this session and collections with persisted records, sorted by name.",[1344,167975,167976,167980,167985],{},[1362,167977,167978],{},[941,167979,693],{},[1362,167981,167982],{},[941,167983,167984],{},"Array\u003C{ collection: string; count: number }>",[1362,167986,167987,167988,167990],{},"From the guard registry, when ",[1007,167989,693],{"href":694}," is opted in.",[1344,167992,167993,167997,168002],{},[1362,167994,167995],{},[941,167996,166283],{},[1362,167998,167999],{},[941,168000,168001],{},"Array\u003C{ name: string; sourceCollections: string[] }>",[1362,168003,168004],{},"From the materialized-view registry, when opted in.",[1344,168006,168007,168011,168016],{},[1362,168008,168009],{},[941,168010,22793],{},[1362,168012,168013],{},[941,168014,168015],{},"Array\u003C{ collection: string; strategies: string[] }>",[1362,168017,168018,168019,168021],{},"Registered ",[1007,168020,820],{"href":821}," strategy names per collection.",[1344,168023,168024,168028,168033],{},[1362,168025,168026],{},[941,168027,167226],{},[1362,168029,168030],{},[941,168031,168032],{},"Array\u003C{ collection: string; permission: Permission }>",[1362,168034,168035],{},"Collections the unlocked user holds a key for, with their permission level.",[1022,168037,68210],{"id":68209},[1111,168039,168040,168052,168080,168093,168105,168115,168130,168139],{},[1114,168041,168042,168047,168048,168051],{},[950,168043,168044,168046],{},[941,168045,167596],{}," reads every record."," It calls ",[941,168049,168050],{},"adapter.get()"," once per record in every collection to compute byte sizes and timestamps — cost scales with total record count. Reserve it for periodic audits or CLI use, not hot request paths.",[1114,168053,168054,1324,168057,168059,168060,168062,168063,70507,168066,168068,168069,1952,168071,168073,168074,168076,168077,168079],{},[950,168055,168056],{},"Sampling is declared but not implemented.",[941,168058,167400],{}," is accepted and defaults to ",[941,168061,26329],{},", but the sampling fallback for collections with no persisted or live schema is not built yet: such collections simply report ",[941,168064,168065],{},"fields: {}",[941,168067,166464],{},", regardless of ",[941,168070,167400],{},[941,168072,167803],{}," exists as a ",[941,168075,167792],{}," value but ",[941,168078,149885],{}," never emits it today.",[1114,168081,168082,168089,168090,168092],{},[950,168083,168084,168086,168087,1520],{},[941,168085,21907],{}," is always ",[941,168088,7262],{}," The shape is reserved on ",[941,168091,167614],{}," but the current walker does not populate it.",[1114,168094,168095,168101,168102,168104],{},[950,168096,168097,168100],{},[941,168098,168099],{},"aclRoles"," is never set."," The field is reserved on ",[941,168103,167345],{}," for a future ACL-role listing, but nothing in the current walker assigns it.",[1114,168106,168107,168114],{},[950,168108,168109,168086,168112,1520],{},[941,168110,168111],{},"MaterializedViewDescriptor.stats",[941,168113,18316],{}," The field exists on the type for a future record-count walk over materialized views; today's walker never sets it.",[1114,168116,168117,1324,168120,968,168122,2462,168124,168126,168127,168129],{},[950,168118,168119],{},"Views and derivations reflect the current process.",[941,168121,166283],{},[941,168123,166294],{},[941,168125,665],{}," are read from the vault's live in-memory registries, not from persisted storage — a fresh process must re-register them (e.g. via the same ",[941,168128,943],{}," opt-ins) before they show up in a snapshot.",[1114,168131,168132,168135,168136,168138],{},[950,168133,168134],{},"A missing key silently degrades, it doesn't throw."," If the unlocked keyring has no decryption key for a collection, ",[941,168137,149885],{}," falls back from the persisted schema to the live validator (or to empty fields) instead of throwing — it never surfaces a permission error.",[1114,168140,168141,168146,168147,168149,168150,168152,168153,168155,168156,168158],{},[950,168142,168143,168145],{},[941,168144,66864],{}," needs a live collection."," It's populated from the in-process ",[941,168148,103],{}," object, so a collection that was never touched via ",[941,168151,2138],{}," this session (e.g. reconstructed purely from a ",[941,168154,1402],{}," bundle) gets no ",[941,168157,66864],{}," block.",[1022,168160,2621],{"id":2620},[1111,168162,168163,168179,168191,168202,168217,168230],{},[1114,168164,168165,964,168167,968,168169,1625,168171,168173,168174,1324,168176,168178],{},[1007,168166,14900],{"href":678},[941,168168,1380],{},[941,168170,167718],{},[941,168172,11817],{},", and the full ",[941,168175,149885],{},[941,168177,66864],{}," block field list.",[1114,168180,168181,168183,168184,168186,168187,168190],{},[1007,168182,780],{"href":781}," — the encrypted ",[941,168185,166841],{}," envelope that produces ",[941,168188,168189],{},"source: 'persisted'"," field data.",[1114,168192,168193,168195,168196,997,168199,1520],{},[1007,168194,820],{"href":821}," — the strategies whose names appear in ",[941,168197,168198],{},"introspect().schemaUpdate",[941,168200,168201],{},"config.schemaUpdate",[1114,168203,168204,1368,168206,168208,168209,997,168211,1625,168214,1520],{},[1007,168205,693],{"href":694},[1007,168207,739],{"href":740}," — the subsystems summarized in ",[941,168210,166252],{},[941,168212,168213],{},"introspect().guards",[941,168215,168216],{},".materializedViews",[1114,168218,168219,1368,168221,1169,168223,1625,168225,168227,168228,1520],{},[1007,168220,768],{"href":769},[1007,168222,665],{"href":666},[941,168224,167915],{},[941,168226,167923],{}," shapes returned by ",[941,168229,149885],{},[1114,168231,168232,1368,168234],{},[1007,168233,61743],{"href":598},[1007,168235,1019],{"href":1018},[2645,168237,168238],{},"html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .su27w, html code.shiki .su27w{--shiki-light:#916B53;--shiki-default:#916B53;--shiki-dark:#916B53}html pre.shiki code .sfNiH, html code.shiki .sfNiH{--shiki-light:#FF5370;--shiki-default:#FF9CAC;--shiki-dark:#FF9CAC}",{"title":1037,"searchDepth":1038,"depth":1038,"links":168240},[168241,168242,168243,168248,168252,168253],{"id":6241,"depth":1041,"text":6242},{"id":58393,"depth":1041,"text":58394},{"id":29892,"depth":1041,"text":29893,"children":168244},[168245,168246,168247],{"id":165681,"depth":1038,"text":165682},{"id":166848,"depth":1038,"text":166849},{"id":167047,"depth":1038,"text":167048},{"id":102888,"depth":1041,"text":102889,"children":168249},[168250,168251],{"id":167329,"depth":1038,"text":167332},{"id":167937,"depth":1038,"text":165499},{"id":68209,"depth":1041,"text":68210},{"id":2620,"depth":1041,"text":2621},"Vault shape introspection: `vault.dumpSchema()` snapshots collections, fields, views, and derivations; `vault.introspect()` is the lighter read.",{},{"title":711,"description":168254},"21D4hlcroal7YulKjkj3LPjoMLgeq80JGKLJKV2zYxY",{"id":168259,"title":715,"access":12,"apiToc":929,"body":168260,"category":1042,"description":170694,"extension":1044,"meta":170695,"navigation":1042,"nest":1047,"path":716,"seo":170696,"sinceVersion":1042,"status":1049,"stem":717,"subtitle":1042,"tier":1050,"tocExternal":1042,"__hash__":170697},"docs\u002Fservices\u002Fjoins.md",{"type":931,"value":168261,"toc":170678},[168262,168264,168267,168269,168296,168305,168322,168324,168333,168673,168675,168679,168694,168940,169089,169123,169127,169136,169581,169586,169688,169692,169711,169748,169982,169990,169994,170009,170199,170219,170221,170227,170326,170352,170428,170498,170503,170505,170509,170554,170556,170621,170623,170675],[934,168263,715],{"id":715},[46871,168265],{":rows":168266},"[{\"label\":\"Subpath\",\"value\":\"*(currently always-core; a dedicated `@noy-db\u002Fhub\u002Fjoins` subpath is a planned follow-up)*\"},{\"label\":\"Factory\",\"value\":\"`withJoins()` *(planned)*\"},{\"label\":\"Cluster\",\"value\":\"A — Read & Query\"}]",[1022,168268,29857],{"id":29856},[937,168270,3289,168271,168273,168274,168279,168280,168282,168283,168285,168286,168288,168289,168292,168293,168295],{},[1007,168272,6471],{"href":58262}," can declare a foreign-key field with ",[1007,168275,168277],{"href":168276},".\u002Fforeign-refs",[941,168278,62282],{}," — for example an invoice's ",[941,168281,59721],{}," pointing at the ",[941,168284,23926],{}," collection. ",[941,168287,61808],{}," is the query-builder method that resolves one of those declared references and attaches the target record onto the result under an alias you choose, so ",[941,168290,168291],{},"invoice.client.name"," is available without a second round trip. Joins run ",[950,168294,132461],{}," — the storage backend only ever sees ciphertext and never runs the query itself.",[937,168297,168298,168299,168301,168302,33515],{},"You want it for parent-record lookups that follow an existing reference: reports that need the client's name next to each invoice, multi-step navigation (invoice → client → region), or a UI pane that should re-render whenever either the invoice or its client changes. For pairing rows that have ",[950,168300,11313],{}," declared reference (every worker × every pay period), use ",[1007,168303,657],{"href":168304},".\u002Fcross-join",[937,168306,168307,168308,168310,168311,168313,168314,168316,168317,168319,168320,1520],{},"Joins are part of the core query builder today — there is no ",[941,168309,943],{}," factory to opt into and no bundle cost to avoid. The catalog reserves a dedicated ",[941,168312,72905],{}," subpath and a ",[941,168315,72899],{}," factory (~470 LOC) for a future extraction, but until that lands ",[941,168318,61808],{}," is simply available on every ",[941,168321,25801],{},[1022,168323,99864],{"id":99863},[937,168325,132505,168326,168328,168329,2311],{},[941,168327,61808],{}," itself. Declare the reference when you create the collection, with ",[1007,168330,168331],{"href":168276},[941,168332,62282],{},[2217,168334,168336],{"className":2219,"code":168335,"language":2221,"meta":1037,"style":1037},"import { createNoydb, ref } from '@noy-db\u002Fhub'\nimport { toMemory } from '@noy-db\u002Fto-memory'\n\ntype Client = { id: string; name: string; tier: 'standard' | 'premium' }\ntype Invoice = { id: string; amount: number; status: 'draft' | 'open' | 'paid'; clientId: string }\n\nconst db = await createNoydb({ store: toMemory(), user: 'alice', secret: 'a-strong-secret' })\nconst vault = await db.openVault('acme')\n\nconst clients = vault.collection\u003CClient>('clients')\nconst invoices = vault.collection\u003CInvoice>('invoices', {\n  refs: { clientId: ref('clients') },\n})\n",[941,168337,168338,168360,168378,168382,168431,168493,168497,168549,168575,168579,168609,168641,168667],{"__ignoreMap":1037},[2225,168339,168340,168342,168344,168346,168348,168350,168352,168354,168356,168358],{"class":2227,"line":2228},[2225,168341,2232],{"class":2231},[2225,168343,2236],{"class":2235},[2225,168345,2744],{"class":2239},[2225,168347,2298],{"class":2235},[2225,168349,62303],{"class":2239},[2225,168351,2243],{"class":2235},[2225,168353,2246],{"class":2231},[2225,168355,2249],{"class":2235},[2225,168357,1086],{"class":2252},[2225,168359,2256],{"class":2235},[2225,168361,168362,168364,168366,168368,168370,168372,168374,168376],{"class":2227,"line":1041},[2225,168363,2232],{"class":2231},[2225,168365,2236],{"class":2235},[2225,168367,68576],{"class":2239},[2225,168369,2243],{"class":2235},[2225,168371,2246],{"class":2231},[2225,168373,2249],{"class":2235},[2225,168375,9638],{"class":2252},[2225,168377,2256],{"class":2235},[2225,168379,168380],{"class":2227,"line":1038},[2225,168381,2285],{"emptyLinePlaceholder":2284},[2225,168383,168384,168386,168388,168390,168392,168394,168396,168398,168400,168402,168404,168406,168408,168411,168413,168415,168418,168420,168422,168424,168427,168429],{"class":2227,"line":2288},[2225,168385,6339],{"class":2774},[2225,168387,54194],{"class":6342},[2225,168389,6346],{"class":2235},[2225,168391,2236],{"class":2235},[2225,168393,7070],{"class":2307},[2225,168395,2311],{"class":2235},[2225,168397,6366],{"class":6342},[2225,168399,3769],{"class":2235},[2225,168401,8104],{"class":2307},[2225,168403,2311],{"class":2235},[2225,168405,6366],{"class":6342},[2225,168407,3769],{"class":2235},[2225,168409,168410],{"class":2307}," tier",[2225,168412,2311],{"class":2235},[2225,168414,2249],{"class":2235},[2225,168416,168417],{"class":2252},"standard",[2225,168419,2805],{"class":2235},[2225,168421,7087],{"class":2235},[2225,168423,2249],{"class":2235},[2225,168425,168426],{"class":2252},"premium",[2225,168428,2805],{"class":2235},[2225,168430,6369],{"class":2235},[2225,168432,168433,168435,168437,168439,168441,168443,168445,168447,168449,168451,168453,168455,168457,168459,168461,168463,168465,168467,168469,168471,168473,168475,168477,168479,168481,168483,168485,168487,168489,168491],{"class":2227,"line":2304},[2225,168434,6339],{"class":2774},[2225,168436,6343],{"class":6342},[2225,168438,6346],{"class":2235},[2225,168440,2236],{"class":2235},[2225,168442,7070],{"class":2307},[2225,168444,2311],{"class":2235},[2225,168446,6366],{"class":6342},[2225,168448,3769],{"class":2235},[2225,168450,6351],{"class":2307},[2225,168452,2311],{"class":2235},[2225,168454,6356],{"class":6342},[2225,168456,3769],{"class":2235},[2225,168458,6361],{"class":2307},[2225,168460,2311],{"class":2235},[2225,168462,2249],{"class":2235},[2225,168464,28429],{"class":2252},[2225,168466,2805],{"class":2235},[2225,168468,7087],{"class":2235},[2225,168470,2249],{"class":2235},[2225,168472,26178],{"class":2252},[2225,168474,2805],{"class":2235},[2225,168476,7087],{"class":2235},[2225,168478,2249],{"class":2235},[2225,168480,6539],{"class":2252},[2225,168482,2805],{"class":2235},[2225,168484,3769],{"class":2235},[2225,168486,62649],{"class":2307},[2225,168488,2311],{"class":2235},[2225,168490,6366],{"class":6342},[2225,168492,6369],{"class":2235},[2225,168494,168495],{"class":2227,"line":2356},[2225,168496,2285],{"emptyLinePlaceholder":2284},[2225,168498,168499,168501,168503,168505,168507,168509,168511,168513,168515,168517,168519,168521,168523,168525,168527,168529,168531,168533,168535,168537,168539,168541,168543,168545,168547],{"class":2227,"line":2830},[2225,168500,2775],{"class":2774},[2225,168502,2778],{"class":2239},[2225,168504,2781],{"class":2235},[2225,168506,2784],{"class":2231},[2225,168508,2744],{"class":2291},[2225,168510,2789],{"class":2239},[2225,168512,2792],{"class":2235},[2225,168514,7564],{"class":2307},[2225,168516,2311],{"class":2235},[2225,168518,68576],{"class":2291},[2225,168520,4228],{"class":2239},[2225,168522,2298],{"class":2235},[2225,168524,2795],{"class":2307},[2225,168526,2311],{"class":2235},[2225,168528,2249],{"class":2235},[2225,168530,6408],{"class":2252},[2225,168532,2805],{"class":2235},[2225,168534,2298],{"class":2235},[2225,168536,2810],{"class":2307},[2225,168538,2311],{"class":2235},[2225,168540,2249],{"class":2235},[2225,168542,6421],{"class":2252},[2225,168544,2805],{"class":2235},[2225,168546,2243],{"class":2235},[2225,168548,2362],{"class":2239},[2225,168550,168551,168553,168555,168557,168559,168561,168563,168565,168567,168569,168571,168573],{"class":2227,"line":2861},[2225,168552,2775],{"class":2774},[2225,168554,2835],{"class":2239},[2225,168556,2781],{"class":2235},[2225,168558,2784],{"class":2231},[2225,168560,2842],{"class":2239},[2225,168562,1520],{"class":2235},[2225,168564,2847],{"class":2291},[2225,168566,2789],{"class":2239},[2225,168568,2805],{"class":2235},[2225,168570,6450],{"class":2252},[2225,168572,2805],{"class":2235},[2225,168574,2362],{"class":2239},[2225,168576,168577],{"class":2227,"line":4246},[2225,168578,2285],{"emptyLinePlaceholder":2284},[2225,168580,168581,168583,168585,168587,168589,168591,168593,168595,168597,168599,168601,168603,168605,168607],{"class":2227,"line":4253},[2225,168582,2775],{"class":2774},[2225,168584,62534],{"class":2239},[2225,168586,2781],{"class":2235},[2225,168588,6466],{"class":2239},[2225,168590,1520],{"class":2235},[2225,168592,6471],{"class":2291},[2225,168594,6474],{"class":2235},[2225,168596,6673],{"class":6342},[2225,168598,6480],{"class":2235},[2225,168600,2789],{"class":2239},[2225,168602,2805],{"class":2235},[2225,168604,23926],{"class":2252},[2225,168606,2805],{"class":2235},[2225,168608,2362],{"class":2239},[2225,168610,168611,168613,168615,168617,168619,168621,168623,168625,168627,168629,168631,168633,168635,168637,168639],{"class":2227,"line":4259},[2225,168612,2775],{"class":2774},[2225,168614,6461],{"class":2239},[2225,168616,2781],{"class":2235},[2225,168618,6466],{"class":2239},[2225,168620,1520],{"class":2235},[2225,168622,6471],{"class":2291},[2225,168624,6474],{"class":2235},[2225,168626,6477],{"class":6342},[2225,168628,6480],{"class":2235},[2225,168630,2789],{"class":2239},[2225,168632,2805],{"class":2235},[2225,168634,6487],{"class":2252},[2225,168636,2805],{"class":2235},[2225,168638,2298],{"class":2235},[2225,168640,2301],{"class":2235},[2225,168642,168643,168645,168647,168649,168651,168653,168655,168657,168659,168661,168663,168665],{"class":2227,"line":6548},[2225,168644,62642],{"class":2307},[2225,168646,2311],{"class":2235},[2225,168648,2236],{"class":2235},[2225,168650,62649],{"class":2307},[2225,168652,2311],{"class":2235},[2225,168654,62303],{"class":2291},[2225,168656,2789],{"class":2239},[2225,168658,2805],{"class":2235},[2225,168660,23926],{"class":2252},[2225,168662,2805],{"class":2235},[2225,168664,8495],{"class":2239},[2225,168666,19008],{"class":2235},[2225,168668,168669,168671],{"class":2227,"line":6576},[2225,168670,2359],{"class":2235},[2225,168672,2362],{"class":2239},[1022,168674,29893],{"id":29892},[1591,168676,168678],{"id":168677},"single-fk-join","Single-FK join",[937,168680,168681,168684,168685,1952,168687,168690,168691,168693],{},[941,168682,168683],{},"query.join(field, { as })"," resolves the declared reference and attaches the target record under ",[941,168686,4899],{},[941,168688,168689],{},"toArray()"," is synchronous — it reads the already-decrypted in-memory cache, same as the rest of the ",[1007,168692,135061],{"href":83069}," terminals:",[2217,168695,168697],{"className":2219,"code":168696,"language":2221,"meta":1037,"style":1037},"await clients.put('cli-A', { id: 'cli-A', name: 'Acme Corp', tier: 'premium' })\nawait invoices.put('inv-1', { id: 'inv-1', amount: 1200, status: 'paid', clientId: 'cli-A' })\n\nconst enriched = invoices.query()\n  .where('status', '==', 'paid')\n  .join\u003C'client', Client>('clientId', { as: 'client' })\n  .toArray()\n\u002F\u002F → 1 row, shaped like:\n",[941,168698,168699,168761,168829,168833,168849,168881,168927,168935],{"__ignoreMap":1037},[2225,168700,168701,168703,168705,168707,168709,168711,168713,168716,168718,168720,168722,168724,168726,168728,168730,168732,168734,168736,168738,168740,168743,168745,168747,168749,168751,168753,168755,168757,168759],{"class":2227,"line":2228},[2225,168702,6500],{"class":2231},[2225,168704,62689],{"class":2239},[2225,168706,1520],{"class":2235},[2225,168708,3161],{"class":2291},[2225,168710,2789],{"class":2239},[2225,168712,2805],{"class":2235},[2225,168714,168715],{"class":2252},"cli-A",[2225,168717,2805],{"class":2235},[2225,168719,2298],{"class":2235},[2225,168721,2236],{"class":2235},[2225,168723,7070],{"class":2307},[2225,168725,2311],{"class":2235},[2225,168727,2249],{"class":2235},[2225,168729,168715],{"class":2252},[2225,168731,2805],{"class":2235},[2225,168733,2298],{"class":2235},[2225,168735,8104],{"class":2307},[2225,168737,2311],{"class":2235},[2225,168739,2249],{"class":2235},[2225,168741,168742],{"class":2252},"Acme Corp",[2225,168744,2805],{"class":2235},[2225,168746,2298],{"class":2235},[2225,168748,168410],{"class":2307},[2225,168750,2311],{"class":2235},[2225,168752,2249],{"class":2235},[2225,168754,168426],{"class":2252},[2225,168756,2805],{"class":2235},[2225,168758,2243],{"class":2235},[2225,168760,2362],{"class":2239},[2225,168762,168763,168765,168767,168769,168771,168773,168775,168777,168779,168781,168783,168785,168787,168789,168791,168793,168795,168797,168799,168801,168803,168805,168807,168809,168811,168813,168815,168817,168819,168821,168823,168825,168827],{"class":2227,"line":1041},[2225,168764,6500],{"class":2231},[2225,168766,6503],{"class":2239},[2225,168768,1520],{"class":2235},[2225,168770,3161],{"class":2291},[2225,168772,2789],{"class":2239},[2225,168774,2805],{"class":2235},[2225,168776,6514],{"class":2252},[2225,168778,2805],{"class":2235},[2225,168780,2298],{"class":2235},[2225,168782,2236],{"class":2235},[2225,168784,7070],{"class":2307},[2225,168786,2311],{"class":2235},[2225,168788,2249],{"class":2235},[2225,168790,6514],{"class":2252},[2225,168792,2805],{"class":2235},[2225,168794,2298],{"class":2235},[2225,168796,6351],{"class":2307},[2225,168798,2311],{"class":2235},[2225,168800,136139],{"class":6527},[2225,168802,2298],{"class":2235},[2225,168804,6361],{"class":2307},[2225,168806,2311],{"class":2235},[2225,168808,2249],{"class":2235},[2225,168810,6539],{"class":2252},[2225,168812,2805],{"class":2235},[2225,168814,2298],{"class":2235},[2225,168816,62649],{"class":2307},[2225,168818,2311],{"class":2235},[2225,168820,2249],{"class":2235},[2225,168822,168715],{"class":2252},[2225,168824,2805],{"class":2235},[2225,168826,2243],{"class":2235},[2225,168828,2362],{"class":2239},[2225,168830,168831],{"class":2227,"line":1038},[2225,168832,2285],{"emptyLinePlaceholder":2284},[2225,168834,168835,168837,168839,168841,168843,168845,168847],{"class":2227,"line":2288},[2225,168836,2775],{"class":2774},[2225,168838,63679],{"class":2239},[2225,168840,2781],{"class":2235},[2225,168842,6503],{"class":2239},[2225,168844,1520],{"class":2235},[2225,168846,5286],{"class":2291},[2225,168848,8146],{"class":2239},[2225,168850,168851,168853,168855,168857,168859,168861,168863,168865,168867,168869,168871,168873,168875,168877,168879],{"class":2227,"line":2304},[2225,168852,26235],{"class":2235},[2225,168854,3016],{"class":2291},[2225,168856,2789],{"class":2239},[2225,168858,2805],{"class":2235},[2225,168860,6631],{"class":2252},[2225,168862,2805],{"class":2235},[2225,168864,2298],{"class":2235},[2225,168866,2249],{"class":2235},[2225,168868,21901],{"class":2252},[2225,168870,2805],{"class":2235},[2225,168872,2298],{"class":2235},[2225,168874,2249],{"class":2235},[2225,168876,6539],{"class":2252},[2225,168878,2805],{"class":2235},[2225,168880,2362],{"class":2239},[2225,168882,168883,168885,168887,168889,168891,168893,168895,168897,168899,168901,168903,168905,168907,168909,168911,168913,168915,168917,168919,168921,168923,168925],{"class":2227,"line":2356},[2225,168884,26235],{"class":2235},[2225,168886,63728],{"class":2291},[2225,168888,6474],{"class":2235},[2225,168890,2805],{"class":2235},[2225,168892,9913],{"class":2252},[2225,168894,2805],{"class":2235},[2225,168896,2298],{"class":2235},[2225,168898,54194],{"class":6342},[2225,168900,6480],{"class":2235},[2225,168902,2789],{"class":2239},[2225,168904,2805],{"class":2235},[2225,168906,59721],{"class":2252},[2225,168908,2805],{"class":2235},[2225,168910,2298],{"class":2235},[2225,168912,2236],{"class":2235},[2225,168914,2266],{"class":2307},[2225,168916,2311],{"class":2235},[2225,168918,2249],{"class":2235},[2225,168920,9913],{"class":2252},[2225,168922,2805],{"class":2235},[2225,168924,2243],{"class":2235},[2225,168926,2362],{"class":2239},[2225,168928,168929,168931,168933],{"class":2227,"line":2830},[2225,168930,26235],{"class":2235},[2225,168932,3230],{"class":2291},[2225,168934,8146],{"class":2239},[2225,168936,168937],{"class":2227,"line":2861},[2225,168938,168939],{"class":2763},"\u002F\u002F → 1 row, shaped like:\n",[2217,168941,168943],{"className":6604,"code":168942,"language":141,"meta":1037,"style":1037},"[\n  {\n    \"id\": \"inv-1\",\n    \"amount\": 1200,\n    \"status\": \"paid\",\n    \"clientId\": \"cli-A\",\n    \"client\": { \"id\": \"cli-A\", \"name\": \"Acme Corp\", \"tier\": \"premium\" }\n  }\n]\n",[941,168944,168945,168949,168953,168971,168985,169003,169021,169081,169085],{"__ignoreMap":1037},[2225,168946,168947],{"class":2227,"line":2228},[2225,168948,26349],{"class":2235},[2225,168950,168951],{"class":2227,"line":1041},[2225,168952,59879],{"class":2235},[2225,168954,168955,168957,168959,168961,168963,168965,168967,168969],{"class":2227,"line":1038},[2225,168956,14201],{"class":2235},[2225,168958,6983],{"class":2774},[2225,168960,6620],{"class":2235},[2225,168962,2311],{"class":2235},[2225,168964,6614],{"class":2235},[2225,168966,6514],{"class":2252},[2225,168968,6620],{"class":2235},[2225,168970,1707],{"class":2235},[2225,168972,168973,168975,168977,168979,168981,168983],{"class":2227,"line":2288},[2225,168974,14201],{"class":2235},[2225,168976,6617],{"class":2774},[2225,168978,6620],{"class":2235},[2225,168980,2311],{"class":2235},[2225,168982,136139],{"class":6527},[2225,168984,1707],{"class":2235},[2225,168986,168987,168989,168991,168993,168995,168997,168999,169001],{"class":2227,"line":2304},[2225,168988,14201],{"class":2235},[2225,168990,6631],{"class":2774},[2225,168992,6620],{"class":2235},[2225,168994,2311],{"class":2235},[2225,168996,6614],{"class":2235},[2225,168998,6539],{"class":2252},[2225,169000,6620],{"class":2235},[2225,169002,1707],{"class":2235},[2225,169004,169005,169007,169009,169011,169013,169015,169017,169019],{"class":2227,"line":2356},[2225,169006,14201],{"class":2235},[2225,169008,59721],{"class":2774},[2225,169010,6620],{"class":2235},[2225,169012,2311],{"class":2235},[2225,169014,6614],{"class":2235},[2225,169016,168715],{"class":2252},[2225,169018,6620],{"class":2235},[2225,169020,1707],{"class":2235},[2225,169022,169023,169025,169027,169029,169031,169033,169035,169037,169039,169041,169043,169045,169047,169049,169051,169053,169055,169057,169059,169061,169063,169065,169067,169069,169071,169073,169075,169077,169079],{"class":2227,"line":2830},[2225,169024,14201],{"class":2235},[2225,169026,9913],{"class":2774},[2225,169028,6620],{"class":2235},[2225,169030,2311],{"class":2235},[2225,169032,2236],{"class":2235},[2225,169034,6614],{"class":2235},[2225,169036,6983],{"class":6342},[2225,169038,6620],{"class":2235},[2225,169040,2311],{"class":2235},[2225,169042,6614],{"class":2235},[2225,169044,168715],{"class":2252},[2225,169046,6620],{"class":2235},[2225,169048,2298],{"class":2235},[2225,169050,6614],{"class":2235},[2225,169052,4914],{"class":6342},[2225,169054,6620],{"class":2235},[2225,169056,2311],{"class":2235},[2225,169058,6614],{"class":2235},[2225,169060,168742],{"class":2252},[2225,169062,6620],{"class":2235},[2225,169064,2298],{"class":2235},[2225,169066,6614],{"class":2235},[2225,169068,17414],{"class":6342},[2225,169070,6620],{"class":2235},[2225,169072,2311],{"class":2235},[2225,169074,6614],{"class":2235},[2225,169076,168426],{"class":2252},[2225,169078,6620],{"class":2235},[2225,169080,6369],{"class":2235},[2225,169082,169083],{"class":2227,"line":2861},[2225,169084,7632],{"class":2235},[2225,169086,169087],{"class":2227,"line":4246},[2225,169088,26484],{"class":2235},[937,169090,960,169091,169094,169095,169097,169098,1952,169100,169102,169103,169105,169106,169109,169110,169113,169114,169116,169117,169119,169120,1520],{},[941,169092,169093],{},"Right"," type parameter (",[941,169096,6673],{}," above) is optional — omit it and the joined field types as ",[941,169099,22421],{},[941,169101,61808],{}," requires a query built via ",[941,169104,25801],{},"; the join planner picks a ",[950,169107,169108],{},"nested-loop"," strategy when the target source supports ",[941,169111,169112],{},"lookupById"," (the normal case for a ",[941,169115,103],{}," target) or a ",[950,169118,134337],{}," strategy otherwise, auto-selected unless you force one with ",[941,169121,169122],{},"{ strategy: 'hash' }",[1591,169124,169126],{"id":169125},"multi-fk-chaining","Multi-FK chaining",[937,169128,169129,169130,169132,169133,169135],{},"Chain ",[941,169131,61808],{}," calls to resolve more than one reference on the same left record — each call adds its own alias. A collection's ",[941,169134,22753],{}," are fixed at the point you first open it, so declare every FK you plan to join up front:",[2217,169137,169139],{"className":2219,"code":169138,"language":2221,"meta":1037,"style":1037},"type Category = { id: string; label: string }\ntype FullInvoice = Invoice & { categoryId: string }\n\nconst categories = vault.collection\u003CCategory>('categories')\nconst fullInvoices = vault.collection\u003CFullInvoice>('full-invoices', {\n  refs: { clientId: ref('clients'), categoryId: ref('categories') },\n})\n\nawait categories.put('cat-1', { id: 'cat-1', label: 'Consulting' })\nawait fullInvoices.put('inv-2', { id: 'inv-2', amount: 800, status: 'open', clientId: 'cli-A', categoryId: 'cat-1' })\n\nconst rows = fullInvoices.query()\n  .join\u003C'client', Client>('clientId', { as: 'client' })\n  .join\u003C'category', Category>('categoryId', { as: 'category' })\n  .toArray()\n\u002F\u002F → [{ ..., client: { id: 'cli-A', name: 'Acme Corp', tier: 'premium' },\n\u002F\u002F      category: { id: 'cat-1', label: 'Consulting' } }]\n",[941,169140,169141,169168,169192,169196,169228,169263,169307,169313,169317,169368,169450,169454,169470,169516,169563,169571,169576],{"__ignoreMap":1037},[2225,169142,169143,169145,169148,169150,169152,169154,169156,169158,169160,169162,169164,169166],{"class":2227,"line":2228},[2225,169144,6339],{"class":2774},[2225,169146,169147],{"class":6342}," Category",[2225,169149,6346],{"class":2235},[2225,169151,2236],{"class":2235},[2225,169153,7070],{"class":2307},[2225,169155,2311],{"class":2235},[2225,169157,6366],{"class":6342},[2225,169159,3769],{"class":2235},[2225,169161,95892],{"class":2307},[2225,169163,2311],{"class":2235},[2225,169165,6366],{"class":6342},[2225,169167,6369],{"class":2235},[2225,169169,169170,169172,169175,169177,169179,169181,169183,169186,169188,169190],{"class":2227,"line":1041},[2225,169171,6339],{"class":2774},[2225,169173,169174],{"class":6342}," FullInvoice",[2225,169176,6346],{"class":2235},[2225,169178,6343],{"class":6342},[2225,169180,134400],{"class":2235},[2225,169182,2236],{"class":2235},[2225,169184,169185],{"class":2307}," categoryId",[2225,169187,2311],{"class":2235},[2225,169189,6366],{"class":6342},[2225,169191,6369],{"class":2235},[2225,169193,169194],{"class":2227,"line":1038},[2225,169195,2285],{"emptyLinePlaceholder":2284},[2225,169197,169198,169200,169203,169205,169207,169209,169211,169213,169216,169218,169220,169222,169224,169226],{"class":2227,"line":2288},[2225,169199,2775],{"class":2774},[2225,169201,169202],{"class":2239}," categories ",[2225,169204,2781],{"class":2235},[2225,169206,6466],{"class":2239},[2225,169208,1520],{"class":2235},[2225,169210,6471],{"class":2291},[2225,169212,6474],{"class":2235},[2225,169214,169215],{"class":6342},"Category",[2225,169217,6480],{"class":2235},[2225,169219,2789],{"class":2239},[2225,169221,2805],{"class":2235},[2225,169223,150556],{"class":2252},[2225,169225,2805],{"class":2235},[2225,169227,2362],{"class":2239},[2225,169229,169230,169232,169235,169237,169239,169241,169243,169245,169248,169250,169252,169254,169257,169259,169261],{"class":2227,"line":2304},[2225,169231,2775],{"class":2774},[2225,169233,169234],{"class":2239}," fullInvoices ",[2225,169236,2781],{"class":2235},[2225,169238,6466],{"class":2239},[2225,169240,1520],{"class":2235},[2225,169242,6471],{"class":2291},[2225,169244,6474],{"class":2235},[2225,169246,169247],{"class":6342},"FullInvoice",[2225,169249,6480],{"class":2235},[2225,169251,2789],{"class":2239},[2225,169253,2805],{"class":2235},[2225,169255,169256],{"class":2252},"full-invoices",[2225,169258,2805],{"class":2235},[2225,169260,2298],{"class":2235},[2225,169262,2301],{"class":2235},[2225,169264,169265,169267,169269,169271,169273,169275,169277,169279,169281,169283,169285,169287,169289,169291,169293,169295,169297,169299,169301,169303,169305],{"class":2227,"line":2356},[2225,169266,62642],{"class":2307},[2225,169268,2311],{"class":2235},[2225,169270,2236],{"class":2235},[2225,169272,62649],{"class":2307},[2225,169274,2311],{"class":2235},[2225,169276,62303],{"class":2291},[2225,169278,2789],{"class":2239},[2225,169280,2805],{"class":2235},[2225,169282,23926],{"class":2252},[2225,169284,2805],{"class":2235},[2225,169286,1381],{"class":2239},[2225,169288,2298],{"class":2235},[2225,169290,169185],{"class":2307},[2225,169292,2311],{"class":2235},[2225,169294,62303],{"class":2291},[2225,169296,2789],{"class":2239},[2225,169298,2805],{"class":2235},[2225,169300,150556],{"class":2252},[2225,169302,2805],{"class":2235},[2225,169304,8495],{"class":2239},[2225,169306,19008],{"class":2235},[2225,169308,169309,169311],{"class":2227,"line":2830},[2225,169310,2359],{"class":2235},[2225,169312,2362],{"class":2239},[2225,169314,169315],{"class":2227,"line":2861},[2225,169316,2285],{"emptyLinePlaceholder":2284},[2225,169318,169319,169321,169324,169326,169328,169330,169332,169335,169337,169339,169341,169343,169345,169347,169349,169351,169353,169355,169357,169359,169362,169364,169366],{"class":2227,"line":4246},[2225,169320,6500],{"class":2231},[2225,169322,169323],{"class":2239}," categories",[2225,169325,1520],{"class":2235},[2225,169327,3161],{"class":2291},[2225,169329,2789],{"class":2239},[2225,169331,2805],{"class":2235},[2225,169333,169334],{"class":2252},"cat-1",[2225,169336,2805],{"class":2235},[2225,169338,2298],{"class":2235},[2225,169340,2236],{"class":2235},[2225,169342,7070],{"class":2307},[2225,169344,2311],{"class":2235},[2225,169346,2249],{"class":2235},[2225,169348,169334],{"class":2252},[2225,169350,2805],{"class":2235},[2225,169352,2298],{"class":2235},[2225,169354,95892],{"class":2307},[2225,169356,2311],{"class":2235},[2225,169358,2249],{"class":2235},[2225,169360,169361],{"class":2252},"Consulting",[2225,169363,2805],{"class":2235},[2225,169365,2243],{"class":2235},[2225,169367,2362],{"class":2239},[2225,169369,169370,169372,169375,169377,169379,169381,169383,169385,169387,169389,169391,169393,169395,169397,169399,169401,169403,169405,169407,169410,169412,169414,169416,169418,169420,169422,169424,169426,169428,169430,169432,169434,169436,169438,169440,169442,169444,169446,169448],{"class":2227,"line":4253},[2225,169371,6500],{"class":2231},[2225,169373,169374],{"class":2239}," fullInvoices",[2225,169376,1520],{"class":2235},[2225,169378,3161],{"class":2291},[2225,169380,2789],{"class":2239},[2225,169382,2805],{"class":2235},[2225,169384,26080],{"class":2252},[2225,169386,2805],{"class":2235},[2225,169388,2298],{"class":2235},[2225,169390,2236],{"class":2235},[2225,169392,7070],{"class":2307},[2225,169394,2311],{"class":2235},[2225,169396,2249],{"class":2235},[2225,169398,26080],{"class":2252},[2225,169400,2805],{"class":2235},[2225,169402,2298],{"class":2235},[2225,169404,6351],{"class":2307},[2225,169406,2311],{"class":2235},[2225,169408,169409],{"class":6527}," 800",[2225,169411,2298],{"class":2235},[2225,169413,6361],{"class":2307},[2225,169415,2311],{"class":2235},[2225,169417,2249],{"class":2235},[2225,169419,26178],{"class":2252},[2225,169421,2805],{"class":2235},[2225,169423,2298],{"class":2235},[2225,169425,62649],{"class":2307},[2225,169427,2311],{"class":2235},[2225,169429,2249],{"class":2235},[2225,169431,168715],{"class":2252},[2225,169433,2805],{"class":2235},[2225,169435,2298],{"class":2235},[2225,169437,169185],{"class":2307},[2225,169439,2311],{"class":2235},[2225,169441,2249],{"class":2235},[2225,169443,169334],{"class":2252},[2225,169445,2805],{"class":2235},[2225,169447,2243],{"class":2235},[2225,169449,2362],{"class":2239},[2225,169451,169452],{"class":2227,"line":4259},[2225,169453,2285],{"emptyLinePlaceholder":2284},[2225,169455,169456,169458,169460,169462,169464,169466,169468],{"class":2227,"line":6548},[2225,169457,2775],{"class":2774},[2225,169459,42003],{"class":2239},[2225,169461,2781],{"class":2235},[2225,169463,169374],{"class":2239},[2225,169465,1520],{"class":2235},[2225,169467,5286],{"class":2291},[2225,169469,8146],{"class":2239},[2225,169471,169472,169474,169476,169478,169480,169482,169484,169486,169488,169490,169492,169494,169496,169498,169500,169502,169504,169506,169508,169510,169512,169514],{"class":2227,"line":6576},[2225,169473,26235],{"class":2235},[2225,169475,63728],{"class":2291},[2225,169477,6474],{"class":2235},[2225,169479,2805],{"class":2235},[2225,169481,9913],{"class":2252},[2225,169483,2805],{"class":2235},[2225,169485,2298],{"class":2235},[2225,169487,54194],{"class":6342},[2225,169489,6480],{"class":2235},[2225,169491,2789],{"class":2239},[2225,169493,2805],{"class":2235},[2225,169495,59721],{"class":2252},[2225,169497,2805],{"class":2235},[2225,169499,2298],{"class":2235},[2225,169501,2236],{"class":2235},[2225,169503,2266],{"class":2307},[2225,169505,2311],{"class":2235},[2225,169507,2249],{"class":2235},[2225,169509,9913],{"class":2252},[2225,169511,2805],{"class":2235},[2225,169513,2243],{"class":2235},[2225,169515,2362],{"class":2239},[2225,169517,169518,169520,169522,169524,169526,169529,169531,169533,169535,169537,169539,169541,169543,169545,169547,169549,169551,169553,169555,169557,169559,169561],{"class":2227,"line":6582},[2225,169519,26235],{"class":2235},[2225,169521,63728],{"class":2291},[2225,169523,6474],{"class":2235},[2225,169525,2805],{"class":2235},[2225,169527,169528],{"class":2252},"category",[2225,169530,2805],{"class":2235},[2225,169532,2298],{"class":2235},[2225,169534,169147],{"class":6342},[2225,169536,6480],{"class":2235},[2225,169538,2789],{"class":2239},[2225,169540,2805],{"class":2235},[2225,169542,151113],{"class":2252},[2225,169544,2805],{"class":2235},[2225,169546,2298],{"class":2235},[2225,169548,2236],{"class":2235},[2225,169550,2266],{"class":2307},[2225,169552,2311],{"class":2235},[2225,169554,2249],{"class":2235},[2225,169556,169528],{"class":2252},[2225,169558,2805],{"class":2235},[2225,169560,2243],{"class":2235},[2225,169562,2362],{"class":2239},[2225,169564,169565,169567,169569],{"class":2227,"line":6587},[2225,169566,26235],{"class":2235},[2225,169568,3230],{"class":2291},[2225,169570,8146],{"class":2239},[2225,169572,169573],{"class":2227,"line":7174},[2225,169574,169575],{"class":2763},"\u002F\u002F → [{ ..., client: { id: 'cli-A', name: 'Acme Corp', tier: 'premium' },\n",[2225,169577,169578],{"class":2227,"line":7180},[2225,169579,169580],{"class":2763},"\u002F\u002F      category: { id: 'cat-1', label: 'Consulting' } }]\n",[937,169582,169583,169584,2311],{},"Post-join filtering on either alias's fields uses the general-purpose ",[941,169585,24728],{},[2217,169587,169589],{"className":2219,"code":169588,"language":2221,"meta":1037,"style":1037},"invoices.query()\n  .join\u003C'client', Client>('clientId', { as: 'client' })\n  .filter(row => row.client?.tier === 'premium')\n  .toArray()\n",[941,169590,169591,169601,169647,169680],{"__ignoreMap":1037},[2225,169592,169593,169595,169597,169599],{"class":2227,"line":2228},[2225,169594,6487],{"class":2239},[2225,169596,1520],{"class":2235},[2225,169598,5286],{"class":2291},[2225,169600,8146],{"class":2239},[2225,169602,169603,169605,169607,169609,169611,169613,169615,169617,169619,169621,169623,169625,169627,169629,169631,169633,169635,169637,169639,169641,169643,169645],{"class":2227,"line":1041},[2225,169604,26235],{"class":2235},[2225,169606,63728],{"class":2291},[2225,169608,6474],{"class":2235},[2225,169610,2805],{"class":2235},[2225,169612,9913],{"class":2252},[2225,169614,2805],{"class":2235},[2225,169616,2298],{"class":2235},[2225,169618,54194],{"class":6342},[2225,169620,6480],{"class":2235},[2225,169622,2789],{"class":2239},[2225,169624,2805],{"class":2235},[2225,169626,59721],{"class":2252},[2225,169628,2805],{"class":2235},[2225,169630,2298],{"class":2235},[2225,169632,2236],{"class":2235},[2225,169634,2266],{"class":2307},[2225,169636,2311],{"class":2235},[2225,169638,2249],{"class":2235},[2225,169640,9913],{"class":2252},[2225,169642,2805],{"class":2235},[2225,169644,2243],{"class":2235},[2225,169646,2362],{"class":2239},[2225,169648,169649,169651,169653,169655,169657,169659,169661,169663,169665,169667,169670,169672,169674,169676,169678],{"class":2227,"line":1038},[2225,169650,26235],{"class":2235},[2225,169652,8686],{"class":2291},[2225,169654,2789],{"class":2239},[2225,169656,134393],{"class":7052},[2225,169658,8116],{"class":2774},[2225,169660,134431],{"class":2239},[2225,169662,1520],{"class":2235},[2225,169664,9913],{"class":2239},[2225,169666,8457],{"class":2235},[2225,169668,169669],{"class":2239},"tier ",[2225,169671,33083],{"class":2235},[2225,169673,2249],{"class":2235},[2225,169675,168426],{"class":2252},[2225,169677,2805],{"class":2235},[2225,169679,2362],{"class":2239},[2225,169681,169682,169684,169686],{"class":2227,"line":2288},[2225,169683,26235],{"class":2235},[2225,169685,3230],{"class":2291},[2225,169687,8146],{"class":2239},[1591,169689,169691],{"id":169690},"dangling-references","Dangling references",[937,169693,169694,169696,169697,169699,169700,968,169702,169704,169705,1952,169708,169710],{},[941,169695,62282],{}," declares one of three enforcement modes — ",[941,169698,140597],{}," (default), ",[941,169701,150565],{},[941,169703,10753],{}," — covered in full on ",[1007,169706,681],{"href":169707},".\u002Fforeign-refs#declaring-refs",[941,169709,61808],{}," respects whichever mode the field was declared with when a left record's FK value points at a target id that no longer exists:",[1111,169712,169713,169722,169739],{},[1114,169714,169715,169719,169720,1520],{},[950,169716,169717],{},[941,169718,140597],{}," — throws ",[941,169721,150678],{},[1114,169723,169724,169728,169729,169732,169733,169735,169736,6300],{},[950,169725,169726],{},[941,169727,150565],{}," — attaches ",[941,169730,169731],{},"client: null"," and emits a one-shot ",[941,169734,15856],{}," per unique dangling ",[941,169737,169738],{},"(field, target, id)",[1114,169740,169741,169728,169745,169747],{},[950,169742,169743],{},[941,169744,10753],{},[941,169746,169731],{}," silently (cascade deletes are expected to clean these up; a dangling ref visible at read time is a mid-flight cascade or a pre-existing orphan, not a DSL error).",[2217,169749,169751],{"className":2219,"code":169750,"language":2221,"meta":1037,"style":1037},"const looseInvoices = vault.collection\u003CInvoice>('invoices-loose', {\n  refs: { clientId: ref('clients', 'warn') },\n})\nawait looseInvoices.put('inv-9', { id: 'inv-9', amount: 50, status: 'open', clientId: 'missing' })\n\nconst rows = looseInvoices.query()\n  .join\u003C'client', Client>('clientId', { as: 'client' })\n  .toArray()\n\u002F\u002F → [{ id: 'inv-9', amount: 50, status: 'open', clientId: 'missing', client: null }]\n\u002F\u002F console.warn: one-shot dangling-ref warning for clientId → clients\u002Fmissing\n",[941,169752,169753,169787,169821,169827,169898,169902,169918,169964,169972,169977],{"__ignoreMap":1037},[2225,169754,169755,169757,169760,169762,169764,169766,169768,169770,169772,169774,169776,169778,169781,169783,169785],{"class":2227,"line":2228},[2225,169756,2775],{"class":2774},[2225,169758,169759],{"class":2239}," looseInvoices ",[2225,169761,2781],{"class":2235},[2225,169763,6466],{"class":2239},[2225,169765,1520],{"class":2235},[2225,169767,6471],{"class":2291},[2225,169769,6474],{"class":2235},[2225,169771,6477],{"class":6342},[2225,169773,6480],{"class":2235},[2225,169775,2789],{"class":2239},[2225,169777,2805],{"class":2235},[2225,169779,169780],{"class":2252},"invoices-loose",[2225,169782,2805],{"class":2235},[2225,169784,2298],{"class":2235},[2225,169786,2301],{"class":2235},[2225,169788,169789,169791,169793,169795,169797,169799,169801,169803,169805,169807,169809,169811,169813,169815,169817,169819],{"class":2227,"line":1041},[2225,169790,62642],{"class":2307},[2225,169792,2311],{"class":2235},[2225,169794,2236],{"class":2235},[2225,169796,62649],{"class":2307},[2225,169798,2311],{"class":2235},[2225,169800,62303],{"class":2291},[2225,169802,2789],{"class":2239},[2225,169804,2805],{"class":2235},[2225,169806,23926],{"class":2252},[2225,169808,2805],{"class":2235},[2225,169810,2298],{"class":2235},[2225,169812,2249],{"class":2235},[2225,169814,150565],{"class":2252},[2225,169816,2805],{"class":2235},[2225,169818,8495],{"class":2239},[2225,169820,19008],{"class":2235},[2225,169822,169823,169825],{"class":2227,"line":1038},[2225,169824,2359],{"class":2235},[2225,169826,2362],{"class":2239},[2225,169828,169829,169831,169834,169836,169838,169840,169842,169845,169847,169849,169851,169853,169855,169857,169859,169861,169863,169865,169867,169869,169871,169873,169875,169877,169879,169881,169883,169885,169887,169889,169892,169894,169896],{"class":2227,"line":2288},[2225,169830,6500],{"class":2231},[2225,169832,169833],{"class":2239}," looseInvoices",[2225,169835,1520],{"class":2235},[2225,169837,3161],{"class":2291},[2225,169839,2789],{"class":2239},[2225,169841,2805],{"class":2235},[2225,169843,169844],{"class":2252},"inv-9",[2225,169846,2805],{"class":2235},[2225,169848,2298],{"class":2235},[2225,169850,2236],{"class":2235},[2225,169852,7070],{"class":2307},[2225,169854,2311],{"class":2235},[2225,169856,2249],{"class":2235},[2225,169858,169844],{"class":2252},[2225,169860,2805],{"class":2235},[2225,169862,2298],{"class":2235},[2225,169864,6351],{"class":2307},[2225,169866,2311],{"class":2235},[2225,169868,37210],{"class":6527},[2225,169870,2298],{"class":2235},[2225,169872,6361],{"class":2307},[2225,169874,2311],{"class":2235},[2225,169876,2249],{"class":2235},[2225,169878,26178],{"class":2252},[2225,169880,2805],{"class":2235},[2225,169882,2298],{"class":2235},[2225,169884,62649],{"class":2307},[2225,169886,2311],{"class":2235},[2225,169888,2249],{"class":2235},[2225,169890,169891],{"class":2252},"missing",[2225,169893,2805],{"class":2235},[2225,169895,2243],{"class":2235},[2225,169897,2362],{"class":2239},[2225,169899,169900],{"class":2227,"line":2304},[2225,169901,2285],{"emptyLinePlaceholder":2284},[2225,169903,169904,169906,169908,169910,169912,169914,169916],{"class":2227,"line":2356},[2225,169905,2775],{"class":2774},[2225,169907,42003],{"class":2239},[2225,169909,2781],{"class":2235},[2225,169911,169833],{"class":2239},[2225,169913,1520],{"class":2235},[2225,169915,5286],{"class":2291},[2225,169917,8146],{"class":2239},[2225,169919,169920,169922,169924,169926,169928,169930,169932,169934,169936,169938,169940,169942,169944,169946,169948,169950,169952,169954,169956,169958,169960,169962],{"class":2227,"line":2830},[2225,169921,26235],{"class":2235},[2225,169923,63728],{"class":2291},[2225,169925,6474],{"class":2235},[2225,169927,2805],{"class":2235},[2225,169929,9913],{"class":2252},[2225,169931,2805],{"class":2235},[2225,169933,2298],{"class":2235},[2225,169935,54194],{"class":6342},[2225,169937,6480],{"class":2235},[2225,169939,2789],{"class":2239},[2225,169941,2805],{"class":2235},[2225,169943,59721],{"class":2252},[2225,169945,2805],{"class":2235},[2225,169947,2298],{"class":2235},[2225,169949,2236],{"class":2235},[2225,169951,2266],{"class":2307},[2225,169953,2311],{"class":2235},[2225,169955,2249],{"class":2235},[2225,169957,9913],{"class":2252},[2225,169959,2805],{"class":2235},[2225,169961,2243],{"class":2235},[2225,169963,2362],{"class":2239},[2225,169965,169966,169968,169970],{"class":2227,"line":2861},[2225,169967,26235],{"class":2235},[2225,169969,3230],{"class":2291},[2225,169971,8146],{"class":2239},[2225,169973,169974],{"class":2227,"line":4246},[2225,169975,169976],{"class":2763},"\u002F\u002F → [{ id: 'inv-9', amount: 50, status: 'open', clientId: 'missing', client: null }]\n",[2225,169978,169979],{"class":2227,"line":4253},[2225,169980,169981],{"class":2763},"\u002F\u002F console.warn: one-shot dangling-ref warning for clientId → clients\u002Fmissing\n",[937,169983,169984,169985,4688,169987,169989],{},"A FK field that is ",[941,169986,6651],{},[941,169988,18316],{}," is not a dangling ref — it's \"no reference at all\", and is always allowed regardless of mode.",[1591,169991,169993],{"id":169992},"reactive-joins","Reactive joins",[937,169995,169996,22207,169999,170001,170002,170005,170006,170008],{},[941,169997,169998],{},"query.live()",[941,170000,42173],{}," that re-runs the query and updates ",[941,170003,170004],{},".value"," whenever a source changes. For a joined query it subscribes to the left collection ",[950,170007,14128],{}," every join target, deduplicated by collection name — so a client update re-fires an invoice query that joined that client:",[2217,170010,170012],{"className":2219,"code":170011,"language":2221,"meta":1037,"style":1037},"const live = await invoices.query()\n  .join\u003C'client', Client>('clientId', { as: 'client' })\n  .live()\n\nlive.subscribe(() => render(live.value))\n\nawait clients.put('cli-A', { id: 'cli-A', name: 'Acme Corporation', tier: 'premium' })\n\u002F\u002F → live.value[0].client.name is now \"Acme Corporation\" — the right-side put re-fired the query\n\nlive.stop()\n",[941,170013,170014,170032,170078,170086,170090,170115,170119,170180,170185,170189],{"__ignoreMap":1037},[2225,170015,170016,170018,170020,170022,170024,170026,170028,170030],{"class":2227,"line":2228},[2225,170017,2775],{"class":2774},[2225,170019,134114],{"class":2239},[2225,170021,2781],{"class":2235},[2225,170023,2784],{"class":2231},[2225,170025,6503],{"class":2239},[2225,170027,1520],{"class":2235},[2225,170029,5286],{"class":2291},[2225,170031,8146],{"class":2239},[2225,170033,170034,170036,170038,170040,170042,170044,170046,170048,170050,170052,170054,170056,170058,170060,170062,170064,170066,170068,170070,170072,170074,170076],{"class":2227,"line":1041},[2225,170035,26235],{"class":2235},[2225,170037,63728],{"class":2291},[2225,170039,6474],{"class":2235},[2225,170041,2805],{"class":2235},[2225,170043,9913],{"class":2252},[2225,170045,2805],{"class":2235},[2225,170047,2298],{"class":2235},[2225,170049,54194],{"class":6342},[2225,170051,6480],{"class":2235},[2225,170053,2789],{"class":2239},[2225,170055,2805],{"class":2235},[2225,170057,59721],{"class":2252},[2225,170059,2805],{"class":2235},[2225,170061,2298],{"class":2235},[2225,170063,2236],{"class":2235},[2225,170065,2266],{"class":2307},[2225,170067,2311],{"class":2235},[2225,170069,2249],{"class":2235},[2225,170071,9913],{"class":2252},[2225,170073,2805],{"class":2235},[2225,170075,2243],{"class":2235},[2225,170077,2362],{"class":2239},[2225,170079,170080,170082,170084],{"class":2227,"line":1038},[2225,170081,26235],{"class":2235},[2225,170083,727],{"class":2291},[2225,170085,8146],{"class":2239},[2225,170087,170088],{"class":2227,"line":2288},[2225,170089,2285],{"emptyLinePlaceholder":2284},[2225,170091,170092,170094,170096,170098,170100,170102,170104,170107,170110,170112],{"class":2227,"line":2304},[2225,170093,727],{"class":2239},[2225,170095,1520],{"class":2235},[2225,170097,25042],{"class":2291},[2225,170099,2789],{"class":2239},[2225,170101,4228],{"class":2235},[2225,170103,8116],{"class":2774},[2225,170105,170106],{"class":2291}," render",[2225,170108,170109],{"class":2239},"(live",[2225,170111,1520],{"class":2235},[2225,170113,170114],{"class":2239},"value))\n",[2225,170116,170117],{"class":2227,"line":2356},[2225,170118,2285],{"emptyLinePlaceholder":2284},[2225,170120,170121,170123,170125,170127,170129,170131,170133,170135,170137,170139,170141,170143,170145,170147,170149,170151,170153,170155,170157,170159,170162,170164,170166,170168,170170,170172,170174,170176,170178],{"class":2227,"line":2830},[2225,170122,6500],{"class":2231},[2225,170124,62689],{"class":2239},[2225,170126,1520],{"class":2235},[2225,170128,3161],{"class":2291},[2225,170130,2789],{"class":2239},[2225,170132,2805],{"class":2235},[2225,170134,168715],{"class":2252},[2225,170136,2805],{"class":2235},[2225,170138,2298],{"class":2235},[2225,170140,2236],{"class":2235},[2225,170142,7070],{"class":2307},[2225,170144,2311],{"class":2235},[2225,170146,2249],{"class":2235},[2225,170148,168715],{"class":2252},[2225,170150,2805],{"class":2235},[2225,170152,2298],{"class":2235},[2225,170154,8104],{"class":2307},[2225,170156,2311],{"class":2235},[2225,170158,2249],{"class":2235},[2225,170160,170161],{"class":2252},"Acme Corporation",[2225,170163,2805],{"class":2235},[2225,170165,2298],{"class":2235},[2225,170167,168410],{"class":2307},[2225,170169,2311],{"class":2235},[2225,170171,2249],{"class":2235},[2225,170173,168426],{"class":2252},[2225,170175,2805],{"class":2235},[2225,170177,2243],{"class":2235},[2225,170179,2362],{"class":2239},[2225,170181,170182],{"class":2227,"line":2861},[2225,170183,170184],{"class":2763},"\u002F\u002F → live.value[0].client.name is now \"Acme Corporation\" — the right-side put re-fired the query\n",[2225,170186,170187],{"class":2227,"line":4246},[2225,170188,2285],{"emptyLinePlaceholder":2284},[2225,170190,170191,170193,170195,170197],{"class":2227,"line":4253},[2225,170192,727],{"class":2239},[2225,170194,1520],{"class":2235},[2225,170196,27604],{"class":2291},[2225,170198,8146],{"class":2239},[937,170200,15432,170201,170203,170204,170206,170207,170210,170211,170213,170214,170216,170217,134102],{},[941,170202,140597],{}," mode, a right-side delete that leaves a dangling ref makes the re-run throw; ",[941,170205,42173],{}," catches it and stores it on ",[941,170208,170209],{},".error"," instead of tearing down the subscription — check ",[941,170212,170209],{}," after each notification. Always call ",[941,170215,64363],{}," when you're done; it tears down every upstream subscription. See ",[1007,170218,727],{"href":134101},[1022,170220,101012],{"id":41259},[1591,170222,170224],{"id":170223},"queryjoinfield-opts",[941,170225,170226],{},"query.join(field, opts)",[2217,170228,170230],{"className":2219,"code":170229,"language":2221,"meta":1037,"style":1037},"join\u003CAs extends string, Right = unknown>(\n  field: string,\n  opts: { as: As; strategy?: 'hash' | 'nested'; maxRows?: number }\n): Query\u003CT & Record\u003CAs, Right | null>>\n",[941,170231,170232,170254,170260,170300],{"__ignoreMap":1037},[2225,170233,170234,170236,170238,170241,170243,170246,170248,170250,170252],{"class":2227,"line":2228},[2225,170235,63728],{"class":2239},[2225,170237,6474],{"class":2235},[2225,170239,170240],{"class":2239},"As extends string",[2225,170242,2298],{"class":2235},[2225,170244,170245],{"class":2239}," Right ",[2225,170247,2781],{"class":2235},[2225,170249,40717],{"class":2239},[2225,170251,6480],{"class":2235},[2225,170253,11683],{"class":2239},[2225,170255,170256,170258],{"class":2227,"line":1041},[2225,170257,165122],{"class":2239},[2225,170259,1707],{"class":2235},[2225,170261,170262,170265,170267,170269,170271,170274,170276,170278,170280,170282,170284,170286,170289,170291,170294,170296,170298],{"class":2227,"line":1038},[2225,170263,170264],{"class":2239},"  opts: ",[2225,170266,2792],{"class":2235},[2225,170268,2266],{"class":2307},[2225,170270,2311],{"class":2235},[2225,170272,170273],{"class":2239}," As; strategy",[2225,170275,7011],{"class":2235},[2225,170277,2249],{"class":2235},[2225,170279,134337],{"class":2252},[2225,170281,2805],{"class":2235},[2225,170283,7087],{"class":2235},[2225,170285,2249],{"class":2235},[2225,170287,170288],{"class":2252},"nested",[2225,170290,2805],{"class":2235},[2225,170292,170293],{"class":2239},"; maxRows",[2225,170295,7011],{"class":2235},[2225,170297,24854],{"class":2239},[2225,170299,7351],{"class":2235},[2225,170301,170302,170305,170307,170309,170311,170313,170315,170318,170320,170322,170324],{"class":2227,"line":2288},[2225,170303,170304],{"class":2239},"): Query",[2225,170306,6474],{"class":2235},[2225,170308,24015],{"class":2239},[2225,170310,16318],{"class":2235},[2225,170312,22527],{"class":2239},[2225,170314,6474],{"class":2235},[2225,170316,170317],{"class":2239},"As",[2225,170319,2298],{"class":2235},[2225,170321,170245],{"class":2239},[2225,170323,18307],{"class":2235},[2225,170325,24645],{"class":2235},[937,170327,170328,170329,170331,170332,170334,170335,170338,170339,170341,170342,170344,170345,170347,170348,170351],{},"Resolves the ",[941,170330,62282],{}," declared on ",[941,170333,25197],{}," and attaches the target record under ",[941,170336,170337],{},"opts.as",". Returns a new immutable ",[941,170340,46896],{}," — the original is unchanged. Throws if the query wasn't built via ",[941,170343,25801],{}," (no join context), or if ",[941,170346,25197],{}," has no matching ",[941,170349,170350],{},"refs: { [field]: ref(...) }"," declaration.",[2217,170353,170355],{"className":2219,"code":170354,"language":2221,"meta":1037,"style":1037},"const rows = invoices.query().join\u003C'client', Client>('clientId', { as: 'client' }).toArray()\n\u002F\u002F → [{ ...invoice, client: Client | null }, ...]\n",[941,170356,170357,170423],{"__ignoreMap":1037},[2225,170358,170359,170361,170363,170365,170367,170369,170371,170373,170375,170377,170379,170381,170383,170385,170387,170389,170391,170393,170395,170397,170399,170401,170403,170405,170407,170409,170411,170413,170415,170417,170419,170421],{"class":2227,"line":2228},[2225,170360,2775],{"class":2774},[2225,170362,42003],{"class":2239},[2225,170364,2781],{"class":2235},[2225,170366,6503],{"class":2239},[2225,170368,1520],{"class":2235},[2225,170370,5286],{"class":2291},[2225,170372,4228],{"class":2239},[2225,170374,1520],{"class":2235},[2225,170376,63728],{"class":2291},[2225,170378,6474],{"class":2235},[2225,170380,2805],{"class":2235},[2225,170382,9913],{"class":2252},[2225,170384,2805],{"class":2235},[2225,170386,2298],{"class":2235},[2225,170388,54194],{"class":6342},[2225,170390,6480],{"class":2235},[2225,170392,2789],{"class":2239},[2225,170394,2805],{"class":2235},[2225,170396,59721],{"class":2252},[2225,170398,2805],{"class":2235},[2225,170400,2298],{"class":2235},[2225,170402,2236],{"class":2235},[2225,170404,2266],{"class":2307},[2225,170406,2311],{"class":2235},[2225,170408,2249],{"class":2235},[2225,170410,9913],{"class":2252},[2225,170412,2805],{"class":2235},[2225,170414,2243],{"class":2235},[2225,170416,1381],{"class":2239},[2225,170418,1520],{"class":2235},[2225,170420,3230],{"class":2291},[2225,170422,8146],{"class":2239},[2225,170424,170425],{"class":2227,"line":1041},[2225,170426,170427],{"class":2763},"\u002F\u002F → [{ ...invoice, client: Client | null }, ...]\n",[1338,170429,170430,170442],{},[1341,170431,170432],{},[1344,170433,170434,170436,170438,170440],{},[1347,170435,101060],{},[1347,170437,20194],{},[1347,170439,20197],{},[1347,170441,7834],{},[1357,170443,170444,170459,170476],{},[1344,170445,170446,170450,170454,170456],{},[1362,170447,170448],{},[941,170449,4899],{},[1362,170451,170452],{},[941,170453,7259],{},[1362,170455,45375],{},[1362,170457,170458],{},"Field name the joined record is attached under.",[1344,170460,170461,170465,170470,170473],{},[1362,170462,170463],{},[941,170464,21111],{},[1362,170466,170467],{},[941,170468,170469],{},"'hash' | 'nested'",[1362,170471,170472],{},"auto-selected",[1362,170474,170475],{},"Force the join planner's strategy; mainly useful for tests.",[1344,170477,170478,170482,170486,170495],{},[1362,170479,170480],{},[941,170481,134878],{},[1362,170483,170484],{},[941,170485,20636],{},[1362,170487,170488,953,170490,106932,170493,1381],{},[941,170489,134887],{},[941,170491,170492],{},"DEFAULT_JOIN_MAX_ROWS",[941,170494,1086],{},[1362,170496,170497],{},"Per-leg row ceiling — see below.",[937,170499,169129,170500,170502],{},[941,170501,61808],{}," again to resolve additional FKs on the same left record — each call adds one alias to the result type.",[1591,170504,120265],{"id":120264},[937,170506,134963,170507,2311],{},[941,170508,1086],{},[1111,170510,170511,170535],{},[1114,170512,170513,2104,170517,170520,170521,953,170524,1884,170527,968,170530,968,170533,1520],{},[950,170514,170515],{},[941,170516,65215],{},[941,170518,170519],{},"JOIN_TOO_LARGE",") — either side of a join leg exceeds the row ceiling. Carries ",[941,170522,170523],{},"side",[941,170525,170526],{},"'left' | 'right'",[941,170528,170529],{},"leftRows",[941,170531,170532],{},"rightRows",[941,170534,134878],{},[1114,170536,170537,2104,170541,170544,170545,170547,170548,968,170550,968,170552,1520],{},[950,170538,170539],{},[941,170540,150678],{},[941,170542,170543],{},"DANGLING_REFERENCE",") — a ",[941,170546,140597],{},"-mode ref points at a target id that doesn't exist. Carries ",[941,170549,25197],{},[941,170551,134984],{},[941,170553,150733],{},[1022,170555,68210],{"id":68209},[1111,170557,170558,170568,170578,170594,170607],{},[1114,170559,170560,1324,170562,170564,170565,170567],{},[950,170561,135020],{},[941,170563,65215],{}," fires at 50,000 rows per side by default; override per-call with ",[941,170566,65219],{}," if you've measured the query is safe larger.",[1114,170569,170570,170572,170573,170575,170576,957],{},[950,170571,135052],{}," A join target must be reachable in the same vault; there is no cross-vault join — use ",[941,170574,27688],{}," for that (see ",[1007,170577,135061],{"href":83069},[1114,170579,170580,1324,170585,170588,170589,170347,170591,170593],{},[950,170581,170582,170584],{},[941,170583,62282],{}," required.",[941,170586,170587],{},".join(field, ...)"," throws if ",[941,170590,25197],{},[941,170592,170350],{}," declaration on the left collection — it does not infer relationships.",[1114,170595,170596,170599,170600,170603,170604,170606],{},[950,170597,170598],{},"Partition-awareness is plumbed but dormant."," Every join leg internally carries a ",[941,170601,170602],{},"partitionScope"," field that is always ",[941,170605,163113],{}," today; the executor doesn't read it yet.",[1114,170608,170609,170616,170617,170620],{},[950,170610,170611,53922,170613,170615],{},[941,170612,3027],{},[941,170614,140597],{},"-mode joins can error."," A right-side delete that leaves a dangling ref makes the live re-run throw; the error surfaces on ",[941,170618,170619],{},"LiveQuery.error"," rather than propagating out of the change handler.",[1022,170622,2621],{"id":2620},[1111,170624,170625,170633,170638,170643,170648,170655,170667],{},[1114,170626,170627,170629,170630,170632],{},[1007,170628,681],{"href":168276}," — declaring ",[941,170631,62282],{}," and the three enforcement modes",[1114,170634,170635,170637],{},[1007,170636,657],{"href":168304}," — pairing rows with no declared reference between them",[1114,170639,170640,170642],{},[1007,170641,705],{"href":139528}," — the indexed nested-loop join strategy uses a collection's index on the join field",[1114,170644,170645,170647],{},[1007,170646,4449],{"href":123485}," — joined rows can be grouped and aggregated",[1114,170649,170650,170652,170653,135137],{},[1007,170651,727],{"href":134101}," — the reactive primitive ",[941,170654,3027],{},[1114,170656,170657,964,170659,1261,170661,1261,170663,997,170665],{},[1007,170658,3216],{"href":83069},[941,170660,3016],{},[941,170662,8686],{},[941,170664,3019],{},[941,170666,27688],{},[1114,170668,101581,170669,1520],{},[1007,170670,170672],{"href":92641,"rel":170671},[3371],[941,170673,170674],{},"12-with-joins",[2645,170676,170677],{},"html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html pre.shiki code .sHdIc, html code.shiki .sHdIc{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#EEFFFF;--shiki-default-font-style:italic;--shiki-dark:#BABED8;--shiki-dark-font-style:italic}",{"title":1037,"searchDepth":1038,"depth":1038,"links":170679},[170680,170681,170682,170688,170692,170693],{"id":29856,"depth":1041,"text":29857},{"id":99863,"depth":1041,"text":99864},{"id":29892,"depth":1041,"text":29893,"children":170683},[170684,170685,170686,170687],{"id":168677,"depth":1038,"text":168678},{"id":169125,"depth":1038,"text":169126},{"id":169690,"depth":1038,"text":169691},{"id":169992,"depth":1038,"text":169993},{"id":41259,"depth":1041,"text":101012,"children":170689},[170690,170691],{"id":170223,"depth":1038,"text":170226},{"id":120264,"depth":1038,"text":120265},{"id":68209,"depth":1041,"text":68210},{"id":2620,"depth":1041,"text":2621},"Eager foreign-key joins in the query DSL: attach a referenced record under an alias, chain joins, and keep the result live as either side changes.",{},{"title":715,"description":170694},"bNxiSmjDQ7viYAL7b__CUilATQ9mx6fqZymjVo-gaDQ",{"id":170699,"title":719,"access":12,"apiToc":929,"body":170700,"category":1042,"description":172073,"extension":1044,"meta":172074,"navigation":1042,"nest":1047,"path":720,"seo":172075,"sinceVersion":1042,"status":1049,"stem":721,"subtitle":1042,"tier":1050,"tocExternal":1042,"__hash__":172076},"docs\u002Fservices\u002Flazy.md",{"type":931,"value":170701,"toc":172052},[170702,170704,170707,170709,170717,170741,170760,170762,170789,170791,171074,171090,171092,171096,171103,171254,171258,171267,171348,171352,171360,171382,171480,171498,171526,171533,171536,171592,171594,171599,171615,171623,171629,171640,171694,171699,171705,171808,171814,171881,171890,171892,171917,171919,172004,172006,172049],[934,170703,719],{"id":719},[46871,170705],{":rows":170706},"[{\"label\":\"Subpath\",\"value\":\"`@noy-db\u002Fhub\u002Flazy`\"},{\"label\":\"Factory\",\"value\":\"`withLazy()`\"},{\"label\":\"Layer\",\"value\":\"`with-store`\"}]",[1022,170708,29857],{"id":29856},[937,170710,170711,170712,7356,170714,170716],{},"By default, a noy-db ",[1007,170713,6471],{"href":58262},[950,170715,138257],{},": opening it\ndecrypts every record into an in-process cache, and reads\u002Fwrites run off that cache afterward. That works well up to\nroughly 1K–50K records per collection. Beyond that, keeping the whole collection in memory\nwastes RAM you'll never use — most sessions only ever touch a handful of records.",[937,170718,61899,170719,170721,170722,170724,170725,170728,170729,170731,170732,170734,170735,170737,170738,170740],{},[941,170720,6836],{},") skips the bulk load. Every read goes per-record to the\n",[1007,170723,6951],{"href":51}," and lands in a small, size-bounded ",[950,170726,170727],{},"LRU"," (least-recently-used)\ncache, so a lazy collection's memory footprint never grows past the budget you declare — old\nentries are evicted to make room for new ones. The ",[941,170730,719],{}," service supplies that bounded\ncache: opt in with ",[941,170733,22060],{}," and the collection's working set is capped by ",[941,170736,64485],{},"\nand\u002For ",[941,170739,64488],{},", whichever you set.",[937,170742,170743,170744,997,170746,170748,170749,170751,170752,170754,170755,170757,170758,33515],{},"The tradeoff is that bulk reads are unavailable — ",[941,170745,24407],{},[941,170747,24715],{}," throw in lazy mode,\nbecause materializing the whole collection defeats the point of a bounded cache. Use\nper-id ",[941,170750,14639],{},", the paged ",[941,170753,7906],{},", or (with ",[1007,170756,705],{"href":706}," enabled)\n",[941,170759,24807],{},[1022,170761,70093],{"id":70092},[1111,170763,170764,170779],{},[1114,170765,170766,170773,170774,1261,170776,170778],{},[950,170767,170768,6837,170770,170772],{},[941,170769,6836],{},[941,170771,6840],{}," budget declared per collection"," — at least one of ",[941,170775,64485],{},[941,170777,64488],{},"; the factory alone changes nothing until a collection opts in.",[1114,170780,170781,1324,170784,170786,170787,1520],{},[1092,170782,170783],{},"For indexed reads over a lazy collection:",[941,170785,137930],{}," with the queried fields declared — see ",[1007,170788,705],{"href":706},[1022,170790,99864],{"id":99863},[2217,170792,170794],{"className":2219,"code":170793,"language":2221,"meta":1037,"style":1037},"import { createNoydb } from '@noy-db\u002Fhub'\nimport { withLazy } from '@noy-db\u002Fhub\u002Flazy'\nimport { toMemory } from '@noy-db\u002Fto-memory'\n\nconst db = await createNoydb({\n  store: toMemory(),\n  user: 'owner',\n  secret: 'correct horse battery staple',\n  lazyStrategy: withLazy(),\n})\n\nconst vault = await db.openVault('books')\n\ninterface Disbursement {\n  id: string\n  clientId: string\n  amount: number\n}\n\nconst disbursements = vault.collection\u003CDisbursement>('disbursements', {\n  prefetch: false,                    \u002F\u002F opt this collection into lazy mode\n  cache: { maxRecords: 1000 },        \u002F\u002F required: bound the LRU working set\n})\n",[941,170795,170796,170814,170832,170850,170854,170870,170882,170896,170910,170922,170928,170932,170958,170962,170971,170979,170987,170995,170999,171003,171036,171049,171068],{"__ignoreMap":1037},[2225,170797,170798,170800,170802,170804,170806,170808,170810,170812],{"class":2227,"line":2228},[2225,170799,2232],{"class":2231},[2225,170801,2236],{"class":2235},[2225,170803,2744],{"class":2239},[2225,170805,2243],{"class":2235},[2225,170807,2246],{"class":2231},[2225,170809,2249],{"class":2235},[2225,170811,1086],{"class":2252},[2225,170813,2256],{"class":2235},[2225,170815,170816,170818,170820,170822,170824,170826,170828,170830],{"class":2227,"line":1041},[2225,170817,2232],{"class":2231},[2225,170819,2236],{"class":2235},[2225,170821,62014],{"class":2239},[2225,170823,2243],{"class":2235},[2225,170825,2246],{"class":2231},[2225,170827,2249],{"class":2235},[2225,170829,62023],{"class":2252},[2225,170831,2256],{"class":2235},[2225,170833,170834,170836,170838,170840,170842,170844,170846,170848],{"class":2227,"line":1038},[2225,170835,2232],{"class":2231},[2225,170837,2236],{"class":2235},[2225,170839,68576],{"class":2239},[2225,170841,2243],{"class":2235},[2225,170843,2246],{"class":2231},[2225,170845,2249],{"class":2235},[2225,170847,9638],{"class":2252},[2225,170849,2256],{"class":2235},[2225,170851,170852],{"class":2227,"line":2288},[2225,170853,2285],{"emptyLinePlaceholder":2284},[2225,170855,170856,170858,170860,170862,170864,170866,170868],{"class":2227,"line":2304},[2225,170857,2775],{"class":2774},[2225,170859,2778],{"class":2239},[2225,170861,2781],{"class":2235},[2225,170863,2784],{"class":2231},[2225,170865,2744],{"class":2291},[2225,170867,2789],{"class":2239},[2225,170869,4200],{"class":2235},[2225,170871,170872,170874,170876,170878,170880],{"class":2227,"line":2356},[2225,170873,15608],{"class":2307},[2225,170875,2311],{"class":2235},[2225,170877,68576],{"class":2291},[2225,170879,4228],{"class":2239},[2225,170881,1707],{"class":2235},[2225,170883,170884,170886,170888,170890,170892,170894],{"class":2227,"line":2830},[2225,170885,4205],{"class":2307},[2225,170887,2311],{"class":2235},[2225,170889,2249],{"class":2235},[2225,170891,9898],{"class":2252},[2225,170893,2805],{"class":2235},[2225,170895,1707],{"class":2235},[2225,170897,170898,170900,170902,170904,170906,170908],{"class":2227,"line":2861},[2225,170899,10423],{"class":2307},[2225,170901,2311],{"class":2235},[2225,170903,2249],{"class":2235},[2225,170905,40260],{"class":2252},[2225,170907,2805],{"class":2235},[2225,170909,1707],{"class":2235},[2225,170911,170912,170914,170916,170918,170920],{"class":2227,"line":4246},[2225,170913,62215],{"class":2307},[2225,170915,2311],{"class":2235},[2225,170917,62014],{"class":2291},[2225,170919,4228],{"class":2239},[2225,170921,1707],{"class":2235},[2225,170923,170924,170926],{"class":2227,"line":4253},[2225,170925,2359],{"class":2235},[2225,170927,2362],{"class":2239},[2225,170929,170930],{"class":2227,"line":4259},[2225,170931,2285],{"emptyLinePlaceholder":2284},[2225,170933,170934,170936,170938,170940,170942,170944,170946,170948,170950,170952,170954,170956],{"class":2227,"line":6548},[2225,170935,2775],{"class":2774},[2225,170937,2835],{"class":2239},[2225,170939,2781],{"class":2235},[2225,170941,2784],{"class":2231},[2225,170943,2842],{"class":2239},[2225,170945,1520],{"class":2235},[2225,170947,2847],{"class":2291},[2225,170949,2789],{"class":2239},[2225,170951,2805],{"class":2235},[2225,170953,65761],{"class":2252},[2225,170955,2805],{"class":2235},[2225,170957,2362],{"class":2239},[2225,170959,170960],{"class":2227,"line":6576},[2225,170961,2285],{"emptyLinePlaceholder":2284},[2225,170963,170964,170966,170969],{"class":2227,"line":6582},[2225,170965,6994],{"class":2774},[2225,170967,170968],{"class":6342}," Disbursement",[2225,170970,2301],{"class":2235},[2225,170972,170973,170975,170977],{"class":2227,"line":6587},[2225,170974,28371],{"class":2307},[2225,170976,2311],{"class":2235},[2225,170978,7014],{"class":6342},[2225,170980,170981,170983,170985],{"class":2227,"line":7174},[2225,170982,58854],{"class":2307},[2225,170984,2311],{"class":2235},[2225,170986,7014],{"class":6342},[2225,170988,170989,170991,170993],{"class":2227,"line":7180},[2225,170990,28389],{"class":2307},[2225,170992,2311],{"class":2235},[2225,170994,12024],{"class":6342},[2225,170996,170997],{"class":2227,"line":7220},[2225,170998,7351],{"class":2235},[2225,171000,171001],{"class":2227,"line":7225},[2225,171002,2285],{"emptyLinePlaceholder":2284},[2225,171004,171005,171007,171010,171012,171014,171016,171018,171020,171022,171024,171026,171028,171030,171032,171034],{"class":2227,"line":7231},[2225,171006,2775],{"class":2774},[2225,171008,171009],{"class":2239}," disbursements ",[2225,171011,2781],{"class":2235},[2225,171013,6466],{"class":2239},[2225,171015,1520],{"class":2235},[2225,171017,6471],{"class":2291},[2225,171019,6474],{"class":2235},[2225,171021,154427],{"class":6342},[2225,171023,6480],{"class":2235},[2225,171025,2789],{"class":2239},[2225,171027,2805],{"class":2235},[2225,171029,154444],{"class":2252},[2225,171031,2805],{"class":2235},[2225,171033,2298],{"class":2235},[2225,171035,2301],{"class":2235},[2225,171037,171038,171040,171042,171044,171046],{"class":2227,"line":7267},[2225,171039,64535],{"class":2307},[2225,171041,2311],{"class":2235},[2225,171043,2341],{"class":2321},[2225,171045,2298],{"class":2235},[2225,171047,171048],{"class":2763},"                    \u002F\u002F opt this collection into lazy mode\n",[2225,171050,171051,171053,171055,171057,171059,171061,171063,171065],{"class":2227,"line":7272},[2225,171052,64549],{"class":2307},[2225,171054,2311],{"class":2235},[2225,171056,2236],{"class":2235},[2225,171058,64556],{"class":2307},[2225,171060,2311],{"class":2235},[2225,171062,26290],{"class":6527},[2225,171064,10965],{"class":2235},[2225,171066,171067],{"class":2763},"        \u002F\u002F required: bound the LRU working set\n",[2225,171069,171070,171072],{"class":2227,"line":7278},[2225,171071,2359],{"class":2235},[2225,171073,2362],{"class":2239},[937,171075,171076,171078,171079,171081,171082,6837,171084,171086,171087,171089],{},[941,171077,64481],{}," is set once on ",[941,171080,12780],{},"; each collection then opts in\nindividually with ",[941,171083,6836],{},[941,171085,6840],{}," budget. Collections left at the default\n",[941,171088,164144],{}," are unaffected.",[1022,171091,29893],{"id":29892},[1591,171093,171095],{"id":171094},"per-record-reads","Per-record reads",[937,171097,171098,997,171100,171102],{},[941,171099,14639],{},[941,171101,20953],{}," work exactly as in eager mode — they just read\u002Fwrite through the store\ninstead of an in-memory copy, populating the LRU as they go:",[2217,171104,171106],{"className":2219,"code":171105,"language":2221,"meta":1037,"style":1037},"await disbursements.put('d-1', { id: 'd-1', clientId: 'c-42', amount: 1200 })\nawait disbursements.put('d-2', { id: 'd-2', clientId: 'c-7', amount: 300 })\n\nawait disbursements.get('d-1')\n\u002F\u002F → { id: 'd-1', clientId: 'c-42', amount: 1200 }\n",[941,171107,171108,171166,171225,171229,171249],{"__ignoreMap":1037},[2225,171109,171110,171112,171115,171117,171119,171121,171123,171126,171128,171130,171132,171134,171136,171138,171140,171142,171144,171146,171148,171150,171152,171154,171156,171158,171160,171162,171164],{"class":2227,"line":2228},[2225,171111,6500],{"class":2231},[2225,171113,171114],{"class":2239}," disbursements",[2225,171116,1520],{"class":2235},[2225,171118,3161],{"class":2291},[2225,171120,2789],{"class":2239},[2225,171122,2805],{"class":2235},[2225,171124,171125],{"class":2252},"d-1",[2225,171127,2805],{"class":2235},[2225,171129,2298],{"class":2235},[2225,171131,2236],{"class":2235},[2225,171133,7070],{"class":2307},[2225,171135,2311],{"class":2235},[2225,171137,2249],{"class":2235},[2225,171139,171125],{"class":2252},[2225,171141,2805],{"class":2235},[2225,171143,2298],{"class":2235},[2225,171145,62649],{"class":2307},[2225,171147,2311],{"class":2235},[2225,171149,2249],{"class":2235},[2225,171151,164547],{"class":2252},[2225,171153,2805],{"class":2235},[2225,171155,2298],{"class":2235},[2225,171157,6351],{"class":2307},[2225,171159,2311],{"class":2235},[2225,171161,136139],{"class":6527},[2225,171163,2243],{"class":2235},[2225,171165,2362],{"class":2239},[2225,171167,171168,171170,171172,171174,171176,171178,171180,171183,171185,171187,171189,171191,171193,171195,171197,171199,171201,171203,171205,171207,171210,171212,171214,171216,171218,171221,171223],{"class":2227,"line":1041},[2225,171169,6500],{"class":2231},[2225,171171,171114],{"class":2239},[2225,171173,1520],{"class":2235},[2225,171175,3161],{"class":2291},[2225,171177,2789],{"class":2239},[2225,171179,2805],{"class":2235},[2225,171181,171182],{"class":2252},"d-2",[2225,171184,2805],{"class":2235},[2225,171186,2298],{"class":2235},[2225,171188,2236],{"class":2235},[2225,171190,7070],{"class":2307},[2225,171192,2311],{"class":2235},[2225,171194,2249],{"class":2235},[2225,171196,171182],{"class":2252},[2225,171198,2805],{"class":2235},[2225,171200,2298],{"class":2235},[2225,171202,62649],{"class":2307},[2225,171204,2311],{"class":2235},[2225,171206,2249],{"class":2235},[2225,171208,171209],{"class":2252},"c-7",[2225,171211,2805],{"class":2235},[2225,171213,2298],{"class":2235},[2225,171215,6351],{"class":2307},[2225,171217,2311],{"class":2235},[2225,171219,171220],{"class":6527}," 300",[2225,171222,2243],{"class":2235},[2225,171224,2362],{"class":2239},[2225,171226,171227],{"class":2227,"line":1038},[2225,171228,2285],{"emptyLinePlaceholder":2284},[2225,171230,171231,171233,171235,171237,171239,171241,171243,171245,171247],{"class":2227,"line":2288},[2225,171232,6500],{"class":2231},[2225,171234,171114],{"class":2239},[2225,171236,1520],{"class":2235},[2225,171238,3158],{"class":2291},[2225,171240,2789],{"class":2239},[2225,171242,2805],{"class":2235},[2225,171244,171125],{"class":2252},[2225,171246,2805],{"class":2235},[2225,171248,2362],{"class":2239},[2225,171250,171251],{"class":2227,"line":2304},[2225,171252,171253],{"class":2763},"\u002F\u002F → { id: 'd-1', clientId: 'c-42', amount: 1200 }\n",[1591,171255,171257],{"id":171256},"iterating-without-a-bulk-load","Iterating without a bulk load",[937,171259,171260,171263,171264,171266],{},[941,171261,171262],{},"scan({ pageSize })"," walks the collection page-by-page, never holding more than ",[941,171265,24875],{},"\ndecrypted records at once — the right tool for a full pass over a lazy collection:",[2217,171268,171270],{"className":2219,"code":171269,"language":2221,"meta":1037,"style":1037},"for await (const record of disbursements.scan({ pageSize: 500 })) {\n  console.log(record.id, record.amount)\n  \u002F\u002F → 'd-1' 1200\n  \u002F\u002F → 'd-2' 300\n}\n",[941,171271,171272,171308,171334,171339,171344],{"__ignoreMap":1037},[2225,171273,171274,171276,171278,171280,171282,171284,171286,171288,171290,171292,171294,171296,171298,171300,171302,171304,171306],{"class":2227,"line":2228},[2225,171275,4646],{"class":2231},[2225,171277,2784],{"class":2231},[2225,171279,953],{"class":2239},[2225,171281,2775],{"class":2774},[2225,171283,43726],{"class":2239},[2225,171285,27066],{"class":2235},[2225,171287,171114],{"class":2239},[2225,171289,1520],{"class":2235},[2225,171291,3239],{"class":2291},[2225,171293,2789],{"class":2239},[2225,171295,2792],{"class":2235},[2225,171297,27079],{"class":2307},[2225,171299,2311],{"class":2235},[2225,171301,27084],{"class":6527},[2225,171303,2243],{"class":2235},[2225,171305,7620],{"class":2239},[2225,171307,4200],{"class":2235},[2225,171309,171310,171312,171314,171316,171318,171320,171322,171324,171326,171328,171330,171332],{"class":2227,"line":1041},[2225,171311,27095],{"class":2239},[2225,171313,1520],{"class":2235},[2225,171315,27100],{"class":2291},[2225,171317,2789],{"class":2307},[2225,171319,24158],{"class":2239},[2225,171321,1520],{"class":2235},[2225,171323,6983],{"class":2239},[2225,171325,2298],{"class":2235},[2225,171327,24268],{"class":2239},[2225,171329,1520],{"class":2235},[2225,171331,6617],{"class":2239},[2225,171333,2362],{"class":2307},[2225,171335,171336],{"class":2227,"line":1038},[2225,171337,171338],{"class":2763},"  \u002F\u002F → 'd-1' 1200\n",[2225,171340,171341],{"class":2227,"line":2288},[2225,171342,171343],{"class":2763},"  \u002F\u002F → 'd-2' 300\n",[2225,171345,171346],{"class":2227,"line":2304},[2225,171347,7351],{"class":2235},[1591,171349,171351],{"id":171350},"cache-stats","Cache stats",[937,171353,171354,171356,171357,171359],{},[941,171355,25523],{}," reports the LRU's hit rate and current occupancy, useful for tuning the\n",[941,171358,6840],{}," budget:",[2217,171361,171363],{"className":2219,"code":171362,"language":2221,"meta":1037,"style":1037},"const stats = disbursements.cacheStats()\n",[941,171364,171365],{"__ignoreMap":1037},[2225,171366,171367,171369,171372,171374,171376,171378,171380],{"class":2227,"line":2228},[2225,171368,2775],{"class":2774},[2225,171370,171371],{"class":2239}," stats ",[2225,171373,2781],{"class":2235},[2225,171375,171114],{"class":2239},[2225,171377,1520],{"class":2235},[2225,171379,25533],{"class":2291},[2225,171381,8146],{"class":2239},[2217,171383,171385],{"className":6604,"code":171384,"language":141,"meta":1037,"style":1037},"{\n  \"hits\": 1,\n  \"misses\": 0,\n  \"evictions\": 0,\n  \"size\": 2,\n  \"bytes\": 86,\n  \"lazy\": true\n}\n",[941,171386,171387,171391,171405,171420,171435,171449,171464,171476],{"__ignoreMap":1037},[2225,171388,171389],{"class":2227,"line":2228},[2225,171390,4200],{"class":2235},[2225,171392,171393,171395,171397,171399,171401,171403],{"class":2227,"line":1041},[2225,171394,9368],{"class":2235},[2225,171396,75623],{"class":2774},[2225,171398,6620],{"class":2235},[2225,171400,2311],{"class":2235},[2225,171402,9377],{"class":6527},[2225,171404,1707],{"class":2235},[2225,171406,171407,171409,171412,171414,171416,171418],{"class":2227,"line":1038},[2225,171408,9368],{"class":2235},[2225,171410,171411],{"class":2774},"misses",[2225,171413,6620],{"class":2235},[2225,171415,2311],{"class":2235},[2225,171417,8464],{"class":6527},[2225,171419,1707],{"class":2235},[2225,171421,171422,171424,171427,171429,171431,171433],{"class":2227,"line":2288},[2225,171423,9368],{"class":2235},[2225,171425,171426],{"class":2774},"evictions",[2225,171428,6620],{"class":2235},[2225,171430,2311],{"class":2235},[2225,171432,8464],{"class":6527},[2225,171434,1707],{"class":2235},[2225,171436,171437,171439,171441,171443,171445,171447],{"class":2227,"line":2304},[2225,171438,9368],{"class":2235},[2225,171440,79350],{"class":2774},[2225,171442,6620],{"class":2235},[2225,171444,2311],{"class":2235},[2225,171446,18961],{"class":6527},[2225,171448,1707],{"class":2235},[2225,171450,171451,171453,171455,171457,171459,171462],{"class":2227,"line":2356},[2225,171452,9368],{"class":2235},[2225,171454,64492],{"class":2774},[2225,171456,6620],{"class":2235},[2225,171458,2311],{"class":2235},[2225,171460,171461],{"class":6527}," 86",[2225,171463,1707],{"class":2235},[2225,171465,171466,171468,171470,171472,171474],{"class":2227,"line":2830},[2225,171467,9368],{"class":2235},[2225,171469,719],{"class":2774},[2225,171471,6620],{"class":2235},[2225,171473,2311],{"class":2235},[2225,171475,71026],{"class":2235},[2225,171477,171478],{"class":2227,"line":2861},[2225,171479,7351],{"class":2235},[937,171481,155374,171482,171484,171485,171488,171489,171491,171492,171494,171495,171497],{},[941,171483,20953],{}," calls above populated the LRU directly, so the later ",[941,171486,171487],{},"get('d-1')"," is a cache\nhit — ",[941,171490,20953],{}," never registers as a hit or miss itself. The ",[941,171493,7906],{}," loop from the previous\nsection doesn't touch these numbers either: lazy ",[941,171496,7906],{}," streams pages straight from the\nstore and deliberately skips the LRU, so it can't be used to warm the cache.",[937,171499,171500,171502,171503,171506,171507,171509,171510,171512,171513,171516,171517,171519,171520,171522,171523,171525],{},[941,171501,64492],{}," is tracked unconditionally — every cached record's approximate size\n(",[941,171504,171505],{},"JSON.stringify(record).length",") is added to the running total on insert, whether or not\n",[941,171508,64488],{}," is configured. Declaring ",[941,171511,64488],{}," doesn't turn tracking on; it only makes that\nrunning total something the cache ",[1092,171514,171515],{},"acts on"," — once the total would exceed it, eviction\nkicks in. A ",[941,171518,64485],{},"-only budget (like ",[941,171521,154444],{}," above) still reports real,\nnon-zero byte usage in ",[941,171524,25523],{},", it just never evicts because of it.",[1591,171527,171529,997,171531,122050],{"id":171528},"list-and-query-throw",[941,171530,24407],{},[941,171532,24715],{},[937,171534,171535],{},"Both bulk-read methods refuse to run on a lazy collection instead of silently materializing\nthe whole store into memory:",[2217,171537,171539],{"className":2219,"code":171538,"language":2221,"meta":1037,"style":1037},"await disbursements.list()\n\u002F\u002F → throws: list() is not available in lazy mode (prefetch: false).\n\u002F\u002F   Use collection.scan({ pageSize }) to iterate over the full collection.\n\ndisbursements.query()\n\u002F\u002F → throws: query() is not available in lazy mode (prefetch: false).\n\u002F\u002F   Use collection.lazyQuery() for indexed reads, or collection.scan({ pageSize })\n\u002F\u002F   and filter the streamed records with a regular for-await loop.\n",[941,171540,171541,171553,171558,171563,171567,171577,171582,171587],{"__ignoreMap":1037},[2225,171542,171543,171545,171547,171549,171551],{"class":2227,"line":2228},[2225,171544,6500],{"class":2231},[2225,171546,171114],{"class":2239},[2225,171548,1520],{"class":2235},[2225,171550,3167],{"class":2291},[2225,171552,8146],{"class":2239},[2225,171554,171555],{"class":2227,"line":1041},[2225,171556,171557],{"class":2763},"\u002F\u002F → throws: list() is not available in lazy mode (prefetch: false).\n",[2225,171559,171560],{"class":2227,"line":1038},[2225,171561,171562],{"class":2763},"\u002F\u002F   Use collection.scan({ pageSize }) to iterate over the full collection.\n",[2225,171564,171565],{"class":2227,"line":2288},[2225,171566,2285],{"emptyLinePlaceholder":2284},[2225,171568,171569,171571,171573,171575],{"class":2227,"line":2304},[2225,171570,154444],{"class":2239},[2225,171572,1520],{"class":2235},[2225,171574,5286],{"class":2291},[2225,171576,8146],{"class":2239},[2225,171578,171579],{"class":2227,"line":2356},[2225,171580,171581],{"class":2763},"\u002F\u002F → throws: query() is not available in lazy mode (prefetch: false).\n",[2225,171583,171584],{"class":2227,"line":2830},[2225,171585,171586],{"class":2763},"\u002F\u002F   Use collection.lazyQuery() for indexed reads, or collection.scan({ pageSize })\n",[2225,171588,171589],{"class":2227,"line":2861},[2225,171590,171591],{"class":2763},"\u002F\u002F   and filter the streamed records with a regular for-await loop.\n",[1022,171593,102889],{"id":102888},[1591,171595,171597],{"id":171596},"withlazy",[941,171598,22060],{},[2217,171600,171602],{"className":2219,"code":171601,"language":2221,"meta":1037,"style":1037},"function withLazy(): LazyStrategy\n",[941,171603,171604],{"__ignoreMap":1037},[2225,171605,171606,171608,171610,171612],{"class":2227,"line":2228},[2225,171607,12641],{"class":2774},[2225,171609,62014],{"class":2291},[2225,171611,112812],{"class":2235},[2225,171613,171614],{"class":6342}," LazyStrategy\n",[937,171616,171617,171618,10313,171621,1520],{},"Builds the strategy that supplies a lazy collection's bounded LRU cache. Pass the result as\n",[941,171619,171620],{},"lazyStrategy",[941,171622,12780],{},[1591,171624,171626],{"id":171625},"cacheoptions",[941,171627,171628],{},"CacheOptions",[937,171630,110833,171631,171634,171635,4688,171637,171639],{},[941,171632,171633],{},"vault.collection\u003CT>(name, { prefetch: false, cache: { … } })",".\nAt least one of ",[941,171636,64485],{},[941,171638,64488],{}," is required — an unbounded lazy cache defeats\nthe purpose and construction throws otherwise.",[1338,171641,171642,171652],{},[1341,171643,171644],{},[1344,171645,171646,171648,171650],{},[1347,171647,101104],{},[1347,171649,20194],{},[1347,171651,7834],{},[1357,171653,171654,171667],{},[1344,171655,171656,171660,171664],{},[1362,171657,171658],{},[941,171659,64485],{},[1362,171661,171662],{},[941,171663,20636],{},[1362,171665,171666],{},"Maximum number of records kept in memory before LRU eviction.",[1344,171668,171669,171673,171678],{},[1362,171670,171671],{},[941,171672,64488],{},[1362,171674,171675],{},[941,171676,171677],{},"number | string",[1362,171679,171680,171681,968,171684,968,171687,171690,171691,171693],{},"Maximum total decrypted byte size before LRU eviction. Accepts a raw number or a human-friendly string (",[941,171682,171683],{},"'50KB'",[941,171685,171686],{},"'50MB'",[941,171688,171689],{},"'1GB'","). With both budgets set, eviction runs until ",[1092,171692,31219],{}," are satisfied.",[1591,171695,171696],{"id":25520},[941,171697,171698],{},"CacheStats",[937,171700,171701,171702,1520],{},"Returned by ",[941,171703,171704],{},"collection.cacheStats()",[1338,171706,171707,171717],{},[1341,171708,171709],{},[1344,171710,171711,171713,171715],{},[1347,171712,101104],{},[1347,171714,20194],{},[1347,171716,20200],{},[1357,171718,171719,171732,171745,171758,171771,171790],{},[1344,171720,171721,171725,171729],{},[1362,171722,171723],{},[941,171724,75623],{},[1362,171726,171727],{},[941,171728,20636],{},[1362,171730,171731],{},"Cache hits since the collection was opened.",[1344,171733,171734,171738,171742],{},[1362,171735,171736],{},[941,171737,171411],{},[1362,171739,171740],{},[941,171741,20636],{},[1362,171743,171744],{},"Cache misses since the collection was opened.",[1344,171746,171747,171751,171755],{},[1362,171748,171749],{},[941,171750,171426],{},[1362,171752,171753],{},[941,171754,20636],{},[1362,171756,171757],{},"Entries evicted since the collection was opened.",[1344,171759,171760,171764,171768],{},[1362,171761,171762],{},[941,171763,79350],{},[1362,171765,171766],{},[941,171767,20636],{},[1362,171769,171770],{},"Current number of cached entries.",[1344,171772,171773,171777,171781],{},[1362,171774,171775],{},[941,171776,64492],{},[1362,171778,171779],{},[941,171780,20636],{},[1362,171782,171783,171784,171786,171787,171789],{},"Current approximate cached byte size, summed from each cached record's ",[941,171785,171505],{}," at insert time. Always tracked, regardless of budget; ",[941,171788,64488],{}," (if set) turns this total into an eviction trigger — it doesn't gate whether bytes are counted.",[1344,171791,171792,171796,171800],{},[1362,171793,171794],{},[941,171795,719],{},[1362,171797,171798],{},[941,171799,16861],{},[1362,171801,171802,171804,171805,171807],{},[941,171803,11790],{}," for a lazy-mode collection; ",[941,171806,2560],{}," for eager (where the other fields read as zeroes \u002F the eager cache's plain size).",[1591,171809,171811],{"id":171810},"lazystrategy",[941,171812,171813],{},"LazyStrategy",[2217,171815,171817],{"className":2219,"code":171816,"language":2221,"meta":1037,"style":1037},"interface LazyStrategy {\n  createCache\u003CV>(collection: string, cache: CacheOptions | undefined): Lru\u003Cstring, V>\n}\n",[941,171818,171819,171828,171877],{"__ignoreMap":1037},[2225,171820,171821,171823,171826],{"class":2227,"line":2228},[2225,171822,6994],{"class":2774},[2225,171824,171825],{"class":6342}," LazyStrategy",[2225,171827,2301],{"class":2235},[2225,171829,171830,171833,171835,171838,171841,171843,171845,171847,171849,171852,171854,171857,171859,171861,171863,171866,171868,171870,171872,171875],{"class":2227,"line":1041},[2225,171831,171832],{"class":2307},"  createCache",[2225,171834,6474],{"class":2235},[2225,171836,171837],{"class":6342},"V",[2225,171839,171840],{"class":2235},">(",[2225,171842,6471],{"class":7052},[2225,171844,2311],{"class":2235},[2225,171846,6366],{"class":6342},[2225,171848,2298],{"class":2235},[2225,171850,171851],{"class":7052}," cache",[2225,171853,2311],{"class":2235},[2225,171855,171856],{"class":6342}," CacheOptions",[2225,171858,7087],{"class":2235},[2225,171860,8483],{"class":6342},[2225,171862,6255],{"class":2235},[2225,171864,171865],{"class":6342}," Lru",[2225,171867,6474],{"class":2235},[2225,171869,7259],{"class":6342},[2225,171871,2298],{"class":2235},[2225,171873,171874],{"class":6342}," V",[2225,171876,7093],{"class":2235},[2225,171878,171879],{"class":2227,"line":1038},[2225,171880,7351],{"class":2235},[937,171882,171883,171884,171886,171887,171889],{},"The interface ",[941,171885,22060],{}," implements. Most consumers never touch this directly — it exists\nso a custom strategy can be substituted for ",[941,171888,22060],{},"'s default budget validation and LRU\nconstruction.",[1022,171891,101425],{"id":101424},[1111,171893,171894,171901],{},[1114,171895,171896,21348,171898,171900],{},[941,171897,6836],{},[941,171899,64481],{}," still works — a deprecated implicit\npath runs the same budget validation and LRU construction, so behavior is identical.",[1114,171902,171903,171904,171906,171907,171910,171911,171913,171914,171916],{},"The implicit path logs one ",[941,171905,15856],{}," per process (suppressed under ",[941,171908,171909],{},"NODE_ENV=test",")\npointing at ",[941,171912,22060],{}," as the forward-stable spelling, and will be removed at 1.0 — pass\n",[941,171915,64481],{}," explicitly to avoid relying on it.",[1022,171918,68210],{"id":68209},[1111,171920,171921,171940,171968,171979,171998],{},[1114,171922,171923,171926,171927,171929,171930,170737,171932,2419,171934,171936,171937,1520],{},[950,171924,171925],{},"A cache budget is mandatory."," Opening a ",[941,171928,6836],{}," collection without ",[941,171931,64485],{},[941,171933,64488],{},[941,171935,6840],{}," throws immediately: ",[941,171938,171939],{},"lazy mode (prefetch: false) requires a cache option with maxRecords and\u002For maxBytes",[1114,171941,171942,171949,171950,171952,171953,171957,171958,1625,171960,171962,171963,171965,171966,957],{},[950,171943,171944,997,171946,171948],{},[941,171945,24407],{},[941,171947,24715],{}," throw in lazy mode."," Use ",[941,171951,171262],{}," for a full pass, or\n",[1007,171954,171955],{"href":706},[941,171956,24807],{}," for indexed ",[941,171959,3016],{},[941,171961,3019],{}," reads (requires the\n",[941,171964,705],{}," service and the queried fields declared in ",[941,171967,21907],{},[1114,171969,171970,171975,171976,171978],{},[950,171971,171972,171973,1520],{},"A lazy collection can't be the right side of a ",[941,171974,61808],{}," Joining against it throws —\nopen the collection eager (",[941,171977,164144],{},", the default) if it needs to participate in\na join.",[1114,171980,171981,124646,171987,171990,171991,171993,171994,171997],{},[950,171982,171983,171986],{},[941,171984,171985],{},"unique"," indexes aren't supported on lazy collections.",[941,171988,171989],{},"unique: true"," on an\nindex for a ",[941,171992,6836],{}," collection throws ",[941,171995,171996],{},"UnsupportedIndexOptionError"," at\nregistration, because the write paths that would enforce it don't run in lazy mode.",[1114,171999,172000,172003],{},[950,172001,172002],{},"The LRU is per-session, in memory."," It is not persisted between restarts; a fresh\nsession starts with an empty cache and repopulates it from the store as records are read.",[1022,172005,2621],{"id":2620},[1111,172007,172008,172014,172020,172029,172036,172041],{},[1114,172009,172010,1368,172012],{},[1007,172011,61743],{"href":598},[1007,172013,1019],{"href":1018},[1114,172015,172016,172019],{},[1007,172017,172018],{"href":48},"Core 01 — Vault & collections"," — eager vs. lazy hydration and when to switch",[1114,172021,172022,172024,172025,1625,172027,70934],{},[1007,172023,16076],{"href":51}," — what a lazy read goes to on each ",[941,172026,14639],{},[941,172028,7906],{},[1114,172030,172031,964,172033,172035],{},[1007,172032,705],{"href":706},[941,172034,24807],{},", the indexed read path for lazy collections",[1114,172037,172038,172040],{},[1007,172039,715],{"href":716}," — why a lazy collection can't be a join's right side",[1114,172042,101581,172043,1520],{},[1007,172044,172046],{"href":94367,"rel":172045},[3371],[941,172047,172048],{},"131-with-lazy",[2645,172050,172051],{},"html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sfNiH, html code.shiki .sfNiH{--shiki-light:#FF5370;--shiki-default:#FF9CAC;--shiki-dark:#FF9CAC}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sHdIc, html code.shiki .sHdIc{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#EEFFFF;--shiki-default-font-style:italic;--shiki-dark:#BABED8;--shiki-dark-font-style:italic}",{"title":1037,"searchDepth":1038,"depth":1038,"links":172053},[172054,172055,172056,172057,172064,172070,172071,172072],{"id":29856,"depth":1041,"text":29857},{"id":70092,"depth":1041,"text":70093},{"id":99863,"depth":1041,"text":99864},{"id":29892,"depth":1041,"text":29893,"children":172058},[172059,172060,172061,172062],{"id":171094,"depth":1038,"text":171095},{"id":171256,"depth":1038,"text":171257},{"id":171350,"depth":1038,"text":171351},{"id":171528,"depth":1038,"text":172063},"list() and query() throw",{"id":102888,"depth":1041,"text":102889,"children":172065},[172066,172067,172068,172069],{"id":171596,"depth":1038,"text":22060},{"id":171625,"depth":1038,"text":171628},{"id":25520,"depth":1038,"text":171698},{"id":171810,"depth":1038,"text":171813},{"id":101424,"depth":1041,"text":101425},{"id":68209,"depth":1041,"text":68210},{"id":2620,"depth":1041,"text":2621},"Bounded-memory reads for collections too large for RAM: `prefetch: false` plus a cache budget, then per-record `get()`\u002F`scan()` over a bounded LRU set.",{},{"title":719,"description":172073},"exMBNDG9WA6Wpr6uSrH4afpLBD6Md96mmjAYZbQlv44",{"id":172078,"title":723,"access":12,"apiToc":929,"body":172079,"category":1042,"description":173749,"extension":1044,"meta":173750,"navigation":1042,"nest":1047,"path":724,"seo":173751,"sinceVersion":1042,"status":1049,"stem":725,"subtitle":1042,"tier":1050,"tocExternal":1042,"__hash__":173752},"docs\u002Fservices\u002Flinks.md",{"type":931,"value":172080,"toc":173732},[172081,172083,172086,172088,172120,172133,172135,172140,172385,172413,172415,172419,172768,172790,172794,172799,172952,172958,173125,173256,173271,173273,173277,173357,173367,173466,173468,173540,173542,173604,173606,173685,173687,173729],[934,172082,723],{"id":723},[46871,172084],{":rows":172085},"[{\"label\":\"Subpath\",\"value\":\"`@noy-db\u002Fhub` (root export)\"},{\"label\":\"Type\",\"value\":\"Core feature — link-set fields are always available.\"}]",[1022,172087,29857],{"id":29856},[937,172089,3289,172090,172093,172094,172096,172097,172099,172100,172102,172103,172105,172106,172109,172110,1625,172112,1625,172114,1625,172116,1625,172118,124645],{},[950,172091,172092],{},"link set"," is a real many-to-many relationship between two ",[1007,172095,2698],{"href":48},"\n— the kind of thing a relational database would model with a join table. ",[941,172098,151195],{},"\ndeclares it; ",[941,172101,151203],{}," gives you a handle to read and write its rows. Under the hood each\nlink set is its own dedicated collection, ",[941,172104,151199],{},", encrypted under its own\n",[1007,172107,172108],{"href":66},"per-collection key"," exactly like any other collection — the storage backend\nnever sees plaintext link data. It stays out of your JavaScript bundle until first use: the storage\nengine behind the handle loads lazily on the first ",[941,172111,54379],{},[941,172113,151463],{},[941,172115,145202],{},[941,172117,27066],{},[941,172119,3167],{},[937,172121,172122,172123,172126,172127,172132],{},"Reach for a link set when the relationship itself needs to be queried from ",[950,172124,172125],{},"either side"," or carry\nits own data (a match quantity, a role, a timestamp). For the simpler case — one record holding an\narray of ids it points at — the lighter-weight ",[1007,172128,172130],{"href":172129},"\u002Fservices\u002Fforeign-refs#array-refs-refarray",[941,172131,147755],{},"\nfield descriptor is usually enough and doesn't need a separate collection.",[1022,172134,58394],{"id":58393},[937,172136,3977,172137,172139],{},[941,172138,943],{}," strategy to enable — link sets are part of the always-on core, reachable from\nany open vault. Declare one by name and endpoints, then get its handle:",[2217,172141,172143],{"className":2219,"code":172142,"language":2221,"meta":1037,"style":1037},"import { createNoydb, ref } from '@noy-db\u002Fhub'\nimport { toMemory } from '@noy-db\u002Fto-memory'\n\nconst db = await createNoydb({ store: toMemory(), user: 'alice', secret: 'alice-pass-2026-strong' })\nconst vault = await db.openVault('firm')\n\nvault.link('saleLineLinks', {\n  a: ref('saleLines'),      \u002F\u002F or the plain string 'saleLines'\n  b: ref('purchaseLines'),\n  onDelete: 'cascade',      \u002F\u002F default; also 'strict' | 'warn'\n})\n\nconst links = vault.links('saleLineLinks')\n",[941,172144,172145,172167,172185,172189,172241,172267,172271,172291,172314,172334,172351,172357,172361],{"__ignoreMap":1037},[2225,172146,172147,172149,172151,172153,172155,172157,172159,172161,172163,172165],{"class":2227,"line":2228},[2225,172148,2232],{"class":2231},[2225,172150,2236],{"class":2235},[2225,172152,2744],{"class":2239},[2225,172154,2298],{"class":2235},[2225,172156,62303],{"class":2239},[2225,172158,2243],{"class":2235},[2225,172160,2246],{"class":2231},[2225,172162,2249],{"class":2235},[2225,172164,1086],{"class":2252},[2225,172166,2256],{"class":2235},[2225,172168,172169,172171,172173,172175,172177,172179,172181,172183],{"class":2227,"line":1041},[2225,172170,2232],{"class":2231},[2225,172172,2236],{"class":2235},[2225,172174,68576],{"class":2239},[2225,172176,2243],{"class":2235},[2225,172178,2246],{"class":2231},[2225,172180,2249],{"class":2235},[2225,172182,9638],{"class":2252},[2225,172184,2256],{"class":2235},[2225,172186,172187],{"class":2227,"line":1038},[2225,172188,2285],{"emptyLinePlaceholder":2284},[2225,172190,172191,172193,172195,172197,172199,172201,172203,172205,172207,172209,172211,172213,172215,172217,172219,172221,172223,172225,172227,172229,172231,172233,172235,172237,172239],{"class":2227,"line":2288},[2225,172192,2775],{"class":2774},[2225,172194,2778],{"class":2239},[2225,172196,2781],{"class":2235},[2225,172198,2784],{"class":2231},[2225,172200,2744],{"class":2291},[2225,172202,2789],{"class":2239},[2225,172204,2792],{"class":2235},[2225,172206,7564],{"class":2307},[2225,172208,2311],{"class":2235},[2225,172210,68576],{"class":2291},[2225,172212,4228],{"class":2239},[2225,172214,2298],{"class":2235},[2225,172216,2795],{"class":2307},[2225,172218,2311],{"class":2235},[2225,172220,2249],{"class":2235},[2225,172222,6408],{"class":2252},[2225,172224,2805],{"class":2235},[2225,172226,2298],{"class":2235},[2225,172228,2810],{"class":2307},[2225,172230,2311],{"class":2235},[2225,172232,2249],{"class":2235},[2225,172234,141438],{"class":2252},[2225,172236,2805],{"class":2235},[2225,172238,2243],{"class":2235},[2225,172240,2362],{"class":2239},[2225,172242,172243,172245,172247,172249,172251,172253,172255,172257,172259,172261,172263,172265],{"class":2227,"line":2304},[2225,172244,2775],{"class":2774},[2225,172246,2835],{"class":2239},[2225,172248,2781],{"class":2235},[2225,172250,2784],{"class":2231},[2225,172252,2842],{"class":2239},[2225,172254,1520],{"class":2235},[2225,172256,2847],{"class":2291},[2225,172258,2789],{"class":2239},[2225,172260,2805],{"class":2235},[2225,172262,39709],{"class":2252},[2225,172264,2805],{"class":2235},[2225,172266,2362],{"class":2239},[2225,172268,172269],{"class":2227,"line":2356},[2225,172270,2285],{"emptyLinePlaceholder":2284},[2225,172272,172273,172275,172277,172279,172281,172283,172285,172287,172289],{"class":2227,"line":2830},[2225,172274,2694],{"class":2239},[2225,172276,1520],{"class":2235},[2225,172278,44484],{"class":2291},[2225,172280,2789],{"class":2239},[2225,172282,2805],{"class":2235},[2225,172284,151246],{"class":2252},[2225,172286,2805],{"class":2235},[2225,172288,2298],{"class":2235},[2225,172290,2301],{"class":2235},[2225,172292,172293,172295,172297,172299,172301,172303,172305,172307,172309,172311],{"class":2227,"line":2861},[2225,172294,151257],{"class":2307},[2225,172296,2311],{"class":2235},[2225,172298,62303],{"class":2291},[2225,172300,2789],{"class":2239},[2225,172302,2805],{"class":2235},[2225,172304,151268],{"class":2252},[2225,172306,2805],{"class":2235},[2225,172308,1381],{"class":2239},[2225,172310,2298],{"class":2235},[2225,172312,172313],{"class":2763},"      \u002F\u002F or the plain string 'saleLines'\n",[2225,172315,172316,172318,172320,172322,172324,172326,172328,172330,172332],{"class":2227,"line":4246},[2225,172317,151282],{"class":2307},[2225,172319,2311],{"class":2235},[2225,172321,62303],{"class":2291},[2225,172323,2789],{"class":2239},[2225,172325,2805],{"class":2235},[2225,172327,151293],{"class":2252},[2225,172329,2805],{"class":2235},[2225,172331,1381],{"class":2239},[2225,172333,1707],{"class":2235},[2225,172335,172336,172338,172340,172342,172344,172346,172348],{"class":2227,"line":4253},[2225,172337,151304],{"class":2307},[2225,172339,2311],{"class":2235},[2225,172341,2249],{"class":2235},[2225,172343,10753],{"class":2252},[2225,172345,2805],{"class":2235},[2225,172347,2298],{"class":2235},[2225,172349,172350],{"class":2763},"      \u002F\u002F default; also 'strict' | 'warn'\n",[2225,172352,172353,172355],{"class":2227,"line":4259},[2225,172354,2359],{"class":2235},[2225,172356,2362],{"class":2239},[2225,172358,172359],{"class":2227,"line":6548},[2225,172360,2285],{"emptyLinePlaceholder":2284},[2225,172362,172363,172365,172367,172369,172371,172373,172375,172377,172379,172381,172383],{"class":2227,"line":6576},[2225,172364,2775],{"class":2774},[2225,172366,151334],{"class":2239},[2225,172368,2781],{"class":2235},[2225,172370,6466],{"class":2239},[2225,172372,1520],{"class":2235},[2225,172374,723],{"class":2291},[2225,172376,2789],{"class":2239},[2225,172378,2805],{"class":2235},[2225,172380,151246],{"class":2252},[2225,172382,2805],{"class":2235},[2225,172384,2362],{"class":2239},[937,172386,172387,997,172389,172391,172392,172394,172395,172397,172398,968,172400,4765,172402,104458,172404,1952,172406,6789,172408,102958,172410,172412],{},[941,172388,1007],{},[941,172390,80529],{}," are the two endpoint collections (they may be the same collection, for a self-link).\nEach accepts a plain collection name or a ",[941,172393,151532],{}," descriptor — only its ",[941,172396,134984],{}," is used, since\na link set enforces its own integrity rather than reusing ref-mode checks. Re-declaring the same name\nwith an identical spec is a no-op; declaring it again with a different ",[941,172399,1007],{},[941,172401,80529],{},[941,172403,137880],{},[941,172405,12881],{},[941,172407,151203],{},[941,172409,12881],{},[941,172411,4914],{}," was never declared.",[1022,172414,29893],{"id":29892},[1591,172416,172418],{"id":172417},"connect-two-records-and-query-from-either-side","Connect two records and query from either side",[2217,172420,172422],{"className":2219,"code":172421,"language":2221,"meta":1037,"style":1037},"const sales = vault.collection('saleLines')\nconst purchases = vault.collection('purchaseLines')\nawait sales.put('s1', { id: 's1', label: 'sale 1' })\nawait purchases.put('p1', { id: 'p1', label: 'purchase 1' })\n\nawait links.connect('s1', 'p1', { matchedQty: 6 }) \u002F\u002F throws LinkEndpointError if either id is missing\n\nawait links.has('s1', 'p1')\n\u002F\u002F → true\n\nawait links.of('s1')\n\u002F\u002F → [{ a: 's1', b: 'p1', meta: { matchedQty: 6 } }]\n\nawait links.of('p1')\n\u002F\u002F → [{ a: 's1', b: 'p1', meta: { matchedQty: 6 } }] — matched on slot b, same row\n\nawait links.list()\n\u002F\u002F → every row in the set: [{ a: 's1', b: 'p1', meta: { matchedQty: 6 } }]\n\nawait links.disconnect('s1', 'p1') \u002F\u002F idempotent — no-op if the link is already gone\n",[941,172423,172424,172448,172473,172522,172572,172576,172619,172623,172651,172655,172659,172679,172683,172687,172707,172712,172716,172728,172733,172737],{"__ignoreMap":1037},[2225,172425,172426,172428,172430,172432,172434,172436,172438,172440,172442,172444,172446],{"class":2227,"line":2228},[2225,172427,2775],{"class":2774},[2225,172429,123547],{"class":2239},[2225,172431,2781],{"class":2235},[2225,172433,6466],{"class":2239},[2225,172435,1520],{"class":2235},[2225,172437,6471],{"class":2291},[2225,172439,2789],{"class":2239},[2225,172441,2805],{"class":2235},[2225,172443,151268],{"class":2252},[2225,172445,2805],{"class":2235},[2225,172447,2362],{"class":2239},[2225,172449,172450,172452,172455,172457,172459,172461,172463,172465,172467,172469,172471],{"class":2227,"line":1041},[2225,172451,2775],{"class":2774},[2225,172453,172454],{"class":2239}," purchases ",[2225,172456,2781],{"class":2235},[2225,172458,6466],{"class":2239},[2225,172460,1520],{"class":2235},[2225,172462,6471],{"class":2291},[2225,172464,2789],{"class":2239},[2225,172466,2805],{"class":2235},[2225,172468,151293],{"class":2252},[2225,172470,2805],{"class":2235},[2225,172472,2362],{"class":2239},[2225,172474,172475,172477,172479,172481,172483,172485,172487,172489,172491,172493,172495,172497,172499,172501,172503,172505,172507,172509,172511,172513,172516,172518,172520],{"class":2227,"line":1038},[2225,172476,6500],{"class":2231},[2225,172478,123785],{"class":2239},[2225,172480,1520],{"class":2235},[2225,172482,3161],{"class":2291},[2225,172484,2789],{"class":2239},[2225,172486,2805],{"class":2235},[2225,172488,151370],{"class":2252},[2225,172490,2805],{"class":2235},[2225,172492,2298],{"class":2235},[2225,172494,2236],{"class":2235},[2225,172496,7070],{"class":2307},[2225,172498,2311],{"class":2235},[2225,172500,2249],{"class":2235},[2225,172502,151370],{"class":2252},[2225,172504,2805],{"class":2235},[2225,172506,2298],{"class":2235},[2225,172508,95892],{"class":2307},[2225,172510,2311],{"class":2235},[2225,172512,2249],{"class":2235},[2225,172514,172515],{"class":2252},"sale 1",[2225,172517,2805],{"class":2235},[2225,172519,2243],{"class":2235},[2225,172521,2362],{"class":2239},[2225,172523,172524,172526,172529,172531,172533,172535,172537,172539,172541,172543,172545,172547,172549,172551,172553,172555,172557,172559,172561,172563,172566,172568,172570],{"class":2227,"line":2288},[2225,172525,6500],{"class":2231},[2225,172527,172528],{"class":2239}," purchases",[2225,172530,1520],{"class":2235},[2225,172532,3161],{"class":2291},[2225,172534,2789],{"class":2239},[2225,172536,2805],{"class":2235},[2225,172538,150929],{"class":2252},[2225,172540,2805],{"class":2235},[2225,172542,2298],{"class":2235},[2225,172544,2236],{"class":2235},[2225,172546,7070],{"class":2307},[2225,172548,2311],{"class":2235},[2225,172550,2249],{"class":2235},[2225,172552,150929],{"class":2252},[2225,172554,2805],{"class":2235},[2225,172556,2298],{"class":2235},[2225,172558,95892],{"class":2307},[2225,172560,2311],{"class":2235},[2225,172562,2249],{"class":2235},[2225,172564,172565],{"class":2252},"purchase 1",[2225,172567,2805],{"class":2235},[2225,172569,2243],{"class":2235},[2225,172571,2362],{"class":2239},[2225,172573,172574],{"class":2227,"line":2304},[2225,172575,2285],{"emptyLinePlaceholder":2284},[2225,172577,172578,172580,172582,172584,172586,172588,172590,172592,172594,172596,172598,172600,172602,172604,172606,172608,172610,172612,172614,172616],{"class":2227,"line":2356},[2225,172579,6500],{"class":2231},[2225,172581,151359],{"class":2239},[2225,172583,1520],{"class":2235},[2225,172585,54379],{"class":2291},[2225,172587,2789],{"class":2239},[2225,172589,2805],{"class":2235},[2225,172591,151370],{"class":2252},[2225,172593,2805],{"class":2235},[2225,172595,2298],{"class":2235},[2225,172597,2249],{"class":2235},[2225,172599,150929],{"class":2252},[2225,172601,2805],{"class":2235},[2225,172603,2298],{"class":2235},[2225,172605,2236],{"class":2235},[2225,172607,151387],{"class":2307},[2225,172609,2311],{"class":2235},[2225,172611,151392],{"class":6527},[2225,172613,2243],{"class":2235},[2225,172615,8495],{"class":2239},[2225,172617,172618],{"class":2763},"\u002F\u002F throws LinkEndpointError if either id is missing\n",[2225,172620,172621],{"class":2227,"line":2830},[2225,172622,2285],{"emptyLinePlaceholder":2284},[2225,172624,172625,172627,172629,172631,172633,172635,172637,172639,172641,172643,172645,172647,172649],{"class":2227,"line":2861},[2225,172626,6500],{"class":2231},[2225,172628,151359],{"class":2239},[2225,172630,1520],{"class":2235},[2225,172632,145202],{"class":2291},[2225,172634,2789],{"class":2239},[2225,172636,2805],{"class":2235},[2225,172638,151370],{"class":2252},[2225,172640,2805],{"class":2235},[2225,172642,2298],{"class":2235},[2225,172644,2249],{"class":2235},[2225,172646,150929],{"class":2252},[2225,172648,2805],{"class":2235},[2225,172650,2362],{"class":2239},[2225,172652,172653],{"class":2227,"line":4246},[2225,172654,77590],{"class":2763},[2225,172656,172657],{"class":2227,"line":4253},[2225,172658,2285],{"emptyLinePlaceholder":2284},[2225,172660,172661,172663,172665,172667,172669,172671,172673,172675,172677],{"class":2227,"line":4259},[2225,172662,6500],{"class":2231},[2225,172664,151359],{"class":2239},[2225,172666,1520],{"class":2235},[2225,172668,27066],{"class":2291},[2225,172670,2789],{"class":2239},[2225,172672,2805],{"class":2235},[2225,172674,151370],{"class":2252},[2225,172676,2805],{"class":2235},[2225,172678,2362],{"class":2239},[2225,172680,172681],{"class":2227,"line":6548},[2225,172682,151422],{"class":2763},[2225,172684,172685],{"class":2227,"line":6576},[2225,172686,2285],{"emptyLinePlaceholder":2284},[2225,172688,172689,172691,172693,172695,172697,172699,172701,172703,172705],{"class":2227,"line":6582},[2225,172690,6500],{"class":2231},[2225,172692,151359],{"class":2239},[2225,172694,1520],{"class":2235},[2225,172696,27066],{"class":2291},[2225,172698,2789],{"class":2239},[2225,172700,2805],{"class":2235},[2225,172702,150929],{"class":2252},[2225,172704,2805],{"class":2235},[2225,172706,2362],{"class":2239},[2225,172708,172709],{"class":2227,"line":6587},[2225,172710,172711],{"class":2763},"\u002F\u002F → [{ a: 's1', b: 'p1', meta: { matchedQty: 6 } }] — matched on slot b, same row\n",[2225,172713,172714],{"class":2227,"line":7174},[2225,172715,2285],{"emptyLinePlaceholder":2284},[2225,172717,172718,172720,172722,172724,172726],{"class":2227,"line":7180},[2225,172719,6500],{"class":2231},[2225,172721,151359],{"class":2239},[2225,172723,1520],{"class":2235},[2225,172725,3167],{"class":2291},[2225,172727,8146],{"class":2239},[2225,172729,172730],{"class":2227,"line":7220},[2225,172731,172732],{"class":2763},"\u002F\u002F → every row in the set: [{ a: 's1', b: 'p1', meta: { matchedQty: 6 } }]\n",[2225,172734,172735],{"class":2227,"line":7225},[2225,172736,2285],{"emptyLinePlaceholder":2284},[2225,172738,172739,172741,172743,172745,172747,172749,172751,172753,172755,172757,172759,172761,172763,172765],{"class":2227,"line":7231},[2225,172740,6500],{"class":2231},[2225,172742,151359],{"class":2239},[2225,172744,1520],{"class":2235},[2225,172746,151463],{"class":2291},[2225,172748,2789],{"class":2239},[2225,172750,2805],{"class":2235},[2225,172752,151370],{"class":2252},[2225,172754,2805],{"class":2235},[2225,172756,2298],{"class":2235},[2225,172758,2249],{"class":2235},[2225,172760,150929],{"class":2252},[2225,172762,2805],{"class":2235},[2225,172764,8495],{"class":2239},[2225,172766,172767],{"class":2763},"\u002F\u002F idempotent — no-op if the link is already gone\n",[937,172769,172770,172771,1324,172773,172775,172776,172778,172779,172781,172782,172784,172785,1261,172787,172789],{},"There is no query-DSL ",[941,172772,61808],{},[1092,172774,1409],{}," a link set (",[941,172777,61808],{}," walks declared ",[941,172780,147776],{}," fields — see\n",[1007,172783,28187],{"href":716},"). Read link rows with ",[941,172786,151520],{},[941,172788,24407],{},", then fetch the endpoint records\nfrom their own collections.",[1591,172791,172793],{"id":172792},"choose-what-happens-when-an-endpoint-is-deleted","Choose what happens when an endpoint is deleted",[937,172795,172796,172798],{},[941,172797,137880],{}," on the link spec governs every row touching the deleted record:",[2217,172800,172802],{"className":2219,"code":172801,"language":2221,"meta":1037,"style":1037},"vault.link('strictLinks', { a: 'saleLines', b: 'purchaseLines', onDelete: 'strict' })\nconst strict = vault.links('strictLinks')\nawait strict.connect('s1', 'p1')\n\nawait sales.delete('s1')\n\u002F\u002F → throws LinkIntegrityError: Cannot delete \"saleLines\"\u002F\"s1\": 1 link(s) in \"strictLinks\"\n\u002F\u002F   still reference it (onDelete: 'strict').\n",[941,172803,172804,172864,172889,172918,172922,172942,172947],{"__ignoreMap":1037},[2225,172805,172806,172808,172810,172812,172814,172816,172819,172821,172823,172825,172827,172829,172831,172833,172835,172837,172839,172841,172843,172845,172847,172849,172852,172854,172856,172858,172860,172862],{"class":2227,"line":2228},[2225,172807,2694],{"class":2239},[2225,172809,1520],{"class":2235},[2225,172811,44484],{"class":2291},[2225,172813,2789],{"class":2239},[2225,172815,2805],{"class":2235},[2225,172817,172818],{"class":2252},"strictLinks",[2225,172820,2805],{"class":2235},[2225,172822,2298],{"class":2235},[2225,172824,2236],{"class":2235},[2225,172826,79316],{"class":2307},[2225,172828,2311],{"class":2235},[2225,172830,2249],{"class":2235},[2225,172832,151268],{"class":2252},[2225,172834,2805],{"class":2235},[2225,172836,2298],{"class":2235},[2225,172838,80536],{"class":2307},[2225,172840,2311],{"class":2235},[2225,172842,2249],{"class":2235},[2225,172844,151293],{"class":2252},[2225,172846,2805],{"class":2235},[2225,172848,2298],{"class":2235},[2225,172850,172851],{"class":2307}," onDelete",[2225,172853,2311],{"class":2235},[2225,172855,2249],{"class":2235},[2225,172857,140597],{"class":2252},[2225,172859,2805],{"class":2235},[2225,172861,2243],{"class":2235},[2225,172863,2362],{"class":2239},[2225,172865,172866,172868,172871,172873,172875,172877,172879,172881,172883,172885,172887],{"class":2227,"line":1041},[2225,172867,2775],{"class":2774},[2225,172869,172870],{"class":2239}," strict ",[2225,172872,2781],{"class":2235},[2225,172874,6466],{"class":2239},[2225,172876,1520],{"class":2235},[2225,172878,723],{"class":2291},[2225,172880,2789],{"class":2239},[2225,172882,2805],{"class":2235},[2225,172884,172818],{"class":2252},[2225,172886,2805],{"class":2235},[2225,172888,2362],{"class":2239},[2225,172890,172891,172893,172896,172898,172900,172902,172904,172906,172908,172910,172912,172914,172916],{"class":2227,"line":1038},[2225,172892,6500],{"class":2231},[2225,172894,172895],{"class":2239}," strict",[2225,172897,1520],{"class":2235},[2225,172899,54379],{"class":2291},[2225,172901,2789],{"class":2239},[2225,172903,2805],{"class":2235},[2225,172905,151370],{"class":2252},[2225,172907,2805],{"class":2235},[2225,172909,2298],{"class":2235},[2225,172911,2249],{"class":2235},[2225,172913,150929],{"class":2252},[2225,172915,2805],{"class":2235},[2225,172917,2362],{"class":2239},[2225,172919,172920],{"class":2227,"line":2288},[2225,172921,2285],{"emptyLinePlaceholder":2284},[2225,172923,172924,172926,172928,172930,172932,172934,172936,172938,172940],{"class":2227,"line":2304},[2225,172925,6500],{"class":2231},[2225,172927,123785],{"class":2239},[2225,172929,1520],{"class":2235},[2225,172931,3164],{"class":2291},[2225,172933,2789],{"class":2239},[2225,172935,2805],{"class":2235},[2225,172937,151370],{"class":2252},[2225,172939,2805],{"class":2235},[2225,172941,2362],{"class":2239},[2225,172943,172944],{"class":2227,"line":2356},[2225,172945,172946],{"class":2763},"\u002F\u002F → throws LinkIntegrityError: Cannot delete \"saleLines\"\u002F\"s1\": 1 link(s) in \"strictLinks\"\n",[2225,172948,172949],{"class":2227,"line":2830},[2225,172950,172951],{"class":2763},"\u002F\u002F   still reference it (onDelete: 'strict').\n",[937,172953,11526,172954,172957],{},[941,172955,172956],{},"onDelete: 'warn'"," the delete is allowed and the orphan row is left in place for an audit pass:",[2217,172959,172961],{"className":2219,"code":172960,"language":2221,"meta":1037,"style":1037},"vault.link('warnLinks', { a: 'saleLines', b: 'purchaseLines', onDelete: 'warn' })\nconst warn = vault.links('warnLinks')\nawait warn.connect('s1', 'p1')\nawait sales.delete('s1') \u002F\u002F succeeds — 'warn' never blocks the delete\n\nconst { violations } = await vault.checkIntegrity()\n",[941,172962,172963,173022,173047,173076,173099,173103],{"__ignoreMap":1037},[2225,172964,172965,172967,172969,172971,172973,172975,172978,172980,172982,172984,172986,172988,172990,172992,172994,172996,172998,173000,173002,173004,173006,173008,173010,173012,173014,173016,173018,173020],{"class":2227,"line":2228},[2225,172966,2694],{"class":2239},[2225,172968,1520],{"class":2235},[2225,172970,44484],{"class":2291},[2225,172972,2789],{"class":2239},[2225,172974,2805],{"class":2235},[2225,172976,172977],{"class":2252},"warnLinks",[2225,172979,2805],{"class":2235},[2225,172981,2298],{"class":2235},[2225,172983,2236],{"class":2235},[2225,172985,79316],{"class":2307},[2225,172987,2311],{"class":2235},[2225,172989,2249],{"class":2235},[2225,172991,151268],{"class":2252},[2225,172993,2805],{"class":2235},[2225,172995,2298],{"class":2235},[2225,172997,80536],{"class":2307},[2225,172999,2311],{"class":2235},[2225,173001,2249],{"class":2235},[2225,173003,151293],{"class":2252},[2225,173005,2805],{"class":2235},[2225,173007,2298],{"class":2235},[2225,173009,172851],{"class":2307},[2225,173011,2311],{"class":2235},[2225,173013,2249],{"class":2235},[2225,173015,150565],{"class":2252},[2225,173017,2805],{"class":2235},[2225,173019,2243],{"class":2235},[2225,173021,2362],{"class":2239},[2225,173023,173024,173026,173029,173031,173033,173035,173037,173039,173041,173043,173045],{"class":2227,"line":1041},[2225,173025,2775],{"class":2774},[2225,173027,173028],{"class":2239}," warn ",[2225,173030,2781],{"class":2235},[2225,173032,6466],{"class":2239},[2225,173034,1520],{"class":2235},[2225,173036,723],{"class":2291},[2225,173038,2789],{"class":2239},[2225,173040,2805],{"class":2235},[2225,173042,172977],{"class":2252},[2225,173044,2805],{"class":2235},[2225,173046,2362],{"class":2239},[2225,173048,173049,173051,173054,173056,173058,173060,173062,173064,173066,173068,173070,173072,173074],{"class":2227,"line":1038},[2225,173050,6500],{"class":2231},[2225,173052,173053],{"class":2239}," warn",[2225,173055,1520],{"class":2235},[2225,173057,54379],{"class":2291},[2225,173059,2789],{"class":2239},[2225,173061,2805],{"class":2235},[2225,173063,151370],{"class":2252},[2225,173065,2805],{"class":2235},[2225,173067,2298],{"class":2235},[2225,173069,2249],{"class":2235},[2225,173071,150929],{"class":2252},[2225,173073,2805],{"class":2235},[2225,173075,2362],{"class":2239},[2225,173077,173078,173080,173082,173084,173086,173088,173090,173092,173094,173096],{"class":2227,"line":2288},[2225,173079,6500],{"class":2231},[2225,173081,123785],{"class":2239},[2225,173083,1520],{"class":2235},[2225,173085,3164],{"class":2291},[2225,173087,2789],{"class":2239},[2225,173089,2805],{"class":2235},[2225,173091,151370],{"class":2252},[2225,173093,2805],{"class":2235},[2225,173095,8495],{"class":2239},[2225,173097,173098],{"class":2763},"\u002F\u002F succeeds — 'warn' never blocks the delete\n",[2225,173100,173101],{"class":2227,"line":2304},[2225,173102,2285],{"emptyLinePlaceholder":2284},[2225,173104,173105,173107,173109,173111,173113,173115,173117,173119,173121,173123],{"class":2227,"line":2356},[2225,173106,2775],{"class":2774},[2225,173108,2236],{"class":2235},[2225,173110,151020],{"class":2239},[2225,173112,2359],{"class":2235},[2225,173114,6346],{"class":2235},[2225,173116,2784],{"class":2231},[2225,173118,6466],{"class":2239},[2225,173120,1520],{"class":2235},[2225,173122,151033],{"class":2291},[2225,173124,8146],{"class":2239},[2217,173126,173128],{"className":6604,"code":173127,"language":141,"meta":1037,"style":1037},"{\n  \"violations\": [\n    { \"collection\": \"_links_warnLinks\", \"id\": \"s1|p1\", \"field\": \"a\", \"refTo\": \"saleLines\", \"refId\": \"s1\", \"mode\": \"warn\" }\n  ]\n}\n",[941,173129,173130,173134,173146,173248,173252],{"__ignoreMap":1037},[2225,173131,173132],{"class":2227,"line":2228},[2225,173133,4200],{"class":2235},[2225,173135,173136,173138,173140,173142,173144],{"class":2227,"line":1041},[2225,173137,9368],{"class":2235},[2225,173139,151051],{"class":2774},[2225,173141,6620],{"class":2235},[2225,173143,2311],{"class":2235},[2225,173145,32491],{"class":2235},[2225,173147,173148,173150,173152,173154,173156,173158,173160,173163,173165,173167,173169,173171,173173,173175,173177,173180,173182,173184,173186,173188,173190,173192,173194,173196,173198,173200,173202,173204,173206,173208,173210,173212,173214,173216,173218,173220,173222,173224,173226,173228,173230,173232,173234,173236,173238,173240,173242,173244,173246],{"class":2227,"line":1038},[2225,173149,32496],{"class":2235},[2225,173151,6614],{"class":2235},[2225,173153,6471],{"class":6342},[2225,173155,6620],{"class":2235},[2225,173157,2311],{"class":2235},[2225,173159,6614],{"class":2235},[2225,173161,173162],{"class":2252},"_links_warnLinks",[2225,173164,6620],{"class":2235},[2225,173166,2298],{"class":2235},[2225,173168,6614],{"class":2235},[2225,173170,6983],{"class":6342},[2225,173172,6620],{"class":2235},[2225,173174,2311],{"class":2235},[2225,173176,6614],{"class":2235},[2225,173178,173179],{"class":2252},"s1|p1",[2225,173181,6620],{"class":2235},[2225,173183,2298],{"class":2235},[2225,173185,6614],{"class":2235},[2225,173187,25197],{"class":6342},[2225,173189,6620],{"class":2235},[2225,173191,2311],{"class":2235},[2225,173193,6614],{"class":2235},[2225,173195,1007],{"class":2252},[2225,173197,6620],{"class":2235},[2225,173199,2298],{"class":2235},[2225,173201,6614],{"class":2235},[2225,173203,150730],{"class":6342},[2225,173205,6620],{"class":2235},[2225,173207,2311],{"class":2235},[2225,173209,6614],{"class":2235},[2225,173211,151268],{"class":2252},[2225,173213,6620],{"class":2235},[2225,173215,2298],{"class":2235},[2225,173217,6614],{"class":2235},[2225,173219,150733],{"class":6342},[2225,173221,6620],{"class":2235},[2225,173223,2311],{"class":2235},[2225,173225,6614],{"class":2235},[2225,173227,151370],{"class":2252},[2225,173229,6620],{"class":2235},[2225,173231,2298],{"class":2235},[2225,173233,6614],{"class":2235},[2225,173235,120875],{"class":6342},[2225,173237,6620],{"class":2235},[2225,173239,2311],{"class":2235},[2225,173241,6614],{"class":2235},[2225,173243,150565],{"class":2252},[2225,173245,6620],{"class":2235},[2225,173247,6369],{"class":2235},[2225,173249,173250],{"class":2227,"line":2288},[2225,173251,110088],{"class":2235},[2225,173253,173254],{"class":2227,"line":2304},[2225,173255,7351],{"class":2235},[937,173257,173258,173260,173261,173263,173264,173266,173267,173270],{},[941,173259,150390],{}," walks every declared ",[941,173262,147776],{}," field ",[1092,173265,14128],{}," every link set in the vault and\nreturns the full list of dangling references instead of throwing — see\n",[1007,173268,681],{"href":173269},"\u002Fservices\u002Fforeign-refs#auditing-the-ref-graph"," for the ref-only side of the same\naudit.",[1022,173272,102889],{"id":102888},[1591,173274,173276],{"id":173275},"declaring-and-reaching-a-link-set","Declaring and reaching a link set",[1338,173278,173279,173289],{},[1341,173280,173281],{},[1344,173282,173283,173285,173287],{},[1347,173284,72632],{},[1347,173286,131736],{},[1347,173288,7834],{},[1357,173290,173291,173318,173340],{},[1344,173292,173293,173297,173302],{},[1362,173294,173295],{},[941,173296,151195],{},[1362,173298,173299],{},[941,173300,173301],{},"(name: string, spec: { a: string | RefDescriptor; b: string | RefDescriptor; onDelete?: LinkOnDelete }) => void",[1362,173303,173304,173305,173307,173308,1625,173310,173312,173313,11060,173315,173317],{},"Declare (or idempotently re-declare) a link set. Throws ",[941,173306,12881],{}," on a conflicting re-declaration, or if ",[941,173309,1007],{},[941,173311,80529],{}," isn't a simple collection name (no ",[941,173314,1625],{},[941,173316,6702],{},"-prefixed).",[1344,173319,173320,173324,173329],{},[1362,173321,173322],{},[941,173323,151203],{},[1362,173325,173326],{},[941,173327,173328],{},"(name: string) => LinkSetHandle",[1362,173330,173331,173332,102958,173334,173336,173337,1520],{},"Get the cached handle for a declared link set. Throws ",[941,173333,12881],{},[941,173335,4914],{}," was never passed to ",[941,173338,173339],{},"link()",[1344,173341,173342,173346,173351],{},[1362,173343,173344],{},[941,173345,150390],{},[1362,173347,173348],{},[941,173349,173350],{},"() => Promise\u003C{ violations: RefViolation[] }>",[1362,173352,173353,173354,173356],{},"Audit every declared ",[941,173355,147776],{}," field and every link set; returns violations instead of throwing.",[1591,173358,173360,173363,173364,173366],{"id":173359},"linksethandle-what-vaultlinksname-returns",[941,173361,173362],{},"LinkSetHandle"," (what ",[941,173365,151203],{}," returns)",[1338,173368,173369,173379],{},[1341,173370,173371],{},[1344,173372,173373,173375,173377],{},[1347,173374,7831],{},[1347,173376,131736],{},[1347,173378,7834],{},[1357,173380,173381,173402,173416,173433,173452],{},[1344,173382,173383,173387,173392],{},[1362,173384,173385],{},[941,173386,54379],{},[1362,173388,173389],{},[941,173390,173391],{},"(aId: string, bId: string, meta?: Record\u003Cstring, unknown>) => Promise\u003Cvoid>",[1362,173393,173394,173395,173398,173399,111128],{},"Create (or overwrite the metadata of) the link ",[941,173396,173397],{},"(aId, bId)",". Validates both endpoints exist first; throws ",[941,173400,173401],{},"LinkEndpointError",[1344,173403,173404,173408,173413],{},[1362,173405,173406],{},[941,173407,151463],{},[1362,173409,173410],{},[941,173411,173412],{},"(aId: string, bId: string) => Promise\u003Cvoid>",[1362,173414,173415],{},"Remove the link. Idempotent — no-op if it doesn't exist.",[1344,173417,173418,173422,173427],{},[1362,173419,173420],{},[941,173421,145202],{},[1362,173423,173424],{},[941,173425,173426],{},"(aId: string, bId: string) => Promise\u003Cboolean>",[1362,173428,173429,173430,173432],{},"Whether the link ",[941,173431,173397],{}," exists.",[1344,173434,173435,173439,173444],{},[1362,173436,173437],{},[941,173438,27066],{},[1362,173440,173441],{},[941,173442,173443],{},"(id: string) => Promise\u003CLinkRow[]>",[1362,173445,173446,173447,53922,173449,173451],{},"Every link touching ",[941,173448,6983],{},[950,173450,151523],{}," endpoint.",[1344,173453,173454,173458,173463],{},[1362,173455,173456],{},[941,173457,3167],{},[1362,173459,173460],{},[941,173461,173462],{},"() => Promise\u003CLinkRow[]>",[1362,173464,173465],{},"Every row in the set.",[1591,173467,103205],{"id":128135},[1338,173469,173470,173480],{},[1341,173471,173472],{},[1344,173473,173474,173476,173478],{},[1347,173475,72632],{},[1347,173477,50797],{},[1347,173479,7834],{},[1357,173481,173482,173505,173520],{},[1344,173483,173484,173489,173494],{},[1362,173485,173486],{},[941,173487,173488],{},"LinkSpec",[1362,173490,173491],{},[941,173492,173493],{},"{ a: string; b: string; onDelete?: LinkOnDelete }",[1362,173495,173496,173497,173499,173500,173502,173503,957],{},"The resolved declaration stored by ",[941,173498,173339],{}," (after any ",[941,173501,62282],{}," descriptor is reduced to its ",[941,173504,134984],{},[1344,173506,173507,173512,173517],{},[1362,173508,173509],{},[941,173510,173511],{},"LinkOnDelete",[1362,173513,173514],{},[941,173515,173516],{},"'cascade' | 'strict' | 'warn'",[1362,173518,173519],{},"What happens to a link's rows when one of its endpoint records is deleted.",[1344,173521,173522,173527,173532],{},[1362,173523,173524],{},[941,173525,173526],{},"LinkRow",[1362,173528,173529],{},[941,173530,173531],{},"{ a: string; b: string; meta?: Record\u003Cstring, unknown> }",[1362,173533,173534,173535,1261,173538,1520],{},"One link tuple, as returned by ",[941,173536,173537],{},"of()",[941,173539,24407],{},[1591,173541,120265],{"id":120264},[1338,173543,173544,173553],{},[1341,173545,173546],{},[1344,173547,173548,173550],{},[1347,173549,72632],{},[1347,173551,173552],{},"When it's thrown",[1357,173554,173555,173583],{},[1344,173556,173557,173561],{},[1362,173558,173559],{},[941,173560,173401],{},[1362,173562,173563,173566,173567,4688,173570,173573,173574,968,173577,968,173580,1520],{},[941,173564,173565],{},"connect()"," was called with an ",[941,173568,173569],{},"aId",[941,173571,173572],{},"bId"," that has no matching record in its endpoint collection. Carries ",[941,173575,173576],{},".link",[941,173578,173579],{},".endpoint",[941,173581,173582],{},".missingId",[1344,173584,173585,173589],{},[1362,173586,173587],{},[941,173588,151554],{},[1362,173590,5093,173591,173594,173595,968,173597,968,173599,968,173601,1520],{},[941,173592,173593],{},"onDelete: 'strict'"," link blocked the delete of an endpoint record that still has touching rows. Carries ",[941,173596,173576],{},[941,173598,173579],{},[941,173600,116501],{},[941,173602,173603],{},".count",[1022,173605,68210],{"id":68209},[1111,173607,173608,173622,173636,173650,173667],{},[1114,173609,173610,173616,173617,4688,173619,173621],{},[950,173611,173612,26623,173614],{},[941,173613,137880],{},[941,173615,150705],{}," if omitted — deleting an endpoint record silently removes\nevery link row that touches it. Set ",[941,173618,150663],{},[941,173620,51655],{}," explicitly wherever silent cascading\ndata loss would be a surprise.",[1114,173623,173624,17244,173627,173629,173630,173632,173633,173635],{},[950,173625,173626],{},"Endpoints must be simple collection names",[941,173628,1625],{}," (link sets are intra-vault only) and no\nleading ",[941,173631,6702],{}," (reserved for internal collections). ",[941,173634,173339],{}," validates this at declaration time, not\nat first use.",[1114,173637,173638,1324,173641,172778,173643,173645,173646,1261,173648,1293],{},[950,173639,173640],{},"No query-DSL join through a link set.",[941,173642,61808],{},[941,173644,147776],{}," fields; a link set's rows\nare read with ",[941,173647,151520],{},[941,173649,24407],{},[1114,173651,173652,66018,173655,173657,173658,968,173660,4765,173662,6789,173664,173666],{},[950,173653,173654],{},"A conflicting re-declaration throws.",[941,173656,173339],{}," again for the same name with a different\n",[941,173659,1007],{},[941,173661,80529],{},[941,173663,137880],{},[941,173665,12881],{}," — an identical re-declaration is a safe no-op.",[1114,173668,173669,1324,173674,104458,173676,173678,173679,1261,173681,173684],{},[950,173670,173671,173673],{},[941,173672,151199],{}," is a reserved collection name.",[941,173675,151572],{},[941,173677,6792],{},"; only ",[941,173680,6816],{},[941,173682,173683],{},"vault.links()"," may touch that namespace.",[1022,173686,2621],{"id":2620},[1111,173688,173689,173699,173709,173715,173721],{},[1114,173690,173691,964,173693,1261,173695,173698],{},[1007,173692,681],{"href":682},[941,173694,62282],{},[941,173696,173697],{},"refArray()",", and how link sets compare to a\nsimple id-array field",[1114,173700,173701,964,173703,173705,173706,173708],{},[1007,173702,28187],{"href":716},[941,173704,61808],{}," over declared ",[941,173707,147776],{}," fields (not link sets)",[1114,173710,173711,1368,173713],{},[1007,173712,3112],{"href":48},[1007,173714,21],{"href":66},[1114,173716,173717,1368,173719],{},[1007,173718,61743],{"href":598},[1007,173720,1019],{"href":1018},[1114,173722,101581,173723,1520],{},[1007,173724,173726],{"href":93993,"rel":173725},[3371],[941,173727,173728],{},"107-vault-link",[2645,173730,173731],{},"html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}",{"title":1037,"searchDepth":1038,"depth":1038,"links":173733},[173734,173735,173736,173740,173747,173748],{"id":29856,"depth":1041,"text":29857},{"id":58393,"depth":1041,"text":58394},{"id":29892,"depth":1041,"text":29893,"children":173737},[173738,173739],{"id":172417,"depth":1038,"text":172418},{"id":172792,"depth":1038,"text":172793},{"id":102888,"depth":1041,"text":102889,"children":173741},[173742,173743,173745,173746],{"id":173275,"depth":1038,"text":173276},{"id":173359,"depth":1038,"text":173744},"LinkSetHandle (what vault.links(name) returns)",{"id":128135,"depth":1038,"text":103205},{"id":120264,"depth":1038,"text":120265},{"id":68209,"depth":1041,"text":68210},{"id":2620,"depth":1041,"text":2621},"Managed many-to-many link sets between two collections — an encrypted junction queryable from either side, with per-link metadata and delete policy.",{},{"title":723,"description":173749},"o1piscOtPZzKP7lP7M8L4d8mCRy0mZbdWlMiEtjEjew",{"id":173754,"title":727,"access":12,"apiToc":929,"body":173755,"category":1042,"description":174808,"extension":1044,"meta":174809,"navigation":1042,"nest":1047,"path":728,"seo":174810,"sinceVersion":1042,"status":1049,"stem":729,"subtitle":1042,"tier":1050,"tocExternal":1042,"__hash__":174811},"docs\u002Fservices\u002Flive.md",{"type":931,"value":173756,"toc":174793},[173757,173759,173762,173764,173792,173794,173805,173807,173821,173823,174315,174317,174322,174330,174452,174467,174472,174482,174520,174650,174662,174666,174682,174686,174708,174710,174729,174731,174747,174749,174790],[934,173758,727],{"id":727},[46871,173760],{":rows":173761},"[{\"label\":\"Subpath\",\"value\":\"*(currently always-core; `@noy-db\u002Fhub\u002Flive` extraction is a planned follow-up)*\"},{\"label\":\"Factory\",\"value\":\"`withLive()` *(planned)*\"},{\"label\":\"Cluster\",\"value\":\"A — Read & Query\"},{\"label\":\"LOC cost\",\"value\":\"~210 (planned — off-bundle when not opted in)\"}]",[1022,173763,29857],{"id":29856},[937,173765,173766,173767,173771,173772,173774,173775,4688,173777,173779,173780,173784,173786,173787,173791],{},"Lets your UI react to data changes instead of polling for them. A ",[950,173768,173769],{},[1007,173770,6471],{"href":58262}," is the typed store you read and write records through; ",[941,173773,27551],{}," calls back after every ",[941,173776,3161],{},[941,173778,3164],{}," committed to it. ",[1007,173781,173782],{"href":83069},[941,173783,24715],{},[941,173785,3027],{}," goes further: it returns a value that holds the current query result and re-runs — updating that value in place — whenever anything the query depends on changes, including every collection reached through a ",[1007,173788,173789],{"href":132487},[941,173790,61808],{},". Both ship as part of the always-on core today; there is nothing to opt into yet.",[1022,173793,111660],{"id":111659},[1111,173795,173796,173799,173802],{},[1114,173797,173798],{},"Vue \u002F Pinia \u002F React stores that need to re-render on data changes",[1114,173800,173801],{},"Real-time UIs (dashboards, live feeds, presence indicators)",[1114,173803,173804],{},"Collaborative features where remote pulls should update local UI",[1022,173806,99864],{"id":99863},[937,173808,173809,173810,997,173813,173815,173816,168313,173818,173820],{},"There is nothing to opt into today — ",[941,173811,173812],{},"collection.subscribe()",[941,173814,169998],{}," ship as part of the always-on core, available on every collection and query with no extra import or wiring. The catalog reserves a ",[941,173817,72975],{},[941,173819,72969],{}," factory (~210 LOC) for a future extraction; until that lands, both are simply there.",[1022,173822,29893],{"id":29892},[2217,173824,173826],{"className":2219,"code":173825,"language":2221,"meta":1037,"style":1037},"import { createNoydb } from '@noy-db\u002Fhub'\n\ninterface Invoice {\n  id: string\n  amount: number\n  status: 'open' | 'paid'\n}\n\nconst db = await createNoydb({ user: 'me', secret: 'a-strong-secret' })\nconst vault = await db.openVault('billing')\nconst invoices = vault.collection\u003CInvoice>('invoices')\n\nfunction render(rows: readonly Invoice[]) {\n  \u002F\u002F your UI update — set a Vue ref, React state, etc.\n}\n\n\u002F\u002F Event stream: fires once per committed put\u002Fdelete\nconst stopEvents = invoices.subscribe((event) => {\n  console.log(event.type, event.id)\n})\n\nawait invoices.put('inv-1', { id: 'inv-1', amount: 100, status: 'open' })\n\u002F\u002F → put inv-1\n\n\u002F\u002F Reactive value: re-runs the query whenever a relevant write commits.\n\u002F\u002F Await it — `live()` hydrates the collection first (see the API note below).\nconst openInvoices = await invoices.query()\n  .where('status', '==', 'open')\n  .live()\n\nrender(openInvoices.value)\n\u002F\u002F → [{ id: 'inv-1', amount: 100, status: 'open' }] — subscribe() below won't fire on registration, so paint this initial value yourself\nopenInvoices.subscribe(() => render(openInvoices.value))\n\n\u002F\u002F later, e.g. on component unmount\nopenInvoices.stop()\nstopEvents()\n",[941,173827,173828,173846,173850,173858,173866,173874,173894,173898,173902,173944,173971,174001,174005,174028,174033,174037,174041,174046,174073,174100,174106,174110,174166,174171,174175,174180,174185,174204,174236,174244,174248,174261,174266,174289,174293,174298,174308],{"__ignoreMap":1037},[2225,173829,173830,173832,173834,173836,173838,173840,173842,173844],{"class":2227,"line":2228},[2225,173831,2232],{"class":2231},[2225,173833,2236],{"class":2235},[2225,173835,2744],{"class":2239},[2225,173837,2243],{"class":2235},[2225,173839,2246],{"class":2231},[2225,173841,2249],{"class":2235},[2225,173843,1086],{"class":2252},[2225,173845,2256],{"class":2235},[2225,173847,173848],{"class":2227,"line":1041},[2225,173849,2285],{"emptyLinePlaceholder":2284},[2225,173851,173852,173854,173856],{"class":2227,"line":1038},[2225,173853,6994],{"class":2774},[2225,173855,6343],{"class":6342},[2225,173857,2301],{"class":2235},[2225,173859,173860,173862,173864],{"class":2227,"line":2288},[2225,173861,28371],{"class":2307},[2225,173863,2311],{"class":2235},[2225,173865,7014],{"class":6342},[2225,173867,173868,173870,173872],{"class":2227,"line":2304},[2225,173869,28389],{"class":2307},[2225,173871,2311],{"class":2235},[2225,173873,12024],{"class":6342},[2225,173875,173876,173878,173880,173882,173884,173886,173888,173890,173892],{"class":2227,"line":2356},[2225,173877,26171],{"class":2307},[2225,173879,2311],{"class":2235},[2225,173881,2249],{"class":2235},[2225,173883,26178],{"class":2252},[2225,173885,2805],{"class":2235},[2225,173887,7087],{"class":2235},[2225,173889,2249],{"class":2235},[2225,173891,6539],{"class":2252},[2225,173893,2256],{"class":2235},[2225,173895,173896],{"class":2227,"line":2830},[2225,173897,7351],{"class":2235},[2225,173899,173900],{"class":2227,"line":2861},[2225,173901,2285],{"emptyLinePlaceholder":2284},[2225,173903,173904,173906,173908,173910,173912,173914,173916,173918,173920,173922,173924,173926,173928,173930,173932,173934,173936,173938,173940,173942],{"class":2227,"line":4246},[2225,173905,2775],{"class":2774},[2225,173907,2778],{"class":2239},[2225,173909,2781],{"class":2235},[2225,173911,2784],{"class":2231},[2225,173913,2744],{"class":2291},[2225,173915,2789],{"class":2239},[2225,173917,2792],{"class":2235},[2225,173919,2795],{"class":2307},[2225,173921,2311],{"class":2235},[2225,173923,2249],{"class":2235},[2225,173925,4212],{"class":2252},[2225,173927,2805],{"class":2235},[2225,173929,2298],{"class":2235},[2225,173931,2810],{"class":2307},[2225,173933,2311],{"class":2235},[2225,173935,2249],{"class":2235},[2225,173937,6421],{"class":2252},[2225,173939,2805],{"class":2235},[2225,173941,2243],{"class":2235},[2225,173943,2362],{"class":2239},[2225,173945,173946,173948,173950,173952,173954,173956,173958,173960,173962,173964,173967,173969],{"class":2227,"line":4253},[2225,173947,2775],{"class":2774},[2225,173949,2835],{"class":2239},[2225,173951,2781],{"class":2235},[2225,173953,2784],{"class":2231},[2225,173955,2842],{"class":2239},[2225,173957,1520],{"class":2235},[2225,173959,2847],{"class":2291},[2225,173961,2789],{"class":2239},[2225,173963,2805],{"class":2235},[2225,173965,173966],{"class":2252},"billing",[2225,173968,2805],{"class":2235},[2225,173970,2362],{"class":2239},[2225,173972,173973,173975,173977,173979,173981,173983,173985,173987,173989,173991,173993,173995,173997,173999],{"class":2227,"line":4259},[2225,173974,2775],{"class":2774},[2225,173976,6461],{"class":2239},[2225,173978,2781],{"class":2235},[2225,173980,6466],{"class":2239},[2225,173982,1520],{"class":2235},[2225,173984,6471],{"class":2291},[2225,173986,6474],{"class":2235},[2225,173988,6477],{"class":6342},[2225,173990,6480],{"class":2235},[2225,173992,2789],{"class":2239},[2225,173994,2805],{"class":2235},[2225,173996,6487],{"class":2252},[2225,173998,2805],{"class":2235},[2225,174000,2362],{"class":2239},[2225,174002,174003],{"class":2227,"line":6548},[2225,174004,2285],{"emptyLinePlaceholder":2284},[2225,174006,174007,174009,174011,174013,174016,174018,174020,174022,174024,174026],{"class":2227,"line":6576},[2225,174008,12641],{"class":2774},[2225,174010,170106],{"class":2291},[2225,174012,2789],{"class":2235},[2225,174014,174015],{"class":7052},"rows",[2225,174017,2311],{"class":2235},[2225,174019,112906],{"class":2774},[2225,174021,6343],{"class":6342},[2225,174023,7262],{"class":2239},[2225,174025,1381],{"class":2235},[2225,174027,2301],{"class":2235},[2225,174029,174030],{"class":2227,"line":6582},[2225,174031,174032],{"class":2763},"  \u002F\u002F your UI update — set a Vue ref, React state, etc.\n",[2225,174034,174035],{"class":2227,"line":6587},[2225,174036,7351],{"class":2235},[2225,174038,174039],{"class":2227,"line":7174},[2225,174040,2285],{"emptyLinePlaceholder":2284},[2225,174042,174043],{"class":2227,"line":7180},[2225,174044,174045],{"class":2763},"\u002F\u002F Event stream: fires once per committed put\u002Fdelete\n",[2225,174047,174048,174050,174053,174055,174057,174059,174061,174063,174065,174067,174069,174071],{"class":2227,"line":7220},[2225,174049,2775],{"class":2774},[2225,174051,174052],{"class":2239}," stopEvents ",[2225,174054,2781],{"class":2235},[2225,174056,6503],{"class":2239},[2225,174058,1520],{"class":2235},[2225,174060,25042],{"class":2291},[2225,174062,2789],{"class":2239},[2225,174064,2789],{"class":2235},[2225,174066,12307],{"class":7052},[2225,174068,1381],{"class":2235},[2225,174070,8116],{"class":2774},[2225,174072,2301],{"class":2235},[2225,174074,174075,174077,174079,174081,174083,174085,174087,174089,174091,174094,174096,174098],{"class":2227,"line":7225},[2225,174076,27095],{"class":2239},[2225,174078,1520],{"class":2235},[2225,174080,27100],{"class":2291},[2225,174082,2789],{"class":2307},[2225,174084,12307],{"class":2239},[2225,174086,1520],{"class":2235},[2225,174088,6339],{"class":2239},[2225,174090,2298],{"class":2235},[2225,174092,174093],{"class":2239}," event",[2225,174095,1520],{"class":2235},[2225,174097,6983],{"class":2239},[2225,174099,2362],{"class":2307},[2225,174101,174102,174104],{"class":2227,"line":7231},[2225,174103,2359],{"class":2235},[2225,174105,2362],{"class":2239},[2225,174107,174108],{"class":2227,"line":7267},[2225,174109,2285],{"emptyLinePlaceholder":2284},[2225,174111,174112,174114,174116,174118,174120,174122,174124,174126,174128,174130,174132,174134,174136,174138,174140,174142,174144,174146,174148,174150,174152,174154,174156,174158,174160,174162,174164],{"class":2227,"line":7272},[2225,174113,6500],{"class":2231},[2225,174115,6503],{"class":2239},[2225,174117,1520],{"class":2235},[2225,174119,3161],{"class":2291},[2225,174121,2789],{"class":2239},[2225,174123,2805],{"class":2235},[2225,174125,6514],{"class":2252},[2225,174127,2805],{"class":2235},[2225,174129,2298],{"class":2235},[2225,174131,2236],{"class":2235},[2225,174133,7070],{"class":2307},[2225,174135,2311],{"class":2235},[2225,174137,2249],{"class":2235},[2225,174139,6514],{"class":2252},[2225,174141,2805],{"class":2235},[2225,174143,2298],{"class":2235},[2225,174145,6351],{"class":2307},[2225,174147,2311],{"class":2235},[2225,174149,6528],{"class":6527},[2225,174151,2298],{"class":2235},[2225,174153,6361],{"class":2307},[2225,174155,2311],{"class":2235},[2225,174157,2249],{"class":2235},[2225,174159,26178],{"class":2252},[2225,174161,2805],{"class":2235},[2225,174163,2243],{"class":2235},[2225,174165,2362],{"class":2239},[2225,174167,174168],{"class":2227,"line":7278},[2225,174169,174170],{"class":2763},"\u002F\u002F → put inv-1\n",[2225,174172,174173],{"class":2227,"line":7303},[2225,174174,2285],{"emptyLinePlaceholder":2284},[2225,174176,174177],{"class":2227,"line":7308},[2225,174178,174179],{"class":2763},"\u002F\u002F Reactive value: re-runs the query whenever a relevant write commits.\n",[2225,174181,174182],{"class":2227,"line":7314},[2225,174183,174184],{"class":2763},"\u002F\u002F Await it — `live()` hydrates the collection first (see the API note below).\n",[2225,174186,174187,174189,174192,174194,174196,174198,174200,174202],{"class":2227,"line":7348},[2225,174188,2775],{"class":2774},[2225,174190,174191],{"class":2239}," openInvoices ",[2225,174193,2781],{"class":2235},[2225,174195,2784],{"class":2231},[2225,174197,6503],{"class":2239},[2225,174199,1520],{"class":2235},[2225,174201,5286],{"class":2291},[2225,174203,8146],{"class":2239},[2225,174205,174206,174208,174210,174212,174214,174216,174218,174220,174222,174224,174226,174228,174230,174232,174234],{"class":2227,"line":8613},[2225,174207,26235],{"class":2235},[2225,174209,3016],{"class":2291},[2225,174211,2789],{"class":2239},[2225,174213,2805],{"class":2235},[2225,174215,6631],{"class":2252},[2225,174217,2805],{"class":2235},[2225,174219,2298],{"class":2235},[2225,174221,2249],{"class":2235},[2225,174223,21901],{"class":2252},[2225,174225,2805],{"class":2235},[2225,174227,2298],{"class":2235},[2225,174229,2249],{"class":2235},[2225,174231,26178],{"class":2252},[2225,174233,2805],{"class":2235},[2225,174235,2362],{"class":2239},[2225,174237,174238,174240,174242],{"class":2227,"line":8633},[2225,174239,26235],{"class":2235},[2225,174241,727],{"class":2291},[2225,174243,8146],{"class":2239},[2225,174245,174246],{"class":2227,"line":8662},[2225,174247,2285],{"emptyLinePlaceholder":2284},[2225,174249,174250,174253,174256,174258],{"class":2227,"line":8747},[2225,174251,174252],{"class":2291},"render",[2225,174254,174255],{"class":2239},"(openInvoices",[2225,174257,1520],{"class":2235},[2225,174259,174260],{"class":2239},"value)\n",[2225,174262,174263],{"class":2227,"line":8752},[2225,174264,174265],{"class":2763},"\u002F\u002F → [{ id: 'inv-1', amount: 100, status: 'open' }] — subscribe() below won't fire on registration, so paint this initial value yourself\n",[2225,174267,174268,174271,174273,174275,174277,174279,174281,174283,174285,174287],{"class":2227,"line":8768},[2225,174269,174270],{"class":2239},"openInvoices",[2225,174272,1520],{"class":2235},[2225,174274,25042],{"class":2291},[2225,174276,2789],{"class":2239},[2225,174278,4228],{"class":2235},[2225,174280,8116],{"class":2774},[2225,174282,170106],{"class":2291},[2225,174284,174255],{"class":2239},[2225,174286,1520],{"class":2235},[2225,174288,170114],{"class":2239},[2225,174290,174291],{"class":2227,"line":8785},[2225,174292,2285],{"emptyLinePlaceholder":2284},[2225,174294,174295],{"class":2227,"line":8815},[2225,174296,174297],{"class":2763},"\u002F\u002F later, e.g. on component unmount\n",[2225,174299,174300,174302,174304,174306],{"class":2227,"line":8854},[2225,174301,174270],{"class":2239},[2225,174303,1520],{"class":2235},[2225,174305,27604],{"class":2291},[2225,174307,8146],{"class":2239},[2225,174309,174310,174313],{"class":2227,"line":8874},[2225,174311,174312],{"class":2291},"stopEvents",[2225,174314,8146],{"class":2239},[1022,174316,101012],{"id":41259},[1591,174318,174320],{"id":174319},"collectionsubscribecb",[941,174321,27551],{},[937,174323,174324,174325,4688,174327,174329],{},"Registers a callback that fires after every committed ",[941,174326,3161],{},[941,174328,3164],{}," on the collection. Returns an unsubscribe function.",[2217,174331,174333],{"className":2219,"code":174332,"language":2221,"meta":1037,"style":1037},"const stop = invoices.subscribe((event) => {\n  console.log(event)\n})\n\nawait invoices.put('inv-1', { id: 'inv-1', amount: 100, status: 'open' })\n\u002F\u002F → { type: 'put', id: 'inv-1', record: { id: 'inv-1', amount: 100, status: 'open' } }\nstop()\n",[941,174334,174335,174361,174375,174381,174385,174441,174446],{"__ignoreMap":1037},[2225,174336,174337,174339,174341,174343,174345,174347,174349,174351,174353,174355,174357,174359],{"class":2227,"line":2228},[2225,174338,2775],{"class":2774},[2225,174340,27567],{"class":2239},[2225,174342,2781],{"class":2235},[2225,174344,6503],{"class":2239},[2225,174346,1520],{"class":2235},[2225,174348,25042],{"class":2291},[2225,174350,2789],{"class":2239},[2225,174352,2789],{"class":2235},[2225,174354,12307],{"class":7052},[2225,174356,1381],{"class":2235},[2225,174358,8116],{"class":2774},[2225,174360,2301],{"class":2235},[2225,174362,174363,174365,174367,174369,174371,174373],{"class":2227,"line":1041},[2225,174364,27095],{"class":2239},[2225,174366,1520],{"class":2235},[2225,174368,27100],{"class":2291},[2225,174370,2789],{"class":2307},[2225,174372,12307],{"class":2239},[2225,174374,2362],{"class":2307},[2225,174376,174377,174379],{"class":2227,"line":1038},[2225,174378,2359],{"class":2235},[2225,174380,2362],{"class":2239},[2225,174382,174383],{"class":2227,"line":2288},[2225,174384,2285],{"emptyLinePlaceholder":2284},[2225,174386,174387,174389,174391,174393,174395,174397,174399,174401,174403,174405,174407,174409,174411,174413,174415,174417,174419,174421,174423,174425,174427,174429,174431,174433,174435,174437,174439],{"class":2227,"line":2304},[2225,174388,6500],{"class":2231},[2225,174390,6503],{"class":2239},[2225,174392,1520],{"class":2235},[2225,174394,3161],{"class":2291},[2225,174396,2789],{"class":2239},[2225,174398,2805],{"class":2235},[2225,174400,6514],{"class":2252},[2225,174402,2805],{"class":2235},[2225,174404,2298],{"class":2235},[2225,174406,2236],{"class":2235},[2225,174408,7070],{"class":2307},[2225,174410,2311],{"class":2235},[2225,174412,2249],{"class":2235},[2225,174414,6514],{"class":2252},[2225,174416,2805],{"class":2235},[2225,174418,2298],{"class":2235},[2225,174420,6351],{"class":2307},[2225,174422,2311],{"class":2235},[2225,174424,6528],{"class":6527},[2225,174426,2298],{"class":2235},[2225,174428,6361],{"class":2307},[2225,174430,2311],{"class":2235},[2225,174432,2249],{"class":2235},[2225,174434,26178],{"class":2252},[2225,174436,2805],{"class":2235},[2225,174438,2243],{"class":2235},[2225,174440,2362],{"class":2239},[2225,174442,174443],{"class":2227,"line":2356},[2225,174444,174445],{"class":2763},"\u002F\u002F → { type: 'put', id: 'inv-1', record: { id: 'inv-1', amount: 100, status: 'open' } }\n",[2225,174447,174448,174450],{"class":2227,"line":2830},[2225,174449,27604],{"class":2291},[2225,174451,8146],{"class":2239},[937,174453,174454,174457,174458,36059,174460,174462,174463,53922,174465,1520],{},[941,174455,174456],{},"event.record"," is the current decrypted value on ",[941,174459,3161],{},[941,174461,6651],{}," in the rare case another op deleted the record before the lookup finished); it is always ",[941,174464,6651],{},[941,174466,3164],{},[1591,174468,174470],{"id":174469},"querylive",[941,174471,169998],{},[937,174473,174474,174475,174478,174479,174481],{},"Returns a ",[941,174476,174477],{},"LiveQuery\u003CT>"," — a reactive value that re-runs the query whenever its collection, or any ",[941,174480,61808],{}," target, changes.",[937,174483,174484,174485,174488,174489,174492,174493,1261,174496,174499,174500,174502,174503,174505,174506,174509,174510,174513,174514,174516,174517,174519],{},"::: warning Await it, or load the collection first\n",[941,174486,174487],{},"live()"," must hydrate the collection before it can answer. ",[950,174490,174491],{},"Await the terminal"," — or call ",[941,174494,174495],{},"await collection.list()",[941,174497,174498],{},"await collection.get(id)"," beforehand, which works just as well (both verified against ",[941,174501,1086],{}," 0.8.0-pre.0). Reading any property of an un-awaited ",[941,174504,174487],{}," on an unloaded collection throws ",[941,174507,174508],{},"CollectionNotHydratedError",", because ",[950,174511,174512],{},"an unloaded collection is not an empty one"," — its snapshot is empty by absence, not by content, and a UI bound to it would render \"no records\" for data that simply had not loaded. Note the awaited value is a ",[1092,174515,136978],{}," object from the one ",[941,174518,174487],{}," returns synchronously; keep the awaited one.\n:::",[2217,174521,174523],{"className":2219,"code":174522,"language":2221,"meta":1037,"style":1037},"const live = await invoices.query().where('status', '==', 'open').live()\n\nlive.value              \u002F\u002F → readonly Invoice[] — current result, updated in place on every change\nlive.error              \u002F\u002F → Error | null — set if the most recent re-run threw\nconst unsub = live.subscribe(() => render(live.value))\nlive.stop()             \u002F\u002F tears down every upstream subscription; also makes subscribe() a no-op\n",[941,174524,174525,174579,174583,174595,174607,174636],{"__ignoreMap":1037},[2225,174526,174527,174529,174531,174533,174535,174537,174539,174541,174543,174545,174547,174549,174551,174553,174555,174557,174559,174561,174563,174565,174567,174569,174571,174573,174575,174577],{"class":2227,"line":2228},[2225,174528,2775],{"class":2774},[2225,174530,134114],{"class":2239},[2225,174532,2781],{"class":2235},[2225,174534,2784],{"class":2231},[2225,174536,6503],{"class":2239},[2225,174538,1520],{"class":2235},[2225,174540,5286],{"class":2291},[2225,174542,4228],{"class":2239},[2225,174544,1520],{"class":2235},[2225,174546,3016],{"class":2291},[2225,174548,2789],{"class":2239},[2225,174550,2805],{"class":2235},[2225,174552,6631],{"class":2252},[2225,174554,2805],{"class":2235},[2225,174556,2298],{"class":2235},[2225,174558,2249],{"class":2235},[2225,174560,21901],{"class":2252},[2225,174562,2805],{"class":2235},[2225,174564,2298],{"class":2235},[2225,174566,2249],{"class":2235},[2225,174568,26178],{"class":2252},[2225,174570,2805],{"class":2235},[2225,174572,1381],{"class":2239},[2225,174574,1520],{"class":2235},[2225,174576,727],{"class":2291},[2225,174578,8146],{"class":2239},[2225,174580,174581],{"class":2227,"line":1041},[2225,174582,2285],{"emptyLinePlaceholder":2284},[2225,174584,174585,174587,174589,174592],{"class":2227,"line":1038},[2225,174586,727],{"class":2239},[2225,174588,1520],{"class":2235},[2225,174590,174591],{"class":2239},"value              ",[2225,174593,174594],{"class":2763},"\u002F\u002F → readonly Invoice[] — current result, updated in place on every change\n",[2225,174596,174597,174599,174601,174604],{"class":2227,"line":2288},[2225,174598,727],{"class":2239},[2225,174600,1520],{"class":2235},[2225,174602,174603],{"class":2239},"error              ",[2225,174605,174606],{"class":2763},"\u002F\u002F → Error | null — set if the most recent re-run threw\n",[2225,174608,174609,174611,174614,174616,174618,174620,174622,174624,174626,174628,174630,174632,174634],{"class":2227,"line":2304},[2225,174610,2775],{"class":2774},[2225,174612,174613],{"class":2239}," unsub ",[2225,174615,2781],{"class":2235},[2225,174617,99296],{"class":2239},[2225,174619,1520],{"class":2235},[2225,174621,25042],{"class":2291},[2225,174623,2789],{"class":2239},[2225,174625,4228],{"class":2235},[2225,174627,8116],{"class":2774},[2225,174629,170106],{"class":2291},[2225,174631,170109],{"class":2239},[2225,174633,1520],{"class":2235},[2225,174635,170114],{"class":2239},[2225,174637,174638,174640,174642,174644,174647],{"class":2227,"line":2356},[2225,174639,727],{"class":2239},[2225,174641,1520],{"class":2235},[2225,174643,27604],{"class":2291},[2225,174645,174646],{"class":2239},"()             ",[2225,174648,174649],{"class":2763},"\u002F\u002F tears down every upstream subscription; also makes subscribe() a no-op\n",[937,174651,174652,174655,174656,174658,174659,174661],{},[941,174653,174654],{},".subscribe()"," does not fire immediately on registration — read ",[941,174657,170004],{}," directly for the initial paint. Always call ",[941,174660,64363],{}," when the live query is no longer needed, or its upstream subscriptions keep re-running it forever.",[1591,174663,174665],{"id":174664},"framework-bindings","Framework bindings",[937,174667,174668,968,174670,174672,174673,997,174675,174677,174678,174681],{},[941,174669,42079],{},[941,174671,38573],{},", and similar packages build their reactive hooks on the same change-event stream that ",[941,174674,25045],{},[941,174676,3027],{}," expose, so app code usually binds to a Vue ",[941,174679,174680],{},"Ref"," or a React hook instead of calling these directly.",[1022,174683,174685],{"id":174684},"behavior-when-not-opted-in-post-extraction","Behavior when NOT opted in (post-extraction)",[1111,174687,174688,174698],{},[1114,174689,174690,997,174693,174695,174696],{},[941,174691,174692],{},"collection.subscribe(...)",[941,174694,169998],{}," will throw with a pointer to ",[941,174697,72975],{},[1114,174699,174700,174701,968,174703,968,174705,174707],{},"One-shot reads (",[941,174702,164984],{},[941,174704,164987],{},[941,174706,65247],{},") stay in core",[1022,174709,114684],{"id":114683},[1111,174711,174712,174717,174723],{},[1114,174713,174714,174716],{},[1007,174715,715],{"href":132487}," — reactive joins merge change-streams from every join target",[1114,174718,174719,174722],{},[1007,174720,653],{"href":174721},".\u002Fcrdt"," — CRDT collection updates surface through live queries",[1114,174724,174725,174728],{},[1007,174726,864],{"href":174727},".\u002Fsync"," — pulls fire change events, propagating to live subscribers",[1022,174730,6825],{"id":6824},[1111,174732,174733,174736,174744],{},[1114,174734,174735],{},"Subscriptions are in-process; cross-tab \u002F cross-process reactivity needs an external coordinator (BroadcastChannel, sync engine)",[1114,174737,174738,174740,174741,174743],{},[941,174739,174487],{}," re-runs the query on every change — for hot collections, prefer ",[941,174742,25045],{}," + manual delta application",[1114,174745,174746],{},"Stop subscriptions on unmount to avoid memory leaks",[1022,174748,2621],{"id":2620},[1111,174750,174751,174758,174763,174768,174782],{},[1114,174752,174753,964,174755,174757],{},[1007,174754,715],{"href":132487},[941,174756,61808],{}," targets are part of what a live query watches",[1114,174759,174760,174762],{},[1007,174761,653],{"href":174721}," — CRDT merges surface to the UI through the same live-query mechanism",[1114,174764,174765,174767],{},[1007,174766,864],{"href":174727}," — pulls fire the change events that live queries and subscriptions react to",[1114,174769,174770,1169,174772,1625,174774,1625,174776,174778,174779,174781],{},[1007,174771,3216],{"href":83069},[941,174773,3016],{},[941,174775,8686],{},[941,174777,3019],{}," pipeline ",[941,174780,3027],{}," wraps",[1114,174783,101581,174784,1520],{},[1007,174785,174787],{"href":92658,"rel":174786},[3371],[941,174788,174789],{},"13-with-live",[2645,174791,174792],{},"html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .sHdIc, html code.shiki .sHdIc{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#EEFFFF;--shiki-default-font-style:italic;--shiki-dark:#BABED8;--shiki-dark-font-style:italic}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":1037,"searchDepth":1038,"depth":1038,"links":174794},[174795,174796,174797,174798,174799,174804,174805,174806,174807],{"id":29856,"depth":1041,"text":29857},{"id":111659,"depth":1041,"text":111660},{"id":99863,"depth":1041,"text":99864},{"id":29892,"depth":1041,"text":29893},{"id":41259,"depth":1041,"text":101012,"children":174800},[174801,174802,174803],{"id":174319,"depth":1038,"text":27551},{"id":174469,"depth":1038,"text":169998},{"id":174664,"depth":1038,"text":174665},{"id":174684,"depth":1041,"text":174685},{"id":114683,"depth":1041,"text":114684},{"id":6824,"depth":1041,"text":6825},{"id":2620,"depth":1041,"text":2621},"Reactive subscriptions: `collection.subscribe()` fires after every committed write, and `query.live()` re-runs and updates in place as its collections change.",{},{"title":727,"description":174808},"e2zzhmo7498lmXodSCWvCjYMkytNfcuxt9l4HZ8gYoo",{"id":174813,"title":731,"access":12,"apiToc":929,"body":174814,"category":1042,"description":178321,"extension":1044,"meta":178322,"navigation":1042,"nest":1047,"path":732,"seo":178323,"sinceVersion":111464,"status":38550,"stem":733,"subtitle":178324,"tier":1050,"tocExternal":1042,"__hash__":178325},"docs\u002Fservices\u002Flookup.md",{"type":931,"value":174815,"toc":178292},[174816,174818,174821,174823,174859,174883,174887,174897,175494,175517,175520,175695,175701,175761,175763,175770,175868,175893,175897,175930,175946,175950,175959,176023,176026,176029,176083,176100,176104,176115,176332,176340,176520,176534,176541,176558,176654,176679,176683,176686,176999,177030,177040,177045,177372,177399,177434,177446,177461,177465,177481,177531,177537,177541,177571,177579,177600,177679,177702,177706,177758,177762,177784,177800,177815,177817,177823,177828,178055,178064,178076,178082,178111,178113,178180,178182,178235,178237,178289],[934,174817,731],{"id":731},[46871,174819],{":rows":174820},"[{\"label\":\"Subpath\",\"value\":\"`@noy-db\u002Fhub` (root export — `lookup`, `enumOf` (also exported as `enum`), `dict`, `UnknownLookupKeyError`, `DictKeyInUseError`)\"},{\"label\":\"Wiring\",\"value\":\"no `with*()` strategy — declare `lookupFields: { field: lookup(...) }` (or `viaFields: { field: via(lookup(...)) }`) on `vault.collection()`\"},{\"label\":\"Kind\",\"value\":\"field feature — declared per field through the `via` surface\"}]",[1022,174822,29857],{"id":29856},[937,174824,174825,174826,953,174828,968,174831,174833,174834,174837,174838,174840,174841,1399,174843,1324,174845,174847,174848,174851,174852,174854,174855,174858],{},"A lookup field stores a stable ",[950,174827,941],{},[941,174829,174830],{},"'US'",[941,174832,58792],{},") and resolves everything else — display\nlabel, membership, ordering, reference integrity — against a ",[950,174835,174836],{},"backing dimension",". One\ndeclaration covers all three places such dimensions live: inline in code (",[941,174839,163868],{},"), in a\nper-vault editable dictionary (",[941,174842,163871],{},[1007,174844,701],{"href":702},[941,174846,6796],{}," machinery), or in a\n",[950,174849,174850],{},"first-class collection"," of the vault itself (",[941,174853,163865],{}," — a \"matrix\" dimension like a\n",[941,174856,174857],{},"countries"," collection, with as many columns as you like).",[937,174860,174861,174862,174865,174866,174869,174870,953,174873,1261,174875,1261,174877,174879,174880,957],{},"Beyond code→label resolution it adds three things codes alone can't do: ",[950,174863,174864],{},"altKeys"," (alternate\nidentifiers that normalize to the canonical key on write — an ISO3 country code, a phone call\nprefix), ",[950,174867,174868],{},"closed vocabulary"," (write-time refusal of unknown keys, checked against the\ndimension's live rows), and ",[950,174871,174872],{},"reference semantics",[941,174874,152822],{},[941,174876,10753],{},[941,174878,152838],{}," when a\nstill-referenced backing row is deleted or ",[1007,174881,174882],{"href":686},"forgotten",[1022,174884,174886],{"id":174885},"the-canonical-example-a-countries-matrix","The canonical example — a countries matrix",[937,174888,174889,174890,174892,174893,174896],{},"The reference pattern for lookup dimensions is a first-class ",[941,174891,174857],{}," collection: ISO2 as the\ncanonical key, ISO3 and call prefix as alternate keys, localized names — and ",[950,174894,174895],{},"sparse, populated\nonly with what you use"," (add a row for a country the day you first need it; no schema migration,\nno 249-row seed):",[2217,174898,174900],{"className":2219,"code":174899,"language":2221,"meta":1037,"style":1037},"import { lookup } from '@noy-db\u002Fhub'\n\ninterface Country { id: string; iso2: string; iso3: string; callPrefix: string; name: Record\u003Cstring, string> }\ninterface Order { id: string; country: string }\n\nconst countries = vault.collection\u003CCountry>('countries', {})\nawait countries.put('row-US', { id: 'row-US', iso2: 'US', iso3: 'USA', callPrefix: '+1',\n  name: { en: 'United States', th: 'สหรัฐอเมริกา' } })\nawait countries.put('row-TH', { id: 'row-TH', iso2: 'TH', iso3: 'THA', callPrefix: '+66',\n  name: { en: 'Thailand', th: 'ประเทศไทย' } })\n\nconst orders = vault.collection\u003COrder>('orders', {\n  lookupFields: {\n    country: lookup('countries', {\n      key: 'iso2',                              \u002F\u002F the canonical key FIELD — not the row's storage id\n      altKeys: ['iso3', 'callPrefix'],          \u002F\u002F candidate values that normalize to `key`\n      present: { label: 'name', by: 'locale' }, \u002F\u002F \u003Cfield>Label resolves row.name[locale]\n      sortBy: 'name',\n      backing: 'collection',\n      vocabulary: 'closed',\n    }),\n  },\n})\n",[941,174901,174902,174921,174925,174987,175013,175017,175053,175127,175165,175239,175277,175281,175313,175322,175343,175362,175394,175431,175446,175461,175476,175484,175488],{"__ignoreMap":1037},[2225,174903,174904,174906,174908,174911,174913,174915,174917,174919],{"class":2227,"line":2228},[2225,174905,2232],{"class":2231},[2225,174907,2236],{"class":2235},[2225,174909,174910],{"class":2239}," lookup",[2225,174912,2243],{"class":2235},[2225,174914,2246],{"class":2231},[2225,174916,2249],{"class":2235},[2225,174918,1086],{"class":2252},[2225,174920,2256],{"class":2235},[2225,174922,174923],{"class":2227,"line":1041},[2225,174924,2285],{"emptyLinePlaceholder":2284},[2225,174926,174927,174929,174932,174934,174936,174938,174940,174942,174945,174947,174949,174951,174954,174956,174958,174960,174963,174965,174967,174969,174971,174973,174975,174977,174979,174981,174983,174985],{"class":2227,"line":1038},[2225,174928,6994],{"class":2774},[2225,174930,174931],{"class":6342}," Country",[2225,174933,2236],{"class":2235},[2225,174935,7070],{"class":2307},[2225,174937,2311],{"class":2235},[2225,174939,6366],{"class":6342},[2225,174941,3769],{"class":2235},[2225,174943,174944],{"class":2307}," iso2",[2225,174946,2311],{"class":2235},[2225,174948,6366],{"class":6342},[2225,174950,3769],{"class":2235},[2225,174952,174953],{"class":2307}," iso3",[2225,174955,2311],{"class":2235},[2225,174957,6366],{"class":6342},[2225,174959,3769],{"class":2235},[2225,174961,174962],{"class":2307}," callPrefix",[2225,174964,2311],{"class":2235},[2225,174966,6366],{"class":6342},[2225,174968,3769],{"class":2235},[2225,174970,8104],{"class":2307},[2225,174972,2311],{"class":2235},[2225,174974,22527],{"class":6342},[2225,174976,6474],{"class":2235},[2225,174978,7259],{"class":6342},[2225,174980,2298],{"class":2235},[2225,174982,6366],{"class":6342},[2225,174984,6480],{"class":2235},[2225,174986,6369],{"class":2235},[2225,174988,174989,174991,174994,174996,174998,175000,175002,175004,175007,175009,175011],{"class":2227,"line":2288},[2225,174990,6994],{"class":2774},[2225,174992,174993],{"class":6342}," Order",[2225,174995,2236],{"class":2235},[2225,174997,7070],{"class":2307},[2225,174999,2311],{"class":2235},[2225,175001,6366],{"class":6342},[2225,175003,3769],{"class":2235},[2225,175005,175006],{"class":2307}," country",[2225,175008,2311],{"class":2235},[2225,175010,6366],{"class":6342},[2225,175012,6369],{"class":2235},[2225,175014,175015],{"class":2227,"line":2304},[2225,175016,2285],{"emptyLinePlaceholder":2284},[2225,175018,175019,175021,175024,175026,175028,175030,175032,175034,175037,175039,175041,175043,175045,175047,175049,175051],{"class":2227,"line":2356},[2225,175020,2775],{"class":2774},[2225,175022,175023],{"class":2239}," countries ",[2225,175025,2781],{"class":2235},[2225,175027,6466],{"class":2239},[2225,175029,1520],{"class":2235},[2225,175031,6471],{"class":2291},[2225,175033,6474],{"class":2235},[2225,175035,175036],{"class":6342},"Country",[2225,175038,6480],{"class":2235},[2225,175040,2789],{"class":2239},[2225,175042,2805],{"class":2235},[2225,175044,174857],{"class":2252},[2225,175046,2805],{"class":2235},[2225,175048,2298],{"class":2235},[2225,175050,8896],{"class":2235},[2225,175052,2362],{"class":2239},[2225,175054,175055,175057,175060,175062,175064,175066,175068,175071,175073,175075,175077,175079,175081,175083,175085,175087,175089,175091,175093,175095,175097,175099,175101,175103,175105,175107,175110,175112,175114,175116,175118,175120,175123,175125],{"class":2227,"line":2830},[2225,175056,6500],{"class":2231},[2225,175058,175059],{"class":2239}," countries",[2225,175061,1520],{"class":2235},[2225,175063,3161],{"class":2291},[2225,175065,2789],{"class":2239},[2225,175067,2805],{"class":2235},[2225,175069,175070],{"class":2252},"row-US",[2225,175072,2805],{"class":2235},[2225,175074,2298],{"class":2235},[2225,175076,2236],{"class":2235},[2225,175078,7070],{"class":2307},[2225,175080,2311],{"class":2235},[2225,175082,2249],{"class":2235},[2225,175084,175070],{"class":2252},[2225,175086,2805],{"class":2235},[2225,175088,2298],{"class":2235},[2225,175090,174944],{"class":2307},[2225,175092,2311],{"class":2235},[2225,175094,2249],{"class":2235},[2225,175096,62477],{"class":2252},[2225,175098,2805],{"class":2235},[2225,175100,2298],{"class":2235},[2225,175102,174953],{"class":2307},[2225,175104,2311],{"class":2235},[2225,175106,2249],{"class":2235},[2225,175108,175109],{"class":2252},"USA",[2225,175111,2805],{"class":2235},[2225,175113,2298],{"class":2235},[2225,175115,174962],{"class":2307},[2225,175117,2311],{"class":2235},[2225,175119,2249],{"class":2235},[2225,175121,175122],{"class":2252},"+1",[2225,175124,2805],{"class":2235},[2225,175126,1707],{"class":2235},[2225,175128,175129,175131,175133,175135,175137,175139,175141,175144,175146,175148,175150,175152,175154,175157,175159,175161,175163],{"class":2227,"line":2861},[2225,175130,7008],{"class":2307},[2225,175132,2311],{"class":2235},[2225,175134,2236],{"class":2235},[2225,175136,58997],{"class":2307},[2225,175138,2311],{"class":2235},[2225,175140,2249],{"class":2235},[2225,175142,175143],{"class":2252},"United States",[2225,175145,2805],{"class":2235},[2225,175147,2298],{"class":2235},[2225,175149,60621],{"class":2307},[2225,175151,2311],{"class":2235},[2225,175153,2249],{"class":2235},[2225,175155,175156],{"class":2252},"สหรัฐอเมริกา",[2225,175158,2805],{"class":2235},[2225,175160,2243],{"class":2235},[2225,175162,2243],{"class":2235},[2225,175164,2362],{"class":2239},[2225,175166,175167,175169,175171,175173,175175,175177,175179,175182,175184,175186,175188,175190,175192,175194,175196,175198,175200,175202,175204,175206,175209,175211,175213,175215,175217,175219,175222,175224,175226,175228,175230,175232,175235,175237],{"class":2227,"line":4246},[2225,175168,6500],{"class":2231},[2225,175170,175059],{"class":2239},[2225,175172,1520],{"class":2235},[2225,175174,3161],{"class":2291},[2225,175176,2789],{"class":2239},[2225,175178,2805],{"class":2235},[2225,175180,175181],{"class":2252},"row-TH",[2225,175183,2805],{"class":2235},[2225,175185,2298],{"class":2235},[2225,175187,2236],{"class":2235},[2225,175189,7070],{"class":2307},[2225,175191,2311],{"class":2235},[2225,175193,2249],{"class":2235},[2225,175195,175181],{"class":2252},[2225,175197,2805],{"class":2235},[2225,175199,2298],{"class":2235},[2225,175201,174944],{"class":2307},[2225,175203,2311],{"class":2235},[2225,175205,2249],{"class":2235},[2225,175207,175208],{"class":2252},"TH",[2225,175210,2805],{"class":2235},[2225,175212,2298],{"class":2235},[2225,175214,174953],{"class":2307},[2225,175216,2311],{"class":2235},[2225,175218,2249],{"class":2235},[2225,175220,175221],{"class":2252},"THA",[2225,175223,2805],{"class":2235},[2225,175225,2298],{"class":2235},[2225,175227,174962],{"class":2307},[2225,175229,2311],{"class":2235},[2225,175231,2249],{"class":2235},[2225,175233,175234],{"class":2252},"+66",[2225,175236,2805],{"class":2235},[2225,175238,1707],{"class":2235},[2225,175240,175241,175243,175245,175247,175249,175251,175253,175256,175258,175260,175262,175264,175266,175269,175271,175273,175275],{"class":2227,"line":4253},[2225,175242,7008],{"class":2307},[2225,175244,2311],{"class":2235},[2225,175246,2236],{"class":2235},[2225,175248,58997],{"class":2307},[2225,175250,2311],{"class":2235},[2225,175252,2249],{"class":2235},[2225,175254,175255],{"class":2252},"Thailand",[2225,175257,2805],{"class":2235},[2225,175259,2298],{"class":2235},[2225,175261,60621],{"class":2307},[2225,175263,2311],{"class":2235},[2225,175265,2249],{"class":2235},[2225,175267,175268],{"class":2252},"ประเทศไทย",[2225,175270,2805],{"class":2235},[2225,175272,2243],{"class":2235},[2225,175274,2243],{"class":2235},[2225,175276,2362],{"class":2239},[2225,175278,175279],{"class":2227,"line":4259},[2225,175280,2285],{"emptyLinePlaceholder":2284},[2225,175282,175283,175285,175287,175289,175291,175293,175295,175297,175299,175301,175303,175305,175307,175309,175311],{"class":2227,"line":6548},[2225,175284,2775],{"class":2774},[2225,175286,150809],{"class":2239},[2225,175288,2781],{"class":2235},[2225,175290,6466],{"class":2239},[2225,175292,1520],{"class":2235},[2225,175294,6471],{"class":2291},[2225,175296,6474],{"class":2235},[2225,175298,124065],{"class":6342},[2225,175300,6480],{"class":2235},[2225,175302,2789],{"class":2239},[2225,175304,2805],{"class":2235},[2225,175306,150830],{"class":2252},[2225,175308,2805],{"class":2235},[2225,175310,2298],{"class":2235},[2225,175312,2301],{"class":2235},[2225,175314,175315,175318,175320],{"class":2227,"line":6576},[2225,175316,175317],{"class":2307},"  lookupFields",[2225,175319,2311],{"class":2235},[2225,175321,2301],{"class":2235},[2225,175323,175324,175327,175329,175331,175333,175335,175337,175339,175341],{"class":2227,"line":6582},[2225,175325,175326],{"class":2307},"    country",[2225,175328,2311],{"class":2235},[2225,175330,174910],{"class":2291},[2225,175332,2789],{"class":2239},[2225,175334,2805],{"class":2235},[2225,175336,174857],{"class":2252},[2225,175338,2805],{"class":2235},[2225,175340,2298],{"class":2235},[2225,175342,2301],{"class":2235},[2225,175344,175345,175348,175350,175352,175355,175357,175359],{"class":2227,"line":6587},[2225,175346,175347],{"class":2307},"      key",[2225,175349,2311],{"class":2235},[2225,175351,2249],{"class":2235},[2225,175353,175354],{"class":2252},"iso2",[2225,175356,2805],{"class":2235},[2225,175358,2298],{"class":2235},[2225,175360,175361],{"class":2763},"                              \u002F\u002F the canonical key FIELD — not the row's storage id\n",[2225,175363,175364,175367,175369,175371,175373,175376,175378,175380,175382,175385,175387,175389,175391],{"class":2227,"line":7174},[2225,175365,175366],{"class":2307},"      altKeys",[2225,175368,2311],{"class":2235},[2225,175370,8667],{"class":2239},[2225,175372,2805],{"class":2235},[2225,175374,175375],{"class":2252},"iso3",[2225,175377,2805],{"class":2235},[2225,175379,2298],{"class":2235},[2225,175381,2249],{"class":2235},[2225,175383,175384],{"class":2252},"callPrefix",[2225,175386,2805],{"class":2235},[2225,175388,8803],{"class":2239},[2225,175390,2298],{"class":2235},[2225,175392,175393],{"class":2763},"          \u002F\u002F candidate values that normalize to `key`\n",[2225,175395,175396,175399,175401,175403,175405,175407,175409,175411,175413,175415,175418,175420,175422,175424,175426,175428],{"class":2227,"line":7180},[2225,175397,175398],{"class":2307},"      present",[2225,175400,2311],{"class":2235},[2225,175402,2236],{"class":2235},[2225,175404,95892],{"class":2307},[2225,175406,2311],{"class":2235},[2225,175408,2249],{"class":2235},[2225,175410,4914],{"class":2252},[2225,175412,2805],{"class":2235},[2225,175414,2298],{"class":2235},[2225,175416,175417],{"class":2307}," by",[2225,175419,2311],{"class":2235},[2225,175421,2249],{"class":2235},[2225,175423,23074],{"class":2252},[2225,175425,2805],{"class":2235},[2225,175427,10965],{"class":2235},[2225,175429,175430],{"class":2763}," \u002F\u002F \u003Cfield>Label resolves row.name[locale]\n",[2225,175432,175433,175436,175438,175440,175442,175444],{"class":2227,"line":7220},[2225,175434,175435],{"class":2307},"      sortBy",[2225,175437,2311],{"class":2235},[2225,175439,2249],{"class":2235},[2225,175441,4914],{"class":2252},[2225,175443,2805],{"class":2235},[2225,175445,1707],{"class":2235},[2225,175447,175448,175451,175453,175455,175457,175459],{"class":2227,"line":7225},[2225,175449,175450],{"class":2307},"      backing",[2225,175452,2311],{"class":2235},[2225,175454,2249],{"class":2235},[2225,175456,6471],{"class":2252},[2225,175458,2805],{"class":2235},[2225,175460,1707],{"class":2235},[2225,175462,175463,175466,175468,175470,175472,175474],{"class":2227,"line":7231},[2225,175464,175465],{"class":2307},"      vocabulary",[2225,175467,2311],{"class":2235},[2225,175469,2249],{"class":2235},[2225,175471,60394],{"class":2252},[2225,175473,2805],{"class":2235},[2225,175475,1707],{"class":2235},[2225,175477,175478,175480,175482],{"class":2227,"line":7267},[2225,175479,80146],{"class":2235},[2225,175481,1381],{"class":2239},[2225,175483,1707],{"class":2235},[2225,175485,175486],{"class":2227,"line":7272},[2225,175487,19017],{"class":2235},[2225,175489,175490,175492],{"class":2227,"line":7278},[2225,175491,2359],{"class":2235},[2225,175493,2362],{"class":2239},[937,175495,175496,175497,175500,175501,175503,175504,10109,175506,175508,175509,175512,175513,175516],{},"Note the row's own storage id (",[941,175498,175499],{},"'row-US'",") is deliberately ",[950,175502,1004],{}," the canonical key (",[941,175505,174830],{},[941,175507,8364],{}," names a ",[1092,175510,175511],{},"field on the backing row",". Every lookup mechanism — membership, altKey\nnormalization, sorting, join dressing — keys by ",[941,175514,175515],{},"row[key]",", never by the row's storage id.",[937,175518,175519],{},"Writes then normalize any accepted identifier to the canonical key:",[2217,175521,175523],{"className":2219,"code":175522,"language":2221,"meta":1037,"style":1037},"await orders.put('o1', { id: 'o1', country: 'USA' })   \u002F\u002F ISO3 candidate\n;(await orders.get('o1'))?.country                     \u002F\u002F → 'US' — normalized on ingest\n\nawait orders.put('o2', { id: 'o2', country: '+66' })   \u002F\u002F call-prefix candidate\n;(await orders.get('o2'))?.country                     \u002F\u002F → 'TH'\n",[941,175524,175525,175576,175608,175612,175664],{"__ignoreMap":1037},[2225,175526,175527,175529,175531,175533,175535,175537,175539,175541,175543,175545,175547,175549,175551,175553,175555,175557,175559,175561,175563,175565,175567,175569,175571,175573],{"class":2227,"line":2228},[2225,175528,6500],{"class":2231},[2225,175530,150889],{"class":2239},[2225,175532,1520],{"class":2235},[2225,175534,3161],{"class":2291},[2225,175536,2789],{"class":2239},[2225,175538,2805],{"class":2235},[2225,175540,150900],{"class":2252},[2225,175542,2805],{"class":2235},[2225,175544,2298],{"class":2235},[2225,175546,2236],{"class":2235},[2225,175548,7070],{"class":2307},[2225,175550,2311],{"class":2235},[2225,175552,2249],{"class":2235},[2225,175554,150900],{"class":2252},[2225,175556,2805],{"class":2235},[2225,175558,2298],{"class":2235},[2225,175560,175006],{"class":2307},[2225,175562,2311],{"class":2235},[2225,175564,2249],{"class":2235},[2225,175566,175109],{"class":2252},[2225,175568,2805],{"class":2235},[2225,175570,2243],{"class":2235},[2225,175572,71781],{"class":2239},[2225,175574,175575],{"class":2763},"\u002F\u002F ISO3 candidate\n",[2225,175577,175578,175580,175582,175584,175586,175588,175590,175592,175594,175596,175598,175600,175602,175605],{"class":2227,"line":1041},[2225,175579,3769],{"class":2235},[2225,175581,2789],{"class":2239},[2225,175583,6500],{"class":2231},[2225,175585,150889],{"class":2239},[2225,175587,1520],{"class":2235},[2225,175589,3158],{"class":2291},[2225,175591,2789],{"class":2239},[2225,175593,2805],{"class":2235},[2225,175595,150900],{"class":2252},[2225,175597,2805],{"class":2235},[2225,175599,8454],{"class":2239},[2225,175601,8457],{"class":2235},[2225,175603,175604],{"class":2239},"country                     ",[2225,175606,175607],{"class":2763},"\u002F\u002F → 'US' — normalized on ingest\n",[2225,175609,175610],{"class":2227,"line":1038},[2225,175611,2285],{"emptyLinePlaceholder":2284},[2225,175613,175614,175616,175618,175620,175622,175624,175626,175629,175631,175633,175635,175637,175639,175641,175643,175645,175647,175649,175651,175653,175655,175657,175659,175661],{"class":2227,"line":2288},[2225,175615,6500],{"class":2231},[2225,175617,150889],{"class":2239},[2225,175619,1520],{"class":2235},[2225,175621,3161],{"class":2291},[2225,175623,2789],{"class":2239},[2225,175625,2805],{"class":2235},[2225,175627,175628],{"class":2252},"o2",[2225,175630,2805],{"class":2235},[2225,175632,2298],{"class":2235},[2225,175634,2236],{"class":2235},[2225,175636,7070],{"class":2307},[2225,175638,2311],{"class":2235},[2225,175640,2249],{"class":2235},[2225,175642,175628],{"class":2252},[2225,175644,2805],{"class":2235},[2225,175646,2298],{"class":2235},[2225,175648,175006],{"class":2307},[2225,175650,2311],{"class":2235},[2225,175652,2249],{"class":2235},[2225,175654,175234],{"class":2252},[2225,175656,2805],{"class":2235},[2225,175658,2243],{"class":2235},[2225,175660,71781],{"class":2239},[2225,175662,175663],{"class":2763},"\u002F\u002F call-prefix candidate\n",[2225,175665,175666,175668,175670,175672,175674,175676,175678,175680,175682,175684,175686,175688,175690,175692],{"class":2227,"line":2304},[2225,175667,3769],{"class":2235},[2225,175669,2789],{"class":2239},[2225,175671,6500],{"class":2231},[2225,175673,150889],{"class":2239},[2225,175675,1520],{"class":2235},[2225,175677,3158],{"class":2291},[2225,175679,2789],{"class":2239},[2225,175681,2805],{"class":2235},[2225,175683,175628],{"class":2252},[2225,175685,2805],{"class":2235},[2225,175687,8454],{"class":2239},[2225,175689,8457],{"class":2235},[2225,175691,175604],{"class":2239},[2225,175693,175694],{"class":2763},"\u002F\u002F → 'TH'\n",[937,175696,175697,175698,2311],{},"And unknown keys are refused at write time under ",[941,175699,175700],{},"vocabulary: 'closed'",[2217,175702,175704],{"className":2219,"code":175703,"language":2221,"meta":1037,"style":1037},"await orders.put('o3', { id: 'o3', country: 'ZZ' })\n\u002F\u002F → rejects with UnknownLookupKeyError\n",[941,175705,175706,175756],{"__ignoreMap":1037},[2225,175707,175708,175710,175712,175714,175716,175718,175720,175723,175725,175727,175729,175731,175733,175735,175737,175739,175741,175743,175745,175747,175750,175752,175754],{"class":2227,"line":2228},[2225,175709,6500],{"class":2231},[2225,175711,150889],{"class":2239},[2225,175713,1520],{"class":2235},[2225,175715,3161],{"class":2291},[2225,175717,2789],{"class":2239},[2225,175719,2805],{"class":2235},[2225,175721,175722],{"class":2252},"o3",[2225,175724,2805],{"class":2235},[2225,175726,2298],{"class":2235},[2225,175728,2236],{"class":2235},[2225,175730,7070],{"class":2307},[2225,175732,2311],{"class":2235},[2225,175734,2249],{"class":2235},[2225,175736,175722],{"class":2252},[2225,175738,2805],{"class":2235},[2225,175740,2298],{"class":2235},[2225,175742,175006],{"class":2307},[2225,175744,2311],{"class":2235},[2225,175746,2249],{"class":2235},[2225,175748,175749],{"class":2252},"ZZ",[2225,175751,2805],{"class":2235},[2225,175753,2243],{"class":2235},[2225,175755,2362],{"class":2239},[2225,175757,175758],{"class":2227,"line":1041},[2225,175759,175760],{"class":2763},"\u002F\u002F → rejects with UnknownLookupKeyError\n",[1022,175762,16993],{"id":16992},[937,175764,175765,175766,175769],{},"All three factories build the same descriptor shape; ",[941,175767,175768],{},"backing"," decides where the dimension lives:",[1338,175771,175772,175789],{},[1341,175773,175774],{},[1344,175775,175776,175778,175780,175783,175786],{},[1347,175777,104737],{},[1347,175779,9533],{},[1347,175781,175782],{},"Backing",[1347,175784,175785],{},"Vocabulary default",[1347,175787,175788],{},"Syncs?",[1357,175790,175791,175815,175843],{},[1344,175792,175793,175797,175802,175808,175812],{},[1362,175794,175795],{},[950,175796,28421],{},[1362,175798,175799],{},[941,175800,175801],{},"enumOf(['a','b'] as const)",[1362,175803,175804,175805,175807],{},"none — inline ",[941,175806,8678],{},", no store",[1362,175809,175810],{},[941,175811,60394],{},[1362,175813,175814],{},"n\u002Fa (no store)",[1344,175816,175817,175821,175826,175836,175840],{},[1362,175818,175819],{},[950,175820,147773],{},[1362,175822,175823],{},[941,175824,175825],{},"dict('status')",[1362,175827,175828,175829,175832,175833,1381],{},"reserved ",[941,175830,175831],{},"_dict_status"," micro-collection (",[941,175834,175835],{},"vault.dictionary('status')",[1362,175837,175838],{},[941,175839,26178],{},[1362,175841,175842],{},"yes — see below",[1344,175844,175845,175850,175855,175861,175865],{},[1362,175846,175847],{},[950,175848,175849],{},"matrix",[1362,175851,175852],{},[941,175853,175854],{},"lookup('countries')",[1362,175856,175857,175858,1381],{},"a first-class collection (",[941,175859,175860],{},"vault.collection('countries')",[1362,175862,175863],{},[941,175864,26178],{},[1362,175866,175867],{},"yes (ordinary collection sync)",[937,175869,175870,175873,175874,41109,175877,1621,175880,2514,175882,175884,175885,175887,175888,2601,175890,175892],{},[941,175871,175872],{},"lookup(dimension, opts)"," is the one factory that can construct any tier\n(",[941,175875,175876],{},"backing: 'static' | 'reserved' | 'collection'",[941,175878,175879],{},"'collection'",[941,175881,163868],{},[941,175883,163871],{}," are fixed-tier conveniences, and the ",[1007,175886,701],{"href":702}," spellings ",[941,175889,58348],{},[941,175891,163843],{}," compile onto the same descriptor shape (see \"dictKey and staticDict are aliases\"\nbelow).",[1022,175894,175896],{"id":175895},"altkeys-alternate-identifiers-normalize-on-write","altKeys — alternate identifiers normalize on write",[937,175898,175899,175901,175902,175904,175905,175907,175908,175911,175912,175914,175915,175918,175919,10326,175921,175923,175924,175926,175927,175929],{},[941,175900,174864],{}," names backing-row fields whose values are accepted as write-time candidates and\nnormalized to the canonical ",[941,175903,8364],{}," — synchronously, from an in-memory index built over the backing\nrows already open in this session (no store read per ",[941,175906,20953],{},"). Because the index is\nsession-local, the backing collection must be ",[950,175909,175910],{},"open in eager mode"," (the default) for matrix-tier\naltKeys; declaring ",[941,175913,174864],{}," on a matrix field whose backing collection was opened\n",[941,175916,175917],{},"{ prefetch: false }"," (lazy) makes ",[941,175920,20953],{},[941,175922,12881],{}," naming the field and dimension.\nThe ",[941,175925,163871],{}," tier has no ",[941,175928,174864],{}," option — its dictionary cache is always synchronous, so the\nrestriction never applies.",[937,175931,175932,175933,175936,175937,175939,175940,175943,175944,1520],{},"A candidate value may be a string ",[950,175934,175935],{},"or a number"," — both coerce to the same string index entry (a\nnumeric ",[941,175938,5437],{}," and the string ",[941,175941,175942],{},"'1'"," are interchangeable). Each candidate must be claimed by at most\none backing row: two rows resolving to the same candidate — across the string\u002Fnumber boundary\nincluded — throw ",[941,175945,12881],{},[1022,175947,175949],{"id":175948},"vocabulary-open-vs-closed-and-why-sparse-dimensions-are-safe","Vocabulary — open vs closed, and why sparse dimensions are safe",[937,175951,175952,175954,175955,175958],{},[941,175953,175700],{}," checks membership against the backing dimension's ",[950,175956,175957],{},"actual current rows",",\nlive — not a hardcoded universe of \"real\" codes. A legitimate ISO2 code that simply hasn't been\npopulated in this vault is refused exactly like a fake one:",[2217,175960,175962],{"className":2219,"code":175961,"language":2221,"meta":1037,"style":1037},"\u002F\u002F Only 'US' and 'TH' rows exist — 'ZA' is a real ISO2 code, just not populated here.\nawait orders.put('o4', { id: 'o4', country: 'ZA' })\n\u002F\u002F → rejects with UnknownLookupKeyError\n",[941,175963,175964,175969,176019],{"__ignoreMap":1037},[2225,175965,175966],{"class":2227,"line":2228},[2225,175967,175968],{"class":2763},"\u002F\u002F Only 'US' and 'TH' rows exist — 'ZA' is a real ISO2 code, just not populated here.\n",[2225,175970,175971,175973,175975,175977,175979,175981,175983,175986,175988,175990,175992,175994,175996,175998,176000,176002,176004,176006,176008,176010,176013,176015,176017],{"class":2227,"line":1041},[2225,175972,6500],{"class":2231},[2225,175974,150889],{"class":2239},[2225,175976,1520],{"class":2235},[2225,175978,3161],{"class":2291},[2225,175980,2789],{"class":2239},[2225,175982,2805],{"class":2235},[2225,175984,175985],{"class":2252},"o4",[2225,175987,2805],{"class":2235},[2225,175989,2298],{"class":2235},[2225,175991,2236],{"class":2235},[2225,175993,7070],{"class":2307},[2225,175995,2311],{"class":2235},[2225,175997,2249],{"class":2235},[2225,175999,175985],{"class":2252},[2225,176001,2805],{"class":2235},[2225,176003,2298],{"class":2235},[2225,176005,175006],{"class":2307},[2225,176007,2311],{"class":2235},[2225,176009,2249],{"class":2235},[2225,176011,176012],{"class":2252},"ZA",[2225,176014,2805],{"class":2235},[2225,176016,2243],{"class":2235},[2225,176018,2362],{"class":2239},[2225,176020,176021],{"class":2227,"line":1038},[2225,176022,175760],{"class":2763},[937,176024,176025],{},"That is what makes populate-only-what-you-use dimensions sound: membership is defined by your\ndata, not by an external standard.",[937,176027,176028],{},"Per-tier detail:",[1111,176030,176031,176037,176048,176066],{},[1114,176032,176033,176036],{},[950,176034,176035],{},"Matrix tier"," has no declared key list at all — membership is purely the backing collection's\nlive rows.",[1114,176038,176039,176042,176043,1324,176045,176047],{},[950,176040,176041],{},"Dict tier"," closed membership is the declared ",[941,176044,8678],{},[950,176046,123641],{}," the reserved dictionary's live\nrows: a declared key is known even before any dictionary row for it exists, and a live row for\nan undeclared key is known too.",[1114,176049,176050,19862,176054,176056,176057,176060,176061,176063,176064,16275],{},[950,176051,176052],{},[941,176053,58348],{},[941,176055,163871],{},") default to ",[941,176058,176059],{},"'open'"," — declaring keys on ",[941,176062,58348],{}," never\nenforced membership, and still doesn't; write-time refusal is exclusively the native\n",[941,176065,175700],{},[1114,176067,176068,176072,176073,176076,176077,176079,176080,176082],{},[950,176069,176070],{},[941,176071,163843],{}," is closed by a different, older mechanism — its own ",[941,176074,176075],{},"validateCodes"," option\n(default ",[941,176078,11790],{},"), throwing ",[941,176081,163803],{},", unchanged.",[937,176084,176085,176088,176089,176092,176093,176095,176096,176099],{},[950,176086,176087],{},"Cold-session caveat."," The live-rows side of the check reads a cache materialized during ",[1092,176090,176091],{},"this\nsession",". A freshly opened vault (or one straight after a pull) that hasn't yet opened the\nmatrix-tier backing collection — or warmed the dict-tier dictionary cache — sees an empty\nlive-rows set, so a ",[941,176094,175700],{}," field refuses even a genuinely valid, already-persisted\nkey until the dimension is hydrated. Open the backing collection first (matrix tier, eager mode),\nor ",[941,176097,176098],{},"await vault.dictionary(name).list()"," (dict tier). This fails safe — it refuses rather than\naccepts unverified keys — but is easy to trip over on a fresh instance.",[1022,176101,176103],{"id":176102},"array-valued-fields-element-wise-normalization","Array-valued fields — element-wise normalization",[937,176105,176106,176107,176110,176111,176114],{},"A lookup field whose value is itself an array (",[941,176108,176109],{},"tags: ['USA', '+66']",") normalizes ",[950,176112,176113],{},"element by\nelement"," — altKey normalization and closed-vocabulary membership run per element, against the same\nbacking index the scalar case uses:",[2217,176116,176118],{"className":2219,"code":176117,"language":2221,"meta":1037,"style":1037},"const orders = vault.collection\u003C{ id: string; tags: string[] }>('orders-tagged', {\n  lookupFields: { tags: lookup('countries', { key: 'iso2', altKeys: ['iso3', 'callPrefix'] }) },\n})\nawait orders.put('o1', { id: 'o1', tags: ['USA', '+66'] })\n;(await orders.get('o1'))?.tags   \u002F\u002F → ['US', 'TH'] — each element normalized to the canonical key\n",[941,176119,176120,176167,176234,176240,176300],{"__ignoreMap":1037},[2225,176121,176122,176124,176126,176128,176130,176132,176134,176136,176138,176140,176142,176144,176146,176148,176150,176152,176154,176156,176158,176161,176163,176165],{"class":2227,"line":2228},[2225,176123,2775],{"class":2774},[2225,176125,150809],{"class":2239},[2225,176127,2781],{"class":2235},[2225,176129,6466],{"class":2239},[2225,176131,1520],{"class":2235},[2225,176133,6471],{"class":2291},[2225,176135,17851],{"class":2235},[2225,176137,7070],{"class":2307},[2225,176139,2311],{"class":2235},[2225,176141,6366],{"class":6342},[2225,176143,3769],{"class":2235},[2225,176145,120648],{"class":2307},[2225,176147,2311],{"class":2235},[2225,176149,6366],{"class":6342},[2225,176151,82213],{"class":2239},[2225,176153,82216],{"class":2235},[2225,176155,2789],{"class":2239},[2225,176157,2805],{"class":2235},[2225,176159,176160],{"class":2252},"orders-tagged",[2225,176162,2805],{"class":2235},[2225,176164,2298],{"class":2235},[2225,176166,2301],{"class":2235},[2225,176168,176169,176171,176173,176175,176177,176179,176181,176183,176185,176187,176189,176191,176193,176195,176197,176199,176201,176203,176205,176208,176210,176212,176214,176216,176218,176220,176222,176224,176226,176228,176230,176232],{"class":2227,"line":1041},[2225,176170,175317],{"class":2307},[2225,176172,2311],{"class":2235},[2225,176174,2236],{"class":2235},[2225,176176,120648],{"class":2307},[2225,176178,2311],{"class":2235},[2225,176180,174910],{"class":2291},[2225,176182,2789],{"class":2239},[2225,176184,2805],{"class":2235},[2225,176186,174857],{"class":2252},[2225,176188,2805],{"class":2235},[2225,176190,2298],{"class":2235},[2225,176192,2236],{"class":2235},[2225,176194,8153],{"class":2307},[2225,176196,2311],{"class":2235},[2225,176198,2249],{"class":2235},[2225,176200,175354],{"class":2252},[2225,176202,2805],{"class":2235},[2225,176204,2298],{"class":2235},[2225,176206,176207],{"class":2307}," altKeys",[2225,176209,2311],{"class":2235},[2225,176211,8667],{"class":2239},[2225,176213,2805],{"class":2235},[2225,176215,175375],{"class":2252},[2225,176217,2805],{"class":2235},[2225,176219,2298],{"class":2235},[2225,176221,2249],{"class":2235},[2225,176223,175384],{"class":2252},[2225,176225,2805],{"class":2235},[2225,176227,8890],{"class":2239},[2225,176229,2359],{"class":2235},[2225,176231,8495],{"class":2239},[2225,176233,19008],{"class":2235},[2225,176235,176236,176238],{"class":2227,"line":1038},[2225,176237,2359],{"class":2235},[2225,176239,2362],{"class":2239},[2225,176241,176242,176244,176246,176248,176250,176252,176254,176256,176258,176260,176262,176264,176266,176268,176270,176272,176274,176276,176278,176280,176282,176284,176286,176288,176290,176292,176294,176296,176298],{"class":2227,"line":2288},[2225,176243,6500],{"class":2231},[2225,176245,150889],{"class":2239},[2225,176247,1520],{"class":2235},[2225,176249,3161],{"class":2291},[2225,176251,2789],{"class":2239},[2225,176253,2805],{"class":2235},[2225,176255,150900],{"class":2252},[2225,176257,2805],{"class":2235},[2225,176259,2298],{"class":2235},[2225,176261,2236],{"class":2235},[2225,176263,7070],{"class":2307},[2225,176265,2311],{"class":2235},[2225,176267,2249],{"class":2235},[2225,176269,150900],{"class":2252},[2225,176271,2805],{"class":2235},[2225,176273,2298],{"class":2235},[2225,176275,120648],{"class":2307},[2225,176277,2311],{"class":2235},[2225,176279,8667],{"class":2239},[2225,176281,2805],{"class":2235},[2225,176283,175109],{"class":2252},[2225,176285,2805],{"class":2235},[2225,176287,2298],{"class":2235},[2225,176289,2249],{"class":2235},[2225,176291,175234],{"class":2252},[2225,176293,2805],{"class":2235},[2225,176295,8890],{"class":2239},[2225,176297,2359],{"class":2235},[2225,176299,2362],{"class":2239},[2225,176301,176302,176304,176306,176308,176310,176312,176314,176316,176318,176320,176322,176324,176326,176329],{"class":2227,"line":2304},[2225,176303,3769],{"class":2235},[2225,176305,2789],{"class":2239},[2225,176307,6500],{"class":2231},[2225,176309,150889],{"class":2239},[2225,176311,1520],{"class":2235},[2225,176313,3158],{"class":2291},[2225,176315,2789],{"class":2239},[2225,176317,2805],{"class":2235},[2225,176319,150900],{"class":2252},[2225,176321,2805],{"class":2235},[2225,176323,8454],{"class":2239},[2225,176325,8457],{"class":2235},[2225,176327,176328],{"class":2239},"tags   ",[2225,176330,176331],{"class":2763},"\u002F\u002F → ['US', 'TH'] — each element normalized to the canonical key\n",[937,176333,15432,176334,176336,176337,176339],{},[941,176335,175700],{},", the write is refused on the ",[950,176338,3233],{}," unknown element:",[2217,176341,176343],{"className":2219,"code":176342,"language":2221,"meta":1037,"style":1037},"const closed = vault.collection\u003C{ id: string; tags: string[] }>('orders-closed', {\n  lookupFields: { tags: lookup('countries', { key: 'iso2', vocabulary: 'closed' }) },\n})\nawait closed.put('o1', { id: 'o1', tags: ['ZZZ', 'nope'] })\n\u002F\u002F → rejects with UnknownLookupKeyError\n",[941,176344,176345,176393,176448,176454,176516],{"__ignoreMap":1037},[2225,176346,176347,176349,176352,176354,176356,176358,176360,176362,176364,176366,176368,176370,176372,176374,176376,176378,176380,176382,176384,176387,176389,176391],{"class":2227,"line":2228},[2225,176348,2775],{"class":2774},[2225,176350,176351],{"class":2239}," closed ",[2225,176353,2781],{"class":2235},[2225,176355,6466],{"class":2239},[2225,176357,1520],{"class":2235},[2225,176359,6471],{"class":2291},[2225,176361,17851],{"class":2235},[2225,176363,7070],{"class":2307},[2225,176365,2311],{"class":2235},[2225,176367,6366],{"class":6342},[2225,176369,3769],{"class":2235},[2225,176371,120648],{"class":2307},[2225,176373,2311],{"class":2235},[2225,176375,6366],{"class":6342},[2225,176377,82213],{"class":2239},[2225,176379,82216],{"class":2235},[2225,176381,2789],{"class":2239},[2225,176383,2805],{"class":2235},[2225,176385,176386],{"class":2252},"orders-closed",[2225,176388,2805],{"class":2235},[2225,176390,2298],{"class":2235},[2225,176392,2301],{"class":2235},[2225,176394,176395,176397,176399,176401,176403,176405,176407,176409,176411,176413,176415,176417,176419,176421,176423,176425,176427,176429,176431,176434,176436,176438,176440,176442,176444,176446],{"class":2227,"line":1041},[2225,176396,175317],{"class":2307},[2225,176398,2311],{"class":2235},[2225,176400,2236],{"class":2235},[2225,176402,120648],{"class":2307},[2225,176404,2311],{"class":2235},[2225,176406,174910],{"class":2291},[2225,176408,2789],{"class":2239},[2225,176410,2805],{"class":2235},[2225,176412,174857],{"class":2252},[2225,176414,2805],{"class":2235},[2225,176416,2298],{"class":2235},[2225,176418,2236],{"class":2235},[2225,176420,8153],{"class":2307},[2225,176422,2311],{"class":2235},[2225,176424,2249],{"class":2235},[2225,176426,175354],{"class":2252},[2225,176428,2805],{"class":2235},[2225,176430,2298],{"class":2235},[2225,176432,176433],{"class":2307}," vocabulary",[2225,176435,2311],{"class":2235},[2225,176437,2249],{"class":2235},[2225,176439,60394],{"class":2252},[2225,176441,2805],{"class":2235},[2225,176443,2243],{"class":2235},[2225,176445,8495],{"class":2239},[2225,176447,19008],{"class":2235},[2225,176449,176450,176452],{"class":2227,"line":1038},[2225,176451,2359],{"class":2235},[2225,176453,2362],{"class":2239},[2225,176455,176456,176458,176461,176463,176465,176467,176469,176471,176473,176475,176477,176479,176481,176483,176485,176487,176489,176491,176493,176495,176497,176500,176502,176504,176506,176508,176510,176512,176514],{"class":2227,"line":2288},[2225,176457,6500],{"class":2231},[2225,176459,176460],{"class":2239}," closed",[2225,176462,1520],{"class":2235},[2225,176464,3161],{"class":2291},[2225,176466,2789],{"class":2239},[2225,176468,2805],{"class":2235},[2225,176470,150900],{"class":2252},[2225,176472,2805],{"class":2235},[2225,176474,2298],{"class":2235},[2225,176476,2236],{"class":2235},[2225,176478,7070],{"class":2307},[2225,176480,2311],{"class":2235},[2225,176482,2249],{"class":2235},[2225,176484,150900],{"class":2252},[2225,176486,2805],{"class":2235},[2225,176488,2298],{"class":2235},[2225,176490,120648],{"class":2307},[2225,176492,2311],{"class":2235},[2225,176494,8667],{"class":2239},[2225,176496,2805],{"class":2235},[2225,176498,176499],{"class":2252},"ZZZ",[2225,176501,2805],{"class":2235},[2225,176503,2298],{"class":2235},[2225,176505,2249],{"class":2235},[2225,176507,118463],{"class":2252},[2225,176509,2805],{"class":2235},[2225,176511,8890],{"class":2239},[2225,176513,2359],{"class":2235},[2225,176515,2362],{"class":2239},[2225,176517,176518],{"class":2227,"line":2304},[2225,176519,175760],{"class":2763},[937,176521,176522,176523,176525,176526,176529,176530,176533],{},"Detail rules: normalization rewrites values in place, never cardinality — duplicate elements are\nkept, not deduplicated; a non-string element (number, object, ",[941,176524,6651],{},") is skipped, exactly as the\nscalar branch skips a non-string value; an empty array is a no-op even under closed vocabulary.\nThis behaves identically at a dotted, non-wildcard path (",[941,176527,176528],{},"'meta.tags'"," for a nested\n",[941,176531,176532],{},"{ meta: { tags: [...] } }"," shape).",[1022,176535,176537,176538,176540],{"id":176536},"presentation-fieldlabel-in-reads-and-joins","Presentation — ",[941,176539,24050],{},", in reads and joins",[937,176542,176543,176544,176546,176547,176550,176551,176554,176555,176557],{},"Reading with a locale resolves a ",[941,176545,24050],{}," sibling on the same record, sourced from the\nbacking row's ",[941,176548,176549],{},"present.label"," field (locale-keyed when ",[941,176552,176553],{},"present.by: 'locale'","). Joining to a\ncollection that declares a lookup field dresses the ",[950,176556,57598],{}," side too, matrix tier included:",[2217,176559,176561],{"className":2219,"code":176560,"language":2221,"meta":1037,"style":1037},"const rows = shipments.query().join('orderId', { as: 'order' }).toArray({ locale: 'th' })\nrows[0].order.countryLabel \u002F\u002F 'สหรัฐอเมริกา' — resolved through the join\n",[941,176562,176563,176633],{"__ignoreMap":1037},[2225,176564,176565,176567,176569,176571,176574,176576,176578,176580,176582,176584,176586,176588,176591,176593,176595,176597,176599,176601,176603,176605,176607,176609,176611,176613,176615,176617,176619,176621,176623,176625,176627,176629,176631],{"class":2227,"line":2228},[2225,176566,2775],{"class":2774},[2225,176568,42003],{"class":2239},[2225,176570,2781],{"class":2235},[2225,176572,176573],{"class":2239}," shipments",[2225,176575,1520],{"class":2235},[2225,176577,5286],{"class":2291},[2225,176579,4228],{"class":2239},[2225,176581,1520],{"class":2235},[2225,176583,63728],{"class":2291},[2225,176585,2789],{"class":2239},[2225,176587,2805],{"class":2235},[2225,176589,176590],{"class":2252},"orderId",[2225,176592,2805],{"class":2235},[2225,176594,2298],{"class":2235},[2225,176596,2236],{"class":2235},[2225,176598,2266],{"class":2307},[2225,176600,2311],{"class":2235},[2225,176602,2249],{"class":2235},[2225,176604,147646],{"class":2252},[2225,176606,2805],{"class":2235},[2225,176608,2243],{"class":2235},[2225,176610,1381],{"class":2239},[2225,176612,1520],{"class":2235},[2225,176614,3230],{"class":2291},[2225,176616,2789],{"class":2239},[2225,176618,2792],{"class":2235},[2225,176620,24003],{"class":2307},[2225,176622,2311],{"class":2235},[2225,176624,2249],{"class":2235},[2225,176626,1347],{"class":2252},[2225,176628,2805],{"class":2235},[2225,176630,2243],{"class":2235},[2225,176632,2362],{"class":2239},[2225,176634,176635,176638,176640,176642,176644,176646,176648,176651],{"class":2227,"line":1041},[2225,176636,176637],{"class":2239},"rows[",[2225,176639,5402],{"class":6527},[2225,176641,8803],{"class":2239},[2225,176643,1520],{"class":2235},[2225,176645,147646],{"class":2239},[2225,176647,1520],{"class":2235},[2225,176649,176650],{"class":2239},"countryLabel ",[2225,176652,176653],{"class":2763},"\u002F\u002F 'สหรัฐอเมริกา' — resolved through the join\n",[937,176655,176656,176657,176659,176660,176663,176664,176667,176668,176671,176672,176675,176676,176678],{},"Direct (non-join) reads of a matrix-tier field resolve ",[941,176658,24050],{}," the same way — keyed by\n",[941,176661,176662],{},"descriptor.key"," through the same session-local snapshot the join path uses — so a custom canonical\nkey like ",[941,176665,176666],{},"key: 'iso2'"," above dresses correctly on a plain read, not only through a join. That\nsnapshot shares the cold-session requirement: the backing dimension must be open and populated in\nthis session (the cold-session caveat above). Only the default ",[941,176669,176670],{},"key: 'id'"," tier keeps a live\non-demand ",[941,176673,176674],{},".get()"," fallback on a snapshot\nmiss; for any custom ",[941,176677,8364],{}," the snapshot is the sole source, so a cold dimension yields no label\nuntil it is hydrated.",[1022,176680,176682],{"id":176681},"sorting-by-the-resolved-label","Sorting by the resolved label",[937,176684,176685],{},"Two independent channels:",[2217,176687,176689],{"className":2219,"code":176688,"language":2221,"meta":1037,"style":1037},"\u002F\u002F (a) declared: sortBy + displayLocale — a plain orderBy() sorts by the label at the\n\u002F\u002F     field's own fixed locale:\nlookup('countries', { key: 'iso2', present: { label: 'name', by: 'locale' },\n  sortBy: 'name', backing: 'collection', displayLocale: 'en' })\norders.query().orderBy('country', 'asc').toArray()\n\n\u002F\u002F (b) per-call: orderBy(..., { by: 'label' }) resolves at the query's own locale —\n\u002F\u002F     no displayLocale needed, and two locales genuinely produce two different\n\u002F\u002F     orders for the same rows (the labels collate differently per language):\norders.query().orderBy('country', 'asc', { by: 'label' }).toArray({ locale: 'en' })\norders.query().orderBy('country', 'asc', { by: 'label' }).toArray({ locale: 'th' })\n",[941,176690,176691,176696,176701,176760,176802,176840,176844,176849,176854,176859,176929],{"__ignoreMap":1037},[2225,176692,176693],{"class":2227,"line":2228},[2225,176694,176695],{"class":2763},"\u002F\u002F (a) declared: sortBy + displayLocale — a plain orderBy() sorts by the label at the\n",[2225,176697,176698],{"class":2227,"line":1041},[2225,176699,176700],{"class":2763},"\u002F\u002F     field's own fixed locale:\n",[2225,176702,176703,176705,176707,176709,176711,176713,176715,176717,176719,176721,176723,176725,176727,176729,176732,176734,176736,176738,176740,176742,176744,176746,176748,176750,176752,176754,176756,176758],{"class":2227,"line":1038},[2225,176704,731],{"class":2291},[2225,176706,2789],{"class":2239},[2225,176708,2805],{"class":2235},[2225,176710,174857],{"class":2252},[2225,176712,2805],{"class":2235},[2225,176714,2298],{"class":2235},[2225,176716,2236],{"class":2235},[2225,176718,8153],{"class":2307},[2225,176720,2311],{"class":2235},[2225,176722,2249],{"class":2235},[2225,176724,175354],{"class":2252},[2225,176726,2805],{"class":2235},[2225,176728,2298],{"class":2235},[2225,176730,176731],{"class":2307}," present",[2225,176733,2311],{"class":2235},[2225,176735,2236],{"class":2235},[2225,176737,95892],{"class":2307},[2225,176739,2311],{"class":2235},[2225,176741,2249],{"class":2235},[2225,176743,4914],{"class":2252},[2225,176745,2805],{"class":2235},[2225,176747,2298],{"class":2235},[2225,176749,175417],{"class":2307},[2225,176751,2311],{"class":2235},[2225,176753,2249],{"class":2235},[2225,176755,23074],{"class":2252},[2225,176757,2805],{"class":2235},[2225,176759,2353],{"class":2235},[2225,176761,176762,176765,176767,176769,176771,176773,176775,176778,176780,176782,176784,176786,176788,176790,176792,176794,176796,176798,176800],{"class":2227,"line":2288},[2225,176763,176764],{"class":2307},"  sortBy",[2225,176766,2311],{"class":2235},[2225,176768,2249],{"class":2235},[2225,176770,4914],{"class":2252},[2225,176772,2805],{"class":2235},[2225,176774,2298],{"class":2235},[2225,176776,176777],{"class":2307}," backing",[2225,176779,2311],{"class":2235},[2225,176781,2249],{"class":2235},[2225,176783,6471],{"class":2252},[2225,176785,2805],{"class":2235},[2225,176787,2298],{"class":2235},[2225,176789,163555],{"class":2307},[2225,176791,2311],{"class":2235},[2225,176793,2249],{"class":2235},[2225,176795,59334],{"class":2252},[2225,176797,2805],{"class":2235},[2225,176799,2243],{"class":2235},[2225,176801,2362],{"class":2239},[2225,176803,176804,176806,176808,176810,176812,176814,176816,176818,176820,176822,176824,176826,176828,176830,176832,176834,176836,176838],{"class":2227,"line":2304},[2225,176805,150830],{"class":2239},[2225,176807,1520],{"class":2235},[2225,176809,5286],{"class":2291},[2225,176811,4228],{"class":2239},[2225,176813,1520],{"class":2235},[2225,176815,3019],{"class":2291},[2225,176817,2789],{"class":2239},[2225,176819,2805],{"class":2235},[2225,176821,160921],{"class":2252},[2225,176823,2805],{"class":2235},[2225,176825,2298],{"class":2235},[2225,176827,2249],{"class":2235},[2225,176829,80946],{"class":2252},[2225,176831,2805],{"class":2235},[2225,176833,1381],{"class":2239},[2225,176835,1520],{"class":2235},[2225,176837,3230],{"class":2291},[2225,176839,8146],{"class":2239},[2225,176841,176842],{"class":2227,"line":2356},[2225,176843,2285],{"emptyLinePlaceholder":2284},[2225,176845,176846],{"class":2227,"line":2830},[2225,176847,176848],{"class":2763},"\u002F\u002F (b) per-call: orderBy(..., { by: 'label' }) resolves at the query's own locale —\n",[2225,176850,176851],{"class":2227,"line":2861},[2225,176852,176853],{"class":2763},"\u002F\u002F     no displayLocale needed, and two locales genuinely produce two different\n",[2225,176855,176856],{"class":2227,"line":4246},[2225,176857,176858],{"class":2763},"\u002F\u002F     orders for the same rows (the labels collate differently per language):\n",[2225,176860,176861,176863,176865,176867,176869,176871,176873,176875,176877,176879,176881,176883,176885,176887,176889,176891,176893,176895,176897,176899,176901,176903,176905,176907,176909,176911,176913,176915,176917,176919,176921,176923,176925,176927],{"class":2227,"line":4253},[2225,176862,150830],{"class":2239},[2225,176864,1520],{"class":2235},[2225,176866,5286],{"class":2291},[2225,176868,4228],{"class":2239},[2225,176870,1520],{"class":2235},[2225,176872,3019],{"class":2291},[2225,176874,2789],{"class":2239},[2225,176876,2805],{"class":2235},[2225,176878,160921],{"class":2252},[2225,176880,2805],{"class":2235},[2225,176882,2298],{"class":2235},[2225,176884,2249],{"class":2235},[2225,176886,80946],{"class":2252},[2225,176888,2805],{"class":2235},[2225,176890,2298],{"class":2235},[2225,176892,2236],{"class":2235},[2225,176894,175417],{"class":2307},[2225,176896,2311],{"class":2235},[2225,176898,2249],{"class":2235},[2225,176900,96028],{"class":2252},[2225,176902,2805],{"class":2235},[2225,176904,2243],{"class":2235},[2225,176906,1381],{"class":2239},[2225,176908,1520],{"class":2235},[2225,176910,3230],{"class":2291},[2225,176912,2789],{"class":2239},[2225,176914,2792],{"class":2235},[2225,176916,24003],{"class":2307},[2225,176918,2311],{"class":2235},[2225,176920,2249],{"class":2235},[2225,176922,59334],{"class":2252},[2225,176924,2805],{"class":2235},[2225,176926,2243],{"class":2235},[2225,176928,2362],{"class":2239},[2225,176930,176931,176933,176935,176937,176939,176941,176943,176945,176947,176949,176951,176953,176955,176957,176959,176961,176963,176965,176967,176969,176971,176973,176975,176977,176979,176981,176983,176985,176987,176989,176991,176993,176995,176997],{"class":2227,"line":4259},[2225,176932,150830],{"class":2239},[2225,176934,1520],{"class":2235},[2225,176936,5286],{"class":2291},[2225,176938,4228],{"class":2239},[2225,176940,1520],{"class":2235},[2225,176942,3019],{"class":2291},[2225,176944,2789],{"class":2239},[2225,176946,2805],{"class":2235},[2225,176948,160921],{"class":2252},[2225,176950,2805],{"class":2235},[2225,176952,2298],{"class":2235},[2225,176954,2249],{"class":2235},[2225,176956,80946],{"class":2252},[2225,176958,2805],{"class":2235},[2225,176960,2298],{"class":2235},[2225,176962,2236],{"class":2235},[2225,176964,175417],{"class":2307},[2225,176966,2311],{"class":2235},[2225,176968,2249],{"class":2235},[2225,176970,96028],{"class":2252},[2225,176972,2805],{"class":2235},[2225,176974,2243],{"class":2235},[2225,176976,1381],{"class":2239},[2225,176978,1520],{"class":2235},[2225,176980,3230],{"class":2291},[2225,176982,2789],{"class":2239},[2225,176984,2792],{"class":2235},[2225,176986,24003],{"class":2307},[2225,176988,2311],{"class":2235},[2225,176990,2249],{"class":2235},[2225,176992,1347],{"class":2252},[2225,176994,2805],{"class":2235},[2225,176996,2243],{"class":2235},[2225,176998,2362],{"class":2239},[937,177000,177001,14636,177006,177009,177010,177013,177014,177016,177017,177020,177021,177023,177024,177026,177027,177029],{},[950,177002,177003,177004,1520],{},"Silent degrade if you skip ",[941,177005,149032],{},[941,177007,177008],{},"sortBy"," field whose ",[941,177011,177012],{},"present.by"," is\nlocale-keyed but has no ",[941,177015,149032],{}," does not throw — a plain ",[941,177018,177019],{},"orderBy()"," degrades to raw\nstored-code order with no query-time warning. ",[941,177022,163865],{}," emits a one-time ",[941,177025,15856],{}," at\ndeclare time when it detects this combination; watch for it, or use channel (b), which needs no\n",[941,177028,149032],{}," at all.",[1022,177031,177033,177034,169699,177036,968,177038],{"id":177032},"reference-semantics-restrict-default-cascade-nullify","Reference semantics — ",[941,177035,152822],{},[941,177037,10753],{},[941,177039,152838],{},[937,177041,177042,177043,2311],{},"A lookup field is a constrained reference to its backing dimension. Deleting (or forgetting) a\nbacking row that is still referenced follows the field's ",[941,177044,137880],{},[2217,177046,177048],{"className":2219,"code":177047,"language":2221,"meta":1037,"style":1037},"import { dict } from '@noy-db\u002Fhub'\n\nconst tickets = vault.collection\u003C{ id: string; status: string }>('tickets', {\n  lookupFields: { status: dict('status') },     \u002F\u002F onDelete defaults to 'restrict'\n})\nawait vault.dictionary('status').put('paid', { en: 'Paid' })\nawait tickets.put('t1', { id: 't1', status: 'paid' })\n\n\u002F\u002F restrict (default) — refuses, naming the referencing collection and count:\nawait vault.dictionary('status').delete('paid')\n\u002F\u002F → rejects with DictKeyInUseError\n\u002F\u002F   err.dictionaryName === 'status'; err.usedBy === 'tickets'; err.count === 1\n\nawait tickets.delete('t1')                      \u002F\u002F retire the reference first\nawait vault.dictionary('status').delete('paid') \u002F\u002F now succeeds\n",[941,177049,177050,177069,177073,177119,177148,177154,177204,177254,177258,177263,177297,177302,177307,177311,177335],{"__ignoreMap":1037},[2225,177051,177052,177054,177056,177059,177061,177063,177065,177067],{"class":2227,"line":2228},[2225,177053,2232],{"class":2231},[2225,177055,2236],{"class":2235},[2225,177057,177058],{"class":2239}," dict",[2225,177060,2243],{"class":2235},[2225,177062,2246],{"class":2231},[2225,177064,2249],{"class":2235},[2225,177066,1086],{"class":2252},[2225,177068,2256],{"class":2235},[2225,177070,177071],{"class":2227,"line":1041},[2225,177072,2285],{"emptyLinePlaceholder":2284},[2225,177074,177075,177077,177080,177082,177084,177086,177088,177090,177092,177094,177096,177098,177100,177102,177104,177106,177108,177110,177113,177115,177117],{"class":2227,"line":1038},[2225,177076,2775],{"class":2774},[2225,177078,177079],{"class":2239}," tickets ",[2225,177081,2781],{"class":2235},[2225,177083,6466],{"class":2239},[2225,177085,1520],{"class":2235},[2225,177087,6471],{"class":2291},[2225,177089,17851],{"class":2235},[2225,177091,7070],{"class":2307},[2225,177093,2311],{"class":2235},[2225,177095,6366],{"class":6342},[2225,177097,3769],{"class":2235},[2225,177099,6361],{"class":2307},[2225,177101,2311],{"class":2235},[2225,177103,6366],{"class":6342},[2225,177105,85293],{"class":2235},[2225,177107,2789],{"class":2239},[2225,177109,2805],{"class":2235},[2225,177111,177112],{"class":2252},"tickets",[2225,177114,2805],{"class":2235},[2225,177116,2298],{"class":2235},[2225,177118,2301],{"class":2235},[2225,177120,177121,177123,177125,177127,177129,177131,177133,177135,177137,177139,177141,177143,177145],{"class":2227,"line":2288},[2225,177122,175317],{"class":2307},[2225,177124,2311],{"class":2235},[2225,177126,2236],{"class":2235},[2225,177128,6361],{"class":2307},[2225,177130,2311],{"class":2235},[2225,177132,177058],{"class":2291},[2225,177134,2789],{"class":2239},[2225,177136,2805],{"class":2235},[2225,177138,6631],{"class":2252},[2225,177140,2805],{"class":2235},[2225,177142,8495],{"class":2239},[2225,177144,33127],{"class":2235},[2225,177146,177147],{"class":2763},"     \u002F\u002F onDelete defaults to 'restrict'\n",[2225,177149,177150,177152],{"class":2227,"line":2304},[2225,177151,2359],{"class":2235},[2225,177153,2362],{"class":2239},[2225,177155,177156,177158,177160,177162,177164,177166,177168,177170,177172,177174,177176,177178,177180,177182,177184,177186,177188,177190,177192,177194,177196,177198,177200,177202],{"class":2227,"line":2356},[2225,177157,6500],{"class":2231},[2225,177159,6466],{"class":2239},[2225,177161,1520],{"class":2235},[2225,177163,58965],{"class":2291},[2225,177165,2789],{"class":2239},[2225,177167,2805],{"class":2235},[2225,177169,6631],{"class":2252},[2225,177171,2805],{"class":2235},[2225,177173,1381],{"class":2239},[2225,177175,1520],{"class":2235},[2225,177177,3161],{"class":2291},[2225,177179,2789],{"class":2239},[2225,177181,2805],{"class":2235},[2225,177183,6539],{"class":2252},[2225,177185,2805],{"class":2235},[2225,177187,2298],{"class":2235},[2225,177189,2236],{"class":2235},[2225,177191,58997],{"class":2307},[2225,177193,2311],{"class":2235},[2225,177195,2249],{"class":2235},[2225,177197,59104],{"class":2252},[2225,177199,2805],{"class":2235},[2225,177201,2243],{"class":2235},[2225,177203,2362],{"class":2239},[2225,177205,177206,177208,177211,177213,177215,177217,177219,177222,177224,177226,177228,177230,177232,177234,177236,177238,177240,177242,177244,177246,177248,177250,177252],{"class":2227,"line":2830},[2225,177207,6500],{"class":2231},[2225,177209,177210],{"class":2239}," tickets",[2225,177212,1520],{"class":2235},[2225,177214,3161],{"class":2291},[2225,177216,2789],{"class":2239},[2225,177218,2805],{"class":2235},[2225,177220,177221],{"class":2252},"t1",[2225,177223,2805],{"class":2235},[2225,177225,2298],{"class":2235},[2225,177227,2236],{"class":2235},[2225,177229,7070],{"class":2307},[2225,177231,2311],{"class":2235},[2225,177233,2249],{"class":2235},[2225,177235,177221],{"class":2252},[2225,177237,2805],{"class":2235},[2225,177239,2298],{"class":2235},[2225,177241,6361],{"class":2307},[2225,177243,2311],{"class":2235},[2225,177245,2249],{"class":2235},[2225,177247,6539],{"class":2252},[2225,177249,2805],{"class":2235},[2225,177251,2243],{"class":2235},[2225,177253,2362],{"class":2239},[2225,177255,177256],{"class":2227,"line":2861},[2225,177257,2285],{"emptyLinePlaceholder":2284},[2225,177259,177260],{"class":2227,"line":4246},[2225,177261,177262],{"class":2763},"\u002F\u002F restrict (default) — refuses, naming the referencing collection and count:\n",[2225,177264,177265,177267,177269,177271,177273,177275,177277,177279,177281,177283,177285,177287,177289,177291,177293,177295],{"class":2227,"line":4253},[2225,177266,6500],{"class":2231},[2225,177268,6466],{"class":2239},[2225,177270,1520],{"class":2235},[2225,177272,58965],{"class":2291},[2225,177274,2789],{"class":2239},[2225,177276,2805],{"class":2235},[2225,177278,6631],{"class":2252},[2225,177280,2805],{"class":2235},[2225,177282,1381],{"class":2239},[2225,177284,1520],{"class":2235},[2225,177286,3164],{"class":2291},[2225,177288,2789],{"class":2239},[2225,177290,2805],{"class":2235},[2225,177292,6539],{"class":2252},[2225,177294,2805],{"class":2235},[2225,177296,2362],{"class":2239},[2225,177298,177299],{"class":2227,"line":4259},[2225,177300,177301],{"class":2763},"\u002F\u002F → rejects with DictKeyInUseError\n",[2225,177303,177304],{"class":2227,"line":6548},[2225,177305,177306],{"class":2763},"\u002F\u002F   err.dictionaryName === 'status'; err.usedBy === 'tickets'; err.count === 1\n",[2225,177308,177309],{"class":2227,"line":6576},[2225,177310,2285],{"emptyLinePlaceholder":2284},[2225,177312,177313,177315,177317,177319,177321,177323,177325,177327,177329,177332],{"class":2227,"line":6582},[2225,177314,6500],{"class":2231},[2225,177316,177210],{"class":2239},[2225,177318,1520],{"class":2235},[2225,177320,3164],{"class":2291},[2225,177322,2789],{"class":2239},[2225,177324,2805],{"class":2235},[2225,177326,177221],{"class":2252},[2225,177328,2805],{"class":2235},[2225,177330,177331],{"class":2239},")                      ",[2225,177333,177334],{"class":2763},"\u002F\u002F retire the reference first\n",[2225,177336,177337,177339,177341,177343,177345,177347,177349,177351,177353,177355,177357,177359,177361,177363,177365,177367,177369],{"class":2227,"line":6587},[2225,177338,6500],{"class":2231},[2225,177340,6466],{"class":2239},[2225,177342,1520],{"class":2235},[2225,177344,58965],{"class":2291},[2225,177346,2789],{"class":2239},[2225,177348,2805],{"class":2235},[2225,177350,6631],{"class":2252},[2225,177352,2805],{"class":2235},[2225,177354,1381],{"class":2239},[2225,177356,1520],{"class":2235},[2225,177358,3164],{"class":2291},[2225,177360,2789],{"class":2239},[2225,177362,2805],{"class":2235},[2225,177364,6539],{"class":2252},[2225,177366,2805],{"class":2235},[2225,177368,8495],{"class":2239},[2225,177370,177371],{"class":2763},"\u002F\u002F now succeeds\n",[1111,177373,177374,177383,177390],{},[1114,177375,177376,177380,177381,1520],{},[950,177377,177378],{},[941,177379,152822],{}," refuses the delete before any mutation with ",[941,177382,152829],{},[1114,177384,177385,177389],{},[950,177386,177387],{},[941,177388,10753],{}," tombstones\u002Fdeletes every referencing record.",[1114,177391,177392,177396,177397,1520],{},[950,177393,177394],{},[941,177395,152838],{}," clears the referencing field via an ordinary ",[941,177398,20953],{},[937,177400,177401,177402,177405,177406,177408,177409,177411,177412,177414,177415,177419,177420,177422,177423,1625,177425,177427,177428,1261,177430,2601,177432,957],{},"The matrix tier behaves identically against a first-class collection — ",[941,177403,177404],{},"countries.delete(...)","\nthrows, cascades, or nullifies the same way — and ",[941,177407,152822],{}," matches against ",[941,177410,175515],{},", not the\nrow's storage id, so the non-default-",[941,177413,8364],{}," recipe above is fully covered.\n",[1007,177416,177417],{"href":686},[941,177418,2546],{}," composes the same way: ",[941,177421,152822],{}," refuses the forget call\nitself before any shred while references exist; ",[941,177424,10753],{},[941,177426,152838],{}," propagate after the shred and\nare reported on the forget result (",[941,177429,152543],{},[941,177431,152558],{},[941,177433,152573],{},[937,177435,7479,177436,177438,177439,177442,177443,177445],{},[950,177437,12111],{}," (rather than delete) a dictionary key that records already reference, use\n",[941,177440,177441],{},"vault.dictionary(name).rename(oldKey, newKey)"," — it rewrites every referencing record to the new\nkey in one operation, and is honored even when the referencing field declares ",[941,177444,175700],{}," (mid-rename both the old and new keys count as members).",[937,177447,177448,177449,177451,177452,177454,177455,2601,177457,177460],{},"A plain delete of a dictionary\u002Fcollection row that ",[950,177450,11313],{}," declared lookup field references is\ncompletely unaffected — ",[941,177453,137880],{}," only fires for dimensions a ",[941,177456,22762],{},[941,177458,177459],{},"via(lookup(...))"," declaration actually points at.",[1591,177462,177464],{"id":177463},"when-a-backing-rows-key-cant-be-read","When a backing row's key can't be read",[937,177466,177467,177468,177470,177471,177473,177474,177476,177477,177480],{},"A matrix dimension with a custom ",[941,177469,8364],{}," resolves each reference by reading ",[941,177472,175515],{}," off the\nbacking row. If that row is corrupted — the ",[941,177475,8364],{}," field missing, or holding a non-scalar value —\nthe reference ",[950,177478,177479],{},"can't be resolved",", and the two policies diverge:",[1111,177482,177483,177507],{},[1114,177484,177485,177489,177490,177493,177494,968,177497,55293,177499,177502,177503,177506],{},[950,177486,177487],{},[941,177488,152822],{}," fails closed: the delete\u002Fforget refuses with ",[941,177491,177492],{},"RestrictRefUnresolvableError","\n(carrying ",[941,177495,177496],{},"dimension",[941,177498,8364],{},[941,177500,177501],{},"referencing"," edge as ",[941,177504,177505],{},"\"collection.field\"","), since it\ncannot prove the row is unreferenced.",[1114,177508,177509,177515,177516,177519,177520,177523,177524,177527,177528,957],{},[950,177510,177511,1261,177513],{},[941,177512,10753],{},[941,177514,152838],{}," proceed, but the edge that could not be propagated is never dropped\nsilently — it is reported on a ",[941,177517,177518],{},"lookup:propagation-residue"," vault event\n(",[941,177521,177522],{},"{ vault, dimension, key, residue }",", each residue entry formatted ",[941,177525,177526],{},"dimension:key:collection.field",",\ne.g. ",[941,177529,177530],{},"'countries:row-broken:orders.country'",[937,177532,177533,177534,177536],{},"A row whose ",[941,177535,8364],{}," is present and scalar is unaffected — this concerns only corrupted backing rows.",[1022,177538,177540],{"id":177539},"reserved-tier-sync-dictionaries-travel","Reserved-tier sync — dictionaries travel",[937,177542,177543,177544,177546,177547,2085,177549,177551,177552,177554,177555,177557,177559,177560,177563,177564,177567,177568,177570],{},"Reserved ",[941,177545,6796],{}," collections participate in ",[1007,177548,864],{"href":865},[941,177550,21460],{},"\nwrites enter the dirty log and dispatch like any other write, and ",[941,177553,56862],{}," applies remote\ndictionary rows through the ordinary path via an explicit reserved-prefix registry (every ",[1092,177556,11805],{},[941,177558,6702],{},"-prefixed internal namespace keeps its skip semantics). Dictionary ",[950,177561,177562],{},"deletes"," travel as\nversion-ordered ",[1007,177565,177566],{"href":57016},"delete markers",", so a deleted\nkey cannot be silently resurrected by a stale peer's next push. Before ",[941,177569,111464],{},", dictionary\nwrites bypassed sync entirely and only crossed devices via backup\u002Fbundle export.",[1022,177572,177574,177576,177577,149889],{"id":177573},"describe-the-normalized-lookup-block",[941,177575,3926],{}," — the normalized ",[941,177578,731],{},[937,177580,177581,177582,1625,177584,1625,177586,177588,177589,177591,177592,177596,177597,177599],{},"A native ",[941,177583,163865],{},[941,177585,163868],{},[941,177587,163871],{}," field contributes a normalized ",[941,177590,731],{}," block to\n",[1007,177593,177594],{"href":678},[941,177595,1380],{}," — alongside, not replacing, the legacy ",[941,177598,147773],{},"\nblock:",[2217,177601,177603],{"className":2219,"code":177602,"language":2221,"meta":1037,"style":1037},"orders.describe().fields.find((f) => f.key === 'country')?.lookup\n\u002F\u002F {\n\u002F\u002F   dimension: 'countries', backing: 'collection', vocabulary: 'closed', key: 'iso2',\n\u002F\u002F   altKeys: ['iso3', 'callPrefix'], present: { label: 'name', by: 'locale' },\n\u002F\u002F   sortBy: 'name', onDelete: 'restrict',\n\u002F\u002F }\n",[941,177604,177605,177654,177659,177664,177669,177674],{"__ignoreMap":1037},[2225,177606,177607,177609,177611,177613,177615,177617,177619,177621,177623,177625,177627,177629,177631,177633,177635,177637,177639,177641,177643,177645,177647,177649,177651],{"class":2227,"line":2228},[2225,177608,150830],{"class":2239},[2225,177610,1520],{"class":2235},[2225,177612,6153],{"class":2291},[2225,177614,4228],{"class":2239},[2225,177616,1520],{"class":2235},[2225,177618,57285],{"class":2239},[2225,177620,1520],{"class":2235},[2225,177622,119296],{"class":2291},[2225,177624,2789],{"class":2239},[2225,177626,2789],{"class":2235},[2225,177628,67059],{"class":7052},[2225,177630,1381],{"class":2235},[2225,177632,8116],{"class":2774},[2225,177634,67074],{"class":2239},[2225,177636,1520],{"class":2235},[2225,177638,77646],{"class":2239},[2225,177640,33083],{"class":2235},[2225,177642,2249],{"class":2235},[2225,177644,160921],{"class":2252},[2225,177646,2805],{"class":2235},[2225,177648,1381],{"class":2239},[2225,177650,8457],{"class":2235},[2225,177652,177653],{"class":2239},"lookup\n",[2225,177655,177656],{"class":2227,"line":1041},[2225,177657,177658],{"class":2763},"\u002F\u002F {\n",[2225,177660,177661],{"class":2227,"line":1038},[2225,177662,177663],{"class":2763},"\u002F\u002F   dimension: 'countries', backing: 'collection', vocabulary: 'closed', key: 'iso2',\n",[2225,177665,177666],{"class":2227,"line":2288},[2225,177667,177668],{"class":2763},"\u002F\u002F   altKeys: ['iso3', 'callPrefix'], present: { label: 'name', by: 'locale' },\n",[2225,177670,177671],{"class":2227,"line":2304},[2225,177672,177673],{"class":2763},"\u002F\u002F   sortBy: 'name', onDelete: 'restrict',\n",[2225,177675,177676],{"class":2227,"line":2356},[2225,177677,177678],{"class":2763},"\u002F\u002F }\n",[937,177680,177681,177683,177684,177686,177687,177689,177690,177692,177693,177695,177696,177698,177699,177701],{},[941,177682,147604],{}," derives to ",[941,177685,149754],{}," whenever a ",[941,177688,731],{}," block is present. The statically-known key set\nappears only where one exists (enum tier, or a dict tier with declared ",[941,177691,8678],{},") — a matrix-tier\nfield has no ",[941,177694,8678],{}," (membership lives in the backing collection's rows), and a bare ",[941,177697,163868],{},"\nfield omits ",[941,177700,177496],{}," entirely (it has no backing store).",[1022,177703,177705],{"id":177704},"dictkey-and-staticdict-are-aliases","dictKey and staticDict are aliases",[937,177707,960,177708,175887,177710,1261,177712,70057,177714,177716,177717,177719,177720,177722,177723,1625,177725,1625,177727,177729,177730,177732,177733,177735,177736,1324,177738,177740,177741,177743,177744,1625,177746,1625,177748,177750,177751,1625,177753,1625,177755,177757],{},[1007,177709,701],{"href":702},[941,177711,58348],{},[941,177713,163843],{},[950,177715,146774],{}," of this\nfeature's descriptor shape — not deprecated, fully supported: internally they build the\nequivalent descriptor (",[941,177718,58348],{}," ≙ the reserved tier, ",[941,177721,163843],{}," ≙ the static tier) and\ntheir stored envelopes, ",[941,177724,6339],{},[941,177726,147604],{},[941,177728,147773],{}," describe() output, and ",[941,177731,61808],{}," dressing are\nbyte-identical to the native equivalent. Two differences remain, by design: the aliases do ",[950,177734,1004],{},"\nemit the new ",[941,177737,3926],{},[941,177739,731],{}," block, and they do not gain the new capabilities — altKeys,\n",[941,177742,175700],{}," refusal, the matrix tier, and ",[941,177745,152822],{},[941,177747,10753],{},[941,177749,152838],{}," semantics\nbelong to the native ",[941,177752,163865],{},[941,177754,163868],{},[941,177756,163871],{}," spellings only.",[1022,177759,177761],{"id":177760},"late-attach-declaring-lookup-fields-after-construction","Late-attach — declaring lookup fields after construction",[937,177763,177764,177766,177767,1324,177770,177773,177774,177776,177777,177779,177780,177783],{},[941,177765,22762],{}," can be declared on a ",[950,177768,177769],{},"later",[941,177771,177772],{},"vault.collection(name, …)"," call against a collection\nthat is already open, not only at first construction — a \"late attach\". The fields go live at once:\nclosed-vocabulary enforcement, altKey normalization, and ",[941,177775,24050],{}," dressing (direct and\nthrough joins) all take effect immediately, and the reference-graph edges (",[941,177778,137880],{},") register on\nattach. Enforcement is ",[950,177781,177782],{},"not retroactive"," — records written before the attach keep whatever value\nthey already held, since they never ran altKey ingest or the closed-vocabulary check.",[937,177785,177786,177787,177790,177791,177793,177794,177796,177797,177799],{},"The matrix tier has one precondition: the backing dimension must ",[950,177788,177789],{},"already be open in this session,\nin eager mode",". A late matrix attach against a dimension that is unopened (or was opened\n",[941,177792,175917],{},") throws ",[941,177795,12881],{}," naming the field and dimension; open the backing\ncollection first and the same attach succeeds. The enum, static, and reserved (",[941,177798,163871],{},") tiers\nattach with no such precondition.",[937,177801,177802,177803,177805,177806,177808,177809,177811,177812,177814],{},"If a late-attached field collides with a field already owned by a different ",[941,177804,896],{}," family (for\nexample attaching a ",[941,177807,731],{}," onto a field already declared through ",[941,177810,22765],{},"), the whole call\nthrows ",[941,177813,12881],{}," naming the field and both families — the attach is all-or-nothing.",[1022,177816,102889],{"id":102888},[1591,177818,177820],{"id":177819},"lookupdimension-opts",[941,177821,177822],{},"lookup(dimension, opts?)",[937,177824,177825,177826,1520],{},"The general factory — any tier via ",[941,177827,175768],{},[1338,177829,177830,177842],{},[1341,177831,177832],{},[1344,177833,177834,177836,177838,177840],{},[1347,177835,101060],{},[1347,177837,20194],{},[1347,177839,20197],{},[1347,177841,7834],{},[1357,177843,177844,177862,177880,177905,177928,177949,177967,177986,178001,178017,178035],{},[1344,177845,177846,177850,177854,177859],{},[1362,177847,177848],{},[941,177849,8364],{},[1362,177851,177852],{},[941,177853,7259],{},[1362,177855,177856],{},[941,177857,177858],{},"'id'",[1362,177860,177861],{},"Backing-row field holding the canonical key.",[1344,177863,177864,177868,177872,177874],{},[1362,177865,177866],{},[941,177867,174864],{},[1362,177869,177870],{},[941,177871,111053],{},[1362,177873,3610],{},[1362,177875,177876,177877,177879],{},"Backing-row fields whose values normalize to ",[941,177878,8364],{}," on write. Matrix tier requires the backing collection open in eager mode.",[1344,177881,177882,177887,177892,177896],{},[1362,177883,177884],{},[941,177885,177886],{},"vocabulary",[1362,177888,177889],{},[941,177890,177891],{},"'open' | 'closed'",[1362,177893,177894],{},[941,177895,176059],{},[1362,177897,177898,177901,177902,957],{},[941,177899,177900],{},"'closed'"," refuses unknown keys at write time (",[941,177903,177904],{},"UnknownLookupKeyError",[1344,177906,177907,177912,177917,177919],{},[1362,177908,177909],{},[941,177910,177911],{},"present",[1362,177913,177914],{},[941,177915,177916],{},"{ label: string; by?: string }",[1362,177918,3610],{},[1362,177920,177921,177922,5502,177924,177927],{},"Which backing-row field supplies ",[941,177923,24050],{},[941,177925,177926],{},"by: 'locale'"," makes it locale-keyed.",[1344,177929,177930,177934,177938,177940],{},[1362,177931,177932],{},[941,177933,177008],{},[1362,177935,177936],{},[941,177937,7259],{},[1362,177939,3610],{},[1362,177941,177942,177943,177945,177946,177948],{},"Backing-row field a plain ",[941,177944,177019],{}," sorts by (needs ",[941,177947,149032],{}," when locale-keyed).",[1344,177950,177951,177955,177960,177964],{},[1362,177952,177953],{},[941,177954,175768],{},[1362,177956,177957],{},[941,177958,177959],{},"'static' | 'reserved' | 'collection'",[1362,177961,177962],{},[941,177963,175879],{},[1362,177965,177966],{},"The tier.",[1344,177968,177969,177973,177978,177983],{},[1362,177970,177971],{},[941,177972,137880],{},[1362,177974,177975],{},[941,177976,177977],{},"'restrict' | 'cascade' | 'nullify'",[1362,177979,177980],{},[941,177981,177982],{},"'restrict'",[1362,177984,177985],{},"Reference semantics on delete\u002Fforget of a referenced backing row.",[1344,177987,177988,177992,177996,177998],{},[1362,177989,177990],{},[941,177991,8678],{},[1362,177993,177994],{},[941,177995,111053],{},[1362,177997,3610],{},[1362,177999,178000],{},"Declared key list (enum\u002Fdict tiers).",[1344,178002,178003,178007,178012,178014],{},[1362,178004,178005],{},[941,178006,1338],{},[1362,178008,178009],{},[941,178010,178011],{},"Record\u003Cstring, Record\u003Cstring, string>>",[1362,178013,3610],{},[1362,178015,178016],{},"Static tier's in-code key → locale → label table.",[1344,178018,178019,178023,178027,178029],{},[1362,178020,178021],{},[941,178022,149032],{},[1362,178024,178025],{},[941,178026,7259],{},[1362,178028,3610],{},[1362,178030,178031,178032,178034],{},"Fixed locale for declared-",[941,178033,177008],{}," ordering and locale-less label reads.",[1344,178036,178037,178045,178047,178049],{},[1362,178038,178039,1261,178041,1261,178043],{},[941,178040,162262],{},[941,178042,161968],{},[941,178044,78251],{},[1362,178046,3610],{},[1362,178048,3610],{},[1362,178050,178051,178052,178054],{},"Label-resolution policy, shared with the ",[1007,178053,701],{"href":702}," engine.",[1591,178056,178058,178061,178062,1381],{"id":178057},"enumofkeys-also-exported-as-enum",[941,178059,178060],{},"enumOf(keys)"," (also exported as ",[941,178063,28421],{},[937,178065,178066,178067,1707,178069,1952,178072,178075],{},"Inline closed enum — no backing store, no dimension name, ",[941,178068,175700],{},[941,178070,178071],{},"onDelete: 'restrict'",[941,178073,178074],{},"enumOf(['draft', 'paid'] as const)"," types the field to the literal\nunion.",[1591,178077,178079],{"id":178078},"dictdimension-opts",[941,178080,178081],{},"dict(dimension, opts?)",[937,178083,178084,178085,34689,178088,178091,178092,178095,178096,968,178098,968,178100,968,178102,1707,178104,968,178106,13675,178108,178110],{},"Reserved-tier dictionary field — backed by ",[941,178086,178087],{},"vault.dictionary(dimension)",[941,178089,178090],{},"_dict_\u003Cdimension>","\ncollection, ",[941,178093,178094],{},"vocabulary: 'open'"," by default. Accepts ",[941,178097,8678],{},[941,178099,177886],{},[941,178101,177911],{},[941,178103,137880],{},[941,178105,162262],{},[941,178107,161968],{},[941,178109,174864],{}," (the dictionary cache is always synchronous, so none of\nthe matrix tier's hydration constraints apply — and none of its altKey support either).",[1591,178112,120265],{"id":120264},[1338,178114,178115,178123],{},[1341,178116,178117],{},[1344,178118,178119,178121],{},[1347,178120,12126],{},[1347,178122,120276],{},[1357,178124,178125,178137,178158],{},[1344,178126,178127,178131],{},[1362,178128,178129],{},[941,178130,177904],{},[1362,178132,178133,178134,178136],{},"A write supplies a key (after altKey normalization) that a ",[941,178135,175700],{}," field's backing dimension doesn't contain.",[1344,178138,178139,178143],{},[1362,178140,178141],{},[941,178142,152829],{},[1362,178144,178145,178146,178148,178149,968,178152,178155,178156,1520],{},"A backing dictionary\u002Fcollection row still referenced by a ",[941,178147,152822],{}," lookup field is deleted or forgotten. Carries ",[941,178150,178151],{},"dictionaryName",[941,178153,178154],{},"usedBy"," (the referencing collection), and ",[941,178157,3236],{},[1344,178159,178160,178164],{},[1362,178161,178162],{},[941,178163,177492],{},[1362,178165,3289,178166,178168,178169,178171,178172,968,178174,2462,178176,953,178178,957],{},[941,178167,152822],{}," delete\u002Fforget cannot resolve a referenced backing row's compare-key (its ",[941,178170,8364],{}," field is missing or non-scalar on a corrupted row), so it fails closed. Carries ",[941,178173,177496],{},[941,178175,8364],{},[941,178177,177501],{},[941,178179,177505],{},[1022,178181,68210],{"id":68209},[1111,178183,178184,178207,178224],{},[1114,178185,178186,178189,178190,178192,178193,178195,178196,178198,178199,178201,178202,178204,178205,1520],{},[950,178187,178188],{},"Matrix-tier altKeys, closed vocabulary, and label presentation need a hydrated backing\ncollection"," — see the cold-session caveat above; with a custom ",[941,178191,8364],{},", a cold dimension yields no\n",[941,178194,24050],{}," until it is opened (only the default ",[941,178197,176670],{}," has an on-demand fallback).\n",[941,178200,174864],{}," additionally refuses lazy (",[941,178203,175917],{},") backing collections at write time\nwith ",[941,178206,12881],{},[1114,178208,178209,178217,178218,178220,178221,1520],{},[950,178210,178211,178212,21348,178214,178216],{},"Declared ",[941,178213,177008],{},[941,178215,149032],{}," silently sorts by code"," when the label is\nlocale-keyed — heed the declare-time ",[941,178219,15856],{},", or use ",[941,178222,178223],{},"orderBy(..., { by: 'label' })",[1114,178225,178226,178231,178232,178234],{},[950,178227,178228,178230],{},[941,178229,152822],{}," is a behavior change for declared lookup fields"," relative to the alias era:\n",[941,178233,152829],{}," existed and was documented long before it was ever thrown; a delete of a\nstill-referenced dictionary row now actually refuses. Undeclared dimensions are unaffected.",[1022,178236,2621],{"id":2620},[1111,178238,178239,178244,178253,178263,178268,178273,178283],{},[1114,178240,178241,178243],{},[1007,178242,896],{"href":897}," — the field-feature surface this binding plugs into",[1114,178245,178246,964,178248,1625,178250,178252],{},[1007,178247,701],{"href":702},[941,178249,58348],{},[941,178251,163843],{},", dictionaries, and the label-resolution engine",[1114,178254,178255,178257,178258,1625,178260,178262],{},[1007,178256,150373],{"href":682}," — record-to-record foreign keys (",[941,178259,62282],{},[941,178261,173697],{},"); lookup fields are code-to-dimension references with label resolution on top",[1114,178264,178265,178267],{},[1007,178266,864],{"href":865}," — delete markers and how reserved dictionaries replicate",[1114,178269,178270,178272],{},[1007,178271,685],{"href":686}," — how reference semantics gate or propagate an erasure",[1114,178274,178275,1169,178277,178279,178280,178282],{},[1007,178276,14900],{"href":678},[941,178278,3926],{}," surface the ",[941,178281,731],{}," block extends",[1114,178284,178285,1368,178287],{},[1007,178286,61743],{"href":598},[1007,178288,1019],{"href":1018},[2645,178290,178291],{},"html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}html pre.shiki code .sHdIc, html code.shiki .sHdIc{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#EEFFFF;--shiki-default-font-style:italic;--shiki-dark:#BABED8;--shiki-dark-font-style:italic}",{"title":1037,"searchDepth":1038,"depth":1038,"links":178293},[178294,178295,178296,178297,178298,178299,178300,178302,178303,178307,178308,178310,178311,178312,178319,178320],{"id":29856,"depth":1041,"text":29857},{"id":174885,"depth":1041,"text":174886},{"id":16992,"depth":1041,"text":16993},{"id":175895,"depth":1041,"text":175896},{"id":175948,"depth":1041,"text":175949},{"id":176102,"depth":1041,"text":176103},{"id":176536,"depth":1041,"text":178301},"Presentation — \u003Cfield>Label, in reads and joins",{"id":176681,"depth":1041,"text":176682},{"id":177032,"depth":1041,"text":178304,"children":178305},"Reference semantics — restrict (default), cascade, nullify",[178306],{"id":177463,"depth":1038,"text":177464},{"id":177539,"depth":1041,"text":177540},{"id":177573,"depth":1041,"text":178309},"describe() — the normalized lookup block",{"id":177704,"depth":1041,"text":177705},{"id":177760,"depth":1041,"text":177761},{"id":102888,"depth":1041,"text":102889,"children":178313},[178314,178315,178317,178318],{"id":177819,"depth":1038,"text":177822},{"id":178057,"depth":1038,"text":178316},"enumOf(keys) (also exported as enum)",{"id":178078,"depth":1038,"text":178081},{"id":120264,"depth":1038,"text":120265},{"id":68209,"depth":1041,"text":68210},{"id":2620,"depth":1041,"text":2621},"The lookup()\u002FenumOf()\u002Fdict() field feature: a field stores a canonical code and resolves against a backing dimension, with reference integrity.",{},{"title":731,"description":178321},"Reference fields over enum, dictionary, and first-class collection dimensions","_MvjHtMPb12hgYIfNRqNRVDP_-IU0IQinZS51KGWkZg",{"id":178327,"title":735,"access":12,"apiToc":929,"body":178328,"category":1042,"description":178826,"extension":1044,"meta":178827,"navigation":1042,"nest":6051,"path":736,"seo":178828,"sinceVersion":1042,"status":38550,"stem":737,"subtitle":1042,"tier":1473,"tocExternal":1042,"__hash__":178829},"docs\u002Fservices\u002Fmanifest.md",{"type":931,"value":178329,"toc":178812},[178330,178332,178335,178337,178344,178366,178377,178390,178392,178406,178408,178413,178447,178449,178453,178459,178557,178569,178575,178579,178683,178692,178696,178711,178715,178726,178729,178739,178743,178759,178765,178769,178778,178787,178793,178797,178809],[934,178331,735],{"id":735},[46871,178333],{":rows":178334},"[{\"label\":\"Subpath\",\"value\":\"`@noy-db\u002Fhub\u002Fpod` (also re-exported from the root barrel)\"},{\"label\":\"Factory\",\"value\":\"none — direct named imports\"},{\"label\":\"Cluster\",\"value\":\"F — Snapshot & Portability\"}]",[1022,178336,29857],{"id":29856},[937,178338,178339,178340,178343],{},"This service gives a pod one place to answer ",[1092,178341,178342],{},"\"what schema generation is this pod at, and does each collection's stored schema still match what it claims?\""," — without opening every collection.",[937,178345,178346,178347,178350,178351,178354,178355,178358,178359,178361,178362,178365],{},"It stores a single ",[950,178348,178349],{},"schema manifest"," record in a reserved ",[941,178352,178353],{},"_manifest"," collection. The manifest is an ",[950,178356,178357],{},"index, not a copy",": for each collection it holds the schema-fence generation, a SHA-256 of that collection's canonicalised JSON Schema, and the stable field ids, but never the schema body itself. The bodies stay at ",[941,178360,166841],{},", each encrypted under ",[950,178363,178364],{},"that collection's own DEK",". Inlining them would either leak one collection's schema to a principal scoped to a different one, or force the manifest onto a shared key it has no business holding — so the manifest carries metadata and leaves bodies where they are.",[937,178367,5093,178368,964,178371,178373,178374,178376],{},[941,178369,178370],{},"aggregateHash",[941,178372,136678],{}," over the canonical JSON of the whole per-collection map — binds the index as a unit. Canonical JSON sorts keys at every depth, so the hash does not depend on insertion order: any change to any entry changes ",[941,178375,178370],{}," too.",[937,178378,178379,178380,1952,178383,178386,178387,178389],{},"The manifest is a ",[950,178381,178382],{},"derivable cache, not a source of truth",[941,178384,178385],{},"deriveSchemaManifest()"," is a pure projection of the fence generation plus the ",[941,178388,35623],{}," entries the caller can decrypt, so re-deriving against unchanged state always produces the same manifest — which is what makes dump → restore → re-derive match the original with no restore-side special-casing.",[1022,178391,111660],{"id":111659},[1111,178393,178394,178400,178403],{},[1114,178395,178396,178397,178399],{},"Opening a ",[941,178398,1402],{}," pod and needing to know, before touching data, whether the reader's code is behind the pod's schema generation",[1114,178401,178402],{},"Verifying that a collection's stored schema still matches the content hash the pod recorded",[1114,178404,178405],{},"Carrying stable field identity (from the schema-identifiers work) across a pod boundary",[1022,178407,99864],{"id":99863},[937,178409,163731,178410,178412],{},[941,178411,943],{}," factory — the surface tree-shakes through direct named imports:",[2217,178414,178416],{"className":2219,"code":178415,"language":2221,"meta":1037,"style":1037},"import { open, deriveSchemaManifest, MANIFEST_COLLECTION } from '@noy-db\u002Fhub\u002Fpod'\n",[941,178417,178418],{"__ignoreMap":1037},[2225,178419,178420,178422,178424,178427,178429,178432,178434,178437,178439,178441,178443,178445],{"class":2227,"line":2228},[2225,178421,2232],{"class":2231},[2225,178423,2236],{"class":2235},[2225,178425,178426],{"class":2239}," open",[2225,178428,2298],{"class":2235},[2225,178430,178431],{"class":2239}," deriveSchemaManifest",[2225,178433,2298],{"class":2235},[2225,178435,178436],{"class":2239}," MANIFEST_COLLECTION",[2225,178438,2243],{"class":2235},[2225,178440,2246],{"class":2231},[2225,178442,2249],{"class":2235},[2225,178444,70036],{"class":2252},[2225,178446,2256],{"class":2235},[1022,178448,29893],{"id":29892},[1591,178450,178452],{"id":178451},"opening-a-pod-through-the-full-read-path","Opening a pod through the full read path",[937,178454,178455,178458],{},[941,178456,178457],{},"open()"," composes the whole sequence: read the header → optionally verify its signature → unlock and restore → re-derive the schema manifest → check the generation fence → hand back the vault.",[2217,178460,178462],{"className":2219,"code":178461,"language":2221,"meta":1037,"style":1037},"import { open } from '@noy-db\u002Fhub'\n\nconst { db, vault } = await open(podBytes, {\n  user: 'alice',\n  secret: 'a-strong-secret',\n  trustedKeys,          \u002F\u002F omit to skip signature verification\n})\n",[941,178463,178464,178482,178486,178513,178527,178541,178551],{"__ignoreMap":1037},[2225,178465,178466,178468,178470,178472,178474,178476,178478,178480],{"class":2227,"line":2228},[2225,178467,2232],{"class":2231},[2225,178469,2236],{"class":2235},[2225,178471,178426],{"class":2239},[2225,178473,2243],{"class":2235},[2225,178475,2246],{"class":2231},[2225,178477,2249],{"class":2235},[2225,178479,1086],{"class":2252},[2225,178481,2256],{"class":2235},[2225,178483,178484],{"class":2227,"line":1041},[2225,178485,2285],{"emptyLinePlaceholder":2284},[2225,178487,178488,178490,178492,178494,178496,178498,178500,178502,178504,178506,178509,178511],{"class":2227,"line":1038},[2225,178489,2775],{"class":2774},[2225,178491,2236],{"class":2235},[2225,178493,2842],{"class":2239},[2225,178495,2298],{"class":2235},[2225,178497,2835],{"class":2239},[2225,178499,2359],{"class":2235},[2225,178501,6346],{"class":2235},[2225,178503,2784],{"class":2231},[2225,178505,178426],{"class":2291},[2225,178507,178508],{"class":2239},"(podBytes",[2225,178510,2298],{"class":2235},[2225,178512,2301],{"class":2235},[2225,178514,178515,178517,178519,178521,178523,178525],{"class":2227,"line":2288},[2225,178516,4205],{"class":2307},[2225,178518,2311],{"class":2235},[2225,178520,2249],{"class":2235},[2225,178522,6408],{"class":2252},[2225,178524,2805],{"class":2235},[2225,178526,1707],{"class":2235},[2225,178528,178529,178531,178533,178535,178537,178539],{"class":2227,"line":2304},[2225,178530,10423],{"class":2307},[2225,178532,2311],{"class":2235},[2225,178534,2249],{"class":2235},[2225,178536,6421],{"class":2252},[2225,178538,2805],{"class":2235},[2225,178540,1707],{"class":2235},[2225,178542,178543,178546,178548],{"class":2227,"line":2356},[2225,178544,178545],{"class":2239},"  trustedKeys",[2225,178547,2298],{"class":2235},[2225,178549,178550],{"class":2763},"          \u002F\u002F omit to skip signature verification\n",[2225,178552,178553,178555],{"class":2227,"line":2830},[2225,178554,2359],{"class":2235},[2225,178556,2362],{"class":2239},[937,178558,178559,178560,6789,178562,178565,178566,1520],{},"If the pod was written by a newer schema generation than the opening code understands, ",[941,178561,178457],{},[941,178563,178564],{},"MigrationRequiredError"," rather than reading data under the wrong assumptions. A caller that knowingly wants to proceed passes ",[941,178567,178568],{},"allowGenerationAhead: true",[937,178570,178571,178572,178574],{},"A pod written before this service existed opens normally — its manifest is simply re-derived from ",[941,178573,35623],{}," on the fly.",[1591,178576,178578],{"id":178577},"reading-the-manifest-directly","Reading the manifest directly",[2217,178580,178582],{"className":2219,"code":178581,"language":2221,"meta":1037,"style":1037},"import { deriveSchemaManifest } from '@noy-db\u002Fhub\u002Fpod'\n\nconst { manifest, undecodableCollections } = await deriveSchemaManifest(store, 'acme', lookupDEK)\n\nmanifest.generation        \u002F\u002F fence generation at derivation time\nmanifest.collections       \u002F\u002F per-collection { generation, contentHash, fieldIds? }\nmanifest.aggregateHash     \u002F\u002F binds the whole index\n",[941,178583,178584,178602,178606,178643,178647,178659,178671],{"__ignoreMap":1037},[2225,178585,178586,178588,178590,178592,178594,178596,178598,178600],{"class":2227,"line":2228},[2225,178587,2232],{"class":2231},[2225,178589,2236],{"class":2235},[2225,178591,178431],{"class":2239},[2225,178593,2243],{"class":2235},[2225,178595,2246],{"class":2231},[2225,178597,2249],{"class":2235},[2225,178599,70036],{"class":2252},[2225,178601,2256],{"class":2235},[2225,178603,178604],{"class":2227,"line":1041},[2225,178605,2285],{"emptyLinePlaceholder":2284},[2225,178607,178608,178610,178612,178615,178617,178620,178622,178624,178626,178628,178630,178632,178634,178636,178638,178640],{"class":2227,"line":1038},[2225,178609,2775],{"class":2774},[2225,178611,2236],{"class":2235},[2225,178613,178614],{"class":2239}," manifest",[2225,178616,2298],{"class":2235},[2225,178618,178619],{"class":2239}," undecodableCollections ",[2225,178621,2359],{"class":2235},[2225,178623,6346],{"class":2235},[2225,178625,2784],{"class":2231},[2225,178627,178431],{"class":2291},[2225,178629,39702],{"class":2239},[2225,178631,2298],{"class":2235},[2225,178633,2249],{"class":2235},[2225,178635,6450],{"class":2252},[2225,178637,2805],{"class":2235},[2225,178639,2298],{"class":2235},[2225,178641,178642],{"class":2239}," lookupDEK)\n",[2225,178644,178645],{"class":2227,"line":2288},[2225,178646,2285],{"emptyLinePlaceholder":2284},[2225,178648,178649,178651,178653,178656],{"class":2227,"line":2304},[2225,178650,735],{"class":2239},[2225,178652,1520],{"class":2235},[2225,178654,178655],{"class":2239},"generation        ",[2225,178657,178658],{"class":2763},"\u002F\u002F fence generation at derivation time\n",[2225,178660,178661,178663,178665,178668],{"class":2227,"line":2356},[2225,178662,735],{"class":2239},[2225,178664,1520],{"class":2235},[2225,178666,178667],{"class":2239},"collections       ",[2225,178669,178670],{"class":2763},"\u002F\u002F per-collection { generation, contentHash, fieldIds? }\n",[2225,178672,178673,178675,178677,178680],{"class":2227,"line":2830},[2225,178674,735],{"class":2239},[2225,178676,1520],{"class":2235},[2225,178678,178679],{"class":2239},"aggregateHash     ",[2225,178681,178682],{"class":2763},"\u002F\u002F binds the whole index\n",[937,178684,178685,178688,178689,178691],{},[941,178686,178687],{},"undecodableCollections"," lists collections that exist in ",[941,178690,35623],{}," but which this principal cannot decrypt — see below.",[1022,178693,178695],{"id":178694},"reserved-collection","Reserved collection",[937,178697,178698,178700,178701,178704,178705,178707,178708,178710],{},[941,178699,178353],{}," is reserved: ",[941,178702,178703],{},"vault.collection('_manifest')"," is refused, even for the owner. Records inside are keyed by manifest ",[1092,178706,27895],{},"; today only ",[941,178709,23934],{}," exists. Like other reserved collections, it travels inside a pod dump.",[1022,178712,178714],{"id":178713},"concurrency-refuse-dont-merge","Concurrency: refuse, don't merge",[937,178716,178717,178718,178721,178722,178725],{},"Direct manifest edits use ",[950,178719,178720],{},"strict CAS — refuse, not retry",". If the expected version does not match, the write throws ",[941,178723,178724],{},"ManifestConflictError",". This is the one writer in the hub that behaves this way; every other reserved-collection writer retries on conflict.",[937,178727,178728],{},"The divergence is deliberate. A manifest write encodes a specific point-in-time claim about the pod's whole schema generation and content-hash set. Two concurrent direct edits must be surfaced, never silently merged — a caller that lost the race needs to re-derive against fresh state and decide, not have a stale claim quietly re-applied on top of someone else's.",[937,178730,178731,178732,178735,178736,178738],{},"The sync path that runs after a schema write is different: it is a ",[1092,178733,178734],{},"re-derivation triggered by a write that already landed",", so it is allowed to retry, and it loops until the manifest provably converges. That matters when several collections are declared in one ",[941,178737,1749],{}," call and their schema writes race each other.",[1022,178740,178742],{"id":178741},"scoped-principals-see-less","Scoped principals see less",[937,178744,178745,178746,178748,178749,178751,178752,178755,178756,178758],{},"A collection-scoped grantee can decrypt ",[941,178747,178353],{}," but not a sibling collection's ",[941,178750,35623],{}," entry. When that happens, the derivation reports the collections it could not read, and the sync path ",[950,178753,178754],{},"skips entirely"," — no compare, no write. Writing a manifest derived from a partial view would drop the invisible siblings and assert an ",[941,178757,178370],{}," over entries it never verified.",[937,178760,178761,178762,178764],{},"This is safe because the source of truth was already updated by the principal's own declare, ",[941,178763,178457],{}," always re-derives rather than trusting the cache, and the next fully-visible principal's declare refreshes it.",[1022,178766,178768],{"id":178767},"disclosure-surface","Disclosure surface",[937,178770,178771,178772,31984,178774,178777],{},"Worth stating plainly: ",[941,178773,178353],{},[950,178775,178776],{},"one DEK for the whole pod",", not one per collection, and the system-prefix propagation rule wraps it into every granted principal's keyring regardless of their permission scope.",[937,178779,178780,178781,178783,178784,178786],{},"So any vault principal, however narrowly scoped, can decrypt the entire index — every collection name, every field name, every content hash and generation, including for collections they have no data access to. A principal scoped to ",[941,178782,6487],{}," can still learn that a ",[941,178785,74544],{}," collection exists and what its fields are called.",[937,178788,178789,178790,178792],{},"Field ",[1092,178791,148357],{}," are never exposed. But collection and field naming conventions are themselves sensitive in some domains, and this is an accepted consequence of the index design rather than an oversight. Choose names accordingly.",[1022,178794,178796],{"id":178795},"availability","Availability",[937,178798,178799,178800,178802,178803,178805,178806,178808],{},"Not opted into: nothing writes ",[941,178801,178353],{},", and a pod simply has no manifest record — ",[941,178804,178457],{}," re-derives from ",[941,178807,35623],{}," when it needs one.",[2645,178810,178811],{},"html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}",{"title":1037,"searchDepth":1038,"depth":1038,"links":178813},[178814,178815,178816,178817,178821,178822,178823,178824,178825],{"id":29856,"depth":1041,"text":29857},{"id":111659,"depth":1041,"text":111660},{"id":99863,"depth":1041,"text":99864},{"id":29892,"depth":1041,"text":29893,"children":178818},[178819,178820],{"id":178451,"depth":1038,"text":178452},{"id":178577,"depth":1038,"text":178578},{"id":178694,"depth":1041,"text":178695},{"id":178713,"depth":1041,"text":178714},{"id":178741,"depth":1041,"text":178742},{"id":178767,"depth":1041,"text":178768},{"id":178795,"depth":1041,"text":178796},"The pod-wide schema manifest — an encrypted index of every collection's schema generation, content hash and field ids, plus the open() read path.",{},{"title":735,"description":178826},"AhJSU67uPl9nUywd3CDfUe8YZdSsAl43JbyDXoc3L04",{"id":178831,"title":739,"access":12,"apiToc":929,"body":178832,"category":1042,"description":181318,"extension":1044,"meta":181319,"navigation":1042,"nest":1047,"path":740,"seo":181320,"sinceVersion":1042,"status":1049,"stem":741,"subtitle":1042,"tier":1050,"tocExternal":1042,"__hash__":181321},"docs\u002Fservices\u002Fmaterialized-views.md",{"type":931,"value":178833,"toc":181298},[178834,178836,178839,178841,178854,178864,178866,178885,178887,179288,179297,179299,179303,179318,179568,179572,179589,179920,179929,179933,179939,179990,180002,180004,180010,180029,180315,180319,180359,180364,180405,180408,180412,180426,180747,180781,180786,180789,180899,180902,180904,180911,180941,180943,181228,181230,181295],[934,178835,739],{"id":739},[46871,178837],{":rows":178838},"[{\"label\":\"Subpath\",\"value\":\"`@noy-db\u002Fhub\u002Fmaterialized-views` (also re-exported from `@noy-db\u002Fhub`)\"},{\"label\":\"Factory\",\"value\":\"`withMaterializedView()`\"},{\"label\":\"Layer\",\"value\":\"`with-formula`\"}]",[1022,178840,29857],{"id":29856},[937,178842,178843,178844,178846,178847,178849,178850,178853],{},"A materialized view takes a query — a filter, a ",[941,178845,27496],{}," + aggregate, a join, or a union of\nseveral source collections — and persists its result as a normal, queryable\n",[1007,178848,6471],{"href":48}," that is kept fresh as the underlying data changes. Instead of re-running an expensive aggregation on every\nread, you register the query once with ",[941,178851,178852],{},"withMaterializedView()"," and read the precomputed output\ncollection like any other.",[937,178855,178856,178857,6030,178859,178861,178862,1520],{},"Reach for it when a read is expensive to compute on the fly (a ",[941,178858,27496],{},[941,178860,28196],{}," over a large\ncollection, a filtered subset you query often) and you'd rather pay the cost on write. Each row\nwritten to the output travels through the vault's normal encrypt-then-store pipeline, so a\nmaterialized view is encrypted the same way as everything else in the vault — the storage backend\nnever sees which rows came from a query versus a direct ",[941,178863,3161],{},[1022,178865,70093],{"id":70092},[1111,178867,178868],{},[1114,178869,178870,178872,178873,178875,178876,178878,178879,178881,178882,178884],{},[950,178871,107493],{}," beyond the per-strategy config — but note two config rules that read like prerequisites: a per-arm ",[941,178874,63728],{},"'s right-side collection must be listed in ",[941,178877,138958],{}," (or registration throws), and ",[941,178880,22765],{}," is UNION-mode-only and requires ",[941,178883,4449],{}," in the same strategy.",[1022,178886,99864],{"id":99863},[2217,178888,178890],{"className":2219,"code":178889,"language":2221,"meta":1037,"style":1037},"import { createNoydb, withMaterializedView } from '@noy-db\u002Fhub'\nimport { sum, count } from '@noy-db\u002Fhub\u002Freduce'\n\ntype Compensation = { id: string; clientId: string; taxAmount: number }\n\nconst pnd1 = withMaterializedView\u003C{ clientId: string; tax: number; count: number }>({\n  name: 'pnd1',\n  query: (db) =>\n    db.collection\u003CCompensation>('compensations')\n      .query()\n      .groupBy('clientId')\n      .aggregate({ tax: sum('taxAmount'), count: count() }),\n  rowKey: (row) => row.clientId,\n  sources: ['compensations'],  \u002F\u002F required when query() returns an aggregate\n  refresh: 'eager',\n})\n\nconst db = await createNoydb({\n  store: \u002F* your NoydbStore *\u002F,\n  user: 'alice',\n  secret: 'a-strong-secret',\n  materializedViewStrategies: [pnd1],\n})\n",[941,178891,178892,178915,178937,178941,178977,178981,179023,179038,179053,179080,179088,179104,179146,179169,179190,179205,179211,179215,179231,179242,179256,179270,179282],{"__ignoreMap":1037},[2225,178893,178894,178896,178898,178900,178902,178905,178907,178909,178911,178913],{"class":2227,"line":2228},[2225,178895,2232],{"class":2231},[2225,178897,2236],{"class":2235},[2225,178899,2744],{"class":2239},[2225,178901,2298],{"class":2235},[2225,178903,178904],{"class":2239}," withMaterializedView",[2225,178906,2243],{"class":2235},[2225,178908,2246],{"class":2231},[2225,178910,2249],{"class":2235},[2225,178912,1086],{"class":2252},[2225,178914,2256],{"class":2235},[2225,178916,178917,178919,178921,178923,178925,178927,178929,178931,178933,178935],{"class":2227,"line":1041},[2225,178918,2232],{"class":2231},[2225,178920,2236],{"class":2235},[2225,178922,27298],{"class":2239},[2225,178924,2298],{"class":2235},[2225,178926,19270],{"class":2239},[2225,178928,2243],{"class":2235},[2225,178930,2246],{"class":2231},[2225,178932,2249],{"class":2235},[2225,178934,27257],{"class":2252},[2225,178936,2256],{"class":2235},[2225,178938,178939],{"class":2227,"line":1038},[2225,178940,2285],{"emptyLinePlaceholder":2284},[2225,178942,178943,178945,178948,178950,178952,178954,178956,178958,178960,178962,178964,178966,178968,178971,178973,178975],{"class":2227,"line":2288},[2225,178944,6339],{"class":2774},[2225,178946,178947],{"class":6342}," Compensation",[2225,178949,6346],{"class":2235},[2225,178951,2236],{"class":2235},[2225,178953,7070],{"class":2307},[2225,178955,2311],{"class":2235},[2225,178957,6366],{"class":6342},[2225,178959,3769],{"class":2235},[2225,178961,62649],{"class":2307},[2225,178963,2311],{"class":2235},[2225,178965,6366],{"class":6342},[2225,178967,3769],{"class":2235},[2225,178969,178970],{"class":2307}," taxAmount",[2225,178972,2311],{"class":2235},[2225,178974,6356],{"class":6342},[2225,178976,6369],{"class":2235},[2225,178978,178979],{"class":2227,"line":2304},[2225,178980,2285],{"emptyLinePlaceholder":2284},[2225,178982,178983,178985,178988,178990,178992,178994,178996,178998,179000,179002,179005,179007,179009,179011,179013,179015,179017,179019,179021],{"class":2227,"line":2356},[2225,178984,2775],{"class":2774},[2225,178986,178987],{"class":2239}," pnd1 ",[2225,178989,2781],{"class":2235},[2225,178991,178904],{"class":2291},[2225,178993,17851],{"class":2235},[2225,178995,62649],{"class":2307},[2225,178997,2311],{"class":2235},[2225,178999,6366],{"class":6342},[2225,179001,3769],{"class":2235},[2225,179003,179004],{"class":2307}," tax",[2225,179006,2311],{"class":2235},[2225,179008,6356],{"class":6342},[2225,179010,3769],{"class":2235},[2225,179012,19270],{"class":2307},[2225,179014,2311],{"class":2235},[2225,179016,6356],{"class":6342},[2225,179018,85293],{"class":2235},[2225,179020,2789],{"class":2239},[2225,179022,4200],{"class":2235},[2225,179024,179025,179027,179029,179031,179034,179036],{"class":2227,"line":2830},[2225,179026,7008],{"class":2307},[2225,179028,2311],{"class":2235},[2225,179030,2249],{"class":2235},[2225,179032,179033],{"class":2252},"pnd1",[2225,179035,2805],{"class":2235},[2225,179037,1707],{"class":2235},[2225,179039,179040,179043,179045,179047,179049,179051],{"class":2227,"line":2861},[2225,179041,179042],{"class":2291},"  query",[2225,179044,2311],{"class":2235},[2225,179046,953],{"class":2235},[2225,179048,6590],{"class":7052},[2225,179050,1381],{"class":2235},[2225,179052,71229],{"class":2774},[2225,179054,179055,179058,179060,179062,179064,179067,179069,179071,179073,179076,179078],{"class":2227,"line":4246},[2225,179056,179057],{"class":2239},"    db",[2225,179059,1520],{"class":2235},[2225,179061,6471],{"class":2291},[2225,179063,6474],{"class":2235},[2225,179065,179066],{"class":6342},"Compensation",[2225,179068,6480],{"class":2235},[2225,179070,2789],{"class":2239},[2225,179072,2805],{"class":2235},[2225,179074,179075],{"class":2252},"compensations",[2225,179077,2805],{"class":2235},[2225,179079,2362],{"class":2239},[2225,179081,179082,179084,179086],{"class":2227,"line":4253},[2225,179083,80721],{"class":2235},[2225,179085,5286],{"class":2291},[2225,179087,8146],{"class":2239},[2225,179089,179090,179092,179094,179096,179098,179100,179102],{"class":2227,"line":4259},[2225,179091,80721],{"class":2235},[2225,179093,27496],{"class":2291},[2225,179095,2789],{"class":2239},[2225,179097,2805],{"class":2235},[2225,179099,59721],{"class":2252},[2225,179101,2805],{"class":2235},[2225,179103,2362],{"class":2239},[2225,179105,179106,179108,179110,179112,179114,179116,179118,179120,179122,179124,179126,179128,179130,179132,179134,179136,179138,179140,179142,179144],{"class":2227,"line":6548},[2225,179107,80721],{"class":2235},[2225,179109,4449],{"class":2291},[2225,179111,2789],{"class":2239},[2225,179113,2792],{"class":2235},[2225,179115,179004],{"class":2307},[2225,179117,2311],{"class":2235},[2225,179119,27298],{"class":2291},[2225,179121,2789],{"class":2239},[2225,179123,2805],{"class":2235},[2225,179125,121640],{"class":2252},[2225,179127,2805],{"class":2235},[2225,179129,1381],{"class":2239},[2225,179131,2298],{"class":2235},[2225,179133,19270],{"class":2307},[2225,179135,2311],{"class":2235},[2225,179137,19270],{"class":2291},[2225,179139,19132],{"class":2239},[2225,179141,2359],{"class":2235},[2225,179143,1381],{"class":2239},[2225,179145,1707],{"class":2235},[2225,179147,179148,179151,179153,179155,179157,179159,179161,179163,179165,179167],{"class":2227,"line":6576},[2225,179149,179150],{"class":2291},"  rowKey",[2225,179152,2311],{"class":2235},[2225,179154,953],{"class":2235},[2225,179156,134393],{"class":7052},[2225,179158,1381],{"class":2235},[2225,179160,8116],{"class":2774},[2225,179162,134431],{"class":2239},[2225,179164,1520],{"class":2235},[2225,179166,59721],{"class":2239},[2225,179168,1707],{"class":2235},[2225,179170,179171,179173,179175,179177,179179,179181,179183,179185,179187],{"class":2227,"line":6582},[2225,179172,139020],{"class":2307},[2225,179174,2311],{"class":2235},[2225,179176,8667],{"class":2239},[2225,179178,2805],{"class":2235},[2225,179180,179075],{"class":2252},[2225,179182,2805],{"class":2235},[2225,179184,8803],{"class":2239},[2225,179186,2298],{"class":2235},[2225,179188,179189],{"class":2763},"  \u002F\u002F required when query() returns an aggregate\n",[2225,179191,179192,179195,179197,179199,179201,179203],{"class":2227,"line":6587},[2225,179193,179194],{"class":2307},"  refresh",[2225,179196,2311],{"class":2235},[2225,179198,2249],{"class":2235},[2225,179200,138257],{"class":2252},[2225,179202,2805],{"class":2235},[2225,179204,1707],{"class":2235},[2225,179206,179207,179209],{"class":2227,"line":7174},[2225,179208,2359],{"class":2235},[2225,179210,2362],{"class":2239},[2225,179212,179213],{"class":2227,"line":7180},[2225,179214,2285],{"emptyLinePlaceholder":2284},[2225,179216,179217,179219,179221,179223,179225,179227,179229],{"class":2227,"line":7220},[2225,179218,2775],{"class":2774},[2225,179220,2778],{"class":2239},[2225,179222,2781],{"class":2235},[2225,179224,2784],{"class":2231},[2225,179226,2744],{"class":2291},[2225,179228,2789],{"class":2239},[2225,179230,4200],{"class":2235},[2225,179232,179233,179235,179237,179240],{"class":2227,"line":7225},[2225,179234,15608],{"class":2307},[2225,179236,2311],{"class":2235},[2225,179238,179239],{"class":2763}," \u002F* your NoydbStore *\u002F",[2225,179241,1707],{"class":2235},[2225,179243,179244,179246,179248,179250,179252,179254],{"class":2227,"line":7231},[2225,179245,4205],{"class":2307},[2225,179247,2311],{"class":2235},[2225,179249,2249],{"class":2235},[2225,179251,6408],{"class":2252},[2225,179253,2805],{"class":2235},[2225,179255,1707],{"class":2235},[2225,179257,179258,179260,179262,179264,179266,179268],{"class":2227,"line":7267},[2225,179259,10423],{"class":2307},[2225,179261,2311],{"class":2235},[2225,179263,2249],{"class":2235},[2225,179265,6421],{"class":2252},[2225,179267,2805],{"class":2235},[2225,179269,1707],{"class":2235},[2225,179271,179272,179275,179277,179280],{"class":2227,"line":7272},[2225,179273,179274],{"class":2307},"  materializedViewStrategies",[2225,179276,2311],{"class":2235},[2225,179278,179279],{"class":2239}," [pnd1]",[2225,179281,1707],{"class":2235},[2225,179283,179284,179286],{"class":2227,"line":7278},[2225,179285,2359],{"class":2235},[2225,179287,2362],{"class":2239},[937,179289,179290,179293,179294,179296],{},[941,179291,179292],{},"materializedViewStrategies"," is the only way to register a materialized view — without it, no\n",[941,179295,178852],{}," strategy runs.",[1022,179298,29893],{"id":29892},[1591,179300,179302],{"id":179301},"registering-a-query-form-view-and-reading-its-output","Registering a query-form view and reading its output",[937,179304,960,179305,179307,179308,179310,179311,179314,179315,179317],{},[941,179306,5286],{}," callback runs at registration and again on every refresh. Its result — a plain\n",[941,179309,24785],{},", or a ",[941,179312,179313],{},"groupBy().aggregate()"," chain — is read out through the normal collection API,\nunder the output collection name (",[941,179316,4914],{}," by default):",[2217,179319,179321],{"className":2219,"code":179320,"language":2221,"meta":1037,"style":1037},"const vault = await db.openVault('demo')\n\nawait vault.collection\u003CCompensation>('compensations').put('c-1', {\n  id: 'c-1', clientId: 'acme', taxAmount: 120,\n})\nawait vault.collection\u003CCompensation>('compensations').put('c-2', {\n  id: 'c-2', clientId: 'acme', taxAmount: 80,\n})\n\nconst row = await vault.collection('pnd1').get('acme')\n\u002F\u002F → { clientId: 'acme', tax: 200, count: 2, _materializedFrom: { mvName: 'pnd1', ... } }\n",[941,179322,179323,179349,179353,179395,179429,179435,179478,179513,179519,179523,179563],{"__ignoreMap":1037},[2225,179324,179325,179327,179329,179331,179333,179335,179337,179339,179341,179343,179345,179347],{"class":2227,"line":2228},[2225,179326,2775],{"class":2774},[2225,179328,2835],{"class":2239},[2225,179330,2781],{"class":2235},[2225,179332,2784],{"class":2231},[2225,179334,2842],{"class":2239},[2225,179336,1520],{"class":2235},[2225,179338,2847],{"class":2291},[2225,179340,2789],{"class":2239},[2225,179342,2805],{"class":2235},[2225,179344,97914],{"class":2252},[2225,179346,2805],{"class":2235},[2225,179348,2362],{"class":2239},[2225,179350,179351],{"class":2227,"line":1041},[2225,179352,2285],{"emptyLinePlaceholder":2284},[2225,179354,179355,179357,179359,179361,179363,179365,179367,179369,179371,179373,179375,179377,179379,179381,179383,179385,179387,179389,179391,179393],{"class":2227,"line":1038},[2225,179356,6500],{"class":2231},[2225,179358,6466],{"class":2239},[2225,179360,1520],{"class":2235},[2225,179362,6471],{"class":2291},[2225,179364,6474],{"class":2235},[2225,179366,179066],{"class":6342},[2225,179368,6480],{"class":2235},[2225,179370,2789],{"class":2239},[2225,179372,2805],{"class":2235},[2225,179374,179075],{"class":2252},[2225,179376,2805],{"class":2235},[2225,179378,1381],{"class":2239},[2225,179380,1520],{"class":2235},[2225,179382,3161],{"class":2291},[2225,179384,2789],{"class":2239},[2225,179386,2805],{"class":2235},[2225,179388,9327],{"class":2252},[2225,179390,2805],{"class":2235},[2225,179392,2298],{"class":2235},[2225,179394,2301],{"class":2235},[2225,179396,179397,179399,179401,179403,179405,179407,179409,179411,179413,179415,179417,179419,179421,179423,179425,179427],{"class":2227,"line":2288},[2225,179398,28371],{"class":2307},[2225,179400,2311],{"class":2235},[2225,179402,2249],{"class":2235},[2225,179404,9327],{"class":2252},[2225,179406,2805],{"class":2235},[2225,179408,2298],{"class":2235},[2225,179410,62649],{"class":2307},[2225,179412,2311],{"class":2235},[2225,179414,2249],{"class":2235},[2225,179416,6450],{"class":2252},[2225,179418,2805],{"class":2235},[2225,179420,2298],{"class":2235},[2225,179422,178970],{"class":2307},[2225,179424,2311],{"class":2235},[2225,179426,28675],{"class":6527},[2225,179428,1707],{"class":2235},[2225,179430,179431,179433],{"class":2227,"line":2304},[2225,179432,2359],{"class":2235},[2225,179434,2362],{"class":2239},[2225,179436,179437,179439,179441,179443,179445,179447,179449,179451,179453,179455,179457,179459,179461,179463,179465,179467,179469,179472,179474,179476],{"class":2227,"line":2356},[2225,179438,6500],{"class":2231},[2225,179440,6466],{"class":2239},[2225,179442,1520],{"class":2235},[2225,179444,6471],{"class":2291},[2225,179446,6474],{"class":2235},[2225,179448,179066],{"class":6342},[2225,179450,6480],{"class":2235},[2225,179452,2789],{"class":2239},[2225,179454,2805],{"class":2235},[2225,179456,179075],{"class":2252},[2225,179458,2805],{"class":2235},[2225,179460,1381],{"class":2239},[2225,179462,1520],{"class":2235},[2225,179464,3161],{"class":2291},[2225,179466,2789],{"class":2239},[2225,179468,2805],{"class":2235},[2225,179470,179471],{"class":2252},"c-2",[2225,179473,2805],{"class":2235},[2225,179475,2298],{"class":2235},[2225,179477,2301],{"class":2235},[2225,179479,179480,179482,179484,179486,179488,179490,179492,179494,179496,179498,179500,179502,179504,179506,179508,179511],{"class":2227,"line":2830},[2225,179481,28371],{"class":2307},[2225,179483,2311],{"class":2235},[2225,179485,2249],{"class":2235},[2225,179487,179471],{"class":2252},[2225,179489,2805],{"class":2235},[2225,179491,2298],{"class":2235},[2225,179493,62649],{"class":2307},[2225,179495,2311],{"class":2235},[2225,179497,2249],{"class":2235},[2225,179499,6450],{"class":2252},[2225,179501,2805],{"class":2235},[2225,179503,2298],{"class":2235},[2225,179505,178970],{"class":2307},[2225,179507,2311],{"class":2235},[2225,179509,179510],{"class":6527}," 80",[2225,179512,1707],{"class":2235},[2225,179514,179515,179517],{"class":2227,"line":2861},[2225,179516,2359],{"class":2235},[2225,179518,2362],{"class":2239},[2225,179520,179521],{"class":2227,"line":4246},[2225,179522,2285],{"emptyLinePlaceholder":2284},[2225,179524,179525,179527,179529,179531,179533,179535,179537,179539,179541,179543,179545,179547,179549,179551,179553,179555,179557,179559,179561],{"class":2227,"line":4253},[2225,179526,2775],{"class":2774},[2225,179528,118164],{"class":2239},[2225,179530,2781],{"class":2235},[2225,179532,2784],{"class":2231},[2225,179534,6466],{"class":2239},[2225,179536,1520],{"class":2235},[2225,179538,6471],{"class":2291},[2225,179540,2789],{"class":2239},[2225,179542,2805],{"class":2235},[2225,179544,179033],{"class":2252},[2225,179546,2805],{"class":2235},[2225,179548,1381],{"class":2239},[2225,179550,1520],{"class":2235},[2225,179552,3158],{"class":2291},[2225,179554,2789],{"class":2239},[2225,179556,2805],{"class":2235},[2225,179558,6450],{"class":2252},[2225,179560,2805],{"class":2235},[2225,179562,2362],{"class":2239},[2225,179564,179565],{"class":2227,"line":4259},[2225,179566,179567],{"class":2763},"\u002F\u002F → { clientId: 'acme', tax: 200, count: 2, _materializedFrom: { mvName: 'pnd1', ... } }\n",[1591,179569,179571],{"id":179570},"union-form-combining-several-source-collections","UNION form — combining several source collections",[937,179573,179574,179577,179578,179580,179581,6030,179583,179585,179586,179588],{},[941,179575,179576],{},"unionSources"," reads rows from several sibling collections, projects each through a per-arm ",[941,179579,8244],{},"\ninto one unified row shape, concatenates the streams, then optionally ",[941,179582,27496],{},[941,179584,4449],{},"s the\nresult. It is mutually exclusive with ",[941,179587,5286],{}," — a strategy declares exactly one.",[2217,179590,179592],{"className":2219,"code":179591,"language":2221,"meta":1037,"style":1037},"import { withMaterializedView } from '@noy-db\u002Fhub'\nimport { sum } from '@noy-db\u002Fhub\u002Freduce'\n\nwithMaterializedView\u003C{ period: string; vat: number }>({\n  name: 'monthlyVat',\n  unionSources: [\n    { collection: 'taxReceipts', map: r => ({ period: r.issuedAt.slice(0, 7), vat:  r.vatAmount }) },\n    { collection: 'creditNotes', map: r => ({ period: r.issuedAt.slice(0, 7), vat: -r.vatAmount }) },\n  ],\n  groupBy: 'period',\n  aggregate: { vat: sum('vat') },\n  rowKey: row => row.period,\n  refresh: 'eager',\n})\n",[941,179593,179594,179612,179630,179634,179662,179677,179686,179760,179833,179839,179854,179882,179900,179914],{"__ignoreMap":1037},[2225,179595,179596,179598,179600,179602,179604,179606,179608,179610],{"class":2227,"line":2228},[2225,179597,2232],{"class":2231},[2225,179599,2236],{"class":2235},[2225,179601,178904],{"class":2239},[2225,179603,2243],{"class":2235},[2225,179605,2246],{"class":2231},[2225,179607,2249],{"class":2235},[2225,179609,1086],{"class":2252},[2225,179611,2256],{"class":2235},[2225,179613,179614,179616,179618,179620,179622,179624,179626,179628],{"class":2227,"line":1041},[2225,179615,2232],{"class":2231},[2225,179617,2236],{"class":2235},[2225,179619,27298],{"class":2239},[2225,179621,2243],{"class":2235},[2225,179623,2246],{"class":2231},[2225,179625,2249],{"class":2235},[2225,179627,27257],{"class":2252},[2225,179629,2256],{"class":2235},[2225,179631,179632],{"class":2227,"line":1038},[2225,179633,2285],{"emptyLinePlaceholder":2284},[2225,179635,179636,179639,179641,179643,179645,179647,179649,179652,179654,179656,179658,179660],{"class":2227,"line":2288},[2225,179637,179638],{"class":2291},"withMaterializedView",[2225,179640,17851],{"class":2235},[2225,179642,133638],{"class":2307},[2225,179644,2311],{"class":2235},[2225,179646,6366],{"class":6342},[2225,179648,3769],{"class":2235},[2225,179650,179651],{"class":2307}," vat",[2225,179653,2311],{"class":2235},[2225,179655,6356],{"class":6342},[2225,179657,85293],{"class":2235},[2225,179659,2789],{"class":2239},[2225,179661,4200],{"class":2235},[2225,179663,179664,179666,179668,179670,179673,179675],{"class":2227,"line":2304},[2225,179665,7008],{"class":2307},[2225,179667,2311],{"class":2235},[2225,179669,2249],{"class":2235},[2225,179671,179672],{"class":2252},"monthlyVat",[2225,179674,2805],{"class":2235},[2225,179676,1707],{"class":2235},[2225,179678,179679,179682,179684],{"class":2227,"line":2356},[2225,179680,179681],{"class":2307},"  unionSources",[2225,179683,2311],{"class":2235},[2225,179685,32491],{"class":2239},[2225,179687,179688,179690,179692,179694,179696,179699,179701,179703,179706,179708,179710,179712,179714,179716,179718,179720,179722,179724,179726,179728,179730,179732,179734,179736,179738,179740,179742,179744,179746,179749,179751,179754,179756,179758],{"class":2227,"line":2830},[2225,179689,32496],{"class":2235},[2225,179691,7061],{"class":2307},[2225,179693,2311],{"class":2235},[2225,179695,2249],{"class":2235},[2225,179697,179698],{"class":2252},"taxReceipts",[2225,179700,2805],{"class":2235},[2225,179702,2298],{"class":2235},[2225,179704,179705],{"class":2291}," map",[2225,179707,2311],{"class":2235},[2225,179709,32537],{"class":7052},[2225,179711,8116],{"class":2774},[2225,179713,953],{"class":2239},[2225,179715,2792],{"class":2235},[2225,179717,133638],{"class":2307},[2225,179719,2311],{"class":2235},[2225,179721,32537],{"class":2239},[2225,179723,1520],{"class":2235},[2225,179725,135826],{"class":2239},[2225,179727,1520],{"class":2235},[2225,179729,8733],{"class":2291},[2225,179731,2789],{"class":2239},[2225,179733,5402],{"class":6527},[2225,179735,2298],{"class":2235},[2225,179737,44442],{"class":6527},[2225,179739,1381],{"class":2239},[2225,179741,2298],{"class":2235},[2225,179743,179651],{"class":2307},[2225,179745,2311],{"class":2235},[2225,179747,179748],{"class":2239},"  r",[2225,179750,1520],{"class":2235},[2225,179752,179753],{"class":2239},"vatAmount ",[2225,179755,2359],{"class":2235},[2225,179757,8495],{"class":2239},[2225,179759,19008],{"class":2235},[2225,179761,179762,179764,179766,179768,179770,179773,179775,179777,179779,179781,179783,179785,179787,179789,179791,179793,179795,179797,179799,179801,179803,179805,179807,179809,179811,179813,179815,179817,179819,179821,179823,179825,179827,179829,179831],{"class":2227,"line":2861},[2225,179763,32496],{"class":2235},[2225,179765,7061],{"class":2307},[2225,179767,2311],{"class":2235},[2225,179769,2249],{"class":2235},[2225,179771,179772],{"class":2252},"creditNotes",[2225,179774,2805],{"class":2235},[2225,179776,2298],{"class":2235},[2225,179778,179705],{"class":2291},[2225,179780,2311],{"class":2235},[2225,179782,32537],{"class":7052},[2225,179784,8116],{"class":2774},[2225,179786,953],{"class":2239},[2225,179788,2792],{"class":2235},[2225,179790,133638],{"class":2307},[2225,179792,2311],{"class":2235},[2225,179794,32537],{"class":2239},[2225,179796,1520],{"class":2235},[2225,179798,135826],{"class":2239},[2225,179800,1520],{"class":2235},[2225,179802,8733],{"class":2291},[2225,179804,2789],{"class":2239},[2225,179806,5402],{"class":6527},[2225,179808,2298],{"class":2235},[2225,179810,44442],{"class":6527},[2225,179812,1381],{"class":2239},[2225,179814,2298],{"class":2235},[2225,179816,179651],{"class":2307},[2225,179818,2311],{"class":2235},[2225,179820,28754],{"class":2235},[2225,179822,13217],{"class":2239},[2225,179824,1520],{"class":2235},[2225,179826,179753],{"class":2239},[2225,179828,2359],{"class":2235},[2225,179830,8495],{"class":2239},[2225,179832,19008],{"class":2235},[2225,179834,179835,179837],{"class":2227,"line":4246},[2225,179836,32588],{"class":2239},[2225,179838,1707],{"class":2235},[2225,179840,179841,179844,179846,179848,179850,179852],{"class":2227,"line":4253},[2225,179842,179843],{"class":2307},"  groupBy",[2225,179845,2311],{"class":2235},[2225,179847,2249],{"class":2235},[2225,179849,133611],{"class":2252},[2225,179851,2805],{"class":2235},[2225,179853,1707],{"class":2235},[2225,179855,179856,179859,179861,179863,179865,179867,179869,179871,179873,179876,179878,179880],{"class":2227,"line":4259},[2225,179857,179858],{"class":2307},"  aggregate",[2225,179860,2311],{"class":2235},[2225,179862,2236],{"class":2235},[2225,179864,179651],{"class":2307},[2225,179866,2311],{"class":2235},[2225,179868,27298],{"class":2291},[2225,179870,2789],{"class":2239},[2225,179872,2805],{"class":2235},[2225,179874,179875],{"class":2252},"vat",[2225,179877,2805],{"class":2235},[2225,179879,8495],{"class":2239},[2225,179881,19008],{"class":2235},[2225,179883,179884,179886,179888,179890,179892,179894,179896,179898],{"class":2227,"line":6548},[2225,179885,179150],{"class":2291},[2225,179887,2311],{"class":2235},[2225,179889,134431],{"class":7052},[2225,179891,8116],{"class":2774},[2225,179893,134431],{"class":2239},[2225,179895,1520],{"class":2235},[2225,179897,133611],{"class":2239},[2225,179899,1707],{"class":2235},[2225,179901,179902,179904,179906,179908,179910,179912],{"class":2227,"line":6576},[2225,179903,179194],{"class":2307},[2225,179905,2311],{"class":2235},[2225,179907,2249],{"class":2235},[2225,179909,138257],{"class":2252},[2225,179911,2805],{"class":2235},[2225,179913,1707],{"class":2235},[2225,179915,179916,179918],{"class":2227,"line":6582},[2225,179917,2359],{"class":2235},[2225,179919,2362],{"class":2239},[937,179921,179922,179923,179925,179926,179928],{},"A single-arm union is also valid — it lets you map → group → aggregate over one collection with a\n",[1092,179924,637],{}," bucket key (e.g. a month sliced from a date field), which the query form's stored-field\n",[941,179927,27496],{}," can't express.",[1591,179930,179932],{"id":179931},"triggering-a-manual-refresh","Triggering a manual refresh",[937,179934,110954,179935,179938],{},[941,179936,179937],{},"refresh: 'manual'"," views, only an explicit call re-materializes:",[2217,179940,179942],{"className":2219,"code":179941,"language":2221,"meta":1037,"style":1037},"const { written, deleted, failed } = await vault.refreshView('pnd1')\n\u002F\u002F → { written: 12, deleted: 1, failed: 0 }\n",[941,179943,179944,179985],{"__ignoreMap":1037},[2225,179945,179946,179948,179950,179953,179955,179958,179960,179962,179964,179966,179968,179970,179972,179975,179977,179979,179981,179983],{"class":2227,"line":2228},[2225,179947,2775],{"class":2774},[2225,179949,2236],{"class":2235},[2225,179951,179952],{"class":2239}," written",[2225,179954,2298],{"class":2235},[2225,179956,179957],{"class":2239}," deleted",[2225,179959,2298],{"class":2235},[2225,179961,140690],{"class":2239},[2225,179963,2359],{"class":2235},[2225,179965,6346],{"class":2235},[2225,179967,2784],{"class":2231},[2225,179969,6466],{"class":2239},[2225,179971,1520],{"class":2235},[2225,179973,179974],{"class":2291},"refreshView",[2225,179976,2789],{"class":2239},[2225,179978,2805],{"class":2235},[2225,179980,179033],{"class":2252},[2225,179982,2805],{"class":2235},[2225,179984,2362],{"class":2239},[2225,179986,179987],{"class":2227,"line":1041},[2225,179988,179989],{"class":2763},"\u002F\u002F → { written: 12, deleted: 1, failed: 0 }\n",[937,179991,179992,179993,102958,179995,179997,179998,180001],{},"Throws a plain ",[941,179994,12126],{},[941,179996,4914],{}," isn't a registered view; it's a no-op\n(",[941,179999,180000],{},"{ written: 0, deleted: 0, failed: 0 }",") if the vault has no materialized-view strategies\nregistered at all.",[1022,180003,102889],{"id":102888},[1591,180005,180007],{"id":180006},"withmaterializedviewspec",[941,180008,180009],{},"withMaterializedView(spec)",[937,180011,180012,180013,180015,180016,4688,180018,180021,180022,180024,180025,180028],{},"Registers a materialized view. Throws ",[941,180014,12881],{}," for a missing ",[941,180017,4914],{},[941,180019,180020],{},"rowKey",", or an\ninvalid ",[941,180023,42973],{}," value; throws ",[941,180026,180027],{},"MaterializedViewConfigError"," for invalid UNION-form combinations\n(see Limits below).",[1338,180030,180031,180042],{},[1341,180032,180033],{},[1344,180034,180035,180037,180040],{},[1347,180036,101104],{},[1347,180038,180039],{"align":9536},"Required?",[1347,180041,20200],{},[1357,180043,180044,180055,180070,180089,180101,180113,180141,180154,180168,180194,180212,180229,180247,180263,180279,180296],{},[1344,180045,180046,180050,180052],{},[1362,180047,180048],{},[941,180049,4914],{},[1362,180051,14852],{"align":9536},[1362,180053,180054],{},"Stable identity. Default output collection name.",[1344,180056,180057,180062,180067],{},[1362,180058,180059],{},[941,180060,180061],{},"query: (db) => Query\u003CTRow>",[1362,180063,180064,180065,1381],{"align":9536},"yes (unless ",[941,180066,179576],{},[1362,180068,180069],{},"Materialized query, run at registration and on every refresh.",[1344,180071,180072,180077,180081],{},[1362,180073,180074],{},[941,180075,180076],{},"unionSources: UnionSource[]",[1362,180078,180064,180079,1381],{"align":9536},[941,180080,5286],{},[1362,180082,180083,180084,180086,180087,1520],{},"Sibling collections to read + a per-arm ",[941,180085,8244],{}," into the unified row shape. Mutually exclusive with ",[941,180088,5286],{},[1344,180090,180091,180096,180098],{},[1362,180092,180093],{},[941,180094,180095],{},"rowKey: (row) => string",[1362,180097,14852],{"align":9536},[1362,180099,180100],{},"Stable id derivation. Explicit — there is no default.",[1344,180102,180103,180108,180110],{},[1362,180104,180105],{},[941,180106,180107],{},"refresh: 'eager' | 'lazy' | 'manual'",[1362,180109,14852],{"align":9536},[1362,180111,180112],{},"See refresh modes below.",[1344,180114,180115,180120,180126],{},[1362,180116,180117],{},[941,180118,180119],{},"sources?: string[]",[1362,180121,180122,180123,180125],{"align":9536},"only when ",[941,180124,24715],{}," returns an aggregate\u002FgroupBy",[1362,180127,180128,180129,180131,180132,180135,180136,180138,180139,1520],{},"Explicit dependencies; a plain ",[941,180130,24785],{}," is auto-analyzed. Not needed for the union's own arm collections (those come from ",[941,180133,180134],{},"unionSources[].collection","), but any collection listed here is added to the dependency set — required for a per-arm ",[941,180137,63728],{},"'s right-side collection, or registration throws ",[941,180140,180027],{},[1344,180142,180143,180148,180151],{},[1362,180144,180145],{},[941,180146,180147],{},"groupBy?: string | string[]",[1362,180149,180150],{"align":9536},"UNION mode only",[1362,180152,180153],{},"Group-key field(s) applied to the concatenated mapped-row stream.",[1344,180155,180156,180161,180163],{},[1362,180157,180158],{},[941,180159,180160],{},"aggregate?: ReduceSpec",[1362,180162,180150],{"align":9536},[1362,180164,180165,180166,1520],{},"Reduction applied per group after ",[941,180167,27496],{},[1344,180169,180170,180175,180180],{},[1362,180171,180172],{},[941,180173,180174],{},"moneyFields?: Record\u003Cstring, MoneyDescriptor>",[1362,180176,180177,180178],{"align":9536},"UNION mode only, requires ",[941,180179,4449],{},[1362,180181,180182,180183,1625,180185,1625,180187,180189,180190,180193],{},"Makes ",[941,180184,28196],{},[941,180186,28204],{},[941,180188,28207],{}," over the named fields exact ",[941,180191,180192],{},"BigInt"," arithmetic instead of float.",[1344,180195,180196,180201,180206],{},[1362,180197,180198],{},[941,180199,180200],{},"i18nLocale?: string",[1362,180202,180203,180204],{"align":9536},"Works in query-form and UNION mode; requires ",[941,180205,22756],{},[1362,180207,180208,180209,180211],{},"Locale used to resolve ",[941,180210,20943],{}," group-key fields to stable strings before bucketing.",[1344,180213,180214,180219,180222],{},[1362,180215,180216],{},[941,180217,180218],{},"i18nFields?: Record\u003Cstring, I18nTextDescriptor>",[1362,180220,180221],{"align":9536},"Works in query-form and UNION mode",[1362,180223,180224,180225,180228],{},"i18n descriptors for the fields named in ",[941,180226,180227],{},"i18nLocale"," resolution.",[1344,180230,180231,180236,180238],{},[1362,180232,180233],{},[941,180234,180235],{},"predicates?: { [name]: { hash, fn } }",[1362,180237,11313],{"align":9536},[1362,180239,180240,180241,9750,180243,180246],{},"Declared deterministic predicates callable from ",[941,180242,24715],{},[941,180244,180245],{},".wherePredicate(name, ctx?)",". Not supported on UNION strategies.",[1344,180248,180249,180254,180256],{},[1362,180250,180251],{},[941,180252,180253],{},"output?: { collection?, partition? }",[1362,180255,11313],{"align":9536},[1362,180257,180258,180259,180262],{},"Custom output collection name; ",[941,180260,180261],{},"partition"," is the same-collection cycle-break discriminator.",[1344,180264,180265,180270,180276],{},[1362,180266,180267],{},[941,180268,180269],{},"onEmpty?: 'delete' | 'keep'",[1362,180271,180272,180273],{"align":9536},"no, default ",[941,180274,180275],{},"'delete'",[1362,180277,180278],{},"What to do when a refresh yields zero rows for a previously-emitted key.",[1344,180280,180281,180286,180290],{},[1362,180282,180283],{},[941,180284,180285],{},"strict?: boolean",[1362,180287,180272,180288],{"align":9536},[941,180289,2560],{},[1362,180291,180292,180293,180295],{},"Re-throw row-write failures so a wrapping ",[941,180294,140657],{}," can roll back the source write.",[1344,180297,180298,180303,180308],{},[1362,180299,180300],{},[941,180301,180302],{},"maxRows?: number",[1362,180304,180272,180305],{"align":9536},[941,180306,180307],{},"100_000",[1362,180309,180310,180311,180314],{},"Row-count ceiling; throws ",[941,180312,180313],{},"MaterializedViewTooLargeError"," before any writes when exceeded.",[1591,180316,180317],{"id":42973},[941,180318,42973],{},[1111,180320,180321,180335,180347],{},[1114,180322,180323,180327,180328,6030,180332,180334],{},[950,180324,180325],{},[941,180326,138257],{}," — every write or delete on a source collection re-runs the query inside the same\ntransaction, for every view that depends on it. Outputs land synchronously. Pairs with\n",[1007,180329,180330],{"href":140654},[941,180331,140657],{},[941,180333,139953],{}," for atomic source-and-view updates.",[1114,180336,180337,180341,180342,4688,180344,180346],{},[950,180338,180339],{},[941,180340,719],{}," — source writes and deletes mark the view stale (in memory, not persisted across a\nvault close). The next read (",[941,180343,138938],{},[941,180345,138941],{},") on the view's output collection triggers\nthe re-materialize before returning. Good for expensive views with read-light workloads.",[1114,180348,180349,180354,180355,180358],{},[950,180350,180351],{},[941,180352,180353],{},"manual"," — only ",[941,180356,180357],{},"vault.refreshView(name)"," triggers re-materialization. Useful for\ntime-dependent queries whose predicate context changes externally, or very expensive views that\nopt out of the source-write hook entirely.",[1591,180360,180362],{"id":180361},"vaultrefreshviewname",[941,180363,180357],{},[2217,180365,180367],{"className":2219,"code":180366,"language":2221,"meta":1037,"style":1037},"refreshView(name: string): Promise\u003C{ written: number; deleted: number; failed: number }>\n",[941,180368,180369],{"__ignoreMap":1037},[2225,180370,180371,180373,180376,180378,180380,180382,180384,180386,180388,180390,180392,180394,180396,180399,180401,180403],{"class":2227,"line":2228},[2225,180372,179974],{"class":2291},[2225,180374,180375],{"class":2239},"(name: string): ",[2225,180377,11740],{"class":6342},[2225,180379,17851],{"class":2235},[2225,180381,179952],{"class":6342},[2225,180383,2311],{"class":2235},[2225,180385,6356],{"class":2239},[2225,180387,3769],{"class":2235},[2225,180389,179957],{"class":6342},[2225,180391,2311],{"class":2235},[2225,180393,6356],{"class":2239},[2225,180395,3769],{"class":2235},[2225,180397,180398],{"class":6342}," failed",[2225,180400,2311],{"class":2235},[2225,180402,6356],{"class":2239},[2225,180404,17861],{"class":2235},[937,180406,180407],{},"Runs the executor for the named view and returns the write\u002Ftombstone\u002Ffailure counts.",[1591,180409,180411],{"id":180410},"declared-deterministic-predicates","Declared deterministic predicates",[937,180413,180414,180415,180417,180418,180420,180421,180423,180424,2311],{},"Some filters can't be expressed as ",[941,180416,26648],{}," — date arithmetic, multi-field\nconditions, references to external time. ",[941,180419,134333],{}," declares named functions the ",[941,180422,24715],{},"\ncallback can invoke via ",[941,180425,180245],{},[2217,180427,180429],{"className":2219,"code":180428,"language":2221,"meta":1037,"style":1037},"import { withMaterializedView } from '@noy-db\u002Fhub'\n\ntype Invoice = { id: string; status: string; dueDate: string }\n\nwithMaterializedView\u003CInvoice>({\n  name: 'overdue',\n  predicates: {\n    isOverdue: {\n      hash: 'is-overdue-v1',\n      fn: (inv, ctx) => inv.status === 'open' && inv.dueDate \u003C (ctx as { asOf: string }).asOf,\n    },\n  },\n  query: (db) => db.collection\u003CInvoice>('invoices').query()\n    .wherePredicate('isOverdue', { asOf: '2026-05-20' }),\n  rowKey: (r) => r.id,\n  refresh: 'eager',\n})\n",[941,180430,180431,180449,180453,180488,180492,180506,180520,180529,180538,180554,180621,180625,180629,180669,180706,180728,180742],{"__ignoreMap":1037},[2225,180432,180433,180435,180437,180439,180441,180443,180445,180447],{"class":2227,"line":2228},[2225,180434,2232],{"class":2231},[2225,180436,2236],{"class":2235},[2225,180438,178904],{"class":2239},[2225,180440,2243],{"class":2235},[2225,180442,2246],{"class":2231},[2225,180444,2249],{"class":2235},[2225,180446,1086],{"class":2252},[2225,180448,2256],{"class":2235},[2225,180450,180451],{"class":2227,"line":1041},[2225,180452,2285],{"emptyLinePlaceholder":2284},[2225,180454,180455,180457,180459,180461,180463,180465,180467,180469,180471,180473,180475,180477,180479,180482,180484,180486],{"class":2227,"line":1038},[2225,180456,6339],{"class":2774},[2225,180458,6343],{"class":6342},[2225,180460,6346],{"class":2235},[2225,180462,2236],{"class":2235},[2225,180464,7070],{"class":2307},[2225,180466,2311],{"class":2235},[2225,180468,6366],{"class":6342},[2225,180470,3769],{"class":2235},[2225,180472,6361],{"class":2307},[2225,180474,2311],{"class":2235},[2225,180476,6366],{"class":6342},[2225,180478,3769],{"class":2235},[2225,180480,180481],{"class":2307}," dueDate",[2225,180483,2311],{"class":2235},[2225,180485,6366],{"class":6342},[2225,180487,6369],{"class":2235},[2225,180489,180490],{"class":2227,"line":2288},[2225,180491,2285],{"emptyLinePlaceholder":2284},[2225,180493,180494,180496,180498,180500,180502,180504],{"class":2227,"line":2304},[2225,180495,179638],{"class":6342},[2225,180497,6474],{"class":2235},[2225,180499,6477],{"class":6342},[2225,180501,6480],{"class":2235},[2225,180503,2789],{"class":2239},[2225,180505,4200],{"class":2235},[2225,180507,180508,180510,180512,180514,180516,180518],{"class":2227,"line":2356},[2225,180509,7008],{"class":2307},[2225,180511,2311],{"class":2235},[2225,180513,2249],{"class":2235},[2225,180515,28454],{"class":2252},[2225,180517,2805],{"class":2235},[2225,180519,1707],{"class":2235},[2225,180521,180522,180525,180527],{"class":2227,"line":2830},[2225,180523,180524],{"class":2307},"  predicates",[2225,180526,2311],{"class":2235},[2225,180528,2301],{"class":2235},[2225,180530,180531,180534,180536],{"class":2227,"line":2861},[2225,180532,180533],{"class":2307},"    isOverdue",[2225,180535,2311],{"class":2235},[2225,180537,2301],{"class":2235},[2225,180539,180540,180543,180545,180547,180550,180552],{"class":2227,"line":4246},[2225,180541,180542],{"class":2307},"      hash",[2225,180544,2311],{"class":2235},[2225,180546,2249],{"class":2235},[2225,180548,180549],{"class":2252},"is-overdue-v1",[2225,180551,2805],{"class":2235},[2225,180553,1707],{"class":2235},[2225,180555,180556,180559,180561,180563,180565,180567,180569,180571,180573,180575,180577,180579,180582,180584,180586,180588,180591,180593,180595,180597,180600,180602,180604,180607,180609,180611,180613,180615,180617,180619],{"class":2227,"line":4253},[2225,180557,180558],{"class":2307},"      fn",[2225,180560,2311],{"class":2235},[2225,180562,953],{"class":2235},[2225,180564,26995],{"class":7052},[2225,180566,2298],{"class":2235},[2225,180568,138692],{"class":7052},[2225,180570,1381],{"class":2235},[2225,180572,8116],{"class":2774},[2225,180574,27000],{"class":6342},[2225,180576,1520],{"class":2235},[2225,180578,6631],{"class":6342},[2225,180580,180581],{"class":2239}," === ",[2225,180583,2805],{"class":2235},[2225,180585,26178],{"class":2252},[2225,180587,2805],{"class":2235},[2225,180589,180590],{"class":2239}," && ",[2225,180592,26995],{"class":2307},[2225,180594,1520],{"class":2239},[2225,180596,39411],{"class":2307},[2225,180598,180599],{"class":2239}," \u003C (",[2225,180601,157376],{"class":2307},[2225,180603,2266],{"class":2307},[2225,180605,180606],{"class":2239}," { ",[2225,180608,67909],{"class":2307},[2225,180610,2311],{"class":2235},[2225,180612,6366],{"class":6342},[2225,180614,2243],{"class":2235},[2225,180616,957],{"class":2239},[2225,180618,67909],{"class":2307},[2225,180620,1707],{"class":2235},[2225,180622,180623],{"class":2227,"line":4259},[2225,180624,8324],{"class":2235},[2225,180626,180627],{"class":2227,"line":6548},[2225,180628,19017],{"class":2235},[2225,180630,180631,180633,180635,180637,180639,180641,180643,180645,180647,180649,180651,180653,180655,180657,180659,180661,180663,180665,180667],{"class":2227,"line":6576},[2225,180632,179042],{"class":2291},[2225,180634,2311],{"class":2235},[2225,180636,953],{"class":2235},[2225,180638,6590],{"class":7052},[2225,180640,1381],{"class":2235},[2225,180642,8116],{"class":2774},[2225,180644,2842],{"class":6342},[2225,180646,1520],{"class":2235},[2225,180648,6471],{"class":6342},[2225,180650,6474],{"class":2235},[2225,180652,6477],{"class":6342},[2225,180654,6480],{"class":2235},[2225,180656,2789],{"class":2239},[2225,180658,2805],{"class":2235},[2225,180660,6487],{"class":2252},[2225,180662,2805],{"class":2235},[2225,180664,957],{"class":2239},[2225,180666,5286],{"class":6342},[2225,180668,8146],{"class":2235},[2225,180670,180671,180673,180676,180678,180680,180683,180685,180687,180689,180691,180693,180695,180698,180700,180702,180704],{"class":2227,"line":6582},[2225,180672,134540],{"class":2239},[2225,180674,180675],{"class":6342},"wherePredicate",[2225,180677,2789],{"class":2239},[2225,180679,2805],{"class":2235},[2225,180681,180682],{"class":2252},"isOverdue",[2225,180684,2805],{"class":2235},[2225,180686,2298],{"class":2235},[2225,180688,2236],{"class":2235},[2225,180690,160126],{"class":2307},[2225,180692,2311],{"class":2235},[2225,180694,2249],{"class":2235},[2225,180696,180697],{"class":2252},"2026-05-20",[2225,180699,2805],{"class":2235},[2225,180701,2243],{"class":2235},[2225,180703,1381],{"class":2239},[2225,180705,1707],{"class":2235},[2225,180707,180708,180710,180712,180714,180716,180718,180720,180722,180724,180726],{"class":2227,"line":6587},[2225,180709,179150],{"class":2291},[2225,180711,2311],{"class":2235},[2225,180713,953],{"class":2235},[2225,180715,13217],{"class":7052},[2225,180717,1381],{"class":2235},[2225,180719,8116],{"class":2774},[2225,180721,32537],{"class":6342},[2225,180723,1520],{"class":2235},[2225,180725,6983],{"class":6342},[2225,180727,1707],{"class":2235},[2225,180729,180730,180732,180734,180736,180738,180740],{"class":2227,"line":7174},[2225,180731,179194],{"class":7052},[2225,180733,2311],{"class":2235},[2225,180735,2249],{"class":2235},[2225,180737,138257],{"class":2252},[2225,180739,2805],{"class":2235},[2225,180741,1707],{"class":2235},[2225,180743,180744],{"class":2227,"line":7180},[2225,180745,180746],{"class":2239},"})\n",[937,180748,180749,180750,180752,180753,180755,180756,180758,180759,180761,180762,180764,180765,180767,180768,180770,180771,180774,180775,180777,180778],{},"The predicate's ",[941,180751,134337],{}," plus a canonical-JSON hash of the ",[941,180754,157376],{}," argument both fold into the view's\n",[941,180757,134324],{}," (stamped into ",[941,180760,153619],{},", below). Bumping ",[941,180763,134337],{}," — because the function's\nmeaning changed — or changing ",[941,180766,157376],{}," forces a refresh on the next visit. Consumer responsibility:\nbump ",[941,180769,134337],{}," whenever the predicate's semantics change; failing to do so after a non-equivalent\nchange leaves stale rows around until the next explicit refresh. Calling ",[941,180772,180773],{},".wherePredicate()"," on a\n",[941,180776,24785],{}," that wasn't produced by an MV declaring that predicate throws\n",[941,180779,180780],{},".wherePredicate(\"name\"): no predicates registered on this Query.",[1591,180782,180784],{"id":180783},"_materializedfrom",[941,180785,153619],{},[937,180787,180788],{},"Every materialized row carries:",[2217,180790,180792],{"className":6604,"code":180791,"language":141,"meta":1037,"style":1037},"{\n  \"_materializedFrom\": {\n    \"mvName\": \"pnd1\",\n    \"queryHash\": \"9f2a1c7e4b0d3f8a1e6c5b2d7f4a9e0c1b3d6f8a2e5c7b0d4f1a9e3c6b8d0f2a\",\n    \"sourceVersions\": { \"compensations\": 7 },\n    \"materializedAt\": \"2026-05-21T00:00:00.000Z\"\n  }\n}\n",[941,180793,180794,180798,180810,180829,180848,180873,180891,180895],{"__ignoreMap":1037},[2225,180795,180796],{"class":2227,"line":2228},[2225,180797,4200],{"class":2235},[2225,180799,180800,180802,180804,180806,180808],{"class":2227,"line":1041},[2225,180801,9368],{"class":2235},[2225,180803,153619],{"class":2774},[2225,180805,6620],{"class":2235},[2225,180807,2311],{"class":2235},[2225,180809,2301],{"class":2235},[2225,180811,180812,180814,180817,180819,180821,180823,180825,180827],{"class":2227,"line":1038},[2225,180813,14201],{"class":2235},[2225,180815,180816],{"class":6342},"mvName",[2225,180818,6620],{"class":2235},[2225,180820,2311],{"class":2235},[2225,180822,6614],{"class":2235},[2225,180824,179033],{"class":2252},[2225,180826,6620],{"class":2235},[2225,180828,1707],{"class":2235},[2225,180830,180831,180833,180835,180837,180839,180841,180844,180846],{"class":2227,"line":2288},[2225,180832,14201],{"class":2235},[2225,180834,134324],{"class":6342},[2225,180836,6620],{"class":2235},[2225,180838,2311],{"class":2235},[2225,180840,6614],{"class":2235},[2225,180842,180843],{"class":2252},"9f2a1c7e4b0d3f8a1e6c5b2d7f4a9e0c1b3d6f8a2e5c7b0d4f1a9e3c6b8d0f2a",[2225,180845,6620],{"class":2235},[2225,180847,1707],{"class":2235},[2225,180849,180850,180852,180855,180857,180859,180861,180863,180865,180867,180869,180871],{"class":2227,"line":2304},[2225,180851,14201],{"class":2235},[2225,180853,180854],{"class":6342},"sourceVersions",[2225,180856,6620],{"class":2235},[2225,180858,2311],{"class":2235},[2225,180860,2236],{"class":2235},[2225,180862,6614],{"class":2235},[2225,180864,179075],{"class":6527},[2225,180866,6620],{"class":2235},[2225,180868,2311],{"class":2235},[2225,180870,44442],{"class":6527},[2225,180872,2353],{"class":2235},[2225,180874,180875,180877,180880,180882,180884,180886,180889],{"class":2227,"line":2356},[2225,180876,14201],{"class":2235},[2225,180878,180879],{"class":6342},"materializedAt",[2225,180881,6620],{"class":2235},[2225,180883,2311],{"class":2235},[2225,180885,6614],{"class":2235},[2225,180887,180888],{"class":2252},"2026-05-21T00:00:00.000Z",[2225,180890,9449],{"class":2235},[2225,180892,180893],{"class":2227,"line":2830},[2225,180894,7632],{"class":2235},[2225,180896,180897],{"class":2227,"line":2861},[2225,180898,7351],{"class":2235},[937,180900,180901],{},"Lives inside the encrypted payload, not the unencrypted envelope — the storage backend cannot\ninfer the view's dependency graph from listing records.",[1591,180903,120265],{"id":120264},[937,180905,140885,180906,180908,180909,2311],{},[941,180907,140888],{},", importable from ",[941,180910,73064],{},[1111,180912,180913,180919,180927,180935],{},[1114,180914,180915,180918],{},[941,180916,180917],{},"MaterializedViewCycleError(path[])"," — the MV graph contains a cycle",[1114,180920,180921,964,180924,180926],{},[941,180922,180923],{},"MaterializedViewSourceUnknownError(mv, source)",[941,180925,138958],{}," names a collection the vault doesn't know",[1114,180928,180929,964,180932,180934],{},[941,180930,180931],{},"MaterializedViewTooLargeError(mv, rowCount, limit)",[941,180933,134878],{}," exceeded",[1114,180936,180937,180940],{},[941,180938,180939],{},"MaterializedViewConfigError(detail)"," — invalid strategy shape (see Limits below)",[1022,180942,68210],{"id":68209},[1111,180944,180945,180958,180984,181083,181100,181119,181133,181159,181179,181205,181217],{},[1114,180946,180947,180954,180955,180957],{},[950,180948,180949,997,180951,180953],{},[941,180950,5286],{},[941,180952,179576],{}," are mutually exclusive."," A strategy must declare exactly one;\nregistration throws ",[941,180956,180027],{}," if both or neither are set.",[1114,180959,180960,180965,180966,65191,180968,1324,180971,56432,180973,180976,180977,180979,180980,180983],{},[950,180961,180962,180964],{},[941,180963,180020],{}," has no default."," It's required on every strategy, query-form or UNION. Since\n",[941,180967,149091],{},[1092,180969,180970],{},"absent",[941,180972,180020],{},[1092,180974,180975],{},"wrong-typed"," one are distinct errors: passing a field\n",[950,180978,4914],{}," — the shape every neighbouring option takes — used to report ",[941,180981,180982],{},"rowKey is required",", which\nnames absence and sends you looking for a missing line. It now says it must be a function, names\nthe type it received, and shows the fix.",[1114,180985,180986,180991,180992,180994,180995,180998,180999,181001,181002,181004,181005,181007,181010,181011,181054,181056,181057,181059,181060,181063,181064,181066,181067,181073,181074,181076,181077,181079,181080,181082],{},[950,180987,3289,180988,180990],{},[941,180989,27496],{}," on a VIRTUAL computed field is refused at registration"," — but the coverage is\npartial, and the boundary is worth knowing (noy-db#1269). A virtual field is evaluated on read\nand never stored, so the MV pipeline read the stored row, found nothing, and bucketed every row\nunder an ",[941,180993,18316],{}," key: ",[950,180996,180997],{},"a well-formed aggregate carrying a wrong number",", on the one path\nwhere nobody re-checks the arithmetic. ",[941,181000,111474],{}," already refused the same field with\n",[941,181003,111478],{},", so the two halves of what reads as one query layer disagreed.",[149168,181006],{},[950,181008,181009],{},"What decides coverage is where the grouping is expressed"," — not which source form you use:",[1338,181012,181013,181023],{},[1341,181014,181015],{},[1344,181016,181017,181020],{},[1347,181018,181019],{},"how the view groups",[1347,181021,181022],{},"refused?",[1357,181024,181025,181038],{},[1344,181026,181027,181034],{},[1362,181028,181029,181030,181033],{},"the declarative top-level ",[941,181031,181032],{},"groupBy: 'field'"," (or an array), as the UNION form takes",[1362,181035,181036],{},[950,181037,14852],{},[1344,181039,181040,181049],{},[1362,181041,181042,53129,181045,181048],{},[941,181043,181044],{},".groupBy('field')",[941,181046,181047],{},"query:"," callback",[1362,181050,181051,181053],{},[950,181052,11313],{}," — silent",[149168,181055],{},"The guard runs when the ",[950,181058,22723],{}," collection is registered, walks the views already recorded\nagainst it, and reads ",[941,181061,181062],{},"spec.groupBy"," and nothing else. A ",[941,181065,21934],{}," call inside a query\ncallback lives in the plan the callback returns and never populates that field, so there is\nnothing for the guard to compare — ",[950,181068,181069,181070,181072],{},"declaring ",[941,181071,138958],{}," does not change this."," The check also\nonly sees views already registered against that source, the same ordering the neighbouring\ntiers+crdt guard has.",[149168,181075],{},"So the residue is real and worth knowing: use ",[941,181078,124059],{}," for anything you group by,\nand prefer the declarative ",[941,181081,27496],{}," when you want the guard to be able to see it.",[1114,181084,181085,181090,181091,181093,181094,181096,181097,181099],{},[950,181086,181087,181089],{},[941,181088,138958],{}," is required for aggregates."," The dependency analyzer can extract source collections\nfrom a plain ",[941,181092,24785],{}," automatically, but not through a ",[941,181095,179313],{}," chain — declare\n",[941,181098,138958],{}," explicitly or the analyzer won't know what to watch.",[1114,181101,181102,181107,181108,181110,181111,181113,181114,181116,181117,1520],{},[950,181103,181104,181105],{},"UNION mode ignores ",[941,181106,138958],{}," for its own arms (dependencies come from\n",[941,181109,180134],{},"), but a per-arm ",[941,181112,63728],{}," resolves a right-side collection the union\ndependency set doesn't include — that collection must still be listed in ",[941,181115,138958],{}," or\nregistration throws ",[941,181118,180027],{},[1114,181120,181121,181126,181127,181129,181130,181132],{},[950,181122,181123,181125],{},[941,181124,134333],{}," is not supported on UNION strategies"," — UNION mode doesn't build a ",[941,181128,24785],{},"\nchain, so ",[941,181131,180773],{}," has nothing to call into.",[1114,181134,181135,181140,181141,181143,181144,181146,181147,181149,181150,50371,181152,181154,181155,6789,181157,1520],{},[950,181136,181137,181139],{},[941,181138,22765],{}," is UNION-only"," and requires ",[941,181142,4449],{}," — the query form has no ",[941,181145,4449],{},"\nfield of its own (its ",[941,181148,179313],{}," chain runs inside ",[941,181151,24715],{},[941,181153,22765],{},"\nwithout ",[941,181156,4449],{},[941,181158,180027],{},[1114,181160,181161,181168,181169,181171,181172,181174,181175,6789,181177,1520],{},[950,181162,181163,1625,181165,181167],{},[941,181164,180227],{},[941,181166,22756],{}," work on both query-form and UNION views"," — either resolves\n",[941,181170,20943],{}," group-key fields to a stable string before bucketing. ",[941,181173,180227],{}," without\n",[941,181176,22756],{},[941,181178,180027],{},[1114,181180,181181,181184,181185,181188,181189,181191,181192,181195,181196,181198,181199,6789,181202,1520],{},[950,181182,181183],{},"Same-collection views need a partition filter."," A view whose output collection equals one of\nits sources is only allowed when ",[941,181186,181187],{},"output.partition"," provably excludes the source filter (",[941,181190,21901],{},"\nagainst a different value, ",[941,181193,181194],{},"!="," against the value, or an ",[941,181197,4891],{}," list that doesn't contain it).\nOtherwise it's a cycle and ",[941,181200,181201],{},"vault.openVault",[941,181203,181204],{},"MaterializedViewCycleError",[1114,181206,181207,181216],{},[950,181208,181209,181212,181213,181215],{},[941,181210,181211],{},"onEmpty: 'delete'"," bypasses ",[941,181214,137880],{}," guards."," Tombstoning a row that dropped out of the\nresult set goes through the same system-internal delete path as derivations, so a guard you\nregistered for human writes doesn't block housekeeping.",[1114,181218,181219,181222,181223,181225,181226,1520],{},[950,181220,181221],{},"Lazy staleness is in-memory only."," A vault close-and-reopen loses pending stale bits; rows\nwith a matching ",[941,181224,134324],{}," are treated as fresh until the next source write or explicit\n",[941,181227,179974],{},[1022,181229,2621],{"id":2620},[1111,181231,181232,181237,181242,181252,181262,181269,181275],{},[1114,181233,181234,181236],{},[1007,181235,665],{"href":124621}," — the row-level sibling: projects one source record into typed\noutputs, instead of materializing a whole query",[1114,181238,181239,181241],{},[1007,181240,768],{"href":137874}," — lets an operator override specific rows of a materialized\nview's output without losing the underlying computation",[1114,181243,181244,1169,181246,181248,181249,181251],{},[1007,181245,4449],{"href":123485},[941,181247,27496],{}," + reducer machinery a query-form view's ",[941,181250,5286],{},"\nbuilds on",[1114,181253,181254,1169,181256,181258,181259,181261],{},[1007,181255,715],{"href":132487},[941,181257,62282],{},"-driven ",[941,181260,61808],{}," that per-arm UNION joins reuse",[1114,181263,181264,141142,181266,181268],{},[1007,181265,876],{"href":140654},[941,181267,139953],{}," rollback",[1114,181270,181271,1368,181273],{},[1007,181272,61743],{"href":598},[1007,181274,1019],{"href":1018},[1114,181276,141161,181277,968,181283,2462,181289,1520],{},[1007,181278,181280],{"href":93551,"rel":181279},[3371],[941,181281,181282],{},"81-with-mv-eager",[1007,181284,181286],{"href":93568,"rel":181285},[3371],[941,181287,181288],{},"82-with-mv-lazy",[1007,181290,181292],{"href":93636,"rel":181291},[3371],[941,181293,181294],{},"86-with-union-mv",[2645,181296,181297],{},"html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .sHdIc, html code.shiki .sHdIc{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#EEFFFF;--shiki-default-font-style:italic;--shiki-dark:#BABED8;--shiki-dark-font-style:italic}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}",{"title":1037,"searchDepth":1038,"depth":1038,"links":181299},[181300,181301,181302,181303,181308,181316,181317],{"id":29856,"depth":1041,"text":29857},{"id":70092,"depth":1041,"text":70093},{"id":99863,"depth":1041,"text":99864},{"id":29892,"depth":1041,"text":29893,"children":181304},[181305,181306,181307],{"id":179301,"depth":1038,"text":179302},{"id":179570,"depth":1038,"text":179571},{"id":179931,"depth":1038,"text":179932},{"id":102888,"depth":1041,"text":102889,"children":181309},[181310,181311,181312,181313,181314,181315],{"id":180006,"depth":1038,"text":180009},{"id":42973,"depth":1038,"text":42973},{"id":180361,"depth":1038,"text":180357},{"id":180410,"depth":1038,"text":180411},{"id":180783,"depth":1038,"text":153619},{"id":120264,"depth":1038,"text":120265},{"id":68209,"depth":1041,"text":68210},{"id":2620,"depth":1041,"text":2621},"withMaterializedView() persists a query's result — filter, groupBy, aggregate, join — as a live collection with eager, lazy, or manual refresh.",{},{"title":739,"description":181318},"fcw2P1J9rhcNzzlTlUapmchabLmlfCKM93bfSdyH-hQ",{"id":181323,"title":743,"access":12,"apiToc":929,"body":181324,"category":1042,"description":181834,"extension":1044,"meta":181835,"navigation":1042,"nest":1047,"path":744,"seo":181836,"sinceVersion":1042,"status":38550,"stem":745,"subtitle":1042,"tier":1050,"tocExternal":1042,"__hash__":181837},"docs\u002Fservices\u002Fmetadata-ladder.md",{"type":931,"value":181325,"toc":181829},[181326,181329,181332,181343,181349,181352,181358,181364,181500,181504,181570,181582,181597,181602,181627,181633,181638,181711,181715,181768,181776,181780,181798,181817,181819,181826],[934,181327,743],{"id":181328},"metadata-ladder",[937,181330,181331],{},"Above field-level metadata sit two more rungs — collection and vault identity — that viewers consume without duplicating their own label logic.",[937,181333,181334,181335,1261,181337,181339,181340,2311],{},"The field-level descriptors (",[941,181336,25333],{},[941,181338,146757],{},") are the bottom rung\nof a ",[950,181341,181342],{},"three-rung metadata ladder",[2217,181344,181347],{"className":181345,"code":181346,"language":5672},[5670],"field  →  collection  →  vault\n",[941,181348,181346],{"__ignoreMap":1037},[937,181350,181351],{},"Each rung adds a friendly identity layer that viewers (devtools, editor, export\nfilename, API doc) consume without duplicating their own label logic.",[1022,181353,181355],{"id":181354},"collectionmeta",[941,181356,181357],{},"collectionMeta",[937,181359,181360,181361,181363],{},"Declare via the ",[941,181362,17587],{}," collection option:",[2217,181365,181367],{"className":2219,"code":181366,"language":2221,"meta":1037,"style":1037},"const invoices = vault.collection\u003CInvoice>('invoices', {\n  meta: {\n    label: 'Sales Invoices',\n    description: 'Outbound sales invoices billed to clients.',\n    pluralLabel: 'Sales Invoices',  \u002F\u002F for list headers\n    icon: 'receipt',                \u002F\u002F semantic icon name (Lucide etc.)\n  },\n  fieldMeta: { … },\n})\n",[941,181368,181369,181401,181410,181426,181441,181459,181477,181481,181494],{"__ignoreMap":1037},[2225,181370,181371,181373,181375,181377,181379,181381,181383,181385,181387,181389,181391,181393,181395,181397,181399],{"class":2227,"line":2228},[2225,181372,2775],{"class":2774},[2225,181374,6461],{"class":2239},[2225,181376,2781],{"class":2235},[2225,181378,6466],{"class":2239},[2225,181380,1520],{"class":2235},[2225,181382,6471],{"class":2291},[2225,181384,6474],{"class":2235},[2225,181386,6477],{"class":6342},[2225,181388,6480],{"class":2235},[2225,181390,2789],{"class":2239},[2225,181392,2805],{"class":2235},[2225,181394,6487],{"class":2252},[2225,181396,2805],{"class":2235},[2225,181398,2298],{"class":2235},[2225,181400,2301],{"class":2235},[2225,181402,181403,181406,181408],{"class":2227,"line":1041},[2225,181404,181405],{"class":2307},"  meta",[2225,181407,2311],{"class":2235},[2225,181409,2301],{"class":2235},[2225,181411,181412,181415,181417,181419,181422,181424],{"class":2227,"line":1038},[2225,181413,181414],{"class":2307},"    label",[2225,181416,2311],{"class":2235},[2225,181418,2249],{"class":2235},[2225,181420,181421],{"class":2252},"Sales Invoices",[2225,181423,2805],{"class":2235},[2225,181425,1707],{"class":2235},[2225,181427,181428,181430,181432,181434,181437,181439],{"class":2227,"line":2288},[2225,181429,59314],{"class":2307},[2225,181431,2311],{"class":2235},[2225,181433,2249],{"class":2235},[2225,181435,181436],{"class":2252},"Outbound sales invoices billed to clients.",[2225,181438,2805],{"class":2235},[2225,181440,1707],{"class":2235},[2225,181442,181443,181446,181448,181450,181452,181454,181456],{"class":2227,"line":2304},[2225,181444,181445],{"class":2307},"    pluralLabel",[2225,181447,2311],{"class":2235},[2225,181449,2249],{"class":2235},[2225,181451,181421],{"class":2252},[2225,181453,2805],{"class":2235},[2225,181455,2298],{"class":2235},[2225,181457,181458],{"class":2763},"  \u002F\u002F for list headers\n",[2225,181460,181461,181464,181466,181468,181470,181472,181474],{"class":2227,"line":2356},[2225,181462,181463],{"class":2307},"    icon",[2225,181465,2311],{"class":2235},[2225,181467,2249],{"class":2235},[2225,181469,107891],{"class":2252},[2225,181471,2805],{"class":2235},[2225,181473,2298],{"class":2235},[2225,181475,181476],{"class":2763},"                \u002F\u002F semantic icon name (Lucide etc.)\n",[2225,181478,181479],{"class":2227,"line":2830},[2225,181480,19017],{"class":2235},[2225,181482,181483,181485,181487,181489,181492],{"class":2227,"line":2861},[2225,181484,147288],{"class":2307},[2225,181486,2311],{"class":2235},[2225,181488,2236],{"class":2235},[2225,181490,181491],{"class":2239}," … ",[2225,181493,19008],{"class":2235},[2225,181495,181496,181498],{"class":2227,"line":4246},[2225,181497,2359],{"class":2235},[2225,181499,2362],{"class":2239},[937,181501,181502,147446],{},[941,181503,167718],{},[1338,181505,181506,181516],{},[1341,181507,181508],{},[1344,181509,181510,181512,181514],{},[1347,181511,101104],{},[1347,181513,20194],{},[1347,181515,7834],{},[1357,181517,181518,181531,181543,181556],{},[1344,181519,181520,181524,181528],{},[1362,181521,181522],{},[941,181523,96028],{},[1362,181525,181526],{},[941,181527,147485],{},[1362,181529,181530],{},"Friendly collection name; falls back to the humanized collection name",[1344,181532,181533,181537,181541],{},[1362,181534,181535],{},[941,181536,59777],{},[1362,181538,181539],{},[941,181540,147485],{},[1362,181542,147488],{},[1344,181544,181545,181549,181553],{},[1362,181546,181547],{},[941,181548,126699],{},[1362,181550,181551],{},[941,181552,147485],{},[1362,181554,181555],{},"Semantic icon name (e.g. a Lucide key), not styling",[1344,181557,181558,181563,181567],{},[1362,181559,181560],{},[941,181561,181562],{},"pluralLabel",[1362,181564,181565],{},[941,181566,147485],{},[1362,181568,181569],{},"Plural form for list headers (\"Invoice\" → \"Invoices\")",[937,181571,181572,181573,968,181576,7356,181579,181581],{},"Unlike ",[941,181574,181575],{},"FieldMeta.label",[941,181577,181578],{},"CollectionMeta.label",[950,181580,2084],{}," — the\ncollection name is already a reasonable fallback identity.",[937,181583,181584,181587,181588,181590,181591,181593,181594,957],{},[950,181585,181586],{},"First-wins reconciler."," When a collection is pre-created by an MV before the\napp's own ",[941,181589,2138],{}," call, the first declared ",[941,181592,17587],{}," wins (mirrors\n",[941,181595,181596],{},"_applyFieldMeta",[937,181598,181599],{},[950,181600,181601],{},"Surfaces in:",[1111,181603,181604,181611,181618],{},[1114,181605,181606,1746,181608],{},[941,181607,1380],{},[941,181609,181610],{},"CollectionDescription.meta",[1114,181612,181613,1746,181615],{},[941,181614,165475],{},[941,181616,181617],{},"CollectionDescriptor.meta",[1114,181619,181620,1324,181622,1746,181624],{},[941,181621,36706],{},[941,181623,150072],{},[941,181625,181626],{},"InspectorCollection.meta",[1022,181628,181630],{"id":181629},"vaultmeta",[941,181631,181632],{},"vaultMeta",[937,181634,181360,181635,181637],{},[941,181636,17587],{}," vault option:",[2217,181639,181641],{"className":2219,"code":181640,"language":2221,"meta":1037,"style":1037},"const vault = await db.openVault('ledger', {\n  meta: { label: 'Acme Ledger 2026', description: 'Main accounting vault.' },\n})\n",[941,181642,181643,181671,181705],{"__ignoreMap":1037},[2225,181644,181645,181647,181649,181651,181653,181655,181657,181659,181661,181663,181665,181667,181669],{"class":2227,"line":2228},[2225,181646,2775],{"class":2774},[2225,181648,2835],{"class":2239},[2225,181650,2781],{"class":2235},[2225,181652,2784],{"class":2231},[2225,181654,2842],{"class":2239},[2225,181656,1520],{"class":2235},[2225,181658,2847],{"class":2291},[2225,181660,2789],{"class":2239},[2225,181662,2805],{"class":2235},[2225,181664,20923],{"class":2252},[2225,181666,2805],{"class":2235},[2225,181668,2298],{"class":2235},[2225,181670,2301],{"class":2235},[2225,181672,181673,181675,181677,181679,181681,181683,181685,181688,181690,181692,181694,181696,181698,181701,181703],{"class":2227,"line":1041},[2225,181674,181405],{"class":2307},[2225,181676,2311],{"class":2235},[2225,181678,2236],{"class":2235},[2225,181680,95892],{"class":2307},[2225,181682,2311],{"class":2235},[2225,181684,2249],{"class":2235},[2225,181686,181687],{"class":2252},"Acme Ledger 2026",[2225,181689,2805],{"class":2235},[2225,181691,2298],{"class":2235},[2225,181693,19510],{"class":2307},[2225,181695,2311],{"class":2235},[2225,181697,2249],{"class":2235},[2225,181699,181700],{"class":2252},"Main accounting vault.",[2225,181702,2805],{"class":2235},[2225,181704,2353],{"class":2235},[2225,181706,181707,181709],{"class":2227,"line":1038},[2225,181708,2359],{"class":2235},[2225,181710,2362],{"class":2239},[937,181712,181713,147446],{},[941,181714,11817],{},[1338,181716,181717,181727],{},[1341,181718,181719],{},[1344,181720,181721,181723,181725],{},[1347,181722,101104],{},[1347,181724,20194],{},[1347,181726,7834],{},[1357,181728,181729,181742,181755],{},[1344,181730,181731,181735,181739],{},[1362,181732,181733],{},[941,181734,96028],{},[1362,181736,181737],{},[941,181738,147485],{},[1362,181740,181741],{},"Friendly vault name; falls back to the vault name",[1344,181743,181744,181748,181752],{},[1362,181745,181746],{},[941,181747,59777],{},[1362,181749,181750],{},[941,181751,147485],{},[1362,181753,181754],{},"Longer context",[1344,181756,181757,181761,181765],{},[1362,181758,181759],{},[941,181760,126699],{},[1362,181762,181763],{},[941,181764,147485],{},[1362,181766,181767],{},"Semantic icon name",[937,181769,181770,181773,181774,1520],{},[950,181771,181772],{},"First-wins."," Re-opening an already-cached vault keeps its ",[941,181775,17587],{},[937,181777,181778],{},[950,181779,181601],{},[1111,181781,181782,181789],{},[1114,181783,181784,1746,181786],{},[941,181785,165475],{},[941,181787,181788],{},"VaultSchemaSnapshot.meta",[1114,181790,181791,1324,181793,1746,181795],{},[941,181792,36706],{},[941,181794,150072],{},[941,181796,181797],{},"InspectorSnapshot.meta",[937,181799,181800,1324,181803,997,181805,181807,181808,181810,181811,6793,181813,181816],{},[950,181801,181802],{},"Cargo export.",[941,181804,167718],{},[941,181806,11817],{}," are also exported from\n",[941,181809,1220],{}," (the klum orchestration seam) so that klum-db's federation\nlayer can reuse ",[941,181812,11817],{},[941,181814,181815],{},"groupMeta"," without taking a full hub dep.",[1022,181818,2621],{"id":2620},[1111,181820,181821],{},[1114,181822,181823,181825],{},[1007,181824,677],{"href":678}," — the describe() contract these extend",[2645,181827,181828],{},"html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}",{"title":1037,"searchDepth":1038,"depth":1038,"links":181830},[181831,181832,181833],{"id":181354,"depth":1041,"text":181357},{"id":181629,"depth":1041,"text":181632},{"id":2620,"depth":1041,"text":2621},"The collection and vault rungs above field-level metadata — collectionMeta and vaultMeta identity layers that describe() and dumpSchema() surface for viewers.",{},{"title":743,"description":181834},"JaUXSy-q7c1s4pBWTmASK_4I0rQt1Ugpy8grOyk0qRw",{"id":181839,"title":747,"access":12,"apiToc":929,"body":181840,"category":1042,"description":183010,"extension":1044,"meta":183011,"navigation":1042,"nest":1047,"path":748,"seo":183012,"sinceVersion":1042,"status":38550,"stem":749,"subtitle":1042,"tier":1050,"tocExternal":1042,"__hash__":183013},"docs\u002Fservices\u002Fmetadata-riders.md",{"type":931,"value":181841,"toc":183003},[181842,181845,181848,181854,181866,181890,181901,182417,182422,182449,182456,182620,182633,182656,182658,182664,182669,182844,182852,182857,182902,182917,182919,182923,182941,182946,182980,182986,182992,182994,183000],[934,181843,747],{"id":181844},"metadata-riders",[937,181846,181847],{},"Three riders extend the describe() contract outward — into JSON Schema, dict labels, and devtools masking.",[1022,181849,181851],{"id":181850},"collectiontojsonschema",[941,181852,181853],{},"collection.toJSONSchema()",[937,181855,181856,181857,181859,181860,181862,181863,181865],{},"An async method on ",[941,181858,103],{}," that produces a JSON Schema object annotated\nwith ",[941,181861,3926],{}," metadata as ",[941,181864,25516],{}," extension keys:",[2217,181867,181869],{"className":2219,"code":181868,"language":2221,"meta":1037,"style":1037},"const schema = await sales.toJSONSchema()\n",[941,181870,181871],{"__ignoreMap":1037},[2225,181872,181873,181875,181878,181880,181882,181884,181886,181888],{"class":2227,"line":2228},[2225,181874,2775],{"class":2774},[2225,181876,181877],{"class":2239}," schema ",[2225,181879,2781],{"class":2235},[2225,181881,2784],{"class":2231},[2225,181883,123785],{"class":2239},[2225,181885,1520],{"class":2235},[2225,181887,25496],{"class":2291},[2225,181889,8146],{"class":2239},[937,181891,181892,181894,181895,181897,181898,181900],{},[941,181893,23934],{},", for the ",[941,181896,123562],{}," collection declared earlier (no zod schema was\nattached, so ",[941,181899,25486],{}," falls back to the minimal-schema path described\nbelow):",[2217,181902,181904],{"className":6604,"code":181903,"language":141,"meta":1037,"style":1037},"{\n  \"type\": \"object\",\n  \"properties\": {\n    \"buyerId\":  { \"type\": \"string\", \"x-label\": \"Buyer\", \"x-semanticType\": \"entity\",\n                  \"x-sensitivity\": \"pii\", \"x-widget\": \"ref-select\", \"x-ref\": \"buyers\" },\n    \"notes\":    { \"type\": \"string\", \"x-label\": \"Internal notes\", \"x-sensitivity\": \"secret\",\n                  \"x-widget\": \"text\" },\n    \"saleDate\": { \"type\": \"string\", \"x-label\": \"Date\", \"x-widget\": \"text\" },\n    \"status\":   { \"type\": \"string\", \"x-label\": \"Status\", \"x-widget\": \"select\" },\n    \"total\":    { \"type\": \"number\", \"x-label\": \"Total (€)\", \"x-unit\": \"€\",\n                  \"x-semanticType\": \"currency\", \"x-widget\": \"money\",\n                  \"x-money\": { \"mode\": \"fixed\", \"currency\": \"EUR\", \"scale\": 2 } }\n  }\n}\n",[941,181905,181906,181910,181928,181941,182003,182057,182117,182135,182195,182255,182316,182350,182409,182413],{"__ignoreMap":1037},[2225,181907,181908],{"class":2227,"line":2228},[2225,181909,4200],{"class":2235},[2225,181911,181912,181914,181916,181918,181920,181922,181924,181926],{"class":2227,"line":1041},[2225,181913,9368],{"class":2235},[2225,181915,6339],{"class":2774},[2225,181917,6620],{"class":2235},[2225,181919,2311],{"class":2235},[2225,181921,6614],{"class":2235},[2225,181923,25505],{"class":2252},[2225,181925,6620],{"class":2235},[2225,181927,1707],{"class":2235},[2225,181929,181930,181932,181935,181937,181939],{"class":2227,"line":1038},[2225,181931,9368],{"class":2235},[2225,181933,181934],{"class":2774},"properties",[2225,181936,6620],{"class":2235},[2225,181938,2311],{"class":2235},[2225,181940,2301],{"class":2235},[2225,181942,181943,181945,181947,181949,181951,181953,181955,181957,181959,181961,181963,181965,181967,181969,181971,181974,181976,181978,181980,181982,181984,181986,181988,181991,181993,181995,181997,181999,182001],{"class":2227,"line":2288},[2225,181944,14201],{"class":2235},[2225,181946,109007],{"class":6342},[2225,181948,6620],{"class":2235},[2225,181950,2311],{"class":2235},[2225,181952,26354],{"class":2235},[2225,181954,6614],{"class":2235},[2225,181956,6339],{"class":6527},[2225,181958,6620],{"class":2235},[2225,181960,2311],{"class":2235},[2225,181962,6614],{"class":2235},[2225,181964,7259],{"class":2252},[2225,181966,6620],{"class":2235},[2225,181968,2298],{"class":2235},[2225,181970,6614],{"class":2235},[2225,181972,181973],{"class":6527},"x-label",[2225,181975,6620],{"class":2235},[2225,181977,2311],{"class":2235},[2225,181979,6614],{"class":2235},[2225,181981,139386],{"class":2252},[2225,181983,6620],{"class":2235},[2225,181985,2298],{"class":2235},[2225,181987,6614],{"class":2235},[2225,181989,181990],{"class":6527},"x-semanticType",[2225,181992,6620],{"class":2235},[2225,181994,2311],{"class":2235},[2225,181996,6614],{"class":2235},[2225,181998,147938],{"class":2252},[2225,182000,6620],{"class":2235},[2225,182002,1707],{"class":2235},[2225,182004,182005,182008,182011,182013,182015,182017,182019,182021,182023,182025,182028,182030,182032,182034,182036,182038,182040,182042,182045,182047,182049,182051,182053,182055],{"class":2227,"line":2304},[2225,182006,182007],{"class":2235},"                  \"",[2225,182009,182010],{"class":6527},"x-sensitivity",[2225,182012,6620],{"class":2235},[2225,182014,2311],{"class":2235},[2225,182016,6614],{"class":2235},[2225,182018,147379],{"class":2252},[2225,182020,6620],{"class":2235},[2225,182022,2298],{"class":2235},[2225,182024,6614],{"class":2235},[2225,182026,182027],{"class":6527},"x-widget",[2225,182029,6620],{"class":2235},[2225,182031,2311],{"class":2235},[2225,182033,6614],{"class":2235},[2225,182035,148031],{"class":2252},[2225,182037,6620],{"class":2235},[2225,182039,2298],{"class":2235},[2225,182041,6614],{"class":2235},[2225,182043,182044],{"class":6527},"x-ref",[2225,182046,6620],{"class":2235},[2225,182048,2311],{"class":2235},[2225,182050,6614],{"class":2235},[2225,182052,139251],{"class":2252},[2225,182054,6620],{"class":2235},[2225,182056,2353],{"class":2235},[2225,182058,182059,182061,182063,182065,182067,182069,182071,182073,182075,182077,182079,182081,182083,182085,182087,182089,182091,182093,182095,182097,182099,182101,182103,182105,182107,182109,182111,182113,182115],{"class":2227,"line":2356},[2225,182060,14201],{"class":2235},[2225,182062,2854],{"class":6342},[2225,182064,6620],{"class":2235},[2225,182066,2311],{"class":2235},[2225,182068,32496],{"class":2235},[2225,182070,6614],{"class":2235},[2225,182072,6339],{"class":6527},[2225,182074,6620],{"class":2235},[2225,182076,2311],{"class":2235},[2225,182078,6614],{"class":2235},[2225,182080,7259],{"class":2252},[2225,182082,6620],{"class":2235},[2225,182084,2298],{"class":2235},[2225,182086,6614],{"class":2235},[2225,182088,181973],{"class":6527},[2225,182090,6620],{"class":2235},[2225,182092,2311],{"class":2235},[2225,182094,6614],{"class":2235},[2225,182096,147414],{"class":2252},[2225,182098,6620],{"class":2235},[2225,182100,2298],{"class":2235},[2225,182102,6614],{"class":2235},[2225,182104,182010],{"class":6527},[2225,182106,6620],{"class":2235},[2225,182108,2311],{"class":2235},[2225,182110,6614],{"class":2235},[2225,182112,10695],{"class":2252},[2225,182114,6620],{"class":2235},[2225,182116,1707],{"class":2235},[2225,182118,182119,182121,182123,182125,182127,182129,182131,182133],{"class":2227,"line":2830},[2225,182120,182007],{"class":2235},[2225,182122,182027],{"class":6527},[2225,182124,6620],{"class":2235},[2225,182126,2311],{"class":2235},[2225,182128,6614],{"class":2235},[2225,182130,5672],{"class":2252},[2225,182132,6620],{"class":2235},[2225,182134,2353],{"class":2235},[2225,182136,182137,182139,182141,182143,182145,182147,182149,182151,182153,182155,182157,182159,182161,182163,182165,182167,182169,182171,182173,182175,182177,182179,182181,182183,182185,182187,182189,182191,182193],{"class":2227,"line":2861},[2225,182138,14201],{"class":2235},[2225,182140,148173],{"class":6342},[2225,182142,6620],{"class":2235},[2225,182144,2311],{"class":2235},[2225,182146,2236],{"class":2235},[2225,182148,6614],{"class":2235},[2225,182150,6339],{"class":6527},[2225,182152,6620],{"class":2235},[2225,182154,2311],{"class":2235},[2225,182156,6614],{"class":2235},[2225,182158,7259],{"class":2252},[2225,182160,6620],{"class":2235},[2225,182162,2298],{"class":2235},[2225,182164,6614],{"class":2235},[2225,182166,181973],{"class":6527},[2225,182168,6620],{"class":2235},[2225,182170,2311],{"class":2235},[2225,182172,6614],{"class":2235},[2225,182174,26712],{"class":2252},[2225,182176,6620],{"class":2235},[2225,182178,2298],{"class":2235},[2225,182180,6614],{"class":2235},[2225,182182,182027],{"class":6527},[2225,182184,6620],{"class":2235},[2225,182186,2311],{"class":2235},[2225,182188,6614],{"class":2235},[2225,182190,5672],{"class":2252},[2225,182192,6620],{"class":2235},[2225,182194,2353],{"class":2235},[2225,182196,182197,182199,182201,182203,182205,182207,182209,182211,182213,182215,182217,182219,182221,182223,182225,182227,182229,182231,182233,182235,182237,182239,182241,182243,182245,182247,182249,182251,182253],{"class":2227,"line":4246},[2225,182198,14201],{"class":2235},[2225,182200,6631],{"class":6342},[2225,182202,6620],{"class":2235},[2225,182204,2311],{"class":2235},[2225,182206,58994],{"class":2235},[2225,182208,6614],{"class":2235},[2225,182210,6339],{"class":6527},[2225,182212,6620],{"class":2235},[2225,182214,2311],{"class":2235},[2225,182216,6614],{"class":2235},[2225,182218,7259],{"class":2252},[2225,182220,6620],{"class":2235},[2225,182222,2298],{"class":2235},[2225,182224,6614],{"class":2235},[2225,182226,181973],{"class":6527},[2225,182228,6620],{"class":2235},[2225,182230,2311],{"class":2235},[2225,182232,6614],{"class":2235},[2225,182234,48731],{"class":2252},[2225,182236,6620],{"class":2235},[2225,182238,2298],{"class":2235},[2225,182240,6614],{"class":2235},[2225,182242,182027],{"class":6527},[2225,182244,6620],{"class":2235},[2225,182246,2311],{"class":2235},[2225,182248,6614],{"class":2235},[2225,182250,51703],{"class":2252},[2225,182252,6620],{"class":2235},[2225,182254,2353],{"class":2235},[2225,182256,182257,182259,182261,182263,182265,182267,182269,182271,182273,182275,182277,182279,182281,182283,182285,182287,182289,182291,182293,182295,182297,182299,182301,182304,182306,182308,182310,182312,182314],{"class":2227,"line":4253},[2225,182258,14201],{"class":2235},[2225,182260,64200],{"class":6342},[2225,182262,6620],{"class":2235},[2225,182264,2311],{"class":2235},[2225,182266,32496],{"class":2235},[2225,182268,6614],{"class":2235},[2225,182270,6339],{"class":6527},[2225,182272,6620],{"class":2235},[2225,182274,2311],{"class":2235},[2225,182276,6614],{"class":2235},[2225,182278,20636],{"class":2252},[2225,182280,6620],{"class":2235},[2225,182282,2298],{"class":2235},[2225,182284,6614],{"class":2235},[2225,182286,181973],{"class":6527},[2225,182288,6620],{"class":2235},[2225,182290,2311],{"class":2235},[2225,182292,6614],{"class":2235},[2225,182294,147330],{"class":2252},[2225,182296,6620],{"class":2235},[2225,182298,2298],{"class":2235},[2225,182300,6614],{"class":2235},[2225,182302,182303],{"class":6527},"x-unit",[2225,182305,6620],{"class":2235},[2225,182307,2311],{"class":2235},[2225,182309,6614],{"class":2235},[2225,182311,147344],{"class":2252},[2225,182313,6620],{"class":2235},[2225,182315,1707],{"class":2235},[2225,182317,182318,182320,182322,182324,182326,182328,182330,182332,182334,182336,182338,182340,182342,182344,182346,182348],{"class":2227,"line":4259},[2225,182319,182007],{"class":2235},[2225,182321,181990],{"class":6527},[2225,182323,6620],{"class":2235},[2225,182325,2311],{"class":2235},[2225,182327,6614],{"class":2235},[2225,182329,148542],{"class":2252},[2225,182331,6620],{"class":2235},[2225,182333,2298],{"class":2235},[2225,182335,6614],{"class":2235},[2225,182337,182027],{"class":6527},[2225,182339,6620],{"class":2235},[2225,182341,2311],{"class":2235},[2225,182343,6614],{"class":2235},[2225,182345,760],{"class":2252},[2225,182347,6620],{"class":2235},[2225,182349,1707],{"class":2235},[2225,182351,182352,182354,182357,182359,182361,182363,182365,182367,182369,182371,182373,182375,182377,182379,182381,182383,182385,182387,182389,182391,182393,182395,182397,182399,182401,182403,182405,182407],{"class":2227,"line":6548},[2225,182353,182007],{"class":2235},[2225,182355,182356],{"class":6527},"x-money",[2225,182358,6620],{"class":2235},[2225,182360,2311],{"class":2235},[2225,182362,2236],{"class":2235},[2225,182364,6614],{"class":2235},[2225,182366,120875],{"class":2307},[2225,182368,6620],{"class":2235},[2225,182370,2311],{"class":2235},[2225,182372,6614],{"class":2235},[2225,182374,148587],{"class":2252},[2225,182376,6620],{"class":2235},[2225,182378,2298],{"class":2235},[2225,182380,6614],{"class":2235},[2225,182382,148542],{"class":2307},[2225,182384,6620],{"class":2235},[2225,182386,2311],{"class":2235},[2225,182388,6614],{"class":2235},[2225,182390,122902],{"class":2252},[2225,182392,6620],{"class":2235},[2225,182394,2298],{"class":2235},[2225,182396,6614],{"class":2235},[2225,182398,148612],{"class":2307},[2225,182400,6620],{"class":2235},[2225,182402,2311],{"class":2235},[2225,182404,18961],{"class":6527},[2225,182406,2243],{"class":2235},[2225,182408,6369],{"class":2235},[2225,182410,182411],{"class":2227,"line":6576},[2225,182412,7632],{"class":2235},[2225,182414,182415],{"class":2227,"line":6582},[2225,182416,7351],{"class":2235},[937,182418,182419],{},[950,182420,182421],{},"Pipeline:",[1234,182423,182424,182437,182443],{},[1114,182425,182426,182429,182430,182433,182434,957],{},[941,182427,182428],{},"derivePersistedSchema(this.schema)"," — produces the base JSON Schema (zod-4\n",[941,182431,182432],{},"z.toJSONSchema()"," path; zod-3 via ",[941,182435,182436],{},"zod-to-json-schema",[1114,182438,182439,182442],{},[941,182440,182441],{},"await this.describe({})"," — the normalized per-field metadata.",[1114,182444,182445,182446,182448],{},"Overlay each property with the ",[941,182447,25516],{}," extension keys below.",[937,182450,182451],{},[950,182452,182453,182455],{},[941,182454,25516],{}," key table:",[1338,182457,182458,182471],{},[1341,182459,182460],{},[1344,182461,182462,182466,182469],{},[1347,182463,182464,124557],{},[941,182465,3926],{},[1347,182467,182468],{},"JSON-Schema key",[1347,182470,9542],{},[1357,182472,182473,182485,182497,182509,182529,182542,182558,182574,182587,182603],{},[1344,182474,182475,182479,182483],{},[1362,182476,182477],{},[941,182478,96028],{},[1362,182480,182481],{},[941,182482,181973],{},[1362,182484],{},[1344,182486,182487,182491,182495],{},[1362,182488,182489],{},[941,182490,146766],{},[1362,182492,182493],{},[941,182494,182303],{},[1362,182496],{},[1344,182498,182499,182503,182507],{},[1362,182500,182501],{},[941,182502,146763],{},[1362,182504,182505],{},[941,182506,181990],{},[1362,182508],{},[1344,182510,182511,182515,182519],{},[1362,182512,182513],{},[941,182514,146769],{},[1362,182516,182517],{},[941,182518,182010],{},[1362,182520,182521,968,182524,4765,182527],{},[941,182522,182523],{},"'public'",[941,182525,182526],{},"'pii'",[941,182528,20359],{},[1344,182530,182531,182535,182539],{},[1362,182532,182533],{},[941,182534,147604],{},[1362,182536,182537],{},[941,182538,182027],{},[1362,182540,182541],{},"derived hint, overridable",[1344,182543,182544,182548,182553],{},[1362,182545,182546],{},[941,182547,124749],{},[1362,182549,182550],{},[941,182551,182552],{},"x-readonly: true",[1362,182554,182555,182556],{},"only emitted when ",[941,182557,2560],{},[1344,182559,182560,182564,182568],{},[1362,182561,182562],{},[941,182563,760],{},[1362,182565,182566],{},[941,182567,182356],{},[1362,182569,182570,182571],{},"full money block: ",[941,182572,182573],{},"{ mode, currency?, scale?, rounding? }",[1344,182575,182576,182580,182584],{},[1362,182577,182578],{},[941,182579,147776],{},[1362,182581,182582],{},[941,182583,182044],{},[1362,182585,182586],{},"bare string — the ref target collection name",[1344,182588,182589,182593,182598],{},[1362,182590,182591],{},[941,182592,146900],{},[1362,182594,182595],{},[941,182596,182597],{},"x-enumLabels",[1362,182599,182600,179705],{},[941,182601,182602],{},"{ [value]: label }",[1344,182604,182605,182609,182614],{},[1362,182606,182607],{},[941,182608,633],{},[1362,182610,182611],{},[941,182612,182613],{},"x-classified",[1362,182615,182616,182617,182619],{},"full classified block (preset, storage, list, ",[941,182618,120049],{},") — emitted whenever present, regardless of gating",[937,182621,182622,182624,182625,182627,182628,182632],{},[941,182623,182613],{}," is emitted whenever a field carries a ",[941,182626,633],{}," spec — it is\nnot gated by whether ",[1007,182629,182630],{"href":634},[941,182631,22007],{}," is opted in\nfor reveal\u002Fverify at the collection level; a deployment that wants the schema\nkey itself gated applies that gate on top.",[937,182634,182635,182638,182639,182642,182643,182645,182646,182649,182650,182652,182653,182655],{},[950,182636,182637],{},"Validator-agnostic fallback:"," when ",[941,182640,182641],{},"derivePersistedSchema"," yields no JSON\nSchema (non-zod validator), ",[941,182644,25486],{}," builds a minimal\n",[941,182647,182648],{},"{ type: 'object', properties }"," from the field types derived by ",[941,182651,3926],{},",\nthen overlays the same ",[941,182654,25516],{}," metadata. No throw; no zod required.",[104892,182657],{},[1022,182659,182661,182663],{"id":182660},"dictkey-inline-labels",[941,182662,21456],{}," inline labels",[937,182665,182666,182668],{},[941,182667,21456],{}," gains an optional inline label map alongside the existing array form:",[2217,182670,182672],{"className":2219,"code":182671,"language":2221,"meta":1037,"style":1037},"\u002F\u002F Today (still valid — bare array):\ndictKey('saleStatus', ['draft', 'to_verify', 'paid'] as const)\n\n\u002F\u002F New — value→label map (keys inferred from the map):\ndictKey('saleStatus', { draft: 'Draft', to_verify: 'To Verify', paid: 'Paid' })\n\n\u002F\u002F Or array + labels option (preserves explicit order):\ndictKey('saleStatus', VALUES, { labels: { to_verify: 'To Verify' } })\n",[941,182673,182674,182679,182725,182729,182734,182791,182795,182800],{"__ignoreMap":1037},[2225,182675,182676],{"class":2227,"line":2228},[2225,182677,182678],{"class":2763},"\u002F\u002F Today (still valid — bare array):\n",[2225,182680,182681,182683,182685,182687,182689,182691,182693,182695,182697,182699,182701,182703,182705,182707,182709,182711,182713,182715,182717,182719,182721,182723],{"class":2227,"line":1041},[2225,182682,21456],{"class":2291},[2225,182684,2789],{"class":2239},[2225,182686,2805],{"class":2235},[2225,182688,147069],{"class":2252},[2225,182690,2805],{"class":2235},[2225,182692,2298],{"class":2235},[2225,182694,8667],{"class":2239},[2225,182696,2805],{"class":2235},[2225,182698,28429],{"class":2252},[2225,182700,2805],{"class":2235},[2225,182702,2298],{"class":2235},[2225,182704,2249],{"class":2235},[2225,182706,147088],{"class":2252},[2225,182708,2805],{"class":2235},[2225,182710,2298],{"class":2235},[2225,182712,2249],{"class":2235},[2225,182714,6539],{"class":2252},[2225,182716,2805],{"class":2235},[2225,182718,8890],{"class":2239},[2225,182720,4899],{"class":2231},[2225,182722,8083],{"class":2774},[2225,182724,2362],{"class":2239},[2225,182726,182727],{"class":2227,"line":1038},[2225,182728,2285],{"emptyLinePlaceholder":2284},[2225,182730,182731],{"class":2227,"line":2288},[2225,182732,182733],{"class":2763},"\u002F\u002F New — value→label map (keys inferred from the map):\n",[2225,182735,182736,182738,182740,182742,182744,182746,182748,182750,182753,182755,182757,182759,182761,182763,182766,182768,182770,182773,182775,182777,182779,182781,182783,182785,182787,182789],{"class":2227,"line":2304},[2225,182737,21456],{"class":2291},[2225,182739,2789],{"class":2239},[2225,182741,2805],{"class":2235},[2225,182743,147069],{"class":2252},[2225,182745,2805],{"class":2235},[2225,182747,2298],{"class":2235},[2225,182749,2236],{"class":2235},[2225,182751,182752],{"class":2307}," draft",[2225,182754,2311],{"class":2235},[2225,182756,2249],{"class":2235},[2225,182758,59004],{"class":2252},[2225,182760,2805],{"class":2235},[2225,182762,2298],{"class":2235},[2225,182764,182765],{"class":2307}," to_verify",[2225,182767,2311],{"class":2235},[2225,182769,2249],{"class":2235},[2225,182771,182772],{"class":2252},"To Verify",[2225,182774,2805],{"class":2235},[2225,182776,2298],{"class":2235},[2225,182778,156816],{"class":2307},[2225,182780,2311],{"class":2235},[2225,182782,2249],{"class":2235},[2225,182784,59104],{"class":2252},[2225,182786,2805],{"class":2235},[2225,182788,2243],{"class":2235},[2225,182790,2362],{"class":2239},[2225,182792,182793],{"class":2227,"line":2356},[2225,182794,2285],{"emptyLinePlaceholder":2284},[2225,182796,182797],{"class":2227,"line":2830},[2225,182798,182799],{"class":2763},"\u002F\u002F Or array + labels option (preserves explicit order):\n",[2225,182801,182802,182804,182806,182808,182810,182812,182814,182817,182819,182821,182824,182826,182828,182830,182832,182834,182836,182838,182840,182842],{"class":2227,"line":2861},[2225,182803,21456],{"class":2291},[2225,182805,2789],{"class":2239},[2225,182807,2805],{"class":2235},[2225,182809,147069],{"class":2252},[2225,182811,2805],{"class":2235},[2225,182813,2298],{"class":2235},[2225,182815,182816],{"class":2239}," VALUES",[2225,182818,2298],{"class":2235},[2225,182820,2236],{"class":2235},[2225,182822,182823],{"class":2307}," labels",[2225,182825,2311],{"class":2235},[2225,182827,2236],{"class":2235},[2225,182829,182765],{"class":2307},[2225,182831,2311],{"class":2235},[2225,182833,2249],{"class":2235},[2225,182835,182772],{"class":2252},[2225,182837,2805],{"class":2235},[2225,182839,2243],{"class":2235},[2225,182841,2243],{"class":2235},[2225,182843,2362],{"class":2239},[937,182845,182846,150104,182849,1520],{},[941,182847,182848],{},"DictKeyDescriptor",[941,182850,182851],{},"readonly labels?: Record\u003Cstring, string>",[937,182853,182854],{},[950,182855,182856],{},"Semantics — display fallback:",[1111,182858,182859,182869,182882,182893],{},[1114,182860,182861,182862,182865,182866,182868],{},"Inline labels are code-provided ",[1092,182863,182864],{},"defaults"," surfaced when the ",[941,182867,22582],{},"\ncollection has no label for a key.",[1114,182870,182871,953,182873,182875,182876,182878,182879,182881],{},[941,182872,3926],{},[950,182874,864],{},") surfaces inline labels into ",[941,182877,146900],{},"\nwith zero store I\u002FO — equivalent to ",[941,182880,162565],{}," for display purposes.",[1114,182883,182884,953,182887,182889,182890,182892],{},[941,182885,182886],{},"describe({ resolveDictLabels: true })",[950,182888,70491],{},") overrides from ",[941,182891,22582],{},"\nand falls back to the inline label.",[1114,182894,182895,1324,182897,182899,182900,1520],{},[941,182896,25486],{},[941,182898,182597],{}," picks them up via ",[941,182901,3926],{},[937,182903,182904,182905,182907,182908,182910,182911,182913,182914,182916],{},"This keeps ",[941,182906,162565],{}," (closed code labels, no ",[941,182909,22582],{},") and\n",[941,182912,21456],{}," + inline labels (dynamic dict ",[1092,182915,5801],{}," code defaults) as distinct,\nnon-overlapping tools.",[104892,182918],{},[1022,182920,182922],{"id":182921},"devtools-pii-masking","Devtools PII masking",[937,182924,182925,182926,953,182929,182931,182932,182935,182936,997,182938,182940],{},"The terminal inspector's ",[941,182927,182928],{},"RecordsPane",[941,182930,36195],{},") masks\nvalues for fields where ",[941,182933,182934],{},"sensitivity !== 'public'"," (i.e. ",[941,182937,147379],{},[941,182939,10695],{},"),\nprotecting data during screen-sharing or shoulder surfing.",[937,182942,182943],{},[950,182944,182945],{},"Behavior:",[1111,182947,182948,182955,182961,182971],{},[1114,182949,182950,182951,182954],{},"Sensitive cells render as ",[941,182952,182953],{},"••••"," by default.",[1114,182956,182957,182958,182960],{},"Press ",[941,182959,13217],{}," to toggle reveal-all for the pane (a single pane-level toggle,\nnot per-field — terminal-appropriate).",[1114,182962,182963,182964,182967,182968,182970],{},"Public fields (",[941,182965,182966],{},"sensitivity: 'public'",") and unclassified fields (no\n",[941,182969,146769],{},") always render their raw values — they are never masked.",[1114,182972,182973,182976,182977,182979],{},[950,182974,182975],{},"Back-compat:"," a collection without ",[941,182978,150158],{}," (non-live or pre-enrichment)\nhas an empty sensitive-field set → nothing is masked → behavior is identical\nto before.",[937,182981,182982,182985],{},[950,182983,182984],{},"Collection-change reset (TUI):"," the reveal-all flag resets when the selected\ncollection index changes, so revealed PII does not linger when navigating to\nanother collection.",[937,182987,182988,182989,182991],{},"This is shoulder-surfing \u002F screen-share safety over already-decrypted local\ndata — it is ",[950,182990,1004],{}," access control. The data is already in the trusted tier.",[1022,182993,2621],{"id":2620},[1111,182995,182996],{},[1114,182997,182998,181825],{},[1007,182999,677],{"href":678},[2645,183001,183002],{},"html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}",{"title":1037,"searchDepth":1038,"depth":1038,"links":183004},[183005,183006,183008,183009],{"id":181850,"depth":1041,"text":181853},{"id":182660,"depth":1041,"text":183007},"dictKey inline labels",{"id":182921,"depth":1041,"text":182922},{"id":2620,"depth":1041,"text":2621},"Three riders on the describe() contract — toJSONSchema() x- extension keys, dictKey inline labels, and devtools PII masking.",{},{"title":747,"description":183010},"TfKCaVI9H4U4-Ky6PH4oJ1e-9Nky4oLbnijpe6jfxgs",{"id":183015,"title":751,"access":12,"apiToc":929,"body":183016,"category":1042,"description":183048,"extension":1044,"meta":183049,"navigation":183050,"nest":1047,"path":752,"seo":183051,"sinceVersion":1042,"status":183052,"stem":753,"subtitle":1042,"tier":1050,"tocExternal":1042,"__hash__":183053},"docs\u002Fservices\u002Fmetrics.md",{"type":931,"value":183017,"toc":183045},[183018,183020,183023,183035,183037],[934,183019,751],{"id":751},[46871,183021],{":rows":183022},"[{\"label\":\"Subpath (reserved, not yet implemented)\",\"value\":\"`@noy-db\u002Fhub\u002Fmetrics`\"},{\"label\":\"Factory\",\"value\":\"none yet\"},{\"label\":\"Layer\",\"value\":\"not yet assigned (reserved slot)\"}]",[937,183024,183025,183026,183029,183030,183034],{},"Metrics is a reserved service name for hub-level observability — operation\ntimings, cache statistics, and sync statistics reported by the vault itself.\nIt does not exist yet: there is no factory and no ",[941,183027,183028],{},"@noy-db\u002Fhub\u002Fmetrics","\nsubpath export. What exists today is store-level metering via the\n",[1007,183031,183032],{"href":396},[941,183033,5125],{}," wrapper, which counts and times the\nenvelope operations a store receives — a narrower, storage-side view of\nthe same question.",[1022,183036,2621],{"id":2620},[1111,183038,183039],{},[1114,183040,183041,1368,183043],{},[1007,183042,61743],{"href":598},[1007,183044,1019],{"href":1018},{"title":1037,"searchDepth":1038,"depth":1038,"links":183046},[183047],{"id":2620,"depth":1041,"text":2621},"Hub-level observability (timings, cache stats, sync stats). Today partial via the `to-meter` store wrapper.",{},{"badge":754},{"title":751,"description":183048},"planned","gj1__IT468kovOdvEihhs809dN-5EzKNw1ufKtvLet0",{"id":183055,"title":756,"access":12,"apiToc":929,"body":183056,"category":1042,"description":183085,"extension":1044,"meta":183086,"navigation":183087,"nest":1047,"path":757,"seo":183088,"sinceVersion":1042,"status":183052,"stem":758,"subtitle":1042,"tier":1050,"tocExternal":1042,"__hash__":183089},"docs\u002Fservices\u002Fmigrations.md",{"type":931,"value":183057,"toc":183082},[183058,183060,183063,183072,183074],[934,183059,756],{"id":756},[46871,183061],{":rows":183062},"[{\"label\":\"Subpath (reserved, not yet implemented)\",\"value\":\"`@noy-db\u002Fhub\u002Fmigrations`\"},{\"label\":\"Factory\",\"value\":\"none yet\"},{\"label\":\"Layer\",\"value\":\"not yet assigned (reserved slot)\"}]",[937,183064,183065,183066,183068,183069,183071],{},"Migrations is a reserved service name for schema evolution — migrating\ncollections between hub versions, renaming collections, and renaming\nfields with a backfill. It does not exist yet: there is no factory and no\n",[941,183067,72807],{}," subpath export. Today, schema changes are\nadditive-only in practice — see ",[1007,183070,23934],{"href":112}," for what the core\nschema layer does and does not cover.",[1022,183073,2621],{"id":2620},[1111,183075,183076],{},[1114,183077,183078,1368,183080],{},[1007,183079,61743],{"href":598},[1007,183081,1019],{"href":1018},{"title":1037,"searchDepth":1038,"depth":1038,"links":183083},[183084],{"id":2620,"depth":1041,"text":2621},"Schema migrations between hub versions \u002F collection renames \u002F field rename + backfill.",{},{"badge":754},{"title":756,"description":183085},"SqivgrOkZpfrCTjKeM3reEsAyC_JvVZAhEWAJJ4sgU4",{"id":183091,"title":760,"access":12,"apiToc":929,"body":183092,"category":1042,"description":185929,"extension":1044,"meta":185930,"navigation":1042,"nest":1047,"path":761,"seo":185931,"sinceVersion":185932,"status":1049,"stem":762,"subtitle":1042,"tier":1050,"tocExternal":1042,"__hash__":185933},"docs\u002Fservices\u002Fmoney.md",{"type":931,"value":183093,"toc":185902},[183094,183096,183099,183101,183125,183136,183156,183158,183170,183523,183543,183549,183575,183577,183581,183594,183743,183763,183769,183775,183872,183878,183884,183981,183985,183996,184055,184093,184102,184120,184511,184534,184559,184563,184572,184948,184987,184989,184994,184999,185029,185050,185119,185188,185196,185221,185227,185256,185261,185266,185315,185364,185369,185424,185455,185462,185468,185496,185523,185535,185616,185643,185645,185652,185709,185711,185851,185853,185899],[934,183095,760],{"id":760},[46871,183097],{":rows":183098},"[{\"label\":\"Subpath\",\"value\":\"`@noy-db\u002Fhub` (root export)\"},{\"label\":\"Type\",\"value\":\"Core feature — activated per field by declaring a `money()` field descriptor.\"}]",[1022,183100,29857],{"id":29856},[937,183102,183103,183104,183106,183107,183110,183111,183113,183114,13571,183117,183120,183121,183124],{},"Storing prices, balances, or totals as a JavaScript ",[941,183105,20636],{}," is a well-known trap: ",[941,183108,183109],{},"0.1 + 0.2"," doesn't equal ",[941,183112,10308],{},", and floats lose precision entirely past ",[941,183115,183116],{},"Number.MAX_SAFE_INTEGER",[941,183118,183119],{},"money()"," field descriptor closes that gap. It marks a schema field as currency-safe: values are quantized to a scaled integer (cents, for a 2-decimal currency) and stored as a digit string, so arithmetic never touches floating point and stays exact past 2^53. On read, the field decodes back to an exact decimal string (",[941,183122,183123],{},"'123.45'","), with optional locale-formatted and plain-number sibling fields for display.",[937,183126,183127,183128,183131,183132,183135],{},"It also ships two arithmetic helpers that solve the two places floating-point money math typically breaks: ",[941,183129,183130],{},"mulRate()"," (apply a rate — VAT, a fee percentage — with one controlled rounding step) and ",[941,183133,183134],{},"allocate()"," (split an amount across buckets so the parts always sum back to the original, to the cent).",[937,183137,183138,183140,183141,183145,183146,1625,183148,183150,183151,183153,183154,1520],{},[941,183139,183119],{}," is a sibling of the ",[1007,183142,183143],{"href":124720},[941,183144,701],{}," service's ",[941,183147,58348],{},[941,183149,58358],{}," field descriptors: a schema-layer declaration, not a ",[941,183152,943],{}," strategy passed to ",[941,183155,2134],{},[1022,183157,58394],{"id":58393},[937,183159,183160,183161,183163,183164,183166,183167,183169],{},"Import ",[941,183162,760],{}," and declare it in a collection's ",[941,183165,22765],{}," map — the field's underlying schema type stays a plain ",[941,183168,171677],{}," union (money accepts either on write):",[2217,183171,183173],{"className":2219,"code":183172,"language":2221,"meta":1037,"style":1037},"import { createNoydb, money } from '@noy-db\u002Fhub'\nimport { toMemory } from '@noy-db\u002Fto-memory'\nimport { z } from 'zod'\n\nconst db = await createNoydb({\n  store: toMemory(),\n  user: 'alice',\n  secret: 'a-strong-secret',\n})\n\ninterface Invoice extends Record\u003Cstring, unknown> {\n  id: string\n  total: number | string\n}\n\nconst vault = await db.openVault('books')\nconst invoices = vault.collection\u003CInvoice>('invoices', {\n  schema: z.object({ id: z.string(), total: z.union([z.number(), z.string()]) }),\n  moneyFields: { total: money({ currency: 'EUR', scale: 2 }) },\n})\n",[941,183174,183175,183197,183215,183233,183237,183253,183265,183279,183293,183299,183303,183325,183333,183345,183349,183353,183379,183411,183475,183517],{"__ignoreMap":1037},[2225,183176,183177,183179,183181,183183,183185,183187,183189,183191,183193,183195],{"class":2227,"line":2228},[2225,183178,2232],{"class":2231},[2225,183180,2236],{"class":2235},[2225,183182,2744],{"class":2239},[2225,183184,2298],{"class":2235},[2225,183186,123521],{"class":2239},[2225,183188,2243],{"class":2235},[2225,183190,2246],{"class":2231},[2225,183192,2249],{"class":2235},[2225,183194,1086],{"class":2252},[2225,183196,2256],{"class":2235},[2225,183198,183199,183201,183203,183205,183207,183209,183211,183213],{"class":2227,"line":1041},[2225,183200,2232],{"class":2231},[2225,183202,2236],{"class":2235},[2225,183204,68576],{"class":2239},[2225,183206,2243],{"class":2235},[2225,183208,2246],{"class":2231},[2225,183210,2249],{"class":2235},[2225,183212,9638],{"class":2252},[2225,183214,2256],{"class":2235},[2225,183216,183217,183219,183221,183223,183225,183227,183229,183231],{"class":2227,"line":1038},[2225,183218,2232],{"class":2231},[2225,183220,2236],{"class":2235},[2225,183222,28314],{"class":2239},[2225,183224,2243],{"class":2235},[2225,183226,2246],{"class":2231},[2225,183228,2249],{"class":2235},[2225,183230,28323],{"class":2252},[2225,183232,2256],{"class":2235},[2225,183234,183235],{"class":2227,"line":2288},[2225,183236,2285],{"emptyLinePlaceholder":2284},[2225,183238,183239,183241,183243,183245,183247,183249,183251],{"class":2227,"line":2304},[2225,183240,2775],{"class":2774},[2225,183242,2778],{"class":2239},[2225,183244,2781],{"class":2235},[2225,183246,2784],{"class":2231},[2225,183248,2744],{"class":2291},[2225,183250,2789],{"class":2239},[2225,183252,4200],{"class":2235},[2225,183254,183255,183257,183259,183261,183263],{"class":2227,"line":2356},[2225,183256,15608],{"class":2307},[2225,183258,2311],{"class":2235},[2225,183260,68576],{"class":2291},[2225,183262,4228],{"class":2239},[2225,183264,1707],{"class":2235},[2225,183266,183267,183269,183271,183273,183275,183277],{"class":2227,"line":2830},[2225,183268,4205],{"class":2307},[2225,183270,2311],{"class":2235},[2225,183272,2249],{"class":2235},[2225,183274,6408],{"class":2252},[2225,183276,2805],{"class":2235},[2225,183278,1707],{"class":2235},[2225,183280,183281,183283,183285,183287,183289,183291],{"class":2227,"line":2861},[2225,183282,10423],{"class":2307},[2225,183284,2311],{"class":2235},[2225,183286,2249],{"class":2235},[2225,183288,6421],{"class":2252},[2225,183290,2805],{"class":2235},[2225,183292,1707],{"class":2235},[2225,183294,183295,183297],{"class":2227,"line":4246},[2225,183296,2359],{"class":2235},[2225,183298,2362],{"class":2239},[2225,183300,183301],{"class":2227,"line":4253},[2225,183302,2285],{"emptyLinePlaceholder":2284},[2225,183304,183305,183307,183309,183311,183313,183315,183317,183319,183321,183323],{"class":2227,"line":4259},[2225,183306,6994],{"class":2774},[2225,183308,6343],{"class":6342},[2225,183310,124350],{"class":2774},[2225,183312,22527],{"class":6342},[2225,183314,6474],{"class":2235},[2225,183316,7259],{"class":6342},[2225,183318,2298],{"class":2235},[2225,183320,40717],{"class":6342},[2225,183322,6480],{"class":2235},[2225,183324,2301],{"class":2235},[2225,183326,183327,183329,183331],{"class":2227,"line":6548},[2225,183328,28371],{"class":2307},[2225,183330,2311],{"class":2235},[2225,183332,7014],{"class":6342},[2225,183334,183335,183337,183339,183341,183343],{"class":2227,"line":6576},[2225,183336,121085],{"class":2307},[2225,183338,2311],{"class":2235},[2225,183340,6356],{"class":6342},[2225,183342,7087],{"class":2235},[2225,183344,7014],{"class":6342},[2225,183346,183347],{"class":2227,"line":6582},[2225,183348,7351],{"class":2235},[2225,183350,183351],{"class":2227,"line":6587},[2225,183352,2285],{"emptyLinePlaceholder":2284},[2225,183354,183355,183357,183359,183361,183363,183365,183367,183369,183371,183373,183375,183377],{"class":2227,"line":7174},[2225,183356,2775],{"class":2774},[2225,183358,2835],{"class":2239},[2225,183360,2781],{"class":2235},[2225,183362,2784],{"class":2231},[2225,183364,2842],{"class":2239},[2225,183366,1520],{"class":2235},[2225,183368,2847],{"class":2291},[2225,183370,2789],{"class":2239},[2225,183372,2805],{"class":2235},[2225,183374,65761],{"class":2252},[2225,183376,2805],{"class":2235},[2225,183378,2362],{"class":2239},[2225,183380,183381,183383,183385,183387,183389,183391,183393,183395,183397,183399,183401,183403,183405,183407,183409],{"class":2227,"line":7180},[2225,183382,2775],{"class":2774},[2225,183384,6461],{"class":2239},[2225,183386,2781],{"class":2235},[2225,183388,6466],{"class":2239},[2225,183390,1520],{"class":2235},[2225,183392,6471],{"class":2291},[2225,183394,6474],{"class":2235},[2225,183396,6477],{"class":6342},[2225,183398,6480],{"class":2235},[2225,183400,2789],{"class":2239},[2225,183402,2805],{"class":2235},[2225,183404,6487],{"class":2252},[2225,183406,2805],{"class":2235},[2225,183408,2298],{"class":2235},[2225,183410,2301],{"class":2235},[2225,183412,183413,183415,183417,183419,183421,183423,183425,183427,183429,183431,183433,183435,183437,183439,183441,183443,183445,183447,183449,183451,183453,183455,183457,183459,183461,183463,183465,183467,183469,183471,183473],{"class":2227,"line":7220},[2225,183414,28618],{"class":2307},[2225,183416,2311],{"class":2235},[2225,183418,28314],{"class":2239},[2225,183420,1520],{"class":2235},[2225,183422,25505],{"class":2291},[2225,183424,2789],{"class":2239},[2225,183426,2792],{"class":2235},[2225,183428,7070],{"class":2307},[2225,183430,2311],{"class":2235},[2225,183432,28314],{"class":2239},[2225,183434,1520],{"class":2235},[2225,183436,7259],{"class":2291},[2225,183438,4228],{"class":2239},[2225,183440,2298],{"class":2235},[2225,183442,27452],{"class":2307},[2225,183444,2311],{"class":2235},[2225,183446,28314],{"class":2239},[2225,183448,1520],{"class":2235},[2225,183450,123641],{"class":2291},[2225,183452,123644],{"class":2239},[2225,183454,1520],{"class":2235},[2225,183456,20636],{"class":2291},[2225,183458,4228],{"class":2239},[2225,183460,2298],{"class":2235},[2225,183462,28314],{"class":2239},[2225,183464,1520],{"class":2235},[2225,183466,7259],{"class":2291},[2225,183468,8978],{"class":2239},[2225,183470,2359],{"class":2235},[2225,183472,1381],{"class":2239},[2225,183474,1707],{"class":2235},[2225,183476,183477,183479,183481,183483,183485,183487,183489,183491,183493,183495,183497,183499,183501,183503,183505,183507,183509,183511,183513,183515],{"class":2227,"line":7225},[2225,183478,123730],{"class":2307},[2225,183480,2311],{"class":2235},[2225,183482,2236],{"class":2235},[2225,183484,27452],{"class":2307},[2225,183486,2311],{"class":2235},[2225,183488,123521],{"class":2291},[2225,183490,2789],{"class":2239},[2225,183492,2792],{"class":2235},[2225,183494,122489],{"class":2307},[2225,183496,2311],{"class":2235},[2225,183498,2249],{"class":2235},[2225,183500,122902],{"class":2252},[2225,183502,2805],{"class":2235},[2225,183504,2298],{"class":2235},[2225,183506,122909],{"class":2307},[2225,183508,2311],{"class":2235},[2225,183510,18961],{"class":6527},[2225,183512,2243],{"class":2235},[2225,183514,8495],{"class":2239},[2225,183516,19008],{"class":2235},[2225,183518,183519,183521],{"class":2227,"line":7231},[2225,183520,2359],{"class":2235},[2225,183522,2362],{"class":2239},[937,183524,10993,183525,183527,183528,183530,183531,183534,183535,183537,183538,183540,183541,1520],{},[941,183526,111464],{},", the same declaration can be spelled\nthrough the ",[1007,183529,896],{"href":897}," field-feature composer — ",[941,183532,183533],{},"viaFields: { total: via(money({ currency: 'EUR', scale: 2 })) }",". Both spellings compile to the same internal\nbinding, with byte-identical stored envelopes and identical ",[941,183536,3926],{}," output; ",[941,183539,22765],{},"\nremains fully supported. Declaring the same field under both spellings throws ",[941,183542,12881],{},[937,183544,183545,183548],{},[941,183546,183547],{},"money(options)"," has two mutually exclusive modes:",[1111,183550,183551,183560],{},[1114,183552,183553,964,183556,183559],{},[950,183554,183555],{},"Fixed",[941,183557,183558],{},"money({ currency: 'EUR' })",": the field always holds one currency. The stored\u002Fread value is a bare amount.",[1114,183561,183562,964,183565,36059,183568,183571,183572,183574],{},[950,183563,183564],{},"Multi",[941,183566,183567],{},"money({ currencies: ['EUR', 'USD'] })",[941,183569,183570],{},"currencies: 'any'","): the currency travels with the record. The value is ",[941,183573,123170],{}," on write; reads decode the same shape.",[1022,183576,29893],{"id":29892},[1591,183578,183580],{"id":183579},"write-and-read-exact-decimals-locale-formatting","Write and read — exact decimals, locale formatting",[937,183582,183583,183584,997,183586,183588,183589,183591,183592,2311],{},"Writing accepts a plain number or a decimal string; reading returns the canonical decimal string, plus ",[941,183585,122735],{},[941,183587,123437],{}," virtuals when a ",[941,183590,23074],{}," is passed to ",[941,183593,14639],{},[2217,183595,183597],{"className":2219,"code":183596,"language":2221,"meta":1037,"style":1037},"await invoices.put('a', { id: 'a', total: 123.45 })\n\nconst read = await invoices.get('a', { locale: 'de-DE' }) as Record\u003Cstring, unknown>\n\u002F\u002F → {\n\u002F\u002F   id: 'a',\n\u002F\u002F   total: '123.45',            \u002F\u002F exact decimal string\n\u002F\u002F   totalFormatted: '123,45 €', \u002F\u002F Intl.NumberFormat at the given locale\n\u002F\u002F   totalNumber: 123.45,        \u002F\u002F convenience JS number (lossy past 2^53 — display only)\n\u002F\u002F }\n",[941,183598,183599,183644,183648,183706,183710,183715,183723,183731,183739],{"__ignoreMap":1037},[2225,183600,183601,183603,183605,183607,183609,183611,183613,183615,183617,183619,183621,183623,183625,183627,183629,183631,183633,183635,183637,183640,183642],{"class":2227,"line":2228},[2225,183602,6500],{"class":2231},[2225,183604,6503],{"class":2239},[2225,183606,1520],{"class":2235},[2225,183608,3161],{"class":2291},[2225,183610,2789],{"class":2239},[2225,183612,2805],{"class":2235},[2225,183614,1007],{"class":2252},[2225,183616,2805],{"class":2235},[2225,183618,2298],{"class":2235},[2225,183620,2236],{"class":2235},[2225,183622,7070],{"class":2307},[2225,183624,2311],{"class":2235},[2225,183626,2249],{"class":2235},[2225,183628,1007],{"class":2252},[2225,183630,2805],{"class":2235},[2225,183632,2298],{"class":2235},[2225,183634,27452],{"class":2307},[2225,183636,2311],{"class":2235},[2225,183638,183639],{"class":6527}," 123.45",[2225,183641,2243],{"class":2235},[2225,183643,2362],{"class":2239},[2225,183645,183646],{"class":2227,"line":1041},[2225,183647,2285],{"emptyLinePlaceholder":2284},[2225,183649,183650,183652,183655,183657,183659,183661,183663,183665,183667,183669,183671,183673,183675,183677,183679,183681,183683,183686,183688,183690,183692,183694,183696,183698,183700,183702,183704],{"class":2227,"line":1038},[2225,183651,2775],{"class":2774},[2225,183653,183654],{"class":2239}," read ",[2225,183656,2781],{"class":2235},[2225,183658,2784],{"class":2231},[2225,183660,6503],{"class":2239},[2225,183662,1520],{"class":2235},[2225,183664,3158],{"class":2291},[2225,183666,2789],{"class":2239},[2225,183668,2805],{"class":2235},[2225,183670,1007],{"class":2252},[2225,183672,2805],{"class":2235},[2225,183674,2298],{"class":2235},[2225,183676,2236],{"class":2235},[2225,183678,24003],{"class":2307},[2225,183680,2311],{"class":2235},[2225,183682,2249],{"class":2235},[2225,183684,183685],{"class":2252},"de-DE",[2225,183687,2805],{"class":2235},[2225,183689,2243],{"class":2235},[2225,183691,8495],{"class":2239},[2225,183693,4899],{"class":2231},[2225,183695,22527],{"class":6342},[2225,183697,6474],{"class":2235},[2225,183699,7259],{"class":6342},[2225,183701,2298],{"class":2235},[2225,183703,40717],{"class":6342},[2225,183705,7093],{"class":2235},[2225,183707,183708],{"class":2227,"line":2288},[2225,183709,164681],{"class":2763},[2225,183711,183712],{"class":2227,"line":2304},[2225,183713,183714],{"class":2763},"\u002F\u002F   id: 'a',\n",[2225,183716,183717,183720],{"class":2227,"line":2356},[2225,183718,183719],{"class":2763},"\u002F\u002F   total: '123.45',",[2225,183721,183722],{"class":2763},"            \u002F\u002F exact decimal string\n",[2225,183724,183725,183728],{"class":2227,"line":2830},[2225,183726,183727],{"class":2763},"\u002F\u002F   totalFormatted: '123,45 €',",[2225,183729,183730],{"class":2763}," \u002F\u002F Intl.NumberFormat at the given locale\n",[2225,183732,183733,183736],{"class":2227,"line":2861},[2225,183734,183735],{"class":2763},"\u002F\u002F   totalNumber: 123.45,",[2225,183737,183738],{"class":2763},"        \u002F\u002F convenience JS number (lossy past 2^53 — display only)\n",[2225,183740,183741],{"class":2227,"line":4246},[2225,183742,177678],{"class":2763},[937,183744,183745,183746,45416,183748,1625,183750,183752,183753,183755,183756,1625,183759,183762],{},"Without a ",[941,183747,23074],{},[941,183749,14639],{},[941,183751,24407],{}," still return the exact decimal string for ",[941,183754,64200],{}," — only the ",[941,183757,183758],{},"Formatted",[941,183760,183761],{},"Number"," siblings are omitted.",[1591,183764,183766,183768],{"id":183765},"mulrate-apply-a-rate-exactly",[941,183767,183130],{}," — apply a rate exactly",[937,183770,183771,183774],{},[941,183772,183773],{},"mulRate(amount, rate, opts?)"," multiplies a money amount by a decimal rate (e.g. a VAT rate) with a single controlled rounding step at the end, instead of the repeated float rounding that produces off-by-a-cent totals:",[2217,183776,183778],{"className":2219,"code":183777,"language":2221,"meta":1037,"style":1037},"import { mulRate } from '@noy-db\u002Fhub'\n\nmulRate('10000.00', 0.07)                     \u002F\u002F → '700.00'   (7% VAT)\nmulRate('33.33', '0.07', { rounding: 'floor' }) \u002F\u002F → '2.33'\n",[941,183779,183780,183799,183803,183828],{"__ignoreMap":1037},[2225,183781,183782,183784,183786,183789,183791,183793,183795,183797],{"class":2227,"line":2228},[2225,183783,2232],{"class":2231},[2225,183785,2236],{"class":2235},[2225,183787,183788],{"class":2239}," mulRate",[2225,183790,2243],{"class":2235},[2225,183792,2246],{"class":2231},[2225,183794,2249],{"class":2235},[2225,183796,1086],{"class":2252},[2225,183798,2256],{"class":2235},[2225,183800,183801],{"class":2227,"line":1041},[2225,183802,2285],{"emptyLinePlaceholder":2284},[2225,183804,183805,183808,183810,183812,183815,183817,183819,183822,183825],{"class":2227,"line":1038},[2225,183806,183807],{"class":2291},"mulRate",[2225,183809,2789],{"class":2239},[2225,183811,2805],{"class":2235},[2225,183813,183814],{"class":2252},"10000.00",[2225,183816,2805],{"class":2235},[2225,183818,2298],{"class":2235},[2225,183820,183821],{"class":6527}," 0.07",[2225,183823,183824],{"class":2239},")                     ",[2225,183826,183827],{"class":2763},"\u002F\u002F → '700.00'   (7% VAT)\n",[2225,183829,183830,183832,183834,183836,183839,183841,183843,183845,183848,183850,183852,183854,183857,183859,183861,183863,183865,183867,183869],{"class":2227,"line":2288},[2225,183831,183807],{"class":2291},[2225,183833,2789],{"class":2239},[2225,183835,2805],{"class":2235},[2225,183837,183838],{"class":2252},"33.33",[2225,183840,2805],{"class":2235},[2225,183842,2298],{"class":2235},[2225,183844,2249],{"class":2235},[2225,183846,183847],{"class":2252},"0.07",[2225,183849,2805],{"class":2235},[2225,183851,2298],{"class":2235},[2225,183853,2236],{"class":2235},[2225,183855,183856],{"class":2307}," rounding",[2225,183858,2311],{"class":2235},[2225,183860,2249],{"class":2235},[2225,183862,17304],{"class":2252},[2225,183864,2805],{"class":2235},[2225,183866,2243],{"class":2235},[2225,183868,8495],{"class":2239},[2225,183870,183871],{"class":2763},"\u002F\u002F → '2.33'\n",[1591,183873,183875,183877],{"id":183874},"allocate-split-with-zero-drift",[941,183876,183134],{}," — split with zero drift",[937,183879,183880,183883],{},[941,183881,183882],{},"allocate(amount, weights, opts?)"," splits an amount across weighted buckets using the largest-remainder method: the returned parts always sum back to the input exactly, with no leftover cent unaccounted for:",[2217,183885,183887],{"className":2219,"code":183886,"language":2221,"meta":1037,"style":1037},"import { allocate } from '@noy-db\u002Fhub'\n\nallocate('100.00', [1, 1, 1])\n\u002F\u002F → ['33.34', '33.33', '33.33']   (sums to '100.00')\n\nallocate('0.05', [3, 7])\n\u002F\u002F → ['0.02', '0.03']\n",[941,183888,183889,183908,183912,183942,183947,183951,183976],{"__ignoreMap":1037},[2225,183890,183891,183893,183895,183898,183900,183902,183904,183906],{"class":2227,"line":2228},[2225,183892,2232],{"class":2231},[2225,183894,2236],{"class":2235},[2225,183896,183897],{"class":2239}," allocate",[2225,183899,2243],{"class":2235},[2225,183901,2246],{"class":2231},[2225,183903,2249],{"class":2235},[2225,183905,1086],{"class":2252},[2225,183907,2256],{"class":2235},[2225,183909,183910],{"class":2227,"line":1041},[2225,183911,2285],{"emptyLinePlaceholder":2284},[2225,183913,183914,183917,183919,183921,183924,183926,183928,183930,183932,183934,183936,183938,183940],{"class":2227,"line":1038},[2225,183915,183916],{"class":2291},"allocate",[2225,183918,2789],{"class":2239},[2225,183920,2805],{"class":2235},[2225,183922,183923],{"class":2252},"100.00",[2225,183925,2805],{"class":2235},[2225,183927,2298],{"class":2235},[2225,183929,8667],{"class":2239},[2225,183931,5437],{"class":6527},[2225,183933,2298],{"class":2235},[2225,183935,9377],{"class":6527},[2225,183937,2298],{"class":2235},[2225,183939,9377],{"class":6527},[2225,183941,10880],{"class":2239},[2225,183943,183944],{"class":2227,"line":2288},[2225,183945,183946],{"class":2763},"\u002F\u002F → ['33.34', '33.33', '33.33']   (sums to '100.00')\n",[2225,183948,183949],{"class":2227,"line":2304},[2225,183950,2285],{"emptyLinePlaceholder":2284},[2225,183952,183953,183955,183957,183959,183962,183964,183966,183968,183970,183972,183974],{"class":2227,"line":2356},[2225,183954,183916],{"class":2291},[2225,183956,2789],{"class":2239},[2225,183958,2805],{"class":2235},[2225,183960,183961],{"class":2252},"0.05",[2225,183963,2805],{"class":2235},[2225,183965,2298],{"class":2235},[2225,183967,8667],{"class":2239},[2225,183969,5512],{"class":6527},[2225,183971,2298],{"class":2235},[2225,183973,44442],{"class":6527},[2225,183975,10880],{"class":2239},[2225,183977,183978],{"class":2227,"line":2830},[2225,183979,183980],{"class":2763},"\u002F\u002F → ['0.02', '0.03']\n",[1591,183982,183984],{"id":183983},"querying-and-summing-a-money-field","Querying and summing a money field",[937,183986,183987,183989,183990,968,183992,183995],{},[941,183988,65223],{}," on a declared money field accepts operands in major units (",[941,183991,24908],{},[941,183993,183994],{},"'100.00'",") and compares exactly in scaled-integer space — no manual cents conversion needed:",[2217,183997,183999],{"className":2219,"code":183998,"language":2221,"meta":1037,"style":1037},"const over100 = invoices.query().where('total', '>', 100).toArray()\n\u002F\u002F → [ { id: 'a', total: '123.45' } ]   (the invoice put earlier, 123.45 > 100)\n",[941,184000,184001,184050],{"__ignoreMap":1037},[2225,184002,184003,184005,184008,184010,184012,184014,184016,184018,184020,184022,184024,184026,184028,184030,184032,184034,184036,184038,184040,184042,184044,184046,184048],{"class":2227,"line":2228},[2225,184004,2775],{"class":2774},[2225,184006,184007],{"class":2239}," over100 ",[2225,184009,2781],{"class":2235},[2225,184011,6503],{"class":2239},[2225,184013,1520],{"class":2235},[2225,184015,5286],{"class":2291},[2225,184017,4228],{"class":2239},[2225,184019,1520],{"class":2235},[2225,184021,3016],{"class":2291},[2225,184023,2789],{"class":2239},[2225,184025,2805],{"class":2235},[2225,184027,64200],{"class":2252},[2225,184029,2805],{"class":2235},[2225,184031,2298],{"class":2235},[2225,184033,2249],{"class":2235},[2225,184035,6480],{"class":2252},[2225,184037,2805],{"class":2235},[2225,184039,2298],{"class":2235},[2225,184041,6528],{"class":6527},[2225,184043,1381],{"class":2239},[2225,184045,1520],{"class":2235},[2225,184047,3230],{"class":2291},[2225,184049,8146],{"class":2239},[2225,184051,184052],{"class":2227,"line":1041},[2225,184053,184054],{"class":2763},"\u002F\u002F → [ { id: 'a', total: '123.45' } ]   (the invoice put earlier, 123.45 > 100)\n",[937,184056,184057,184058,184060,184061,10313,184063,184065,184066,1625,184069,1625,184072,184075,184076,1625,184078,1625,184080,184082,184083,184085,184086,184088,184089,1520],{},"Summing, averaging, or taking the min\u002Fmax of a money field is part of the separate ",[1007,184059,4449],{"href":123485}," service: pass ",[941,184062,21926],{},[941,184064,12780],{},", then use ",[941,184067,184068],{},"moneySum",[941,184070,184071],{},"moneyMin",[941,184073,184074],{},"moneyMax"," (or plain ",[941,184077,28196],{},[941,184079,28204],{},[941,184081,28207],{},", which are auto-rewritten to the exact ",[941,184084,180192],{}," reducer for any field declared in ",[941,184087,22765],{},") — see ",[1007,184090,184092],{"href":184091},".\u002Freduce#reducer-factories","aggregate → exact money aggregation",[1591,184094,184096,184097,1625,184099,184101],{"id":184095},"indexing-a-money-field-for-in-lookups","Indexing a money field for ",[941,184098,21901],{},[941,184100,4891],{}," lookups",[937,184103,184104,184105,184107,184108,184110,184111,997,184114,56825,184117,59639],{},"If you add the ",[1007,184106,705],{"href":139528}," service and declare a money field in ",[941,184109,21907],{},", an equality or membership query on that field skips the linear scan and resolves through the index. The fast path covers ",[941,184112,184113],{},"where(field, '==', …)",[941,184115,184116],{},"where(field, 'in', […])",[950,184118,184119],{},"fixed-mode",[2217,184121,184123],{"className":2219,"code":184122,"language":2221,"meta":1037,"style":1037},"import { withIndexing } from '@noy-db\u002Fhub\u002Findexing'\n\nconst db = await createNoydb({\n  store: toMemory(),\n  user: 'alice',\n  secret: 'a-strong-secret',\n  indexingStrategy: withIndexing(),\n})\nconst vault = await db.openVault('books')\nconst invoices = vault.collection\u003CInvoice>('invoices', {\n  schema: z.object({ id: z.string(), total: z.union([z.number(), z.string()]) }),\n  moneyFields: { total: money({ currency: 'EUR', scale: 2 }) },\n  indexes: ['total'],\n})\n\nawait invoices.put('a', { id: 'a', total: 123.45 })\nconst exact = invoices.query().where('total', '==', 123.45).toArray()\n\u002F\u002F → [ { id: 'a', total: '123.45' } ]   (index lookup, no scan)\n",[941,184124,184125,184143,184147,184163,184175,184189,184203,184215,184221,184247,184279,184343,184385,184403,184409,184413,184457,184506],{"__ignoreMap":1037},[2225,184126,184127,184129,184131,184133,184135,184137,184139,184141],{"class":2227,"line":2228},[2225,184128,2232],{"class":2231},[2225,184130,2236],{"class":2235},[2225,184132,4165],{"class":2239},[2225,184134,2243],{"class":2235},[2225,184136,2246],{"class":2231},[2225,184138,2249],{"class":2235},[2225,184140,4174],{"class":2252},[2225,184142,2256],{"class":2235},[2225,184144,184145],{"class":2227,"line":1041},[2225,184146,2285],{"emptyLinePlaceholder":2284},[2225,184148,184149,184151,184153,184155,184157,184159,184161],{"class":2227,"line":1038},[2225,184150,2775],{"class":2774},[2225,184152,2778],{"class":2239},[2225,184154,2781],{"class":2235},[2225,184156,2784],{"class":2231},[2225,184158,2744],{"class":2291},[2225,184160,2789],{"class":2239},[2225,184162,4200],{"class":2235},[2225,184164,184165,184167,184169,184171,184173],{"class":2227,"line":2288},[2225,184166,15608],{"class":2307},[2225,184168,2311],{"class":2235},[2225,184170,68576],{"class":2291},[2225,184172,4228],{"class":2239},[2225,184174,1707],{"class":2235},[2225,184176,184177,184179,184181,184183,184185,184187],{"class":2227,"line":2304},[2225,184178,4205],{"class":2307},[2225,184180,2311],{"class":2235},[2225,184182,2249],{"class":2235},[2225,184184,6408],{"class":2252},[2225,184186,2805],{"class":2235},[2225,184188,1707],{"class":2235},[2225,184190,184191,184193,184195,184197,184199,184201],{"class":2227,"line":2356},[2225,184192,10423],{"class":2307},[2225,184194,2311],{"class":2235},[2225,184196,2249],{"class":2235},[2225,184198,6421],{"class":2252},[2225,184200,2805],{"class":2235},[2225,184202,1707],{"class":2235},[2225,184204,184205,184207,184209,184211,184213],{"class":2227,"line":2830},[2225,184206,4235],{"class":2307},[2225,184208,2311],{"class":2235},[2225,184210,4165],{"class":2291},[2225,184212,4228],{"class":2239},[2225,184214,1707],{"class":2235},[2225,184216,184217,184219],{"class":2227,"line":2861},[2225,184218,2359],{"class":2235},[2225,184220,2362],{"class":2239},[2225,184222,184223,184225,184227,184229,184231,184233,184235,184237,184239,184241,184243,184245],{"class":2227,"line":4246},[2225,184224,2775],{"class":2774},[2225,184226,2835],{"class":2239},[2225,184228,2781],{"class":2235},[2225,184230,2784],{"class":2231},[2225,184232,2842],{"class":2239},[2225,184234,1520],{"class":2235},[2225,184236,2847],{"class":2291},[2225,184238,2789],{"class":2239},[2225,184240,2805],{"class":2235},[2225,184242,65761],{"class":2252},[2225,184244,2805],{"class":2235},[2225,184246,2362],{"class":2239},[2225,184248,184249,184251,184253,184255,184257,184259,184261,184263,184265,184267,184269,184271,184273,184275,184277],{"class":2227,"line":4253},[2225,184250,2775],{"class":2774},[2225,184252,6461],{"class":2239},[2225,184254,2781],{"class":2235},[2225,184256,6466],{"class":2239},[2225,184258,1520],{"class":2235},[2225,184260,6471],{"class":2291},[2225,184262,6474],{"class":2235},[2225,184264,6477],{"class":6342},[2225,184266,6480],{"class":2235},[2225,184268,2789],{"class":2239},[2225,184270,2805],{"class":2235},[2225,184272,6487],{"class":2252},[2225,184274,2805],{"class":2235},[2225,184276,2298],{"class":2235},[2225,184278,2301],{"class":2235},[2225,184280,184281,184283,184285,184287,184289,184291,184293,184295,184297,184299,184301,184303,184305,184307,184309,184311,184313,184315,184317,184319,184321,184323,184325,184327,184329,184331,184333,184335,184337,184339,184341],{"class":2227,"line":4259},[2225,184282,28618],{"class":2307},[2225,184284,2311],{"class":2235},[2225,184286,28314],{"class":2239},[2225,184288,1520],{"class":2235},[2225,184290,25505],{"class":2291},[2225,184292,2789],{"class":2239},[2225,184294,2792],{"class":2235},[2225,184296,7070],{"class":2307},[2225,184298,2311],{"class":2235},[2225,184300,28314],{"class":2239},[2225,184302,1520],{"class":2235},[2225,184304,7259],{"class":2291},[2225,184306,4228],{"class":2239},[2225,184308,2298],{"class":2235},[2225,184310,27452],{"class":2307},[2225,184312,2311],{"class":2235},[2225,184314,28314],{"class":2239},[2225,184316,1520],{"class":2235},[2225,184318,123641],{"class":2291},[2225,184320,123644],{"class":2239},[2225,184322,1520],{"class":2235},[2225,184324,20636],{"class":2291},[2225,184326,4228],{"class":2239},[2225,184328,2298],{"class":2235},[2225,184330,28314],{"class":2239},[2225,184332,1520],{"class":2235},[2225,184334,7259],{"class":2291},[2225,184336,8978],{"class":2239},[2225,184338,2359],{"class":2235},[2225,184340,1381],{"class":2239},[2225,184342,1707],{"class":2235},[2225,184344,184345,184347,184349,184351,184353,184355,184357,184359,184361,184363,184365,184367,184369,184371,184373,184375,184377,184379,184381,184383],{"class":2227,"line":6548},[2225,184346,123730],{"class":2307},[2225,184348,2311],{"class":2235},[2225,184350,2236],{"class":2235},[2225,184352,27452],{"class":2307},[2225,184354,2311],{"class":2235},[2225,184356,123521],{"class":2291},[2225,184358,2789],{"class":2239},[2225,184360,2792],{"class":2235},[2225,184362,122489],{"class":2307},[2225,184364,2311],{"class":2235},[2225,184366,2249],{"class":2235},[2225,184368,122902],{"class":2252},[2225,184370,2805],{"class":2235},[2225,184372,2298],{"class":2235},[2225,184374,122909],{"class":2307},[2225,184376,2311],{"class":2235},[2225,184378,18961],{"class":6527},[2225,184380,2243],{"class":2235},[2225,184382,8495],{"class":2239},[2225,184384,19008],{"class":2235},[2225,184386,184387,184389,184391,184393,184395,184397,184399,184401],{"class":2227,"line":6576},[2225,184388,62599],{"class":2307},[2225,184390,2311],{"class":2235},[2225,184392,8667],{"class":2239},[2225,184394,2805],{"class":2235},[2225,184396,64200],{"class":2252},[2225,184398,2805],{"class":2235},[2225,184400,8803],{"class":2239},[2225,184402,1707],{"class":2235},[2225,184404,184405,184407],{"class":2227,"line":6582},[2225,184406,2359],{"class":2235},[2225,184408,2362],{"class":2239},[2225,184410,184411],{"class":2227,"line":6587},[2225,184412,2285],{"emptyLinePlaceholder":2284},[2225,184414,184415,184417,184419,184421,184423,184425,184427,184429,184431,184433,184435,184437,184439,184441,184443,184445,184447,184449,184451,184453,184455],{"class":2227,"line":7174},[2225,184416,6500],{"class":2231},[2225,184418,6503],{"class":2239},[2225,184420,1520],{"class":2235},[2225,184422,3161],{"class":2291},[2225,184424,2789],{"class":2239},[2225,184426,2805],{"class":2235},[2225,184428,1007],{"class":2252},[2225,184430,2805],{"class":2235},[2225,184432,2298],{"class":2235},[2225,184434,2236],{"class":2235},[2225,184436,7070],{"class":2307},[2225,184438,2311],{"class":2235},[2225,184440,2249],{"class":2235},[2225,184442,1007],{"class":2252},[2225,184444,2805],{"class":2235},[2225,184446,2298],{"class":2235},[2225,184448,27452],{"class":2307},[2225,184450,2311],{"class":2235},[2225,184452,183639],{"class":6527},[2225,184454,2243],{"class":2235},[2225,184456,2362],{"class":2239},[2225,184458,184459,184461,184464,184466,184468,184470,184472,184474,184476,184478,184480,184482,184484,184486,184488,184490,184492,184494,184496,184498,184500,184502,184504],{"class":2227,"line":7180},[2225,184460,2775],{"class":2774},[2225,184462,184463],{"class":2239}," exact ",[2225,184465,2781],{"class":2235},[2225,184467,6503],{"class":2239},[2225,184469,1520],{"class":2235},[2225,184471,5286],{"class":2291},[2225,184473,4228],{"class":2239},[2225,184475,1520],{"class":2235},[2225,184477,3016],{"class":2291},[2225,184479,2789],{"class":2239},[2225,184481,2805],{"class":2235},[2225,184483,64200],{"class":2252},[2225,184485,2805],{"class":2235},[2225,184487,2298],{"class":2235},[2225,184489,2249],{"class":2235},[2225,184491,21901],{"class":2252},[2225,184493,2805],{"class":2235},[2225,184495,2298],{"class":2235},[2225,184497,183639],{"class":6527},[2225,184499,1381],{"class":2239},[2225,184501,1520],{"class":2235},[2225,184503,3230],{"class":2291},[2225,184505,8146],{"class":2239},[2225,184507,184508],{"class":2227,"line":7220},[2225,184509,184510],{"class":2763},"\u002F\u002F → [ { id: 'a', total: '123.45' } ]   (index lookup, no scan)\n",[937,184512,184513,184514,184517,184518,997,184520,184522,184523,968,184525,968,184527,184529,184530,184533],{},"The index is probed with the field's stored scaled-integer form, so an operand written in major units (",[941,184515,184516],{},"123.45",") matches the stored value without a floating-point round-trip. Only ",[941,184519,21901],{},[941,184521,4891],{}," take the fast path: ",[941,184524,28291],{},[941,184526,6480],{},[941,184528,6474],{},", and the other range operators — and any query on a ",[950,184531,184532],{},"multi-currency"," field — always fall back to a full scan, because a hash index has no single stored value to serve for those.",[937,184535,184536,184539,184540,184543,184544,184547,184548,1625,184550,184552,184553,184555,184556,184558],{},[950,184537,184538],{},"Mixed-era records still match."," A record written before its field was declared money (or that otherwise skipped the money write path) can carry a non-canonical stored string — ",[941,184541,184542],{},"'0100'"," where a canonical write would store ",[941,184545,184546],{},"'100'",". The eager-mode index normalizes every money bucket key as it writes it, so a legacy record lands in the same bucket a canonical write produces: an ",[941,184549,21901],{},[941,184551,4891],{}," probe for the canonical amount finds it, and the indexed result matches a forced scan exactly. This holds when the index is first built, when a record is upserted with ",[941,184554,20953],{},", and when one is removed with ",[941,184557,9467],{},", so updating or deleting a legacy record leaves its bucket correct.",[1591,184560,184562],{"id":184561},"formatting-a-virtual-computed-field","Formatting a virtual computed field",[937,184564,3289,184565,184569,184570,2311],{},[1007,184566,184568],{"href":184567},".\u002Fcomputed","virtual computed"," field — one whose value is recomputed on every read and never stored — can be declared as money on the same field, and money formats its output exactly like a stored money field. Compose the two with ",[941,184571,3040],{},[2217,184573,184575],{"className":2219,"code":184574,"language":2221,"meta":1037,"style":1037},"import { via, computed } from '@noy-db\u002Fhub'\n\ninterface Priced extends Record\u003Cstring, unknown> { id: string; base: number }\n\nconst priced = vault.collection\u003CPriced>('priced', {\n  viaFields: {\n    doubledPrice: via(\n      computed((r) => (r.base as number) * 2, { deps: ['base'], mode: 'virtual' }),\n      money({ currency: 'EUR', scale: 2 }),\n    ),\n  },\n})\n\nawait priced.put('a', { id: 'a', base: 10.5 })\nconst read = await priced.get('a', { locale: 'de-DE' }) as Record\u003Cstring, unknown>\n\u002F\u002F → {\n\u002F\u002F   id: 'a',\n\u002F\u002F   doubledPrice: '21.00',            \u002F\u002F the fn's 21 quantized to the field's scale\n\u002F\u002F   doubledPriceFormatted: '21,00 €', \u002F\u002F dressed like any stored money field\n\u002F\u002F   doubledPriceNumber: 21,\n\u002F\u002F }\n",[941,184576,184577,184599,184603,184642,184646,184679,184687,184697,184763,184795,184801,184805,184811,184815,184859,184915,184919,184923,184931,184939,184944],{"__ignoreMap":1037},[2225,184578,184579,184581,184583,184585,184587,184589,184591,184593,184595,184597],{"class":2227,"line":2228},[2225,184580,2232],{"class":2231},[2225,184582,2236],{"class":2235},[2225,184584,122163],{"class":2239},[2225,184586,2298],{"class":2235},[2225,184588,122168],{"class":2239},[2225,184590,2243],{"class":2235},[2225,184592,2246],{"class":2231},[2225,184594,2249],{"class":2235},[2225,184596,1086],{"class":2252},[2225,184598,2256],{"class":2235},[2225,184600,184601],{"class":2227,"line":1041},[2225,184602,2285],{"emptyLinePlaceholder":2284},[2225,184604,184605,184607,184610,184612,184614,184616,184618,184620,184622,184624,184626,184628,184630,184632,184634,184636,184638,184640],{"class":2227,"line":1038},[2225,184606,6994],{"class":2774},[2225,184608,184609],{"class":6342}," Priced",[2225,184611,124350],{"class":2774},[2225,184613,22527],{"class":6342},[2225,184615,6474],{"class":2235},[2225,184617,7259],{"class":6342},[2225,184619,2298],{"class":2235},[2225,184621,40717],{"class":6342},[2225,184623,6480],{"class":2235},[2225,184625,2236],{"class":2235},[2225,184627,7070],{"class":2307},[2225,184629,2311],{"class":2235},[2225,184631,6366],{"class":6342},[2225,184633,3769],{"class":2235},[2225,184635,122769],{"class":2307},[2225,184637,2311],{"class":2235},[2225,184639,6356],{"class":6342},[2225,184641,6369],{"class":2235},[2225,184643,184644],{"class":2227,"line":2288},[2225,184645,2285],{"emptyLinePlaceholder":2284},[2225,184647,184648,184650,184652,184654,184656,184658,184660,184662,184665,184667,184669,184671,184673,184675,184677],{"class":2227,"line":2304},[2225,184649,2775],{"class":2774},[2225,184651,122748],{"class":2239},[2225,184653,2781],{"class":2235},[2225,184655,6466],{"class":2239},[2225,184657,1520],{"class":2235},[2225,184659,6471],{"class":2291},[2225,184661,6474],{"class":2235},[2225,184663,184664],{"class":6342},"Priced",[2225,184666,6480],{"class":2235},[2225,184668,2789],{"class":2239},[2225,184670,2805],{"class":2235},[2225,184672,122791],{"class":2252},[2225,184674,2805],{"class":2235},[2225,184676,2298],{"class":2235},[2225,184678,2301],{"class":2235},[2225,184680,184681,184683,184685],{"class":2227,"line":2356},[2225,184682,122240],{"class":2307},[2225,184684,2311],{"class":2235},[2225,184686,2301],{"class":2235},[2225,184688,184689,184691,184693,184695],{"class":2227,"line":2830},[2225,184690,122810],{"class":2307},[2225,184692,2311],{"class":2235},[2225,184694,122163],{"class":2291},[2225,184696,11683],{"class":2239},[2225,184698,184699,184701,184703,184705,184707,184709,184711,184713,184715,184717,184719,184721,184723,184725,184727,184729,184731,184733,184735,184737,184739,184741,184743,184745,184747,184749,184751,184753,184755,184757,184759,184761],{"class":2227,"line":2861},[2225,184700,122821],{"class":2291},[2225,184702,2789],{"class":2239},[2225,184704,2789],{"class":2235},[2225,184706,13217],{"class":7052},[2225,184708,1381],{"class":2235},[2225,184710,8116],{"class":2774},[2225,184712,121331],{"class":2239},[2225,184714,1520],{"class":2235},[2225,184716,122838],{"class":2239},[2225,184718,4899],{"class":2231},[2225,184720,6356],{"class":6342},[2225,184722,8495],{"class":2239},[2225,184724,121345],{"class":2235},[2225,184726,18961],{"class":6527},[2225,184728,2298],{"class":2235},[2225,184730,2236],{"class":2235},[2225,184732,122290],{"class":2307},[2225,184734,2311],{"class":2235},[2225,184736,8667],{"class":2239},[2225,184738,2805],{"class":2235},[2225,184740,57272],{"class":2252},[2225,184742,2805],{"class":2235},[2225,184744,8803],{"class":2239},[2225,184746,2298],{"class":2235},[2225,184748,32303],{"class":2307},[2225,184750,2311],{"class":2235},[2225,184752,2249],{"class":2235},[2225,184754,122313],{"class":2252},[2225,184756,2805],{"class":2235},[2225,184758,2243],{"class":2235},[2225,184760,1381],{"class":2239},[2225,184762,1707],{"class":2235},[2225,184764,184765,184767,184769,184771,184773,184775,184777,184779,184781,184783,184785,184787,184789,184791,184793],{"class":2227,"line":4246},[2225,184766,122889],{"class":2291},[2225,184768,2789],{"class":2239},[2225,184770,2792],{"class":2235},[2225,184772,122489],{"class":2307},[2225,184774,2311],{"class":2235},[2225,184776,2249],{"class":2235},[2225,184778,122902],{"class":2252},[2225,184780,2805],{"class":2235},[2225,184782,2298],{"class":2235},[2225,184784,122909],{"class":2307},[2225,184786,2311],{"class":2235},[2225,184788,18961],{"class":6527},[2225,184790,2243],{"class":2235},[2225,184792,1381],{"class":2239},[2225,184794,1707],{"class":2235},[2225,184796,184797,184799],{"class":2227,"line":4253},[2225,184798,122924],{"class":2239},[2225,184800,1707],{"class":2235},[2225,184802,184803],{"class":2227,"line":4259},[2225,184804,19017],{"class":2235},[2225,184806,184807,184809],{"class":2227,"line":6548},[2225,184808,2359],{"class":2235},[2225,184810,2362],{"class":2239},[2225,184812,184813],{"class":2227,"line":6576},[2225,184814,2285],{"emptyLinePlaceholder":2284},[2225,184816,184817,184819,184821,184823,184825,184827,184829,184831,184833,184835,184837,184839,184841,184843,184845,184847,184849,184851,184853,184855,184857],{"class":2227,"line":6582},[2225,184818,6500],{"class":2231},[2225,184820,122947],{"class":2239},[2225,184822,1520],{"class":2235},[2225,184824,3161],{"class":2291},[2225,184826,2789],{"class":2239},[2225,184828,2805],{"class":2235},[2225,184830,1007],{"class":2252},[2225,184832,2805],{"class":2235},[2225,184834,2298],{"class":2235},[2225,184836,2236],{"class":2235},[2225,184838,7070],{"class":2307},[2225,184840,2311],{"class":2235},[2225,184842,2249],{"class":2235},[2225,184844,1007],{"class":2252},[2225,184846,2805],{"class":2235},[2225,184848,2298],{"class":2235},[2225,184850,122769],{"class":2307},[2225,184852,2311],{"class":2235},[2225,184854,122982],{"class":6527},[2225,184856,2243],{"class":2235},[2225,184858,2362],{"class":2239},[2225,184860,184861,184863,184865,184867,184869,184871,184873,184875,184877,184879,184881,184883,184885,184887,184889,184891,184893,184895,184897,184899,184901,184903,184905,184907,184909,184911,184913],{"class":2227,"line":6587},[2225,184862,2775],{"class":2774},[2225,184864,183654],{"class":2239},[2225,184866,2781],{"class":2235},[2225,184868,2784],{"class":2231},[2225,184870,122947],{"class":2239},[2225,184872,1520],{"class":2235},[2225,184874,3158],{"class":2291},[2225,184876,2789],{"class":2239},[2225,184878,2805],{"class":2235},[2225,184880,1007],{"class":2252},[2225,184882,2805],{"class":2235},[2225,184884,2298],{"class":2235},[2225,184886,2236],{"class":2235},[2225,184888,24003],{"class":2307},[2225,184890,2311],{"class":2235},[2225,184892,2249],{"class":2235},[2225,184894,183685],{"class":2252},[2225,184896,2805],{"class":2235},[2225,184898,2243],{"class":2235},[2225,184900,8495],{"class":2239},[2225,184902,4899],{"class":2231},[2225,184904,22527],{"class":6342},[2225,184906,6474],{"class":2235},[2225,184908,7259],{"class":6342},[2225,184910,2298],{"class":2235},[2225,184912,40717],{"class":6342},[2225,184914,7093],{"class":2235},[2225,184916,184917],{"class":2227,"line":7174},[2225,184918,164681],{"class":2763},[2225,184920,184921],{"class":2227,"line":7180},[2225,184922,183714],{"class":2763},[2225,184924,184925,184928],{"class":2227,"line":7220},[2225,184926,184927],{"class":2763},"\u002F\u002F   doubledPrice: '21.00',",[2225,184929,184930],{"class":2763},"            \u002F\u002F the fn's 21 quantized to the field's scale\n",[2225,184932,184933,184936],{"class":2227,"line":7225},[2225,184934,184935],{"class":2763},"\u002F\u002F   doubledPriceFormatted: '21,00 €',",[2225,184937,184938],{"class":2763}," \u002F\u002F dressed like any stored money field\n",[2225,184940,184941],{"class":2227,"line":7231},[2225,184942,184943],{"class":2763},"\u002F\u002F   doubledPriceNumber: 21,\n",[2225,184945,184946],{"class":2227,"line":7267},[2225,184947,177678],{"class":2763},[937,184949,184950,184951,184953,184954,184956,184957,1625,184959,184961,184962,184965,184966,184968,184969,184971,184972,184974,184975,184978,184979,184981,184982,1625,184984,184986],{},"Money takes the function's major-units result, quantizes it to the field's ",[941,184952,148612],{}," (applying any declared ",[941,184955,123174],{},"), and adds the ",[941,184958,183758],{},[941,184960,183761],{}," virtuals under a non-",[941,184963,184964],{},"'raw'"," locale. If the function returns more precision than ",[941,184967,148612],{}," allows and no ",[941,184970,123174],{}," is declared, the value is left raw and untouched rather than throwing — read-time formatting must never break a read; declaring a ",[941,184973,123174],{}," policy makes it quantize instead. This dressing is ",[950,184976,184977],{},"fixed-mode only",": a virtual field's plain number has no ",[941,184980,123170],{}," shape for multi-currency mode. A materialized computed field declared as money is already formatted the same way. If the field is redacted by a taint policy, its ",[941,184983,183758],{},[941,184985,183761],{}," companions are stripped along with it, never left exposing the pre-redaction value.",[1022,184988,101012],{"id":41259},[937,184990,184991,184992,101421],{},"All exports below are available from the ",[941,184993,1086],{},[1591,184995,184997],{"id":184996},"moneyoptions",[941,184998,183547],{},[2217,185000,185002],{"className":2219,"code":185001,"language":2221,"meta":1037,"style":1037},"function money(options: MoneyOptionsFixed | MoneyOptionsMulti): MoneyDescriptor\n",[941,185003,185004],{"__ignoreMap":1037},[2225,185005,185006,185008,185010,185012,185014,185016,185019,185021,185024,185026],{"class":2227,"line":2228},[2225,185007,12641],{"class":2774},[2225,185009,123521],{"class":2291},[2225,185011,2789],{"class":2235},[2225,185013,16362],{"class":7052},[2225,185015,2311],{"class":2235},[2225,185017,185018],{"class":6342}," MoneyOptionsFixed",[2225,185020,7087],{"class":2235},[2225,185022,185023],{"class":6342}," MoneyOptionsMulti",[2225,185025,6255],{"class":2235},[2225,185027,185028],{"class":6342}," MoneyDescriptor\n",[937,185030,185031,185032,185034,185035,1625,185037,185040,185041,185044,185045,1625,185047,957],{},"Builds the field descriptor. Throws ",[941,185033,24221],{}," if both or neither of ",[941,185036,148542],{},[941,185038,185039],{},"currencies"," are given, and throws ",[941,185042,185043],{},"MoneyCurrencyError"," at construction if a currency has no resolvable scale (unlisted ISO-4217 code with no explicit ",[941,185046,148612],{},[941,185048,185049],{},"scaleOverrides",[1338,185051,185052,185065],{},[1341,185053,185054],{},[1344,185055,185056,185059,185061,185063],{},[1347,185057,185058],{},"Option (fixed mode)",[1347,185060,20194],{},[1347,185062,20197],{},[1347,185064,7834],{},[1357,185066,185067,185082,185100],{},[1344,185068,185069,185073,185077,185079],{},[1362,185070,185071],{},[941,185072,148542],{},[1362,185074,185075],{},[941,185076,7259],{},[1362,185078,20215],{},[1362,185080,185081],{},"The one currency this field holds",[1344,185083,185084,185088,185092,185097],{},[1362,185085,185086],{},[941,185087,148612],{},[1362,185089,185090],{},[941,185091,20636],{},[1362,185093,185094,185095],{},"ISO-4217 minor-unit count for ",[941,185096,148542],{},[1362,185098,185099],{},"Override the decimal places; required for a currency code not in the built-in ISO-4217 table",[1344,185101,185102,185106,185111,185114],{},[1362,185103,185104],{},[941,185105,123174],{},[1362,185107,185108],{},[941,185109,185110],{},"RoundingMode",[1362,185112,185113],{},"none (excess precision throws)",[1362,185115,185116,185117],{},"Rounding applied when a written value carries more precision than ",[941,185118,148612],{},[1338,185120,185121,185134],{},[1341,185122,185123],{},[1344,185124,185125,185128,185130,185132],{},[1347,185126,185127],{},"Option (multi mode)",[1347,185129,20194],{},[1347,185131,20197],{},[1347,185133,7834],{},[1357,185135,185136,185155,185173],{},[1344,185137,185138,185142,185147,185149],{},[1362,185139,185140],{},[941,185141,185039],{},[1362,185143,185144],{},[941,185145,185146],{},"'any' | readonly string[]",[1362,185148,20215],{},[1362,185150,185151,185152,185154],{},"Allow-list of currencies, or ",[941,185153,163117],{}," for every ISO-4217-known code",[1344,185156,185157,185161,185166,185170],{},[1362,185158,185159],{},[941,185160,185049],{},[1362,185162,185163],{},[941,185164,185165],{},"Record\u003Cstring, number>",[1362,185167,185168],{},[941,185169,9261],{},[1362,185171,185172],{},"Per-currency decimal-place overrides; required for a currency not in the built-in table",[1344,185174,185175,185179,185183,185185],{},[1362,185176,185177],{},[941,185178,123174],{},[1362,185180,185181],{},[941,185182,185110],{},[1362,185184,185113],{},[1362,185186,185187],{},"Same as fixed mode",[937,185189,185190,185192,185193,1520],{},[941,185191,185110],{}," is one of ",[941,185194,185195],{},"'half-up' | 'half-even' | 'half-down' | 'up' | 'down' | 'ceil' | 'floor'",[937,185197,185198,185201,185202,953,185204,1884,185207,185210,185211,968,185214,2462,185217,185220],{},[941,185199,185200],{},"MoneyDescriptor"," (the return value) exposes ",[941,185203,120875],{},[941,185205,185206],{},"'fixed' | 'multi'",[941,185208,185209],{},"fixedCurrency"," (set in fixed mode), ",[941,185212,185213],{},"scaleFor(currency)",[941,185215,185216],{},"allows(currency)",[941,185218,185219],{},"soleCurrency()"," (the one currency implied by the descriptor, if any).",[1591,185222,185224],{"id":185223},"ismoneydescriptorx",[941,185225,185226],{},"isMoneyDescriptor(x)",[2217,185228,185230],{"className":2219,"code":185229,"language":2221,"meta":1037,"style":1037},"function isMoneyDescriptor(x: unknown): x is MoneyDescriptor\n",[941,185231,185232],{"__ignoreMap":1037},[2225,185233,185234,185236,185239,185241,185243,185245,185247,185249,185251,185254],{"class":2227,"line":2228},[2225,185235,12641],{"class":2774},[2225,185237,185238],{"class":2291}," isMoneyDescriptor",[2225,185240,2789],{"class":2235},[2225,185242,121980],{"class":7052},[2225,185244,2311],{"class":2235},[2225,185246,40717],{"class":6342},[2225,185248,6255],{"class":2235},[2225,185250,91304],{"class":7052},[2225,185252,185253],{"class":2235}," is",[2225,185255,185028],{"class":6342},[937,185257,185258,185259,1520],{},"Runtime predicate for detecting a value built by ",[941,185260,183119],{},[1591,185262,185264],{"id":185263},"mulrateamount-rate-opts",[941,185265,183773],{},[2217,185267,185269],{"className":2219,"code":185268,"language":2221,"meta":1037,"style":1037},"function mulRate(amount: number | string, rate: number | string, opts?: MulRateOptions): string\n",[941,185270,185271],{"__ignoreMap":1037},[2225,185272,185273,185275,185277,185279,185281,185283,185285,185287,185289,185291,185294,185296,185298,185300,185302,185304,185306,185308,185311,185313],{"class":2227,"line":2228},[2225,185274,12641],{"class":2774},[2225,185276,183788],{"class":2291},[2225,185278,2789],{"class":2235},[2225,185280,6617],{"class":7052},[2225,185282,2311],{"class":2235},[2225,185284,6356],{"class":6342},[2225,185286,7087],{"class":2235},[2225,185288,6366],{"class":6342},[2225,185290,2298],{"class":2235},[2225,185292,185293],{"class":7052}," rate",[2225,185295,2311],{"class":2235},[2225,185297,6356],{"class":6342},[2225,185299,7087],{"class":2235},[2225,185301,6366],{"class":6342},[2225,185303,2298],{"class":2235},[2225,185305,8232],{"class":7052},[2225,185307,7011],{"class":2235},[2225,185309,185310],{"class":6342}," MulRateOptions",[2225,185312,6255],{"class":2235},[2225,185314,7014],{"class":6342},[1338,185316,185317,185329],{},[1341,185318,185319],{},[1344,185320,185321,185323,185325,185327],{},[1347,185322,101060],{},[1347,185324,20194],{},[1347,185326,20197],{},[1347,185328,116059],{},[1357,185330,185331,185347],{},[1344,185332,185333,185337,185341,185344],{},[1362,185334,185335],{},[941,185336,148612],{},[1362,185338,185339],{},[941,185340,20636],{},[1362,185342,185343],{},"the amount's own fractional digits",[1362,185345,185346],{},"Output scale",[1344,185348,185349,185353,185357,185361],{},[1362,185350,185351],{},[941,185352,123174],{},[1362,185354,185355],{},[941,185356,185110],{},[1362,185358,185359],{},[941,185360,123189],{},[1362,185362,185363],{},"Rounding for the final re-scale",[1591,185365,185367],{"id":185366},"allocateamount-weights-opts",[941,185368,183882],{},[2217,185370,185372],{"className":2219,"code":185371,"language":2221,"meta":1037,"style":1037},"function allocate(amount: number | string, weights: ReadonlyArray\u003Cnumber | string>, opts?: AllocateOptions): string[]\n",[941,185373,185374],{"__ignoreMap":1037},[2225,185375,185376,185378,185380,185382,185384,185386,185388,185390,185392,185394,185397,185399,185401,185403,185405,185407,185409,185411,185413,185415,185418,185420,185422],{"class":2227,"line":2228},[2225,185377,12641],{"class":2774},[2225,185379,183897],{"class":2291},[2225,185381,2789],{"class":2235},[2225,185383,6617],{"class":7052},[2225,185385,2311],{"class":2235},[2225,185387,6356],{"class":6342},[2225,185389,7087],{"class":2235},[2225,185391,6366],{"class":6342},[2225,185393,2298],{"class":2235},[2225,185395,185396],{"class":7052}," weights",[2225,185398,2311],{"class":2235},[2225,185400,19754],{"class":6342},[2225,185402,6474],{"class":2235},[2225,185404,20636],{"class":6342},[2225,185406,7087],{"class":2235},[2225,185408,6366],{"class":6342},[2225,185410,17840],{"class":2235},[2225,185412,8232],{"class":7052},[2225,185414,7011],{"class":2235},[2225,185416,185417],{"class":6342}," AllocateOptions",[2225,185419,6255],{"class":2235},[2225,185421,6366],{"class":6342},[2225,185423,78511],{"class":2239},[1338,185425,185426,185438],{},[1341,185427,185428],{},[1344,185429,185430,185432,185434,185436],{},[1347,185431,101060],{},[1347,185433,20194],{},[1347,185435,20197],{},[1347,185437,116059],{},[1357,185439,185440],{},[1344,185441,185442,185446,185450,185452],{},[1362,185443,185444],{},[941,185445,148612],{},[1362,185447,185448],{},[941,185449,20636],{},[1362,185451,185343],{},[1362,185453,185454],{},"Working scale",[937,185456,185457,185458,185461],{},"Weights must be non-negative decimals with a positive sum; throws ",[941,185459,185460],{},"MoneyUnsupportedError"," otherwise. Negative amounts allocate the magnitude and negate every part, so the parts still sum to the (negative) input exactly.",[1591,185463,185465],{"id":185464},"scaleforcurrencycode",[941,185466,185467],{},"scaleForCurrency(code)",[2217,185469,185471],{"className":2219,"code":185470,"language":2221,"meta":1037,"style":1037},"function scaleForCurrency(code: string): number | null\n",[941,185472,185473],{"__ignoreMap":1037},[2225,185474,185475,185477,185480,185482,185484,185486,185488,185490,185492,185494],{"class":2227,"line":2228},[2225,185476,12641],{"class":2774},[2225,185478,185479],{"class":2291}," scaleForCurrency",[2225,185481,2789],{"class":2235},[2225,185483,941],{"class":7052},[2225,185485,2311],{"class":2235},[2225,185487,6366],{"class":6342},[2225,185489,6255],{"class":2235},[2225,185491,6356],{"class":6342},[2225,185493,7087],{"class":2235},[2225,185495,8384],{"class":6342},[937,185497,185498,185499,1746,185502,968,185504,1746,185507,968,185509,1746,185512,185514,185515,185517,185518,1625,185520,185522],{},"Looks up the ISO-4217 minor-unit count for a currency code (",[941,185500,185501],{},"'EUR'",[941,185503,5470],{},[941,185505,185506],{},"'JPY'",[941,185508,5402],{},[941,185510,185511],{},"'BHD'",[941,185513,5512],{},"); returns ",[941,185516,6651],{}," for a code outside the built-in table, which is what forces an explicit ",[941,185519,148612],{},[941,185521,185049],{}," for exotic currencies.",[1591,185524,185526,1261,185529,1261,185532],{"id":185525},"asmoneyvalue-ismoneystringvalue-moneynumbervalue",[941,185527,185528],{},"asMoney(value)",[941,185530,185531],{},"isMoneyString(value)",[941,185533,185534],{},"moneyNumber(value)",[2217,185536,185538],{"className":2219,"code":185537,"language":2221,"meta":1037,"style":1037},"function asMoney(value: string | number): MoneyString\nfunction isMoneyString(value: unknown): value is MoneyString\nfunction moneyNumber(value: MoneyString | string | number): number\n",[941,185539,185540,185564,185588],{"__ignoreMap":1037},[2225,185541,185542,185544,185547,185549,185551,185553,185555,185557,185559,185561],{"class":2227,"line":2228},[2225,185543,12641],{"class":2774},[2225,185545,185546],{"class":2291}," asMoney",[2225,185548,2789],{"class":2235},[2225,185550,25201],{"class":7052},[2225,185552,2311],{"class":2235},[2225,185554,6366],{"class":6342},[2225,185556,7087],{"class":2235},[2225,185558,6356],{"class":6342},[2225,185560,6255],{"class":2235},[2225,185562,185563],{"class":6342}," MoneyString\n",[2225,185565,185566,185568,185571,185573,185575,185577,185579,185581,185584,185586],{"class":2227,"line":1041},[2225,185567,12641],{"class":2774},[2225,185569,185570],{"class":2291}," isMoneyString",[2225,185572,2789],{"class":2235},[2225,185574,25201],{"class":7052},[2225,185576,2311],{"class":2235},[2225,185578,40717],{"class":6342},[2225,185580,6255],{"class":2235},[2225,185582,185583],{"class":7052}," value",[2225,185585,185253],{"class":2235},[2225,185587,185563],{"class":6342},[2225,185589,185590,185592,185595,185597,185599,185601,185604,185606,185608,185610,185612,185614],{"class":2227,"line":1038},[2225,185591,12641],{"class":2774},[2225,185593,185594],{"class":2291}," moneyNumber",[2225,185596,2789],{"class":2235},[2225,185598,25201],{"class":7052},[2225,185600,2311],{"class":2235},[2225,185602,185603],{"class":6342}," MoneyString",[2225,185605,7087],{"class":2235},[2225,185607,6366],{"class":6342},[2225,185609,7087],{"class":2235},[2225,185611,6356],{"class":6342},[2225,185613,6255],{"class":2235},[2225,185615,12024],{"class":6342},[937,185617,185618,185621,185622,185624,185625,185628,185629,185631,185632,185635,185636,185638,185639,1625,185641,33515],{},[941,185619,185620],{},"MoneyString"," is a branded ",[941,185623,7259],{}," type (zero runtime cost) marking a value that has been through the money decode path, so a validator or read site can distinguish \"already-canonical decimal\" from a plain user-typed string. ",[941,185626,185627],{},"asMoney()"," is a runtime-guarded cast for trust boundaries (deserialized JSON, route params) — it throws ",[941,185630,185460],{}," on a non-finite\u002Fnon-decimal input. ",[941,185633,185634],{},"moneyNumber()"," is the one sanctioned escape hatch to a plain JS ",[941,185637,20636],{},", for display math and chart axes — explicitly lossy past 2^53; never feed the result back into a stored amount, use ",[941,185640,183807],{},[941,185642,183916],{},[1591,185644,120265],{"id":120264},[937,185646,140885,185647,185649,185650,1520],{},[941,185648,140888],{}," and are exported from ",[941,185651,1086],{},[1338,185653,185654,185662],{},[1341,185655,185656],{},[1344,185657,185658,185660],{},[1347,185659,12126],{},[1347,185661,120276],{},[1357,185663,185664,185679,185688],{},[1344,185665,185666,185671],{},[1362,185667,185668],{},[941,185669,185670],{},"MoneyPrecisionError",[1362,185672,185673,185674,184968,185676,185678],{},"A written value carries more decimal precision than the field's ",[941,185675,148612],{},[941,185677,123174],{}," mode is configured",[1344,185680,185681,185685],{},[1362,185682,185683],{},[941,185684,185043],{},[1362,185686,185687],{},"A currency is disallowed for the field, or has no resolvable scale (unlisted ISO-4217 code with no explicit scale)",[1344,185689,185690,185694],{},[1362,185691,185692],{},[941,185693,185460],{},[1362,185695,185696,185697,1625,185699,185701,185702,185704,185705,185708],{},"An unsupported operation is attempted on a money field or value — e.g. a non-decimal amount\u002Fweight passed to ",[941,185698,183807],{},[941,185700,183916],{},", an empty ",[941,185703,183916],{}," weight list, or ",[941,185706,185707],{},"avg()"," over a declared money field (see Limits below)",[1022,185710,68210],{"id":68209},[1111,185712,185713,185733,185748,185768,185782,185794,185807],{},[1114,185714,185715,185720,185721,185723,185724,997,185727,185729,185730,185732],{},[950,185716,185717,185719],{},[941,185718,185707],{}," is unsupported on money fields"," — it throws ",[941,185722,185460],{}," rather than silently returning a lossy figure. Use ",[941,185725,185726],{},"moneySum()",[941,185728,24913],{}," from the ",[1007,185731,4449],{"href":123485}," service and divide at the boundary.",[1114,185734,185735,185742,185743,46975,185745,185747],{},[950,185736,185737,997,185739,185741],{},[941,185738,148542],{},[941,185740,185039],{}," are mutually exclusive"," — passing both, or neither, throws a ",[941,185744,24221],{},[941,185746,183119],{}," construction.",[1114,185749,185750,964,185753,185756,185757,185759,185760,185762,185763,6789,185765,185767],{},[950,185751,185752],{},"Unlisted currencies need an explicit scale",[941,185754,185755],{},"scaleForCurrency()"," only covers the common ISO-4217 set; a code outside it (or a non-standard unit like a precious-metal ticker) must pass ",[941,185758,148612],{}," (fixed mode) or ",[941,185761,185049],{}," (multi mode), or ",[941,185764,183119],{},[941,185766,185043],{}," at construction.",[1114,185769,185770,185777,185778,185781],{},[950,185771,185772,1625,185774,185776],{},[941,185773,183807],{},[941,185775,183916],{}," are pure functions"," — they take and return plain decimal strings\u002Fnumbers with no vault or descriptor dependency; they don't read or write a field, and the working scale is inferred from the amount's own fractional digits unless ",[941,185779,185780],{},"opts.scale"," is given.",[1114,185783,185784,185787,185788,185790,185791,185793],{},[950,185785,185786],{},"Cross-currency comparisons are undefined"," — in multi-currency mode, a ",[941,185789,65223],{}," operand carries one currency; a record in a different currency matches ",[941,185792,181194],{}," and nothing else (no implied ordering across currencies).",[1114,185795,185796,185803,185804,185806],{},[950,185797,185798,1625,185800,185802],{},[941,185799,185634],{},[941,185801,123437],{}," are display-only"," — both are explicitly lossy past ",[941,185805,183116],{},"; never write the result back into a stored money field.",[1114,185808,185809,185812,185813,185815,185816,1625,185818,185820,185821,185823,185824,185826,185827,185833,185834,185836,185837,185839,185840,185842,185843,185845,185846,1625,185848,185850],{},[950,185810,185811],{},"Lazy-mode money queries are Via-aware (since 0.3.0-pre.12)"," — a lazy-mode collection (",[941,185814,6836],{},") keeps its own durable side-car indexes, and these now canonicalize each money bucket key exactly as the eager index does; the lazy ",[941,185817,21901],{},[941,185819,4891],{}," post-filter also runs Via-aware against the raw stored record, so an indexed ",[941,185822,24807],{}," no longer diverges from a forced scan at any ",[941,185825,148612],{},". Two narrower limits remain: ",[950,185828,185829,185830,185832],{},"lazy ",[941,185831,3019],{}," on a money field doesn't yet sort in scaled space"," — equality\u002F",[941,185835,4891],{}," filtering is correct, but ordering parity with eager is still pending; and a legacy record written ",[1092,185838,11357],{}," the field's ",[941,185841,139522],{}," declaration has no side-car until backfilled, so it needs ",[941,185844,165383],{}," (or an explicit ",[941,185847,164173],{},[941,185849,25686],{}," call) to become visible to the fast path.",[1022,185852,2621],{"id":2620},[1111,185854,185855,185864,185878,185888,185893],{},[1114,185856,185857,185860,185861,185863],{},[1007,185858,896],{"href":185859},".\u002Fvia"," — the field-feature composer ",[941,185862,183119],{}," plugs into",[1114,185865,185866,964,185868,1625,185870,1625,185872,185874,185875,185877],{},[1007,185867,4449],{"href":123485},[941,185869,184068],{},[941,185871,184071],{},[941,185873,184074],{}," and exact ",[941,185876,180192],{}," summation over declared money fields",[1114,185879,185880,185882,185883,1625,185885,185887],{},[1007,185881,701],{"href":124720}," — the sibling field-descriptor pattern (",[941,185884,58348],{},[941,185886,58358],{},") money follows",[1114,185889,185890,185892],{},[1007,185891,637],{"href":184567}," — derived fields, which compose with money-typed fields",[1114,185894,185895,1368,185897],{},[1007,185896,61743],{"href":598},[1007,185898,1019],{"href":1018},[2645,185900,185901],{},"html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html pre.shiki code .sHdIc, html code.shiki .sHdIc{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#EEFFFF;--shiki-default-font-style:italic;--shiki-dark:#BABED8;--shiki-dark-font-style:italic}",{"title":1037,"searchDepth":1038,"depth":1038,"links":185903},[185904,185905,185906,185917,185927,185928],{"id":29856,"depth":1041,"text":29857},{"id":58393,"depth":1041,"text":58394},{"id":29892,"depth":1041,"text":29893,"children":185907},[185908,185909,185911,185913,185914,185916],{"id":183579,"depth":1038,"text":183580},{"id":183765,"depth":1038,"text":185910},"mulRate() — apply a rate exactly",{"id":183874,"depth":1038,"text":185912},"allocate() — split with zero drift",{"id":183983,"depth":1038,"text":183984},{"id":184095,"depth":1038,"text":185915},"Indexing a money field for ==\u002Fin lookups",{"id":184561,"depth":1038,"text":184562},{"id":41259,"depth":1041,"text":101012,"children":185918},[185919,185920,185921,185922,185923,185924,185926],{"id":184996,"depth":1038,"text":183547},{"id":185223,"depth":1038,"text":185226},{"id":185263,"depth":1038,"text":183773},{"id":185366,"depth":1038,"text":183882},{"id":185464,"depth":1038,"text":185467},{"id":185525,"depth":1038,"text":185925},"asMoney(value) \u002F isMoneyString(value) \u002F moneyNumber(value)",{"id":120264,"depth":1038,"text":120265},{"id":68209,"depth":1041,"text":68210},{"id":2620,"depth":1041,"text":2621},"The `money()` field descriptor: exact-decimal currency storage with no floating-point drift, plus allocate()\u002FmulRate() helpers that always add back up.",{},{"title":760,"description":185929},"0.2.0-pre.12","gAIJr72M53VHqEvoPhbCkZwU3I346jOdqJhuMWEeQBo",{"id":185935,"title":764,"access":12,"apiToc":929,"body":185936,"category":1042,"description":186931,"extension":1044,"meta":186932,"navigation":1042,"nest":1047,"path":765,"seo":186933,"sinceVersion":1042,"status":1049,"stem":766,"subtitle":1042,"tier":1050,"tocExternal":1042,"__hash__":186934},"docs\u002Fservices\u002Fnumbering.md",{"type":931,"value":185937,"toc":186913},[185938,185940,185943,185945,185962,185969,185971,186007,186009,186025,186218,186220,186224,186385,186389,186476,186480,186524,186526,186532,186540,186601,186687,186693,186714,186720,186728,186733,186801,186810,186815,186817,186883,186885,186911],[934,185939,764],{"id":764},[46871,185941],{":rows":185942},"[{\"label\":\"Subpath\",\"value\":\"exported from `@noy-db\u002Fhub` root (no dedicated subpath)\"},{\"label\":\"Factory\",\"value\":\"`withDeferredNumbering()`\"},{\"label\":\"Layer\",\"value\":\"`with-commit`\"}]",[1022,185944,29857],{"id":29856},[937,185946,185947,185948,185952,185953,185955,185956,10326,185959,1520],{},"Deferred numbering hands out gap-free, duplicate-free serial numbers — invoice numbers, fiscal receipt numbers, anything a regulator expects to increment by exactly one with no reuse — to records in a collection. The ",[1007,185949,185950],{"href":837},[941,185951,836],{}," service does the same job immediately, but it needs the store to support an atomic compare-and-swap (CAS) write: check the current counter and write the next value in one race-free operation. Not every backend can do that — ",[941,185954,9551],{}," (local disk \u002F USB storage), for example, has no CAS and would make ",[941,185957,185958],{},"sequence()",[941,185960,185961],{},"SequenceOfflineError",[937,185963,185964,185965,185968],{},"Deferred numbering works around the missing CAS. Instead of allocating a number the instant you ask, it records that a number is wanted for a given record, and later — when you explicitly run a ",[1092,185966,185967],{},"numbering pass"," — it assigns numbers to every record whose write is provably finished, in the order the store actually committed them. The tradeoff: numbers are not available immediately, and you are responsible for triggering passes.",[1022,185970,70093],{"id":70092},[1111,185972,185973,185999],{},[1114,185974,185975,185978,185979,185981,185982,4765,185984,1261,185987,10326,185990,185993,185994,997,185996,185998],{},[950,185976,185977],{},"A store clock"," — the store must implement ",[941,185980,7858],{}," and advertise ",[941,185983,7868],{},[941,185985,185986],{},"next()",[941,185988,185989],{},"runNumberingPass()",[941,185991,185992],{},"NumberingUncertaintyError",". Both ",[941,185995,20491],{},[941,185997,86432],{}," provide it; check other adapters' docs.",[1114,186000,186001,964,186004,186006],{},[950,186002,186003],{},"A scheduled numbering pass",[941,186005,185989],{}," is never called automatically; your application triggers it (cron, queue worker, end of batch).",[1022,186008,99864],{"id":99863},[937,186010,186011,186012,186014,186015,186017,186018,186021,186022,186024],{},"Unlike most ",[941,186013,943],{}," capabilities, deferred numbering has no strategy option on ",[941,186016,2134],{},". Instead, pass one ",[941,186019,186020],{},"withDeferredNumbering()"," descriptor per series in the ",[941,186023,764],{}," array:",[2217,186026,186028],{"className":2219,"code":186027,"language":2221,"meta":1037,"style":1037},"import { createNoydb, withDeferredNumbering } from '@noy-db\u002Fhub'\nimport { toMemory } from '@noy-db\u002Fto-memory'\n\nconst db = await createNoydb({\n  user: 'alice',\n  secret: 's3cret',\n  store: toMemory(), \u002F\u002F any store with capabilities.serverWriteTime — see Limits below\n  numbering: [\n    withDeferredNumbering({\n      series: 'invoices',\n      collection: 'sales',\n      field: 'fiscalNumber',\n    }),\n  ],\n})\n",[941,186029,186030,186053,186071,186075,186091,186105,186119,186134,186143,186152,186167,186182,186198,186206,186212],{"__ignoreMap":1037},[2225,186031,186032,186034,186036,186038,186040,186043,186045,186047,186049,186051],{"class":2227,"line":2228},[2225,186033,2232],{"class":2231},[2225,186035,2236],{"class":2235},[2225,186037,2744],{"class":2239},[2225,186039,2298],{"class":2235},[2225,186041,186042],{"class":2239}," withDeferredNumbering",[2225,186044,2243],{"class":2235},[2225,186046,2246],{"class":2231},[2225,186048,2249],{"class":2235},[2225,186050,1086],{"class":2252},[2225,186052,2256],{"class":2235},[2225,186054,186055,186057,186059,186061,186063,186065,186067,186069],{"class":2227,"line":1041},[2225,186056,2232],{"class":2231},[2225,186058,2236],{"class":2235},[2225,186060,68576],{"class":2239},[2225,186062,2243],{"class":2235},[2225,186064,2246],{"class":2231},[2225,186066,2249],{"class":2235},[2225,186068,9638],{"class":2252},[2225,186070,2256],{"class":2235},[2225,186072,186073],{"class":2227,"line":1038},[2225,186074,2285],{"emptyLinePlaceholder":2284},[2225,186076,186077,186079,186081,186083,186085,186087,186089],{"class":2227,"line":2288},[2225,186078,2775],{"class":2774},[2225,186080,2778],{"class":2239},[2225,186082,2781],{"class":2235},[2225,186084,2784],{"class":2231},[2225,186086,2744],{"class":2291},[2225,186088,2789],{"class":2239},[2225,186090,4200],{"class":2235},[2225,186092,186093,186095,186097,186099,186101,186103],{"class":2227,"line":2304},[2225,186094,4205],{"class":2307},[2225,186096,2311],{"class":2235},[2225,186098,2249],{"class":2235},[2225,186100,6408],{"class":2252},[2225,186102,2805],{"class":2235},[2225,186104,1707],{"class":2235},[2225,186106,186107,186109,186111,186113,186115,186117],{"class":2227,"line":2356},[2225,186108,10423],{"class":2307},[2225,186110,2311],{"class":2235},[2225,186112,2249],{"class":2235},[2225,186114,9245],{"class":2252},[2225,186116,2805],{"class":2235},[2225,186118,1707],{"class":2235},[2225,186120,186121,186123,186125,186127,186129,186131],{"class":2227,"line":2830},[2225,186122,15608],{"class":2307},[2225,186124,2311],{"class":2235},[2225,186126,68576],{"class":2291},[2225,186128,4228],{"class":2239},[2225,186130,2298],{"class":2235},[2225,186132,186133],{"class":2763}," \u002F\u002F any store with capabilities.serverWriteTime — see Limits below\n",[2225,186135,186136,186139,186141],{"class":2227,"line":2861},[2225,186137,186138],{"class":2307},"  numbering",[2225,186140,2311],{"class":2235},[2225,186142,32491],{"class":2239},[2225,186144,186145,186148,186150],{"class":2227,"line":4246},[2225,186146,186147],{"class":2291},"    withDeferredNumbering",[2225,186149,2789],{"class":2239},[2225,186151,4200],{"class":2235},[2225,186153,186154,186157,186159,186161,186163,186165],{"class":2227,"line":4253},[2225,186155,186156],{"class":2307},"      series",[2225,186158,2311],{"class":2235},[2225,186160,2249],{"class":2235},[2225,186162,6487],{"class":2252},[2225,186164,2805],{"class":2235},[2225,186166,1707],{"class":2235},[2225,186168,186169,186172,186174,186176,186178,186180],{"class":2227,"line":4259},[2225,186170,186171],{"class":2307},"      collection",[2225,186173,2311],{"class":2235},[2225,186175,2249],{"class":2235},[2225,186177,123562],{"class":2252},[2225,186179,2805],{"class":2235},[2225,186181,1707],{"class":2235},[2225,186183,186184,186187,186189,186191,186194,186196],{"class":2227,"line":6548},[2225,186185,186186],{"class":2307},"      field",[2225,186188,2311],{"class":2235},[2225,186190,2249],{"class":2235},[2225,186192,186193],{"class":2252},"fiscalNumber",[2225,186195,2805],{"class":2235},[2225,186197,1707],{"class":2235},[2225,186199,186200,186202,186204],{"class":2227,"line":6576},[2225,186201,80146],{"class":2235},[2225,186203,1381],{"class":2239},[2225,186205,1707],{"class":2235},[2225,186207,186208,186210],{"class":2227,"line":6582},[2225,186209,32588],{"class":2239},[2225,186211,1707],{"class":2235},[2225,186213,186214,186216],{"class":2227,"line":6587},[2225,186215,2359],{"class":2235},[2225,186217,2362],{"class":2239},[1022,186219,29893],{"id":29892},[1591,186221,186223],{"id":186222},"_1-enqueue-a-record-for-numbering","1. Enqueue a record for numbering",[2217,186225,186227],{"className":2219,"code":186226,"language":2221,"meta":1037,"style":1037},"const vault = await db.openVault('acme')\nconst sales = vault.collection('sales')\n\nawait sales.put('order-42', { customer: 'Ada', total: 100 })\n\nconst pending = vault.sequence('invoices').next({ for: 'order-42' })\n\u002F\u002F pending is a Promise\u003Cnumber> — it does not resolve until a numbering pass runs\n",[941,186228,186229,186255,186279,186283,186328,186332,186380],{"__ignoreMap":1037},[2225,186230,186231,186233,186235,186237,186239,186241,186243,186245,186247,186249,186251,186253],{"class":2227,"line":2228},[2225,186232,2775],{"class":2774},[2225,186234,2835],{"class":2239},[2225,186236,2781],{"class":2235},[2225,186238,2784],{"class":2231},[2225,186240,2842],{"class":2239},[2225,186242,1520],{"class":2235},[2225,186244,2847],{"class":2291},[2225,186246,2789],{"class":2239},[2225,186248,2805],{"class":2235},[2225,186250,6450],{"class":2252},[2225,186252,2805],{"class":2235},[2225,186254,2362],{"class":2239},[2225,186256,186257,186259,186261,186263,186265,186267,186269,186271,186273,186275,186277],{"class":2227,"line":1041},[2225,186258,2775],{"class":2774},[2225,186260,123547],{"class":2239},[2225,186262,2781],{"class":2235},[2225,186264,6466],{"class":2239},[2225,186266,1520],{"class":2235},[2225,186268,6471],{"class":2291},[2225,186270,2789],{"class":2239},[2225,186272,2805],{"class":2235},[2225,186274,123562],{"class":2252},[2225,186276,2805],{"class":2235},[2225,186278,2362],{"class":2239},[2225,186280,186281],{"class":2227,"line":1038},[2225,186282,2285],{"emptyLinePlaceholder":2284},[2225,186284,186285,186287,186289,186291,186293,186295,186297,186300,186302,186304,186306,186308,186310,186312,186314,186316,186318,186320,186322,186324,186326],{"class":2227,"line":2288},[2225,186286,6500],{"class":2231},[2225,186288,123785],{"class":2239},[2225,186290,1520],{"class":2235},[2225,186292,3161],{"class":2291},[2225,186294,2789],{"class":2239},[2225,186296,2805],{"class":2235},[2225,186298,186299],{"class":2252},"order-42",[2225,186301,2805],{"class":2235},[2225,186303,2298],{"class":2235},[2225,186305,2236],{"class":2235},[2225,186307,88787],{"class":2307},[2225,186309,2311],{"class":2235},[2225,186311,2249],{"class":2235},[2225,186313,9342],{"class":2252},[2225,186315,2805],{"class":2235},[2225,186317,2298],{"class":2235},[2225,186319,27452],{"class":2307},[2225,186321,2311],{"class":2235},[2225,186323,6528],{"class":6527},[2225,186325,2243],{"class":2235},[2225,186327,2362],{"class":2239},[2225,186329,186330],{"class":2227,"line":2304},[2225,186331,2285],{"emptyLinePlaceholder":2284},[2225,186333,186334,186336,186339,186341,186343,186345,186347,186349,186351,186353,186355,186357,186359,186361,186363,186365,186368,186370,186372,186374,186376,186378],{"class":2227,"line":2356},[2225,186335,2775],{"class":2774},[2225,186337,186338],{"class":2239}," pending ",[2225,186340,2781],{"class":2235},[2225,186342,6466],{"class":2239},[2225,186344,1520],{"class":2235},[2225,186346,836],{"class":2291},[2225,186348,2789],{"class":2239},[2225,186350,2805],{"class":2235},[2225,186352,6487],{"class":2252},[2225,186354,2805],{"class":2235},[2225,186356,1381],{"class":2239},[2225,186358,1520],{"class":2235},[2225,186360,1454],{"class":2291},[2225,186362,2789],{"class":2239},[2225,186364,2792],{"class":2235},[2225,186366,186367],{"class":2307}," for",[2225,186369,2311],{"class":2235},[2225,186371,2249],{"class":2235},[2225,186373,186299],{"class":2252},[2225,186375,2805],{"class":2235},[2225,186377,2243],{"class":2235},[2225,186379,2362],{"class":2239},[2225,186381,186382],{"class":2227,"line":2830},[2225,186383,186384],{"class":2763},"\u002F\u002F pending is a Promise\u003Cnumber> — it does not resolve until a numbering pass runs\n",[1591,186386,186388],{"id":186387},"_2-run-a-numbering-pass","2. Run a numbering pass",[2217,186390,186392],{"className":2219,"code":186391,"language":2221,"meta":1037,"style":1037},"const assignments = await vault.runNumberingPass('invoices')\n\u002F\u002F → [{ recordId: 'order-42', serial: 1 }]\n\nconst numbered = await sales.get('order-42')\n\u002F\u002F → { customer: 'Ada', total: 100, fiscalNumber: 1 }\n\nawait pending \u002F\u002F 1 — the next() promise from step 1 now resolves\n",[941,186393,186394,186422,186427,186431,186458,186463,186467],{"__ignoreMap":1037},[2225,186395,186396,186398,186401,186403,186405,186407,186409,186412,186414,186416,186418,186420],{"class":2227,"line":2228},[2225,186397,2775],{"class":2774},[2225,186399,186400],{"class":2239}," assignments ",[2225,186402,2781],{"class":2235},[2225,186404,2784],{"class":2231},[2225,186406,6466],{"class":2239},[2225,186408,1520],{"class":2235},[2225,186410,186411],{"class":2291},"runNumberingPass",[2225,186413,2789],{"class":2239},[2225,186415,2805],{"class":2235},[2225,186417,6487],{"class":2252},[2225,186419,2805],{"class":2235},[2225,186421,2362],{"class":2239},[2225,186423,186424],{"class":2227,"line":1041},[2225,186425,186426],{"class":2763},"\u002F\u002F → [{ recordId: 'order-42', serial: 1 }]\n",[2225,186428,186429],{"class":2227,"line":1038},[2225,186430,2285],{"emptyLinePlaceholder":2284},[2225,186432,186433,186435,186438,186440,186442,186444,186446,186448,186450,186452,186454,186456],{"class":2227,"line":2288},[2225,186434,2775],{"class":2774},[2225,186436,186437],{"class":2239}," numbered ",[2225,186439,2781],{"class":2235},[2225,186441,2784],{"class":2231},[2225,186443,123785],{"class":2239},[2225,186445,1520],{"class":2235},[2225,186447,3158],{"class":2291},[2225,186449,2789],{"class":2239},[2225,186451,2805],{"class":2235},[2225,186453,186299],{"class":2252},[2225,186455,2805],{"class":2235},[2225,186457,2362],{"class":2239},[2225,186459,186460],{"class":2227,"line":2304},[2225,186461,186462],{"class":2763},"\u002F\u002F → { customer: 'Ada', total: 100, fiscalNumber: 1 }\n",[2225,186464,186465],{"class":2227,"line":2356},[2225,186466,2285],{"emptyLinePlaceholder":2284},[2225,186468,186469,186471,186473],{"class":2227,"line":2830},[2225,186470,6500],{"class":2231},[2225,186472,186338],{"class":2239},[2225,186474,186475],{"class":2763},"\u002F\u002F 1 — the next() promise from step 1 now resolves\n",[1591,186477,186479],{"id":186478},"_3-peek-at-the-last-assigned-serial","3. Peek at the last assigned serial",[2217,186481,186483],{"className":2219,"code":186482,"language":2221,"meta":1037,"style":1037},"const last = await vault.sequence('invoices').peek()\n\u002F\u002F → 1\n",[941,186484,186485,186519],{"__ignoreMap":1037},[2225,186486,186487,186489,186492,186494,186496,186498,186500,186502,186504,186506,186508,186510,186512,186514,186517],{"class":2227,"line":2228},[2225,186488,2775],{"class":2774},[2225,186490,186491],{"class":2239}," last ",[2225,186493,2781],{"class":2235},[2225,186495,2784],{"class":2231},[2225,186497,6466],{"class":2239},[2225,186499,1520],{"class":2235},[2225,186501,836],{"class":2291},[2225,186503,2789],{"class":2239},[2225,186505,2805],{"class":2235},[2225,186507,6487],{"class":2252},[2225,186509,2805],{"class":2235},[2225,186511,1381],{"class":2239},[2225,186513,1520],{"class":2235},[2225,186515,186516],{"class":2291},"peek",[2225,186518,8146],{"class":2239},[2225,186520,186521],{"class":2227,"line":1041},[2225,186522,186523],{"class":2763},"\u002F\u002F → 1\n",[1022,186525,102889],{"id":102888},[1591,186527,186529],{"id":186528},"withdeferrednumberingconfig",[941,186530,186531],{},"withDeferredNumbering(config)",[937,186533,186534,186535,186537,186538,1520],{},"Declares one deferred-numbering series. Pass one or more results in the ",[941,186536,764],{}," array of ",[941,186539,2134],{},[2217,186541,186543],{"className":2219,"code":186542,"language":2221,"meta":1037,"style":1037},"function withDeferredNumbering(config: {\n  series: string\n  collection: string\n  field: string\n  settleWindowMs?: number\n}): DeferredNumberingConfig\n",[941,186544,186545,186559,186568,186576,186584,186593],{"__ignoreMap":1037},[2225,186546,186547,186549,186551,186553,186555,186557],{"class":2227,"line":2228},[2225,186548,12641],{"class":2774},[2225,186550,186042],{"class":2291},[2225,186552,2789],{"class":2235},[2225,186554,66864],{"class":7052},[2225,186556,2311],{"class":2235},[2225,186558,2301],{"class":2235},[2225,186560,186561,186564,186566],{"class":2227,"line":1041},[2225,186562,186563],{"class":2307},"  series",[2225,186565,2311],{"class":2235},[2225,186567,7014],{"class":6342},[2225,186569,186570,186572,186574],{"class":2227,"line":1038},[2225,186571,30275],{"class":2307},[2225,186573,2311],{"class":2235},[2225,186575,7014],{"class":6342},[2225,186577,186578,186580,186582],{"class":2227,"line":2288},[2225,186579,140171],{"class":2307},[2225,186581,2311],{"class":2235},[2225,186583,7014],{"class":6342},[2225,186585,186586,186589,186591],{"class":2227,"line":2304},[2225,186587,186588],{"class":2307},"  settleWindowMs",[2225,186590,7011],{"class":2235},[2225,186592,12024],{"class":6342},[2225,186594,186595,186598],{"class":2227,"line":2356},[2225,186596,186597],{"class":2235},"}):",[2225,186599,186600],{"class":6342}," DeferredNumberingConfig\n",[1338,186602,186603,186615],{},[1341,186604,186605],{},[1344,186606,186607,186609,186611,186613],{},[1347,186608,101060],{},[1347,186610,20194],{},[1347,186612,20197],{},[1347,186614,20200],{},[1357,186616,186617,186636,186651,186666],{},[1344,186618,186619,186624,186628,186630],{},[1362,186620,186621],{},[941,186622,186623],{},"series",[1362,186625,186626],{},[941,186627,7259],{},[1362,186629,3610],{},[1362,186631,186632,186633,1520],{},"The name passed to ",[941,186634,186635],{},"vault.sequence(series)",[1344,186637,186638,186642,186646,186648],{},[1362,186639,186640],{},[941,186641,6471],{},[1362,186643,186644],{},[941,186645,7259],{},[1362,186647,3610],{},[1362,186649,186650],{},"The collection holding the records to number.",[1344,186652,186653,186657,186661,186663],{},[1362,186654,186655],{},[941,186656,25197],{},[1362,186658,186659],{},[941,186660,7259],{},[1362,186662,3610],{},[1362,186664,186665],{},"The field on each record where the assigned serial is written.",[1344,186667,186668,186673,186677,186681],{},[1362,186669,186670],{},[941,186671,186672],{},"settleWindowMs",[1362,186674,186675],{},[941,186676,20636],{},[1362,186678,186679],{},[941,186680,5402],{},[1362,186682,186683,186684,186686],{},"Accepted and stored on the config, but not currently read by ",[941,186685,185989],{}," — eligibility today is governed only by the store-clock commit-wait interval described in Limits below.",[1591,186688,186690],{"id":186689},"vaultsequenceseriesnext-for-recordid",[941,186691,186692],{},"vault.sequence(series).next({ for: recordId })",[937,186694,186695,186696,968,186699,186701,186702,186704,186705,186707,186708,21348,186710,6789,186712,1520],{},"On a series declared via ",[941,186697,186698],{},"withDeferredNumbering",[941,186700,185986],{}," requires a ",[941,186703,4646],{}," (the id of the record to number) and returns ",[941,186706,160412],{}," that resolves once a numbering pass processes that record. Calling ",[941,186709,185986],{},[941,186711,4646],{},[941,186713,12881],{},[1591,186715,186717],{"id":186716},"vaultsequenceseriespeek",[941,186718,186719],{},"vault.sequence(series).peek()",[937,186721,111113,186722,186724,186725,186727],{},[941,186723,160412],{}," — the last serial assigned to the series so far, or ",[941,186726,5402],{}," if no pass has run yet. Read-only; never allocates a number.",[1591,186729,186731],{"id":186730},"vaultrunnumberingpassseries",[941,186732,21759],{},[2217,186734,186736],{"className":2219,"code":186735,"language":2221,"meta":1037,"style":1037},"function runNumberingPass(series: string): Promise\u003CAssignment[]>\n\ninterface Assignment {\n  recordId: string\n  serial: number\n}\n",[941,186737,186738,186766,186770,186779,186788,186797],{"__ignoreMap":1037},[2225,186739,186740,186742,186745,186747,186749,186751,186753,186755,186757,186759,186762,186764],{"class":2227,"line":2228},[2225,186741,12641],{"class":2774},[2225,186743,186744],{"class":2291}," runNumberingPass",[2225,186746,2789],{"class":2235},[2225,186748,186623],{"class":7052},[2225,186750,2311],{"class":2235},[2225,186752,6366],{"class":6342},[2225,186754,6255],{"class":2235},[2225,186756,7079],{"class":6342},[2225,186758,6474],{"class":2235},[2225,186760,186761],{"class":6342},"Assignment",[2225,186763,7262],{"class":2239},[2225,186765,7093],{"class":2235},[2225,186767,186768],{"class":2227,"line":1041},[2225,186769,2285],{"emptyLinePlaceholder":2284},[2225,186771,186772,186774,186777],{"class":2227,"line":1038},[2225,186773,6994],{"class":2774},[2225,186775,186776],{"class":6342}," Assignment",[2225,186778,2301],{"class":2235},[2225,186780,186781,186784,186786],{"class":2227,"line":2288},[2225,186782,186783],{"class":2307},"  recordId",[2225,186785,2311],{"class":2235},[2225,186787,7014],{"class":6342},[2225,186789,186790,186793,186795],{"class":2227,"line":2304},[2225,186791,186792],{"class":2307},"  serial",[2225,186794,2311],{"class":2235},[2225,186796,12024],{"class":6342},[2225,186798,186799],{"class":2227,"line":2356},[2225,186800,7351],{"class":2235},[937,186802,186803,186804,186806,186807,186809],{},"Runs one numbering pass for ",[941,186805,186623],{},": finds every enqueued record whose commit-time interval is fully in the past relative to the store's current time, orders them by commit time (then by record id), assigns the next serials in order, writes the assigned field on each record through the collection layer (so caches, indexes, and materialized views stay coherent), and advances the series' last-serial marker with one atomic write. Returns the assignments made in this pass — ",[941,186808,7262],{}," if nothing was ready yet, or if a concurrent pass won the race (the next pass reconciles; no serial is lost or duplicated).",[937,186811,186812,186814],{},[941,186813,186411],{}," is never called automatically — trigger it on whatever schedule your application needs (a cron job, a queue worker, the end of a batch import).",[1022,186816,68210],{"id":68209},[1111,186818,186819,186845,186851,186868,186877],{},[1114,186820,186821,186824,186825,185981,186827,186829,186830,997,186832,10326,186834,185993,186836,953,186838,47474,186840,953,186842,186844],{},[950,186822,186823],{},"Requires a store clock."," The store must implement ",[941,186826,7858],{},[941,186828,7868],{},"; otherwise ",[941,186831,185986],{},[941,186833,185989],{},[941,186835,185992],{},[941,186837,20491],{},[941,186839,9638],{},[941,186841,86432],{},[941,186843,9551],{},") provide it; check a given adapter's own docs for others.",[1114,186846,186847,186850],{},[950,186848,186849],{},"Passes are manual."," noy-db never runs a numbering pass on its own.",[1114,186852,186853,15920,186856,186858,186859,186862,186863,186865,186866,1520],{},[950,186854,186855],{},"No formatted serials, no seeding.",[941,186857,23270],{}," render template and ",[941,186860,186861],{},"seedTo()"," that ",[941,186864,185958],{}," supports for CAS-backed series apply to CAS sequences only — calling either on a deferred series throws ",[941,186867,12881],{},[1114,186869,186870,186873,186874,186876],{},[950,186871,186872],{},"A settle delay is inherent."," An entry becomes eligible only once the store's current time interval starts after the entry's own commit interval ended, so there is a short, store-clock-uncertainty-bounded delay between enqueueing and eligibility. The ",[941,186875,186672],{}," config option exists but is not read by the current pass logic — it has no effect on timing.",[1114,186878,186879,186882],{},[950,186880,186881],{},"Deleted records don't burn a number."," If a record is deleted before its pass runs, the pass skips it without assigning it a serial.",[1022,186884,2621],{"id":2620},[1111,186886,186887,186892,186897,186903],{},[1114,186888,186889,186891],{},[1007,186890,836],{"href":837}," — the CAS-backed, immediate alternative for stores that support atomic compare-and-swap.",[1114,186893,186894,186896],{},[1007,186895,860],{"href":861}," — per-adapter store capabilities.",[1114,186898,186899,1368,186901],{},[1007,186900,61743],{"href":598},[1007,186902,1019],{"href":1018},[1114,186904,101581,186905,1520],{},[1007,186906,186908],{"href":94384,"rel":186907},[3371],[941,186909,186910],{},"132-deferred-numbering",[2645,186912,97673],{},{"title":1037,"searchDepth":1038,"depth":1038,"links":186914},[186915,186916,186917,186918,186923,186929,186930],{"id":29856,"depth":1041,"text":29857},{"id":70092,"depth":1041,"text":70093},{"id":99863,"depth":1041,"text":99864},{"id":29892,"depth":1041,"text":29893,"children":186919},[186920,186921,186922],{"id":186222,"depth":1038,"text":186223},{"id":186387,"depth":1038,"text":186388},{"id":186478,"depth":1038,"text":186479},{"id":102888,"depth":1041,"text":102889,"children":186924},[186925,186926,186927,186928],{"id":186528,"depth":1038,"text":186531},{"id":186689,"depth":1038,"text":186692},{"id":186716,"depth":1038,"text":186719},{"id":186730,"depth":1038,"text":21759},{"id":68209,"depth":1041,"text":68210},{"id":2620,"depth":1041,"text":2621},"Deferred numbering: gap-free, duplicate-free serial numbers assigned by an explicit batch pass, for stores without atomic compare-and-swap.",{},{"title":764,"description":186931},"o5lo4cqUURHV7dWXeiM6uVWvx-VK6W5c_n7xW9TM7bc",{"id":186936,"title":768,"access":12,"apiToc":929,"body":186937,"category":1042,"description":188829,"extension":1044,"meta":188830,"navigation":1042,"nest":1047,"path":769,"seo":188831,"sinceVersion":1042,"status":1049,"stem":770,"subtitle":1042,"tier":1050,"tocExternal":1042,"__hash__":188832},"docs\u002Fservices\u002Foverlay-views.md",{"type":931,"value":186938,"toc":188811},[186939,186941,186944,186946,186966,186972,186974,186992,186994,187493,187499,187501,187505,187883,187887,187890,188084,188088,188094,188316,188340,188342,188348,188375,188475,188481,188583,188585,188591,188655,188657,188769,188771,188808],[934,186940,768],{"id":768},[46871,186942],{":rows":186943},"[{\"label\":\"Subpath\",\"value\":\"`@noy-db\u002Fhub\u002Foverlay-views` (also re-exported from `@noy-db\u002Fhub`)\"},{\"label\":\"Factory\",\"value\":\"`withOverlayedView()`\"},{\"label\":\"Layer\",\"value\":\"`with-formula`\"}]",[1022,186945,29857],{"id":29856},[937,186947,186948,186951,186952,186955,186956,186958,186959,186961,186962,186965],{},[941,186949,186950],{},"withOverlayedView()"," creates a ",[950,186953,186954],{},"virtual collection",": a collection name that has no rows of\nits own. Instead, every read against it is computed on the fly by merging two real collections —\na ",[941,186957,57272],{}," collection (typically the output of a ",[1007,186960,134318],{"href":124624},") and\nan ",[941,186963,186964],{},"overlay"," collection that a person can write to directly. One field on the overlay row, the\n\"shadow field\", decides which side wins for a given record: if it equals a value you choose, the\noverlay row is returned in full; otherwise the base row shows through.",[937,186967,186968,186969,1520],{},"This solves a narrow but common problem: a materialized view computes the \"right\" answer for a\nrow, but occasionally a human needs to correct or annotate a specific record — without hand-editing\nthe source data (which would silently change what the computation is supposed to represent) and\nwithout losing the original computed value. Available since ",[941,186970,186971],{},"0.1.0-pre.14",[1022,186973,70093],{"id":70092},[1111,186975,186976,186984],{},[1114,186977,186978,186983],{},[950,186979,186980,186981],{},"A concrete ",[941,186982,57272],{}," — a real source collection or a materialized-view output, never another overlay.",[1114,186985,186986,186991],{},[950,186987,186988,186989,7061],{},"A real, writable ",[941,186990,186964],{}," that is not a materialized-view output.",[1022,186993,99864],{"id":99863},[2217,186995,186997],{"className":2219,"code":186996,"language":2221,"meta":1037,"style":1037},"import { createNoydb, withMaterializedView, withOverlayedView } from '@noy-db\u002Fhub'\nimport { sum } from '@noy-db\u002Fhub\u002Freduce'\n\ninterface Invoice { id: string; clientId: string; amount: number }\n\n\u002F\u002F Base: a materialized view computing one total row per client.\nconst invoiceTotals = withMaterializedView\u003C{ clientId: string; total: number }>({\n  name: 'invoice-totals',\n  query: (db) =>\n    db.collection\u003CInvoice>('invoices')\n      .query()\n      .groupBy('clientId')\n      .aggregate({ total: sum('amount') }),\n  rowKey: (row) => row.clientId,\n  sources: ['invoices'],\n  refresh: 'eager',\n})\n\n\u002F\u002F Overlay: the virtual collection wiring base → overlay via a shadow field.\nconst invoiceTotalsLive = withOverlayedView({\n  name: 'invoice-totals-live',        \u002F\u002F the merged collection you read from\n  base: 'invoice-totals',             \u002F\u002F the materialized view's output\n  overlay: 'invoice-totals-overlay',  \u002F\u002F a real, writable collection\n  shadowField: 'status',\n  shadowValue: 'override',\n})\n\nconst db = await createNoydb({\n  store: \u002F* your NoydbStore *\u002F,\n  user: 'alice',\n  secret: 'a-strong-secret',\n  materializedViewStrategies: [invoiceTotals],\n  overlayedViewStrategies: [invoiceTotalsLive],\n})\n",[941,186998,186999,187026,187044,187048,187080,187084,187089,187122,187137,187151,187175,187183,187199,187231,187253,187271,187285,187291,187295,187300,187315,187333,187351,187370,187385,187400,187406,187410,187426,187436,187450,187464,187475,187487],{"__ignoreMap":1037},[2225,187000,187001,187003,187005,187007,187009,187011,187013,187016,187018,187020,187022,187024],{"class":2227,"line":2228},[2225,187002,2232],{"class":2231},[2225,187004,2236],{"class":2235},[2225,187006,2744],{"class":2239},[2225,187008,2298],{"class":2235},[2225,187010,178904],{"class":2239},[2225,187012,2298],{"class":2235},[2225,187014,187015],{"class":2239}," withOverlayedView",[2225,187017,2243],{"class":2235},[2225,187019,2246],{"class":2231},[2225,187021,2249],{"class":2235},[2225,187023,1086],{"class":2252},[2225,187025,2256],{"class":2235},[2225,187027,187028,187030,187032,187034,187036,187038,187040,187042],{"class":2227,"line":1041},[2225,187029,2232],{"class":2231},[2225,187031,2236],{"class":2235},[2225,187033,27298],{"class":2239},[2225,187035,2243],{"class":2235},[2225,187037,2246],{"class":2231},[2225,187039,2249],{"class":2235},[2225,187041,27257],{"class":2252},[2225,187043,2256],{"class":2235},[2225,187045,187046],{"class":2227,"line":1038},[2225,187047,2285],{"emptyLinePlaceholder":2284},[2225,187049,187050,187052,187054,187056,187058,187060,187062,187064,187066,187068,187070,187072,187074,187076,187078],{"class":2227,"line":2288},[2225,187051,6994],{"class":2774},[2225,187053,6343],{"class":6342},[2225,187055,2236],{"class":2235},[2225,187057,7070],{"class":2307},[2225,187059,2311],{"class":2235},[2225,187061,6366],{"class":6342},[2225,187063,3769],{"class":2235},[2225,187065,62649],{"class":2307},[2225,187067,2311],{"class":2235},[2225,187069,6366],{"class":6342},[2225,187071,3769],{"class":2235},[2225,187073,6351],{"class":2307},[2225,187075,2311],{"class":2235},[2225,187077,6356],{"class":6342},[2225,187079,6369],{"class":2235},[2225,187081,187082],{"class":2227,"line":2304},[2225,187083,2285],{"emptyLinePlaceholder":2284},[2225,187085,187086],{"class":2227,"line":2356},[2225,187087,187088],{"class":2763},"\u002F\u002F Base: a materialized view computing one total row per client.\n",[2225,187090,187091,187093,187096,187098,187100,187102,187104,187106,187108,187110,187112,187114,187116,187118,187120],{"class":2227,"line":2830},[2225,187092,2775],{"class":2774},[2225,187094,187095],{"class":2239}," invoiceTotals ",[2225,187097,2781],{"class":2235},[2225,187099,178904],{"class":2291},[2225,187101,17851],{"class":2235},[2225,187103,62649],{"class":2307},[2225,187105,2311],{"class":2235},[2225,187107,6366],{"class":6342},[2225,187109,3769],{"class":2235},[2225,187111,27452],{"class":2307},[2225,187113,2311],{"class":2235},[2225,187115,6356],{"class":6342},[2225,187117,85293],{"class":2235},[2225,187119,2789],{"class":2239},[2225,187121,4200],{"class":2235},[2225,187123,187124,187126,187128,187130,187133,187135],{"class":2227,"line":2861},[2225,187125,7008],{"class":2307},[2225,187127,2311],{"class":2235},[2225,187129,2249],{"class":2235},[2225,187131,187132],{"class":2252},"invoice-totals",[2225,187134,2805],{"class":2235},[2225,187136,1707],{"class":2235},[2225,187138,187139,187141,187143,187145,187147,187149],{"class":2227,"line":4246},[2225,187140,179042],{"class":2291},[2225,187142,2311],{"class":2235},[2225,187144,953],{"class":2235},[2225,187146,6590],{"class":7052},[2225,187148,1381],{"class":2235},[2225,187150,71229],{"class":2774},[2225,187152,187153,187155,187157,187159,187161,187163,187165,187167,187169,187171,187173],{"class":2227,"line":4253},[2225,187154,179057],{"class":2239},[2225,187156,1520],{"class":2235},[2225,187158,6471],{"class":2291},[2225,187160,6474],{"class":2235},[2225,187162,6477],{"class":6342},[2225,187164,6480],{"class":2235},[2225,187166,2789],{"class":2239},[2225,187168,2805],{"class":2235},[2225,187170,6487],{"class":2252},[2225,187172,2805],{"class":2235},[2225,187174,2362],{"class":2239},[2225,187176,187177,187179,187181],{"class":2227,"line":4259},[2225,187178,80721],{"class":2235},[2225,187180,5286],{"class":2291},[2225,187182,8146],{"class":2239},[2225,187184,187185,187187,187189,187191,187193,187195,187197],{"class":2227,"line":6548},[2225,187186,80721],{"class":2235},[2225,187188,27496],{"class":2291},[2225,187190,2789],{"class":2239},[2225,187192,2805],{"class":2235},[2225,187194,59721],{"class":2252},[2225,187196,2805],{"class":2235},[2225,187198,2362],{"class":2239},[2225,187200,187201,187203,187205,187207,187209,187211,187213,187215,187217,187219,187221,187223,187225,187227,187229],{"class":2227,"line":6576},[2225,187202,80721],{"class":2235},[2225,187204,4449],{"class":2291},[2225,187206,2789],{"class":2239},[2225,187208,2792],{"class":2235},[2225,187210,27452],{"class":2307},[2225,187212,2311],{"class":2235},[2225,187214,27298],{"class":2291},[2225,187216,2789],{"class":2239},[2225,187218,2805],{"class":2235},[2225,187220,6617],{"class":2252},[2225,187222,2805],{"class":2235},[2225,187224,8495],{"class":2239},[2225,187226,2359],{"class":2235},[2225,187228,1381],{"class":2239},[2225,187230,1707],{"class":2235},[2225,187232,187233,187235,187237,187239,187241,187243,187245,187247,187249,187251],{"class":2227,"line":6582},[2225,187234,179150],{"class":2291},[2225,187236,2311],{"class":2235},[2225,187238,953],{"class":2235},[2225,187240,134393],{"class":7052},[2225,187242,1381],{"class":2235},[2225,187244,8116],{"class":2774},[2225,187246,134431],{"class":2239},[2225,187248,1520],{"class":2235},[2225,187250,59721],{"class":2239},[2225,187252,1707],{"class":2235},[2225,187254,187255,187257,187259,187261,187263,187265,187267,187269],{"class":2227,"line":6587},[2225,187256,139020],{"class":2307},[2225,187258,2311],{"class":2235},[2225,187260,8667],{"class":2239},[2225,187262,2805],{"class":2235},[2225,187264,6487],{"class":2252},[2225,187266,2805],{"class":2235},[2225,187268,8803],{"class":2239},[2225,187270,1707],{"class":2235},[2225,187272,187273,187275,187277,187279,187281,187283],{"class":2227,"line":7174},[2225,187274,179194],{"class":2307},[2225,187276,2311],{"class":2235},[2225,187278,2249],{"class":2235},[2225,187280,138257],{"class":2252},[2225,187282,2805],{"class":2235},[2225,187284,1707],{"class":2235},[2225,187286,187287,187289],{"class":2227,"line":7180},[2225,187288,2359],{"class":2235},[2225,187290,2362],{"class":2239},[2225,187292,187293],{"class":2227,"line":7220},[2225,187294,2285],{"emptyLinePlaceholder":2284},[2225,187296,187297],{"class":2227,"line":7225},[2225,187298,187299],{"class":2763},"\u002F\u002F Overlay: the virtual collection wiring base → overlay via a shadow field.\n",[2225,187301,187302,187304,187307,187309,187311,187313],{"class":2227,"line":7231},[2225,187303,2775],{"class":2774},[2225,187305,187306],{"class":2239}," invoiceTotalsLive ",[2225,187308,2781],{"class":2235},[2225,187310,187015],{"class":2291},[2225,187312,2789],{"class":2239},[2225,187314,4200],{"class":2235},[2225,187316,187317,187319,187321,187323,187326,187328,187330],{"class":2227,"line":7267},[2225,187318,7008],{"class":2307},[2225,187320,2311],{"class":2235},[2225,187322,2249],{"class":2235},[2225,187324,187325],{"class":2252},"invoice-totals-live",[2225,187327,2805],{"class":2235},[2225,187329,2298],{"class":2235},[2225,187331,187332],{"class":2763},"        \u002F\u002F the merged collection you read from\n",[2225,187334,187335,187338,187340,187342,187344,187346,187348],{"class":2227,"line":7272},[2225,187336,187337],{"class":2307},"  base",[2225,187339,2311],{"class":2235},[2225,187341,2249],{"class":2235},[2225,187343,187132],{"class":2252},[2225,187345,2805],{"class":2235},[2225,187347,2298],{"class":2235},[2225,187349,187350],{"class":2763},"             \u002F\u002F the materialized view's output\n",[2225,187352,187353,187356,187358,187360,187363,187365,187367],{"class":2227,"line":7278},[2225,187354,187355],{"class":2307},"  overlay",[2225,187357,2311],{"class":2235},[2225,187359,2249],{"class":2235},[2225,187361,187362],{"class":2252},"invoice-totals-overlay",[2225,187364,2805],{"class":2235},[2225,187366,2298],{"class":2235},[2225,187368,187369],{"class":2763},"  \u002F\u002F a real, writable collection\n",[2225,187371,187372,187375,187377,187379,187381,187383],{"class":2227,"line":7303},[2225,187373,187374],{"class":2307},"  shadowField",[2225,187376,2311],{"class":2235},[2225,187378,2249],{"class":2235},[2225,187380,6631],{"class":2252},[2225,187382,2805],{"class":2235},[2225,187384,1707],{"class":2235},[2225,187386,187387,187390,187392,187394,187396,187398],{"class":2227,"line":7308},[2225,187388,187389],{"class":2307},"  shadowValue",[2225,187391,2311],{"class":2235},[2225,187393,2249],{"class":2235},[2225,187395,12962],{"class":2252},[2225,187397,2805],{"class":2235},[2225,187399,1707],{"class":2235},[2225,187401,187402,187404],{"class":2227,"line":7314},[2225,187403,2359],{"class":2235},[2225,187405,2362],{"class":2239},[2225,187407,187408],{"class":2227,"line":7348},[2225,187409,2285],{"emptyLinePlaceholder":2284},[2225,187411,187412,187414,187416,187418,187420,187422,187424],{"class":2227,"line":8613},[2225,187413,2775],{"class":2774},[2225,187415,2778],{"class":2239},[2225,187417,2781],{"class":2235},[2225,187419,2784],{"class":2231},[2225,187421,2744],{"class":2291},[2225,187423,2789],{"class":2239},[2225,187425,4200],{"class":2235},[2225,187427,187428,187430,187432,187434],{"class":2227,"line":8633},[2225,187429,15608],{"class":2307},[2225,187431,2311],{"class":2235},[2225,187433,179239],{"class":2763},[2225,187435,1707],{"class":2235},[2225,187437,187438,187440,187442,187444,187446,187448],{"class":2227,"line":8662},[2225,187439,4205],{"class":2307},[2225,187441,2311],{"class":2235},[2225,187443,2249],{"class":2235},[2225,187445,6408],{"class":2252},[2225,187447,2805],{"class":2235},[2225,187449,1707],{"class":2235},[2225,187451,187452,187454,187456,187458,187460,187462],{"class":2227,"line":8747},[2225,187453,10423],{"class":2307},[2225,187455,2311],{"class":2235},[2225,187457,2249],{"class":2235},[2225,187459,6421],{"class":2252},[2225,187461,2805],{"class":2235},[2225,187463,1707],{"class":2235},[2225,187465,187466,187468,187470,187473],{"class":2227,"line":8752},[2225,187467,179274],{"class":2307},[2225,187469,2311],{"class":2235},[2225,187471,187472],{"class":2239}," [invoiceTotals]",[2225,187474,1707],{"class":2235},[2225,187476,187477,187480,187482,187485],{"class":2227,"line":8768},[2225,187478,187479],{"class":2307},"  overlayedViewStrategies",[2225,187481,2311],{"class":2235},[2225,187483,187484],{"class":2239}," [invoiceTotalsLive]",[2225,187486,1707],{"class":2235},[2225,187488,187489,187491],{"class":2227,"line":8785},[2225,187490,2359],{"class":2235},[2225,187492,2362],{"class":2239},[937,187494,187495,187498],{},[941,187496,187497],{},"overlayedViewStrategies"," is the only way to register an overlay — without it, the virtual\ncollection name doesn't exist on the vault.",[1022,187500,29893],{"id":29892},[1591,187502,187504],{"id":187503},"reading-the-merged-collection","Reading the merged collection",[2217,187506,187508],{"className":2219,"code":187507,"language":2221,"meta":1037,"style":1037},"const vault = await db.openVault('demo')\n\nawait vault.collection\u003CInvoice>('invoices').put('inv-1', { id: 'inv-1', clientId: 'acme', amount: 120 })\nawait vault.collection\u003CInvoice>('invoices').put('inv-2', { id: 'inv-2', clientId: 'acme', amount: 80 })\n\n\u002F\u002F No overlay row yet — the merged read falls through to the computed base row.\nconst row1 = await vault.collection('invoice-totals-live').get('acme')\n\u002F\u002F → { clientId: 'acme', total: 200 }\n\n\u002F\u002F An operator corrects the total by writing to the overlay collection directly.\nawait vault.collection('invoice-totals-overlay').put('acme', {\n  clientId: 'acme', total: 250, status: 'override',\n})\n\nconst row2 = await vault.collection('invoice-totals-live').get('acme')\n\u002F\u002F → { clientId: 'acme', total: 250, status: 'override' }\n",[941,187509,187510,187536,187540,187616,187692,187696,187701,187742,187747,187751,187756,187792,187827,187833,187837,187878],{"__ignoreMap":1037},[2225,187511,187512,187514,187516,187518,187520,187522,187524,187526,187528,187530,187532,187534],{"class":2227,"line":2228},[2225,187513,2775],{"class":2774},[2225,187515,2835],{"class":2239},[2225,187517,2781],{"class":2235},[2225,187519,2784],{"class":2231},[2225,187521,2842],{"class":2239},[2225,187523,1520],{"class":2235},[2225,187525,2847],{"class":2291},[2225,187527,2789],{"class":2239},[2225,187529,2805],{"class":2235},[2225,187531,97914],{"class":2252},[2225,187533,2805],{"class":2235},[2225,187535,2362],{"class":2239},[2225,187537,187538],{"class":2227,"line":1041},[2225,187539,2285],{"emptyLinePlaceholder":2284},[2225,187541,187542,187544,187546,187548,187550,187552,187554,187556,187558,187560,187562,187564,187566,187568,187570,187572,187574,187576,187578,187580,187582,187584,187586,187588,187590,187592,187594,187596,187598,187600,187602,187604,187606,187608,187610,187612,187614],{"class":2227,"line":1038},[2225,187543,6500],{"class":2231},[2225,187545,6466],{"class":2239},[2225,187547,1520],{"class":2235},[2225,187549,6471],{"class":2291},[2225,187551,6474],{"class":2235},[2225,187553,6477],{"class":6342},[2225,187555,6480],{"class":2235},[2225,187557,2789],{"class":2239},[2225,187559,2805],{"class":2235},[2225,187561,6487],{"class":2252},[2225,187563,2805],{"class":2235},[2225,187565,1381],{"class":2239},[2225,187567,1520],{"class":2235},[2225,187569,3161],{"class":2291},[2225,187571,2789],{"class":2239},[2225,187573,2805],{"class":2235},[2225,187575,6514],{"class":2252},[2225,187577,2805],{"class":2235},[2225,187579,2298],{"class":2235},[2225,187581,2236],{"class":2235},[2225,187583,7070],{"class":2307},[2225,187585,2311],{"class":2235},[2225,187587,2249],{"class":2235},[2225,187589,6514],{"class":2252},[2225,187591,2805],{"class":2235},[2225,187593,2298],{"class":2235},[2225,187595,62649],{"class":2307},[2225,187597,2311],{"class":2235},[2225,187599,2249],{"class":2235},[2225,187601,6450],{"class":2252},[2225,187603,2805],{"class":2235},[2225,187605,2298],{"class":2235},[2225,187607,6351],{"class":2307},[2225,187609,2311],{"class":2235},[2225,187611,28675],{"class":6527},[2225,187613,2243],{"class":2235},[2225,187615,2362],{"class":2239},[2225,187617,187618,187620,187622,187624,187626,187628,187630,187632,187634,187636,187638,187640,187642,187644,187646,187648,187650,187652,187654,187656,187658,187660,187662,187664,187666,187668,187670,187672,187674,187676,187678,187680,187682,187684,187686,187688,187690],{"class":2227,"line":2288},[2225,187619,6500],{"class":2231},[2225,187621,6466],{"class":2239},[2225,187623,1520],{"class":2235},[2225,187625,6471],{"class":2291},[2225,187627,6474],{"class":2235},[2225,187629,6477],{"class":6342},[2225,187631,6480],{"class":2235},[2225,187633,2789],{"class":2239},[2225,187635,2805],{"class":2235},[2225,187637,6487],{"class":2252},[2225,187639,2805],{"class":2235},[2225,187641,1381],{"class":2239},[2225,187643,1520],{"class":2235},[2225,187645,3161],{"class":2291},[2225,187647,2789],{"class":2239},[2225,187649,2805],{"class":2235},[2225,187651,26080],{"class":2252},[2225,187653,2805],{"class":2235},[2225,187655,2298],{"class":2235},[2225,187657,2236],{"class":2235},[2225,187659,7070],{"class":2307},[2225,187661,2311],{"class":2235},[2225,187663,2249],{"class":2235},[2225,187665,26080],{"class":2252},[2225,187667,2805],{"class":2235},[2225,187669,2298],{"class":2235},[2225,187671,62649],{"class":2307},[2225,187673,2311],{"class":2235},[2225,187675,2249],{"class":2235},[2225,187677,6450],{"class":2252},[2225,187679,2805],{"class":2235},[2225,187681,2298],{"class":2235},[2225,187683,6351],{"class":2307},[2225,187685,2311],{"class":2235},[2225,187687,179510],{"class":6527},[2225,187689,2243],{"class":2235},[2225,187691,2362],{"class":2239},[2225,187693,187694],{"class":2227,"line":2304},[2225,187695,2285],{"emptyLinePlaceholder":2284},[2225,187697,187698],{"class":2227,"line":2356},[2225,187699,187700],{"class":2763},"\u002F\u002F No overlay row yet — the merged read falls through to the computed base row.\n",[2225,187702,187703,187705,187708,187710,187712,187714,187716,187718,187720,187722,187724,187726,187728,187730,187732,187734,187736,187738,187740],{"class":2227,"line":2830},[2225,187704,2775],{"class":2774},[2225,187706,187707],{"class":2239}," row1 ",[2225,187709,2781],{"class":2235},[2225,187711,2784],{"class":2231},[2225,187713,6466],{"class":2239},[2225,187715,1520],{"class":2235},[2225,187717,6471],{"class":2291},[2225,187719,2789],{"class":2239},[2225,187721,2805],{"class":2235},[2225,187723,187325],{"class":2252},[2225,187725,2805],{"class":2235},[2225,187727,1381],{"class":2239},[2225,187729,1520],{"class":2235},[2225,187731,3158],{"class":2291},[2225,187733,2789],{"class":2239},[2225,187735,2805],{"class":2235},[2225,187737,6450],{"class":2252},[2225,187739,2805],{"class":2235},[2225,187741,2362],{"class":2239},[2225,187743,187744],{"class":2227,"line":2861},[2225,187745,187746],{"class":2763},"\u002F\u002F → { clientId: 'acme', total: 200 }\n",[2225,187748,187749],{"class":2227,"line":4246},[2225,187750,2285],{"emptyLinePlaceholder":2284},[2225,187752,187753],{"class":2227,"line":4253},[2225,187754,187755],{"class":2763},"\u002F\u002F An operator corrects the total by writing to the overlay collection directly.\n",[2225,187757,187758,187760,187762,187764,187766,187768,187770,187772,187774,187776,187778,187780,187782,187784,187786,187788,187790],{"class":2227,"line":4259},[2225,187759,6500],{"class":2231},[2225,187761,6466],{"class":2239},[2225,187763,1520],{"class":2235},[2225,187765,6471],{"class":2291},[2225,187767,2789],{"class":2239},[2225,187769,2805],{"class":2235},[2225,187771,187362],{"class":2252},[2225,187773,2805],{"class":2235},[2225,187775,1381],{"class":2239},[2225,187777,1520],{"class":2235},[2225,187779,3161],{"class":2291},[2225,187781,2789],{"class":2239},[2225,187783,2805],{"class":2235},[2225,187785,6450],{"class":2252},[2225,187787,2805],{"class":2235},[2225,187789,2298],{"class":2235},[2225,187791,2301],{"class":2235},[2225,187793,187794,187796,187798,187800,187802,187804,187806,187808,187810,187813,187815,187817,187819,187821,187823,187825],{"class":2227,"line":6548},[2225,187795,58854],{"class":2307},[2225,187797,2311],{"class":2235},[2225,187799,2249],{"class":2235},[2225,187801,6450],{"class":2252},[2225,187803,2805],{"class":2235},[2225,187805,2298],{"class":2235},[2225,187807,27452],{"class":2307},[2225,187809,2311],{"class":2235},[2225,187811,187812],{"class":6527}," 250",[2225,187814,2298],{"class":2235},[2225,187816,6361],{"class":2307},[2225,187818,2311],{"class":2235},[2225,187820,2249],{"class":2235},[2225,187822,12962],{"class":2252},[2225,187824,2805],{"class":2235},[2225,187826,1707],{"class":2235},[2225,187828,187829,187831],{"class":2227,"line":6576},[2225,187830,2359],{"class":2235},[2225,187832,2362],{"class":2239},[2225,187834,187835],{"class":2227,"line":6582},[2225,187836,2285],{"emptyLinePlaceholder":2284},[2225,187838,187839,187841,187844,187846,187848,187850,187852,187854,187856,187858,187860,187862,187864,187866,187868,187870,187872,187874,187876],{"class":2227,"line":6587},[2225,187840,2775],{"class":2774},[2225,187842,187843],{"class":2239}," row2 ",[2225,187845,2781],{"class":2235},[2225,187847,2784],{"class":2231},[2225,187849,6466],{"class":2239},[2225,187851,1520],{"class":2235},[2225,187853,6471],{"class":2291},[2225,187855,2789],{"class":2239},[2225,187857,2805],{"class":2235},[2225,187859,187325],{"class":2252},[2225,187861,2805],{"class":2235},[2225,187863,1381],{"class":2239},[2225,187865,1520],{"class":2235},[2225,187867,3158],{"class":2291},[2225,187869,2789],{"class":2239},[2225,187871,2805],{"class":2235},[2225,187873,6450],{"class":2252},[2225,187875,2805],{"class":2235},[2225,187877,2362],{"class":2239},[2225,187879,187880],{"class":2227,"line":7174},[2225,187881,187882],{"class":2763},"\u002F\u002F → { clientId: 'acme', total: 250, status: 'override' }\n",[1591,187884,187886],{"id":187885},"writing-and-deleting-through-the-virtual-collection","Writing and deleting through the virtual collection",[937,187888,187889],{},"Writes always route to the overlay collection — the base collection is never touched by a write\nthrough the virtual name:",[2217,187891,187893],{"className":2219,"code":187892,"language":2221,"meta":1037,"style":1037},"\u002F\u002F put(record): id is derived from the base materialized view's rowKey.\nawait vault.collection('invoice-totals-live').put({ clientId: 'acme', total: 300, status: 'override' })\n\n\u002F\u002F put(id, record): id must match rowKey(record), else throws OverlayIdMismatchError.\nawait vault.collection('invoice-totals-live').put('acme', { clientId: 'acme', total: 300, status: 'override' })\n\n\u002F\u002F delete removes the overlay row only — the base row resurfaces on the next read.\nawait vault.collection('invoice-totals-live').delete('acme')\n",[941,187894,187895,187900,187962,187966,187971,188041,188045,188050],{"__ignoreMap":1037},[2225,187896,187897],{"class":2227,"line":2228},[2225,187898,187899],{"class":2763},"\u002F\u002F put(record): id is derived from the base materialized view's rowKey.\n",[2225,187901,187902,187904,187906,187908,187910,187912,187914,187916,187918,187920,187922,187924,187926,187928,187930,187932,187934,187936,187938,187940,187942,187944,187946,187948,187950,187952,187954,187956,187958,187960],{"class":2227,"line":1041},[2225,187903,6500],{"class":2231},[2225,187905,6466],{"class":2239},[2225,187907,1520],{"class":2235},[2225,187909,6471],{"class":2291},[2225,187911,2789],{"class":2239},[2225,187913,2805],{"class":2235},[2225,187915,187325],{"class":2252},[2225,187917,2805],{"class":2235},[2225,187919,1381],{"class":2239},[2225,187921,1520],{"class":2235},[2225,187923,3161],{"class":2291},[2225,187925,2789],{"class":2239},[2225,187927,2792],{"class":2235},[2225,187929,62649],{"class":2307},[2225,187931,2311],{"class":2235},[2225,187933,2249],{"class":2235},[2225,187935,6450],{"class":2252},[2225,187937,2805],{"class":2235},[2225,187939,2298],{"class":2235},[2225,187941,27452],{"class":2307},[2225,187943,2311],{"class":2235},[2225,187945,171220],{"class":6527},[2225,187947,2298],{"class":2235},[2225,187949,6361],{"class":2307},[2225,187951,2311],{"class":2235},[2225,187953,2249],{"class":2235},[2225,187955,12962],{"class":2252},[2225,187957,2805],{"class":2235},[2225,187959,2243],{"class":2235},[2225,187961,2362],{"class":2239},[2225,187963,187964],{"class":2227,"line":1038},[2225,187965,2285],{"emptyLinePlaceholder":2284},[2225,187967,187968],{"class":2227,"line":2288},[2225,187969,187970],{"class":2763},"\u002F\u002F put(id, record): id must match rowKey(record), else throws OverlayIdMismatchError.\n",[2225,187972,187973,187975,187977,187979,187981,187983,187985,187987,187989,187991,187993,187995,187997,187999,188001,188003,188005,188007,188009,188011,188013,188015,188017,188019,188021,188023,188025,188027,188029,188031,188033,188035,188037,188039],{"class":2227,"line":2304},[2225,187974,6500],{"class":2231},[2225,187976,6466],{"class":2239},[2225,187978,1520],{"class":2235},[2225,187980,6471],{"class":2291},[2225,187982,2789],{"class":2239},[2225,187984,2805],{"class":2235},[2225,187986,187325],{"class":2252},[2225,187988,2805],{"class":2235},[2225,187990,1381],{"class":2239},[2225,187992,1520],{"class":2235},[2225,187994,3161],{"class":2291},[2225,187996,2789],{"class":2239},[2225,187998,2805],{"class":2235},[2225,188000,6450],{"class":2252},[2225,188002,2805],{"class":2235},[2225,188004,2298],{"class":2235},[2225,188006,2236],{"class":2235},[2225,188008,62649],{"class":2307},[2225,188010,2311],{"class":2235},[2225,188012,2249],{"class":2235},[2225,188014,6450],{"class":2252},[2225,188016,2805],{"class":2235},[2225,188018,2298],{"class":2235},[2225,188020,27452],{"class":2307},[2225,188022,2311],{"class":2235},[2225,188024,171220],{"class":6527},[2225,188026,2298],{"class":2235},[2225,188028,6361],{"class":2307},[2225,188030,2311],{"class":2235},[2225,188032,2249],{"class":2235},[2225,188034,12962],{"class":2252},[2225,188036,2805],{"class":2235},[2225,188038,2243],{"class":2235},[2225,188040,2362],{"class":2239},[2225,188042,188043],{"class":2227,"line":2356},[2225,188044,2285],{"emptyLinePlaceholder":2284},[2225,188046,188047],{"class":2227,"line":2830},[2225,188048,188049],{"class":2763},"\u002F\u002F delete removes the overlay row only — the base row resurfaces on the next read.\n",[2225,188051,188052,188054,188056,188058,188060,188062,188064,188066,188068,188070,188072,188074,188076,188078,188080,188082],{"class":2227,"line":2861},[2225,188053,6500],{"class":2231},[2225,188055,6466],{"class":2239},[2225,188057,1520],{"class":2235},[2225,188059,6471],{"class":2291},[2225,188061,2789],{"class":2239},[2225,188063,2805],{"class":2235},[2225,188065,187325],{"class":2252},[2225,188067,2805],{"class":2235},[2225,188069,1381],{"class":2239},[2225,188071,1520],{"class":2235},[2225,188073,3164],{"class":2291},[2225,188075,2789],{"class":2239},[2225,188077,2805],{"class":2235},[2225,188079,6450],{"class":2252},[2225,188081,2805],{"class":2235},[2225,188083,2362],{"class":2239},[1591,188085,188087],{"id":188086},"field-level-merge-instead-of-swapping-the-whole-row","Field-level merge instead of swapping the whole row",[937,188089,124907,188090,188093],{},[941,188091,188092],{},"mergeMode"," to pull only a declared subset of fields from the overlay when an intermediate\nstatus matches, leaving every other field on the base row:",[2217,188095,188097],{"className":2219,"code":188096,"language":2221,"meta":1037,"style":1037},"withOverlayedView({\n  name: 'invoice-totals-live',\n  base: 'invoice-totals',\n  overlay: 'invoice-totals-overlay',\n  shadowField: 'status',\n  shadowValue: 'override',   \u002F\u002F full-override path is unchanged by mergeMode\n  mergeMode: {\n    kind: 'field-merge',\n    rules: [\n      { whenStatus: 'amount-corrected', overlayFields: ['status', 'total'] },\n      { whenStatus: 'flagged', overlayFields: ['status', 'reviewer'] },\n    ],\n  },\n})\n",[941,188098,188099,188108,188122,188136,188150,188164,188181,188190,188206,188215,188258,188300,188306,188310],{"__ignoreMap":1037},[2225,188100,188101,188104,188106],{"class":2227,"line":2228},[2225,188102,188103],{"class":2291},"withOverlayedView",[2225,188105,2789],{"class":2239},[2225,188107,4200],{"class":2235},[2225,188109,188110,188112,188114,188116,188118,188120],{"class":2227,"line":1041},[2225,188111,7008],{"class":2307},[2225,188113,2311],{"class":2235},[2225,188115,2249],{"class":2235},[2225,188117,187325],{"class":2252},[2225,188119,2805],{"class":2235},[2225,188121,1707],{"class":2235},[2225,188123,188124,188126,188128,188130,188132,188134],{"class":2227,"line":1038},[2225,188125,187337],{"class":2307},[2225,188127,2311],{"class":2235},[2225,188129,2249],{"class":2235},[2225,188131,187132],{"class":2252},[2225,188133,2805],{"class":2235},[2225,188135,1707],{"class":2235},[2225,188137,188138,188140,188142,188144,188146,188148],{"class":2227,"line":2288},[2225,188139,187355],{"class":2307},[2225,188141,2311],{"class":2235},[2225,188143,2249],{"class":2235},[2225,188145,187362],{"class":2252},[2225,188147,2805],{"class":2235},[2225,188149,1707],{"class":2235},[2225,188151,188152,188154,188156,188158,188160,188162],{"class":2227,"line":2304},[2225,188153,187374],{"class":2307},[2225,188155,2311],{"class":2235},[2225,188157,2249],{"class":2235},[2225,188159,6631],{"class":2252},[2225,188161,2805],{"class":2235},[2225,188163,1707],{"class":2235},[2225,188165,188166,188168,188170,188172,188174,188176,188178],{"class":2227,"line":2356},[2225,188167,187389],{"class":2307},[2225,188169,2311],{"class":2235},[2225,188171,2249],{"class":2235},[2225,188173,12962],{"class":2252},[2225,188175,2805],{"class":2235},[2225,188177,2298],{"class":2235},[2225,188179,188180],{"class":2763},"   \u002F\u002F full-override path is unchanged by mergeMode\n",[2225,188182,188183,188186,188188],{"class":2227,"line":2830},[2225,188184,188185],{"class":2307},"  mergeMode",[2225,188187,2311],{"class":2235},[2225,188189,2301],{"class":2235},[2225,188191,188192,188195,188197,188199,188202,188204],{"class":2227,"line":2861},[2225,188193,188194],{"class":2307},"    kind",[2225,188196,2311],{"class":2235},[2225,188198,2249],{"class":2235},[2225,188200,188201],{"class":2252},"field-merge",[2225,188203,2805],{"class":2235},[2225,188205,1707],{"class":2235},[2225,188207,188208,188211,188213],{"class":2227,"line":4246},[2225,188209,188210],{"class":2307},"    rules",[2225,188212,2311],{"class":2235},[2225,188214,32491],{"class":2239},[2225,188216,188217,188219,188222,188224,188226,188229,188231,188233,188236,188238,188240,188242,188244,188246,188248,188250,188252,188254,188256],{"class":2227,"line":4253},[2225,188218,88593],{"class":2235},[2225,188220,188221],{"class":2307}," whenStatus",[2225,188223,2311],{"class":2235},[2225,188225,2249],{"class":2235},[2225,188227,188228],{"class":2252},"amount-corrected",[2225,188230,2805],{"class":2235},[2225,188232,2298],{"class":2235},[2225,188234,188235],{"class":2307}," overlayFields",[2225,188237,2311],{"class":2235},[2225,188239,8667],{"class":2239},[2225,188241,2805],{"class":2235},[2225,188243,6631],{"class":2252},[2225,188245,2805],{"class":2235},[2225,188247,2298],{"class":2235},[2225,188249,2249],{"class":2235},[2225,188251,64200],{"class":2252},[2225,188253,2805],{"class":2235},[2225,188255,8890],{"class":2239},[2225,188257,19008],{"class":2235},[2225,188259,188260,188262,188264,188266,188268,188271,188273,188275,188277,188279,188281,188283,188285,188287,188289,188291,188294,188296,188298],{"class":2227,"line":4259},[2225,188261,88593],{"class":2235},[2225,188263,188221],{"class":2307},[2225,188265,2311],{"class":2235},[2225,188267,2249],{"class":2235},[2225,188269,188270],{"class":2252},"flagged",[2225,188272,2805],{"class":2235},[2225,188274,2298],{"class":2235},[2225,188276,188235],{"class":2307},[2225,188278,2311],{"class":2235},[2225,188280,8667],{"class":2239},[2225,188282,2805],{"class":2235},[2225,188284,6631],{"class":2252},[2225,188286,2805],{"class":2235},[2225,188288,2298],{"class":2235},[2225,188290,2249],{"class":2235},[2225,188292,188293],{"class":2252},"reviewer",[2225,188295,2805],{"class":2235},[2225,188297,8890],{"class":2239},[2225,188299,19008],{"class":2235},[2225,188301,188302,188304],{"class":2227,"line":6548},[2225,188303,158220],{"class":2239},[2225,188305,1707],{"class":2235},[2225,188307,188308],{"class":2227,"line":6576},[2225,188309,19017],{"class":2235},[2225,188311,188312,188314],{"class":2227,"line":6582},[2225,188313,2359],{"class":2235},[2225,188315,2362],{"class":2239},[937,188317,188318,188319,188321,188322,188325,188326,188329,188330,188333,188334,188336,188337,188339],{},"Rules are checked in declaration order; the ",[950,188320,3233],{}," rule whose ",[941,188323,188324],{},"whenStatus"," equals the overlay\nrow's shadow field wins, and only that rule's ",[941,188327,188328],{},"overlayFields"," are pulled from the overlay — every\nother field still comes from the base row. The binary full-override check\n(",[941,188331,188332],{},"shadowField === shadowValue",") always runs first and ignores ",[941,188335,188092],{}," entirely, so adding\n",[941,188338,188092],{}," never changes existing full-override behavior.",[1022,188341,102889],{"id":102888},[1591,188343,188345],{"id":188344},"withoverlayedviewspec",[941,188346,188347],{},"withOverlayedView(spec)",[937,188349,188350,188351,188354,188355,188357,188358,1707,188360,968,188362,4765,188364,188367,188368,968,188370,2462,188372,188374],{},"Registers an overlay strategy and returns a handle you pass to\n",[941,188352,188353],{},"createNoydb({ overlayedViewStrategies: [...] })",". Throws ",[941,188356,12881],{}," immediately if ",[941,188359,4914],{},[941,188361,57272],{},[941,188363,186964],{},[941,188365,188366],{},"shadowField"," is missing, or if ",[941,188369,4914],{},[941,188371,57272],{},[941,188373,186964],{}," aren't three\ndistinct collection names.",[1338,188376,188377,188387],{},[1341,188378,188379],{},[1344,188380,188381,188383,188385],{},[1347,188382,101104],{},[1347,188384,180039],{"align":9536},[1347,188386,20200],{},[1357,188388,188389,188408,188419,188430,188445,188460],{},[1344,188390,188391,188395,188397],{},[1362,188392,188393],{},[941,188394,4914],{},[1362,188396,14852],{"align":9536},[1362,188398,188399,188400,188403,188404,997,188406,1520],{},"Name of the virtual collection. ",[941,188401,188402],{},"vault.collection(name)"," returns the merged read\u002Fwrite proxy. Must differ from ",[941,188405,57272],{},[941,188407,186964],{},[1344,188409,188410,188414,188416],{},[1362,188411,188412],{},[941,188413,57272],{},[1362,188415,14852],{"align":9536},[1362,188417,188418],{},"The collection providing the default row — typically a materialized view's output, but any concrete (non-virtual) collection works.",[1344,188420,188421,188425,188427],{},[1362,188422,188423],{},[941,188424,186964],{},[1362,188426,14852],{"align":9536},[1362,188428,188429],{},"A real, vault-known, user-writable collection that is not a materialized-view output. Carries the override rows.",[1344,188431,188432,188436,188438],{},[1362,188433,188434],{},[941,188435,188366],{},[1362,188437,14852],{"align":9536},[1362,188439,188440,188441,188444],{},"The overlay field checked against ",[941,188442,188443],{},"shadowValue"," to decide whether the overlay row wins.",[1344,188446,188447,188451,188453],{},[1362,188448,188449],{},[941,188450,188443],{},[1362,188452,14852],{"align":9536},[1362,188454,188455,188456,188459],{},"The value that, when equal to ",[941,188457,188458],{},"overlay[shadowField]",", makes the overlay row win entirely for that id.",[1344,188461,188462,188467,188469],{},[1362,188463,188464],{},[941,188465,188466],{},"mergeMode?",[1362,188468,11313],{"align":9536},[1362,188470,188471,188474],{},[941,188472,188473],{},"{ kind: 'field-merge', rules: [{ whenStatus, overlayFields }] }"," — pulls a declared subset of fields from the overlay for an intermediate status, instead of an all-or-nothing swap.",[1591,188476,188478,188479],{"id":188477},"the-merged-collection-vaultcollectionname","The merged collection — ",[941,188480,188402],{},[1338,188482,188483,188491],{},[1341,188484,188485],{},[1344,188486,188487,188489],{},[1347,188488,7831],{},[1347,188490,124024],{},[1357,188492,188493,188512,188521,188551,188573],{},[1344,188494,188495,188499],{},[1362,188496,188497],{},[941,188498,131714],{},[1362,188500,188501,188502,188505,188506,188508,188509,188511],{},"Overlay row wins if ",[941,188503,188504],{},"overlay[shadowField] === shadowValue","; else the ",[941,188507,188092],{},"-blended row if a rule matches; else the base row; ",[941,188510,6651],{}," if neither side has the id.",[1344,188513,188514,188518],{},[1362,188515,188516],{},[941,188517,24407],{},[1362,188519,188520],{},"Union of base + overlay ids, with the same per-id merge applied to each.",[1344,188522,188523,188528],{},[1362,188524,188525],{},[941,188526,188527],{},"put(record)",[1362,188529,188530,188531,188533,188534,188536,188537,188539,188540,102958,188542,188544,188545,188547,188548,33515],{},"Derives ",[941,188532,6983],{}," via the base materialized view's ",[941,188535,180020],{},", then writes to ",[941,188538,186964],{},". Throws a plain ",[941,188541,12126],{},[941,188543,57272],{}," isn't a materialized view (no ",[941,188546,180020],{}," to derive from) — use ",[941,188549,188550],{},"put(id, record)",[1344,188552,188553,188557],{},[1362,188554,188555],{},[941,188556,188550],{},[1362,188558,188559,188560,46975,188562,188354,188564,102958,188567,188569,188570,1520],{},"Writes to ",[941,188561,186964],{},[941,188563,6983],{},[941,188565,188566],{},"OverlayIdMismatchError",[941,188568,6983],{}," doesn't match the base MV's ",[941,188571,188572],{},"rowKey(record)",[1344,188574,188575,188580],{},[1362,188576,188577],{},[941,188578,188579],{},"delete(id)",[1362,188581,188582],{},"Removes the overlay row only. The base row is untouched and resurfaces on the next read.",[1591,188584,120265],{"id":120264},[937,188586,140885,188587,180908,188589,2311],{},[941,188588,140888],{},[941,188590,73087],{},[1111,188592,188593,188610,188622,188632,188642],{},[1114,188594,188595,188597,188598,188600,188601,1707,188603,968,188605,188607,188608,1520],{},[941,188596,12881],{}," — thrown by ",[941,188599,186950],{}," itself for a missing or duplicate ",[941,188602,4914],{},[941,188604,57272],{},[941,188606,186964],{},", or a missing ",[941,188609,188366],{},[1114,188611,188612,188615,188616,182638,188619,188621],{},[941,188613,188614],{},"OverlayNameCollisionError(name)"," — thrown at ",[941,188617,188618],{},"db.openVault()",[941,188620,4914],{}," collides with a\nmaterialized-view output or another registered overlay's virtual name.",[1114,188623,188624,188615,188627,182638,188629,188631],{},[941,188625,188626],{},"OverlayBaseIsVirtualError(name, base)",[941,188628,188618],{},[941,188630,57272],{}," names another\noverlay's virtual collection; overlay-on-overlay stacking isn't supported.",[1114,188633,188634,188615,188637,182638,188639,188641],{},[941,188635,188636],{},"OverlayCollectionUnavailableError(name, overlay)",[941,188638,188618],{},[941,188640,186964],{},"\nnames a materialized-view output instead of a real writable collection.",[1114,188643,188644,188647,188648,182638,188650,188652,188653,1520],{},[941,188645,188646],{},"OverlayIdMismatchError(actual, expected)"," — thrown from ",[941,188649,188550],{},[941,188651,6983],{}," doesn't\nequal the base MV's ",[941,188654,188572],{},[1022,188656,68210],{"id":68209},[1111,188658,188659,188699,188707,188718,188735,188752],{},[1114,188660,188661,66018,188672,1707,188674,968,188676,968,188678,968,188681,968,188683,968,188685,968,188687,51161,188690,188693,188694,1261,188696,188698],{},[950,188662,188663,188664,968,188666,968,188668,2462,188670,1520],{},"The merged collection only implements ",[941,188665,3158],{},[941,188667,3167],{},[941,188669,3161],{},[941,188671,3164],{},[941,188673,138944],{},[941,188675,174654],{},[941,188677,3027],{},[941,188679,188680],{},".scan()",[941,188682,21910],{},[941,188684,164987],{},[941,188686,65247],{},[941,188688,188689],{},".putManyAtomic()",[941,188691,188692],{},".deleteMany()"," on it throws a clear \"not yet implemented\" error instead of silently\nmisbehaving. Query or subscribe to ",[941,188695,57272],{},[941,188697,186964],{}," directly instead.",[1114,188700,188701,188706],{},[950,188702,188703,188705],{},[941,188704,57272],{}," must be concrete."," A real source collection or a materialized-view output — never\nanother overlay's virtual name. Multi-overlay stacking isn't supported.",[1114,188708,188709,188714,188715,188717],{},[950,188710,188711,188713],{},[941,188712,186964],{}," must be a real, writable collection that isn't a materialized-view output."," Direct\nwrites to it go through its own normal write pipeline, so any ",[1007,188716,693],{"href":124814}," or\nderivations registered on that collection still apply.",[1114,188719,188720,188728,188729,188731,188732,188734],{},[950,188721,188722,188724,188725,188727],{},[941,188723,188527],{}," (single-arg) only works when ",[941,188726,57272],{}," is a materialized view."," With no ",[941,188730,180020],{}," to\nderive an id from, you must call ",[941,188733,188550],{}," explicitly.",[1114,188736,188737,188746,188747,2557,188749,188751],{},[950,188738,188739,188740,188742,188743,188745],{},"An overlay-only row that doesn't match ",[941,188741,188443],{}," (and no ",[941,188744,188092],{}," rule matches) is\ninvisible."," If there's no base row either, ",[941,188748,14639],{},[941,188750,6651],{}," even though a row exists in\nthe overlay collection.",[1114,188753,188754,188763,188764,1625,188766,188768],{},[950,188755,188756,188757,188759,188760,188762],{},"Cross-reference validation happens at ",[941,188758,188618],{},", not at ",[941,188761,186950],{}," call\ntime."," Name collisions and bad ",[941,188765,57272],{},[941,188767,186964],{}," references only surface when the vault opens.",[1022,188770,2621],{"id":2620},[1111,188772,188773,188781,188786,188794,188800],{},[1114,188774,188775,188777,188778,188780],{},[1007,188776,739],{"href":124624}," — the usual source of ",[941,188779,57272],{},": a query kept fresh\nas a queryable collection",[1114,188782,188783,188785],{},[1007,188784,665],{"href":124621}," — the row-level sibling that projects one source record into\ntyped outputs",[1114,188787,188788,188790,188791,188793],{},[1007,188789,693],{"href":124814}," — write-time rules that still apply to direct writes on the ",[941,188792,186964],{},"\ncollection",[1114,188795,188796,1368,188798],{},[1007,188797,61743],{"href":598},[1007,188799,1019],{"href":1018},[1114,188801,101581,188802,1520],{},[1007,188803,188805],{"href":93585,"rel":188804},[3371],[941,188806,188807],{},"83-with-overlay",[2645,188809,188810],{},"html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .sHdIc, html code.shiki .sHdIc{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#EEFFFF;--shiki-default-font-style:italic;--shiki-dark:#BABED8;--shiki-dark-font-style:italic}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}",{"title":1037,"searchDepth":1038,"depth":1038,"links":188812},[188813,188814,188815,188816,188821,188827,188828],{"id":29856,"depth":1041,"text":29857},{"id":70092,"depth":1041,"text":70093},{"id":99863,"depth":1041,"text":99864},{"id":29892,"depth":1041,"text":29893,"children":188817},[188818,188819,188820],{"id":187503,"depth":1038,"text":187504},{"id":187885,"depth":1038,"text":187886},{"id":188086,"depth":1038,"text":188087},{"id":102888,"depth":1041,"text":102889,"children":188822},[188823,188824,188826],{"id":188344,"depth":1038,"text":188347},{"id":188477,"depth":1038,"text":188825},"The merged collection — vault.collection(name)",{"id":120264,"depth":1038,"text":120265},{"id":68209,"depth":1041,"text":68210},{"id":2620,"depth":1041,"text":2621},"withOverlayedView() merges a computed base collection with a user-writable overlay, so an operator can correct one row without touching the computation.",{},{"title":768,"description":188829},"BZFDsTT6C90nxjvsDvGRyDM9a4eYbQkpsTAr4CgzRYk",{"id":188834,"title":772,"access":12,"apiToc":929,"body":188835,"category":1042,"description":188883,"extension":1044,"meta":188884,"navigation":188885,"nest":1047,"path":773,"seo":188886,"sinceVersion":1042,"status":183052,"stem":774,"subtitle":1042,"tier":1050,"tocExternal":1042,"__hash__":188887},"docs\u002Fservices\u002Fpartitioning.md",{"type":931,"value":188836,"toc":188879},[188837,188839,188842,188856,188860,188869,188871],[934,188838,772],{"id":772},[46871,188840],{":rows":188841},"[{\"label\":\"Subpath (reserved, not yet implemented)\",\"value\":\"`@noy-db\u002Fhub\u002Fpartitioning`\"},{\"label\":\"Factory\",\"value\":\"none yet\"},{\"label\":\"Layer\",\"value\":\"not yet assigned\"}]",[937,188843,188844,188845,188847,188848,188851,188852,188855],{},"Partitioning is a planned service that would let a query be scoped to a\nsubset of a ",[1007,188846,6471],{"href":48}," — for example\nonly the last 30 days, only one region, or only one tenant — instead of\nscanning every record. It does not exist yet: there is no ",[941,188849,188850],{},"with-partitioning","\nsubsystem and no factory to opt in with. The only related public surface is\nthe ",[941,188853,188854],{},"JoinLeg.partitionScope"," type field described below, which the query\nexecutor ignores today.",[1022,188857,188859],{"id":188858},"current-status","Current status",[937,188861,188862,188863,188865,188866,188868],{},"The query engine's join logic already carries a per-join-leg ",[941,188864,170602],{},"\nfield internally, but every leg is always scoped to ",[941,188867,163113],{}," today — nothing\nin the engine narrows execution by partition yet. This is internal plumbing,\nnot a public API: there is no way to opt into partition scoping from\napplication code.",[1022,188870,2621],{"id":2620},[1111,188872,188873],{},[1114,188874,188875,1368,188877],{},[1007,188876,61743],{"href":598},[1007,188878,1019],{"href":1018},{"title":1037,"searchDepth":1038,"depth":1038,"links":188880},[188881,188882],{"id":188858,"depth":1041,"text":188859},{"id":2620,"depth":1041,"text":2621},"Scoping query execution to a subset of a collection by time range, region, or tenant — planned, not yet implemented.",{},{"badge":754},{"title":772,"description":188883},"1mqF7swpI5FKvrKIgODHJDmMD9_tURAoVwJf0pqPWGw",{"id":188889,"title":776,"access":12,"apiToc":929,"body":188890,"category":1042,"description":191868,"extension":1044,"meta":191869,"navigation":1042,"nest":1047,"path":777,"seo":191870,"sinceVersion":1042,"status":1049,"stem":778,"subtitle":1042,"tier":1050,"tocExternal":1042,"__hash__":191871},"docs\u002Fservices\u002Fperiods.md",{"type":931,"value":188891,"toc":191839},[188892,188894,188897,188899,188943,188945,188956,188958,188988,188990,189189,189191,189195,189400,189534,189537,189613,189617,189641,189782,189842,189846,189871,189919,189941,189945,189970,190135,190158,190162,190183,190249,190267,190271,190561,190743,190772,190774,190779,190795,190802,190808,190832,190838,190843,190911,190917,190956,190967,190972,191056,191061,191108,191113,191136,191164,191169,191192,191226,191231,191254,191286,191291,191315,191320,191326,191352,191358,191363,191366,191618,191623,191646,191669,191671,191699,191701,191789,191791,191837],[934,188893,776],{"id":776},[46871,188895],{":rows":188896},"[{\"label\":\"Subpath\",\"value\":\"`@noy-db\u002Fhub\u002Fperiods`\"},{\"label\":\"Factory\",\"value\":\"`withPeriods()`\"},{\"label\":\"Layer\",\"value\":\"`with-audit`\"},{\"label\":\"Cluster\",\"value\":\"E — Time & Audit\"},{\"label\":\"LOC cost\",\"value\":\"~334 (off-bundle when not opted in)\"}]",[1022,188898,29857],{"id":29856},[937,188900,188901,188902,188904,188905,188907,188908,188911,188912,188914,188915,188917,188918,4688,188920,188922,188923,1952,188925,188927,188928,188930,188931,188933,188934,188936,188937,188940,188941,1520],{},"Accounting and other regulated systems need to \"close the books\": once a fiscal quarter is\nfinalized, nobody should be able to edit records that belong to it — not even by mistake. The\nperiods service adds that seal to a ",[950,188903,2694],{}," — noy-db's encrypted, per-tenant document store\n(see ",[1007,188906,65376],{"href":48},"). Call ",[941,188909,188910],{},"vault.closePeriod({ name, endDate })"," to freeze every record dated at or before ",[941,188913,60233],{}," — by business date when you pass\n",[941,188916,60237],{},", otherwise by write time (see Limits & gotchas); any later ",[941,188919,3161],{},[941,188921,3164],{}," against a\nfrozen record throws ",[941,188924,157253],{},[941,188926,21479],{}," then\ncarries forward opening balances into the next period. Every close and open is written as a\nnormal record and, when the ",[1007,188929,697],{"href":698}," service is also enabled, appended to\nthe vault's hash-chained, tamper-evident audit trail (the ",[950,188932,20923],{},") — so period closures\nthemselves become an auditable, chained sequence. Since ",[941,188935,56991],{},", a closed period also anchors a ",[950,188938,188939],{},"retention lifecycle",": freeze it to physically reclaim\nthe space held by converged sync delete markers, archive it to relocate its records to a cold\nstorage tier, and target-purge it to sweep markers off push-only sync targets — each recorded as\nits own companion record + ledger entry, with the hash-chained period record itself staying\nbyte-immutable. The whole service stays off your bundle unless you import ",[941,188942,58229],{},[1022,188944,111660],{"id":111659},[1111,188946,188947,188950,188953],{},[1114,188948,188949],{},"Accounting \u002F financial books that need year-end and quarter-end seals",[1114,188951,188952],{},"Audit \u002F regulatory workflows where post-close edits must be impossible",[1114,188954,188955],{},"Inventory or reporting systems with closed periods",[1022,188957,70093],{"id":70092},[1111,188959,188960,188967,188975],{},[1114,188961,188962,964,188964,188966],{},[950,188963,107493],{},[941,188965,6757],{}," alone enables close\u002Ffreeze\u002Freopen.",[1114,188968,188969,1324,188972,188974],{},[1092,188970,188971],{},"Optional:",[941,188973,84477],{}," chains period events into the tamper-evident ledger; without it closes still work but are not ledger-chained.",[1114,188976,188977,188982,188983,188985,188986,1520],{},[1092,188978,110954,188979,188981],{},[941,188980,72293],{}," (hot→cold):"," the vault's store must be a ",[941,188984,48447],{}," with a cold route — see ",[1007,188987,808],{"href":809},[1022,188989,99864],{"id":99863},[2217,188991,188993],{"className":2219,"code":188992,"language":2221,"meta":1037,"style":1037},"import { createNoydb } from '@noy-db\u002Fhub'\nimport { withPeriods } from '@noy-db\u002Fhub\u002Fperiods'\nimport { withHistory } from '@noy-db\u002Fhub\u002Fhistory' \u002F\u002F recommended — chains closes into the ledger\nimport { toMemory } from '@noy-db\u002Fto-memory'\n\nconst db = await createNoydb({\n  store: toMemory(),\n  user: 'firm',\n  secret: 'firm-secret-2026',\n  periodsStrategy: withPeriods(),\n  historyStrategy: withHistory(), \u002F\u002F optional — without it, closes\u002Fopens still work but nothing is chained into a ledger\n})\nconst vault = await db.openVault('books')\n",[941,188994,188995,189013,189031,189052,189070,189074,189090,189102,189116,189130,189142,189157,189163],{"__ignoreMap":1037},[2225,188996,188997,188999,189001,189003,189005,189007,189009,189011],{"class":2227,"line":2228},[2225,188998,2232],{"class":2231},[2225,189000,2236],{"class":2235},[2225,189002,2744],{"class":2239},[2225,189004,2243],{"class":2235},[2225,189006,2246],{"class":2231},[2225,189008,2249],{"class":2235},[2225,189010,1086],{"class":2252},[2225,189012,2256],{"class":2235},[2225,189014,189015,189017,189019,189021,189023,189025,189027,189029],{"class":2227,"line":1041},[2225,189016,2232],{"class":2231},[2225,189018,2236],{"class":2235},[2225,189020,58465],{"class":2239},[2225,189022,2243],{"class":2235},[2225,189024,2246],{"class":2231},[2225,189026,2249],{"class":2235},[2225,189028,58474],{"class":2252},[2225,189030,2256],{"class":2235},[2225,189032,189033,189035,189037,189039,189041,189043,189045,189047,189049],{"class":2227,"line":1038},[2225,189034,2232],{"class":2231},[2225,189036,2236],{"class":2235},[2225,189038,4142],{"class":2239},[2225,189040,2243],{"class":2235},[2225,189042,2246],{"class":2231},[2225,189044,2249],{"class":2235},[2225,189046,4151],{"class":2252},[2225,189048,2805],{"class":2235},[2225,189050,189051],{"class":2763}," \u002F\u002F recommended — chains closes into the ledger\n",[2225,189053,189054,189056,189058,189060,189062,189064,189066,189068],{"class":2227,"line":2288},[2225,189055,2232],{"class":2231},[2225,189057,2236],{"class":2235},[2225,189059,68576],{"class":2239},[2225,189061,2243],{"class":2235},[2225,189063,2246],{"class":2231},[2225,189065,2249],{"class":2235},[2225,189067,9638],{"class":2252},[2225,189069,2256],{"class":2235},[2225,189071,189072],{"class":2227,"line":2304},[2225,189073,2285],{"emptyLinePlaceholder":2284},[2225,189075,189076,189078,189080,189082,189084,189086,189088],{"class":2227,"line":2356},[2225,189077,2775],{"class":2774},[2225,189079,2778],{"class":2239},[2225,189081,2781],{"class":2235},[2225,189083,2784],{"class":2231},[2225,189085,2744],{"class":2291},[2225,189087,2789],{"class":2239},[2225,189089,4200],{"class":2235},[2225,189091,189092,189094,189096,189098,189100],{"class":2227,"line":2830},[2225,189093,15608],{"class":2307},[2225,189095,2311],{"class":2235},[2225,189097,68576],{"class":2291},[2225,189099,4228],{"class":2239},[2225,189101,1707],{"class":2235},[2225,189103,189104,189106,189108,189110,189112,189114],{"class":2227,"line":2861},[2225,189105,4205],{"class":2307},[2225,189107,2311],{"class":2235},[2225,189109,2249],{"class":2235},[2225,189111,39709],{"class":2252},[2225,189113,2805],{"class":2235},[2225,189115,1707],{"class":2235},[2225,189117,189118,189120,189122,189124,189126,189128],{"class":2227,"line":4246},[2225,189119,10423],{"class":2307},[2225,189121,2311],{"class":2235},[2225,189123,2249],{"class":2235},[2225,189125,65709],{"class":2252},[2225,189127,2805],{"class":2235},[2225,189129,1707],{"class":2235},[2225,189131,189132,189134,189136,189138,189140],{"class":2227,"line":4253},[2225,189133,58715],{"class":2307},[2225,189135,2311],{"class":2235},[2225,189137,58465],{"class":2291},[2225,189139,4228],{"class":2239},[2225,189141,1707],{"class":2235},[2225,189143,189144,189146,189148,189150,189152,189154],{"class":2227,"line":4259},[2225,189145,4221],{"class":2307},[2225,189147,2311],{"class":2235},[2225,189149,4142],{"class":2291},[2225,189151,4228],{"class":2239},[2225,189153,2298],{"class":2235},[2225,189155,189156],{"class":2763}," \u002F\u002F optional — without it, closes\u002Fopens still work but nothing is chained into a ledger\n",[2225,189158,189159,189161],{"class":2227,"line":6548},[2225,189160,2359],{"class":2235},[2225,189162,2362],{"class":2239},[2225,189164,189165,189167,189169,189171,189173,189175,189177,189179,189181,189183,189185,189187],{"class":2227,"line":6576},[2225,189166,2775],{"class":2774},[2225,189168,2835],{"class":2239},[2225,189170,2781],{"class":2235},[2225,189172,2784],{"class":2231},[2225,189174,2842],{"class":2239},[2225,189176,1520],{"class":2235},[2225,189178,2847],{"class":2291},[2225,189180,2789],{"class":2239},[2225,189182,2805],{"class":2235},[2225,189184,65761],{"class":2252},[2225,189186,2805],{"class":2235},[2225,189188,2362],{"class":2239},[1022,189190,29893],{"id":29892},[1591,189192,189194],{"id":189193},"close-a-period","Close a period",[2217,189196,189198],{"className":2219,"code":189197,"language":2221,"meta":1037,"style":1037},"interface Invoice { amount: number; status: string; date: string }\nconst invoices = vault.collection\u003CInvoice>('invoices')\n\nawait invoices.put('inv-1', { amount: 1000, status: 'draft', date: '2026-01-15' })\n\nconst q1 = await vault.closePeriod({\n  name: 'FY2026-Q1',\n  endDate: '2026-03-31',\n  dateField: 'date',   \u002F\u002F seal by business date, not write time — see Limits & gotchas\n})\n",[941,189199,189200,189232,189262,189266,189323,189327,189348,189363,189377,189394],{"__ignoreMap":1037},[2225,189201,189202,189204,189206,189208,189210,189212,189214,189216,189218,189220,189222,189224,189226,189228,189230],{"class":2227,"line":2228},[2225,189203,6994],{"class":2774},[2225,189205,6343],{"class":6342},[2225,189207,2236],{"class":2235},[2225,189209,6351],{"class":2307},[2225,189211,2311],{"class":2235},[2225,189213,6356],{"class":6342},[2225,189215,3769],{"class":2235},[2225,189217,6361],{"class":2307},[2225,189219,2311],{"class":2235},[2225,189221,6366],{"class":6342},[2225,189223,3769],{"class":2235},[2225,189225,25880],{"class":2307},[2225,189227,2311],{"class":2235},[2225,189229,6366],{"class":6342},[2225,189231,6369],{"class":2235},[2225,189233,189234,189236,189238,189240,189242,189244,189246,189248,189250,189252,189254,189256,189258,189260],{"class":2227,"line":1041},[2225,189235,2775],{"class":2774},[2225,189237,6461],{"class":2239},[2225,189239,2781],{"class":2235},[2225,189241,6466],{"class":2239},[2225,189243,1520],{"class":2235},[2225,189245,6471],{"class":2291},[2225,189247,6474],{"class":2235},[2225,189249,6477],{"class":6342},[2225,189251,6480],{"class":2235},[2225,189253,2789],{"class":2239},[2225,189255,2805],{"class":2235},[2225,189257,6487],{"class":2252},[2225,189259,2805],{"class":2235},[2225,189261,2362],{"class":2239},[2225,189263,189264],{"class":2227,"line":1038},[2225,189265,2285],{"emptyLinePlaceholder":2284},[2225,189267,189268,189270,189272,189274,189276,189278,189280,189282,189284,189286,189288,189290,189292,189294,189296,189298,189300,189302,189304,189306,189308,189310,189312,189314,189317,189319,189321],{"class":2227,"line":2288},[2225,189269,6500],{"class":2231},[2225,189271,6503],{"class":2239},[2225,189273,1520],{"class":2235},[2225,189275,3161],{"class":2291},[2225,189277,2789],{"class":2239},[2225,189279,2805],{"class":2235},[2225,189281,6514],{"class":2252},[2225,189283,2805],{"class":2235},[2225,189285,2298],{"class":2235},[2225,189287,2236],{"class":2235},[2225,189289,6351],{"class":2307},[2225,189291,2311],{"class":2235},[2225,189293,26290],{"class":6527},[2225,189295,2298],{"class":2235},[2225,189297,6361],{"class":2307},[2225,189299,2311],{"class":2235},[2225,189301,2249],{"class":2235},[2225,189303,28429],{"class":2252},[2225,189305,2805],{"class":2235},[2225,189307,2298],{"class":2235},[2225,189309,25880],{"class":2307},[2225,189311,2311],{"class":2235},[2225,189313,2249],{"class":2235},[2225,189315,189316],{"class":2252},"2026-01-15",[2225,189318,2805],{"class":2235},[2225,189320,2243],{"class":2235},[2225,189322,2362],{"class":2239},[2225,189324,189325],{"class":2227,"line":2304},[2225,189326,2285],{"emptyLinePlaceholder":2284},[2225,189328,189329,189331,189334,189336,189338,189340,189342,189344,189346],{"class":2227,"line":2356},[2225,189330,2775],{"class":2774},[2225,189332,189333],{"class":2239}," q1 ",[2225,189335,2781],{"class":2235},[2225,189337,2784],{"class":2231},[2225,189339,6466],{"class":2239},[2225,189341,1520],{"class":2235},[2225,189343,60226],{"class":2291},[2225,189345,2789],{"class":2239},[2225,189347,4200],{"class":2235},[2225,189349,189350,189352,189354,189356,189359,189361],{"class":2227,"line":2830},[2225,189351,7008],{"class":2307},[2225,189353,2311],{"class":2235},[2225,189355,2249],{"class":2235},[2225,189357,189358],{"class":2252},"FY2026-Q1",[2225,189360,2805],{"class":2235},[2225,189362,1707],{"class":2235},[2225,189364,189365,189367,189369,189371,189373,189375],{"class":2227,"line":2861},[2225,189366,60320],{"class":2307},[2225,189368,2311],{"class":2235},[2225,189370,2249],{"class":2235},[2225,189372,60327],{"class":2252},[2225,189374,2805],{"class":2235},[2225,189376,1707],{"class":2235},[2225,189378,189379,189381,189383,189385,189387,189389,189391],{"class":2227,"line":4246},[2225,189380,60336],{"class":2307},[2225,189382,2311],{"class":2235},[2225,189384,2249],{"class":2235},[2225,189386,26305],{"class":2252},[2225,189388,2805],{"class":2235},[2225,189390,2298],{"class":2235},[2225,189392,189393],{"class":2763},"   \u002F\u002F seal by business date, not write time — see Limits & gotchas\n",[2225,189395,189396,189398],{"class":2227,"line":4253},[2225,189397,2359],{"class":2235},[2225,189399,2362],{"class":2239},[2217,189401,189403],{"className":6604,"code":189402,"language":141,"meta":1037,"style":1037},"{\n  \"name\": \"FY2026-Q1\",\n  \"kind\": \"closed\",\n  \"endDate\": \"2026-03-31\",\n  \"dateField\": \"date\",\n  \"closedAt\": \"2026-07-08T09:14:03.412Z\",\n  \"closedBy\": \"firm\",\n  \"priorPeriodHash\": \"\"\n}\n",[941,189404,189405,189409,189427,189445,189463,189481,189499,189517,189530],{"__ignoreMap":1037},[2225,189406,189407],{"class":2227,"line":2228},[2225,189408,4200],{"class":2235},[2225,189410,189411,189413,189415,189417,189419,189421,189423,189425],{"class":2227,"line":1041},[2225,189412,9368],{"class":2235},[2225,189414,4914],{"class":2774},[2225,189416,6620],{"class":2235},[2225,189418,2311],{"class":2235},[2225,189420,6614],{"class":2235},[2225,189422,189358],{"class":2252},[2225,189424,6620],{"class":2235},[2225,189426,1707],{"class":2235},[2225,189428,189429,189431,189433,189435,189437,189439,189441,189443],{"class":2227,"line":1038},[2225,189430,9368],{"class":2235},[2225,189432,27895],{"class":2774},[2225,189434,6620],{"class":2235},[2225,189436,2311],{"class":2235},[2225,189438,6614],{"class":2235},[2225,189440,60394],{"class":2252},[2225,189442,6620],{"class":2235},[2225,189444,1707],{"class":2235},[2225,189446,189447,189449,189451,189453,189455,189457,189459,189461],{"class":2227,"line":2288},[2225,189448,9368],{"class":2235},[2225,189450,60233],{"class":2774},[2225,189452,6620],{"class":2235},[2225,189454,2311],{"class":2235},[2225,189456,6614],{"class":2235},[2225,189458,60327],{"class":2252},[2225,189460,6620],{"class":2235},[2225,189462,1707],{"class":2235},[2225,189464,189465,189467,189469,189471,189473,189475,189477,189479],{"class":2227,"line":2304},[2225,189466,9368],{"class":2235},[2225,189468,60237],{"class":2774},[2225,189470,6620],{"class":2235},[2225,189472,2311],{"class":2235},[2225,189474,6614],{"class":2235},[2225,189476,26305],{"class":2252},[2225,189478,6620],{"class":2235},[2225,189480,1707],{"class":2235},[2225,189482,189483,189485,189487,189489,189491,189493,189495,189497],{"class":2227,"line":2356},[2225,189484,9368],{"class":2235},[2225,189486,60423],{"class":2774},[2225,189488,6620],{"class":2235},[2225,189490,2311],{"class":2235},[2225,189492,6614],{"class":2235},[2225,189494,125448],{"class":2252},[2225,189496,6620],{"class":2235},[2225,189498,1707],{"class":2235},[2225,189500,189501,189503,189505,189507,189509,189511,189513,189515],{"class":2227,"line":2830},[2225,189502,9368],{"class":2235},[2225,189504,60443],{"class":2774},[2225,189506,6620],{"class":2235},[2225,189508,2311],{"class":2235},[2225,189510,6614],{"class":2235},[2225,189512,39709],{"class":2252},[2225,189514,6620],{"class":2235},[2225,189516,1707],{"class":2235},[2225,189518,189519,189521,189523,189525,189527],{"class":2227,"line":2861},[2225,189520,9368],{"class":2235},[2225,189522,60462],{"class":2774},[2225,189524,6620],{"class":2235},[2225,189526,2311],{"class":2235},[2225,189528,189529],{"class":2235}," \"\"\n",[2225,189531,189532],{"class":2227,"line":4246},[2225,189533,7351],{"class":2235},[937,189535,189536],{},"Any later write to a record whose business date falls inside the closed range now throws — even\nan edit that only touches an unrelated field:",[2217,189538,189540],{"className":2219,"code":189539,"language":2221,"meta":1037,"style":1037},"await invoices.put('inv-1', { amount: 2000, status: 'paid', date: '2026-01-15' })\n\u002F\u002F → throws PeriodClosedError:\n\u002F\u002F   'Cannot modify record (last written existing[date]=2026-01-15) — sealed by closed period\n\u002F\u002F    \"FY2026-Q1\" (endDate: 2026-03-31). Post a compensating entry in a new period instead.'\n",[941,189541,189542,189598,189603,189608],{"__ignoreMap":1037},[2225,189543,189544,189546,189548,189550,189552,189554,189556,189558,189560,189562,189564,189566,189568,189570,189572,189574,189576,189578,189580,189582,189584,189586,189588,189590,189592,189594,189596],{"class":2227,"line":2228},[2225,189545,6500],{"class":2231},[2225,189547,6503],{"class":2239},[2225,189549,1520],{"class":2235},[2225,189551,3161],{"class":2291},[2225,189553,2789],{"class":2239},[2225,189555,2805],{"class":2235},[2225,189557,6514],{"class":2252},[2225,189559,2805],{"class":2235},[2225,189561,2298],{"class":2235},[2225,189563,2236],{"class":2235},[2225,189565,6351],{"class":2307},[2225,189567,2311],{"class":2235},[2225,189569,159089],{"class":6527},[2225,189571,2298],{"class":2235},[2225,189573,6361],{"class":2307},[2225,189575,2311],{"class":2235},[2225,189577,2249],{"class":2235},[2225,189579,6539],{"class":2252},[2225,189581,2805],{"class":2235},[2225,189583,2298],{"class":2235},[2225,189585,25880],{"class":2307},[2225,189587,2311],{"class":2235},[2225,189589,2249],{"class":2235},[2225,189591,189316],{"class":2252},[2225,189593,2805],{"class":2235},[2225,189595,2243],{"class":2235},[2225,189597,2362],{"class":2239},[2225,189599,189600],{"class":2227,"line":1041},[2225,189601,189602],{"class":2763},"\u002F\u002F → throws PeriodClosedError:\n",[2225,189604,189605],{"class":2227,"line":1038},[2225,189606,189607],{"class":2763},"\u002F\u002F   'Cannot modify record (last written existing[date]=2026-01-15) — sealed by closed period\n",[2225,189609,189610],{"class":2227,"line":2288},[2225,189611,189612],{"class":2763},"\u002F\u002F    \"FY2026-Q1\" (endDate: 2026-03-31). Post a compensating entry in a new period instead.'\n",[1591,189614,189616],{"id":189615},"list-and-look-up-periods","List and look up periods",[2217,189618,189620],{"className":2219,"code":189619,"language":2221,"meta":1037,"style":1037},"const periods = await vault.listPeriods()\n",[941,189621,189622],{"__ignoreMap":1037},[2225,189623,189624,189626,189628,189630,189632,189634,189636,189639],{"class":2227,"line":2228},[2225,189625,2775],{"class":2774},[2225,189627,132786],{"class":2239},[2225,189629,2781],{"class":2235},[2225,189631,2784],{"class":2231},[2225,189633,6466],{"class":2239},[2225,189635,1520],{"class":2235},[2225,189637,189638],{"class":2291},"listPeriods",[2225,189640,8146],{"class":2239},[2217,189642,189644],{"className":6604,"code":189643,"language":141,"meta":1037,"style":1037},"[\n  {\n    \"name\": \"FY2026-Q1\",\n    \"kind\": \"closed\",\n    \"endDate\": \"2026-03-31\",\n    \"dateField\": \"date\",\n    \"closedAt\": \"2026-07-08T09:14:03.412Z\",\n    \"closedBy\": \"firm\",\n    \"priorPeriodHash\": \"\"\n  }\n]\n",[941,189645,189646,189650,189654,189672,189690,189708,189726,189744,189762,189774,189778],{"__ignoreMap":1037},[2225,189647,189648],{"class":2227,"line":2228},[2225,189649,26349],{"class":2235},[2225,189651,189652],{"class":2227,"line":1041},[2225,189653,59879],{"class":2235},[2225,189655,189656,189658,189660,189662,189664,189666,189668,189670],{"class":2227,"line":1038},[2225,189657,14201],{"class":2235},[2225,189659,4914],{"class":2774},[2225,189661,6620],{"class":2235},[2225,189663,2311],{"class":2235},[2225,189665,6614],{"class":2235},[2225,189667,189358],{"class":2252},[2225,189669,6620],{"class":2235},[2225,189671,1707],{"class":2235},[2225,189673,189674,189676,189678,189680,189682,189684,189686,189688],{"class":2227,"line":2288},[2225,189675,14201],{"class":2235},[2225,189677,27895],{"class":2774},[2225,189679,6620],{"class":2235},[2225,189681,2311],{"class":2235},[2225,189683,6614],{"class":2235},[2225,189685,60394],{"class":2252},[2225,189687,6620],{"class":2235},[2225,189689,1707],{"class":2235},[2225,189691,189692,189694,189696,189698,189700,189702,189704,189706],{"class":2227,"line":2304},[2225,189693,14201],{"class":2235},[2225,189695,60233],{"class":2774},[2225,189697,6620],{"class":2235},[2225,189699,2311],{"class":2235},[2225,189701,6614],{"class":2235},[2225,189703,60327],{"class":2252},[2225,189705,6620],{"class":2235},[2225,189707,1707],{"class":2235},[2225,189709,189710,189712,189714,189716,189718,189720,189722,189724],{"class":2227,"line":2356},[2225,189711,14201],{"class":2235},[2225,189713,60237],{"class":2774},[2225,189715,6620],{"class":2235},[2225,189717,2311],{"class":2235},[2225,189719,6614],{"class":2235},[2225,189721,26305],{"class":2252},[2225,189723,6620],{"class":2235},[2225,189725,1707],{"class":2235},[2225,189727,189728,189730,189732,189734,189736,189738,189740,189742],{"class":2227,"line":2830},[2225,189729,14201],{"class":2235},[2225,189731,60423],{"class":2774},[2225,189733,6620],{"class":2235},[2225,189735,2311],{"class":2235},[2225,189737,6614],{"class":2235},[2225,189739,125448],{"class":2252},[2225,189741,6620],{"class":2235},[2225,189743,1707],{"class":2235},[2225,189745,189746,189748,189750,189752,189754,189756,189758,189760],{"class":2227,"line":2861},[2225,189747,14201],{"class":2235},[2225,189749,60443],{"class":2774},[2225,189751,6620],{"class":2235},[2225,189753,2311],{"class":2235},[2225,189755,6614],{"class":2235},[2225,189757,39709],{"class":2252},[2225,189759,6620],{"class":2235},[2225,189761,1707],{"class":2235},[2225,189763,189764,189766,189768,189770,189772],{"class":2227,"line":4246},[2225,189765,14201],{"class":2235},[2225,189767,60462],{"class":2774},[2225,189769,6620],{"class":2235},[2225,189771,2311],{"class":2235},[2225,189773,189529],{"class":2235},[2225,189775,189776],{"class":2227,"line":4253},[2225,189777,7632],{"class":2235},[2225,189779,189780],{"class":2227,"line":4259},[2225,189781,26484],{"class":2235},[2217,189783,189785],{"className":2219,"code":189784,"language":2221,"meta":1037,"style":1037},"await vault.getPeriod('FY2026-Q1')\n\u002F\u002F → the same PeriodRecord as above\n\nawait vault.getPeriod('NEVER')\n\u002F\u002F → null\n",[941,189786,189787,189808,189813,189817,189838],{"__ignoreMap":1037},[2225,189788,189789,189791,189793,189795,189798,189800,189802,189804,189806],{"class":2227,"line":2228},[2225,189790,6500],{"class":2231},[2225,189792,6466],{"class":2239},[2225,189794,1520],{"class":2235},[2225,189796,189797],{"class":2291},"getPeriod",[2225,189799,2789],{"class":2239},[2225,189801,2805],{"class":2235},[2225,189803,189358],{"class":2252},[2225,189805,2805],{"class":2235},[2225,189807,2362],{"class":2239},[2225,189809,189810],{"class":2227,"line":1041},[2225,189811,189812],{"class":2763},"\u002F\u002F → the same PeriodRecord as above\n",[2225,189814,189815],{"class":2227,"line":1038},[2225,189816,2285],{"emptyLinePlaceholder":2284},[2225,189818,189819,189821,189823,189825,189827,189829,189831,189834,189836],{"class":2227,"line":2288},[2225,189820,6500],{"class":2231},[2225,189822,6466],{"class":2239},[2225,189824,1520],{"class":2235},[2225,189826,189797],{"class":2291},[2225,189828,2789],{"class":2239},[2225,189830,2805],{"class":2235},[2225,189832,189833],{"class":2252},"NEVER",[2225,189835,2805],{"class":2235},[2225,189837,2362],{"class":2239},[2225,189839,189840],{"class":2227,"line":2304},[2225,189841,76419],{"class":2763},[1591,189843,189845],{"id":189844},"freeze-a-closed-period-physically-reclaim-delete-marker-space","Freeze a closed period — physically reclaim delete-marker space",[937,189847,189848,189849,189852,189853,189856,189857,152740,189860,189862,189863,189866,189867,189870],{},"On a synced vault, ",[1007,189850,189851],{"href":57016},"deletes converge as delete markers","\nthat accumulate until explicitly purged — purging one too early would let an offline peer\nresurrect the deleted record on its next push. A ",[950,189854,189855],{},"closed period is the operator-asserted\nsafe-point"," that makes the purge sound: ",[941,189858,189859],{},"vault.freezePeriod(name)",[941,189861,56991],{},") physically purges the delete markers whose write-time falls\ninside the closed period's window, records a ",[941,189864,189865],{},"_period_freezes\u002F\u003Cname>"," companion record plus a\ntamper-evident ledger entry, and leaves the hash-chained ",[941,189868,189869],{},"_periods\u002F\u003Cname>"," record byte-immutable:",[2217,189872,189874],{"className":2219,"code":189873,"language":2221,"meta":1037,"style":1037},"const frozen = await vault.freezePeriod('FY2026-Q1')\n\u002F\u002F frozen.frozenAt \u002F frozen.frozenBy \u002F frozen.purgedMarkerCount\n\u002F\u002F merged onto the returned PeriodRecord from the companion — the stored\n\u002F\u002F _periods record itself is never rewritten\n",[941,189875,189876,189904,189909,189914],{"__ignoreMap":1037},[2225,189877,189878,189880,189883,189885,189887,189889,189891,189894,189896,189898,189900,189902],{"class":2227,"line":2228},[2225,189879,2775],{"class":2774},[2225,189881,189882],{"class":2239}," frozen ",[2225,189884,2781],{"class":2235},[2225,189886,2784],{"class":2231},[2225,189888,6466],{"class":2239},[2225,189890,1520],{"class":2235},[2225,189892,189893],{"class":2291},"freezePeriod",[2225,189895,2789],{"class":2239},[2225,189897,2805],{"class":2235},[2225,189899,189358],{"class":2252},[2225,189901,2805],{"class":2235},[2225,189903,2362],{"class":2239},[2225,189905,189906],{"class":2227,"line":1041},[2225,189907,189908],{"class":2763},"\u002F\u002F frozen.frozenAt \u002F frozen.frozenBy \u002F frozen.purgedMarkerCount\n",[2225,189910,189911],{"class":2227,"line":1038},[2225,189912,189913],{"class":2763},"\u002F\u002F merged onto the returned PeriodRecord from the companion — the stored\n",[2225,189915,189916],{"class":2227,"line":2288},[2225,189917,189918],{"class":2763},"\u002F\u002F _periods record itself is never rewritten\n",[937,189920,189921,189922,189924,189925,189928,189929,189931,189932,189934,189935,189937,189938,189940],{},"Freezing is ",[950,189923,56895],{}," (a frozen period stays frozen) and ",[950,189926,189927],{},"idempotent"," (a second call is a\nno-op returning the same merged record, without re-purging or re-appending a ledger entry). It\npurges delete markers only: forget-tombstones (crypto-shred erasure evidence), ",[941,189930,6715],{},"\nversions, and live records are untouched by construction. A period whose purge window reaches\ninto the future is refused with ",[941,189933,12881],{}," — very recent delete markers may not have\nconverged to every peer yet. Freeze purges the ",[950,189936,49766],{}," store only; markers already pushed to\nsync targets survive there (benign — they still read as deleted) and a re-imported marker is\nreclaimed by the ",[1092,189939,1454],{}," period's freeze.",[1591,189942,189944],{"id":189943},"archive-a-closed-period-to-cold-storage","Archive a closed period to cold storage",[937,189946,189947,152740,189950,189952,189953,189955,189956,189958,189959,189962,189963,189966,189967,189969],{},[941,189948,189949],{},"vault.archivePeriod(name)",[941,189951,56991],{},") relocates a closed period's in-window records\nfrom the hot store to a configured cold tier, driving the ",[1007,189954,808],{"href":809}," service's\nhot→cold migration. It requires the vault's store to be a ",[941,189957,48447],{}," with a cold route\n(",[941,189960,189961],{},"age: { cold }",") — advertised as the ",[941,189964,189965],{},"coldArchival"," store capability — and throws\n",[941,189968,12881],{}," otherwise:",[2217,189971,189973],{"className":2219,"code":189972,"language":2221,"meta":1037,"style":1037},"import { routeStore } from '@noy-db\u002Fhub'\nimport { toMemory } from '@noy-db\u002Fto-memory'\n\nconst hot = toMemory()\nconst cold = toMemory()\n\u002F\u002F `coldAfterDays` may be omitted: `age: { cold }` alone means period-driven\n\u002F\u002F archival only — archivePeriod() supplies the cutoff per call.\nconst store = routeStore({ default: hot, age: { cold } })\n\n\u002F\u002F … createNoydb({ store, … }), then close FY2026-Q1 as above …\n\nconst archived = await vault.archivePeriod('FY2026-Q1')\n\u002F\u002F archived.archivedAt \u002F archived.archivedBy \u002F archived.archivedRecordCount\n",[941,189974,189975,189993,190011,190015,190028,190041,190046,190051,190089,190093,190098,190102,190130],{"__ignoreMap":1037},[2225,189976,189977,189979,189981,189983,189985,189987,189989,189991],{"class":2227,"line":2228},[2225,189978,2232],{"class":2231},[2225,189980,2236],{"class":2235},[2225,189982,48541],{"class":2239},[2225,189984,2243],{"class":2235},[2225,189986,2246],{"class":2231},[2225,189988,2249],{"class":2235},[2225,189990,1086],{"class":2252},[2225,189992,2256],{"class":2235},[2225,189994,189995,189997,189999,190001,190003,190005,190007,190009],{"class":2227,"line":1041},[2225,189996,2232],{"class":2231},[2225,189998,2236],{"class":2235},[2225,190000,68576],{"class":2239},[2225,190002,2243],{"class":2235},[2225,190004,2246],{"class":2231},[2225,190006,2249],{"class":2235},[2225,190008,9638],{"class":2252},[2225,190010,2256],{"class":2235},[2225,190012,190013],{"class":2227,"line":1038},[2225,190014,2285],{"emptyLinePlaceholder":2284},[2225,190016,190017,190019,190022,190024,190026],{"class":2227,"line":2288},[2225,190018,2775],{"class":2774},[2225,190020,190021],{"class":2239}," hot ",[2225,190023,2781],{"class":2235},[2225,190025,68576],{"class":2291},[2225,190027,8146],{"class":2239},[2225,190029,190030,190032,190035,190037,190039],{"class":2227,"line":2304},[2225,190031,2775],{"class":2774},[2225,190033,190034],{"class":2239}," cold ",[2225,190036,2781],{"class":2235},[2225,190038,68576],{"class":2291},[2225,190040,8146],{"class":2239},[2225,190042,190043],{"class":2227,"line":2356},[2225,190044,190045],{"class":2763},"\u002F\u002F `coldAfterDays` may be omitted: `age: { cold }` alone means period-driven\n",[2225,190047,190048],{"class":2227,"line":2830},[2225,190049,190050],{"class":2763},"\u002F\u002F archival only — archivePeriod() supplies the cutoff per call.\n",[2225,190052,190053,190055,190057,190059,190061,190063,190065,190067,190069,190072,190074,190077,190079,190081,190083,190085,190087],{"class":2227,"line":2861},[2225,190054,2775],{"class":2774},[2225,190056,39676],{"class":2239},[2225,190058,2781],{"class":2235},[2225,190060,48541],{"class":2291},[2225,190062,2789],{"class":2239},[2225,190064,2792],{"class":2235},[2225,190066,36571],{"class":2307},[2225,190068,2311],{"class":2235},[2225,190070,190071],{"class":2239}," hot",[2225,190073,2298],{"class":2235},[2225,190075,190076],{"class":2307}," age",[2225,190078,2311],{"class":2235},[2225,190080,2236],{"class":2235},[2225,190082,190034],{"class":2239},[2225,190084,2359],{"class":2235},[2225,190086,2243],{"class":2235},[2225,190088,2362],{"class":2239},[2225,190090,190091],{"class":2227,"line":4246},[2225,190092,2285],{"emptyLinePlaceholder":2284},[2225,190094,190095],{"class":2227,"line":4253},[2225,190096,190097],{"class":2763},"\u002F\u002F … createNoydb({ store, … }), then close FY2026-Q1 as above …\n",[2225,190099,190100],{"class":2227,"line":4259},[2225,190101,2285],{"emptyLinePlaceholder":2284},[2225,190103,190104,190106,190109,190111,190113,190115,190117,190120,190122,190124,190126,190128],{"class":2227,"line":6548},[2225,190105,2775],{"class":2774},[2225,190107,190108],{"class":2239}," archived ",[2225,190110,2781],{"class":2235},[2225,190112,2784],{"class":2231},[2225,190114,6466],{"class":2239},[2225,190116,1520],{"class":2235},[2225,190118,190119],{"class":2291},"archivePeriod",[2225,190121,2789],{"class":2239},[2225,190123,2805],{"class":2235},[2225,190125,189358],{"class":2252},[2225,190127,2805],{"class":2235},[2225,190129,2362],{"class":2239},[2225,190131,190132],{"class":2227,"line":6576},[2225,190133,190134],{"class":2763},"\u002F\u002F archived.archivedAt \u002F archived.archivedBy \u002F archived.archivedRecordCount\n",[937,190136,190137,190138,190141,190142,11060,190144,190147,190148,190151,190152,190154,190155,190157],{},"Archival is ",[950,190139,190140],{},"non-destructive"," — routed reads fall through to the cold tier on a hot miss, so an\narchived record still reads normally — which is why it is gated only on ",[941,190143,60394],{},[941,190145,190146],{},"frozen","\n(it re-opens no resurrection window). It records a ",[941,190149,190150],{},"_period_archives\u002F\u003Cname>"," companion + ledger\nentry, is idempotent, and composes with freeze in either order. Records are bounded by write-time\n",[941,190153,1703],{},", not business date: a late-booked record archives at the ",[1092,190156,1454],{}," period's archival, the same\nrule freeze uses for late-booked delete markers.",[1591,190159,190161],{"id":190160},"sweep-markers-off-push-only-sync-targets","Sweep markers off push-only sync targets",[937,190163,190164,152740,190167,190169,190170,190172,190173,1261,190176,190179,190180,190182],{},[941,190165,190166],{},"vault.purgePeriodTargets(name)",[941,190168,56991],{},") extends freeze's local marker purge to the\nvault's own remote sinks: it sweeps the period's delete markers off every ",[950,190171,95965],{}," sync\ntarget (",[941,190174,190175],{},"'backup'",[941,190177,190178],{},"'archive'"," roles — see ",[1007,190181,864],{"href":865},"), reclaiming remote marker\nspace the local freeze cannot reach:",[2217,190184,190186],{"className":2219,"code":190185,"language":2221,"meta":1037,"style":1037},"await vault.freezePeriod('FY2026-Q1')          \u002F\u002F required first: closed → frozen → target-purged\nconst purged = await vault.purgePeriodTargets('FY2026-Q1')\n\u002F\u002F purged.targetsPurgedAt \u002F purged.targetsPurgedBy\n\u002F\u002F purged.targetsPurged → [{ role: 'backup', purgedCount: 3 }]\n",[941,190187,190188,190211,190239,190244],{"__ignoreMap":1037},[2225,190189,190190,190192,190194,190196,190198,190200,190202,190204,190206,190208],{"class":2227,"line":2228},[2225,190191,6500],{"class":2231},[2225,190193,6466],{"class":2239},[2225,190195,1520],{"class":2235},[2225,190197,189893],{"class":2291},[2225,190199,2789],{"class":2239},[2225,190201,2805],{"class":2235},[2225,190203,189358],{"class":2252},[2225,190205,2805],{"class":2235},[2225,190207,32077],{"class":2239},[2225,190209,190210],{"class":2763},"\u002F\u002F required first: closed → frozen → target-purged\n",[2225,190212,190213,190215,190218,190220,190222,190224,190226,190229,190231,190233,190235,190237],{"class":2227,"line":1041},[2225,190214,2775],{"class":2774},[2225,190216,190217],{"class":2239}," purged ",[2225,190219,2781],{"class":2235},[2225,190221,2784],{"class":2231},[2225,190223,6466],{"class":2239},[2225,190225,1520],{"class":2235},[2225,190227,190228],{"class":2291},"purgePeriodTargets",[2225,190230,2789],{"class":2239},[2225,190232,2805],{"class":2235},[2225,190234,189358],{"class":2252},[2225,190236,2805],{"class":2235},[2225,190238,2362],{"class":2239},[2225,190240,190241],{"class":2227,"line":1038},[2225,190242,190243],{"class":2763},"\u002F\u002F purged.targetsPurgedAt \u002F purged.targetsPurgedBy\n",[2225,190245,190246],{"class":2227,"line":2288},[2225,190247,190248],{"class":2763},"\u002F\u002F purged.targetsPurged → [{ role: 'backup', purgedCount: 3 }]\n",[937,190250,190251,190254,190255,190257,190258,190261,190262,190264,190265,957],{},[941,190252,190253],{},"'sync-peer'"," (bidirectional) targets are deliberately ",[950,190256,75655],{}," — purging a marker from a live\npeer could re-open the resurrection window for a client that has been offline since before the\ncutoff, an assertion no single vault can verify. Push-only targets are safe because they are\nnever pulled from. The call requires the period to be frozen first, records a\n",[941,190259,190260],{},"_period_target_purges\u002F\u003Cname>"," companion + ledger entry, and is idempotent once run; a vault with\n",[950,190263,11313],{}," push-only targets writes no companion and stays re-runnable, so a target added later is\nstill swept. This is single-vault only — fleet-wide purging across many vaults is the companion\ncontrol plane's concern (klum-db, over ",[941,190266,1220],{},[1591,190268,190270],{"id":190269},"open-the-next-period-and-carry-forward-opening-balances","Open the next period and carry forward opening balances",[2217,190272,190274],{"className":2219,"code":190273,"language":2221,"meta":1037,"style":1037},"const q2 = await vault.openPeriod({\n  name: 'FY2026-Q2',\n  startDate: '2026-04-01',\n  fromPeriod: 'FY2026-Q1',\n  carryForward: async (ctx) => {\n    const closing = await ctx.collection\u003CInvoice>('invoices').list()\n    const opening: Record\u003Cstring, Invoice> = {}\n    for (const entry of closing) {\n      if (entry.date \u003C= ctx.priorEndDate) {\n        opening[`ob-${entry.date}`] = { ...entry, status: 'carried-forward', date: '2026-04-01' }\n      }\n    }\n    return { invoices: opening }\n  },\n})\n",[941,190275,190276,190298,190313,190329,190344,190363,190402,190427,190445,190470,190529,190533,190537,190551,190555],{"__ignoreMap":1037},[2225,190277,190278,190280,190283,190285,190287,190289,190291,190294,190296],{"class":2227,"line":2228},[2225,190279,2775],{"class":2774},[2225,190281,190282],{"class":2239}," q2 ",[2225,190284,2781],{"class":2235},[2225,190286,2784],{"class":2231},[2225,190288,6466],{"class":2239},[2225,190290,1520],{"class":2235},[2225,190292,190293],{"class":2291},"openPeriod",[2225,190295,2789],{"class":2239},[2225,190297,4200],{"class":2235},[2225,190299,190300,190302,190304,190306,190309,190311],{"class":2227,"line":1041},[2225,190301,7008],{"class":2307},[2225,190303,2311],{"class":2235},[2225,190305,2249],{"class":2235},[2225,190307,190308],{"class":2252},"FY2026-Q2",[2225,190310,2805],{"class":2235},[2225,190312,1707],{"class":2235},[2225,190314,190315,190318,190320,190322,190325,190327],{"class":2227,"line":1038},[2225,190316,190317],{"class":2307},"  startDate",[2225,190319,2311],{"class":2235},[2225,190321,2249],{"class":2235},[2225,190323,190324],{"class":2252},"2026-04-01",[2225,190326,2805],{"class":2235},[2225,190328,1707],{"class":2235},[2225,190330,190331,190334,190336,190338,190340,190342],{"class":2227,"line":2288},[2225,190332,190333],{"class":2307},"  fromPeriod",[2225,190335,2311],{"class":2235},[2225,190337,2249],{"class":2235},[2225,190339,189358],{"class":2252},[2225,190341,2805],{"class":2235},[2225,190343,1707],{"class":2235},[2225,190345,190346,190349,190351,190353,190355,190357,190359,190361],{"class":2227,"line":2304},[2225,190347,190348],{"class":2291},"  carryForward",[2225,190350,2311],{"class":2235},[2225,190352,38717],{"class":2774},[2225,190354,953],{"class":2235},[2225,190356,157376],{"class":7052},[2225,190358,1381],{"class":2235},[2225,190360,8116],{"class":2774},[2225,190362,2301],{"class":2235},[2225,190364,190365,190367,190370,190372,190374,190376,190378,190380,190382,190384,190386,190388,190390,190392,190394,190396,190398,190400],{"class":2227,"line":2356},[2225,190366,80048],{"class":2774},[2225,190368,190369],{"class":2239}," closing",[2225,190371,6346],{"class":2235},[2225,190373,2784],{"class":2231},[2225,190375,138692],{"class":2239},[2225,190377,1520],{"class":2235},[2225,190379,6471],{"class":2291},[2225,190381,6474],{"class":2235},[2225,190383,6477],{"class":6342},[2225,190385,6480],{"class":2235},[2225,190387,2789],{"class":2307},[2225,190389,2805],{"class":2235},[2225,190391,6487],{"class":2252},[2225,190393,2805],{"class":2235},[2225,190395,1381],{"class":2307},[2225,190397,1520],{"class":2235},[2225,190399,3167],{"class":2291},[2225,190401,8146],{"class":2307},[2225,190403,190404,190406,190409,190411,190413,190415,190417,190419,190421,190423,190425],{"class":2227,"line":2830},[2225,190405,80048],{"class":2774},[2225,190407,190408],{"class":2239}," opening",[2225,190410,2311],{"class":2235},[2225,190412,22527],{"class":6342},[2225,190414,6474],{"class":2235},[2225,190416,7259],{"class":6342},[2225,190418,2298],{"class":2235},[2225,190420,6343],{"class":6342},[2225,190422,6480],{"class":2235},[2225,190424,6346],{"class":2235},[2225,190426,8782],{"class":2235},[2225,190428,190429,190431,190433,190435,190437,190439,190441,190443],{"class":2227,"line":2861},[2225,190430,156377],{"class":2231},[2225,190432,953],{"class":2307},[2225,190434,2775],{"class":2774},[2225,190436,45642],{"class":2239},[2225,190438,8806],{"class":2235},[2225,190440,190369],{"class":2239},[2225,190442,8495],{"class":2307},[2225,190444,4200],{"class":2235},[2225,190446,190447,190449,190451,190453,190455,190457,190459,190461,190463,190466,190468],{"class":2227,"line":4246},[2225,190448,8472],{"class":2231},[2225,190450,953],{"class":2307},[2225,190452,45678],{"class":2239},[2225,190454,1520],{"class":2235},[2225,190456,26305],{"class":2239},[2225,190458,134428],{"class":2235},[2225,190460,138692],{"class":2239},[2225,190462,1520],{"class":2235},[2225,190464,190465],{"class":2239},"priorEndDate",[2225,190467,8495],{"class":2307},[2225,190469,4200],{"class":2235},[2225,190471,190472,190475,190477,190479,190482,190484,190486,190488,190490,190492,190494,190496,190498,190500,190502,190504,190506,190508,190510,190513,190515,190517,190519,190521,190523,190525,190527],{"class":2227,"line":4253},[2225,190473,190474],{"class":2239},"        opening",[2225,190476,8885],{"class":2307},[2225,190478,44476],{"class":2235},[2225,190480,190481],{"class":2252},"ob-",[2225,190483,8197],{"class":2235},[2225,190485,45678],{"class":2239},[2225,190487,1520],{"class":2235},[2225,190489,26305],{"class":2239},[2225,190491,43799],{"class":2235},[2225,190493,8890],{"class":2307},[2225,190495,2781],{"class":2235},[2225,190497,2236],{"class":2235},[2225,190499,67770],{"class":2235},[2225,190501,45678],{"class":2239},[2225,190503,2298],{"class":2235},[2225,190505,6361],{"class":2307},[2225,190507,2311],{"class":2235},[2225,190509,2249],{"class":2235},[2225,190511,190512],{"class":2252},"carried-forward",[2225,190514,2805],{"class":2235},[2225,190516,2298],{"class":2235},[2225,190518,25880],{"class":2307},[2225,190520,2311],{"class":2235},[2225,190522,2249],{"class":2235},[2225,190524,190324],{"class":2252},[2225,190526,2805],{"class":2235},[2225,190528,6369],{"class":2235},[2225,190530,190531],{"class":2227,"line":4259},[2225,190532,8518],{"class":2235},[2225,190534,190535],{"class":2227,"line":6548},[2225,190536,60103],{"class":2235},[2225,190538,190539,190541,190543,190545,190547,190549],{"class":2227,"line":6576},[2225,190540,66675],{"class":2231},[2225,190542,2236],{"class":2235},[2225,190544,6503],{"class":2307},[2225,190546,2311],{"class":2235},[2225,190548,190408],{"class":2239},[2225,190550,6369],{"class":2235},[2225,190552,190553],{"class":2227,"line":6582},[2225,190554,19017],{"class":2235},[2225,190556,190557,190559],{"class":2227,"line":6587},[2225,190558,2359],{"class":2235},[2225,190560,2362],{"class":2239},[2217,190562,190564],{"className":6604,"code":190563,"language":141,"meta":1037,"style":1037},"{\n  \"name\": \"FY2026-Q2\",\n  \"kind\": \"opened\",\n  \"startDate\": \"2026-04-01\",\n  \"endDate\": \"2026-03-31\",\n  \"closedAt\": \"2026-07-08T09:15:11.048Z\",\n  \"closedBy\": \"firm\",\n  \"priorPeriodName\": \"FY2026-Q1\",\n  \"priorPeriodHash\": \"9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08\",\n  \"openingCollections\": [\"invoices\"]\n}\n",[941,190565,190566,190570,190588,190607,190626,190644,190663,190681,190699,190718,190739],{"__ignoreMap":1037},[2225,190567,190568],{"class":2227,"line":2228},[2225,190569,4200],{"class":2235},[2225,190571,190572,190574,190576,190578,190580,190582,190584,190586],{"class":2227,"line":1041},[2225,190573,9368],{"class":2235},[2225,190575,4914],{"class":2774},[2225,190577,6620],{"class":2235},[2225,190579,2311],{"class":2235},[2225,190581,6614],{"class":2235},[2225,190583,190308],{"class":2252},[2225,190585,6620],{"class":2235},[2225,190587,1707],{"class":2235},[2225,190589,190590,190592,190594,190596,190598,190600,190603,190605],{"class":2227,"line":1038},[2225,190591,9368],{"class":2235},[2225,190593,27895],{"class":2774},[2225,190595,6620],{"class":2235},[2225,190597,2311],{"class":2235},[2225,190599,6614],{"class":2235},[2225,190601,190602],{"class":2252},"opened",[2225,190604,6620],{"class":2235},[2225,190606,1707],{"class":2235},[2225,190608,190609,190611,190614,190616,190618,190620,190622,190624],{"class":2227,"line":2288},[2225,190610,9368],{"class":2235},[2225,190612,190613],{"class":2774},"startDate",[2225,190615,6620],{"class":2235},[2225,190617,2311],{"class":2235},[2225,190619,6614],{"class":2235},[2225,190621,190324],{"class":2252},[2225,190623,6620],{"class":2235},[2225,190625,1707],{"class":2235},[2225,190627,190628,190630,190632,190634,190636,190638,190640,190642],{"class":2227,"line":2304},[2225,190629,9368],{"class":2235},[2225,190631,60233],{"class":2774},[2225,190633,6620],{"class":2235},[2225,190635,2311],{"class":2235},[2225,190637,6614],{"class":2235},[2225,190639,60327],{"class":2252},[2225,190641,6620],{"class":2235},[2225,190643,1707],{"class":2235},[2225,190645,190646,190648,190650,190652,190654,190656,190659,190661],{"class":2227,"line":2356},[2225,190647,9368],{"class":2235},[2225,190649,60423],{"class":2774},[2225,190651,6620],{"class":2235},[2225,190653,2311],{"class":2235},[2225,190655,6614],{"class":2235},[2225,190657,190658],{"class":2252},"2026-07-08T09:15:11.048Z",[2225,190660,6620],{"class":2235},[2225,190662,1707],{"class":2235},[2225,190664,190665,190667,190669,190671,190673,190675,190677,190679],{"class":2227,"line":2830},[2225,190666,9368],{"class":2235},[2225,190668,60443],{"class":2774},[2225,190670,6620],{"class":2235},[2225,190672,2311],{"class":2235},[2225,190674,6614],{"class":2235},[2225,190676,39709],{"class":2252},[2225,190678,6620],{"class":2235},[2225,190680,1707],{"class":2235},[2225,190682,190683,190685,190687,190689,190691,190693,190695,190697],{"class":2227,"line":2861},[2225,190684,9368],{"class":2235},[2225,190686,60505],{"class":2774},[2225,190688,6620],{"class":2235},[2225,190690,2311],{"class":2235},[2225,190692,6614],{"class":2235},[2225,190694,189358],{"class":2252},[2225,190696,6620],{"class":2235},[2225,190698,1707],{"class":2235},[2225,190700,190701,190703,190705,190707,190709,190711,190714,190716],{"class":2227,"line":4246},[2225,190702,9368],{"class":2235},[2225,190704,60462],{"class":2774},[2225,190706,6620],{"class":2235},[2225,190708,2311],{"class":2235},[2225,190710,6614],{"class":2235},[2225,190712,190713],{"class":2252},"9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08",[2225,190715,6620],{"class":2235},[2225,190717,1707],{"class":2235},[2225,190719,190720,190722,190725,190727,190729,190731,190733,190735,190737],{"class":2227,"line":4253},[2225,190721,9368],{"class":2235},[2225,190723,190724],{"class":2774},"openingCollections",[2225,190726,6620],{"class":2235},[2225,190728,2311],{"class":2235},[2225,190730,8667],{"class":2235},[2225,190732,6620],{"class":2235},[2225,190734,6487],{"class":2252},[2225,190736,6620],{"class":2235},[2225,190738,26484],{"class":2235},[2225,190740,190741],{"class":2227,"line":4259},[2225,190742,7351],{"class":2235},[937,190744,190745,190748,190749,1261,190752,190754,190755,190757,190758,190761,190762,190764,190765,190768,190769,190771],{},[941,190746,190747],{},"carryForward"," receives a read-only facade (",[941,190750,190751],{},"ctx.collection(name).get()",[941,190753,138941],{},") over the\nvault's ",[1092,190756,8511],{}," state, plus ",[941,190759,190760],{},"ctx.priorEndDate"," — the just-closed period's ",[941,190763,60233],{},". Because\nclosed records are frozen, current state and closing state are the same thing, so filtering by\n",[941,190766,190767],{},"entry[dateField] \u003C= ctx.priorEndDate"," selects exactly what closed. The records returned from\n",[941,190770,190747],{}," are written through the normal collection path (encrypted, ledger-logged) with\nfresh timestamps, so they land outside every closed period.",[1022,190773,102889],{"id":102888},[1591,190775,190777],{"id":190776},"withperiods",[941,190778,6757],{},[2217,190780,190782],{"className":2219,"code":190781,"language":2221,"meta":1037,"style":1037},"function withPeriods(): PeriodsStrategy\n",[941,190783,190784],{"__ignoreMap":1037},[2225,190785,190786,190788,190790,190792],{"class":2227,"line":2228},[2225,190787,12641],{"class":2774},[2225,190789,58465],{"class":2291},[2225,190791,112812],{"class":2235},[2225,190793,190794],{"class":6342}," PeriodsStrategy\n",[937,190796,190797,190798,10313,190800,131589],{},"Builds the active periods strategy. Pass it as ",[941,190799,73170],{},[941,190801,12780],{},[1591,190803,190805],{"id":190804},"vaultcloseperiodoptions",[941,190806,190807],{},"vault.closePeriod(options)",[2217,190809,190811],{"className":2219,"code":190810,"language":2221,"meta":1037,"style":1037},"vault.closePeriod(options: ClosePeriodOptions): Promise\u003CPeriodRecord>\n",[941,190812,190813],{"__ignoreMap":1037},[2225,190814,190815,190817,190819,190821,190824,190826,190828,190830],{"class":2227,"line":2228},[2225,190816,2694],{"class":2239},[2225,190818,1520],{"class":2235},[2225,190820,60226],{"class":2291},[2225,190822,190823],{"class":2239},"(options: ClosePeriodOptions): ",[2225,190825,11740],{"class":6342},[2225,190827,6474],{"class":2235},[2225,190829,60499],{"class":2239},[2225,190831,7093],{"class":2235},[937,190833,190834,190835,190837],{},"Seals a period and returns the ",[941,190836,60499],{}," that was written.",[937,190839,190840,2311],{},[941,190841,190842],{},"ClosePeriodOptions",[1338,190844,190845,190855],{},[1341,190846,190847],{},[1344,190848,190849,190851,190853],{},[1347,190850,101104],{},[1347,190852,20194],{},[1347,190854,7834],{},[1357,190856,190857,190877,190897],{},[1344,190858,190859,190863,190867],{},[1362,190860,190861],{},[941,190862,4914],{},[1362,190864,190865],{},[941,190866,7259],{},[1362,190868,190869,190870,190873,190874,190876],{},"Unique period name (e.g. ",[941,190871,190872],{},"'FY2026-Q1'","). Throws ",[941,190875,12881],{}," if it collides with an existing period.",[1344,190878,190879,190883,190887],{},[1362,190880,190881],{},[941,190882,60233],{},[1362,190884,190885],{},[941,190886,7259],{},[1362,190888,190889,190890,190893,190894,190896],{},"Inclusive ISO cutoff. A record is sealed once ",[941,190891,190892],{},"record[dateField]"," (or, when ",[941,190895,60237],{}," is omitted, the record's write timestamp) is at or before this date.",[1344,190898,190899,190904,190908],{},[1362,190900,190901],{},[941,190902,190903],{},"dateField?",[1362,190905,190906],{},[941,190907,7259],{},[1362,190909,190910],{},"Record field carrying the business date used for sealing. Omit to fall back to write-time sealing (see Limits & gotchas).",[1591,190912,190914],{"id":190913},"vaultopenperiodoptions",[941,190915,190916],{},"vault.openPeriod(options)",[2217,190918,190920],{"className":2219,"code":190919,"language":2221,"meta":1037,"style":1037},"vault.openPeriod\u003CTCollections>(options: OpenPeriodOptions\u003CTCollections>): Promise\u003CPeriodRecord>\n",[941,190921,190922],{"__ignoreMap":1037},[2225,190923,190924,190926,190928,190930,190932,190935,190937,190940,190942,190944,190946,190948,190950,190952,190954],{"class":2227,"line":2228},[2225,190925,2694],{"class":2239},[2225,190927,1520],{"class":2235},[2225,190929,190293],{"class":2291},[2225,190931,6474],{"class":2235},[2225,190933,190934],{"class":6342},"TCollections",[2225,190936,6480],{"class":2235},[2225,190938,190939],{"class":2239},"(options: OpenPeriodOptions",[2225,190941,6474],{"class":2235},[2225,190943,190934],{"class":2239},[2225,190945,6480],{"class":2235},[2225,190947,11737],{"class":2239},[2225,190949,11740],{"class":6342},[2225,190951,6474],{"class":2235},[2225,190953,60499],{"class":2239},[2225,190955,7093],{"class":2235},[937,190957,190958,190959,190961,190962,953,190964,957],{},"Opens the next period, materializes opening-balance records via ",[941,190960,190747],{},", and returns the\nnew ",[941,190963,60499],{},[941,190965,190966],{},"kind: 'opened'",[937,190968,190969,2311],{},[941,190970,190971],{},"OpenPeriodOptions",[1338,190973,190974,190984],{},[1341,190975,190976],{},[1344,190977,190978,190980,190982],{},[1347,190979,101104],{},[1347,190981,20194],{},[1347,190983,7834],{},[1357,190985,190986,190999,191015,191038],{},[1344,190987,190988,190992,190996],{},[1362,190989,190990],{},[941,190991,4914],{},[1362,190993,190994],{},[941,190995,7259],{},[1362,190997,190998],{},"Unique name for the new period.",[1344,191000,191001,191005,191009],{},[1362,191002,191003],{},[941,191004,190613],{},[1362,191006,191007],{},[941,191008,7259],{},[1362,191010,191011,191012,191014],{},"ISO lower bound of the new period — usually the prior period's ",[941,191013,60233],{}," + 1 day.",[1344,191016,191017,191022,191026],{},[1362,191018,191019],{},[941,191020,191021],{},"fromPeriod",[1362,191023,191024],{},[941,191025,7259],{},[1362,191027,191028,191029,191031,191032,191034,191035,1520],{},"Name of the prior ",[950,191030,60394],{}," period this one carries forward from. Throws ",[941,191033,12881],{}," if it doesn't exist or isn't ",[941,191036,191037],{},"kind: 'closed'",[1344,191039,191040,191044,191049],{},[1362,191041,191042],{},[941,191043,190747],{},[1362,191045,191046],{},[941,191047,191048],{},"(ctx: CarryForwardContext) => Promise\u003CTCollections> | TCollections",[1362,191050,191051,191052,191055],{},"Computes opening-balance records from the closing state. Return value is ",[941,191053,191054],{},"{ [collectionName]: { [id]: record } }","; each record is written via the normal collection path.",[937,191057,191058,2311],{},[941,191059,191060],{},"CarryForwardContext",[1338,191062,191063,191073],{},[1341,191064,191065],{},[1344,191066,191067,191069,191071],{},[1347,191068,101104],{},[1347,191070,20194],{},[1347,191072,7834],{},[1357,191074,191075,191093],{},[1344,191076,191077,191081,191085],{},[1362,191078,191079],{},[941,191080,190465],{},[1362,191082,191083],{},[941,191084,7259],{},[1362,191086,960,191087,191089,191090,191092],{},[941,191088,60233],{}," of the period named in ",[941,191091,191021],{}," — the closing boundary to filter by.",[1344,191094,191095,191100,191105],{},[1362,191096,191097],{},[941,191098,191099],{},"collection(name)",[1362,191101,191102],{},[941,191103,191104],{},"\u003CT>(name: string) => { get(id): Promise\u003CT | null>, list(): Promise\u003CT[]> }",[1362,191106,191107],{},"Read-only view of the named collection's current state.",[1591,191109,191111],{"id":191110},"vaultfreezeperiodname",[941,191112,189859],{},[2217,191114,191116],{"className":2219,"code":191115,"language":2221,"meta":1037,"style":1037},"vault.freezePeriod(name: string): Promise\u003CPeriodRecord>\n",[941,191117,191118],{"__ignoreMap":1037},[2225,191119,191120,191122,191124,191126,191128,191130,191132,191134],{"class":2227,"line":2228},[2225,191121,2694],{"class":2239},[2225,191123,1520],{"class":2235},[2225,191125,189893],{"class":2291},[2225,191127,180375],{"class":2239},[2225,191129,11740],{"class":6342},[2225,191131,6474],{"class":2235},[2225,191133,60499],{"class":2239},[2225,191135,7093],{"class":2235},[937,191137,10993,191138,191140,191141,191143,191144,28806,191146,1261,191148,1261,191151,191154,191155,191157,191158,191160,191161,191163],{},[941,191139,56991],{},". Purges the delete markers whose write-time falls inside the closed period's\nwindow and records the ",[941,191142,189865],{}," companion + ledger entry. Returns the\n",[941,191145,60499],{},[941,191147,136713],{},[941,191149,191150],{},"frozenBy",[941,191152,191153],{},"purgedMarkerCount"," merged on (read-time merge — the\nstored ",[941,191156,6753],{}," record is never mutated). Terminal and idempotent. Throws ",[941,191159,12881],{}," when\nthe period doesn't exist, isn't ",[941,191162,191037],{},", or its purge window has not fully elapsed yet\n(recent delete markers may not have converged).",[1591,191165,191167],{"id":191166},"vaultarchiveperiodname",[941,191168,189949],{},[2217,191170,191172],{"className":2219,"code":191171,"language":2221,"meta":1037,"style":1037},"vault.archivePeriod(name: string): Promise\u003CPeriodRecord>\n",[941,191173,191174],{"__ignoreMap":1037},[2225,191175,191176,191178,191180,191182,191184,191186,191188,191190],{"class":2227,"line":2228},[2225,191177,2694],{"class":2239},[2225,191179,1520],{"class":2235},[2225,191181,190119],{"class":2291},[2225,191183,180375],{"class":2239},[2225,191185,11740],{"class":6342},[2225,191187,6474],{"class":2235},[2225,191189,60499],{"class":2239},[2225,191191,7093],{"class":2235},[937,191193,10993,191194,191196,191197,191199,191200,191202,191203,28806,191205,1261,191208,2601,191211,191214,191215,191217,191218,191220,191221,191223,191224,957],{},[941,191195,56991],{},". Relocates the closed period's in-window records (write-time ",[941,191198,1703],{}," inside the\nwindow) from the hot store to the routed cold tier, and records the ",[941,191201,190150],{},"\ncompanion + ledger entry. Returns the ",[941,191204,60499],{},[941,191206,191207],{},"archivedAt",[941,191209,191210],{},"archivedBy",[941,191212,191213],{},"archivedRecordCount"," merged on. Non-destructive (reads fall through to cold), gated only on\n",[941,191216,60394],{},", idempotent. Throws ",[941,191219,12881],{}," without a ",[941,191222,4620],{}," cold route\n(",[1007,191225,808],{"href":809},[1591,191227,191229],{"id":191228},"vaultpurgeperiodtargetsname",[941,191230,190166],{},[2217,191232,191234],{"className":2219,"code":191233,"language":2221,"meta":1037,"style":1037},"vault.purgePeriodTargets(name: string): Promise\u003CPeriodRecord>\n",[941,191235,191236],{"__ignoreMap":1037},[2225,191237,191238,191240,191242,191244,191246,191248,191250,191252],{"class":2227,"line":2228},[2225,191239,2694],{"class":2239},[2225,191241,1520],{"class":2235},[2225,191243,190228],{"class":2291},[2225,191245,180375],{"class":2239},[2225,191247,11740],{"class":6342},[2225,191249,6474],{"class":2235},[2225,191251,60499],{"class":2239},[2225,191253,7093],{"class":2235},[937,191255,10993,191256,191258,191259,1261,191261,191263,191264,191266,191267,191269,191270,69860,191272,1261,191275,1261,191278,191281,191282,191285],{},[941,191257,56991],{},". Sweeps the period's delete markers off every push-only sync target\n(",[941,191260,190175],{},[941,191262,190178],{},"), skipping ",[941,191265,190253],{}," targets, and records the\n",[941,191268,190260],{}," companion + ledger entry. Returns the ",[941,191271,60499],{},[941,191273,191274],{},"targetsPurgedAt",[941,191276,191277],{},"targetsPurgedBy",[941,191279,191280],{},"targetsPurged"," (per-target ",[941,191283,191284],{},"{ label?, role, purgedCount }",")\nmerged on. Requires the period to be frozen first; idempotent once run; with no push-only targets\nit writes no companion and is re-runnable.",[1591,191287,191289],{"id":191288},"vaultlistperiods",[941,191290,21485],{},[2217,191292,191294],{"className":2219,"code":191293,"language":2221,"meta":1037,"style":1037},"vault.listPeriods(): Promise\u003Creadonly PeriodRecord[]>\n",[941,191295,191296],{"__ignoreMap":1037},[2225,191297,191298,191300,191302,191304,191306,191308,191310,191313],{"class":2227,"line":2228},[2225,191299,2694],{"class":2239},[2225,191301,1520],{"class":2235},[2225,191303,189638],{"class":2291},[2225,191305,12809],{"class":2239},[2225,191307,11740],{"class":6342},[2225,191309,6474],{"class":2235},[2225,191311,191312],{"class":2239},"readonly PeriodRecord[]",[2225,191314,7093],{"class":2235},[937,191316,191317,191318,1520],{},"Returns every closed\u002Fopened period, ordered by ",[941,191319,60423],{},[1591,191321,191323],{"id":191322},"vaultgetperiodname",[941,191324,191325],{},"vault.getPeriod(name)",[2217,191327,191329],{"className":2219,"code":191328,"language":2221,"meta":1037,"style":1037},"vault.getPeriod(name: string): Promise\u003CPeriodRecord | null>\n",[941,191330,191331],{"__ignoreMap":1037},[2225,191332,191333,191335,191337,191339,191341,191343,191345,191348,191350],{"class":2227,"line":2228},[2225,191334,2694],{"class":2239},[2225,191336,1520],{"class":2235},[2225,191338,189797],{"class":2291},[2225,191340,180375],{"class":2239},[2225,191342,11740],{"class":6342},[2225,191344,6474],{"class":2235},[2225,191346,191347],{"class":2239},"PeriodRecord ",[2225,191349,18307],{"class":2235},[2225,191351,24020],{"class":2235},[937,191353,191354,191355,191357],{},"Looks up one period by name; ",[941,191356,6651],{}," if it doesn't exist.",[1591,191359,191361],{"id":191360},"periodrecord",[941,191362,60499],{},[937,191364,191365],{},"The stored record for one closed or opened period, one entry per period:",[1338,191367,191368,191378],{},[1341,191369,191370],{},[1344,191371,191372,191374,191376],{},[1347,191373,101104],{},[1347,191375,20194],{},[1347,191377,20200],{},[1357,191379,191380,191393,191407,191424,191441,191454,191472,191488,191502,191518,191534,191564,191591],{},[1344,191381,191382,191386,191390],{},[1362,191383,191384],{},[941,191385,4914],{},[1362,191387,191388],{},[941,191389,7259],{},[1362,191391,191392],{},"Unique period name.",[1344,191394,191395,191399,191404],{},[1362,191396,191397],{},[941,191398,27895],{},[1362,191400,191401],{},[941,191402,191403],{},"'closed' | 'opened'",[1362,191405,191406],{},"Whether this entry is a close or a carried-forward open.",[1344,191408,191409,191413,191417],{},[1362,191410,191411],{},[941,191412,60233],{},[1362,191414,191415],{},[941,191416,7259],{},[1362,191418,191419,191420,191423],{},"Sealing boundary — inherited from the prior close on ",[941,191421,191422],{},"'opened'"," records.",[1344,191425,191426,191431,191435],{},[1362,191427,191428],{},[941,191429,191430],{},"startDate?",[1362,191432,191433],{},[941,191434,7259],{},[1362,191436,191437,191438,191440],{},"Present on ",[941,191439,191422],{}," records only.",[1344,191442,191443,191447,191451],{},[1362,191444,191445],{},[941,191446,190903],{},[1362,191448,191449],{},[941,191450,7259],{},[1362,191452,191453],{},"The business-date field this close sealed by, if any.",[1344,191455,191456,191460,191464],{},[1362,191457,191458],{},[941,191459,60423],{},[1362,191461,191462],{},[941,191463,7259],{},[1362,191465,191466,191467,1261,191470,124645],{},"ISO timestamp of the ",[941,191468,191469],{},"closePeriod()",[941,191471,58327],{},[1344,191473,191474,191478,191482],{},[1362,191475,191476],{},[941,191477,60443],{},[1362,191479,191480],{},[941,191481,7259],{},[1362,191483,191484,191485,191487],{},"The user id of the ",[1007,191486,2705],{"href":54}," that made the close\u002Fopen call.",[1344,191489,191490,191495,191499],{},[1362,191491,191492],{},[941,191493,191494],{},"priorPeriodName?",[1362,191496,191497],{},[941,191498,7259],{},[1362,191500,191501],{},"Name of the period this one chains from. Absent on the first period.",[1344,191503,191504,191508,191512],{},[1362,191505,191506],{},[941,191507,60462],{},[1362,191509,191510],{},[941,191511,7259],{},[1362,191513,191514,191517],{},[941,191515,191516],{},"sha256(canonicalJson(priorPeriod))"," — empty string for the first period.",[1344,191519,191520,191525,191529],{},[1362,191521,191522],{},[941,191523,191524],{},"openingCollections?",[1362,191526,191527],{},[941,191528,111053],{},[1362,191530,191531,191533],{},[941,191532,191422],{}," records only — names of collections that received carried-forward records.",[1344,191535,191536,191547,191555],{},[1362,191537,191538,1261,191541,1261,191544],{},[941,191539,191540],{},"frozenAt?",[941,191542,191543],{},"frozenBy?",[941,191545,191546],{},"purgedMarkerCount?",[1362,191548,191549,1261,191551,1261,191553],{},[941,191550,7259],{},[941,191552,7259],{},[941,191554,20636],{},[1362,191556,191557,191558,191560,191561,191563],{},"Merged at read time from the ",[941,191559,189865],{}," companion; absent until frozen. Never written into the stored ",[941,191562,6753],{}," record (that would break the hash chain).",[1344,191565,191566,191577,191585],{},[1362,191567,191568,1261,191571,1261,191574],{},[941,191569,191570],{},"archivedAt?",[941,191572,191573],{},"archivedBy?",[941,191575,191576],{},"archivedRecordCount?",[1362,191578,191579,1261,191581,1261,191583],{},[941,191580,7259],{},[941,191582,7259],{},[941,191584,20636],{},[1362,191586,191587,191588,191590],{},"Merged from the ",[941,191589,190150],{}," companion; absent until archived.",[1344,191592,191593,191604,191613],{},[1362,191594,191595,1261,191598,1261,191601],{},[941,191596,191597],{},"targetsPurgedAt?",[941,191599,191600],{},"targetsPurgedBy?",[941,191602,191603],{},"targetsPurged?",[1362,191605,191606,1261,191608,1261,191610],{},[941,191607,7259],{},[941,191609,7259],{},[941,191611,191612],{},"readonly { label?, role, purgedCount }[]",[1362,191614,191587,191615,191617],{},[941,191616,190260],{}," companion; absent until target-purged (or when the vault has no push-only targets).",[1591,191619,191621],{"id":191620},"periodclosederror",[941,191622,157253],{},[2217,191624,191626],{"className":2219,"code":191625,"language":2221,"meta":1037,"style":1037},"import { PeriodClosedError } from '@noy-db\u002Fhub'\n",[941,191627,191628],{"__ignoreMap":1037},[2225,191629,191630,191632,191634,191636,191638,191640,191642,191644],{"class":2227,"line":2228},[2225,191631,2232],{"class":2231},[2225,191633,2236],{"class":2235},[2225,191635,60265],{"class":2239},[2225,191637,2243],{"class":2235},[2225,191639,2246],{"class":2231},[2225,191641,2249],{"class":2235},[2225,191643,1086],{"class":2252},[2225,191645,2256],{"class":2235},[937,191647,142449,191648,1261,191650,191652,191653,968,191656,2462,191658,191661,191662,191665,191666,191668],{},[941,191649,20953],{},[941,191651,9467],{}," when the target record falls inside a closed period. Carries\n",[941,191654,191655],{},"periodName",[941,191657,60233],{},[941,191659,191660],{},"recordTs"," (the offending timestamp, or a ",[941,191663,191664],{},"field=value"," pair when\nsealed by ",[941,191667,60237],{},") as typed fields, so an audit UI can render \"locked in FY2026-Q1 (closed\n2026-03-31)\" without parsing the message string.",[1022,191670,101425],{"id":101424},[1111,191672,191673,191682,191693,191696],{},[1114,191674,191675,1261,191677,191679,191680,1520],{},[941,191676,21482],{},[941,191678,21479],{}," throw, pointing you at ",[941,191681,58474],{},[1114,191683,191684,2557,191686,5502,191688,2557,191691,1520],{},[941,191685,21485],{},[941,191687,7262],{},[941,191689,191690],{},"vault.getPeriod()",[941,191692,6651],{},[1114,191694,191695],{},"The write-guard is a no-op — every put\u002Fdelete succeeds regardless of date.",[1114,191697,191698],{},"None of the ~334 LOC ships in your bundle.",[1022,191700,68210],{"id":68209},[1111,191702,191703,191708,191714,191722,191725,191737,191746,191761,191777],{},[1114,191704,191705,191707],{},[941,191706,60237],{}," is recommended over the write-time fallback — accounting semantics use the\nrecord's business date, not when it happened to be written.",[1114,191709,191710,191711,191713],{},"A late entry (write-time after close, business date inside the closed range) is the canonical\ncorrectness check: a write-time-only seal would silently let it through. ",[941,191712,60237],{}," sealing\ncatches it because it compares the business date on both the existing and incoming record —\ncallers can't slide a record into a closed period by backdating it, either.",[1114,191715,191716,191717,34689,191719,191721],{},"There's no partial re-opening of a closed period. The sanctioned way to correct a sealed record\nis a compensating entry in the next period via ",[941,191718,58327],{},[941,191720,190747],{},", not unsealing\nthe old one.",[1114,191723,191724],{},"Closing and opening are explicit, deliberate calls — nothing rolls a period over automatically.\nThe same goes for the retention lifecycle: freeze, archive, and target-purge each run only when\ncalled.",[1114,191726,191727,191728,191731,191732,191734,191735,1520],{},"The retention sequence is ",[941,191729,191730],{},"closed → frozen → target-purged","; archival is independent of freeze\nand composes in either order. Freeze and target-purge reclaim ",[950,191733,56835],{}," space; archival\nrelocates ",[950,191736,9136],{},[1114,191738,191739,191740,191742,191743,191745],{},"Freeze\u002Farchive\u002Ftarget-purge bound by write-time ",[941,191741,1703],{},", not business date — the store tier sees\nonly encrypted envelopes. A late-booked record (or delete) is picked up by the ",[1092,191744,1454],{}," period's\nfreeze\u002Farchival.",[1114,191747,191748,191749,191751,191752,191754,191755,191757,191758,191760],{},"Archival's read cost depends on the read mode: with ",[1007,191750,719],{"href":720}," per-id reads,\narchived records are truly cold (fetched only on access); in the default hydrated mode,\n",[941,191753,3170],{}," merges the cold store, so archived records still load into RAM on vault open —\nhot-tier ",[1092,191756,20458],{}," is reclaimed, RAM is not. Reserved ",[941,191759,6702],{},"-prefixed summaries always stay hot.",[1114,191762,191763,191764,191767,191768,191771,191772,27678,191775,957],{},"A backup\u002Frestore round-trip re-materializes archived records into the hot tier, while the\nrestored ",[941,191765,191766],{},"_period_archives"," companion keeps ",[941,191769,191770],{},"archivePeriod()"," an idempotent no-op. Reads stay\ncorrect; to reclaim hot space again, re-run the migration directly\n(",[941,191773,191774],{},"store.compact(vault, { before })",[1007,191776,808],{"href":809},[1114,191778,191779,191780,191782,191783,191785,191786,191788],{},"Tampering with a stored ",[941,191781,60499],{}," changes the payload hash embedded in its ledger entry,\nso it's caught by ",[941,191784,58313],{},"'s chain-and-data cross-check — but only when\nthe ",[1007,191787,697],{"href":698}," service is also enabled. Without a ledger, there's nothing to\ncross-check the period record against.",[1022,191790,2621],{"id":2620},[1111,191792,191793,191799,191804,191809,191814,191824,191829],{},[1114,191794,191795,1368,191797],{},[1007,191796,61743],{"href":598},[1007,191798,1019],{"href":1018},[1114,191800,191801,191803],{},[1007,191802,697],{"href":698}," — the ledger period closes\u002Fopens append their entries to",[1114,191805,191806,191808],{},[1007,191807,693],{"href":694}," — record-level locks; periods lock by business-date range instead of by record",[1114,191810,191811,191813],{},[1007,191812,864],{"href":865}," — the delete markers freeze and target-purge reclaim, and the target roles they respect",[1114,191815,191816,1169,191818,191820,191821,191823],{},[1007,191817,808],{"href":809},[941,191819,48447],{}," cold route ",[941,191822,191770],{}," drives",[1114,191825,191826,191828],{},[1007,191827,65376],{"href":48}," — the vault\u002Fcollection model this service extends",[1114,191830,101581,191831,1520],{},[1007,191832,191834],{"href":92739,"rel":191833},[3371],[941,191835,191836],{},"18-with-periods",[2645,191838,99779],{},{"title":1037,"searchDepth":1038,"depth":1038,"links":191840},[191841,191842,191843,191844,191845,191853,191865,191866,191867],{"id":29856,"depth":1041,"text":29857},{"id":111659,"depth":1041,"text":111660},{"id":70092,"depth":1041,"text":70093},{"id":99863,"depth":1041,"text":99864},{"id":29892,"depth":1041,"text":29893,"children":191846},[191847,191848,191849,191850,191851,191852],{"id":189193,"depth":1038,"text":189194},{"id":189615,"depth":1038,"text":189616},{"id":189844,"depth":1038,"text":189845},{"id":189943,"depth":1038,"text":189944},{"id":190160,"depth":1038,"text":190161},{"id":190269,"depth":1038,"text":190270},{"id":102888,"depth":1041,"text":102889,"children":191854},[191855,191856,191857,191858,191859,191860,191861,191862,191863,191864],{"id":190776,"depth":1038,"text":6757},{"id":190804,"depth":1038,"text":190807},{"id":190913,"depth":1038,"text":190916},{"id":191110,"depth":1038,"text":189859},{"id":191166,"depth":1038,"text":189949},{"id":191228,"depth":1038,"text":190166},{"id":191288,"depth":1038,"text":21485},{"id":191322,"depth":1038,"text":191325},{"id":191360,"depth":1038,"text":60499},{"id":191620,"depth":1038,"text":157253},{"id":101424,"depth":1041,"text":101425},{"id":68209,"depth":1041,"text":68210},{"id":2620,"depth":1041,"text":2621},"Accounting-style period closes with a write-guard: closePeriod() seals records, openPeriod() carries forward balances, and closes chain into the audit ledger.",{},{"title":776,"description":191868},"JZRF9vPK6WcZ7x2AZ8qXSjvNxIN66Kgpzziqx313kBI",{"id":191873,"title":780,"access":12,"apiToc":929,"body":191874,"category":1042,"description":194521,"extension":1044,"meta":194522,"navigation":1042,"nest":1047,"path":781,"seo":194523,"sinceVersion":1042,"status":1049,"stem":782,"subtitle":1042,"tier":1050,"tocExternal":1042,"__hash__":194524},"docs\u002Fservices\u002Fpersisted-schemas.md",{"type":931,"value":191875,"toc":194497},[191876,191878,191881,191883,191895,191904,191914,191916,192194,192208,192214,192232,192234,192238,192298,192392,192403,192619,192623,192647,192843,192846,192850,192862,193120,193124,193135,193257,193362,193382,193384,193388,193393,193444,193455,193461,193491,193500,193578,193584,193608,193615,193623,193809,193843,193849,193869,193872,193881,194025,194059,194068,194228,194251,194253,194443,194445,194494],[934,191877,780],{"id":780},[46871,191879],{":rows":191880},"[{\"label\":\"Subpath\",\"value\":\"`@noy-db\u002Fhub` (root export)\"},{\"label\":\"Type\",\"value\":\"Core feature — activated per collection by the `persistJsonSchema: true` option.\"}]",[1022,191882,29857],{"id":29856},[937,191884,191885,191886,191888,191889,191894],{},"Your collection's schema (a Zod object, say) lives in your application code — it's not visible to anything that only has the vault's data, like a bundle file handed to a teammate or the ",[941,191887,36126],{}," CLI. Persisted JSON Schema closes that gap: opt a collection in and, on every vault open, the hub converts your validator to ",[1007,191890,191893],{"href":191891,"rel":191892},"https:\u002F\u002Fjson-schema.org\u002F",[3371],"JSON Schema"," and writes it into the vault as an encrypted record, next to the collection's own data.",[937,191896,191897,191898,191900,191901,191903],{},"The record lives at ",[941,191899,166841],{}," and is encrypted with the same per-collection key (",[1007,191902,5627],{"href":60250},") that encrypts the collection's records — so anyone who can unlock the collection's data can also read its schema, and no one else. Writing is skip-aware: the hub hashes the freshly-derived JSON Schema and compares it to what's already stored, so an unchanged schema costs one read and no write.",[937,191905,191906,191907,191909,191910,191913],{},"This is always on within its layer — there's no ",[941,191908,943],{}," factory to add. The opt-in is the ",[941,191911,191912],{},"persistJsonSchema: true"," collection option.",[1022,191915,58394],{"id":58393},[2217,191917,191919],{"className":2219,"code":191918,"language":2221,"meta":1037,"style":1037},"import { z } from 'zod'\nimport { createNoydb } from '@noy-db\u002Fhub'\nimport { toMemory } from '@noy-db\u002Fto-memory'\n\nconst store = toMemory()\nconst db = await createNoydb({\n  store,\n  user: 'alice',\n  secret: 'a-strong-secret',\n})\n\nconst vault = await db.openVault('acme')\n\nconst InvoiceSchema = z.object({\n  id: z.string(),\n  amount: z.number(),\n})\n\nconst invoices = vault.collection('invoices', {\n  schema: InvoiceSchema,\n  persistJsonSchema: true,\n})\n",[941,191920,191921,191939,191957,191975,191979,191991,192007,192013,192027,192041,192047,192051,192077,192081,192099,192115,192131,192137,192141,192167,192177,192188],{"__ignoreMap":1037},[2225,191922,191923,191925,191927,191929,191931,191933,191935,191937],{"class":2227,"line":2228},[2225,191924,2232],{"class":2231},[2225,191926,2236],{"class":2235},[2225,191928,28314],{"class":2239},[2225,191930,2243],{"class":2235},[2225,191932,2246],{"class":2231},[2225,191934,2249],{"class":2235},[2225,191936,28323],{"class":2252},[2225,191938,2256],{"class":2235},[2225,191940,191941,191943,191945,191947,191949,191951,191953,191955],{"class":2227,"line":1041},[2225,191942,2232],{"class":2231},[2225,191944,2236],{"class":2235},[2225,191946,2744],{"class":2239},[2225,191948,2243],{"class":2235},[2225,191950,2246],{"class":2231},[2225,191952,2249],{"class":2235},[2225,191954,1086],{"class":2252},[2225,191956,2256],{"class":2235},[2225,191958,191959,191961,191963,191965,191967,191969,191971,191973],{"class":2227,"line":1038},[2225,191960,2232],{"class":2231},[2225,191962,2236],{"class":2235},[2225,191964,68576],{"class":2239},[2225,191966,2243],{"class":2235},[2225,191968,2246],{"class":2231},[2225,191970,2249],{"class":2235},[2225,191972,9638],{"class":2252},[2225,191974,2256],{"class":2235},[2225,191976,191977],{"class":2227,"line":2288},[2225,191978,2285],{"emptyLinePlaceholder":2284},[2225,191980,191981,191983,191985,191987,191989],{"class":2227,"line":2304},[2225,191982,2775],{"class":2774},[2225,191984,39676],{"class":2239},[2225,191986,2781],{"class":2235},[2225,191988,68576],{"class":2291},[2225,191990,8146],{"class":2239},[2225,191992,191993,191995,191997,191999,192001,192003,192005],{"class":2227,"line":2356},[2225,191994,2775],{"class":2774},[2225,191996,2778],{"class":2239},[2225,191998,2781],{"class":2235},[2225,192000,2784],{"class":2231},[2225,192002,2744],{"class":2291},[2225,192004,2789],{"class":2239},[2225,192006,4200],{"class":2235},[2225,192008,192009,192011],{"class":2227,"line":2830},[2225,192010,15608],{"class":2239},[2225,192012,1707],{"class":2235},[2225,192014,192015,192017,192019,192021,192023,192025],{"class":2227,"line":2861},[2225,192016,4205],{"class":2307},[2225,192018,2311],{"class":2235},[2225,192020,2249],{"class":2235},[2225,192022,6408],{"class":2252},[2225,192024,2805],{"class":2235},[2225,192026,1707],{"class":2235},[2225,192028,192029,192031,192033,192035,192037,192039],{"class":2227,"line":4246},[2225,192030,10423],{"class":2307},[2225,192032,2311],{"class":2235},[2225,192034,2249],{"class":2235},[2225,192036,6421],{"class":2252},[2225,192038,2805],{"class":2235},[2225,192040,1707],{"class":2235},[2225,192042,192043,192045],{"class":2227,"line":4253},[2225,192044,2359],{"class":2235},[2225,192046,2362],{"class":2239},[2225,192048,192049],{"class":2227,"line":4259},[2225,192050,2285],{"emptyLinePlaceholder":2284},[2225,192052,192053,192055,192057,192059,192061,192063,192065,192067,192069,192071,192073,192075],{"class":2227,"line":6548},[2225,192054,2775],{"class":2774},[2225,192056,2835],{"class":2239},[2225,192058,2781],{"class":2235},[2225,192060,2784],{"class":2231},[2225,192062,2842],{"class":2239},[2225,192064,1520],{"class":2235},[2225,192066,2847],{"class":2291},[2225,192068,2789],{"class":2239},[2225,192070,2805],{"class":2235},[2225,192072,6450],{"class":2252},[2225,192074,2805],{"class":2235},[2225,192076,2362],{"class":2239},[2225,192078,192079],{"class":2227,"line":6576},[2225,192080,2285],{"emptyLinePlaceholder":2284},[2225,192082,192083,192085,192087,192089,192091,192093,192095,192097],{"class":2227,"line":6582},[2225,192084,2775],{"class":2774},[2225,192086,28354],{"class":2239},[2225,192088,2781],{"class":2235},[2225,192090,28314],{"class":2239},[2225,192092,1520],{"class":2235},[2225,192094,25505],{"class":2291},[2225,192096,2789],{"class":2239},[2225,192098,4200],{"class":2235},[2225,192100,192101,192103,192105,192107,192109,192111,192113],{"class":2227,"line":6587},[2225,192102,28371],{"class":2307},[2225,192104,2311],{"class":2235},[2225,192106,28314],{"class":2239},[2225,192108,1520],{"class":2235},[2225,192110,7259],{"class":2291},[2225,192112,4228],{"class":2239},[2225,192114,1707],{"class":2235},[2225,192116,192117,192119,192121,192123,192125,192127,192129],{"class":2227,"line":7174},[2225,192118,28389],{"class":2307},[2225,192120,2311],{"class":2235},[2225,192122,28314],{"class":2239},[2225,192124,1520],{"class":2235},[2225,192126,20636],{"class":2291},[2225,192128,4228],{"class":2239},[2225,192130,1707],{"class":2235},[2225,192132,192133,192135],{"class":2227,"line":7180},[2225,192134,2359],{"class":2235},[2225,192136,2362],{"class":2239},[2225,192138,192139],{"class":2227,"line":7220},[2225,192140,2285],{"emptyLinePlaceholder":2284},[2225,192142,192143,192145,192147,192149,192151,192153,192155,192157,192159,192161,192163,192165],{"class":2227,"line":7225},[2225,192144,2775],{"class":2774},[2225,192146,6461],{"class":2239},[2225,192148,2781],{"class":2235},[2225,192150,6466],{"class":2239},[2225,192152,1520],{"class":2235},[2225,192154,6471],{"class":2291},[2225,192156,2789],{"class":2239},[2225,192158,2805],{"class":2235},[2225,192160,6487],{"class":2252},[2225,192162,2805],{"class":2235},[2225,192164,2298],{"class":2235},[2225,192166,2301],{"class":2235},[2225,192168,192169,192171,192173,192175],{"class":2227,"line":7231},[2225,192170,28618],{"class":2307},[2225,192172,2311],{"class":2235},[2225,192174,28598],{"class":2239},[2225,192176,1707],{"class":2235},[2225,192178,192179,192182,192184,192186],{"class":2227,"line":7267},[2225,192180,192181],{"class":2307},"  persistJsonSchema",[2225,192183,2311],{"class":2235},[2225,192185,2322],{"class":2321},[2225,192187,1707],{"class":2235},[2225,192189,192190,192192],{"class":2227,"line":7272},[2225,192191,2359],{"class":2235},[2225,192193,2362],{"class":2239},[937,192195,192196,192197,192199,192200,47474,192202,192204,192205,192207],{},"Two things are required: a ",[941,192198,23934],{}," (v0 derives real JSON Schema only from Zod — see ",[1007,192201,68210],{"href":126812},[941,192203,191912],{},". Default is ",[941,192206,2560],{},"; omitting the option writes nothing.",[937,192209,192210,192211,192213],{},"The write is asynchronous and does not block ",[941,192212,2138],{}," — it's queued in the background against the DEK derivation and the store write, so normal application code never waits on it. Tests (or anything that must observe the write immediately, as in the examples below) can drain the queue explicitly:",[2217,192215,192217],{"className":2219,"code":192216,"language":2221,"meta":1037,"style":1037},"await vault._drainPendingSchemaWrites()\n",[941,192218,192219],{"__ignoreMap":1037},[2225,192220,192221,192223,192225,192227,192230],{"class":2227,"line":2228},[2225,192222,6500],{"class":2231},[2225,192224,6466],{"class":2239},[2225,192226,1520],{"class":2235},[2225,192228,192229],{"class":2291},"_drainPendingSchemaWrites",[2225,192231,8146],{"class":2239},[1022,192233,29893],{"id":29892},[1591,192235,192237],{"id":192236},"first-open-the-envelope-is-written","First open — the envelope is written",[2217,192239,192241],{"className":2219,"code":192240,"language":2221,"meta":1037,"style":1037},"await vault._drainPendingSchemaWrites()\nconst stored = await store.get('acme', '_schemas', 'invoices')\n",[941,192242,192243,192255],{"__ignoreMap":1037},[2225,192244,192245,192247,192249,192251,192253],{"class":2227,"line":2228},[2225,192246,6500],{"class":2231},[2225,192248,6466],{"class":2239},[2225,192250,1520],{"class":2235},[2225,192252,192229],{"class":2291},[2225,192254,8146],{"class":2239},[2225,192256,192257,192259,192262,192264,192266,192268,192270,192272,192274,192276,192278,192280,192282,192284,192286,192288,192290,192292,192294,192296],{"class":2227,"line":1041},[2225,192258,2775],{"class":2774},[2225,192260,192261],{"class":2239}," stored ",[2225,192263,2781],{"class":2235},[2225,192265,2784],{"class":2231},[2225,192267,7564],{"class":2239},[2225,192269,1520],{"class":2235},[2225,192271,3158],{"class":2291},[2225,192273,2789],{"class":2239},[2225,192275,2805],{"class":2235},[2225,192277,6450],{"class":2252},[2225,192279,2805],{"class":2235},[2225,192281,2298],{"class":2235},[2225,192283,2249],{"class":2235},[2225,192285,35623],{"class":2252},[2225,192287,2805],{"class":2235},[2225,192289,2298],{"class":2235},[2225,192291,2249],{"class":2235},[2225,192293,6487],{"class":2252},[2225,192295,2805],{"class":2235},[2225,192297,2362],{"class":2239},[2217,192299,192301],{"className":6604,"code":192300,"language":141,"meta":1037,"style":1037},"{\n  \"_noydb\": 1,\n  \"_v\": 1,\n  \"_ts\": \"2026-07-08T10:00:00.000Z\",\n  \"_iv\": \"…\",\n  \"_data\": \"…\"\n}\n",[941,192302,192303,192307,192321,192335,192354,192372,192388],{"__ignoreMap":1037},[2225,192304,192305],{"class":2227,"line":2228},[2225,192306,4200],{"class":2235},[2225,192308,192309,192311,192313,192315,192317,192319],{"class":2227,"line":1041},[2225,192310,9368],{"class":2235},[2225,192312,1697],{"class":2774},[2225,192314,6620],{"class":2235},[2225,192316,2311],{"class":2235},[2225,192318,9377],{"class":6527},[2225,192320,1707],{"class":2235},[2225,192322,192323,192325,192327,192329,192331,192333],{"class":2227,"line":1038},[2225,192324,9368],{"class":2235},[2225,192326,1700],{"class":2774},[2225,192328,6620],{"class":2235},[2225,192330,2311],{"class":2235},[2225,192332,9377],{"class":6527},[2225,192334,1707],{"class":2235},[2225,192336,192337,192339,192341,192343,192345,192347,192350,192352],{"class":2227,"line":2288},[2225,192338,9368],{"class":2235},[2225,192340,1703],{"class":2774},[2225,192342,6620],{"class":2235},[2225,192344,2311],{"class":2235},[2225,192346,6614],{"class":2235},[2225,192348,192349],{"class":2252},"2026-07-08T10:00:00.000Z",[2225,192351,6620],{"class":2235},[2225,192353,1707],{"class":2235},[2225,192355,192356,192358,192360,192362,192364,192366,192368,192370],{"class":2227,"line":2304},[2225,192357,9368],{"class":2235},[2225,192359,1143],{"class":2774},[2225,192361,6620],{"class":2235},[2225,192363,2311],{"class":2235},[2225,192365,6614],{"class":2235},[2225,192367,14475],{"class":2252},[2225,192369,6620],{"class":2235},[2225,192371,1707],{"class":2235},[2225,192373,192374,192376,192378,192380,192382,192384,192386],{"class":2227,"line":2356},[2225,192375,9368],{"class":2235},[2225,192377,1146],{"class":2774},[2225,192379,6620],{"class":2235},[2225,192381,2311],{"class":2235},[2225,192383,6614],{"class":2235},[2225,192385,14475],{"class":2252},[2225,192387,9449],{"class":2235},[2225,192389,192390],{"class":2227,"line":2830},[2225,192391,7351],{"class":2235},[937,192393,192394,192395,1625,192397,192399,192400,192402],{},"The record on the wire is a standard encrypted envelope — ",[941,192396,1143],{},[941,192398,1146],{}," hide the payload from anything that doesn't hold the collection's DEK. Decrypted, ",[941,192401,1146],{}," holds:",[2217,192404,192406],{"className":6604,"code":192405,"language":141,"meta":1037,"style":1037},"{\n  \"_noydb_schema\": 1,\n  \"kind\": \"Zod\",\n  \"jsonSchema\": {\n    \"type\": \"object\",\n    \"properties\": {\n      \"id\": { \"type\": \"string\" },\n      \"amount\": { \"type\": \"number\" }\n    },\n    \"required\": [\"id\", \"amount\"]\n  },\n  \"hash\": \"3f2e...c9\",\n  \"derivedAt\": \"2026-07-08T10:00:00.000Z\"\n}\n",[941,192407,192408,192412,192427,192445,192458,192476,192488,192516,192544,192548,192576,192580,192599,192615],{"__ignoreMap":1037},[2225,192409,192410],{"class":2227,"line":2228},[2225,192411,4200],{"class":2235},[2225,192413,192414,192416,192419,192421,192423,192425],{"class":2227,"line":1041},[2225,192415,9368],{"class":2235},[2225,192417,192418],{"class":2774},"_noydb_schema",[2225,192420,6620],{"class":2235},[2225,192422,2311],{"class":2235},[2225,192424,9377],{"class":6527},[2225,192426,1707],{"class":2235},[2225,192428,192429,192431,192433,192435,192437,192439,192441,192443],{"class":2227,"line":1038},[2225,192430,9368],{"class":2235},[2225,192432,27895],{"class":2774},[2225,192434,6620],{"class":2235},[2225,192436,2311],{"class":2235},[2225,192438,6614],{"class":2235},[2225,192440,166483],{"class":2252},[2225,192442,6620],{"class":2235},[2225,192444,1707],{"class":2235},[2225,192446,192447,192449,192452,192454,192456],{"class":2227,"line":2288},[2225,192448,9368],{"class":2235},[2225,192450,192451],{"class":2774},"jsonSchema",[2225,192453,6620],{"class":2235},[2225,192455,2311],{"class":2235},[2225,192457,2301],{"class":2235},[2225,192459,192460,192462,192464,192466,192468,192470,192472,192474],{"class":2227,"line":2304},[2225,192461,14201],{"class":2235},[2225,192463,6339],{"class":6342},[2225,192465,6620],{"class":2235},[2225,192467,2311],{"class":2235},[2225,192469,6614],{"class":2235},[2225,192471,25505],{"class":2252},[2225,192473,6620],{"class":2235},[2225,192475,1707],{"class":2235},[2225,192477,192478,192480,192482,192484,192486],{"class":2227,"line":2356},[2225,192479,14201],{"class":2235},[2225,192481,181934],{"class":6342},[2225,192483,6620],{"class":2235},[2225,192485,2311],{"class":2235},[2225,192487,2301],{"class":2235},[2225,192489,192490,192492,192494,192496,192498,192500,192502,192504,192506,192508,192510,192512,192514],{"class":2227,"line":2830},[2225,192491,59948],{"class":2235},[2225,192493,6983],{"class":6527},[2225,192495,6620],{"class":2235},[2225,192497,2311],{"class":2235},[2225,192499,2236],{"class":2235},[2225,192501,6614],{"class":2235},[2225,192503,6339],{"class":2307},[2225,192505,6620],{"class":2235},[2225,192507,2311],{"class":2235},[2225,192509,6614],{"class":2235},[2225,192511,7259],{"class":2252},[2225,192513,6620],{"class":2235},[2225,192515,2353],{"class":2235},[2225,192517,192518,192520,192522,192524,192526,192528,192530,192532,192534,192536,192538,192540,192542],{"class":2227,"line":2861},[2225,192519,59948],{"class":2235},[2225,192521,6617],{"class":6527},[2225,192523,6620],{"class":2235},[2225,192525,2311],{"class":2235},[2225,192527,2236],{"class":2235},[2225,192529,6614],{"class":2235},[2225,192531,6339],{"class":2307},[2225,192533,6620],{"class":2235},[2225,192535,2311],{"class":2235},[2225,192537,6614],{"class":2235},[2225,192539,20636],{"class":2252},[2225,192541,6620],{"class":2235},[2225,192543,6369],{"class":2235},[2225,192545,192546],{"class":2227,"line":4246},[2225,192547,8324],{"class":2235},[2225,192549,192550,192552,192554,192556,192558,192560,192562,192564,192566,192568,192570,192572,192574],{"class":2227,"line":4253},[2225,192551,14201],{"class":2235},[2225,192553,45375],{"class":6342},[2225,192555,6620],{"class":2235},[2225,192557,2311],{"class":2235},[2225,192559,8667],{"class":2235},[2225,192561,6620],{"class":2235},[2225,192563,6983],{"class":2252},[2225,192565,6620],{"class":2235},[2225,192567,2298],{"class":2235},[2225,192569,6614],{"class":2235},[2225,192571,6617],{"class":2252},[2225,192573,6620],{"class":2235},[2225,192575,26484],{"class":2235},[2225,192577,192578],{"class":2227,"line":4259},[2225,192579,19017],{"class":2235},[2225,192581,192582,192584,192586,192588,192590,192592,192595,192597],{"class":2227,"line":6548},[2225,192583,9368],{"class":2235},[2225,192585,134337],{"class":2774},[2225,192587,6620],{"class":2235},[2225,192589,2311],{"class":2235},[2225,192591,6614],{"class":2235},[2225,192593,192594],{"class":2252},"3f2e...c9",[2225,192596,6620],{"class":2235},[2225,192598,1707],{"class":2235},[2225,192600,192601,192603,192605,192607,192609,192611,192613],{"class":2227,"line":6576},[2225,192602,9368],{"class":2235},[2225,192604,140806],{"class":2774},[2225,192606,6620],{"class":2235},[2225,192608,2311],{"class":2235},[2225,192610,6614],{"class":2235},[2225,192612,192349],{"class":2252},[2225,192614,9449],{"class":2235},[2225,192616,192617],{"class":2227,"line":6582},[2225,192618,7351],{"class":2235},[1591,192620,192622],{"id":192621},"reopening-with-an-unchanged-schema-no-write","Reopening with an unchanged schema — no write",[937,192624,192625,192627,192628,192630,192631,192634,192635,192637,192638,192640,192641,192643,192644,192646],{},[941,192626,2138],{}," caches by name on that ",[941,192629,94],{}," instance — calling it again for ",[941,192632,192633],{},"'invoices'"," on the same ",[941,192636,2694],{}," object is a no-op, it doesn't re-derive anything. To observe the hash-skip you need a genuinely fresh session: a new ",[941,192639,62],{}," (via ",[941,192642,12780],{},") opening the same vault name against the same ",[941,192645,6951],{},", exactly as a second process or a later run of your app would:",[2217,192648,192650],{"className":2219,"code":192649,"language":2221,"meta":1037,"style":1037},"\u002F\u002F a fresh session against the same store — same user\u002Fsecret, new Noydb instance:\nconst db2 = await createNoydb({ store, user: 'alice', secret: 'a-strong-secret' })\nconst vault2 = await db2.openVault('acme')\n\nvault2.collection('invoices', { schema: InvoiceSchema, persistJsonSchema: true })\nawait vault2._drainPendingSchemaWrites()\n\nconst after = await store.get('acme', '_schemas', 'invoices')\nafter._v \u002F\u002F → 1  (unchanged — the derived hash matched, so the write was skipped)\n",[941,192651,192652,192657,192703,192729,192733,192773,192785,192789,192831],{"__ignoreMap":1037},[2225,192653,192654],{"class":2227,"line":2228},[2225,192655,192656],{"class":2763},"\u002F\u002F a fresh session against the same store — same user\u002Fsecret, new Noydb instance:\n",[2225,192658,192659,192661,192663,192665,192667,192669,192671,192673,192675,192677,192679,192681,192683,192685,192687,192689,192691,192693,192695,192697,192699,192701],{"class":2227,"line":1041},[2225,192660,2775],{"class":2774},[2225,192662,89127],{"class":2239},[2225,192664,2781],{"class":2235},[2225,192666,2784],{"class":2231},[2225,192668,2744],{"class":2291},[2225,192670,2789],{"class":2239},[2225,192672,2792],{"class":2235},[2225,192674,7564],{"class":2239},[2225,192676,2298],{"class":2235},[2225,192678,2795],{"class":2307},[2225,192680,2311],{"class":2235},[2225,192682,2249],{"class":2235},[2225,192684,6408],{"class":2252},[2225,192686,2805],{"class":2235},[2225,192688,2298],{"class":2235},[2225,192690,2810],{"class":2307},[2225,192692,2311],{"class":2235},[2225,192694,2249],{"class":2235},[2225,192696,6421],{"class":2252},[2225,192698,2805],{"class":2235},[2225,192700,2243],{"class":2235},[2225,192702,2362],{"class":2239},[2225,192704,192705,192707,192709,192711,192713,192715,192717,192719,192721,192723,192725,192727],{"class":2227,"line":1038},[2225,192706,2775],{"class":2774},[2225,192708,89180],{"class":2239},[2225,192710,2781],{"class":2235},[2225,192712,2784],{"class":2231},[2225,192714,89187],{"class":2239},[2225,192716,1520],{"class":2235},[2225,192718,2847],{"class":2291},[2225,192720,2789],{"class":2239},[2225,192722,2805],{"class":2235},[2225,192724,6450],{"class":2252},[2225,192726,2805],{"class":2235},[2225,192728,2362],{"class":2239},[2225,192730,192731],{"class":2227,"line":2288},[2225,192732,2285],{"emptyLinePlaceholder":2284},[2225,192734,192735,192738,192740,192742,192744,192746,192748,192750,192752,192754,192756,192758,192760,192762,192765,192767,192769,192771],{"class":2227,"line":2304},[2225,192736,192737],{"class":2239},"vault2",[2225,192739,1520],{"class":2235},[2225,192741,6471],{"class":2291},[2225,192743,2789],{"class":2239},[2225,192745,2805],{"class":2235},[2225,192747,6487],{"class":2252},[2225,192749,2805],{"class":2235},[2225,192751,2298],{"class":2235},[2225,192753,2236],{"class":2235},[2225,192755,51997],{"class":2307},[2225,192757,2311],{"class":2235},[2225,192759,28598],{"class":2239},[2225,192761,2298],{"class":2235},[2225,192763,192764],{"class":2307}," persistJsonSchema",[2225,192766,2311],{"class":2235},[2225,192768,2322],{"class":2321},[2225,192770,2243],{"class":2235},[2225,192772,2362],{"class":2239},[2225,192774,192775,192777,192779,192781,192783],{"class":2227,"line":2356},[2225,192776,6500],{"class":2231},[2225,192778,89208],{"class":2239},[2225,192780,1520],{"class":2235},[2225,192782,192229],{"class":2291},[2225,192784,8146],{"class":2239},[2225,192786,192787],{"class":2227,"line":2830},[2225,192788,2285],{"emptyLinePlaceholder":2284},[2225,192790,192791,192793,192795,192797,192799,192801,192803,192805,192807,192809,192811,192813,192815,192817,192819,192821,192823,192825,192827,192829],{"class":2227,"line":2861},[2225,192792,2775],{"class":2774},[2225,192794,22876],{"class":2239},[2225,192796,2781],{"class":2235},[2225,192798,2784],{"class":2231},[2225,192800,7564],{"class":2239},[2225,192802,1520],{"class":2235},[2225,192804,3158],{"class":2291},[2225,192806,2789],{"class":2239},[2225,192808,2805],{"class":2235},[2225,192810,6450],{"class":2252},[2225,192812,2805],{"class":2235},[2225,192814,2298],{"class":2235},[2225,192816,2249],{"class":2235},[2225,192818,35623],{"class":2252},[2225,192820,2805],{"class":2235},[2225,192822,2298],{"class":2235},[2225,192824,2249],{"class":2235},[2225,192826,6487],{"class":2252},[2225,192828,2805],{"class":2235},[2225,192830,2362],{"class":2239},[2225,192832,192833,192835,192837,192840],{"class":2227,"line":4246},[2225,192834,10761],{"class":2239},[2225,192836,1520],{"class":2235},[2225,192838,192839],{"class":2239},"_v ",[2225,192841,192842],{"class":2763},"\u002F\u002F → 1  (unchanged — the derived hash matched, so the write was skipped)\n",[937,192844,192845],{},"The hub re-derives the JSON Schema, hashes it, and compares the hash to the one already stored. Equal hash means equal shape, so it skips the write entirely — reopening a vault with hundreds of persisted-schema collections doesn't churn every one of them on every startup.",[1591,192847,192849],{"id":192848},"changing-the-schema-a-new-version-is-written","Changing the schema — a new version is written",[937,192851,192852,192853,192855,192856,192858,192859,192861],{},"Same pattern — a fresh ",[941,192854,62],{},"\u002Fvault against the same ",[941,192857,6951],{}," — but this time the collection's ",[941,192860,23934],{}," has a new field:",[2217,192863,192865],{"className":2219,"code":192864,"language":2221,"meta":1037,"style":1037},"const db3 = await createNoydb({ store, user: 'alice', secret: 'a-strong-secret' })\nconst vault3 = await db3.openVault('acme')\n\nvault3.collection('invoices', {\n  schema: z.object({ id: z.string(), amount: z.number(), note: z.string() }), \u002F\u002F shape changed\n  persistJsonSchema: true,\n})\nawait vault3._drainPendingSchemaWrites()\n\nconst bumped = await store.get('acme', '_schemas', 'invoices')\nbumped._v \u002F\u002F → 2\n",[941,192866,192867,192914,192942,192946,192967,193033,193043,193049,193062,193066,193109],{"__ignoreMap":1037},[2225,192868,192869,192871,192874,192876,192878,192880,192882,192884,192886,192888,192890,192892,192894,192896,192898,192900,192902,192904,192906,192908,192910,192912],{"class":2227,"line":2228},[2225,192870,2775],{"class":2774},[2225,192872,192873],{"class":2239}," db3 ",[2225,192875,2781],{"class":2235},[2225,192877,2784],{"class":2231},[2225,192879,2744],{"class":2291},[2225,192881,2789],{"class":2239},[2225,192883,2792],{"class":2235},[2225,192885,7564],{"class":2239},[2225,192887,2298],{"class":2235},[2225,192889,2795],{"class":2307},[2225,192891,2311],{"class":2235},[2225,192893,2249],{"class":2235},[2225,192895,6408],{"class":2252},[2225,192897,2805],{"class":2235},[2225,192899,2298],{"class":2235},[2225,192901,2810],{"class":2307},[2225,192903,2311],{"class":2235},[2225,192905,2249],{"class":2235},[2225,192907,6421],{"class":2252},[2225,192909,2805],{"class":2235},[2225,192911,2243],{"class":2235},[2225,192913,2362],{"class":2239},[2225,192915,192916,192918,192921,192923,192925,192928,192930,192932,192934,192936,192938,192940],{"class":2227,"line":1041},[2225,192917,2775],{"class":2774},[2225,192919,192920],{"class":2239}," vault3 ",[2225,192922,2781],{"class":2235},[2225,192924,2784],{"class":2231},[2225,192926,192927],{"class":2239}," db3",[2225,192929,1520],{"class":2235},[2225,192931,2847],{"class":2291},[2225,192933,2789],{"class":2239},[2225,192935,2805],{"class":2235},[2225,192937,6450],{"class":2252},[2225,192939,2805],{"class":2235},[2225,192941,2362],{"class":2239},[2225,192943,192944],{"class":2227,"line":1038},[2225,192945,2285],{"emptyLinePlaceholder":2284},[2225,192947,192948,192951,192953,192955,192957,192959,192961,192963,192965],{"class":2227,"line":2288},[2225,192949,192950],{"class":2239},"vault3",[2225,192952,1520],{"class":2235},[2225,192954,6471],{"class":2291},[2225,192956,2789],{"class":2239},[2225,192958,2805],{"class":2235},[2225,192960,6487],{"class":2252},[2225,192962,2805],{"class":2235},[2225,192964,2298],{"class":2235},[2225,192966,2301],{"class":2235},[2225,192968,192969,192971,192973,192975,192977,192979,192981,192983,192985,192987,192989,192991,192993,192995,192997,192999,193001,193003,193005,193007,193009,193011,193014,193016,193018,193020,193022,193024,193026,193028,193030],{"class":2227,"line":2304},[2225,192970,28618],{"class":2307},[2225,192972,2311],{"class":2235},[2225,192974,28314],{"class":2239},[2225,192976,1520],{"class":2235},[2225,192978,25505],{"class":2291},[2225,192980,2789],{"class":2239},[2225,192982,2792],{"class":2235},[2225,192984,7070],{"class":2307},[2225,192986,2311],{"class":2235},[2225,192988,28314],{"class":2239},[2225,192990,1520],{"class":2235},[2225,192992,7259],{"class":2291},[2225,192994,4228],{"class":2239},[2225,192996,2298],{"class":2235},[2225,192998,6351],{"class":2307},[2225,193000,2311],{"class":2235},[2225,193002,28314],{"class":2239},[2225,193004,1520],{"class":2235},[2225,193006,20636],{"class":2291},[2225,193008,4228],{"class":2239},[2225,193010,2298],{"class":2235},[2225,193012,193013],{"class":2307}," note",[2225,193015,2311],{"class":2235},[2225,193017,28314],{"class":2239},[2225,193019,1520],{"class":2235},[2225,193021,7259],{"class":2291},[2225,193023,19132],{"class":2239},[2225,193025,2359],{"class":2235},[2225,193027,1381],{"class":2239},[2225,193029,2298],{"class":2235},[2225,193031,193032],{"class":2763}," \u002F\u002F shape changed\n",[2225,193034,193035,193037,193039,193041],{"class":2227,"line":2356},[2225,193036,192181],{"class":2307},[2225,193038,2311],{"class":2235},[2225,193040,2322],{"class":2321},[2225,193042,1707],{"class":2235},[2225,193044,193045,193047],{"class":2227,"line":2830},[2225,193046,2359],{"class":2235},[2225,193048,2362],{"class":2239},[2225,193050,193051,193053,193056,193058,193060],{"class":2227,"line":2861},[2225,193052,6500],{"class":2231},[2225,193054,193055],{"class":2239}," vault3",[2225,193057,1520],{"class":2235},[2225,193059,192229],{"class":2291},[2225,193061,8146],{"class":2239},[2225,193063,193064],{"class":2227,"line":4246},[2225,193065,2285],{"emptyLinePlaceholder":2284},[2225,193067,193068,193070,193073,193075,193077,193079,193081,193083,193085,193087,193089,193091,193093,193095,193097,193099,193101,193103,193105,193107],{"class":2227,"line":4253},[2225,193069,2775],{"class":2774},[2225,193071,193072],{"class":2239}," bumped ",[2225,193074,2781],{"class":2235},[2225,193076,2784],{"class":2231},[2225,193078,7564],{"class":2239},[2225,193080,1520],{"class":2235},[2225,193082,3158],{"class":2291},[2225,193084,2789],{"class":2239},[2225,193086,2805],{"class":2235},[2225,193088,6450],{"class":2252},[2225,193090,2805],{"class":2235},[2225,193092,2298],{"class":2235},[2225,193094,2249],{"class":2235},[2225,193096,35623],{"class":2252},[2225,193098,2805],{"class":2235},[2225,193100,2298],{"class":2235},[2225,193102,2249],{"class":2235},[2225,193104,6487],{"class":2252},[2225,193106,2805],{"class":2235},[2225,193108,2362],{"class":2239},[2225,193110,193111,193114,193116,193118],{"class":2227,"line":4259},[2225,193112,193113],{"class":2239},"bumped",[2225,193115,1520],{"class":2235},[2225,193117,192839],{"class":2239},[2225,193119,26947],{"class":2763},[1591,193121,193123],{"id":193122},"a-non-zod-validator-a-stub-envelope-not-a-json-schema","A non-Zod validator — a stub envelope, not a JSON Schema",[937,193125,193126,193127,193129,193130,193134],{},"If the collection's ",[941,193128,23934],{}," isn't Zod (Valibot, ArkType, Effect Schema — any other ",[1007,193131,193133],{"href":22620,"rel":193132},[3371],"Standard Schema"," validator), the hub still writes a record, but without a derived body:",[2217,193136,193138],{"className":2219,"code":193137,"language":2221,"meta":1037,"style":1037},"import { derivePersistedSchema } from '@noy-db\u002Fhub'\n\n\u002F\u002F a Standard Schema v1 validator that isn't Zod (shape a real Valibot\u002FArkType\u002F\n\u002F\u002F Effect Schema value carries — the `~standard.vendor` tag is what's detected)\nconst nonZodValidator = { '~standard': { version: 1, vendor: 'valibot', validate: () => ({}) } }\n\nconst stub = await derivePersistedSchema(nonZodValidator)\n",[941,193139,193140,193159,193163,193168,193173,193237,193241],{"__ignoreMap":1037},[2225,193141,193142,193144,193146,193149,193151,193153,193155,193157],{"class":2227,"line":2228},[2225,193143,2232],{"class":2231},[2225,193145,2236],{"class":2235},[2225,193147,193148],{"class":2239}," derivePersistedSchema",[2225,193150,2243],{"class":2235},[2225,193152,2246],{"class":2231},[2225,193154,2249],{"class":2235},[2225,193156,1086],{"class":2252},[2225,193158,2256],{"class":2235},[2225,193160,193161],{"class":2227,"line":1041},[2225,193162,2285],{"emptyLinePlaceholder":2284},[2225,193164,193165],{"class":2227,"line":1038},[2225,193166,193167],{"class":2763},"\u002F\u002F a Standard Schema v1 validator that isn't Zod (shape a real Valibot\u002FArkType\u002F\n",[2225,193169,193170],{"class":2227,"line":2288},[2225,193171,193172],{"class":2763},"\u002F\u002F Effect Schema value carries — the `~standard.vendor` tag is what's detected)\n",[2225,193174,193175,193177,193180,193182,193184,193186,193189,193191,193193,193195,193198,193200,193202,193204,193207,193209,193211,193214,193216,193218,193221,193223,193225,193227,193229,193231,193233,193235],{"class":2227,"line":2304},[2225,193176,2775],{"class":2774},[2225,193178,193179],{"class":2239}," nonZodValidator ",[2225,193181,2781],{"class":2235},[2225,193183,2236],{"class":2235},[2225,193185,2249],{"class":2235},[2225,193187,193188],{"class":2307},"~standard",[2225,193190,2805],{"class":2235},[2225,193192,2311],{"class":2235},[2225,193194,2236],{"class":2235},[2225,193196,193197],{"class":2307}," version",[2225,193199,2311],{"class":2235},[2225,193201,9377],{"class":6527},[2225,193203,2298],{"class":2235},[2225,193205,193206],{"class":2307}," vendor",[2225,193208,2311],{"class":2235},[2225,193210,2249],{"class":2235},[2225,193212,193213],{"class":2252},"valibot",[2225,193215,2805],{"class":2235},[2225,193217,2298],{"class":2235},[2225,193219,193220],{"class":2291}," validate",[2225,193222,2311],{"class":2235},[2225,193224,13130],{"class":2235},[2225,193226,8116],{"class":2774},[2225,193228,953],{"class":2239},[2225,193230,9261],{"class":2235},[2225,193232,8495],{"class":2239},[2225,193234,2359],{"class":2235},[2225,193236,6369],{"class":2235},[2225,193238,193239],{"class":2227,"line":2356},[2225,193240,2285],{"emptyLinePlaceholder":2284},[2225,193242,193243,193245,193248,193250,193252,193254],{"class":2227,"line":2830},[2225,193244,2775],{"class":2774},[2225,193246,193247],{"class":2239}," stub ",[2225,193249,2781],{"class":2235},[2225,193251,2784],{"class":2231},[2225,193253,193148],{"class":2291},[2225,193255,193256],{"class":2239},"(nonZodValidator)\n",[2217,193258,193260],{"className":6604,"code":193259,"language":141,"meta":1037,"style":1037},"{\n  \"_noydb_schema\": 1,\n  \"kind\": \"Unknown\",\n  \"jsonSchema\": null,\n  \"hash\": null,\n  \"reason\": \"derivation not yet supported for kind=Unknown (v0 supports Zod only)\",\n  \"derivedAt\": \"2026-07-08T10:00:00.000Z\"\n}\n",[941,193261,193262,193266,193280,193299,193311,193323,193342,193358],{"__ignoreMap":1037},[2225,193263,193264],{"class":2227,"line":2228},[2225,193265,4200],{"class":2235},[2225,193267,193268,193270,193272,193274,193276,193278],{"class":2227,"line":1041},[2225,193269,9368],{"class":2235},[2225,193271,192418],{"class":2774},[2225,193273,6620],{"class":2235},[2225,193275,2311],{"class":2235},[2225,193277,9377],{"class":6527},[2225,193279,1707],{"class":2235},[2225,193281,193282,193284,193286,193288,193290,193292,193295,193297],{"class":2227,"line":1038},[2225,193283,9368],{"class":2235},[2225,193285,27895],{"class":2774},[2225,193287,6620],{"class":2235},[2225,193289,2311],{"class":2235},[2225,193291,6614],{"class":2235},[2225,193293,193294],{"class":2252},"Unknown",[2225,193296,6620],{"class":2235},[2225,193298,1707],{"class":2235},[2225,193300,193301,193303,193305,193307,193309],{"class":2227,"line":2288},[2225,193302,9368],{"class":2235},[2225,193304,192451],{"class":2774},[2225,193306,6620],{"class":2235},[2225,193308,2311],{"class":2235},[2225,193310,67699],{"class":2235},[2225,193312,193313,193315,193317,193319,193321],{"class":2227,"line":2304},[2225,193314,9368],{"class":2235},[2225,193316,134337],{"class":2774},[2225,193318,6620],{"class":2235},[2225,193320,2311],{"class":2235},[2225,193322,67699],{"class":2235},[2225,193324,193325,193327,193329,193331,193333,193335,193338,193340],{"class":2227,"line":2356},[2225,193326,9368],{"class":2235},[2225,193328,43668],{"class":2774},[2225,193330,6620],{"class":2235},[2225,193332,2311],{"class":2235},[2225,193334,6614],{"class":2235},[2225,193336,193337],{"class":2252},"derivation not yet supported for kind=Unknown (v0 supports Zod only)",[2225,193339,6620],{"class":2235},[2225,193341,1707],{"class":2235},[2225,193343,193344,193346,193348,193350,193352,193354,193356],{"class":2227,"line":2830},[2225,193345,9368],{"class":2235},[2225,193347,140806],{"class":2774},[2225,193349,6620],{"class":2235},[2225,193351,2311],{"class":2235},[2225,193353,6614],{"class":2235},[2225,193355,192349],{"class":2252},[2225,193357,9449],{"class":2235},[2225,193359,193360],{"class":2227,"line":2861},[2225,193361,7351],{"class":2235},[937,193363,193364,150978,193366,193369,193370,193372,193373,193375,193376,193378,193379,193381],{},[941,193365,27895],{},[941,193367,193368],{},"'Unknown'"," for anything the hub can't fingerprint as Zod today (see ",[1007,193371,68210],{"href":126812}," for what ",[941,193374,27895],{}," can be). Because there's no hash to compare, a later reopen skips the write only when ",[941,193377,27895],{}," hasn't changed — a ",[941,193380,27895],{}," change is the only signal available without a body.",[1022,193383,102889],{"id":102888},[937,193385,123952,193386,101421],{},[941,193387,1086],{},[1591,193389,193391],{"id":193390},"persistjsonschema",[941,193392,22790],{},[2217,193394,193396],{"className":2219,"code":193395,"language":2221,"meta":1037,"style":1037},"vault.collection\u003CT>(name, {\n  schema,\n  persistJsonSchema?: boolean,  \u002F\u002F default: false\n})\n",[941,193397,193398,193418,193424,193438],{"__ignoreMap":1037},[2225,193399,193400,193402,193404,193406,193408,193410,193412,193414,193416],{"class":2227,"line":2228},[2225,193401,2694],{"class":2239},[2225,193403,1520],{"class":2235},[2225,193405,6471],{"class":2291},[2225,193407,6474],{"class":2235},[2225,193409,11968],{"class":6342},[2225,193411,6480],{"class":2235},[2225,193413,123977],{"class":2239},[2225,193415,2298],{"class":2235},[2225,193417,2301],{"class":2235},[2225,193419,193420,193422],{"class":2227,"line":1041},[2225,193421,28618],{"class":2239},[2225,193423,1707],{"class":2235},[2225,193425,193426,193429,193431,193433,193435],{"class":2227,"line":1038},[2225,193427,193428],{"class":2239},"  persistJsonSchema?",[2225,193430,2311],{"class":2235},[2225,193432,7723],{"class":2239},[2225,193434,2298],{"class":2235},[2225,193436,193437],{"class":2763},"  \u002F\u002F default: false\n",[2225,193439,193440,193442],{"class":2227,"line":2288},[2225,193441,2359],{"class":2235},[2225,193443,2362],{"class":2239},[937,193445,20818,193446,997,193448,193450,193451,193454],{},[941,193447,11790],{},[941,193449,23934],{}," is set, the hub derives, hashes, and (if changed) writes the ",[941,193452,193453],{},"_schemas\u002F\u003Cname>"," envelope on every vault open.",[1591,193456,193458],{"id":193457},"derivepersistedschemavalidator",[941,193459,193460],{},"derivePersistedSchema(validator)",[2217,193462,193464],{"className":2219,"code":193463,"language":2221,"meta":1037,"style":1037},"function derivePersistedSchema(validator: unknown): Promise\u003CPersistedSchemaEnvelope>\n",[941,193465,193466],{"__ignoreMap":1037},[2225,193467,193468,193470,193472,193474,193476,193478,193480,193482,193484,193486,193489],{"class":2227,"line":2228},[2225,193469,12641],{"class":2774},[2225,193471,193148],{"class":2291},[2225,193473,2789],{"class":2235},[2225,193475,166464],{"class":7052},[2225,193477,2311],{"class":2235},[2225,193479,40717],{"class":6342},[2225,193481,6255],{"class":2235},[2225,193483,7079],{"class":6342},[2225,193485,6474],{"class":2235},[2225,193487,193488],{"class":6342},"PersistedSchemaEnvelope",[2225,193490,7093],{"class":2235},[937,193492,193493,193494,193496,193497,193499],{},"Converts a validator to a ",[941,193495,193488],{}," without touching storage — the pure derive step ",[941,193498,22790],{}," runs internally. Useful for previewing what would be persisted, or for computing a JSON Schema outside a vault entirely.",[2217,193501,193503],{"className":2219,"code":193502,"language":2221,"meta":1037,"style":1037},"import { derivePersistedSchema } from '@noy-db\u002Fhub'\n\nconst env = await derivePersistedSchema(z.object({ id: z.string() }))\nenv.hash \u002F\u002F → 64-char hex SHA-256 of the canonicalized JSON Schema\n",[941,193504,193505,193523,193527,193566],{"__ignoreMap":1037},[2225,193506,193507,193509,193511,193513,193515,193517,193519,193521],{"class":2227,"line":2228},[2225,193508,2232],{"class":2231},[2225,193510,2236],{"class":2235},[2225,193512,193148],{"class":2239},[2225,193514,2243],{"class":2235},[2225,193516,2246],{"class":2231},[2225,193518,2249],{"class":2235},[2225,193520,1086],{"class":2252},[2225,193522,2256],{"class":2235},[2225,193524,193525],{"class":2227,"line":1041},[2225,193526,2285],{"emptyLinePlaceholder":2284},[2225,193528,193529,193531,193533,193535,193537,193539,193542,193544,193546,193548,193550,193552,193554,193556,193558,193560,193562,193564],{"class":2227,"line":1038},[2225,193530,2775],{"class":2774},[2225,193532,15788],{"class":2239},[2225,193534,2781],{"class":2235},[2225,193536,2784],{"class":2231},[2225,193538,193148],{"class":2291},[2225,193540,193541],{"class":2239},"(z",[2225,193543,1520],{"class":2235},[2225,193545,25505],{"class":2291},[2225,193547,2789],{"class":2239},[2225,193549,2792],{"class":2235},[2225,193551,7070],{"class":2307},[2225,193553,2311],{"class":2235},[2225,193555,28314],{"class":2239},[2225,193557,1520],{"class":2235},[2225,193559,7259],{"class":2291},[2225,193561,19132],{"class":2239},[2225,193563,2359],{"class":2235},[2225,193565,8606],{"class":2239},[2225,193567,193568,193570,193572,193575],{"class":2227,"line":2288},[2225,193569,183],{"class":2239},[2225,193571,1520],{"class":2235},[2225,193573,193574],{"class":2239},"hash ",[2225,193576,193577],{"class":2763},"\u002F\u002F → 64-char hex SHA-256 of the canonicalized JSON Schema\n",[1591,193579,193581],{"id":193580},"iszodschemavalue",[941,193582,193583],{},"isZodSchema(value)",[2217,193585,193587],{"className":2219,"code":193586,"language":2221,"meta":1037,"style":1037},"function isZodSchema(value: unknown): boolean\n",[941,193588,193589],{"__ignoreMap":1037},[2225,193590,193591,193593,193596,193598,193600,193602,193604,193606],{"class":2227,"line":2228},[2225,193592,12641],{"class":2774},[2225,193594,193595],{"class":2291}," isZodSchema",[2225,193597,2789],{"class":2235},[2225,193599,25201],{"class":7052},[2225,193601,2311],{"class":2235},[2225,193603,40717],{"class":6342},[2225,193605,6255],{"class":2235},[2225,193607,11718],{"class":6342},[937,193609,193610,182638,193612,193614],{},[941,193611,11790],{},[941,193613,25201],{}," is a Zod schema (Zod v3 or v4). Used internally to pick the derivation path; exported because it's a generally useful duck-type check.",[1591,193616,193618,1261,193620,120416],{"id":193617},"persistedschemaenvelope-persistedschemakind-types",[941,193619,193488],{},[941,193621,193622],{},"PersistedSchemaKind",[2217,193624,193626],{"className":2219,"code":193625,"language":2221,"meta":1037,"style":1037},"type PersistedSchemaKind = 'Zod' | 'Valibot' | 'ArkType' | 'Effect' | 'Unknown'\n\ninterface PersistedSchemaEnvelope {\n  readonly _noydb_schema: 1\n  readonly kind: PersistedSchemaKind\n  readonly jsonSchema: object | null   \u002F\u002F null when kind isn't Zod\n  readonly hash: string | null         \u002F\u002F null when jsonSchema is null\n  readonly reason?: string             \u002F\u002F populated when jsonSchema is null\n  readonly derivedAt: string           \u002F\u002F ISO-8601\n  readonly classified?: ClassifiedMarker  \u002F\u002F config-drift marker, see classified.md\n  readonly satellite?: PairingMarker      \u002F\u002F satellite pairing marker, see satellites docs\n}\n",[941,193627,193628,193677,193681,193690,193701,193712,193731,193749,193762,193776,193790,193805],{"__ignoreMap":1037},[2225,193629,193630,193632,193635,193637,193639,193641,193643,193645,193647,193650,193652,193654,193656,193659,193661,193663,193665,193667,193669,193671,193673,193675],{"class":2227,"line":2228},[2225,193631,6339],{"class":2774},[2225,193633,193634],{"class":6342}," PersistedSchemaKind",[2225,193636,6346],{"class":2235},[2225,193638,2249],{"class":2235},[2225,193640,166483],{"class":2252},[2225,193642,2805],{"class":2235},[2225,193644,7087],{"class":2235},[2225,193646,2249],{"class":2235},[2225,193648,193649],{"class":2252},"Valibot",[2225,193651,2805],{"class":2235},[2225,193653,7087],{"class":2235},[2225,193655,2249],{"class":2235},[2225,193657,193658],{"class":2252},"ArkType",[2225,193660,2805],{"class":2235},[2225,193662,7087],{"class":2235},[2225,193664,2249],{"class":2235},[2225,193666,116059],{"class":2252},[2225,193668,2805],{"class":2235},[2225,193670,7087],{"class":2235},[2225,193672,2249],{"class":2235},[2225,193674,193294],{"class":2252},[2225,193676,2256],{"class":2235},[2225,193678,193679],{"class":2227,"line":1041},[2225,193680,2285],{"emptyLinePlaceholder":2284},[2225,193682,193683,193685,193688],{"class":2227,"line":1038},[2225,193684,6994],{"class":2774},[2225,193686,193687],{"class":6342}," PersistedSchemaEnvelope",[2225,193689,2301],{"class":2235},[2225,193691,193692,193694,193697,193699],{"class":2227,"line":2288},[2225,193693,53580],{"class":2774},[2225,193695,193696],{"class":2307}," _noydb_schema",[2225,193698,2311],{"class":2235},[2225,193700,74228],{"class":6527},[2225,193702,193703,193705,193707,193709],{"class":2227,"line":2304},[2225,193704,53580],{"class":2774},[2225,193706,8285],{"class":2307},[2225,193708,2311],{"class":2235},[2225,193710,193711],{"class":6342}," PersistedSchemaKind\n",[2225,193713,193714,193716,193719,193721,193724,193726,193728],{"class":2227,"line":2356},[2225,193715,53580],{"class":2774},[2225,193717,193718],{"class":2307}," jsonSchema",[2225,193720,2311],{"class":2235},[2225,193722,193723],{"class":6342}," object",[2225,193725,7087],{"class":2235},[2225,193727,7090],{"class":6342},[2225,193729,193730],{"class":2763},"   \u002F\u002F null when kind isn't Zod\n",[2225,193732,193733,193735,193738,193740,193742,193744,193746],{"class":2227,"line":2830},[2225,193734,53580],{"class":2774},[2225,193736,193737],{"class":2307}," hash",[2225,193739,2311],{"class":2235},[2225,193741,6366],{"class":6342},[2225,193743,7087],{"class":2235},[2225,193745,7090],{"class":6342},[2225,193747,193748],{"class":2763},"         \u002F\u002F null when jsonSchema is null\n",[2225,193750,193751,193753,193755,193757,193759],{"class":2227,"line":2861},[2225,193752,53580],{"class":2774},[2225,193754,155427],{"class":2307},[2225,193756,7011],{"class":2235},[2225,193758,6366],{"class":6342},[2225,193760,193761],{"class":2763},"             \u002F\u002F populated when jsonSchema is null\n",[2225,193763,193764,193766,193769,193771,193773],{"class":2227,"line":4246},[2225,193765,53580],{"class":2774},[2225,193767,193768],{"class":2307}," derivedAt",[2225,193770,2311],{"class":2235},[2225,193772,6366],{"class":6342},[2225,193774,193775],{"class":2763},"           \u002F\u002F ISO-8601\n",[2225,193777,193778,193780,193782,193784,193787],{"class":2227,"line":4253},[2225,193779,53580],{"class":2774},[2225,193781,117343],{"class":2307},[2225,193783,7011],{"class":2235},[2225,193785,193786],{"class":6342}," ClassifiedMarker",[2225,193788,193789],{"class":2763},"  \u002F\u002F config-drift marker, see classified.md\n",[2225,193791,193792,193794,193797,193799,193802],{"class":2227,"line":4259},[2225,193793,53580],{"class":2774},[2225,193795,193796],{"class":2307}," satellite",[2225,193798,7011],{"class":2235},[2225,193800,193801],{"class":6342}," PairingMarker",[2225,193803,193804],{"class":2763},"      \u002F\u002F satellite pairing marker, see satellites docs\n",[2225,193806,193807],{"class":2227,"line":6548},[2225,193808,7351],{"class":2235},[937,193810,193811,193812,193814,193815,2993,193817,193820,193821,6793,193823,193826,193827,56432,193830,1952,193832,997,193834,193836,193837,193839,193840,193842],{},"The decrypted shape of the ",[941,193813,166841],{}," record's payload. ",[941,193816,27895],{},[1092,193818,193819],{},"detected"," label — v0 only ever derives a real ",[941,193822,192451],{},[941,193824,193825],{},"'Zod'","; every other value in the union writes a stub with ",[941,193828,193829],{},"jsonSchema: null",[941,193831,43668],{},[941,193833,633],{},[941,193835,4836],{}," are independent of ",[941,193838,22790],{}," — they're written into this same shared record by ",[1007,193841,633],{"href":124757}," collections and satellite declarations respectively, and a schema-persisting write preserves whichever of them is already stored rather than dropping it.",[1591,193844,193846],{"id":193845},"schemas_collection",[941,193847,193848],{},"SCHEMAS_COLLECTION",[2217,193850,193852],{"className":2219,"code":193851,"language":2221,"meta":1037,"style":1037},"const SCHEMAS_COLLECTION = '_schemas'\n",[941,193853,193854],{"__ignoreMap":1037},[2225,193855,193856,193858,193861,193863,193865,193867],{"class":2227,"line":2228},[2225,193857,2775],{"class":2774},[2225,193859,193860],{"class":2239}," SCHEMAS_COLLECTION ",[2225,193862,2781],{"class":2235},[2225,193864,2249],{"class":2235},[2225,193866,35623],{"class":2252},[2225,193868,2256],{"class":2235},[937,193870,193871],{},"The reserved collection name the envelopes are stored under, exported so callers don't have to hardcode the string.",[1591,193873,193875,1261,193878],{"id":193874},"loadpersistedschemastore-vault-collection-dek-savepersistedschemastore-vault-collection-dek-payload",[941,193876,193877],{},"loadPersistedSchema(store, vault, collection, dek)",[941,193879,193880],{},"savePersistedSchema(store, vault, collection, dek, payload)",[2217,193882,193884],{"className":2219,"code":193883,"language":2221,"meta":1037,"style":1037},"function loadPersistedSchema(\n  store: NoydbStore, vault: string, collection: string, dek: EnclaveKey,\n): Promise\u003CPersistedSchemaEnvelope | undefined>\n\nfunction savePersistedSchema(\n  store: NoydbStore, vault: string, collection: string, dek: EnclaveKey,\n  payload: PersistedSchemaEnvelope, expectedVersion?: number,\n): Promise\u003Cvoid>\n",[941,193885,193886,193895,193931,193947,193951,193960,193994,194013],{"__ignoreMap":1037},[2225,193887,193888,193890,193893],{"class":2227,"line":2228},[2225,193889,12641],{"class":2774},[2225,193891,193892],{"class":2291}," loadPersistedSchema",[2225,193894,11683],{"class":2235},[2225,193896,193897,193899,193901,193903,193905,193907,193909,193911,193913,193915,193917,193919,193921,193924,193926,193929],{"class":2227,"line":1041},[2225,193898,15608],{"class":7052},[2225,193900,2311],{"class":2235},[2225,193902,3404],{"class":6342},[2225,193904,2298],{"class":2235},[2225,193906,6466],{"class":7052},[2225,193908,2311],{"class":2235},[2225,193910,6366],{"class":6342},[2225,193912,2298],{"class":2235},[2225,193914,7061],{"class":7052},[2225,193916,2311],{"class":2235},[2225,193918,6366],{"class":6342},[2225,193920,2298],{"class":2235},[2225,193922,193923],{"class":7052}," dek",[2225,193925,2311],{"class":2235},[2225,193927,193928],{"class":6342}," EnclaveKey",[2225,193930,1707],{"class":2235},[2225,193932,193933,193935,193937,193939,193941,193943,193945],{"class":2227,"line":1038},[2225,193934,6255],{"class":2235},[2225,193936,7079],{"class":6342},[2225,193938,6474],{"class":2235},[2225,193940,193488],{"class":6342},[2225,193942,7087],{"class":2235},[2225,193944,8483],{"class":6342},[2225,193946,7093],{"class":2235},[2225,193948,193949],{"class":2227,"line":2288},[2225,193950,2285],{"emptyLinePlaceholder":2284},[2225,193952,193953,193955,193958],{"class":2227,"line":2304},[2225,193954,12641],{"class":2774},[2225,193956,193957],{"class":2291}," savePersistedSchema",[2225,193959,11683],{"class":2235},[2225,193961,193962,193964,193966,193968,193970,193972,193974,193976,193978,193980,193982,193984,193986,193988,193990,193992],{"class":2227,"line":2356},[2225,193963,15608],{"class":7052},[2225,193965,2311],{"class":2235},[2225,193967,3404],{"class":6342},[2225,193969,2298],{"class":2235},[2225,193971,6466],{"class":7052},[2225,193973,2311],{"class":2235},[2225,193975,6366],{"class":6342},[2225,193977,2298],{"class":2235},[2225,193979,7061],{"class":7052},[2225,193981,2311],{"class":2235},[2225,193983,6366],{"class":6342},[2225,193985,2298],{"class":2235},[2225,193987,193923],{"class":7052},[2225,193989,2311],{"class":2235},[2225,193991,193928],{"class":6342},[2225,193993,1707],{"class":2235},[2225,193995,193996,193999,194001,194003,194005,194007,194009,194011],{"class":2227,"line":2830},[2225,193997,193998],{"class":7052},"  payload",[2225,194000,2311],{"class":2235},[2225,194002,193687],{"class":6342},[2225,194004,2298],{"class":2235},[2225,194006,7152],{"class":7052},[2225,194008,7011],{"class":2235},[2225,194010,6356],{"class":6342},[2225,194012,1707],{"class":2235},[2225,194014,194015,194017,194019,194021,194023],{"class":2227,"line":2861},[2225,194016,6255],{"class":2235},[2225,194018,7079],{"class":6342},[2225,194020,6474],{"class":2235},[2225,194022,7165],{"class":6342},[2225,194024,7093],{"class":2235},[937,194026,194027,194028,1625,194030,194032,194033,194036,194037,194039,194040,194042,194043,6000,194047,194049,194050,194052,194053,2557,194056,194058],{},"Low-level read\u002Fwrite against the raw storage adapter and a resolved DEK, rather than a ",[941,194029,94],{},[941,194031,103],{}," handle. ",[941,194034,194035],{},"dek"," is an opaque ",[941,194038,1832],{}," token — the hub's internal per-collection key type, not part of the public surface exported from ",[941,194041,1086],{}," — obtainable only from inside the hub itself. These two functions are documented here for completeness (they're the layer ",[1007,194044,194045],{"href":124740},[941,194046,1380],{},[941,194048,36126],{}," bundle-inspection command use internally to read a schema from a store or bundle that has no live validator in memory), not as plumbing you wire up yourself; ",[941,194051,191912],{}," is the opt-in that matters for application code. ",[941,194054,194055],{},"loadPersistedSchema",[941,194057,18316],{}," when nothing has been written yet, or when decryption fails (wrong DEK, corrupt record).",[1591,194060,194062,1261,194065],{"id":194061},"persistschemaifneededopts-persistschemaresult",[941,194063,194064],{},"persistSchemaIfNeeded(opts)",[941,194066,194067],{},"PersistSchemaResult",[2217,194069,194071],{"className":2219,"code":194070,"language":2221,"meta":1037,"style":1037},"function persistSchemaIfNeeded(opts: {\n  store: NoydbStore\n  vault: string\n  collectionName: string\n  validator: unknown\n  dek: EnclaveKey\n  strategies?: readonly SchemaUpdateStrategy[]\n}): Promise\u003CPersistSchemaResult>\n\ninterface PersistSchemaResult {\n  readonly written: boolean   \u002F\u002F true when a fresh envelope was written\n  readonly skipped: boolean   \u002F\u002F true when the stored envelope already matched\n  readonly envelope: PersistedSchemaEnvelope\n  readonly decision?: UpdateDecision   \u002F\u002F present when strategies ran, see schema-update.md\n}\n",[941,194072,194073,194088,194097,194105,194114,194123,194133,194147,194159,194163,194172,194185,194198,194209,194224],{"__ignoreMap":1037},[2225,194074,194075,194077,194080,194082,194084,194086],{"class":2227,"line":2228},[2225,194076,12641],{"class":2774},[2225,194078,194079],{"class":2291}," persistSchemaIfNeeded",[2225,194081,2789],{"class":2235},[2225,194083,8097],{"class":7052},[2225,194085,2311],{"class":2235},[2225,194087,2301],{"class":2235},[2225,194089,194090,194092,194094],{"class":2227,"line":1041},[2225,194091,15608],{"class":2307},[2225,194093,2311],{"class":2235},[2225,194095,194096],{"class":6342}," NoydbStore\n",[2225,194098,194099,194101,194103],{"class":2227,"line":1038},[2225,194100,36933],{"class":2307},[2225,194102,2311],{"class":2235},[2225,194104,7014],{"class":6342},[2225,194106,194107,194110,194112],{"class":2227,"line":2288},[2225,194108,194109],{"class":2307},"  collectionName",[2225,194111,2311],{"class":2235},[2225,194113,7014],{"class":6342},[2225,194115,194116,194119,194121],{"class":2227,"line":2304},[2225,194117,194118],{"class":2307},"  validator",[2225,194120,2311],{"class":2235},[2225,194122,124280],{"class":6342},[2225,194124,194125,194128,194130],{"class":2227,"line":2356},[2225,194126,194127],{"class":2307},"  dek",[2225,194129,2311],{"class":2235},[2225,194131,194132],{"class":6342}," EnclaveKey\n",[2225,194134,194135,194138,194140,194142,194145],{"class":2227,"line":2830},[2225,194136,194137],{"class":2307},"  strategies",[2225,194139,7011],{"class":2235},[2225,194141,112906],{"class":2774},[2225,194143,194144],{"class":6342}," SchemaUpdateStrategy",[2225,194146,78511],{"class":2239},[2225,194148,194149,194151,194153,194155,194157],{"class":2227,"line":2861},[2225,194150,186597],{"class":2235},[2225,194152,7079],{"class":6342},[2225,194154,6474],{"class":2235},[2225,194156,194067],{"class":6342},[2225,194158,7093],{"class":2235},[2225,194160,194161],{"class":2227,"line":4246},[2225,194162,2285],{"emptyLinePlaceholder":2284},[2225,194164,194165,194167,194170],{"class":2227,"line":4253},[2225,194166,6994],{"class":2774},[2225,194168,194169],{"class":6342}," PersistSchemaResult",[2225,194171,2301],{"class":2235},[2225,194173,194174,194176,194178,194180,194182],{"class":2227,"line":4259},[2225,194175,53580],{"class":2774},[2225,194177,179952],{"class":2307},[2225,194179,2311],{"class":2235},[2225,194181,7723],{"class":6342},[2225,194183,194184],{"class":2763},"   \u002F\u002F true when a fresh envelope was written\n",[2225,194186,194187,194189,194191,194193,194195],{"class":2227,"line":6548},[2225,194188,53580],{"class":2774},[2225,194190,146241],{"class":2307},[2225,194192,2311],{"class":2235},[2225,194194,7723],{"class":6342},[2225,194196,194197],{"class":2763},"   \u002F\u002F true when the stored envelope already matched\n",[2225,194199,194200,194202,194204,194206],{"class":2227,"line":6576},[2225,194201,53580],{"class":2774},[2225,194203,7585],{"class":2307},[2225,194205,2311],{"class":2235},[2225,194207,194208],{"class":6342}," PersistedSchemaEnvelope\n",[2225,194210,194211,194213,194216,194218,194221],{"class":2227,"line":6582},[2225,194212,53580],{"class":2774},[2225,194214,194215],{"class":2307}," decision",[2225,194217,7011],{"class":2235},[2225,194219,194220],{"class":6342}," UpdateDecision",[2225,194222,194223],{"class":2763},"   \u002F\u002F present when strategies ran, see schema-update.md\n",[2225,194225,194226],{"class":2227,"line":6587},[2225,194227,7351],{"class":2235},[937,194229,194230,194231,194234,194235,1952,194237,194239,194240,194243,194244,194247,194248,194250],{},"The derive → hash-compare → skip-or-write orchestration that ",[941,194232,194233],{},"vault.collection({ persistJsonSchema: true })"," calls internally. Exported for advanced callers that manage their own store\u002FDEK plumbing outside a ",[941,194236,94],{},[941,194238,20156],{}," is the opt-in ",[1007,194241,820],{"href":194242},".\u002Fschema-update"," plugs into: when the derived schema changed and a stored baseline exists, each strategy gets a chance to allow, reject, or run a coordinated cutover before the write happens — ",[941,194245,194246],{},"decision"," reports which. Omit ",[941,194249,20156],{}," (or pass none) and every change is allowed, matching the plain skip-or-write behavior described above.",[1022,194252,68210],{"id":68209},[1111,194254,194255,194290,194305,194311,194317,194328],{},[1114,194256,194257,194260,194261,194263,194264,194266,194267,194269,194270,194272,194273,1261,194276,1261,194279,194282,194283,194286,194287,194289],{},[950,194258,194259],{},"v0 detects Zod vs. everything else — not each validator family individually."," Zod 3 (via the optional peer-dep ",[941,194262,182436],{},") and Zod 4 (via Zod's own ",[941,194265,25486],{},") are both supported and auto-detected, and derive a real ",[941,194268,192451],{}," body. ",[941,194271,193622],{}," also has ",[941,194274,194275],{},"'Valibot'",[941,194277,194278],{},"'ArkType'",[941,194280,194281],{},"'Effect'"," members for future per-library detection, but today any non-Zod Standard Schema validator is reported as ",[941,194284,194285],{},"kind: 'Unknown'"," and always gets a stub envelope (",[941,194288,193829],{},"), never a derived JSON Schema body.",[1114,194291,194292,194297,194298,194301,194302,194304],{},[950,194293,194294,194295,1520],{},"The write is asynchronous and not awaited by ",[941,194296,2138],{}," It's queued against a background drain; call ",[941,194299,194300],{},"await vault._drainPendingSchemaWrites()"," when a test or script needs to observe the write before reading ",[941,194303,166841],{}," back.",[1114,194306,194307,194310],{},[950,194308,194309],{},"Hash-skip only inspects the derived JSON Schema, not your Zod source."," Renaming a field or changing a constraint changes the derived shape (and the hash) and triggers a write; comments or code reordering in your schema definition do not.",[1114,194312,194313,194316],{},[950,194314,194315],{},"Encrypted with the collection's own key."," There is no separate \"schema visibility\" permission — whoever can decrypt the collection's records can decrypt its schema, and no one else can.",[1114,194318,194319,66018,194322,194324,194325,194327],{},[950,194320,194321],{},"Requires Zod's optional peer-dep for Zod 3 schemas.",[941,194323,191912],{}," on a Zod 3 validator without ",[941,194326,182436],{}," installed throws with an install hint; Zod 4 schemas use Zod's built-in converter and need no extra package.",[1114,194329,194330,194339,194340,27251,194342,194345,194346,194349,194350,194352,194353,194355,194356],{},[950,194331,194332,194334,194335,17904,194337],{},[941,194333,28323],{}," itself is an optional peer of ",[941,194336,1086],{},[941,194338,149091],{}," (noy-db#1227). Hub\npreviously bundled ",[941,194341,149237],{},[941,194343,194344],{},"dist\u002F"," at a build-frozen version, declared in no manifest\nfield — so ",[941,194347,194348],{},"npm ls zod"," showed nothing while that copy was reachable, SBOM tooling missed it, and\na consumer could not remediate a zod advisory by bumping their own. Declaring it is also the\ncorrectness fix: ",[941,194351,25496],{}," reads a schema's internals, and the schema is built by ",[950,194354,98134],{},"\nzod, so there is now one zod rather than two with undetected skew.",[7646,194357,194358,194367,194382,194428],{"color":10789,"icon":39006},[937,194359,194360,194366],{},[950,194361,194362,194363],{},"The range is ",[941,194364,194365],{},"^3.0.0 || ^4.0.0"," — both majors, and the peer is optional, so a project with\nno zod at all installs fine and never reaches the converter.",[937,194368,46418,194369,194374,194375,194378,194379,194381],{},[950,194370,194371,194373],{},[941,194372,149091],{}," is the one release to skip if you are on Zod 3."," It declared the peer as\n",[941,194376,194377],{},"^4.0.0"," alone, and a published manifest cannot be amended, so that version refuses the pair\nunder npm permanently. ",[941,194380,149301],{}," corrects the declaration. Measured:",[1338,194383,194384,194392],{},[1341,194385,194386],{},[1344,194387,194388,194390],{},[1347,194389],{},[1347,194391,11195],{},[1357,194393,194394,194404,194413],{},[1344,194395,194396,194401],{},[1362,194397,194398],{},[941,194399,194400],{},"npm i @noy-db\u002Fhub@0.7.0-pre.15 zod@3",[1362,194402,194403],{},"fine",[1344,194405,194406,194411],{},[1362,194407,194408],{},[941,194409,194410],{},"npm i @noy-db\u002Fhub@0.7.0-pre.15 zod@4",[1362,194412,194403],{},[1344,194414,194415,194420],{},[1362,194416,194417],{},[941,194418,194419],{},"npm i @noy-db\u002Fhub@0.7.0-pre.14 zod@3",[1362,194421,194422,194427],{},[950,194423,194424],{},[941,194425,194426],{},"ERESOLVE"," — that release only",[937,194429,194430,173678,194433,194436,194437,194439,194440,194442],{},[950,194431,194432],{},"Zod 3 was never unsupported",[941,194434,194435],{},"pre.14","'s declaration said otherwise, and the runtime\nbehaviour was unaffected throughout. If you meet that error, the fix is to move off ",[941,194438,194435],{}," —\nnot to downgrade a plan onto zod 4, and not ",[941,194441,43475],{},", which would have suppressed a\ntrue statement about a real manifest rather than working around a false one.",[1022,194444,2621],{"id":2620},[1111,194446,194447,194452,194459,194468,194473,194480,194488],{},[1114,194448,194449,194451],{},[1007,194450,21],{"href":60250}," — the DEK and envelope format persisted-schema envelopes reuse",[1114,194453,194454,964,194456,194458],{},[1007,194455,711],{"href":124740},[941,194457,1380],{}," reads the persisted envelope as a fallback when no live validator is in memory",[1114,194460,194461,194463,194464,194467],{},[1007,194462,820],{"href":194242}," — strategies that react to a ",[1092,194465,194466],{},"changed"," persisted schema (additive-only, locked, coordinated cutover)",[1114,194469,194470,194472],{},[1007,194471,888],{"href":889}," — planned richer runtime validators; today the JSON Schema derived and persisted here is the closest thing to a schema-level validation surface",[1114,194474,194475,194477,194478,24268],{},[1007,194476,633],{"href":124757}," — a config-drift marker that shares the same ",[941,194479,166841],{},[1114,194481,194482,194485,194486,24268],{},[1007,194483,812],{"href":194484},".\u002Fsatellites"," — the satellite pairing marker that also shares the ",[941,194487,166841],{},[1114,194489,194490,1368,194492],{},[1007,194491,61743],{"href":598},[1007,194493,1019],{"href":1018},[2645,194495,194496],{},"html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .sfNiH, html code.shiki .sfNiH{--shiki-light:#FF5370;--shiki-default:#FF9CAC;--shiki-dark:#FF9CAC}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html pre.shiki code .sHdIc, html code.shiki .sHdIc{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#EEFFFF;--shiki-default-font-style:italic;--shiki-dark:#BABED8;--shiki-dark-font-style:italic}",{"title":1037,"searchDepth":1038,"depth":1038,"links":194498},[194499,194500,194501,194507,194519,194520],{"id":29856,"depth":1041,"text":29857},{"id":58393,"depth":1041,"text":58394},{"id":29892,"depth":1041,"text":29893,"children":194502},[194503,194504,194505,194506],{"id":192236,"depth":1038,"text":192237},{"id":192621,"depth":1038,"text":192622},{"id":192848,"depth":1038,"text":192849},{"id":193122,"depth":1038,"text":193123},{"id":102888,"depth":1041,"text":102889,"children":194508},[194509,194510,194511,194512,194514,194515,194517],{"id":193390,"depth":1038,"text":22790},{"id":193457,"depth":1038,"text":193460},{"id":193580,"depth":1038,"text":193583},{"id":193617,"depth":1038,"text":194513},"PersistedSchemaEnvelope \u002F PersistedSchemaKind (types)",{"id":193845,"depth":1038,"text":193848},{"id":193874,"depth":1038,"text":194516},"loadPersistedSchema(store, vault, collection, dek) \u002F savePersistedSchema(store, vault, collection, dek, payload)",{"id":194061,"depth":1038,"text":194518},"persistSchemaIfNeeded(opts) \u002F PersistSchemaResult",{"id":68209,"depth":1041,"text":68210},{"id":2620,"depth":1041,"text":2621},"Persisted JSON Schema: write an encrypted snapshot of your validator into the vault so tools without your live validator can still see the shape.",{},{"title":780,"description":194521},"alnnZQMfChR2vFDdlkFD0TWKhl0cOkVf_PS_36qoePM",{"id":194526,"title":784,"access":12,"apiToc":929,"body":194527,"category":1042,"description":195486,"extension":1044,"meta":195487,"navigation":1042,"nest":1047,"path":785,"seo":195488,"sinceVersion":1042,"status":1049,"stem":786,"subtitle":1042,"tier":1050,"tocExternal":1042,"__hash__":195489},"docs\u002Fservices\u002Fplaintext-bypass.md",{"type":931,"value":194528,"toc":195468},[194529,194531,194539,194542,194546,194568,194577,194579,194593,194595,194599,194608,194724,194730,194735,194797,194808,194810,194815,194818,194845,194854,194950,194954,194959,194980,194998,195002,195005,195221,195225,195312,195316,195319,195368,195375,195379,195382,195413,195415,195465],[934,194530,784],{"id":784},[1061,194532,194533],{},[937,194534,194535,194538],{},[950,194536,194537],{},"Reference:"," the fixed set of internal collections in a noy-db vault that store plain JSON, by design.",[46871,194540],{":rows":194541},"[{\"label\":\"Cluster\",\"value\":\"disk-format\"},{\"label\":\"Cross-cuts\",\"value\":\"`@noy-db\u002Fhub` core, every `@noy-db\u002Fto-*` storage backend, the `.noydb` bundle format, `@noy-db\u002Fon-magic-link`.\"}]",[1022,194543,194545],{"id":194544},"what-this-doc-is","What this doc is",[937,194547,194548,194549,968,194552,194555,194556,953,194558,47474,194561,194564,194565,194567],{},"noy-db's headline guarantee is that a storage backend — S3, IndexedDB, Postgres, whatever adapter you plug in — only ever sees ciphertext. Every record you write through a ",[1007,194550,194551],{"href":58262},"vault's collection",[941,194553,194554],{},"vault.collection\u003CT>(...).put(id, payload)",", is wrapped in an ",[950,194557,1659],{},[941,194559,194560],{},"{ _noydb, _v, _ts, _iv, _data }",[1007,194562,194563],{"href":60250},"encrypted"," with AES-256-GCM under a per-collection ",[950,194566,5627],{}," (data-encryption key) before it reaches the store.",[937,194569,194570,194571,194573,194574,194576],{},"That guarantee has one deliberate carve-out. A handful of collections the hub itself uses to operate — unlocking a ",[1007,194572,2705],{"href":86452}," (the record that carries a user's wrapped keys), reading vault policy, running the sync engine, redeeming an invite — need to be readable ",[1092,194575,11357],{}," any key material exists to decrypt them with. Encrypting those would create a chicken-and-egg problem: you'd need to unlock the vault to read the document that tells you how to unlock the vault. Those collections store plain JSON instead. This page is the complete list of which ones, why each is exempt, and what a person who compromises only the storage backend (never any key) can learn from them.",[1022,194578,99864],{"id":99863},[937,194580,194581,194582,194584,194585,194588,194589,194592],{},"There is nothing to opt into. Every vault has this fixed set of plaintext collections\nunconditionally — there is no ",[941,194583,943],{}," strategy, no factory, and no ",[941,194586,194587],{},"createVault"," option that\nturns it on or off. The only thing you can opt into is ",[1092,194590,194591],{},"reading"," one of these envelopes directly,\nwhich is what the next section shows.",[1022,194594,29893],{"id":29892},[1591,194596,194598],{"id":194597},"read-a-plaintext-envelopes-raw-fields","Read a plaintext envelope's raw fields",[937,194600,194601,194602,194604,194605,194607],{},"A store backend exposes envelopes however it persists them — a ",[941,194603,7461],{}," store, for example,\nwrites one JSON file per record. You can inspect the ",[941,194606,1143],{}," field yourself to confirm a given\nenvelope is a plaintext bypass rather than ciphertext:",[2217,194609,194611],{"className":2219,"code":194610,"language":2221,"meta":1037,"style":1037},"import { readFileSync } from 'node:fs'\nimport type { EncryptedEnvelope } from '@noy-db\u002Fhub'\n\nconst envelope = JSON.parse(\n  readFileSync('data\u002F\u003Cvault>\u002F_keyring\u002Falice.json', 'utf8'),\n) as EncryptedEnvelope\n\nenvelope._iv\n\u002F\u002F → ''  (empty — this is a plaintext bypass; a non-empty _iv would mean ciphertext)\n",[941,194612,194613,194631,194651,194655,194671,194697,194706,194710,194719],{"__ignoreMap":1037},[2225,194614,194615,194617,194619,194621,194623,194625,194627,194629],{"class":2227,"line":2228},[2225,194616,2232],{"class":2231},[2225,194618,2236],{"class":2235},[2225,194620,15746],{"class":2239},[2225,194622,2243],{"class":2235},[2225,194624,2246],{"class":2231},[2225,194626,2249],{"class":2235},[2225,194628,15755],{"class":2252},[2225,194630,2256],{"class":2235},[2225,194632,194633,194635,194637,194639,194641,194643,194645,194647,194649],{"class":2227,"line":1041},[2225,194634,2232],{"class":2231},[2225,194636,3399],{"class":2231},[2225,194638,2236],{"class":2235},[2225,194640,3409],{"class":2239},[2225,194642,2243],{"class":2235},[2225,194644,2246],{"class":2231},[2225,194646,2249],{"class":2235},[2225,194648,1086],{"class":2252},[2225,194650,2256],{"class":2235},[2225,194652,194653],{"class":2227,"line":1038},[2225,194654,2285],{"emptyLinePlaceholder":2284},[2225,194656,194657,194659,194661,194663,194665,194667,194669],{"class":2227,"line":2288},[2225,194658,2775],{"class":2774},[2225,194660,127011],{"class":2239},[2225,194662,2781],{"class":2235},[2225,194664,15793],{"class":2239},[2225,194666,1520],{"class":2235},[2225,194668,15798],{"class":2291},[2225,194670,11683],{"class":2239},[2225,194672,194673,194676,194678,194680,194683,194685,194687,194689,194691,194693,194695],{"class":2227,"line":2304},[2225,194674,194675],{"class":2291},"  readFileSync",[2225,194677,2789],{"class":2239},[2225,194679,2805],{"class":2235},[2225,194681,194682],{"class":2252},"data\u002F\u003Cvault>\u002F_keyring\u002Falice.json",[2225,194684,2805],{"class":2235},[2225,194686,2298],{"class":2235},[2225,194688,2249],{"class":2235},[2225,194690,15819],{"class":2252},[2225,194692,2805],{"class":2235},[2225,194694,1381],{"class":2239},[2225,194696,1707],{"class":2235},[2225,194698,194699,194701,194703],{"class":2227,"line":2356},[2225,194700,8495],{"class":2239},[2225,194702,4899],{"class":2231},[2225,194704,194705],{"class":6342}," EncryptedEnvelope\n",[2225,194707,194708],{"class":2227,"line":2830},[2225,194709,2285],{"emptyLinePlaceholder":2284},[2225,194711,194712,194714,194716],{"class":2227,"line":2861},[2225,194713,1659],{"class":2239},[2225,194715,1520],{"class":2235},[2225,194717,194718],{"class":2239},"_iv\n",[2225,194720,194721],{"class":2227,"line":4246},[2225,194722,194723],{"class":2763},"\u002F\u002F → ''  (empty — this is a plaintext bypass; a non-empty _iv would mean ciphertext)\n",[1591,194725,194727,194728],{"id":194726},"unwrap-the-record-with-readplaintextrecord","Unwrap the record with ",[941,194729,15828],{},[937,194731,194732,194734],{},[941,194733,1086],{}," exports a small helper that turns a plaintext envelope into the record it carries,\nwithout needing a vault or any key material:",[2217,194736,194738],{"className":2219,"code":194737,"language":2221,"meta":1037,"style":1037},"import { readPlaintextRecord } from '@noy-db\u002Fhub\u002Fdebug'\n\nconst keyringRecord = readPlaintextRecord(envelope)\nconsole.log(keyringRecord)\n\u002F\u002F → { role: 'owner', permissions: { '*': 'rw' }, granted_by: 'alice',\n\u002F\u002F     display_name: 'Alice', deks: { 'default': '…wrapped…' } }\n",[941,194739,194740,194758,194762,194776,194787,194792],{"__ignoreMap":1037},[2225,194741,194742,194744,194746,194748,194750,194752,194754,194756],{"class":2227,"line":2228},[2225,194743,2232],{"class":2231},[2225,194745,2236],{"class":2235},[2225,194747,15766],{"class":2239},[2225,194749,2243],{"class":2235},[2225,194751,2246],{"class":2231},[2225,194753,2249],{"class":2235},[2225,194755,15775],{"class":2252},[2225,194757,2256],{"class":2235},[2225,194759,194760],{"class":2227,"line":1041},[2225,194761,2285],{"emptyLinePlaceholder":2284},[2225,194763,194764,194766,194769,194771,194773],{"class":2227,"line":1038},[2225,194765,2775],{"class":2774},[2225,194767,194768],{"class":2239}," keyringRecord ",[2225,194770,2781],{"class":2235},[2225,194772,15766],{"class":2291},[2225,194774,194775],{"class":2239},"(envelope)\n",[2225,194777,194778,194780,194782,194784],{"class":2227,"line":2288},[2225,194779,28031],{"class":2239},[2225,194781,1520],{"class":2235},[2225,194783,27100],{"class":2291},[2225,194785,194786],{"class":2239},"(keyringRecord)\n",[2225,194788,194789],{"class":2227,"line":2304},[2225,194790,194791],{"class":2763},"\u002F\u002F → { role: 'owner', permissions: { '*': 'rw' }, granted_by: 'alice',\n",[2225,194793,194794],{"class":2227,"line":2356},[2225,194795,194796],{"class":2763},"\u002F\u002F     display_name: 'Alice', deks: { 'default': '…wrapped…' } }\n",[937,194798,194799,194801,194802,194804,194805,194807],{},[941,194800,15828],{}," throws if handed an ",[950,194803,194563],{}," envelope (non-empty ",[941,194806,1143],{},") — there is no\nkey here to decrypt with; go through the vault instead.",[1022,194809,102889],{"id":102888},[1591,194811,194813],{"id":194812},"readplaintextrecordenvelope",[941,194814,15731],{},[937,194816,194817],{},"Extracts the record from a plaintext-stored envelope. Handles both layouts a store can hold:",[1111,194819,194820,194831],{},[1114,194821,194822,953,194825,194827,194828,1520],{},[950,194823,194824],{},"Classic plaintext",[941,194826,11453],{},", or one of the unconditionally-plaintext collections on\nthis page) — the record is a JSON string in ",[941,194829,194830],{},"envelope._data",[1114,194832,194833,953,194836,6030,194838,53922,194840,194842,194843,1520],{},[950,194834,194835],{},"Debug-plaintext",[941,194837,11453],{},[941,194839,15541],{},[941,194841,12780],{},") — the record's\nfields are inlined beside the envelope metadata instead of being JSON-stringified into ",[941,194844,1146],{},[937,194846,111113,194847,194849,194850,194853],{},[941,194848,6651],{}," for an empty\u002Fabsent body. Throws if ",[941,194851,194852],{},"envelope._iv"," is non-empty (an encrypted\nenvelope) — decrypt through the vault instead.",[2217,194855,194857],{"className":2219,"code":194856,"language":2221,"meta":1037,"style":1037},"import { readPlaintextRecord } from '@noy-db\u002Fhub\u002Fdebug'\n\nreadPlaintextRecord({ _noydb: 1, _v: 1, _ts: 't', _iv: '', _data: '{\"id\":\"d1\",\"n\":2}' })\n\u002F\u002F → { id: 'd1', n: 2 }\n",[941,194858,194859,194877,194881,194945],{"__ignoreMap":1037},[2225,194860,194861,194863,194865,194867,194869,194871,194873,194875],{"class":2227,"line":2228},[2225,194862,2232],{"class":2231},[2225,194864,2236],{"class":2235},[2225,194866,15766],{"class":2239},[2225,194868,2243],{"class":2235},[2225,194870,2246],{"class":2231},[2225,194872,2249],{"class":2235},[2225,194874,15775],{"class":2252},[2225,194876,2256],{"class":2235},[2225,194878,194879],{"class":2227,"line":1041},[2225,194880,2285],{"emptyLinePlaceholder":2284},[2225,194882,194883,194885,194887,194889,194892,194894,194896,194898,194901,194903,194905,194907,194910,194912,194914,194916,194918,194920,194923,194925,194927,194929,194932,194934,194936,194939,194941,194943],{"class":2227,"line":1038},[2225,194884,15828],{"class":2291},[2225,194886,2789],{"class":2239},[2225,194888,2792],{"class":2235},[2225,194890,194891],{"class":2307}," _noydb",[2225,194893,2311],{"class":2235},[2225,194895,9377],{"class":6527},[2225,194897,2298],{"class":2235},[2225,194899,194900],{"class":2307}," _v",[2225,194902,2311],{"class":2235},[2225,194904,9377],{"class":6527},[2225,194906,2298],{"class":2235},[2225,194908,194909],{"class":2307}," _ts",[2225,194911,2311],{"class":2235},[2225,194913,2249],{"class":2235},[2225,194915,13267],{"class":2252},[2225,194917,2805],{"class":2235},[2225,194919,2298],{"class":2235},[2225,194921,194922],{"class":2307}," _iv",[2225,194924,2311],{"class":2235},[2225,194926,12500],{"class":2235},[2225,194928,2298],{"class":2235},[2225,194930,194931],{"class":2307}," _data",[2225,194933,2311],{"class":2235},[2225,194935,2249],{"class":2235},[2225,194937,194938],{"class":2252},"{\"id\":\"d1\",\"n\":2}",[2225,194940,2805],{"class":2235},[2225,194942,2243],{"class":2235},[2225,194944,2362],{"class":2239},[2225,194946,194947],{"class":2227,"line":2288},[2225,194948,194949],{"class":2763},"\u002F\u002F → { id: 'd1', n: 2 }\n",[1022,194951,194953],{"id":194952},"the-discriminator-on-the-wire","The discriminator on the wire",[937,194955,194956,194957,2311],{},"Every envelope read starts by checking one field, ",[941,194958,1143],{},[1111,194960,194961,194969],{},[1114,194962,194963,194965,194966,194968],{},[941,194964,1143],{}," non-empty → the envelope is encrypted with AES-256-GCM under a DEK; ",[941,194967,1146],{}," is ciphertext.",[1114,194970,194971,194973,194974,5502,194977,194979],{},[941,194972,128244],{}," (empty string) → the envelope is a ",[950,194975,194976],{},"plaintext bypass",[941,194978,1146],{}," is JSON, readable without any key.",[937,194981,194982,194983,194985,194986,194989,194990,194992,194993,194995,194996,9954],{},"Both paths share the same envelope shape, so the sync engine, conformance tests, and every ",[941,194984,1779],{}," backend read either kind the same way — a backend never has to know in advance which collections are which. The discriminator lives on each envelope, not declared once for a whole collection: ",[941,194987,194988],{},"vault.collection\u003CT>(...)"," data is always encrypted, and a few operational collections (the sync-credential store, the tamper-evident ledger, the consent-audit log, blob metadata) carry their ",[1092,194991,95679],{}," DEK and are encrypted the same way — the only time they turn up as plaintext is if the entire vault was opened with ",[941,194994,11453],{}," (a whole-vault, all-collections opt-out documented on the vault-creation options, not a per-collection bypass). The collections catalogued below are different: they are plaintext in every vault, unconditionally, regardless of the ",[941,194997,1624],{},[1022,194999,195001],{"id":195000},"the-unconditionally-plaintext-collections","The unconditionally-plaintext collections",[937,195003,195004],{},"Each row is a deliberate exemption: the \"Why\" column is the argument that a change breaking that assumption would need to overturn.",[1338,195006,195007,195018],{},[1341,195008,195009],{},[1344,195010,195011,195013,195015],{},[1347,195012,103],{},[1347,195014,29661],{},[1347,195016,195017],{},"Why it must be plaintext",[1357,195019,195020,195053,195067,195085,195101,195118,195143,195163,195180,195206],{},[1344,195021,195022,195026,195030],{},[1362,195023,195024],{},[941,195025,9871],{},[1362,195027,195028],{},[941,195029,29763],{},[1362,195031,960,195032,1324,195034,195036,195037,195039,195040,968,195042,968,195044,968,195046,968,195049,195052],{},[1007,195033,2705],{"href":86452},[950,195035,3082],{}," the file that carries a user's wrapped DEKs and their ",[950,195038,9879],{}," (key-encryption key) salt. Reading it without keys is the entry point for unlock — encrypting it would mean you'd need a key to find out how to get a key. The fields it exposes (",[941,195041,16785],{},[941,195043,16800],{},[941,195045,16744],{},[941,195047,195048],{},"expires_at",[941,195050,195051],{},"display_name",", and the count of wrapped DEKs) are a documented metadata leak: they let a caller check role and permissions without unwrapping anything.",[1344,195054,195055,195059,195064],{},[1362,195056,195057],{},[941,195058,12784],{},[1362,195060,195061],{},[941,195062,195063],{},"with-party\u002Fpolicy\u002Fstorage.ts",[1362,195065,195066],{},"The vault's factor\u002Fgate policy has to be readable before any unlock decision is made — a gate can require \"tier-1 unlock\" to even be attempted, and the engine can't know that without reading the policy first.",[1344,195068,195069,195073,195078],{},[1362,195070,195071],{},[941,195072,106993],{},[1362,195074,195075],{},[941,195076,195077],{},"with-party\u002Fteam\u002Frecovery.ts",[1362,195079,195080,195081,195084],{},"Each entry holds a set of DEKs wrapped under a key derived from the user's paper recovery code (PBKDF2-SHA256, 600,000 iterations) plus its own salt and IV. The ",[1092,195082,195083],{},"wrapping"," is the security boundary — the document itself is inert without the recovery code, so there's nothing to protect by also encrypting the envelope.",[1344,195086,195087,195091,195095],{},[1362,195088,195089],{},[941,195090,116026],{},[1362,195092,195093],{},[941,195094,29678],{},[1362,195096,195097,195098,195100],{},"An opaque ULID identifying a ",[941,195099,1402],{}," bundle, generated at first export and reused on every later export of that vault. Read by a multi-vault picker to label bundles before any user is authenticated — and the same ULID already sits unencrypted in the bundle header, so encrypting the vault-side copy would protect nothing.",[1344,195102,195103,195107,195112],{},[1362,195104,195105],{},[941,195106,127468],{},[1362,195108,195109],{},[941,195110,195111],{},"with-party\u002Fdirectory\u002Fcover\u002Fstorage.ts",[1362,195113,195114,195115,195117],{},"Owner-curated label (name, description, icon, locale) that the owner explicitly opts every field into — see ",[1007,195116,645],{"href":128438},". Meant to be readable without unlocking the vault, so it can't be encrypted.",[1344,195119,195120,195125,195132],{},[1362,195121,195122],{},[941,195123,195124],{},"_meta\u002Finvite-audit-\u003CtokenId>",[1362,195126,195127,968,195129],{},[941,195128,44320],{},[941,195130,195131],{},"src\u002Finvite.ts",[1362,195133,195134,195135,195138,195139,195142],{},"Audit record for a magic-link invite, read by the recipient's session before they hold any keyring, and mutated when the issuer revokes the invite. It's also the \"revoked-link-shadow-keyring\" defense: if ",[941,195136,195137],{},"acceptInvite"," finds no audit doc for a token, it throws ",[941,195140,195141],{},"InviteAuditMissingError"," and refuses to proceed even if the rest of the link is well-formed — so the check has to run before any key exists to gate it.",[1344,195144,195145,195149,195154],{},[1362,195146,195147],{},[941,195148,16900],{},[1362,195150,195151],{},[941,195152,195153],{},"with-party\u002Fdirectory\u002Fstorage.ts",[1362,195155,195156,195157,195159,195160,195162],{},"A one-field vault-level toggle (",[941,195158,142123],{},") read by the user-enumeration helper to decide whether non-admin callers may list vault members — the read has to happen on the same before-any-unlock path as ",[941,195161,12784],{},". A UX flag, not a privacy boundary.",[1344,195164,195165,195169,195174],{},[1362,195166,195167],{},[941,195168,142146],{},[1362,195170,195171],{},[941,195172,195173],{},"with-party\u002Fdirectory\u002Fvisibility.ts",[1362,195175,195176,195177,195179],{},"A per-user \"hide me from the member list\" opt-out (",[941,195178,142140],{},"). Has to be readable even when that user's own envelope can't be decrypted (legacy keyring, missing DEK propagation, corrupted ciphertext), so the enumeration helper can still filter it in.",[1344,195181,195182,195187,195192],{},[1362,195183,195184],{},[941,195185,195186],{},"_meta\u002Fadoption",[1362,195188,195189],{},[941,195190,195191],{},"with-cargo\u002Fadopt-partition.ts",[1362,195193,195194,195195,195198,195199,195201,195202,195205],{},"Marker written when a transferable partition is adopted into a vault that has no keyring yet — there is no DEK to encrypt under. While the partition is adopted-but-unowned it carries a ",[941,195196,195197],{},"transferSeal"," (the partition's DEK set, sealed with AES-GCM under an out-of-band, never-stored 32-byte transfer key) — the ",[1092,195200,1135],{},", not envelope encryption, is the security boundary. Once an owner is minted, the seal is destroyed and the row is rewritten to ",[941,195203,195204],{},"{ sealId, adoptedAt, consumedAt }"," with no key material left in it.",[1344,195207,195208,195213,195218],{},[1362,195209,195210],{},[941,195211,195212],{},"_sync\u002Fmeta",[1362,195214,195215],{},[941,195216,195217],{},"with-party\u002Fteam\u002Fsync.ts",[1362,195219,195220],{},"The sync engine's own bookkeeping — last-pushed timestamp, last-pulled cursor, the dirty-record set. Has to be readable before the next pull can run, the same chicken-and-egg constraint as the other pre-unlock reads above.",[1022,195222,195224],{"id":195223},"what-is-not-in-this-set-verified-encrypted","What is NOT in this set (verified encrypted)",[1111,195226,195227,195241,195258,195268,195282,195298,195305],{},[1114,195228,195229,195232,195233,195235,195236,1261,195238,195240],{},[950,195230,195231],{},"Every user-data collection."," Anything written through ",[941,195234,194554],{}," is AES-256-GCM-encrypted under a per-collection DEK. A store sees only ciphertext plus the ",[941,195237,1700],{},[941,195239,1703],{}," metadata fields.",[1114,195242,195243,195247,195248,195251,195252,195254,195255,195257],{},[950,195244,195245],{},[941,195246,97735],{}," — the per-user envelope (contact info, presence, ",[1007,195249,884],{"href":195250},".\u002Fuser-envelope","). Encrypted under its own DEK, propagated to every keyring the same way collection DEKs are. The owner-curated ",[1092,195253,2694],{}," label above (",[941,195256,127468],{},") is the plaintext one; the per-user envelope is not.",[1114,195259,195260,195264,195265,195267],{},[950,195261,195262],{},[941,195263,6806],{}," — per-adapter sync transport secrets (tokens, connection strings). Encrypted under a dedicated DEK and served only through its own owner\u002Fadmin-gated API — never reachable through the generic ",[941,195266,2138],{}," handle, and never propagated to a sub-admin grantee, because leaking it would leak a live transport secret rather than metadata.",[1114,195269,195270,195274,195275,195279,195280,1520],{},[950,195271,195272],{},[941,195273,6724],{}," (the hash-chained tamper-evidence log) and ",[950,195276,195277],{},[941,195278,6744],{}," (the per-access consent log) — each is encrypted under its own DEK by default. Both can only be read in plaintext if the whole vault was created with ",[941,195281,11453],{},[1114,195283,195284,997,195289,195294,195295,195297],{},[950,195285,195286],{},[941,195287,195288],{},"_blob_index",[950,195290,195291],{},[941,195292,195293],{},"_blob_chunks"," — blob metadata and encrypted chunk bytes, both under a vault-shared blob DEK by default; same ",[941,195296,11453],{}," caveat as above.",[1114,195299,195300,195304],{},[950,195301,195302],{},[941,195303,6715],{}," — full-record version snapshots, encrypted under the host collection's own DEK.",[1114,195306,195307,195311],{},[950,195308,195309],{},[941,195310,61864],{}," — query-index side-car entries, encrypted under the host collection's DEK so a leaked index can't be reverse-engineered into record contents.",[1022,195313,195315],{"id":195314},"what-an-attacker-with-store-only-access-can-learn","What an attacker with store-only access can learn",[937,195317,195318],{},"A compromise of a store backend (a leaked S3 bucket, an IndexedDB dump, a captured WebDAV session) — with no key material at all — yields these plaintext fields:",[1111,195320,195321,195331,195338,195345,195352],{},[1114,195322,195323,195327,195328],{},[950,195324,30857,195325,2311],{},[941,195326,11794],{}," every user id with access to the vault, their role, their per-collection permissions, how many DEKs they hold, who granted their access, expiry timestamps, and KEK salts. ",[950,195329,195330],{},"No raw keys.",[1114,195332,195333,195337],{},[950,195334,30857,195335,2311],{},[941,195336,12784],{}," exactly which factors each gate requires. Useful intelligence before attempting a policy bypass; does not help the bypass itself.",[1114,195339,195340,195344],{},[950,195341,30857,195342,2311],{},[941,195343,106993],{}," the wrapped-DEK blob, offline-attackable against the recovery code. The 600,000-iteration PBKDF2 wrap is the entire defense — a short or reused recovery code is the failure mode.",[1114,195346,195347,195351],{},[950,195348,30857,195349,2311],{},[941,195350,127468],{}," whatever the owner explicitly chose to publish.",[1114,195353,195354,195359,195360,195363,195364,195367],{},[950,195355,30857,195356,195358],{},[941,195357,195186],{}," (only while a partition is adopted-but-unowned):"," the sealed partition-DEK blob, offline-attackable against the 32-byte transfer key. That key is 256 bits of randomness delivered out-of-band and never itself stored, so brute-forcing it is infeasible — the exposure is the sealed blob plus the ",[941,195361,195362],{},"sealId"," and adoption timestamp. Once ",[941,195365,195366],{},"createOwnerOnAdoptedPartition"," runs, the blob is gone.",[937,195369,195370,195371,195374],{},"The \"stores see only ciphertext\" guarantee applies to data that flows through ",[941,195372,195373],{},"Collection\u003CT>.put",". The collections above are a documented, bounded exception to it — infrastructure metadata the hub needs before it has any key, not application data.",[1022,195376,195378],{"id":195377},"consequences-of-changing-this-set","Consequences of changing this set",[937,195380,195381],{},"Every row above is load-bearing for something the hub does before a key exists — that's what keeps the set small. Two things follow from that:",[1111,195383,195384,195396],{},[1114,195385,195386,195389,195390,195392,195393,195395],{},[950,195387,195388],{},"Adding"," a collection to this list changes the on-disk format contract: every ",[941,195391,1779],{}," backend and every conformance test that reads ",[941,195394,1143],{}," to decide whether to decrypt now has one more collection to special-case, and the new plaintext fields become part of the store-only attacker surface described above.",[1114,195397,195398,195401,195402,1261,195405,195408,195409,195412],{},[950,195399,195400],{},"Removing"," a collection from this list — encrypting something that used to be plaintext — is a breaking on-disk format change for existing vaults: it requires a new envelope-format version (the hub tracks this via ",[941,195403,195404],{},"NOYDB_FORMAT_VERSION",[941,195406,195407],{},"NOYDB_KEYRING_VERSION",") and a migration that rewrites every existing envelope of that shape, since old readers would otherwise try to ",[941,195410,195411],{},"JSON.parse"," ciphertext.",[1022,195414,2621],{"id":2620},[1111,195416,195417,195426,195431,195442,195449,195457],{},[1114,195418,195419,195421,195422,195425],{},[1007,195420,645],{"href":128438}," — the one bypass that is ",[1092,195423,195424],{},"consumer-controlled",": the owner explicitly opts every field in.",[1114,195427,195428,195430],{},[1007,195429,884],{"href":195250}," — the encrypted counterpart: per-user, not per-vault, and not on this list.",[1114,195432,195433,1169,195436,195438,195439,195441],{},[1007,195434,629],{"href":195435},".\u002Fbundle",[941,195437,1402],{}," container format; ",[941,195440,116026],{}," is shared between the vault and the bundle header.",[1114,195443,195444,964,195446,195448],{},[1007,195445,844],{"href":129031},[941,195447,12784],{}," is the input to the tier\u002Fgate policy engine.",[1114,195450,195451,195454,195455,1520],{},[1007,195452,116737],{"href":195453},".\u002Ftransferable-partitions"," — the adoption flow that writes ",[941,195456,195186],{},[1114,195458,101581,195459,1520],{},[1007,195460,195462],{"href":94044,"rel":195461},[3371],[941,195463,195464],{},"112-debug-plaintext-mode",[2645,195466,195467],{},"html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}",{"title":1037,"searchDepth":1038,"depth":1038,"links":195469},[195470,195471,195472,195477,195480,195481,195482,195483,195484,195485],{"id":194544,"depth":1041,"text":194545},{"id":99863,"depth":1041,"text":99864},{"id":29892,"depth":1041,"text":29893,"children":195473},[195474,195475],{"id":194597,"depth":1038,"text":194598},{"id":194726,"depth":1038,"text":195476},"Unwrap the record with readPlaintextRecord",{"id":102888,"depth":1041,"text":102889,"children":195478},[195479],{"id":194812,"depth":1038,"text":15731},{"id":194952,"depth":1041,"text":194953},{"id":195000,"depth":1041,"text":195001},{"id":195223,"depth":1041,"text":195224},{"id":195314,"depth":1041,"text":195315},{"id":195377,"depth":1041,"text":195378},{"id":2620,"depth":1041,"text":2621},"The fixed set of internal collections that store plain JSON instead of ciphertext, why each needs to, and what a store-only attacker learns from them.",{},{"title":784,"description":195486},"1oOhlS6OPzSFaTlePqX3K-vG5AcmA7TqMkzPh317_uM",{"id":195491,"title":788,"access":12,"apiToc":929,"body":195492,"category":1042,"description":196129,"extension":1044,"meta":196130,"navigation":1042,"nest":6051,"path":789,"seo":196131,"sinceVersion":1042,"status":38550,"stem":790,"subtitle":1042,"tier":1473,"tocExternal":1042,"__hash__":196132},"docs\u002Fservices\u002Fpod-redirect.md",{"type":931,"value":195493,"toc":196120},[195494,195496,195499,195524,195528,195635,195638,195642,195658,195673,195700,195704,195717,195723,195743,195746,195769,195774,195879,195882,195923,195926,195988,196001,196005,196019,196056,196060,196065,196074,196086,196089,196102,196104,196117],[934,195495,788],{"id":788},[46871,195497],{":rows":195498},"[{\"label\":\"Subpath\",\"value\":\"`@noy-db\u002Fhub\u002Fpod`\"},{\"label\":\"Factory\",\"value\":\"none — direct named imports\"},{\"label\":\"Cluster\",\"value\":\"F — Snapshot & Portability\"},{\"label\":\"Since\",\"value\":\"0.6.0\"}]",[937,195500,195501,195502,195504,195505,195508,195509,1261,195512,195515,195516,195519,195520,195523],{},"Four situations need the same shape: a redirect pod that points at another pod, a decommissioned store's \"moved to…\" tombstone, a manifest re-point, and a fleet-member reference. Rather than four ad-hoc pointer formats, ",[941,195503,70036],{}," ships one ",[950,195506,195507],{},"Redirect"," record, plus ",[941,195510,195511],{},"signRedirect",[941,195513,195514],{},"verifyRedirect"," to mint and check it, ",[941,195517,195518],{},"readPodRedirect"," to pull it off a pod header, and ",[941,195521,195522],{},"followRedirects"," to walk a chain.",[1022,195525,195527],{"id":195526},"the-record","The record",[2217,195529,195531],{"className":2219,"code":195530,"language":2221,"meta":1037,"style":1037},"interface Redirect {\n  readonly v: 1\n  readonly target: string\n  readonly reason: 'moved' | 'release' | 'tombstone' | 'repoint'\n  readonly issuedBy: string  \u002F\u002F keyId (16-hex) of the signer\n  readonly sig: string       \u002F\u002F base64url Ed25519 over the record minus `sig`\n}\n",[941,195532,195533,195542,195552,195563,195603,195617,195631],{"__ignoreMap":1037},[2225,195534,195535,195537,195540],{"class":2227,"line":2228},[2225,195536,6994],{"class":2774},[2225,195538,195539],{"class":6342}," Redirect",[2225,195541,2301],{"class":2235},[2225,195543,195544,195546,195548,195550],{"class":2227,"line":1041},[2225,195545,53580],{"class":2774},[2225,195547,8866],{"class":2307},[2225,195549,2311],{"class":2235},[2225,195551,74228],{"class":6527},[2225,195553,195554,195556,195559,195561],{"class":2227,"line":1038},[2225,195555,53580],{"class":2774},[2225,195557,195558],{"class":2307}," target",[2225,195560,2311],{"class":2235},[2225,195562,7014],{"class":6342},[2225,195564,195565,195567,195569,195571,195573,195576,195578,195580,195582,195584,195586,195588,195590,195592,195594,195596,195598,195601],{"class":2227,"line":2288},[2225,195566,53580],{"class":2774},[2225,195568,155427],{"class":2307},[2225,195570,2311],{"class":2235},[2225,195572,2249],{"class":2235},[2225,195574,195575],{"class":2252},"moved",[2225,195577,2805],{"class":2235},[2225,195579,7087],{"class":2235},[2225,195581,2249],{"class":2235},[2225,195583,75043],{"class":2252},[2225,195585,2805],{"class":2235},[2225,195587,7087],{"class":2235},[2225,195589,2249],{"class":2235},[2225,195591,154045],{"class":2252},[2225,195593,2805],{"class":2235},[2225,195595,7087],{"class":2235},[2225,195597,2249],{"class":2235},[2225,195599,195600],{"class":2252},"repoint",[2225,195602,2256],{"class":2235},[2225,195604,195605,195607,195610,195612,195614],{"class":2227,"line":2304},[2225,195606,53580],{"class":2774},[2225,195608,195609],{"class":2307}," issuedBy",[2225,195611,2311],{"class":2235},[2225,195613,6366],{"class":6342},[2225,195615,195616],{"class":2763},"  \u002F\u002F keyId (16-hex) of the signer\n",[2225,195618,195619,195621,195624,195626,195628],{"class":2227,"line":2356},[2225,195620,53580],{"class":2774},[2225,195622,195623],{"class":2307}," sig",[2225,195625,2311],{"class":2235},[2225,195627,6366],{"class":6342},[2225,195629,195630],{"class":2763},"       \u002F\u002F base64url Ed25519 over the record minus `sig`\n",[2225,195632,195633],{"class":2227,"line":2830},[2225,195634,7351],{"class":2235},[937,195636,195637],{},"Five fields, deliberately. No timestamps, no identities beyond the signer's fingerprint. A redirect target is disclosure-appropriate by definition — pointing somewhere is the whole job — so there is nothing else worth withholding.",[1022,195639,195641],{"id":195640},"fail-closed-not-unverified","Fail-closed, not \"unverified\"",[937,195643,195644,195646,195647,1952,195651,195654,195655,126776],{},[941,195645,195511],{}," signs through the same canonical-JSON Ed25519 convention as the ",[1007,195648,195650],{"href":195649},".\u002Fpod-signature","pod header",[941,195652,195653],{},"verifyRedirect(record, trustedKeys)"," checks it against a caller-supplied ",[941,195656,195657],{},"keyId → publicKeyB64",[937,195659,195660,195661,195664,195665,195668,195669,195672],{},"The trust semantics differ from the pod header on purpose. A pod may legitimately be ",[941,195662,195663],{},"unsigned"," — legacy v1 pods exist and ",[941,195666,195667],{},"{ sign: false }"," is supported. ",[950,195670,195671],{},"A Redirect has no legacy install base",", so:",[1111,195674,195675,195684],{},[1114,195676,195677,195678,7356,195680,195683],{},"An absent or malformed ",[941,195679,66206],{},[950,195681,195682],{},"invalid",". Not \"unverified\", not a degraded state to pass through.",[1114,195685,5093,195686,195689,195690,195693,195694,34689,195697,1520],{},[941,195687,195688],{},"issuedBy"," outside ",[941,195691,195692],{},"trustedKeys"," is treated exactly like a forged signature. There is no \"untrusted but structurally fine\" outcome, unlike ",[941,195695,195696],{},"verifyPodHeader",[941,195698,195699],{},"untrusted",[1022,195701,195703],{"id":195702},"why-it-lives-in-the-plaintext-header","Why it lives in the plaintext header",[937,195705,195706,195707,195710,195711,997,195714,1520],{},"The Redirect rides in the pod's ",[950,195708,195709],{},"plaintext header",", not the encrypted body — the same allowlisted-header-key mechanism as ",[941,195712,195713],{},"species",[941,195715,195716],{},"engineRange",[937,195718,195719,195720,195722],{},"The reason is structural. Every consumer this record exists for must follow it ",[1092,195721,11357],{}," authentication:",[1111,195724,195725,195731,195737],{},[1114,195726,3289,195727,195730],{},[950,195728,195729],{},"dispatcher"," or landing page routes a request to the right pod before any unlock happens.",[1114,195732,3289,195733,195736],{},[950,195734,195735],{},"connection-pod open flow"," must know it has been redirected before it has a key to decrypt with.",[1114,195738,3289,195739,195742],{},[950,195740,195741],{},"static page"," — no store, no enclave, no vault — resolves a chain using only the bytes it was handed and WebCrypto.",[937,195744,195745],{},"None of those can decrypt a body. A Redirect in the encrypted body would require exactly the credential the redirect exists to route around.",[937,195747,195748,195751,195752,195755,195756,195758,195759,195762,195763,195765,195766,195768],{},[941,195749,195750],{},"readPodRedirect(bytes)"," is a pure, synchronous, secret-free read. ",[950,195753,195754],{},"It does not verify"," — it extracts and structurally validates the shape, because a parser has no ",[941,195757,195692],{}," to check against. Any caller that intends to ",[1092,195760,195761],{},"act"," on a Redirect must call ",[941,195764,195514],{}," first, or use ",[941,195767,195522],{},", which does it for you.",[1022,195770,195772],{"id":195771},"followredirects",[941,195773,195522],{},[2217,195775,195777],{"className":2219,"code":195776,"language":2221,"meta":1037,"style":1037},"async function followRedirects(\n  start: Uint8Array,\n  fetcher: (target: string) => Promise\u003CUint8Array | null>,\n  opts: { trustedKeys: Record\u003Cstring, string>; maxDepth?: number },\n): Promise\u003CFollowRedirectsResult>\n",[941,195778,195779,195790,195801,195832,195866],{"__ignoreMap":1037},[2225,195780,195781,195783,195785,195788],{"class":2227,"line":2228},[2225,195782,70491],{"class":2774},[2225,195784,40856],{"class":2774},[2225,195786,195787],{"class":2291}," followRedirects",[2225,195789,11683],{"class":2235},[2225,195791,195792,195795,195797,195799],{"class":2227,"line":1041},[2225,195793,195794],{"class":7052},"  start",[2225,195796,2311],{"class":2235},[2225,195798,60935],{"class":6342},[2225,195800,1707],{"class":2235},[2225,195802,195803,195806,195808,195810,195812,195814,195816,195818,195820,195822,195824,195826,195828,195830],{"class":2227,"line":1038},[2225,195804,195805],{"class":2291},"  fetcher",[2225,195807,2311],{"class":2235},[2225,195809,953],{"class":2235},[2225,195811,134984],{"class":7052},[2225,195813,2311],{"class":2235},[2225,195815,6366],{"class":6342},[2225,195817,1381],{"class":2235},[2225,195819,8116],{"class":2774},[2225,195821,7079],{"class":6342},[2225,195823,6474],{"class":2235},[2225,195825,45820],{"class":6342},[2225,195827,7087],{"class":2235},[2225,195829,7090],{"class":6342},[2225,195831,12006],{"class":2235},[2225,195833,195834,195836,195838,195840,195843,195845,195847,195849,195851,195853,195855,195857,195860,195862,195864],{"class":2227,"line":2288},[2225,195835,11695],{"class":7052},[2225,195837,2311],{"class":2235},[2225,195839,2236],{"class":2235},[2225,195841,195842],{"class":2307}," trustedKeys",[2225,195844,2311],{"class":2235},[2225,195846,22527],{"class":6342},[2225,195848,6474],{"class":2235},[2225,195850,7259],{"class":6342},[2225,195852,2298],{"class":2235},[2225,195854,6366],{"class":6342},[2225,195856,19762],{"class":2235},[2225,195858,195859],{"class":2307}," maxDepth",[2225,195861,7011],{"class":2235},[2225,195863,6356],{"class":6342},[2225,195865,2353],{"class":2235},[2225,195867,195868,195870,195872,195874,195877],{"class":2227,"line":2304},[2225,195869,6255],{"class":2235},[2225,195871,7079],{"class":6342},[2225,195873,6474],{"class":2235},[2225,195875,195876],{"class":6342},"FollowRedirectsResult",[2225,195878,7093],{"class":2235},[937,195880,195881],{},"Walks the chain HTTP-redirect style, with four protections:",[1234,195883,195884,195895,195903,195909],{},[1114,195885,195886,195889,195890,1324,195892,195894],{},[950,195887,195888],{},"Verify before follow."," Each hop is verified against ",[941,195891,195692],{},[1092,195893,11357],{}," it advances the walk. An untrusted or forged hop never moves you.",[1114,195896,195897,1324,195900,195902],{},[950,195898,195899],{},"Capped depth.",[941,195901,140905],{}," defaults to 8.",[1114,195904,195905,195908],{},[950,195906,195907],{},"Loop detection."," A target seen twice is a loop, not a longer chain.",[1114,195910,195911,195914,195915,195918,195919,195922],{},[950,195912,195913],{},"Hop provenance."," Each hop's ",[941,195916,195917],{},"{ target, reason, issuedBy }"," accumulates into an ordered ",[941,195920,195921],{},"hops"," list, so a UI can render \"moved from X via Y\".",[937,195924,195925],{},"Four typed failures:",[1338,195927,195928,195938],{},[1341,195929,195930],{},[1344,195931,195932,195934],{},[1347,195933,12126],{},[1347,195935,195936],{},[941,195937,941],{},[1357,195939,195940,195952,195964,195976],{},[1344,195941,195942,195947],{},[1362,195943,195944],{},[941,195945,195946],{},"RedirectBadSignatureError",[1362,195948,195949],{},[941,195950,195951],{},"REDIRECT_BAD_SIGNATURE",[1344,195953,195954,195959],{},[1362,195955,195956],{},[941,195957,195958],{},"RedirectLoopError",[1362,195960,195961],{},[941,195962,195963],{},"REDIRECT_LOOP",[1344,195965,195966,195971],{},[1362,195967,195968],{},[941,195969,195970],{},"RedirectDepthExceededError",[1362,195972,195973],{},[941,195974,195975],{},"REDIRECT_DEPTH_EXCEEDED",[1344,195977,195978,195983],{},[1362,195979,195980],{},[941,195981,195982],{},"RedirectUnreachableError",[1362,195984,195985],{},[941,195986,195987],{},"REDIRECT_UNREACHABLE",[937,195989,195990,195991,150626,195994,195996,195997,196000],{},"Success returns ",[941,195992,195993],{},"{ terminal, hops }",[941,195995,906],{}," with no redirect header returns ",[941,195998,195999],{},"{ terminal: start, hops: [] }"," immediately.",[1022,196002,196004],{"id":196003},"tombstones-failing-forward","Tombstones — failing forward",[937,196006,3289,196007,196010,196011,196014,196015,196018],{},[950,196008,196009],{},"route tombstone"," is a Redirect with ",[941,196012,196013],{},"reason: 'tombstone'",": what a decommissioned store leaves behind so stale connection pods fail ",[1092,196016,196017],{},"forward"," instead of with a bare connection error.",[1111,196020,196021,196035,196041],{},[1114,196022,196023,196026,196027,196030,196031,196034],{},[950,196024,196025],{},"Writing one is store wiring, not hub behaviour."," The hub defines the record, the resolver, and these semantics. A store being decommissioned is expected to leave a ",[941,196028,196029],{},"species: 'redirect'"," pod — or a ",[941,196032,196033],{},"redirect"," header field on its existing connection pod — at the location clients already fetch.",[1114,196036,196037,196040],{},[950,196038,196039],{},"No new discovery path."," It occupies the same location the connection pod already did, so a client that knows how to fetch that connection pod will now fetch the tombstone instead.",[1114,196042,196043,196046,196047,196049,196050,196052,196053,196055],{},[950,196044,196045],{},"Offline clients show the last-known re-point."," A client offline when the tombstone is written cannot learn of it in real time; it shows whatever hop list it cached from its last successful reach. This is precisely why ",[941,196048,195522],{}," surfaces the whole ",[941,196051,195921],{}," list rather than only the terminal — the cached list ",[1092,196054,3082],{}," the \"last known re-point\", and re-resolving on reconnect confirms or updates it.",[1022,196057,196059],{"id":196058},"redirect-is-not-a-locator","Redirect is not a Locator",[937,196061,196062],{},[950,196063,196064],{},"A Redirect says \"go elsewhere once.\" A Locator says \"where cargo lives.\"",[937,196066,196067,196068,196070,196071,196073],{},"A Redirect is a signed, single-hop pointer documenting an ",[1092,196069,12307],{}," — hence the ",[941,196072,43668],{},". Following one is meant to end the redirect relationship, not establish an ongoing one; chains compose transiently.",[937,196075,3289,196076,196079,196080,196082,196083,196085],{},[950,196077,196078],{},"Locator"," is the steady-state answer to \"where does this cargo actually live\" — an address, not an event. ",[941,196081,134984],{}," is a plain ",[941,196084,7259],{}," today specifically because the Locator type does not exist yet; that is a forward seam, noted rather than blurred.",[937,196087,196088],{},"Keep them apart:",[1111,196090,196091,196094,196099],{},[1114,196092,196093],{},"Don't use a Redirect as a long-lived address.",[1114,196095,196096,196097,1520],{},"Don't expect a Locator to carry a signature or a ",[941,196098,43668],{},[1114,196100,196101],{},"A snapshot pod's \"newer version at…\" hint is a Redirect — an event — never a live route. That line is what keeps static artifacts honest about what they claim.",[1022,196103,2621],{"id":2620},[1111,196105,196106,196111],{},[1114,196107,196108,196110],{},[1007,196109,792],{"href":195649}," — the signing convention this shares",[1114,196112,196113,196116],{},[1007,196114,735],{"href":196115},".\u002Fmanifest"," — where a manifest re-point uses this record",[2645,196118,196119],{},"html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .sHdIc, html code.shiki .sHdIc{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#EEFFFF;--shiki-default-font-style:italic;--shiki-dark:#BABED8;--shiki-dark-font-style:italic}",{"title":1037,"searchDepth":1038,"depth":1038,"links":196121},[196122,196123,196124,196125,196126,196127,196128],{"id":195526,"depth":1041,"text":195527},{"id":195640,"depth":1041,"text":195641},{"id":195702,"depth":1041,"text":195703},{"id":195771,"depth":1041,"text":195522},{"id":196003,"depth":1041,"text":196004},{"id":196058,"depth":1041,"text":196059},{"id":2620,"depth":1041,"text":2621},"One signed 'this moved, go there' record for pods, store tombstones and manifest re-points — followable pre-auth, verified before every hop.",{},{"title":788,"description":196129},"CYYsqaGZydDlCjxOOKTwLo04RlBbPvoX65-92FqSozA",{"id":196134,"title":792,"access":12,"apiToc":929,"body":196135,"category":1042,"description":196598,"extension":1044,"meta":196599,"navigation":1042,"nest":6051,"path":793,"seo":196600,"sinceVersion":1042,"status":38550,"stem":794,"subtitle":1042,"tier":1473,"tocExternal":1042,"__hash__":196601},"docs\u002Fservices\u002Fpod-signature.md",{"type":931,"value":196136,"toc":196590},[196137,196139,196141,196156,196160,196196,196203,196253,196261,196263,196281,196287,196316,196321,196381,196384,196458,196462,196465,196484,196490,196514,196523,196527,196539,196542,196567,196569,196588],[934,196138,792],{"id":792},[46871,196140],{":rows":195498},[937,196142,960,196143,196145,196146,196148,196149,196151,196152,196155],{},[941,196144,1402],{}," pod format wraps ",[941,196147,6820],{}," for safe cloud-storage drops: a 10-byte magic prefix, a JSON header, and a compressed body. ",[941,196150,35759],{}," signs that header by default whenever the source vault has a persisted document signer, and ",[941,196153,196154],{},"verifyPodHeader(bytes, trustedKeys)"," authenticates it using nothing but WebCrypto — no store, no enclave, no vault, no decompression.",[1022,196157,196159],{"id":196158},"what-gets-signed","What gets signed",[937,196161,196162,196163,968,196165,968,196167,968,196169,196171,196172,1261,196175,1261,196177,1261,196180,1261,196182,1261,196184,1261,196187,1261,196190,1261,196192,196195],{},"The header — ",[941,196164,36353],{},[941,196166,36368],{},[941,196168,36388],{},[941,196170,36404],{},", plus whichever of ",[941,196173,196174],{},"publicEnvelope",[941,196176,116597],{},[941,196178,196179],{},"bundleKind",[941,196181,195197],{},[941,196183,195716],{},[941,196185,196186],{},"unlockMethods",[941,196188,196189],{},"hasApp",[941,196191,195713],{},[941,196193,196194],{},"pointerMode"," are present — is signed as canonical JSON over Ed25519. The Redirect record and manifest writes sign through the same convention, so a verifier learns one scheme rather than three.",[937,196197,196198,196199,196202],{},"Signing bumps the header to ",[941,196200,196201],{},"formatVersion: 2"," and attaches a three-part tuple:",[1338,196204,196205,196213],{},[1341,196206,196207],{},[1344,196208,196209,196211],{},[1347,196210,101104],{},[1347,196212],{},[1357,196214,196215,196228,196244],{},[1344,196216,196217,196222],{},[1362,196218,196219],{},[941,196220,196221],{},"sigAlg",[1362,196223,196224,196225],{},"currently always ",[941,196226,196227],{},"'ed25519'",[1344,196229,196230,196234],{},[1362,196231,196232],{},[941,196233,66206],{},[1362,196235,196236,196237,997,196239,196241,196242,9634],{},"base64url Ed25519 signature over the header, including ",[941,196238,66012],{},[941,196240,196221],{},", excluding ",[941,196243,66206],{},[1344,196245,196246,196250],{},[1362,196247,196248],{},[941,196249,66012],{},[1362,196251,196252],{},"a 16-hex-character fingerprint of the signing public key",[937,196254,196255,196257,196258,196260],{},[941,196256,196221],{}," rides ",[950,196259,1094],{}," the signed bytes, not alongside them. Stripping or swapping the algorithm tag breaks verification rather than silently downgrading to an unauthenticated check.",[1022,196262,68180],{"id":68179},[937,196264,196265,196266,196269,196270,196273,196274,196277,196278,196280],{},"The signing key lives on the vault as a ",[941,196267,196268],{},"DocSigner"," in the encrypted ",[941,196271,196272],{},"_attestations\u002F_signer"," record. That record holds the ",[950,196275,196276],{},"private"," key and is deliberately excluded from ",[941,196279,6820],{}," — it never rides in the pod body.",[937,196282,196283,196284,196286],{},"The header carries only ",[941,196285,66012],{},", never the public key. Three consequences:",[1111,196288,196289,196298,196309],{},[1114,196290,196291,196293,196294,196297],{},[941,196292,66012],{}," is readable ",[950,196295,196296],{},"pre-unlock",", straight off the plaintext header, so a caller pinning a known vault can recognise \"this is signed by the key I expect\" before decrypting anything.",[1114,196299,196300,1324,196303,196305,196306,196308],{},[950,196301,196302],{},"The verifier must supply the public key out of band.",[941,196304,195696],{}," takes a ",[941,196307,195657],{}," map the caller already trusts — pinned from a prior interaction, distributed by the attestation issuer, or configured by an operator.",[1114,196310,196311,196312,196315],{},"There is ",[950,196313,196314],{},"no in-pod channel for learning a new public key",". A first-contact verifier cannot bootstrap trust from a pod alone. Publishing the public key in the pod body to enable that is a known follow-up, not built.",[1022,196317,196319],{"id":196318},"verifypodheader",[941,196320,195696],{},[2217,196322,196324],{"className":2219,"code":196323,"language":2221,"meta":1037,"style":1037},"import { verifyPodHeader } from '@noy-db\u002Fhub\u002Fpod'\n\nconst result = await verifyPodHeader(bytes, { [keyId]: publicKeyB64 })\n",[941,196325,196326,196345,196349],{"__ignoreMap":1037},[2225,196327,196328,196330,196332,196335,196337,196339,196341,196343],{"class":2227,"line":2228},[2225,196329,2232],{"class":2231},[2225,196331,2236],{"class":2235},[2225,196333,196334],{"class":2239}," verifyPodHeader",[2225,196336,2243],{"class":2235},[2225,196338,2246],{"class":2231},[2225,196340,2249],{"class":2235},[2225,196342,70036],{"class":2252},[2225,196344,2256],{"class":2235},[2225,196346,196347],{"class":2227,"line":1041},[2225,196348,2285],{"emptyLinePlaceholder":2284},[2225,196350,196351,196353,196355,196357,196359,196361,196363,196365,196367,196369,196371,196373,196375,196377,196379],{"class":2227,"line":1038},[2225,196352,2775],{"class":2774},[2225,196354,36920],{"class":2239},[2225,196356,2781],{"class":2235},[2225,196358,2784],{"class":2231},[2225,196360,196334],{"class":2291},[2225,196362,127440],{"class":2239},[2225,196364,2298],{"class":2235},[2225,196366,2236],{"class":2235},[2225,196368,8667],{"class":2307},[2225,196370,66012],{"class":2239},[2225,196372,8803],{"class":2307},[2225,196374,2311],{"class":2235},[2225,196376,65966],{"class":2239},[2225,196378,2359],{"class":2235},[2225,196380,2362],{"class":2239},[937,196382,196383],{},"It reads only the prefix and header region. Four outcomes:",[1338,196385,196386,196396],{},[1341,196387,196388],{},[1344,196389,196390,196394],{},[1347,196391,196392],{},[941,196393,6631],{},[1347,196395,20200],{},[1357,196397,196398,196411,196428,196446],{},[1344,196399,196400,196405],{},[1362,196401,196402],{},[941,196403,196404],{},"verified",[1362,196406,196407,196408,196410],{},"Signed, ",[941,196409,66012],{}," is trusted, signature checks out.",[1344,196412,196413,196417],{},[1362,196414,196415],{},[941,196416,195663],{},[1362,196418,196419,196420,1952,196422,196425,196426,1520],{},"No signature tuple — a legacy v1 pod, or written with ",[941,196421,195667],{},[950,196423,196424],{},"Not an error",", and never conflated with ",[941,196427,196404],{},[1344,196429,196430,196434],{},[1362,196431,196432],{},[941,196433,195699],{},[1362,196435,196436,196437,196439,196440,196442,196443,196445],{},"Signed, but ",[941,196438,66012],{}," is not in ",[941,196441,195692],{},". The signature was ",[950,196444,1004],{}," checked.",[1344,196447,196448,196453],{},[1362,196449,196450],{},[941,196451,196452],{},"tampered",[1362,196454,196455,196456,1520],{},"Signed by a trusted key, but the signature fails — the header was altered, or the wrong public key is mapped to that ",[941,196457,66012],{},[1022,196459,196461],{"id":196460},"authenticity-is-not-integrity-run-both","Authenticity is not integrity — run both",[937,196463,196464],{},"This is the part that bites.",[937,196466,196467,196469,196470,196472,196473,1324,196476,150626,196478,196480,196481,196483],{},[941,196468,195696],{}," authenticates the ",[950,196471,116552],{},", and the header contains the ",[1092,196474,196475],{},"claimed",[941,196477,36404],{},[941,196479,196404],{}," result proves the signer vouched for that hash. It does ",[950,196482,1004],{}," prove the body matches it.",[937,196485,196486,196487,196489],{},"To trust pod ",[1092,196488,11007],{}," you must compose two checks and treat either failure as untrusted:",[1234,196491,196492,196504],{},[1114,196493,196494,2557,196496,196498,196499,196501,196502],{},[941,196495,196154],{},[941,196497,196404],{}," — the header, including ",[941,196500,36404],{},", is signed by a key you trust; ",[950,196503,14128],{},[1114,196505,196506,196508,196509,196511,196512,1520],{},[941,196507,70088],{}," succeeds — the body hashes to that signed ",[941,196510,36404],{},", or it throws ",[941,196513,115868],{},[937,196515,196516,196517,1324,196520,196522],{},"Running only step 1 is a footgun: ",[950,196518,196519],{},"a verified header paired with a swapped body is caught only by step 2.",[941,196521,195696],{}," never folds in the body check by design — it has to stay body-free so a static page can authenticate a header without pulling megabytes. The composition is the caller's responsibility.",[1022,196524,196526],{"id":196525},"legacy-and-scope","Legacy and scope",[937,196528,196529,196530,196532,196533,196535,196536,196538],{},"A header with no ",[941,196531,66206],{}," is always reported ",[941,196534,195663],{},", never silently ",[941,196537,196404],{},". The tuple is all-or-nothing: a partial tuple is a validation error, not a degraded-trust state. A v1 reader meeting a v2 signed pod fails with a clean format-version error rather than misreading the new fields.",[937,196540,196541],{},"Current limits, stated plainly:",[1111,196543,196544,196550,196561],{},[1114,196545,196546,196549],{},[950,196547,196548],{},"Partitions are unsigned."," Signing does not extend to the partition-transfer format.",[1114,196551,196552,1324,196555,196557,196558,196560],{},[950,196553,196554],{},"Verification is additive.",[941,196556,70321],{}," does not fold signature status into its return value; callers that want authenticity call ",[941,196559,195696],{}," themselves.",[1114,196562,196563,196566],{},[950,196564,196565],{},"No revocation."," Attestation has a revocation list; connecting it to pod verification is not wired up.",[1022,196568,2621],{"id":2620},[1111,196570,196571,196577,196582],{},[1114,196572,196573,196576],{},[1007,196574,788],{"href":196575},".\u002Fpod-redirect"," — the signed \"this moved\" pointer, using the same signing convention",[1114,196578,196579,196581],{},[1007,196580,735],{"href":196115}," — the pod-wide schema manifest",[1114,196583,196584,196587],{},[1007,196585,609],{"href":196586},".\u002Fattestation"," — where the document signer comes from",[2645,196589,37016],{},{"title":1037,"searchDepth":1038,"depth":1038,"links":196591},[196592,196593,196594,196595,196596,196597],{"id":196158,"depth":1041,"text":196159},{"id":68179,"depth":1041,"text":68180},{"id":196318,"depth":1041,"text":195696},{"id":196460,"depth":1041,"text":196461},{"id":196525,"depth":1041,"text":196526},{"id":2620,"depth":1041,"text":2621},"Ed25519 authentication of a .noydb pod's plaintext header, verifiable by a static page with no store, no vault and no key material beyond WebCrypto.",{},{"title":792,"description":196598},"Aby6K_MhKKkoi7tksRhPtY_A9IpLPzGXO8UN4rGTlmU",{"id":196603,"title":796,"access":12,"apiToc":929,"body":196604,"category":1042,"description":198548,"extension":1044,"meta":198549,"navigation":1042,"nest":1047,"path":797,"seo":198550,"sinceVersion":1042,"status":1049,"stem":798,"subtitle":1042,"tier":1050,"tocExternal":1042,"__hash__":198551},"docs\u002Fservices\u002Fpolicy.md",{"type":931,"value":196605,"toc":198523},[196606,196608,196611,196613,196630,196661,196667,196669,196683,196861,196878,196880,196884,196893,197096,197119,197200,197204,197329,197340,197344,197353,197457,197459,197464,197524,197533,197538,197617,197691,197705,197710,197771,197776,197892,197895,197903,198002,198034,198042,198102,198109,198314,198322,198326,198358,198360,198388,198390,198483,198485,198520],[934,196607,796],{"id":796},[46871,196609],{":rows":196610},"[{\"label\":\"Subpath\",\"value\":\"`@noy-db\u002Fhub` (root export)\"},{\"label\":\"Type\",\"value\":\"Core feature — every vault has a policy from creation; configured via the `policy` option on `createNoydb()`.\"}]",[1022,196612,29857],{"id":29856},[937,196614,196615,196616,196618,196619,196622,196623,196625,196626,196629],{},"Some operations on a vault are more dangerous than others — rotating the secret, exporting\nplaintext, enrolling a new authenticator, granting or revoking a user. ",[941,196617,796],{}," lets you declare,\nper operation, the minimum authentication strength required before that operation is allowed to\nrun: a minimum ",[950,196620,196621],{},"session tier"," (how the caller unlocked the vault — secret, routine\nlogin, or quick-resume PIN; see ",[1007,196624,844],{"href":129031},") and, optionally, extra\n",[950,196627,196628],{},"factor proofs"," (a fresh TOTP code, an emailed one-time code, a recovery code, …) presented at\nthe moment of the call.",[937,196631,196632,196633,196635,196636,1707,196638,968,196641,196644,196645,196647,196648,196650,196651,196653,196654,968,196656,2462,196658,196660],{},"Each guarded operation is called a ",[950,196634,2723],{},". A gate has a name (",[941,196637,13041],{},[941,196639,196640],{},"'export-plaintext'",[941,196642,196643],{},"'enroll-user'",", …) and a ",[941,196646,104535],{}," — the rule that decides whether a\ngiven call is allowed. The full set of gates for a vault is a ",[941,196649,12870],{},", persisted once at\n",[941,196652,12784],{}," on first vault creation. ",[941,196655,840],{},[941,196657,872],{},[941,196659,868],{}," build their\nre-authentication prompts and permission checks on top of this same gate-check enforcement.",[937,196662,196663,196664,196666],{},"There is nothing to opt into — every vault has a policy from the moment it's created. If you pass\nnone, noy-db uses a sensible default (",[941,196665,12830],{},"); you only need to reach for the API below\nwhen you want to change the defaults or check a gate yourself.",[1022,196668,58394],{"id":58393},[937,196670,196671,196673,196674,196676,196677,196679,196680,196682],{},[941,196672,796],{}," has no ",[941,196675,943],{}," strategy — pass a ",[941,196678,12870],{}," directly to ",[941,196681,2134],{},". Start from\none of the two built-in presets and override only the gates you need to change:",[2217,196684,196686],{"className":2219,"code":196685,"language":2221,"meta":1037,"style":1037},"import { createNoydb, STRICT_POLICY, mergePolicy } from '@noy-db\u002Fhub'\n\nconst db = await createNoydb({\n  store,\n  user: 'u1',\n  \u002F\u002F STRICT_POLICY, with one extra app-defined gate layered on top.\n  policy: mergePolicy(STRICT_POLICY, {\n    gates: {\n      'app:approve-large-payment': {\n        minTier: 2,\n        factors: [{ anyOf: ['totp'] }],\n      },\n    },\n  }),\n})\n",[941,196687,196688,196716,196720,196736,196742,196756,196761,196776,196784,196797,196808,196839,196843,196847,196855],{"__ignoreMap":1037},[2225,196689,196690,196692,196694,196696,196698,196701,196703,196706,196708,196710,196712,196714],{"class":2227,"line":2228},[2225,196691,2232],{"class":2231},[2225,196693,2236],{"class":2235},[2225,196695,2744],{"class":2239},[2225,196697,2298],{"class":2235},[2225,196699,196700],{"class":2239}," STRICT_POLICY",[2225,196702,2298],{"class":2235},[2225,196704,196705],{"class":2239}," mergePolicy",[2225,196707,2243],{"class":2235},[2225,196709,2246],{"class":2231},[2225,196711,2249],{"class":2235},[2225,196713,1086],{"class":2252},[2225,196715,2256],{"class":2235},[2225,196717,196718],{"class":2227,"line":1041},[2225,196719,2285],{"emptyLinePlaceholder":2284},[2225,196721,196722,196724,196726,196728,196730,196732,196734],{"class":2227,"line":1038},[2225,196723,2775],{"class":2774},[2225,196725,2778],{"class":2239},[2225,196727,2781],{"class":2235},[2225,196729,2784],{"class":2231},[2225,196731,2744],{"class":2291},[2225,196733,2789],{"class":2239},[2225,196735,4200],{"class":2235},[2225,196737,196738,196740],{"class":2227,"line":2288},[2225,196739,15608],{"class":2239},[2225,196741,1707],{"class":2235},[2225,196743,196744,196746,196748,196750,196752,196754],{"class":2227,"line":2304},[2225,196745,4205],{"class":2307},[2225,196747,2311],{"class":2235},[2225,196749,2249],{"class":2235},[2225,196751,118667],{"class":2252},[2225,196753,2805],{"class":2235},[2225,196755,1707],{"class":2235},[2225,196757,196758],{"class":2227,"line":2356},[2225,196759,196760],{"class":2763},"  \u002F\u002F STRICT_POLICY, with one extra app-defined gate layered on top.\n",[2225,196762,196763,196765,196767,196769,196772,196774],{"class":2227,"line":2830},[2225,196764,18899],{"class":2307},[2225,196766,2311],{"class":2235},[2225,196768,196705],{"class":2291},[2225,196770,196771],{"class":2239},"(STRICT_POLICY",[2225,196773,2298],{"class":2235},[2225,196775,2301],{"class":2235},[2225,196777,196778,196780,196782],{"class":2227,"line":2861},[2225,196779,18917],{"class":2307},[2225,196781,2311],{"class":2235},[2225,196783,2301],{"class":2235},[2225,196785,196786,196788,196791,196793,196795],{"class":2227,"line":4246},[2225,196787,18944],{"class":2235},[2225,196789,196790],{"class":2307},"app:approve-large-payment",[2225,196792,2805],{"class":2235},[2225,196794,2311],{"class":2235},[2225,196796,2301],{"class":2235},[2225,196798,196799,196802,196804,196806],{"class":2227,"line":4253},[2225,196800,196801],{"class":2307},"        minTier",[2225,196803,2311],{"class":2235},[2225,196805,18961],{"class":6527},[2225,196807,1707],{"class":2235},[2225,196809,196810,196813,196815,196817,196819,196821,196823,196825,196827,196829,196831,196833,196835,196837],{"class":2227,"line":4259},[2225,196811,196812],{"class":2307},"        factors",[2225,196814,2311],{"class":2235},[2225,196816,8667],{"class":2239},[2225,196818,2792],{"class":2235},[2225,196820,18974],{"class":2307},[2225,196822,2311],{"class":2235},[2225,196824,8667],{"class":2239},[2225,196826,2805],{"class":2235},[2225,196828,333],{"class":2252},[2225,196830,2805],{"class":2235},[2225,196832,8890],{"class":2239},[2225,196834,2359],{"class":2235},[2225,196836,8803],{"class":2239},[2225,196838,1707],{"class":2235},[2225,196840,196841],{"class":2227,"line":6548},[2225,196842,60068],{"class":2235},[2225,196844,196845],{"class":2227,"line":6576},[2225,196846,8324],{"class":2235},[2225,196848,196849,196851,196853],{"class":2227,"line":6582},[2225,196850,19143],{"class":2235},[2225,196852,1381],{"class":2239},[2225,196854,1707],{"class":2235},[2225,196856,196857,196859],{"class":2227,"line":6587},[2225,196858,2359],{"class":2235},[2225,196860,2362],{"class":2239},[937,196862,196863,196864,196866,196867,196869,196870,196872,196873,196875,196876,1520],{},"If you omit ",[941,196865,796],{}," entirely, noy-db persists ",[941,196868,12830],{}," on first vault creation. The\non-disk document is the source of truth after that: the ",[941,196871,796],{}," option only takes effect the\nfirst time a vault is created, not on every ",[941,196874,2134],{}," call. Change it later with\n",[941,196877,104516],{},[1022,196879,29893],{"id":29892},[1591,196881,196883],{"id":196882},"_1-check-a-gate-before-calling-a-guarded-method","1. Check a gate before calling a guarded method",[937,196885,196886,196889,196890,196892],{},[941,196887,196888],{},"db.checkGate(vault, gate, factors?)"," evaluates a gate against the vault's current session tier\nand the factor proofs you present, without performing any other action. Use it to pre-flight a UI\n(\"do I need to ask for a TOTP code before showing the export button?\") or to guard your own\n",[941,196891,13045],{}," actions.",[2217,196894,196896],{"className":2219,"code":196895,"language":2221,"meta":1037,"style":1037},"import { PolicyDeniedError } from '@noy-db\u002Fhub'\n\ntry {\n  await db.checkGate('books', 'export-plaintext', {\n    factors: [{ kind: 'totp', mintedAt: new Date().toISOString() }],\n  })\n  \u002F\u002F → resolves with undefined: allowed\n} catch (err) {\n  if (err instanceof PolicyDeniedError) {\n    \u002F\u002F → err.reason: 'insufficient-tier' | 'missing-factor' | 'stale-proof'\n    \u002F\u002F               | 'disabled' | 'shared-device-blocked'\n    \u002F\u002F → err.required: the GatePolicy the caller failed to satisfy\n    console.log(`denied: ${err.reason}`, err.required)\n  }\n}\n",[941,196897,196898,196916,196920,196926,196956,197000,197006,197011,197021,197037,197042,197047,197052,197088,197092],{"__ignoreMap":1037},[2225,196899,196900,196902,196904,196906,196908,196910,196912,196914],{"class":2227,"line":2228},[2225,196901,2232],{"class":2231},[2225,196903,2236],{"class":2235},[2225,196905,18842],{"class":2239},[2225,196907,2243],{"class":2235},[2225,196909,2246],{"class":2231},[2225,196911,2249],{"class":2235},[2225,196913,1086],{"class":2252},[2225,196915,2256],{"class":2235},[2225,196917,196918],{"class":2227,"line":1041},[2225,196919,2285],{"emptyLinePlaceholder":2284},[2225,196921,196922,196924],{"class":2227,"line":1038},[2225,196923,7554],{"class":2231},[2225,196925,2301],{"class":2235},[2225,196927,196928,196930,196932,196934,196936,196938,196940,196942,196944,196946,196948,196950,196952,196954],{"class":2227,"line":2288},[2225,196929,7561],{"class":2231},[2225,196931,2842],{"class":2239},[2225,196933,1520],{"class":2235},[2225,196935,12982],{"class":2291},[2225,196937,2789],{"class":2307},[2225,196939,2805],{"class":2235},[2225,196941,65761],{"class":2252},[2225,196943,2805],{"class":2235},[2225,196945,2298],{"class":2235},[2225,196947,2249],{"class":2235},[2225,196949,18947],{"class":2252},[2225,196951,2805],{"class":2235},[2225,196953,2298],{"class":2235},[2225,196955,2301],{"class":2235},[2225,196957,196958,196960,196962,196964,196966,196968,196970,196972,196974,196976,196978,196980,196982,196984,196986,196988,196990,196992,196994,196996,196998],{"class":2227,"line":2304},[2225,196959,19073],{"class":2307},[2225,196961,2311],{"class":2235},[2225,196963,8667],{"class":2307},[2225,196965,2792],{"class":2235},[2225,196967,8285],{"class":2307},[2225,196969,2311],{"class":2235},[2225,196971,2249],{"class":2235},[2225,196973,333],{"class":2252},[2225,196975,2805],{"class":2235},[2225,196977,2298],{"class":2235},[2225,196979,19115],{"class":2307},[2225,196981,2311],{"class":2235},[2225,196983,8130],{"class":2235},[2225,196985,19122],{"class":2291},[2225,196987,4228],{"class":2307},[2225,196989,1520],{"class":2235},[2225,196991,19129],{"class":2291},[2225,196993,19132],{"class":2307},[2225,196995,2359],{"class":2235},[2225,196997,8803],{"class":2307},[2225,196999,1707],{"class":2235},[2225,197001,197002,197004],{"class":2227,"line":2356},[2225,197003,19143],{"class":2235},[2225,197005,2362],{"class":2307},[2225,197007,197008],{"class":2227,"line":2830},[2225,197009,197010],{"class":2763},"  \u002F\u002F → resolves with undefined: allowed\n",[2225,197012,197013,197015,197017,197019],{"class":2227,"line":2861},[2225,197014,2359],{"class":2235},[2225,197016,7598],{"class":2231},[2225,197018,7601],{"class":2239},[2225,197020,4200],{"class":2235},[2225,197022,197023,197025,197027,197029,197031,197033,197035],{"class":2227,"line":4246},[2225,197024,7608],{"class":2231},[2225,197026,953],{"class":2307},[2225,197028,7617],{"class":2239},[2225,197030,19172],{"class":2235},[2225,197032,18842],{"class":6342},[2225,197034,8495],{"class":2307},[2225,197036,4200],{"class":2235},[2225,197038,197039],{"class":2227,"line":4253},[2225,197040,197041],{"class":2763},"    \u002F\u002F → err.reason: 'insufficient-tier' | 'missing-factor' | 'stale-proof'\n",[2225,197043,197044],{"class":2227,"line":4259},[2225,197045,197046],{"class":2763},"    \u002F\u002F               | 'disabled' | 'shared-device-blocked'\n",[2225,197048,197049],{"class":2227,"line":6548},[2225,197050,197051],{"class":2763},"    \u002F\u002F → err.required: the GatePolicy the caller failed to satisfy\n",[2225,197053,197054,197056,197058,197060,197062,197064,197067,197069,197071,197073,197075,197077,197079,197082,197084,197086],{"class":2227,"line":6576},[2225,197055,27912],{"class":2239},[2225,197057,1520],{"class":2235},[2225,197059,27100],{"class":2291},[2225,197061,2789],{"class":2307},[2225,197063,44476],{"class":2235},[2225,197065,197066],{"class":2252},"denied: ",[2225,197068,8197],{"class":2235},[2225,197070,7617],{"class":2239},[2225,197072,1520],{"class":2235},[2225,197074,43668],{"class":2239},[2225,197076,43799],{"class":2235},[2225,197078,2298],{"class":2235},[2225,197080,197081],{"class":2239}," err",[2225,197083,1520],{"class":2235},[2225,197085,45375],{"class":2239},[2225,197087,2362],{"class":2307},[2225,197089,197090],{"class":2227,"line":6582},[2225,197091,7632],{"class":2235},[2225,197093,197094],{"class":2227,"line":6587},[2225,197095,7351],{"class":2235},[937,197097,197098,197099,968,197102,968,197104,197107,197108,197110,197111,197113,197114,197116,197117,2311],{},"Built-in gated methods (",[941,197100,197101],{},"db.rotateSecret",[941,197103,107017],{},[941,197105,197106],{},"db.updateUser",", …) take\nthe same ",[941,197109,104285],{}," as their last argument and run through ",[941,197112,12982],{}," internally, so you\nusually don't call ",[941,197115,12982],{}," directly for those — just pass ",[941,197118,13051],{},[2217,197120,197122],{"className":2219,"code":197121,"language":2221,"meta":1037,"style":1037},"await db.team.rotateSecret(\n  'books',\n  { newSecret: 'glasses cabinet bicycle umbrella thunder velvet' },\n  { factors: [{ kind: 'totp' }] },\n)\n",[941,197123,197124,197140,197150,197168,197196],{"__ignoreMap":1037},[2225,197125,197126,197128,197130,197132,197134,197136,197138],{"class":2227,"line":2228},[2225,197127,6500],{"class":2231},[2225,197129,2842],{"class":2239},[2225,197131,1520],{"class":2235},[2225,197133,868],{"class":2239},[2225,197135,1520],{"class":2235},[2225,197137,15216],{"class":2291},[2225,197139,11683],{"class":2239},[2225,197141,197142,197144,197146,197148],{"class":2227,"line":1041},[2225,197143,124511],{"class":2235},[2225,197145,65761],{"class":2252},[2225,197147,2805],{"class":2235},[2225,197149,1707],{"class":2235},[2225,197151,197152,197154,197157,197159,197161,197164,197166],{"class":2227,"line":1038},[2225,197153,26354],{"class":2235},[2225,197155,197156],{"class":2307}," newSecret",[2225,197158,2311],{"class":2235},[2225,197160,2249],{"class":2235},[2225,197162,197163],{"class":2252},"glasses cabinet bicycle umbrella thunder velvet",[2225,197165,2805],{"class":2235},[2225,197167,2353],{"class":2235},[2225,197169,197170,197172,197174,197176,197178,197180,197182,197184,197186,197188,197190,197192,197194],{"class":2227,"line":2288},[2225,197171,26354],{"class":2235},[2225,197173,17534],{"class":2307},[2225,197175,2311],{"class":2235},[2225,197177,8667],{"class":2239},[2225,197179,2792],{"class":2235},[2225,197181,8285],{"class":2307},[2225,197183,2311],{"class":2235},[2225,197185,2249],{"class":2235},[2225,197187,333],{"class":2252},[2225,197189,2805],{"class":2235},[2225,197191,2243],{"class":2235},[2225,197193,8890],{"class":2239},[2225,197195,19008],{"class":2235},[2225,197197,197198],{"class":2227,"line":2304},[2225,197199,2362],{"class":2239},[1591,197201,197203],{"id":197202},"_2-read-and-update-the-vaults-policy","2. Read and update the vault's policy",[2217,197205,197207],{"className":2219,"code":197206,"language":2221,"meta":1037,"style":1037},"const current = await db.getPolicy('books')\n\u002F\u002F → { secret: { minWords: 6, ... }, gates: { 'rotate-secret': { minTier: 1, ... }, ... } }\n\nconst updated = await db.updatePolicy('books', {\n  gates: {\n    'view-team-profiles': { minTier: 1, enabled: false }, \u002F\u002F privacy-strict opt-out\n  },\n})\n\u002F\u002F → merged VaultPolicy: every other gate untouched, 'view-team-profiles' replaced\n",[941,197208,197209,197236,197241,197245,197274,197283,197314,197318,197324],{"__ignoreMap":1037},[2225,197210,197211,197213,197216,197218,197220,197222,197224,197226,197228,197230,197232,197234],{"class":2227,"line":2228},[2225,197212,2775],{"class":2774},[2225,197214,197215],{"class":2239}," current ",[2225,197217,2781],{"class":2235},[2225,197219,2784],{"class":2231},[2225,197221,2842],{"class":2239},[2225,197223,1520],{"class":2235},[2225,197225,12861],{"class":2291},[2225,197227,2789],{"class":2239},[2225,197229,2805],{"class":2235},[2225,197231,65761],{"class":2252},[2225,197233,2805],{"class":2235},[2225,197235,2362],{"class":2239},[2225,197237,197238],{"class":2227,"line":1041},[2225,197239,197240],{"class":2763},"\u002F\u002F → { secret: { minWords: 6, ... }, gates: { 'rotate-secret': { minTier: 1, ... }, ... } }\n",[2225,197242,197243],{"class":2227,"line":1038},[2225,197244,2285],{"emptyLinePlaceholder":2284},[2225,197246,197247,197249,197252,197254,197256,197258,197260,197262,197264,197266,197268,197270,197272],{"class":2227,"line":2288},[2225,197248,2775],{"class":2774},[2225,197250,197251],{"class":2239}," updated ",[2225,197253,2781],{"class":2235},[2225,197255,2784],{"class":2231},[2225,197257,2842],{"class":2239},[2225,197259,1520],{"class":2235},[2225,197261,12905],{"class":2291},[2225,197263,2789],{"class":2239},[2225,197265,2805],{"class":2235},[2225,197267,65761],{"class":2252},[2225,197269,2805],{"class":2235},[2225,197271,2298],{"class":2235},[2225,197273,2301],{"class":2235},[2225,197275,197276,197279,197281],{"class":2227,"line":2304},[2225,197277,197278],{"class":2307},"  gates",[2225,197280,2311],{"class":2235},[2225,197282,2301],{"class":2235},[2225,197284,197285,197287,197289,197291,197293,197295,197297,197299,197301,197303,197305,197307,197309,197311],{"class":2227,"line":2356},[2225,197286,76714],{"class":2235},[2225,197288,98879],{"class":2307},[2225,197290,2805],{"class":2235},[2225,197292,2311],{"class":2235},[2225,197294,2236],{"class":2235},[2225,197296,18956],{"class":2307},[2225,197298,2311],{"class":2235},[2225,197300,9377],{"class":6527},[2225,197302,2298],{"class":2235},[2225,197304,135470],{"class":2307},[2225,197306,2311],{"class":2235},[2225,197308,2341],{"class":2321},[2225,197310,10965],{"class":2235},[2225,197312,197313],{"class":2763}," \u002F\u002F privacy-strict opt-out\n",[2225,197315,197316],{"class":2227,"line":2830},[2225,197317,19017],{"class":2235},[2225,197319,197320,197322],{"class":2227,"line":2861},[2225,197321,2359],{"class":2235},[2225,197323,2362],{"class":2239},[2225,197325,197326],{"class":2227,"line":4246},[2225,197327,197328],{"class":2763},"\u002F\u002F → merged VaultPolicy: every other gate untouched, 'view-team-profiles' replaced\n",[937,197330,197331,197333,197334,197336,197337,197339],{},[941,197332,12905],{}," fully ",[950,197335,98556],{}," the entry for any gate you specify (it does not deep-merge a\nsingle gate's fields) and leaves every other gate as-is. It is itself gated by ",[941,197338,196643],{}," —\nchanging the policy is a privilege-management action.",[1591,197341,197343],{"id":197342},"_3-preview-a-verdict-without-throwing","3. Preview a verdict without throwing",[937,197345,197346,197349,197350,197352],{},[941,197347,197348],{},"describeGate"," runs the same check as ",[941,197351,12982],{}," but returns a structured result instead of\nthrowing — useful for building an error UI (\"you'll need a TOTP code plus a recovery code to do\nthat\") ahead of the user attempting the action:",[2217,197354,197356],{"className":2219,"code":197355,"language":2221,"meta":1037,"style":1037},"import { describeGate } from '@noy-db\u002Fhub'\n\nconst verdict = await describeGate(await db.getPolicy('books'), 'rotate-secret', {\n  activeTier: 1,\n  factors: [],\n})\n\u002F\u002F → { ok: false, reason: 'missing-factor', required: { minTier: 1, factors: [...] } }\n",[941,197357,197358,197377,197381,197425,197436,197446,197452],{"__ignoreMap":1037},[2225,197359,197360,197362,197364,197367,197369,197371,197373,197375],{"class":2227,"line":2228},[2225,197361,2232],{"class":2231},[2225,197363,2236],{"class":2235},[2225,197365,197366],{"class":2239}," describeGate",[2225,197368,2243],{"class":2235},[2225,197370,2246],{"class":2231},[2225,197372,2249],{"class":2235},[2225,197374,1086],{"class":2252},[2225,197376,2256],{"class":2235},[2225,197378,197379],{"class":2227,"line":1041},[2225,197380,2285],{"emptyLinePlaceholder":2284},[2225,197382,197383,197385,197387,197389,197391,197393,197395,197397,197399,197401,197403,197405,197407,197409,197411,197413,197415,197417,197419,197421,197423],{"class":2227,"line":1038},[2225,197384,2775],{"class":2774},[2225,197386,61576],{"class":2239},[2225,197388,2781],{"class":2235},[2225,197390,2784],{"class":2231},[2225,197392,197366],{"class":2291},[2225,197394,2789],{"class":2239},[2225,197396,6500],{"class":2231},[2225,197398,2842],{"class":2239},[2225,197400,1520],{"class":2235},[2225,197402,12861],{"class":2291},[2225,197404,2789],{"class":2239},[2225,197406,2805],{"class":2235},[2225,197408,65761],{"class":2252},[2225,197410,2805],{"class":2235},[2225,197412,1381],{"class":2239},[2225,197414,2298],{"class":2235},[2225,197416,2249],{"class":2235},[2225,197418,15169],{"class":2252},[2225,197420,2805],{"class":2235},[2225,197422,2298],{"class":2235},[2225,197424,2301],{"class":2235},[2225,197426,197427,197430,197432,197434],{"class":2227,"line":2288},[2225,197428,197429],{"class":2307},"  activeTier",[2225,197431,2311],{"class":2235},[2225,197433,9377],{"class":6527},[2225,197435,1707],{"class":2235},[2225,197437,197438,197440,197442,197444],{"class":2227,"line":2304},[2225,197439,13002],{"class":2307},[2225,197441,2311],{"class":2235},[2225,197443,66698],{"class":2239},[2225,197445,1707],{"class":2235},[2225,197447,197448,197450],{"class":2227,"line":2356},[2225,197449,2359],{"class":2235},[2225,197451,2362],{"class":2239},[2225,197453,197454],{"class":2227,"line":2830},[2225,197455,197456],{"class":2763},"\u002F\u002F → { ok: false, reason: 'missing-factor', required: { minTier: 1, factors: [...] } }\n",[1022,197458,102889],{"id":102888},[1591,197460,197462],{"id":197461},"vaultpolicy",[941,197463,12870],{},[2217,197465,197467],{"className":2219,"code":197466,"language":2221,"meta":1037,"style":1037},"interface VaultPolicy {\n  readonly secret?: SecretPolicy   \u002F\u002F strength rules applied at secret ingress\n  readonly gates: Partial\u003CRecord\u003CGateName, GatePolicy>>\n}\n",[941,197468,197469,197478,197492,197520],{"__ignoreMap":1037},[2225,197470,197471,197473,197476],{"class":2227,"line":2228},[2225,197472,6994],{"class":2774},[2225,197474,197475],{"class":6342}," VaultPolicy",[2225,197477,2301],{"class":2235},[2225,197479,197480,197482,197484,197486,197489],{"class":2227,"line":1041},[2225,197481,53580],{"class":2774},[2225,197483,2810],{"class":2307},[2225,197485,7011],{"class":2235},[2225,197487,197488],{"class":6342}," SecretPolicy",[2225,197490,197491],{"class":2763},"   \u002F\u002F strength rules applied at secret ingress\n",[2225,197493,197494,197496,197498,197500,197503,197505,197508,197510,197513,197515,197518],{"class":2227,"line":1038},[2225,197495,53580],{"class":2774},[2225,197497,136482],{"class":2307},[2225,197499,2311],{"class":2235},[2225,197501,197502],{"class":6342}," Partial",[2225,197504,6474],{"class":2235},[2225,197506,197507],{"class":6342},"Record",[2225,197509,6474],{"class":2235},[2225,197511,197512],{"class":6342},"GateName",[2225,197514,2298],{"class":2235},[2225,197516,197517],{"class":6342}," GatePolicy",[2225,197519,17688],{"class":2235},[2225,197521,197522],{"class":2227,"line":2288},[2225,197523,7351],{"class":2235},[937,197525,197526,197527,197529,197530,197532],{},"Persisted once at ",[941,197528,12784],{}," when a vault is first created; ",[941,197531,104516],{}," is the only\nsupported way to change it afterward.",[1591,197534,197536],{"id":197535},"gatepolicy",[941,197537,104535],{},[2217,197539,197541],{"className":2219,"code":197540,"language":2221,"meta":1037,"style":1037},"interface GatePolicy {\n  readonly minTier: 1 | 2 | 3          \u002F\u002F minimum session tier the caller must hold\n  readonly factors?: ReadonlyArray\u003CFactorRequirement>\n  readonly warn?: WarningRules\n  readonly enabled?: boolean            \u002F\u002F false disables the action entirely\n}\n",[941,197542,197543,197551,197572,197589,197600,197613],{"__ignoreMap":1037},[2225,197544,197545,197547,197549],{"class":2227,"line":2228},[2225,197546,6994],{"class":2774},[2225,197548,197517],{"class":6342},[2225,197550,2301],{"class":2235},[2225,197552,197553,197555,197557,197559,197561,197563,197565,197567,197569],{"class":2227,"line":1041},[2225,197554,53580],{"class":2774},[2225,197556,18956],{"class":2307},[2225,197558,2311],{"class":2235},[2225,197560,9377],{"class":6527},[2225,197562,7087],{"class":2235},[2225,197564,18961],{"class":6527},[2225,197566,7087],{"class":2235},[2225,197568,14460],{"class":6527},[2225,197570,197571],{"class":2763},"          \u002F\u002F minimum session tier the caller must hold\n",[2225,197573,197574,197576,197578,197580,197582,197584,197587],{"class":2227,"line":1038},[2225,197575,53580],{"class":2774},[2225,197577,17534],{"class":2307},[2225,197579,7011],{"class":2235},[2225,197581,19754],{"class":6342},[2225,197583,6474],{"class":2235},[2225,197585,197586],{"class":6342},"FactorRequirement",[2225,197588,7093],{"class":2235},[2225,197590,197591,197593,197595,197597],{"class":2227,"line":2288},[2225,197592,53580],{"class":2774},[2225,197594,173053],{"class":2307},[2225,197596,7011],{"class":2235},[2225,197598,197599],{"class":6342}," WarningRules\n",[2225,197601,197602,197604,197606,197608,197610],{"class":2227,"line":2304},[2225,197603,53580],{"class":2774},[2225,197605,135470],{"class":2307},[2225,197607,7011],{"class":2235},[2225,197609,7723],{"class":6342},[2225,197611,197612],{"class":2763},"            \u002F\u002F false disables the action entirely\n",[2225,197614,197615],{"class":2227,"line":2356},[2225,197616,7351],{"class":2235},[1338,197618,197619,197627],{},[1341,197620,197621],{},[1344,197622,197623,197625],{},[1347,197624,101104],{},[1347,197626,7834],{},[1357,197628,197629,197644,197655,197676],{},[1344,197630,197631,197635],{},[1362,197632,197633],{},[941,197634,17301],{},[1362,197636,197637,197638,197640,197641,1520],{},"Lower number is more privileged (",[941,197639,5437],{}," = secret unlock). The caller's active tier must be ",[941,197642,197643],{},"\u003C= minTier",[1344,197645,197646,197650],{},[1362,197647,197648],{},[941,197649,13051],{},[1362,197651,197652,197653,1520],{},"Extra freshness-bound proofs required on top of the tier check — see ",[941,197654,197586],{},[1344,197656,197657,197661],{},[1362,197658,197659],{},[941,197660,150565],{},[1362,197662,197663,197664,197667,197668,197671,197672,197675],{},"Soft signals layered on the verdict. Today only ",[941,197665,197666],{},"warn.sharedDevice: 'block'"," is enforced — it turns a shared-device call into a denial. ",[941,197669,197670],{},"warn.weakAuthenticator"," is declared on the ",[941,197673,197674],{},"WarningRules"," type but not yet read or enforced by the engine.",[1344,197677,197678,197682],{},[1362,197679,197680],{},[941,197681,16929],{},[1362,197683,197684,197685,197687,197688,197690],{},"Set ",[941,197686,2560],{}," to disable the action outright (e.g. ",[941,197689,15169],{}," is impossible by construction in managed-secret mode).",[937,197692,197693,197696,197697,5584,197699,953,197702,197704],{},[950,197694,197695],{},"Built-in gates without a configured policy fail closed"," (treated as ",[941,197698,103519],{},[950,197700,197701],{},"App-defined gates",[941,197703,13045],{},") without a configured policy are no-ops — the engine\nonly enforces gates you've explicitly registered.",[1591,197706,197708],{"id":197707},"factorrequirement",[941,197709,197586],{},[2217,197711,197713],{"className":2219,"code":197712,"language":2221,"meta":1037,"style":1037},"interface FactorRequirement {\n  readonly anyOf: ReadonlyArray\u003CFactorKind>\n  readonly count?: number        \u002F\u002F distinct fresh proofs required, default 1\n  readonly freshnessMs?: number  \u002F\u002F acceptance window per proof, default 5 minutes\n}\n",[941,197714,197715,197724,197741,197754,197767],{"__ignoreMap":1037},[2225,197716,197717,197719,197722],{"class":2227,"line":2228},[2225,197718,6994],{"class":2774},[2225,197720,197721],{"class":6342}," FactorRequirement",[2225,197723,2301],{"class":2235},[2225,197725,197726,197728,197730,197732,197734,197736,197739],{"class":2227,"line":1041},[2225,197727,53580],{"class":2774},[2225,197729,18974],{"class":2307},[2225,197731,2311],{"class":2235},[2225,197733,19754],{"class":6342},[2225,197735,6474],{"class":2235},[2225,197737,197738],{"class":6342},"FactorKind",[2225,197740,7093],{"class":2235},[2225,197742,197743,197745,197747,197749,197751],{"class":2227,"line":1038},[2225,197744,53580],{"class":2774},[2225,197746,19270],{"class":2307},[2225,197748,7011],{"class":2235},[2225,197750,6356],{"class":6342},[2225,197752,197753],{"class":2763},"        \u002F\u002F distinct fresh proofs required, default 1\n",[2225,197755,197756,197758,197760,197762,197764],{"class":2227,"line":2288},[2225,197757,53580],{"class":2774},[2225,197759,18991],{"class":2307},[2225,197761,7011],{"class":2235},[2225,197763,6356],{"class":6342},[2225,197765,197766],{"class":2763},"  \u002F\u002F acceptance window per proof, default 5 minutes\n",[2225,197768,197769],{"class":2227,"line":2304},[2225,197770,7351],{"class":2235},[1591,197772,197774],{"id":197773},"factorkind",[941,197775,197738],{},[1338,197777,197778,197789],{},[1341,197779,197780],{},[1344,197781,197782,197784,197786],{},[1347,197783,37901],{},[1347,197785,29661],{},[1347,197787,197788],{},"Off-device?",[1357,197790,197791,197802,197813,197827,197840,197853,197866,197879],{},[1344,197792,197793,197797,197800],{},[1362,197794,197795],{},[941,197796,333],{},[1362,197798,197799],{},"RFC 6238 authenticator app (Google Authenticator, 1Password)",[1362,197801,14852],{},[1344,197803,197804,197808,197811],{},[1362,197805,197806],{},[941,197807,301],{},[1362,197809,197810],{},"one-time code mailed to the user",[1362,197812,14852],{},[1344,197814,197815,197819,197824],{},[1362,197816,197817],{},[941,197818,321],{},[1362,197820,197821,197822,1381],{},"printable recovery code (",[941,197823,45343],{},[1362,197825,197826],{},"yes (paper)",[1344,197828,197829,197833,197838],{},[1362,197830,197831],{},[941,197832,325],{},[1362,197834,197835,197836,1381],{},"k-of-n threshold share (",[941,197837,45751],{},[1362,197839,14852],{},[1344,197841,197842,197847,197850],{},[1362,197843,197844],{},[941,197845,197846],{},"webauthn-roaming",[1362,197848,197849],{},"hardware key (YubiKey, SoloKey, Titan)",[1362,197851,197852],{},"yes (key is portable)",[1344,197854,197855,197860,197863],{},[1362,197856,197857],{},[941,197858,197859],{},"webauthn-platform",[1362,197861,197862],{},"platform passkey (Touch ID, Face ID, Windows Hello)",[1362,197864,197865],{},"no (device-bound)",[1344,197867,197868,197872,197877],{},[1362,197869,197870],{},[941,197871,313],{},[1362,197873,197874,197875,1381],{},"tier-2 password (",[941,197876,17035],{},[1362,197878,11313],{},[1344,197880,197881,197885,197890],{},[1362,197882,197883],{},[941,197884,317],{},[1362,197886,197887,197888,1381],{},"tier-3 quick-resume PIN (",[941,197889,17062],{},[1362,197891,11313],{},[937,197893,197894],{},"Off-device kinds are the strongest proofs because they require something separate from the device\nthe user just unlocked; platform\u002Fpassword\u002FPIN prove \"this is still the same user\" without binding\nacross devices.",[1591,197896,197898,1261,197901],{"id":197897},"factorproof-factorproofbundle",[941,197899,197900],{},"FactorProof",[941,197902,104285],{},[2217,197904,197906],{"className":2219,"code":197905,"language":2221,"meta":1037,"style":1037},"interface FactorProof {\n  readonly kind: FactorKind\n  readonly mintedAt?: string   \u002F\u002F ISO-8601 timestamp, compared against freshnessMs\n  readonly payload?: unknown   \u002F\u002F method-specific, opaque to the engine\n}\n\ninterface FactorProofBundle {\n  readonly factors?: ReadonlyArray\u003CFactorProof>\n  readonly sharedDevice?: boolean\n}\n",[941,197907,197908,197917,197928,197941,197954,197958,197962,197971,197987,197998],{"__ignoreMap":1037},[2225,197909,197910,197912,197915],{"class":2227,"line":2228},[2225,197911,6994],{"class":2774},[2225,197913,197914],{"class":6342}," FactorProof",[2225,197916,2301],{"class":2235},[2225,197918,197919,197921,197923,197925],{"class":2227,"line":1041},[2225,197920,53580],{"class":2774},[2225,197922,8285],{"class":2307},[2225,197924,2311],{"class":2235},[2225,197926,197927],{"class":6342}," FactorKind\n",[2225,197929,197930,197932,197934,197936,197938],{"class":2227,"line":1038},[2225,197931,53580],{"class":2774},[2225,197933,19115],{"class":2307},[2225,197935,7011],{"class":2235},[2225,197937,6366],{"class":6342},[2225,197939,197940],{"class":2763},"   \u002F\u002F ISO-8601 timestamp, compared against freshnessMs\n",[2225,197942,197943,197945,197947,197949,197951],{"class":2227,"line":2288},[2225,197944,53580],{"class":2774},[2225,197946,19094],{"class":2307},[2225,197948,7011],{"class":2235},[2225,197950,40717],{"class":6342},[2225,197952,197953],{"class":2763},"   \u002F\u002F method-specific, opaque to the engine\n",[2225,197955,197956],{"class":2227,"line":2304},[2225,197957,7351],{"class":2235},[2225,197959,197960],{"class":2227,"line":2356},[2225,197961,2285],{"emptyLinePlaceholder":2284},[2225,197963,197964,197966,197969],{"class":2227,"line":2830},[2225,197965,6994],{"class":2774},[2225,197967,197968],{"class":6342}," FactorProofBundle",[2225,197970,2301],{"class":2235},[2225,197972,197973,197975,197977,197979,197981,197983,197985],{"class":2227,"line":2861},[2225,197974,53580],{"class":2774},[2225,197976,17534],{"class":2307},[2225,197978,7011],{"class":2235},[2225,197980,19754],{"class":6342},[2225,197982,6474],{"class":2235},[2225,197984,197900],{"class":6342},[2225,197986,7093],{"class":2235},[2225,197988,197989,197991,197994,197996],{"class":2227,"line":4246},[2225,197990,53580],{"class":2774},[2225,197992,197993],{"class":2307}," sharedDevice",[2225,197995,7011],{"class":2235},[2225,197997,11718],{"class":6342},[2225,197999,198000],{"class":2227,"line":4253},[2225,198001,7351],{"class":2235},[937,198003,198004,198006,198007,198009,198010,1707,198012,968,198014,968,198016,968,198018,1707,198021,968,198024,968,198026,968,198028,1707,198031,110643],{},[941,198005,104285],{}," is the optional last argument of every gated ",[941,198008,62],{}," method (",[941,198011,10319],{},[941,198013,10322],{},[941,198015,197106],{},[941,198017,107017],{},[941,198019,198020],{},"db.removeAuthenticator",[941,198022,198023],{},"db.updateAuthenticator",[941,198025,197101],{},[941,198027,107014],{},[941,198029,198030],{},"db.recoverUser",[941,198032,198033],{},"db.enrollUnlock",[1591,198035,198037,1261,198039],{"id":198036},"gatename-builtingatename",[941,198038,197512],{},[941,198040,198041],{},"BuiltInGateName",[937,198043,198044,198046,198047,198049,198050,198053,198054,968,198056,968,198058,1707,198060,968,198062,968,198064,968,198067,968,198069,1707,198071,968,198073,968,198076,968,198078,968,198081,1707,198083,968,198085,968,198087,968,198089,1707,198092,968,198095,968,198098,968,198100,1520],{},[941,198045,197512],{}," is either a ",[941,198048,198041],{}," or an app-defined name of the form ",[941,198051,198052],{},"`app:${string}`",".\nBuilt-in gates: ",[941,198055,15169],{},[941,198057,16586],{},[941,198059,17553],{},[941,198061,17634],{},[941,198063,17761],{},[941,198065,198066],{},"rotate-recovery",[941,198068,18250],{},[941,198070,12946],{},[941,198072,135279],{},[941,198074,198075],{},"peer-recover-user",[941,198077,16751],{},[941,198079,198080],{},"export-bundle",[941,198082,18947],{},[941,198084,19321],{},[941,198086,98678],{},[941,198088,98879],{},[941,198090,198091],{},"client-unilateral-withdraw",[941,198093,198094],{},"user-request-withdrawal",[941,198096,198097],{},"approve-user-withdrawal",[941,198099,135272],{},[941,198101,135275],{},[1591,198103,198105,1261,198107],{"id":198104},"checkgate-describegate",[941,198106,12982],{},[941,198108,197348],{},[2217,198110,198112],{"className":2219,"code":198111,"language":2221,"meta":1037,"style":1037},"function checkGate(policy: VaultPolicy, gate: GateName, context: CheckGateContext): Promise\u003Cvoid>\nfunction describeGate(\n  policy: VaultPolicy,\n  gate: GateName,\n  context: CheckGateContext,\n): Promise\u003C{ ok: true } | { ok: false; reason: PolicyDenyReason; required: GatePolicy }>\n\ninterface CheckGateContext {\n  readonly activeTier: ActiveTier         \u002F\u002F 1 | 2 | 3\n  readonly factors?: ReadonlyArray\u003CFactorProof>\n  readonly sharedDevice?: boolean\n  readonly now?: number                    \u002F\u002F override Date.now() for tests\n}\n",[941,198113,198114,198158,198166,198176,198187,198198,198243,198247,198255,198270,198286,198296,198310],{"__ignoreMap":1037},[2225,198115,198116,198118,198121,198123,198125,198127,198129,198131,198133,198135,198138,198140,198143,198145,198148,198150,198152,198154,198156],{"class":2227,"line":2228},[2225,198117,12641],{"class":2774},[2225,198119,198120],{"class":2291}," checkGate",[2225,198122,2789],{"class":2235},[2225,198124,796],{"class":7052},[2225,198126,2311],{"class":2235},[2225,198128,197475],{"class":6342},[2225,198130,2298],{"class":2235},[2225,198132,17161],{"class":7052},[2225,198134,2311],{"class":2235},[2225,198136,198137],{"class":6342}," GateName",[2225,198139,2298],{"class":2235},[2225,198141,198142],{"class":7052}," context",[2225,198144,2311],{"class":2235},[2225,198146,198147],{"class":6342}," CheckGateContext",[2225,198149,6255],{"class":2235},[2225,198151,7079],{"class":6342},[2225,198153,6474],{"class":2235},[2225,198155,7165],{"class":6342},[2225,198157,7093],{"class":2235},[2225,198159,198160,198162,198164],{"class":2227,"line":1041},[2225,198161,12641],{"class":2774},[2225,198163,197366],{"class":2291},[2225,198165,11683],{"class":2235},[2225,198167,198168,198170,198172,198174],{"class":2227,"line":1038},[2225,198169,18899],{"class":7052},[2225,198171,2311],{"class":2235},[2225,198173,197475],{"class":6342},[2225,198175,1707],{"class":2235},[2225,198177,198178,198181,198183,198185],{"class":2227,"line":2288},[2225,198179,198180],{"class":7052},"  gate",[2225,198182,2311],{"class":2235},[2225,198184,198137],{"class":6342},[2225,198186,1707],{"class":2235},[2225,198188,198189,198192,198194,198196],{"class":2227,"line":2304},[2225,198190,198191],{"class":7052},"  context",[2225,198193,2311],{"class":2235},[2225,198195,198147],{"class":6342},[2225,198197,1707],{"class":2235},[2225,198199,198200,198202,198204,198206,198208,198210,198212,198214,198216,198218,198220,198222,198224,198226,198228,198230,198233,198235,198237,198239,198241],{"class":2227,"line":2356},[2225,198201,6255],{"class":2235},[2225,198203,7079],{"class":6342},[2225,198205,17851],{"class":2235},[2225,198207,85637],{"class":2307},[2225,198209,2311],{"class":2235},[2225,198211,2322],{"class":6342},[2225,198213,2243],{"class":2235},[2225,198215,7087],{"class":2235},[2225,198217,2236],{"class":2235},[2225,198219,85637],{"class":2307},[2225,198221,2311],{"class":2235},[2225,198223,2341],{"class":6342},[2225,198225,3769],{"class":2235},[2225,198227,155427],{"class":2307},[2225,198229,2311],{"class":2235},[2225,198231,198232],{"class":6342}," PolicyDenyReason",[2225,198234,3769],{"class":2235},[2225,198236,8299],{"class":2307},[2225,198238,2311],{"class":2235},[2225,198240,197517],{"class":6342},[2225,198242,17861],{"class":2235},[2225,198244,198245],{"class":2227,"line":2830},[2225,198246,2285],{"emptyLinePlaceholder":2284},[2225,198248,198249,198251,198253],{"class":2227,"line":2861},[2225,198250,6994],{"class":2774},[2225,198252,198147],{"class":6342},[2225,198254,2301],{"class":2235},[2225,198256,198257,198259,198262,198264,198267],{"class":2227,"line":4246},[2225,198258,53580],{"class":2774},[2225,198260,198261],{"class":2307}," activeTier",[2225,198263,2311],{"class":2235},[2225,198265,198266],{"class":6342}," ActiveTier",[2225,198268,198269],{"class":2763},"         \u002F\u002F 1 | 2 | 3\n",[2225,198271,198272,198274,198276,198278,198280,198282,198284],{"class":2227,"line":4253},[2225,198273,53580],{"class":2774},[2225,198275,17534],{"class":2307},[2225,198277,7011],{"class":2235},[2225,198279,19754],{"class":6342},[2225,198281,6474],{"class":2235},[2225,198283,197900],{"class":6342},[2225,198285,7093],{"class":2235},[2225,198287,198288,198290,198292,198294],{"class":2227,"line":4259},[2225,198289,53580],{"class":2774},[2225,198291,197993],{"class":2307},[2225,198293,7011],{"class":2235},[2225,198295,11718],{"class":6342},[2225,198297,198298,198300,198303,198305,198307],{"class":2227,"line":6548},[2225,198299,53580],{"class":2774},[2225,198301,198302],{"class":2307}," now",[2225,198304,7011],{"class":2235},[2225,198306,6356],{"class":6342},[2225,198308,198309],{"class":2763},"                    \u002F\u002F override Date.now() for tests\n",[2225,198311,198312],{"class":2227,"line":6576},[2225,198313,7351],{"class":2235},[937,198315,198316,198318,198319,198321],{},[941,198317,196888],{}," is the convenience wrapper — it loads the vault's policy and\nactive tier for you and calls ",[941,198320,12982],{}," underneath.",[1591,198323,198325],{"id":198324},"presets-and-merging","Presets and merging",[1111,198327,198328,198338,198346],{},[1114,198329,198330,198332,198333,1707,198335,198337],{},[941,198331,12830],{}," — the default for personal vaults and small deployments. Off-device factors\n(TOTP \u002F email-OTP \u002F recovery) are required only where they matter most (",[941,198334,15169],{},[941,198336,18947],{},"); most gates are satisfied by a plain tier-1 unlock.",[1114,198339,198340,198342,198343,1520],{},[941,198341,12834],{}," — for regulated deployments and shared workstations. Raises the secret floor\nto 8 words, requires two distinct off-device factors for sensitive exports, and blocks exports\non a device flagged ",[941,198344,198345],{},"sharedDevice",[1114,198347,198348,198351,198352,198354,198355,198357],{},[941,198349,198350],{},"mergePolicy(base, override?)"," — merges a partial override onto a preset. Unspecified gates\ninherit from ",[941,198353,57272],{},"; gates present in ",[941,198356,12962],{}," fully replace the base entry for that gate.",[1591,198359,120265],{"id":120264},[1111,198361,198362,198381],{},[1114,198363,198364,188597,198366,198368,198369,968,198371,23423,198373,2098,198376,2878,198378,198380],{},[941,198365,13025],{},[941,198367,12982],{}," (and any gated method) on denial. Carries\n",[941,198370,2723],{},[941,198372,43668],{},[941,198374,198375],{},"PolicyDenyReason",[941,198377,45375],{},[941,198379,104535],{}," that was not met).",[1114,198382,198383,964,198385,1520],{},[941,198384,198375],{},[941,198386,198387],{},"'insufficient-tier' | 'missing-factor' | 'stale-proof' | 'disabled' | 'shared-device-blocked'",[1022,198389,68210],{"id":68209},[1111,198391,198392,198411,198423,198435,198455,198466],{},[1114,198393,198394,1324,198397,53922,198399,198401,198402,198404,198405,198407,198408,198410],{},[950,198395,198396],{},"The on-disk policy wins.",[941,198398,796],{},[941,198400,2134],{}," is only honored the first time a vault\nis created; every later ",[941,198403,2134],{}," call for the same vault reads the persisted document\nfrom ",[941,198406,12784],{},". Use ",[941,198409,104516],{}," to change it deliberately — a developer override\ncan never silently weaken what the vault already committed to.",[1114,198412,198413,198416,198417,198419,198420,198422],{},[950,198414,198415],{},"Built-in gates fail closed."," A built-in gate with no entry in ",[941,198418,12826],{}," behaves as\n",[941,198421,103519],{}," — it denies every call until you configure it.",[1114,198424,198425,198428,198429,198431,198432,198434],{},[950,198426,198427],{},"App gates fail open."," An ",[941,198430,13045],{}," gate with no entry is a no-op (always allowed) — register a\n",[941,198433,104535],{}," for it if you want enforcement.",[1114,198436,198437,198442,198443,198445,198446,198449,198450,198452,198453,1520],{},[950,198438,198439,198441],{},[941,198440,17301],{}," is \"at least as privileged\", not \"exactly\"."," Tier ",[941,198444,5437],{}," (secret) satisfies a gate\nrequiring ",[941,198447,198448],{},"minTier: 3","; a session at tier ",[941,198451,5512],{}," (PIN-unlocked) does not satisfy a gate requiring\n",[941,198454,17160],{},[1114,198456,198457,198462,198463,198465],{},[950,198458,198459,198461],{},[941,198460,12905],{}," replaces a gate wholesale."," Overriding one field of a preset's gate means\nre-specifying the whole ",[941,198464,104535],{}," for that gate, not patching a single field.",[1114,198467,198468,198473,198474,964,198477,198480,198481,1520],{},[950,198469,198470,198472],{},[941,198471,150565],{}," never blocks by itself"," unless explicitly set to ",[941,198475,198476],{},"'block'",[941,198478,198479],{},"sharedDevice: 'warn'"," is\npurely informational and never raises ",[941,198482,13025],{},[1022,198484,2621],{"id":2620},[1111,198486,198487,198502,198508,198514],{},[1114,198488,198489,198491,198492,1625,198494,1625,198496,198498,198499,198501],{},[1007,198490,844],{"href":129031}," — the tier model (",[941,198493,5437],{},[941,198495,5470],{},[941,198497,5512],{},") that ",[941,198500,17301],{}," checks against.",[1114,198503,198504,198507],{},[1007,198505,840],{"href":198506},".\u002Fsession"," — session lifecycle and re-authentication built on top of gate checks.",[1114,198509,198510,198513],{},[1007,198511,868],{"href":198512},".\u002Fteam"," — grant\u002Frevoke\u002Fupdate-user flows gated by this policy DSL.",[1114,198515,198516,1368,198518],{},[1007,198517,61743],{"href":598},[1007,198519,1019],{"href":1018},[2645,198521,198522],{},"html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sfNiH, html code.shiki .sfNiH{--shiki-light:#FF5370;--shiki-default:#FF9CAC;--shiki-dark:#FF9CAC}html pre.shiki code .sHdIc, html code.shiki .sHdIc{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#EEFFFF;--shiki-default-font-style:italic;--shiki-dark:#BABED8;--shiki-dark-font-style:italic}",{"title":1037,"searchDepth":1038,"depth":1038,"links":198524},[198525,198526,198527,198532,198546,198547],{"id":29856,"depth":1041,"text":29857},{"id":58393,"depth":1041,"text":58394},{"id":29892,"depth":1041,"text":29893,"children":198528},[198529,198530,198531],{"id":196882,"depth":1038,"text":196883},{"id":197202,"depth":1038,"text":197203},{"id":197342,"depth":1038,"text":197343},{"id":102888,"depth":1041,"text":102889,"children":198533},[198534,198535,198536,198537,198538,198540,198542,198544,198545],{"id":197461,"depth":1038,"text":12870},{"id":197535,"depth":1038,"text":104535},{"id":197707,"depth":1038,"text":197586},{"id":197773,"depth":1038,"text":197738},{"id":197897,"depth":1038,"text":198539},"FactorProof \u002F FactorProofBundle",{"id":198036,"depth":1038,"text":198541},"GateName \u002F BuiltInGateName",{"id":198104,"depth":1038,"text":198543},"checkGate \u002F describeGate",{"id":198324,"depth":1038,"text":198325},{"id":120264,"depth":1038,"text":120265},{"id":68209,"depth":1041,"text":68210},{"id":2620,"depth":1041,"text":2621},"Gate sensitive vault operations behind a minimum session tier and optional extra-factor proofs, via the VaultPolicy \u002F GatePolicy config types.",{},{"title":796,"description":198548},"_NXcpkJ61NbiAT0bqjVuf1Q_hFC8egpWKJP2NbiJMr4",{"id":198553,"title":800,"access":12,"apiToc":929,"body":198554,"category":1042,"description":199364,"extension":1044,"meta":199365,"navigation":1042,"nest":1047,"path":801,"seo":199366,"sinceVersion":1042,"status":1049,"stem":802,"subtitle":1042,"tier":1050,"tocExternal":1042,"__hash__":199367},"docs\u002Fservices\u002Fportability.md",{"type":931,"value":198555,"toc":199349},[198556,198558,198561,198582,198584,198611,198629,198638,198640,198657,198659,198686,198688,198704,198875,198883,198887,198894,198988,199002,199006,199012,199071,199082,199084,199103,199109,199115,199182,199205,199207,199232,199247,199250,199262,199286,199293,199295,199308,199310,199347],[934,198557,800],{"id":800},[46871,198559],{":rows":198560},"[{\"label\":\"Subpath\",\"value\":\"`@noy-db\u002Fhub\u002Fportability`\"},{\"label\":\"Factory\",\"value\":\"`withPortability()`\"},{\"label\":\"Layer\",\"value\":\"`with-audit`\"}]",[7646,198562,198563],{"color":11278,"icon":369},[937,198564,198565,198568,198569,198571,198572,198574,198575,198577,198578,1520],{},[950,198566,198567],{},"Two halves of one service."," Export and withdrawal are the two workflows of the single ",[941,198570,21604],{}," opt-in. This page covers ",[950,198573,8080],{}," — taking a non-destructive portable copy of your data. To ",[1092,198576,39213],{}," your data (one-party self-serve, or a two-party request → approve ceremony), see ",[950,198579,198580],{},[1007,198581,900],{"href":901},[1022,198583,29857],{"id":29856},[937,198585,198586,198587,198589,198590,198592,198593,968,198595,968,198597,968,198599,968,198601,968,198603,198605,198606,198608,198609,957],{},"A noy-db ",[950,198588,2694],{}," is an encrypted document store that several people can share: each\nmember unlocks it with their own secret-protected ",[950,198591,2705],{},", which holds a role\n(",[941,198594,9898],{},[941,198596,9901],{},[941,198598,9907],{},[941,198600,9913],{},[941,198602,9910],{},[941,198604,9904],{},") and the per-collection\n",[950,198607,104611],{}," that role was granted (see ",[1007,198610,868],{"href":198512},[937,198612,960,198613,198615,198616,198618,198619,198622,198623,198625,198626,198628],{},[941,198614,21604],{}," service adds a data-sovereignty surface to ",[941,198617,21609],{},": a\n",[950,198620,198621],{},"non-owner"," can take a portable copy of ",[950,198624,17470],{}," accessible data (this page), or\nwithdraw it entirely (the ",[1007,198627,900],{"href":901}," page). \"Their own\" is\nenforced cryptographically, not by a runtime allowlist — the API operates only on\ncollections whose DEKs the caller's keyring holds, so a record outside the caller's keys\nis undecryptable and can never enter the export.",[937,198630,198631,198632,198634,198635,198637],{},"Export — ",[941,198633,21613],{}," — is the ",[950,198636,190140],{}," half: it always\nsucceeds (there is no policy gate a firm could use to deny a member a copy of data they\ncan already decrypt) and is audited.",[1022,198639,111660],{"id":111659},[1111,198641,198642,198649,198652],{},[1114,198643,198644,198645,198648],{},"GDPR-style ",[950,198646,198647],{},"data portability"," (Art. 20) for the clients of a firm-run vault — a\nmember takes a re-keyed, independently-openable copy of their records with them.",[1114,198650,198651],{},"Personal backups: a member snapshots the data they can decrypt, under their own\nsecret, without touching the live vault.",[1114,198653,198654,198655,1520],{},"The first, always-safe step of an offboarding flow — the destructive follow-through\n(erase \u002F hand-over) lives on ",[1007,198656,900],{"href":901},[1022,198658,70093],{"id":70092},[1111,198660,198661,198670,198678],{},[1114,198662,198663,198665,198666,107496,198668,1520],{},[950,198664,107493],{}," for the read-only export surface — ",[941,198667,21604],{},[941,198669,21609],{},[1114,198671,198672,1324,198675,198677],{},[1092,198673,198674],{},"In a multi-user vault:",[941,198676,10312],{}," provides the roles and grants the export scoping reads.",[1114,198679,198680,198683,198684,1520],{},[1092,198681,198682],{},"For destructive self-serve withdrawal:"," additional gates apply — see ",[1007,198685,900],{"href":901},[1022,198687,99864],{"id":99863},[937,198689,124907,198690,10313,198692,198694,198695,198697,198698,198700,198701,198703],{},[941,198691,83182],{},[941,198693,2134],{},". Without it, every\n",[941,198696,21609],{}," portability method (export ",[950,198699,14128],{}," withdrawal) throws\n",[941,198702,21786],{},", and the export\u002Fwithdraw\u002Frequest engines stay out of your\nbundle (they are loaded on demand behind the opt-in).",[2217,198705,198707],{"className":2219,"code":198706,"language":2221,"meta":1037,"style":1037},"import { createNoydb } from '@noy-db\u002Fhub'\nimport { withTeam } from '@noy-db\u002Fhub\u002Fteam'\nimport { withPortability } from '@noy-db\u002Fhub\u002Fportability'\n\nconst db = await createNoydb({\n  user: 'client1',\n  secret: 'client-pw-long-enough',\n  teamStrategy: withTeam(),                 \u002F\u002F multi-user vault: roles + grants\n  portabilityStrategy: withPortability(),   \u002F\u002F enables vault.user.* export + withdrawal\n})\nconst vault = await db.openVault('acme')\n",[941,198708,198709,198727,198745,198763,198767,198783,198798,198813,198828,198843,198849],{"__ignoreMap":1037},[2225,198710,198711,198713,198715,198717,198719,198721,198723,198725],{"class":2227,"line":2228},[2225,198712,2232],{"class":2231},[2225,198714,2236],{"class":2235},[2225,198716,2744],{"class":2239},[2225,198718,2243],{"class":2235},[2225,198720,2246],{"class":2231},[2225,198722,2249],{"class":2235},[2225,198724,1086],{"class":2252},[2225,198726,2256],{"class":2235},[2225,198728,198729,198731,198733,198735,198737,198739,198741,198743],{"class":2227,"line":1041},[2225,198730,2232],{"class":2231},[2225,198732,2236],{"class":2235},[2225,198734,10373],{"class":2239},[2225,198736,2243],{"class":2235},[2225,198738,2246],{"class":2231},[2225,198740,2249],{"class":2235},[2225,198742,4680],{"class":2252},[2225,198744,2256],{"class":2235},[2225,198746,198747,198749,198751,198753,198755,198757,198759,198761],{"class":2227,"line":1038},[2225,198748,2232],{"class":2231},[2225,198750,2236],{"class":2235},[2225,198752,83981],{"class":2239},[2225,198754,2243],{"class":2235},[2225,198756,2246],{"class":2231},[2225,198758,2249],{"class":2235},[2225,198760,73238],{"class":2252},[2225,198762,2256],{"class":2235},[2225,198764,198765],{"class":2227,"line":2288},[2225,198766,2285],{"emptyLinePlaceholder":2284},[2225,198768,198769,198771,198773,198775,198777,198779,198781],{"class":2227,"line":2304},[2225,198770,2775],{"class":2774},[2225,198772,2778],{"class":2239},[2225,198774,2781],{"class":2235},[2225,198776,2784],{"class":2231},[2225,198778,2744],{"class":2291},[2225,198780,2789],{"class":2239},[2225,198782,4200],{"class":2235},[2225,198784,198785,198787,198789,198791,198794,198796],{"class":2227,"line":2356},[2225,198786,4205],{"class":2307},[2225,198788,2311],{"class":2235},[2225,198790,2249],{"class":2235},[2225,198792,198793],{"class":2252},"client1",[2225,198795,2805],{"class":2235},[2225,198797,1707],{"class":2235},[2225,198799,198800,198802,198804,198806,198809,198811],{"class":2227,"line":2830},[2225,198801,10423],{"class":2307},[2225,198803,2311],{"class":2235},[2225,198805,2249],{"class":2235},[2225,198807,198808],{"class":2252},"client-pw-long-enough",[2225,198810,2805],{"class":2235},[2225,198812,1707],{"class":2235},[2225,198814,198815,198817,198819,198821,198823,198825],{"class":2227,"line":2861},[2225,198816,10439],{"class":2307},[2225,198818,2311],{"class":2235},[2225,198820,10373],{"class":2291},[2225,198822,4228],{"class":2239},[2225,198824,2298],{"class":2235},[2225,198826,198827],{"class":2763},"                 \u002F\u002F multi-user vault: roles + grants\n",[2225,198829,198830,198832,198834,198836,198838,198840],{"class":2227,"line":4246},[2225,198831,84049],{"class":2307},[2225,198833,2311],{"class":2235},[2225,198835,83981],{"class":2291},[2225,198837,4228],{"class":2239},[2225,198839,2298],{"class":2235},[2225,198841,198842],{"class":2763},"   \u002F\u002F enables vault.user.* export + withdrawal\n",[2225,198844,198845,198847],{"class":2227,"line":4253},[2225,198846,2359],{"class":2235},[2225,198848,2362],{"class":2239},[2225,198850,198851,198853,198855,198857,198859,198861,198863,198865,198867,198869,198871,198873],{"class":2227,"line":4259},[2225,198852,2775],{"class":2774},[2225,198854,2835],{"class":2239},[2225,198856,2781],{"class":2235},[2225,198858,2784],{"class":2231},[2225,198860,2842],{"class":2239},[2225,198862,1520],{"class":2235},[2225,198864,2847],{"class":2291},[2225,198866,2789],{"class":2239},[2225,198868,2805],{"class":2235},[2225,198870,6450],{"class":2252},[2225,198872,2805],{"class":2235},[2225,198874,2362],{"class":2239},[937,198876,198877,198878,198882],{},"This one opt-in enables both halves of the service. The multi-user scenarios assume a\nvault provisioned with ",[1007,198879,198880],{"href":198512},[941,198881,3197],{},", where the firm (owner) has granted the\ncaller a keyring.",[1022,198884,198886],{"id":198885},"export-a-portable-copy","Export a portable copy",[937,198888,198889,198890,198893],{},"Non-destructive and ",[950,198891,198892],{},"always allowed"," — there is no policy gate a firm could use to\ndeny a member a copy of the data they can already decrypt. The call is audited.",[2217,198895,198897],{"className":2219,"code":198896,"language":2221,"meta":1037,"style":1037},"const bytes = await vault.user.exportMyAccessibleData({\n  reKey: { secret: 'my-portable-copy-secret' },\n  scope: { collections: ['invoices'] },     \u002F\u002F optional narrowing\n})\n\u002F\u002F → Uint8Array — a standard `.noydb` bundle, independently openable\n\u002F\u002F   with the reKey secret (see the bundle service)\n",[941,198898,198899,198923,198944,198972,198978,198983],{"__ignoreMap":1037},[2225,198900,198901,198903,198905,198907,198909,198911,198913,198915,198917,198919,198921],{"class":2227,"line":2228},[2225,198902,2775],{"class":2774},[2225,198904,32469],{"class":2239},[2225,198906,2781],{"class":2235},[2225,198908,2784],{"class":2231},[2225,198910,6466],{"class":2239},[2225,198912,1520],{"class":2235},[2225,198914,16225],{"class":2239},[2225,198916,1520],{"class":2235},[2225,198918,84115],{"class":2291},[2225,198920,2789],{"class":2239},[2225,198922,4200],{"class":2235},[2225,198924,198925,198927,198929,198931,198933,198935,198937,198940,198942],{"class":2227,"line":1041},[2225,198926,84124],{"class":2307},[2225,198928,2311],{"class":2235},[2225,198930,2236],{"class":2235},[2225,198932,2810],{"class":2307},[2225,198934,2311],{"class":2235},[2225,198936,2249],{"class":2235},[2225,198938,198939],{"class":2252},"my-portable-copy-secret",[2225,198941,2805],{"class":2235},[2225,198943,2353],{"class":2235},[2225,198945,198946,198949,198951,198953,198955,198957,198959,198961,198963,198965,198967,198969],{"class":2227,"line":1038},[2225,198947,198948],{"class":2307},"  scope",[2225,198950,2311],{"class":2235},[2225,198952,2236],{"class":2235},[2225,198954,22066],{"class":2307},[2225,198956,2311],{"class":2235},[2225,198958,8667],{"class":2239},[2225,198960,2805],{"class":2235},[2225,198962,6487],{"class":2252},[2225,198964,2805],{"class":2235},[2225,198966,8890],{"class":2239},[2225,198968,33127],{"class":2235},[2225,198970,198971],{"class":2763},"     \u002F\u002F optional narrowing\n",[2225,198973,198974,198976],{"class":2227,"line":2288},[2225,198975,2359],{"class":2235},[2225,198977,2362],{"class":2239},[2225,198979,198980],{"class":2227,"line":2304},[2225,198981,198982],{"class":2763},"\u002F\u002F → Uint8Array — a standard `.noydb` bundle, independently openable\n",[2225,198984,198985],{"class":2227,"line":2356},[2225,198986,198987],{"class":2763},"\u002F\u002F   with the reKey secret (see the bundle service)\n",[1111,198989,198990,198997],{},[1114,198991,198992,198993,198996],{},"Omit ",[941,198994,198995],{},"reKey"," to keep the bundle under the source keyring (a personal backup).",[1114,198998,198992,198999,199001],{},[941,199000,13544],{}," to export everything the caller can decrypt.",[1022,199003,199005],{"id":199004},"who-can-do-what","Who can do what",[937,199007,199008,199009,199011],{},"Export is available to ",[950,199010,2119],{}," role, always scoped to what the caller can decrypt:",[1338,199013,199014,199025],{},[1341,199015,199016],{},[1344,199017,199018,199020],{},[1347,199019,2970],{},[1347,199021,199022,199024],{},[941,199023,84115],{}," scope",[1357,199026,199027,199038,199047,199055,199063],{},[1344,199028,199029,199035],{},[1362,199030,199031,1261,199033],{},[941,199032,9898],{},[941,199034,9901],{},[1362,199036,199037],{},"all collections",[1344,199039,199040,199044],{},[1362,199041,199042],{},[941,199043,9907],{},[1362,199045,199046],{},"granted collections",[1344,199048,199049,199053],{},[1362,199050,199051],{},[941,199052,9913],{},[1362,199054,199046],{},[1344,199056,199057,199061],{},[1362,199058,199059],{},[941,199060,9910],{},[1362,199062,199037],{},[1344,199064,199065,199069],{},[1362,199066,199067],{},[941,199068,9904],{},[1362,199070,199037],{},[937,199072,199073,199074,199077,199078,1520],{},"Export is non-destructive, so there is no role that is denied it — a member exporting a\ncopy of data they can already read reveals nothing new. The ",[950,199075,199076],{},"destructive"," side (who\nmay delete vs. who must request) is governed separately on\n",[1007,199079,199081],{"href":199080},"\u002Fservices\u002Fwithdrawal#who-can-do-what","withdrawal → Who can do what",[1022,199083,102889],{"id":102888},[937,199085,960,199086,199088,199089,199091,199092,199095,199096,199099,199100,199102],{},[941,199087,73238],{}," subpath exports ",[941,199090,21604],{},", the\n",[941,199093,199094],{},"PortabilityStrategy"," type, the ",[941,199097,199098],{},"NO_PORTABILITY"," no-op default,\n",[941,199101,21786],{},", and every option\u002Fresult type across both halves of the\nservice.",[1591,199104,199106],{"id":199105},"vaultuserexportmyaccessibledataopts",[941,199107,199108],{},"vault.user.exportMyAccessibleData(opts?)",[937,199110,199111,199112,199114],{},"Re-keyed, access-scoped ",[941,199113,1402],{}," bundle of the caller's accessible data.",[1338,199116,199117,199129],{},[1341,199118,199119],{},[1344,199120,199121,199123,199125,199127],{},[1347,199122,101060],{},[1347,199124,20194],{},[1347,199126,20197],{},[1347,199128,20200],{},[1357,199130,199131,199148,199165],{},[1344,199132,199133,199137,199142,199145],{},[1362,199134,199135],{},[941,199136,198995],{},[1362,199138,199139],{},[941,199140,199141],{},"{ secret: string }",[1362,199143,199144],{},"inherit source keyring",[1362,199146,199147],{},"Make the bundle independently openable by a new owner",[1344,199149,199150,199154,199159,199162],{},[1362,199151,199152],{},[941,199153,13544],{},[1362,199155,199156],{},[941,199157,199158],{},"{ collections?: string[] }",[1362,199160,199161],{},"all accessible",[1362,199163,199164],{},"Narrow to a subset of the caller's collections",[1344,199166,199167,199171,199175,199179],{},[1362,199168,199169],{},[941,199170,110539],{},[1362,199172,199173],{},[941,199174,116072],{},[1362,199176,199177],{},[941,199178,116077],{},[1362,199180,199181],{},"Bundle compression",[937,199183,199184,199185,968,199188,1707,199191,968,199194,968,199197,199200,199201,1520],{},"The five withdrawal methods (",[941,199186,199187],{},"unilateralWithdrawal",[941,199189,199190],{},"requestWithdrawal",[941,199192,199193],{},"listWithdrawalRequests",[941,199195,199196],{},"approveWithdrawal",[941,199198,199199],{},"rejectWithdrawal",") are documented on\n",[1007,199202,199204],{"href":199203},"\u002Fservices\u002Fwithdrawal#api-reference","withdrawal → API reference",[1022,199206,120265],{"id":120264},[1338,199208,199209,199217],{},[1341,199210,199211],{},[1344,199212,199213,199215],{},[1347,199214,12126],{},[1347,199216,120276],{},[1357,199218,199219],{},[1344,199220,199221,199225],{},[1362,199222,199223],{},[941,199224,21786],{},[1362,199226,100279,199227,199229,199230],{},[941,199228,21609],{}," method called without ",[941,199231,83182],{},[937,199233,199234,199235,968,199237,968,199239,199242,199243,1520],{},"Export has no other failure modes — it cannot be policy-denied. The withdrawal-specific\nerrors (",[941,199236,11361],{},[941,199238,13025],{},[941,199240,199241],{},"WithdrawalRequestError",") are covered on\n",[1007,199244,199246],{"href":199245},"\u002Fservices\u002Fwithdrawal#errors","withdrawal → Errors",[1022,199248,199249],{"id":119664},"Audit",[937,199251,199252,199253,199256,199257,199259,199260,2311],{},"An export appends an ",[941,199254,199255],{},"op: 'lifecycle'"," entry to the tamper-evident ledger when the vault\nhas ",[1007,199258,697],{"href":140989}," enabled (without it, auditing is a silent no-op). The entry\nparticipates in the ledger's hash chain but carries no data payload — only a structured\n",[941,199261,43668],{},[1338,199263,199264,199275],{},[1341,199265,199266],{},[1344,199267,199268,199271],{},[1347,199269,199270],{},"Operation",[1347,199272,199273],{},[941,199274,43668],{},[1357,199276,199277],{},[1344,199278,199279,199281],{},[1362,199280,72632],{},[1362,199282,199283],{},[941,199284,199285],{},"user-export:\u003CuserId>",[937,199287,199288,199289,1520],{},"The withdrawal operations write their own ledger entries — see\n",[1007,199290,199292],{"href":199291},"\u002Fservices\u002Fwithdrawal#audit","withdrawal → Audit",[1022,199294,68210],{"id":68209},[1111,199296,199297,199300],{},[1114,199298,199299],{},"Export is scoped to the caller's keys: a record whose DEK the caller's keyring does not\nhold is undecryptable and never enters the bundle. There is no way to export beyond\nyour own access.",[1114,199301,199302,199303,199307],{},"Audit entries are only written when a history\u002Fledger strategy is configured; enable\n",[1007,199304,199305],{"href":140989},[941,199306,6719],{}," for the compliance trail.",[1022,199309,2621],{"id":2620},[1111,199311,199312,199320,199329,199334,199339],{},[1114,199313,199314,199316,199317,199319],{},[1007,199315,900],{"href":901}," — the destructive other half of ",[941,199318,21604],{},": one-party and two-party removal, source dispositions, and the policy gates.",[1114,199321,199322,1169,199324,195438,199326,199328],{},[1007,199323,629],{"href":195435},[941,199325,1402],{},[941,199327,116870],{}," to open an exported bundle.",[1114,199330,199331,199333],{},[1007,199332,868],{"href":198512}," — roles, grants, and per-collection permissions.",[1114,199335,199336,199338],{},[1007,199337,697],{"href":140989}," — the tamper-evident ledger the audit entry lands in.",[1114,199340,101581,199341,1520],{},[1007,199342,199344],{"href":94180,"rel":199343},[3371],[941,199345,199346],{},"120-client-portability-withdrawal",[2645,199348,30448],{},{"title":1037,"searchDepth":1038,"depth":1038,"links":199350},[199351,199352,199353,199354,199355,199356,199357,199360,199361,199362,199363],{"id":29856,"depth":1041,"text":29857},{"id":111659,"depth":1041,"text":111660},{"id":70092,"depth":1041,"text":70093},{"id":99863,"depth":1041,"text":99864},{"id":198885,"depth":1041,"text":198886},{"id":199004,"depth":1041,"text":199005},{"id":102888,"depth":1041,"text":102889,"children":199358},[199359],{"id":199105,"depth":1038,"text":199108},{"id":120264,"depth":1041,"text":120265},{"id":119664,"depth":1041,"text":199249},{"id":68209,"depth":1041,"text":68210},{"id":2620,"depth":1041,"text":2621},"Export half of `withPortability()`: a non-owner takes a portable, re-keyed .noydb copy of what their keyring can decrypt — non-destructive, always allowed.",{},{"title":800,"description":199364},"8KoT3FKKg1sQElSlEPPiwhdNWBLnd0J7fn6PByBo5Bs",{"id":199369,"title":804,"access":12,"apiToc":929,"body":199370,"category":1042,"description":202443,"extension":1044,"meta":202444,"navigation":1042,"nest":1047,"path":805,"seo":202445,"sinceVersion":1042,"status":1049,"stem":806,"subtitle":1042,"tier":1050,"tocExternal":1042,"__hash__":202446},"docs\u002Fservices\u002Freduce.md",{"type":931,"value":199371,"toc":202418},[199372,199374,199377,199379,199411,199425,199427,199440,199442,199578,199580,199586,200003,200007,200018,200143,200156,200160,200168,200285,200387,200391,200397,200513,200670,200684,200688,200696,200796,200801,200803,200812,200817,200829,200842,200846,201027,201034,201120,201149,201154,201225,201247,201339,201345,201359,201365,201444,201454,201486,201497,201502,201583,201604,201610,201615,201677,201853,201859,201885,202106,202110,202134,202136,202194,202196,202235,202237,202351,202353,202415],[934,199373,804],{"id":804},[46871,199375],{":rows":199376},"[{\"label\":\"Subpath\",\"value\":\"`@noy-db\u002Fhub\u002Freduce`\"},{\"label\":\"Factory\",\"value\":\"`withReduce()`\"},{\"label\":\"Layer\",\"value\":\"`with-lookup`\"}]",[1022,199378,29857],{"id":29856},[937,199380,132458,199381,199383,199384,968,199386,968,199388,968,199390,968,199392,199394,199395,199397,199398,199402,199403,1625,199405,199407,199408,199410],{},[950,199382,132461],{}," — the store only ever sees ciphertext. The reduce service adds summary computation to that query layer: ",[941,199385,3236],{},[941,199387,28196],{},[941,199389,28201],{},[941,199391,28204],{},[941,199393,28207],{}," reducers and ",[941,199396,27496],{}," bucketing on ",[1007,199399,199400],{"href":83069},[941,199401,24715],{},", computed in-memory over the records that match your ",[941,199404,3016],{},[941,199406,8686],{}," clauses. For collections too large for the in-memory path, the streaming ",[941,199409,61811],{}," terminal reduces record-by-record with memory bound by the number of reducers, not the number of records.",[937,199412,199413,199414,199417,199418,1625,199421,199424],{},"Typical uses: per-period totals (",[941,199415,199416],{},"sum('amount')"," grouped by month), per-segment averages, status counts, and multi-reducer rollups in one pass. The service is a tree-shake seam — when you don't opt in, its ~886 lines of ",[941,199419,199420],{},"Reduction",[941,199422,199423],{},"GroupedQuery"," machinery never reach your bundle.",[1022,199426,70093],{"id":70092},[1111,199428,199429],{},[1114,199430,199431,964,199433,107496,199435,997,199437,199439],{},[950,199432,107493],{},[941,199434,21926],{},[941,199436,21931],{},[941,199438,21934],{}," on every collection query.",[1022,199441,99864],{"id":99863},[2217,199443,199444],{"className":2219,"code":132524,"language":2221,"meta":1037,"style":1037},[941,199445,199446,199464,199482,199500,199504,199520,199532,199546,199560,199572],{"__ignoreMap":1037},[2225,199447,199448,199450,199452,199454,199456,199458,199460,199462],{"class":2227,"line":2228},[2225,199449,2232],{"class":2231},[2225,199451,2236],{"class":2235},[2225,199453,2744],{"class":2239},[2225,199455,2243],{"class":2235},[2225,199457,2246],{"class":2231},[2225,199459,2249],{"class":2235},[2225,199461,1086],{"class":2252},[2225,199463,2256],{"class":2235},[2225,199465,199466,199468,199470,199472,199474,199476,199478,199480],{"class":2227,"line":1041},[2225,199467,2232],{"class":2231},[2225,199469,2236],{"class":2235},[2225,199471,27293],{"class":2239},[2225,199473,2243],{"class":2235},[2225,199475,2246],{"class":2231},[2225,199477,2249],{"class":2235},[2225,199479,27257],{"class":2252},[2225,199481,2256],{"class":2235},[2225,199483,199484,199486,199488,199490,199492,199494,199496,199498],{"class":2227,"line":1038},[2225,199485,2232],{"class":2231},[2225,199487,2236],{"class":2235},[2225,199489,68576],{"class":2239},[2225,199491,2243],{"class":2235},[2225,199493,2246],{"class":2231},[2225,199495,2249],{"class":2235},[2225,199497,9638],{"class":2252},[2225,199499,2256],{"class":2235},[2225,199501,199502],{"class":2227,"line":2288},[2225,199503,2285],{"emptyLinePlaceholder":2284},[2225,199505,199506,199508,199510,199512,199514,199516,199518],{"class":2227,"line":2304},[2225,199507,2775],{"class":2774},[2225,199509,2778],{"class":2239},[2225,199511,2781],{"class":2235},[2225,199513,2784],{"class":2231},[2225,199515,2744],{"class":2291},[2225,199517,2789],{"class":2239},[2225,199519,4200],{"class":2235},[2225,199521,199522,199524,199526,199528,199530],{"class":2227,"line":2356},[2225,199523,15608],{"class":2307},[2225,199525,2311],{"class":2235},[2225,199527,68576],{"class":2291},[2225,199529,4228],{"class":2239},[2225,199531,1707],{"class":2235},[2225,199533,199534,199536,199538,199540,199542,199544],{"class":2227,"line":2830},[2225,199535,4205],{"class":2307},[2225,199537,2311],{"class":2235},[2225,199539,2249],{"class":2235},[2225,199541,6408],{"class":2252},[2225,199543,2805],{"class":2235},[2225,199545,1707],{"class":2235},[2225,199547,199548,199550,199552,199554,199556,199558],{"class":2227,"line":2861},[2225,199549,10423],{"class":2307},[2225,199551,2311],{"class":2235},[2225,199553,2249],{"class":2235},[2225,199555,6421],{"class":2252},[2225,199557,2805],{"class":2235},[2225,199559,1707],{"class":2235},[2225,199561,199562,199564,199566,199568,199570],{"class":2227,"line":4246},[2225,199563,27365],{"class":2307},[2225,199565,2311],{"class":2235},[2225,199567,27293],{"class":2291},[2225,199569,4228],{"class":2239},[2225,199571,1707],{"class":2235},[2225,199573,199574,199576],{"class":2227,"line":4253},[2225,199575,2359],{"class":2235},[2225,199577,2362],{"class":2239},[1022,199579,29893],{"id":29892},[937,199581,199582,199583,199585],{},"The flows below share this setup — a ",[1007,199584,2694],{"href":58262}," with a few invoices:",[2217,199587,199589],{"className":2219,"code":199588,"language":2221,"meta":1037,"style":1037},"import { count, sum, avg } from '@noy-db\u002Fhub\u002Freduce'\n\ntype Invoice = { clientId: string; period: string; status: string; amount: number }\n\nconst vault = await db.openVault('acme')\nconst invoices = vault.collection\u003CInvoice>('invoices')\n\nawait invoices.put('inv-1', { clientId: 'c1', period: '2026-05', status: 'paid', amount: 100 })\nawait invoices.put('inv-2', { clientId: 'c1', period: '2026-06', status: 'paid', amount: 250 })\nawait invoices.put('inv-3', { clientId: 'c2', period: '2026-05', status: 'paid', amount: 850 })\nawait invoices.put('inv-4', { clientId: 'c2', period: '2026-05', status: 'open', amount: 5000 })\n",[941,199590,199591,199617,199621,199663,199667,199693,199723,199727,199796,199864,199933],{"__ignoreMap":1037},[2225,199592,199593,199595,199597,199599,199601,199603,199605,199607,199609,199611,199613,199615],{"class":2227,"line":2228},[2225,199594,2232],{"class":2231},[2225,199596,2236],{"class":2235},[2225,199598,19270],{"class":2239},[2225,199600,2298],{"class":2235},[2225,199602,27298],{"class":2239},[2225,199604,2298],{"class":2235},[2225,199606,64004],{"class":2239},[2225,199608,2243],{"class":2235},[2225,199610,2246],{"class":2231},[2225,199612,2249],{"class":2235},[2225,199614,27257],{"class":2252},[2225,199616,2256],{"class":2235},[2225,199618,199619],{"class":2227,"line":1041},[2225,199620,2285],{"emptyLinePlaceholder":2284},[2225,199622,199623,199625,199627,199629,199631,199633,199635,199637,199639,199641,199643,199645,199647,199649,199651,199653,199655,199657,199659,199661],{"class":2227,"line":1038},[2225,199624,6339],{"class":2774},[2225,199626,6343],{"class":6342},[2225,199628,6346],{"class":2235},[2225,199630,2236],{"class":2235},[2225,199632,62649],{"class":2307},[2225,199634,2311],{"class":2235},[2225,199636,6366],{"class":6342},[2225,199638,3769],{"class":2235},[2225,199640,133638],{"class":2307},[2225,199642,2311],{"class":2235},[2225,199644,6366],{"class":6342},[2225,199646,3769],{"class":2235},[2225,199648,6361],{"class":2307},[2225,199650,2311],{"class":2235},[2225,199652,6366],{"class":6342},[2225,199654,3769],{"class":2235},[2225,199656,6351],{"class":2307},[2225,199658,2311],{"class":2235},[2225,199660,6356],{"class":6342},[2225,199662,6369],{"class":2235},[2225,199664,199665],{"class":2227,"line":2288},[2225,199666,2285],{"emptyLinePlaceholder":2284},[2225,199668,199669,199671,199673,199675,199677,199679,199681,199683,199685,199687,199689,199691],{"class":2227,"line":2304},[2225,199670,2775],{"class":2774},[2225,199672,2835],{"class":2239},[2225,199674,2781],{"class":2235},[2225,199676,2784],{"class":2231},[2225,199678,2842],{"class":2239},[2225,199680,1520],{"class":2235},[2225,199682,2847],{"class":2291},[2225,199684,2789],{"class":2239},[2225,199686,2805],{"class":2235},[2225,199688,6450],{"class":2252},[2225,199690,2805],{"class":2235},[2225,199692,2362],{"class":2239},[2225,199694,199695,199697,199699,199701,199703,199705,199707,199709,199711,199713,199715,199717,199719,199721],{"class":2227,"line":2356},[2225,199696,2775],{"class":2774},[2225,199698,6461],{"class":2239},[2225,199700,2781],{"class":2235},[2225,199702,6466],{"class":2239},[2225,199704,1520],{"class":2235},[2225,199706,6471],{"class":2291},[2225,199708,6474],{"class":2235},[2225,199710,6477],{"class":6342},[2225,199712,6480],{"class":2235},[2225,199714,2789],{"class":2239},[2225,199716,2805],{"class":2235},[2225,199718,6487],{"class":2252},[2225,199720,2805],{"class":2235},[2225,199722,2362],{"class":2239},[2225,199724,199725],{"class":2227,"line":2830},[2225,199726,2285],{"emptyLinePlaceholder":2284},[2225,199728,199729,199731,199733,199735,199737,199739,199741,199743,199745,199747,199749,199751,199753,199755,199757,199759,199761,199763,199765,199767,199770,199772,199774,199776,199778,199780,199782,199784,199786,199788,199790,199792,199794],{"class":2227,"line":2861},[2225,199730,6500],{"class":2231},[2225,199732,6503],{"class":2239},[2225,199734,1520],{"class":2235},[2225,199736,3161],{"class":2291},[2225,199738,2789],{"class":2239},[2225,199740,2805],{"class":2235},[2225,199742,6514],{"class":2252},[2225,199744,2805],{"class":2235},[2225,199746,2298],{"class":2235},[2225,199748,2236],{"class":2235},[2225,199750,62649],{"class":2307},[2225,199752,2311],{"class":2235},[2225,199754,2249],{"class":2235},[2225,199756,62700],{"class":2252},[2225,199758,2805],{"class":2235},[2225,199760,2298],{"class":2235},[2225,199762,133638],{"class":2307},[2225,199764,2311],{"class":2235},[2225,199766,2249],{"class":2235},[2225,199768,199769],{"class":2252},"2026-05",[2225,199771,2805],{"class":2235},[2225,199773,2298],{"class":2235},[2225,199775,6361],{"class":2307},[2225,199777,2311],{"class":2235},[2225,199779,2249],{"class":2235},[2225,199781,6539],{"class":2252},[2225,199783,2805],{"class":2235},[2225,199785,2298],{"class":2235},[2225,199787,6351],{"class":2307},[2225,199789,2311],{"class":2235},[2225,199791,6528],{"class":6527},[2225,199793,2243],{"class":2235},[2225,199795,2362],{"class":2239},[2225,199797,199798,199800,199802,199804,199806,199808,199810,199812,199814,199816,199818,199820,199822,199824,199826,199828,199830,199832,199834,199836,199838,199840,199842,199844,199846,199848,199850,199852,199854,199856,199858,199860,199862],{"class":2227,"line":4246},[2225,199799,6500],{"class":2231},[2225,199801,6503],{"class":2239},[2225,199803,1520],{"class":2235},[2225,199805,3161],{"class":2291},[2225,199807,2789],{"class":2239},[2225,199809,2805],{"class":2235},[2225,199811,26080],{"class":2252},[2225,199813,2805],{"class":2235},[2225,199815,2298],{"class":2235},[2225,199817,2236],{"class":2235},[2225,199819,62649],{"class":2307},[2225,199821,2311],{"class":2235},[2225,199823,2249],{"class":2235},[2225,199825,62700],{"class":2252},[2225,199827,2805],{"class":2235},[2225,199829,2298],{"class":2235},[2225,199831,133638],{"class":2307},[2225,199833,2311],{"class":2235},[2225,199835,2249],{"class":2235},[2225,199837,27015],{"class":2252},[2225,199839,2805],{"class":2235},[2225,199841,2298],{"class":2235},[2225,199843,6361],{"class":2307},[2225,199845,2311],{"class":2235},[2225,199847,2249],{"class":2235},[2225,199849,6539],{"class":2252},[2225,199851,2805],{"class":2235},[2225,199853,2298],{"class":2235},[2225,199855,6351],{"class":2307},[2225,199857,2311],{"class":2235},[2225,199859,187812],{"class":6527},[2225,199861,2243],{"class":2235},[2225,199863,2362],{"class":2239},[2225,199865,199866,199868,199870,199872,199874,199876,199878,199880,199882,199884,199886,199888,199890,199892,199894,199896,199898,199900,199902,199904,199906,199908,199910,199912,199914,199916,199918,199920,199922,199924,199926,199929,199931],{"class":2227,"line":4253},[2225,199867,6500],{"class":2231},[2225,199869,6503],{"class":2239},[2225,199871,1520],{"class":2235},[2225,199873,3161],{"class":2291},[2225,199875,2789],{"class":2239},[2225,199877,2805],{"class":2235},[2225,199879,26153],{"class":2252},[2225,199881,2805],{"class":2235},[2225,199883,2298],{"class":2235},[2225,199885,2236],{"class":2235},[2225,199887,62649],{"class":2307},[2225,199889,2311],{"class":2235},[2225,199891,2249],{"class":2235},[2225,199893,62762],{"class":2252},[2225,199895,2805],{"class":2235},[2225,199897,2298],{"class":2235},[2225,199899,133638],{"class":2307},[2225,199901,2311],{"class":2235},[2225,199903,2249],{"class":2235},[2225,199905,199769],{"class":2252},[2225,199907,2805],{"class":2235},[2225,199909,2298],{"class":2235},[2225,199911,6361],{"class":2307},[2225,199913,2311],{"class":2235},[2225,199915,2249],{"class":2235},[2225,199917,6539],{"class":2252},[2225,199919,2805],{"class":2235},[2225,199921,2298],{"class":2235},[2225,199923,6351],{"class":2307},[2225,199925,2311],{"class":2235},[2225,199927,199928],{"class":6527}," 850",[2225,199930,2243],{"class":2235},[2225,199932,2362],{"class":2239},[2225,199934,199935,199937,199939,199941,199943,199945,199947,199950,199952,199954,199956,199958,199960,199962,199964,199966,199968,199970,199972,199974,199976,199978,199980,199982,199984,199986,199988,199990,199992,199994,199996,199999,200001],{"class":2227,"line":4259},[2225,199936,6500],{"class":2231},[2225,199938,6503],{"class":2239},[2225,199940,1520],{"class":2235},[2225,199942,3161],{"class":2291},[2225,199944,2789],{"class":2239},[2225,199946,2805],{"class":2235},[2225,199948,199949],{"class":2252},"inv-4",[2225,199951,2805],{"class":2235},[2225,199953,2298],{"class":2235},[2225,199955,2236],{"class":2235},[2225,199957,62649],{"class":2307},[2225,199959,2311],{"class":2235},[2225,199961,2249],{"class":2235},[2225,199963,62762],{"class":2252},[2225,199965,2805],{"class":2235},[2225,199967,2298],{"class":2235},[2225,199969,133638],{"class":2307},[2225,199971,2311],{"class":2235},[2225,199973,2249],{"class":2235},[2225,199975,199769],{"class":2252},[2225,199977,2805],{"class":2235},[2225,199979,2298],{"class":2235},[2225,199981,6361],{"class":2307},[2225,199983,2311],{"class":2235},[2225,199985,2249],{"class":2235},[2225,199987,26178],{"class":2252},[2225,199989,2805],{"class":2235},[2225,199991,2298],{"class":2235},[2225,199993,6351],{"class":2307},[2225,199995,2311],{"class":2235},[2225,199997,199998],{"class":6527}," 5000",[2225,200000,2243],{"class":2235},[2225,200002,2362],{"class":2239},[1591,200004,200006],{"id":200005},"one-shot-totals","One-shot totals",[937,200008,200009,200012,200013,5502,200015,200017],{},[941,200010,200011],{},"query().aggregate(spec)"," takes a record of named reducers and returns an ",[941,200014,199420],{},[941,200016,61378],{}," executes synchronously (the eager query path reads the already-decrypted cache) and returns one result field per reducer:",[2217,200019,200021],{"className":2219,"code":200020,"language":2221,"meta":1037,"style":1037},"const totals = invoices.query()\n  .where('status', '==', 'paid')\n  .aggregate({ total: sum('amount'), n: count(), mean: avg('amount') })\n  .run()\n\u002F\u002F → { total: 1200, n: 3, mean: 400 }\n",[941,200022,200023,200039,200071,200130,200138],{"__ignoreMap":1037},[2225,200024,200025,200027,200029,200031,200033,200035,200037],{"class":2227,"line":2228},[2225,200026,2775],{"class":2774},[2225,200028,27395],{"class":2239},[2225,200030,2781],{"class":2235},[2225,200032,6503],{"class":2239},[2225,200034,1520],{"class":2235},[2225,200036,5286],{"class":2291},[2225,200038,8146],{"class":2239},[2225,200040,200041,200043,200045,200047,200049,200051,200053,200055,200057,200059,200061,200063,200065,200067,200069],{"class":2227,"line":1041},[2225,200042,26235],{"class":2235},[2225,200044,3016],{"class":2291},[2225,200046,2789],{"class":2239},[2225,200048,2805],{"class":2235},[2225,200050,6631],{"class":2252},[2225,200052,2805],{"class":2235},[2225,200054,2298],{"class":2235},[2225,200056,2249],{"class":2235},[2225,200058,21901],{"class":2252},[2225,200060,2805],{"class":2235},[2225,200062,2298],{"class":2235},[2225,200064,2249],{"class":2235},[2225,200066,6539],{"class":2252},[2225,200068,2805],{"class":2235},[2225,200070,2362],{"class":2239},[2225,200072,200073,200075,200077,200079,200081,200083,200085,200087,200089,200091,200093,200095,200097,200099,200101,200103,200105,200107,200109,200112,200114,200116,200118,200120,200122,200124,200126,200128],{"class":2227,"line":1038},[2225,200074,26235],{"class":2235},[2225,200076,4449],{"class":2291},[2225,200078,2789],{"class":2239},[2225,200080,2792],{"class":2235},[2225,200082,27452],{"class":2307},[2225,200084,2311],{"class":2235},[2225,200086,27298],{"class":2291},[2225,200088,2789],{"class":2239},[2225,200090,2805],{"class":2235},[2225,200092,6617],{"class":2252},[2225,200094,2805],{"class":2235},[2225,200096,1381],{"class":2239},[2225,200098,2298],{"class":2235},[2225,200100,27471],{"class":2307},[2225,200102,2311],{"class":2235},[2225,200104,19270],{"class":2291},[2225,200106,4228],{"class":2239},[2225,200108,2298],{"class":2235},[2225,200110,200111],{"class":2307}," mean",[2225,200113,2311],{"class":2235},[2225,200115,64004],{"class":2291},[2225,200117,2789],{"class":2239},[2225,200119,2805],{"class":2235},[2225,200121,6617],{"class":2252},[2225,200123,2805],{"class":2235},[2225,200125,8495],{"class":2239},[2225,200127,2359],{"class":2235},[2225,200129,2362],{"class":2239},[2225,200131,200132,200134,200136],{"class":2227,"line":2288},[2225,200133,26235],{"class":2235},[2225,200135,61526],{"class":2291},[2225,200137,8146],{"class":2239},[2225,200139,200140],{"class":2227,"line":2304},[2225,200141,200142],{"class":2763},"\u002F\u002F → { total: 1200, n: 3, mean: 400 }\n",[937,200144,200145,200146,964,200149,1261,200152,200155],{},"Reduction always runs over the ",[950,200147,200148],{},"full match set",[941,200150,200151],{},".limit()",[941,200153,200154],{},".offset()"," are ignored (a paginated aggregation would be a different operation).",[1591,200157,200159],{"id":200158},"grouped-totals","Grouped totals",[937,200161,200162,200164,200165,200167],{},[941,200163,63979],{}," between the clauses and ",[941,200166,24892],{}," partitions the matches into buckets and reduces each bucket. Every result row carries the group key plus the reducer outputs:",[2217,200169,200171],{"className":2219,"code":200170,"language":2221,"meta":1037,"style":1037},"const byClient = invoices.query()\n  .where('status', '==', 'paid')\n  .groupBy('clientId')\n  .aggregate({ total: sum('amount'), n: count() })\n  .run()\n",[941,200172,200173,200189,200221,200237,200277],{"__ignoreMap":1037},[2225,200174,200175,200177,200179,200181,200183,200185,200187],{"class":2227,"line":2228},[2225,200176,2775],{"class":2774},[2225,200178,61420],{"class":2239},[2225,200180,2781],{"class":2235},[2225,200182,6503],{"class":2239},[2225,200184,1520],{"class":2235},[2225,200186,5286],{"class":2291},[2225,200188,8146],{"class":2239},[2225,200190,200191,200193,200195,200197,200199,200201,200203,200205,200207,200209,200211,200213,200215,200217,200219],{"class":2227,"line":1041},[2225,200192,26235],{"class":2235},[2225,200194,3016],{"class":2291},[2225,200196,2789],{"class":2239},[2225,200198,2805],{"class":2235},[2225,200200,6631],{"class":2252},[2225,200202,2805],{"class":2235},[2225,200204,2298],{"class":2235},[2225,200206,2249],{"class":2235},[2225,200208,21901],{"class":2252},[2225,200210,2805],{"class":2235},[2225,200212,2298],{"class":2235},[2225,200214,2249],{"class":2235},[2225,200216,6539],{"class":2252},[2225,200218,2805],{"class":2235},[2225,200220,2362],{"class":2239},[2225,200222,200223,200225,200227,200229,200231,200233,200235],{"class":2227,"line":1038},[2225,200224,26235],{"class":2235},[2225,200226,27496],{"class":2291},[2225,200228,2789],{"class":2239},[2225,200230,2805],{"class":2235},[2225,200232,59721],{"class":2252},[2225,200234,2805],{"class":2235},[2225,200236,2362],{"class":2239},[2225,200238,200239,200241,200243,200245,200247,200249,200251,200253,200255,200257,200259,200261,200263,200265,200267,200269,200271,200273,200275],{"class":2227,"line":2288},[2225,200240,26235],{"class":2235},[2225,200242,4449],{"class":2291},[2225,200244,2789],{"class":2239},[2225,200246,2792],{"class":2235},[2225,200248,27452],{"class":2307},[2225,200250,2311],{"class":2235},[2225,200252,27298],{"class":2291},[2225,200254,2789],{"class":2239},[2225,200256,2805],{"class":2235},[2225,200258,6617],{"class":2252},[2225,200260,2805],{"class":2235},[2225,200262,1381],{"class":2239},[2225,200264,2298],{"class":2235},[2225,200266,27471],{"class":2307},[2225,200268,2311],{"class":2235},[2225,200270,19270],{"class":2291},[2225,200272,19132],{"class":2239},[2225,200274,2359],{"class":2235},[2225,200276,2362],{"class":2239},[2225,200278,200279,200281,200283],{"class":2227,"line":2304},[2225,200280,26235],{"class":2235},[2225,200282,61526],{"class":2291},[2225,200284,8146],{"class":2239},[2217,200286,200288],{"className":6604,"code":200287,"language":141,"meta":1037,"style":1037},"[\n  { \"clientId\": \"c1\", \"total\": 350, \"n\": 2 },\n  { \"clientId\": \"c2\", \"total\": 850, \"n\": 1 }\n]\n",[941,200289,200290,200294,200339,200383],{"__ignoreMap":1037},[2225,200291,200292],{"class":2227,"line":2228},[2225,200293,26349],{"class":2235},[2225,200295,200296,200298,200300,200302,200304,200306,200308,200310,200312,200314,200316,200318,200320,200322,200325,200327,200329,200331,200333,200335,200337],{"class":2227,"line":1041},[2225,200297,26354],{"class":2235},[2225,200299,6614],{"class":2235},[2225,200301,59721],{"class":2774},[2225,200303,6620],{"class":2235},[2225,200305,2311],{"class":2235},[2225,200307,6614],{"class":2235},[2225,200309,62700],{"class":2252},[2225,200311,6620],{"class":2235},[2225,200313,2298],{"class":2235},[2225,200315,6614],{"class":2235},[2225,200317,64200],{"class":2774},[2225,200319,6620],{"class":2235},[2225,200321,2311],{"class":2235},[2225,200323,200324],{"class":6527}," 350",[2225,200326,2298],{"class":2235},[2225,200328,6614],{"class":2235},[2225,200330,26634],{"class":2774},[2225,200332,6620],{"class":2235},[2225,200334,2311],{"class":2235},[2225,200336,18961],{"class":6527},[2225,200338,2353],{"class":2235},[2225,200340,200341,200343,200345,200347,200349,200351,200353,200355,200357,200359,200361,200363,200365,200367,200369,200371,200373,200375,200377,200379,200381],{"class":2227,"line":1038},[2225,200342,26354],{"class":2235},[2225,200344,6614],{"class":2235},[2225,200346,59721],{"class":2774},[2225,200348,6620],{"class":2235},[2225,200350,2311],{"class":2235},[2225,200352,6614],{"class":2235},[2225,200354,62762],{"class":2252},[2225,200356,6620],{"class":2235},[2225,200358,2298],{"class":2235},[2225,200360,6614],{"class":2235},[2225,200362,64200],{"class":2774},[2225,200364,6620],{"class":2235},[2225,200366,2311],{"class":2235},[2225,200368,199928],{"class":6527},[2225,200370,2298],{"class":2235},[2225,200372,6614],{"class":2235},[2225,200374,26634],{"class":2774},[2225,200376,6620],{"class":2235},[2225,200378,2311],{"class":2235},[2225,200380,9377],{"class":6527},[2225,200382,6369],{"class":2235},[2225,200384,200385],{"class":2227,"line":2288},[2225,200386,26484],{"class":2235},[1591,200388,200390],{"id":200389},"multi-key-groupby","Multi-key groupBy",[937,200392,200393,200396],{},[941,200394,200395],{},"groupBy(...fields)"," also accepts multiple fields; rows then carry every grouped field in declaration order:",[2217,200398,200400],{"className":2219,"code":200399,"language":2221,"meta":1037,"style":1037},"const byClientAndPeriod = invoices.query()\n  .where('status', '==', 'paid')\n  .groupBy('clientId', 'period')\n  .aggregate({ total: sum('amount') })\n  .run()\n",[941,200401,200402,200419,200451,200475,200505],{"__ignoreMap":1037},[2225,200403,200404,200406,200409,200411,200413,200415,200417],{"class":2227,"line":2228},[2225,200405,2775],{"class":2774},[2225,200407,200408],{"class":2239}," byClientAndPeriod ",[2225,200410,2781],{"class":2235},[2225,200412,6503],{"class":2239},[2225,200414,1520],{"class":2235},[2225,200416,5286],{"class":2291},[2225,200418,8146],{"class":2239},[2225,200420,200421,200423,200425,200427,200429,200431,200433,200435,200437,200439,200441,200443,200445,200447,200449],{"class":2227,"line":1041},[2225,200422,26235],{"class":2235},[2225,200424,3016],{"class":2291},[2225,200426,2789],{"class":2239},[2225,200428,2805],{"class":2235},[2225,200430,6631],{"class":2252},[2225,200432,2805],{"class":2235},[2225,200434,2298],{"class":2235},[2225,200436,2249],{"class":2235},[2225,200438,21901],{"class":2252},[2225,200440,2805],{"class":2235},[2225,200442,2298],{"class":2235},[2225,200444,2249],{"class":2235},[2225,200446,6539],{"class":2252},[2225,200448,2805],{"class":2235},[2225,200450,2362],{"class":2239},[2225,200452,200453,200455,200457,200459,200461,200463,200465,200467,200469,200471,200473],{"class":2227,"line":1038},[2225,200454,26235],{"class":2235},[2225,200456,27496],{"class":2291},[2225,200458,2789],{"class":2239},[2225,200460,2805],{"class":2235},[2225,200462,59721],{"class":2252},[2225,200464,2805],{"class":2235},[2225,200466,2298],{"class":2235},[2225,200468,2249],{"class":2235},[2225,200470,133611],{"class":2252},[2225,200472,2805],{"class":2235},[2225,200474,2362],{"class":2239},[2225,200476,200477,200479,200481,200483,200485,200487,200489,200491,200493,200495,200497,200499,200501,200503],{"class":2227,"line":2288},[2225,200478,26235],{"class":2235},[2225,200480,4449],{"class":2291},[2225,200482,2789],{"class":2239},[2225,200484,2792],{"class":2235},[2225,200486,27452],{"class":2307},[2225,200488,2311],{"class":2235},[2225,200490,27298],{"class":2291},[2225,200492,2789],{"class":2239},[2225,200494,2805],{"class":2235},[2225,200496,6617],{"class":2252},[2225,200498,2805],{"class":2235},[2225,200500,8495],{"class":2239},[2225,200502,2359],{"class":2235},[2225,200504,2362],{"class":2239},[2225,200506,200507,200509,200511],{"class":2227,"line":2304},[2225,200508,26235],{"class":2235},[2225,200510,61526],{"class":2291},[2225,200512,8146],{"class":2239},[2217,200514,200516],{"className":6604,"code":200515,"language":141,"meta":1037,"style":1037},"[\n  { \"clientId\": \"c1\", \"period\": \"2026-05\", \"total\": 100 },\n  { \"clientId\": \"c1\", \"period\": \"2026-06\", \"total\": 250 },\n  { \"clientId\": \"c2\", \"period\": \"2026-05\", \"total\": 850 }\n]\n",[941,200517,200518,200522,200570,200618,200666],{"__ignoreMap":1037},[2225,200519,200520],{"class":2227,"line":2228},[2225,200521,26349],{"class":2235},[2225,200523,200524,200526,200528,200530,200532,200534,200536,200538,200540,200542,200544,200546,200548,200550,200552,200554,200556,200558,200560,200562,200564,200566,200568],{"class":2227,"line":1041},[2225,200525,26354],{"class":2235},[2225,200527,6614],{"class":2235},[2225,200529,59721],{"class":2774},[2225,200531,6620],{"class":2235},[2225,200533,2311],{"class":2235},[2225,200535,6614],{"class":2235},[2225,200537,62700],{"class":2252},[2225,200539,6620],{"class":2235},[2225,200541,2298],{"class":2235},[2225,200543,6614],{"class":2235},[2225,200545,133611],{"class":2774},[2225,200547,6620],{"class":2235},[2225,200549,2311],{"class":2235},[2225,200551,6614],{"class":2235},[2225,200553,199769],{"class":2252},[2225,200555,6620],{"class":2235},[2225,200557,2298],{"class":2235},[2225,200559,6614],{"class":2235},[2225,200561,64200],{"class":2774},[2225,200563,6620],{"class":2235},[2225,200565,2311],{"class":2235},[2225,200567,6528],{"class":6527},[2225,200569,2353],{"class":2235},[2225,200571,200572,200574,200576,200578,200580,200582,200584,200586,200588,200590,200592,200594,200596,200598,200600,200602,200604,200606,200608,200610,200612,200614,200616],{"class":2227,"line":1038},[2225,200573,26354],{"class":2235},[2225,200575,6614],{"class":2235},[2225,200577,59721],{"class":2774},[2225,200579,6620],{"class":2235},[2225,200581,2311],{"class":2235},[2225,200583,6614],{"class":2235},[2225,200585,62700],{"class":2252},[2225,200587,6620],{"class":2235},[2225,200589,2298],{"class":2235},[2225,200591,6614],{"class":2235},[2225,200593,133611],{"class":2774},[2225,200595,6620],{"class":2235},[2225,200597,2311],{"class":2235},[2225,200599,6614],{"class":2235},[2225,200601,27015],{"class":2252},[2225,200603,6620],{"class":2235},[2225,200605,2298],{"class":2235},[2225,200607,6614],{"class":2235},[2225,200609,64200],{"class":2774},[2225,200611,6620],{"class":2235},[2225,200613,2311],{"class":2235},[2225,200615,187812],{"class":6527},[2225,200617,2353],{"class":2235},[2225,200619,200620,200622,200624,200626,200628,200630,200632,200634,200636,200638,200640,200642,200644,200646,200648,200650,200652,200654,200656,200658,200660,200662,200664],{"class":2227,"line":2288},[2225,200621,26354],{"class":2235},[2225,200623,6614],{"class":2235},[2225,200625,59721],{"class":2774},[2225,200627,6620],{"class":2235},[2225,200629,2311],{"class":2235},[2225,200631,6614],{"class":2235},[2225,200633,62762],{"class":2252},[2225,200635,6620],{"class":2235},[2225,200637,2298],{"class":2235},[2225,200639,6614],{"class":2235},[2225,200641,133611],{"class":2774},[2225,200643,6620],{"class":2235},[2225,200645,2311],{"class":2235},[2225,200647,6614],{"class":2235},[2225,200649,199769],{"class":2252},[2225,200651,6620],{"class":2235},[2225,200653,2298],{"class":2235},[2225,200655,6614],{"class":2235},[2225,200657,64200],{"class":2774},[2225,200659,6620],{"class":2235},[2225,200661,2311],{"class":2235},[2225,200663,199928],{"class":6527},[2225,200665,6369],{"class":2235},[2225,200667,200668],{"class":2227,"line":2304},[2225,200669,26484],{"class":2235},[937,200671,200672,200673,997,200676,200679,200680,200683],{},"Bucket identity is order-insensitive — ",[941,200674,200675],{},".groupBy('a', 'b')",[941,200677,200678],{},".groupBy('b', 'a')"," produce the same buckets (the internal canonical key sorts field names); the declaration order only determines the order of the key fields on each output row. Buckets are emitted in first-seen order — ",[941,200681,200682],{},".sort()"," the rows downstream if you need a specific ordering.",[1591,200685,200687],{"id":200686},"streaming-aggregation-over-large-collections","Streaming aggregation over large collections",[937,200689,200690,5691,200693,200695],{},[941,200691,200692],{},"scan().aggregate(spec)",[950,200694,70491],{}," terminal that reduces the scan stream one record at a time. Memory is O(reducers), never O(records), which makes it the right tool for collections that don't fit in memory:",[2217,200697,200699],{"className":2219,"code":200698,"language":2221,"meta":1037,"style":1037},"const paid = await invoices.scan()\n  .where('status', '==', 'paid')\n  .aggregate({ total: sum('amount'), n: count() })\n\u002F\u002F → { total: 1200, n: 3 }\n",[941,200700,200701,200719,200751,200791],{"__ignoreMap":1037},[2225,200702,200703,200705,200707,200709,200711,200713,200715,200717],{"class":2227,"line":2228},[2225,200704,2775],{"class":2774},[2225,200706,164506],{"class":2239},[2225,200708,2781],{"class":2235},[2225,200710,2784],{"class":2231},[2225,200712,6503],{"class":2239},[2225,200714,1520],{"class":2235},[2225,200716,3239],{"class":2291},[2225,200718,8146],{"class":2239},[2225,200720,200721,200723,200725,200727,200729,200731,200733,200735,200737,200739,200741,200743,200745,200747,200749],{"class":2227,"line":1041},[2225,200722,26235],{"class":2235},[2225,200724,3016],{"class":2291},[2225,200726,2789],{"class":2239},[2225,200728,2805],{"class":2235},[2225,200730,6631],{"class":2252},[2225,200732,2805],{"class":2235},[2225,200734,2298],{"class":2235},[2225,200736,2249],{"class":2235},[2225,200738,21901],{"class":2252},[2225,200740,2805],{"class":2235},[2225,200742,2298],{"class":2235},[2225,200744,2249],{"class":2235},[2225,200746,6539],{"class":2252},[2225,200748,2805],{"class":2235},[2225,200750,2362],{"class":2239},[2225,200752,200753,200755,200757,200759,200761,200763,200765,200767,200769,200771,200773,200775,200777,200779,200781,200783,200785,200787,200789],{"class":2227,"line":1038},[2225,200754,26235],{"class":2235},[2225,200756,4449],{"class":2291},[2225,200758,2789],{"class":2239},[2225,200760,2792],{"class":2235},[2225,200762,27452],{"class":2307},[2225,200764,2311],{"class":2235},[2225,200766,27298],{"class":2291},[2225,200768,2789],{"class":2239},[2225,200770,2805],{"class":2235},[2225,200772,6617],{"class":2252},[2225,200774,2805],{"class":2235},[2225,200776,1381],{"class":2239},[2225,200778,2298],{"class":2235},[2225,200780,27471],{"class":2307},[2225,200782,2311],{"class":2235},[2225,200784,19270],{"class":2291},[2225,200786,19132],{"class":2239},[2225,200788,2359],{"class":2235},[2225,200790,2362],{"class":2239},[2225,200792,200793],{"class":2227,"line":2288},[2225,200794,200795],{"class":2763},"\u002F\u002F → { total: 1200, n: 3 }\n",[937,200797,52166,200798,200800],{},[941,200799,61811],{}," call pays a full scan; for several aggregates in one pass, put them all in one spec.",[1022,200802,101012],{"id":41259},[937,200804,200805,200806,200808,200809,200811],{},"All value exports below are available from ",[941,200807,27257],{}," (preferred for new code) and re-exported from the ",[941,200810,1086],{}," root for backward compatibility.",[1591,200813,200815],{"id":200814},"withreduce",[941,200816,21926],{},[2217,200818,200820],{"className":2219,"code":200819,"language":2221,"meta":1037,"style":1037},"withReduce(): ReduceStrategy\n",[941,200821,200822],{"__ignoreMap":1037},[2225,200823,200824,200826],{"class":2227,"line":2228},[2225,200825,58241],{"class":2291},[2225,200827,200828],{"class":2239},"(): ReduceStrategy\n",[937,200830,200831,200832,1261,200834,200836,200837,10313,200839,1520],{},"Builds the strategy that activates ",[941,200833,21931],{},[941,200835,21934],{}," on queries. Pass it as ",[941,200838,72927],{},[941,200840,200841],{},"createNoydb(...)",[1591,200843,200845],{"id":200844},"reducer-factories","Reducer factories",[1338,200847,200848,200861],{},[1341,200849,200850],{},[1344,200851,200852,200854,200856,200859],{},[1347,200853,9533],{},[1347,200855,131736],{},[1347,200857,200858],{},"Empty result set",[1347,200860,7834],{},[1357,200862,200863,200881,200901,200923,200941,200959,200984,201006],{},[1344,200864,200865,200869,200874,200878],{},[1362,200866,200867],{},[941,200868,3236],{},[1362,200870,200871],{},[941,200872,200873],{},"count(opts?): Reducer\u003Cnumber>",[1362,200875,200876],{},[941,200877,5402],{},[1362,200879,200880],{},"Number of matching records (ignores field values)",[1344,200882,200883,200887,200892,200896],{},[1362,200884,200885],{},[941,200886,28196],{},[1362,200888,200889],{},[941,200890,200891],{},"sum(field, opts?): Reducer\u003Cnumber>",[1362,200893,200894],{},[941,200895,5402],{},[1362,200897,200898,200899],{},"Sum of a numeric field; non-number values coerce to ",[941,200900,5402],{},[1344,200902,200903,200907,200912,200916],{},[1362,200904,200905],{},[941,200906,28201],{},[1362,200908,200909],{},[941,200910,200911],{},"avg(field, opts?): Reducer\u003Cnumber | null>",[1362,200913,200914],{},[941,200915,6651],{},[1362,200917,200918,200919,200922],{},"Arithmetic mean; coalesce with ",[941,200920,200921],{},"?? 0"," if you want a zero",[1344,200924,200925,200929,200934,200938],{},[1362,200926,200927],{},[941,200928,28204],{},[1362,200930,200931],{},[941,200932,200933],{},"min(field, opts?): Reducer\u003Cnumber | null>",[1362,200935,200936],{},[941,200937,6651],{},[1362,200939,200940],{},"Smallest value of a numeric field",[1344,200942,200943,200947,200952,200956],{},[1362,200944,200945],{},[941,200946,28207],{},[1362,200948,200949],{},[941,200950,200951],{},"max(field, opts?): Reducer\u003Cnumber | null>",[1362,200953,200954],{},[941,200955,6651],{},[1362,200957,200958],{},"Largest value of a numeric field",[1344,200960,200961,200965,200970,200979],{},[1362,200962,200963],{},[941,200964,184068],{},[1362,200966,200967],{},[941,200968,200969],{},"moneySum(field, opts?): Reducer\u003CMoneyString>",[1362,200971,200972,200973,953,200976,200978],{},"zero decimal, e.g. ",[941,200974,200975],{},"'0.00'",[941,200977,9261],{}," in multi-currency mode)",[1362,200980,200981,200983],{},[941,200982,28196],{}," over a declared money field, typed to the exact decimal-string result",[1344,200985,200986,200990,200995,201001],{},[1362,200987,200988],{},[941,200989,184071],{},[1362,200991,200992],{},[941,200993,200994],{},"moneyMin(field, opts?): Reducer\u003CMoneyString | null>",[1362,200996,200997,953,200999,200978],{},[941,200998,6651],{},[941,201000,9261],{},[1362,201002,201003,201005],{},[941,201004,28204],{}," over a declared money field",[1344,201007,201008,201012,201017,201023],{},[1362,201009,201010],{},[941,201011,184074],{},[1362,201013,201014],{},[941,201015,201016],{},"moneyMax(field, opts?): Reducer\u003CMoneyString | null>",[1362,201018,201019,953,201021,200978],{},[941,201020,6651],{},[941,201022,9261],{},[1362,201024,201025,201005],{},[941,201026,28207],{},[937,201028,201029,201030,201033],{},"Every factory accepts a ",[941,201031,201032],{},"ReducerOptions"," bag:",[1338,201035,201036,201049],{},[1341,201037,201038],{},[1344,201039,201040,201042,201044,201047],{},[1347,201041,101060],{},[1347,201043,20194],{},[1347,201045,201046],{},"Applies to",[1347,201048,116059],{},[1357,201050,201051,201073,201097],{},[1344,201052,201053,201058,201063,201066],{},[1362,201054,201055],{},[941,201056,201057],{},"seed",[1362,201059,201060],{},[941,201061,201062],{},"TSeed",[1362,201064,201065],{},"all factories",[1362,201067,201068,201069,201072],{},"Reserved seam for partition-carry aggregation. Accepted and plumbed through, ",[950,201070,201071],{},"currently unused"," — every run starts from the factory's zero value.",[1344,201074,201075,201080,201084,201089],{},[1362,201076,201077],{},[941,201078,201079],{},"convertTo",[1362,201081,201082],{},[941,201083,7259],{},[1362,201085,201086,201088],{},[941,201087,28196],{}," over a multi-currency money field",[1362,201090,201091,201092,1625,201094,201096],{},"Convert per-currency subtotals into this single currency. No effect on ",[941,201093,28204],{},[941,201095,28207],{}," (cross-currency min\u002Fmax is unsupported).",[1344,201098,201099,201104,201109,201114],{},[1362,201100,201101],{},[941,201102,201103],{},"fx",[1362,201105,201106],{},[941,201107,201108],{},"Record\u003Cstring, number | string>",[1362,201110,201111,201112],{},"with ",[941,201113,201079],{},[1362,201115,201116,201117,1520],{},"FX rate map keyed ",[941,201118,201119],{},"'USD->EUR'",[937,201121,201122,201125,201126,201128,201129,201131,201132,1625,201134,1625,201136,201138,201139,201142,201143,201145,201146,201148],{},[950,201123,201124],{},"Exact money aggregation:"," collections that declare a ",[941,201127,22765],{}," map get it automatically — at ",[941,201130,21931],{}," time, ",[941,201133,28196],{},[941,201135,28204],{},[941,201137,28207],{}," over a declared money field are rewritten to exact BigInt reducers that finalize to a decimal string (never a float). Union-form ",[1007,201140,124625],{"href":201141},".\u002Fmaterialized-views#union-form-combining-several-source-collections"," aggregate over a concatenated stream with no backing collection context, so they opt in via the ",[941,201144,22765],{}," map on the ",[941,201147,179638],{}," strategy instead.",[1591,201150,201152],{"id":201151},"queryaggregatespec",[941,201153,200011],{},[2217,201155,201157],{"className":2219,"code":201156,"language":2221,"meta":1037,"style":1037},"aggregate(spec: ReduceSpec): Reduction\u003CReduceResult\u003CSpec>>\naggregate(build: (b: ReducerBuilder\u003CT, S, M>) => Spec): Reduction\u003CReduceResult\u003CSpec>>\n",[941,201158,201159,201178],{"__ignoreMap":1037},[2225,201160,201161,201163,201166,201168,201171,201173,201176],{"class":2227,"line":2228},[2225,201162,4449],{"class":2291},[2225,201164,201165],{"class":2239},"(spec: ReduceSpec): Reduction",[2225,201167,6474],{"class":2235},[2225,201169,201170],{"class":2239},"ReduceResult",[2225,201172,6474],{"class":2235},[2225,201174,201175],{"class":2239},"Spec",[2225,201177,17688],{"class":2235},[2225,201179,201180,201182,201185,201187,201189,201191,201194,201196,201198,201200,201203,201205,201208,201210,201212,201215,201217,201219,201221,201223],{"class":2227,"line":1041},[2225,201181,4449],{"class":2291},[2225,201183,201184],{"class":2239},"(build: ",[2225,201186,2789],{"class":2235},[2225,201188,80529],{"class":7052},[2225,201190,2311],{"class":2235},[2225,201192,201193],{"class":6342}," ReducerBuilder",[2225,201195,6474],{"class":2235},[2225,201197,11968],{"class":6342},[2225,201199,2298],{"class":2235},[2225,201201,201202],{"class":6342}," S",[2225,201204,2298],{"class":2235},[2225,201206,201207],{"class":6342}," M",[2225,201209,25073],{"class":2235},[2225,201211,8116],{"class":2774},[2225,201213,201214],{"class":2239}," Spec): Reduction",[2225,201216,6474],{"class":2235},[2225,201218,201170],{"class":2239},[2225,201220,6474],{"class":2235},[2225,201222,201175],{"class":2239},[2225,201224,17688],{"class":2235},[937,201226,201227,201230,201231,201237,201238,1625,201240,1625,201242,201244,201245,2311],{},[941,201228,201229],{},"ReduceSpec"," is a plain record of named reducers; the result carries one field per spec key. The builder overload types field arguments against the collection's record type, ",[950,201232,201233,201234,201236],{},"refuses ",[941,201235,119728],{}," fields at compile time",", and types ",[941,201239,28196],{},[941,201241,28204],{},[941,201243,28207],{}," over declared money fields as ",[941,201246,185620],{},[2217,201248,201250],{"className":2219,"code":201249,"language":2221,"meta":1037,"style":1037},"const totals = invoices.query()\n  .aggregate((b) => ({ total: b.sum('amount'), n: b.count() }))\n  .run()\n\u002F\u002F → { total: 6200, n: 4 }\n",[941,201251,201252,201268,201326,201334],{"__ignoreMap":1037},[2225,201253,201254,201256,201258,201260,201262,201264,201266],{"class":2227,"line":2228},[2225,201255,2775],{"class":2774},[2225,201257,27395],{"class":2239},[2225,201259,2781],{"class":2235},[2225,201261,6503],{"class":2239},[2225,201263,1520],{"class":2235},[2225,201265,5286],{"class":2291},[2225,201267,8146],{"class":2239},[2225,201269,201270,201272,201274,201276,201278,201280,201282,201284,201286,201288,201290,201292,201294,201296,201298,201300,201302,201304,201306,201308,201310,201312,201314,201316,201318,201320,201322,201324],{"class":2227,"line":1041},[2225,201271,26235],{"class":2235},[2225,201273,4449],{"class":2291},[2225,201275,2789],{"class":2239},[2225,201277,2789],{"class":2235},[2225,201279,80529],{"class":7052},[2225,201281,1381],{"class":2235},[2225,201283,8116],{"class":2774},[2225,201285,953],{"class":2239},[2225,201287,2792],{"class":2235},[2225,201289,27452],{"class":2307},[2225,201291,2311],{"class":2235},[2225,201293,80536],{"class":2239},[2225,201295,1520],{"class":2235},[2225,201297,28196],{"class":2291},[2225,201299,2789],{"class":2239},[2225,201301,2805],{"class":2235},[2225,201303,6617],{"class":2252},[2225,201305,2805],{"class":2235},[2225,201307,1381],{"class":2239},[2225,201309,2298],{"class":2235},[2225,201311,27471],{"class":2307},[2225,201313,2311],{"class":2235},[2225,201315,80536],{"class":2239},[2225,201317,1520],{"class":2235},[2225,201319,3236],{"class":2291},[2225,201321,19132],{"class":2239},[2225,201323,2359],{"class":2235},[2225,201325,8606],{"class":2239},[2225,201327,201328,201330,201332],{"class":2227,"line":1038},[2225,201329,26235],{"class":2235},[2225,201331,61526],{"class":2291},[2225,201333,8146],{"class":2239},[2225,201335,201336],{"class":2227,"line":2288},[2225,201337,201338],{"class":2763},"\u002F\u002F → { total: 6200, n: 4 }\n",[937,201340,201341,201342,2311],{},"Terminals on ",[941,201343,201344],{},"Reduction\u003CR>",[1111,201346,201347,201353],{},[1114,201348,201349,201352],{},[941,201350,201351],{},".run(): R"," — execute synchronously.",[1114,201354,201355,201358],{},[941,201356,201357],{},".live(): LiveAggregation\u003CR>"," — reactive value that recomputes whenever the source collection changes.",[1591,201360,201362],{"id":201361},"querygroupbyfields",[941,201363,201364],{},"query().groupBy(...fields)",[2217,201366,201368],{"className":2219,"code":201367,"language":2221,"meta":1037,"style":1037},"groupBy\u003CF>(field: F): GroupedQuery\u003CT, F, S, M>          \u002F\u002F single key\ngroupBy\u003CF>(...fields: F): GroupedQueryN\u003CT, F, S, M>     \u002F\u002F two or more keys\n",[941,201369,201370,201406],{"__ignoreMap":1037},[2225,201371,201372,201374,201376,201379,201381,201384,201386,201388,201390,201393,201395,201397,201399,201401,201403],{"class":2227,"line":2228},[2225,201373,27496],{"class":2291},[2225,201375,6474],{"class":2235},[2225,201377,201378],{"class":6342},"F",[2225,201380,6480],{"class":2235},[2225,201382,201383],{"class":2239},"(field: F): GroupedQuery",[2225,201385,6474],{"class":2235},[2225,201387,11968],{"class":2239},[2225,201389,2298],{"class":2235},[2225,201391,201392],{"class":2239}," F",[2225,201394,2298],{"class":2235},[2225,201396,201202],{"class":2239},[2225,201398,2298],{"class":2235},[2225,201400,201207],{"class":2239},[2225,201402,6480],{"class":2235},[2225,201404,201405],{"class":2763},"          \u002F\u002F single key\n",[2225,201407,201408,201410,201412,201414,201416,201418,201420,201423,201425,201427,201429,201431,201433,201435,201437,201439,201441],{"class":2227,"line":1041},[2225,201409,27496],{"class":2291},[2225,201411,6474],{"class":2235},[2225,201413,201378],{"class":6342},[2225,201415,6480],{"class":2235},[2225,201417,2789],{"class":2239},[2225,201419,8670],{"class":2235},[2225,201421,201422],{"class":2239},"fields: F): GroupedQueryN",[2225,201424,6474],{"class":2235},[2225,201426,11968],{"class":2239},[2225,201428,2298],{"class":2235},[2225,201430,201392],{"class":2239},[2225,201432,2298],{"class":2235},[2225,201434,201202],{"class":2239},[2225,201436,2298],{"class":2235},[2225,201438,201207],{"class":2239},[2225,201440,6480],{"class":2235},[2225,201442,201443],{"class":2763},"     \u002F\u002F two or more keys\n",[937,201445,201446,201447,201449,201450,201453],{},"The only operation on a grouped query is ",[941,201448,24892],{}," (same bare-spec and builder overloads as above), which returns a ",[941,201451,201452],{},"GroupedReduction"," with three terminals:",[1111,201455,201456,201462,201480],{},[1114,201457,201458,201461],{},[941,201459,201460],{},".run(): R[]"," — one row per bucket, group key fields first, reducer outputs after.",[1114,201463,201464,201467,201468,201470,201471,201473,201474,201476,201477,201479],{},[941,201465,201466],{},".runAsync({ locale?, fallback? }): Promise\u003CR[]>"," — like ",[941,201469,61378],{},", and when the single grouping field is a dictionary-key field (see ",[1007,201472,701],{"href":124720},") and a ",[941,201475,23074],{}," is given, appends a resolved ",[941,201478,24050],{}," to each row. Single-field groupings only.",[1114,201481,201482,201485],{},[941,201483,201484],{},".live(): LiveAggregation\u003CR[]>"," — reactive grouped rows.",[937,201487,201488,201489,1324,201491,104755,201493,201496],{},"Ordering, limiting, and filtering belong on the query ",[950,201490,11357],{},[941,201492,21934],{},[941,201494,201495],{},"having"," \u002F post-group ordering.",[1591,201498,201500],{"id":201499},"scanaggregatespec",[941,201501,200692],{},[2217,201503,201505],{"className":2219,"code":201504,"language":2221,"meta":1037,"style":1037},"scan().aggregate(spec: ReduceSpec): Promise\u003CReduceResult\u003CSpec>>\nscan().aggregate(build: (b: ReducerBuilder\u003CT, S, M>) => Spec): Promise\u003CReduceResult\u003CSpec>>\n",[941,201506,201507,201532],{"__ignoreMap":1037},[2225,201508,201509,201511,201513,201515,201517,201520,201522,201524,201526,201528,201530],{"class":2227,"line":2228},[2225,201510,3239],{"class":2291},[2225,201512,4228],{"class":2239},[2225,201514,1520],{"class":2235},[2225,201516,4449],{"class":2291},[2225,201518,201519],{"class":2239},"(spec: ReduceSpec): ",[2225,201521,11740],{"class":6342},[2225,201523,6474],{"class":2235},[2225,201525,201170],{"class":2239},[2225,201527,6474],{"class":2235},[2225,201529,201175],{"class":2239},[2225,201531,17688],{"class":2235},[2225,201533,201534,201536,201538,201540,201542,201544,201546,201548,201550,201552,201554,201556,201558,201560,201562,201564,201566,201568,201571,201573,201575,201577,201579,201581],{"class":2227,"line":1041},[2225,201535,3239],{"class":2291},[2225,201537,4228],{"class":2239},[2225,201539,1520],{"class":2235},[2225,201541,4449],{"class":2291},[2225,201543,201184],{"class":2239},[2225,201545,2789],{"class":2235},[2225,201547,80529],{"class":7052},[2225,201549,2311],{"class":2235},[2225,201551,201193],{"class":6342},[2225,201553,6474],{"class":2235},[2225,201555,11968],{"class":6342},[2225,201557,2298],{"class":2235},[2225,201559,201202],{"class":6342},[2225,201561,2298],{"class":2235},[2225,201563,201207],{"class":6342},[2225,201565,25073],{"class":2235},[2225,201567,8116],{"class":2774},[2225,201569,201570],{"class":2239}," Spec): ",[2225,201572,11740],{"class":6342},[2225,201574,6474],{"class":2235},[2225,201576,201170],{"class":2239},[2225,201578,6474],{"class":2235},[2225,201580,201175],{"class":2239},[2225,201582,17688],{"class":2235},[937,201584,201585,201586,1261,201588,201590,201591,1324,201594,201596,201597,997,201600,201603],{},"Async, streaming, O(reducers) memory. Honors the scan's ",[941,201587,24883],{},[941,201589,24728],{}," clauses. Works ",[950,201592,201593],{},"with or without",[941,201595,21926],{}," — the streaming reducer loop lives in the core scan builder. ",[941,201598,201599],{},"scan().groupBy()",[941,201601,201602],{},"scan().aggregate().live()"," do not exist.",[1591,201605,201607],{"id":201606},"liveaggregationr",[941,201608,201609],{},"LiveAggregation\u003CR>",[937,201611,171701,201612,201614],{},[941,201613,3027],{}," on both terminals. Framework-agnostic reactive primitive:",[1338,201616,201617,201626],{},[1341,201618,201619],{},[1344,201620,201621,201624],{},[1347,201622,201623],{},"Member",[1347,201625,7834],{},[1357,201627,201628,201641,201654,201664],{},[1344,201629,201630,201635],{},[1362,201631,201632],{},[941,201633,201634],{},"value: R | undefined",[1362,201636,201637,201638,201640],{},"Current reduced value (",[941,201639,18316],{}," only if the first compute threw)",[1344,201642,201643,201648],{},[1362,201644,201645],{},[941,201646,201647],{},"error: unknown",[1362,201649,201650,201651,201653],{},"Last execution error; the previous good ",[941,201652,25201],{}," is preserved on failure",[1344,201655,201656,201661],{},[1362,201657,201658],{},[941,201659,201660],{},"subscribe(cb): () => void",[1362,201662,201663],{},"Notify on every recomputation; returns an unsubscribe function",[1344,201665,201666,201671],{},[1362,201667,201668],{},[941,201669,201670],{},"stop(): void",[1362,201672,201673,201674],{},"Tear down the upstream subscription. Idempotent. ",[950,201675,201676],{},"Always call it when done.",[2217,201678,201680],{"className":2219,"code":201679,"language":2221,"meta":1037,"style":1037},"\u002F\u002F Await it: `live()` hydrates the collection first, and reading an\n\u002F\u002F un-awaited handle on an unloaded collection throws\n\u002F\u002F `CollectionNotHydratedError` — the aggregate path carries the same guard\n\u002F\u002F as a plain `query().live()` (verified against @noy-db\u002Fhub 0.8.0-pre.0).\nconst open = await invoices.query()\n  .where('status', '==', 'open')\n  .aggregate({ total: sum('amount') })\n  .live()\n\nopen.value \u002F\u002F → { total: 5000 }\nopen.subscribe(() => console.log(open.value)) \u002F\u002F returns an unsubscribe fn\n\u002F\u002F … later\nopen.stop()\n",[941,201681,201682,201687,201692,201697,201702,201721,201753,201783,201791,201795,201807,201838,201843],{"__ignoreMap":1037},[2225,201683,201684],{"class":2227,"line":2228},[2225,201685,201686],{"class":2763},"\u002F\u002F Await it: `live()` hydrates the collection first, and reading an\n",[2225,201688,201689],{"class":2227,"line":1041},[2225,201690,201691],{"class":2763},"\u002F\u002F un-awaited handle on an unloaded collection throws\n",[2225,201693,201694],{"class":2227,"line":1038},[2225,201695,201696],{"class":2763},"\u002F\u002F `CollectionNotHydratedError` — the aggregate path carries the same guard\n",[2225,201698,201699],{"class":2227,"line":2288},[2225,201700,201701],{"class":2763},"\u002F\u002F as a plain `query().live()` (verified against @noy-db\u002Fhub 0.8.0-pre.0).\n",[2225,201703,201704,201706,201709,201711,201713,201715,201717,201719],{"class":2227,"line":2304},[2225,201705,2775],{"class":2774},[2225,201707,201708],{"class":2239}," open ",[2225,201710,2781],{"class":2235},[2225,201712,2784],{"class":2231},[2225,201714,6503],{"class":2239},[2225,201716,1520],{"class":2235},[2225,201718,5286],{"class":2291},[2225,201720,8146],{"class":2239},[2225,201722,201723,201725,201727,201729,201731,201733,201735,201737,201739,201741,201743,201745,201747,201749,201751],{"class":2227,"line":2356},[2225,201724,26235],{"class":2235},[2225,201726,3016],{"class":2291},[2225,201728,2789],{"class":2239},[2225,201730,2805],{"class":2235},[2225,201732,6631],{"class":2252},[2225,201734,2805],{"class":2235},[2225,201736,2298],{"class":2235},[2225,201738,2249],{"class":2235},[2225,201740,21901],{"class":2252},[2225,201742,2805],{"class":2235},[2225,201744,2298],{"class":2235},[2225,201746,2249],{"class":2235},[2225,201748,26178],{"class":2252},[2225,201750,2805],{"class":2235},[2225,201752,2362],{"class":2239},[2225,201754,201755,201757,201759,201761,201763,201765,201767,201769,201771,201773,201775,201777,201779,201781],{"class":2227,"line":2830},[2225,201756,26235],{"class":2235},[2225,201758,4449],{"class":2291},[2225,201760,2789],{"class":2239},[2225,201762,2792],{"class":2235},[2225,201764,27452],{"class":2307},[2225,201766,2311],{"class":2235},[2225,201768,27298],{"class":2291},[2225,201770,2789],{"class":2239},[2225,201772,2805],{"class":2235},[2225,201774,6617],{"class":2252},[2225,201776,2805],{"class":2235},[2225,201778,8495],{"class":2239},[2225,201780,2359],{"class":2235},[2225,201782,2362],{"class":2239},[2225,201784,201785,201787,201789],{"class":2227,"line":2861},[2225,201786,26235],{"class":2235},[2225,201788,727],{"class":2291},[2225,201790,8146],{"class":2239},[2225,201792,201793],{"class":2227,"line":4246},[2225,201794,2285],{"emptyLinePlaceholder":2284},[2225,201796,201797,201799,201801,201804],{"class":2227,"line":4253},[2225,201798,26178],{"class":2239},[2225,201800,1520],{"class":2235},[2225,201802,201803],{"class":2239},"value ",[2225,201805,201806],{"class":2763},"\u002F\u002F → { total: 5000 }\n",[2225,201808,201809,201811,201813,201815,201817,201819,201821,201823,201825,201827,201830,201832,201835],{"class":2227,"line":4259},[2225,201810,26178],{"class":2239},[2225,201812,1520],{"class":2235},[2225,201814,25042],{"class":2291},[2225,201816,2789],{"class":2239},[2225,201818,4228],{"class":2235},[2225,201820,8116],{"class":2774},[2225,201822,134201],{"class":2239},[2225,201824,1520],{"class":2235},[2225,201826,27100],{"class":2291},[2225,201828,201829],{"class":2239},"(open",[2225,201831,1520],{"class":2235},[2225,201833,201834],{"class":2239},"value)) ",[2225,201836,201837],{"class":2763},"\u002F\u002F returns an unsubscribe fn\n",[2225,201839,201840],{"class":2227,"line":6548},[2225,201841,201842],{"class":2763},"\u002F\u002F … later\n",[2225,201844,201845,201847,201849,201851],{"class":2227,"line":6576},[2225,201846,26178],{"class":2239},[2225,201848,1520],{"class":2235},[2225,201850,27604],{"class":2291},[2225,201852,8146],{"class":2239},[1591,201854,201856],{"id":201855},"reducerr-s",[941,201857,201858],{},"Reducer\u003CR, S>",[937,201860,201861,201862,201865,201866,201869,201870,201873,201874,201877,201878,201881,201882,201884],{},"Reducers are pure data: ",[941,201863,201864],{},"init()"," builds the initial state, ",[941,201867,201868],{},"step(state, record)"," folds a record in, ",[941,201871,201872],{},"finalize(state)"," collapses to the user-visible result. Optional ",[941,201875,201876],{},"remove(state, record)"," un-folds a record (admits incremental maintenance) and ",[941,201879,201880],{},"merge(a, b)"," combines partial states (must be associative + commutative). Any object with this shape plugs into an ",[941,201883,21931],{}," spec:",[2217,201886,201888],{"className":2219,"code":201887,"language":2221,"meta":1037,"style":1037},"import type { Reducer } from '@noy-db\u002Fhub\u002Freduce'\n\nconst distinct = (field: string): Reducer\u003Cnumber, Set\u003Cunknown>> => ({\n  init: () => new Set(),\n  step: (s, r) => s.add((r as Record\u003Cstring, unknown>)[field]),\n  finalize: (s) => s.size,\n})\n\ninvoices.query().aggregate({ clients: distinct('clientId') }).run()\n\u002F\u002F → { clients: 2 }\n",[941,201889,201890,201911,201915,201956,201975,202023,202046,202052,202056,202101],{"__ignoreMap":1037},[2225,201891,201892,201894,201896,201898,201901,201903,201905,201907,201909],{"class":2227,"line":2228},[2225,201893,2232],{"class":2231},[2225,201895,3399],{"class":2231},[2225,201897,2236],{"class":2235},[2225,201899,201900],{"class":2239}," Reducer",[2225,201902,2243],{"class":2235},[2225,201904,2246],{"class":2231},[2225,201906,2249],{"class":2235},[2225,201908,27257],{"class":2252},[2225,201910,2256],{"class":2235},[2225,201912,201913],{"class":2227,"line":1041},[2225,201914,2285],{"emptyLinePlaceholder":2284},[2225,201916,201917,201919,201922,201924,201926,201928,201930,201932,201934,201936,201938,201940,201942,201944,201946,201948,201950,201952,201954],{"class":2227,"line":1038},[2225,201918,2775],{"class":2774},[2225,201920,201921],{"class":2239}," distinct ",[2225,201923,2781],{"class":2235},[2225,201925,953],{"class":2235},[2225,201927,25197],{"class":7052},[2225,201929,2311],{"class":2235},[2225,201931,6366],{"class":6342},[2225,201933,6255],{"class":2235},[2225,201935,201900],{"class":6342},[2225,201937,6474],{"class":2235},[2225,201939,20636],{"class":6342},[2225,201941,2298],{"class":2235},[2225,201943,145097],{"class":6342},[2225,201945,6474],{"class":2235},[2225,201947,22421],{"class":6342},[2225,201949,28601],{"class":2235},[2225,201951,8116],{"class":2774},[2225,201953,953],{"class":2239},[2225,201955,4200],{"class":2235},[2225,201957,201958,201961,201963,201965,201967,201969,201971,201973],{"class":2227,"line":2288},[2225,201959,201960],{"class":2291},"  init",[2225,201962,2311],{"class":2235},[2225,201964,13130],{"class":2235},[2225,201966,8116],{"class":2774},[2225,201968,8130],{"class":2235},[2225,201970,145097],{"class":2291},[2225,201972,4228],{"class":2239},[2225,201974,1707],{"class":2235},[2225,201976,201977,201980,201982,201984,201986,201988,201990,201992,201994,201997,201999,202001,202004,202006,202008,202010,202012,202014,202016,202018,202021],{"class":2227,"line":2304},[2225,201978,201979],{"class":2291},"  step",[2225,201981,2311],{"class":2235},[2225,201983,953],{"class":2235},[2225,201985,123092],{"class":7052},[2225,201987,2298],{"class":2235},[2225,201989,32537],{"class":7052},[2225,201991,1381],{"class":2235},[2225,201993,8116],{"class":2774},[2225,201995,201996],{"class":2239}," s",[2225,201998,1520],{"class":2235},[2225,202000,36269],{"class":2291},[2225,202002,202003],{"class":2239},"((r ",[2225,202005,4899],{"class":2231},[2225,202007,22527],{"class":6342},[2225,202009,6474],{"class":2235},[2225,202011,7259],{"class":6342},[2225,202013,2298],{"class":2235},[2225,202015,40717],{"class":6342},[2225,202017,6480],{"class":2235},[2225,202019,202020],{"class":2239},")[field])",[2225,202022,1707],{"class":2235},[2225,202024,202025,202028,202030,202032,202034,202036,202038,202040,202042,202044],{"class":2227,"line":2356},[2225,202026,202027],{"class":2291},"  finalize",[2225,202029,2311],{"class":2235},[2225,202031,953],{"class":2235},[2225,202033,123092],{"class":7052},[2225,202035,1381],{"class":2235},[2225,202037,8116],{"class":2774},[2225,202039,201996],{"class":2239},[2225,202041,1520],{"class":2235},[2225,202043,79350],{"class":2239},[2225,202045,1707],{"class":2235},[2225,202047,202048,202050],{"class":2227,"line":2830},[2225,202049,2359],{"class":2235},[2225,202051,2362],{"class":2239},[2225,202053,202054],{"class":2227,"line":2861},[2225,202055,2285],{"emptyLinePlaceholder":2284},[2225,202057,202058,202060,202062,202064,202066,202068,202070,202072,202074,202076,202078,202081,202083,202085,202087,202089,202091,202093,202095,202097,202099],{"class":2227,"line":4246},[2225,202059,6487],{"class":2239},[2225,202061,1520],{"class":2235},[2225,202063,5286],{"class":2291},[2225,202065,4228],{"class":2239},[2225,202067,1520],{"class":2235},[2225,202069,4449],{"class":2291},[2225,202071,2789],{"class":2239},[2225,202073,2792],{"class":2235},[2225,202075,62689],{"class":2307},[2225,202077,2311],{"class":2235},[2225,202079,202080],{"class":2291}," distinct",[2225,202082,2789],{"class":2239},[2225,202084,2805],{"class":2235},[2225,202086,59721],{"class":2252},[2225,202088,2805],{"class":2235},[2225,202090,8495],{"class":2239},[2225,202092,2359],{"class":2235},[2225,202094,1381],{"class":2239},[2225,202096,1520],{"class":2235},[2225,202098,61526],{"class":2291},[2225,202100,8146],{"class":2239},[2225,202102,202103],{"class":2227,"line":4253},[2225,202104,202105],{"class":2763},"\u002F\u002F → { clients: 2 }\n",[1591,202107,202109],{"id":202108},"constants-and-errors","Constants and errors",[1111,202111,202112,202121],{},[1114,202113,202114,953,202117,202120],{},[941,202115,202116],{},"GROUPBY_WARN_CARDINALITY",[941,202118,202119],{},"10_000",") — distinct-group count at which a one-shot console warning fires (deduplicated per field set).",[1114,202122,202123,953,202126,202128,202129,118123,202131,202133],{},[941,202124,202125],{},"GROUPBY_MAX_CARDINALITY",[941,202127,180307],{},") — hard cap; the run throws ",[941,202130,65235],{},[941,202132,1086],{},") the moment the cap would be exceeded.",[1591,202135,120387],{"id":120386},[937,202137,202138,202139,968,202141,968,202143,968,202146,202148,202149,997,202152,968,202155,202158,202159,202162,202163,202166,202167,968,202170,968,202172,968,202174,968,202177,968,202179,968,202182,968,202185,968,202188,968,202191,1520],{},"For building on the service directly: the ",[941,202140,199420],{},[941,202142,199423],{},[941,202144,202145],{},"GroupedQueryN",[941,202147,201452],{}," classes, the pure pipeline functions ",[941,202150,202151],{},"reduceRecords(records, spec)",[941,202153,202154],{},"groupAndReduce(records, fields, spec, moneyFields?)",[941,202156,202157],{},"buildLiveReduction(recompute, upstreams)",", the shared ",[941,202160,202161],{},"reducerBuilder"," instance, and ",[941,202164,202165],{},"resetGroupByWarnings()"," (test helper that clears the cardinality-warning dedup). Types: ",[941,202168,202169],{},"ReduceStrategy",[941,202171,201229],{},[941,202173,201170],{},[941,202175,202176],{},"Reducer",[941,202178,201032],{},[941,202180,202181],{},"ReducerBuilder",[941,202183,202184],{},"GroupedRow",[941,202186,202187],{},"GroupedRowN",[941,202189,202190],{},"LiveAggregation",[941,202192,202193],{},"AggregationUpstream",[1022,202195,101425],{"id":101424},[1111,202197,202198,202221,202232],{},[1114,202199,202200,997,202203,202206,202207],{},[941,202201,202202],{},"query().aggregate(...)",[941,202204,202205],{},"query().groupBy(...)"," throw immediately: ",[1092,202208,202209,202210,17904,202213,202216,202217,202220],{},"\"Aggregate \u002F groupBy is not enabled on this Noydb instance. Import ",[941,202211,202212],{},"{ withReduce }",[941,202214,202215],{},"\"@noy-db\u002Fhub\u002Freduce\""," and pass it to ",[941,202218,202219],{},"createNoydb({ reduceStrategy: withReduce() })",".\"",[1114,202222,202223,202226,202227,1261,202229,202231],{},[941,202224,202225],{},"scan().aggregate(...)"," still works — the streaming terminal is part of the core scan builder and doesn't use the heavy ",[941,202228,199420],{},[941,202230,199423],{}," classes.",[1114,202233,202234],{},"None of the ~886 LOC reaches the bundle.",[1022,202236,68210],{"id":68209},[1111,202238,202239,202258,202270,202293,202324,202340],{},[1114,202240,202241,202246,202247,202249,202250,968,202252,202254,202255,202257],{},[950,202242,202243,202245],{},[941,202244,27496],{}," cardinality is capped",": one-shot warning at 10,000 distinct groups, ",[941,202248,65235],{}," at 100,000. The caps are fixed constants, not options. Grouping by a high-uniqueness field (",[941,202251,6983],{},[941,202253,87098],{},") is almost always a query mistake; beyond the cap you want streaming ",[941,202256,61811],{}," per segment or an external OLAP system.",[1114,202259,202260,2085,202263,202265,202266,1261,202268,1520],{},[950,202261,202262],{},"Full match set only",[941,202264,21931],{}," ignores ",[941,202267,200151],{},[941,202269,200154],{},[1114,202271,202272,2085,202275,1625,202277,1625,202279,1625,202281,202283,202284,202286,202287,202289,202290,202292],{},[950,202273,202274],{},"Non-number coercion",[941,202276,28196],{},[941,202278,28201],{},[941,202280,28204],{},[941,202282,28207],{}," coerce non-number field values (null, undefined, strings, objects) to ",[941,202285,5402],{}," rather than producing ",[941,202288,52582],{},". Filter upstream with ",[941,202291,24883],{}," if you need strict behavior.",[1114,202294,202295,2085,202298,202300,202301,202303,202304,202306,202307,20621,202309,1625,202311,1625,202313,202315,202316,202318,202319,202321,202322,1520],{},[950,202296,202297],{},"Money typing",[941,202299,124805],{}," over a declared money field ",[1092,202302,42111],{}," an exact decimal string (or a per-currency map in multi-currency mode without ",[941,202305,201079],{},") even though its static type says ",[941,202308,20636],{},[941,202310,184068],{},[941,202312,184071],{},[941,202314,184074],{}," for correctly-typed results. A money-shaped value (",[941,202317,123170],{},") reaching a plain numeric reducer on an ",[950,202320,119552],{}," field throws, telling you to declare it in the collection's ",[941,202323,22765],{},[1114,202325,202326,202329,202330,202332,202333,202335,202336,202339],{},[950,202327,202328],{},"Bucket keys",": records missing the group field (",[941,202331,18316],{},") and records with an explicit ",[941,202334,6651],{}," get ",[1092,202337,202338],{},"separate"," buckets. Coalesce upstream if you want them merged.",[1114,202341,202342,202347,202348,1520],{},[950,202343,202344,202346],{},[941,202345,3027],{}," recomputes fully"," on every source change (no incremental per-delta maintenance yet) and must be released with ",[941,202349,202350],{},"stop()",[1022,202352,2621],{"id":2620},[1111,202354,202355,202366,202373,202381,202390,202401,202407],{},[1114,202356,202357,1169,202359,1625,202361,1625,202363,202365],{},[1007,202358,3216],{"href":83069},[941,202360,3016],{},[941,202362,8686],{},[941,202364,3019],{}," pipeline aggregation sits on",[1114,202367,202368,1169,202370,202372],{},[1007,202369,705],{"href":139528},[941,202371,24883],{}," clauses ahead of an aggregation dispatch through indexes",[1114,202374,202375,202377,202378,202380],{},[1007,202376,727],{"href":134101}," — reactive queries; ",[941,202379,202190],{}," follows the same subscribe\u002Fstop contract",[1114,202382,202383,202385,202386,202389],{},[1007,202384,701],{"href":124720}," — dictionary-key fields whose labels ",[941,202387,202388],{},".runAsync({ locale })"," resolves per row",[1114,202391,202392,202394,202395,202398,202399],{},[1007,202393,124625],{"href":124624}," — multi-key groupBy inside materialized-view queries, and ",[1007,202396,202397],{"href":201141},"UNION sources"," with strategy-level ",[941,202400,22765],{},[1114,202402,202403],{},[1007,202404,202406],{"href":202405},"..\u002Frecipes\u002Fanalytics-app","Analytics app recipe",[1114,202408,101581,202409,1520],{},[1007,202410,202412],{"href":92608,"rel":202411},[3371],[941,202413,202414],{},"10-with-aggregate",[2645,202416,202417],{},"html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html pre.shiki code .sHdIc, html code.shiki .sHdIc{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#EEFFFF;--shiki-default-font-style:italic;--shiki-dark:#BABED8;--shiki-dark-font-style:italic}",{"title":1037,"searchDepth":1038,"depth":1038,"links":202419},[202420,202421,202422,202423,202429,202440,202441,202442],{"id":29856,"depth":1041,"text":29857},{"id":70092,"depth":1041,"text":70093},{"id":99863,"depth":1041,"text":99864},{"id":29892,"depth":1041,"text":29893,"children":202424},[202425,202426,202427,202428],{"id":200005,"depth":1038,"text":200006},{"id":200158,"depth":1038,"text":200159},{"id":200389,"depth":1038,"text":200390},{"id":200686,"depth":1038,"text":200687},{"id":41259,"depth":1041,"text":101012,"children":202430},[202431,202432,202433,202434,202435,202436,202437,202438,202439],{"id":200814,"depth":1038,"text":21926},{"id":200844,"depth":1038,"text":200845},{"id":201151,"depth":1038,"text":200011},{"id":201361,"depth":1038,"text":201364},{"id":201499,"depth":1038,"text":200692},{"id":201606,"depth":1038,"text":201609},{"id":201855,"depth":1038,"text":201858},{"id":202108,"depth":1038,"text":202109},{"id":120386,"depth":1038,"text":120387},{"id":101424,"depth":1041,"text":101425},{"id":68209,"depth":1041,"text":68210},{"id":2620,"depth":1041,"text":2621},"Opt-in record reduction for the query DSL: count\u002Fsum\u002Favg\u002Fmin\u002Fmax reducers and groupBy, computed in-memory after decryption — plus streaming scan().aggregate().",{},{"title":804,"description":202443},"xGSiak079s0rm9O2AtS36pdndLaIReOZtEhi-Dd9y6o",{"id":202448,"title":808,"access":12,"apiToc":929,"body":202449,"category":1042,"description":205132,"extension":1044,"meta":205133,"navigation":1042,"nest":1047,"path":809,"seo":205134,"sinceVersion":1042,"status":1049,"stem":810,"subtitle":1042,"tier":1050,"tocExternal":1042,"__hash__":205135},"docs\u002Fservices\u002Frouting.md",{"type":931,"value":202450,"toc":205108},[202451,202453,202456,202458,202476,202501,202503,202520,202522,203047,203052,203054,203058,203362,203379,203411,203415,203655,203768,203772,203922,203968,203970,203976,204001,204010,204016,204276,204299,204305,204477,204501,204505,204526,204553,204559,204618,204627,204630,204854,204856,204871,204873,204915,204917,205059,205061,205105],[934,202452,808],{"id":808},[46871,202454],{":rows":202455},"[{\"label\":\"Subpath\",\"value\":\"`@noy-db\u002Fhub` (root export)\"},{\"label\":\"Type\",\"value\":\"Core feature — `routeStore`, `wrapStore`, and the middleware factories are direct named imports, always available.\"}]",[1022,202457,29857],{"id":29856},[937,202459,202460,202461,202463,202464,202466,202467,202469,202470,50371,202472,202475],{},"Multi-store routing lets a single noy-db ",[950,202462,2694],{}," (the end-to-end-encrypted document store — see ",[1007,202465,65376],{"href":48},") spread its data across more than one storage backend: most collections on one database, blob attachments on cheap object storage, an audit-log collection on its own bucket, aging records migrated to a cold archive. ",[941,202468,48447],{}," builds that multiplexer; the result satisfies the same store contract as a single backend (see ",[1007,202471,6891],{"href":51},[941,202473,202474],{},"createNoydb({ store })"," never has to know the difference.",[937,202477,202478,202479,202482,202483,202486,202487,997,202489,202491,202492,202494,202495,202497,202498,202500],{},"Alongside it, ",[941,202480,202481],{},"wrapStore()"," composes resilience and observability ",[950,202484,202485],{},"middleware"," around any store, routed or not: retry with backoff, a circuit breaker, health-check auto-suspend, a read-through cache, structured logging, and metrics. Both ",[941,202488,48447],{},[941,202490,202481],{}," are ordinary named exports of ",[941,202493,1086],{},"'s main entry point — there's no ",[941,202496,943],{}," opt-in factory to call, and no separate subpath to import them from selectively. They're part of the core the same way ",[941,202499,12780],{}," is.",[1022,202502,111660],{"id":111659},[1111,202504,202505,202508,202511,202514,202517],{},[1114,202506,202507],{},"Hot\u002Fcold tiering — recent records stay on a fast\u002Flocal store, older ones migrate to cheap archive storage",[1114,202509,202510],{},"Multi-backend layouts — blob attachments to S3\u002FR2, an audit-log collection to its own bucket, one vault region's data to an EU-only store",[1114,202512,202513],{},"Resilience around a flaky backend — retry transient failures, trip a circuit breaker on persistent ones, auto-suspend on failed health checks",[1114,202515,202516],{},"Observability — structured logging or a metrics callback around every store call",[1114,202518,202519],{},"Maintenance windows — suspend a backend, optionally buffer writes, resume and replay them later",[1022,202521,58394],{"id":58393},[2217,202523,202525],{"className":2219,"code":202524,"language":2221,"meta":1037,"style":1037},"import { createNoydb, routeStore, wrapStore, withRetry, withCircuitBreaker, withMetrics } from '@noy-db\u002Fhub'\nimport { toAwsDynamo } from '@noy-db\u002Fto-aws-dynamo'\nimport { toCloudflareR2 } from '@noy-db\u002Fto-cloudflare-r2'\n\nconst routed = routeStore({\n  default: toAwsDynamo({ table: 'myapp' }),\n  blobs: toCloudflareR2({\n    accountId: 'acct-1',\n    bucket: 'myapp-blobs',\n    credentials: async () => ({ kind: 'aws', accessKeyId: '…', secretAccessKey: '…' }),\n  }),\n  routes: {\n    auditLog: toCloudflareR2({\n      accountId: 'acct-1',\n      bucket: 'myapp-audit',\n      credentials: async () => ({ kind: 'aws', accessKeyId: '…', secretAccessKey: '…' }),\n    }),\n  },\n})\n\nconst resilient = wrapStore(\n  routed,\n  withRetry({ maxRetries: 3 }),\n  withCircuitBreaker({ failureThreshold: 5 }),\n  withMetrics({ onOperation: (op) => metrics.record(op) }),\n)\n\nconst db = await createNoydb({ store: resilient, user: 'alice', secret: 'correct horse battery staple' })\n",[941,202526,202527,202566,202584,202602,202606,202621,202650,202663,202679,202695,202752,202760,202769,202782,202797,202813,202870,202878,202882,202888,202892,202904,202911,202931,202951,202989,202993,202997],{"__ignoreMap":1037},[2225,202528,202529,202531,202533,202535,202537,202539,202541,202543,202545,202547,202549,202551,202553,202556,202558,202560,202562,202564],{"class":2227,"line":2228},[2225,202530,2232],{"class":2231},[2225,202532,2236],{"class":2235},[2225,202534,2744],{"class":2239},[2225,202536,2298],{"class":2235},[2225,202538,48541],{"class":2239},[2225,202540,2298],{"class":2235},[2225,202542,72089],{"class":2239},[2225,202544,2298],{"class":2235},[2225,202546,72094],{"class":2239},[2225,202548,2298],{"class":2235},[2225,202550,72099],{"class":2239},[2225,202552,2298],{"class":2235},[2225,202554,202555],{"class":2239}," withMetrics",[2225,202557,2243],{"class":2235},[2225,202559,2246],{"class":2231},[2225,202561,2249],{"class":2235},[2225,202563,1086],{"class":2252},[2225,202565,2256],{"class":2235},[2225,202567,202568,202570,202572,202574,202576,202578,202580,202582],{"class":2227,"line":1041},[2225,202569,2232],{"class":2231},[2225,202571,2236],{"class":2235},[2225,202573,95763],{"class":2239},[2225,202575,2243],{"class":2235},[2225,202577,2246],{"class":2231},[2225,202579,2249],{"class":2235},[2225,202581,46865],{"class":2252},[2225,202583,2256],{"class":2235},[2225,202585,202586,202588,202590,202592,202594,202596,202598,202600],{"class":2227,"line":1038},[2225,202587,2232],{"class":2231},[2225,202589,2236],{"class":2235},[2225,202591,89652],{"class":2239},[2225,202593,2243],{"class":2235},[2225,202595,2246],{"class":2231},[2225,202597,2249],{"class":2235},[2225,202599,49123],{"class":2252},[2225,202601,2256],{"class":2235},[2225,202603,202604],{"class":2227,"line":2288},[2225,202605,2285],{"emptyLinePlaceholder":2284},[2225,202607,202608,202610,202613,202615,202617,202619],{"class":2227,"line":2304},[2225,202609,2775],{"class":2774},[2225,202611,202612],{"class":2239}," routed ",[2225,202614,2781],{"class":2235},[2225,202616,48541],{"class":2291},[2225,202618,2789],{"class":2239},[2225,202620,4200],{"class":2235},[2225,202622,202623,202626,202628,202630,202632,202634,202636,202638,202640,202642,202644,202646,202648],{"class":2227,"line":2356},[2225,202624,202625],{"class":2307},"  default",[2225,202627,2311],{"class":2235},[2225,202629,95763],{"class":2291},[2225,202631,2789],{"class":2239},[2225,202633,2792],{"class":2235},[2225,202635,47030],{"class":2307},[2225,202637,2311],{"class":2235},[2225,202639,2249],{"class":2235},[2225,202641,48176],{"class":2252},[2225,202643,2805],{"class":2235},[2225,202645,2243],{"class":2235},[2225,202647,1381],{"class":2239},[2225,202649,1707],{"class":2235},[2225,202651,202652,202655,202657,202659,202661],{"class":2227,"line":2830},[2225,202653,202654],{"class":2307},"  blobs",[2225,202656,2311],{"class":2235},[2225,202658,89652],{"class":2291},[2225,202660,2789],{"class":2239},[2225,202662,4200],{"class":2235},[2225,202664,202665,202668,202670,202672,202675,202677],{"class":2227,"line":2861},[2225,202666,202667],{"class":2307},"    accountId",[2225,202669,2311],{"class":2235},[2225,202671,2249],{"class":2235},[2225,202673,202674],{"class":2252},"acct-1",[2225,202676,2805],{"class":2235},[2225,202678,1707],{"class":2235},[2225,202680,202681,202684,202686,202688,202691,202693],{"class":2227,"line":4246},[2225,202682,202683],{"class":2307},"    bucket",[2225,202685,2311],{"class":2235},[2225,202687,2249],{"class":2235},[2225,202689,202690],{"class":2252},"myapp-blobs",[2225,202692,2805],{"class":2235},[2225,202694,1707],{"class":2235},[2225,202696,202697,202700,202702,202704,202706,202708,202710,202712,202714,202716,202718,202720,202722,202724,202726,202728,202730,202732,202734,202736,202738,202740,202742,202744,202746,202748,202750],{"class":2227,"line":4253},[2225,202698,202699],{"class":2291},"    credentials",[2225,202701,2311],{"class":2235},[2225,202703,38717],{"class":2774},[2225,202705,13130],{"class":2235},[2225,202707,8116],{"class":2774},[2225,202709,953],{"class":2239},[2225,202711,2792],{"class":2235},[2225,202713,8285],{"class":2307},[2225,202715,2311],{"class":2235},[2225,202717,2249],{"class":2235},[2225,202719,49315],{"class":2252},[2225,202721,2805],{"class":2235},[2225,202723,2298],{"class":2235},[2225,202725,49254],{"class":2307},[2225,202727,2311],{"class":2235},[2225,202729,2249],{"class":2235},[2225,202731,14475],{"class":2252},[2225,202733,2805],{"class":2235},[2225,202735,2298],{"class":2235},[2225,202737,49326],{"class":2307},[2225,202739,2311],{"class":2235},[2225,202741,2249],{"class":2235},[2225,202743,14475],{"class":2252},[2225,202745,2805],{"class":2235},[2225,202747,2243],{"class":2235},[2225,202749,1381],{"class":2239},[2225,202751,1707],{"class":2235},[2225,202753,202754,202756,202758],{"class":2227,"line":4259},[2225,202755,19143],{"class":2235},[2225,202757,1381],{"class":2239},[2225,202759,1707],{"class":2235},[2225,202761,202762,202765,202767],{"class":2227,"line":6548},[2225,202763,202764],{"class":2307},"  routes",[2225,202766,2311],{"class":2235},[2225,202768,2301],{"class":2235},[2225,202770,202771,202774,202776,202778,202780],{"class":2227,"line":6576},[2225,202772,202773],{"class":2307},"    auditLog",[2225,202775,2311],{"class":2235},[2225,202777,89652],{"class":2291},[2225,202779,2789],{"class":2239},[2225,202781,4200],{"class":2235},[2225,202783,202784,202787,202789,202791,202793,202795],{"class":2227,"line":6582},[2225,202785,202786],{"class":2307},"      accountId",[2225,202788,2311],{"class":2235},[2225,202790,2249],{"class":2235},[2225,202792,202674],{"class":2252},[2225,202794,2805],{"class":2235},[2225,202796,1707],{"class":2235},[2225,202798,202799,202802,202804,202806,202809,202811],{"class":2227,"line":6587},[2225,202800,202801],{"class":2307},"      bucket",[2225,202803,2311],{"class":2235},[2225,202805,2249],{"class":2235},[2225,202807,202808],{"class":2252},"myapp-audit",[2225,202810,2805],{"class":2235},[2225,202812,1707],{"class":2235},[2225,202814,202815,202818,202820,202822,202824,202826,202828,202830,202832,202834,202836,202838,202840,202842,202844,202846,202848,202850,202852,202854,202856,202858,202860,202862,202864,202866,202868],{"class":2227,"line":7174},[2225,202816,202817],{"class":2291},"      credentials",[2225,202819,2311],{"class":2235},[2225,202821,38717],{"class":2774},[2225,202823,13130],{"class":2235},[2225,202825,8116],{"class":2774},[2225,202827,953],{"class":2239},[2225,202829,2792],{"class":2235},[2225,202831,8285],{"class":2307},[2225,202833,2311],{"class":2235},[2225,202835,2249],{"class":2235},[2225,202837,49315],{"class":2252},[2225,202839,2805],{"class":2235},[2225,202841,2298],{"class":2235},[2225,202843,49254],{"class":2307},[2225,202845,2311],{"class":2235},[2225,202847,2249],{"class":2235},[2225,202849,14475],{"class":2252},[2225,202851,2805],{"class":2235},[2225,202853,2298],{"class":2235},[2225,202855,49326],{"class":2307},[2225,202857,2311],{"class":2235},[2225,202859,2249],{"class":2235},[2225,202861,14475],{"class":2252},[2225,202863,2805],{"class":2235},[2225,202865,2243],{"class":2235},[2225,202867,1381],{"class":2239},[2225,202869,1707],{"class":2235},[2225,202871,202872,202874,202876],{"class":2227,"line":7180},[2225,202873,80146],{"class":2235},[2225,202875,1381],{"class":2239},[2225,202877,1707],{"class":2235},[2225,202879,202880],{"class":2227,"line":7220},[2225,202881,19017],{"class":2235},[2225,202883,202884,202886],{"class":2227,"line":7225},[2225,202885,2359],{"class":2235},[2225,202887,2362],{"class":2239},[2225,202889,202890],{"class":2227,"line":7231},[2225,202891,2285],{"emptyLinePlaceholder":2284},[2225,202893,202894,202896,202898,202900,202902],{"class":2227,"line":7267},[2225,202895,2775],{"class":2774},[2225,202897,72139],{"class":2239},[2225,202899,2781],{"class":2235},[2225,202901,72089],{"class":2291},[2225,202903,11683],{"class":2239},[2225,202905,202906,202909],{"class":2227,"line":7272},[2225,202907,202908],{"class":2239},"  routed",[2225,202910,1707],{"class":2235},[2225,202912,202913,202915,202917,202919,202921,202923,202925,202927,202929],{"class":2227,"line":7278},[2225,202914,72202],{"class":2291},[2225,202916,2789],{"class":2239},[2225,202918,2792],{"class":2235},[2225,202920,72209],{"class":2307},[2225,202922,2311],{"class":2235},[2225,202924,14460],{"class":6527},[2225,202926,2243],{"class":2235},[2225,202928,1381],{"class":2239},[2225,202930,1707],{"class":2235},[2225,202932,202933,202935,202937,202939,202941,202943,202945,202947,202949],{"class":2227,"line":7303},[2225,202934,72224],{"class":2291},[2225,202936,2789],{"class":2239},[2225,202938,2792],{"class":2235},[2225,202940,72231],{"class":2307},[2225,202942,2311],{"class":2235},[2225,202944,18996],{"class":6527},[2225,202946,2243],{"class":2235},[2225,202948,1381],{"class":2239},[2225,202950,1707],{"class":2235},[2225,202952,202953,202956,202958,202960,202963,202965,202967,202969,202971,202973,202976,202978,202980,202983,202985,202987],{"class":2227,"line":7308},[2225,202954,202955],{"class":2291},"  withMetrics",[2225,202957,2789],{"class":2239},[2225,202959,2792],{"class":2235},[2225,202961,202962],{"class":2291}," onOperation",[2225,202964,2311],{"class":2235},[2225,202966,953],{"class":2235},[2225,202968,61313],{"class":7052},[2225,202970,1381],{"class":2235},[2225,202972,8116],{"class":2774},[2225,202974,202975],{"class":2239}," metrics",[2225,202977,1520],{"class":2235},[2225,202979,24158],{"class":2291},[2225,202981,202982],{"class":2239},"(op) ",[2225,202984,2359],{"class":2235},[2225,202986,1381],{"class":2239},[2225,202988,1707],{"class":2235},[2225,202990,202991],{"class":2227,"line":7314},[2225,202992,2362],{"class":2239},[2225,202994,202995],{"class":2227,"line":7348},[2225,202996,2285],{"emptyLinePlaceholder":2284},[2225,202998,202999,203001,203003,203005,203007,203009,203011,203013,203015,203017,203019,203021,203023,203025,203027,203029,203031,203033,203035,203037,203039,203041,203043,203045],{"class":2227,"line":8613},[2225,203000,2775],{"class":2774},[2225,203002,2778],{"class":2239},[2225,203004,2781],{"class":2235},[2225,203006,2784],{"class":2231},[2225,203008,2744],{"class":2291},[2225,203010,2789],{"class":2239},[2225,203012,2792],{"class":2235},[2225,203014,7564],{"class":2307},[2225,203016,2311],{"class":2235},[2225,203018,72272],{"class":2239},[2225,203020,2298],{"class":2235},[2225,203022,2795],{"class":2307},[2225,203024,2311],{"class":2235},[2225,203026,2249],{"class":2235},[2225,203028,6408],{"class":2252},[2225,203030,2805],{"class":2235},[2225,203032,2298],{"class":2235},[2225,203034,2810],{"class":2307},[2225,203036,2311],{"class":2235},[2225,203038,2249],{"class":2235},[2225,203040,40260],{"class":2252},[2225,203042,2805],{"class":2235},[2225,203044,2243],{"class":2235},[2225,203046,2362],{"class":2239},[937,203048,203049,203051],{},[941,203050,202481],{}," takes the store followed by any number of middleware — the first middleware listed is outermost (sees the request first, the response last).",[1022,203053,29893],{"id":29892},[1591,203055,203057],{"id":203056},"hotcold-tiering-with-age-based-routing","Hot\u002Fcold tiering with age-based routing",[2217,203059,203061],{"className":2219,"code":203060,"language":2221,"meta":1037,"style":1037},"import { createNoydb, routeStore } from '@noy-db\u002Fhub'\nimport { toMemory } from '@noy-db\u002Fto-memory'\n\nconst hot = toMemory()\nconst cold = toMemory()\n\nconst store = routeStore({\n  default: hot,\n  age: { cold, coldAfterDays: 90 },\n})\n\nconst db = await createNoydb({ store, user: 'alice', secret: 'correct horse battery staple' })\nconst vault = await db.openVault('acme')\n\nawait vault.collection('invoices').put('inv-1', { id: 'inv-1', amount: 100 })\n\nconst migrated = await store.compact('acme')\n\u002F\u002F → 0   (inv-1 was just written — nothing is older than 90 days yet)\n",[941,203062,203063,203085,203103,203107,203119,203131,203135,203149,203159,203182,203188,203192,203238,203264,203268,203326,203330,203357],{"__ignoreMap":1037},[2225,203064,203065,203067,203069,203071,203073,203075,203077,203079,203081,203083],{"class":2227,"line":2228},[2225,203066,2232],{"class":2231},[2225,203068,2236],{"class":2235},[2225,203070,2744],{"class":2239},[2225,203072,2298],{"class":2235},[2225,203074,48541],{"class":2239},[2225,203076,2243],{"class":2235},[2225,203078,2246],{"class":2231},[2225,203080,2249],{"class":2235},[2225,203082,1086],{"class":2252},[2225,203084,2256],{"class":2235},[2225,203086,203087,203089,203091,203093,203095,203097,203099,203101],{"class":2227,"line":1041},[2225,203088,2232],{"class":2231},[2225,203090,2236],{"class":2235},[2225,203092,68576],{"class":2239},[2225,203094,2243],{"class":2235},[2225,203096,2246],{"class":2231},[2225,203098,2249],{"class":2235},[2225,203100,9638],{"class":2252},[2225,203102,2256],{"class":2235},[2225,203104,203105],{"class":2227,"line":1038},[2225,203106,2285],{"emptyLinePlaceholder":2284},[2225,203108,203109,203111,203113,203115,203117],{"class":2227,"line":2288},[2225,203110,2775],{"class":2774},[2225,203112,190021],{"class":2239},[2225,203114,2781],{"class":2235},[2225,203116,68576],{"class":2291},[2225,203118,8146],{"class":2239},[2225,203120,203121,203123,203125,203127,203129],{"class":2227,"line":2304},[2225,203122,2775],{"class":2774},[2225,203124,190034],{"class":2239},[2225,203126,2781],{"class":2235},[2225,203128,68576],{"class":2291},[2225,203130,8146],{"class":2239},[2225,203132,203133],{"class":2227,"line":2356},[2225,203134,2285],{"emptyLinePlaceholder":2284},[2225,203136,203137,203139,203141,203143,203145,203147],{"class":2227,"line":2830},[2225,203138,2775],{"class":2774},[2225,203140,39676],{"class":2239},[2225,203142,2781],{"class":2235},[2225,203144,48541],{"class":2291},[2225,203146,2789],{"class":2239},[2225,203148,4200],{"class":2235},[2225,203150,203151,203153,203155,203157],{"class":2227,"line":2861},[2225,203152,202625],{"class":2307},[2225,203154,2311],{"class":2235},[2225,203156,190071],{"class":2239},[2225,203158,1707],{"class":2235},[2225,203160,203161,203164,203166,203168,203171,203173,203176,203178,203180],{"class":2227,"line":4246},[2225,203162,203163],{"class":2307},"  age",[2225,203165,2311],{"class":2235},[2225,203167,2236],{"class":2235},[2225,203169,203170],{"class":2239}," cold",[2225,203172,2298],{"class":2235},[2225,203174,203175],{"class":2307}," coldAfterDays",[2225,203177,2311],{"class":2235},[2225,203179,65133],{"class":6527},[2225,203181,2353],{"class":2235},[2225,203183,203184,203186],{"class":2227,"line":4253},[2225,203185,2359],{"class":2235},[2225,203187,2362],{"class":2239},[2225,203189,203190],{"class":2227,"line":4259},[2225,203191,2285],{"emptyLinePlaceholder":2284},[2225,203193,203194,203196,203198,203200,203202,203204,203206,203208,203210,203212,203214,203216,203218,203220,203222,203224,203226,203228,203230,203232,203234,203236],{"class":2227,"line":6548},[2225,203195,2775],{"class":2774},[2225,203197,2778],{"class":2239},[2225,203199,2781],{"class":2235},[2225,203201,2784],{"class":2231},[2225,203203,2744],{"class":2291},[2225,203205,2789],{"class":2239},[2225,203207,2792],{"class":2235},[2225,203209,7564],{"class":2239},[2225,203211,2298],{"class":2235},[2225,203213,2795],{"class":2307},[2225,203215,2311],{"class":2235},[2225,203217,2249],{"class":2235},[2225,203219,6408],{"class":2252},[2225,203221,2805],{"class":2235},[2225,203223,2298],{"class":2235},[2225,203225,2810],{"class":2307},[2225,203227,2311],{"class":2235},[2225,203229,2249],{"class":2235},[2225,203231,40260],{"class":2252},[2225,203233,2805],{"class":2235},[2225,203235,2243],{"class":2235},[2225,203237,2362],{"class":2239},[2225,203239,203240,203242,203244,203246,203248,203250,203252,203254,203256,203258,203260,203262],{"class":2227,"line":6576},[2225,203241,2775],{"class":2774},[2225,203243,2835],{"class":2239},[2225,203245,2781],{"class":2235},[2225,203247,2784],{"class":2231},[2225,203249,2842],{"class":2239},[2225,203251,1520],{"class":2235},[2225,203253,2847],{"class":2291},[2225,203255,2789],{"class":2239},[2225,203257,2805],{"class":2235},[2225,203259,6450],{"class":2252},[2225,203261,2805],{"class":2235},[2225,203263,2362],{"class":2239},[2225,203265,203266],{"class":2227,"line":6582},[2225,203267,2285],{"emptyLinePlaceholder":2284},[2225,203269,203270,203272,203274,203276,203278,203280,203282,203284,203286,203288,203290,203292,203294,203296,203298,203300,203302,203304,203306,203308,203310,203312,203314,203316,203318,203320,203322,203324],{"class":2227,"line":6587},[2225,203271,6500],{"class":2231},[2225,203273,6466],{"class":2239},[2225,203275,1520],{"class":2235},[2225,203277,6471],{"class":2291},[2225,203279,2789],{"class":2239},[2225,203281,2805],{"class":2235},[2225,203283,6487],{"class":2252},[2225,203285,2805],{"class":2235},[2225,203287,1381],{"class":2239},[2225,203289,1520],{"class":2235},[2225,203291,3161],{"class":2291},[2225,203293,2789],{"class":2239},[2225,203295,2805],{"class":2235},[2225,203297,6514],{"class":2252},[2225,203299,2805],{"class":2235},[2225,203301,2298],{"class":2235},[2225,203303,2236],{"class":2235},[2225,203305,7070],{"class":2307},[2225,203307,2311],{"class":2235},[2225,203309,2249],{"class":2235},[2225,203311,6514],{"class":2252},[2225,203313,2805],{"class":2235},[2225,203315,2298],{"class":2235},[2225,203317,6351],{"class":2307},[2225,203319,2311],{"class":2235},[2225,203321,6528],{"class":6527},[2225,203323,2243],{"class":2235},[2225,203325,2362],{"class":2239},[2225,203327,203328],{"class":2227,"line":7174},[2225,203329,2285],{"emptyLinePlaceholder":2284},[2225,203331,203332,203334,203337,203339,203341,203343,203345,203347,203349,203351,203353,203355],{"class":2227,"line":7180},[2225,203333,2775],{"class":2774},[2225,203335,203336],{"class":2239}," migrated ",[2225,203338,2781],{"class":2235},[2225,203340,2784],{"class":2231},[2225,203342,7564],{"class":2239},[2225,203344,1520],{"class":2235},[2225,203346,48456],{"class":2291},[2225,203348,2789],{"class":2239},[2225,203350,2805],{"class":2235},[2225,203352,6450],{"class":2252},[2225,203354,2805],{"class":2235},[2225,203356,2362],{"class":2239},[2225,203358,203359],{"class":2227,"line":7220},[2225,203360,203361],{"class":2763},"\u002F\u002F → 0   (inv-1 was just written — nothing is older than 90 days yet)\n",[937,203363,203364,203366,203367,203369,203370,203372,203373,203375,203376,203378],{},[941,203365,64920],{}," scans age-eligible collections and moves any record whose ",[941,203368,1703],{}," is older than ",[941,203371,64916],{}," from the hot store to ",[941,203374,17328],{},"; reads transparently fall through to ",[941,203377,17328],{}," when the hot store returns nothing, so callers never need to know which tier a record lives in.",[937,203380,10993,203381,968,203383,203385,203386,964,203389,203392,203393,203396,203397,203399,203400,203402,203403,203405,203406,183145,203408,203410],{},[941,203382,56991],{},[941,203384,64920],{}," also accepts an ",[950,203387,203388],{},"explicit\ncutoff",[941,203390,203391],{},"store.compact(vault, { before: isoTimestamp })"," migrates every record with\n",[941,203394,203395],{},"_ts \u003C before"," regardless of ",[941,203398,64916],{}," — and ",[941,203401,64916],{}," itself became optional:\n",[941,203404,189961],{}," alone declares a cold tier used only by explicitly-cut-off migrations. That\nper-call form is what the ",[1007,203407,776],{"href":777},[941,203409,189949],{},"\ndrives: period-driven cold archival of a closed accounting period, with the cutoff derived from\nthe period's end date instead of a rolling age.",[1591,203412,203414],{"id":203413},"resilience-and-metrics-middleware","Resilience and metrics middleware",[2217,203416,203418],{"className":2219,"code":203417,"language":2221,"meta":1037,"style":1037},"import { wrapStore, withRetry, withCircuitBreaker, withMetrics } from '@noy-db\u002Fhub'\nimport { toAwsDynamo } from '@noy-db\u002Fto-aws-dynamo'\n\nconst resilient = wrapStore(\n  toAwsDynamo({ table: 'myapp' }),\n  withRetry({ maxRetries: 3 }),\n  withCircuitBreaker({ failureThreshold: 5, onOpen: () => console.warn('backend unavailable') }),\n  withMetrics({ onOperation: (op) => console.log(op) }),\n)\n\nawait resilient.get('acme', 'invoices', 'inv-1')\n",[941,203419,203420,203450,203468,203472,203484,203509,203529,203577,203611,203615,203619],{"__ignoreMap":1037},[2225,203421,203422,203424,203426,203428,203430,203432,203434,203436,203438,203440,203442,203444,203446,203448],{"class":2227,"line":2228},[2225,203423,2232],{"class":2231},[2225,203425,2236],{"class":2235},[2225,203427,72089],{"class":2239},[2225,203429,2298],{"class":2235},[2225,203431,72094],{"class":2239},[2225,203433,2298],{"class":2235},[2225,203435,72099],{"class":2239},[2225,203437,2298],{"class":2235},[2225,203439,202555],{"class":2239},[2225,203441,2243],{"class":2235},[2225,203443,2246],{"class":2231},[2225,203445,2249],{"class":2235},[2225,203447,1086],{"class":2252},[2225,203449,2256],{"class":2235},[2225,203451,203452,203454,203456,203458,203460,203462,203464,203466],{"class":2227,"line":1041},[2225,203453,2232],{"class":2231},[2225,203455,2236],{"class":2235},[2225,203457,95763],{"class":2239},[2225,203459,2243],{"class":2235},[2225,203461,2246],{"class":2231},[2225,203463,2249],{"class":2235},[2225,203465,46865],{"class":2252},[2225,203467,2256],{"class":2235},[2225,203469,203470],{"class":2227,"line":1038},[2225,203471,2285],{"emptyLinePlaceholder":2284},[2225,203473,203474,203476,203478,203480,203482],{"class":2227,"line":2288},[2225,203475,2775],{"class":2774},[2225,203477,72139],{"class":2239},[2225,203479,2781],{"class":2235},[2225,203481,72089],{"class":2291},[2225,203483,11683],{"class":2239},[2225,203485,203486,203489,203491,203493,203495,203497,203499,203501,203503,203505,203507],{"class":2227,"line":2304},[2225,203487,203488],{"class":2291},"  toAwsDynamo",[2225,203490,2789],{"class":2239},[2225,203492,2792],{"class":2235},[2225,203494,47030],{"class":2307},[2225,203496,2311],{"class":2235},[2225,203498,2249],{"class":2235},[2225,203500,48176],{"class":2252},[2225,203502,2805],{"class":2235},[2225,203504,2243],{"class":2235},[2225,203506,1381],{"class":2239},[2225,203508,1707],{"class":2235},[2225,203510,203511,203513,203515,203517,203519,203521,203523,203525,203527],{"class":2227,"line":2356},[2225,203512,72202],{"class":2291},[2225,203514,2789],{"class":2239},[2225,203516,2792],{"class":2235},[2225,203518,72209],{"class":2307},[2225,203520,2311],{"class":2235},[2225,203522,14460],{"class":6527},[2225,203524,2243],{"class":2235},[2225,203526,1381],{"class":2239},[2225,203528,1707],{"class":2235},[2225,203530,203531,203533,203535,203537,203539,203541,203543,203545,203548,203550,203552,203554,203556,203558,203560,203562,203564,203567,203569,203571,203573,203575],{"class":2227,"line":2830},[2225,203532,72224],{"class":2291},[2225,203534,2789],{"class":2239},[2225,203536,2792],{"class":2235},[2225,203538,72231],{"class":2307},[2225,203540,2311],{"class":2235},[2225,203542,18996],{"class":6527},[2225,203544,2298],{"class":2235},[2225,203546,203547],{"class":2291}," onOpen",[2225,203549,2311],{"class":2235},[2225,203551,13130],{"class":2235},[2225,203553,8116],{"class":2774},[2225,203555,134201],{"class":2239},[2225,203557,1520],{"class":2235},[2225,203559,150565],{"class":2291},[2225,203561,2789],{"class":2239},[2225,203563,2805],{"class":2235},[2225,203565,203566],{"class":2252},"backend unavailable",[2225,203568,2805],{"class":2235},[2225,203570,8495],{"class":2239},[2225,203572,2359],{"class":2235},[2225,203574,1381],{"class":2239},[2225,203576,1707],{"class":2235},[2225,203578,203579,203581,203583,203585,203587,203589,203591,203593,203595,203597,203599,203601,203603,203605,203607,203609],{"class":2227,"line":2861},[2225,203580,202955],{"class":2291},[2225,203582,2789],{"class":2239},[2225,203584,2792],{"class":2235},[2225,203586,202962],{"class":2291},[2225,203588,2311],{"class":2235},[2225,203590,953],{"class":2235},[2225,203592,61313],{"class":7052},[2225,203594,1381],{"class":2235},[2225,203596,8116],{"class":2774},[2225,203598,134201],{"class":2239},[2225,203600,1520],{"class":2235},[2225,203602,27100],{"class":2291},[2225,203604,202982],{"class":2239},[2225,203606,2359],{"class":2235},[2225,203608,1381],{"class":2239},[2225,203610,1707],{"class":2235},[2225,203612,203613],{"class":2227,"line":4246},[2225,203614,2362],{"class":2239},[2225,203616,203617],{"class":2227,"line":4253},[2225,203618,2285],{"emptyLinePlaceholder":2284},[2225,203620,203621,203623,203625,203627,203629,203631,203633,203635,203637,203639,203641,203643,203645,203647,203649,203651,203653],{"class":2227,"line":4259},[2225,203622,6500],{"class":2231},[2225,203624,72272],{"class":2239},[2225,203626,1520],{"class":2235},[2225,203628,3158],{"class":2291},[2225,203630,2789],{"class":2239},[2225,203632,2805],{"class":2235},[2225,203634,6450],{"class":2252},[2225,203636,2805],{"class":2235},[2225,203638,2298],{"class":2235},[2225,203640,2249],{"class":2235},[2225,203642,6487],{"class":2252},[2225,203644,2805],{"class":2235},[2225,203646,2298],{"class":2235},[2225,203648,2249],{"class":2235},[2225,203650,6514],{"class":2252},[2225,203652,2805],{"class":2235},[2225,203654,2362],{"class":2239},[2217,203656,203658],{"className":6604,"code":203657,"language":141,"meta":1037,"style":1037},"{\n  \"method\": \"get\",\n  \"vault\": \"acme\",\n  \"collection\": \"invoices\",\n  \"id\": \"inv-1\",\n  \"durationMs\": 4,\n  \"success\": true\n}\n",[941,203659,203660,203664,203682,203700,203718,203736,203751,203764],{"__ignoreMap":1037},[2225,203661,203662],{"class":2227,"line":2228},[2225,203663,4200],{"class":2235},[2225,203665,203666,203668,203670,203672,203674,203676,203678,203680],{"class":2227,"line":1041},[2225,203667,9368],{"class":2235},[2225,203669,17574],{"class":2774},[2225,203671,6620],{"class":2235},[2225,203673,2311],{"class":2235},[2225,203675,6614],{"class":2235},[2225,203677,3158],{"class":2252},[2225,203679,6620],{"class":2235},[2225,203681,1707],{"class":2235},[2225,203683,203684,203686,203688,203690,203692,203694,203696,203698],{"class":2227,"line":1038},[2225,203685,9368],{"class":2235},[2225,203687,2694],{"class":2774},[2225,203689,6620],{"class":2235},[2225,203691,2311],{"class":2235},[2225,203693,6614],{"class":2235},[2225,203695,6450],{"class":2252},[2225,203697,6620],{"class":2235},[2225,203699,1707],{"class":2235},[2225,203701,203702,203704,203706,203708,203710,203712,203714,203716],{"class":2227,"line":2288},[2225,203703,9368],{"class":2235},[2225,203705,6471],{"class":2774},[2225,203707,6620],{"class":2235},[2225,203709,2311],{"class":2235},[2225,203711,6614],{"class":2235},[2225,203713,6487],{"class":2252},[2225,203715,6620],{"class":2235},[2225,203717,1707],{"class":2235},[2225,203719,203720,203722,203724,203726,203728,203730,203732,203734],{"class":2227,"line":2304},[2225,203721,9368],{"class":2235},[2225,203723,6983],{"class":2774},[2225,203725,6620],{"class":2235},[2225,203727,2311],{"class":2235},[2225,203729,6614],{"class":2235},[2225,203731,6514],{"class":2252},[2225,203733,6620],{"class":2235},[2225,203735,1707],{"class":2235},[2225,203737,203738,203740,203743,203745,203747,203749],{"class":2227,"line":2356},[2225,203739,9368],{"class":2235},[2225,203741,203742],{"class":2774},"durationMs",[2225,203744,6620],{"class":2235},[2225,203746,2311],{"class":2235},[2225,203748,100626],{"class":6527},[2225,203750,1707],{"class":2235},[2225,203752,203753,203755,203758,203760,203762],{"class":2227,"line":2830},[2225,203754,9368],{"class":2235},[2225,203756,203757],{"class":2774},"success",[2225,203759,6620],{"class":2235},[2225,203761,2311],{"class":2235},[2225,203763,71026],{"class":2235},[2225,203765,203766],{"class":2227,"line":2861},[2225,203767,7351],{"class":2235},[1591,203769,203771],{"id":203770},"maintenance-window-suspend-buffer-resume","Maintenance window: suspend, buffer, resume",[2217,203773,203775],{"className":2219,"code":203774,"language":2221,"meta":1037,"style":1037},"store.suspend('default', { queue: true })\n\u002F\u002F writes to the default route now buffer in memory instead of reaching the backend\nawait vault.collection('invoices').put('inv-2', { id: 'inv-2', amount: 50 })\n\nconst replayed = await store.resume('default')\n\u002F\u002F → 1   (the buffered put replays against the restored store)\n\nstore.routeStatus()\n",[941,203776,203777,203808,203813,203871,203875,203902,203907,203911],{"__ignoreMap":1037},[2225,203778,203779,203781,203783,203785,203787,203789,203791,203793,203795,203797,203800,203802,203804,203806],{"class":2227,"line":2228},[2225,203780,6951],{"class":2239},[2225,203782,1520],{"class":2235},[2225,203784,48453],{"class":2291},[2225,203786,2789],{"class":2239},[2225,203788,2805],{"class":2235},[2225,203790,64897],{"class":2252},[2225,203792,2805],{"class":2235},[2225,203794,2298],{"class":2235},[2225,203796,2236],{"class":2235},[2225,203798,203799],{"class":2307}," queue",[2225,203801,2311],{"class":2235},[2225,203803,2322],{"class":2321},[2225,203805,2243],{"class":2235},[2225,203807,2362],{"class":2239},[2225,203809,203810],{"class":2227,"line":1041},[2225,203811,203812],{"class":2763},"\u002F\u002F writes to the default route now buffer in memory instead of reaching the backend\n",[2225,203814,203815,203817,203819,203821,203823,203825,203827,203829,203831,203833,203835,203837,203839,203841,203843,203845,203847,203849,203851,203853,203855,203857,203859,203861,203863,203865,203867,203869],{"class":2227,"line":1038},[2225,203816,6500],{"class":2231},[2225,203818,6466],{"class":2239},[2225,203820,1520],{"class":2235},[2225,203822,6471],{"class":2291},[2225,203824,2789],{"class":2239},[2225,203826,2805],{"class":2235},[2225,203828,6487],{"class":2252},[2225,203830,2805],{"class":2235},[2225,203832,1381],{"class":2239},[2225,203834,1520],{"class":2235},[2225,203836,3161],{"class":2291},[2225,203838,2789],{"class":2239},[2225,203840,2805],{"class":2235},[2225,203842,26080],{"class":2252},[2225,203844,2805],{"class":2235},[2225,203846,2298],{"class":2235},[2225,203848,2236],{"class":2235},[2225,203850,7070],{"class":2307},[2225,203852,2311],{"class":2235},[2225,203854,2249],{"class":2235},[2225,203856,26080],{"class":2252},[2225,203858,2805],{"class":2235},[2225,203860,2298],{"class":2235},[2225,203862,6351],{"class":2307},[2225,203864,2311],{"class":2235},[2225,203866,37210],{"class":6527},[2225,203868,2243],{"class":2235},[2225,203870,2362],{"class":2239},[2225,203872,203873],{"class":2227,"line":2288},[2225,203874,2285],{"emptyLinePlaceholder":2284},[2225,203876,203877,203879,203882,203884,203886,203888,203890,203892,203894,203896,203898,203900],{"class":2227,"line":2304},[2225,203878,2775],{"class":2774},[2225,203880,203881],{"class":2239}," replayed ",[2225,203883,2781],{"class":2235},[2225,203885,2784],{"class":2231},[2225,203887,7564],{"class":2239},[2225,203889,1520],{"class":2235},[2225,203891,18320],{"class":2291},[2225,203893,2789],{"class":2239},[2225,203895,2805],{"class":2235},[2225,203897,64897],{"class":2252},[2225,203899,2805],{"class":2235},[2225,203901,2362],{"class":2239},[2225,203903,203904],{"class":2227,"line":2356},[2225,203905,203906],{"class":2763},"\u002F\u002F → 1   (the buffered put replays against the restored store)\n",[2225,203908,203909],{"class":2227,"line":2830},[2225,203910,2285],{"emptyLinePlaceholder":2284},[2225,203912,203913,203915,203917,203920],{"class":2227,"line":2861},[2225,203914,6951],{"class":2239},[2225,203916,1520],{"class":2235},[2225,203918,203919],{"class":2291},"routeStatus",[2225,203921,8146],{"class":2239},[2217,203923,203925],{"className":6604,"code":203924,"language":141,"meta":1037,"style":1037},"{ \"overrides\": {}, \"suspended\": [], \"queued\": {} }\n",[941,203926,203927],{"__ignoreMap":1037},[2225,203928,203929,203931,203933,203936,203938,203940,203943,203945,203948,203950,203952,203955,203957,203960,203962,203964,203966],{"class":2227,"line":2228},[2225,203930,2792],{"class":2235},[2225,203932,6614],{"class":2235},[2225,203934,203935],{"class":2774},"overrides",[2225,203937,6620],{"class":2235},[2225,203939,2311],{"class":2235},[2225,203941,203942],{"class":2235}," {},",[2225,203944,6614],{"class":2235},[2225,203946,203947],{"class":2774},"suspended",[2225,203949,6620],{"class":2235},[2225,203951,2311],{"class":2235},[2225,203953,203954],{"class":2235}," [],",[2225,203956,6614],{"class":2235},[2225,203958,203959],{"class":2774},"queued",[2225,203961,6620],{"class":2235},[2225,203963,2311],{"class":2235},[2225,203965,8896],{"class":2235},[2225,203967,6369],{"class":2235},[1022,203969,102889],{"id":102888},[1591,203971,203973],{"id":203972},"routestoreoptions",[941,203974,203975],{},"routeStore(options)",[2217,203977,203979],{"className":2219,"code":203978,"language":2221,"meta":1037,"style":1037},"function routeStore(options: RouteStoreOptions): RoutedNoydbStore\n",[941,203980,203981],{"__ignoreMap":1037},[2225,203982,203983,203985,203987,203989,203991,203993,203996,203998],{"class":2227,"line":2228},[2225,203984,12641],{"class":2774},[2225,203986,48541],{"class":2291},[2225,203988,2789],{"class":2235},[2225,203990,16362],{"class":7052},[2225,203992,2311],{"class":2235},[2225,203994,203995],{"class":6342}," RouteStoreOptions",[2225,203997,6255],{"class":2235},[2225,203999,204000],{"class":6342}," RoutedNoydbStore\n",[937,204002,204003,204004,204007,204008,1520],{},"Builds the store multiplexer. Returns a ",[941,204005,204006],{},"RoutedNoydbStore"," — a full store plus runtime control methods — that can be passed directly to ",[941,204009,202474],{},[24204,204011,204013],{"id":204012},"routestoreoptions-1",[941,204014,204015],{},"RouteStoreOptions",[1338,204017,204018,204028],{},[1341,204019,204020],{},[1344,204021,204022,204024,204026],{},[1347,204023,101104],{},[1347,204025,20194],{},[1347,204027,7834],{},[1357,204029,204030,204043,204072,204088,204106,204128,204163,204194,204228,204255],{},[1344,204031,204032,204036,204040],{},[1362,204033,204034],{},[941,204035,64897],{},[1362,204037,204038],{},[941,204039,3154],{},[1362,204041,204042],{},"Required. Store for every operation no other rule matches.",[1344,204044,204045,204049,204054],{},[1362,204046,204047],{},[941,204048,621],{},[1362,204050,204051],{},[941,204052,204053],{},"NoydbStore | { small?, large, threshold? }",[1362,204055,204056,204057,204060,204061,102573,204064,1625,204066,204068,204069,1520],{},"Routes blob chunk data to a dedicated store. Pass a size-tiered object to send blobs under ",[941,204058,204059],{},"threshold"," bytes (default ",[941,204062,204063],{},"400 * 1024",[941,204065,78274],{},[941,204067,64897],{}," and larger ones to ",[941,204070,204071],{},"large",[1344,204073,204074,204079,204083],{},[1362,204075,204076],{},[941,204077,204078],{},"routeBlobMeta",[1362,204080,204081],{},[941,204082,16861],{},[1362,204084,204085,204086,1520],{},"Also route blob index\u002Fslot\u002Fversion metadata to the blobs store. Default ",[941,204087,2560],{},[1344,204089,204090,204094,204099],{},[1362,204091,204092],{},[941,204093,64901],{},[1362,204095,204096],{},[941,204097,204098],{},"Record\u003Cstring, NoydbStore>",[1362,204100,204101,204102,204105],{},"Route specific ",[950,204103,204104],{},"collection names"," (exact match) to dedicated stores.",[1344,204107,204108,204112,204116],{},[1362,204109,204110],{},[941,204111,64905],{},[1362,204113,204114],{},[941,204115,204098],{},[1362,204117,204118,204119,33694,204122,204125,204126,1520],{},"Route by ",[950,204120,204121],{},"vault name prefix",[941,204123,204124],{},"'EU-'","). Checked before ",[941,204127,64901],{},[1344,204129,204130,204134,204139],{},[1362,204131,204132],{},[941,204133,64912],{},[1362,204135,204136],{},[941,204137,204138],{},"{ cold, coldAfterDays?, collections? }",[1362,204140,204141,204142,204144,204145,53922,204147,204149,204150,204152,204153,204155,204156,204158,204159,204162],{},"Hot\u002Fcold tiering: records older than ",[941,204143,64916],{}," migrate to ",[941,204146,17328],{},[941,204148,64920],{},"; reads fall through to ",[941,204151,17328],{}," when the hot store returns null. ",[941,204154,64916],{}," is optional since ",[941,204157,56991],{}," — omit it for period-driven archival only, where ",[941,204160,204161],{},"compact(vault, { before })"," supplies the cutoff per call.",[1344,204164,204165,204170,204174],{},[1362,204166,204167],{},[941,204168,204169],{},"blobRoutes",[1362,204171,204172],{},[941,204173,204098],{},[1362,204175,204176,204177,1625,204179,1625,204181,204183,204184,964,204187,204190,204191,1520],{},"Accepted and its stores are included in ",[941,204178,3170],{},[941,204180,3173],{},[941,204182,3127],{},", but ",[950,204185,204186],{},"not yet wired to routing decisions",[941,204188,204189],{},"storeFor()"," never consults it, so blob chunks are never routed by MIME type or ",[941,204192,204193],{},"storeHint",[1344,204195,204196,204201,204206],{},[1362,204197,204198],{},[941,204199,204200],{},"blobLifecycle",[1362,204202,204203],{},[941,204204,204205],{},"{ orphanRetentionDays?, archiveAfterDays?, archiveStore?, expireAfterDays? }",[1362,204207,204208,204209,204212,204213,1625,204215,1625,204217,204183,204219,964,204222,204224,204225,204227],{},"Accepted and ",[941,204210,204211],{},"archiveStore"," (if set) is included in ",[941,204214,3170],{},[941,204216,3173],{},[941,204218,3127],{},[950,204220,204221],{},"not yet evaluated anywhere",[941,204223,64920],{}," only performs ",[941,204226,64912],{},"-based hot\u002Fcold migration and never reads this option. No orphan cleanup, archival, or expiry runs.",[1344,204229,204230,204235,204239],{},[1362,204231,204232],{},[941,204233,204234],{},"overflow",[1362,204236,204237],{},[941,204238,3154],{},[1362,204240,204241,204242,1625,204244,1625,204246,204183,204248,204251,204252,204254],{},"Accepted and included in ",[941,204243,3170],{},[941,204245,3173],{},[941,204247,3127],{},[950,204249,204250],{},"the quota check that would activate it is not yet wired up"," — it is hardcoded to never trigger, so writes always go to ",[941,204253,64897],{}," regardless of usage.",[1344,204256,204257,204262,204266],{},[1362,204258,204259],{},[941,204260,204261],{},"quotaThreshold",[1362,204263,204264],{},[941,204265,20636],{},[1362,204267,204268,204269,204272,204273,204275],{},"Overflow trigger, 0–1. Default ",[941,204270,204271],{},"0.8",". Has no effect while ",[941,204274,204234],{}," is unwired (see above).",[937,204277,204278,204279,997,204281,204283,204284,1625,204286,204288,204289,204291,204292,204294,204295,111938,204297,1520],{},"There is no glob-pattern list evaluated in order — ",[941,204280,64901],{},[941,204282,64905],{}," are plain object maps, matched by exact collection name and vault-name prefix respectively. Resolution order for a given vault + collection: an active runtime ",[941,204285,12962],{},[941,204287,48453],{}," wins first, then ",[941,204290,64905],{}," (prefix), then ",[941,204293,64901],{}," (exact name), then blob routing, then quota ",[941,204296,204234],{},[941,204298,64897],{},[24204,204300,204302,204304],{"id":204301},"routednoydbstore-extra-methods-beyond-the-store-contract",[941,204303,204006],{}," — extra methods beyond the store contract",[1338,204306,204307,204317],{},[1341,204308,204309],{},[1344,204310,204311,204313,204315],{},[1347,204312,7831],{},[1347,204314,102906],{},[1347,204316,7834],{},[1357,204318,204319,204346,204373,204387,204425,204439,204457],{},[1344,204320,204321,204326,204330],{},[1362,204322,204323],{},[941,204324,204325],{},"compact(vault, opts?)",[1362,204327,204328],{},[941,204329,160412],{},[1362,204331,204332,204333,204336,204337,204339,204340,204342,204343,204345],{},"Migrates age-eligible records from hot to cold; returns the count migrated. ",[941,204334,204335],{},"opts.before"," (ISO timestamp, since ",[941,204338,56991],{},") migrates everything with ",[941,204341,203395],{}," instead of the rolling ",[941,204344,64916],{}," window.",[1344,204347,204348,204353,204358],{},[1362,204349,204350],{},[941,204351,204352],{},"override(route, store, opts?)",[1362,204354,204355],{},[941,204356,204357],{},"void | Promise\u003Cvoid>",[1362,204359,204360,204361,953,204364,204366,204367,204369,204370,204372],{},"Swap a named route's backing store at runtime. ",[941,204362,204363],{},"opts.hydrate",[941,204365,11790],{},", or a collection-name array) only switches the return type to a ",[941,204368,11740],{}," — it does ",[950,204371,1004],{}," copy any data itself; you must copy records from the original store into the new store before or during the call (see below).",[1344,204374,204375,204380,204384],{},[1362,204376,204377],{},[941,204378,204379],{},"clearOverride(route)",[1362,204381,204382],{},[941,204383,7165],{},[1362,204385,204386],{},"Revert a route to its original store.",[1344,204388,204389,204394,204398],{},[1362,204390,204391],{},[941,204392,204393],{},"suspend(route, opts?)",[1362,204395,204396],{},[941,204397,7165],{},[1362,204399,204400,204401,1746,204403,968,204405,1625,204407,204409,204410,1746,204412,5584,204414,204417,204418,204421,204422,204424],{},"Turn a route into a no-op sink (",[941,204402,3158],{},[941,204404,6651],{},[941,204406,3161],{},[941,204408,3164],{}," → no-op, ",[941,204411,3167],{},[941,204413,7262],{},[941,204415,204416],{},"opts.queue: true"," buffers writes for later replay; ",[941,204419,204420],{},"opts.maxQueueSize"," bounds the buffer (default ",[941,204423,202119],{},", oldest dropped first).",[1344,204426,204427,204432,204436],{},[1362,204428,204429],{},[941,204430,204431],{},"resume(route)",[1362,204433,204434],{},[941,204435,160412],{},[1362,204437,204438],{},"Reactivate a suspended route; replays any queued writes and returns how many succeeded.",[1344,204440,204441,204446,204451],{},[1362,204442,204443],{},[941,204444,204445],{},"routeStatus()",[1362,204447,204448],{},[941,204449,204450],{},"RouteStatus",[1362,204452,204453,204454,1520],{},"Snapshot: ",[941,204455,204456],{},"{ overrides, suspended, queued }",[1344,204458,204459,204464,204468],{},[1362,204460,204461],{},[941,204462,204463],{},"resolveBackend(vaultId)",[1362,204465,204466],{},[941,204467,3154],{},[1362,204469,204470,204471,204473,204474,204476],{},"Resolve which store a vault id maps to via ",[941,204472,64905],{},", falling back to ",[941,204475,64897],{},". Used for diagnostics.",[937,204478,204479,204481,204482,204485,204486,968,204489,968,204492,204495,204496,204498,204499,1520],{},[941,204480,41275],{}," above is an ",[941,204483,204484],{},"OverrideTarget",": the literal ",[941,204487,204488],{},"'default'",[941,204490,204491],{},"'blobs'",[941,204493,204494],{},"'cold'",", or any custom name — a collection from ",[941,204497,64901],{},", a prefix from ",[941,204500,64905],{},[24204,204502,204504],{"id":204503},"capabilities-of-a-routed-store","Capabilities of a routed store",[937,204506,10993,204507,52758,204509,204511,204512,204518,204519,204522,204523,204525],{},[941,204508,56991],{},[941,204510,48447],{}," surfaces its ",[950,204513,204514,204515,204517],{},"primary (",[941,204516,64897],{},") store's capabilities","\n(previously it exposed none), layering ",[941,204520,204521],{},"coldArchival: true"," on top when — and only when — the\nrouter itself declares an ",[941,204524,64912],{}," cold route. Two consequences:",[1111,204527,204528,204539],{},[1114,204529,204530,204531,7808,204536,204538],{},"Features gated on a store capability now see through the router: e.g. the gap-free\n",[1007,204532,204533],{"href":837},[941,204534,204535],{},"sequence().next()",[941,204537,9539],{},", and is permitted on a\nrouteStore-backed vault whenever the primary store reports it — previously it refused on any\nrouted store.",[1114,204540,204541,204543,204544,204549,204550,204552],{},[941,204542,189965],{}," is what ",[1007,204545,204546],{"href":777},[941,204547,204548],{},"vault.archivePeriod()"," checks before archiving. A\nrouter ",[950,204551,12773],{}," its own cold route never advertises it, even when nested over a cold-capable\nprimary.",[1591,204554,204556],{"id":204555},"wrapstorestore-middlewares",[941,204557,204558],{},"wrapStore(store, ...middlewares)",[2217,204560,204562],{"className":2219,"code":204561,"language":2221,"meta":1037,"style":1037},"type StoreMiddleware = (next: NoydbStore) => NoydbStore\nfunction wrapStore(store: NoydbStore, ...middlewares: StoreMiddleware[]): NoydbStore\n",[941,204563,204564,204587],{"__ignoreMap":1037},[2225,204565,204566,204568,204571,204573,204575,204577,204579,204581,204583,204585],{"class":2227,"line":2228},[2225,204567,6339],{"class":2774},[2225,204569,204570],{"class":6342}," StoreMiddleware",[2225,204572,6346],{"class":2235},[2225,204574,953],{"class":2235},[2225,204576,1454],{"class":7052},[2225,204578,2311],{"class":2235},[2225,204580,3404],{"class":6342},[2225,204582,1381],{"class":2235},[2225,204584,8116],{"class":2774},[2225,204586,194096],{"class":6342},[2225,204588,204589,204591,204593,204595,204597,204599,204601,204603,204605,204608,204610,204612,204614,204616],{"class":2227,"line":1041},[2225,204590,12641],{"class":2774},[2225,204592,72089],{"class":2291},[2225,204594,2789],{"class":2235},[2225,204596,6951],{"class":7052},[2225,204598,2311],{"class":2235},[2225,204600,3404],{"class":6342},[2225,204602,2298],{"class":2235},[2225,204604,67770],{"class":2235},[2225,204606,204607],{"class":7052},"middlewares",[2225,204609,2311],{"class":2235},[2225,204611,204570],{"class":6342},[2225,204613,7262],{"class":2239},[2225,204615,6255],{"class":2235},[2225,204617,194096],{"class":6342},[937,204619,204620,204621,204623,204624,204626],{},"Composes any number of middleware around a store, left-to-right — the first argument after ",[941,204622,6951],{}," is outermost. ",[941,204625,71563],{}," takes middleware as variadic arguments, not an array.",[1591,204628,204629],{"id":202485},"Middleware",[1338,204631,204632,204643],{},[1341,204633,204634],{},[1344,204635,204636,204638,204641],{},[1347,204637,9533],{},[1347,204639,204640],{},"Options",[1347,204642,7834],{},[1357,204644,204645,204684,204713,204735,204774,204810],{},[1344,204646,204647,204652,204673],{},[1362,204648,204649],{},[941,204650,204651],{},"withRetry(opts?)",[1362,204653,204654,30200,204657,1884,204659,30200,204662,1884,204665,30200,204668,1884,204670],{},[941,204655,204656],{},"maxRetries",[941,204658,5512],{},[941,204660,204661],{},"backoffMs",[941,204663,204664],{},"500",[941,204666,204667],{},"jitter",[941,204669,10308],{},[941,204671,204672],{},"retryOn?: string[]",[1362,204674,204675,204676,204679,204680,204683],{},"Retries a failed call with exponential backoff and jitter. ",[941,204677,204678],{},"retryOn"," limits retries to matching ",[941,204681,204682],{},"error.code","s; omitted, every error retries.",[1344,204685,204686,204691,204707],{},[1362,204687,204688],{},[941,204689,204690],{},"withLogging(opts?)",[1362,204692,204693,30200,204696,1884,204699,968,204702,30200,204705,1381],{},[941,204694,204695],{},"level",[941,204697,204698],{},"'info'",[941,204700,204701],{},"logger?",[941,204703,204704],{},"logData",[941,204706,2560],{},[1362,204708,204709,204710,1520],{},"Logs every call's method, arguments, and duration. Envelope payloads are redacted unless ",[941,204711,204712],{},"logData: true",[1344,204714,204715,204720,204725],{},[1362,204716,204717],{},[941,204718,204719],{},"withMetrics(opts)",[1362,204721,204722],{},[941,204723,204724],{},"onOperation: (op: StoreOperation) => void",[1362,204726,204727,204728,204731,204732,1520],{},"Calls ",[941,204729,204730],{},"onOperation"," after every call with ",[941,204733,204734],{},"{ method, vault, collection?, id?, durationMs, success, error? }",[1344,204736,204737,204742,204761],{},[1362,204738,204739],{},[941,204740,204741],{},"withCircuitBreaker(opts?)",[1362,204743,204744,30200,204747,1884,204749,30200,204752,1884,204755,968,204758],{},[941,204745,204746],{},"failureThreshold",[941,204748,28757],{},[941,204750,204751],{},"resetTimeoutMs",[941,204753,204754],{},"30_000",[941,204756,204757],{},"onOpen?",[941,204759,204760],{},"onClose?",[1362,204762,204763,204764,204766,204767,1625,204769,1625,204771,204773],{},"Opens after ",[941,204765,204746],{}," consecutive failures, short-circuiting to safe fallbacks (",[941,204768,6651],{},[941,204770,7262],{},[941,204772,9261],{},") until a half-open probe succeeds.",[1344,204775,204776,204781,204793],{},[1362,204777,204778],{},[941,204779,204780],{},"withCache(opts?)",[1362,204782,204783,30200,204786,1884,204788,30200,204790,1381],{},[941,204784,204785],{},"maxEntries",[941,204787,204664],{},[941,204789,75026],{},[941,204791,204792],{},"60_000",[1362,204794,204795,204796,1952,204798,1625,204800,204802,204803,1625,204805,1625,204807,204809],{},"Read-through LRU cache for ",[941,204797,14639],{},[941,204799,3161],{},[941,204801,3164],{}," invalidate the entry immediately; ",[941,204804,3167],{},[941,204806,3170],{},[941,204808,3173],{}," bypass the cache entirely. Caches encrypted envelopes, not plaintext.",[1344,204811,204812,204817,204843],{},[1362,204813,204814],{},[941,204815,204816],{},"withHealthCheck(opts?)",[1362,204818,204819,30200,204822,1884,204824,30200,204827,1884,204829,30200,204832,1884,204834,968,204837,968,204840],{},[941,204820,204821],{},"checkIntervalMs",[941,204823,204754],{},[941,204825,204826],{},"suspendAfterFailures",[941,204828,5512],{},[941,204830,204831],{},"resumeAfterSuccess",[941,204833,5437],{},[941,204835,204836],{},"onSuspend?",[941,204838,204839],{},"onResume?",[941,204841,204842],{},"check?",[1362,204844,204845,204846,204849,204850,204853],{},"Pings the store on an interval (",[941,204847,204848],{},"store.ping()",", or a custom ",[941,204851,204852],{},"check()","); auto-suspends the store after repeated failures and auto-resumes on recovery.",[1022,204855,178796],{"id":178795},[937,204857,204858,204859,968,204861,204863,204864,204866,204867,204870],{},"There is no opt-in step: ",[941,204860,4620],{},[941,204862,71563],{},", and the middleware factories are core exports of ",[941,204865,1086],{},", available to every consumer whether or not they're used. Plain single-store usage (",[941,204868,204869],{},"createNoydb({ store: someStore })",") simply never calls them.",[1022,204872,114684],{"id":114683},[1111,204874,204875,204894,204906],{},[1114,204876,204877,204881,204882,1625,204884,56403,204886,204888,204889,204891,204892,53239],{},[950,204878,204879],{},[1007,204880,629],{"href":629}," — a pod store (",[941,204883,114926],{},[941,204885,115937],{},[941,204887,70036],{},") can be one leg of a ",[941,204890,48447],{}," setup, e.g. archiving to a ",[941,204893,1402],{},[1114,204895,204896,165412,204900,204902,204903,204905],{},[950,204897,204898],{},[1007,204899,719],{"href":719},[941,204901,6836],{}," collections combine naturally with ",[941,204904,64912],{},"-based hot\u002Fcold routing",[1114,204907,204908,204912,204913],{},[950,204909,204910],{},[1007,204911,705],{"href":705}," — required if a routed store's lazy-mode collections need ",[941,204914,24807],{},[1022,204916,6825],{"id":6824},[1111,204918,204919,204950,204962,204983,205001,205022,205032],{},[1114,204920,204921,968,204923,968,204925,2462,204927,204929,204930,204932,204933,1625,204935,1625,204937,204939,204940,1625,204942,204944,204945,4688,204947,204949],{},[941,204922,204234],{},[941,204924,204261],{},[941,204926,204169],{},[941,204928,204200],{}," are accepted by ",[941,204931,204015],{}," and their stores participate in ",[941,204934,3170],{},[941,204936,3173],{},[941,204938,3127],{},", but none of the four affects a routing decision in the current version: quota-triggered overflow can never trigger, and ",[941,204941,204169],{},[941,204943,204200],{}," are never read by ",[941,204946,204189],{},[941,204948,64920],{},". Configuring them is a no-op beyond making those stores reachable for bulk export\u002Fimport.",[1114,204951,204952,204955,204956,204958,204959,204961],{},[941,204953,204954],{},"resolveBackend()"," only consults ",[941,204957,64905],{},"; it does not account for per-collection ",[941,204960,64901],{}," overrides — it exists for the vault-prefix geographic-routing case specifically.",[1114,204963,204964,204965,1746,204967,968,204969,1746,204971,968,204973,1746,204975,204977,204978,28806,204980,204982],{},"The circuit breaker's fallback values are type-shaped but empty (",[941,204966,3158],{},[941,204968,6651],{},[941,204970,3167],{},[941,204972,7262],{},[941,204974,3170],{},[941,204976,9261],{},") — callers see \"no data\" rather than an error while the circuit is open. Pair ",[941,204979,4630],{},[941,204981,4624],{}," (listed before it) so transient errors are absorbed before they count toward the failure threshold.",[1114,204984,204985,204987,204988,204990,204991,968,204993,2462,204995,204997,204998,205000],{},[941,204986,4627],{}," is store-level: it caches whatever ",[941,204989,14639],{}," returns per vault\u002Fcollection\u002Fid. ",[941,204992,3167],{},[941,204994,3170],{},[941,204996,3173],{}," always bypass it, so cached data can't go stale from a bulk write you didn't also ",[941,204999,14639],{}," through the same wrapped store.",[1114,205002,205003,205004,205006,205007,205010,205011,205013,205014,1625,205016,205018,205019,205021],{},"Size-tiered ",[941,205005,621],{}," routing (",[941,205008,205009],{},"{ small, large, threshold }",") can only pick a store by size at write time — ",[941,205012,20953],{}," sees the envelope's byte length, but a plain ",[941,205015,14639],{},[941,205017,24407],{}," route for blob chunks defaults to ",[941,205020,204071],{}," when it can't determine size up front.",[1114,205023,205024,205027,205028,205031],{},[941,205025,205026],{},"suspend(route, { queue: true })"," drops the oldest queued write once ",[941,205029,205030],{},"maxQueueSize"," is reached; size the queue for your expected outage window if you rely on it.",[1114,205033,205034,205037,205038,205041,205042,205045,205046,205048,205049,205052,205053,205055,205056,205058],{},[941,205035,205036],{},"override(route, store, { hydrate: true })"," does not copy any data — ",[941,205039,205040],{},"hydrate"," only makes ",[941,205043,205044],{},"override()"," return a ",[941,205047,11740],{}," instead of resolving synchronously. You are responsible for copying every record from the route's current store into the new store yourself, e.g. ",[941,205050,205051],{},"await copyAllRecords(oldStore, newStore)"," before calling ",[941,205054,205044],{},", or inline inside your own ",[941,205057,70491],{}," wrapper around it. Skip this and the route switches to an empty store.",[1022,205060,2621],{"id":2620},[1111,205062,205063,205068,205073,205078,205085,205090,205097],{},[1114,205064,205065,205067],{},[1007,205066,629],{"href":630}," — pod stores as a routing target",[1114,205069,205070,205072],{},[1007,205071,719],{"href":720}," — bounded-memory collections that pair well with hot\u002Fcold routing",[1114,205074,205075,205077],{},[1007,205076,621],{"href":622}," — the usual reason to route: chunked binary data on a separate backend",[1114,205079,205080,141142,205082,205084],{},[1007,205081,705],{"href":706},[941,205083,24807],{}," on a routed store's lazy collections",[1114,205086,205087,205089],{},[1007,205088,6891],{"href":51}," — the store contract every route and middleware layer satisfies",[1114,205091,205092,964,205094,205096],{},[1007,205093,776],{"href":777},[941,205095,204548],{},", the period-driven consumer of the cold route",[1114,205098,101581,205099,1520],{},[1007,205100,205102],{"href":92528,"rel":205101},[3371],[941,205103,205104],{},"05-storage-routing",[2645,205106,205107],{},"html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}html pre.shiki code .sHdIc, html code.shiki .sHdIc{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#EEFFFF;--shiki-default-font-style:italic;--shiki-dark:#BABED8;--shiki-dark-font-style:italic}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html pre.shiki code .sfNiH, html code.shiki .sfNiH{--shiki-light:#FF5370;--shiki-default:#FF9CAC;--shiki-dark:#FF9CAC}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}",{"title":1037,"searchDepth":1038,"depth":1038,"links":205109},[205110,205111,205112,205113,205118,205128,205129,205130,205131],{"id":29856,"depth":1041,"text":29857},{"id":111659,"depth":1041,"text":111660},{"id":58393,"depth":1041,"text":58394},{"id":29892,"depth":1041,"text":29893,"children":205114},[205115,205116,205117],{"id":203056,"depth":1038,"text":203057},{"id":203413,"depth":1038,"text":203414},{"id":203770,"depth":1038,"text":203771},{"id":102888,"depth":1041,"text":102889,"children":205119},[205120,205126,205127],{"id":203972,"depth":1038,"text":203975,"children":205121},[205122,205123,205125],{"id":204012,"depth":2288,"text":204015},{"id":204301,"depth":2288,"text":205124},"RoutedNoydbStore — extra methods beyond the store contract",{"id":204503,"depth":2288,"text":204504},{"id":204555,"depth":1038,"text":204558},{"id":202485,"depth":1038,"text":204629},{"id":178795,"depth":1041,"text":178796},{"id":114683,"depth":1041,"text":114684},{"id":6824,"depth":1041,"text":6825},{"id":2620,"depth":1041,"text":2621},"Multi-store routing (routeStore()) and composable store middleware (wrapStore() + retry\u002Fcircuit-breaker\u002Fhealth\u002Fcache\u002Flogging\u002Fmetrics) — a core feature.",{},{"title":808,"description":205132},"8Q0icvexHbJrOog66HbO3Sjshdw__ME0jJU8DatqaKY",{"id":205137,"title":812,"access":12,"apiToc":929,"body":205138,"category":1042,"description":207216,"extension":1044,"meta":207217,"navigation":1042,"nest":1047,"path":813,"seo":207218,"sinceVersion":57147,"status":38550,"stem":814,"subtitle":1042,"tier":1050,"tocExternal":1042,"__hash__":207219},"docs\u002Fservices\u002Fsatellites.md",{"type":931,"value":205139,"toc":207195},[205140,205142,205145,205164,205166,205174,205189,205195,205199,205205,205335,205375,205455,205457,205474,205791,205793,205797,205805,206086,206090,206093,206186,206190,206193,206342,206344,206350,206393,206453,206484,206490,206539,206551,206562,206723,206756,206762,206822,206826,206832,206950,206954,207045,207047,207141,207143,207177,207179,207192],[934,205141,812],{"id":812},[46871,205143],{":rows":205144},"[{\"label\":\"Subpath\",\"value\":\"`@noy-db\u002Fhub\u002Fsatellites` — types (`SatelliteSpec`, `PairingMarker`, `JoinedHandle`) and `SatelliteConfigError`\"},{\"label\":\"Wiring\",\"value\":\"no `with*()` strategy — pass `satelliteOf`\u002F`fields`\u002F`joined` to `vault.collection()` and call `vault.joined()`, both core `Vault` methods from `@noy-db\u002Fhub` root\"},{\"label\":\"Layer\",\"value\":\"`with-shape`\"},{\"label\":\"Cluster\",\"value\":\"D — Data Shape\"}]",[1061,205146,205147],{},[937,205148,205149,205150,205153,205154,9515,205156,56845,205158,205161,205162,1520],{},"Preview feature (",[941,205151,205152],{},"status: \"experimental\"","): the shape and error messages below are stable\nenough to build against, but may still change before the API is marked stable. Ships in\n",[941,205155,1086],{},[941,205157,57147],{},[941,205159,205160],{},"@latest"," as of ",[941,205163,56850],{},[1022,205165,6242],{"id":6241},[937,205167,205168,205169,205173],{},"Every noy-db record is encrypted as one ",[950,205170,205171],{},[1007,205172,1659],{"href":65194}," —\nthe ciphertext blob a store actually persists. There is no partial decryption: reading any\nsingle field means decrypting the whole envelope. That's fine for small records, but it gets\nexpensive when a collection mixes a few small fields you read constantly (a sender, a\nsubject line, a received date) with one or two large fields you rarely need (a full message\nbody, a long document). Every list view pays to decrypt the large field too, even though it\nnever displays it.",[937,205175,3289,205176,205179,205180,205182,205183,205185,205186,205188],{},[950,205177,205178],{},"satellite collection"," solves this by splitting the record in two at the modeling level:\na small, hot ",[950,205181,57272],{}," collection holds the fields you read often, and a separate ",[950,205184,4836],{},"\ncollection — sharing the base's record id — holds the heavy fields. A third, virtual\n",[950,205187,57598],{}," handle reads and writes both as if they were one record, for the code paths that\ndo need the full thing:",[2217,205190,205193],{"className":205191,"code":205192,"language":5672},[5670],"msgs:       msg_id, from, to, subject_short, received_date   ← hot, small, listed constantly\nmsgs_text:  msg_id, subject, body                            ← cold, large, rarely loaded\n",[941,205194,205192],{"__ignoreMap":1037},[1022,205196,205198],{"id":205197},"the-model","The model",[937,205200,205201,205202,205204],{},"You declare the pairing where you declare the satellite collection: its ",[941,205203,57285],{}," list is the\nrouting table that says which fields live on that side of the split.",[2217,205206,205208],{"className":2219,"code":205207,"language":2221,"meta":1037,"style":1037},"vault.collection('msgs', { \u002F* schema: from, to, subject_short, received_date *\u002F })\n\nvault.collection('msgs_text', {\n  satelliteOf: 'msgs',          \u002F\u002F pairs this collection to 'msgs' by shared record id\n  fields: ['subject', 'body'],  \u002F\u002F routing table — explicit, fixed at declaration\n  joined: 'msgs_full',          \u002F\u002F optional: registers vault.joined('msgs_full')\n  \u002F* schema: subject, body *\u002F\n})\n",[941,205209,205210,205237,205241,205261,205278,205307,205324,205329],{"__ignoreMap":1037},[2225,205211,205212,205214,205216,205218,205220,205222,205224,205226,205228,205230,205233,205235],{"class":2227,"line":2228},[2225,205213,2694],{"class":2239},[2225,205215,1520],{"class":2235},[2225,205217,6471],{"class":2291},[2225,205219,2789],{"class":2239},[2225,205221,2805],{"class":2235},[2225,205223,57468],{"class":2252},[2225,205225,2805],{"class":2235},[2225,205227,2298],{"class":2235},[2225,205229,2236],{"class":2235},[2225,205231,205232],{"class":2763}," \u002F* schema: from, to, subject_short, received_date *\u002F",[2225,205234,2243],{"class":2235},[2225,205236,2362],{"class":2239},[2225,205238,205239],{"class":2227,"line":1041},[2225,205240,2285],{"emptyLinePlaceholder":2284},[2225,205242,205243,205245,205247,205249,205251,205253,205255,205257,205259],{"class":2227,"line":1038},[2225,205244,2694],{"class":2239},[2225,205246,1520],{"class":2235},[2225,205248,6471],{"class":2291},[2225,205250,2789],{"class":2239},[2225,205252,2805],{"class":2235},[2225,205254,57496],{"class":2252},[2225,205256,2805],{"class":2235},[2225,205258,2298],{"class":2235},[2225,205260,2301],{"class":2235},[2225,205262,205263,205265,205267,205269,205271,205273,205275],{"class":2227,"line":2288},[2225,205264,57507],{"class":2307},[2225,205266,2311],{"class":2235},[2225,205268,2249],{"class":2235},[2225,205270,57468],{"class":2252},[2225,205272,2805],{"class":2235},[2225,205274,2298],{"class":2235},[2225,205276,205277],{"class":2763},"          \u002F\u002F pairs this collection to 'msgs' by shared record id\n",[2225,205279,205280,205282,205284,205286,205288,205290,205292,205294,205296,205298,205300,205302,205304],{"class":2227,"line":2304},[2225,205281,57522],{"class":2307},[2225,205283,2311],{"class":2235},[2225,205285,8667],{"class":2239},[2225,205287,2805],{"class":2235},[2225,205289,57531],{"class":2252},[2225,205291,2805],{"class":2235},[2225,205293,2298],{"class":2235},[2225,205295,2249],{"class":2235},[2225,205297,42850],{"class":2252},[2225,205299,2805],{"class":2235},[2225,205301,8803],{"class":2239},[2225,205303,2298],{"class":2235},[2225,205305,205306],{"class":2763},"  \u002F\u002F routing table — explicit, fixed at declaration\n",[2225,205308,205309,205311,205313,205315,205317,205319,205321],{"class":2227,"line":2356},[2225,205310,57553],{"class":2307},[2225,205312,2311],{"class":2235},[2225,205314,2249],{"class":2235},[2225,205316,57560],{"class":2252},[2225,205318,2805],{"class":2235},[2225,205320,2298],{"class":2235},[2225,205322,205323],{"class":2763},"          \u002F\u002F optional: registers vault.joined('msgs_full')\n",[2225,205325,205326],{"class":2227,"line":2830},[2225,205327,205328],{"class":2763},"  \u002F* schema: subject, body *\u002F\n",[2225,205330,205331,205333],{"class":2227,"line":2861},[2225,205332,2359],{"class":2235},[2225,205334,2362],{"class":2239},[1111,205336,205337,205343,205360,205366],{},[1114,205338,205339,205342],{},[950,205340,205341],{},"Exactly one satellite per base in v1"," — the shared key is the record id itself, which is\nwhat makes every satellite read an O(1) same-id lookup, never a scan.",[1114,205344,205345,205346,205348,205349,968,205351,2156,205353,205356,205357,205359],{},"The pairing (",[941,205347,57276],{},", a hash of ",[941,205350,57285],{},[941,205352,57598],{},[950,205354,205355],{},"persisted",", not\nsession-local: the first declaration writes a pairing marker into the reserved ",[941,205358,35623],{},"\ncollection. A later declaration whose config disagrees with that marker is refused (see\nRefusal matrix, R-S9) rather than silently splitting records differently across app\nversions.",[1114,205361,205362,205365],{},[950,205363,205364],{},"No auto-create."," A base write never creates a satellite envelope — absent reads as\nall-null, so there's nothing to create. The satellite envelope is born on its first real\nsatellite or joined write.",[1114,205367,205368,205369,205371,205372,205374],{},"The base is oblivious and standalone: ",[941,205370,57468],{}," works fully on its own and knows nothing about\n",[941,205373,57496],{},". Only the vault's pairing registry knows the link.",[1338,205376,205377,205391],{},[1341,205378,205379],{},[1344,205380,205381,205384,205386,205388],{},[1347,205382,205383],{},"Handle",[1347,205385,68053],{},[1347,205387,11385],{},[1347,205389,205390],{},"Existence check",[1357,205392,205393,205414,205434],{},[1344,205394,205395,205400,205405,205408],{},[1362,205396,205397],{},[950,205398,205399],{},"Base",[1362,205401,205402],{},[941,205403,205404],{},"msgs.get(id)",[1362,205406,205407],{},"Hot fields only — no satellite envelope fetched or decrypted",[1362,205409,205410,205411,205413],{},"none needed; the base ",[1092,205412,3082],{}," the authority",[1344,205415,205416,205421,205426,205429],{},[1362,205417,205418],{},[950,205419,205420],{},"Satellite",[1362,205422,205423],{},[941,205424,205425],{},"msgs_text.get(id)",[1362,205427,205428],{},"Heavy fields only, validated by the satellite's own schema",[1362,205430,205431,205433],{},[941,205432,6651],{}," if the base is absent or tombstoned, even if a satellite envelope exists",[1344,205435,205436,205441,205446,205449],{},[1362,205437,205438],{},[950,205439,205440],{},"Joined",[1362,205442,205443],{},[941,205444,205445],{},"vault.joined('msgs_full')",[1362,205447,205448],{},"Base ⊕ satellite, merged by field (two O(1) same-id gets)",[1362,205450,205451,205452,205454],{},"absent satellite reads as all-null; ",[941,205453,6651],{}," if the base is absent or tombstoned",[1022,205456,99864],{"id":99863},[937,205458,205459,205460,205462,205463,205465,205466,1625,205468,1625,205470,77939,205472,2311],{},"Satellite pairing isn't a ",[941,205461,943],{}," strategy — there's nothing to enable on ",[941,205464,12780],{},".\nYou opt a collection in per-declaration by passing ",[941,205467,57276],{},[941,205469,57285],{},[941,205471,57598],{},[941,205473,2138],{},[2217,205475,205477],{"className":2219,"code":205476,"language":2221,"meta":1037,"style":1037},"import { createNoydb } from '@noy-db\u002Fhub'\nimport { toMemory } from '@noy-db\u002Fto-memory'\n\ninterface Msg extends Record\u003Cstring, unknown> {\n  from?: string\n  subject?: string\n  body?: string\n}\n\nconst db = await createNoydb({\n  store: toMemory(),\n  user: 'alice',\n  secret: 'correct horse battery staple',\n})\nconst vault = await db.openVault('inbox')\n\nvault.collection\u003CMsg>('msgs')             \u002F\u002F base — hot fields only\n\nvault.collection\u003CMsg>('msgs_text', {      \u002F\u002F satellite — heavy fields\n  satelliteOf: 'msgs',\n  fields: ['subject', 'body'],\n  joined: 'msgs_full',                    \u002F\u002F registers vault.joined('msgs_full')\n})\n",[941,205478,205479,205497,205515,205519,205542,205550,205558,205566,205570,205574,205590,205602,205616,205630,205636,205663,205667,205695,205699,205728,205742,205768,205785],{"__ignoreMap":1037},[2225,205480,205481,205483,205485,205487,205489,205491,205493,205495],{"class":2227,"line":2228},[2225,205482,2232],{"class":2231},[2225,205484,2236],{"class":2235},[2225,205486,2744],{"class":2239},[2225,205488,2243],{"class":2235},[2225,205490,2246],{"class":2231},[2225,205492,2249],{"class":2235},[2225,205494,1086],{"class":2252},[2225,205496,2256],{"class":2235},[2225,205498,205499,205501,205503,205505,205507,205509,205511,205513],{"class":2227,"line":1041},[2225,205500,2232],{"class":2231},[2225,205502,2236],{"class":2235},[2225,205504,68576],{"class":2239},[2225,205506,2243],{"class":2235},[2225,205508,2246],{"class":2231},[2225,205510,2249],{"class":2235},[2225,205512,9638],{"class":2252},[2225,205514,2256],{"class":2235},[2225,205516,205517],{"class":2227,"line":1038},[2225,205518,2285],{"emptyLinePlaceholder":2284},[2225,205520,205521,205523,205526,205528,205530,205532,205534,205536,205538,205540],{"class":2227,"line":2288},[2225,205522,6994],{"class":2774},[2225,205524,205525],{"class":6342}," Msg",[2225,205527,124350],{"class":2774},[2225,205529,22527],{"class":6342},[2225,205531,6474],{"class":2235},[2225,205533,7259],{"class":6342},[2225,205535,2298],{"class":2235},[2225,205537,40717],{"class":6342},[2225,205539,6480],{"class":2235},[2225,205541,2301],{"class":2235},[2225,205543,205544,205546,205548],{"class":2227,"line":2304},[2225,205545,78520],{"class":2307},[2225,205547,7011],{"class":2235},[2225,205549,7014],{"class":6342},[2225,205551,205552,205554,205556],{"class":2227,"line":2356},[2225,205553,78563],{"class":2307},[2225,205555,7011],{"class":2235},[2225,205557,7014],{"class":6342},[2225,205559,205560,205562,205564],{"class":2227,"line":2830},[2225,205561,86679],{"class":2307},[2225,205563,7011],{"class":2235},[2225,205565,7014],{"class":6342},[2225,205567,205568],{"class":2227,"line":2861},[2225,205569,7351],{"class":2235},[2225,205571,205572],{"class":2227,"line":4246},[2225,205573,2285],{"emptyLinePlaceholder":2284},[2225,205575,205576,205578,205580,205582,205584,205586,205588],{"class":2227,"line":4253},[2225,205577,2775],{"class":2774},[2225,205579,2778],{"class":2239},[2225,205581,2781],{"class":2235},[2225,205583,2784],{"class":2231},[2225,205585,2744],{"class":2291},[2225,205587,2789],{"class":2239},[2225,205589,4200],{"class":2235},[2225,205591,205592,205594,205596,205598,205600],{"class":2227,"line":4259},[2225,205593,15608],{"class":2307},[2225,205595,2311],{"class":2235},[2225,205597,68576],{"class":2291},[2225,205599,4228],{"class":2239},[2225,205601,1707],{"class":2235},[2225,205603,205604,205606,205608,205610,205612,205614],{"class":2227,"line":6548},[2225,205605,4205],{"class":2307},[2225,205607,2311],{"class":2235},[2225,205609,2249],{"class":2235},[2225,205611,6408],{"class":2252},[2225,205613,2805],{"class":2235},[2225,205615,1707],{"class":2235},[2225,205617,205618,205620,205622,205624,205626,205628],{"class":2227,"line":6576},[2225,205619,10423],{"class":2307},[2225,205621,2311],{"class":2235},[2225,205623,2249],{"class":2235},[2225,205625,40260],{"class":2252},[2225,205627,2805],{"class":2235},[2225,205629,1707],{"class":2235},[2225,205631,205632,205634],{"class":2227,"line":6582},[2225,205633,2359],{"class":2235},[2225,205635,2362],{"class":2239},[2225,205637,205638,205640,205642,205644,205646,205648,205650,205652,205654,205656,205659,205661],{"class":2227,"line":6587},[2225,205639,2775],{"class":2774},[2225,205641,2835],{"class":2239},[2225,205643,2781],{"class":2235},[2225,205645,2784],{"class":2231},[2225,205647,2842],{"class":2239},[2225,205649,1520],{"class":2235},[2225,205651,2847],{"class":2291},[2225,205653,2789],{"class":2239},[2225,205655,2805],{"class":2235},[2225,205657,205658],{"class":2252},"inbox",[2225,205660,2805],{"class":2235},[2225,205662,2362],{"class":2239},[2225,205664,205665],{"class":2227,"line":7174},[2225,205666,2285],{"emptyLinePlaceholder":2284},[2225,205668,205669,205671,205673,205675,205677,205680,205682,205684,205686,205688,205690,205692],{"class":2227,"line":7180},[2225,205670,2694],{"class":2239},[2225,205672,1520],{"class":2235},[2225,205674,6471],{"class":2291},[2225,205676,6474],{"class":2235},[2225,205678,205679],{"class":6342},"Msg",[2225,205681,6480],{"class":2235},[2225,205683,2789],{"class":2239},[2225,205685,2805],{"class":2235},[2225,205687,57468],{"class":2252},[2225,205689,2805],{"class":2235},[2225,205691,47840],{"class":2239},[2225,205693,205694],{"class":2763},"\u002F\u002F base — hot fields only\n",[2225,205696,205697],{"class":2227,"line":7220},[2225,205698,2285],{"emptyLinePlaceholder":2284},[2225,205700,205701,205703,205705,205707,205709,205711,205713,205715,205717,205719,205721,205723,205725],{"class":2227,"line":7225},[2225,205702,2694],{"class":2239},[2225,205704,1520],{"class":2235},[2225,205706,6471],{"class":2291},[2225,205708,6474],{"class":2235},[2225,205710,205679],{"class":6342},[2225,205712,6480],{"class":2235},[2225,205714,2789],{"class":2239},[2225,205716,2805],{"class":2235},[2225,205718,57496],{"class":2252},[2225,205720,2805],{"class":2235},[2225,205722,2298],{"class":2235},[2225,205724,2236],{"class":2235},[2225,205726,205727],{"class":2763},"      \u002F\u002F satellite — heavy fields\n",[2225,205729,205730,205732,205734,205736,205738,205740],{"class":2227,"line":7231},[2225,205731,57507],{"class":2307},[2225,205733,2311],{"class":2235},[2225,205735,2249],{"class":2235},[2225,205737,57468],{"class":2252},[2225,205739,2805],{"class":2235},[2225,205741,1707],{"class":2235},[2225,205743,205744,205746,205748,205750,205752,205754,205756,205758,205760,205762,205764,205766],{"class":2227,"line":7267},[2225,205745,57522],{"class":2307},[2225,205747,2311],{"class":2235},[2225,205749,8667],{"class":2239},[2225,205751,2805],{"class":2235},[2225,205753,57531],{"class":2252},[2225,205755,2805],{"class":2235},[2225,205757,2298],{"class":2235},[2225,205759,2249],{"class":2235},[2225,205761,42850],{"class":2252},[2225,205763,2805],{"class":2235},[2225,205765,8803],{"class":2239},[2225,205767,1707],{"class":2235},[2225,205769,205770,205772,205774,205776,205778,205780,205782],{"class":2227,"line":7272},[2225,205771,57553],{"class":2307},[2225,205773,2311],{"class":2235},[2225,205775,2249],{"class":2235},[2225,205777,57560],{"class":2252},[2225,205779,2805],{"class":2235},[2225,205781,2298],{"class":2235},[2225,205783,205784],{"class":2763},"                    \u002F\u002F registers vault.joined('msgs_full')\n",[2225,205786,205787,205789],{"class":2227,"line":7278},[2225,205788,2359],{"class":2235},[2225,205790,2362],{"class":2239},[1022,205792,29893],{"id":29892},[1591,205794,205796],{"id":205795},"read-and-write-the-full-record-through-the-joined-handle","Read and write the full record through the joined handle",[937,205798,205799,22207,205802,205804],{},[941,205800,205801],{},"vault.joined()",[941,205803,57752],{}," that merges both legs on read and fans writes out\nto both collections:",[2217,205806,205808],{"className":2219,"code":205807,"language":2221,"meta":1037,"style":1037},"await vault.collection\u003CMsg>('msgs').put('m1', { from: 'alice@example.com' })\n\nawait vault.joined\u003CMsg>('msgs_full').get('m1')\n\u002F\u002F → { from: 'alice@example.com', subject: null, body: null }   (satellite not written yet)\n\nawait vault.collection\u003CMsg>('msgs_text').put('m1', { subject: 'Hello', body: 'Full message body…' })\n\nawait vault.joined\u003CMsg>('msgs_full').get('m1')\n\u002F\u002F → { from: 'alice@example.com', subject: 'Hello', body: 'Full message body…' }\n\nawait vault.collection\u003CMsg>('msgs').get('m1')\n\u002F\u002F → { from: 'alice@example.com' }   (base read alone — never touches the satellite envelope)\n",[941,205809,205810,205866,205870,205910,205915,205919,205988,205992,206032,206037,206041,206081],{"__ignoreMap":1037},[2225,205811,205812,205814,205816,205818,205820,205822,205824,205826,205828,205830,205832,205834,205836,205838,205840,205842,205844,205846,205848,205850,205852,205854,205856,205858,205860,205862,205864],{"class":2227,"line":2228},[2225,205813,6500],{"class":2231},[2225,205815,6466],{"class":2239},[2225,205817,1520],{"class":2235},[2225,205819,6471],{"class":2291},[2225,205821,6474],{"class":2235},[2225,205823,205679],{"class":6342},[2225,205825,6480],{"class":2235},[2225,205827,2789],{"class":2239},[2225,205829,2805],{"class":2235},[2225,205831,57468],{"class":2252},[2225,205833,2805],{"class":2235},[2225,205835,1381],{"class":2239},[2225,205837,1520],{"class":2235},[2225,205839,3161],{"class":2291},[2225,205841,2789],{"class":2239},[2225,205843,2805],{"class":2235},[2225,205845,57626],{"class":2252},[2225,205847,2805],{"class":2235},[2225,205849,2298],{"class":2235},[2225,205851,2236],{"class":2235},[2225,205853,2246],{"class":2307},[2225,205855,2311],{"class":2235},[2225,205857,2249],{"class":2235},[2225,205859,10517],{"class":2252},[2225,205861,2805],{"class":2235},[2225,205863,2243],{"class":2235},[2225,205865,2362],{"class":2239},[2225,205867,205868],{"class":2227,"line":1041},[2225,205869,2285],{"emptyLinePlaceholder":2284},[2225,205871,205872,205874,205876,205878,205880,205882,205884,205886,205888,205890,205892,205894,205896,205898,205900,205902,205904,205906,205908],{"class":2227,"line":1038},[2225,205873,6500],{"class":2231},[2225,205875,6466],{"class":2239},[2225,205877,1520],{"class":2235},[2225,205879,57598],{"class":2291},[2225,205881,6474],{"class":2235},[2225,205883,205679],{"class":6342},[2225,205885,6480],{"class":2235},[2225,205887,2789],{"class":2239},[2225,205889,2805],{"class":2235},[2225,205891,57560],{"class":2252},[2225,205893,2805],{"class":2235},[2225,205895,1381],{"class":2239},[2225,205897,1520],{"class":2235},[2225,205899,3158],{"class":2291},[2225,205901,2789],{"class":2239},[2225,205903,2805],{"class":2235},[2225,205905,57626],{"class":2252},[2225,205907,2805],{"class":2235},[2225,205909,2362],{"class":2239},[2225,205911,205912],{"class":2227,"line":2288},[2225,205913,205914],{"class":2763},"\u002F\u002F → { from: 'alice@example.com', subject: null, body: null }   (satellite not written yet)\n",[2225,205916,205917],{"class":2227,"line":2304},[2225,205918,2285],{"emptyLinePlaceholder":2284},[2225,205920,205921,205923,205925,205927,205929,205931,205933,205935,205937,205939,205941,205943,205945,205947,205949,205951,205953,205955,205957,205959,205961,205963,205965,205967,205969,205971,205973,205975,205977,205979,205982,205984,205986],{"class":2227,"line":2356},[2225,205922,6500],{"class":2231},[2225,205924,6466],{"class":2239},[2225,205926,1520],{"class":2235},[2225,205928,6471],{"class":2291},[2225,205930,6474],{"class":2235},[2225,205932,205679],{"class":6342},[2225,205934,6480],{"class":2235},[2225,205936,2789],{"class":2239},[2225,205938,2805],{"class":2235},[2225,205940,57496],{"class":2252},[2225,205942,2805],{"class":2235},[2225,205944,1381],{"class":2239},[2225,205946,1520],{"class":2235},[2225,205948,3161],{"class":2291},[2225,205950,2789],{"class":2239},[2225,205952,2805],{"class":2235},[2225,205954,57626],{"class":2252},[2225,205956,2805],{"class":2235},[2225,205958,2298],{"class":2235},[2225,205960,2236],{"class":2235},[2225,205962,57648],{"class":2307},[2225,205964,2311],{"class":2235},[2225,205966,2249],{"class":2235},[2225,205968,90543],{"class":2252},[2225,205970,2805],{"class":2235},[2225,205972,2298],{"class":2235},[2225,205974,42791],{"class":2307},[2225,205976,2311],{"class":2235},[2225,205978,2249],{"class":2235},[2225,205980,205981],{"class":2252},"Full message body…",[2225,205983,2805],{"class":2235},[2225,205985,2243],{"class":2235},[2225,205987,2362],{"class":2239},[2225,205989,205990],{"class":2227,"line":2830},[2225,205991,2285],{"emptyLinePlaceholder":2284},[2225,205993,205994,205996,205998,206000,206002,206004,206006,206008,206010,206012,206014,206016,206018,206020,206022,206024,206026,206028,206030],{"class":2227,"line":2861},[2225,205995,6500],{"class":2231},[2225,205997,6466],{"class":2239},[2225,205999,1520],{"class":2235},[2225,206001,57598],{"class":2291},[2225,206003,6474],{"class":2235},[2225,206005,205679],{"class":6342},[2225,206007,6480],{"class":2235},[2225,206009,2789],{"class":2239},[2225,206011,2805],{"class":2235},[2225,206013,57560],{"class":2252},[2225,206015,2805],{"class":2235},[2225,206017,1381],{"class":2239},[2225,206019,1520],{"class":2235},[2225,206021,3158],{"class":2291},[2225,206023,2789],{"class":2239},[2225,206025,2805],{"class":2235},[2225,206027,57626],{"class":2252},[2225,206029,2805],{"class":2235},[2225,206031,2362],{"class":2239},[2225,206033,206034],{"class":2227,"line":4246},[2225,206035,206036],{"class":2763},"\u002F\u002F → { from: 'alice@example.com', subject: 'Hello', body: 'Full message body…' }\n",[2225,206038,206039],{"class":2227,"line":4253},[2225,206040,2285],{"emptyLinePlaceholder":2284},[2225,206042,206043,206045,206047,206049,206051,206053,206055,206057,206059,206061,206063,206065,206067,206069,206071,206073,206075,206077,206079],{"class":2227,"line":4259},[2225,206044,6500],{"class":2231},[2225,206046,6466],{"class":2239},[2225,206048,1520],{"class":2235},[2225,206050,6471],{"class":2291},[2225,206052,6474],{"class":2235},[2225,206054,205679],{"class":6342},[2225,206056,6480],{"class":2235},[2225,206058,2789],{"class":2239},[2225,206060,2805],{"class":2235},[2225,206062,57468],{"class":2252},[2225,206064,2805],{"class":2235},[2225,206066,1381],{"class":2239},[2225,206068,1520],{"class":2235},[2225,206070,3158],{"class":2291},[2225,206072,2789],{"class":2239},[2225,206074,2805],{"class":2235},[2225,206076,57626],{"class":2252},[2225,206078,2805],{"class":2235},[2225,206080,2362],{"class":2239},[2225,206082,206083],{"class":2227,"line":6548},[2225,206084,206085],{"class":2763},"\u002F\u002F → { from: 'alice@example.com' }   (base read alone — never touches the satellite envelope)\n",[1591,206087,206089],{"id":206088},"delete-removes-both-legs-satellite-first","Delete removes both legs, satellite first",[937,206091,206092],{},"Deleting through the base or the joined handle fans out to both collections, satellite leg\nfirst — the reverse order from writes — so a crash mid-delete can never leave a base-less\nsatellite behind:",[2217,206094,206096],{"className":2219,"code":206095,"language":2221,"meta":1037,"style":1037},"await vault.collection\u003CMsg>('msgs').delete('m1')\n\nawait vault.joined\u003CMsg>('msgs_full').get('m1')\n\u002F\u002F → null\n",[941,206097,206098,206138,206142,206182],{"__ignoreMap":1037},[2225,206099,206100,206102,206104,206106,206108,206110,206112,206114,206116,206118,206120,206122,206124,206126,206128,206130,206132,206134,206136],{"class":2227,"line":2228},[2225,206101,6500],{"class":2231},[2225,206103,6466],{"class":2239},[2225,206105,1520],{"class":2235},[2225,206107,6471],{"class":2291},[2225,206109,6474],{"class":2235},[2225,206111,205679],{"class":6342},[2225,206113,6480],{"class":2235},[2225,206115,2789],{"class":2239},[2225,206117,2805],{"class":2235},[2225,206119,57468],{"class":2252},[2225,206121,2805],{"class":2235},[2225,206123,1381],{"class":2239},[2225,206125,1520],{"class":2235},[2225,206127,3164],{"class":2291},[2225,206129,2789],{"class":2239},[2225,206131,2805],{"class":2235},[2225,206133,57626],{"class":2252},[2225,206135,2805],{"class":2235},[2225,206137,2362],{"class":2239},[2225,206139,206140],{"class":2227,"line":1041},[2225,206141,2285],{"emptyLinePlaceholder":2284},[2225,206143,206144,206146,206148,206150,206152,206154,206156,206158,206160,206162,206164,206166,206168,206170,206172,206174,206176,206178,206180],{"class":2227,"line":1038},[2225,206145,6500],{"class":2231},[2225,206147,6466],{"class":2239},[2225,206149,1520],{"class":2235},[2225,206151,57598],{"class":2291},[2225,206153,6474],{"class":2235},[2225,206155,205679],{"class":6342},[2225,206157,6480],{"class":2235},[2225,206159,2789],{"class":2239},[2225,206161,2805],{"class":2235},[2225,206163,57560],{"class":2252},[2225,206165,2805],{"class":2235},[2225,206167,1381],{"class":2239},[2225,206169,1520],{"class":2235},[2225,206171,3158],{"class":2291},[2225,206173,2789],{"class":2239},[2225,206175,2805],{"class":2235},[2225,206177,57626],{"class":2252},[2225,206179,2805],{"class":2235},[2225,206181,2362],{"class":2239},[2225,206183,206184],{"class":2227,"line":2288},[2225,206185,76419],{"class":2763},[1591,206187,206189],{"id":206188},"handle-a-refused-write","Handle a refused write",[937,206191,206192],{},"Writing to a satellite for an id with no live base record is refused rather than silently\ncreating an orphan:",[2217,206194,206196],{"className":2219,"code":206195,"language":2221,"meta":1037,"style":1037},"import { SatelliteConfigError } from '@noy-db\u002Fhub\u002Fsatellites'\n\ntry {\n  await vault.collection\u003CMsg>('msgs_text').put('ghost', { subject: 'orphan', body: '…' })\n} catch (err) {\n  if (err instanceof SatelliteConfigError) {\n    \u002F\u002F → R-S6: satellite \"msgs_text\" put for \"ghost\" with no live base record in \"msgs\" —\n    \u002F\u002F   create the base first (or write through the joined handle).\n  }\n}\n",[941,206197,206198,206218,206222,206228,206298,206308,206324,206329,206334,206338],{"__ignoreMap":1037},[2225,206199,206200,206202,206204,206207,206209,206211,206213,206216],{"class":2227,"line":2228},[2225,206201,2232],{"class":2231},[2225,206203,2236],{"class":2235},[2225,206205,206206],{"class":2239}," SatelliteConfigError",[2225,206208,2243],{"class":2235},[2225,206210,2246],{"class":2231},[2225,206212,2249],{"class":2235},[2225,206214,206215],{"class":2252},"@noy-db\u002Fhub\u002Fsatellites",[2225,206217,2256],{"class":2235},[2225,206219,206220],{"class":2227,"line":1041},[2225,206221,2285],{"emptyLinePlaceholder":2284},[2225,206223,206224,206226],{"class":2227,"line":1038},[2225,206225,7554],{"class":2231},[2225,206227,2301],{"class":2235},[2225,206229,206230,206232,206234,206236,206238,206240,206242,206244,206246,206248,206250,206252,206254,206256,206258,206260,206262,206265,206267,206269,206271,206273,206275,206277,206280,206282,206284,206286,206288,206290,206292,206294,206296],{"class":2227,"line":2288},[2225,206231,7561],{"class":2231},[2225,206233,6466],{"class":2239},[2225,206235,1520],{"class":2235},[2225,206237,6471],{"class":2291},[2225,206239,6474],{"class":2235},[2225,206241,205679],{"class":6342},[2225,206243,6480],{"class":2235},[2225,206245,2789],{"class":2307},[2225,206247,2805],{"class":2235},[2225,206249,57496],{"class":2252},[2225,206251,2805],{"class":2235},[2225,206253,1381],{"class":2307},[2225,206255,1520],{"class":2235},[2225,206257,3161],{"class":2291},[2225,206259,2789],{"class":2307},[2225,206261,2805],{"class":2235},[2225,206263,206264],{"class":2252},"ghost",[2225,206266,2805],{"class":2235},[2225,206268,2298],{"class":2235},[2225,206270,2236],{"class":2235},[2225,206272,57648],{"class":2307},[2225,206274,2311],{"class":2235},[2225,206276,2249],{"class":2235},[2225,206278,206279],{"class":2252},"orphan",[2225,206281,2805],{"class":2235},[2225,206283,2298],{"class":2235},[2225,206285,42791],{"class":2307},[2225,206287,2311],{"class":2235},[2225,206289,2249],{"class":2235},[2225,206291,14475],{"class":2252},[2225,206293,2805],{"class":2235},[2225,206295,2243],{"class":2235},[2225,206297,2362],{"class":2307},[2225,206299,206300,206302,206304,206306],{"class":2227,"line":2304},[2225,206301,2359],{"class":2235},[2225,206303,7598],{"class":2231},[2225,206305,7601],{"class":2239},[2225,206307,4200],{"class":2235},[2225,206309,206310,206312,206314,206316,206318,206320,206322],{"class":2227,"line":2356},[2225,206311,7608],{"class":2231},[2225,206313,953],{"class":2307},[2225,206315,7617],{"class":2239},[2225,206317,19172],{"class":2235},[2225,206319,206206],{"class":6342},[2225,206321,8495],{"class":2307},[2225,206323,4200],{"class":2235},[2225,206325,206326],{"class":2227,"line":2830},[2225,206327,206328],{"class":2763},"    \u002F\u002F → R-S6: satellite \"msgs_text\" put for \"ghost\" with no live base record in \"msgs\" —\n",[2225,206330,206331],{"class":2227,"line":2861},[2225,206332,206333],{"class":2763},"    \u002F\u002F   create the base first (or write through the joined handle).\n",[2225,206335,206336],{"class":2227,"line":4246},[2225,206337,7632],{"class":2235},[2225,206339,206340],{"class":2227,"line":4253},[2225,206341,7351],{"class":2235},[1022,206343,102889],{"id":102888},[1591,206345,206347],{"id":206346},"vaultcollectionname-options",[941,206348,206349],{},"vault.collection(name, options)",[2217,206351,206353],{"className":2219,"code":206352,"language":2221,"meta":1037,"style":1037},"vault.collection\u003CT>(name: string, options?: { \u002F* satelliteOf, fields, joined, … *\u002F }): Collection\u003CT>\n",[941,206354,206355],{"__ignoreMap":1037},[2225,206356,206357,206359,206361,206363,206365,206367,206369,206372,206374,206376,206378,206380,206383,206385,206387,206389,206391],{"class":2227,"line":2228},[2225,206358,2694],{"class":2239},[2225,206360,1520],{"class":2235},[2225,206362,6471],{"class":2291},[2225,206364,6474],{"class":2235},[2225,206366,11968],{"class":6342},[2225,206368,6480],{"class":2235},[2225,206370,206371],{"class":2239},"(name: string",[2225,206373,2298],{"class":2235},[2225,206375,53625],{"class":2239},[2225,206377,7011],{"class":2235},[2225,206379,2236],{"class":2235},[2225,206381,206382],{"class":2763}," \u002F* satelliteOf, fields, joined, … *\u002F",[2225,206384,2243],{"class":2235},[2225,206386,22555],{"class":2239},[2225,206388,6474],{"class":2235},[2225,206390,11968],{"class":2239},[2225,206392,7093],{"class":2235},[1338,206394,206395,206405],{},[1341,206396,206397],{},[1344,206398,206399,206401,206403],{},[1347,206400,101060],{},[1347,206402,20194],{},[1347,206404,20200],{},[1357,206406,206407,206420,206436],{},[1344,206408,206409,206413,206417],{},[1362,206410,206411],{},[941,206412,57276],{},[1362,206414,206415],{},[941,206416,7259],{},[1362,206418,206419],{},"Pairs this collection to a base collection by shared record id.",[1344,206421,206422,206426,206430],{},[1362,206423,206424],{},[941,206425,57285],{},[1362,206427,206428],{},[941,206429,111053],{},[1362,206431,206432,206433,206435],{},"Routing table — required with ",[941,206434,57276],{},"; the fields that live on this satellite.",[1344,206437,206438,206442,206446],{},[1362,206439,206440],{},[941,206441,57598],{},[1362,206443,206444],{},[941,206445,7259],{},[1362,206447,206448,206449,206452],{},"Registers ",[941,206450,206451],{},"vault.joined(joined)"," for this pair.",[937,206454,206455,206456,206458,206459,968,206461,968,206463,206466,206467,206469,206470,206472,206473,206475,206476,206478,206479,162562,206481,206483],{},"Every other ",[941,206457,103],{}," option (",[941,206460,23934],{},[941,206462,14552],{},[941,206464,206465],{},"crdtMode",", etc.) is still\naccepted alongside these three. Declaring ",[941,206468,57276],{}," registers ",[941,206471,4914],{}," as the satellite half of a pair. A refused\ndeclaration (see Refusal matrix) throws ",[941,206474,57816],{}," and leaves no side effect — no\nregistry entry, no pairing marker written. Once registered, the returned handle for the\nsatellite name is wrapped to enforce existence authority (satellite reads return ",[941,206477,6651],{}," when\nthe base is absent or tombstoned); the handle for the base name is wrapped so ",[941,206480,3164],{},[941,206482,24681],{}," fan out to the satellite first.",[1591,206485,206487],{"id":206486},"vaultjoinedtname",[941,206488,206489],{},"vault.joined\u003CT>(name)",[2217,206491,206493],{"className":2219,"code":206492,"language":2221,"meta":1037,"style":1037},"vault.joined\u003CT extends Record\u003Cstring, unknown>>(name: string): JoinedHandle\u003CT>\n",[941,206494,206495],{"__ignoreMap":1037},[2225,206496,206497,206499,206501,206503,206505,206507,206509,206511,206513,206515,206517,206519,206522,206524,206526,206528,206530,206533,206535,206537],{"class":2227,"line":2228},[2225,206498,2694],{"class":2239},[2225,206500,1520],{"class":2235},[2225,206502,57598],{"class":2239},[2225,206504,6474],{"class":2235},[2225,206506,11968],{"class":6342},[2225,206508,124350],{"class":2774},[2225,206510,22527],{"class":6342},[2225,206512,6474],{"class":2235},[2225,206514,7259],{"class":6342},[2225,206516,2298],{"class":2235},[2225,206518,40717],{"class":6342},[2225,206520,206521],{"class":2235},">>(",[2225,206523,4914],{"class":7052},[2225,206525,2311],{"class":2235},[2225,206527,6366],{"class":6342},[2225,206529,6255],{"class":2235},[2225,206531,206532],{"class":6342}," JoinedHandle",[2225,206534,6474],{"class":2235},[2225,206536,11968],{"class":6342},[2225,206538,7093],{"class":2235},[937,206540,206541,206542,206545,206546,102958,206548,206550],{},"Returns the full-record handle registered by a satellite's ",[941,206543,206544],{},"joined:"," option. Throws\n",[941,206547,57816],{},[941,206549,4914],{}," was never registered.",[937,206552,206553,118123,206555,206557,206558,4724,206560,2311],{},[941,206554,57752],{},[941,206556,206215],{},") is deliberately narrow — it is\n",[950,206559,1004],{},[941,206561,3121],{},[2217,206563,206565],{"className":2219,"code":206564,"language":2221,"meta":1037,"style":1037},"interface JoinedHandle\u003CT extends Record\u003Cstring, unknown> = Record\u003Cstring, unknown>> {\n  get(id: string): Promise\u003CT | null>\n  put(id: string, record: T): Promise\u003Cvoid>\n  delete(id: string): Promise\u003Cvoid>\n  list(): Promise\u003CT[]>\n  describe(): Promise\u003CCollectionDescription>   \u002F\u002F the @noy-db\u002Fui contract\n}\n",[941,206566,206567,206607,206633,206663,206685,206701,206719],{"__ignoreMap":1037},[2225,206568,206569,206571,206573,206575,206577,206579,206581,206583,206585,206587,206589,206591,206593,206595,206597,206599,206601,206603,206605],{"class":2227,"line":2228},[2225,206570,6994],{"class":2774},[2225,206572,206532],{"class":6342},[2225,206574,6474],{"class":2235},[2225,206576,11968],{"class":6342},[2225,206578,124350],{"class":2774},[2225,206580,22527],{"class":6342},[2225,206582,6474],{"class":2235},[2225,206584,7259],{"class":6342},[2225,206586,2298],{"class":2235},[2225,206588,40717],{"class":6342},[2225,206590,6480],{"class":2235},[2225,206592,6346],{"class":2235},[2225,206594,22527],{"class":6342},[2225,206596,6474],{"class":2235},[2225,206598,7259],{"class":6342},[2225,206600,2298],{"class":2235},[2225,206602,40717],{"class":6342},[2225,206604,28601],{"class":2235},[2225,206606,2301],{"class":2235},[2225,206608,206609,206611,206613,206615,206617,206619,206621,206623,206625,206627,206629,206631],{"class":2227,"line":1041},[2225,206610,7047],{"class":2307},[2225,206612,2789],{"class":2235},[2225,206614,6983],{"class":7052},[2225,206616,2311],{"class":2235},[2225,206618,6366],{"class":6342},[2225,206620,6255],{"class":2235},[2225,206622,7079],{"class":6342},[2225,206624,6474],{"class":2235},[2225,206626,11968],{"class":6342},[2225,206628,7087],{"class":2235},[2225,206630,7090],{"class":6342},[2225,206632,7093],{"class":2235},[2225,206634,206635,206637,206639,206641,206643,206645,206647,206649,206651,206653,206655,206657,206659,206661],{"class":2227,"line":1038},[2225,206636,7112],{"class":2307},[2225,206638,2789],{"class":2235},[2225,206640,6983],{"class":7052},[2225,206642,2311],{"class":2235},[2225,206644,6366],{"class":6342},[2225,206646,2298],{"class":2235},[2225,206648,24268],{"class":7052},[2225,206650,2311],{"class":2235},[2225,206652,24772],{"class":6342},[2225,206654,6255],{"class":2235},[2225,206656,7079],{"class":6342},[2225,206658,6474],{"class":2235},[2225,206660,7165],{"class":6342},[2225,206662,7093],{"class":2235},[2225,206664,206665,206667,206669,206671,206673,206675,206677,206679,206681,206683],{"class":2227,"line":2288},[2225,206666,7183],{"class":2307},[2225,206668,2789],{"class":2235},[2225,206670,6983],{"class":7052},[2225,206672,2311],{"class":2235},[2225,206674,6366],{"class":6342},[2225,206676,6255],{"class":2235},[2225,206678,7079],{"class":6342},[2225,206680,6474],{"class":2235},[2225,206682,7165],{"class":6342},[2225,206684,7093],{"class":2235},[2225,206686,206687,206689,206691,206693,206695,206697,206699],{"class":2227,"line":2304},[2225,206688,7234],{"class":2307},[2225,206690,112812],{"class":2235},[2225,206692,7079],{"class":6342},[2225,206694,6474],{"class":2235},[2225,206696,11968],{"class":6342},[2225,206698,7262],{"class":2239},[2225,206700,7093],{"class":2235},[2225,206702,206703,206706,206708,206710,206712,206714,206716],{"class":2227,"line":2356},[2225,206704,206705],{"class":2307},"  describe",[2225,206707,112812],{"class":2235},[2225,206709,7079],{"class":6342},[2225,206711,6474],{"class":2235},[2225,206713,25317],{"class":6342},[2225,206715,6480],{"class":2235},[2225,206717,206718],{"class":2763},"   \u002F\u002F the @noy-db\u002Fui contract\n",[2225,206720,206721],{"class":2227,"line":2830},[2225,206722,7351],{"class":2235},[937,206724,206725,1625,206727,206729,206730,206732,206733,206735,206736,206738,206739,206741,206742,1625,206744,1625,206746,206748,206749,1707,206751,968,206753,206755],{},[941,206726,3158],{},[941,206728,3167],{}," merge base ⊕ satellite by field (absent satellite reads as all-null; ",[941,206731,6651],{}," if\nthe base is absent or tombstoned). ",[941,206734,3161],{}," splits the record across both collections by the\nsatellite's ",[941,206737,57285],{}," routing table and writes base-first. ",[941,206740,3164],{}," removes both collections,\nsatellite-first. There is no ",[941,206743,697],{},[941,206745,80362],{},[941,206747,159494],{},", and no reactive members (",[941,206750,727],{},[941,206752,25042],{},[941,206754,25115],{},") — calling one throws \"not yet implemented\" (see Limits &\ngotchas).",[1591,206757,206759,206760],{"id":206758},"other-exports-of-noy-dbhubsatellites","Other exports of ",[941,206761,206215],{},[1338,206763,206764,206774],{},[1341,206765,206766],{},[1344,206767,206768,206770,206772],{},[1347,206769,72632],{},[1347,206771,37901],{},[1347,206773,7834],{},[1357,206775,206776,206791,206810],{},[1344,206777,206778,206783,206785],{},[1362,206779,206780],{},[941,206781,206782],{},"SatelliteSpec",[1362,206784,6339],{},[1362,206786,206787,206788,1520],{},"One base↔satellite pair: ",[941,206789,206790],{},"{ base, satellite, fields, joined? }",[1344,206792,206793,206798,206800],{},[1362,206794,206795],{},[941,206796,206797],{},"PairingMarker",[1362,206799,6339],{},[1362,206801,206802,206803,964,206806,206809],{},"The shape persisted into ",[941,206804,206805],{},"_schemas\u002F\u003Csatellite>",[941,206807,206808],{},"{ base, fieldsHash, joined? }"," — used to detect a divergent re-declaration (R-S9).",[1344,206811,206812,206816,206819],{},[1362,206813,206814],{},[941,206815,57816],{},[1362,206817,206818],{},"error class",[1362,206820,206821],{},"Thrown for every refused declaration or refused operation listed in the Refusal matrix below.",[1022,206823,206825],{"id":206824},"refusal-matrix","Refusal matrix",[937,206827,206828,206829,206831],{},"Every configuration or write that would produce an unsafe or ambiguous pairing is refused\nwith ",[941,206830,57816],{}," rather than silently accepted:",[1338,206833,206834,206843],{},[1341,206835,206836],{},[1344,206837,206838,206840],{},[1347,206839,3096],{},[1347,206841,206842],{},"Refused condition",[1357,206844,206845,206856,206868,206878,206888,206904,206915,206929,206939],{},[1344,206846,206847,206850],{},[1362,206848,206849],{},"R-S1",[1362,206851,206852,206853,206855],{},"A satellite's ",[941,206854,57285],{}," overlaps the base's fields (best-effort cross-check).",[1344,206857,206858,206861],{},[1362,206859,206860],{},"R-S2",[1362,206862,206863,206864,11060,206866,1520],{},"A joined write's non-satellite keys route to the base, where the base's own validator (if any) rejects unknown fields — throws the base validator's ",[941,206865,22629],{},[941,206867,57816],{},[1344,206869,206870,206873],{},[1362,206871,206872],{},"R-S3",[1362,206874,206875,206877],{},[941,206876,57276],{}," names a non-existent base, or a base that is itself a satellite (no chains).",[1344,206879,206880,206883],{},[1362,206881,206882],{},"R-S4",[1362,206884,206885,206887],{},[941,206886,2542],{}," cannot process a synthesized satellite ref through the full purge suite — fails loud, never a partial forget.",[1344,206889,206890,206893],{},[1362,206891,206892],{},"R-S5",[1362,206894,206895,206897,206898,206900,206901,206903],{},[941,206896,57285],{}," omitted, empty, or id-bearing; ",[941,206899,57598],{}," name collides with an existing collection; or ",[941,206902,57285],{}," contradicts derivable schema info.",[1344,206905,206906,206909],{},[1362,206907,206908],{},"R-S6",[1362,206910,206911,206912,206914],{},"A satellite-handle ",[941,206913,3161],{}," for an id with no live base record (orphan prevention; best-effort under cross-client concurrency).",[1344,206916,206917,206920],{},[1362,206918,206919],{},"R-S7",[1362,206921,206922,206923,206925,206926,206928],{},"A satellite on a ",[941,206924,152885],{},"-covered base declared without ",[941,206927,14552],{},"; or adding forget coverage over a base whose existing satellite lacks it.",[1344,206930,206931,206934],{},[1362,206932,206933],{},"R-S8",[1362,206935,206936,206938],{},[941,206937,206465],{}," set on either member of a satellite pair (v1 — a revert can't compensate a merge).",[1344,206940,206941,206943],{},[1362,206942,57841],{},[1362,206944,206945,206946,206949],{},"A declaration whose ",[941,206947,206948],{},"(satelliteOf, fields hash, joined)"," disagrees with the persisted pairing marker.",[1022,206951,206953],{"id":206952},"convergence-rules","Convergence rules",[1234,206955,206956,206989,206995,207001,207014,207020,207029],{},[1114,206957,206958,206961,206962,206964,206965,206967,206968,206970,206971,1625,206973,206975,206976,34689,206979,206981,206982,206984,206985,206988],{},[950,206959,206960],{},"Existence authority."," The base row is the sole authority on record existence. This is\nenforced on ",[941,206963,57790],{},"\u002Fjoined ",[941,206966,3158],{}," (returns ",[941,206969,6651],{},"), satellite ",[941,206972,3167],{},[941,206974,5286],{},"\n(id-filtered against live base ids), ",[941,206977,206978],{},"withSearch",[941,206980,22035],{}," on a satellite\n(post-filtered), and bundle\u002Fpod export (envelope-level filter). It is ",[950,206983,1004],{}," enforced on\nsync push, raw ",[941,206986,206987],{},"dumpEnvelopes()",", or any service reading through the raw adapter directly.",[1114,206990,206991,206994],{},[950,206992,206993],{},"Absent ≡ all-null."," The read path never assumes a physically-present satellite\nenvelope — a missing one reads as all-null.",[1114,206996,206997,207000],{},[950,206998,206999],{},"Fan-out ordering is a local-store-only guarantee."," Joined writes go base-first and pair\ndeletes go satellite-first, so a local crash can only leave the safe direction — but sync\npush replay does not preserve this order (per-entry error isolation and dedup can\ntransiently expose a base-less satellite remotely, with no local crash at all).",[1114,207002,207003,207006,207007,207009,207010,207013],{},[950,207004,207005],{},"Delete is logical; forget is cryptographic."," A plain delete leaves no tombstone, so v1\ndoes not sweep base-less satellites — existence authority (rule 1) simply makes them\npermanently unreachable instead. For ",[941,207008,2542],{},", containment of a resurrected tombstone is\n",[950,207011,207012],{},"observational only",": the system detects and reports a satellite whose base record comes\nback on a higher version after being forgotten, it does not yet prevent it. Satellites will\ninherit that fix automatically once the underlying tombstone-resurrection gap in sync pull\nis closed, with no change on the satellites side.",[1114,207015,207016,207019],{},[950,207017,207018],{},"Conflict granularity is per-collection."," Two clients' joined writes can converge to\nbase-from-A ⊕ satellite-from-B — a record neither client wrote. This field-group\ngranularity is the joined handle's documented contract; registering a conflict resolver on\neither pair member registers it for both.",[1114,207021,207022,207025,207026,207028],{},[950,207023,207024],{},"No snapshot consistency under active sync."," Pull applies a pair's records in arbitrary\norder with no cache invalidation, so a joined ",[941,207027,3158],{}," racing a pull can merge new hot fields\nwith not-yet-pulled heavy fields. This is by design, not a bug to fix.",[1114,207030,207031,1324,207034,1625,207036,1324,207038,207040,207041,207044],{},[950,207032,207033],{},"Partial-sync filters treat a pair as a unit.",[941,207035,82367],{},[941,207037,91149],{},[941,207039,2698],{}," allow-lists\nexpand pair-wise via the pairing marker — naming the base implicitly includes its\nsatellite. ",[941,207042,207043],{},"modifiedSince"," can still split a pair by timestamp.",[1022,207046,68210],{"id":68209},[1111,207048,207049,207055,207061,207069,207099,207111,207117],{},[1114,207050,207051,207054],{},[950,207052,207053],{},"Exactly one satellite per base."," N-satellites-per-base is a deliberately deferred\nextension, not something to route around today.",[1114,207056,207057,207060],{},[950,207058,207059],{},"New collections only — no backfill."," There is no machinery to relocate field values out\nof an existing, populated base collection; v1 applies only to collections declared\nsatellite-paired from their first write.",[1114,207062,207063,207068],{},[950,207064,207065,207067],{},[941,207066,206465],{}," is refused on either pair member (R-S8)."," Reverting a fan-out's CRDT leg with\na stale prior envelope would silently destroy other actors' merged operations.",[1114,207070,207071,207079,207080,1625,207082,1625,207084,207086,207087,52185,207089,207091,207092,1625,207094,1625,207096,207098],{},[950,207072,207073,207074,207076,207077,1520],{},"Satellite ",[941,207075,24715],{}," refuses with ",[941,207078,57816],{}," Query's terminal methods\n(",[941,207081,3230],{},[941,207083,3233],{},[941,207085,3236],{},") read the in-memory cache synchronously, while existence\nauthority needs an async, undecrypted check — the two shapes don't compose. Use ",[941,207088,24407],{},[941,207090,14639],{}," on a satellite instead; ",[941,207093,4457],{},[941,207095,75590],{},[941,207097,144133],{}," get their own existence\npost-filter and remain available.",[1114,207100,207101,1324,207104,968,207106,2462,207108,207110],{},[950,207102,207103],{},"Joined reactive APIs are absent.",[941,207105,727],{},[941,207107,25042],{},[941,207109,25115],{}," throw \"not\nyet implemented\" on the joined handle in v1.",[1114,207112,207113,207116],{},[950,207114,207115],{},"Bundle export degrades leak-on-error, never drop-live-data."," If the base-liveness check\nerrors mid-export, the filter includes the satellite envelope rather than excluding it — a\nleaked dead satellite can be swept later, but a wrongly dropped live record can't be\nrecovered. This also opens a narrow, session-scoped race window: between a satellite's\nfirst declaration writing its pairing marker and that write becoming durable, a concurrent\nexport reading the not-yet-marked collection applies no existence filtering at all.",[1114,207118,207119,207127,207128,207131,207132,207134,207135,207137,207138,207140],{},[950,207120,207121,1625,207124,207126],{},[941,207122,207123],{},"pushFiltered",[941,207125,4687],{}," are not pair-expanded."," Pair-unit dirty-entry expansion\ncovers the ordinary ",[941,207129,207130],{},"push({ collections })"," allow-list path only — ",[941,207133,207123],{},"'s\nper-record predicate and ",[941,207136,4687],{},"'s explicit entry list do ",[950,207139,1004],{}," implicitly pull\nin the satellite partner. A caller using either path against a satellite pair is\nresponsible for including both collections itself.",[1022,207142,114684],{"id":114683},[1111,207144,207145,207157,207170],{},[1114,207146,207147,964,207151,207153,207154,207156],{},[950,207148,207149],{},[1007,207150,685],{"href":686},[941,207152,2542],{}," fans out to the satellite via a synthesized ref\nthrough the full per-ref purge suite (tombstone, history, index, search index, blobs,\nsealed material); the satellite inherits the base's ",[941,207155,14552],{}," classification.",[1114,207158,207159,964,207163,207166,207167,207169],{},[950,207160,207161],{},[1007,207162,4457],{"href":833},[941,207164,207165],{},"withSearch(['subject', 'body'])"," lives on the satellite;\n",[941,207168,22035],{}," results pass the existence filter.",[1114,207171,207172,207176],{},[950,207173,207174],{},[1007,207175,697],{"href":698}," — each side keeps its own version stream and ledger\nentries; the joined handle does not merge them.",[1022,207178,2621],{"id":2620},[1111,207180,207181,207187],{},[1114,207182,207183,1368,207185],{},[1007,207184,61743],{"href":598},[1007,207186,1019],{"href":1018},[1114,207188,207189,207191],{},[1007,207190,1516],{"href":66}," — the envelope format every satellite\u002Fbase leg is written as",[2645,207193,207194],{},"html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sHdIc, html code.shiki .sHdIc{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#EEFFFF;--shiki-default-font-style:italic;--shiki-dark:#BABED8;--shiki-dark-font-style:italic}",{"title":1037,"searchDepth":1038,"depth":1038,"links":207196},[207197,207198,207199,207200,207205,207211,207212,207213,207214,207215],{"id":6241,"depth":1041,"text":6242},{"id":205197,"depth":1041,"text":205198},{"id":99863,"depth":1041,"text":99864},{"id":29892,"depth":1041,"text":29893,"children":207201},[207202,207203,207204],{"id":205795,"depth":1038,"text":205796},{"id":206088,"depth":1038,"text":206089},{"id":206188,"depth":1038,"text":206189},{"id":102888,"depth":1041,"text":102889,"children":207206},[207207,207208,207209],{"id":206346,"depth":1038,"text":206349},{"id":206486,"depth":1038,"text":206489},{"id":206758,"depth":1038,"text":207210},"Other exports of @noy-db\u002Fhub\u002Fsatellites",{"id":206824,"depth":1041,"text":206825},{"id":206952,"depth":1041,"text":206953},{"id":68209,"depth":1041,"text":68210},{"id":114683,"depth":1041,"text":114684},{"id":2620,"depth":1041,"text":2621},"Split a record's hot, small fields from its heavy, cold ones into two paired collections, with a joined handle that merges the full record.",{},{"title":812,"description":207216},"mtEmf1X5OTzgDMJgPn7VC3l-UZMV4aT3Fmqm5bWm6xI",{"id":207221,"title":816,"access":12,"apiToc":929,"body":207222,"category":1042,"description":207561,"extension":1044,"meta":207562,"navigation":1042,"nest":1047,"path":817,"seo":207563,"sinceVersion":1042,"status":38550,"stem":818,"subtitle":1042,"tier":1473,"tocExternal":1042,"__hash__":207564},"docs\u002Fservices\u002Fschema-identifiers.md",{"type":931,"value":207223,"toc":207548},[207224,207226,207229,207238,207242,207247,207306,207312,207318,207343,207353,207357,207360,207380,207383,207387,207406,207412,207416,207426,207453,207466,207483,207496,207500,207507,207511,207520,207522],[934,207225,816],{"id":816},[46871,207227],{":rows":207228},"[{\"label\":\"Subpath\",\"value\":\"`@noy-db\u002Fhub` (via `describe()` \u002F `describeAsync()` and the persisted-schema surface)\"},{\"label\":\"Factory\",\"value\":\"none — rides `persistJsonSchema: true`\"},{\"label\":\"Cluster\",\"value\":\"C — Schema & Evolution\"},{\"label\":\"Since\",\"value\":\"0.6.0\"}]",[937,207230,207231,207232,207234,207235,207237],{},"Two identity gaps blocked honest schema-evolution tooling: a field had no identity independent of its ",[950,207233,4914],{},", and \"which schema was generation N\" was unanswerable from published reads. Both are closed additively on the existing ",[941,207236,166841],{}," envelope and the existing vault-wide schema fence — no new reserved collection, no new store contract.",[1022,207239,207241],{"id":207240},"field-ids","Field ids",[937,207243,207244,207245,2311],{},"Every top-level field in a persisted schema gets an opaque, permanent ",[941,207246,6983],{},[1111,207248,207249,207261,207269,207283,207289],{},[1114,207250,207251,207254,207255,207257,207258],{},[950,207252,207253],{},"Random only"," — 12 bytes from ",[941,207256,13674],{},", encoded as a 20-character base32url token. No timestamp, no name derivation: ",[950,207259,207260],{},"a name-derived id would change on rename, defeating the entire point.",[1114,207262,207263,207266,207267,1520],{},[950,207264,207265],{},"Minted lazily, once"," — on the first persist for a collection with ",[941,207268,191912],{},[1114,207270,207271,964,207274,207277,207278,997,207280,207282],{},[950,207272,207273],{},"Stored as a name-keyed map",[941,207275,207276],{},"fieldIds?: Record\u003Cstring, string>"," alongside ",[941,207279,192451],{},[941,207281,134337],{}," in the same encrypted record. Absent on pre-0.6 envelopes.",[1114,207284,207285,207288],{},[950,207286,207287],{},"Preserved by name across re-derivation"," — re-persisting carries forward the id for every name still present; only a genuinely new name mints a fresh one.",[1114,207290,207291,207294,207295,207298,207299,207302,207303],{},[950,207292,207293],{},"Carried across a rename"," — the delta computation detects an unambiguous 1:1 rename (a removed name and an added name whose canonicalised subschemas match, with no other candidate sharing that shape) and reports it as ",[941,207296,207297],{},"renamed: { from, to }[]",". The id is then looked up under the ",[1092,207300,207301],{},"old"," name. ",[950,207304,207305],{},"The id moves with the field; the name is just its current label.",[1591,207307,207309,207311],{"id":207308},"describe-surfaces-it-with-one-asymmetry",[941,207310,3926],{}," surfaces it — with one asymmetry",[937,207313,207314,207317],{},[941,207315,207316],{},"DescribedField.id?: string"," appears in both overloads, but they differ:",[1111,207319,207320,207334],{},[1114,207321,207322,207328,207329,7356,207331,207333],{},[950,207323,207324,207325,207327],{},"Sync ",[941,207326,3926],{}," never touches the store",", so every field's ",[941,207330,6983],{},[941,207332,18316],{},". This is not a bug — the sync path has no persisted state to read an id from.",[1114,207335,207336,207342],{},[950,207337,207338,207339],{},"Async ",[941,207340,207341],{},"describeAsync()"," resolves the persisted map and threads it through. It silently degrades to no ids on any failure: no DEK, no persisted envelope yet, or an envelope without the map.",[937,207344,207345,207346,207348,207349,207352],{},"A collection that never opted into ",[941,207347,22790],{}," therefore describes every field with ",[941,207350,207351],{},"id: undefined"," on both paths.",[1022,207354,207356],{"id":207355},"generation-content-hash","Generation ↔ content hash",[937,207358,207359],{},"The vault-wide fence counter is now bound to the schema-set content hash live at that generation:",[1111,207361,207362,207371],{},[1114,207363,207364,207367,207368,1520],{},[941,207365,207366],{},"FenceDoc.schemaHash?: string"," — the envelope hash most recently persisted while the vault sat at ",[941,207369,207370],{},"currentSchemaVersion",[1114,207372,207373,207376,207377,207379],{},[941,207374,207375],{},"PersistedSchemaEnvelope.generation?: number"," — mirrors ",[941,207378,207370],{}," as of that envelope's most recent write.",[937,207381,207382],{},"Together these make \"generation N = which content hash\" answerable from published reads alone. Both are optional: a fresh vault, or a fence written before 0.6, simply omits them.",[1591,207384,207386],{"id":207385},"the-lazy-stamp-caveat","The lazy-stamp caveat",[937,207388,207389,14636,207392,207394,207395,207397,207398,207401,207402,207405],{},[950,207390,207391],{},"These fields do not update the instant a cutover completes.",[941,207393,57956],{}," finishes its drain barrier and advances ",[941,207396,207370],{}," immediately, but the envelope's ",[941,207399,207400],{},"generation"," and the fence's ",[941,207403,207404],{},"schemaHash"," only catch up on the next ordinary schema write — typically the very next collection registration or open.",[937,207407,207408,207409,207411],{},"So there is a brief window where ",[941,207410,207370],{}," has advanced but the hash pairing has not been re-stamped. This is pre-existing behaviour of the persist cycle rather than a new race, and it is stated here so nobody is surprised by it. The fence document is re-read immediately before stamping, which narrows — but does not eliminate — the window against a concurrent cutover on another collection.",[1022,207413,207415],{"id":207414},"renames-still-trip-the-guards-deliberately","Renames still trip the guards — deliberately",[937,207417,207418,207419,207422,207423,2311],{},"A rename is classified as ",[941,207420,207421],{},"kind: 'additive'"," (a label change is additive-safe), yet ",[950,207424,207425],{},"both schema-update guards still block it",[1111,207427,207428,207440],{},[1114,207429,207430,207435,207436,207439],{},[950,207431,207432],{},[941,207433,207434],{},"additiveOnly()"," rejects whenever ",[941,207437,207438],{},"renamed"," is non-empty, via the same non-additive error path as any other blocked change.",[1114,207441,207442,207447,207448,997,207450,207452],{},[950,207443,207444],{},[941,207445,207446],{},"lockSchema({ fields })"," treats both the ",[941,207449,2272],{},[941,207451,4879],{}," names as touching those fields — renaming away from a locked name, or onto one, still violates the lock.",[937,207454,207455,207456,207459,207460,207462,207463],{},"The reason matters. Reporting a rename performs ",[950,207457,207458],{},"no transform of existing record values"," under the old key. Only ",[941,207461,57956],{}," actually migrates data. If the guards treated a rename as freely admissible, a caller could rename a field with neither guard tripping and no migration running — ",[950,207464,207465],{},"silently orphaning every existing record's value under the old key.",[937,207467,207468,207469,207471,207472,207475,207476,207478,207479,207482],{},"And ",[941,207470,57956],{}," itself fires its transform on a rename, not only on a non-additive change: its gate checks ",[941,207473,207474],{},"delta.kind === 'non-additive' || renamed.length > 0",". Before field ids existed, a rename was a plain drop-plus-add and was classified non-additive, so the transform already fired. Without the explicit ",[941,207477,207438],{}," check, reclassifying it as additive would have ",[1092,207480,207481],{},"silently stopped the transform from running"," — exactly the orphaning the guards exist to prevent.",[937,207484,207485,207486,207488,207489,207492,207493],{},"The story now holds end to end: ",[941,207487,207434],{}," blocks a bare rename → its message points at ",[941,207490,207491],{},"coordinatedCutover()"," → registering one runs the transform, re-keying each record → and the id carries from the old name to the new. ",[950,207494,207495],{},"Identity and data survive the rename together.",[1022,207497,207499],{"id":207498},"why-generation-stays-vault-wide","Why generation stays vault-wide",[937,207501,207502,207503,207506],{},"Per-collection generation was evaluated and ",[950,207504,207505],{},"rejected",". The actual requirement — binding a generation number to a concrete content hash — is fully satisfied at vault granularity. Moving to per-collection counters would mean migrating the fence model itself: a new per-collection counter, per-collection drain-barrier semantics, and a new companion record shape, with nothing in the requirement demanding it. If finer-grained tracking is ever needed, it belongs with the manifest-engine work that is already redesigning how schema state is addressed.",[1022,207508,207510],{"id":207509},"back-compat","Back-compat",[937,207512,207513,207514,207516,207517],{},"Every new field is optional and additive. A pre-0.6 envelope loads and describes fine with ",[941,207515,6983],{}," simply absent; a pre-0.6 fence loads fine with the hash key treated as optional. Ids are minted lazily on the first persist after upgrade — ",[950,207518,207519],{},"nothing needs an explicit migration step.",[1022,207521,2621],{"id":2620},[1111,207523,207524,207530,207535,207543],{},[1114,207525,207526,207529],{},[1007,207527,780],{"href":207528},".\u002Fpersisted-schemas"," — the envelope these fields live on",[1114,207531,207532,207534],{},[1007,207533,820],{"href":194242}," — the guards and cutover strategy",[1114,207536,207537,964,207539,997,207541],{},[1007,207538,711],{"href":124740},[941,207540,3926],{},[941,207542,207341],{},[1114,207544,207545,207547],{},[1007,207546,735],{"href":196115}," — the manifest engine that consumes these fields",{"title":1037,"searchDepth":1038,"depth":1038,"links":207549},[207550,207554,207557,207558,207559,207560],{"id":207240,"depth":1041,"text":207241,"children":207551},[207552],{"id":207308,"depth":1038,"text":207553},"describe() surfaces it — with one asymmetry",{"id":207355,"depth":1041,"text":207356,"children":207555},[207556],{"id":207385,"depth":1038,"text":207386},{"id":207414,"depth":1041,"text":207415},{"id":207498,"depth":1041,"text":207499},{"id":207509,"depth":1041,"text":207510},{"id":2620,"depth":1041,"text":2621},"Opaque permanent field ids that survive a rename, plus a generation↔content-hash binding that makes 'which schema is generation N' answerable.",{},{"title":816,"description":207561},"7QyP-5jR81-WneqdnawN10G2-c9hFZgwqsNuWUNnt8g",{"id":207566,"title":820,"access":12,"apiToc":929,"body":207567,"category":1042,"description":210544,"extension":1044,"meta":210545,"navigation":1042,"nest":1047,"path":821,"seo":210546,"sinceVersion":1042,"status":1049,"stem":822,"subtitle":1042,"tier":1050,"tocExternal":1042,"__hash__":210547},"docs\u002Fservices\u002Fschema-update.md",{"type":931,"value":207568,"toc":210518},[207569,207571,207574,207576,207588,207600,207602,207876,207887,207889,207915,207919,207922,208269,208273,208279,208492,208503,208507,208524,208761,208765,208774,209543,209552,209554,209558,209564,209581,209587,209592,209607,209614,209620,209656,209667,209673,209705,209733,209739,209805,209820,209828,209995,210016,210024,210145,210165,210171,210220,210233,210239,210257,210267,210273,210382,210385,210387,210480,210482,210515],[934,207570,820],{"id":820},[46871,207572],{":rows":207573},"[{\"label\":\"Subpath\",\"value\":\"`@noy-db\u002Fhub` (root export)\"},{\"label\":\"Type\",\"value\":\"Core feature — activated by the `schemaUpdate` collection option (needs `persistJsonSchema: true` as the baseline).\"}]",[1022,207575,29857],{"id":29856},[937,207577,207578,207579,207581,207582,207584,207585,207587],{},"Your application's schema evolves — a field gets added, renamed, or its type changes — but a vault's existing records were encrypted and stored under the ",[1092,207580,207301],{}," shape. schema-update is how the hub reacts when it detects that drift: on every vault open it compares the freshly-derived schema against the one it persisted last time (see ",[1007,207583,780],{"href":207528},", which this feature builds on and requires), classifies the difference, and runs it past an ordered list of strategies you register per collection. Each strategy either lets the change through, rejects it (blocking writes until fixed), or — for ",[941,207586,57956],{}," — schedules a one-time bulk migration of the collection's existing records.",[937,207589,191906,207590,207592,207593,207595,207596,207599],{},[941,207591,943],{}," factory to add. The opt-in is two collection options together: ",[941,207594,191912],{}," (the baseline to detect against) and ",[941,207597,207598],{},"schemaUpdate: [...]"," (the strategies to run when a change is detected).",[1022,207601,58394],{"id":58393},[2217,207603,207605],{"className":2219,"code":207604,"language":2221,"meta":1037,"style":1037},"import { z } from 'zod'\nimport { createNoydb, additiveOnly } from '@noy-db\u002Fhub'\nimport { toMemory } from '@noy-db\u002Fto-memory'\n\nconst store = toMemory()\nconst db = await createNoydb({\n  store,\n  user: 'alice',\n  secret: 'a-strong-secret',\n})\n\nconst vault = await db.openVault('acme')\n\nconst invoices = vault.collection('invoices', {\n  schema: z.object({ id: z.string(), amount: z.number() }),\n  persistJsonSchema: true,\n  schemaUpdate: [additiveOnly()],\n})\n",[941,207606,207607,207625,207648,207666,207670,207682,207698,207704,207718,207732,207738,207742,207768,207772,207798,207846,207856,207870],{"__ignoreMap":1037},[2225,207608,207609,207611,207613,207615,207617,207619,207621,207623],{"class":2227,"line":2228},[2225,207610,2232],{"class":2231},[2225,207612,2236],{"class":2235},[2225,207614,28314],{"class":2239},[2225,207616,2243],{"class":2235},[2225,207618,2246],{"class":2231},[2225,207620,2249],{"class":2235},[2225,207622,28323],{"class":2252},[2225,207624,2256],{"class":2235},[2225,207626,207627,207629,207631,207633,207635,207638,207640,207642,207644,207646],{"class":2227,"line":1041},[2225,207628,2232],{"class":2231},[2225,207630,2236],{"class":2235},[2225,207632,2744],{"class":2239},[2225,207634,2298],{"class":2235},[2225,207636,207637],{"class":2239}," additiveOnly",[2225,207639,2243],{"class":2235},[2225,207641,2246],{"class":2231},[2225,207643,2249],{"class":2235},[2225,207645,1086],{"class":2252},[2225,207647,2256],{"class":2235},[2225,207649,207650,207652,207654,207656,207658,207660,207662,207664],{"class":2227,"line":1038},[2225,207651,2232],{"class":2231},[2225,207653,2236],{"class":2235},[2225,207655,68576],{"class":2239},[2225,207657,2243],{"class":2235},[2225,207659,2246],{"class":2231},[2225,207661,2249],{"class":2235},[2225,207663,9638],{"class":2252},[2225,207665,2256],{"class":2235},[2225,207667,207668],{"class":2227,"line":2288},[2225,207669,2285],{"emptyLinePlaceholder":2284},[2225,207671,207672,207674,207676,207678,207680],{"class":2227,"line":2304},[2225,207673,2775],{"class":2774},[2225,207675,39676],{"class":2239},[2225,207677,2781],{"class":2235},[2225,207679,68576],{"class":2291},[2225,207681,8146],{"class":2239},[2225,207683,207684,207686,207688,207690,207692,207694,207696],{"class":2227,"line":2356},[2225,207685,2775],{"class":2774},[2225,207687,2778],{"class":2239},[2225,207689,2781],{"class":2235},[2225,207691,2784],{"class":2231},[2225,207693,2744],{"class":2291},[2225,207695,2789],{"class":2239},[2225,207697,4200],{"class":2235},[2225,207699,207700,207702],{"class":2227,"line":2830},[2225,207701,15608],{"class":2239},[2225,207703,1707],{"class":2235},[2225,207705,207706,207708,207710,207712,207714,207716],{"class":2227,"line":2861},[2225,207707,4205],{"class":2307},[2225,207709,2311],{"class":2235},[2225,207711,2249],{"class":2235},[2225,207713,6408],{"class":2252},[2225,207715,2805],{"class":2235},[2225,207717,1707],{"class":2235},[2225,207719,207720,207722,207724,207726,207728,207730],{"class":2227,"line":4246},[2225,207721,10423],{"class":2307},[2225,207723,2311],{"class":2235},[2225,207725,2249],{"class":2235},[2225,207727,6421],{"class":2252},[2225,207729,2805],{"class":2235},[2225,207731,1707],{"class":2235},[2225,207733,207734,207736],{"class":2227,"line":4253},[2225,207735,2359],{"class":2235},[2225,207737,2362],{"class":2239},[2225,207739,207740],{"class":2227,"line":4259},[2225,207741,2285],{"emptyLinePlaceholder":2284},[2225,207743,207744,207746,207748,207750,207752,207754,207756,207758,207760,207762,207764,207766],{"class":2227,"line":6548},[2225,207745,2775],{"class":2774},[2225,207747,2835],{"class":2239},[2225,207749,2781],{"class":2235},[2225,207751,2784],{"class":2231},[2225,207753,2842],{"class":2239},[2225,207755,1520],{"class":2235},[2225,207757,2847],{"class":2291},[2225,207759,2789],{"class":2239},[2225,207761,2805],{"class":2235},[2225,207763,6450],{"class":2252},[2225,207765,2805],{"class":2235},[2225,207767,2362],{"class":2239},[2225,207769,207770],{"class":2227,"line":6576},[2225,207771,2285],{"emptyLinePlaceholder":2284},[2225,207773,207774,207776,207778,207780,207782,207784,207786,207788,207790,207792,207794,207796],{"class":2227,"line":6582},[2225,207775,2775],{"class":2774},[2225,207777,6461],{"class":2239},[2225,207779,2781],{"class":2235},[2225,207781,6466],{"class":2239},[2225,207783,1520],{"class":2235},[2225,207785,6471],{"class":2291},[2225,207787,2789],{"class":2239},[2225,207789,2805],{"class":2235},[2225,207791,6487],{"class":2252},[2225,207793,2805],{"class":2235},[2225,207795,2298],{"class":2235},[2225,207797,2301],{"class":2235},[2225,207799,207800,207802,207804,207806,207808,207810,207812,207814,207816,207818,207820,207822,207824,207826,207828,207830,207832,207834,207836,207838,207840,207842,207844],{"class":2227,"line":6587},[2225,207801,28618],{"class":2307},[2225,207803,2311],{"class":2235},[2225,207805,28314],{"class":2239},[2225,207807,1520],{"class":2235},[2225,207809,25505],{"class":2291},[2225,207811,2789],{"class":2239},[2225,207813,2792],{"class":2235},[2225,207815,7070],{"class":2307},[2225,207817,2311],{"class":2235},[2225,207819,28314],{"class":2239},[2225,207821,1520],{"class":2235},[2225,207823,7259],{"class":2291},[2225,207825,4228],{"class":2239},[2225,207827,2298],{"class":2235},[2225,207829,6351],{"class":2307},[2225,207831,2311],{"class":2235},[2225,207833,28314],{"class":2239},[2225,207835,1520],{"class":2235},[2225,207837,20636],{"class":2291},[2225,207839,19132],{"class":2239},[2225,207841,2359],{"class":2235},[2225,207843,1381],{"class":2239},[2225,207845,1707],{"class":2235},[2225,207847,207848,207850,207852,207854],{"class":2227,"line":7174},[2225,207849,192181],{"class":2307},[2225,207851,2311],{"class":2235},[2225,207853,2322],{"class":2321},[2225,207855,1707],{"class":2235},[2225,207857,207858,207860,207862,207864,207866,207868],{"class":2227,"line":7180},[2225,207859,150038],{"class":2307},[2225,207861,2311],{"class":2235},[2225,207863,8667],{"class":2239},[2225,207865,58176],{"class":2291},[2225,207867,8681],{"class":2239},[2225,207869,1707],{"class":2235},[2225,207871,207872,207874],{"class":2227,"line":7220},[2225,207873,2359],{"class":2235},[2225,207875,2362],{"class":2239},[937,207877,207878,207880,207881,207883,207884,207886],{},[941,207879,22793],{}," is ignored unless ",[941,207882,191912],{}," is also set — detection needs the persisted baseline to compare against. With no ",[941,207885,22793],{}," strategies at all, schema changes are accepted silently (the pre-schema-update behavior): only registering strategies turns on enforcement.",[1022,207888,29893],{"id":29892},[7646,207890,207891],{"color":7648,"icon":579},[937,207892,207893,207896,207897,207900,207901,207904,207905,207907,207908,207911,207912,207914],{},[950,207894,207895],{},"About these snippets."," Each one reopens the same vault twice in a row — ",[941,207898,207899],{},"gen 0"," then\n",[941,207902,207903],{},"gen 1"," — to simulate two separate application deployments seeing two versions of the\nschema, without needing two real processes. ",[941,207906,191912],{}," writes the schema\nbaseline in the background, so the snippets call ",[941,207909,207910],{},"vault._drainPendingSchemaWrites()"," — a\ntest-only synchronization helper — to wait for that write before moving on to ",[941,207913,207903],{},". A\nreal deployment reopening a vault in a later process never needs to call it: by the time\nthat process starts, the previous write has long since finished.",[1591,207916,207918],{"id":207917},"an-additive-change-passes","An additive change passes",[937,207920,207921],{},"Adding a new optional field is the safe case — every registered strategy allows it by default.",[2217,207923,207925],{"className":2219,"code":207924,"language":2221,"meta":1037,"style":1037},"\u002F\u002F gen 0: register with the original shape\nlet vault = await db.openVault('acme')\nvault.collection('invoices', {\n  schema: z.object({ id: z.string() }),\n  persistJsonSchema: true,\n  schemaUpdate: [additiveOnly()],\n})\nawait vault._drainPendingSchemaWrites() \u002F\u002F test-only: see note above\n\n\u002F\u002F gen 1 (a later process, or a reopened vault): a new optional field\nvault = await db.openVault('acme')\nconst invoices = vault.collection('invoices', {\n  schema: z.object({ id: z.string(), amount: z.number().optional() }), \u002F\u002F additive\n  persistJsonSchema: true,\n  schemaUpdate: [additiveOnly()],\n})\nawait vault._drainPendingSchemaWrites() \u002F\u002F test-only: see note above\n\nawait invoices.put('i1', { id: 'i1', amount: 10 }) \u002F\u002F → resolves\n",[941,207926,207927,207932,207958,207978,208012,208022,208036,208042,208057,208061,208066,208091,208117,208174,208184,208198,208204,208218,208222],{"__ignoreMap":1037},[2225,207928,207929],{"class":2227,"line":2228},[2225,207930,207931],{"class":2763},"\u002F\u002F gen 0: register with the original shape\n",[2225,207933,207934,207936,207938,207940,207942,207944,207946,207948,207950,207952,207954,207956],{"class":2227,"line":1041},[2225,207935,46091],{"class":2774},[2225,207937,2835],{"class":2239},[2225,207939,2781],{"class":2235},[2225,207941,2784],{"class":2231},[2225,207943,2842],{"class":2239},[2225,207945,1520],{"class":2235},[2225,207947,2847],{"class":2291},[2225,207949,2789],{"class":2239},[2225,207951,2805],{"class":2235},[2225,207953,6450],{"class":2252},[2225,207955,2805],{"class":2235},[2225,207957,2362],{"class":2239},[2225,207959,207960,207962,207964,207966,207968,207970,207972,207974,207976],{"class":2227,"line":1038},[2225,207961,2694],{"class":2239},[2225,207963,1520],{"class":2235},[2225,207965,6471],{"class":2291},[2225,207967,2789],{"class":2239},[2225,207969,2805],{"class":2235},[2225,207971,6487],{"class":2252},[2225,207973,2805],{"class":2235},[2225,207975,2298],{"class":2235},[2225,207977,2301],{"class":2235},[2225,207979,207980,207982,207984,207986,207988,207990,207992,207994,207996,207998,208000,208002,208004,208006,208008,208010],{"class":2227,"line":2288},[2225,207981,28618],{"class":2307},[2225,207983,2311],{"class":2235},[2225,207985,28314],{"class":2239},[2225,207987,1520],{"class":2235},[2225,207989,25505],{"class":2291},[2225,207991,2789],{"class":2239},[2225,207993,2792],{"class":2235},[2225,207995,7070],{"class":2307},[2225,207997,2311],{"class":2235},[2225,207999,28314],{"class":2239},[2225,208001,1520],{"class":2235},[2225,208003,7259],{"class":2291},[2225,208005,19132],{"class":2239},[2225,208007,2359],{"class":2235},[2225,208009,1381],{"class":2239},[2225,208011,1707],{"class":2235},[2225,208013,208014,208016,208018,208020],{"class":2227,"line":2304},[2225,208015,192181],{"class":2307},[2225,208017,2311],{"class":2235},[2225,208019,2322],{"class":2321},[2225,208021,1707],{"class":2235},[2225,208023,208024,208026,208028,208030,208032,208034],{"class":2227,"line":2356},[2225,208025,150038],{"class":2307},[2225,208027,2311],{"class":2235},[2225,208029,8667],{"class":2239},[2225,208031,58176],{"class":2291},[2225,208033,8681],{"class":2239},[2225,208035,1707],{"class":2235},[2225,208037,208038,208040],{"class":2227,"line":2830},[2225,208039,2359],{"class":2235},[2225,208041,2362],{"class":2239},[2225,208043,208044,208046,208048,208050,208052,208054],{"class":2227,"line":2861},[2225,208045,6500],{"class":2231},[2225,208047,6466],{"class":2239},[2225,208049,1520],{"class":2235},[2225,208051,192229],{"class":2291},[2225,208053,19132],{"class":2239},[2225,208055,208056],{"class":2763},"\u002F\u002F test-only: see note above\n",[2225,208058,208059],{"class":2227,"line":4246},[2225,208060,2285],{"emptyLinePlaceholder":2284},[2225,208062,208063],{"class":2227,"line":4253},[2225,208064,208065],{"class":2763},"\u002F\u002F gen 1 (a later process, or a reopened vault): a new optional field\n",[2225,208067,208068,208071,208073,208075,208077,208079,208081,208083,208085,208087,208089],{"class":2227,"line":4259},[2225,208069,208070],{"class":2239},"vault ",[2225,208072,2781],{"class":2235},[2225,208074,2784],{"class":2231},[2225,208076,2842],{"class":2239},[2225,208078,1520],{"class":2235},[2225,208080,2847],{"class":2291},[2225,208082,2789],{"class":2239},[2225,208084,2805],{"class":2235},[2225,208086,6450],{"class":2252},[2225,208088,2805],{"class":2235},[2225,208090,2362],{"class":2239},[2225,208092,208093,208095,208097,208099,208101,208103,208105,208107,208109,208111,208113,208115],{"class":2227,"line":6548},[2225,208094,2775],{"class":2774},[2225,208096,6461],{"class":2239},[2225,208098,2781],{"class":2235},[2225,208100,6466],{"class":2239},[2225,208102,1520],{"class":2235},[2225,208104,6471],{"class":2291},[2225,208106,2789],{"class":2239},[2225,208108,2805],{"class":2235},[2225,208110,6487],{"class":2252},[2225,208112,2805],{"class":2235},[2225,208114,2298],{"class":2235},[2225,208116,2301],{"class":2235},[2225,208118,208119,208121,208123,208125,208127,208129,208131,208133,208135,208137,208139,208141,208143,208145,208147,208149,208151,208153,208155,208157,208159,208161,208163,208165,208167,208169,208171],{"class":2227,"line":6576},[2225,208120,28618],{"class":2307},[2225,208122,2311],{"class":2235},[2225,208124,28314],{"class":2239},[2225,208126,1520],{"class":2235},[2225,208128,25505],{"class":2291},[2225,208130,2789],{"class":2239},[2225,208132,2792],{"class":2235},[2225,208134,7070],{"class":2307},[2225,208136,2311],{"class":2235},[2225,208138,28314],{"class":2239},[2225,208140,1520],{"class":2235},[2225,208142,7259],{"class":2291},[2225,208144,4228],{"class":2239},[2225,208146,2298],{"class":2235},[2225,208148,6351],{"class":2307},[2225,208150,2311],{"class":2235},[2225,208152,28314],{"class":2239},[2225,208154,1520],{"class":2235},[2225,208156,20636],{"class":2291},[2225,208158,4228],{"class":2239},[2225,208160,1520],{"class":2235},[2225,208162,2084],{"class":2291},[2225,208164,19132],{"class":2239},[2225,208166,2359],{"class":2235},[2225,208168,1381],{"class":2239},[2225,208170,2298],{"class":2235},[2225,208172,208173],{"class":2763}," \u002F\u002F additive\n",[2225,208175,208176,208178,208180,208182],{"class":2227,"line":6582},[2225,208177,192181],{"class":2307},[2225,208179,2311],{"class":2235},[2225,208181,2322],{"class":2321},[2225,208183,1707],{"class":2235},[2225,208185,208186,208188,208190,208192,208194,208196],{"class":2227,"line":6587},[2225,208187,150038],{"class":2307},[2225,208189,2311],{"class":2235},[2225,208191,8667],{"class":2239},[2225,208193,58176],{"class":2291},[2225,208195,8681],{"class":2239},[2225,208197,1707],{"class":2235},[2225,208199,208200,208202],{"class":2227,"line":7174},[2225,208201,2359],{"class":2235},[2225,208203,2362],{"class":2239},[2225,208205,208206,208208,208210,208212,208214,208216],{"class":2227,"line":7180},[2225,208207,6500],{"class":2231},[2225,208209,6466],{"class":2239},[2225,208211,1520],{"class":2235},[2225,208213,192229],{"class":2291},[2225,208215,19132],{"class":2239},[2225,208217,208056],{"class":2763},[2225,208219,208220],{"class":2227,"line":7220},[2225,208221,2285],{"emptyLinePlaceholder":2284},[2225,208223,208224,208226,208228,208230,208232,208234,208236,208238,208240,208242,208244,208246,208248,208250,208252,208254,208256,208258,208260,208262,208264,208266],{"class":2227,"line":7225},[2225,208225,6500],{"class":2231},[2225,208227,6503],{"class":2239},[2225,208229,1520],{"class":2235},[2225,208231,3161],{"class":2291},[2225,208233,2789],{"class":2239},[2225,208235,2805],{"class":2235},[2225,208237,62891],{"class":2252},[2225,208239,2805],{"class":2235},[2225,208241,2298],{"class":2235},[2225,208243,2236],{"class":2235},[2225,208245,7070],{"class":2307},[2225,208247,2311],{"class":2235},[2225,208249,2249],{"class":2235},[2225,208251,62891],{"class":2252},[2225,208253,2805],{"class":2235},[2225,208255,2298],{"class":2235},[2225,208257,6351],{"class":2307},[2225,208259,2311],{"class":2235},[2225,208261,45554],{"class":6527},[2225,208263,2243],{"class":2235},[2225,208265,8495],{"class":2239},[2225,208267,208268],{"class":2763},"\u002F\u002F → resolves\n",[1591,208270,208272],{"id":208271},"a-non-additive-change-is-rejected","A non-additive change is rejected",[937,208274,208275,208276,208278],{},"Removing a field, changing a field's shape, or flipping required-ness is non-additive. ",[941,208277,207434],{}," is the safety backstop — it rejects the change and every write throws until you fix the schema or add a migration strategy:",[2217,208280,208282],{"className":2219,"code":208281,"language":2221,"meta":1037,"style":1037},"import { additiveOnly } from '@noy-db\u002Fhub'\n\n\u002F\u002F gen 1 removes 'amount' from the schema above — non-additive\nvault = await db.openVault('acme')\nconst invoices = vault.collection('invoices', {\n  schema: z.object({ id: z.string() }),\n  persistJsonSchema: true,\n  schemaUpdate: [additiveOnly()],\n})\nawait vault._drainPendingSchemaWrites()\n\nawait invoices.put('i1', { id: 'i1' })\n\u002F\u002F → throws NonAdditiveSchemaChangeError:\n\u002F\u002F 'Non-additive schema change to \"invoices\" (added: [], removed: [amount], changed: []).\n\u002F\u002F  Register a coordinatedCutover() strategy to migrate, or revert the change.'\n",[941,208283,208284,208302,208306,208311,208335,208361,208395,208405,208419,208425,208437,208441,208477,208482,208487],{"__ignoreMap":1037},[2225,208285,208286,208288,208290,208292,208294,208296,208298,208300],{"class":2227,"line":2228},[2225,208287,2232],{"class":2231},[2225,208289,2236],{"class":2235},[2225,208291,207637],{"class":2239},[2225,208293,2243],{"class":2235},[2225,208295,2246],{"class":2231},[2225,208297,2249],{"class":2235},[2225,208299,1086],{"class":2252},[2225,208301,2256],{"class":2235},[2225,208303,208304],{"class":2227,"line":1041},[2225,208305,2285],{"emptyLinePlaceholder":2284},[2225,208307,208308],{"class":2227,"line":1038},[2225,208309,208310],{"class":2763},"\u002F\u002F gen 1 removes 'amount' from the schema above — non-additive\n",[2225,208312,208313,208315,208317,208319,208321,208323,208325,208327,208329,208331,208333],{"class":2227,"line":2288},[2225,208314,208070],{"class":2239},[2225,208316,2781],{"class":2235},[2225,208318,2784],{"class":2231},[2225,208320,2842],{"class":2239},[2225,208322,1520],{"class":2235},[2225,208324,2847],{"class":2291},[2225,208326,2789],{"class":2239},[2225,208328,2805],{"class":2235},[2225,208330,6450],{"class":2252},[2225,208332,2805],{"class":2235},[2225,208334,2362],{"class":2239},[2225,208336,208337,208339,208341,208343,208345,208347,208349,208351,208353,208355,208357,208359],{"class":2227,"line":2304},[2225,208338,2775],{"class":2774},[2225,208340,6461],{"class":2239},[2225,208342,2781],{"class":2235},[2225,208344,6466],{"class":2239},[2225,208346,1520],{"class":2235},[2225,208348,6471],{"class":2291},[2225,208350,2789],{"class":2239},[2225,208352,2805],{"class":2235},[2225,208354,6487],{"class":2252},[2225,208356,2805],{"class":2235},[2225,208358,2298],{"class":2235},[2225,208360,2301],{"class":2235},[2225,208362,208363,208365,208367,208369,208371,208373,208375,208377,208379,208381,208383,208385,208387,208389,208391,208393],{"class":2227,"line":2356},[2225,208364,28618],{"class":2307},[2225,208366,2311],{"class":2235},[2225,208368,28314],{"class":2239},[2225,208370,1520],{"class":2235},[2225,208372,25505],{"class":2291},[2225,208374,2789],{"class":2239},[2225,208376,2792],{"class":2235},[2225,208378,7070],{"class":2307},[2225,208380,2311],{"class":2235},[2225,208382,28314],{"class":2239},[2225,208384,1520],{"class":2235},[2225,208386,7259],{"class":2291},[2225,208388,19132],{"class":2239},[2225,208390,2359],{"class":2235},[2225,208392,1381],{"class":2239},[2225,208394,1707],{"class":2235},[2225,208396,208397,208399,208401,208403],{"class":2227,"line":2830},[2225,208398,192181],{"class":2307},[2225,208400,2311],{"class":2235},[2225,208402,2322],{"class":2321},[2225,208404,1707],{"class":2235},[2225,208406,208407,208409,208411,208413,208415,208417],{"class":2227,"line":2861},[2225,208408,150038],{"class":2307},[2225,208410,2311],{"class":2235},[2225,208412,8667],{"class":2239},[2225,208414,58176],{"class":2291},[2225,208416,8681],{"class":2239},[2225,208418,1707],{"class":2235},[2225,208420,208421,208423],{"class":2227,"line":4246},[2225,208422,2359],{"class":2235},[2225,208424,2362],{"class":2239},[2225,208426,208427,208429,208431,208433,208435],{"class":2227,"line":4253},[2225,208428,6500],{"class":2231},[2225,208430,6466],{"class":2239},[2225,208432,1520],{"class":2235},[2225,208434,192229],{"class":2291},[2225,208436,8146],{"class":2239},[2225,208438,208439],{"class":2227,"line":4259},[2225,208440,2285],{"emptyLinePlaceholder":2284},[2225,208442,208443,208445,208447,208449,208451,208453,208455,208457,208459,208461,208463,208465,208467,208469,208471,208473,208475],{"class":2227,"line":6548},[2225,208444,6500],{"class":2231},[2225,208446,6503],{"class":2239},[2225,208448,1520],{"class":2235},[2225,208450,3161],{"class":2291},[2225,208452,2789],{"class":2239},[2225,208454,2805],{"class":2235},[2225,208456,62891],{"class":2252},[2225,208458,2805],{"class":2235},[2225,208460,2298],{"class":2235},[2225,208462,2236],{"class":2235},[2225,208464,7070],{"class":2307},[2225,208466,2311],{"class":2235},[2225,208468,2249],{"class":2235},[2225,208470,62891],{"class":2252},[2225,208472,2805],{"class":2235},[2225,208474,2243],{"class":2235},[2225,208476,2362],{"class":2239},[2225,208478,208479],{"class":2227,"line":6576},[2225,208480,208481],{"class":2763},"\u002F\u002F → throws NonAdditiveSchemaChangeError:\n",[2225,208483,208484],{"class":2227,"line":6582},[2225,208485,208486],{"class":2763},"\u002F\u002F 'Non-additive schema change to \"invoices\" (added: [], removed: [amount], changed: []).\n",[2225,208488,208489],{"class":2227,"line":6587},[2225,208490,208491],{"class":2763},"\u002F\u002F  Register a coordinatedCutover() strategy to migrate, or revert the change.'\n",[937,208493,208494,208495,1625,208497,208499,208500,208502],{},"The rejection happens at write time (",[941,208496,3161],{},[941,208498,3164],{},"), not at ",[941,208501,2138],{}," — registering a collection with a rejected schema change never throws; only the next write does.",[1591,208504,208506],{"id":208505},"locking-a-collection-outright","Locking a collection outright",[937,208508,208509,208512,208513,208515,208516,208518,208519,162552,208521,208523],{},[941,208510,208511],{},"lockSchema()"," refuses any schema change (or, with ",[941,208514,57285],{},", only changes that touch specific fields) regardless of whether the change would otherwise be additive. Strategies run in order and the first non-",[941,208517,34720],{}," decision wins, so putting ",[941,208520,208511],{},[941,208522,207434],{}," makes the lock take precedence:",[2217,208525,208527],{"className":2219,"code":208526,"language":2221,"meta":1037,"style":1037},"import { lockSchema, additiveOnly } from '@noy-db\u002Fhub'\n\nvault = await db.openVault('acme')\nconst invoices = vault.collection('invoices', {\n  schema: z.object({ id: z.string(), note: z.string().optional() }), \u002F\u002F additive, but locked\n  persistJsonSchema: true,\n  schemaUpdate: [lockSchema(), additiveOnly()],\n})\nawait vault._drainPendingSchemaWrites()\n\nawait invoices.put('i1', { id: 'i1' })\n\u002F\u002F → throws SchemaLockedError:\n\u002F\u002F 'Schema for \"invoices\" is locked; the change was refused.'\n",[941,208528,208529,208552,208556,208580,208606,208663,208673,208693,208699,208711,208715,208751,208756],{"__ignoreMap":1037},[2225,208530,208531,208533,208535,208538,208540,208542,208544,208546,208548,208550],{"class":2227,"line":2228},[2225,208532,2232],{"class":2231},[2225,208534,2236],{"class":2235},[2225,208536,208537],{"class":2239}," lockSchema",[2225,208539,2298],{"class":2235},[2225,208541,207637],{"class":2239},[2225,208543,2243],{"class":2235},[2225,208545,2246],{"class":2231},[2225,208547,2249],{"class":2235},[2225,208549,1086],{"class":2252},[2225,208551,2256],{"class":2235},[2225,208553,208554],{"class":2227,"line":1041},[2225,208555,2285],{"emptyLinePlaceholder":2284},[2225,208557,208558,208560,208562,208564,208566,208568,208570,208572,208574,208576,208578],{"class":2227,"line":1038},[2225,208559,208070],{"class":2239},[2225,208561,2781],{"class":2235},[2225,208563,2784],{"class":2231},[2225,208565,2842],{"class":2239},[2225,208567,1520],{"class":2235},[2225,208569,2847],{"class":2291},[2225,208571,2789],{"class":2239},[2225,208573,2805],{"class":2235},[2225,208575,6450],{"class":2252},[2225,208577,2805],{"class":2235},[2225,208579,2362],{"class":2239},[2225,208581,208582,208584,208586,208588,208590,208592,208594,208596,208598,208600,208602,208604],{"class":2227,"line":2288},[2225,208583,2775],{"class":2774},[2225,208585,6461],{"class":2239},[2225,208587,2781],{"class":2235},[2225,208589,6466],{"class":2239},[2225,208591,1520],{"class":2235},[2225,208593,6471],{"class":2291},[2225,208595,2789],{"class":2239},[2225,208597,2805],{"class":2235},[2225,208599,6487],{"class":2252},[2225,208601,2805],{"class":2235},[2225,208603,2298],{"class":2235},[2225,208605,2301],{"class":2235},[2225,208607,208608,208610,208612,208614,208616,208618,208620,208622,208624,208626,208628,208630,208632,208634,208636,208638,208640,208642,208644,208646,208648,208650,208652,208654,208656,208658,208660],{"class":2227,"line":2304},[2225,208609,28618],{"class":2307},[2225,208611,2311],{"class":2235},[2225,208613,28314],{"class":2239},[2225,208615,1520],{"class":2235},[2225,208617,25505],{"class":2291},[2225,208619,2789],{"class":2239},[2225,208621,2792],{"class":2235},[2225,208623,7070],{"class":2307},[2225,208625,2311],{"class":2235},[2225,208627,28314],{"class":2239},[2225,208629,1520],{"class":2235},[2225,208631,7259],{"class":2291},[2225,208633,4228],{"class":2239},[2225,208635,2298],{"class":2235},[2225,208637,193013],{"class":2307},[2225,208639,2311],{"class":2235},[2225,208641,28314],{"class":2239},[2225,208643,1520],{"class":2235},[2225,208645,7259],{"class":2291},[2225,208647,4228],{"class":2239},[2225,208649,1520],{"class":2235},[2225,208651,2084],{"class":2291},[2225,208653,19132],{"class":2239},[2225,208655,2359],{"class":2235},[2225,208657,1381],{"class":2239},[2225,208659,2298],{"class":2235},[2225,208661,208662],{"class":2763}," \u002F\u002F additive, but locked\n",[2225,208664,208665,208667,208669,208671],{"class":2227,"line":2356},[2225,208666,192181],{"class":2307},[2225,208668,2311],{"class":2235},[2225,208670,2322],{"class":2321},[2225,208672,1707],{"class":2235},[2225,208674,208675,208677,208679,208681,208683,208685,208687,208689,208691],{"class":2227,"line":2830},[2225,208676,150038],{"class":2307},[2225,208678,2311],{"class":2235},[2225,208680,8667],{"class":2239},[2225,208682,58179],{"class":2291},[2225,208684,4228],{"class":2239},[2225,208686,2298],{"class":2235},[2225,208688,207637],{"class":2291},[2225,208690,8681],{"class":2239},[2225,208692,1707],{"class":2235},[2225,208694,208695,208697],{"class":2227,"line":2861},[2225,208696,2359],{"class":2235},[2225,208698,2362],{"class":2239},[2225,208700,208701,208703,208705,208707,208709],{"class":2227,"line":4246},[2225,208702,6500],{"class":2231},[2225,208704,6466],{"class":2239},[2225,208706,1520],{"class":2235},[2225,208708,192229],{"class":2291},[2225,208710,8146],{"class":2239},[2225,208712,208713],{"class":2227,"line":4253},[2225,208714,2285],{"emptyLinePlaceholder":2284},[2225,208716,208717,208719,208721,208723,208725,208727,208729,208731,208733,208735,208737,208739,208741,208743,208745,208747,208749],{"class":2227,"line":4259},[2225,208718,6500],{"class":2231},[2225,208720,6503],{"class":2239},[2225,208722,1520],{"class":2235},[2225,208724,3161],{"class":2291},[2225,208726,2789],{"class":2239},[2225,208728,2805],{"class":2235},[2225,208730,62891],{"class":2252},[2225,208732,2805],{"class":2235},[2225,208734,2298],{"class":2235},[2225,208736,2236],{"class":2235},[2225,208738,7070],{"class":2307},[2225,208740,2311],{"class":2235},[2225,208742,2249],{"class":2235},[2225,208744,62891],{"class":2252},[2225,208746,2805],{"class":2235},[2225,208748,2243],{"class":2235},[2225,208750,2362],{"class":2239},[2225,208752,208753],{"class":2227,"line":6548},[2225,208754,208755],{"class":2763},"\u002F\u002F → throws SchemaLockedError:\n",[2225,208757,208758],{"class":2227,"line":6576},[2225,208759,208760],{"class":2763},"\u002F\u002F 'Schema for \"invoices\" is locked; the change was refused.'\n",[1591,208762,208764],{"id":208763},"a-coordinated-cutover-migrates-existing-records","A coordinated cutover migrates existing records",[937,208766,208767,208768,208770,208771,208773],{},"Some non-additive changes are legitimate — a field is being reshaped, not dropped. ",[941,208769,207491],{}," accepts the change but defers it: it registers a pending migration (a ",[941,208772,28839],{}," function run over every existing record) and fences the collection's writes until you explicitly run the cutover.",[2217,208775,208777],{"className":2219,"code":208776,"language":2221,"meta":1037,"style":1037},"import { coordinatedCutover, additiveOnly } from '@noy-db\u002Fhub'\n\ninterface InvOld extends Record\u003Cstring, unknown> { id: string; total: number }\ninterface InvNew extends Record\u003Cstring, unknown> { id: string; amount: { gross: number } }\n\n\u002F\u002F gen 0: old shape, one record\nlet vault = await db.openVault('acme')\nconst oldInvoices = vault.collection\u003CInvOld>('invoices', {\n  schema: z.object({ id: z.string(), total: z.number() }),\n  persistJsonSchema: true,\n})\nawait vault._drainPendingSchemaWrites()\nawait oldInvoices.put('i1', { id: 'i1', total: 100 })\n\n\u002F\u002F gen 1: reshape `total` into `amount.gross` — non-additive\nvault = await db.openVault('acme')\nconst invoices = vault.collection\u003CInvNew>('invoices', {\n  schema: z.object({ id: z.string(), amount: z.object({ gross: z.number() }) }),\n  persistJsonSchema: true,\n  schemaUpdate: [\n    coordinatedCutover({ transform: (d) => ({ id: d['id'], amount: { gross: d['total'] } }) }),\n    additiveOnly(),\n  ],\n})\nawait vault._drainPendingSchemaWrites()\n\nawait invoices.put('i2', { id: 'i2', amount: { gross: 5 } })\n\u002F\u002F → throws SchemaFenceError: writes are blocked while the cutover is pending\n\nconst result = await vault.runSchemaCutover()\nresult.migrated \u002F\u002F → 1  (i1 rewritten in place through the transform)\n\nconst i1 = await invoices.get('i1')\ni1?.amount.gross \u002F\u002F → 100\n\nawait invoices.put('i2', { id: 'i2', amount: { gross: 5 } }) \u002F\u002F → resolves — the fence is clear\n",[941,208778,208779,208802,208806,208845,208893,208897,208902,208928,208962,209010,209020,209026,209038,209083,209087,209092,209116,209149,209215,209225,209233,209308,209317,209323,209329,209341,209345,209397,209402,209406,209425,209437,209441,209468,209484,209488],{"__ignoreMap":1037},[2225,208780,208781,208783,208785,208788,208790,208792,208794,208796,208798,208800],{"class":2227,"line":2228},[2225,208782,2232],{"class":2231},[2225,208784,2236],{"class":2235},[2225,208786,208787],{"class":2239}," coordinatedCutover",[2225,208789,2298],{"class":2235},[2225,208791,207637],{"class":2239},[2225,208793,2243],{"class":2235},[2225,208795,2246],{"class":2231},[2225,208797,2249],{"class":2235},[2225,208799,1086],{"class":2252},[2225,208801,2256],{"class":2235},[2225,208803,208804],{"class":2227,"line":1041},[2225,208805,2285],{"emptyLinePlaceholder":2284},[2225,208807,208808,208810,208813,208815,208817,208819,208821,208823,208825,208827,208829,208831,208833,208835,208837,208839,208841,208843],{"class":2227,"line":1038},[2225,208809,6994],{"class":2774},[2225,208811,208812],{"class":6342}," InvOld",[2225,208814,124350],{"class":2774},[2225,208816,22527],{"class":6342},[2225,208818,6474],{"class":2235},[2225,208820,7259],{"class":6342},[2225,208822,2298],{"class":2235},[2225,208824,40717],{"class":6342},[2225,208826,6480],{"class":2235},[2225,208828,2236],{"class":2235},[2225,208830,7070],{"class":2307},[2225,208832,2311],{"class":2235},[2225,208834,6366],{"class":6342},[2225,208836,3769],{"class":2235},[2225,208838,27452],{"class":2307},[2225,208840,2311],{"class":2235},[2225,208842,6356],{"class":6342},[2225,208844,6369],{"class":2235},[2225,208846,208847,208849,208852,208854,208856,208858,208860,208862,208864,208866,208868,208870,208872,208874,208876,208878,208880,208882,208885,208887,208889,208891],{"class":2227,"line":2288},[2225,208848,6994],{"class":2774},[2225,208850,208851],{"class":6342}," InvNew",[2225,208853,124350],{"class":2774},[2225,208855,22527],{"class":6342},[2225,208857,6474],{"class":2235},[2225,208859,7259],{"class":6342},[2225,208861,2298],{"class":2235},[2225,208863,40717],{"class":6342},[2225,208865,6480],{"class":2235},[2225,208867,2236],{"class":2235},[2225,208869,7070],{"class":2307},[2225,208871,2311],{"class":2235},[2225,208873,6366],{"class":6342},[2225,208875,3769],{"class":2235},[2225,208877,6351],{"class":2307},[2225,208879,2311],{"class":2235},[2225,208881,2236],{"class":2235},[2225,208883,208884],{"class":2307}," gross",[2225,208886,2311],{"class":2235},[2225,208888,6356],{"class":6342},[2225,208890,2243],{"class":2235},[2225,208892,6369],{"class":2235},[2225,208894,208895],{"class":2227,"line":2304},[2225,208896,2285],{"emptyLinePlaceholder":2284},[2225,208898,208899],{"class":2227,"line":2356},[2225,208900,208901],{"class":2763},"\u002F\u002F gen 0: old shape, one record\n",[2225,208903,208904,208906,208908,208910,208912,208914,208916,208918,208920,208922,208924,208926],{"class":2227,"line":2830},[2225,208905,46091],{"class":2774},[2225,208907,2835],{"class":2239},[2225,208909,2781],{"class":2235},[2225,208911,2784],{"class":2231},[2225,208913,2842],{"class":2239},[2225,208915,1520],{"class":2235},[2225,208917,2847],{"class":2291},[2225,208919,2789],{"class":2239},[2225,208921,2805],{"class":2235},[2225,208923,6450],{"class":2252},[2225,208925,2805],{"class":2235},[2225,208927,2362],{"class":2239},[2225,208929,208930,208932,208935,208937,208939,208941,208943,208945,208948,208950,208952,208954,208956,208958,208960],{"class":2227,"line":2861},[2225,208931,2775],{"class":2774},[2225,208933,208934],{"class":2239}," oldInvoices ",[2225,208936,2781],{"class":2235},[2225,208938,6466],{"class":2239},[2225,208940,1520],{"class":2235},[2225,208942,6471],{"class":2291},[2225,208944,6474],{"class":2235},[2225,208946,208947],{"class":6342},"InvOld",[2225,208949,6480],{"class":2235},[2225,208951,2789],{"class":2239},[2225,208953,2805],{"class":2235},[2225,208955,6487],{"class":2252},[2225,208957,2805],{"class":2235},[2225,208959,2298],{"class":2235},[2225,208961,2301],{"class":2235},[2225,208963,208964,208966,208968,208970,208972,208974,208976,208978,208980,208982,208984,208986,208988,208990,208992,208994,208996,208998,209000,209002,209004,209006,209008],{"class":2227,"line":4246},[2225,208965,28618],{"class":2307},[2225,208967,2311],{"class":2235},[2225,208969,28314],{"class":2239},[2225,208971,1520],{"class":2235},[2225,208973,25505],{"class":2291},[2225,208975,2789],{"class":2239},[2225,208977,2792],{"class":2235},[2225,208979,7070],{"class":2307},[2225,208981,2311],{"class":2235},[2225,208983,28314],{"class":2239},[2225,208985,1520],{"class":2235},[2225,208987,7259],{"class":2291},[2225,208989,4228],{"class":2239},[2225,208991,2298],{"class":2235},[2225,208993,27452],{"class":2307},[2225,208995,2311],{"class":2235},[2225,208997,28314],{"class":2239},[2225,208999,1520],{"class":2235},[2225,209001,20636],{"class":2291},[2225,209003,19132],{"class":2239},[2225,209005,2359],{"class":2235},[2225,209007,1381],{"class":2239},[2225,209009,1707],{"class":2235},[2225,209011,209012,209014,209016,209018],{"class":2227,"line":4253},[2225,209013,192181],{"class":2307},[2225,209015,2311],{"class":2235},[2225,209017,2322],{"class":2321},[2225,209019,1707],{"class":2235},[2225,209021,209022,209024],{"class":2227,"line":4259},[2225,209023,2359],{"class":2235},[2225,209025,2362],{"class":2239},[2225,209027,209028,209030,209032,209034,209036],{"class":2227,"line":6548},[2225,209029,6500],{"class":2231},[2225,209031,6466],{"class":2239},[2225,209033,1520],{"class":2235},[2225,209035,192229],{"class":2291},[2225,209037,8146],{"class":2239},[2225,209039,209040,209042,209045,209047,209049,209051,209053,209055,209057,209059,209061,209063,209065,209067,209069,209071,209073,209075,209077,209079,209081],{"class":2227,"line":6576},[2225,209041,6500],{"class":2231},[2225,209043,209044],{"class":2239}," oldInvoices",[2225,209046,1520],{"class":2235},[2225,209048,3161],{"class":2291},[2225,209050,2789],{"class":2239},[2225,209052,2805],{"class":2235},[2225,209054,62891],{"class":2252},[2225,209056,2805],{"class":2235},[2225,209058,2298],{"class":2235},[2225,209060,2236],{"class":2235},[2225,209062,7070],{"class":2307},[2225,209064,2311],{"class":2235},[2225,209066,2249],{"class":2235},[2225,209068,62891],{"class":2252},[2225,209070,2805],{"class":2235},[2225,209072,2298],{"class":2235},[2225,209074,27452],{"class":2307},[2225,209076,2311],{"class":2235},[2225,209078,6528],{"class":6527},[2225,209080,2243],{"class":2235},[2225,209082,2362],{"class":2239},[2225,209084,209085],{"class":2227,"line":6582},[2225,209086,2285],{"emptyLinePlaceholder":2284},[2225,209088,209089],{"class":2227,"line":6587},[2225,209090,209091],{"class":2763},"\u002F\u002F gen 1: reshape `total` into `amount.gross` — non-additive\n",[2225,209093,209094,209096,209098,209100,209102,209104,209106,209108,209110,209112,209114],{"class":2227,"line":7174},[2225,209095,208070],{"class":2239},[2225,209097,2781],{"class":2235},[2225,209099,2784],{"class":2231},[2225,209101,2842],{"class":2239},[2225,209103,1520],{"class":2235},[2225,209105,2847],{"class":2291},[2225,209107,2789],{"class":2239},[2225,209109,2805],{"class":2235},[2225,209111,6450],{"class":2252},[2225,209113,2805],{"class":2235},[2225,209115,2362],{"class":2239},[2225,209117,209118,209120,209122,209124,209126,209128,209130,209132,209135,209137,209139,209141,209143,209145,209147],{"class":2227,"line":7180},[2225,209119,2775],{"class":2774},[2225,209121,6461],{"class":2239},[2225,209123,2781],{"class":2235},[2225,209125,6466],{"class":2239},[2225,209127,1520],{"class":2235},[2225,209129,6471],{"class":2291},[2225,209131,6474],{"class":2235},[2225,209133,209134],{"class":6342},"InvNew",[2225,209136,6480],{"class":2235},[2225,209138,2789],{"class":2239},[2225,209140,2805],{"class":2235},[2225,209142,6487],{"class":2252},[2225,209144,2805],{"class":2235},[2225,209146,2298],{"class":2235},[2225,209148,2301],{"class":2235},[2225,209150,209151,209153,209155,209157,209159,209161,209163,209165,209167,209169,209171,209173,209175,209177,209179,209181,209183,209185,209187,209189,209191,209193,209195,209197,209199,209201,209203,209205,209207,209209,209211,209213],{"class":2227,"line":7220},[2225,209152,28618],{"class":2307},[2225,209154,2311],{"class":2235},[2225,209156,28314],{"class":2239},[2225,209158,1520],{"class":2235},[2225,209160,25505],{"class":2291},[2225,209162,2789],{"class":2239},[2225,209164,2792],{"class":2235},[2225,209166,7070],{"class":2307},[2225,209168,2311],{"class":2235},[2225,209170,28314],{"class":2239},[2225,209172,1520],{"class":2235},[2225,209174,7259],{"class":2291},[2225,209176,4228],{"class":2239},[2225,209178,2298],{"class":2235},[2225,209180,6351],{"class":2307},[2225,209182,2311],{"class":2235},[2225,209184,28314],{"class":2239},[2225,209186,1520],{"class":2235},[2225,209188,25505],{"class":2291},[2225,209190,2789],{"class":2239},[2225,209192,2792],{"class":2235},[2225,209194,208884],{"class":2307},[2225,209196,2311],{"class":2235},[2225,209198,28314],{"class":2239},[2225,209200,1520],{"class":2235},[2225,209202,20636],{"class":2291},[2225,209204,19132],{"class":2239},[2225,209206,2359],{"class":2235},[2225,209208,8495],{"class":2239},[2225,209210,2359],{"class":2235},[2225,209212,1381],{"class":2239},[2225,209214,1707],{"class":2235},[2225,209216,209217,209219,209221,209223],{"class":2227,"line":7225},[2225,209218,192181],{"class":2307},[2225,209220,2311],{"class":2235},[2225,209222,2322],{"class":2321},[2225,209224,1707],{"class":2235},[2225,209226,209227,209229,209231],{"class":2227,"line":7231},[2225,209228,150038],{"class":2307},[2225,209230,2311],{"class":2235},[2225,209232,32491],{"class":2239},[2225,209234,209235,209238,209240,209242,209245,209247,209249,209251,209253,209255,209257,209259,209261,209263,209266,209268,209270,209272,209274,209276,209278,209280,209282,209284,209286,209288,209290,209292,209294,209296,209298,209300,209302,209304,209306],{"class":2227,"line":7267},[2225,209236,209237],{"class":2291},"    coordinatedCutover",[2225,209239,2789],{"class":2239},[2225,209241,2792],{"class":2235},[2225,209243,209244],{"class":2291}," transform",[2225,209246,2311],{"class":2235},[2225,209248,953],{"class":2235},[2225,209250,147815],{"class":7052},[2225,209252,1381],{"class":2235},[2225,209254,8116],{"class":2774},[2225,209256,953],{"class":2239},[2225,209258,2792],{"class":2235},[2225,209260,7070],{"class":2307},[2225,209262,2311],{"class":2235},[2225,209264,209265],{"class":2239}," d[",[2225,209267,2805],{"class":2235},[2225,209269,6983],{"class":2252},[2225,209271,2805],{"class":2235},[2225,209273,8803],{"class":2239},[2225,209275,2298],{"class":2235},[2225,209277,6351],{"class":2307},[2225,209279,2311],{"class":2235},[2225,209281,2236],{"class":2235},[2225,209283,208884],{"class":2307},[2225,209285,2311],{"class":2235},[2225,209287,209265],{"class":2239},[2225,209289,2805],{"class":2235},[2225,209291,64200],{"class":2252},[2225,209293,2805],{"class":2235},[2225,209295,8890],{"class":2239},[2225,209297,2359],{"class":2235},[2225,209299,2243],{"class":2235},[2225,209301,8495],{"class":2239},[2225,209303,2359],{"class":2235},[2225,209305,1381],{"class":2239},[2225,209307,1707],{"class":2235},[2225,209309,209310,209313,209315],{"class":2227,"line":7272},[2225,209311,209312],{"class":2291},"    additiveOnly",[2225,209314,4228],{"class":2239},[2225,209316,1707],{"class":2235},[2225,209318,209319,209321],{"class":2227,"line":7278},[2225,209320,32588],{"class":2239},[2225,209322,1707],{"class":2235},[2225,209324,209325,209327],{"class":2227,"line":7303},[2225,209326,2359],{"class":2235},[2225,209328,2362],{"class":2239},[2225,209330,209331,209333,209335,209337,209339],{"class":2227,"line":7308},[2225,209332,6500],{"class":2231},[2225,209334,6466],{"class":2239},[2225,209336,1520],{"class":2235},[2225,209338,192229],{"class":2291},[2225,209340,8146],{"class":2239},[2225,209342,209343],{"class":2227,"line":7314},[2225,209344,2285],{"emptyLinePlaceholder":2284},[2225,209346,209347,209349,209351,209353,209355,209357,209359,209361,209363,209365,209367,209369,209371,209373,209375,209377,209379,209381,209383,209385,209387,209389,209391,209393,209395],{"class":2227,"line":7348},[2225,209348,6500],{"class":2231},[2225,209350,6503],{"class":2239},[2225,209352,1520],{"class":2235},[2225,209354,3161],{"class":2291},[2225,209356,2789],{"class":2239},[2225,209358,2805],{"class":2235},[2225,209360,62986],{"class":2252},[2225,209362,2805],{"class":2235},[2225,209364,2298],{"class":2235},[2225,209366,2236],{"class":2235},[2225,209368,7070],{"class":2307},[2225,209370,2311],{"class":2235},[2225,209372,2249],{"class":2235},[2225,209374,62986],{"class":2252},[2225,209376,2805],{"class":2235},[2225,209378,2298],{"class":2235},[2225,209380,6351],{"class":2307},[2225,209382,2311],{"class":2235},[2225,209384,2236],{"class":2235},[2225,209386,208884],{"class":2307},[2225,209388,2311],{"class":2235},[2225,209390,18996],{"class":6527},[2225,209392,2243],{"class":2235},[2225,209394,2243],{"class":2235},[2225,209396,2362],{"class":2239},[2225,209398,209399],{"class":2227,"line":8613},[2225,209400,209401],{"class":2763},"\u002F\u002F → throws SchemaFenceError: writes are blocked while the cutover is pending\n",[2225,209403,209404],{"class":2227,"line":8633},[2225,209405,2285],{"emptyLinePlaceholder":2284},[2225,209407,209408,209410,209412,209414,209416,209418,209420,209423],{"class":2227,"line":8662},[2225,209409,2775],{"class":2774},[2225,209411,36920],{"class":2239},[2225,209413,2781],{"class":2235},[2225,209415,2784],{"class":2231},[2225,209417,6466],{"class":2239},[2225,209419,1520],{"class":2235},[2225,209421,209422],{"class":2291},"runSchemaCutover",[2225,209424,8146],{"class":2239},[2225,209426,209427,209429,209431,209434],{"class":2227,"line":8747},[2225,209428,11195],{"class":2239},[2225,209430,1520],{"class":2235},[2225,209432,209433],{"class":2239},"migrated ",[2225,209435,209436],{"class":2763},"\u002F\u002F → 1  (i1 rewritten in place through the transform)\n",[2225,209438,209439],{"class":2227,"line":8752},[2225,209440,2285],{"emptyLinePlaceholder":2284},[2225,209442,209443,209445,209448,209450,209452,209454,209456,209458,209460,209462,209464,209466],{"class":2227,"line":8768},[2225,209444,2775],{"class":2774},[2225,209446,209447],{"class":2239}," i1 ",[2225,209449,2781],{"class":2235},[2225,209451,2784],{"class":2231},[2225,209453,6503],{"class":2239},[2225,209455,1520],{"class":2235},[2225,209457,3158],{"class":2291},[2225,209459,2789],{"class":2239},[2225,209461,2805],{"class":2235},[2225,209463,62891],{"class":2252},[2225,209465,2805],{"class":2235},[2225,209467,2362],{"class":2239},[2225,209469,209470,209472,209474,209476,209478,209481],{"class":2227,"line":8785},[2225,209471,62891],{"class":2239},[2225,209473,8457],{"class":2235},[2225,209475,6617],{"class":2239},[2225,209477,1520],{"class":2235},[2225,209479,209480],{"class":2239},"gross ",[2225,209482,209483],{"class":2763},"\u002F\u002F → 100\n",[2225,209485,209486],{"class":2227,"line":8815},[2225,209487,2285],{"emptyLinePlaceholder":2284},[2225,209489,209490,209492,209494,209496,209498,209500,209502,209504,209506,209508,209510,209512,209514,209516,209518,209520,209522,209524,209526,209528,209530,209532,209534,209536,209538,209540],{"class":2227,"line":8854},[2225,209491,6500],{"class":2231},[2225,209493,6503],{"class":2239},[2225,209495,1520],{"class":2235},[2225,209497,3161],{"class":2291},[2225,209499,2789],{"class":2239},[2225,209501,2805],{"class":2235},[2225,209503,62986],{"class":2252},[2225,209505,2805],{"class":2235},[2225,209507,2298],{"class":2235},[2225,209509,2236],{"class":2235},[2225,209511,7070],{"class":2307},[2225,209513,2311],{"class":2235},[2225,209515,2249],{"class":2235},[2225,209517,62986],{"class":2252},[2225,209519,2805],{"class":2235},[2225,209521,2298],{"class":2235},[2225,209523,6351],{"class":2307},[2225,209525,2311],{"class":2235},[2225,209527,2236],{"class":2235},[2225,209529,208884],{"class":2307},[2225,209531,2311],{"class":2235},[2225,209533,18996],{"class":6527},[2225,209535,2243],{"class":2235},[2225,209537,2243],{"class":2235},[2225,209539,8495],{"class":2239},[2225,209541,209542],{"class":2763},"\u002F\u002F → resolves — the fence is clear\n",[937,209544,209545,209548,209549,209551],{},[941,209546,209547],{},"runSchemaCutover()"," waits for every other active client on the vault to acknowledge the pending schema generation (a drain barrier) before it rewrites records and clears the fence — see ",[1007,209550,68210],{"href":126812}," for the timeout and stale-client behavior.",[1022,209553,102889],{"id":102888},[937,209555,123952,209556,101421],{},[941,209557,1086],{},[1591,209559,209561],{"id":209560},"blindupdate",[941,209562,209563],{},"blindUpdate()",[2217,209565,209567],{"className":2219,"code":209566,"language":2221,"meta":1037,"style":1037},"function blindUpdate(): SchemaUpdateStrategy\n",[941,209568,209569],{"__ignoreMap":1037},[2225,209570,209571,209573,209576,209578],{"class":2227,"line":2228},[2225,209572,12641],{"class":2774},[2225,209574,209575],{"class":2291}," blindUpdate",[2225,209577,112812],{"class":2235},[2225,209579,209580],{"class":6342}," SchemaUpdateStrategy\n",[937,209582,209583,209584,209586],{},"Allows every schema change unconditionally. Explicit opt-in to the pre-schema-update, back-compat behavior — useful as a documented no-op strategy rather than omitting ",[941,209585,22793],{}," entirely.",[1591,209588,209590],{"id":209589},"additiveonly",[941,209591,207434],{},[2217,209593,209595],{"className":2219,"code":209594,"language":2221,"meta":1037,"style":1037},"function additiveOnly(): SchemaUpdateStrategy\n",[941,209596,209597],{"__ignoreMap":1037},[2225,209598,209599,209601,209603,209605],{"class":2227,"line":2228},[2225,209600,12641],{"class":2774},[2225,209602,207637],{"class":2291},[2225,209604,112812],{"class":2235},[2225,209606,209580],{"class":6342},[937,209608,209609,209610,209613],{},"Allows additive changes (new optional fields only); rejects anything else with ",[941,209611,209612],{},"NonAdditiveSchemaChangeError",". The recommended default safety backstop.",[1591,209615,209617],{"id":209616},"lockschemaopts",[941,209618,209619],{},"lockSchema(opts?)",[2217,209621,209623],{"className":2219,"code":209622,"language":2221,"meta":1037,"style":1037},"function lockSchema(opts?: { readonly fields?: readonly string[] }): SchemaUpdateStrategy\n",[941,209624,209625],{"__ignoreMap":1037},[2225,209626,209627,209629,209631,209633,209635,209637,209639,209641,209644,209646,209648,209650,209652,209654],{"class":2227,"line":2228},[2225,209628,12641],{"class":2774},[2225,209630,208537],{"class":2291},[2225,209632,2789],{"class":2235},[2225,209634,8097],{"class":7052},[2225,209636,7011],{"class":2235},[2225,209638,2236],{"class":2235},[2225,209640,112906],{"class":2774},[2225,209642,209643],{"class":2307}," fields",[2225,209645,7011],{"class":2235},[2225,209647,112906],{"class":2774},[2225,209649,6366],{"class":6342},[2225,209651,82213],{"class":2239},[2225,209653,186597],{"class":2235},[2225,209655,209580],{"class":6342},[937,209657,209658,209659,209662,209663,209666],{},"Rejects any schema change with ",[941,209660,209661],{},"SchemaLockedError",". With ",[941,209664,209665],{},"opts.fields",", only rejects when one of the listed fields was added, removed, or changed — changes to other fields are allowed through to the next strategy.",[1591,209668,209670],{"id":209669},"coordinatedcutoveropts",[941,209671,209672],{},"coordinatedCutover(opts)",[2217,209674,209676],{"className":2219,"code":209675,"language":2221,"meta":1037,"style":1037},"function coordinatedCutover(opts: { readonly transform: TransformFn }): SchemaUpdateStrategy\n",[941,209677,209678],{"__ignoreMap":1037},[2225,209679,209680,209682,209684,209686,209688,209690,209692,209694,209696,209698,209701,209703],{"class":2227,"line":2228},[2225,209681,12641],{"class":2774},[2225,209683,208787],{"class":2291},[2225,209685,2789],{"class":2235},[2225,209687,8097],{"class":7052},[2225,209689,2311],{"class":2235},[2225,209691,2236],{"class":2235},[2225,209693,112906],{"class":2774},[2225,209695,209244],{"class":2307},[2225,209697,2311],{"class":2235},[2225,209699,209700],{"class":6342}," TransformFn",[2225,209702,8111],{"class":2235},[2225,209704,209580],{"class":6342},[937,209706,209707,209710,209711,209714,209715,209717,209718,209720,209721,22207,209723,209726,209727,209732],{},[941,209708,209709],{},"opts.transform"," has the shape ",[941,209712,209713],{},"(doc: Record\u003Cstring, unknown>) => Record\u003Cstring, unknown>"," (its ",[941,209716,57949],{}," type alias is internal, not exported from ",[941,209719,1086],{},"). On a non-additive delta, ",[941,209722,207491],{},[941,209724,209725],{},"cutover"," decision carrying that transform instead of rejecting. The transform runs once per existing record when ",[1007,209728,209730],{"href":209729},"#vaultrunschemacutoveropts",[941,209731,58002],{}," executes; additive and unchanged deltas are allowed through untouched.",[1591,209734,209736],{"id":209735},"schemaupdatestrategy",[941,209737,209738],{},"SchemaUpdateStrategy",[2217,209740,209742],{"className":2219,"code":209741,"language":2221,"meta":1037,"style":1037},"interface SchemaUpdateStrategy {\n  readonly name: string\n  onSchemaDelta(delta: SchemaDelta, ctx: UpdateContext): UpdateDecision | Promise\u003CUpdateDecision>\n}\n",[941,209743,209744,209752,209762,209801],{"__ignoreMap":1037},[2225,209745,209746,209748,209750],{"class":2227,"line":2228},[2225,209747,6994],{"class":2774},[2225,209749,194144],{"class":6342},[2225,209751,2301],{"class":2235},[2225,209753,209754,209756,209758,209760],{"class":2227,"line":1041},[2225,209755,53580],{"class":2774},[2225,209757,8104],{"class":2307},[2225,209759,2311],{"class":2235},[2225,209761,7014],{"class":6342},[2225,209763,209764,209767,209769,209772,209774,209777,209779,209781,209783,209786,209788,209790,209792,209794,209796,209799],{"class":2227,"line":1038},[2225,209765,209766],{"class":2307},"  onSchemaDelta",[2225,209768,2789],{"class":2235},[2225,209770,209771],{"class":7052},"delta",[2225,209773,2311],{"class":2235},[2225,209775,209776],{"class":6342}," SchemaDelta",[2225,209778,2298],{"class":2235},[2225,209780,138692],{"class":7052},[2225,209782,2311],{"class":2235},[2225,209784,209785],{"class":6342}," UpdateContext",[2225,209787,6255],{"class":2235},[2225,209789,194220],{"class":6342},[2225,209791,7087],{"class":2235},[2225,209793,7079],{"class":6342},[2225,209795,6474],{"class":2235},[2225,209797,209798],{"class":6342},"UpdateDecision",[2225,209800,7093],{"class":2235},[2225,209802,209803],{"class":2227,"line":2288},[2225,209804,7351],{"class":2235},[937,209806,209807,209808,209811,209812,209815,209816,1324,209818,1520],{},"The shape every strategy — built-in or your own — implements. ",[941,209809,209810],{},"evaluateStrategies"," (internal) runs a collection's strategy list in order against the detected ",[941,209813,209814],{},"SchemaDelta"," and returns the first non-",[941,209817,34720],{},[941,209819,209798],{},[1591,209821,209823,1261,209825,120416],{"id":209822},"schemadelta-fieldchange-types",[941,209824,209814],{},[941,209826,209827],{},"FieldChange",[2217,209829,209831],{"className":2219,"code":209830,"language":2221,"meta":1037,"style":1037},"interface SchemaDelta {\n  readonly collection: string\n  readonly kind: 'none' | 'additive' | 'non-additive'\n  readonly added: readonly string[]     \u002F\u002F new top-level properties\n  readonly removed: readonly string[]   \u002F\u002F removed top-level properties\n  readonly changed: readonly FieldChange[]\n}\n\ninterface FieldChange {\n  readonly field: string\n  readonly requiredChanged: boolean  \u002F\u002F required-ness flipped\n  readonly shapeChanged: boolean     \u002F\u002F subschema shape changed\n}\n",[941,209832,209833,209841,209851,209883,209902,209921,209937,209941,209945,209953,209963,209977,209991],{"__ignoreMap":1037},[2225,209834,209835,209837,209839],{"class":2227,"line":2228},[2225,209836,6994],{"class":2774},[2225,209838,209776],{"class":6342},[2225,209840,2301],{"class":2235},[2225,209842,209843,209845,209847,209849],{"class":2227,"line":1041},[2225,209844,53580],{"class":2774},[2225,209846,7061],{"class":2307},[2225,209848,2311],{"class":2235},[2225,209850,7014],{"class":6342},[2225,209852,209853,209855,209857,209859,209861,209863,209865,209867,209869,209872,209874,209876,209878,209881],{"class":2227,"line":1038},[2225,209854,53580],{"class":2774},[2225,209856,8285],{"class":2307},[2225,209858,2311],{"class":2235},[2225,209860,2249],{"class":2235},[2225,209862,8292],{"class":2252},[2225,209864,2805],{"class":2235},[2225,209866,7087],{"class":2235},[2225,209868,2249],{"class":2235},[2225,209870,209871],{"class":2252},"additive",[2225,209873,2805],{"class":2235},[2225,209875,7087],{"class":2235},[2225,209877,2249],{"class":2235},[2225,209879,209880],{"class":2252},"non-additive",[2225,209882,2256],{"class":2235},[2225,209884,209885,209887,209890,209892,209894,209896,209899],{"class":2227,"line":2288},[2225,209886,53580],{"class":2774},[2225,209888,209889],{"class":2307}," added",[2225,209891,2311],{"class":2235},[2225,209893,112906],{"class":2774},[2225,209895,6366],{"class":6342},[2225,209897,209898],{"class":2239},"[]     ",[2225,209900,209901],{"class":2763},"\u002F\u002F new top-level properties\n",[2225,209903,209904,209906,209909,209911,209913,209915,209918],{"class":2227,"line":2304},[2225,209905,53580],{"class":2774},[2225,209907,209908],{"class":2307}," removed",[2225,209910,2311],{"class":2235},[2225,209912,112906],{"class":2774},[2225,209914,6366],{"class":6342},[2225,209916,209917],{"class":2239},"[]   ",[2225,209919,209920],{"class":2763},"\u002F\u002F removed top-level properties\n",[2225,209922,209923,209925,209928,209930,209932,209935],{"class":2227,"line":2356},[2225,209924,53580],{"class":2774},[2225,209926,209927],{"class":2307}," changed",[2225,209929,2311],{"class":2235},[2225,209931,112906],{"class":2774},[2225,209933,209934],{"class":6342}," FieldChange",[2225,209936,78511],{"class":2239},[2225,209938,209939],{"class":2227,"line":2830},[2225,209940,7351],{"class":2235},[2225,209942,209943],{"class":2227,"line":2861},[2225,209944,2285],{"emptyLinePlaceholder":2284},[2225,209946,209947,209949,209951],{"class":2227,"line":4246},[2225,209948,6994],{"class":2774},[2225,209950,209934],{"class":6342},[2225,209952,2301],{"class":2235},[2225,209954,209955,209957,209959,209961],{"class":2227,"line":4253},[2225,209956,53580],{"class":2774},[2225,209958,124557],{"class":2307},[2225,209960,2311],{"class":2235},[2225,209962,7014],{"class":6342},[2225,209964,209965,209967,209970,209972,209974],{"class":2227,"line":4259},[2225,209966,53580],{"class":2774},[2225,209968,209969],{"class":2307}," requiredChanged",[2225,209971,2311],{"class":2235},[2225,209973,7723],{"class":6342},[2225,209975,209976],{"class":2763},"  \u002F\u002F required-ness flipped\n",[2225,209978,209979,209981,209984,209986,209988],{"class":2227,"line":6548},[2225,209980,53580],{"class":2774},[2225,209982,209983],{"class":2307}," shapeChanged",[2225,209985,2311],{"class":2235},[2225,209987,7723],{"class":6342},[2225,209989,209990],{"class":2763},"     \u002F\u002F subschema shape changed\n",[2225,209992,209993],{"class":2227,"line":6576},[2225,209994,7351],{"class":2235},[937,209996,209997,209998,7356,210000,210003,210004,210006,210007,210009,210010,210013,210014,1520],{},"The classified difference between the previously-persisted JSON Schema and the freshly-derived one, computed internally on every vault open. ",[941,209999,27895],{},[941,210001,210002],{},"'additive'"," only when every change is a new ",[1092,210005,2084],{}," top-level property — any removal, shape change, required-ness flip, or new ",[1092,210008,45375],{}," property makes it ",[941,210011,210012],{},"'non-additive'",". Nested-object and union-level changes are not yet classified individually; see ",[1007,210015,68210],{"href":126812},[1591,210017,210019,1261,210022,120416],{"id":210018},"updatecontext-updatedecision-types",[941,210020,210021],{},"UpdateContext",[941,210023,209798],{},[2217,210025,210027],{"className":2219,"code":210026,"language":2221,"meta":1037,"style":1037},"interface UpdateContext {\n  readonly collection: string\n}\n\ntype UpdateDecision =\n  | { readonly action: 'allow' }\n  | { readonly action: 'reject'; readonly error: Error }\n  | { readonly action: 'cutover'; readonly transform: TransformFn }\n",[941,210028,210029,210037,210047,210051,210055,210063,210084,210115],{"__ignoreMap":1037},[2225,210030,210031,210033,210035],{"class":2227,"line":2228},[2225,210032,6994],{"class":2774},[2225,210034,209785],{"class":6342},[2225,210036,2301],{"class":2235},[2225,210038,210039,210041,210043,210045],{"class":2227,"line":1041},[2225,210040,53580],{"class":2774},[2225,210042,7061],{"class":2307},[2225,210044,2311],{"class":2235},[2225,210046,7014],{"class":6342},[2225,210048,210049],{"class":2227,"line":1038},[2225,210050,7351],{"class":2235},[2225,210052,210053],{"class":2227,"line":2288},[2225,210054,2285],{"emptyLinePlaceholder":2284},[2225,210056,210057,210059,210061],{"class":2227,"line":2304},[2225,210058,6339],{"class":2774},[2225,210060,194220],{"class":6342},[2225,210062,66396],{"class":2235},[2225,210064,210065,210067,210069,210071,210074,210076,210078,210080,210082],{"class":2227,"line":2356},[2225,210066,66401],{"class":2235},[2225,210068,2236],{"class":2235},[2225,210070,112906],{"class":2774},[2225,210072,210073],{"class":2307}," action",[2225,210075,2311],{"class":2235},[2225,210077,2249],{"class":2235},[2225,210079,34720],{"class":2252},[2225,210081,2805],{"class":2235},[2225,210083,6369],{"class":2235},[2225,210085,210086,210088,210090,210092,210094,210096,210098,210101,210103,210105,210107,210109,210111,210113],{"class":2227,"line":2830},[2225,210087,66401],{"class":2235},[2225,210089,2236],{"class":2235},[2225,210091,112906],{"class":2774},[2225,210093,210073],{"class":2307},[2225,210095,2311],{"class":2235},[2225,210097,2249],{"class":2235},[2225,210099,210100],{"class":2252},"reject",[2225,210102,2805],{"class":2235},[2225,210104,3769],{"class":2235},[2225,210106,112906],{"class":2774},[2225,210108,85497],{"class":2307},[2225,210110,2311],{"class":2235},[2225,210112,46519],{"class":6342},[2225,210114,6369],{"class":2235},[2225,210116,210117,210119,210121,210123,210125,210127,210129,210131,210133,210135,210137,210139,210141,210143],{"class":2227,"line":2861},[2225,210118,66401],{"class":2235},[2225,210120,2236],{"class":2235},[2225,210122,112906],{"class":2774},[2225,210124,210073],{"class":2307},[2225,210126,2311],{"class":2235},[2225,210128,2249],{"class":2235},[2225,210130,209725],{"class":2252},[2225,210132,2805],{"class":2235},[2225,210134,3769],{"class":2235},[2225,210136,112906],{"class":2774},[2225,210138,209244],{"class":2307},[2225,210140,2311],{"class":2235},[2225,210142,209700],{"class":6342},[2225,210144,6369],{"class":2235},[937,210146,210147,210149,210150,210153,210154,210156,210157,210159,210160,1625,210162,210164],{},[941,210148,210021],{}," is what a strategy's ",[941,210151,210152],{},"onSchemaDelta"," receives alongside the delta. ",[941,210155,209798],{}," is its verdict: ",[941,210158,34720],{}," falls through to the next strategy, ",[941,210161,210100],{},[941,210163,209725],{}," are terminal.",[1591,210166,210168],{"id":210167},"vaultrunschemacutoveropts",[941,210169,210170],{},"vault.runSchemaCutover(opts?)",[2217,210172,210174],{"className":2219,"code":210173,"language":2221,"meta":1037,"style":1037},"async runSchemaCutover(opts?: { onPoll?: () => Promise\u003Cvoid> }): Promise\u003C{ migrated: number }>\n",[941,210175,210176],{"__ignoreMap":1037},[2225,210177,210178,210181,210183,210185,210187,210189,210192,210194,210196,210198,210200,210203,210205,210207,210209,210211,210214,210216,210218],{"class":2227,"line":2228},[2225,210179,210180],{"class":2239},"async ",[2225,210182,209422],{"class":2291},[2225,210184,24842],{"class":2239},[2225,210186,7011],{"class":2235},[2225,210188,2236],{"class":2235},[2225,210190,210191],{"class":2239}," onPoll?",[2225,210193,2311],{"class":2235},[2225,210195,13130],{"class":2235},[2225,210197,8116],{"class":2774},[2225,210199,7079],{"class":6342},[2225,210201,210202],{"class":2235},"\u003Cvoid>",[2225,210204,2243],{"class":2235},[2225,210206,11737],{"class":2239},[2225,210208,11740],{"class":6342},[2225,210210,17851],{"class":2235},[2225,210212,210213],{"class":6342}," migrated",[2225,210215,2311],{"class":2235},[2225,210217,6356],{"class":2239},[2225,210219,17861],{"class":2235},[937,210221,210222,210223,210225,210226,210228,210229,210232],{},"Runs every pending ",[941,210224,57956],{}," for this vault: waits for the active client set to quiesce (see ",[1007,210227,68210],{"href":126812},"), applies each collection's transform to every existing record, bumps the vault's schema generation, and clears the fence. Returns how many collections were migrated. ",[941,210230,210231],{},"opts.onPoll"," is a test hook for advancing other simulated clients between barrier checks.",[1591,210234,210236],{"id":210235},"vaultabortschemacutover",[941,210237,210238],{},"vault.abortSchemaCutover()",[2217,210240,210242],{"className":2219,"code":210241,"language":2221,"meta":1037,"style":1037},"async abortSchemaCutover(): Promise\u003Cvoid>\n",[941,210243,210244],{"__ignoreMap":1037},[2225,210245,210246,210248,210251,210253,210255],{"class":2227,"line":2228},[2225,210247,210180],{"class":2239},[2225,210249,210250],{"class":2291},"abortSchemaCutover",[2225,210252,12809],{"class":2239},[2225,210254,11740],{"class":6342},[2225,210256,13016],{"class":2235},[937,210258,210259,210260,210263,210264,957],{},"Resets a stuck fence back to ",[941,210261,210262],{},"'normal'"," without migrating or bumping the generation — a manual recovery escape hatch (for example, after a ",[941,210265,210266],{},"QuiesceTimeoutError",[1591,210268,210270],{"id":210269},"vaultschemafencestate",[941,210271,210272],{},"vault.schemaFenceState()",[2217,210274,210276],{"className":2219,"code":210275,"language":2221,"meta":1037,"style":1037},"async schemaFenceState(): Promise\u003CFenceDoc>\n\ntype FenceState = 'normal' | 'draining' | 'migrating' | 'complete'\n\ninterface FenceDoc {\n  readonly currentSchemaVersion: number\n  readonly fenceState: FenceState\n}\n",[941,210277,210278,210296,210300,210342,210346,210355,210366,210378],{"__ignoreMap":1037},[2225,210279,210280,210282,210285,210287,210289,210291,210294],{"class":2227,"line":2228},[2225,210281,210180],{"class":2239},[2225,210283,210284],{"class":2291},"schemaFenceState",[2225,210286,12809],{"class":2239},[2225,210288,11740],{"class":6342},[2225,210290,6474],{"class":2235},[2225,210292,210293],{"class":2239},"FenceDoc",[2225,210295,7093],{"class":2235},[2225,210297,210298],{"class":2227,"line":1041},[2225,210299,2285],{"emptyLinePlaceholder":2284},[2225,210301,210302,210304,210307,210309,210311,210314,210316,210318,210320,210323,210325,210327,210329,210332,210334,210336,210338,210340],{"class":2227,"line":1038},[2225,210303,6339],{"class":2774},[2225,210305,210306],{"class":6342}," FenceState",[2225,210308,6346],{"class":2235},[2225,210310,2249],{"class":2235},[2225,210312,210313],{"class":2252},"normal",[2225,210315,2805],{"class":2235},[2225,210317,7087],{"class":2235},[2225,210319,2249],{"class":2235},[2225,210321,210322],{"class":2252},"draining",[2225,210324,2805],{"class":2235},[2225,210326,7087],{"class":2235},[2225,210328,2249],{"class":2235},[2225,210330,210331],{"class":2252},"migrating",[2225,210333,2805],{"class":2235},[2225,210335,7087],{"class":2235},[2225,210337,2249],{"class":2235},[2225,210339,77631],{"class":2252},[2225,210341,2256],{"class":2235},[2225,210343,210344],{"class":2227,"line":2288},[2225,210345,2285],{"emptyLinePlaceholder":2284},[2225,210347,210348,210350,210353],{"class":2227,"line":2304},[2225,210349,6994],{"class":2774},[2225,210351,210352],{"class":6342}," FenceDoc",[2225,210354,2301],{"class":2235},[2225,210356,210357,210359,210362,210364],{"class":2227,"line":2356},[2225,210358,53580],{"class":2774},[2225,210360,210361],{"class":2307}," currentSchemaVersion",[2225,210363,2311],{"class":2235},[2225,210365,12024],{"class":6342},[2225,210367,210368,210370,210373,210375],{"class":2227,"line":2830},[2225,210369,53580],{"class":2774},[2225,210371,210372],{"class":2307}," fenceState",[2225,210374,2311],{"class":2235},[2225,210376,210377],{"class":6342}," FenceState\n",[2225,210379,210380],{"class":2227,"line":2861},[2225,210381,7351],{"class":2235},[937,210383,210384],{},"A thin, uncached read of the vault's current cutover state — useful for a status UI or a health check without holding a reference to the client that started the cutover.",[1022,210386,68210],{"id":68209},[1111,210388,210389,210398,210411,210420,210435,210458,210466],{},[1114,210390,210391,210397],{},[950,210392,210393,7808,210395,1520],{},[941,210394,22793],{},[941,210396,191912],{}," Without a persisted baseline there's nothing to diff against, so strategies never run and changes pass through silently.",[1114,210399,210400,210403,210404,210406,210407,210410],{},[950,210401,210402],{},"Detection is top-level only."," v0's ",[941,210405,209814],{}," classifies top-level object properties and their required-ness; a shape change nested two levels deep is reported as ",[941,210408,210409],{},"shapeChanged"," on the outer field, not decomposed further.",[1114,210412,210413,210416,210417,210419],{},[950,210414,210415],{},"A detection failure never blocks writes."," If computing the delta itself throws (a storage error, a corrupt persisted-schema record), that failure is swallowed — schema-update is a safety net on top of normal writes, not a correctness gate that can wedge the collection. Only an explicit ",[941,210418,210100],{}," decision from a strategy blocks writes.",[1114,210421,210422,210425,210426,210428,210429,210431,210432,210434],{},[950,210423,210424],{},"Order matters."," Strategies run in array order and the first non-",[941,210427,34720],{}," decision wins — put the strictest strategy (",[941,210430,208511],{},") before a permissive one (",[941,210433,207434],{},") if you want the lock to take precedence.",[1114,210436,210437,210446,210447,210449,210450,1625,210452,10326,210454,210457],{},[950,210438,210439,210440,210442,210443,210445],{},"A pending ",[941,210441,57956],{}," blocks ",[1092,210444,59366],{}," writes to that collection",", not just ones touching the changed fields, until ",[941,210448,58002],{}," completes — ",[941,210451,3161],{},[941,210453,3164],{},[941,210455,210456],{},"SchemaFenceError"," in the meantime.",[1114,210459,210460,210465],{},[950,210461,210462,210463],{},"A stale client that already opened the vault before a cutover ran gets ",[941,210464,178564],{}," on its next write once the generation counter has bumped — it must reopen the vault to pick up the new schema.",[1114,210467,210468,210473,210474,210476,210477,210479],{},[950,210469,210470,210472],{},[941,210471,209547],{}," can time out."," If another connected client never quiesces, it throws ",[941,210475,210266],{},"; recover with ",[941,210478,210238],{}," and retry once the holdout client is closed or upgraded.",[1022,210481,2621],{"id":2620},[1111,210483,210484,210489,210496,210504,210509],{},[1114,210485,210486,210488],{},[1007,210487,780],{"href":207528}," — the persisted JSON Schema baseline schema-update detects drift against",[1114,210490,210491,964,210493,210495],{},[1007,210492,711],{"href":124740},[941,210494,165499],{}," reports each collection's registered strategy names",[1114,210497,210498,210500,210501,210503],{},[1007,210499,756],{"href":757}," — planned broader migration surface (collection renames, cross-version moves); ",[941,210502,207491],{}," here already covers the field-level backfill case",[1114,210505,210506,210508],{},[1007,210507,21],{"href":60250}," — the DEK and envelope format the persisted schema (and vault metadata) reuse",[1114,210510,210511,1368,210513],{},[1007,210512,61743],{"href":598},[1007,210514,1019],{"href":1018},[2645,210516,210517],{},"html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .sfNiH, html code.shiki .sfNiH{--shiki-light:#FF5370;--shiki-default:#FF9CAC;--shiki-dark:#FF9CAC}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sHdIc, html code.shiki .sHdIc{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#EEFFFF;--shiki-default-font-style:italic;--shiki-dark:#BABED8;--shiki-dark-font-style:italic}",{"title":1037,"searchDepth":1038,"depth":1038,"links":210519},[210520,210521,210522,210528,210542,210543],{"id":29856,"depth":1041,"text":29857},{"id":58393,"depth":1041,"text":58394},{"id":29892,"depth":1041,"text":29893,"children":210523},[210524,210525,210526,210527],{"id":207917,"depth":1038,"text":207918},{"id":208271,"depth":1038,"text":208272},{"id":208505,"depth":1038,"text":208506},{"id":208763,"depth":1038,"text":208764},{"id":102888,"depth":1041,"text":102889,"children":210529},[210530,210531,210532,210533,210534,210535,210537,210539,210540,210541],{"id":209560,"depth":1038,"text":209563},{"id":209589,"depth":1038,"text":207434},{"id":209616,"depth":1038,"text":209619},{"id":209669,"depth":1038,"text":209672},{"id":209735,"depth":1038,"text":209738},{"id":209822,"depth":1038,"text":210536},"SchemaDelta \u002F FieldChange (types)",{"id":210018,"depth":1038,"text":210538},"UpdateContext \u002F UpdateDecision (types)",{"id":210167,"depth":1038,"text":210170},{"id":210235,"depth":1038,"text":210238},{"id":210269,"depth":1038,"text":210272},{"id":68209,"depth":1041,"text":68210},{"id":2620,"depth":1041,"text":2621},"Detect a collection's schema changing between vault opens — allow, reject, lock, or run a coordinated cutover that migrates records before unblocking writes.",{},{"title":820,"description":210544},"YV1Ps_957CLqRCFkZJbc4YmrwPJKDGBjO53ftY_KRdw",{"id":210549,"title":824,"access":12,"apiToc":929,"body":210550,"category":1042,"description":212287,"extension":1044,"meta":212288,"navigation":1042,"nest":1047,"path":825,"seo":212289,"sinceVersion":1042,"status":1049,"stem":826,"subtitle":1042,"tier":1050,"tocExternal":1042,"__hash__":212290},"docs\u002Fservices\u002Fsealed-record.md",{"type":931,"value":210551,"toc":212266},[210552,210554,210557,210559,210585,210608,210611,210646,210648,210664,210666,210678,210902,210918,210920,210924,210936,211152,211195,211204,211268,211272,211278,211373,211414,211437,211529,211533,211545,211649,211654,211656,211661,211677,211693,211699,211769,211795,211801,211856,211873,211878,211897,211903,211909,212029,212052,212054,212119,212121,212185,212187,212224,212226,212263],[934,210553,824],{"id":824},[46871,210555],{":rows":210556},"[{\"label\":\"Subpath\",\"value\":\"`@noy-db\u002Fhub\u002Fsealed-record`\"},{\"label\":\"Factory\",\"value\":\"`withSealedRecord()`\"},{\"label\":\"Layer\",\"value\":\"`with-audit`\"}]",[1022,210558,29857],{"id":29856},[937,210560,210561,210562,210565,210566,210568,210569,129115,210571,210573,210574,210577,210578,210581,210582,210584],{},"Sometimes an outside system needs to read ",[950,210563,210564],{},"one specific record"," — and only that record —\nwithout becoming a full member of the vault (an encrypted, per-tenant data space that groups\nrelated ",[950,210567,2698],{}," of records — see ",[1007,210570,124874],{"href":48},[941,210572,824],{}," lets a vault owner (the ",[950,210575,210576],{},"grantor",", who holds the collection's shared\n",[1007,210579,210580],{"href":66},"data-encryption key",") seal a single record's own per-record key to an\nexternal ",[950,210583,14346],{},", so that host can decrypt exactly that record and nothing else: not the\ncollection key, not any other record.",[937,210586,210587,210588,210591,210592,210594,210595,210597,210598,210601,210602,210607],{},"This builds on ",[1007,210589,210590],{"href":154040},"per-record keys",": when a\ncollection opts into ",[941,210593,73811],{},", every record gets its own AES-256-GCM key (the\n",[950,210596,13876],{},", content-encryption key) instead of sharing the collection's key. Sealing hands a\n",[1092,210599,210600],{},"copy"," of one record's CEK to the host, wrapped (\"sealed\") so only that host can open it —\nthe same sealing mechanism the ",[1007,210603,210604,210606],{"href":170},[941,210605,1139],{}," key-provider family"," uses elsewhere in\nnoy-db. The host never touches the vault's collection key and cannot decrypt anything the\ngrantor didn't explicitly seal to it.",[937,210609,210610],{},"Two sides make up the feature:",[1111,210612,210613,210630],{},[1114,210614,210615,953,210618,968,210621,1707,210624,210627,210628,1520],{},[950,210616,210617],{},"Grantor side",[941,210619,210620],{},"vault.sealRecordToHost",[941,210622,210623],{},"vault.revokeSealedRecord",[941,210625,210626],{},"vault.rotateRecordCek",") — lives on the vault, because sealing a CEK requires the\ncollection key to unwrap it first. This is the opt-in capability gated by\n",[941,210629,21577],{},[1114,210631,210632,953,210635,210638,210639,210641,210642,210645],{},[950,210633,210634],{},"Recipient-host side",[941,210636,210637],{},"openSealedRecord",") — a plain function the host runs. It needs no\nvault, no ",[941,210640,12780],{}," instance, and is ",[950,210643,210644],{},"not gated"," by the opt-in — it's exported for\nuse on a remote host that only has the sealed delivery and its own unsealer.",[1022,210647,70093],{"id":70092},[1111,210649,210650,210658],{},[1114,210651,210652,210657],{},[950,210653,210654,210656],{},[941,210655,73811],{}," on the collection you seal from"," — only per-record-key records have a CEK to seal.",[1114,210659,210660,210661,210663],{},"The recipient host needs none of this: ",[941,210662,210637],{}," is a plain function taking the sealed envelope — no hub, no strategy, no keys beyond what the envelope carries.",[1022,210665,99864],{"id":99863},[937,210667,210668,210669,10313,210672,210674,210675,210677],{},"The grantor side is opt-in and tree-shaken until you enable it. Pass\n",[941,210670,210671],{},"sealedRecordStrategy: withSealedRecord()",[941,210673,12780],{},", and open the collection you'll\nseal records from with ",[941,210676,73811],{}," (only per-record-key records have a CEK to seal):",[2217,210679,210681],{"className":2219,"code":210680,"language":2221,"meta":1037,"style":1037},"import { createNoydb } from '@noy-db\u002Fhub'\nimport { withSealedRecord } from '@noy-db\u002Fhub\u002Fsealed-record'\nimport { toMemory } from '@noy-db\u002Fto-memory'\n\nconst db = await createNoydb({\n  store: toMemory(),\n  user: 'alice',\n  secret: 'owner-secret-long',\n  sealedRecordStrategy: withSealedRecord(),\n})\n\nconst vault = await db.openVault('v')\nconst docs = vault.collection\u003C{ id: string; secret: string }>('docs', { perRecordKeys: true })\n",[941,210682,210683,210701,210721,210739,210743,210759,210771,210785,210799,210812,210818,210822,210848],{"__ignoreMap":1037},[2225,210684,210685,210687,210689,210691,210693,210695,210697,210699],{"class":2227,"line":2228},[2225,210686,2232],{"class":2231},[2225,210688,2236],{"class":2235},[2225,210690,2744],{"class":2239},[2225,210692,2243],{"class":2235},[2225,210694,2246],{"class":2231},[2225,210696,2249],{"class":2235},[2225,210698,1086],{"class":2252},[2225,210700,2256],{"class":2235},[2225,210702,210703,210705,210707,210710,210712,210714,210716,210719],{"class":2227,"line":1041},[2225,210704,2232],{"class":2231},[2225,210706,2236],{"class":2235},[2225,210708,210709],{"class":2239}," withSealedRecord",[2225,210711,2243],{"class":2235},[2225,210713,2246],{"class":2231},[2225,210715,2249],{"class":2235},[2225,210717,210718],{"class":2252},"@noy-db\u002Fhub\u002Fsealed-record",[2225,210720,2256],{"class":2235},[2225,210722,210723,210725,210727,210729,210731,210733,210735,210737],{"class":2227,"line":1038},[2225,210724,2232],{"class":2231},[2225,210726,2236],{"class":2235},[2225,210728,68576],{"class":2239},[2225,210730,2243],{"class":2235},[2225,210732,2246],{"class":2231},[2225,210734,2249],{"class":2235},[2225,210736,9638],{"class":2252},[2225,210738,2256],{"class":2235},[2225,210740,210741],{"class":2227,"line":2288},[2225,210742,2285],{"emptyLinePlaceholder":2284},[2225,210744,210745,210747,210749,210751,210753,210755,210757],{"class":2227,"line":2304},[2225,210746,2775],{"class":2774},[2225,210748,2778],{"class":2239},[2225,210750,2781],{"class":2235},[2225,210752,2784],{"class":2231},[2225,210754,2744],{"class":2291},[2225,210756,2789],{"class":2239},[2225,210758,4200],{"class":2235},[2225,210760,210761,210763,210765,210767,210769],{"class":2227,"line":2356},[2225,210762,15608],{"class":2307},[2225,210764,2311],{"class":2235},[2225,210766,68576],{"class":2291},[2225,210768,4228],{"class":2239},[2225,210770,1707],{"class":2235},[2225,210772,210773,210775,210777,210779,210781,210783],{"class":2227,"line":2830},[2225,210774,4205],{"class":2307},[2225,210776,2311],{"class":2235},[2225,210778,2249],{"class":2235},[2225,210780,6408],{"class":2252},[2225,210782,2805],{"class":2235},[2225,210784,1707],{"class":2235},[2225,210786,210787,210789,210791,210793,210795,210797],{"class":2227,"line":2861},[2225,210788,10423],{"class":2307},[2225,210790,2311],{"class":2235},[2225,210792,2249],{"class":2235},[2225,210794,135404],{"class":2252},[2225,210796,2805],{"class":2235},[2225,210798,1707],{"class":2235},[2225,210800,210801,210804,210806,210808,210810],{"class":2227,"line":4246},[2225,210802,210803],{"class":2307},"  sealedRecordStrategy",[2225,210805,2311],{"class":2235},[2225,210807,210709],{"class":2291},[2225,210809,4228],{"class":2239},[2225,210811,1707],{"class":2235},[2225,210813,210814,210816],{"class":2227,"line":4253},[2225,210815,2359],{"class":2235},[2225,210817,2362],{"class":2239},[2225,210819,210820],{"class":2227,"line":4259},[2225,210821,2285],{"emptyLinePlaceholder":2284},[2225,210823,210824,210826,210828,210830,210832,210834,210836,210838,210840,210842,210844,210846],{"class":2227,"line":6548},[2225,210825,2775],{"class":2774},[2225,210827,2835],{"class":2239},[2225,210829,2781],{"class":2235},[2225,210831,2784],{"class":2231},[2225,210833,2842],{"class":2239},[2225,210835,1520],{"class":2235},[2225,210837,2847],{"class":2291},[2225,210839,2789],{"class":2239},[2225,210841,2805],{"class":2235},[2225,210843,8160],{"class":2252},[2225,210845,2805],{"class":2235},[2225,210847,2362],{"class":2239},[2225,210849,210850,210852,210854,210856,210858,210860,210862,210864,210866,210868,210870,210872,210874,210876,210878,210880,210882,210884,210886,210888,210890,210892,210894,210896,210898,210900],{"class":2227,"line":6576},[2225,210851,2775],{"class":2774},[2225,210853,90113],{"class":2239},[2225,210855,2781],{"class":2235},[2225,210857,6466],{"class":2239},[2225,210859,1520],{"class":2235},[2225,210861,6471],{"class":2291},[2225,210863,17851],{"class":2235},[2225,210865,7070],{"class":2307},[2225,210867,2311],{"class":2235},[2225,210869,6366],{"class":6342},[2225,210871,3769],{"class":2235},[2225,210873,2810],{"class":2307},[2225,210875,2311],{"class":2235},[2225,210877,6366],{"class":6342},[2225,210879,85293],{"class":2235},[2225,210881,2789],{"class":2239},[2225,210883,2805],{"class":2235},[2225,210885,69487],{"class":2252},[2225,210887,2805],{"class":2235},[2225,210889,2298],{"class":2235},[2225,210891,2236],{"class":2235},[2225,210893,2336],{"class":2307},[2225,210895,2311],{"class":2235},[2225,210897,2322],{"class":2321},[2225,210899,2243],{"class":2235},[2225,210901,2362],{"class":2239},[937,210903,90416,210904,968,210906,1261,210908,1261,210910,210912,210913,13571,210915,210917],{},[941,210905,21577],{},[941,210907,1939],{},[941,210909,1942],{},[941,210911,1945],{},"\nall throw ",[941,210914,21782],{},[941,210916,210637],{}," opener works with or without\nopting in — it's a separate, ungated function meant to run on the recipient host, which has\nno vault of its own.",[1022,210919,29893],{"id":29892},[1591,210921,210923],{"id":210922},"seal-a-record-to-a-host","Seal a record to a host",[937,210925,210926,210927,38633,210929,210932,210933,210935],{},"The host publishes a \"recipient hint\" (e.g. its public key); the grantor uses it to seal the\nrecord's CEK into a delivery envelope written at ",[941,210928,14403],{},[941,210930,210931],{},"MemoryRecipientSealer"," is the in-memory RSA-OAEP stand-in used below for a self-contained\nexample — production hosts (a KMS-backed service, say) implement the same ",[941,210934,34334],{},"\ninterface.",[2217,210937,210939],{"className":2219,"code":210938,"language":2221,"meta":1037,"style":1037},"import { MemoryRecipientSealer } from '@noy-db\u002Fhub'\n\nawait docs.put('d-1', { id: 'd-1', secret: 'the eagle lands at dawn' })\n\nconst host = new MemoryRecipientSealer({ id: 'kms:host-A' })\n\nconst { pid, envelopeKey } = await vault.sealRecordToHost('docs', 'd-1', host, {\n  expiresAt: new Date(Date.now() + 60 * 60 * 1000).toISOString(), \u002F\u002F 1 hour\n})\n",[941,210940,210941,210960,210964,211013,211017,211048,211052,211102,211146],{"__ignoreMap":1037},[2225,210942,210943,210945,210947,210950,210952,210954,210956,210958],{"class":2227,"line":2228},[2225,210944,2232],{"class":2231},[2225,210946,2236],{"class":2235},[2225,210948,210949],{"class":2239}," MemoryRecipientSealer",[2225,210951,2243],{"class":2235},[2225,210953,2246],{"class":2231},[2225,210955,2249],{"class":2235},[2225,210957,1086],{"class":2252},[2225,210959,2256],{"class":2235},[2225,210961,210962],{"class":2227,"line":1041},[2225,210963,2285],{"emptyLinePlaceholder":2284},[2225,210965,210966,210968,210970,210972,210974,210976,210978,210980,210982,210984,210986,210988,210990,210992,210994,210996,210998,211000,211002,211004,211007,211009,211011],{"class":2227,"line":1038},[2225,210967,6500],{"class":2231},[2225,210969,90184],{"class":2239},[2225,210971,1520],{"class":2235},[2225,210973,3161],{"class":2291},[2225,210975,2789],{"class":2239},[2225,210977,2805],{"class":2235},[2225,210979,171125],{"class":2252},[2225,210981,2805],{"class":2235},[2225,210983,2298],{"class":2235},[2225,210985,2236],{"class":2235},[2225,210987,7070],{"class":2307},[2225,210989,2311],{"class":2235},[2225,210991,2249],{"class":2235},[2225,210993,171125],{"class":2252},[2225,210995,2805],{"class":2235},[2225,210997,2298],{"class":2235},[2225,210999,2810],{"class":2307},[2225,211001,2311],{"class":2235},[2225,211003,2249],{"class":2235},[2225,211005,211006],{"class":2252},"the eagle lands at dawn",[2225,211008,2805],{"class":2235},[2225,211010,2243],{"class":2235},[2225,211012,2362],{"class":2239},[2225,211014,211015],{"class":2227,"line":2288},[2225,211016,2285],{"emptyLinePlaceholder":2284},[2225,211018,211019,211021,211023,211025,211027,211029,211031,211033,211035,211037,211039,211042,211044,211046],{"class":2227,"line":2304},[2225,211020,2775],{"class":2774},[2225,211022,13538],{"class":2239},[2225,211024,2781],{"class":2235},[2225,211026,8130],{"class":2235},[2225,211028,210949],{"class":2291},[2225,211030,2789],{"class":2239},[2225,211032,2792],{"class":2235},[2225,211034,7070],{"class":2307},[2225,211036,2311],{"class":2235},[2225,211038,2249],{"class":2235},[2225,211040,211041],{"class":2252},"kms:host-A",[2225,211043,2805],{"class":2235},[2225,211045,2243],{"class":2235},[2225,211047,2362],{"class":2239},[2225,211049,211050],{"class":2227,"line":2356},[2225,211051,2285],{"emptyLinePlaceholder":2284},[2225,211053,211054,211056,211058,211061,211063,211066,211068,211070,211072,211074,211076,211078,211080,211082,211084,211086,211088,211090,211092,211094,211096,211098,211100],{"class":2227,"line":2830},[2225,211055,2775],{"class":2774},[2225,211057,2236],{"class":2235},[2225,211059,211060],{"class":2239}," pid",[2225,211062,2298],{"class":2235},[2225,211064,211065],{"class":2239}," envelopeKey ",[2225,211067,2359],{"class":2235},[2225,211069,6346],{"class":2235},[2225,211071,2784],{"class":2231},[2225,211073,6466],{"class":2239},[2225,211075,1520],{"class":2235},[2225,211077,1939],{"class":2291},[2225,211079,2789],{"class":2239},[2225,211081,2805],{"class":2235},[2225,211083,69487],{"class":2252},[2225,211085,2805],{"class":2235},[2225,211087,2298],{"class":2235},[2225,211089,2249],{"class":2235},[2225,211091,171125],{"class":2252},[2225,211093,2805],{"class":2235},[2225,211095,2298],{"class":2235},[2225,211097,17373],{"class":2239},[2225,211099,2298],{"class":2235},[2225,211101,2301],{"class":2235},[2225,211103,211104,211107,211109,211111,211113,211115,211117,211119,211121,211123,211125,211127,211129,211131,211133,211135,211137,211139,211141,211143],{"class":2227,"line":2861},[2225,211105,211106],{"class":2307},"  expiresAt",[2225,211108,2311],{"class":2235},[2225,211110,8130],{"class":2235},[2225,211112,19122],{"class":2291},[2225,211114,75275],{"class":2239},[2225,211116,1520],{"class":2235},[2225,211118,75280],{"class":2291},[2225,211120,19132],{"class":2239},[2225,211122,71297],{"class":2235},[2225,211124,44452],{"class":6527},[2225,211126,2263],{"class":2235},[2225,211128,44452],{"class":6527},[2225,211130,2263],{"class":2235},[2225,211132,26290],{"class":6527},[2225,211134,1381],{"class":2239},[2225,211136,1520],{"class":2235},[2225,211138,19129],{"class":2291},[2225,211140,4228],{"class":2239},[2225,211142,2298],{"class":2235},[2225,211144,211145],{"class":2763}," \u002F\u002F 1 hour\n",[2225,211147,211148,211150],{"class":2227,"line":4246},[2225,211149,2359],{"class":2235},[2225,211151,2362],{"class":2239},[2217,211153,211155],{"className":6604,"code":211154,"language":141,"meta":1037,"style":1037},"{ \"pid\": \"kms:host-A\", \"envelopeKey\": \"docs\u002Fd-1\u002Fkms:host-A\" }\n",[941,211156,211157],{"__ignoreMap":1037},[2225,211158,211159,211161,211163,211165,211167,211169,211171,211173,211175,211177,211179,211182,211184,211186,211188,211191,211193],{"class":2227,"line":2228},[2225,211160,2792],{"class":2235},[2225,211162,6614],{"class":2235},[2225,211164,33316],{"class":2774},[2225,211166,6620],{"class":2235},[2225,211168,2311],{"class":2235},[2225,211170,6614],{"class":2235},[2225,211172,211041],{"class":2252},[2225,211174,6620],{"class":2235},[2225,211176,2298],{"class":2235},[2225,211178,6614],{"class":2235},[2225,211180,211181],{"class":2774},"envelopeKey",[2225,211183,6620],{"class":2235},[2225,211185,2311],{"class":2235},[2225,211187,6614],{"class":2235},[2225,211189,211190],{"class":2252},"docs\u002Fd-1\u002Fkms:host-A",[2225,211192,6620],{"class":2235},[2225,211194,6369],{"class":2235},[937,211196,211197,211200,211201,211203],{},[941,211198,211199],{},"opts.expiresAt"," is required — it's sealed ",[1092,211202,1094],{}," the delivery so the host can't extend its\nown access by tampering with the outer envelope.",[13905,211205,211206],{"style":13907},[13905,211207,211209,211213,211226,211228,211231,211233,211235,211243,211245,211248,211250,211252,211264],{"style":211208},"display:flex; flex-direction:column; align-items:center; min-width:23rem; text-align:center; line-height:1.35;",[13905,211210,211212],{"style":211211},"font-size:0.6875rem; font-weight:600; letter-spacing:0.05em; text-transform:uppercase; color:var(--ui-text-muted); margin-bottom:0.5rem;","Sealing ceremony — grantor side",[13905,211214,211215,211220],{"style":13913},[13905,211216,211217,211218],{"style":13916},"Record ",[2225,211219,171125],{"style":13950},[13905,211221,211222,211223,211225],{"style":13920},"its envelope carries ",[2225,211224,1149],{"style":13950}," — the record’s key, wrapped under the collection DEK",[13905,211227],{"style":13924},[13905,211229,211230],{"style":13927},"AES-KW unwrap under the collection DEK",[13905,211232],{"style":13924},[13905,211234,13934],{"style":13933},[13905,211236,211237,211239],{"style":13937},[13905,211238,54048],{"style":13916},[13905,211240,211242],{"style":211241},"font-size:0.75rem; font-family:ui-monospace,monospace; color:var(--ui-text-muted);","{ collection, id, cek, expiresAt }",[13905,211244],{"style":13953},[13905,211246,211247],{"style":13927},"sealed under the host’s published RSA-OAEP public key",[13905,211249],{"style":13924},[13905,211251,13934],{"style":13933},[13905,211253,211254,211257],{"style":13986},[13905,211255,211256],{"style":13916},"Delivery envelope",[13905,211258,211259,211260,211263],{"style":13920},"written to ",[2225,211261,211262],{"style":13950},"_sealed_cek\u002Fdocs\u002Fd-1\u002Fkms:host-A"," — opaque to everyone but the host",[13905,211265,211267],{"style":211266},"font-size:0.75rem; color:var(--ui-text-muted); margin-top:0.75rem;","The collection DEK — and every other record’s CEK — never leaves the grantor side.",[1591,211269,211271],{"id":211270},"open-the-sealed-record-on-the-host","Open the sealed record on the host",[937,211273,211274,211275,211277],{},"The host reads the delivery envelope and the record's encrypted body (however those travel\nto it — e.g. exported via the vault's store) and calls ",[941,211276,210637],{}," with its own\nunsealer:",[2217,211279,211281],{"className":2219,"code":211280,"language":2221,"meta":1037,"style":1037},"import { openSealedRecord } from '@noy-db\u002Fhub\u002Fsealed-record'\n\n\u002F\u002F `delivery` = the SealedCekDeliveryEnvelope from _sealed_cek\u002Fdocs\u002Fd-1\u002Fkms:host-A\n\u002F\u002F `recordEnvelope` = the record's stored { _iv, _data } body\nconst json = await openSealedRecord(delivery, recordEnvelope, host, 'docs', 'd-1')\n\nJSON.parse(json)\n",[941,211282,211283,211302,211306,211311,211316,211358,211362],{"__ignoreMap":1037},[2225,211284,211285,211287,211289,211292,211294,211296,211298,211300],{"class":2227,"line":2228},[2225,211286,2232],{"class":2231},[2225,211288,2236],{"class":2235},[2225,211290,211291],{"class":2239}," openSealedRecord",[2225,211293,2243],{"class":2235},[2225,211295,2246],{"class":2231},[2225,211297,2249],{"class":2235},[2225,211299,210718],{"class":2252},[2225,211301,2256],{"class":2235},[2225,211303,211304],{"class":2227,"line":1041},[2225,211305,2285],{"emptyLinePlaceholder":2284},[2225,211307,211308],{"class":2227,"line":1038},[2225,211309,211310],{"class":2763},"\u002F\u002F `delivery` = the SealedCekDeliveryEnvelope from _sealed_cek\u002Fdocs\u002Fd-1\u002Fkms:host-A\n",[2225,211312,211313],{"class":2227,"line":2288},[2225,211314,211315],{"class":2763},"\u002F\u002F `recordEnvelope` = the record's stored { _iv, _data } body\n",[2225,211317,211318,211320,211322,211324,211326,211328,211331,211333,211336,211338,211340,211342,211344,211346,211348,211350,211352,211354,211356],{"class":2227,"line":2304},[2225,211319,2775],{"class":2774},[2225,211321,31437],{"class":2239},[2225,211323,2781],{"class":2235},[2225,211325,2784],{"class":2231},[2225,211327,211291],{"class":2291},[2225,211329,211330],{"class":2239},"(delivery",[2225,211332,2298],{"class":2235},[2225,211334,211335],{"class":2239}," recordEnvelope",[2225,211337,2298],{"class":2235},[2225,211339,17373],{"class":2239},[2225,211341,2298],{"class":2235},[2225,211343,2249],{"class":2235},[2225,211345,69487],{"class":2252},[2225,211347,2805],{"class":2235},[2225,211349,2298],{"class":2235},[2225,211351,2249],{"class":2235},[2225,211353,171125],{"class":2252},[2225,211355,2805],{"class":2235},[2225,211357,2362],{"class":2239},[2225,211359,211360],{"class":2227,"line":2356},[2225,211361,2285],{"emptyLinePlaceholder":2284},[2225,211363,211364,211366,211368,211370],{"class":2227,"line":2830},[2225,211365,50521],{"class":2239},[2225,211367,1520],{"class":2235},[2225,211369,15798],{"class":2291},[2225,211371,211372],{"class":2239},"(json)\n",[2217,211374,211376],{"className":6604,"code":211375,"language":141,"meta":1037,"style":1037},"{ \"id\": \"d-1\", \"secret\": \"the eagle lands at dawn\" }\n",[941,211377,211378],{"__ignoreMap":1037},[2225,211379,211380,211382,211384,211386,211388,211390,211392,211394,211396,211398,211400,211402,211404,211406,211408,211410,211412],{"class":2227,"line":2228},[2225,211381,2792],{"class":2235},[2225,211383,6614],{"class":2235},[2225,211385,6983],{"class":2774},[2225,211387,6620],{"class":2235},[2225,211389,2311],{"class":2235},[2225,211391,6614],{"class":2235},[2225,211393,171125],{"class":2252},[2225,211395,6620],{"class":2235},[2225,211397,2298],{"class":2235},[2225,211399,6614],{"class":2235},[2225,211401,10695],{"class":2774},[2225,211403,6620],{"class":2235},[2225,211405,2311],{"class":2235},[2225,211407,6614],{"class":2235},[2225,211409,211006],{"class":2252},[2225,211411,6620],{"class":2235},[2225,211413,6369],{"class":2235},[937,211415,211416,211418,211419,211422,211423,211425,211426,211428,211429,211431,211432,211434,211435,65227],{},[941,211417,210637],{}," checks, in order: the delivery's fast-path expiry, then unseals the\nbinding, then verifies the binding's ",[941,211420,211421],{},"{collection, id}"," matches what the host expected\n(rejecting a CEK sealed for a different record), then re-checks the binding's own\nauthoritative expiry, then decrypts. A CEK sealed before a ",[941,211424,21589],{}," call passes\nall of those checks but fails decryption against the rotated body, surfacing as the same\n",[941,211427,11762],{}," a tampered ciphertext produces. That collision is why ",[941,211430,11762],{}," is not read\nas an accusation on its own: a stale CEK, a pre-",[941,211433,15421],{}," record and a hostile store all\narrive at the same error. Check ",[941,211436,11063],{},[13905,211438,211439],{"style":13907},[13905,211440,211441,211444,211457,211459,211465,211467,211472,211474,211476,211482,211484,211493,211495,211503,211505,211514,211516,211518,211526],{"style":211208},[13905,211442,211443],{"style":211211},"Unlock ceremony — host side",[13905,211445,211446,211449],{"style":13913},[13905,211447,211448],{"style":13916},"Delivery envelope arrives",[13905,211450,5001,211451,211453,211454,211456],{"style":13920},[2225,211452,66738],{"style":13950}," + clear-text ",[2225,211455,44339],{"style":13950}," hint",[13905,211458],{"style":13924},[13905,211460,211461,211462],{"style":13927},"1 · fast-path expiry check — expired → ",[2225,211463,211464],{"style":13950},"SealedRecordExpiredError",[13905,211466],{"style":13924},[13905,211468,211469,211470],{"style":13927},"2 · host’s own unsealer opens ",[2225,211471,66738],{"style":13950},[13905,211473],{"style":13924},[13905,211475,13934],{"style":13933},[13905,211477,211478,211480],{"style":13937},[13905,211479,54048],{"style":13916},[13905,211481,211242],{"style":211241},[13905,211483],{"style":13953},[13905,211485,211486,211487,211489,211490],{"style":13927},"3 · ",[2225,211488,211421],{"style":13950}," must name the record being opened — mismatch → ",[2225,211491,211492],{"style":13950},"SealedRecordMismatchError",[13905,211494],{"style":13924},[13905,211496,211497,211498,211500,211501],{"style":13927},"4 · authoritative ",[2225,211499,44339],{"style":13950}," re-check — expired → ",[2225,211502,211464],{"style":13950},[13905,211504],{"style":13924},[13905,211506,211507,211508,211511,211512],{"style":13927},"5 · CEK decrypts ",[2225,211509,211510],{"style":13950},"{ _iv, _data }"," — a pre-rotation CEK fails the GCM tag → ",[2225,211513,11762],{"style":13950},[13905,211515],{"style":13924},[13905,211517,13934],{"style":13933},[13905,211519,211520,211523],{"style":13986},[13905,211521,211522],{"style":13916},"Plaintext record",[13905,211524,211525],{"style":13920},"exactly this one record — nothing else unlocks",[13905,211527,211528],{"style":211266},"The vault’s collection DEK is never present on this side.",[1591,211530,211532],{"id":211531},"revoke-access-soft-vs-hard","Revoke access — soft vs. hard",[937,211534,211535,7356,211537,211540,211541,211544],{},[941,211536,1942],{},[950,211538,211539],{},"soft"," by default: it deletes the delivery envelope so no new host\ncan fetch it, but a host that already unsealed the CEK keeps the ability to decrypt (there's\nno way to un-teach a host something it has already read). Pass ",[941,211542,211543],{},"{ hard: true }"," to also\nrotate the record's CEK, which cuts off every previously sealed copy — including ones already\nfetched — because they no longer match the record's (now re-encrypted) body:",[2217,211546,211548],{"className":2219,"code":211547,"language":2221,"meta":1037,"style":1037},"\u002F\u002F Soft (default): stops future fetches only.\nawait vault.revokeSealedRecord('docs', 'd-1', 'kms:host-A')\n\n\u002F\u002F Hard: also rotates the CEK, so any already-fetched sealed copy stops working.\nawait vault.revokeSealedRecord('docs', 'd-1', 'kms:host-A', { hard: true })\n",[941,211549,211550,211555,211591,211595,211600],{"__ignoreMap":1037},[2225,211551,211552],{"class":2227,"line":2228},[2225,211553,211554],{"class":2763},"\u002F\u002F Soft (default): stops future fetches only.\n",[2225,211556,211557,211559,211561,211563,211565,211567,211569,211571,211573,211575,211577,211579,211581,211583,211585,211587,211589],{"class":2227,"line":1041},[2225,211558,6500],{"class":2231},[2225,211560,6466],{"class":2239},[2225,211562,1520],{"class":2235},[2225,211564,1942],{"class":2291},[2225,211566,2789],{"class":2239},[2225,211568,2805],{"class":2235},[2225,211570,69487],{"class":2252},[2225,211572,2805],{"class":2235},[2225,211574,2298],{"class":2235},[2225,211576,2249],{"class":2235},[2225,211578,171125],{"class":2252},[2225,211580,2805],{"class":2235},[2225,211582,2298],{"class":2235},[2225,211584,2249],{"class":2235},[2225,211586,211041],{"class":2252},[2225,211588,2805],{"class":2235},[2225,211590,2362],{"class":2239},[2225,211592,211593],{"class":2227,"line":1038},[2225,211594,2285],{"emptyLinePlaceholder":2284},[2225,211596,211597],{"class":2227,"line":2288},[2225,211598,211599],{"class":2763},"\u002F\u002F Hard: also rotates the CEK, so any already-fetched sealed copy stops working.\n",[2225,211601,211602,211604,211606,211608,211610,211612,211614,211616,211618,211620,211622,211624,211626,211628,211630,211632,211634,211636,211638,211641,211643,211645,211647],{"class":2227,"line":2304},[2225,211603,6500],{"class":2231},[2225,211605,6466],{"class":2239},[2225,211607,1520],{"class":2235},[2225,211609,1942],{"class":2291},[2225,211611,2789],{"class":2239},[2225,211613,2805],{"class":2235},[2225,211615,69487],{"class":2252},[2225,211617,2805],{"class":2235},[2225,211619,2298],{"class":2235},[2225,211621,2249],{"class":2235},[2225,211623,171125],{"class":2252},[2225,211625,2805],{"class":2235},[2225,211627,2298],{"class":2235},[2225,211629,2249],{"class":2235},[2225,211631,211041],{"class":2252},[2225,211633,2805],{"class":2235},[2225,211635,2298],{"class":2235},[2225,211637,2236],{"class":2235},[2225,211639,211640],{"class":2307}," hard",[2225,211642,2311],{"class":2235},[2225,211644,2322],{"class":2321},[2225,211646,2243],{"class":2235},[2225,211648,2362],{"class":2239},[937,211650,211651,211653],{},[941,211652,1945],{}," (used internally by hard revoke, and callable directly) re-encrypts the\nlive record body under a fresh CEK and deletes every sealed-CEK delivery envelope for that\nrecord. Prior history versions keep their original CEK and remain openable by hosts sealed\nagainst them — only the live record is cut over.",[1022,211655,102889],{"id":102888},[1591,211657,211659],{"id":211658},"withsealedrecord",[941,211660,21577],{},[2217,211662,211664],{"className":2219,"code":211663,"language":2221,"meta":1037,"style":1037},"function withSealedRecord(): SealedRecordStrategy\n",[941,211665,211666],{"__ignoreMap":1037},[2225,211667,211668,211670,211672,211674],{"class":2227,"line":2228},[2225,211669,12641],{"class":2774},[2225,211671,210709],{"class":2291},[2225,211673,112812],{"class":2235},[2225,211675,211676],{"class":6342}," SealedRecordStrategy\n",[937,211678,211679,211680,1261,211682,2601,211684,211686,211687,10313,211690,211692],{},"Returns the strategy that makes ",[941,211681,210620],{},[941,211683,210623],{},[941,211685,210626],{}," live. Pass it as ",[941,211688,211689],{},"sealedRecordStrategy",[941,211691,12780],{},". The\nsealing\u002Frotation engine is dynamically imported inside it, so it's excluded from your bundle\nuntil you opt in.",[1591,211694,211696],{"id":211695},"vaultsealrecordtohostcollection-id-hostsealer-opts",[941,211697,211698],{},"vault.sealRecordToHost(collection, id, hostSealer, opts)",[2217,211700,211702],{"className":2219,"code":211701,"language":2221,"meta":1037,"style":1037},"sealRecordToHost(\n  collection: string,\n  id: string,\n  hostSealer: RecipientSealer,\n  opts: { expiresAt: string },\n): Promise\u003C{ pid: string; envelopeKey: string }>\n",[941,211703,211704,211710,211717,211723,211730,211744],{"__ignoreMap":1037},[2225,211705,211706,211708],{"class":2227,"line":2228},[2225,211707,1939],{"class":2291},[2225,211709,11683],{"class":2239},[2225,211711,211712,211715],{"class":2227,"line":1041},[2225,211713,211714],{"class":2239},"  collection: string",[2225,211716,1707],{"class":2235},[2225,211718,211719,211721],{"class":2227,"line":1038},[2225,211720,24079],{"class":2239},[2225,211722,1707],{"class":2235},[2225,211724,211725,211728],{"class":2227,"line":2288},[2225,211726,211727],{"class":2239},"  hostSealer: RecipientSealer",[2225,211729,1707],{"class":2235},[2225,211731,211732,211734,211736,211738,211740,211742],{"class":2227,"line":2304},[2225,211733,170264],{"class":2239},[2225,211735,2792],{"class":2235},[2225,211737,112955],{"class":2307},[2225,211739,2311],{"class":2235},[2225,211741,19923],{"class":2239},[2225,211743,19008],{"class":2235},[2225,211745,211746,211748,211750,211752,211754,211756,211758,211760,211763,211765,211767],{"class":2227,"line":2356},[2225,211747,11737],{"class":2239},[2225,211749,11740],{"class":6342},[2225,211751,17851],{"class":2235},[2225,211753,211060],{"class":6342},[2225,211755,2311],{"class":2235},[2225,211757,6366],{"class":2239},[2225,211759,3769],{"class":2235},[2225,211761,211762],{"class":6342}," envelopeKey",[2225,211764,2311],{"class":2235},[2225,211766,6366],{"class":2239},[2225,211768,17861],{"class":2235},[937,211770,211771,211772,211775,211776,211779,211780,211782,211783,211785,211786,211788,211789,211792,211793,1520],{},"Seals ",[941,211773,211774],{},"collection\u002Fid","'s CEK to ",[941,211777,211778],{},"hostSealer"," and writes the delivery envelope. ",[941,211781,44339],{},"\n(ISO 8601) is required and becomes the authoritative expiry sealed inside the binding.\nReturns the host's provider id (",[941,211784,33316],{},") and the store key (",[941,211787,211181],{},") the delivery was\nwritten under. Throws ",[941,211790,211791],{},"RecordCekNotFoundError"," if the record has no CEK — either it doesn't\nexist, or its collection wasn't opened with ",[941,211794,73811],{},[1591,211796,211798],{"id":211797},"vaultrevokesealedrecordcollection-id-pid-opts",[941,211799,211800],{},"vault.revokeSealedRecord(collection, id, pid, opts?)",[2217,211802,211804],{"className":2219,"code":211803,"language":2221,"meta":1037,"style":1037},"revokeSealedRecord(\n  collection: string,\n  id: string,\n  pid: string,\n  opts?: { hard?: boolean },\n): Promise\u003Cvoid>\n",[941,211805,211806,211812,211818,211824,211831,211848],{"__ignoreMap":1037},[2225,211807,211808,211810],{"class":2227,"line":2228},[2225,211809,1942],{"class":2291},[2225,211811,11683],{"class":2239},[2225,211813,211814,211816],{"class":2227,"line":1041},[2225,211815,211714],{"class":2239},[2225,211817,1707],{"class":2235},[2225,211819,211820,211822],{"class":2227,"line":1038},[2225,211821,24079],{"class":2239},[2225,211823,1707],{"class":2235},[2225,211825,211826,211829],{"class":2227,"line":2288},[2225,211827,211828],{"class":2239},"  pid: string",[2225,211830,1707],{"class":2235},[2225,211832,211833,211835,211837,211839,211842,211844,211846],{"class":2227,"line":2304},[2225,211834,11695],{"class":2239},[2225,211836,7011],{"class":2235},[2225,211838,2236],{"class":2235},[2225,211840,211841],{"class":2239}," hard?",[2225,211843,2311],{"class":2235},[2225,211845,146181],{"class":2239},[2225,211847,19008],{"class":2235},[2225,211849,211850,211852,211854],{"class":2227,"line":2356},[2225,211851,11737],{"class":2239},[2225,211853,11740],{"class":6342},[2225,211855,13016],{"class":2235},[937,211857,211858,211859,211861,211862,211864,211865,211869,211870,211872],{},"Deletes the ",[941,211860,33316],{}," delivery envelope for ",[941,211863,211774],{},". Soft by default (see ",[1007,211866,211868],{"href":211867},"#revoke-access-soft-vs-hard","Revoke\naccess"," above); ",[941,211871,211543],{}," additionally rotates the\nrecord's CEK.",[1591,211874,211876],{"id":211875},"vaultrotaterecordcekcollection-id",[941,211877,15285],{},[2217,211879,211881],{"className":2219,"code":211880,"language":2221,"meta":1037,"style":1037},"rotateRecordCek(collection: string, id: string): Promise\u003Cvoid>\n",[941,211882,211883],{"__ignoreMap":1037},[2225,211884,211885,211887,211889,211891,211893,211895],{"class":2227,"line":2228},[2225,211886,1945],{"class":2291},[2225,211888,101322],{"class":2239},[2225,211890,2298],{"class":2235},[2225,211892,101327],{"class":2239},[2225,211894,11740],{"class":6342},[2225,211896,13016],{"class":2235},[937,211898,211899,211900,211902],{},"Re-encrypts the live record body under a freshly minted CEK and deletes every sealed-CEK\ndelivery envelope for the record. Throws ",[941,211901,211791],{}," if the record is missing or\nhas no CEK.",[1591,211904,211906],{"id":211905},"opensealedrecordsealedcekenvelope-recordenvelope-recipientsealer-expectedcollection-expectedid",[941,211907,211908],{},"openSealedRecord(sealedCekEnvelope, recordEnvelope, recipientSealer, expectedCollection, expectedId)",[2217,211910,211912],{"className":2219,"code":211911,"language":2221,"meta":1037,"style":1037},"function openSealedRecord(\n  sealedCekEnvelope: SealedCekDeliveryEnvelope,\n  recordEnvelope: { readonly _iv: string; readonly _data: string },\n  recipientSealer: { unseal(bytes: Uint8Array): Promise\u003CUint8Array> },\n  expectedCollection: string,\n  expectedId: string,\n): Promise\u003Cstring>\n",[941,211913,211914,211922,211934,211963,211995,212006,212017],{"__ignoreMap":1037},[2225,211915,211916,211918,211920],{"class":2227,"line":2228},[2225,211917,12641],{"class":2774},[2225,211919,211291],{"class":2291},[2225,211921,11683],{"class":2235},[2225,211923,211924,211927,211929,211932],{"class":2227,"line":1041},[2225,211925,211926],{"class":7052},"  sealedCekEnvelope",[2225,211928,2311],{"class":2235},[2225,211930,211931],{"class":6342}," SealedCekDeliveryEnvelope",[2225,211933,1707],{"class":2235},[2225,211935,211936,211939,211941,211943,211945,211947,211949,211951,211953,211955,211957,211959,211961],{"class":2227,"line":1038},[2225,211937,211938],{"class":7052},"  recordEnvelope",[2225,211940,2311],{"class":2235},[2225,211942,2236],{"class":2235},[2225,211944,112906],{"class":2774},[2225,211946,194922],{"class":2307},[2225,211948,2311],{"class":2235},[2225,211950,6366],{"class":6342},[2225,211952,3769],{"class":2235},[2225,211954,112906],{"class":2774},[2225,211956,194931],{"class":2307},[2225,211958,2311],{"class":2235},[2225,211960,6366],{"class":6342},[2225,211962,2353],{"class":2235},[2225,211964,211965,211968,211970,211972,211975,211977,211979,211981,211983,211985,211987,211989,211991,211993],{"class":2227,"line":2288},[2225,211966,211967],{"class":7052},"  recipientSealer",[2225,211969,2311],{"class":2235},[2225,211971,2236],{"class":2235},[2225,211973,211974],{"class":2307}," unseal",[2225,211976,2789],{"class":2235},[2225,211978,64492],{"class":7052},[2225,211980,2311],{"class":2235},[2225,211982,60935],{"class":6342},[2225,211984,6255],{"class":2235},[2225,211986,7079],{"class":6342},[2225,211988,6474],{"class":2235},[2225,211990,45820],{"class":6342},[2225,211992,6480],{"class":2235},[2225,211994,2353],{"class":2235},[2225,211996,211997,212000,212002,212004],{"class":2227,"line":2304},[2225,211998,211999],{"class":7052},"  expectedCollection",[2225,212001,2311],{"class":2235},[2225,212003,6366],{"class":6342},[2225,212005,1707],{"class":2235},[2225,212007,212008,212011,212013,212015],{"class":2227,"line":2356},[2225,212009,212010],{"class":7052},"  expectedId",[2225,212012,2311],{"class":2235},[2225,212014,6366],{"class":6342},[2225,212016,1707],{"class":2235},[2225,212018,212019,212021,212023,212025,212027],{"class":2227,"line":2830},[2225,212020,6255],{"class":2235},[2225,212022,7079],{"class":6342},[2225,212024,6474],{"class":2235},[2225,212026,7259],{"class":6342},[2225,212028,7093],{"class":2235},[937,212030,212031,212032,212034,212035,212037,212038,212040,212041,4688,212043,212045,212046,212049,212050,1520],{},"Runs on the recipient host — ",[950,212033,1004],{}," gated by ",[941,212036,21577],{},". Reconstructs the\nrecord's plaintext (returned as a JSON string) from the delivery envelope, the record's\nencrypted ",[941,212039,211510],{}," body, and the host's own unsealer. Throws\n",[941,212042,211464],{},[941,212044,211492],{}," on the checks described in\n",[1007,212047,29893],{"href":212048},"#open-the-sealed-record-on-the-host","; a CEK that unseals but no longer matches the\nrecord's ciphertext (e.g. after rotation) surfaces as ",[941,212051,11762],{},[1591,212053,103205],{"id":128135},[1338,212055,212056,212066],{},[1341,212057,212058],{},[1344,212059,212060,212062,212064],{},[1347,212061,20194],{},[1347,212063,50797],{},[1347,212065,7834],{},[1357,212067,212068,212091],{},[1344,212069,212070,212075,212080],{},[1362,212071,212072],{},[941,212073,212074],{},"SealedCekDeliveryEnvelope",[1362,212076,212077],{},[941,212078,212079],{},"{ v: 1, _noydb_sealed_cek: 1, pid: string, payload: string, expiresAt: string }",[1362,212081,212082,212083,1952,212085,212087,212088,212090],{},"The thin envelope written to ",[941,212084,14403],{},[941,212086,66738],{}," is the opaque sealed binding; ",[941,212089,44339],{}," here is a fast-path hint only.",[1344,212092,212093,212098,212103],{},[1362,212094,212095],{},[941,212096,212097],{},"SealedCekBinding",[1362,212099,212100],{},[941,212101,212102],{},"{ collection: string, id: string, cek: string, expiresAt: string }",[1362,212104,212105,212106,1324,212108,212110,212111,1625,212113,212115,212116,212118],{},"The plaintext struct sealed ",[1092,212107,1094],{},[941,212109,66738],{},". This — not the delivery envelope — is the security boundary: ",[941,212112,6471],{},[941,212114,6983],{}," bind the CEK to one record, and this ",[941,212117,44339],{}," is authoritative.",[1591,212120,120265],{"id":120264},[1338,212122,212123,212131],{},[1341,212124,212125],{},[1344,212126,212127,212129],{},[1347,212128,12126],{},[1347,212130,120276],{},[1357,212132,212133,212144,212159,212173],{},[1344,212134,212135,212139],{},[1362,212136,212137],{},[941,212138,21782],{},[1362,212140,212141,212142,1520],{},"A grantor-side method is called without ",[941,212143,210671],{},[1344,212145,212146,212150],{},[1362,212147,212148],{},[941,212149,211791],{},[1362,212151,212152,1261,212154,212156,212157,957],{},[941,212153,1939],{},[941,212155,1945],{}," target a record with no CEK (missing, or its collection lacks ",[941,212158,73811],{},[1344,212160,212161,212165],{},[1362,212162,212163],{},[941,212164,211464],{},[1362,212166,212167,212169,212170,212172],{},[941,212168,210637],{},"'s fast-path or authoritative ",[941,212171,44339],{}," check fails.",[1344,212174,212175,212179],{},[1362,212176,212177],{},[941,212178,211492],{},[1362,212180,212181,212182,212184],{},"The unsealed binding's ",[941,212183,211421],{}," doesn't match what the host expected — a CEK sealed for one record presented against another.",[1022,212186,68210],{"id":68209},[1111,212188,212189,212201,212210,212216],{},[1114,212190,212191,212195,212196,6789,212198,212200],{},[950,212192,145486,212193,1520],{},[941,212194,73811],{}," Legacy records (body encrypted directly under the\ncollection key) have no CEK to seal — ",[941,212197,1939],{},[941,212199,211791],{},".\nSealing deliberately never exposes the shared collection key itself.",[1114,212202,212203,212206,212207,212209],{},[950,212204,212205],{},"Soft revoke doesn't reach already-fetched copies."," If a host unsealed the CEK before you\nrevoke, deleting the delivery envelope doesn't stop it from decrypting the record it\nalready has. Use ",[941,212208,211543],{}," when a leaked or misbehaving host must actually lose\naccess.",[1114,212211,212212,212215],{},[950,212213,212214],{},"Hard revoke \u002F rotation only cuts off the live record."," History versions keep their\noriginal CEK; a host previously sealed against a specific history version can still open\nthat version after the live record is rotated.",[1114,212217,212218,212223],{},[950,212219,212220,212222],{},[941,212221,44339],{}," has two copies for a reason."," The clear-text copy on the delivery envelope\nis only a cheap pre-unseal reject; the copy inside the sealed binding is the one that\ncan't be forged by editing the envelope. Both are checked, and both fail closed on a\nmalformed or empty timestamp.",[1022,212225,2621],{"id":2620},[1111,212227,212228,212234,212244,212249,212255],{},[1114,212229,212230,212233],{},[1007,212231,212232],{"href":66},"Enclave (Core 02)"," — per-record keys (CEK), the collection DEK, and the envelope format this service builds on.",[1114,212235,212236,212240,212241,212243],{},[1007,212237,212238,9789],{"href":170},[941,212239,1139],{}," — sealing-key providers a production ",[941,212242,34334],{}," would be built on.",[1114,212245,212246,212248],{},[1007,212247,661],{"href":662}," — a related sealing pattern for handing off an entire vault's ownership rather than a single record.",[1114,212250,212251,1368,212253],{},[1007,212252,61743],{"href":598},[1007,212254,1019],{"href":1018},[1114,212256,101581,212257,1520],{},[1007,212258,212260],{"href":94401,"rel":212259},[3371],[941,212261,212262],{},"133-sealed-record",[2645,212264,212265],{},"html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sfNiH, html code.shiki .sfNiH{--shiki-light:#FF5370;--shiki-default:#FF9CAC;--shiki-dark:#FF9CAC}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html pre.shiki code .sHdIc, html code.shiki .sHdIc{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#EEFFFF;--shiki-default-font-style:italic;--shiki-dark:#BABED8;--shiki-dark-font-style:italic}",{"title":1037,"searchDepth":1038,"depth":1038,"links":212267},[212268,212269,212270,212271,212276,212285,212286],{"id":29856,"depth":1041,"text":29857},{"id":70092,"depth":1041,"text":70093},{"id":99863,"depth":1041,"text":99864},{"id":29892,"depth":1041,"text":29893,"children":212272},[212273,212274,212275],{"id":210922,"depth":1038,"text":210923},{"id":211270,"depth":1038,"text":211271},{"id":211531,"depth":1038,"text":211532},{"id":102888,"depth":1041,"text":102889,"children":212277},[212278,212279,212280,212281,212282,212283,212284],{"id":211658,"depth":1038,"text":21577},{"id":211695,"depth":1038,"text":211698},{"id":211797,"depth":1038,"text":211800},{"id":211875,"depth":1038,"text":15285},{"id":211905,"depth":1038,"text":211908},{"id":128135,"depth":1038,"text":103205},{"id":120264,"depth":1038,"text":120265},{"id":68209,"depth":1041,"text":68210},{"id":2620,"depth":1041,"text":2621},"Seal a single record's decryption key to an external host (e.g.",{},{"title":824,"description":212287},"03cYln7PkmQxKs7ArLms48oLyQqaxu_F27WPsCXq00E",{"id":212292,"title":828,"access":12,"apiToc":929,"body":212293,"category":1042,"description":212802,"extension":1044,"meta":212803,"navigation":1042,"nest":1047,"path":829,"seo":212804,"sinceVersion":1042,"status":1049,"stem":830,"subtitle":212805,"tier":1050,"tocExternal":1042,"__hash__":212806},"docs\u002Fservices\u002Fsealing-pid-stability.md",{"type":931,"value":212294,"toc":212792},[212295,212298,212301,212329,212343,212347,212362,212368,212417,212421,212543,212550,212554,212560,212637,212652,212679,212685,212692,212709,212715,212724,212753,212756,212758,212789],[934,212296,828],{"id":212297},"sealing-provider-id",[46871,212299],{":rows":212300},"[{\"label\":\"Subpath\",\"value\":\"`@noy-db\u002Fat-*` providers (the `pid` string)\"},{\"label\":\"Factory\",\"value\":\"— reference rule; no API of its own\"},{\"label\":\"Layer\",\"value\":\"`with-party`\"},{\"label\":\"Kind\",\"value\":\"reference\"},{\"label\":\"Contract\",\"value\":\"`NoydbSealer` — see the [`@noy-db\u002Fat-*` family overview](\u002Ffamilies\u002Fat)\"}]",[937,212302,212303,212304,1169,212306,212308,212309,212311,212312,212315,212316,212318,212319,212322,212323,953,212325,212328],{},"This page assumes you're already using ",[1007,212305,14373],{"href":170},[941,212307,16186],{}," family (env var, OS keychain, cloud KMS) that lets a server open a vault without a human typing a secret. In that mode, ",[941,212310,1086],{}," (the always-on core library every noy-db app imports) generates a random secret once, hands it to your chosen ",[950,212313,212314],{},"sealing key provider"," to encrypt (\"seal\"), and persists the sealed result inside the vault at ",[941,212317,14380],{}," — a small JSON record called the ",[950,212320,212321],{},"sealed envelope",". Every provider publishes a ",[941,212324,33316],{},[950,212326,212327],{},"provider id","): a short string, unique to that provider instance, stamped into the envelope alongside the sealed bytes.",[937,212330,212331,212332,212334,212335,212337,212338,2419,212340,212342],{},"This page captures the one rule that governs that ",[941,212333,33316],{}," string across the ",[941,212336,1139],{}," family, and why it's frozen once a provider ships. It has no opt-in of its own — it governs providers you've already wired in via ",[941,212339,13515],{},[941,212341,12780],{}," (see the family overview linked above for that snippet).",[1022,212344,212346],{"id":212345},"the-rule","The rule",[1061,212348,212349],{},[937,212350,212351],{},[950,212352,52166,212353,212355,212356,212358,212359,212361],{},[941,212354,1139],{}," provider owns its ",[941,212357,33316],{}," format. Once a provider ships v1.x, the ",[941,212360,33316],{}," format is semver-frozen — changing it is a major-version break of that provider package, treated with the same discipline as a public API break.",[937,212363,212364,212365,212367],{},"Concretely, every ",[941,212366,1139],{}," package:",[1234,212369,212370,212400,212414],{},[1114,212371,212372,212373,212375,212376,212379,212380,34689,212382,2557,212384,212387,212388,33294,212390,212392,212393,212395,212396,212399],{},"Fixes its ",[941,212374,33316],{}," format as a ",[941,212377,212378],{},"readonly id: string"," on the object the provider factory returns — e.g. ",[941,212381,33656],{},[941,212383,33671],{},[941,212385,212386],{},"id: \\","env:${envVar}`",[941,212389,968],{},[941,212391,34689],{},"atAwsKms()",[941,212394,42111],{},"id: `aws-kms:${keyId}`",[941,212397,212398],{},". Today only ","@noy-db\u002Fat-macos-keychain` additionally documents its format in the package README, under a \"Provider id format\" heading; for the other four providers the format lives in source and in the table below.",[1114,212401,212402,212403,34689,212405,968,212408,34689,212410,212413],{},"Locks the format with a golden-string test (e.g. ",[941,212404,13481],{},[941,212406,212407],{},"pid-stability.test.ts",[941,212409,13497],{},[941,212411,212412],{},"expect(p.id).toBe('macos-keychain:com.acme.app\u002Falice@acme.example')","), so an accidental rename fails CI instead of shipping silently.",[1114,212415,212416],{},"Treats any future format change as a major-version bump with a migration story, not a patch.",[1022,212418,212420],{"id":212419},"current-formats","Current formats",[1338,212422,212423,212438],{},[1341,212424,212425],{},[1344,212426,212427,212429,212433,212435],{},[1347,212428,9530],{},[1347,212430,212431,36875],{},[941,212432,33316],{},[1347,212434,68053],{},[1347,212436,212437],{},"Test location",[1357,212439,212440,212460,212480,212501,212522],{},[1344,212441,212442,212446,212451,212455],{},[1362,212443,212444],{},[941,212445,33656],{},[1362,212447,212448],{},[941,212449,212450],{},"env:{envVar}",[1362,212452,212453],{},[941,212454,33697],{},[1362,212456,212457],{},[941,212458,212459],{},"packages\u002Fat-env\u002F__tests__\u002Fpid-stability.test.ts",[1344,212461,212462,212466,212471,212475],{},[1362,212463,212464],{},[941,212465,34296],{},[1362,212467,212468],{},[941,212469,212470],{},"macos-keychain:{service}\u002F{account}",[1362,212472,212473],{},[941,212474,34323],{},[1362,212476,212477],{},[941,212478,212479],{},"packages\u002Fat-macos-keychain\u002F__tests__\u002Fat-macos-keychain.test.ts",[1344,212481,212482,212486,212491,212496],{},[1362,212483,212484],{},[941,212485,33294],{},[1362,212487,212488],{},[941,212489,212490],{},"aws-kms:{keyId}",[1362,212492,212493],{},[941,212494,212495],{},"aws-kms:arn:aws:kms:us-east-1:1:key\u002Fabc",[1362,212497,212498],{},[941,212499,212500],{},"packages\u002Fat-aws-kms\u002F__tests__\u002Fat-aws-kms.test.ts",[1344,212502,212503,212507,212512,212517],{},[1362,212504,212505],{},[941,212506,33836],{},[1362,212508,212509],{},[941,212510,212511],{},"gcp-kms:{keyName}",[1362,212513,212514],{},[941,212515,212516],{},"gcp-kms:projects\u002Fmy-project\u002Flocations\u002Fglobal\u002FkeyRings\u002Fring\u002FcryptoKeys\u002Fkey",[1362,212518,212519],{},[941,212520,212521],{},"packages\u002Fat-gcp-kms\u002F__tests__\u002Fat-gcp-kms.test.ts",[1344,212523,212524,212528,212533,212538],{},[1362,212525,212526],{},[941,212527,33472],{},[1362,212529,212530],{},[941,212531,212532],{},"azure-kv:{keyId}",[1362,212534,212535],{},[941,212536,212537],{},"azure-kv:https:\u002F\u002Fmy-vault.vault.azure.net\u002Fkeys\u002Fnoydb-sealing\u002Fabc123",[1362,212539,212540],{},[941,212541,212542],{},"packages\u002Fat-azure-keyvault\u002F__tests__\u002Fat-azure-keyvault.test.ts",[937,212544,212545,212546,212549],{},"If you build a custom provider, follow the same ",[941,212547,212548],{},"\u003Cfamily>:\u003Cscope>"," shape and add a row here once it ships.",[1022,212551,212553],{"id":212552},"why-this-matters","Why this matters",[937,212555,212556,212557,212559],{},"A sealed envelope persisted at ",[941,212558,14380],{}," looks like this:",[2217,212561,212563],{"className":6604,"code":212562,"language":141,"meta":1037,"style":1037},"{\n  \"v\": 1,\n  \"_noydb_sealed\": 1,\n  \"pid\": \"env:NOYDB_SEALING_KEY\",\n  \"payload\": \"\u003Cbase64 sealed bytes>\"\n}\n",[941,212564,212565,212569,212583,212598,212616,212633],{"__ignoreMap":1037},[2225,212566,212567],{"class":2227,"line":2228},[2225,212568,4200],{"class":2235},[2225,212570,212571,212573,212575,212577,212579,212581],{"class":2227,"line":1041},[2225,212572,9368],{"class":2235},[2225,212574,8160],{"class":2774},[2225,212576,6620],{"class":2235},[2225,212578,2311],{"class":2235},[2225,212580,9377],{"class":6527},[2225,212582,1707],{"class":2235},[2225,212584,212585,212587,212590,212592,212594,212596],{"class":2227,"line":1038},[2225,212586,9368],{"class":2235},[2225,212588,212589],{"class":2774},"_noydb_sealed",[2225,212591,6620],{"class":2235},[2225,212593,2311],{"class":2235},[2225,212595,9377],{"class":6527},[2225,212597,1707],{"class":2235},[2225,212599,212600,212602,212604,212606,212608,212610,212612,212614],{"class":2227,"line":2288},[2225,212601,9368],{"class":2235},[2225,212603,33316],{"class":2774},[2225,212605,6620],{"class":2235},[2225,212607,2311],{"class":2235},[2225,212609,6614],{"class":2235},[2225,212611,33697],{"class":2252},[2225,212613,6620],{"class":2235},[2225,212615,1707],{"class":2235},[2225,212617,212618,212620,212622,212624,212626,212628,212631],{"class":2227,"line":2304},[2225,212619,9368],{"class":2235},[2225,212621,66738],{"class":2774},[2225,212623,6620],{"class":2235},[2225,212625,2311],{"class":2235},[2225,212627,6614],{"class":2235},[2225,212629,212630],{"class":2252},"\u003Cbase64 sealed bytes>",[2225,212632,9449],{"class":2235},[2225,212634,212635],{"class":2227,"line":2356},[2225,212636,7351],{"class":2235},[937,212638,960,212639,212641,212642,212644,212645,212647,212648,212651],{},[941,212640,33316],{}," field is the only thing your app has to check that it's opening a vault with the provider that originally sealed it. If ",[941,212643,33656],{}," v1 emits ",[941,212646,33697],{}," and a hypothetical v2 started emitting ",[941,212649,212650],{},"at-env:NOYDB_SEALING_KEY",", every existing vault sealed under v1 would look like it was sealed by a different, unrecognized provider.",[937,212653,212654,212655,212657,212658,163847,212660,212662,212663,212665,212666,212669,212670,212672,212673,212675,212676,212678],{},"Concretely: when you reopen a managed-secret vault, hub reads the envelope and compares its ",[941,212656,33316],{}," to the ",[941,212659,116501],{},[941,212661,14350],{}," you passed to ",[941,212664,12780],{},". On a match, it calls that provider's ",[941,212667,212668],{},"unseal()",". On a mismatch, it throws before touching any key material, naming both the envelope's ",[941,212671,33316],{}," and the current provider's ",[941,212674,6983],{}," in the error — there's no silent fallback to a \"closest matching\" provider. This is a single point-in-time comparison, not a registry that dispatches across many installed providers, so a ",[941,212677,33316],{}," rename is indistinguishable, from hub's point of view, from swapping in an entirely different provider.",[937,212680,212681,212682,212684],{},"This is a stronger discipline than typical npm packages because the artifact (the sealed envelope) lives on disk indefinitely — often longer than the package's release cadence. A ",[941,212683,33316],{}," change is more like a database schema migration than a code-level API tweak.",[1022,212686,212688,212689,212691],{"id":212687},"analogue-to-adapter-resource-triples","Analogue: ",[941,212690,1779],{}," adapter resource triples",[937,212693,960,212694,212696,212697,212699,212700,1625,212702,1625,212704,212706,212707,1520],{},[941,212695,1779],{}," storage adapter family follows the same discipline for the ",[941,212698,6299],{}," triple every store's ",[941,212701,3158],{},[941,212703,3161],{},[941,212705,3164],{}," takes: once shipped, the format is frozen. Changing it would orphan every record on disk written under the old shape. The reasoning is identical for ",[941,212708,33316],{},[1022,212710,212712,212713,36875],{"id":212711},"if-you-ever-genuinely-need-to-change-pid-format","If you ever genuinely need to change ",[941,212714,33316],{},[937,212716,212717,212718,212720,212721,212723],{},"Because hub only ever checks one configured provider's ",[941,212719,6983],{}," against the envelope's ",[941,212722,33316],{},", a rename isn't something hub does for you — it's a migration you'd build into your own app and your provider's next major version:",[1234,212725,212726,212735,212741,212750],{},[1114,212727,212728,212729,212731,212732,212734],{},"Ship a new provider version and, in your app, try unsealing under the new ",[941,212730,33316],{}," first, falling back to the old provider\u002F",[941,212733,33316],{}," for vaults not yet migrated.",[1114,212736,212737,212738,212740],{},"Deprecate the old ",[941,212739,33316],{}," over a release window, documented in the provider's CHANGELOG.",[1114,212742,212743,212744,212746,212747,212749],{},"Migrate lazily: the first time a vault opens successfully under the new provider, reseal the secret and overwrite ",[941,212745,14380],{}," with a new-",[941,212748,33316],{}," envelope, dropping the old one.",[1114,212751,212752],{},"After the deprecation window, drop the old-provider fallback from your app entirely.",[937,212754,212755],{},"This is genuinely expensive — it means running two providers side by side in your app during the transition — and should be reserved for actual format problems, not cosmetic preference. Most \"I want to change the pid\" requests are better resolved by NOT changing it: the format is opaque dispatch metadata, not user-facing copy.",[1022,212757,34600],{"id":34599},[1111,212759,212760,212768,212784],{},[1114,212761,212762,212767],{},[1007,212763,212764,212766],{"href":170},[941,212765,16186],{}," — Sealing-key providers"," — the provider family this rule governs, and the managed-secret model it seals.",[1114,212769,212770,1169,212777,212780,212781,212783],{},[1007,212771,212774],{"href":212772,"rel":212773},"https:\u002F\u002Fgithub.com\u002Fnoy-db\u002Fcore\u002Fblob\u002Fmain\u002Fpackages\u002Fhub\u002Fsrc\u002Fwith-party\u002Fteam\u002Fmanaged-secret.ts",[3371],[941,212775,212776],{},"packages\u002Fhub\u002Fsrc\u002Fwith-party\u002Fteam\u002Fmanaged-secret.ts",[941,212778,212779],{},"SealedEnvelope"," type definition and the code that compares ",[941,212782,33316],{}," on unseal.",[1114,212785,212786,212788],{},[1007,212787,21],{"href":66}," — how the secret a sealing provider protects feeds into the rest of the key hierarchy.",[2645,212790,212791],{},"html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":1037,"searchDepth":1038,"depth":1038,"links":212793},[212794,212795,212796,212797,212799,212801],{"id":212345,"depth":1041,"text":212346},{"id":212419,"depth":1041,"text":212420},{"id":212552,"depth":1041,"text":212553},{"id":212687,"depth":1041,"text":212798},"Analogue: to-* adapter resource triples",{"id":212711,"depth":1041,"text":212800},"If you ever genuinely need to change pid format",{"id":34599,"depth":1041,"text":34600},"Reference rule: a sealing provider's pid string is semver-frozen from v1.x, and how hub detects a mismatch when a vault reopens under a different provider.",{},{"title":828,"description":212802},"the pid string, semver-frozen once a provider ships v1.x","MQQtBZOLVP37FyYvWedWRn-O550qqXIaY4-BZRA3Wvg",{"id":212808,"title":832,"access":12,"apiToc":929,"body":212809,"category":1042,"description":215457,"extension":1044,"meta":215458,"navigation":1042,"nest":1047,"path":833,"seo":215459,"sinceVersion":1042,"status":38550,"stem":834,"subtitle":215460,"tier":1050,"tocExternal":1042,"__hash__":215461},"docs\u002Fservices\u002Fsearch.md",{"type":931,"value":212810,"toc":215429},[212811,212814,212816,212818,212847,212849,212855,212858,212874,212897,212917,212934,212936,212977,212979,213004,213273,213278,213280,213287,213290,213486,213499,213508,213515,213539,213701,213706,213708,213714,213762,213769,213774,213834,213839,213896,213902,213953,213966,213970,214107,214111,214225,214236,214241,214334,214349,214354,214370,214386,214391,214407,214418,214420,214501,214505,214623,214632,214777,214783,214789,214800,214806,214837,214847,214854,214864,214955,214972,214979,214986,215019,215024,215120,215124,215217,215224,215226,215351,215353,215426],[934,212812,832],{"id":212813},"search-full-text",[46871,212815],{":rows":142686},[937,212817,142689],{},[7646,212819,212820],{"color":7648,"icon":369},[937,212821,212822,212824,212825,142700,212827,142703,212829,142707,212831,142723,212833,142727,212835,1952,212837,142707,212839,142710,212841,212843,212844,212846],{},[950,212823,142696],{}," Search and ",[1007,212826,673],{"href":674},[941,212828,22027],{},[950,212830,142706],{},[1092,212832,142722],{},[941,212834,142726],{},[941,212836,22035],{},[950,212838,142719],{},[1092,212840,82055],{},[941,212842,4452],{}," encoder, ",[941,212845,22038],{},", and hybrid lexical⊕semantic fusion. Opting in once enables both.",[1022,212848,6242],{"id":6241},[937,212850,212851,212852,212854],{},"Search moves keyword lookup — full-text search, typeahead, \"find records matching these words\" — out of your application code and into the database, without weakening ",[1007,212853,29021],{"href":42}," (noy-db's core guarantee: the store only ever holds ciphertext, and no server or storage backend can decrypt it): every match is computed client-side, after decryption, and nothing about the query or the matched terms is written to the store.",[937,212856,212857],{},"There are two ways to search by keyword, both covered on this page:",[1111,212859,212860,212867],{},[1114,212861,212862,212866],{},[950,212863,212864],{},[941,212865,22032],{}," — a zero-setup scan: decrypt the collection and rank matches with BM25 on every call. No index to build or keep warm; the right default for most collections.",[1114,212868,212869,212873],{},[950,212870,212871],{},[941,212872,135090],{}," — an indexed path: build an in-memory inverted index once (client-side, never written to the store by default), then serve ranked, multi-field hits with snippet extraction. Optionally persist the index as an encrypted blob so it survives across sessions.",[937,212875,212876,212878,212879,212881,212882,212884,212885,212887,212888,212890,212891,212893,212894,212896],{},[941,212877,135090],{}," also has a ",[941,212880,120875],{}," option for searching by ",[1092,212883,82055],{}," instead of exact words (",[941,212886,145516],{},") or combining both (",[941,212889,145519],{},") — those modes need an ",[941,212892,4452],{}," config and are documented on the ",[1007,212895,673],{"href":674}," page, which this page's lexical mode feeds into.",[7646,212898,212900],{"color":7648,"icon":212899},"i-lucide-search",[937,212901,212902,212905,212906,212909,212910,212913,212914,212916],{},[950,212903,212904],{},"Why lexical search still matters."," Even as AI-powered ",[1007,212907,212908],{"href":674},"semantic search"," spreads, keyword search stays essential — sometimes the ",[1092,212911,212912],{},"literal"," word is the point. Invoice numbers, product SKUs, people's names, error codes, legal terms: for these, \"close in meaning\" is wrong and an exact-token match is right. BM25 is also self-contained and cheap — no model, no encoder, no external call, deterministic ranking you can reason about, and it runs fully offline. That's why the strongest setups use ",[950,212915,95269],{}," retrieval: semantic search finds meaning, lexical search nails the exact tokens, and fusing the two beats either alone.",[937,212918,155374,212919,997,212921,212923,212924,953,212927,212929,212930,953,212932,957],{},[941,212920,142726],{},[941,212922,22035],{}," require ",[950,212925,212926],{},"eager mode",[941,212928,164144],{},", the default) — they read the fully-decrypted in-memory cache and throw in ",[1007,212931,25683],{"href":720},[941,212933,6836],{},[1022,212935,70093],{"id":70092},[1111,212937,212938,212945,212960,212967],{},[1114,212939,212940,6793,212942,212944],{},[950,212941,107493],{},[941,212943,142726],{}," — it scans whatever field you name at call time.",[1114,212946,212947,4724,212954,212956,212957,212959],{},[1092,212948,212949,212950,1261,212952,2311],{},"For indexed ",[941,212951,22035],{},[941,212953,22041],{},[941,212955,22775],{}," allowlist declared on the collection — calling ",[941,212958,22035],{}," without one throws.",[1114,212961,212962,212966],{},[1092,212963,110954,212964,2311],{},[941,212965,22041],{}," eager mode (the default); pre-building on a lazy collection throws.",[1114,212968,212969,65191,212972,212974,212975,1520],{},[1092,212970,212971],{},"For semantic \u002F hybrid modes:",[941,212973,4452],{}," encoder config — see ",[1007,212976,673],{"href":674},[1022,212978,99864],{"id":99863},[937,212980,212981,212982,212984,212985,212987,212988,968,212990,968,212992,968,212995,2462,212998,213001,213002,1520],{},"Search and retrieval are gated behind a ",[941,212983,22027],{}," capability you pass to ",[941,212986,2134],{},". Without it, ",[941,212989,22032],{},[941,212991,135090],{},[941,212993,212994],{},"collection.similarTo()",[941,212996,212997],{},"collection.warmIndex()",[941,212999,213000],{},"collection.flushIndex()"," all throw ",[941,213003,22160],{},[2217,213005,213007],{"className":2219,"code":213006,"language":2221,"meta":1037,"style":1037},"import { createNoydb, withSearch } from '@noy-db\u002Fhub'\n\nconst db = await createNoydb({\n  user: 'alice',\n  secret: 'alice-secret-2026',\n  searchStrategy: withSearch(),\n})\nconst vault = await db.openVault('books')\n\ninterface Invoice { id: string; title: string; statusLabel: string; notes: string; category: string; attachmentName: string }\n\n\u002F\u002F search() needs nothing else — it scans whatever field you name at call time.\n\u002F\u002F retrieve() additionally needs an explicit opt-in allowlist of fields to index:\nconst invoices = vault.collection\u003CInvoice>('invoices', {\n  textIndexes: ['title', 'statusLabel', 'attachmentName'],\n})\n",[941,213008,213009,213031,213035,213051,213065,213079,213091,213097,213123,213127,213186,213190,213195,213200,213232,213267],{"__ignoreMap":1037},[2225,213010,213011,213013,213015,213017,213019,213021,213023,213025,213027,213029],{"class":2227,"line":2228},[2225,213012,2232],{"class":2231},[2225,213014,2236],{"class":2235},[2225,213016,2744],{"class":2239},[2225,213018,2298],{"class":2235},[2225,213020,73949],{"class":2239},[2225,213022,2243],{"class":2235},[2225,213024,2246],{"class":2231},[2225,213026,2249],{"class":2235},[2225,213028,1086],{"class":2252},[2225,213030,2256],{"class":2235},[2225,213032,213033],{"class":2227,"line":1041},[2225,213034,2285],{"emptyLinePlaceholder":2284},[2225,213036,213037,213039,213041,213043,213045,213047,213049],{"class":2227,"line":1038},[2225,213038,2775],{"class":2774},[2225,213040,2778],{"class":2239},[2225,213042,2781],{"class":2235},[2225,213044,2784],{"class":2231},[2225,213046,2744],{"class":2291},[2225,213048,2789],{"class":2239},[2225,213050,4200],{"class":2235},[2225,213052,213053,213055,213057,213059,213061,213063],{"class":2227,"line":2288},[2225,213054,4205],{"class":2307},[2225,213056,2311],{"class":2235},[2225,213058,2249],{"class":2235},[2225,213060,6408],{"class":2252},[2225,213062,2805],{"class":2235},[2225,213064,1707],{"class":2235},[2225,213066,213067,213069,213071,213073,213075,213077],{"class":2227,"line":2304},[2225,213068,10423],{"class":2307},[2225,213070,2311],{"class":2235},[2225,213072,2249],{"class":2235},[2225,213074,107607],{"class":2252},[2225,213076,2805],{"class":2235},[2225,213078,1707],{"class":2235},[2225,213080,213081,213083,213085,213087,213089],{"class":2227,"line":2356},[2225,213082,74331],{"class":2307},[2225,213084,2311],{"class":2235},[2225,213086,73949],{"class":2291},[2225,213088,4228],{"class":2239},[2225,213090,1707],{"class":2235},[2225,213092,213093,213095],{"class":2227,"line":2830},[2225,213094,2359],{"class":2235},[2225,213096,2362],{"class":2239},[2225,213098,213099,213101,213103,213105,213107,213109,213111,213113,213115,213117,213119,213121],{"class":2227,"line":2861},[2225,213100,2775],{"class":2774},[2225,213102,2835],{"class":2239},[2225,213104,2781],{"class":2235},[2225,213106,2784],{"class":2231},[2225,213108,2842],{"class":2239},[2225,213110,1520],{"class":2235},[2225,213112,2847],{"class":2291},[2225,213114,2789],{"class":2239},[2225,213116,2805],{"class":2235},[2225,213118,65761],{"class":2252},[2225,213120,2805],{"class":2235},[2225,213122,2362],{"class":2239},[2225,213124,213125],{"class":2227,"line":4246},[2225,213126,2285],{"emptyLinePlaceholder":2284},[2225,213128,213129,213131,213133,213135,213137,213139,213141,213143,213145,213147,213149,213151,213154,213156,213158,213160,213162,213164,213166,213168,213171,213173,213175,213177,213180,213182,213184],{"class":2227,"line":4253},[2225,213130,6994],{"class":2774},[2225,213132,6343],{"class":6342},[2225,213134,2236],{"class":2235},[2225,213136,7070],{"class":2307},[2225,213138,2311],{"class":2235},[2225,213140,6366],{"class":6342},[2225,213142,3769],{"class":2235},[2225,213144,74169],{"class":2307},[2225,213146,2311],{"class":2235},[2225,213148,6366],{"class":6342},[2225,213150,3769],{"class":2235},[2225,213152,213153],{"class":2307}," statusLabel",[2225,213155,2311],{"class":2235},[2225,213157,6366],{"class":6342},[2225,213159,3769],{"class":2235},[2225,213161,42817],{"class":2307},[2225,213163,2311],{"class":2235},[2225,213165,6366],{"class":6342},[2225,213167,3769],{"class":2235},[2225,213169,213170],{"class":2307}," category",[2225,213172,2311],{"class":2235},[2225,213174,6366],{"class":6342},[2225,213176,3769],{"class":2235},[2225,213178,213179],{"class":2307}," attachmentName",[2225,213181,2311],{"class":2235},[2225,213183,6366],{"class":6342},[2225,213185,6369],{"class":2235},[2225,213187,213188],{"class":2227,"line":4259},[2225,213189,2285],{"emptyLinePlaceholder":2284},[2225,213191,213192],{"class":2227,"line":6548},[2225,213193,213194],{"class":2763},"\u002F\u002F search() needs nothing else — it scans whatever field you name at call time.\n",[2225,213196,213197],{"class":2227,"line":6576},[2225,213198,213199],{"class":2763},"\u002F\u002F retrieve() additionally needs an explicit opt-in allowlist of fields to index:\n",[2225,213201,213202,213204,213206,213208,213210,213212,213214,213216,213218,213220,213222,213224,213226,213228,213230],{"class":2227,"line":6582},[2225,213203,2775],{"class":2774},[2225,213205,6461],{"class":2239},[2225,213207,2781],{"class":2235},[2225,213209,6466],{"class":2239},[2225,213211,1520],{"class":2235},[2225,213213,6471],{"class":2291},[2225,213215,6474],{"class":2235},[2225,213217,6477],{"class":6342},[2225,213219,6480],{"class":2235},[2225,213221,2789],{"class":2239},[2225,213223,2805],{"class":2235},[2225,213225,6487],{"class":2252},[2225,213227,2805],{"class":2235},[2225,213229,2298],{"class":2235},[2225,213231,2301],{"class":2235},[2225,213233,213234,213236,213238,213240,213242,213244,213246,213248,213250,213252,213254,213256,213258,213261,213263,213265],{"class":2227,"line":6587},[2225,213235,94988],{"class":2307},[2225,213237,2311],{"class":2235},[2225,213239,8667],{"class":2239},[2225,213241,2805],{"class":2235},[2225,213243,87181],{"class":2252},[2225,213245,2805],{"class":2235},[2225,213247,2298],{"class":2235},[2225,213249,2249],{"class":2235},[2225,213251,59758],{"class":2252},[2225,213253,2805],{"class":2235},[2225,213255,2298],{"class":2235},[2225,213257,2249],{"class":2235},[2225,213259,213260],{"class":2252},"attachmentName",[2225,213262,2805],{"class":2235},[2225,213264,8803],{"class":2239},[2225,213266,1707],{"class":2235},[2225,213268,213269,213271],{"class":2227,"line":7174},[2225,213270,2359],{"class":2235},[2225,213272,2362],{"class":2239},[937,213274,213275,213277],{},[941,213276,22027],{}," is dynamically imported behind the scenes, so the search\u002Fretrieval engine stays out of your bundle until you opt in.",[1022,213279,29893],{"id":29892},[1591,213281,213283,213286],{"id":213282},"collectionsearchfield-query-opts-scan-mode",[941,213284,213285],{},"collection.search(field, query, opts)"," — scan mode",[937,213288,213289],{},"Ranks records by relevance against a tokenized query, scoring in memory with BM25 on every call.",[2217,213291,213293],{"className":2219,"code":213292,"language":2221,"meta":1037,"style":1037},"const hits = await invoices.search('title', 'overdue invoice')\n\u002F\u002F → [{ id, score, record }, …]  (best first, OR of query terms)\n\nawait invoices.search('title', 'overdue invoice', { match: 'all' })\n\u002F\u002F → same shape, AND of query terms — a record must contain both \"overdue\" and \"invoice\"\n\nawait invoices.search('title', 'mee', { prefix: true })\n\u002F\u002F → same shape — the last term (\"mee\") is matched as a prefix, e.g. \"meeting\"\n\nawait invoices.search('title', 'invoice', { limit: 10 })\n\u002F\u002F → same shape, capped to the top 10 by score\n",[941,213294,213295,213329,213334,213338,213382,213387,213391,213432,213437,213441,213481],{"__ignoreMap":1037},[2225,213296,213297,213299,213301,213303,213305,213307,213309,213311,213313,213315,213317,213319,213321,213323,213325,213327],{"class":2227,"line":2228},[2225,213298,2775],{"class":2774},[2225,213300,75579],{"class":2239},[2225,213302,2781],{"class":2235},[2225,213304,2784],{"class":2231},[2225,213306,6503],{"class":2239},[2225,213308,1520],{"class":2235},[2225,213310,4457],{"class":2291},[2225,213312,2789],{"class":2239},[2225,213314,2805],{"class":2235},[2225,213316,87181],{"class":2252},[2225,213318,2805],{"class":2235},[2225,213320,2298],{"class":2235},[2225,213322,2249],{"class":2235},[2225,213324,144174],{"class":2252},[2225,213326,2805],{"class":2235},[2225,213328,2362],{"class":2239},[2225,213330,213331],{"class":2227,"line":1041},[2225,213332,213333],{"class":2763},"\u002F\u002F → [{ id, score, record }, …]  (best first, OR of query terms)\n",[2225,213335,213336],{"class":2227,"line":1038},[2225,213337,2285],{"emptyLinePlaceholder":2284},[2225,213339,213340,213342,213344,213346,213348,213350,213352,213354,213356,213358,213360,213362,213364,213366,213368,213370,213372,213374,213376,213378,213380],{"class":2227,"line":2288},[2225,213341,6500],{"class":2231},[2225,213343,6503],{"class":2239},[2225,213345,1520],{"class":2235},[2225,213347,4457],{"class":2291},[2225,213349,2789],{"class":2239},[2225,213351,2805],{"class":2235},[2225,213353,87181],{"class":2252},[2225,213355,2805],{"class":2235},[2225,213357,2298],{"class":2235},[2225,213359,2249],{"class":2235},[2225,213361,144174],{"class":2252},[2225,213363,2805],{"class":2235},[2225,213365,2298],{"class":2235},[2225,213367,2236],{"class":2235},[2225,213369,66511],{"class":2307},[2225,213371,2311],{"class":2235},[2225,213373,2249],{"class":2235},[2225,213375,59366],{"class":2252},[2225,213377,2805],{"class":2235},[2225,213379,2243],{"class":2235},[2225,213381,2362],{"class":2239},[2225,213383,213384],{"class":2227,"line":2304},[2225,213385,213386],{"class":2763},"\u002F\u002F → same shape, AND of query terms — a record must contain both \"overdue\" and \"invoice\"\n",[2225,213388,213389],{"class":2227,"line":2356},[2225,213390,2285],{"emptyLinePlaceholder":2284},[2225,213392,213393,213395,213397,213399,213401,213403,213405,213407,213409,213411,213413,213416,213418,213420,213422,213424,213426,213428,213430],{"class":2227,"line":2830},[2225,213394,6500],{"class":2231},[2225,213396,6503],{"class":2239},[2225,213398,1520],{"class":2235},[2225,213400,4457],{"class":2291},[2225,213402,2789],{"class":2239},[2225,213404,2805],{"class":2235},[2225,213406,87181],{"class":2252},[2225,213408,2805],{"class":2235},[2225,213410,2298],{"class":2235},[2225,213412,2249],{"class":2235},[2225,213414,213415],{"class":2252},"mee",[2225,213417,2805],{"class":2235},[2225,213419,2298],{"class":2235},[2225,213421,2236],{"class":2235},[2225,213423,8638],{"class":2307},[2225,213425,2311],{"class":2235},[2225,213427,2322],{"class":2321},[2225,213429,2243],{"class":2235},[2225,213431,2362],{"class":2239},[2225,213433,213434],{"class":2227,"line":2861},[2225,213435,213436],{"class":2763},"\u002F\u002F → same shape — the last term (\"mee\") is matched as a prefix, e.g. \"meeting\"\n",[2225,213438,213439],{"class":2227,"line":4246},[2225,213440,2285],{"emptyLinePlaceholder":2284},[2225,213442,213443,213445,213447,213449,213451,213453,213455,213457,213459,213461,213463,213465,213467,213469,213471,213473,213475,213477,213479],{"class":2227,"line":4253},[2225,213444,6500],{"class":2231},[2225,213446,6503],{"class":2239},[2225,213448,1520],{"class":2235},[2225,213450,4457],{"class":2291},[2225,213452,2789],{"class":2239},[2225,213454,2805],{"class":2235},[2225,213456,87181],{"class":2252},[2225,213458,2805],{"class":2235},[2225,213460,2298],{"class":2235},[2225,213462,2249],{"class":2235},[2225,213464,27732],{"class":2252},[2225,213466,2805],{"class":2235},[2225,213468,2298],{"class":2235},[2225,213470,2236],{"class":2235},[2225,213472,37205],{"class":2307},[2225,213474,2311],{"class":2235},[2225,213476,45554],{"class":6527},[2225,213478,2243],{"class":2235},[2225,213480,2362],{"class":2239},[2225,213482,213483],{"class":2227,"line":4259},[2225,213484,213485],{"class":2763},"\u002F\u002F → same shape, capped to the top 10 by score\n",[937,213487,213488,213489,213492,213493,213495,213496,213498],{},"Scan mode decrypts the collection in memory and scores client-side, so ",[950,213490,213491],{},"nothing searchable is written to the store"," — it adds ",[1092,213494,11313],{}," leakage beyond what reading the collection already does. It's the safe default for sensitive fields (names, notes). Cost is O(n) per query. If the collection also carries large fields you never search, moving them to a ",[1007,213497,205178],{"href":813}," keeps them off this decrypt path.",[937,213500,213501,213502,213504,213505,13528],{},"Results are descending by score. Deleted or ",[1007,213503,174882],{"href":686}," records never appear. Full option and return-shape reference: see ",[1007,213506,102889],{"href":213507},"#api-reference",[1591,213509,213511,213514],{"id":213510},"collectionretrievequery-opts-lexical-index",[941,213512,213513],{},"collection.retrieve(query, opts)"," — lexical index",[937,213516,213517,213518,213521,213522,213525,213526,213529,213530,213533,213534,213536,213537,212959],{},"Builds an ",[950,213519,213520],{},"in-memory inverted index"," on first call (or on collection open, if ",[941,213523,213524],{},"warmIndexOnOpen: true","), then scores query terms with ",[950,213527,213528],{},"multi-field BM25"," and returns ranked hits with ",[950,213531,213532],{},"per-hit snippet extraction",". Requires the collection to declare ",[941,213535,22775],{}," (as configured above); calling ",[941,213538,22035],{},[2217,213540,213542],{"className":2219,"code":213541,"language":2221,"meta":1037,"style":1037},"\u002F\u002F Multi-field ranked search, over the `invoices` collection from Opt in:\nconst hits = await invoices.retrieve('overdue invoice')\n\u002F\u002F → RetrieveHit[]  (best first)\n\u002F\u002F   { id, score, rank, field, snippet, locale?, record? }\n\n\u002F\u002F Prefix autocomplete (typeahead):\nconst ac = await invoices.retrieve('over', { prefix: true, limit: 5 })\n\u002F\u002F → same shape, capped to the top 5 hits, last term (\"over\") matched as a prefix\n\n\u002F\u002F Include the full decrypted record:\nconst detail = await invoices.retrieve('overdue', { includeRecord: true })\n\u002F\u002F → same shape, each hit additionally carries `record: Invoice`\n",[941,213543,213544,213549,213575,213580,213585,213589,213594,213642,213647,213651,213656,213696],{"__ignoreMap":1037},[2225,213545,213546],{"class":2227,"line":2228},[2225,213547,213548],{"class":2763},"\u002F\u002F Multi-field ranked search, over the `invoices` collection from Opt in:\n",[2225,213550,213551,213553,213555,213557,213559,213561,213563,213565,213567,213569,213571,213573],{"class":2227,"line":1041},[2225,213552,2775],{"class":2774},[2225,213554,75579],{"class":2239},[2225,213556,2781],{"class":2235},[2225,213558,2784],{"class":2231},[2225,213560,6503],{"class":2239},[2225,213562,1520],{"class":2235},[2225,213564,75590],{"class":2291},[2225,213566,2789],{"class":2239},[2225,213568,2805],{"class":2235},[2225,213570,144174],{"class":2252},[2225,213572,2805],{"class":2235},[2225,213574,2362],{"class":2239},[2225,213576,213577],{"class":2227,"line":1038},[2225,213578,213579],{"class":2763},"\u002F\u002F → RetrieveHit[]  (best first)\n",[2225,213581,213582],{"class":2227,"line":2288},[2225,213583,213584],{"class":2763},"\u002F\u002F   { id, score, rank, field, snippet, locale?, record? }\n",[2225,213586,213587],{"class":2227,"line":2304},[2225,213588,2285],{"emptyLinePlaceholder":2284},[2225,213590,213591],{"class":2227,"line":2356},[2225,213592,213593],{"class":2763},"\u002F\u002F Prefix autocomplete (typeahead):\n",[2225,213595,213596,213598,213601,213603,213605,213607,213609,213611,213613,213615,213618,213620,213622,213624,213626,213628,213630,213632,213634,213636,213638,213640],{"class":2227,"line":2830},[2225,213597,2775],{"class":2774},[2225,213599,213600],{"class":2239}," ac ",[2225,213602,2781],{"class":2235},[2225,213604,2784],{"class":2231},[2225,213606,6503],{"class":2239},[2225,213608,1520],{"class":2235},[2225,213610,75590],{"class":2291},[2225,213612,2789],{"class":2239},[2225,213614,2805],{"class":2235},[2225,213616,213617],{"class":2252},"over",[2225,213619,2805],{"class":2235},[2225,213621,2298],{"class":2235},[2225,213623,2236],{"class":2235},[2225,213625,8638],{"class":2307},[2225,213627,2311],{"class":2235},[2225,213629,2322],{"class":2321},[2225,213631,2298],{"class":2235},[2225,213633,37205],{"class":2307},[2225,213635,2311],{"class":2235},[2225,213637,18996],{"class":6527},[2225,213639,2243],{"class":2235},[2225,213641,2362],{"class":2239},[2225,213643,213644],{"class":2227,"line":2861},[2225,213645,213646],{"class":2763},"\u002F\u002F → same shape, capped to the top 5 hits, last term (\"over\") matched as a prefix\n",[2225,213648,213649],{"class":2227,"line":4246},[2225,213650,2285],{"emptyLinePlaceholder":2284},[2225,213652,213653],{"class":2227,"line":4253},[2225,213654,213655],{"class":2763},"\u002F\u002F Include the full decrypted record:\n",[2225,213657,213658,213660,213663,213665,213667,213669,213671,213673,213675,213677,213679,213681,213683,213685,213688,213690,213692,213694],{"class":2227,"line":4259},[2225,213659,2775],{"class":2774},[2225,213661,213662],{"class":2239}," detail ",[2225,213664,2781],{"class":2235},[2225,213666,2784],{"class":2231},[2225,213668,6503],{"class":2239},[2225,213670,1520],{"class":2235},[2225,213672,75590],{"class":2291},[2225,213674,2789],{"class":2239},[2225,213676,2805],{"class":2235},[2225,213678,28454],{"class":2252},[2225,213680,2805],{"class":2235},[2225,213682,2298],{"class":2235},[2225,213684,2236],{"class":2235},[2225,213686,213687],{"class":2307}," includeRecord",[2225,213689,2311],{"class":2235},[2225,213691,2322],{"class":2321},[2225,213693,2243],{"class":2235},[2225,213695,2362],{"class":2239},[2225,213697,213698],{"class":2227,"line":6548},[2225,213699,213700],{"class":2763},"\u002F\u002F → same shape, each hit additionally carries `record: Invoice`\n",[937,213702,213703,213704,13528],{},"Full option and return-shape reference: see ",[1007,213705,102889],{"href":213507},[1022,213707,102889],{"id":102888},[1591,213709,213711],{"id":213710},"collectionsearchfield-query-opts",[941,213712,213713],{},"collection.search(field, query, opts?)",[2217,213715,213717],{"className":2219,"code":213716,"language":2221,"meta":1037,"style":1037},"search\u003CT>(field: string, query: string, opts?: SearchOptions): Promise\u003CSearchResult\u003CT>[]>\n",[941,213718,213719],{"__ignoreMap":1037},[2225,213720,213721,213723,213725,213727,213729,213731,213733,213736,213738,213740,213742,213745,213747,213749,213752,213754,213756,213758,213760],{"class":2227,"line":2228},[2225,213722,4457],{"class":2291},[2225,213724,6474],{"class":2235},[2225,213726,11968],{"class":6342},[2225,213728,6480],{"class":2235},[2225,213730,25176],{"class":2239},[2225,213732,2298],{"class":2235},[2225,213734,213735],{"class":2239}," query: string",[2225,213737,2298],{"class":2235},[2225,213739,8232],{"class":2239},[2225,213741,7011],{"class":2235},[2225,213743,213744],{"class":2239}," SearchOptions): ",[2225,213746,11740],{"class":6342},[2225,213748,6474],{"class":2235},[2225,213750,213751],{"class":2239},"SearchResult",[2225,213753,6474],{"class":2235},[2225,213755,11968],{"class":2239},[2225,213757,6480],{"class":2235},[2225,213759,7262],{"class":2239},[2225,213761,7093],{"class":2235},[937,213763,213764,213765,213768],{},"Scan-mode BM25 search over one named field, decrypting and scoring in memory on every call — see ",[1007,213766,29893],{"href":213767},"#usage"," above.",[937,213770,213771,2311],{},[941,213772,213773],{},"SearchOptions",[1338,213775,213776,213786],{},[1341,213777,213778],{},[1344,213779,213780,213782,213784],{},[1347,213781,101060],{},[1347,213783,20197],{},[1347,213785,20200],{},[1357,213787,213788,213806,213823],{},[1344,213789,213790,213794,213798],{},[1362,213791,213792],{},[941,213793,7482],{},[1362,213795,213796],{},[941,213797,163117],{},[1362,213799,213800,213802,213803,213805],{},[941,213801,163117],{}," = OR of query terms; ",[941,213804,163113],{}," = every term must match",[1344,213807,213808,213812,213816],{},[1362,213809,213810],{},[941,213811,8710],{},[1362,213813,213814],{},[941,213815,2560],{},[1362,213817,213818,213819,213822],{},"Treat the ",[950,213820,213821],{},"last"," query term as a prefix (typeahead)",[1344,213824,213825,213829,213831],{},[1362,213826,213827],{},[941,213828,3022],{},[1362,213830,3610],{},[1362,213832,213833],{},"Return only the top-N by score",[937,213835,213836,2311],{},[941,213837,213838],{},"SearchResult\u003CT>",[2217,213840,213842],{"className":2219,"code":213841,"language":2221,"meta":1037,"style":1037},"interface SearchResult\u003CT> {\n  readonly id: string\n  readonly score: number   \u002F\u002F BM25, descending\n  readonly record: T\n}\n",[941,213843,213844,213859,213869,213882,213892],{"__ignoreMap":1037},[2225,213845,213846,213848,213851,213853,213855,213857],{"class":2227,"line":2228},[2225,213847,6994],{"class":2774},[2225,213849,213850],{"class":6342}," SearchResult",[2225,213852,6474],{"class":2235},[2225,213854,11968],{"class":6342},[2225,213856,6480],{"class":2235},[2225,213858,2301],{"class":2235},[2225,213860,213861,213863,213865,213867],{"class":2227,"line":1041},[2225,213862,53580],{"class":2774},[2225,213864,7070],{"class":2307},[2225,213866,2311],{"class":2235},[2225,213868,7014],{"class":6342},[2225,213870,213871,213873,213875,213877,213879],{"class":2227,"line":1038},[2225,213872,53580],{"class":2774},[2225,213874,144714],{"class":2307},[2225,213876,2311],{"class":2235},[2225,213878,6356],{"class":6342},[2225,213880,213881],{"class":2763},"   \u002F\u002F BM25, descending\n",[2225,213883,213884,213886,213888,213890],{"class":2227,"line":2288},[2225,213885,53580],{"class":2774},[2225,213887,24268],{"class":2307},[2225,213889,2311],{"class":2235},[2225,213891,124401],{"class":6342},[2225,213893,213894],{"class":2227,"line":2304},[2225,213895,7351],{"class":2235},[1591,213897,213899],{"id":213898},"collectionretrievequery-opts",[941,213900,213901],{},"collection.retrieve(query, opts?)",[2217,213903,213905],{"className":2219,"code":213904,"language":2221,"meta":1037,"style":1037},"retrieve\u003CT>(query: string, opts?: RetrieveOptions\u003CT>): Promise\u003CRetrieveHit\u003CT>[]>\n",[941,213906,213907],{"__ignoreMap":1037},[2225,213908,213909,213911,213913,213915,213917,213920,213922,213924,213926,213929,213931,213933,213935,213937,213939,213941,213943,213945,213947,213949,213951],{"class":2227,"line":2228},[2225,213910,75590],{"class":2291},[2225,213912,6474],{"class":2235},[2225,213914,11968],{"class":6342},[2225,213916,6480],{"class":2235},[2225,213918,213919],{"class":2239},"(query: string",[2225,213921,2298],{"class":2235},[2225,213923,8232],{"class":2239},[2225,213925,7011],{"class":2235},[2225,213927,213928],{"class":2239}," RetrieveOptions",[2225,213930,6474],{"class":2235},[2225,213932,11968],{"class":2239},[2225,213934,6480],{"class":2235},[2225,213936,11737],{"class":2239},[2225,213938,11740],{"class":6342},[2225,213940,6474],{"class":2235},[2225,213942,145772],{"class":2239},[2225,213944,6474],{"class":2235},[2225,213946,11968],{"class":2239},[2225,213948,6480],{"class":2235},[2225,213950,7262],{"class":2239},[2225,213952,7093],{"class":2235},[937,213954,213955,213956,27678,213958,213960,213961,213963,213964,957],{},"Indexed, multi-field BM25 retrieval with snippet extraction, over the fields declared in ",[941,213957,22775],{},[1007,213959,29893],{"href":213767}," above. Throws if the collection has no ",[941,213962,22775],{}," configured, and throws in lazy mode (",[941,213965,6836],{},[937,213967,213968,2311],{},[941,213969,146056],{},[1338,213971,213972,213982],{},[1341,213973,213974],{},[1344,213975,213976,213978,213980],{},[1347,213977,101060],{},[1347,213979,20197],{},[1347,213981,20200],{},[1357,213983,213984,214005,214016,214034,214047,214061,214075,214088],{},[1344,213985,213986,213990,213994],{},[1362,213987,213988],{},[941,213989,120875],{},[1362,213991,213992],{},[941,213993,145513],{},[1362,213995,213996,213998,213999,4765,214001,27678,214003],{},[941,213997,145513],{}," (this page's BM25 index), ",[941,214000,145516],{},[941,214002,145519],{},[1007,214004,673],{"href":674},[1344,214006,214007,214011,214013],{},[1362,214008,214009],{},[941,214010,3022],{},[1362,214012,3610],{},[1362,214014,214015],{},"Return top-N hits only",[1344,214017,214018,214022,214026],{},[1362,214019,214020],{},[941,214021,7482],{},[1362,214023,214024],{},[941,214025,163117],{},[1362,214027,214028,214030,214031,214033],{},[941,214029,163117],{}," = OR, ",[941,214032,163113],{}," = AND of query terms",[1344,214035,214036,214040,214044],{},[1362,214037,214038],{},[941,214039,8710],{},[1362,214041,214042],{},[941,214043,2560],{},[1362,214045,214046],{},"Last query term is a prefix (typeahead \u002F autocomplete)",[1344,214048,214049,214054,214058],{},[1362,214050,214051],{},[941,214052,214053],{},"snippetWindow",[1362,214055,214056],{},[941,214057,93526],{},[1362,214059,214060],{},"Half-window char count around the best match",[1344,214062,214063,214067,214072],{},[1362,214064,214065],{},[941,214066,57285],{},[1362,214068,214069,214070],{},"all ",[941,214071,22775],{},[1362,214073,214074],{},"Restrict search to a subset of indexed fields",[1344,214076,214077,214081,214085],{},[1362,214078,214079],{},[941,214080,145039],{},[1362,214082,214083],{},[941,214084,2560],{},[1362,214086,214087],{},"Attach the full decrypted record to each hit",[1344,214089,214090,214094,214096],{},[1362,214091,214092],{},[941,214093,111736],{},[1362,214095,3610],{},[1362,214097,3289,214098,17904,214100,214103,214104],{},[941,214099,24785],{},[941,214101,214102],{},"collection.query().where(…)"," — intersects hits with a predicate; see ",[1007,214105,673],{"href":214106},"\u002Fservices\u002Fembeddings#within-retrieve-intersected-with-a-structured-query",[937,214108,214109,2311],{},[941,214110,144677],{},[2217,214112,214114],{"className":2219,"code":214113,"language":2221,"meta":1037,"style":1037},"interface RetrieveHit\u003CT> {\n  readonly id: string       \u002F\u002F record key\n  readonly score: number    \u002F\u002F BM25, descending\n  readonly rank: number     \u002F\u002F 1-based position, monotonic with score order\n  readonly field: string    \u002F\u002F winning field (highest per-field BM25)\n  readonly snippet: string  \u002F\u002F ±window chars around the best match\n  readonly locale?: string  \u002F\u002F set for i18n-text and dictionary-key hits\n  readonly record?: T       \u002F\u002F only when includeRecord: true\n}\n",[941,214115,214116,214130,214143,214156,214169,214182,214195,214208,214221],{"__ignoreMap":1037},[2225,214117,214118,214120,214122,214124,214126,214128],{"class":2227,"line":2228},[2225,214119,6994],{"class":2774},[2225,214121,144689],{"class":6342},[2225,214123,6474],{"class":2235},[2225,214125,11968],{"class":6342},[2225,214127,6480],{"class":2235},[2225,214129,2301],{"class":2235},[2225,214131,214132,214134,214136,214138,214140],{"class":2227,"line":1041},[2225,214133,53580],{"class":2774},[2225,214135,7070],{"class":2307},[2225,214137,2311],{"class":2235},[2225,214139,6366],{"class":6342},[2225,214141,214142],{"class":2763},"       \u002F\u002F record key\n",[2225,214144,214145,214147,214149,214151,214153],{"class":2227,"line":1038},[2225,214146,53580],{"class":2774},[2225,214148,144714],{"class":2307},[2225,214150,2311],{"class":2235},[2225,214152,6356],{"class":6342},[2225,214154,214155],{"class":2763},"    \u002F\u002F BM25, descending\n",[2225,214157,214158,214160,214162,214164,214166],{"class":2227,"line":2288},[2225,214159,53580],{"class":2774},[2225,214161,144728],{"class":2307},[2225,214163,2311],{"class":2235},[2225,214165,6356],{"class":6342},[2225,214167,214168],{"class":2763},"     \u002F\u002F 1-based position, monotonic with score order\n",[2225,214170,214171,214173,214175,214177,214179],{"class":2227,"line":2304},[2225,214172,53580],{"class":2774},[2225,214174,124557],{"class":2307},[2225,214176,2311],{"class":2235},[2225,214178,6366],{"class":6342},[2225,214180,214181],{"class":2763},"    \u002F\u002F winning field (highest per-field BM25)\n",[2225,214183,214184,214186,214188,214190,214192],{"class":2227,"line":2356},[2225,214185,53580],{"class":2774},[2225,214187,144755],{"class":2307},[2225,214189,2311],{"class":2235},[2225,214191,6366],{"class":6342},[2225,214193,214194],{"class":2763},"  \u002F\u002F ±window chars around the best match\n",[2225,214196,214197,214199,214201,214203,214205],{"class":2227,"line":2830},[2225,214198,53580],{"class":2774},[2225,214200,24003],{"class":2307},[2225,214202,7011],{"class":2235},[2225,214204,6366],{"class":6342},[2225,214206,214207],{"class":2763},"  \u002F\u002F set for i18n-text and dictionary-key hits\n",[2225,214209,214210,214212,214214,214216,214218],{"class":2227,"line":2861},[2225,214211,53580],{"class":2774},[2225,214213,24268],{"class":2307},[2225,214215,7011],{"class":2235},[2225,214217,24772],{"class":6342},[2225,214219,214220],{"class":2763},"       \u002F\u002F only when includeRecord: true\n",[2225,214222,214223],{"class":2227,"line":4246},[2225,214224,7351],{"class":2235},[937,214226,214227,214229,214230,214235],{},[941,214228,144243],{}," matters beyond display order: it's what ",[1007,214231,214233],{"href":214232},"\u002Fservices\u002Fembeddings#fuseretrievallists-opts-the-fusion-primitive-standalone",[941,214234,145887],{}," uses to combine ranked lists (from different retrieval modes, or from different vaults) without comparing raw scores across lists that aren't on the same scale.",[1591,214237,214239],{"id":214238},"textindexes",[941,214240,22775],{},[2217,214242,214244],{"className":2219,"code":214243,"language":2221,"meta":1037,"style":1037},"vault.collection\u003CInvoice>('invoices', {\n  textIndexes: ['title', 'notes', 'category', 'attachmentName'],\n  warmIndexOnOpen: true,   \u002F\u002F auto-build on collection open\n})\n",[941,214245,214246,214272,214314,214328],{"__ignoreMap":1037},[2225,214247,214248,214250,214252,214254,214256,214258,214260,214262,214264,214266,214268,214270],{"class":2227,"line":2228},[2225,214249,2694],{"class":2239},[2225,214251,1520],{"class":2235},[2225,214253,6471],{"class":2291},[2225,214255,6474],{"class":2235},[2225,214257,6477],{"class":6342},[2225,214259,6480],{"class":2235},[2225,214261,2789],{"class":2239},[2225,214263,2805],{"class":2235},[2225,214265,6487],{"class":2252},[2225,214267,2805],{"class":2235},[2225,214269,2298],{"class":2235},[2225,214271,2301],{"class":2235},[2225,214273,214274,214276,214278,214280,214282,214284,214286,214288,214290,214292,214294,214296,214298,214300,214302,214304,214306,214308,214310,214312],{"class":2227,"line":1041},[2225,214275,94988],{"class":2307},[2225,214277,2311],{"class":2235},[2225,214279,8667],{"class":2239},[2225,214281,2805],{"class":2235},[2225,214283,87181],{"class":2252},[2225,214285,2805],{"class":2235},[2225,214287,2298],{"class":2235},[2225,214289,2249],{"class":2235},[2225,214291,2854],{"class":2252},[2225,214293,2805],{"class":2235},[2225,214295,2298],{"class":2235},[2225,214297,2249],{"class":2235},[2225,214299,169528],{"class":2252},[2225,214301,2805],{"class":2235},[2225,214303,2298],{"class":2235},[2225,214305,2249],{"class":2235},[2225,214307,213260],{"class":2252},[2225,214309,2805],{"class":2235},[2225,214311,8803],{"class":2239},[2225,214313,1707],{"class":2235},[2225,214315,214316,214319,214321,214323,214325],{"class":2227,"line":1038},[2225,214317,214318],{"class":2307},"  warmIndexOnOpen",[2225,214320,2311],{"class":2235},[2225,214322,2322],{"class":2321},[2225,214324,2298],{"class":2235},[2225,214326,214327],{"class":2763},"   \u002F\u002F auto-build on collection open\n",[2225,214329,214330,214332],{"class":2227,"line":2288},[2225,214331,2359],{"class":2235},[2225,214333,2362],{"class":2239},[937,214335,5093,214336,214339,214340,214342,214343,214345,214346,214348],{},[950,214337,214338],{},"opt-in allowlist"," of field paths (supports nested paths and ",[941,214341,7262],{},"-wildcard arrays). Only fields in this list enter the index — collections with no ",[941,214344,22775],{}," pay zero overhead, and ",[941,214347,22035],{}," throws without one.",[1591,214350,214352],{"id":214351},"collectionwarmindex",[941,214353,212997],{},[2217,214355,214357],{"className":2219,"code":214356,"language":2221,"meta":1037,"style":1037},"warmIndex(): Promise\u003Cvoid>\n",[941,214358,214359],{"__ignoreMap":1037},[2225,214360,214361,214364,214366,214368],{"class":2227,"line":2228},[2225,214362,214363],{"class":2291},"warmIndex",[2225,214365,12809],{"class":2239},[2225,214367,11740],{"class":6342},[2225,214369,13016],{"class":2235},[937,214371,214372,214373,22630,214375,214383,214384,1520],{},"Pre-builds the lexical index instead of paying the build cost on the first ",[941,214374,22035],{},[1007,214376,214378,997,214380],{"href":214377},"#warmindex-and-warmindexonopen",[941,214379,22041],{},[941,214381,214382],{},"warmIndexOnOpen"," below for its exact behavior in lazy mode and with no ",[941,214385,22775],{},[1591,214387,214389],{"id":214388},"collectionflushindex",[941,214390,213000],{},[2217,214392,214394],{"className":2219,"code":214393,"language":2221,"meta":1037,"style":1037},"flushIndex(): Promise\u003Cvoid>\n",[941,214395,214396],{"__ignoreMap":1037},[2225,214397,214398,214401,214403,214405],{"class":2227,"line":2228},[2225,214399,214400],{"class":2291},"flushIndex",[2225,214402,12809],{"class":2239},[2225,214404,11740],{"class":6342},[2225,214406,13016],{"class":2235},[937,214408,214409,214410,214413,214414,13528],{},"Force-persists the in-memory index immediately. Only meaningful with ",[941,214411,214412],{},"textIndexPersist: true"," — a no-op otherwise. See ",[1007,214415,214417],{"href":214416},"#session-local-by-default-or-persisted-with-textindexpersist","persistence",[1591,214419,126393],{"id":126392},[1111,214421,214422,214433,214464,214477],{},[1114,214423,214424,1169,214429,214432],{},[950,214425,214426],{},[941,214427,214428],{},"SearchEntry\u003CT>",[941,214430,214431],{},"{ id, record }"," input-entry shape scan mode builds internally from the eager cache before scoring; not something you construct yourself.",[1114,214434,214435,214440,214441,214443,214444,968,214446,968,214448,968,214450,968,214452,968,214454,214456,214457,214459,214460,10313,214462,1520],{},[950,214436,214437],{},[941,214438,214439],{},"SearchStrategy"," — the interface ",[941,214442,22027],{}," implements (",[941,214445,4457],{},[941,214447,75590],{},[941,214449,144133],{},[941,214451,214363],{},[941,214453,214400],{},[941,214455,146446],{},"). Advanced\u002Finternal: most code never touches it directly, it just passes ",[941,214458,22027],{}," as ",[941,214461,72946],{},[941,214463,2134],{},[1114,214465,214466,214471,214472,214474,214475,1520],{},[950,214467,214468],{},[941,214469,214470],{},"NO_SEARCH"," — the disabled-sentinel ",[941,214473,214439],{}," every collection uses until you opt in; every method on it throws ",[941,214476,22160],{},[1114,214478,214479,214484,214485,214487,214488,214491,214492,214494,214495,1746,214498,1520],{},[950,214480,214481],{},[941,214482,214483],{},"tokenize(text)"," — the standalone scan-mode tokenizer ",[941,214486,142726],{}," uses internally (NFKC-normalize, lowercase, split on Unicode letter\u002Fnumber runs — no ",[941,214489,214490],{},"Intl.Segmenter",", so it does ",[950,214493,1004],{}," word-segment Thai\u002FCJK): ",[941,214496,214497],{},"tokenize('Hello World')",[941,214499,214500],{},"['hello', 'world']",[1022,214502,214504],{"id":214503},"field-type-matrix","Field-type matrix",[1338,214506,214507,214521],{},[1341,214508,214509],{},[1344,214510,214511,214514,214519],{},[1347,214512,214513],{},"Field type",[1347,214515,214516,214517,35940],{},"Indexed by ",[941,214518,22035],{},[1347,214520,58068],{},[1357,214522,214523,214534,214551,214566,214585,214606],{},[1344,214524,214525,214529,214531],{},[1362,214526,214527],{},[941,214528,7259],{},[1362,214530,55428],{},[1362,214532,214533],{},"Tokenized directly",[1344,214535,214536,214543,214545],{},[1362,214537,214538,953,214540,1381],{},[941,214539,20943],{},[941,214541,214542],{},"{[locale]:string}",[1362,214544,55428],{},[1362,214546,214547,214548,214550],{},"All locale values indexed; hit carries the matched ",[941,214549,23074],{},"; search is locale-agnostic",[1344,214552,214553,214557,214560],{},[1362,214554,214555,95892],{},[941,214556,21456],{},[1362,214558,214559],{},"Yes, opt-in",[1362,214561,214562,214563,214565],{},"Resolved labels (all locales) from the dictionary; the opaque key itself is not text-indexed — use ",[941,214564,65223],{}," for key equality",[1344,214567,214568,214573,214576],{},[1362,214569,214570,214571],{},"Blob field — ",[941,214572,79137],{},[1362,214574,214575],{},"Yes (heaviest, indexed last)",[1362,214577,214578,214579,953,214581,214584],{},"Slot metadata via the blob facade's ",[941,214580,24407],{},[941,214582,214583],{},"blob(recordId).list()","), one async lookup per record against the separate blob collection; file bytes are never tokenized",[1344,214586,214587,214597,214600],{},[1362,214588,214589,1261,214591,1261,214593,1261,214595],{},[941,214590,760],{},[941,214592,20636],{},[941,214594,26305],{},[941,214596,16861],{},[1362,214598,214599],{},"No, by design",[1362,214601,116274,214602,214605],{},[941,214603,214604],{},"where('amount', '>', 1000)"," — text-formatting variance makes full-text matching wrong for these",[1344,214607,214608,214614,214617],{},[1362,214609,214610,214611,214613],{},"Blob ",[950,214612,76735],{}," (PDF \u002F image bytes)",[1362,214615,214616],{},"No, out of scope",[1362,214618,214619,214620,214622],{},"Extract text into a ",[941,214621,7259],{}," field yourself (OCR \u002F PDF→text) — that field then indexes normally",[937,214624,214625],{},[950,214626,214627,214628,997,214630,2311],{},"Combining ",[941,214629,22035],{},[941,214631,65223],{},[2217,214633,214635],{"className":2219,"code":214634,"language":2221,"meta":1037,"style":1037},"const textHits = new Set((await invoices.retrieve('overdue')).map((h) => h.id))\nconst rows = invoices.query().where('amount', '>', 1000).run()\nconst combined = rows.filter((r) => textHits.has(r.id))\n",[941,214636,214637,214690,214738],{"__ignoreMap":1037},[2225,214638,214639,214641,214644,214646,214648,214650,214652,214654,214656,214658,214660,214662,214664,214666,214668,214670,214672,214674,214676,214678,214680,214682,214684,214686,214688],{"class":2227,"line":2228},[2225,214640,2775],{"class":2774},[2225,214642,214643],{"class":2239}," textHits ",[2225,214645,2781],{"class":2235},[2225,214647,8130],{"class":2235},[2225,214649,145097],{"class":2291},[2225,214651,91688],{"class":2239},[2225,214653,6500],{"class":2231},[2225,214655,6503],{"class":2239},[2225,214657,1520],{"class":2235},[2225,214659,75590],{"class":2291},[2225,214661,2789],{"class":2239},[2225,214663,2805],{"class":2235},[2225,214665,28454],{"class":2252},[2225,214667,2805],{"class":2235},[2225,214669,8454],{"class":2239},[2225,214671,1520],{"class":2235},[2225,214673,8244],{"class":2291},[2225,214675,2789],{"class":2239},[2225,214677,2789],{"class":2235},[2225,214679,75634],{"class":7052},[2225,214681,1381],{"class":2235},[2225,214683,8116],{"class":2774},[2225,214685,75641],{"class":2239},[2225,214687,1520],{"class":2235},[2225,214689,145168],{"class":2239},[2225,214691,214692,214694,214696,214698,214700,214702,214704,214706,214708,214710,214712,214714,214716,214718,214720,214722,214724,214726,214728,214730,214732,214734,214736],{"class":2227,"line":1041},[2225,214693,2775],{"class":2774},[2225,214695,42003],{"class":2239},[2225,214697,2781],{"class":2235},[2225,214699,6503],{"class":2239},[2225,214701,1520],{"class":2235},[2225,214703,5286],{"class":2291},[2225,214705,4228],{"class":2239},[2225,214707,1520],{"class":2235},[2225,214709,3016],{"class":2291},[2225,214711,2789],{"class":2239},[2225,214713,2805],{"class":2235},[2225,214715,6617],{"class":2252},[2225,214717,2805],{"class":2235},[2225,214719,2298],{"class":2235},[2225,214721,2249],{"class":2235},[2225,214723,6480],{"class":2252},[2225,214725,2805],{"class":2235},[2225,214727,2298],{"class":2235},[2225,214729,26290],{"class":6527},[2225,214731,1381],{"class":2239},[2225,214733,1520],{"class":2235},[2225,214735,61526],{"class":2291},[2225,214737,8146],{"class":2239},[2225,214739,214740,214742,214745,214747,214750,214752,214754,214756,214758,214760,214762,214764,214767,214769,214771,214773,214775],{"class":2227,"line":1038},[2225,214741,2775],{"class":2774},[2225,214743,214744],{"class":2239}," combined ",[2225,214746,2781],{"class":2235},[2225,214748,214749],{"class":2239}," rows",[2225,214751,1520],{"class":2235},[2225,214753,8686],{"class":2291},[2225,214755,2789],{"class":2239},[2225,214757,2789],{"class":2235},[2225,214759,13217],{"class":7052},[2225,214761,1381],{"class":2235},[2225,214763,8116],{"class":2774},[2225,214765,214766],{"class":2239}," textHits",[2225,214768,1520],{"class":2235},[2225,214770,145202],{"class":2291},[2225,214772,28008],{"class":2239},[2225,214774,1520],{"class":2235},[2225,214776,145168],{"class":2239},[937,214778,2789,214779,214782],{},[941,214780,214781],{},"RetrieveOptions.within"," does the same intersection in one call — see the table above.)",[1022,214784,214786,214787],{"id":214785},"i18n-tokenizer-intlsegmenter","i18n tokenizer — ",[941,214788,214490],{},[937,214790,214791,214792,953,214796,214799],{},"The lexical index's tokenizer uses ",[950,214793,214794],{},[941,214795,214490],{},[941,214797,214798],{},"granularity: 'word'","), then NFKC-normalizes, lowercases, and keeps only word-like segments. This correctly segments Thai, CJK, and other scripts written without inter-word spaces — a run of Thai text is split into individual words instead of collapsing into one token.",[937,214801,214802,214803,112881],{},"A custom tokenizer is pluggable via the ",[941,214804,214805],{},"Tokenizer",[2217,214807,214809],{"className":2219,"code":214808,"language":2221,"meta":1037,"style":1037},"import type { Tokenizer } from '@noy-db\u002Fhub'\n\u002F\u002F Tokenizer = (text: string) => string[]\n",[941,214810,214811,214832],{"__ignoreMap":1037},[2225,214812,214813,214815,214817,214819,214822,214824,214826,214828,214830],{"class":2227,"line":2228},[2225,214814,2232],{"class":2231},[2225,214816,3399],{"class":2231},[2225,214818,2236],{"class":2235},[2225,214820,214821],{"class":2239}," Tokenizer",[2225,214823,2243],{"class":2235},[2225,214825,2246],{"class":2231},[2225,214827,2249],{"class":2235},[2225,214829,1086],{"class":2252},[2225,214831,2256],{"class":2235},[2225,214833,214834],{"class":2227,"line":1041},[2225,214835,214836],{"class":2763},"\u002F\u002F Tokenizer = (text: string) => string[]\n",[937,214838,214839,214841,214842,214844,214845,1520],{},[941,214840,142726],{},"'s scan-mode tokenizer is simpler — NFKC-normalize, lowercase, split on Unicode letter\u002Fnumber runs, no ",[941,214843,214490],{}," — so for Thai\u002FCJK fields prefer ",[941,214846,22035],{},[1022,214848,214850,997,214852],{"id":214849},"warmindex-and-warmindexonopen",[941,214851,22041],{},[941,214853,214382],{},[937,214855,214856,214857,214860,214861,214863],{},"The index is ",[950,214858,214859],{},"lazy by default"," — built on the first ",[941,214862,22035],{}," call. To pre-build it:",[2217,214865,214867],{"className":2219,"code":214866,"language":2221,"meta":1037,"style":1037},"\u002F\u002F On-demand pre-build:\nawait invoices.warmIndex()\n\n\u002F\u002F Auto-build on collection open (set in collection options):\nvault.collection\u003CInvoice>('invoices', {\n  textIndexes: ['title'],\n  warmIndexOnOpen: true,\n})\n",[941,214868,214869,214874,214886,214890,214895,214921,214939,214949],{"__ignoreMap":1037},[2225,214870,214871],{"class":2227,"line":2228},[2225,214872,214873],{"class":2763},"\u002F\u002F On-demand pre-build:\n",[2225,214875,214876,214878,214880,214882,214884],{"class":2227,"line":1041},[2225,214877,6500],{"class":2231},[2225,214879,6503],{"class":2239},[2225,214881,1520],{"class":2235},[2225,214883,214363],{"class":2291},[2225,214885,8146],{"class":2239},[2225,214887,214888],{"class":2227,"line":1038},[2225,214889,2285],{"emptyLinePlaceholder":2284},[2225,214891,214892],{"class":2227,"line":2288},[2225,214893,214894],{"class":2763},"\u002F\u002F Auto-build on collection open (set in collection options):\n",[2225,214896,214897,214899,214901,214903,214905,214907,214909,214911,214913,214915,214917,214919],{"class":2227,"line":2304},[2225,214898,2694],{"class":2239},[2225,214900,1520],{"class":2235},[2225,214902,6471],{"class":2291},[2225,214904,6474],{"class":2235},[2225,214906,6477],{"class":6342},[2225,214908,6480],{"class":2235},[2225,214910,2789],{"class":2239},[2225,214912,2805],{"class":2235},[2225,214914,6487],{"class":2252},[2225,214916,2805],{"class":2235},[2225,214918,2298],{"class":2235},[2225,214920,2301],{"class":2235},[2225,214922,214923,214925,214927,214929,214931,214933,214935,214937],{"class":2227,"line":2356},[2225,214924,94988],{"class":2307},[2225,214926,2311],{"class":2235},[2225,214928,8667],{"class":2239},[2225,214930,2805],{"class":2235},[2225,214932,87181],{"class":2252},[2225,214934,2805],{"class":2235},[2225,214936,8803],{"class":2239},[2225,214938,1707],{"class":2235},[2225,214940,214941,214943,214945,214947],{"class":2227,"line":2830},[2225,214942,214318],{"class":2307},[2225,214944,2311],{"class":2235},[2225,214946,2322],{"class":2321},[2225,214948,1707],{"class":2235},[2225,214950,214951,214953],{"class":2227,"line":2861},[2225,214952,2359],{"class":2235},[2225,214954,2362],{"class":2239},[937,214956,214957,56825,214959,140295,214961,214963,214964,214966,214967,214971],{},[941,214958,22041],{},[950,214960,719],{},[941,214962,6836],{},") throws — pre-building requires eager mode. ",[941,214965,22041],{}," on a collection with ",[950,214968,46960,214969],{},[941,214970,22775],{}," configured is a silent no-op instead: there's no index to build, so it returns immediately without throwing and without building anything.",[1022,214973,214975,214976],{"id":214974},"session-local-by-default-or-persisted-with-textindexpersist","Session-local by default, or persisted with ",[941,214977,214978],{},"textIndexPersist",[937,214980,214981,214982,214985],{},"The lexical index is ",[950,214983,214984],{},"session-scoped and in-memory"," unless you opt into persistence:",[1111,214987,214988,214991,214998,215005],{},[1114,214989,214990],{},"It's built from the already-decrypted eager cache — no extra store reads.",[1114,214992,214993,214994,214997],{},"By default it's ",[950,214995,214996],{},"never written to the store"," — zero added leakage.",[1114,214999,215000,215001,1261,215003,124645],{},"On a new session it rebuilds on the first ",[941,215002,22035],{},[941,215004,22041],{},[1114,215006,215007,215008,1261,215010,215012,215013,215015,215016,215018],{},"A write (",[941,215009,3161],{},[941,215011,3164],{},") marks the index ",[950,215014,71061],{},"; the next ",[941,215017,22035],{}," triggers a full rebuild.",[937,215020,197684,215021,215023],{},[941,215022,214412],{}," to keep the index warm across sessions and devices instead of rebuilding it every time:",[2217,215025,215027],{"className":2219,"code":215026,"language":2221,"meta":1037,"style":1037},"const persisted = vault.collection\u003CInvoice>('invoices', {\n  textIndexes: ['title', 'notes'],\n  textIndexPersist: true,   \u002F\u002F persist the index as an opaque encrypted blob\n  warmIndexOnOpen: true,    \u002F\u002F combine with warm load for an instant first retrieve()\n})\n",[941,215028,215029,215062,215088,215101,215114],{"__ignoreMap":1037},[2225,215030,215031,215033,215036,215038,215040,215042,215044,215046,215048,215050,215052,215054,215056,215058,215060],{"class":2227,"line":2228},[2225,215032,2775],{"class":2774},[2225,215034,215035],{"class":2239}," persisted ",[2225,215037,2781],{"class":2235},[2225,215039,6466],{"class":2239},[2225,215041,1520],{"class":2235},[2225,215043,6471],{"class":2291},[2225,215045,6474],{"class":2235},[2225,215047,6477],{"class":6342},[2225,215049,6480],{"class":2235},[2225,215051,2789],{"class":2239},[2225,215053,2805],{"class":2235},[2225,215055,6487],{"class":2252},[2225,215057,2805],{"class":2235},[2225,215059,2298],{"class":2235},[2225,215061,2301],{"class":2235},[2225,215063,215064,215066,215068,215070,215072,215074,215076,215078,215080,215082,215084,215086],{"class":2227,"line":1041},[2225,215065,94988],{"class":2307},[2225,215067,2311],{"class":2235},[2225,215069,8667],{"class":2239},[2225,215071,2805],{"class":2235},[2225,215073,87181],{"class":2252},[2225,215075,2805],{"class":2235},[2225,215077,2298],{"class":2235},[2225,215079,2249],{"class":2235},[2225,215081,2854],{"class":2252},[2225,215083,2805],{"class":2235},[2225,215085,8803],{"class":2239},[2225,215087,1707],{"class":2235},[2225,215089,215090,215092,215094,215096,215098],{"class":2227,"line":1038},[2225,215091,149928],{"class":2307},[2225,215093,2311],{"class":2235},[2225,215095,2322],{"class":2321},[2225,215097,2298],{"class":2235},[2225,215099,215100],{"class":2763},"   \u002F\u002F persist the index as an opaque encrypted blob\n",[2225,215102,215103,215105,215107,215109,215111],{"class":2227,"line":2288},[2225,215104,214318],{"class":2307},[2225,215106,2311],{"class":2235},[2225,215108,2322],{"class":2321},[2225,215110,2298],{"class":2235},[2225,215112,215113],{"class":2763},"    \u002F\u002F combine with warm load for an instant first retrieve()\n",[2225,215115,215116,215118],{"class":2227,"line":2304},[2225,215117,2359],{"class":2235},[2225,215119,2362],{"class":2239},[937,215121,11526,215122,2311],{},[941,215123,214412],{},[1111,215125,215126,215142,215157,215164,215188,215196],{},[1114,215127,215128,215129,215134,215135,46975,215138,215141],{},"The in-memory index is serialized and ",[950,215130,215131,215132],{},"encrypted under the collection's ",[1007,215133,5627],{"href":66}," — the same key that encrypts every record body. The store sees ",[950,215136,215137],{},"one opaque ciphertext blob",[941,215139,215140],{},"_ftindex\u002F\u003Ccollection>",": no terms, postings, or plaintext, and no per-term addressability the way a store-side term index would have.",[1114,215143,215144,215145,1261,215147,215149,215150,215153,215154,215156],{},"On the next ",[941,215146,22035],{},[941,215148,22041],{}," in a new session, the blob is loaded and checked against a fingerprint (",[941,215151,215152],{},"{ count, maxVersion }"," derived cheaply from the eager cache: record count, and the highest ",[941,215155,1700],{}," version stamp across all records). A matching fingerprint means the blob is used as-is — a cold load with no re-tokenizing. A mismatch (a write happened on another device or tab) triggers a rebuild and re-persist.",[1114,215158,215159,215160,215163],{},"Writes mark the index dirty and schedule a ",[950,215161,215162],{},"debounced flush"," (about one second after the last write by default) — typically one blob write per burst of writes, not one per record.",[1114,215165,215166,215170,215171],{},[950,215167,215168],{},[941,215169,22044],{}," force-persists the index immediately — call it before saving a checkpoint or before a known idle window:",[2217,215172,215174],{"className":2219,"code":215173,"language":2221,"meta":1037,"style":1037},"await invoices.flushIndex()\n",[941,215175,215176],{"__ignoreMap":1037},[2225,215177,215178,215180,215182,215184,215186],{"class":2227,"line":2228},[2225,215179,6500],{"class":2231},[2225,215181,6503],{"class":2239},[2225,215183,1520],{"class":2235},[2225,215185,214400],{"class":2291},[2225,215187,8146],{"class":2239},[1114,215189,215190,2085,215193,215195],{},[950,215191,215192],{},"Close-flush",[941,215194,13331],{}," triggers a best-effort flush of any pending dirty index, so the next session opens against the freshest blob.",[1114,215197,215198,2085,215202,1324,215204,215207,215208,215210,215211,215213,215214,215216],{},[950,215199,215200,146467],{},[941,215201,2542],{},[941,215203,76197],{},[1007,215205,215206],{"href":686},"crypto-shreds"," matching records and also deletes the ",[941,215209,35628],{}," blob, because the blob is encrypted under the same collection DEK and would otherwise still decrypt the forgotten subject's indexed terms. The next ",[941,215212,22035],{}," rebuilds from the post-forget record set. If the blob couldn't be deleted (for example, the store is offline at shred time), that's reported as residue in the ",[941,215215,2542],{}," return value — the same residue-tracking pattern used for the record-level index side-cars.",[937,215218,215219,215220,215223],{},"Persistence adds no leakage beyond the size of that one blob: the store can't distinguish an index write from a record write by its content, the fingerprint itself lives inside the encrypted envelope, and a collection with ",[941,215221,215222],{},"textIndexPersist: false"," (the default) writes nothing extra at all.",[1022,215225,68210],{"id":68209},[1338,215227,215228,215236],{},[1341,215229,215230],{},[1344,215231,215232,215234],{},[1347,215233,146489],{},[1347,215235,124024],{},[1357,215237,215238,215260,215280,215293,215305,215317,215340],{},[1344,215239,215240,215246],{},[1362,215241,215242,146571,215244],{},[941,215243,94750],{},[941,215245,2134],{},[1362,215247,215248,968,215250,968,215252,968,215254,968,215256,213001,215258],{},[941,215249,4457],{},[941,215251,75590],{},[941,215253,144133],{},[941,215255,214363],{},[941,215257,214400],{},[941,215259,22160],{},[1344,215261,215262,215267],{},[1362,215263,215264,215265,1381],{},"Collection is lazy (",[941,215266,6836],{},[1362,215268,215269,997,215271,215273,215274,215276,215277,215279],{},[941,215270,142726],{},[941,215272,22035],{}," always throw; ",[941,215275,22041],{}," throws too, but only when ",[941,215278,22775],{}," is configured (see next row)",[1344,215281,215282,215290],{},[1362,215283,215284,215286,215287,215289],{},[941,215285,22035],{}," called with no ",[941,215288,22775],{}," configured",[1362,215291,215292],{},"Throws immediately",[1344,215294,215295,215302],{},[1362,215296,215297,215299,215300,215289],{},[941,215298,22041],{}," on a collection with no ",[941,215301,22775],{},[1362,215303,215304],{},"Silent no-op — nothing to build, resolves without error, even in lazy mode",[1344,215306,215307,215314],{},[1362,215308,215309,215310,215313],{},"Two corpora share the same ",[941,215311,215312],{},"{count, maxVersion}"," fingerprint by coincidence",[1362,215315,215316],{},"The stale blob is used as-is (best-effort staleness check) — rare in practice, since almost every write changes at least one of the two values",[1344,215318,215319,215332],{},[1362,215320,215321,1261,215323,1261,215325,1261,215327,215329,215330],{},[941,215322,760],{},[941,215324,20636],{},[941,215326,26305],{},[941,215328,16861],{}," fields in ",[941,215331,22775],{},[1362,215333,215334,215335,20621,215337,215339],{},"Not matched by ",[941,215336,22035],{},[941,215338,65223],{}," for those",[1344,215341,215342,215345],{},[1362,215343,215344],{},"Blob content (file bytes)",[1362,215346,215347,215348,215350],{},"Never tokenized — extract text into a ",[941,215349,7259],{}," field first",[1022,215352,2621],{"id":2620},[1111,215354,215355,215375,215390,215396,215401,215406],{},[1114,215356,215357,215359,215360,215363,215364,215366,215367,2462,215369,215372,215373,16275],{},[1007,215358,673],{"href":674}," — semantic (",[941,215361,215362],{},"mode: 'semantic'",") and hybrid (",[941,215365,142881],{},") retrieval, ",[941,215368,22038],{},[941,215370,215371],{},"fuseRetrieval()",", all part of the same ",[941,215374,22027],{},[1114,215376,215377,1169,215380,215382,215383,968,215385,2462,215387,215389],{},[1007,215378,215379],{"href":720},"Lazy",[941,215381,6836],{}," mode that ",[941,215384,142726],{},[941,215386,22035],{},[941,215388,22041],{}," all throw under.",[1114,215391,215392,215395],{},[1007,215393,215394],{"href":813},"Satellites"," — split large non-searched fields off the decrypt path that scan mode reads.",[1114,215397,215398,215400],{},[1007,215399,146655],{"href":686}," — the erasure path this page's persisted-index teardown plugs into.",[1114,215402,215403,215405],{},[1007,215404,21],{"href":66}," — the collection DEK that encrypts the persisted index blob.",[1114,215407,141161,215408,968,215414,2462,215420,1520],{},[1007,215409,215411],{"href":94027,"rel":215410},[3371],[941,215412,215413],{},"111-scan-search",[1007,215415,215417],{"href":94214,"rel":215416},[3371],[941,215418,215419],{},"122-with-retrieve",[1007,215421,215423],{"href":94231,"rel":215422},[3371],[941,215424,215425],{},"123-persisted-index",[2645,215427,215428],{},"html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sfNiH, html code.shiki .sfNiH{--shiki-light:#FF5370;--shiki-default:#FF9CAC;--shiki-dark:#FF9CAC}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}html pre.shiki code .sHdIc, html code.shiki .sHdIc{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#EEFFFF;--shiki-default-font-style:italic;--shiki-dark:#BABED8;--shiki-dark-font-style:italic}",{"title":1037,"searchDepth":1038,"depth":1038,"links":215430},[215431,215432,215433,215434,215440,215448,215449,215451,215453,215455,215456],{"id":6241,"depth":1041,"text":6242},{"id":70092,"depth":1041,"text":70093},{"id":99863,"depth":1041,"text":99864},{"id":29892,"depth":1041,"text":29893,"children":215435},[215436,215438],{"id":213282,"depth":1038,"text":215437},"collection.search(field, query, opts) — scan mode",{"id":213510,"depth":1038,"text":215439},"collection.retrieve(query, opts) — lexical index",{"id":102888,"depth":1041,"text":102889,"children":215441},[215442,215443,215444,215445,215446,215447],{"id":213710,"depth":1038,"text":213713},{"id":213898,"depth":1038,"text":213901},{"id":214238,"depth":1038,"text":22775},{"id":214351,"depth":1038,"text":212997},{"id":214388,"depth":1038,"text":213000},{"id":126392,"depth":1038,"text":126393},{"id":214503,"depth":1041,"text":214504},{"id":214785,"depth":1041,"text":215450},"i18n tokenizer — Intl.Segmenter",{"id":214849,"depth":1041,"text":215452},"warmIndex() and warmIndexOnOpen",{"id":214974,"depth":1041,"text":215454},"Session-local by default, or persisted with textIndexPersist",{"id":68209,"depth":1041,"text":68210},{"id":2620,"depth":1041,"text":2621},"Keyword lookup via `withSearch()`: zero-setup `search()` with BM25, or indexed `retrieve()` with ranking and snippets — all client-side after decryption.",{},{"title":832,"description":215457},"full-text scan and lexical retrieval","PdFQp_ffTr0WADJFvbR80OAxF0OiMM6YPhipWq7ZLig",{"id":215463,"title":836,"access":12,"apiToc":929,"body":215464,"category":1042,"description":216468,"extension":1044,"meta":216469,"navigation":1042,"nest":1047,"path":837,"seo":216470,"sinceVersion":1042,"status":1049,"stem":838,"subtitle":1042,"tier":1050,"tocExternal":1042,"__hash__":216471},"docs\u002Fservices\u002Fsequence.md",{"type":931,"value":215465,"toc":216450},[215466,215468,215471,215473,215486,215507,215509,215527,215529,215534,215674,215676,215680,215760,215766,215770,215805,215812,215816,215852,215864,215868,215871,215971,215977,216008,216010,216016,216025,216080,216128,216134,216197,216217,216219,216289,216293,216295,216412,216414,216448],[934,215467,836],{"id":836},[46871,215469],{":rows":215470},"[{\"label\":\"Subpath\",\"value\":\"exported from `@noy-db\u002Fhub` root (no dedicated subpath)\"},{\"label\":\"Factory\",\"value\":\"`withSequence()`\"},{\"label\":\"Layer\",\"value\":\"`with-commit`\"}]",[1022,215472,29857],{"id":29856},[937,215474,215475,215476,215479,215480,215483,215484,957],{},"Invoice numbers, receipt numbers, ticket numbers — anything that must count up by\nexactly 1 with no gaps and no repeats, even when two callers ask for the next number at\nthe same instant. ",[941,215477,215478],{},"vault.sequence('invoice-2026').next()"," returns that number: each\nnamed sequence is an independent counter stored (encrypted, like any other record) at\n",[941,215481,215482],{},"_sequences\u002F\u003Cname>"," inside the vault (see\n",[1007,215485,65376],{"href":48},[937,215487,215488,215489,215492,215493,215495,215496,7356,215498,215501,215502,215504,215505,957],{},"Concurrency safety comes from ",[950,215490,215491],{},"compare-and-swap (CAS)",": to allocate a value, the\ncounter is read, incremented, and written back with a condition — \"only accept this\nwrite if the counter is still at the value I read.\" If another caller won by writing\nfirst, the condition fails and the write is rejected instead of silently overwriting;\n",[941,215494,185958],{}," then re-reads and retries with backoff, up to 16 attempts. This is the same\noptimistic-retry pattern the vault's audit ledger uses internally. Because it depends on\nthe backing store enforcing that write condition, ",[941,215497,185986],{},[950,215499,215500],{},"explicitly online-only","\n— it throws ",[941,215503,185961],{}," on a store that cannot guarantee atomic writes (see\n",[1007,215506,68210],{"href":126812},[1022,215508,70093],{"id":70092},[1111,215510,215511],{},[1114,215512,215513,215516,215517,215519,215520,10326,215522,215524,215525,33515],{},[950,215514,215515],{},"A store with atomic compare-and-swap (CAS) writes"," — allocation is a CAS loop, so backends without CAS (e.g. ",[941,215518,9551],{},") make ",[941,215521,185986],{},[941,215523,185961],{},". Offline or CAS-less setups should use ",[1007,215526,55438],{"href":765},[1022,215528,99864],{"id":99863},[937,215530,101689,215531,215533],{},[941,215532,12780],{},"; no collection-level configuration is needed:",[2217,215535,215537],{"className":2219,"code":215536,"language":2221,"meta":1037,"style":1037},"import { createNoydb } from '@noy-db\u002Fhub'\nimport { withSequence } from '@noy-db\u002Fhub'\n\nconst db = await createNoydb({\n  user: 'firm',                    \u002F\u002F omitting `store` uses the built-in in-memory store;\n  secret: 'firm-secret-2026',  \u002F\u002F pass to-file \u002F to-postgres \u002F … in production\n  sequenceStrategy: withSequence(),\n})\nconst vault = await db.openVault('books')\n",[941,215538,215539,215557,215576,215580,215596,215612,215629,215642,215648],{"__ignoreMap":1037},[2225,215540,215541,215543,215545,215547,215549,215551,215553,215555],{"class":2227,"line":2228},[2225,215542,2232],{"class":2231},[2225,215544,2236],{"class":2235},[2225,215546,2744],{"class":2239},[2225,215548,2243],{"class":2235},[2225,215550,2246],{"class":2231},[2225,215552,2249],{"class":2235},[2225,215554,1086],{"class":2252},[2225,215556,2256],{"class":2235},[2225,215558,215559,215561,215563,215566,215568,215570,215572,215574],{"class":2227,"line":1041},[2225,215560,2232],{"class":2231},[2225,215562,2236],{"class":2235},[2225,215564,215565],{"class":2239}," withSequence",[2225,215567,2243],{"class":2235},[2225,215569,2246],{"class":2231},[2225,215571,2249],{"class":2235},[2225,215573,1086],{"class":2252},[2225,215575,2256],{"class":2235},[2225,215577,215578],{"class":2227,"line":1038},[2225,215579,2285],{"emptyLinePlaceholder":2284},[2225,215581,215582,215584,215586,215588,215590,215592,215594],{"class":2227,"line":2288},[2225,215583,2775],{"class":2774},[2225,215585,2778],{"class":2239},[2225,215587,2781],{"class":2235},[2225,215589,2784],{"class":2231},[2225,215591,2744],{"class":2291},[2225,215593,2789],{"class":2239},[2225,215595,4200],{"class":2235},[2225,215597,215598,215600,215602,215604,215606,215608,215610],{"class":2227,"line":2304},[2225,215599,4205],{"class":2307},[2225,215601,2311],{"class":2235},[2225,215603,2249],{"class":2235},[2225,215605,39709],{"class":2252},[2225,215607,2805],{"class":2235},[2225,215609,2298],{"class":2235},[2225,215611,107596],{"class":2763},[2225,215613,215614,215616,215618,215620,215622,215624,215626],{"class":2227,"line":2356},[2225,215615,10423],{"class":2307},[2225,215617,2311],{"class":2235},[2225,215619,2249],{"class":2235},[2225,215621,65709],{"class":2252},[2225,215623,2805],{"class":2235},[2225,215625,2298],{"class":2235},[2225,215627,215628],{"class":2763},"  \u002F\u002F pass to-file \u002F to-postgres \u002F … in production\n",[2225,215630,215631,215634,215636,215638,215640],{"class":2227,"line":2830},[2225,215632,215633],{"class":2307},"  sequenceStrategy",[2225,215635,2311],{"class":2235},[2225,215637,215565],{"class":2291},[2225,215639,4228],{"class":2239},[2225,215641,1707],{"class":2235},[2225,215643,215644,215646],{"class":2227,"line":2861},[2225,215645,2359],{"class":2235},[2225,215647,2362],{"class":2239},[2225,215649,215650,215652,215654,215656,215658,215660,215662,215664,215666,215668,215670,215672],{"class":2227,"line":4246},[2225,215651,2775],{"class":2774},[2225,215653,2835],{"class":2239},[2225,215655,2781],{"class":2235},[2225,215657,2784],{"class":2231},[2225,215659,2842],{"class":2239},[2225,215661,1520],{"class":2235},[2225,215663,2847],{"class":2291},[2225,215665,2789],{"class":2239},[2225,215667,2805],{"class":2235},[2225,215669,65761],{"class":2252},[2225,215671,2805],{"class":2235},[2225,215673,2362],{"class":2239},[1022,215675,29893],{"id":29892},[1591,215677,215679],{"id":215678},"allocate-the-next-number","Allocate the next number",[2217,215681,215683],{"className":2219,"code":215682,"language":2221,"meta":1037,"style":1037},"const n1 = await vault.sequence('invoice-2026').next()\n\u002F\u002F → 1\nconst n2 = await vault.sequence('invoice-2026').next()\n\u002F\u002F → 2\n",[941,215684,215685,215719,215723,215756],{"__ignoreMap":1037},[2225,215686,215687,215689,215692,215694,215696,215698,215700,215702,215704,215706,215709,215711,215713,215715,215717],{"class":2227,"line":2228},[2225,215688,2775],{"class":2774},[2225,215690,215691],{"class":2239}," n1 ",[2225,215693,2781],{"class":2235},[2225,215695,2784],{"class":2231},[2225,215697,6466],{"class":2239},[2225,215699,1520],{"class":2235},[2225,215701,836],{"class":2291},[2225,215703,2789],{"class":2239},[2225,215705,2805],{"class":2235},[2225,215707,215708],{"class":2252},"invoice-2026",[2225,215710,2805],{"class":2235},[2225,215712,1381],{"class":2239},[2225,215714,1520],{"class":2235},[2225,215716,1454],{"class":2291},[2225,215718,8146],{"class":2239},[2225,215720,215721],{"class":2227,"line":1041},[2225,215722,186523],{"class":2763},[2225,215724,215725,215727,215730,215732,215734,215736,215738,215740,215742,215744,215746,215748,215750,215752,215754],{"class":2227,"line":1038},[2225,215726,2775],{"class":2774},[2225,215728,215729],{"class":2239}," n2 ",[2225,215731,2781],{"class":2235},[2225,215733,2784],{"class":2231},[2225,215735,6466],{"class":2239},[2225,215737,1520],{"class":2235},[2225,215739,836],{"class":2291},[2225,215741,2789],{"class":2239},[2225,215743,2805],{"class":2235},[2225,215745,215708],{"class":2252},[2225,215747,2805],{"class":2235},[2225,215749,1381],{"class":2239},[2225,215751,1520],{"class":2235},[2225,215753,1454],{"class":2291},[2225,215755,8146],{"class":2239},[2225,215757,215758],{"class":2227,"line":2288},[2225,215759,26947],{"class":2763},[937,215761,215762,215763,215765],{},"Assign the returned value to its record in the same operation you allocated it in — if\na caller discards an allocated value without using it, that value is skipped forever\n(a gap in ",[1092,215764,29892],{},", not in the counter itself).",[1591,215767,215769],{"id":215768},"read-without-allocating","Read without allocating",[2217,215771,215773],{"className":2219,"code":215772,"language":2221,"meta":1037,"style":1037},"await vault.sequence('invoice-2026').peek()\n\u002F\u002F → 2\n",[941,215774,215775,215801],{"__ignoreMap":1037},[2225,215776,215777,215779,215781,215783,215785,215787,215789,215791,215793,215795,215797,215799],{"class":2227,"line":2228},[2225,215778,6500],{"class":2231},[2225,215780,6466],{"class":2239},[2225,215782,1520],{"class":2235},[2225,215784,836],{"class":2291},[2225,215786,2789],{"class":2239},[2225,215788,2805],{"class":2235},[2225,215790,215708],{"class":2252},[2225,215792,2805],{"class":2235},[2225,215794,1381],{"class":2239},[2225,215796,1520],{"class":2235},[2225,215798,186516],{"class":2291},[2225,215800,8146],{"class":2239},[2225,215802,215803],{"class":2227,"line":1041},[2225,215804,26947],{"class":2763},[937,215806,215807,2557,215809,215811],{},[941,215808,31993],{},[941,215810,5402],{}," for a series that has never been used — it never allocates or\nwrites.",[1591,215813,215815],{"id":215814},"fast-forward-after-an-import","Fast-forward after an import",[2217,215817,215819],{"className":2219,"code":215818,"language":2221,"meta":1037,"style":1037},"await vault.sequence('invoice-2026').seedTo(500)\n",[941,215820,215821],{"__ignoreMap":1037},[2225,215822,215823,215825,215827,215829,215831,215833,215835,215837,215839,215841,215843,215846,215848,215850],{"class":2227,"line":2228},[2225,215824,6500],{"class":2231},[2225,215826,6466],{"class":2239},[2225,215828,1520],{"class":2235},[2225,215830,836],{"class":2291},[2225,215832,2789],{"class":2239},[2225,215834,2805],{"class":2235},[2225,215836,215708],{"class":2252},[2225,215838,2805],{"class":2235},[2225,215840,1381],{"class":2239},[2225,215842,1520],{"class":2235},[2225,215844,215845],{"class":2291},"seedTo",[2225,215847,2789],{"class":2239},[2225,215849,204664],{"class":6527},[2225,215851,2362],{"class":2239},[937,215853,215854,215857,215858,215860,215861,215863],{},[941,215855,215856],{},"seedTo(n)"," is set-if-greater: it advances the counter to at least ",[941,215859,26634],{}," and is a no-op if\nthe counter is already there (so it never rewinds a series). Use it after importing a\nCSV or bundle of pre-numbered records, so the next ",[941,215862,185986],{}," call cannot re-issue a\nnumber that is already on a record.",[1591,215865,215867],{"id":215866},"partitioned-and-formatted-series","Partitioned and formatted series",[937,215869,215870],{},"A sequence can be scoped to a tuple of values — e.g. numbering EU invoices separately\nfrom US invoices — and rendered straight to a serial string instead of a bare integer:",[2217,215872,215874],{"className":2219,"code":215873,"language":2221,"meta":1037,"style":1037},"const invoiceSeq = vault.sequence('fatture', {\n  partition: [2026, 'EU'],\n  format: '{partition.0}\u002F{partition.1}\u002F{seq:04}',\n})\n\nawait invoiceSeq.next()\n\u002F\u002F → { serial: 1, formatted: '2026\u002FEU\u002F0001' }\n",[941,215875,215876,215904,215928,215943,215949,215953,215966],{"__ignoreMap":1037},[2225,215877,215878,215880,215883,215885,215887,215889,215891,215893,215895,215898,215900,215902],{"class":2227,"line":2228},[2225,215879,2775],{"class":2774},[2225,215881,215882],{"class":2239}," invoiceSeq ",[2225,215884,2781],{"class":2235},[2225,215886,6466],{"class":2239},[2225,215888,1520],{"class":2235},[2225,215890,836],{"class":2291},[2225,215892,2789],{"class":2239},[2225,215894,2805],{"class":2235},[2225,215896,215897],{"class":2252},"fatture",[2225,215899,2805],{"class":2235},[2225,215901,2298],{"class":2235},[2225,215903,2301],{"class":2235},[2225,215905,215906,215909,215911,215913,215916,215918,215920,215922,215924,215926],{"class":2227,"line":1041},[2225,215907,215908],{"class":2307},"  partition",[2225,215910,2311],{"class":2235},[2225,215912,8667],{"class":2239},[2225,215914,215915],{"class":6527},"2026",[2225,215917,2298],{"class":2235},[2225,215919,2249],{"class":2235},[2225,215921,62468],{"class":2252},[2225,215923,2805],{"class":2235},[2225,215925,8803],{"class":2239},[2225,215927,1707],{"class":2235},[2225,215929,215930,215932,215934,215936,215939,215941],{"class":2227,"line":1038},[2225,215931,95362],{"class":2307},[2225,215933,2311],{"class":2235},[2225,215935,2249],{"class":2235},[2225,215937,215938],{"class":2252},"{partition.0}\u002F{partition.1}\u002F{seq:04}",[2225,215940,2805],{"class":2235},[2225,215942,1707],{"class":2235},[2225,215944,215945,215947],{"class":2227,"line":2288},[2225,215946,2359],{"class":2235},[2225,215948,2362],{"class":2239},[2225,215950,215951],{"class":2227,"line":2304},[2225,215952,2285],{"emptyLinePlaceholder":2284},[2225,215954,215955,215957,215960,215962,215964],{"class":2227,"line":2356},[2225,215956,6500],{"class":2231},[2225,215958,215959],{"class":2239}," invoiceSeq",[2225,215961,1520],{"class":2235},[2225,215963,1454],{"class":2291},[2225,215965,8146],{"class":2239},[2225,215967,215968],{"class":2227,"line":2830},[2225,215969,215970],{"class":2763},"\u002F\u002F → { serial: 1, formatted: '2026\u002FEU\u002F0001' }\n",[937,215972,215973,215974,215976],{},"Each distinct ",[941,215975,180261],{}," tuple is its own counter, so a new partition always starts at",[1234,215978,215979],{},[1114,215980,215981,54824,215983,968,215986,215989,215990,215992,215993,215996,215997,104458,216000,216002,216003,216005,216006,1520],{},[941,215982,23270],{},[941,215984,215985],{},"{seq}",[941,215987,215988],{},"{seq:0N}"," (zero-padded to width ",[941,215991,57103],{},"), and\n",[941,215994,215995],{},"{partition.i}"," tokens; an unknown token or an out-of-range ",[941,215998,215999],{},"partition.i",[941,216001,12881],{}," immediately when you call ",[941,216004,6813],{},", not later at ",[941,216007,185986],{},[1022,216009,102889],{"id":102888},[1591,216011,216013],{"id":216012},"vaultsequenceseries-opts",[941,216014,216015],{},"vault.sequence(series, opts?)",[937,216017,216018,216019,216021,216022,216024],{},"Returns a handle bound to one named series. Calling it twice with the same ",[941,216020,186623],{}," (and\nthe same ",[941,216023,180261],{},", if any) returns handles over the same underlying counter.",[2217,216026,216028],{"className":2219,"code":216027,"language":2221,"meta":1037,"style":1037},"vault.sequence(series: string, opts?: SequenceOptions): SequenceHandle\nvault.sequence(series: string, opts: SequenceOptions & { format: string }): FormattedSequenceHandle\n",[941,216029,216030,216050],{"__ignoreMap":1037},[2225,216031,216032,216034,216036,216038,216041,216043,216045,216047],{"class":2227,"line":2228},[2225,216033,2694],{"class":2239},[2225,216035,1520],{"class":2235},[2225,216037,836],{"class":2291},[2225,216039,216040],{"class":2239},"(series: string",[2225,216042,2298],{"class":2235},[2225,216044,8232],{"class":2239},[2225,216046,7011],{"class":2235},[2225,216048,216049],{"class":2239}," SequenceOptions): SequenceHandle\n",[2225,216051,216052,216054,216056,216058,216060,216062,216065,216067,216069,216071,216073,216075,216077],{"class":2227,"line":1041},[2225,216053,2694],{"class":2239},[2225,216055,1520],{"class":2235},[2225,216057,836],{"class":2291},[2225,216059,216040],{"class":2239},[2225,216061,2298],{"class":2235},[2225,216063,216064],{"class":2239}," opts: SequenceOptions ",[2225,216066,16318],{"class":2235},[2225,216068,2236],{"class":2235},[2225,216070,36875],{"class":2307},[2225,216072,2311],{"class":2235},[2225,216074,19923],{"class":2239},[2225,216076,2359],{"class":2235},[2225,216078,216079],{"class":2239},"): FormattedSequenceHandle\n",[1338,216081,216082,216092],{},[1341,216083,216084],{},[1344,216085,216086,216088,216090],{},[1347,216087,101060],{},[1347,216089,20194],{},[1347,216091,7834],{},[1357,216093,216094,216108],{},[1344,216095,216096,216100,216105],{},[1362,216097,216098],{},[941,216099,180261],{},[1362,216101,216102],{},[941,216103,216104],{},"readonly (string | number)[]",[1362,216106,216107],{},"Scopes the counter to this tuple of values; each distinct tuple is an independent series starting at 1.",[1344,216109,216110,216114,216118],{},[1362,216111,216112],{},[941,216113,23270],{},[1362,216115,216116],{},[941,216117,7259],{},[1362,216119,216120,216121,216123,216124,216127],{},"Render template; when set, ",[941,216122,185986],{}," resolves to ",[941,216125,216126],{},"{ serial, formatted }"," instead of a bare number.",[1591,216129,216131],{"id":216130},"sequencehandle",[941,216132,216133],{},"SequenceHandle",[1338,216135,216136,216146],{},[1341,216137,216138],{},[1344,216139,216140,216142,216144],{},[1347,216141,7831],{},[1347,216143,102906],{},[1347,216145,7834],{},[1357,216147,216148,216162,216178],{},[1344,216149,216150,216155,216159],{},[1362,216151,216152],{},[941,216153,216154],{},"next(opts?)",[1362,216156,216157],{},[941,216158,160412],{},[1362,216160,216161],{},"Atomically allocate and return the next value.",[1344,216163,216164,216168,216172],{},[1362,216165,216166],{},[941,216167,31993],{},[1362,216169,216170],{},[941,216171,160412],{},[1362,216173,216174,216175,216177],{},"Read the current value without allocating; ",[941,216176,5402],{}," if never used.",[1344,216179,216180,216184,216188],{},[1362,216181,216182],{},[941,216183,215856],{},[1362,216185,216186],{},[941,216187,116014],{},[1362,216189,216190,216191,216193,216194,1520],{},"Set-if-greater: advance the counter to at least ",[941,216192,26634],{},"; no-op if already there or if ",[941,216195,216196],{},"n \u003C= 0",[937,216198,216199,216202,216203,216205,216206,216123,216208,216211,216212,1261,216214,216216],{},[941,216200,216201],{},"FormattedSequenceHandle"," (returned when ",[941,216204,23270],{}," is set) has the same three methods;\n",[941,216207,185986],{},[941,216209,216210],{},"{ serial: number; formatted: string }"," instead of a bare number,\nand ",[941,216213,31993],{},[941,216215,186861],{}," still operate on the underlying integer counter.",[1591,216218,120265],{"id":120264},[1338,216220,216221,216229],{},[1341,216222,216223],{},[1344,216224,216225,216227],{},[1347,216226,12126],{},[1347,216228,120276],{},[1357,216230,216231,216248,216261,216275],{},[1344,216232,216233,216237],{},[1362,216234,216235],{},[941,216236,21790],{},[1362,216238,216239,216241,216242,216245,216246,1520],{},[941,216240,6813],{}," is called without ",[941,216243,216244],{},"sequenceStrategy: withSequence()"," passed to ",[941,216247,12780],{},[1344,216249,216250,216254],{},[1362,216251,216252],{},[941,216253,185961],{},[1362,216255,216256,4688,216258,216260],{},[941,216257,185986],{},[941,216259,186861],{}," is called against a store that does not advertise atomic compare-and-swap writes.",[1344,216262,216263,216268],{},[1362,216264,216265],{},[941,216266,216267],{},"SequenceContentionError",[1362,216269,216270,1261,216272,216274],{},[941,216271,185986],{},[941,216273,186861],{}," exhausted their 16-attempt retry budget under sustained contention on the same series; the counter is intact and the caller may retry.",[1344,216276,216277,216281],{},[1362,216278,216279],{},[941,216280,12881],{},[1362,216282,216283,216284,216286,216287,1520],{},"The series name contains a null byte, or a ",[941,216285,23270],{}," template references an unknown token or an out-of-range ",[941,216288,215999],{},[937,216290,120374,216291,1520],{},[941,216292,1086],{},[1022,216294,68210],{"id":68209},[1111,216296,216297,216324,216352,216361,216373,216381,216395],{},[1114,216298,46418,216299,216305,216306,216308,216309,216311,216312,216314,216315,216317,216318,216323],{},[950,216300,216301,216302,216304],{},"Unique per DEVICE, not across synced devices — through hub ",[941,216303,2126],{}," at least.","\nCounters are compare-and-swapped against the ",[1092,216307,49766],{}," store, and the ",[941,216310,6799],{}," collection does\nnot sync, so two devices replicating one vault issue the same numbers (measured upstream: device A\n",[941,216313,185986],{}," → 1, A pushes, B pulls the whole vault, B ",[941,216316,185986],{}," → 1). Do not use it for a fleet-unique\ninvoice number until ",[1007,216319,216322],{"href":216320,"rel":216321},"https:\u002F\u002Fgithub.com\u002Fnoy-db\u002Ffamily\u002Fissues\u002F206",[3371],"family#206"," lands; until then\nallocate numbers on a single authority — one process against one store — and let the other\ndevices receive the numbered records through sync.",[1114,216325,216326,216329,216330,2514,216332,216334,216335,216337,216338,216340,216341,216343,216344,997,216346,216348,216349,216351],{},[950,216327,216328],{},"Online-only, by design."," Gap-free numbering requires one authority serializing\nwrites to the counter, which an offline writer cannot guarantee. ",[941,216331,185986],{},[941,216333,186861],{}," both throw ",[941,216336,185961],{}," unless the store's ",[941,216339,160746],{},"\nis ",[941,216342,11790],{},". Among the built-in stores, ",[941,216345,2992],{},[941,216347,9507],{}," support it;\n",[941,216350,7461],{}," does not (no atomic compare-and-swap at the filesystem layer) — check the\ncapabilities of whichever store you use in production.",[1114,216353,216354,216357,216358,216360],{},[950,216355,216356],{},"Gap-free means the counter never skips or repeats a value it hands out"," — it says\nnothing about values you allocate and then discard. Assign a ",[941,216359,185986],{}," result to its\nrecord in the same operation you allocated it.",[1114,216362,216363,216366,216367,216369,216370,216372],{},[950,216364,216365],{},"Contention has a budget, not a queue."," A burst of concurrent callers on the ",[1092,216368,4664],{},"\nseries beyond the retry budget surfaces ",[941,216371,216267],{}," rather than blocking\nindefinitely — the caller decides whether to retry or queue.",[1114,216374,216375,953,216378,216380],{},[950,216376,216377],{},"A series name cannot contain a null byte",[941,216379,7432],{},") — that byte is the internal\nseparator between a series name and its partition key.",[1114,216382,216383,216388,216389,216391,216392,216394],{},[950,216384,216385,216387],{},[941,216386,23270],{}," is validated eagerly."," A bad token throws at ",[941,216390,6813],{}," time, not\nat ",[941,216393,185986],{}," time, so a misconfigured template fails fast.",[1114,216396,216397,216400,216401,2878,216403,57277,216405,216407,216408,4688,216410,1520],{},[950,216398,216399],{},"Deferred-numbering series are a different capability."," A series declared via\n",[941,216402,21749],{},[941,216404,764],{},[941,216406,12780],{},") is numbered at\na batch pass instead of via CAS, and does not support ",[941,216409,23270],{},[941,216411,215845],{},[1022,216413,2621],{"id":2620},[1111,216415,216416,216422,216430,216434,216440],{},[1114,216417,216418,216421],{},[1007,216419,216420],{"href":698},"History & audit ledger"," — the append-only ledger uses the same\noptimistic-CAS retry pattern",[1114,216423,216424,216426,216427,216429],{},[1007,216425,764],{"href":765}," — the deferred, batch-pass alternative for stores that\ncan't provide the atomic compare-and-swap ",[941,216428,185958],{}," depends on",[1114,216431,216432],{},[1007,216433,65376],{"href":48},[1114,216435,216436,1368,216438],{},[1007,216437,61743],{"href":598},[1007,216439,1019],{"href":1018},[1114,216441,101581,216442,1520],{},[1007,216443,216445],{"href":93908,"rel":216444},[3371],[941,216446,216447],{},"102-formatted-sequence",[2645,216449,72339],{},{"title":1037,"searchDepth":1038,"depth":1038,"links":216451},[216452,216453,216454,216455,216461,216466,216467],{"id":29856,"depth":1041,"text":29857},{"id":70092,"depth":1041,"text":70093},{"id":99863,"depth":1041,"text":99864},{"id":29892,"depth":1041,"text":29893,"children":216456},[216457,216458,216459,216460],{"id":215678,"depth":1038,"text":215679},{"id":215768,"depth":1038,"text":215769},{"id":215814,"depth":1038,"text":215815},{"id":215866,"depth":1038,"text":215867},{"id":102888,"depth":1041,"text":102889,"children":216462},[216463,216464,216465],{"id":216012,"depth":1038,"text":216015},{"id":216130,"depth":1038,"text":216133},{"id":120264,"depth":1038,"text":120265},{"id":68209,"depth":1041,"text":68210},{"id":2620,"depth":1041,"text":2621},"`vault.sequence(name).next()` hands out gap-free, duplicate-free serial numbers — 1, 2, 3, … — even when multiple callers on one store allocate at once.",{},{"title":836,"description":216468},"YRjrnK7gCqgQc-lVODnFVM0_NTssMPkK8zrvZ0_CAHw",{"id":216473,"title":840,"access":12,"apiToc":929,"body":216474,"category":1042,"description":218344,"extension":1044,"meta":218345,"navigation":1042,"nest":1047,"path":841,"seo":218346,"sinceVersion":1042,"status":1049,"stem":842,"subtitle":1042,"tier":1050,"tocExternal":1042,"__hash__":218347},"docs\u002Fservices\u002Fsession.md",{"type":931,"value":216475,"toc":218323},[216476,216478,216481,216483,216515,216517,216537,216539,216571,216573,216789,216792,216826,216828,216832,216841,217079,217237,217255,217286,217306,217310,217330,217361,217617,217630,217634,217712,217714,217889,217894,217994,218002,218008,218048,218054,218120,218124,218169,218171,218195,218197,218239,218241,218287,218289,218320],[934,216477,840],{"id":840},[46871,216479],{":rows":216480},"[{\"label\":\"Subpath\",\"value\":\"`@noy-db\u002Fhub\u002Fsession`\"},{\"label\":\"Factory\",\"value\":\"`withSession()`\"},{\"label\":\"Cluster\",\"value\":\"G — Collaboration & Auth\"},{\"label\":\"LOC cost\",\"value\":\"~495 (off-bundle when not opted in)\"}]",[1022,216482,29857],{"id":29856},[937,216484,216485,216486,216488,216489,216491,216492,216494,216495,216498,216499,216502,216503,216506,216507,216510,216511,216514],{},"Every noy-db vault starts locked: the app calls an unlock function (secret, WebAuthn, OIDC, magic-link — one of the ",[941,216487,1787],{}," packages) and gets back a ",[950,216490,2705],{},", the decrypted per-user key material held in memory for that call. ",[941,216493,62043],{}," builds two things on top of a single unlock: ",[950,216496,216497],{},"session tokens"," — a serializable handle you can stash in ",[941,216500,216501],{},"sessionStorage"," and hand to ",[941,216504,216505],{},"resolveSession()"," later so the app doesn't re-run the unlock ceremony on every call — and a ",[950,216508,216509],{},"session policy"," shape (idle\u002Fabsolute timeouts, an optional lock-on-background, and a list of high-risk operations that should require re-auth) that a ",[941,216512,216513],{},"PolicyEnforcer"," object can enforce for you once you drive it from your own code (see Usage). It also keeps a process-wide registry of active sessions so a \"log out everywhere\" action can revoke them all at once.",[1022,216516,111660],{"id":111659},[1111,216518,216519,216522,216525,216528],{},[1114,216520,216521],{},"Long-running web apps that need idle\u002Fabsolute timeouts",[1114,216523,216524],{},"Sensitive operations that must require fresh re-auth",[1114,216526,216527],{},"Mobile\u002Fdesktop apps that should lock when backgrounded",[1114,216529,216530,216531,1261,216534],{},"Multi-tab coordination via ",[941,216532,216533],{},"revokeSession(id)",[941,216535,216536],{},"revokeAllSessions()",[1022,216538,70093],{"id":70092},[1111,216540,216541,216564],{},[1114,216542,216543,216546,216547,216550,216551,216554,216555,216557,216558,17904,216561,216563],{},[950,216544,216545],{},"An enrolled unlock method to re-verify against"," — the policy engine decides ",[1092,216548,216549],{},"when"," re-auth is due; ",[1092,216552,216553],{},"how"," is your app calling an ",[941,216556,1787],{}," verifier (e.g. ",[941,216559,216560],{},"verifyPasswordSlot",[941,216562,17035],{},") and reporting the result. Without an enrolled method there is nothing to re-verify.",[1114,216565,216566,216570],{},[950,216567,3289,216568],{},[941,216569,20690],{}," — the factory enables enforcement; the policy object declares which operations require fresh auth.",[1022,216572,99864],{"id":99863},[2217,216574,216576],{"className":2219,"code":216575,"language":2221,"meta":1037,"style":1037},"import { createNoydb } from '@noy-db\u002Fhub'\nimport { toMemory } from '@noy-db\u002Fto-memory'\nimport { withSession } from '@noy-db\u002Fhub\u002Fsession'\n\nconst db = await createNoydb({\n  store: toMemory(),\n  user: 'alice',\n  sessionStrategy: withSession(),\n\n  sessionPolicy: {\n    idleTimeoutMs: 15 * 60_000,\n    absoluteTimeoutMs: 8 * 60 * 60_000,\n    lockOnBackground: true,\n    requireReAuthFor: ['export', 'grant', 'rotate'],\n  },\n})\n",[941,216577,216578,216596,216614,216632,216636,216652,216664,216678,216690,216694,216702,216716,216734,216744,216779,216783],{"__ignoreMap":1037},[2225,216579,216580,216582,216584,216586,216588,216590,216592,216594],{"class":2227,"line":2228},[2225,216581,2232],{"class":2231},[2225,216583,2236],{"class":2235},[2225,216585,2744],{"class":2239},[2225,216587,2243],{"class":2235},[2225,216589,2246],{"class":2231},[2225,216591,2249],{"class":2235},[2225,216593,1086],{"class":2252},[2225,216595,2256],{"class":2235},[2225,216597,216598,216600,216602,216604,216606,216608,216610,216612],{"class":2227,"line":1041},[2225,216599,2232],{"class":2231},[2225,216601,2236],{"class":2235},[2225,216603,68576],{"class":2239},[2225,216605,2243],{"class":2235},[2225,216607,2246],{"class":2231},[2225,216609,2249],{"class":2235},[2225,216611,9638],{"class":2252},[2225,216613,2256],{"class":2235},[2225,216615,216616,216618,216620,216622,216624,216626,216628,216630],{"class":2227,"line":1038},[2225,216617,2232],{"class":2231},[2225,216619,2236],{"class":2235},[2225,216621,62034],{"class":2239},[2225,216623,2243],{"class":2235},[2225,216625,2246],{"class":2231},[2225,216627,2249],{"class":2235},[2225,216629,62043],{"class":2252},[2225,216631,2256],{"class":2235},[2225,216633,216634],{"class":2227,"line":2288},[2225,216635,2285],{"emptyLinePlaceholder":2284},[2225,216637,216638,216640,216642,216644,216646,216648,216650],{"class":2227,"line":2304},[2225,216639,2775],{"class":2774},[2225,216641,2778],{"class":2239},[2225,216643,2781],{"class":2235},[2225,216645,2784],{"class":2231},[2225,216647,2744],{"class":2291},[2225,216649,2789],{"class":2239},[2225,216651,4200],{"class":2235},[2225,216653,216654,216656,216658,216660,216662],{"class":2227,"line":2356},[2225,216655,15608],{"class":2307},[2225,216657,2311],{"class":2235},[2225,216659,68576],{"class":2291},[2225,216661,4228],{"class":2239},[2225,216663,1707],{"class":2235},[2225,216665,216666,216668,216670,216672,216674,216676],{"class":2227,"line":2830},[2225,216667,4205],{"class":2307},[2225,216669,2311],{"class":2235},[2225,216671,2249],{"class":2235},[2225,216673,6408],{"class":2252},[2225,216675,2805],{"class":2235},[2225,216677,1707],{"class":2235},[2225,216679,216680,216682,216684,216686,216688],{"class":2227,"line":2861},[2225,216681,62228],{"class":2307},[2225,216683,2311],{"class":2235},[2225,216685,62034],{"class":2291},[2225,216687,4228],{"class":2239},[2225,216689,1707],{"class":2235},[2225,216691,216692],{"class":2227,"line":4246},[2225,216693,2285],{"emptyLinePlaceholder":2284},[2225,216695,216696,216698,216700],{"class":2227,"line":4253},[2225,216697,62245],{"class":2307},[2225,216699,2311],{"class":2235},[2225,216701,2301],{"class":2235},[2225,216703,216704,216706,216708,216710,216712,216714],{"class":2227,"line":4259},[2225,216705,89950],{"class":2307},[2225,216707,2311],{"class":2235},[2225,216709,45248],{"class":6527},[2225,216711,2263],{"class":2235},[2225,216713,19001],{"class":6527},[2225,216715,1707],{"class":2235},[2225,216717,216718,216720,216722,216724,216726,216728,216730,216732],{"class":2227,"line":6548},[2225,216719,89968],{"class":2307},[2225,216721,2311],{"class":2235},[2225,216723,89973],{"class":6527},[2225,216725,2263],{"class":2235},[2225,216727,44452],{"class":6527},[2225,216729,2263],{"class":2235},[2225,216731,19001],{"class":6527},[2225,216733,1707],{"class":2235},[2225,216735,216736,216738,216740,216742],{"class":2227,"line":6576},[2225,216737,89991],{"class":2307},[2225,216739,2311],{"class":2235},[2225,216741,2322],{"class":2321},[2225,216743,1707],{"class":2235},[2225,216745,216746,216749,216751,216753,216755,216757,216759,216761,216763,216765,216767,216769,216771,216773,216775,216777],{"class":2227,"line":6582},[2225,216747,216748],{"class":2307},"    requireReAuthFor",[2225,216750,2311],{"class":2235},[2225,216752,8667],{"class":2239},[2225,216754,2805],{"class":2235},[2225,216756,8080],{"class":2252},[2225,216758,2805],{"class":2235},[2225,216760,2298],{"class":2235},[2225,216762,2249],{"class":2235},[2225,216764,10493],{"class":2252},[2225,216766,2805],{"class":2235},[2225,216768,2298],{"class":2235},[2225,216770,2249],{"class":2235},[2225,216772,10859],{"class":2252},[2225,216774,2805],{"class":2235},[2225,216776,8803],{"class":2239},[2225,216778,1707],{"class":2235},[2225,216780,216781],{"class":2227,"line":6587},[2225,216782,19017],{"class":2235},[2225,216784,216785,216787],{"class":2227,"line":7174},[2225,216786,2359],{"class":2235},[2225,216788,2362],{"class":2239},[937,216790,216791],{},"Dev-unlock (separate, no strategy):",[2217,216793,216795],{"className":2219,"code":216794,"language":2221,"meta":1037,"style":1037},"import { enableDevUnlock, loadDevUnlock, isDevUnlockActive } from '@noy-db\u002Fhub'\n",[941,216796,216797],{"__ignoreMap":1037},[2225,216798,216799,216801,216803,216806,216808,216811,216813,216816,216818,216820,216822,216824],{"class":2227,"line":2228},[2225,216800,2232],{"class":2231},[2225,216802,2236],{"class":2235},[2225,216804,216805],{"class":2239}," enableDevUnlock",[2225,216807,2298],{"class":2235},[2225,216809,216810],{"class":2239}," loadDevUnlock",[2225,216812,2298],{"class":2235},[2225,216814,216815],{"class":2239}," isDevUnlockActive",[2225,216817,2243],{"class":2235},[2225,216819,2246],{"class":2231},[2225,216821,2249],{"class":2235},[2225,216823,1086],{"class":2252},[2225,216825,2256],{"class":2235},[1022,216827,29893],{"id":29892},[1591,216829,216831],{"id":216830},"create-and-resolve-a-session-token","Create and resolve a session token",[937,216833,216834,216837,216838,216840],{},[941,216835,216836],{},"createSession()"," wraps an already-unlocked keyring in a token; ",[941,216839,216505],{}," turns that token back into a keyring later, without re-running the unlock ceremony.",[2217,216842,216844],{"className":2219,"code":216843,"language":2221,"meta":1037,"style":1037},"import { createNoydb } from '@noy-db\u002Fhub'\nimport { withSession, createSession, resolveSession } from '@noy-db\u002Fhub\u002Fsession'\nimport { verifyPasswordSlot } from '@noy-db\u002Fon-password'\n\nconst db = await createNoydb({\n  store: myStore,\n  user: 'alice',\n  sessionStrategy: withSession(),\n})\n\n\u002F\u002F verifyPasswordSlot() is one unlock path — it returns an UnlockedKeyring\n\u002F\u002F (role, permissions, and the DEKs used to read\u002Fwrite each collection).\n\u002F\u002F WebAuthn, OIDC, and magic-link unlocks return the same shape.\nconst keyring = await verifyPasswordSlot(passwordSlot, 'correct horse battery staple')\n\nconst { token, sessionId } = await createSession(keyring, 'acme-corp', {\n  ttlMs: 15 * 60_000, \u002F\u002F defaults to 60 minutes if omitted\n})\n",[941,216845,216846,216864,216891,216910,216914,216930,216941,216955,216967,216973,216977,216982,216987,216992,217017,217021,217056,217073],{"__ignoreMap":1037},[2225,216847,216848,216850,216852,216854,216856,216858,216860,216862],{"class":2227,"line":2228},[2225,216849,2232],{"class":2231},[2225,216851,2236],{"class":2235},[2225,216853,2744],{"class":2239},[2225,216855,2243],{"class":2235},[2225,216857,2246],{"class":2231},[2225,216859,2249],{"class":2235},[2225,216861,1086],{"class":2252},[2225,216863,2256],{"class":2235},[2225,216865,216866,216868,216870,216872,216874,216876,216878,216881,216883,216885,216887,216889],{"class":2227,"line":1041},[2225,216867,2232],{"class":2231},[2225,216869,2236],{"class":2235},[2225,216871,62034],{"class":2239},[2225,216873,2298],{"class":2235},[2225,216875,97049],{"class":2239},[2225,216877,2298],{"class":2235},[2225,216879,216880],{"class":2239}," resolveSession",[2225,216882,2243],{"class":2235},[2225,216884,2246],{"class":2231},[2225,216886,2249],{"class":2235},[2225,216888,62043],{"class":2252},[2225,216890,2256],{"class":2235},[2225,216892,216893,216895,216897,216900,216902,216904,216906,216908],{"class":2227,"line":1038},[2225,216894,2232],{"class":2231},[2225,216896,2236],{"class":2235},[2225,216898,216899],{"class":2239}," verifyPasswordSlot",[2225,216901,2243],{"class":2235},[2225,216903,2246],{"class":2231},[2225,216905,2249],{"class":2235},[2225,216907,17035],{"class":2252},[2225,216909,2256],{"class":2235},[2225,216911,216912],{"class":2227,"line":2288},[2225,216913,2285],{"emptyLinePlaceholder":2284},[2225,216915,216916,216918,216920,216922,216924,216926,216928],{"class":2227,"line":2304},[2225,216917,2775],{"class":2774},[2225,216919,2778],{"class":2239},[2225,216921,2781],{"class":2235},[2225,216923,2784],{"class":2231},[2225,216925,2744],{"class":2291},[2225,216927,2789],{"class":2239},[2225,216929,4200],{"class":2235},[2225,216931,216932,216934,216936,216939],{"class":2227,"line":2356},[2225,216933,15608],{"class":2307},[2225,216935,2311],{"class":2235},[2225,216937,216938],{"class":2239}," myStore",[2225,216940,1707],{"class":2235},[2225,216942,216943,216945,216947,216949,216951,216953],{"class":2227,"line":2830},[2225,216944,4205],{"class":2307},[2225,216946,2311],{"class":2235},[2225,216948,2249],{"class":2235},[2225,216950,6408],{"class":2252},[2225,216952,2805],{"class":2235},[2225,216954,1707],{"class":2235},[2225,216956,216957,216959,216961,216963,216965],{"class":2227,"line":2861},[2225,216958,62228],{"class":2307},[2225,216960,2311],{"class":2235},[2225,216962,62034],{"class":2291},[2225,216964,4228],{"class":2239},[2225,216966,1707],{"class":2235},[2225,216968,216969,216971],{"class":2227,"line":4246},[2225,216970,2359],{"class":2235},[2225,216972,2362],{"class":2239},[2225,216974,216975],{"class":2227,"line":4253},[2225,216976,2285],{"emptyLinePlaceholder":2284},[2225,216978,216979],{"class":2227,"line":4259},[2225,216980,216981],{"class":2763},"\u002F\u002F verifyPasswordSlot() is one unlock path — it returns an UnlockedKeyring\n",[2225,216983,216984],{"class":2227,"line":6548},[2225,216985,216986],{"class":2763},"\u002F\u002F (role, permissions, and the DEKs used to read\u002Fwrite each collection).\n",[2225,216988,216989],{"class":2227,"line":6576},[2225,216990,216991],{"class":2763},"\u002F\u002F WebAuthn, OIDC, and magic-link unlocks return the same shape.\n",[2225,216993,216994,216996,216998,217000,217002,217004,217007,217009,217011,217013,217015],{"class":2227,"line":6582},[2225,216995,2775],{"class":2774},[2225,216997,44845],{"class":2239},[2225,216999,2781],{"class":2235},[2225,217001,2784],{"class":2231},[2225,217003,216899],{"class":2291},[2225,217005,217006],{"class":2239},"(passwordSlot",[2225,217008,2298],{"class":2235},[2225,217010,2249],{"class":2235},[2225,217012,40260],{"class":2252},[2225,217014,2805],{"class":2235},[2225,217016,2362],{"class":2239},[2225,217018,217019],{"class":2227,"line":6587},[2225,217020,2285],{"emptyLinePlaceholder":2284},[2225,217022,217023,217025,217027,217029,217031,217033,217035,217037,217039,217041,217043,217045,217047,217050,217052,217054],{"class":2227,"line":7174},[2225,217024,2775],{"class":2774},[2225,217026,2236],{"class":2235},[2225,217028,97096],{"class":2239},[2225,217030,2298],{"class":2235},[2225,217032,97101],{"class":2239},[2225,217034,2359],{"class":2235},[2225,217036,6346],{"class":2235},[2225,217038,2784],{"class":2231},[2225,217040,97049],{"class":2291},[2225,217042,45008],{"class":2239},[2225,217044,2298],{"class":2235},[2225,217046,2249],{"class":2235},[2225,217048,217049],{"class":2252},"acme-corp",[2225,217051,2805],{"class":2235},[2225,217053,2298],{"class":2235},[2225,217055,2301],{"class":2235},[2225,217057,217058,217060,217062,217064,217066,217068,217070],{"class":2227,"line":7180},[2225,217059,69818],{"class":2307},[2225,217061,2311],{"class":2235},[2225,217063,45248],{"class":6527},[2225,217065,2263],{"class":2235},[2225,217067,19001],{"class":6527},[2225,217069,2298],{"class":2235},[2225,217071,217072],{"class":2763}," \u002F\u002F defaults to 60 minutes if omitted\n",[2225,217074,217075,217077],{"class":2227,"line":7220},[2225,217076,2359],{"class":2235},[2225,217078,2362],{"class":2239},[2217,217080,217082],{"className":6604,"code":217081,"language":141,"meta":1037,"style":1037},"{\n  \"_noydb_session\": 1,\n  \"sessionId\": \"01J8Z3F9R2K4Q6T8V0W2X4Y6Z8\",\n  \"userId\": \"alice\",\n  \"vault\": \"acme-corp\",\n  \"role\": \"owner\",\n  \"expiresAt\": \"2026-07-09T13:15:00.000Z\",\n  \"wrappedKek\": \"Qk3f9CjH2s0LpV…\",\n  \"kekIv\": \"u8xQnR2b…\"\n}\n",[941,217083,217084,217088,217103,217122,217140,217158,217176,217195,217215,217233],{"__ignoreMap":1037},[2225,217085,217086],{"class":2227,"line":2228},[2225,217087,4200],{"class":2235},[2225,217089,217090,217092,217095,217097,217099,217101],{"class":2227,"line":1041},[2225,217091,9368],{"class":2235},[2225,217093,217094],{"class":2774},"_noydb_session",[2225,217096,6620],{"class":2235},[2225,217098,2311],{"class":2235},[2225,217100,9377],{"class":6527},[2225,217102,1707],{"class":2235},[2225,217104,217105,217107,217109,217111,217113,217115,217118,217120],{"class":2227,"line":1038},[2225,217106,9368],{"class":2235},[2225,217108,21033],{"class":2774},[2225,217110,6620],{"class":2235},[2225,217112,2311],{"class":2235},[2225,217114,6614],{"class":2235},[2225,217116,217117],{"class":2252},"01J8Z3F9R2K4Q6T8V0W2X4Y6Z8",[2225,217119,6620],{"class":2235},[2225,217121,1707],{"class":2235},[2225,217123,217124,217126,217128,217130,217132,217134,217136,217138],{"class":2227,"line":2288},[2225,217125,9368],{"class":2235},[2225,217127,10749],{"class":2774},[2225,217129,6620],{"class":2235},[2225,217131,2311],{"class":2235},[2225,217133,6614],{"class":2235},[2225,217135,6408],{"class":2252},[2225,217137,6620],{"class":2235},[2225,217139,1707],{"class":2235},[2225,217141,217142,217144,217146,217148,217150,217152,217154,217156],{"class":2227,"line":2304},[2225,217143,9368],{"class":2235},[2225,217145,2694],{"class":2774},[2225,217147,6620],{"class":2235},[2225,217149,2311],{"class":2235},[2225,217151,6614],{"class":2235},[2225,217153,217049],{"class":2252},[2225,217155,6620],{"class":2235},[2225,217157,1707],{"class":2235},[2225,217159,217160,217162,217164,217166,217168,217170,217172,217174],{"class":2227,"line":2356},[2225,217161,9368],{"class":2235},[2225,217163,16785],{"class":2774},[2225,217165,6620],{"class":2235},[2225,217167,2311],{"class":2235},[2225,217169,6614],{"class":2235},[2225,217171,9898],{"class":2252},[2225,217173,6620],{"class":2235},[2225,217175,1707],{"class":2235},[2225,217177,217178,217180,217182,217184,217186,217188,217191,217193],{"class":2227,"line":2830},[2225,217179,9368],{"class":2235},[2225,217181,44339],{"class":2774},[2225,217183,6620],{"class":2235},[2225,217185,2311],{"class":2235},[2225,217187,6614],{"class":2235},[2225,217189,217190],{"class":2252},"2026-07-09T13:15:00.000Z",[2225,217192,6620],{"class":2235},[2225,217194,1707],{"class":2235},[2225,217196,217197,217199,217202,217204,217206,217208,217211,217213],{"class":2227,"line":2861},[2225,217198,9368],{"class":2235},[2225,217200,217201],{"class":2774},"wrappedKek",[2225,217203,6620],{"class":2235},[2225,217205,2311],{"class":2235},[2225,217207,6614],{"class":2235},[2225,217209,217210],{"class":2252},"Qk3f9CjH2s0LpV…",[2225,217212,6620],{"class":2235},[2225,217214,1707],{"class":2235},[2225,217216,217217,217219,217222,217224,217226,217228,217231],{"class":2227,"line":4246},[2225,217218,9368],{"class":2235},[2225,217220,217221],{"class":2774},"kekIv",[2225,217223,6620],{"class":2235},[2225,217225,2311],{"class":2235},[2225,217227,6614],{"class":2235},[2225,217229,217230],{"class":2252},"u8xQnR2b…",[2225,217232,9449],{"class":2235},[2225,217234,217235],{"class":2227,"line":4253},[2225,217236,7351],{"class":2235},[937,217238,217239,217241,217242,217244,217245,217247,217248,217251,217252,217254],{},[941,217240,44489],{}," is JSON-serializable — store it in ",[941,217243,216501],{},". Despite the field name, ",[941,217246,217201],{}," does not carry the KEK itself (the KEK is a non-extractable key and can't be exported); it holds the vault's DEK set plus role\u002Fpermissions, encrypted with a separate, non-extractable ",[950,217249,217250],{},"session key"," that lives only in a module-level ",[941,217253,8002],{}," for the life of the tab. Closing the tab drops the session key and makes every token issued in it permanently unresolvable — that's the built-in \"closing the tab logs you out\" behavior.",[2217,217256,217258],{"className":2219,"code":217257,"language":2221,"meta":1037,"style":1037},"\u002F\u002F Later — same tab, e.g. after a client-side navigation:\nconst keyringAgain = await resolveSession(token)\n\u002F\u002F → { userId: 'alice', displayName: 'Alice', role: 'owner', permissions: { 'acme-corp': 'rw' }, deks: Map(2) {…}, kek: null, salt: Uint8Array(32), authenticators: [] }\n",[941,217259,217260,217265,217281],{"__ignoreMap":1037},[2225,217261,217262],{"class":2227,"line":2228},[2225,217263,217264],{"class":2763},"\u002F\u002F Later — same tab, e.g. after a client-side navigation:\n",[2225,217266,217267,217269,217272,217274,217276,217278],{"class":2227,"line":1041},[2225,217268,2775],{"class":2774},[2225,217270,217271],{"class":2239}," keyringAgain ",[2225,217273,2781],{"class":2235},[2225,217275,2784],{"class":2231},[2225,217277,216880],{"class":2291},[2225,217279,217280],{"class":2239},"(token)\n",[2225,217282,217283],{"class":2227,"line":1038},[2225,217284,217285],{"class":2763},"\u002F\u002F → { userId: 'alice', displayName: 'Alice', role: 'owner', permissions: { 'acme-corp': 'rw' }, deks: Map(2) {…}, kek: null, salt: Uint8Array(32), authenticators: [] }\n",[937,217287,217288,152723,217290,217292,217293,217296,217297,6789,217299,217302,217303,1520],{},[941,217289,45415],{},[941,217291,6651],{}," — sessions only need the DEKs to read\u002Fwrite records, not the KEK. If ",[941,217294,217295],{},"token.expiresAt"," has passed, ",[941,217298,216505],{},[941,217300,217301],{},"SessionExpiredError","; if the session key isn't in the store (tab reloaded, or the session was revoked), it throws ",[941,217304,217305],{},"SessionNotFoundError",[1591,217307,217309],{"id":217308},"validate-a-policy-then-enforce-it-yourself","Validate a policy, then enforce it yourself",[937,217311,127199,217312,10313,217314,217316,217317,217320,217321,217323,217324,217326,217327,1520],{},[941,217313,20690],{},[941,217315,2134],{}," validates it at construction time — ",[941,217318,217319],{},"validateSessionPolicy()"," (called automatically when ",[941,217322,21321],{}," is wired in) throws a plain ",[941,217325,12126],{}," if the shape is malformed, e.g. ",[941,217328,217329],{},"idleTimeoutMs >= absoluteTimeoutMs",[937,217331,217332,217333,217336,217337,217339,217340,968,217342,968,217344,2462,217346,217349,217350,217352,217353,217355,217356,217358,217359,2311],{},"Validation is as far as the wiring goes today. Idle\u002Fabsolute timeouts and the ",[941,217334,217335],{},"requireReAuthFor"," gate are ",[950,217338,1004],{}," currently enforced automatically: ",[941,217341,21197],{},[941,217343,21200],{},[941,217345,10734],{},[941,217347,217348],{},"db.changeSecret()"," do not check ",[941,217351,20690],{}," on their own, and there is no public API to attach an enforcer to a running ",[941,217354,62],{}," instance. If you need that behavior today, drive a ",[941,217357,216513],{}," yourself with ",[941,217360,55834],{},[2217,217362,217364],{"className":2219,"code":217363,"language":2221,"meta":1037,"style":1037},"import { createEnforcer } from '@noy-db\u002Fhub\u002Fsession'\n\nconst enforcer = createEnforcer({\n  policy: {\n    idleTimeoutMs: 15 * 60_000,\n    absoluteTimeoutMs: 8 * 60 * 60_000,\n    requireReAuthFor: ['grant', 'rotate'],\n  },\n  sessionId,\n  onRevoke: (reason) => {\n    \u002F\u002F 'idle' | 'absolute' | 'background' — decide what your app does,\n    \u002F\u002F e.g. clear local UI state and prompt for re-unlock.\n  },\n})\n\nenforcer.touch() \u002F\u002F call at the top of every operation you want the idle timer to track\n\nenforcer.checkOperation('grant')\n\u002F\u002F → throws SessionPolicyError: Operation \"grant\" requires re-authentication per the active session policy.\n\u002F\u002F (or SessionExpiredError, thrown defensively, if absoluteTimeoutMs has already elapsed)\n\nawait db.grant('acme-corp', { grantee: 'bob@example.com', role: 'operator' })\n",[941,217365,217366,217384,217388,217402,217410,217424,217442,217468,217472,217478,217494,217499,217504,217508,217514,217518,217531,217535,217553,217558,217563,217567],{"__ignoreMap":1037},[2225,217367,217368,217370,217372,217374,217376,217378,217380,217382],{"class":2227,"line":2228},[2225,217369,2232],{"class":2231},[2225,217371,2236],{"class":2235},[2225,217373,97054],{"class":2239},[2225,217375,2243],{"class":2235},[2225,217377,2246],{"class":2231},[2225,217379,2249],{"class":2235},[2225,217381,62043],{"class":2252},[2225,217383,2256],{"class":2235},[2225,217385,217386],{"class":2227,"line":1041},[2225,217387,2285],{"emptyLinePlaceholder":2284},[2225,217389,217390,217392,217394,217396,217398,217400],{"class":2227,"line":1038},[2225,217391,2775],{"class":2774},[2225,217393,97148],{"class":2239},[2225,217395,2781],{"class":2235},[2225,217397,97054],{"class":2291},[2225,217399,2789],{"class":2239},[2225,217401,4200],{"class":2235},[2225,217403,217404,217406,217408],{"class":2227,"line":2288},[2225,217405,18899],{"class":2307},[2225,217407,2311],{"class":2235},[2225,217409,2301],{"class":2235},[2225,217411,217412,217414,217416,217418,217420,217422],{"class":2227,"line":2304},[2225,217413,89950],{"class":2307},[2225,217415,2311],{"class":2235},[2225,217417,45248],{"class":6527},[2225,217419,2263],{"class":2235},[2225,217421,19001],{"class":6527},[2225,217423,1707],{"class":2235},[2225,217425,217426,217428,217430,217432,217434,217436,217438,217440],{"class":2227,"line":2356},[2225,217427,89968],{"class":2307},[2225,217429,2311],{"class":2235},[2225,217431,89973],{"class":6527},[2225,217433,2263],{"class":2235},[2225,217435,44452],{"class":6527},[2225,217437,2263],{"class":2235},[2225,217439,19001],{"class":6527},[2225,217441,1707],{"class":2235},[2225,217443,217444,217446,217448,217450,217452,217454,217456,217458,217460,217462,217464,217466],{"class":2227,"line":2830},[2225,217445,216748],{"class":2307},[2225,217447,2311],{"class":2235},[2225,217449,8667],{"class":2239},[2225,217451,2805],{"class":2235},[2225,217453,10493],{"class":2252},[2225,217455,2805],{"class":2235},[2225,217457,2298],{"class":2235},[2225,217459,2249],{"class":2235},[2225,217461,10859],{"class":2252},[2225,217463,2805],{"class":2235},[2225,217465,8803],{"class":2239},[2225,217467,1707],{"class":2235},[2225,217469,217470],{"class":2227,"line":2861},[2225,217471,19017],{"class":2235},[2225,217473,217474,217476],{"class":2227,"line":4246},[2225,217475,97198],{"class":2239},[2225,217477,1707],{"class":2235},[2225,217479,217480,217482,217484,217486,217488,217490,217492],{"class":2227,"line":4253},[2225,217481,97205],{"class":2291},[2225,217483,2311],{"class":2235},[2225,217485,953],{"class":2235},[2225,217487,43668],{"class":7052},[2225,217489,1381],{"class":2235},[2225,217491,8116],{"class":2774},[2225,217493,2301],{"class":2235},[2225,217495,217496],{"class":2227,"line":4259},[2225,217497,217498],{"class":2763},"    \u002F\u002F 'idle' | 'absolute' | 'background' — decide what your app does,\n",[2225,217500,217501],{"class":2227,"line":6548},[2225,217502,217503],{"class":2763},"    \u002F\u002F e.g. clear local UI state and prompt for re-unlock.\n",[2225,217505,217506],{"class":2227,"line":6576},[2225,217507,19017],{"class":2235},[2225,217509,217510,217512],{"class":2227,"line":6582},[2225,217511,2359],{"class":2235},[2225,217513,2362],{"class":2239},[2225,217515,217516],{"class":2227,"line":6587},[2225,217517,2285],{"emptyLinePlaceholder":2284},[2225,217519,217520,217522,217524,217526,217528],{"class":2227,"line":7174},[2225,217521,97241],{"class":2239},[2225,217523,1520],{"class":2235},[2225,217525,97246],{"class":2291},[2225,217527,19132],{"class":2239},[2225,217529,217530],{"class":2763},"\u002F\u002F call at the top of every operation you want the idle timer to track\n",[2225,217532,217533],{"class":2227,"line":7180},[2225,217534,2285],{"emptyLinePlaceholder":2284},[2225,217536,217537,217539,217541,217543,217545,217547,217549,217551],{"class":2227,"line":7220},[2225,217538,97241],{"class":2239},[2225,217540,1520],{"class":2235},[2225,217542,97271],{"class":2291},[2225,217544,2789],{"class":2239},[2225,217546,2805],{"class":2235},[2225,217548,10493],{"class":2252},[2225,217550,2805],{"class":2235},[2225,217552,2362],{"class":2239},[2225,217554,217555],{"class":2227,"line":7225},[2225,217556,217557],{"class":2763},"\u002F\u002F → throws SessionPolicyError: Operation \"grant\" requires re-authentication per the active session policy.\n",[2225,217559,217560],{"class":2227,"line":7231},[2225,217561,217562],{"class":2763},"\u002F\u002F (or SessionExpiredError, thrown defensively, if absoluteTimeoutMs has already elapsed)\n",[2225,217564,217565],{"class":2227,"line":7267},[2225,217566,2285],{"emptyLinePlaceholder":2284},[2225,217568,217569,217571,217573,217575,217577,217579,217581,217583,217585,217587,217589,217592,217594,217596,217599,217601,217603,217605,217607,217609,217611,217613,217615],{"class":2227,"line":7272},[2225,217570,6500],{"class":2231},[2225,217572,2842],{"class":2239},[2225,217574,1520],{"class":2235},[2225,217576,10493],{"class":2291},[2225,217578,2789],{"class":2239},[2225,217580,2805],{"class":2235},[2225,217582,217049],{"class":2252},[2225,217584,2805],{"class":2235},[2225,217586,2298],{"class":2235},[2225,217588,2236],{"class":2235},[2225,217590,217591],{"class":2307}," grantee",[2225,217593,2311],{"class":2235},[2225,217595,2249],{"class":2235},[2225,217597,217598],{"class":2252},"bob@example.com",[2225,217600,2805],{"class":2235},[2225,217602,2298],{"class":2235},[2225,217604,34944],{"class":2307},[2225,217606,2311],{"class":2235},[2225,217608,2249],{"class":2235},[2225,217610,9907],{"class":2252},[2225,217612,2805],{"class":2235},[2225,217614,2243],{"class":2235},[2225,217616,2362],{"class":2239},[937,217618,217619,217622,217623,53891,217626,217629],{},[941,217620,217621],{},"checkOperation()"," is synchronous and only inspects the policy you passed in — call it yourself immediately before any operation you want gated; ",[941,217624,217625],{},"absoluteTimeoutMs",[941,217627,217628],{},"idleTimeoutMs"," once the deadline passes, regardless of activity.",[1591,217631,217633],{"id":217632},"check-liveness-and-revoke","Check liveness and revoke",[2217,217635,217637],{"className":2219,"code":217636,"language":2221,"meta":1037,"style":1037},"import { isSessionAlive, revokeSession, activeSessionCount } from '@noy-db\u002Fhub\u002Fsession'\n\nisSessionAlive(token)     \u002F\u002F → true\nrevokeSession(sessionId)  \u002F\u002F explicit logout — removes the session key from the store\nisSessionAlive(token)     \u002F\u002F → false\nactiveSessionCount()      \u002F\u002F → 0\n",[941,217638,217639,217668,217672,217682,217693,217701],{"__ignoreMap":1037},[2225,217640,217641,217643,217645,217648,217650,217653,217655,217658,217660,217662,217664,217666],{"class":2227,"line":2228},[2225,217642,2232],{"class":2231},[2225,217644,2236],{"class":2235},[2225,217646,217647],{"class":2239}," isSessionAlive",[2225,217649,2298],{"class":2235},[2225,217651,217652],{"class":2239}," revokeSession",[2225,217654,2298],{"class":2235},[2225,217656,217657],{"class":2239}," activeSessionCount",[2225,217659,2243],{"class":2235},[2225,217661,2246],{"class":2231},[2225,217663,2249],{"class":2235},[2225,217665,62043],{"class":2252},[2225,217667,2256],{"class":2235},[2225,217669,217670],{"class":2227,"line":1041},[2225,217671,2285],{"emptyLinePlaceholder":2284},[2225,217673,217674,217677,217680],{"class":2227,"line":1038},[2225,217675,217676],{"class":2291},"isSessionAlive",[2225,217678,217679],{"class":2239},"(token)     ",[2225,217681,77590],{"class":2763},[2225,217683,217684,217687,217690],{"class":2227,"line":2288},[2225,217685,217686],{"class":2291},"revokeSession",[2225,217688,217689],{"class":2239},"(sessionId)  ",[2225,217691,217692],{"class":2763},"\u002F\u002F explicit logout — removes the session key from the store\n",[2225,217694,217695,217697,217699],{"class":2227,"line":2304},[2225,217696,217676],{"class":2291},[2225,217698,217679],{"class":2239},[2225,217700,141824],{"class":2763},[2225,217702,217703,217706,217709],{"class":2227,"line":2356},[2225,217704,217705],{"class":2291},"activeSessionCount",[2225,217707,217708],{"class":2239},"()      ",[2225,217710,217711],{"class":2763},"\u002F\u002F → 0\n",[1022,217713,101012],{"id":41259},[1111,217715,217716,217729,217741,217749,217758,217766,217774,217797,217808,217829,217858,217873],{},[1114,217717,217718,1746,217721,217724,217725,217728],{},[941,217719,217720],{},"createSession(keyring, vault, options?)",[941,217722,217723],{},"Promise\u003C{ token: SessionToken, sessionId: string }>"," — wrap an unlocked keyring in a serializable token. ",[941,217726,217727],{},"options.ttlMs"," defaults to 60 minutes.",[1114,217730,217731,1746,217734,217736,217737,1261,217739,1520],{},[941,217732,217733],{},"resolveSession(token)",[941,217735,137490],{}," — turn a token back into a keyring. Throws ",[941,217738,217301],{},[941,217740,217305],{},[1114,217742,217743,1746,217746,217748],{},[941,217744,217745],{},"revokeSession(sessionId)",[941,217747,7165],{}," — drop one session's key from the store.",[1114,217750,217751,1746,217753,217755,217756,957],{},[941,217752,216536],{},[941,217754,7165],{}," — clear every active session (called by ",[941,217757,13331],{},[1114,217759,217760,1746,217763,217765],{},[941,217761,217762],{},"isSessionAlive(token)",[941,217764,16861],{}," — metadata-only check (not expired + key still in the store); does not decrypt anything.",[1114,217767,217768,1746,217771,217773],{},[941,217769,217770],{},"activeSessionCount()",[941,217772,20636],{}," — size of the session-key store; useful for diagnostics\u002Ftests.",[1114,217775,217776,1746,217779,217781,217782,217784,217785,217787,217788,217790,217791,217794,217795,1520],{},[941,217777,217778],{},"validateSessionPolicy(policy)",[941,217780,7165],{}," — call-time validation of a ",[941,217783,20702],{},"; throws a plain ",[941,217786,12126],{}," (not a ",[941,217789,140888],{},") on a malformed policy. Runs automatically when ",[941,217792,217793],{},"createNoydb({ sessionPolicy })"," is used with ",[941,217796,21321],{},[1114,217798,217799,1746,217801,217804,217805,1520],{},[941,217800,21321],{},[941,217802,217803],{},"SessionStrategy"," — the opt-in factory passed to ",[941,217806,217807],{},"createNoydb({ sessionStrategy })",[1114,217809,217810,1746,217813,217815,217816,217819,217820,217822,217823,1625,217826,217828],{},[941,217811,217812],{},"createEnforcer(opts)",[941,217814,216513],{}," — builds a stateful timer\u002Flistener object for one session from ",[941,217817,217818],{},"{ policy, sessionId, onRevoke }",". Nothing in ",[941,217821,62],{}," installs one automatically today; call it yourself and drive it with ",[941,217824,217825],{},"touch()",[941,217827,217621],{}," if you want idle\u002Fabsolute-timeout or re-auth-gate enforcement (see Usage above).",[1114,217830,217831,217833,217834,217836,217837,217839,217840,217843,217844,217846,217847,217850,217851,1625,217854,217857],{},[941,217832,216513],{}," — the class ",[941,217835,55834],{}," returns: ",[941,217838,217825],{}," (reset the idle timer), ",[941,217841,217842],{},"checkOperation(op)"," (throw if ",[941,217845,61313],{}," needs re-auth or the absolute timeout has passed), ",[941,217848,217849],{},"destroy()"," (tear down timers\u002Flisteners), plus ",[941,217852,217853],{},"idleMs",[941,217855,217856],{},"ageMs"," getters.",[1114,217859,217860,1261,217863,1261,217866,1261,217869,217872],{},[941,217861,217862],{},"enableDevUnlock(vault, userId, keyring, options)",[941,217864,217865],{},"loadDevUnlock(vault, userId, options?)",[941,217867,217868],{},"clearDevUnlock(vault, userId, options?)",[941,217870,217871],{},"isDevUnlockActive(vault, userId, options?)"," — development-only persistent unlock (see below).",[1114,217874,217875,968,217877,968,217879,217882,217883,217885,217886,217888],{},[941,217876,217301],{},[941,217878,217305],{},[941,217880,217881],{},"SessionPolicyError"," — re-exported here so ",[941,217884,7490],{}," checks don't need the root ",[941,217887,1086],{}," barrel.",[1591,217890,217892],{"id":217891},"sessionpolicy",[941,217893,20702],{},[1338,217895,217896,217908],{},[1341,217897,217898],{},[1344,217899,217900,217902,217904,217906],{},[1347,217901,101060],{},[1347,217903,20194],{},[1347,217905,20197],{},[1347,217907,116059],{},[1357,217909,217910,217928,217948,217969],{},[1344,217911,217912,217916,217920,217925],{},[1362,217913,217914],{},[941,217915,217628],{},[1362,217917,217918],{},[941,217919,20636],{},[1362,217921,217922,217924],{},[941,217923,18316],{}," (no idle limit)",[1362,217926,217927],{},"Session is revoked if no operation runs for this long; the clock resets on every successful operation.",[1344,217929,217930,217934,217938,217943],{},[1362,217931,217932],{},[941,217933,217625],{},[1362,217935,217936],{},[941,217937,20636],{},[1362,217939,217940,217942],{},[941,217941,18316],{}," (no absolute limit)",[1362,217944,217945,217946,1520],{},"Session is revoked this long after creation, regardless of activity. Overrides ",[941,217947,217628],{},[1344,217949,217950,217954,217959,217963],{},[1362,217951,217952],{},[941,217953,217335],{},[1362,217955,217956],{},[941,217957,217958],{},"readonly ('export' | 'grant' | 'revoke' | 'rotate' | 'changeSecret')[]",[1362,217960,217961],{},[941,217962,7262],{},[1362,217964,217965,217966,217968],{},"Operations that throw ",[941,217967,217881],{}," until the user re-unlocks, even with a live session.",[1344,217970,217971,217976,217980,217984],{},[1362,217972,217973],{},[941,217974,217975],{},"lockOnBackground",[1362,217977,217978],{},[941,217979,16861],{},[1362,217981,217982],{},[941,217983,2560],{},[1362,217985,217986,217987,968,217990,217993],{},"Revoke the session when the tab is hidden (",[941,217988,217989],{},"visibilitychange",[941,217991,217992],{},"document.hidden === true","). No-op outside a browser.",[937,217995,217996,217998,217999,218001],{},[941,217997,217319],{}," also rejects a policy where ",[941,218000,217329],{}," — the idle window has to fit inside the absolute one.",[1591,218003,218005],{"id":218004},"createsessionoptions",[941,218006,218007],{},"CreateSessionOptions",[1338,218009,218010,218022],{},[1341,218011,218012],{},[1344,218013,218014,218016,218018,218020],{},[1347,218015,101060],{},[1347,218017,20194],{},[1347,218019,20197],{},[1347,218021,116059],{},[1357,218023,218024],{},[1344,218025,218026,218030,218034,218040],{},[1362,218027,218028],{},[941,218029,75026],{},[1362,218031,218032],{},[941,218033,20636],{},[1362,218035,218036,218039],{},[941,218037,218038],{},"3_600_000"," (60 minutes)",[1362,218041,218042,218043,6789,218045,218047],{},"How long the token is valid; ",[941,218044,216505],{},[941,218046,217301],{}," past this.",[1591,218049,218051],{"id":218050},"devunlockoptions",[941,218052,218053],{},"DevUnlockOptions",[1338,218055,218056,218068],{},[1341,218057,218058],{},[1344,218059,218060,218062,218064,218066],{},[1347,218061,101060],{},[1347,218063,20194],{},[1347,218065,20197],{},[1347,218067,116059],{},[1357,218069,218070,218093],{},[1344,218071,218072,218077,218081,218084],{},[1362,218073,218074],{},[941,218075,218076],{},"acknowledge",[1362,218078,218079],{},[941,218080,7259],{},[1362,218082,218083],{},"— required",[1362,218085,218086,218087,4765,218090,20243],{},"Must be exactly ",[941,218088,218089],{},"'I-UNDERSTAND-THIS-DISABLES-UNLOCK-SECURITY'",[941,218091,218092],{},"enableDevUnlock()",[1344,218094,218095,218100,218104,218108],{},[1362,218096,218097],{},[941,218098,218099],{},"persistAcrossTabs",[1362,218101,218102],{},[941,218103,16861],{},[1362,218105,218106],{},[941,218107,2560],{},[1362,218109,218110,218112,218113,218115,218116,218112,218118,1520],{},[941,218111,2560],{}," stores in ",[941,218114,216501],{}," (cleared on tab close); ",[941,218117,11790],{},[941,218119,41955],{},[1022,218121,218123],{"id":218122},"dev-unlock","Dev-unlock",[937,218125,218126,1261,218128,218131,218132,218135,218136,218139,218140,1625,218143,1625,218146,1625,218149,218152,218153,218155,218156,218159,218160,218163,218164,218166,218167,1520],{},[941,218127,218092],{},[941,218129,218130],{},"loadDevUnlock()"," skip the unlock ceremony entirely on every page load by writing the keyring's DEKs to browser storage — it exists purely to remove secret re-entry from the hot-reload inner loop. It refuses to run unless ",[941,218133,218134],{},"NODE_ENV"," is not ",[941,218137,218138],{},"production"," and the hostname is ",[941,218141,218142],{},"localhost",[941,218144,218145],{},"127.0.0.1",[941,218147,218148],{},"::1",[941,218150,218151],{},"*.local",", requires the exact ",[941,218154,218076],{}," string above, and prints an unsuppressable console warning on first use. ",[941,218157,218158],{},"clearDevUnlock()"," removes the stored payload; ",[941,218161,218162],{},"isDevUnlockActive()"," checks whether one exists. It is a separate named import from ",[941,218165,1086],{}," and tree-shakes independently of ",[941,218168,21321],{},[1022,218170,101425],{"id":101424},[1111,218172,218173,218185,218190],{},[1114,218174,218175,218176,2419,218178,21348,218180,218182,218183,1520],{},"Setting ",[941,218177,20690],{},[941,218179,12780],{},[941,218181,96503],{}," throws at construction time, with a pointer to ",[941,218184,62043],{},[1114,218186,218187,218189],{},[941,218188,55834],{}," throws under the same gate.",[1114,218191,218192,218194],{},[941,218193,216536],{}," is a silent no-op (the registry was never populated).",[1022,218196,114684],{"id":114683},[1111,218198,218199,218209,218229],{},[1114,218200,218201,964,218203,218205,218206,218208],{},[950,218202,868],{},[941,218204,216505],{}," restores the same role\u002Fpermissions team assigned at unlock; gating grant\u002Frevoke\u002Frotate behind re-auth requires driving a ",[941,218207,216513],{}," yourself (see Usage)",[1114,218210,218211,218215,218216,218219,218220,218222,218223,218225,218226,218228],{},[950,218212,218213],{},[1007,218214,625],{"href":626}," — a cloud-store-backed app that gates ",[1092,218217,218218],{},"storage-backend"," access with ",[941,218221,113132],{}," typically also gates ",[1092,218224,41270],{}," sessions with ",[941,218227,21321],{},"; the two are independent",[1114,218230,218231,218234,218235,21112,218237],{},[950,218232,218233],{},"on-webauthn \u002F on-oidc \u002F on-magic-link \u002F on-password"," (separate packages) — unlock paths that produce the ",[941,218236,11493],{},[941,218238,216836],{},[1022,218240,6825],{"id":6824},[1111,218242,218243,218251,218257,218284],{},[1114,218244,218245,218247,218248,218250],{},[941,218246,217975],{}," registers a ",[941,218249,217989],{}," listener; no-op in non-browser environments.",[1114,218252,218253,218254,218256],{},"Absolute timeout overrides idle timeout — you can't extend a session past ",[941,218255,217625],{}," no matter how active you are.",[1114,218258,218259,6789,218262,218264,218265,218267,218268,218270,218271,968,218273,968,218275,2462,218277,218280,218281,218283],{},[941,218260,218261],{},"PolicyEnforcer.checkOperation(op)",[941,218263,217881],{}," for an ",[941,218266,61313],{}," listed in ",[941,218269,217335],{}," — but only when your own code calls it (see Usage). ",[941,218272,10319],{},[941,218274,10322],{},[941,218276,10325],{},[941,218278,218279],{},"db.changeSecret"," do not call it on their own; there is currently no public way to attach an enforcer to a ",[941,218282,62],{}," instance.",[1114,218285,218286],{},"The session key store is module-level and tab-scoped: it does not survive a full page reload, and sessions from one tab are not resolvable in another.",[1022,218288,2621],{"id":2620},[1111,218290,218291,218296,218300,218311],{},[1114,218292,218293,218295],{},[1007,218294,751],{"href":752}," — planned hub-level observability (timings, cache stats,\nsync stats); session lifecycle timing is the kind of signal it would surface",[1114,218297,218298,61744],{},[1007,218299,597],{"href":598},[1114,218301,218302,968,218305,968,218308],{},[941,218303,218304],{},"__tests__\u002Fsession.test.ts",[941,218306,218307],{},"__tests__\u002Fsession-policy.test.ts",[941,218309,218310],{},"__tests__\u002Fdev-unlock.test.ts",[1114,218312,218313,218314,1520],{},"Proven by the recipe walkthrough ",[1007,218315,218317],{"href":94620,"rel":218316},[3371],[941,218318,218319],{},"recipe-unlock-ceremonies",[2645,218321,218322],{},"html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}html pre.shiki code .sfNiH, html code.shiki .sfNiH{--shiki-light:#FF5370;--shiki-default:#FF9CAC;--shiki-dark:#FF9CAC}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html pre.shiki code .sHdIc, html code.shiki .sHdIc{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#EEFFFF;--shiki-default-font-style:italic;--shiki-dark:#BABED8;--shiki-dark-font-style:italic}",{"title":1037,"searchDepth":1038,"depth":1038,"links":218324},[218325,218326,218327,218328,218329,218334,218339,218340,218341,218342,218343],{"id":29856,"depth":1041,"text":29857},{"id":111659,"depth":1041,"text":111660},{"id":70092,"depth":1041,"text":70093},{"id":99863,"depth":1041,"text":99864},{"id":29892,"depth":1041,"text":29893,"children":218330},[218331,218332,218333],{"id":216830,"depth":1038,"text":216831},{"id":217308,"depth":1038,"text":217309},{"id":217632,"depth":1038,"text":217633},{"id":41259,"depth":1041,"text":101012,"children":218335},[218336,218337,218338],{"id":217891,"depth":1038,"text":20702},{"id":218004,"depth":1038,"text":218007},{"id":218050,"depth":1038,"text":218053},{"id":218122,"depth":1041,"text":218123},{"id":101424,"depth":1041,"text":101425},{"id":114683,"depth":1041,"text":114684},{"id":6824,"depth":1041,"text":6825},{"id":2620,"depth":1041,"text":2621},"Serializable session tokens that skip re-running the unlock ceremony, plus a SessionPolicy for idle\u002Fabsolute timeouts and re-auth gates.",{},{"title":840,"description":218344},"zsEXKo7QVeCgADirXdUbR_hgZ7hHqJTCqOviuiAZZXE",{"id":218349,"title":844,"access":12,"apiToc":929,"body":218350,"category":1042,"description":225184,"extension":1044,"meta":225185,"navigation":1042,"nest":1047,"path":845,"seo":225186,"sinceVersion":1042,"status":1049,"stem":846,"subtitle":1042,"tier":1050,"tocExternal":1042,"__hash__":225187},"docs\u002Fservices\u002Fsession-tiers.md",{"type":931,"value":218351,"toc":225128},[218352,218354,218375,218378,218380,218401,218404,218416,218419,218421,218433,218492,218513,218617,218619,218718,218725,218728,218754,218756,218759,218763,218766,218776,218845,218855,218859,218866,218942,218948,218966,218970,219075,219078,219119,219159,219163,219166,219183,219187,219197,219431,219435,219508,219512,219524,219832,219836,219846,220035,220041,220045,220060,220249,220262,220266,220269,220435,220438,220442,220682,220684,220883,220887,220901,220905,220909,221028,221037,221046,221144,221148,221157,221228,221331,221337,221407,221419,221423,221430,221439,221445,221464,221471,221474,221477,221677,221705,221708,221767,221770,221774,221780,221845,221974,221996,222000,222116,222134,222141,222288,222318,222322,222328,222431,222435,222442,222508,222512,222515,222594,222606,222752,222770,222779,222874,222888,222892,222904,223166,223194,223200,223236,223240,223250,223431,223454,223457,223530,223532,223642,223645,223655,223659,223662,223877,223881,223924,223929,224012,224015,224018,224020,224024,224112,224115,224141,224145,224229,224231,224248,224250,224339,224343,224392,224394,224442,224446,224588,224597,224640,224643,224676,224678,224742,224746,224794,224796,224810,224814,224972,224976,225074,225076,225125],[934,218353,844],{"id":844},[1061,218355,218356],{},[937,218357,218358,218361,218362,218364,218365,218367,218368,1625,218370,218372,218373,1520],{},[950,218359,218360],{},"Session tiers, not record tiers."," This page covers session ",[1092,218363,16960],{}," tiers\n(how far a user has unlocked). For per-record ",[1092,218366,8673],{}," tiers — ",[941,218369,14606],{},[941,218371,14609],{},",\ntier DEKs, tiered blobs — see ",[1007,218374,872],{"href":873},[46871,218376],{":rows":218377},"[{\"label\":\"Subpath\",\"value\":\"`@noy-db\u002Fhub` (core) — session tiers, policy gates, and secret lifecycle ship in `@noy-db\u002Fhub`'s main entry point; tier-2\u002F3 factor packages are the `@noy-db\u002Fon-*` family\"},{\"label\":\"Factory\",\"value\":\"none — always on for every `createNoydb()` call\"},{\"label\":\"Cluster\",\"value\":\"collaboration-and-auth\"},{\"label\":\"Cross-cuts\",\"value\":\"every `@noy-db\u002Fon-*` unlock package, plus `@noy-db\u002Fhub`'s KEK derivation, keyring layout, and session module.\"}]",[1022,218379,6242],{"id":6241},[937,218381,218382,218383,218385,218386,218388,218389,218391,218392,218394,218395,218397,218398,218400],{},"Every noy-db vault is unlocked with a ",[950,218384,10695],{}," — the one secret the ",[1007,218387,5188],{"href":60250}," never sees in plaintext outside a short-lived in-memory derivation. (The hub is the trust-boundary module — ",[941,218390,1086],{}," — that owns encryption and the ",[1007,218393,2705],{"href":86452},"; storage backends only ever see ciphertext.) Typing that secret on every read is not realistic, so noy-db layers two lighter-weight unlock methods on top of it and calls the resulting stack the ",[950,218396,17003],{},". Sensitive actions (rotating the secret, enrolling a new device, exporting plaintext, granting a teammate access, …) are guarded by a ",[950,218399,16983],{},": a small declarative rule that says which tier — and which extra proof — an action requires.",[937,218402,218403],{},"Two terms recur throughout this page:",[1111,218405,218406,218411],{},[1114,218407,218408,218410],{},[950,218409,9879],{}," (key-encryption key) — the key derived from the secret. It never touches disk; its only job is to unwrap the DEKs.",[1114,218412,218413,218415],{},[950,218414,5627],{}," (data-encryption key) — one per collection. DEKs are what actually encrypt your records with AES-256-GCM; the KEK just wraps and unwraps them.",[937,218417,218418],{},"The ladder exists to satisfy three constraints at once: the vault owner controls the root secret and nobody else can read it (zero-knowledge); routine use shouldn't require retyping a secret; and losing a device, a passkey, or the secret itself each has a recovery path that doesn't collapse the security model.",[1022,218420,99864],{"id":99863},[937,218422,218423,218424,218426,218427,218429,218430,218432],{},"Session tiers require no opt-in. Every ",[941,218425,2134],{}," call gets the three-tier ladder and a default policy (",[941,218428,12830],{},") automatically — there is no ",[941,218431,943],{}," strategy to wire in for tier 1.",[2217,218434,218436],{"className":2219,"code":218435,"language":2221,"meta":1037,"style":1037},"import { createNoydb } from '@noy-db\u002Fhub'\n\n\u002F\u002F Tier 1 (secret) and the default policy are already active — nothing to opt into.\nconst db = await createNoydb({ store, user })\n",[941,218437,218438,218456,218460,218465],{"__ignoreMap":1037},[2225,218439,218440,218442,218444,218446,218448,218450,218452,218454],{"class":2227,"line":2228},[2225,218441,2232],{"class":2231},[2225,218443,2236],{"class":2235},[2225,218445,2744],{"class":2239},[2225,218447,2243],{"class":2235},[2225,218449,2246],{"class":2231},[2225,218451,2249],{"class":2235},[2225,218453,1086],{"class":2252},[2225,218455,2256],{"class":2235},[2225,218457,218458],{"class":2227,"line":1041},[2225,218459,2285],{"emptyLinePlaceholder":2284},[2225,218461,218462],{"class":2227,"line":1038},[2225,218463,218464],{"class":2763},"\u002F\u002F Tier 1 (secret) and the default policy are already active — nothing to opt into.\n",[2225,218466,218467,218469,218471,218473,218475,218477,218479,218481,218483,218485,218488,218490],{"class":2227,"line":2288},[2225,218468,2775],{"class":2774},[2225,218470,2778],{"class":2239},[2225,218472,2781],{"class":2235},[2225,218474,2784],{"class":2231},[2225,218476,2744],{"class":2291},[2225,218478,2789],{"class":2239},[2225,218480,2792],{"class":2235},[2225,218482,7564],{"class":2239},[2225,218484,2298],{"class":2235},[2225,218486,218487],{"class":2239}," user ",[2225,218489,2359],{"class":2235},[2225,218491,2362],{"class":2239},[937,218493,218494,218495,218497,218498,218501,218502,997,218506,218510,218511,2311],{},"Tier 2 and tier 3 are different: each method lives in its own ",[941,218496,9945],{}," package, and enrolling a slot means importing that package's enrollment function and calling the matching ",[941,218499,218500],{},"db.enroll*"," method (see ",[1007,218503,218505],{"href":218504},"#tier-2-authenticate-multi-slot","Tier 2",[1007,218507,218509],{"href":218508},"#tier-3-unlock-quick-resume","Tier 3"," below). For example, tier 3 via ",[941,218512,17062],{},[2217,218514,218516],{"className":2219,"code":218515,"language":2221,"meta":1037,"style":1037},"import { enrollPin } from '@noy-db\u002Fon-pin'\n\nconst pinState = await enrollPin(unlockedKeyring, { pin: '1234', ttlMs: 15 * 60 * 1000 })\nawait db.team.enrollUnlock('acme', pinState)\n",[941,218517,218518,218536,218540,218590],{"__ignoreMap":1037},[2225,218519,218520,218522,218524,218526,218528,218530,218532,218534],{"class":2227,"line":2228},[2225,218521,2232],{"class":2231},[2225,218523,2236],{"class":2235},[2225,218525,45189],{"class":2239},[2225,218527,2243],{"class":2235},[2225,218529,2246],{"class":2231},[2225,218531,2249],{"class":2235},[2225,218533,17062],{"class":2252},[2225,218535,2256],{"class":2235},[2225,218537,218538],{"class":2227,"line":1041},[2225,218539,2285],{"emptyLinePlaceholder":2284},[2225,218541,218542,218544,218547,218549,218551,218553,218556,218558,218560,218562,218564,218566,218568,218570,218572,218574,218576,218578,218580,218582,218584,218586,218588],{"class":2227,"line":1038},[2225,218543,2775],{"class":2774},[2225,218545,218546],{"class":2239}," pinState ",[2225,218548,2781],{"class":2235},[2225,218550,2784],{"class":2231},[2225,218552,45189],{"class":2291},[2225,218554,218555],{"class":2239},"(unlockedKeyring",[2225,218557,2298],{"class":2235},[2225,218559,2236],{"class":2235},[2225,218561,45230],{"class":2307},[2225,218563,2311],{"class":2235},[2225,218565,2249],{"class":2235},[2225,218567,117882],{"class":2252},[2225,218569,2805],{"class":2235},[2225,218571,2298],{"class":2235},[2225,218573,44437],{"class":2307},[2225,218575,2311],{"class":2235},[2225,218577,45248],{"class":6527},[2225,218579,2263],{"class":2235},[2225,218581,44452],{"class":6527},[2225,218583,2263],{"class":2235},[2225,218585,26290],{"class":6527},[2225,218587,2243],{"class":2235},[2225,218589,2362],{"class":2239},[2225,218591,218592,218594,218596,218598,218600,218602,218604,218606,218608,218610,218612,218614],{"class":2227,"line":2288},[2225,218593,6500],{"class":2231},[2225,218595,2842],{"class":2239},[2225,218597,1520],{"class":2235},[2225,218599,868],{"class":2239},[2225,218601,1520],{"class":2235},[2225,218603,18197],{"class":2291},[2225,218605,2789],{"class":2239},[2225,218607,2805],{"class":2235},[2225,218609,6450],{"class":2252},[2225,218611,2805],{"class":2235},[2225,218613,2298],{"class":2235},[2225,218615,218616],{"class":2239}," pinState)\n",[1022,218618,16993],{"id":16992},[1338,218620,218621,218636],{},[1341,218622,218623],{},[1344,218624,218625,218627,218630,218632,218634],{},[1347,218626,104737],{},[1347,218628,218629],{},"Name",[1347,218631,2970],{},[1347,218633,17223],{},[1347,218635,17203],{},[1357,218637,218638,218666,218696],{},[1344,218639,218640,218644,218649,218657,218660],{},[1362,218641,218642],{},[950,218643,5437],{},[1362,218645,218646,218648],{},[950,218647,13917],{}," (root)",[1362,218650,218651,218652,218654,218655,104759],{},"Derives the KEK via PBKDF2. Built into ",[941,218653,1086],{}," core — there is no ",[941,218656,104758],{},[1362,218658,218659],{},"Touched only at enrollment, recovery, and tier-1-gated actions.",[1362,218661,218662,218663,218665],{},"The user's memory (standard and echo modes) or a developer-supplied ",[941,218664,14350],{}," (managed mode).",[1344,218667,218668,218672,218677,218684,218687],{},[1362,218669,218670],{},[950,218671,5470],{},[1362,218673,218674],{},[950,218675,218676],{},"Authenticate",[1362,218678,218679,218680,218683],{},"Routine login — replaces retyping the secret. ",[950,218681,218682],{},"Multi-slot",": any one enrolled method unlocks.",[1362,218685,218686],{},"Days to weeks, app-defined.",[1362,218688,218689,1261,218691,1261,218693,218695],{},[941,218690,15944],{},[941,218692,15969],{},[941,218694,15953],{}," slots inside the keyring file.",[1344,218697,218698,218702,218707,218710,218713],{},[1362,218699,218700],{},[950,218701,5512],{},[1362,218703,218704],{},[950,218705,218706],{},"Unlock",[1362,218708,218709],{},"Quick-resume after idle.",[1362,218711,218712],{},"Minutes (e.g. 15 min), app-defined.",[1362,218714,218715,218717],{},[941,218716,15958],{}," — the canonical tier-3 primitive.",[937,218719,218720,218721,2993,218723,17305],{},"Tier 1 outranks tier 2, which outranks tier 3: only a higher tier can reset a lower one, and a policy gate's ",[941,218722,17301],{},[1092,218724,17304],{},[937,218726,218727],{},"Two auxiliary mechanisms sit alongside the ladder rather than inside it:",[1111,218729,218730,218746],{},[1114,218731,218732,964,218735,968,218737,968,218739,968,218741,18155,218743,218745],{},[950,218733,218734],{},"2FA factors",[941,218736,15974],{},[941,218738,15979],{},[941,218740,18152],{},[941,218742,15994],{},[941,218744,15944],{}," supply fresh, off-device proofs that policy gates can additionally demand for sensitive actions. They are not session tiers; a fresh proof is required every time a gate asks for one.",[1114,218747,218748,964,218751,218753],{},[950,218749,218750],{},"Threat overlay",[941,218752,15984],{}," layers lockout, duress-secret wipe, and honeypot routing on top of tier-1\u002Ftier-2 attempts, independently of the policy gates.",[1022,218755,29893],{"id":29892},[937,218757,218758],{},"Each tier below is a self-contained flow: enroll a factor, then unlock or act with it. Policy gates, secret rotation\u002Frecovery, and managed-secret mode build on top of the three tiers and are covered in their own subsections.",[1591,218760,218762],{"id":218761},"tier-1-secret","Tier 1 — Secret",[937,218764,218765],{},"The secret is the only secret the user genuinely owns. The hub derives the KEK from it via PBKDF2-SHA256 with 600,000 iterations. The KDF, wrap algorithm (AES-KW), envelope format, and salt\u002FIV sizes are fixed inside the hub — there are no options to change them.",[937,218767,218768,218769,218772,218773,218775],{},"Tier 1 ships in ",[950,218770,218771],{},"three modes",". You pick one per vault with ",[941,218774,20272],{},"; the choice is recorded in the keyring, so the data itself decides how it may be opened — not the app that happens to be reading it.",[1338,218777,218778,218794],{},[1341,218779,218780],{},[1344,218781,218782,218784,218788,218791],{},[1347,218783,150640],{},[1347,218785,218786],{},[941,218787,20272],{},[1347,218789,218790],{},"What the user supplies",[1347,218792,218793],{},"Pick it when",[1357,218795,218796,218812,218829],{},[1344,218797,218798,218802,218806,218809],{},[1362,218799,218800,91937],{},[950,218801,168417],{},[1362,218803,218804],{},[941,218805,20282],{},[1362,218807,218808],{},"one phrase, typed into one field",[1362,218810,218811],{},"The ordinary case: a phrase the user remembers, typed rarely.",[1344,218813,218814,218818,218823,218826],{},[1362,218815,218816],{},[950,218817,55896],{},[1362,218819,218820],{},[941,218821,218822],{},"'echo'",[1362,218824,218825],{},"one sentence enrolled as three parts (prompt \u002F echo \u002F key)",[1362,218827,218828],{},"You want the rare tier-1 prompt to be phishing-resistant, and a built-in memory cue for a secret the user types twice a year.",[1344,218830,218831,218835,218839,218842],{},[1362,218832,218833],{},[950,218834,71912],{},[1362,218836,218837],{},[941,218838,20293],{},[1362,218840,218841],{},"nothing — the hub generates and seals the secret",[1362,218843,218844],{},"You want coercion resistance: the user has no secret to reveal.",[937,218846,218847,218848,997,218851,218854],{},"The rest of this subsection describes standard mode; ",[1007,218849,55896],{"href":218850},"#tier-1-echo-secret",[1007,218852,71912],{"href":218853},"#managed-secret-mode"," have their own subsections below. All three derive the same KEK and feed the same tier-2\u002Ftier-3 ladder — the mode changes only how tier 1 is presented and proven.",[24204,218856,218858],{"id":218857},"phrase-format","Phrase format",[937,218860,218861,218862,218865],{},"Secrets are ",[950,218863,218864],{},"phrases",": several simple lowercase words, easy to remember, constrained enough that a weak choice can't silently collapse the security floor.",[1338,218867,218868,218879],{},[1341,218869,218870],{},[1344,218871,218872,218874,218876],{},[1347,218873,35913],{},[1347,218875,20197],{},[1347,218877,218878],{},"Strict policy",[1357,218880,218881,218894,218903,218912,218923,218933],{},[1344,218882,218883,218886,218892],{},[1362,218884,218885],{},"Character pattern",[1362,218887,218888,218891],{},[941,218889,218890],{},"\u002F^[a-z]+( [a-z]+)*$\u002F"," (lowercase letters and single spaces only)",[1362,218893,4664],{},[1344,218895,218896,218899,218901],{},[1362,218897,218898],{},"No punctuation, symbols, digits",[1362,218900,45375],{},[1362,218902,45375],{},[1344,218904,218905,218908,218910],{},[1362,218906,218907],{},"No leading\u002Ftrailing or doubled whitespace",[1362,218909,45375],{},[1362,218911,45375],{},[1344,218913,218914,218917,218920],{},[1362,218915,218916],{},"Minimum word count",[1362,218918,218919],{},"6 (~77 bits)",[1362,218921,218922],{},"8 (~103 bits)",[1344,218924,218925,218928,218931],{},[1362,218926,218927],{},"Minimum word length",[1362,218929,218930],{},"3 characters",[1362,218932,218930],{},[1344,218934,218935,218938,218940],{},[1362,218936,218937],{},"Repeated adjacent words",[1362,218939,207505],{},[1362,218941,207505],{},[2217,218943,218946],{"className":218944,"code":218945,"language":5672},[5670],"✅ \"correct horse battery staple printer toaster\"\n❌ \"abc\"                                              — too few words\n❌ \"Correct horse battery staple printer toaster\"    — uppercase\n❌ \"correct  horse battery staple printer toaster\"   — double space\n❌ \"correct horse battery staple printer the the\"    — repeated adjacent\n",[941,218947,218945],{"__ignoreMap":1037},[937,218949,218950,218951,218953,218954,218957,218958,218960,218961,218963,218964,957],{},"Word count is enforced separately from the character pattern — a phrase must both match the pattern ",[950,218952,14128],{}," meet ",[941,218955,218956],{},"minWords",". Every rule in the table is overridable per vault via ",[941,218959,16365],{},", including a full ",[941,218962,16385],{}," escape hatch for non-space-delimited scripts or organization-specific rules (see ",[1007,218965,102889],{"href":213507},[24204,218967,218969],{"id":218968},"strength-validation","Strength validation",[2217,218971,218973],{"className":2219,"code":218972,"language":2221,"meta":1037,"style":1037},"import { validateSecret, assertStrongSecret, WeakSecretError } from '@noy-db\u002Fhub'\n\nvalidateSecret('correct horse battery staple printer toaster')\n\u002F\u002F → { ok: true, words: 6 }\n\nvalidateSecret('abc')\n\u002F\u002F → { ok: false, reason: 'too-few-words', minimum: 6, got: 1 }\n\nassertStrongSecret('abc')\n\u002F\u002F → throws WeakSecretError { reason: 'too-few-words', suggestion: '...' }\n",[941,218974,218975,219004,219008,219023,219028,219032,219046,219051,219055,219070],{"__ignoreMap":1037},[2225,218976,218977,218979,218981,218984,218986,218989,218991,218994,218996,218998,219000,219002],{"class":2227,"line":2228},[2225,218978,2232],{"class":2231},[2225,218980,2236],{"class":2235},[2225,218982,218983],{"class":2239}," validateSecret",[2225,218985,2298],{"class":2235},[2225,218987,218988],{"class":2239}," assertStrongSecret",[2225,218990,2298],{"class":2235},[2225,218992,218993],{"class":2239}," WeakSecretError",[2225,218995,2243],{"class":2235},[2225,218997,2246],{"class":2231},[2225,218999,2249],{"class":2235},[2225,219001,1086],{"class":2252},[2225,219003,2256],{"class":2235},[2225,219005,219006],{"class":2227,"line":1041},[2225,219007,2285],{"emptyLinePlaceholder":2284},[2225,219009,219010,219012,219014,219016,219019,219021],{"class":2227,"line":1038},[2225,219011,17425],{"class":2291},[2225,219013,2789],{"class":2239},[2225,219015,2805],{"class":2235},[2225,219017,219018],{"class":2252},"correct horse battery staple printer toaster",[2225,219020,2805],{"class":2235},[2225,219022,2362],{"class":2239},[2225,219024,219025],{"class":2227,"line":2288},[2225,219026,219027],{"class":2763},"\u002F\u002F → { ok: true, words: 6 }\n",[2225,219029,219030],{"class":2227,"line":2304},[2225,219031,2285],{"emptyLinePlaceholder":2284},[2225,219033,219034,219036,219038,219040,219042,219044],{"class":2227,"line":2356},[2225,219035,17425],{"class":2291},[2225,219037,2789],{"class":2239},[2225,219039,2805],{"class":2235},[2225,219041,140780],{"class":2252},[2225,219043,2805],{"class":2235},[2225,219045,2362],{"class":2239},[2225,219047,219048],{"class":2227,"line":2830},[2225,219049,219050],{"class":2763},"\u002F\u002F → { ok: false, reason: 'too-few-words', minimum: 6, got: 1 }\n",[2225,219052,219053],{"class":2227,"line":2861},[2225,219054,2285],{"emptyLinePlaceholder":2284},[2225,219056,219057,219060,219062,219064,219066,219068],{"class":2227,"line":4246},[2225,219058,219059],{"class":2291},"assertStrongSecret",[2225,219061,2789],{"class":2239},[2225,219063,2805],{"class":2235},[2225,219065,140780],{"class":2252},[2225,219067,2805],{"class":2235},[2225,219069,2362],{"class":2239},[2225,219071,219072],{"class":2227,"line":4253},[2225,219073,219074],{"class":2763},"\u002F\u002F → throws WeakSecretError { reason: 'too-few-words', suggestion: '...' }\n",[937,219076,219077],{},"Enforcement differs by ingress point:",[1111,219079,219080,219101],{},[1114,219081,219082,953,219085,47474,219087,219091,219092,10313,219094,4765,219096,2419,219099,1520],{},[950,219083,219084],{},"First-time vault creation",[941,219086,12780],{},[950,219088,219089],{},[941,219090,10831],{}," validate the phrase only when the caller opts in — pass ",[941,219093,17438],{},[941,219095,12780],{},[941,219097,219098],{},"{ validateSecret: true }",[941,219100,137077],{},[1114,219102,219103,997,219107,219111,219112,219114,219115,219118],{},[950,219104,219105],{},[941,219106,197101],{},[950,219108,219109],{},[941,219110,107014],{}," validate the ",[1092,219113,70371],{}," phrase by default; pass ",[941,219116,219117],{},"{ allowWeakSecret: true }"," to bypass (test fixtures, CLI scripts).",[2217,219120,219122],{"className":2219,"code":219121,"language":2221,"meta":1037,"style":1037},"const db = await createNoydb({ store, validateSecret: true })\n\u002F\u002F → new vaults now throw WeakSecretError on a weak initial secret\n",[941,219123,219124,219154],{"__ignoreMap":1037},[2225,219125,219126,219128,219130,219132,219134,219136,219138,219140,219142,219144,219146,219148,219150,219152],{"class":2227,"line":2228},[2225,219127,2775],{"class":2774},[2225,219129,2778],{"class":2239},[2225,219131,2781],{"class":2235},[2225,219133,2784],{"class":2231},[2225,219135,2744],{"class":2291},[2225,219137,2789],{"class":2239},[2225,219139,2792],{"class":2235},[2225,219141,7564],{"class":2239},[2225,219143,2298],{"class":2235},[2225,219145,218983],{"class":2307},[2225,219147,2311],{"class":2235},[2225,219149,2322],{"class":2321},[2225,219151,2243],{"class":2235},[2225,219153,2362],{"class":2239},[2225,219155,219156],{"class":2227,"line":1041},[2225,219157,219158],{"class":2763},"\u002F\u002F → new vaults now throw WeakSecretError on a weak initial secret\n",[24204,219160,219162],{"id":219161},"secret-handling-developer-responsibility","Secret handling (developer responsibility)",[937,219164,219165],{},"The cryptographic risk is in the hub; the handling discipline is in your code:",[1111,219167,219168,219171,219177],{},[1114,219169,219170],{},"Never write the secret to logs, telemetry, error reports, or analytics.",[1114,219172,219173,219174,219176],{},"Never persist it in ",[941,219175,41955],{},", cookies, or any cache that outlives the unlock call.",[1114,219178,116274,219179,219182],{},[941,219180,219181],{},"\u003Cinput type=\"password\" autocomplete=\"new-password\">"," for enrollment forms — don't pre-fill from clipboard or autofill.",[1591,219184,219186],{"id":219185},"tier-2-authenticate-multi-slot","Tier 2 — Authenticate (multi-slot)",[937,219188,219189,219190,219193,219194,219196],{},"A user enrolls one or more ",[950,219191,219192],{},"authenticator slots",". Each slot independently wraps the same KEK (or, for ",[941,219195,15953],{},", the DEK set directly) under a method-specific key — any one enrolled slot unlocks the vault. Adding or removing a slot is a constant-time keyring write; it never re-keys the DEKs.",[2217,219198,219200],{"className":2219,"code":219199,"language":2221,"meta":1037,"style":1037},"interface KeyringAuthenticatorBase {\n  id: string                          \u002F\u002F caller-chosen — e.g. 'webauthn-yubikey-blue'\n  method: 'webauthn' | 'oidc' | 'password'\n  enrolled_at: string                 \u002F\u002F ISO 8601\n  enrolled_via_tier: 1 | 2            \u002F\u002F tier that enrolled this slot\n  meta: Record\u003Cstring, unknown>       \u002F\u002F method-specific — cred id, issuer\u002Fsub, salt, ...\n}\n\n\u002F\u002F wrap-KEK slots (webauthn, oidc) — wrap the SAME KEK under a method-derived key\ntype KeyringAuthenticatorWrappingKEK = KeyringAuthenticatorBase & {\n  wrapKind?: 'kek'\n  wrapped_kek: string\n}\n\n\u002F\u002F wrap-DEKs slots (password) — wrap the DEK set directly, sidestepping the non-extractable KEK\ntype KeyringAuthenticatorWrappingDEKs = KeyringAuthenticatorBase & {\n  wrapKind: 'deks'\n  wrapped_deks: string\n  iv: string\n}\n\ntype KeyringAuthenticator = KeyringAuthenticatorWrappingKEK | KeyringAuthenticatorWrappingDEKs\n",[941,219201,219202,219211,219222,219251,219263,219279,219300,219304,219308,219313,219328,219341,219350,219354,219358,219363,219378,219390,219399,219408,219412,219416],{"__ignoreMap":1037},[2225,219203,219204,219206,219209],{"class":2227,"line":2228},[2225,219205,6994],{"class":2774},[2225,219207,219208],{"class":6342}," KeyringAuthenticatorBase",[2225,219210,2301],{"class":2235},[2225,219212,219213,219215,219217,219219],{"class":2227,"line":1041},[2225,219214,28371],{"class":2307},[2225,219216,2311],{"class":2235},[2225,219218,6366],{"class":6342},[2225,219220,219221],{"class":2763},"                          \u002F\u002F caller-chosen — e.g. 'webauthn-yubikey-blue'\n",[2225,219223,219224,219227,219229,219231,219233,219235,219237,219239,219241,219243,219245,219247,219249],{"class":2227,"line":1038},[2225,219225,219226],{"class":2307},"  method",[2225,219228,2311],{"class":2235},[2225,219230,2249],{"class":2235},[2225,219232,337],{"class":2252},[2225,219234,2805],{"class":2235},[2225,219236,7087],{"class":2235},[2225,219238,2249],{"class":2235},[2225,219240,309],{"class":2252},[2225,219242,2805],{"class":2235},[2225,219244,7087],{"class":2235},[2225,219246,2249],{"class":2235},[2225,219248,313],{"class":2252},[2225,219250,2256],{"class":2235},[2225,219252,219253,219256,219258,219260],{"class":2227,"line":2288},[2225,219254,219255],{"class":2307},"  enrolled_at",[2225,219257,2311],{"class":2235},[2225,219259,6366],{"class":6342},[2225,219261,219262],{"class":2763},"                 \u002F\u002F ISO 8601\n",[2225,219264,219265,219268,219270,219272,219274,219276],{"class":2227,"line":2304},[2225,219266,219267],{"class":2307},"  enrolled_via_tier",[2225,219269,2311],{"class":2235},[2225,219271,9377],{"class":6527},[2225,219273,7087],{"class":2235},[2225,219275,18961],{"class":6527},[2225,219277,219278],{"class":2763},"            \u002F\u002F tier that enrolled this slot\n",[2225,219280,219281,219283,219285,219287,219289,219291,219293,219295,219297],{"class":2227,"line":2356},[2225,219282,181405],{"class":2307},[2225,219284,2311],{"class":2235},[2225,219286,22527],{"class":6342},[2225,219288,6474],{"class":2235},[2225,219290,7259],{"class":6342},[2225,219292,2298],{"class":2235},[2225,219294,40717],{"class":6342},[2225,219296,6480],{"class":2235},[2225,219298,219299],{"class":2763},"       \u002F\u002F method-specific — cred id, issuer\u002Fsub, salt, ...\n",[2225,219301,219302],{"class":2227,"line":2830},[2225,219303,7351],{"class":2235},[2225,219305,219306],{"class":2227,"line":2861},[2225,219307,2285],{"emptyLinePlaceholder":2284},[2225,219309,219310],{"class":2227,"line":4246},[2225,219311,219312],{"class":2763},"\u002F\u002F wrap-KEK slots (webauthn, oidc) — wrap the SAME KEK under a method-derived key\n",[2225,219314,219315,219317,219320,219322,219324,219326],{"class":2227,"line":4253},[2225,219316,6339],{"class":2774},[2225,219318,219319],{"class":6342}," KeyringAuthenticatorWrappingKEK",[2225,219321,6346],{"class":2235},[2225,219323,219208],{"class":6342},[2225,219325,134400],{"class":2235},[2225,219327,2301],{"class":2235},[2225,219329,219330,219333,219335,219337,219339],{"class":2227,"line":4259},[2225,219331,219332],{"class":2307},"  wrapKind",[2225,219334,7011],{"class":2235},[2225,219336,2249],{"class":2235},[2225,219338,45415],{"class":2252},[2225,219340,2256],{"class":2235},[2225,219342,219343,219346,219348],{"class":2227,"line":6548},[2225,219344,219345],{"class":2307},"  wrapped_kek",[2225,219347,2311],{"class":2235},[2225,219349,7014],{"class":6342},[2225,219351,219352],{"class":2227,"line":6576},[2225,219353,7351],{"class":2235},[2225,219355,219356],{"class":2227,"line":6582},[2225,219357,2285],{"emptyLinePlaceholder":2284},[2225,219359,219360],{"class":2227,"line":6587},[2225,219361,219362],{"class":2763},"\u002F\u002F wrap-DEKs slots (password) — wrap the DEK set directly, sidestepping the non-extractable KEK\n",[2225,219364,219365,219367,219370,219372,219374,219376],{"class":2227,"line":7174},[2225,219366,6339],{"class":2774},[2225,219368,219369],{"class":6342}," KeyringAuthenticatorWrappingDEKs",[2225,219371,6346],{"class":2235},[2225,219373,219208],{"class":6342},[2225,219375,134400],{"class":2235},[2225,219377,2301],{"class":2235},[2225,219379,219380,219382,219384,219386,219388],{"class":2227,"line":7180},[2225,219381,219332],{"class":2307},[2225,219383,2311],{"class":2235},[2225,219385,2249],{"class":2235},[2225,219387,45372],{"class":2252},[2225,219389,2256],{"class":2235},[2225,219391,219392,219395,219397],{"class":2227,"line":7220},[2225,219393,219394],{"class":2307},"  wrapped_deks",[2225,219396,2311],{"class":2235},[2225,219398,7014],{"class":6342},[2225,219400,219401,219404,219406],{"class":2227,"line":7225},[2225,219402,219403],{"class":2307},"  iv",[2225,219405,2311],{"class":2235},[2225,219407,7014],{"class":6342},[2225,219409,219410],{"class":2227,"line":7231},[2225,219411,7351],{"class":2235},[2225,219413,219414],{"class":2227,"line":7267},[2225,219415,2285],{"emptyLinePlaceholder":2284},[2225,219417,219418,219420,219422,219424,219426,219428],{"class":2227,"line":7272},[2225,219419,6339],{"class":2774},[2225,219421,18023],{"class":6342},[2225,219423,6346],{"class":2235},[2225,219425,219319],{"class":6342},[2225,219427,7087],{"class":2235},[2225,219429,219430],{"class":6342}," KeyringAuthenticatorWrappingDEKs\n",[24204,219432,219434],{"id":219433},"available-tier-2-methods","Available tier-2 methods",[1338,219436,219437,219449],{},[1341,219438,219439],{},[1344,219440,219441,219443,219445,219447],{},[1347,219442,7831],{},[1347,219444,9530],{},[1347,219446,104855],{},[1347,219448,9542],{},[1357,219450,219451,219471,219487],{},[1344,219452,219453,219457,219461,219464],{},[1362,219454,219455],{},[941,219456,337],{},[1362,219458,219459],{},[941,219460,17049],{},[1362,219462,219463],{},"device (platform passkey) or possession (roaming key)",[1362,219465,219466,219467,219470],{},"Prefers the PRF extension for deterministic key derivation; falls back to a rawId-derived key. ",[941,219468,219469],{},"requireSingleDevice"," rejects credentials whose backup-eligible (BE) flag is set.",[1344,219472,219473,219477,219481,219484],{},[1362,219474,219475],{},[941,219476,309],{},[1362,219478,219479],{},[941,219480,44580],{},[1362,219482,219483],{},"possession (provider session) + knowledge (split-key secret half)",[1362,219485,219486],{},"Bitwarden-style split key: half the KEK lives on the device, half comes from the OIDC provider's key connector on successful login.",[1344,219488,219489,219493,219497,219500],{},[1362,219490,219491],{},[941,219492,313],{},[1362,219494,219495],{},[941,219496,17035],{},[1362,219498,219499],{},"knowledge (a second secret, distinct from the tier-1 phrase)",[1362,219501,219502,219503,4688,219505,219507],{},"PBKDF2-derived wrap-DEKs slot. A separate credential from the tier-1 phrase — pair with ",[941,219504,15974],{},[941,219506,15979],{}," for a typical SaaS UX.",[24204,219509,219511],{"id":219510},"enroll-remove-update-unlock","Enroll, remove, update, unlock",[937,219513,219514,1261,219516,1261,219518,219520,219521,219523],{},[941,219515,17521],{},[941,219517,17609],{},[941,219519,17724],{}," all take the ",[950,219522,17549],{},", not a keyring — the hub checks the relevant policy gate and fetches the active keyring internally.",[2217,219525,219527],{"className":2219,"code":219526,"language":2221,"meta":1037,"style":1037},"\u002F\u002F Enroll a new slot. Checks the `enroll-authenticator` gate, then persists.\nawait db.team.enrollAuthenticator('acme', {\n  id: 'webauthn-yubikey-blue',\n  method: 'webauthn',\n  wrapped_kek: enrollment.wrappedKek,        \u002F\u002F produced by @noy-db\u002Fon-webauthn\n  meta: { credentialId: enrollment.credentialId },\n})\n\n\u002F\u002F Remove a slot. Idempotent — removing a missing slot is a no-op. Gated by `remove-authenticator`.\nawait db.team.removeAuthenticator('acme', 'webauthn-yubikey-blue')\n\n\u002F\u002F Rename or re-label a slot's meta (id\u002Fmethod\u002Fwrap material are immutable). Gated by `update-authenticator`.\nawait db.team.updateAuthenticator('acme', 'webauthn-yubikey-blue', { meta: { label: 'Work laptop' } })\n\n\u002F\u002F Unlock via an enrolled slot — `verify` receives the stored slot and\n\u002F\u002F returns the unlocked keyring (typically delegating to the matching on-* package).\nconst unlocked = await db.team.unlockViaAuthenticator('acme', 'webauthn-yubikey-blue', async (slot) => {\n  return unlockWebAuthn(slot, \u002F* on-webauthn assertion options *\u002F)\n})\n\u002F\u002F → unlocked keyring; subsequent checkGate() calls now see a tier-2 session\n",[941,219528,219529,219534,219560,219575,219589,219606,219627,219633,219637,219642,219674,219678,219683,219740,219744,219749,219754,219804,219821,219827],{"__ignoreMap":1037},[2225,219530,219531],{"class":2227,"line":2228},[2225,219532,219533],{"class":2763},"\u002F\u002F Enroll a new slot. Checks the `enroll-authenticator` gate, then persists.\n",[2225,219535,219536,219538,219540,219542,219544,219546,219548,219550,219552,219554,219556,219558],{"class":2227,"line":1041},[2225,219537,6500],{"class":2231},[2225,219539,2842],{"class":2239},[2225,219541,1520],{"class":2235},[2225,219543,868],{"class":2239},[2225,219545,1520],{"class":2235},[2225,219547,17521],{"class":2291},[2225,219549,2789],{"class":2239},[2225,219551,2805],{"class":2235},[2225,219553,6450],{"class":2252},[2225,219555,2805],{"class":2235},[2225,219557,2298],{"class":2235},[2225,219559,2301],{"class":2235},[2225,219561,219562,219564,219566,219568,219571,219573],{"class":2227,"line":1038},[2225,219563,28371],{"class":2307},[2225,219565,2311],{"class":2235},[2225,219567,2249],{"class":2235},[2225,219569,219570],{"class":2252},"webauthn-yubikey-blue",[2225,219572,2805],{"class":2235},[2225,219574,1707],{"class":2235},[2225,219576,219577,219579,219581,219583,219585,219587],{"class":2227,"line":2288},[2225,219578,219226],{"class":2307},[2225,219580,2311],{"class":2235},[2225,219582,2249],{"class":2235},[2225,219584,337],{"class":2252},[2225,219586,2805],{"class":2235},[2225,219588,1707],{"class":2235},[2225,219590,219591,219593,219595,219597,219599,219601,219603],{"class":2227,"line":2304},[2225,219592,219345],{"class":2307},[2225,219594,2311],{"class":2235},[2225,219596,96772],{"class":2239},[2225,219598,1520],{"class":2235},[2225,219600,217201],{"class":2239},[2225,219602,2298],{"class":2235},[2225,219604,219605],{"class":2763},"        \u002F\u002F produced by @noy-db\u002Fon-webauthn\n",[2225,219607,219608,219610,219612,219614,219616,219618,219620,219622,219625],{"class":2227,"line":2356},[2225,219609,181405],{"class":2307},[2225,219611,2311],{"class":2235},[2225,219613,2236],{"class":2235},[2225,219615,17854],{"class":2307},[2225,219617,2311],{"class":2235},[2225,219619,96772],{"class":2239},[2225,219621,1520],{"class":2235},[2225,219623,219624],{"class":2239},"credentialId ",[2225,219626,19008],{"class":2235},[2225,219628,219629,219631],{"class":2227,"line":2830},[2225,219630,2359],{"class":2235},[2225,219632,2362],{"class":2239},[2225,219634,219635],{"class":2227,"line":2861},[2225,219636,2285],{"emptyLinePlaceholder":2284},[2225,219638,219639],{"class":2227,"line":4246},[2225,219640,219641],{"class":2763},"\u002F\u002F Remove a slot. Idempotent — removing a missing slot is a no-op. Gated by `remove-authenticator`.\n",[2225,219643,219644,219646,219648,219650,219652,219654,219656,219658,219660,219662,219664,219666,219668,219670,219672],{"class":2227,"line":4253},[2225,219645,6500],{"class":2231},[2225,219647,2842],{"class":2239},[2225,219649,1520],{"class":2235},[2225,219651,868],{"class":2239},[2225,219653,1520],{"class":2235},[2225,219655,17609],{"class":2291},[2225,219657,2789],{"class":2239},[2225,219659,2805],{"class":2235},[2225,219661,6450],{"class":2252},[2225,219663,2805],{"class":2235},[2225,219665,2298],{"class":2235},[2225,219667,2249],{"class":2235},[2225,219669,219570],{"class":2252},[2225,219671,2805],{"class":2235},[2225,219673,2362],{"class":2239},[2225,219675,219676],{"class":2227,"line":4259},[2225,219677,2285],{"emptyLinePlaceholder":2284},[2225,219679,219680],{"class":2227,"line":6548},[2225,219681,219682],{"class":2763},"\u002F\u002F Rename or re-label a slot's meta (id\u002Fmethod\u002Fwrap material are immutable). Gated by `update-authenticator`.\n",[2225,219684,219685,219687,219689,219691,219693,219695,219697,219699,219701,219703,219705,219707,219709,219711,219713,219715,219717,219719,219721,219723,219725,219727,219729,219732,219734,219736,219738],{"class":2227,"line":6576},[2225,219686,6500],{"class":2231},[2225,219688,2842],{"class":2239},[2225,219690,1520],{"class":2235},[2225,219692,868],{"class":2239},[2225,219694,1520],{"class":2235},[2225,219696,17724],{"class":2291},[2225,219698,2789],{"class":2239},[2225,219700,2805],{"class":2235},[2225,219702,6450],{"class":2252},[2225,219704,2805],{"class":2235},[2225,219706,2298],{"class":2235},[2225,219708,2249],{"class":2235},[2225,219710,219570],{"class":2252},[2225,219712,2805],{"class":2235},[2225,219714,2298],{"class":2235},[2225,219716,2236],{"class":2235},[2225,219718,90153],{"class":2307},[2225,219720,2311],{"class":2235},[2225,219722,2236],{"class":2235},[2225,219724,95892],{"class":2307},[2225,219726,2311],{"class":2235},[2225,219728,2249],{"class":2235},[2225,219730,219731],{"class":2252},"Work laptop",[2225,219733,2805],{"class":2235},[2225,219735,2243],{"class":2235},[2225,219737,2243],{"class":2235},[2225,219739,2362],{"class":2239},[2225,219741,219742],{"class":2227,"line":6582},[2225,219743,2285],{"emptyLinePlaceholder":2284},[2225,219745,219746],{"class":2227,"line":6587},[2225,219747,219748],{"class":2763},"\u002F\u002F Unlock via an enrolled slot — `verify` receives the stored slot and\n",[2225,219750,219751],{"class":2227,"line":7174},[2225,219752,219753],{"class":2763},"\u002F\u002F returns the unlocked keyring (typically delegating to the matching on-* package).\n",[2225,219755,219756,219758,219760,219762,219764,219766,219768,219770,219772,219774,219776,219778,219780,219782,219784,219786,219788,219790,219792,219794,219796,219798,219800,219802],{"class":2227,"line":7180},[2225,219757,2775],{"class":2774},[2225,219759,77619],{"class":2239},[2225,219761,2781],{"class":2235},[2225,219763,2784],{"class":2231},[2225,219765,2842],{"class":2239},[2225,219767,1520],{"class":2235},[2225,219769,868],{"class":2239},[2225,219771,1520],{"class":2235},[2225,219773,18002],{"class":2291},[2225,219775,2789],{"class":2239},[2225,219777,2805],{"class":2235},[2225,219779,6450],{"class":2252},[2225,219781,2805],{"class":2235},[2225,219783,2298],{"class":2235},[2225,219785,2249],{"class":2235},[2225,219787,219570],{"class":2252},[2225,219789,2805],{"class":2235},[2225,219791,2298],{"class":2235},[2225,219793,38717],{"class":2774},[2225,219795,953],{"class":2235},[2225,219797,18018],{"class":7052},[2225,219799,1381],{"class":2235},[2225,219801,8116],{"class":2774},[2225,219803,2301],{"class":2235},[2225,219805,219806,219808,219810,219812,219814,219816,219819],{"class":2227,"line":7220},[2225,219807,8220],{"class":2231},[2225,219809,46641],{"class":2291},[2225,219811,2789],{"class":2307},[2225,219813,18018],{"class":2239},[2225,219815,2298],{"class":2235},[2225,219817,219818],{"class":2763}," \u002F* on-webauthn assertion options *\u002F",[2225,219820,2362],{"class":2307},[2225,219822,219823,219825],{"class":2227,"line":7225},[2225,219824,2359],{"class":2235},[2225,219826,2362],{"class":2239},[2225,219828,219829],{"class":2227,"line":7231},[2225,219830,219831],{"class":2763},"\u002F\u002F → unlocked keyring; subsequent checkGate() calls now see a tier-2 session\n",[1591,219833,219835],{"id":219834},"tier-3-unlock-quick-resume","Tier 3 — Unlock (quick-resume)",[937,219837,219838,219839,219841,219842,219845],{},"The canonical tier-3 primitive is ",[941,219840,17062],{},". Its own words: ",[1092,219843,219844],{},"\"PIN never replaces the secret; only resumes an already-unlocked session.\""," A PIN derives a short-lived wrapping key (PBKDF2, 100,000 iterations) over the DEKs already cached from a tier-1 or tier-2 unlock; it cannot open a cold vault. On idle expiry, the cache is wiped and tier 2 must be re-presented.",[2217,219847,219849],{"className":2219,"code":219848,"language":2221,"meta":1037,"style":1037},"import { enrollPin, resumePin } from '@noy-db\u002Fon-pin'\n\n\u002F\u002F After a full unlock — mint a resumable state, keep it in memory only.\nconst pinState = await enrollPin(unlockedKeyring, { pin: '1234', ttlMs: 15 * 60 * 1000 })\nawait db.team.enrollUnlock('acme', pinState)\n\n\u002F\u002F Later, on session resume:\nconst resumed = await db.team.unlockViaPin('acme', (state) => resumePin(state, { pin: '1234' }))\n\u002F\u002F → resumed is `undefined` if no tier-3 state is registered for the vault;\n\u002F\u002F   resumePin() throws PinExpiredError \u002F PinAttemptsExceededError past the TTL\u002Fattempt limit\n",[941,219850,219851,219873,219877,219882,219930,219956,219960,219965,220025,220030],{"__ignoreMap":1037},[2225,219852,219853,219855,219857,219859,219861,219863,219865,219867,219869,219871],{"class":2227,"line":2228},[2225,219854,2232],{"class":2231},[2225,219856,2236],{"class":2235},[2225,219858,45189],{"class":2239},[2225,219860,2298],{"class":2235},[2225,219862,45194],{"class":2239},[2225,219864,2243],{"class":2235},[2225,219866,2246],{"class":2231},[2225,219868,2249],{"class":2235},[2225,219870,17062],{"class":2252},[2225,219872,2256],{"class":2235},[2225,219874,219875],{"class":2227,"line":1041},[2225,219876,2285],{"emptyLinePlaceholder":2284},[2225,219878,219879],{"class":2227,"line":1038},[2225,219880,219881],{"class":2763},"\u002F\u002F After a full unlock — mint a resumable state, keep it in memory only.\n",[2225,219883,219884,219886,219888,219890,219892,219894,219896,219898,219900,219902,219904,219906,219908,219910,219912,219914,219916,219918,219920,219922,219924,219926,219928],{"class":2227,"line":2288},[2225,219885,2775],{"class":2774},[2225,219887,218546],{"class":2239},[2225,219889,2781],{"class":2235},[2225,219891,2784],{"class":2231},[2225,219893,45189],{"class":2291},[2225,219895,218555],{"class":2239},[2225,219897,2298],{"class":2235},[2225,219899,2236],{"class":2235},[2225,219901,45230],{"class":2307},[2225,219903,2311],{"class":2235},[2225,219905,2249],{"class":2235},[2225,219907,117882],{"class":2252},[2225,219909,2805],{"class":2235},[2225,219911,2298],{"class":2235},[2225,219913,44437],{"class":2307},[2225,219915,2311],{"class":2235},[2225,219917,45248],{"class":6527},[2225,219919,2263],{"class":2235},[2225,219921,44452],{"class":6527},[2225,219923,2263],{"class":2235},[2225,219925,26290],{"class":6527},[2225,219927,2243],{"class":2235},[2225,219929,2362],{"class":2239},[2225,219931,219932,219934,219936,219938,219940,219942,219944,219946,219948,219950,219952,219954],{"class":2227,"line":2304},[2225,219933,6500],{"class":2231},[2225,219935,2842],{"class":2239},[2225,219937,1520],{"class":2235},[2225,219939,868],{"class":2239},[2225,219941,1520],{"class":2235},[2225,219943,18197],{"class":2291},[2225,219945,2789],{"class":2239},[2225,219947,2805],{"class":2235},[2225,219949,6450],{"class":2252},[2225,219951,2805],{"class":2235},[2225,219953,2298],{"class":2235},[2225,219955,218616],{"class":2239},[2225,219957,219958],{"class":2227,"line":2356},[2225,219959,2285],{"emptyLinePlaceholder":2284},[2225,219961,219962],{"class":2227,"line":2830},[2225,219963,219964],{"class":2763},"\u002F\u002F Later, on session resume:\n",[2225,219966,219967,219969,219971,219973,219975,219977,219979,219981,219983,219985,219987,219989,219991,219993,219995,219997,219999,220001,220003,220005,220007,220009,220011,220013,220015,220017,220019,220021,220023],{"class":2227,"line":2861},[2225,219968,2775],{"class":2774},[2225,219970,45276],{"class":2239},[2225,219972,2781],{"class":2235},[2225,219974,2784],{"class":2231},[2225,219976,2842],{"class":2239},[2225,219978,1520],{"class":2235},[2225,219980,868],{"class":2239},[2225,219982,1520],{"class":2235},[2225,219984,18267],{"class":2291},[2225,219986,2789],{"class":2239},[2225,219988,2805],{"class":2235},[2225,219990,6450],{"class":2252},[2225,219992,2805],{"class":2235},[2225,219994,2298],{"class":2235},[2225,219996,953],{"class":2235},[2225,219998,4656],{"class":7052},[2225,220000,1381],{"class":2235},[2225,220002,8116],{"class":2774},[2225,220004,45194],{"class":2291},[2225,220006,45285],{"class":2239},[2225,220008,2298],{"class":2235},[2225,220010,2236],{"class":2235},[2225,220012,45230],{"class":2307},[2225,220014,2311],{"class":2235},[2225,220016,2249],{"class":2235},[2225,220018,117882],{"class":2252},[2225,220020,2805],{"class":2235},[2225,220022,2243],{"class":2235},[2225,220024,8606],{"class":2239},[2225,220026,220027],{"class":2227,"line":4246},[2225,220028,220029],{"class":2763},"\u002F\u002F → resumed is `undefined` if no tier-3 state is registered for the vault;\n",[2225,220031,220032],{"class":2227,"line":4253},[2225,220033,220034],{"class":2763},"\u002F\u002F   resumePin() throws PinExpiredError \u002F PinAttemptsExceededError past the TTL\u002Fattempt limit\n",[937,220036,220037,220038,220040],{},"Tier 3 is never safe on a shared workstation — the cached DEKs live on the device for the whole idle window. The idle timeout (",[941,220039,75026],{},") is an application choice: a shared kiosk might use minutes, a single-user tablet longer.",[1591,220042,220044],{"id":220043},"policy-gates","Policy gates",[937,220046,220047,220048,220050,220051,220053,220054,220056,220057,220059],{},"Sensitive operations are gated by a typed ",[941,220049,12870],{},". You supply one at vault creation (or accept the default); the hub persists it once at ",[941,220052,12784],{}," and every later ",[941,220055,2134],{}," call for that vault reads the on-disk copy — ",[941,220058,104516],{}," is the sanctioned way to change it afterward.",[2217,220061,220063],{"className":2219,"code":220062,"language":2221,"meta":1037,"style":1037},"interface VaultPolicy {\n  secret?: SecretPolicy\n  gates: Partial\u003CRecord\u003CGateName, GatePolicy>>\n}\n\ninterface GatePolicy {\n  minTier: 1 | 2 | 3                            \u002F\u002F session tier required\n  factors?: ReadonlyArray\u003CFactorRequirement>    \u002F\u002F extra fresh proofs at action time\n  warn?: WarningRules                           \u002F\u002F soft signals, never block on their own\n  enabled?: boolean                             \u002F\u002F false disables the action entirely\n}\n\ninterface FactorRequirement {\n  anyOf: ReadonlyArray\u003CFactorKind>              \u002F\u002F 'totp' | 'email-otp' | 'recovery' | 'shamir' |\n                                                 \u002F\u002F 'webauthn-roaming' | 'webauthn-platform' | 'password' | 'pin'\n  count?: number                                \u002F\u002F distinct factors required — default 1\n  freshnessMs?: number                          \u002F\u002F default 5 minutes\n}\n",[941,220064,220065,220073,220082,220104,220108,220112,220120,220140,220157,220170,220182,220186,220190,220198,220216,220221,220233,220245],{"__ignoreMap":1037},[2225,220066,220067,220069,220071],{"class":2227,"line":2228},[2225,220068,6994],{"class":2774},[2225,220070,197475],{"class":6342},[2225,220072,2301],{"class":2235},[2225,220074,220075,220077,220079],{"class":2227,"line":1041},[2225,220076,10423],{"class":2307},[2225,220078,7011],{"class":2235},[2225,220080,220081],{"class":6342}," SecretPolicy\n",[2225,220083,220084,220086,220088,220090,220092,220094,220096,220098,220100,220102],{"class":2227,"line":1038},[2225,220085,197278],{"class":2307},[2225,220087,2311],{"class":2235},[2225,220089,197502],{"class":6342},[2225,220091,6474],{"class":2235},[2225,220093,197507],{"class":6342},[2225,220095,6474],{"class":2235},[2225,220097,197512],{"class":6342},[2225,220099,2298],{"class":2235},[2225,220101,197517],{"class":6342},[2225,220103,17688],{"class":2235},[2225,220105,220106],{"class":2227,"line":2288},[2225,220107,7351],{"class":2235},[2225,220109,220110],{"class":2227,"line":2304},[2225,220111,2285],{"emptyLinePlaceholder":2284},[2225,220113,220114,220116,220118],{"class":2227,"line":2356},[2225,220115,6994],{"class":2774},[2225,220117,197517],{"class":6342},[2225,220119,2301],{"class":2235},[2225,220121,220122,220125,220127,220129,220131,220133,220135,220137],{"class":2227,"line":2830},[2225,220123,220124],{"class":2307},"  minTier",[2225,220126,2311],{"class":2235},[2225,220128,9377],{"class":6527},[2225,220130,7087],{"class":2235},[2225,220132,18961],{"class":6527},[2225,220134,7087],{"class":2235},[2225,220136,14460],{"class":6527},[2225,220138,220139],{"class":2763},"                            \u002F\u002F session tier required\n",[2225,220141,220142,220144,220146,220148,220150,220152,220154],{"class":2227,"line":2861},[2225,220143,13002],{"class":2307},[2225,220145,7011],{"class":2235},[2225,220147,19754],{"class":6342},[2225,220149,6474],{"class":2235},[2225,220151,197586],{"class":6342},[2225,220153,6480],{"class":2235},[2225,220155,220156],{"class":2763},"    \u002F\u002F extra fresh proofs at action time\n",[2225,220158,220159,220162,220164,220167],{"class":2227,"line":4246},[2225,220160,220161],{"class":2307},"  warn",[2225,220163,7011],{"class":2235},[2225,220165,220166],{"class":6342}," WarningRules",[2225,220168,220169],{"class":2763},"                           \u002F\u002F soft signals, never block on their own\n",[2225,220171,220172,220175,220177,220179],{"class":2227,"line":4253},[2225,220173,220174],{"class":2307},"  enabled",[2225,220176,7011],{"class":2235},[2225,220178,7723],{"class":6342},[2225,220180,220181],{"class":2763},"                             \u002F\u002F false disables the action entirely\n",[2225,220183,220184],{"class":2227,"line":4259},[2225,220185,7351],{"class":2235},[2225,220187,220188],{"class":2227,"line":6548},[2225,220189,2285],{"emptyLinePlaceholder":2284},[2225,220191,220192,220194,220196],{"class":2227,"line":6576},[2225,220193,6994],{"class":2774},[2225,220195,197721],{"class":6342},[2225,220197,2301],{"class":2235},[2225,220199,220200,220203,220205,220207,220209,220211,220213],{"class":2227,"line":6582},[2225,220201,220202],{"class":2307},"  anyOf",[2225,220204,2311],{"class":2235},[2225,220206,19754],{"class":6342},[2225,220208,6474],{"class":2235},[2225,220210,197738],{"class":6342},[2225,220212,6480],{"class":2235},[2225,220214,220215],{"class":2763},"              \u002F\u002F 'totp' | 'email-otp' | 'recovery' | 'shamir' |\n",[2225,220217,220218],{"class":2227,"line":6587},[2225,220219,220220],{"class":2763},"                                                 \u002F\u002F 'webauthn-roaming' | 'webauthn-platform' | 'password' | 'pin'\n",[2225,220222,220223,220226,220228,220230],{"class":2227,"line":7174},[2225,220224,220225],{"class":2307},"  count",[2225,220227,7011],{"class":2235},[2225,220229,6356],{"class":6342},[2225,220231,220232],{"class":2763},"                                \u002F\u002F distinct factors required — default 1\n",[2225,220234,220235,220238,220240,220242],{"class":2227,"line":7180},[2225,220236,220237],{"class":2307},"  freshnessMs",[2225,220239,7011],{"class":2235},[2225,220241,6356],{"class":6342},[2225,220243,220244],{"class":2763},"                          \u002F\u002F default 5 minutes\n",[2225,220246,220247],{"class":2227,"line":7220},[2225,220248,7351],{"class":2235},[937,220250,220251,220252,220255,220256,220258,220259,220261],{},"App-defined gates use the ",[941,220253,220254],{},"app:\u003Cname>"," namespace (e.g. ",[941,220257,196790],{},") and run through the same engine; an ",[941,220260,13045],{}," gate with no configured policy is treated as an unenforced label rather than failing closed.",[24204,220263,220265],{"id":220264},"built-in-gates-session-tier-subset","Built-in gates (session-tier subset)",[937,220267,220268],{},"Selected built-in gates and their defaults under the two shipped presets — the engine also enforces built-in gates for team\u002Fwithdrawal\u002Fcustody operations outside the scope of this page:",[1338,220270,220271,220287],{},[1341,220272,220273],{},[1344,220274,220275,220277,220279,220283],{},[1347,220276,104381],{},[1347,220278,7834],{},[1347,220280,220281,91937],{},[941,220282,12830],{},[1347,220284,220285],{},[941,220286,12834],{},[1357,220288,220289,220308,220324,220342,220359,220377,220396,220417],{},[1344,220290,220291,220295,220298,220303],{},[1362,220292,220293],{},[941,220294,15169],{},[1362,220296,220297],{},"User remembers old, sets new",[1362,220299,220300,220302],{},[941,220301,17160],{},", any one of totp \u002F email-otp \u002F recovery \u002F webauthn \u002F password \u002F pin",[1362,220304,220305,220307],{},[941,220306,17160],{},", 2 distinct of totp \u002F email-otp \u002F recovery",[1344,220309,220310,220314,220317,220322],{},[1362,220311,220312],{},[941,220313,16586],{},[1362,220315,220316],{},"User forgot, supplies a recovery proof",[1362,220318,220319,220321],{},[941,220320,17160],{},", enabled",[1362,220323,4664],{},[1344,220325,220326,220330,220333,220337],{},[1362,220327,220328],{},[941,220329,17553],{},[1362,220331,220332],{},"Add a tier-2 slot",[1362,220334,220335],{},[941,220336,17160],{},[1362,220338,220339,220341],{},[941,220340,17160],{}," + totp\u002Femail-otp",[1344,220343,220344,220348,220351,220355],{},[1362,220345,220346],{},[941,220347,17634],{},[1362,220349,220350],{},"Remove a tier-2 slot",[1362,220352,220353],{},[941,220354,17160],{},[1362,220356,220357,220341],{},[941,220358,17160],{},[1344,220360,220361,220365,220368,220373],{},[1362,220362,220363],{},[941,220364,18250],{},[1362,220366,220367],{},"Change the tier-3 method",[1362,220369,220370],{},[941,220371,220372],{},"minTier: 2",[1362,220374,220375],{},[941,220376,17160],{},[1344,220378,220379,220385,220388,220392],{},[1362,220380,220381,1261,220383],{},[941,220382,12946],{},[941,220384,135279],{},[1362,220386,220387],{},"Grant or revoke a keyring",[1362,220389,220390],{},[941,220391,17160],{},[1362,220393,220394,220341],{},[941,220395,17160],{},[1344,220397,220398,220402,220408,220412],{},[1362,220399,220400],{},[941,220401,198080],{},[1362,220403,220404,220405,220407],{},"Encrypted ",[941,220406,1402],{}," bundle export",[1362,220409,220410],{},[941,220411,17160],{},[1362,220413,220414,220416],{},[941,220415,17160],{}," + totp\u002Femail-otp, blocks on a shared device",[1344,220418,220419,220423,220426,220430],{},[1362,220420,220421],{},[941,220422,18947],{},[1362,220424,220425],{},"CSV\u002FXLSX\u002FJSON export",[1362,220427,220428,220341],{},[941,220429,17160],{},[1362,220431,220432,220434],{},[941,220433,17160],{},", 2 distinct of totp\u002Femail-otp, blocks on a shared device",[937,220436,220437],{},"Unspecified gates inherit from the preset; overriding one gate never wipes out the rest.",[24204,220439,220441],{"id":220440},"presets-and-overrides","Presets and overrides",[2217,220443,220445],{"className":2219,"code":220444,"language":2221,"meta":1037,"style":1037},"import { createNoydb, PERSONAL_POLICY, STRICT_POLICY } from '@noy-db\u002Fhub'\n\n\u002F\u002F Default — single-user \u002F SMB\nconst db = await createNoydb({ store, policy: PERSONAL_POLICY })\n\n\u002F\u002F Strict — regulated \u002F shared-workstation\nconst db = await createNoydb({ store, policy: STRICT_POLICY })\n\n\u002F\u002F Custom override on top of a preset — unspecified gates keep the preset's values\nconst db = await createNoydb({\n  store,\n  policy: {\n    ...PERSONAL_POLICY,\n    gates: {\n      ...PERSONAL_POLICY.gates,\n      'app:approve-large-payment': { minTier: 2, factors: [{ anyOf: ['totp'] }] },\n    },\n  },\n})\n",[941,220446,220447,220473,220477,220482,220513,220517,220522,220553,220557,220562,220578,220584,220592,220600,220608,220620,220668,220672,220676],{"__ignoreMap":1037},[2225,220448,220449,220451,220453,220455,220457,220459,220461,220463,220465,220467,220469,220471],{"class":2227,"line":2228},[2225,220450,2232],{"class":2231},[2225,220452,2236],{"class":2235},[2225,220454,2744],{"class":2239},[2225,220456,2298],{"class":2235},[2225,220458,18837],{"class":2239},[2225,220460,2298],{"class":2235},[2225,220462,196700],{"class":2239},[2225,220464,2243],{"class":2235},[2225,220466,2246],{"class":2231},[2225,220468,2249],{"class":2235},[2225,220470,1086],{"class":2252},[2225,220472,2256],{"class":2235},[2225,220474,220475],{"class":2227,"line":1041},[2225,220476,2285],{"emptyLinePlaceholder":2284},[2225,220478,220479],{"class":2227,"line":1038},[2225,220480,220481],{"class":2763},"\u002F\u002F Default — single-user \u002F SMB\n",[2225,220483,220484,220486,220488,220490,220492,220494,220496,220498,220500,220502,220504,220506,220509,220511],{"class":2227,"line":2288},[2225,220485,2775],{"class":2774},[2225,220487,2778],{"class":2239},[2225,220489,2781],{"class":2235},[2225,220491,2784],{"class":2231},[2225,220493,2744],{"class":2291},[2225,220495,2789],{"class":2239},[2225,220497,2792],{"class":2235},[2225,220499,7564],{"class":2239},[2225,220501,2298],{"class":2235},[2225,220503,110830],{"class":2307},[2225,220505,2311],{"class":2235},[2225,220507,220508],{"class":2239}," PERSONAL_POLICY ",[2225,220510,2359],{"class":2235},[2225,220512,2362],{"class":2239},[2225,220514,220515],{"class":2227,"line":2304},[2225,220516,2285],{"emptyLinePlaceholder":2284},[2225,220518,220519],{"class":2227,"line":2356},[2225,220520,220521],{"class":2763},"\u002F\u002F Strict — regulated \u002F shared-workstation\n",[2225,220523,220524,220526,220528,220530,220532,220534,220536,220538,220540,220542,220544,220546,220549,220551],{"class":2227,"line":2830},[2225,220525,2775],{"class":2774},[2225,220527,2778],{"class":2239},[2225,220529,2781],{"class":2235},[2225,220531,2784],{"class":2231},[2225,220533,2744],{"class":2291},[2225,220535,2789],{"class":2239},[2225,220537,2792],{"class":2235},[2225,220539,7564],{"class":2239},[2225,220541,2298],{"class":2235},[2225,220543,110830],{"class":2307},[2225,220545,2311],{"class":2235},[2225,220547,220548],{"class":2239}," STRICT_POLICY ",[2225,220550,2359],{"class":2235},[2225,220552,2362],{"class":2239},[2225,220554,220555],{"class":2227,"line":2861},[2225,220556,2285],{"emptyLinePlaceholder":2284},[2225,220558,220559],{"class":2227,"line":4246},[2225,220560,220561],{"class":2763},"\u002F\u002F Custom override on top of a preset — unspecified gates keep the preset's values\n",[2225,220563,220564,220566,220568,220570,220572,220574,220576],{"class":2227,"line":4253},[2225,220565,2775],{"class":2774},[2225,220567,2778],{"class":2239},[2225,220569,2781],{"class":2235},[2225,220571,2784],{"class":2231},[2225,220573,2744],{"class":2291},[2225,220575,2789],{"class":2239},[2225,220577,4200],{"class":2235},[2225,220579,220580,220582],{"class":2227,"line":4259},[2225,220581,15608],{"class":2239},[2225,220583,1707],{"class":2235},[2225,220585,220586,220588,220590],{"class":2227,"line":6548},[2225,220587,18899],{"class":2307},[2225,220589,2311],{"class":2235},[2225,220591,2301],{"class":2235},[2225,220593,220594,220596,220598],{"class":2227,"line":6576},[2225,220595,18908],{"class":2235},[2225,220597,12830],{"class":2239},[2225,220599,1707],{"class":2235},[2225,220601,220602,220604,220606],{"class":2227,"line":6582},[2225,220603,18917],{"class":2307},[2225,220605,2311],{"class":2235},[2225,220607,2301],{"class":2235},[2225,220609,220610,220612,220614,220616,220618],{"class":2227,"line":6587},[2225,220611,18926],{"class":2235},[2225,220613,12830],{"class":2239},[2225,220615,1520],{"class":2235},[2225,220617,12826],{"class":2239},[2225,220619,1707],{"class":2235},[2225,220621,220622,220624,220626,220628,220630,220632,220634,220636,220638,220640,220642,220644,220646,220648,220650,220652,220654,220656,220658,220660,220662,220664,220666],{"class":2227,"line":7174},[2225,220623,18944],{"class":2235},[2225,220625,196790],{"class":2307},[2225,220627,2805],{"class":2235},[2225,220629,2311],{"class":2235},[2225,220631,2236],{"class":2235},[2225,220633,18956],{"class":2307},[2225,220635,2311],{"class":2235},[2225,220637,18961],{"class":6527},[2225,220639,2298],{"class":2235},[2225,220641,17534],{"class":2307},[2225,220643,2311],{"class":2235},[2225,220645,8667],{"class":2239},[2225,220647,2792],{"class":2235},[2225,220649,18974],{"class":2307},[2225,220651,2311],{"class":2235},[2225,220653,8667],{"class":2239},[2225,220655,2805],{"class":2235},[2225,220657,333],{"class":2252},[2225,220659,2805],{"class":2235},[2225,220661,8890],{"class":2239},[2225,220663,2359],{"class":2235},[2225,220665,8890],{"class":2239},[2225,220667,19008],{"class":2235},[2225,220669,220670],{"class":2227,"line":7180},[2225,220671,8324],{"class":2235},[2225,220673,220674],{"class":2227,"line":7220},[2225,220675,19017],{"class":2235},[2225,220677,220678,220680],{"class":2227,"line":7225},[2225,220679,2359],{"class":2235},[2225,220681,2362],{"class":2239},[24204,220683,12972],{"id":12969},[2217,220685,220687],{"className":2219,"code":220686,"language":2221,"meta":1037,"style":1037},"import { PolicyDeniedError } from '@noy-db\u002Fhub'\n\ntry {\n  await db.checkGate('acme', 'app:approve-large-payment', {\n    factors: [{ kind: 'totp', payload: { code: '123456' }, mintedAt: new Date().toISOString() }],\n  })\n  await invoices.put('inv-002', payment)\n} catch (err) {\n  if (err instanceof PolicyDeniedError) {\n    \u002F\u002F err.gate, err.required, and err.reason:\n    \u002F\u002F   'insufficient-tier' | 'missing-factor' | 'stale-proof' | 'disabled' | 'shared-device-blocked'\n  }\n}\n",[941,220688,220689,220707,220711,220717,220747,220809,220815,220839,220849,220865,220870,220875,220879],{"__ignoreMap":1037},[2225,220690,220691,220693,220695,220697,220699,220701,220703,220705],{"class":2227,"line":2228},[2225,220692,2232],{"class":2231},[2225,220694,2236],{"class":2235},[2225,220696,18842],{"class":2239},[2225,220698,2243],{"class":2235},[2225,220700,2246],{"class":2231},[2225,220702,2249],{"class":2235},[2225,220704,1086],{"class":2252},[2225,220706,2256],{"class":2235},[2225,220708,220709],{"class":2227,"line":1041},[2225,220710,2285],{"emptyLinePlaceholder":2284},[2225,220712,220713,220715],{"class":2227,"line":1038},[2225,220714,7554],{"class":2231},[2225,220716,2301],{"class":2235},[2225,220718,220719,220721,220723,220725,220727,220729,220731,220733,220735,220737,220739,220741,220743,220745],{"class":2227,"line":2288},[2225,220720,7561],{"class":2231},[2225,220722,2842],{"class":2239},[2225,220724,1520],{"class":2235},[2225,220726,12982],{"class":2291},[2225,220728,2789],{"class":2307},[2225,220730,2805],{"class":2235},[2225,220732,6450],{"class":2252},[2225,220734,2805],{"class":2235},[2225,220736,2298],{"class":2235},[2225,220738,2249],{"class":2235},[2225,220740,196790],{"class":2252},[2225,220742,2805],{"class":2235},[2225,220744,2298],{"class":2235},[2225,220746,2301],{"class":2235},[2225,220748,220749,220751,220753,220755,220757,220759,220761,220763,220765,220767,220769,220771,220773,220775,220777,220779,220781,220783,220785,220787,220789,220791,220793,220795,220797,220799,220801,220803,220805,220807],{"class":2227,"line":2304},[2225,220750,19073],{"class":2307},[2225,220752,2311],{"class":2235},[2225,220754,8667],{"class":2307},[2225,220756,2792],{"class":2235},[2225,220758,8285],{"class":2307},[2225,220760,2311],{"class":2235},[2225,220762,2249],{"class":2235},[2225,220764,333],{"class":2252},[2225,220766,2805],{"class":2235},[2225,220768,2298],{"class":2235},[2225,220770,19094],{"class":2307},[2225,220772,2311],{"class":2235},[2225,220774,2236],{"class":2235},[2225,220776,19101],{"class":2307},[2225,220778,2311],{"class":2235},[2225,220780,2249],{"class":2235},[2225,220782,19108],{"class":2252},[2225,220784,2805],{"class":2235},[2225,220786,10965],{"class":2235},[2225,220788,19115],{"class":2307},[2225,220790,2311],{"class":2235},[2225,220792,8130],{"class":2235},[2225,220794,19122],{"class":2291},[2225,220796,4228],{"class":2307},[2225,220798,1520],{"class":2235},[2225,220800,19129],{"class":2291},[2225,220802,19132],{"class":2307},[2225,220804,2359],{"class":2235},[2225,220806,8803],{"class":2307},[2225,220808,1707],{"class":2235},[2225,220810,220811,220813],{"class":2227,"line":2356},[2225,220812,19143],{"class":2235},[2225,220814,2362],{"class":2307},[2225,220816,220817,220819,220821,220823,220825,220827,220829,220831,220833,220835,220837],{"class":2227,"line":2830},[2225,220818,7561],{"class":2231},[2225,220820,6503],{"class":2239},[2225,220822,1520],{"class":2235},[2225,220824,3161],{"class":2291},[2225,220826,2789],{"class":2307},[2225,220828,2805],{"class":2235},[2225,220830,136114],{"class":2252},[2225,220832,2805],{"class":2235},[2225,220834,2298],{"class":2235},[2225,220836,138705],{"class":2239},[2225,220838,2362],{"class":2307},[2225,220840,220841,220843,220845,220847],{"class":2227,"line":2861},[2225,220842,2359],{"class":2235},[2225,220844,7598],{"class":2231},[2225,220846,7601],{"class":2239},[2225,220848,4200],{"class":2235},[2225,220850,220851,220853,220855,220857,220859,220861,220863],{"class":2227,"line":4246},[2225,220852,7608],{"class":2231},[2225,220854,953],{"class":2307},[2225,220856,7617],{"class":2239},[2225,220858,19172],{"class":2235},[2225,220860,18842],{"class":6342},[2225,220862,8495],{"class":2307},[2225,220864,4200],{"class":2235},[2225,220866,220867],{"class":2227,"line":4253},[2225,220868,220869],{"class":2763},"    \u002F\u002F err.gate, err.required, and err.reason:\n",[2225,220871,220872],{"class":2227,"line":4259},[2225,220873,220874],{"class":2763},"    \u002F\u002F   'insufficient-tier' | 'missing-factor' | 'stale-proof' | 'disabled' | 'shared-device-blocked'\n",[2225,220876,220877],{"class":2227,"line":6548},[2225,220878,7632],{"class":2235},[2225,220880,220881],{"class":2227,"line":6576},[2225,220882,7351],{"class":2235},[24204,220884,220886],{"id":220885},"per-keyring-policy-override-reserved","Per-keyring policy override — reserved",[937,220888,220889,220890,220893,220894,220897,220898,220900],{},"The on-disk keyring format has a ",[941,220891,220892],{},"policy?: VaultPolicy"," field reserved for a future per-keyring override (a keyring-level policy that can only ",[1092,220895,220896],{},"strengthen",", never weaken, the vault-level floor). Today that field is parsed and round-tripped but not enforced — the vault's ",[941,220899,12784],{}," document is the sole source of truth. No API exists yet for the merge behavior; adding it later will not require a vault-format migration.",[1591,220902,220904],{"id":220903},"rotate-and-recover-the-secret","Rotate and recover the secret",[24204,220906,220908],{"id":220907},"rotate-user-remembers-the-old-secret","Rotate (user remembers the old secret)",[2217,220910,220912],{"className":2219,"code":220911,"language":2221,"meta":1037,"style":1037},"await db.team.rotateSecret('acme', {\n  oldSecret: 'correct horse battery staple printer toaster',\n  newSecret: 'glasses cabinet bicycle umbrella thunder velvet',\n}, {\n  factors: [{ kind: 'totp', payload: { code: '123456' } }],\n})\n",[941,220913,220914,220940,220955,220970,220976,221022],{"__ignoreMap":1037},[2225,220915,220916,220918,220920,220922,220924,220926,220928,220930,220932,220934,220936,220938],{"class":2227,"line":2228},[2225,220917,6500],{"class":2231},[2225,220919,2842],{"class":2239},[2225,220921,1520],{"class":2235},[2225,220923,868],{"class":2239},[2225,220925,1520],{"class":2235},[2225,220927,15216],{"class":2291},[2225,220929,2789],{"class":2239},[2225,220931,2805],{"class":2235},[2225,220933,6450],{"class":2252},[2225,220935,2805],{"class":2235},[2225,220937,2298],{"class":2235},[2225,220939,2301],{"class":2235},[2225,220941,220942,220945,220947,220949,220951,220953],{"class":2227,"line":1041},[2225,220943,220944],{"class":2307},"  oldSecret",[2225,220946,2311],{"class":2235},[2225,220948,2249],{"class":2235},[2225,220950,219018],{"class":2252},[2225,220952,2805],{"class":2235},[2225,220954,1707],{"class":2235},[2225,220956,220957,220960,220962,220964,220966,220968],{"class":2227,"line":1038},[2225,220958,220959],{"class":2307},"  newSecret",[2225,220961,2311],{"class":2235},[2225,220963,2249],{"class":2235},[2225,220965,197163],{"class":2252},[2225,220967,2805],{"class":2235},[2225,220969,1707],{"class":2235},[2225,220971,220972,220974],{"class":2227,"line":2288},[2225,220973,33127],{"class":2235},[2225,220975,2301],{"class":2235},[2225,220977,220978,220980,220982,220984,220986,220988,220990,220992,220994,220996,220998,221000,221002,221004,221006,221008,221010,221012,221014,221016,221018,221020],{"class":2227,"line":2304},[2225,220979,13002],{"class":2307},[2225,220981,2311],{"class":2235},[2225,220983,8667],{"class":2239},[2225,220985,2792],{"class":2235},[2225,220987,8285],{"class":2307},[2225,220989,2311],{"class":2235},[2225,220991,2249],{"class":2235},[2225,220993,333],{"class":2252},[2225,220995,2805],{"class":2235},[2225,220997,2298],{"class":2235},[2225,220999,19094],{"class":2307},[2225,221001,2311],{"class":2235},[2225,221003,2236],{"class":2235},[2225,221005,19101],{"class":2307},[2225,221007,2311],{"class":2235},[2225,221009,2249],{"class":2235},[2225,221011,19108],{"class":2252},[2225,221013,2805],{"class":2235},[2225,221015,2243],{"class":2235},[2225,221017,2243],{"class":2235},[2225,221019,8803],{"class":2239},[2225,221021,1707],{"class":2235},[2225,221023,221024,221026],{"class":2227,"line":2356},[2225,221025,2359],{"class":2235},[2225,221027,2362],{"class":2239},[937,221029,221030,221031,221033,221034,221036],{},"The hub re-derives the old KEK from ",[941,221032,16457],{}," (this also validates it — a wrong secret throws ",[941,221035,10290],{},"), validates the new secret, generates a fresh salt and KEK, rewraps every DEK, and persists.",[937,221038,221039,221042,221043,221045],{},[950,221040,221041],{},"Tier-2 slots are dropped by default on rotation"," — each slot wraps material derived under the old KEK, and the hub has no way to re-derive that without the method-specific ceremony. To preserve a slot across rotation, supply a ",[941,221044,16469],{}," entry keyed by slot id; the ceremony re-derives the slot's wrapping under the new keyring:",[2217,221047,221049],{"className":2219,"code":221048,"language":2221,"meta":1037,"style":1037},"await db.team.rotateSecret('acme', {\n  oldSecret, newSecret,\n  slotCeremonies: {\n    'webauthn-yubikey-blue': async (ctx) => webAuthnSlotRewrapCeremony(\u002F* ... *\u002F)(ctx),\n  },\n})\n\u002F\u002F → slots omitted from `slotCeremonies` are dropped; re-enroll them via db.enrollAuthenticator\n",[941,221050,221051,221077,221087,221096,221129,221133,221139],{"__ignoreMap":1037},[2225,221052,221053,221055,221057,221059,221061,221063,221065,221067,221069,221071,221073,221075],{"class":2227,"line":2228},[2225,221054,6500],{"class":2231},[2225,221056,2842],{"class":2239},[2225,221058,1520],{"class":2235},[2225,221060,868],{"class":2239},[2225,221062,1520],{"class":2235},[2225,221064,15216],{"class":2291},[2225,221066,2789],{"class":2239},[2225,221068,2805],{"class":2235},[2225,221070,6450],{"class":2252},[2225,221072,2805],{"class":2235},[2225,221074,2298],{"class":2235},[2225,221076,2301],{"class":2235},[2225,221078,221079,221081,221083,221085],{"class":2227,"line":1041},[2225,221080,220944],{"class":2239},[2225,221082,2298],{"class":2235},[2225,221084,197156],{"class":2239},[2225,221086,1707],{"class":2235},[2225,221088,221089,221092,221094],{"class":2227,"line":1038},[2225,221090,221091],{"class":2307},"  slotCeremonies",[2225,221093,2311],{"class":2235},[2225,221095,2301],{"class":2235},[2225,221097,221098,221100,221102,221104,221106,221108,221110,221112,221114,221116,221119,221121,221124,221127],{"class":2227,"line":2288},[2225,221099,76714],{"class":2235},[2225,221101,219570],{"class":2307},[2225,221103,2805],{"class":2235},[2225,221105,2311],{"class":2235},[2225,221107,38717],{"class":2774},[2225,221109,953],{"class":2235},[2225,221111,157376],{"class":7052},[2225,221113,1381],{"class":2235},[2225,221115,8116],{"class":2774},[2225,221117,221118],{"class":2291}," webAuthnSlotRewrapCeremony",[2225,221120,2789],{"class":2239},[2225,221122,221123],{"class":2763},"\u002F* ... *\u002F",[2225,221125,221126],{"class":2239},")(ctx)",[2225,221128,1707],{"class":2235},[2225,221130,221131],{"class":2227,"line":2304},[2225,221132,19017],{"class":2235},[2225,221134,221135,221137],{"class":2227,"line":2356},[2225,221136,2359],{"class":2235},[2225,221138,2362],{"class":2239},[2225,221140,221141],{"class":2227,"line":2830},[2225,221142,221143],{"class":2763},"\u002F\u002F → slots omitted from `slotCeremonies` are dropped; re-enroll them via db.enrollAuthenticator\n",[24204,221145,221147],{"id":221146},"recover-user-forgot-the-secret","Recover (user forgot the secret)",[937,221149,221150,221151,221153,221154,221156],{},"The vault needs at least one ",[950,221152,19592],{}," enrolled; without one, ",[941,221155,16586],{}," is disabled and recovery is impossible.",[1338,221158,221159,221170],{},[1341,221160,221161],{},[1344,221162,221163,221166,221168],{},[1347,221164,221165],{},"Profile",[1347,221167,106623],{},[1347,221169,48731],{},[1357,221171,221172,221184,221201,221215],{},[1344,221173,221174,221178,221181],{},[1362,221175,221176],{},[950,221177,19603],{},[1362,221179,221180],{},"Single-use codes; a matching code unwraps the cached DEKs, then rewraps them under a fresh KEK.",[1362,221182,221183],{},"Implemented end to end.",[1344,221185,221186,221190,221199],{},[1362,221187,221188],{},[950,221189,325],{},[1362,221191,221192,221193,9483,221195,19618,221197,19621],{},"K-of-N shares reconstruct the DEK set; requires a ",[941,221194,19615],{},[941,221196,45751],{},[941,221198,12780],{},[1362,221200,221183],{},[1344,221202,221203,221207,221210],{},[1362,221204,221205],{},[950,221206,19627],{},[1362,221208,221209],{},"Reserved for a 2-of-3 email\u002FPIN\u002Fpaper recovery combination.",[1362,221211,221212,221213,1520],{},"No API yet — calling it throws ",[941,221214,16540],{},[1344,221216,221217,221221,221224],{},[1362,221218,221219],{},[950,221220,19630],{},[1362,221222,221223],{},"Reserved for an admin-issued reset that preserves zero-knowledge.",[1362,221225,221212,221226,1520],{},[941,221227,16540],{},[2217,221229,221231],{"className":2219,"code":221230,"language":2221,"meta":1037,"style":1037},"await db.team.recoverSecret('acme', {\n  newSecret: 'glasses cabinet bicycle umbrella thunder velvet',\n  recoveryProof: { profile: 'paper', payload: { code: 'ABCD-EFGH-IJKL-MNOP-QRST-UVWX-YZ12' } },\n})\n\u002F\u002F → { newCodes: [...] }  — by default the remaining paper codes are burned and reissued;\n\u002F\u002F   pass { rotateRemainingCodes: false } to keep the unused codes valid\n",[941,221232,221233,221259,221273,221315,221321,221326],{"__ignoreMap":1037},[2225,221234,221235,221237,221239,221241,221243,221245,221247,221249,221251,221253,221255,221257],{"class":2227,"line":2228},[2225,221236,6500],{"class":2231},[2225,221238,2842],{"class":2239},[2225,221240,1520],{"class":2235},[2225,221242,868],{"class":2239},[2225,221244,1520],{"class":2235},[2225,221246,16494],{"class":2291},[2225,221248,2789],{"class":2239},[2225,221250,2805],{"class":2235},[2225,221252,6450],{"class":2252},[2225,221254,2805],{"class":2235},[2225,221256,2298],{"class":2235},[2225,221258,2301],{"class":2235},[2225,221260,221261,221263,221265,221267,221269,221271],{"class":2227,"line":1041},[2225,221262,220959],{"class":2307},[2225,221264,2311],{"class":2235},[2225,221266,2249],{"class":2235},[2225,221268,197163],{"class":2252},[2225,221270,2805],{"class":2235},[2225,221272,1707],{"class":2235},[2225,221274,221275,221278,221280,221282,221284,221286,221288,221290,221292,221294,221296,221298,221300,221302,221304,221306,221309,221311,221313],{"class":2227,"line":1038},[2225,221276,221277],{"class":2307},"  recoveryProof",[2225,221279,2311],{"class":2235},[2225,221281,2236],{"class":2235},[2225,221283,72016],{"class":2307},[2225,221285,2311],{"class":2235},[2225,221287,2249],{"class":2235},[2225,221289,19603],{"class":2252},[2225,221291,2805],{"class":2235},[2225,221293,2298],{"class":2235},[2225,221295,19094],{"class":2307},[2225,221297,2311],{"class":2235},[2225,221299,2236],{"class":2235},[2225,221301,19101],{"class":2307},[2225,221303,2311],{"class":2235},[2225,221305,2249],{"class":2235},[2225,221307,221308],{"class":2252},"ABCD-EFGH-IJKL-MNOP-QRST-UVWX-YZ12",[2225,221310,2805],{"class":2235},[2225,221312,2243],{"class":2235},[2225,221314,2353],{"class":2235},[2225,221316,221317,221319],{"class":2227,"line":2288},[2225,221318,2359],{"class":2235},[2225,221320,2362],{"class":2239},[2225,221322,221323],{"class":2227,"line":2304},[2225,221324,221325],{"class":2763},"\u002F\u002F → { newCodes: [...] }  — by default the remaining paper codes are burned and reissued;\n",[2225,221327,221328],{"class":2227,"line":2356},[2225,221329,221330],{"class":2763},"\u002F\u002F   pass { rotateRemainingCodes: false } to keep the unused codes valid\n",[937,221332,221333,221334,221336],{},"Enroll recovery at vault creation so ",[941,221335,16586],{}," is never left disabled by accident:",[2217,221338,221340],{"className":2219,"code":221339,"language":2221,"meta":1037,"style":1037},"await createNoydb({\n  store,\n  recovery: [\n    { profile: 'paper', codes: 10 },\n    \u002F\u002F or: { profile: 'shamir', k: 2, n: 3, trustees: ['alice@acme.com', 'bob@acme.com'] },\n  ],\n})\n",[941,221341,221342,221352,221358,221366,221390,221395,221401],{"__ignoreMap":1037},[2225,221343,221344,221346,221348,221350],{"class":2227,"line":2228},[2225,221345,6500],{"class":2231},[2225,221347,2744],{"class":2291},[2225,221349,2789],{"class":2239},[2225,221351,4200],{"class":2235},[2225,221353,221354,221356],{"class":2227,"line":1041},[2225,221355,15608],{"class":2239},[2225,221357,1707],{"class":2235},[2225,221359,221360,221362,221364],{"class":2227,"line":1038},[2225,221361,72007],{"class":2307},[2225,221363,2311],{"class":2235},[2225,221365,32491],{"class":2239},[2225,221367,221368,221370,221372,221374,221376,221378,221380,221382,221384,221386,221388],{"class":2227,"line":2288},[2225,221369,32496],{"class":2235},[2225,221371,72016],{"class":2307},[2225,221373,2311],{"class":2235},[2225,221375,2249],{"class":2235},[2225,221377,19603],{"class":2252},[2225,221379,2805],{"class":2235},[2225,221381,2298],{"class":2235},[2225,221383,45525],{"class":2307},[2225,221385,2311],{"class":2235},[2225,221387,45554],{"class":6527},[2225,221389,2353],{"class":2235},[2225,221391,221392],{"class":2227,"line":2304},[2225,221393,221394],{"class":2763},"    \u002F\u002F or: { profile: 'shamir', k: 2, n: 3, trustees: ['alice@acme.com', 'bob@acme.com'] },\n",[2225,221396,221397,221399],{"class":2227,"line":2356},[2225,221398,32588],{"class":2239},[2225,221400,1707],{"class":2235},[2225,221402,221403,221405],{"class":2227,"line":2830},[2225,221404,2359],{"class":2235},[2225,221406,2362],{"class":2239},[937,221408,150155,221409,221411,221412,221414,221415,6789,221417,1520],{},[941,221410,321],{}," is omitted and the ",[941,221413,16586],{}," gate is not explicitly disabled, ",[941,221416,12780],{},[941,221418,20826],{},[1591,221420,221422],{"id":221421},"tier-1-echo-secret","Tier 1 — Echo secret",[937,221424,221425],{},[1092,221426,221427,221428,1520],{},"Available since ",[941,221429,11337],{},[937,221431,221432,221433,221435,221436,2311],{},"Selected at vault creation with ",[941,221434,55889],{},", this mode makes the tier-1 prompt resistant to phishing. The secret is ",[950,221437,221438],{},"one memorable sentence enrolled as three parts",[2217,221440,221443],{"className":221441,"code":221442,"language":5672},[5670],"prompt (the user types)  →  echo (the vault reveals)  →  key (the user types)\n\"mi chiamo vicio\"        →  \"ma da piccolo al santanna mi chiamavano\"  →  \"ciccio\"\n",[941,221444,221442],{"__ignoreMap":1037},[937,221446,221447,221448,221450,221451,221453,221454,221456,221457,221460,221461,221463],{},"At unlock the user types only the ",[950,221449,47651],{},". The vault answers by revealing the ",[950,221452,55896],{}," — the middle of the user's own sentence, which nothing but a keyring holding that secret can produce. The user recognizes it and finishes with the ",[950,221455,8364],{},". This is ",[950,221458,221459],{},"mutual authentication",": the vault proves it holds the secret ",[1092,221462,11357],{}," the user hands over the rest of theirs. A phishing page that replays a convincing-looking unlock screen has nothing to show at the reveal step, and there is no single-field secret entry it could imitate instead — an echo keyring cannot be opened by any single typed string.",[937,221465,221466,221467,221470],{},"The echo doubles as a ",[950,221468,221469],{},"memory aid",": tier 1 is typed so rarely that forgetting it is the dominant real-world failure, and the revealed middle of your own sentence is a retrieval cue for its ending.",[937,221472,221473],{},"Echo is orthogonal to the other two modes: it is still tier 1, still derives the KEK, and tier-2 slots and tier-3 PINs enroll from an echo session exactly as from a standard one.",[24204,221475,221476],{"id":112267},"Enroll",[2217,221478,221480],{"className":2219,"code":221479,"language":2221,"meta":1037,"style":1037},"import { createNoydb } from '@noy-db\u002Fhub'\n\nconst db = await createNoydb({\n  store,\n  user: 'vicio',\n  secretMode: 'echo',\n  secret: {\n    prompt: 'mi chiamo vicio',\n    echo: 'ma da piccolo al santanna mi chiamavano',\n    key: 'ciccio',\n  },\n  echoMaskHint: 'first-letters',   \u002F\u002F optional: how the player should mask the echo on screen\n  validateSecret: true,\n})\n\nconst vault = await db.openVault('acme')   \u002F\u002F the keyring — and its echo block — is written here\n",[941,221481,221482,221500,221504,221520,221526,221540,221554,221562,221577,221592,221607,221611,221628,221638,221644,221648],{"__ignoreMap":1037},[2225,221483,221484,221486,221488,221490,221492,221494,221496,221498],{"class":2227,"line":2228},[2225,221485,2232],{"class":2231},[2225,221487,2236],{"class":2235},[2225,221489,2744],{"class":2239},[2225,221491,2243],{"class":2235},[2225,221493,2246],{"class":2231},[2225,221495,2249],{"class":2235},[2225,221497,1086],{"class":2252},[2225,221499,2256],{"class":2235},[2225,221501,221502],{"class":2227,"line":1041},[2225,221503,2285],{"emptyLinePlaceholder":2284},[2225,221505,221506,221508,221510,221512,221514,221516,221518],{"class":2227,"line":1038},[2225,221507,2775],{"class":2774},[2225,221509,2778],{"class":2239},[2225,221511,2781],{"class":2235},[2225,221513,2784],{"class":2231},[2225,221515,2744],{"class":2291},[2225,221517,2789],{"class":2239},[2225,221519,4200],{"class":2235},[2225,221521,221522,221524],{"class":2227,"line":2288},[2225,221523,15608],{"class":2239},[2225,221525,1707],{"class":2235},[2225,221527,221528,221530,221532,221534,221536,221538],{"class":2227,"line":2304},[2225,221529,4205],{"class":2307},[2225,221531,2311],{"class":2235},[2225,221533,2249],{"class":2235},[2225,221535,77062],{"class":2252},[2225,221537,2805],{"class":2235},[2225,221539,1707],{"class":2235},[2225,221541,221542,221544,221546,221548,221550,221552],{"class":2227,"line":2356},[2225,221543,71905],{"class":2307},[2225,221545,2311],{"class":2235},[2225,221547,2249],{"class":2235},[2225,221549,55896],{"class":2252},[2225,221551,2805],{"class":2235},[2225,221553,1707],{"class":2235},[2225,221555,221556,221558,221560],{"class":2227,"line":2830},[2225,221557,10423],{"class":2307},[2225,221559,2311],{"class":2235},[2225,221561,2301],{"class":2235},[2225,221563,221564,221567,221569,221571,221573,221575],{"class":2227,"line":2861},[2225,221565,221566],{"class":2307},"    prompt",[2225,221568,2311],{"class":2235},[2225,221570,2249],{"class":2235},[2225,221572,76684],{"class":2252},[2225,221574,2805],{"class":2235},[2225,221576,1707],{"class":2235},[2225,221578,221579,221582,221584,221586,221588,221590],{"class":2227,"line":4246},[2225,221580,221581],{"class":2307},"    echo",[2225,221583,2311],{"class":2235},[2225,221585,2249],{"class":2235},[2225,221587,76701],{"class":2252},[2225,221589,2805],{"class":2235},[2225,221591,1707],{"class":2235},[2225,221593,221594,221597,221599,221601,221603,221605],{"class":2227,"line":4253},[2225,221595,221596],{"class":2307},"    key",[2225,221598,2311],{"class":2235},[2225,221600,2249],{"class":2235},[2225,221602,76717],{"class":2252},[2225,221604,2805],{"class":2235},[2225,221606,1707],{"class":2235},[2225,221608,221609],{"class":2227,"line":4259},[2225,221610,19017],{"class":2235},[2225,221612,221613,221615,221617,221619,221621,221623,221625],{"class":2227,"line":6548},[2225,221614,77093],{"class":2307},[2225,221616,2311],{"class":2235},[2225,221618,2249],{"class":2235},[2225,221620,77100],{"class":2252},[2225,221622,2805],{"class":2235},[2225,221624,2298],{"class":2235},[2225,221626,221627],{"class":2763},"   \u002F\u002F optional: how the player should mask the echo on screen\n",[2225,221629,221630,221632,221634,221636],{"class":2227,"line":6576},[2225,221631,77425],{"class":2307},[2225,221633,2311],{"class":2235},[2225,221635,2322],{"class":2321},[2225,221637,1707],{"class":2235},[2225,221639,221640,221642],{"class":2227,"line":6582},[2225,221641,2359],{"class":2235},[2225,221643,2362],{"class":2239},[2225,221645,221646],{"class":2227,"line":6587},[2225,221647,2285],{"emptyLinePlaceholder":2284},[2225,221649,221650,221652,221654,221656,221658,221660,221662,221664,221666,221668,221670,221672,221674],{"class":2227,"line":7174},[2225,221651,2775],{"class":2774},[2225,221653,2835],{"class":2239},[2225,221655,2781],{"class":2235},[2225,221657,2784],{"class":2231},[2225,221659,2842],{"class":2239},[2225,221661,1520],{"class":2235},[2225,221663,2847],{"class":2291},[2225,221665,2789],{"class":2239},[2225,221667,2805],{"class":2235},[2225,221669,6450],{"class":2252},[2225,221671,2805],{"class":2235},[2225,221673,71781],{"class":2239},[2225,221675,221676],{"class":2763},"\u002F\u002F the keyring — and its echo block — is written here\n",[937,221678,221679,196305,221681,221684,221685,221687,221688,221690,221691,20300,221693,221695,221696,1261,221699,1261,221701,221704],{},[941,221680,10695],{},[941,221682,221683],{},"{ prompt, echo, key }"," struct instead of a string, and the two must agree: a string under ",[941,221686,55889],{},", or a struct without it, throws ",[941,221689,12881],{}," before any store call. ",[941,221692,55889],{},[941,221694,13515],{}," (that is managed mode), and ",[941,221697,221698],{},"deviceSeal",[941,221700,77022],{},[941,221702,221703],{},"echoSecretPolicy"," are rejected outside echo mode.",[937,221706,221707],{},"Reopening a vault when the app already holds all three parts needs no ceremony — pass the same struct again:",[2217,221709,221711],{"className":2219,"code":221710,"language":2221,"meta":1037,"style":1037},"const db = await createNoydb({ store, user: 'vicio', secretMode: 'echo', secret: parts })\n",[941,221712,221713],{"__ignoreMap":1037},[2225,221714,221715,221717,221719,221721,221723,221725,221727,221729,221731,221733,221735,221737,221739,221741,221743,221745,221747,221749,221751,221753,221755,221757,221759,221761,221763,221765],{"class":2227,"line":2228},[2225,221716,2775],{"class":2774},[2225,221718,2778],{"class":2239},[2225,221720,2781],{"class":2235},[2225,221722,2784],{"class":2231},[2225,221724,2744],{"class":2291},[2225,221726,2789],{"class":2239},[2225,221728,2792],{"class":2235},[2225,221730,7564],{"class":2239},[2225,221732,2298],{"class":2235},[2225,221734,2795],{"class":2307},[2225,221736,2311],{"class":2235},[2225,221738,2249],{"class":2235},[2225,221740,77062],{"class":2252},[2225,221742,2805],{"class":2235},[2225,221744,2298],{"class":2235},[2225,221746,77069],{"class":2307},[2225,221748,2311],{"class":2235},[2225,221750,2249],{"class":2235},[2225,221752,55896],{"class":2252},[2225,221754,2805],{"class":2235},[2225,221756,2298],{"class":2235},[2225,221758,2810],{"class":2307},[2225,221760,2311],{"class":2235},[2225,221762,76668],{"class":2239},[2225,221764,2359],{"class":2235},[2225,221766,2362],{"class":2239},[937,221768,221769],{},"That is the unattended path (tests, servers, a headless job). Interactive apps use the ceremony below, because the point of echo mode is the reveal.",[24204,221771,221773],{"id":221772},"the-reveal-blob-portable-sealed-or-none","The reveal blob: portable, sealed, or none",[937,221775,221776,221777,2311],{},"The echo has to come from somewhere at unlock time. Each keyring records one ",[950,221778,221779],{},"reveal policy",[1338,221781,221782,221796],{},[1341,221783,221784],{},[1344,221785,221786,221790,221793],{},[1347,221787,221788,8285],{},[941,221789,117298],{},[1347,221791,221792],{},"Where the echo lives",[1347,221794,221795],{},"Enrolled by",[1357,221797,221798,221810,221830],{},[1344,221799,221800,221804,221807],{},[1362,221801,221802],{},[941,221803,55959],{},[1362,221805,221806],{},"encrypted in the keyring under a key derived from the prompt — travels with the file",[1362,221808,221809],{},"the default for live keyrings and for pod recipients",[1344,221811,221812,221816,221819],{},[1362,221813,221814],{},[941,221815,55954],{},[1362,221817,221818],{},"sealed by a device-local key store; the keyring holds opaque bytes plus the sealing provider's id",[1362,221820,221821,221822,46975,221824,36059,221826,221829],{},"passing ",[941,221823,221698],{},[941,221825,12780],{},[941,221827,221828],{},"echoOptions.deviceSeal"," on rotation)",[1344,221831,221832,221836,221839],{},[1362,221833,221834],{},[941,221835,8292],{},[1362,221837,221838],{},"nowhere — the ceremony asks the user to type the echo, and verifies it",[1362,221840,221841,221842],{},"opting a pod recipient into ",[941,221843,221844],{},"reveal: 'none'",[2217,221846,221848],{"className":2219,"code":221847,"language":2221,"meta":1037,"style":1037},"import { createNoydb, MemoryDeviceSeal } from '@noy-db\u002Fhub'\n\nconst db = await createNoydb({\n  store, user: 'vicio', secretMode: 'echo', secret: parts,\n  deviceSeal: new MemoryDeviceSeal({ id: 'device-1' }),\n})\n\u002F\u002F → the owner keyring enrolls reveal: 'sealed', pinned to provider id 'device-1'\n",[941,221849,221850,221873,221877,221893,221931,221963,221969],{"__ignoreMap":1037},[2225,221851,221852,221854,221856,221858,221860,221863,221865,221867,221869,221871],{"class":2227,"line":2228},[2225,221853,2232],{"class":2231},[2225,221855,2236],{"class":2235},[2225,221857,2744],{"class":2239},[2225,221859,2298],{"class":2235},[2225,221861,221862],{"class":2239}," MemoryDeviceSeal",[2225,221864,2243],{"class":2235},[2225,221866,2246],{"class":2231},[2225,221868,2249],{"class":2235},[2225,221870,1086],{"class":2252},[2225,221872,2256],{"class":2235},[2225,221874,221875],{"class":2227,"line":1041},[2225,221876,2285],{"emptyLinePlaceholder":2284},[2225,221878,221879,221881,221883,221885,221887,221889,221891],{"class":2227,"line":1038},[2225,221880,2775],{"class":2774},[2225,221882,2778],{"class":2239},[2225,221884,2781],{"class":2235},[2225,221886,2784],{"class":2231},[2225,221888,2744],{"class":2291},[2225,221890,2789],{"class":2239},[2225,221892,4200],{"class":2235},[2225,221894,221895,221897,221899,221901,221903,221905,221907,221909,221911,221913,221915,221917,221919,221921,221923,221925,221927,221929],{"class":2227,"line":2288},[2225,221896,15608],{"class":2239},[2225,221898,2298],{"class":2235},[2225,221900,2795],{"class":2307},[2225,221902,2311],{"class":2235},[2225,221904,2249],{"class":2235},[2225,221906,77062],{"class":2252},[2225,221908,2805],{"class":2235},[2225,221910,2298],{"class":2235},[2225,221912,77069],{"class":2307},[2225,221914,2311],{"class":2235},[2225,221916,2249],{"class":2235},[2225,221918,55896],{"class":2252},[2225,221920,2805],{"class":2235},[2225,221922,2298],{"class":2235},[2225,221924,2810],{"class":2307},[2225,221926,2311],{"class":2235},[2225,221928,77086],{"class":2239},[2225,221930,1707],{"class":2235},[2225,221932,221933,221936,221938,221940,221942,221944,221946,221948,221950,221952,221955,221957,221959,221961],{"class":2227,"line":2304},[2225,221934,221935],{"class":2307},"  deviceSeal",[2225,221937,2311],{"class":2235},[2225,221939,8130],{"class":2235},[2225,221941,221862],{"class":2291},[2225,221943,2789],{"class":2239},[2225,221945,2792],{"class":2235},[2225,221947,7070],{"class":2307},[2225,221949,2311],{"class":2235},[2225,221951,2249],{"class":2235},[2225,221953,221954],{"class":2252},"device-1",[2225,221956,2805],{"class":2235},[2225,221958,2243],{"class":2235},[2225,221960,1381],{"class":2239},[2225,221962,1707],{"class":2235},[2225,221964,221965,221967],{"class":2227,"line":2356},[2225,221966,2359],{"class":2235},[2225,221968,2362],{"class":2239},[2225,221970,221971],{"class":2227,"line":2830},[2225,221972,221973],{"class":2763},"\u002F\u002F → the owner keyring enrolls reveal: 'sealed', pinned to provider id 'device-1'\n",[937,221975,221976,7356,221979,221982,221983,221985,221986,221989,221990,221992,221993,221995],{},[941,221977,221978],{},"NoydbDeviceSeal",[941,221980,221981],{},"{ id, seal(bytes), unseal(bytes) }"," — the same shape as ",[941,221984,14350],{},", deliberately a distinct name because it seals only the echo reveal blob, never the secret. ",[941,221987,221988],{},"MemoryDeviceSeal"," is the only implementation shipped in ",[941,221991,1086],{}," (in-memory, test-only); real ones belong in the app or an ",[941,221994,1783],{}," binding, over IndexedDB, a file, or an OS keychain — the hub stays portable and owns no storage.",[24204,221997,221999],{"id":221998},"the-unlock-ceremony","The unlock ceremony",[2217,222001,222003],{"className":2219,"code":222002,"language":2221,"meta":1037,"style":1037},"import { beginEchoUnlock, WrongPromptError, WrongEchoError } from '@noy-db\u002Fhub'\n\nconst ceremony = await beginEchoUnlock(store, 'acme', {\n  userId: 'vicio',\n  prompt: typedPrompt,        \u002F\u002F step 1 — the only thing typed so far\n  deviceSeal,                 \u002F\u002F optional; needed to open a `sealed` reveal\n})\n\u002F\u002F ceremony.reveal   → 'ma da piccolo al santanna mi chiamavano'  (null ⇒ degraded, see below)\n\u002F\u002F ceremony.maskHint → 'first-letters' | undefined\n",[941,222004,222005,222033,222037,222063,222077,222091,222100,222106,222111],{"__ignoreMap":1037},[2225,222006,222007,222009,222011,222013,222015,222018,222020,222023,222025,222027,222029,222031],{"class":2227,"line":2228},[2225,222008,2232],{"class":2231},[2225,222010,2236],{"class":2235},[2225,222012,77494],{"class":2239},[2225,222014,2298],{"class":2235},[2225,222016,222017],{"class":2239}," WrongPromptError",[2225,222019,2298],{"class":2235},[2225,222021,222022],{"class":2239}," WrongEchoError",[2225,222024,2243],{"class":2235},[2225,222026,2246],{"class":2231},[2225,222028,2249],{"class":2235},[2225,222030,1086],{"class":2252},[2225,222032,2256],{"class":2235},[2225,222034,222035],{"class":2227,"line":1041},[2225,222036,2285],{"emptyLinePlaceholder":2284},[2225,222038,222039,222041,222043,222045,222047,222049,222051,222053,222055,222057,222059,222061],{"class":2227,"line":1038},[2225,222040,2775],{"class":2774},[2225,222042,77515],{"class":2239},[2225,222044,2781],{"class":2235},[2225,222046,2784],{"class":2231},[2225,222048,77494],{"class":2291},[2225,222050,39702],{"class":2239},[2225,222052,2298],{"class":2235},[2225,222054,2249],{"class":2235},[2225,222056,6450],{"class":2252},[2225,222058,2805],{"class":2235},[2225,222060,2298],{"class":2235},[2225,222062,2301],{"class":2235},[2225,222064,222065,222067,222069,222071,222073,222075],{"class":2227,"line":2288},[2225,222066,10510],{"class":2307},[2225,222068,2311],{"class":2235},[2225,222070,2249],{"class":2235},[2225,222072,77062],{"class":2252},[2225,222074,2805],{"class":2235},[2225,222076,1707],{"class":2235},[2225,222078,222079,222081,222083,222086,222088],{"class":2227,"line":2304},[2225,222080,76677],{"class":2307},[2225,222082,2311],{"class":2235},[2225,222084,222085],{"class":2239}," typedPrompt",[2225,222087,2298],{"class":2235},[2225,222089,222090],{"class":2763},"        \u002F\u002F step 1 — the only thing typed so far\n",[2225,222092,222093,222095,222097],{"class":2227,"line":2356},[2225,222094,221935],{"class":2239},[2225,222096,2298],{"class":2235},[2225,222098,222099],{"class":2763},"                 \u002F\u002F optional; needed to open a `sealed` reveal\n",[2225,222101,222102,222104],{"class":2227,"line":2830},[2225,222103,2359],{"class":2235},[2225,222105,2362],{"class":2239},[2225,222107,222108],{"class":2227,"line":2861},[2225,222109,222110],{"class":2763},"\u002F\u002F ceremony.reveal   → 'ma da piccolo al santanna mi chiamavano'  (null ⇒ degraded, see below)\n",[2225,222112,222113],{"class":2227,"line":4246},[2225,222114,222115],{"class":2763},"\u002F\u002F ceremony.maskHint → 'first-letters' | undefined\n",[937,222117,222118,139685,222121,222124,222125,222127,222128,222130,222131,222133],{},[941,222119,222120],{},"beginEchoUnlock",[941,222122,222123],{},"_keyring\u002F{userId}"," row itself, refuses an expired slot ",[1092,222126,11357],{}," verifying anything, throws ",[941,222129,12881],{}," if the keyring is not an echo keyring, and throws ",[941,222132,77201],{}," if the prompt fails its verifier. Only then does it resolve the echo.",[937,222135,222136,222137,222140],{},"Step 2 is the user's: show ",[941,222138,222139],{},"ceremony.reveal"," and let them recognize it. Step 3 completes:",[2217,222142,222144],{"className":2219,"code":222143,"language":2221,"meta":1037,"style":1037},"const unlocked = ceremony.reveal !== null\n  ? await ceremony.complete({ key: typedKey })                    \u002F\u002F recognized\n  : await ceremony.complete({ echo: typedEcho, key: typedKey })   \u002F\u002F degraded: echo typed and verified\n\nconst db = await createNoydb({ store, user: 'vicio', getKeyring: async () => unlocked })\n",[941,222145,222146,222164,222196,222235,222239],{"__ignoreMap":1037},[2225,222147,222148,222150,222152,222154,222156,222158,222160,222162],{"class":2227,"line":2228},[2225,222149,2775],{"class":2774},[2225,222151,77619],{"class":2239},[2225,222153,2781],{"class":2235},[2225,222155,77626],{"class":2239},[2225,222157,1520],{"class":2235},[2225,222159,77578],{"class":2239},[2225,222161,32545],{"class":2235},[2225,222163,8384],{"class":2235},[2225,222165,222166,222169,222171,222173,222175,222177,222179,222181,222183,222185,222188,222190,222193],{"class":2227,"line":1041},[2225,222167,222168],{"class":2235},"  ?",[2225,222170,2784],{"class":2231},[2225,222172,77626],{"class":2239},[2225,222174,1520],{"class":2235},[2225,222176,77631],{"class":2291},[2225,222178,2789],{"class":2239},[2225,222180,2792],{"class":2235},[2225,222182,8153],{"class":2307},[2225,222184,2311],{"class":2235},[2225,222186,222187],{"class":2239}," typedKey ",[2225,222189,2359],{"class":2235},[2225,222191,222192],{"class":2239},")                    ",[2225,222194,222195],{"class":2763},"\u002F\u002F recognized\n",[2225,222197,222198,222201,222203,222205,222207,222209,222211,222213,222215,222217,222220,222222,222224,222226,222228,222230,222232],{"class":2227,"line":1038},[2225,222199,222200],{"class":2235},"  :",[2225,222202,2784],{"class":2231},[2225,222204,77626],{"class":2239},[2225,222206,1520],{"class":2235},[2225,222208,77631],{"class":2291},[2225,222210,2789],{"class":2239},[2225,222212,2792],{"class":2235},[2225,222214,76961],{"class":2307},[2225,222216,2311],{"class":2235},[2225,222218,222219],{"class":2239}," typedEcho",[2225,222221,2298],{"class":2235},[2225,222223,8153],{"class":2307},[2225,222225,2311],{"class":2235},[2225,222227,222187],{"class":2239},[2225,222229,2359],{"class":2235},[2225,222231,71781],{"class":2239},[2225,222233,222234],{"class":2763},"\u002F\u002F degraded: echo typed and verified\n",[2225,222236,222237],{"class":2227,"line":2288},[2225,222238,2285],{"emptyLinePlaceholder":2284},[2225,222240,222241,222243,222245,222247,222249,222251,222253,222255,222257,222259,222261,222263,222265,222267,222269,222271,222274,222276,222278,222280,222282,222284,222286],{"class":2227,"line":2304},[2225,222242,2775],{"class":2774},[2225,222244,2778],{"class":2239},[2225,222246,2781],{"class":2235},[2225,222248,2784],{"class":2231},[2225,222250,2744],{"class":2291},[2225,222252,2789],{"class":2239},[2225,222254,2792],{"class":2235},[2225,222256,7564],{"class":2239},[2225,222258,2298],{"class":2235},[2225,222260,2795],{"class":2307},[2225,222262,2311],{"class":2235},[2225,222264,2249],{"class":2235},[2225,222266,77062],{"class":2252},[2225,222268,2805],{"class":2235},[2225,222270,2298],{"class":2235},[2225,222272,222273],{"class":2291}," getKeyring",[2225,222275,2311],{"class":2235},[2225,222277,38717],{"class":2774},[2225,222279,13130],{"class":2235},[2225,222281,8116],{"class":2774},[2225,222283,77619],{"class":2239},[2225,222285,2359],{"class":2235},[2225,222287,2362],{"class":2239},[937,222289,222290,222292,222293,7356,222295,968,222297,222299,222300,222302,222303,222306,222307,7356,222309,222311,222312,222314,222315,222317],{},[941,222291,77191],{}," is stateless — a wrong key can simply be retried on the same handle. When ",[941,222294,117298],{},[941,222296,6651],{},[941,222298,55896],{}," is required (omitting it throws ",[941,222301,12881],{},") and is checked against the keyring's echo verifier (",[941,222304,222305],{},"WrongEchoError"," on mismatch). When ",[941,222308,117298],{},[1092,222310,1004],{}," null, passing an ",[941,222313,55896],{}," that disagrees with it also throws ",[941,222316,222305],{}," rather than unlocking on a mismatched pair.",[24204,222319,222321],{"id":222320},"degradation-matrix","Degradation matrix",[937,222323,222324,222325,222327],{},"The ceremony never falls back to a weaker ",[1092,222326,76731],{},"; it falls back only from \"the vault reveals the echo\" to \"the user types the echo, verified\".",[1338,222329,222330,222347],{},[1341,222331,222332],{},[1344,222333,222334,222337,222341,222344],{},[1347,222335,222336],{},"Context",[1347,222338,222339,8285],{},[941,222340,117298],{},[1347,222342,222343],{},"What the user does",[1347,222345,222346],{},"What it still buys",[1357,222348,222349,222367,222384,222399,222415],{},[1344,222350,222351,222357,222361,222364],{},[1362,222352,222353,222354,222356],{},"Enrolled device, ",[941,222355,221698],{}," wired",[1362,222358,222359],{},[941,222360,55954],{},[1362,222362,222363],{},"recognizes the revealed echo",[1362,222365,222366],{},"Full mutual authentication; someone holding a copy of the keyring file cannot read the echo out of it.",[1344,222368,222369,222375,222379,222381],{},[1362,222370,222371,222372,222374],{},"Live keyring enrolled without ",[941,222373,221698],{},", or a pod recipient (default)",[1362,222376,222377],{},[941,222378,55959],{},[1362,222380,222363],{},[1362,222382,222383],{},"Full mutual authentication against anyone who does not hold the file. A file-holder who also knows the prompt can decrypt the blob.",[1344,222385,222386,222389,222393,222396],{},[1362,222387,222388],{},"Sealed keyring opened on a foreign device (no provider, or a different provider id)",[1362,222390,222391],{},[941,222392,55954],{},[1362,222394,222395],{},"types the echo",[1362,222397,222398],{},"The three-step structure and a verified echo; no reveal, so no mutual authentication.",[1344,222400,222401,222406,222410,222412],{},[1362,222402,222403,222404],{},"Pod recipient the owner enrolled as ",[941,222405,221844],{},[1362,222407,222408],{},[941,222409,8292],{},[1362,222411,222395],{},[1362,222413,222414],{},"Same as above.",[1344,222416,222417,222420,222422,222425],{},[1362,222418,222419],{},"A player that only offers single-field secret entry",[1362,222421,11932],{},[1362,222423,222424],{},"nothing — it cannot proceed",[1362,222426,222427,222430],{},[941,222428,222429],{},"EchoCeremonyRequiredError",". There is no downgrade to a plain phrase.",[24204,222432,222434],{"id":222433},"the-conforming-player-contract","The conforming-player contract",[937,222436,222437,222438,222441],{},"Anything that opens an echo keyring — your app, a generic viewer, a future universal player — is a ",[950,222439,222440],{},"player",", and the ceremony is a property of the data it opens, not of the app. The kernel enforces what an API can enforce; the rest is normative and stated here so that \"conforming\" has a meaning:",[1111,222443,222444,222458,222481,222495],{},[1114,222445,222446,222447,222450,222451,222453,222454,222457],{},"A player ",[950,222448,222449],{},"MUST NOT"," present a single-field secret entry for an echo keyring, and ",[950,222452,222449],{}," offer a \"paste your full secret\" escape hatch. None can work: the KDF input is a length-prefixed encoding of the three parts that begins with a byte ",[941,222455,222456],{},"TextEncoder"," can never emit, so no string's encoding can equal it, and the key-derivation dispatch additionally refuses a string outright on an echo keyring.",[1114,222459,222460,222461,222463,222464,222467,222468,222471,222472,222474,222475,222477,222478,222480],{},"When the user does ",[950,222462,1004],{}," recognize the revealed echo, the player ",[950,222465,222466],{},"MUST"," treat the ceremony as dead and show a hard warning: ",[1092,222469,222470],{},"stop, do not continue, open the official app, rotate the secret now",". It ",[950,222473,222449],{}," offer a \"skip verification\" or \"verification unavailable\" affordance, and ",[950,222476,222466],{}," abandon the ceremony without calling ",[941,222479,77191],{},". The kernel cannot enforce this terminal state — it is the player's obligation.",[1114,222482,222483,222484,222487,222488,222490,222491,957],{},"Rate limiting is also the player's obligation: the kernel keeps ",[950,222485,222486],{},"no attempt counter"," on the prompt. Wrong prompts return ",[941,222489,77201],{}," indefinitely; lockout, backoff, and duress handling belong to the app (see ",[1007,222492,222493],{"href":330},[941,222494,15984],{},[1114,222496,222446,222497,222500,222501,222503,222504,222507],{},[950,222498,222499],{},"SHOULD"," mask the echo on screen when ",[941,222502,77026],{}," is present, and ",[950,222505,222506],{},"MAY"," mask it by its own rules when it is absent — the reveal is on-screen material, so it is shoulder-surfable.",[24204,222509,222511],{"id":222510},"validation-per-part-floors","Validation — per-part floors",[937,222513,222514],{},"Echo secrets are natural-language sentences, so their floors differ from the standard phrase's:",[1338,222516,222517,222528],{},[1341,222518,222519],{},[1344,222520,222521,222524,222526],{},[1347,222522,222523],{},"Check",[1347,222525,35913],{},[1347,222527,20197],{},[1357,222529,222530,222540,222554,222564,222581],{},[1344,222531,222532,222535,222538],{},[1362,222533,222534],{},"Each of the three parts",[1362,222536,222537],{},"non-empty after trimming",[1362,222539,45375],{},[1344,222541,222542,222545,222548],{},[1362,222543,222544],{},"Prompt",[1362,222546,222547],{},"minimum word count",[1362,222549,222550,222551,1381],{},"3 words (",[941,222552,222553],{},"DEFAULT_ECHO_PROMPT_MIN_WORDS",[1344,222555,222556,222559,222561],{},[1362,222557,222558],{},"Prompt + echo + key, joined by single spaces",[1362,222560,222547],{},[1362,222562,222563],{},"6 words — the standard whole-secret floor",[1344,222565,222566,222569,222572],{},[1362,222567,222568],{},"Every word, in both checks",[1362,222570,222571],{},"minimum length",[1362,222573,222574,953,222577,222580],{},[950,222575,222576],{},"1 character",[941,222578,222579],{},"DEFAULT_ECHO_MIN_WORD_LENGTH","), relaxed from the standard 3",[1344,222582,222583,222586,222588],{},[1362,222584,222585],{},"Every part",[1362,222587,76841],{},[1362,222589,222590,222591],{},"lowercase letters and single spaces, as in ",[1007,222592,218858],{"href":222593},"#phrase-format",[937,222595,222596,222597,222599,222600,11060,222603,1520],{},"The relaxed word-length floor is what lets natural sentences validate: ",[941,222598,76684],{}," is three real words, two of them shorter than the standard 3-character floor. The character pattern is unchanged, so punctuation still has to go — write ",[941,222601,222602],{},"santanna",[941,222604,222605],{},"sant.anna",[2217,222607,222609],{"className":2219,"code":222608,"language":2221,"meta":1037,"style":1037},"import { validateEchoSecret, assertStrongEchoSecret } from '@noy-db\u002Fhub'\n\nvalidateEchoSecret({\n  prompt: 'mi chiamo vicio',\n  echo: 'ma da piccolo al santanna mi chiamavano',\n  key: 'ciccio',\n})\n\u002F\u002F → { ok: true, words: 11 }\n\nvalidateEchoSecret({ prompt: 'ciao', echo: 'come stai oggi amico', key: 'bene grazie' })\n\u002F\u002F → { ok: false, reason: 'too-few-words', minimum: 3, got: 1 }   — the PROMPT floor\n",[941,222610,222611,222633,222637,222645,222659,222673,222687,222693,222697,222701,222747],{"__ignoreMap":1037},[2225,222612,222613,222615,222617,222619,222621,222623,222625,222627,222629,222631],{"class":2227,"line":2228},[2225,222614,2232],{"class":2231},[2225,222616,2236],{"class":2235},[2225,222618,76856],{"class":2239},[2225,222620,2298],{"class":2235},[2225,222622,77230],{"class":2239},[2225,222624,2243],{"class":2235},[2225,222626,2246],{"class":2231},[2225,222628,2249],{"class":2235},[2225,222630,1086],{"class":2252},[2225,222632,2256],{"class":2235},[2225,222634,222635],{"class":2227,"line":1041},[2225,222636,2285],{"emptyLinePlaceholder":2284},[2225,222638,222639,222641,222643],{"class":2227,"line":1038},[2225,222640,76875],{"class":2291},[2225,222642,2789],{"class":2239},[2225,222644,4200],{"class":2235},[2225,222646,222647,222649,222651,222653,222655,222657],{"class":2227,"line":2288},[2225,222648,76677],{"class":2307},[2225,222650,2311],{"class":2235},[2225,222652,2249],{"class":2235},[2225,222654,76684],{"class":2252},[2225,222656,2805],{"class":2235},[2225,222658,1707],{"class":2235},[2225,222660,222661,222663,222665,222667,222669,222671],{"class":2227,"line":2304},[2225,222662,76693],{"class":2307},[2225,222664,2311],{"class":2235},[2225,222666,2249],{"class":2235},[2225,222668,76701],{"class":2252},[2225,222670,2805],{"class":2235},[2225,222672,1707],{"class":2235},[2225,222674,222675,222677,222679,222681,222683,222685],{"class":2227,"line":2356},[2225,222676,69281],{"class":2307},[2225,222678,2311],{"class":2235},[2225,222680,2249],{"class":2235},[2225,222682,76717],{"class":2252},[2225,222684,2805],{"class":2235},[2225,222686,1707],{"class":2235},[2225,222688,222689,222691],{"class":2227,"line":2830},[2225,222690,2359],{"class":2235},[2225,222692,2362],{"class":2239},[2225,222694,222695],{"class":2227,"line":2861},[2225,222696,76932],{"class":2763},[2225,222698,222699],{"class":2227,"line":4246},[2225,222700,2285],{"emptyLinePlaceholder":2284},[2225,222702,222703,222705,222707,222709,222711,222713,222715,222717,222719,222721,222723,222725,222727,222729,222731,222733,222735,222737,222739,222741,222743,222745],{"class":2227,"line":4253},[2225,222704,76875],{"class":2291},[2225,222706,2789],{"class":2239},[2225,222708,2792],{"class":2235},[2225,222710,76947],{"class":2307},[2225,222712,2311],{"class":2235},[2225,222714,2249],{"class":2235},[2225,222716,76954],{"class":2252},[2225,222718,2805],{"class":2235},[2225,222720,2298],{"class":2235},[2225,222722,76961],{"class":2307},[2225,222724,2311],{"class":2235},[2225,222726,2249],{"class":2235},[2225,222728,76968],{"class":2252},[2225,222730,2805],{"class":2235},[2225,222732,2298],{"class":2235},[2225,222734,8153],{"class":2307},[2225,222736,2311],{"class":2235},[2225,222738,2249],{"class":2235},[2225,222740,76981],{"class":2252},[2225,222742,2805],{"class":2235},[2225,222744,2243],{"class":2235},[2225,222746,2362],{"class":2239},[2225,222748,222749],{"class":2227,"line":4259},[2225,222750,222751],{"class":2763},"\u002F\u002F → { ok: false, reason: 'too-few-words', minimum: 3, got: 1 }   — the PROMPT floor\n",[937,222753,222754,222755,222758,222759,222762,222763,222765,222766,222769],{},"The prompt gets its own floor because it is the part an attacker can attack ",[1092,222756,222757],{},"in isolation",": a guessed prompt yields the echo, which is exactly what a convincing phish would need. The ",[950,222760,222761],{},"echo has no dedicated floor"," — it is free-form and never typed on the normal path — but it is not free of consequence either: it is grindable offline by anyone holding the keyring file, so longer is better. See the ",[1007,222764,13661],{"href":76804}," for what that means, and the ",[1007,222767,222768],{"href":540},"echo-secret enrollment recipe"," for how to choose one.",[937,222771,222772,222773,222776,222777,2311],{},"Override the floors per vault with ",[941,222774,222775],{},"echoSecretPolicy: { prompt?: SecretPolicy, combined?: SecretPolicy }"," — the parts-path counterpart of ",[941,222778,16463],{},[2217,222780,222782],{"className":2219,"code":222781,"language":2221,"meta":1037,"style":1037},"await createNoydb({\n  store, user: 'vicio', secretMode: 'echo', secret: parts,\n  validateSecret: true,\n  echoSecretPolicy: { prompt: { minWords: 4 } },\n})\n",[941,222783,222784,222794,222832,222842,222868],{"__ignoreMap":1037},[2225,222785,222786,222788,222790,222792],{"class":2227,"line":2228},[2225,222787,6500],{"class":2231},[2225,222789,2744],{"class":2291},[2225,222791,2789],{"class":2239},[2225,222793,4200],{"class":2235},[2225,222795,222796,222798,222800,222802,222804,222806,222808,222810,222812,222814,222816,222818,222820,222822,222824,222826,222828,222830],{"class":2227,"line":1041},[2225,222797,15608],{"class":2239},[2225,222799,2298],{"class":2235},[2225,222801,2795],{"class":2307},[2225,222803,2311],{"class":2235},[2225,222805,2249],{"class":2235},[2225,222807,77062],{"class":2252},[2225,222809,2805],{"class":2235},[2225,222811,2298],{"class":2235},[2225,222813,77069],{"class":2307},[2225,222815,2311],{"class":2235},[2225,222817,2249],{"class":2235},[2225,222819,55896],{"class":2252},[2225,222821,2805],{"class":2235},[2225,222823,2298],{"class":2235},[2225,222825,2810],{"class":2307},[2225,222827,2311],{"class":2235},[2225,222829,77086],{"class":2239},[2225,222831,1707],{"class":2235},[2225,222833,222834,222836,222838,222840],{"class":2227,"line":1038},[2225,222835,77425],{"class":2307},[2225,222837,2311],{"class":2235},[2225,222839,2322],{"class":2321},[2225,222841,1707],{"class":2235},[2225,222843,222844,222847,222849,222851,222853,222855,222857,222860,222862,222864,222866],{"class":2227,"line":2288},[2225,222845,222846],{"class":2307},"  echoSecretPolicy",[2225,222848,2311],{"class":2235},[2225,222850,2236],{"class":2235},[2225,222852,76947],{"class":2307},[2225,222854,2311],{"class":2235},[2225,222856,2236],{"class":2235},[2225,222858,222859],{"class":2307}," minWords",[2225,222861,2311],{"class":2235},[2225,222863,100626],{"class":6527},[2225,222865,2243],{"class":2235},[2225,222867,2353],{"class":2235},[2225,222869,222870,222872],{"class":2227,"line":2304},[2225,222871,2359],{"class":2235},[2225,222873,2362],{"class":2239},[937,222875,222876,222877,222879,222880,1261,222882,222884,222885,957],{},"Enforcement follows the same rule as standard mode: opt-in at vault creation (",[941,222878,17438],{},"), on by default for ",[941,222881,15216],{},[941,222883,16494],{}," (bypass with ",[941,222886,222887],{},"allowWeakSecret: true",[24204,222889,222891],{"id":222890},"rotation-and-migration","Rotation and migration",[937,222893,960,222894,222900,222901,222903],{},[950,222895,222896,222897,222899],{},"shape of ",[941,222898,16356],{}," decides the mode after rotation"," — a struct enrolls (or re-mints) an echo block, a string removes it. That makes ",[941,222902,15216],{}," the migration path in both directions:",[2217,222905,222907],{"className":2219,"code":222906,"language":2221,"meta":1037,"style":1037},"\u002F\u002F standard → echo (the upgrade)\nawait db.team.rotateSecret('acme', {\n  oldSecret: 'correct horse battery staple printer toaster',\n  newSecret: { prompt: 'mi chiamo vicio', echo: 'ma da piccolo al santanna mi chiamavano', key: 'ciccio' },\n  echoOptions: { deviceSeal, maskHint: 'first-letters' },   \u002F\u002F omit deviceSeal ⇒ reveal: 'portable'\n})\n\n\u002F\u002F echo → echo (re-mint: fresh verifiers, fresh reveal blob)\nawait db.team.rotateSecret('acme', { oldSecret: oldParts, newSecret: newParts })\n\n\u002F\u002F echo → standard (the downgrade) — refused unless you say so\nawait db.team.rotateSecret('acme', {\n  oldSecret: oldParts,\n  newSecret: 'glasses cabinet bicycle umbrella thunder velvet',\n  allowModeDowngrade: true,\n})\n",[941,222908,222909,222914,222940,222954,222998,223028,223034,223038,223043,223090,223094,223099,223125,223135,223149,223160],{"__ignoreMap":1037},[2225,222910,222911],{"class":2227,"line":2228},[2225,222912,222913],{"class":2763},"\u002F\u002F standard → echo (the upgrade)\n",[2225,222915,222916,222918,222920,222922,222924,222926,222928,222930,222932,222934,222936,222938],{"class":2227,"line":1041},[2225,222917,6500],{"class":2231},[2225,222919,2842],{"class":2239},[2225,222921,1520],{"class":2235},[2225,222923,868],{"class":2239},[2225,222925,1520],{"class":2235},[2225,222927,15216],{"class":2291},[2225,222929,2789],{"class":2239},[2225,222931,2805],{"class":2235},[2225,222933,6450],{"class":2252},[2225,222935,2805],{"class":2235},[2225,222937,2298],{"class":2235},[2225,222939,2301],{"class":2235},[2225,222941,222942,222944,222946,222948,222950,222952],{"class":2227,"line":1038},[2225,222943,220944],{"class":2307},[2225,222945,2311],{"class":2235},[2225,222947,2249],{"class":2235},[2225,222949,219018],{"class":2252},[2225,222951,2805],{"class":2235},[2225,222953,1707],{"class":2235},[2225,222955,222956,222958,222960,222962,222964,222966,222968,222970,222972,222974,222976,222978,222980,222982,222984,222986,222988,222990,222992,222994,222996],{"class":2227,"line":2288},[2225,222957,220959],{"class":2307},[2225,222959,2311],{"class":2235},[2225,222961,2236],{"class":2235},[2225,222963,76947],{"class":2307},[2225,222965,2311],{"class":2235},[2225,222967,2249],{"class":2235},[2225,222969,76684],{"class":2252},[2225,222971,2805],{"class":2235},[2225,222973,2298],{"class":2235},[2225,222975,76961],{"class":2307},[2225,222977,2311],{"class":2235},[2225,222979,2249],{"class":2235},[2225,222981,76701],{"class":2252},[2225,222983,2805],{"class":2235},[2225,222985,2298],{"class":2235},[2225,222987,8153],{"class":2307},[2225,222989,2311],{"class":2235},[2225,222991,2249],{"class":2235},[2225,222993,76717],{"class":2252},[2225,222995,2805],{"class":2235},[2225,222997,2353],{"class":2235},[2225,222999,223000,223003,223005,223007,223010,223012,223015,223017,223019,223021,223023,223025],{"class":2227,"line":2304},[2225,223001,223002],{"class":2307},"  echoOptions",[2225,223004,2311],{"class":2235},[2225,223006,2236],{"class":2235},[2225,223008,223009],{"class":2239}," deviceSeal",[2225,223011,2298],{"class":2235},[2225,223013,223014],{"class":2307}," maskHint",[2225,223016,2311],{"class":2235},[2225,223018,2249],{"class":2235},[2225,223020,77100],{"class":2252},[2225,223022,2805],{"class":2235},[2225,223024,10965],{"class":2235},[2225,223026,223027],{"class":2763},"   \u002F\u002F omit deviceSeal ⇒ reveal: 'portable'\n",[2225,223029,223030,223032],{"class":2227,"line":2356},[2225,223031,2359],{"class":2235},[2225,223033,2362],{"class":2239},[2225,223035,223036],{"class":2227,"line":2830},[2225,223037,2285],{"emptyLinePlaceholder":2284},[2225,223039,223040],{"class":2227,"line":2861},[2225,223041,223042],{"class":2763},"\u002F\u002F echo → echo (re-mint: fresh verifiers, fresh reveal blob)\n",[2225,223044,223045,223047,223049,223051,223053,223055,223057,223059,223061,223063,223065,223067,223069,223072,223074,223077,223079,223081,223083,223086,223088],{"class":2227,"line":4246},[2225,223046,6500],{"class":2231},[2225,223048,2842],{"class":2239},[2225,223050,1520],{"class":2235},[2225,223052,868],{"class":2239},[2225,223054,1520],{"class":2235},[2225,223056,15216],{"class":2291},[2225,223058,2789],{"class":2239},[2225,223060,2805],{"class":2235},[2225,223062,6450],{"class":2252},[2225,223064,2805],{"class":2235},[2225,223066,2298],{"class":2235},[2225,223068,2236],{"class":2235},[2225,223070,223071],{"class":2307}," oldSecret",[2225,223073,2311],{"class":2235},[2225,223075,223076],{"class":2239}," oldParts",[2225,223078,2298],{"class":2235},[2225,223080,197156],{"class":2307},[2225,223082,2311],{"class":2235},[2225,223084,223085],{"class":2239}," newParts ",[2225,223087,2359],{"class":2235},[2225,223089,2362],{"class":2239},[2225,223091,223092],{"class":2227,"line":4253},[2225,223093,2285],{"emptyLinePlaceholder":2284},[2225,223095,223096],{"class":2227,"line":4259},[2225,223097,223098],{"class":2763},"\u002F\u002F echo → standard (the downgrade) — refused unless you say so\n",[2225,223100,223101,223103,223105,223107,223109,223111,223113,223115,223117,223119,223121,223123],{"class":2227,"line":6548},[2225,223102,6500],{"class":2231},[2225,223104,2842],{"class":2239},[2225,223106,1520],{"class":2235},[2225,223108,868],{"class":2239},[2225,223110,1520],{"class":2235},[2225,223112,15216],{"class":2291},[2225,223114,2789],{"class":2239},[2225,223116,2805],{"class":2235},[2225,223118,6450],{"class":2252},[2225,223120,2805],{"class":2235},[2225,223122,2298],{"class":2235},[2225,223124,2301],{"class":2235},[2225,223126,223127,223129,223131,223133],{"class":2227,"line":6576},[2225,223128,220944],{"class":2307},[2225,223130,2311],{"class":2235},[2225,223132,223076],{"class":2239},[2225,223134,1707],{"class":2235},[2225,223136,223137,223139,223141,223143,223145,223147],{"class":2227,"line":6582},[2225,223138,220959],{"class":2307},[2225,223140,2311],{"class":2235},[2225,223142,2249],{"class":2235},[2225,223144,197163],{"class":2252},[2225,223146,2805],{"class":2235},[2225,223148,1707],{"class":2235},[2225,223150,223151,223154,223156,223158],{"class":2227,"line":6587},[2225,223152,223153],{"class":2307},"  allowModeDowngrade",[2225,223155,2311],{"class":2235},[2225,223157,2322],{"class":2321},[2225,223159,1707],{"class":2235},[2225,223161,223162,223164],{"class":2227,"line":7174},[2225,223163,2359],{"class":2235},[2225,223165,2362],{"class":2239},[937,223167,90416,223168,223171,223172,6789,223174,1324,223176,223179,223180,223182,223183,223185,223186,223188,223189,223191,223192,1520],{},[941,223169,223170],{},"allowModeDowngrade: true",", an echo keyring plus a string ",[941,223173,16356],{},[941,223175,12881],{},[950,223177,223178],{},"before anything is written"," — losing the anti-phishing ceremony should never be the accidental consequence of an argument shape. ",[941,223181,16457],{}," must match the ",[1092,223184,90930],{}," mode: a string presented to an echo keyring throws ",[941,223187,222429],{},", a struct presented to a standard one throws ",[941,223190,12881],{},". Tier-2 slots survive an echo rotation exactly as elsewhere — via ",[941,223193,16469],{},[937,223195,223196,223197,223199],{},"Two reset paths convert echo → standard ",[950,223198,12773],{}," the flag, by design, because both are already proof-gated ceremonies rather than a routine change:",[1111,223201,223202,223222],{},[1114,223203,223204,223209,223210,223215,223216,223219,223220,957],{},[950,223205,223206],{},[941,223207,223208],{},"db.team.recoverSecret"," — the caller has presented a recovery code or Shamir shares. It accepts either shape; a struct enrolls a fresh echo block that is ",[950,223211,223212,223213],{},"always ",[941,223214,55959],{}," (there is no ",[941,223217,223218],{},"echoOptions"," here: the device running a recovery is not necessarily the device that will unlock later — seal it afterwards with an explicit ",[941,223221,15216],{},[1114,223223,223224,223229,223230,223232,223233,223235],{},[950,223225,223226],{},[941,223227,223228],{},"db.team.recoverUser"," (peer recovery) — an issuer re-wraps someone else's keyring under a temporary ",[950,223231,7259],{}," secret, so the target always lands on a standard keyring and its echo block is dropped. Peer recovery is a single-use bridge; the recipient picks the echo shape back up with their own ",[941,223234,15216],{}," on first session.",[24204,223237,223239],{"id":223238},"pod-recipients-the-ceremony-travels-with-the-data","Pod recipients — the ceremony travels with the data",[937,223241,223242,223243,1324,223245,1324,223247,223249],{},"A recipient slot inside a ",[941,223244,1402],{},[1007,223246,6051],{"href":630},[1092,223248,3082],{}," a keyring, so an echo recipient carries the ceremony to whoever opens the pod — including a pod that has outlived the app that wrote it:",[2217,223251,223253],{"className":2219,"code":223252,"language":2221,"meta":1037,"style":1037},"import { writePod } from '@noy-db\u002Fhub\u002Fpod'\n\nconst bytes = await writePod(vault, {\n  recipients: [\n    { id: 'auditor', role: 'viewer', secret: { prompt, echo, key } },                   \u002F\u002F reveal: 'portable'\n    { id: 'notary',  role: 'viewer', secret: { prompt, echo, key, reveal: 'none' } },   \u002F\u002F verified typed echo\n  ],\n})\n",[941,223254,223255,223273,223277,223295,223303,223355,223419,223425],{"__ignoreMap":1037},[2225,223256,223257,223259,223261,223263,223265,223267,223269,223271],{"class":2227,"line":2228},[2225,223258,2232],{"class":2231},[2225,223260,2236],{"class":2235},[2225,223262,70188],{"class":2239},[2225,223264,2243],{"class":2235},[2225,223266,2246],{"class":2231},[2225,223268,2249],{"class":2235},[2225,223270,70036],{"class":2252},[2225,223272,2256],{"class":2235},[2225,223274,223275],{"class":2227,"line":1041},[2225,223276,2285],{"emptyLinePlaceholder":2284},[2225,223278,223279,223281,223283,223285,223287,223289,223291,223293],{"class":2227,"line":1038},[2225,223280,2775],{"class":2774},[2225,223282,32469],{"class":2239},[2225,223284,2781],{"class":2235},[2225,223286,2784],{"class":2231},[2225,223288,70188],{"class":2291},[2225,223290,30266],{"class":2239},[2225,223292,2298],{"class":2235},[2225,223294,2301],{"class":2235},[2225,223296,223297,223299,223301],{"class":2227,"line":2288},[2225,223298,115634],{"class":2307},[2225,223300,2311],{"class":2235},[2225,223302,32491],{"class":2239},[2225,223304,223305,223307,223309,223311,223313,223315,223317,223319,223321,223323,223325,223327,223329,223331,223333,223335,223337,223339,223341,223343,223345,223348,223350,223352],{"class":2227,"line":2304},[2225,223306,32496],{"class":2235},[2225,223308,7070],{"class":2307},[2225,223310,2311],{"class":2235},[2225,223312,2249],{"class":2235},[2225,223314,115654],{"class":2252},[2225,223316,2805],{"class":2235},[2225,223318,2298],{"class":2235},[2225,223320,34944],{"class":2307},[2225,223322,2311],{"class":2235},[2225,223324,2249],{"class":2235},[2225,223326,9910],{"class":2252},[2225,223328,2805],{"class":2235},[2225,223330,2298],{"class":2235},[2225,223332,2810],{"class":2307},[2225,223334,2311],{"class":2235},[2225,223336,2236],{"class":2235},[2225,223338,76947],{"class":2239},[2225,223340,2298],{"class":2235},[2225,223342,76961],{"class":2239},[2225,223344,2298],{"class":2235},[2225,223346,223347],{"class":2239}," key ",[2225,223349,2359],{"class":2235},[2225,223351,10965],{"class":2235},[2225,223353,223354],{"class":2763},"                   \u002F\u002F reveal: 'portable'\n",[2225,223356,223357,223359,223361,223363,223365,223368,223370,223372,223374,223376,223378,223380,223382,223384,223386,223388,223390,223392,223394,223396,223398,223400,223402,223404,223406,223408,223410,223412,223414,223416],{"class":2227,"line":2356},[2225,223358,32496],{"class":2235},[2225,223360,7070],{"class":2307},[2225,223362,2311],{"class":2235},[2225,223364,2249],{"class":2235},[2225,223366,223367],{"class":2252},"notary",[2225,223369,2805],{"class":2235},[2225,223371,2298],{"class":2235},[2225,223373,10542],{"class":2307},[2225,223375,2311],{"class":2235},[2225,223377,2249],{"class":2235},[2225,223379,9910],{"class":2252},[2225,223381,2805],{"class":2235},[2225,223383,2298],{"class":2235},[2225,223385,2810],{"class":2307},[2225,223387,2311],{"class":2235},[2225,223389,2236],{"class":2235},[2225,223391,76947],{"class":2239},[2225,223393,2298],{"class":2235},[2225,223395,76961],{"class":2239},[2225,223397,2298],{"class":2235},[2225,223399,8153],{"class":2239},[2225,223401,2298],{"class":2235},[2225,223403,55955],{"class":2307},[2225,223405,2311],{"class":2235},[2225,223407,2249],{"class":2235},[2225,223409,8292],{"class":2252},[2225,223411,2805],{"class":2235},[2225,223413,2243],{"class":2235},[2225,223415,10965],{"class":2235},[2225,223417,223418],{"class":2763},"   \u002F\u002F verified typed echo\n",[2225,223420,223421,223423],{"class":2227,"line":2830},[2225,223422,32588],{"class":2239},[2225,223424,1707],{"class":2235},[2225,223426,223427,223429],{"class":2227,"line":2861},[2225,223428,2359],{"class":2235},[2225,223430,2362],{"class":2239},[937,223432,223433,223436,223437,165359,223440,7356,223443,1952,223446,7356,223448,223453],{},[941,223434,223435],{},"PodRecipient.secret"," widens to ",[941,223438,223439],{},"string | EchoRecipientSecret",[941,223441,223442],{},"EchoRecipientSecret",[941,223444,223445],{},"{ prompt, echo, key, reveal?: 'portable' | 'none' }",[941,223447,116601],{},[950,223449,223450,223451],{},"not reachable from ",[941,223452,35759],{}," — sealing needs a device to seal against, and a pod being written has none.",[937,223455,223456],{},"On the other end, a player that restores the pod's keyrings runs the same ceremony against the restored row:",[2217,223458,223460],{"className":2219,"code":223459,"language":2221,"meta":1037,"style":1037},"const ceremony = await beginEchoUnlock(restoredStore, compartment, { userId: 'auditor', prompt })\nconst unlocked = await ceremony.complete({ key })\n",[941,223461,223462,223504],{"__ignoreMap":1037},[2225,223463,223464,223466,223468,223470,223472,223474,223477,223479,223481,223483,223485,223487,223489,223491,223493,223495,223497,223500,223502],{"class":2227,"line":2228},[2225,223465,2775],{"class":2774},[2225,223467,77515],{"class":2239},[2225,223469,2781],{"class":2235},[2225,223471,2784],{"class":2231},[2225,223473,77494],{"class":2291},[2225,223475,223476],{"class":2239},"(restoredStore",[2225,223478,2298],{"class":2235},[2225,223480,39120],{"class":2239},[2225,223482,2298],{"class":2235},[2225,223484,2236],{"class":2235},[2225,223486,10668],{"class":2307},[2225,223488,2311],{"class":2235},[2225,223490,2249],{"class":2235},[2225,223492,115654],{"class":2252},[2225,223494,2805],{"class":2235},[2225,223496,2298],{"class":2235},[2225,223498,223499],{"class":2239}," prompt ",[2225,223501,2359],{"class":2235},[2225,223503,2362],{"class":2239},[2225,223505,223506,223508,223510,223512,223514,223516,223518,223520,223522,223524,223526,223528],{"class":2227,"line":1041},[2225,223507,2775],{"class":2774},[2225,223509,77619],{"class":2239},[2225,223511,2781],{"class":2235},[2225,223513,2784],{"class":2231},[2225,223515,77626],{"class":2239},[2225,223517,1520],{"class":2235},[2225,223519,77631],{"class":2291},[2225,223521,2789],{"class":2239},[2225,223523,2792],{"class":2235},[2225,223525,223347],{"class":2239},[2225,223527,2359],{"class":2235},[2225,223529,2362],{"class":2239},[24204,223531,120265],{"id":120264},[1338,223533,223534,223545],{},[1341,223535,223536],{},[1344,223537,223538,223540,223543],{},[1347,223539,12126],{},[1347,223541,223542],{},"Code",[1347,223544,120276],{},[1357,223546,223547,223570,223587,223601,223626],{},[1344,223548,223549,223553,223558],{},[1362,223550,223551],{},[941,223552,222429],{},[1362,223554,223555],{},[941,223556,223557],{},"ECHO_CEREMONY_REQUIRED",[1362,223559,223560,223561,223564,223565,53922,223567,223569],{},"A single string was presented to an echo keyring — ",[941,223562,223563],{},"createNoydb({ secret: '…' })",", or a string ",[941,223566,16457],{},[941,223568,15216],{},". The message names the ceremony; there is no fallback.",[1344,223571,223572,223576,223581],{},[1362,223573,223574],{},[941,223575,77201],{},[1362,223577,223578],{},[941,223579,223580],{},"WRONG_PROMPT",[1362,223582,223583,223584,223586],{},"The typed prompt failed its verifier, or could not open a ",[941,223585,55959],{}," reveal blob. Nothing is revealed.",[1344,223588,223589,223593,223598],{},[1362,223590,223591],{},[941,223592,222305],{},[1362,223594,223595],{},[941,223596,223597],{},"WRONG_ECHO",[1362,223599,223600],{},"Degraded path: the typed echo failed its verifier — or a typed echo was supplied that disagrees with an already-resolved reveal.",[1344,223602,223603,223607,223609],{},[1362,223604,223605],{},[941,223606,12881],{},[1362,223608,3610],{},[1362,223610,223611,223612,5502,223614,223616,223617,223619,223620,223622,223623,1520],{},"Mode\u002Fshape disagreements at ",[941,223613,12780],{},[941,223615,222120],{}," on a keyring with no echo block; ",[941,223618,77191],{}," without an ",[941,223621,55896],{}," on a degraded ceremony; an echo → standard rotation without ",[941,223624,223625],{},"allowModeDowngrade",[1344,223627,223628,223632,223634],{},[1362,223629,223630],{},[941,223631,20752],{},[1362,223633,3610],{},[1362,223635,223636,223637,997,223639,223641],{},"A part fails its floor when validation is on; ",[941,223638,43668],{},[941,223640,77011],{}," say which.",[24204,223643,463],{"id":223644},"threat-model",[937,223646,223647,223648,223651,223652,223654],{},"Summarized in ",[1007,223649,223650],{"href":76804},"Threat model — a phishing page imitating the unlock screen",", including what echo mode does ",[1092,223653,1004],{}," defend: the keyring's prompt and echo verifiers are offline brute-force oracles for anyone holding the file, and a sealed reveal blob discloses the echo's byte length. Nothing protects a user who types a secret into genuinely hostile code — echo mode targets remote phishing pages and honest-but-lazy players, not a malicious player the user hands the pod to.",[1591,223656,223658],{"id":223657},"managed-secret-mode","Managed-secret mode",[937,223660,223661],{},"Selected at vault creation, this mode makes the vault resistant to coercion: the hub — not the user — generates the secret, so there is nothing for the user to reveal.",[2217,223663,223665],{"className":2219,"code":223664,"language":2221,"meta":1037,"style":1037},"import { createNoydb, MemorySealer } from '@noy-db\u002Fhub'\n\nconst db = await createNoydb({\n  store,\n  secretMode: 'managed',\n  sealingKey: new MemorySealer({ id: 'dev-test' }), \u002F\u002F swap for a real per-platform provider in production\n})\n\n\u002F\u002F Managed mode requires at least one strong (Shamir) recovery profile —\n\u002F\u002F paper alone has no user-known secret to fall back on.\nconst { vault, recoveryEnrollments } = await db.team.openVaultAndEnrollRecovery('acme', {\n  recovery: [{ profile: 'shamir', k: 2, n: 3 }],\n})\n\u002F\u002F → recoveryEnrollments[0].shares — show once, the hub does not retain them\n",[941,223666,223667,223689,223693,223709,223715,223729,223763,223769,223773,223778,223783,223824,223866,223872],{"__ignoreMap":1037},[2225,223668,223669,223671,223673,223675,223677,223679,223681,223683,223685,223687],{"class":2227,"line":2228},[2225,223670,2232],{"class":2231},[2225,223672,2236],{"class":2235},[2225,223674,2744],{"class":2239},[2225,223676,2298],{"class":2235},[2225,223678,135636],{"class":2239},[2225,223680,2243],{"class":2235},[2225,223682,2246],{"class":2231},[2225,223684,2249],{"class":2235},[2225,223686,1086],{"class":2252},[2225,223688,2256],{"class":2235},[2225,223690,223691],{"class":2227,"line":1041},[2225,223692,2285],{"emptyLinePlaceholder":2284},[2225,223694,223695,223697,223699,223701,223703,223705,223707],{"class":2227,"line":1038},[2225,223696,2775],{"class":2774},[2225,223698,2778],{"class":2239},[2225,223700,2781],{"class":2235},[2225,223702,2784],{"class":2231},[2225,223704,2744],{"class":2291},[2225,223706,2789],{"class":2239},[2225,223708,4200],{"class":2235},[2225,223710,223711,223713],{"class":2227,"line":2288},[2225,223712,15608],{"class":2239},[2225,223714,1707],{"class":2235},[2225,223716,223717,223719,223721,223723,223725,223727],{"class":2227,"line":2304},[2225,223718,71905],{"class":2307},[2225,223720,2311],{"class":2235},[2225,223722,2249],{"class":2235},[2225,223724,71912],{"class":2252},[2225,223726,2805],{"class":2235},[2225,223728,1707],{"class":2235},[2225,223730,223731,223733,223735,223737,223739,223741,223743,223745,223747,223749,223752,223754,223756,223758,223760],{"class":2227,"line":2356},[2225,223732,71921],{"class":2307},[2225,223734,2311],{"class":2235},[2225,223736,8130],{"class":2235},[2225,223738,135636],{"class":2291},[2225,223740,2789],{"class":2239},[2225,223742,2792],{"class":2235},[2225,223744,7070],{"class":2307},[2225,223746,2311],{"class":2235},[2225,223748,2249],{"class":2235},[2225,223750,223751],{"class":2252},"dev-test",[2225,223753,2805],{"class":2235},[2225,223755,2243],{"class":2235},[2225,223757,1381],{"class":2239},[2225,223759,2298],{"class":2235},[2225,223761,223762],{"class":2763}," \u002F\u002F swap for a real per-platform provider in production\n",[2225,223764,223765,223767],{"class":2227,"line":2830},[2225,223766,2359],{"class":2235},[2225,223768,2362],{"class":2239},[2225,223770,223771],{"class":2227,"line":2861},[2225,223772,2285],{"emptyLinePlaceholder":2284},[2225,223774,223775],{"class":2227,"line":4246},[2225,223776,223777],{"class":2763},"\u002F\u002F Managed mode requires at least one strong (Shamir) recovery profile —\n",[2225,223779,223780],{"class":2227,"line":4253},[2225,223781,223782],{"class":2763},"\u002F\u002F paper alone has no user-known secret to fall back on.\n",[2225,223784,223785,223787,223789,223791,223793,223796,223798,223800,223802,223804,223806,223808,223810,223812,223814,223816,223818,223820,223822],{"class":2227,"line":4259},[2225,223786,2775],{"class":2774},[2225,223788,2236],{"class":2235},[2225,223790,6466],{"class":2239},[2225,223792,2298],{"class":2235},[2225,223794,223795],{"class":2239}," recoveryEnrollments ",[2225,223797,2359],{"class":2235},[2225,223799,6346],{"class":2235},[2225,223801,2784],{"class":2231},[2225,223803,2842],{"class":2239},[2225,223805,1520],{"class":2235},[2225,223807,868],{"class":2239},[2225,223809,1520],{"class":2235},[2225,223811,19893],{"class":2291},[2225,223813,2789],{"class":2239},[2225,223815,2805],{"class":2235},[2225,223817,6450],{"class":2252},[2225,223819,2805],{"class":2235},[2225,223821,2298],{"class":2235},[2225,223823,2301],{"class":2235},[2225,223825,223826,223828,223830,223832,223834,223836,223838,223840,223842,223844,223846,223848,223850,223852,223854,223856,223858,223860,223862,223864],{"class":2227,"line":6548},[2225,223827,72007],{"class":2307},[2225,223829,2311],{"class":2235},[2225,223831,8667],{"class":2239},[2225,223833,2792],{"class":2235},[2225,223835,72016],{"class":2307},[2225,223837,2311],{"class":2235},[2225,223839,2249],{"class":2235},[2225,223841,325],{"class":2252},[2225,223843,2805],{"class":2235},[2225,223845,2298],{"class":2235},[2225,223847,8700],{"class":2307},[2225,223849,2311],{"class":2235},[2225,223851,18961],{"class":6527},[2225,223853,2298],{"class":2235},[2225,223855,27471],{"class":2307},[2225,223857,2311],{"class":2235},[2225,223859,14460],{"class":6527},[2225,223861,2243],{"class":2235},[2225,223863,8803],{"class":2239},[2225,223865,1707],{"class":2235},[2225,223867,223868,223870],{"class":2227,"line":6576},[2225,223869,2359],{"class":2235},[2225,223871,2362],{"class":2239},[2225,223873,223874],{"class":2227,"line":6582},[2225,223875,223876],{"class":2763},"\u002F\u002F → recoveryEnrollments[0].shares — show once, the hub does not retain them\n",[24204,223878,223880],{"id":223879},"mechanics","Mechanics",[1111,223882,223883,223891,223894,223899],{},[1114,223884,223885,223886,223888,223889,1520],{},"The hub generates a random 256-bit secret at first open and seals it under the supplied ",[941,223887,14350],{},", persisting the sealed form at ",[941,223890,14380],{},[1114,223892,223893],{},"The user never sees or types this secret; it never enters any UI surface.",[1114,223895,223896,223898],{},[941,223897,15169],{}," is unavailable in managed mode — the secret can only be replaced by recovering it under a fresh sealing key.",[1114,223900,223901,223902,223904,223905,2085,223909,968,223911,968,223913,968,223915,997,223917,223919,223920,223923],{},"Concrete ",[941,223903,14350],{}," implementations ship as separate packages outside the hub — the ",[1007,223906,223907,9789],{"href":170},[941,223908,1139],{},[941,223910,13485],{},[941,223912,13493],{},[941,223914,13489],{},[941,223916,13497],{},[941,223918,13481],{},". The hub itself carries only the in-memory test provider (",[941,223921,223922],{},"MemorySealer",", shown above).",[24204,223925,223927,54003],{"id":223926},"noydbsealer-interface",[941,223928,14350],{},[2217,223930,223932],{"className":2219,"code":223931,"language":2221,"meta":1037,"style":1037},"interface NoydbSealer {\n  readonly id: string                                    \u002F\u002F non-secret identifier, safe to log\n  seal(secret: Uint8Array): Promise\u003CUint8Array>       \u002F\u002F called once, at enrollment\n  unseal(sealed: Uint8Array): Promise\u003CUint8Array>         \u002F\u002F called on every unlock; must throw on tamper\n}\n",[941,223933,223934,223943,223956,223982,224008],{"__ignoreMap":1037},[2225,223935,223936,223938,223941],{"class":2227,"line":2228},[2225,223937,6994],{"class":2774},[2225,223939,223940],{"class":6342}," NoydbSealer",[2225,223942,2301],{"class":2235},[2225,223944,223945,223947,223949,223951,223953],{"class":2227,"line":1041},[2225,223946,53580],{"class":2774},[2225,223948,7070],{"class":2307},[2225,223950,2311],{"class":2235},[2225,223952,6366],{"class":6342},[2225,223954,223955],{"class":2763},"                                    \u002F\u002F non-secret identifier, safe to log\n",[2225,223957,223958,223961,223963,223965,223967,223969,223971,223973,223975,223977,223979],{"class":2227,"line":1038},[2225,223959,223960],{"class":2307},"  seal",[2225,223962,2789],{"class":2235},[2225,223964,10695],{"class":7052},[2225,223966,2311],{"class":2235},[2225,223968,60935],{"class":6342},[2225,223970,6255],{"class":2235},[2225,223972,7079],{"class":6342},[2225,223974,6474],{"class":2235},[2225,223976,45820],{"class":6342},[2225,223978,6480],{"class":2235},[2225,223980,223981],{"class":2763},"       \u002F\u002F called once, at enrollment\n",[2225,223983,223984,223987,223989,223991,223993,223995,223997,223999,224001,224003,224005],{"class":2227,"line":2288},[2225,223985,223986],{"class":2307},"  unseal",[2225,223988,2789],{"class":2235},[2225,223990,55954],{"class":7052},[2225,223992,2311],{"class":2235},[2225,223994,60935],{"class":6342},[2225,223996,6255],{"class":2235},[2225,223998,7079],{"class":6342},[2225,224000,6474],{"class":2235},[2225,224002,45820],{"class":6342},[2225,224004,6480],{"class":2235},[2225,224006,224007],{"class":2763},"         \u002F\u002F called on every unlock; must throw on tamper\n",[2225,224009,224010],{"class":2227,"line":2304},[2225,224011,7351],{"class":2235},[24204,224013,463],{"id":224014},"threat-model-1",[937,224016,224017],{},"Coercing a user to reveal their secret (the \"rubber-hose\" attack) collapses, because the user has no secret to give up. The attack surface moves to compromising the sealing-key store itself (the OS keyring or KMS), or to compromising enough recovery shares to reach the Shamir threshold — neither of which is coercive against the user. The trade-off is a hard dependency on the sealing-key store: if it is wiped, recovery is the only way back in.",[1022,224019,102889],{"id":102888},[1591,224021,224023],{"id":224022},"secret-tier-1","Secret (tier 1)",[1338,224025,224026,224036],{},[1341,224027,224028],{},[1344,224029,224030,224032,224034],{},[1347,224031,72632],{},[1347,224033,102906],{},[1347,224035,7834],{},[1357,224037,224038,224059,224079,224095],{},[1344,224039,224040,224045,224050],{},[1362,224041,224042],{},[941,224043,224044],{},"validateSecret(phrase, opts?)",[1362,224046,224047],{},[941,224048,224049],{},"SecretValidationResult",[1362,224051,224052,224053,224055,224056,224058],{},"Checks a phrase against the format rules without throwing. ",[941,224054,8097],{}," overrides ",[941,224057,16365],{}," fields.",[1344,224060,224061,224066,224071],{},[1362,224062,224063],{},[941,224064,224065],{},"assertStrongSecret(phrase, opts?)",[1362,224067,224068,224070],{},[941,224069,7165],{}," (throws)",[1362,224072,224073,224074,23244,224076,224078],{},"Same check as ",[941,224075,17425],{},[941,224077,20752],{}," on failure.",[1344,224080,224081,224086,224090],{},[1362,224082,224083],{},[941,224084,224085],{},"db.team.rotateSecret(vault, { oldSecret, newSecret, slotCeremonies? }, factors?)",[1362,224087,224088],{},[941,224089,116014],{},[1362,224091,224092,224093,1520],{},"Re-derives the old KEK, validates and derives the new one, rewraps every DEK. Gated by ",[941,224094,15169],{},[1344,224096,224097,224102,224107],{},[1362,224098,224099],{},[941,224100,224101],{},"db.team.recoverSecret(vault, { newSecret, recoveryProof, rotateRemainingCodes? }, factors?)",[1362,224103,224104],{},[941,224105,224106],{},"Promise\u003CRecoverSecretResult>",[1362,224108,224109,224110,1520],{},"Resets the secret from a recovery proof instead of the old one. Gated by ",[941,224111,16586],{},[937,224113,224114],{},"Types:",[1111,224116,224117,224127,224134],{},[1114,224118,224119,964,224121,224124,224125,1520],{},[941,224120,16365],{},[941,224122,224123],{},"{ minWords?, minWordLength?, rejectRepeatedAdjacent?, pattern?, customValidator? }"," — per-vault secret rule overrides; see ",[1007,224126,218858],{"href":222593},[1114,224128,224129,964,224131,1520],{},[941,224130,224049],{},[941,224132,224133],{},"{ ok: true, words: number } | { ok: false, reason: WeakSecretReason, minimum?, got? }",[1114,224135,224136,188597,224138,224140],{},[941,224137,20752],{},[941,224139,219059],{}," and by every hub ingress point where validation is on and the phrase fails.",[1591,224142,224144],{"id":224143},"tier-2-authenticator-slots","Tier 2 — authenticator slots",[1338,224146,224147,224157],{},[1341,224148,224149],{},[1344,224150,224151,224153,224155],{},[1347,224152,7831],{},[1347,224154,102906],{},[1347,224156,7834],{},[1357,224158,224159,224175,224191,224210],{},[1344,224160,224161,224166,224170],{},[1362,224162,224163],{},[941,224164,224165],{},"db.team.enrollAuthenticator(vault, options, factors?)",[1362,224167,224168],{},[941,224169,116014],{},[1362,224171,224172,224173,1520],{},"Add a slot. Gated by ",[941,224174,17553],{},[1344,224176,224177,224182,224186],{},[1362,224178,224179],{},[941,224180,224181],{},"db.team.removeAuthenticator(vault, slotId, factors?)",[1362,224183,224184],{},[941,224185,116014],{},[1362,224187,224188,224189,1520],{},"Remove a slot; idempotent. Gated by ",[941,224190,17634],{},[1344,224192,224193,224198,224202],{},[1362,224194,224195],{},[941,224196,224197],{},"db.team.updateAuthenticator(vault, slotId, options, factors?)",[1362,224199,224200],{},[941,224201,116014],{},[1362,224203,224204,224205,224207,224208,1520],{},"Rename\u002Frelabel a slot's ",[941,224206,17587],{},"; id\u002Fmethod\u002Fwrap material are immutable. Gated by ",[941,224209,17761],{},[1344,224211,224212,224217,224221],{},[1362,224213,224214],{},[941,224215,224216],{},"db.team.unlockViaAuthenticator(vault, slotId, verify)",[1362,224218,224219],{},[941,224220,137490],{},[1362,224222,224223,224224,224226,224227,104759],{},"Unlock using an enrolled slot; ",[941,224225,18051],{}," typically delegates to the matching ",[941,224228,1787],{},[937,224230,224114],{},[1111,224232,224233],{},[1114,224234,224235,17694,224237,968,224239,17699,224241,224243,224244,224247],{},[941,224236,17685],{},[941,224238,337],{},[941,224240,309],{},[941,224242,313],{},"); see ",[1007,224245,224246],{"href":218504},"Tier 2 — Authenticate"," for the full shape.",[1591,224249,17096],{"id":18170},[1338,224251,224252,224262],{},[1341,224253,224254],{},[1344,224255,224256,224258,224260],{},[1347,224257,72632],{},[1347,224259,102906],{},[1347,224261,7834],{},[1357,224263,224264,224285,224303,224320],{},[1344,224265,224266,224271,224275],{},[1362,224267,224268],{},[941,224269,224270],{},"db.team.enrollUnlock(vault, state, factors?)",[1362,224272,224273],{},[941,224274,116014],{},[1362,224276,224277,224278,224281,224282,224284],{},"Register a tier-3 quick-unlock state produced by ",[941,224279,224280],{},"enrollPin"," (or another ",[941,224283,1787],{}," tier-3 primitive).",[1344,224286,224287,224292,224297],{},[1362,224288,224289],{},[941,224290,224291],{},"db.team.unlockViaPin(vault, resume)",[1362,224293,224294],{},[941,224295,224296],{},"Promise\u003CUnlockedKeyring | undefined>",[1362,224298,224299,224300,224302],{},"Resume a session via the registered tier-3 state; ",[941,224301,18316],{}," if none is registered.",[1344,224304,224305,224312,224317],{},[1362,224306,224307,953,224310,1381],{},[941,224308,224309],{},"enrollPin(unlockedKeyring, { pin, ttlMs })",[941,224311,17062],{},[1362,224313,224314],{},[941,224315,224316],{},"Promise\u003CQuickUnlockState>",[1362,224318,224319],{},"Mint a resumable tier-3 state from an already-unlocked keyring.",[1344,224321,224322,224328,224332],{},[1362,224323,224324,953,224326,1381],{},[941,224325,45162],{},[941,224327,17062],{},[1362,224329,224330],{},[941,224331,137490],{},[1362,224333,224334,224335,1261,224337,1520],{},"Verify the PIN and return the cached keyring; throws ",[941,224336,45166],{},[941,224338,45169],{},[1591,224340,224342],{"id":224341},"policy-gates-methods-and-types","Policy gates (methods and types)",[1338,224344,224345,224355],{},[1341,224346,224347],{},[1344,224348,224349,224351,224353],{},[1347,224350,7831],{},[1347,224352,102906],{},[1347,224354,7834],{},[1357,224356,224357,224372],{},[1344,224358,224359,224363,224367],{},[1362,224360,224361],{},[941,224362,196888],{},[1362,224364,224365,224070],{},[941,224366,116014],{},[1362,224368,224369,224370,13026],{},"Evaluate a gate against the active session tier and presented factors; throws ",[941,224371,13025],{},[1344,224373,224374,224379,224384],{},[1362,224375,224376],{},[941,224377,224378],{},"db.updatePolicy(vault, override)",[1362,224380,224381],{},[941,224382,224383],{},"Promise\u003CVaultPolicy>",[1362,224385,224386,224387,224389,224390,1520],{},"Replace the policy document at ",[941,224388,12784],{},"; unspecified gates keep their previous values. Gated by ",[941,224391,12946],{},[937,224393,224114],{},[1111,224395,224396,224403,224410,224417,224433],{},[1114,224397,224398,964,224400,1520],{},[941,224399,12870],{},[941,224401,224402],{},"{ secret?: SecretPolicy, gates: Partial\u003CRecord\u003CGateName, GatePolicy>> }",[1114,224404,224405,964,224407,1520],{},[941,224406,104535],{},[941,224408,224409],{},"{ minTier: 1 | 2 | 3, factors?, warn?, enabled? }",[1114,224411,224412,964,224414,1520],{},[941,224413,197586],{},[941,224415,224416],{},"{ anyOf: FactorKind[], count?, freshnessMs? }",[1114,224418,224419,188597,224421,224423,224424,968,224427,2462,224430,1520],{},[941,224420,13025],{},[941,224422,12982],{},"; carries ",[941,224425,224426],{},".gate",[941,224428,224429],{},".required",[941,224431,224432],{},".reason",[1114,224434,224435,1261,224437,224439,224440,957],{},[941,224436,12830],{},[941,224438,12834],{}," — the two shipped policy presets (",[941,224441,1086],{},[1591,224443,224445],{"id":224444},"echo-secret-mode-tier-1","Echo-secret mode (tier 1)",[1338,224447,224448,224458],{},[1341,224449,224450],{},[1344,224451,224452,224454,224456],{},[1347,224453,72632],{},[1347,224455,102906],{},[1347,224457,7834],{},[1357,224459,224460,224483,224499,224512,224534,224551,224571],{},[1344,224461,224462,224467,224472],{},[1362,224463,224464],{},[941,224465,224466],{},"beginEchoUnlock(store, vault, { userId, prompt, deviceSeal? })",[1362,224468,224469],{},[941,224470,224471],{},"Promise\u003CEchoCeremony>",[1362,224473,224474,224475,1261,224477,1261,224479,1261,224481,1520],{},"Start the interactive ceremony: read the keyring, refuse an expired slot, verify the prompt, resolve the echo. Throws ",[941,224476,11801],{},[941,224478,116458],{},[941,224480,12881],{},[941,224482,77201],{},[1344,224484,224485,224489,224493],{},[1362,224486,224487],{},[941,224488,222139],{},[1362,224490,224491],{},[941,224492,110591],{},[1362,224494,224495,224496,224498],{},"The echo to display for recognition; ",[941,224497,6651],{}," means the degraded typed-echo path.",[1344,224500,224501,224505,224509],{},[1362,224502,224503],{},[941,224504,77026],{},[1362,224506,224507],{},[941,224508,163102],{},[1362,224510,224511],{},"The keyring's display-masking hint, if the owner set one.",[1344,224513,224514,224519,224523],{},[1362,224515,224516],{},[941,224517,224518],{},"ceremony.complete({ echo?, key })",[1362,224520,224521],{},[941,224522,137490],{},[1362,224524,224525,224526,224528,224529,7356,224531,224533],{},"Derive the KEK from all three parts and unlock. ",[941,224527,55896],{}," is required when ",[941,224530,117298],{},[941,224532,6651],{},". Stateless — retryable.",[1344,224535,224536,224541,224545],{},[1362,224537,224538],{},[941,224539,224540],{},"validateEchoSecret(parts, opts?)",[1362,224542,224543],{},[941,224544,224049],{},[1362,224546,224547,224548,224550],{},"Check a ",[941,224549,221683],{}," struct against the per-part floors without throwing.",[1344,224552,224553,224558,224562],{},[1362,224554,224555],{},[941,224556,224557],{},"assertStrongEchoSecret(parts, opts?)",[1362,224559,224560,224070],{},[941,224561,7165],{},[1362,224563,224564,224565,1952,224567,224570],{},"Same check; throws ",[941,224566,20752],{},[941,224568,224569],{},"opts.allowWeakSecret"," skips it.",[1344,224572,224573,224577,224579],{},[1362,224574,224575],{},[941,224576,221988],{},[1362,224578,7670],{},[1362,224580,224581,224582,224584,224585,224587],{},"The only ",[941,224583,221978],{}," shipped in ",[941,224586,1086],{}," (in-memory, test-only).",[937,224589,224590,224591,224593,224594,224596],{},"Options on ",[941,224592,12780],{}," (echo mode only — each throws ",[941,224595,12881],{}," outside it):",[1111,224598,224599,224611,224623,224631],{},[1114,224600,224601,224603,224604,224606,224607,224610],{},[941,224602,55889],{}," — selects the mode; ",[941,224605,10695],{}," must then be an ",[941,224608,224609],{},"EchoSecretParts"," struct.",[1114,224612,224613,224616,224617,224620,224621,1520],{},[941,224614,224615],{},"deviceSeal?: NoydbDeviceSeal"," — present ⇒ the keyring enrolls ",[941,224618,224619],{},"reveal: 'sealed'","; absent ⇒ ",[941,224622,55959],{},[1114,224624,224625,224628,224629,1520],{},[941,224626,224627],{},"echoMaskHint?: string"," — stored on the keyring, surfaced later as ",[941,224630,77026],{},[1114,224632,224633,224636,224637,224639],{},[941,224634,224635],{},"echoSecretPolicy?: EchoSecretPolicy"," — per-vault prompt \u002F combined floors, applied when ",[941,224638,17425],{}," is on.",[937,224641,224642],{},"Options on rotation and recovery:",[1111,224644,224645,224651,224657,224666],{},[1114,224646,224647,224650],{},[941,224648,224649],{},"RotateSecretInput.newSecret: string | EchoSecretParts"," — the shape selects the mode after rotation.",[1114,224652,224653,224656],{},[941,224654,224655],{},"RotateSecretInput.allowModeDowngrade?: boolean"," — required to rotate an echo keyring to a string secret.",[1114,224658,224659,224662,224663,224665],{},[941,224660,224661],{},"RotateSecretInput.echoOptions?: { deviceSeal?, maskHint? }"," — enrollment knobs for the block minted when ",[941,224664,16356],{}," is 3-part.",[1114,224667,224668,1261,224671,222776,224674,1520],{},[941,224669,224670],{},"RotateSecretInput.echoSecretPolicy?",[941,224672,224673],{},"RecoverSecretInput.echoSecretPolicy?",[941,224675,16463],{},[937,224677,224114],{},[1111,224679,224680,224688,224699,224707,224715,224724,224731],{},[1114,224681,224682,964,224684,224687],{},[941,224683,224609],{},[941,224685,224686],{},"{ prompt: string, echo: string, key: string }","; the secret shape for echo mode.",[1114,224689,224690,964,224693,92388,224696,1520],{},[941,224691,224692],{},"EchoCeremony",[941,224694,224695],{},"{ reveal, maskHint, complete() }",[1007,224697,221999],{"href":224698},"#the-unlock-ceremony",[1114,224700,224701,964,224704,1520],{},[941,224702,224703],{},"EchoSecretPolicy",[941,224705,224706],{},"{ prompt?: SecretPolicy, combined?: SecretPolicy }",[1114,224708,224709,964,224711,224714],{},[941,224710,223442],{},[941,224712,224713],{},"EchoSecretParts & { reveal?: 'portable' | 'none' }","; a pod recipient's echo secret.",[1114,224716,224717,224720,224721,957],{},[941,224718,224719],{},"KeyringEchoBlock"," — the on-keyring declaration (verifiers, reveal policy, optional ",[941,224722,224723],{},"mask_hint",[1114,224725,224726,964,224728,224730],{},[941,224727,221978],{},[941,224729,221981],{},"; seals only the echo reveal blob.",[1114,224732,224733,1261,224735,1261,224737,27678,224739,1520],{},[941,224734,222429],{},[941,224736,77201],{},[941,224738,222305],{},[1007,224740,120265],{"href":224741},"#errors",[1591,224743,224745],{"id":224744},"managed-secret-mode-methods-and-types","Managed-secret mode (methods and types)",[1338,224747,224748,224758],{},[1341,224749,224750],{},[1344,224751,224752,224754,224756],{},[1347,224753,72632],{},[1347,224755,102906],{},[1347,224757,7834],{},[1357,224759,224760,224778],{},[1344,224761,224762,224767,224772],{},[1362,224763,224764],{},[941,224765,224766],{},"db.team.openVaultAndEnrollRecovery(vault, { recovery, locale? })",[1362,224768,224769],{},[941,224770,224771],{},"Promise\u003C{ vault, recoveryEnrollments }>",[1362,224773,224774,224775,224777],{},"Atomic create-and-enroll for managed-mode vaults — required because ",[941,224776,16586],{}," cannot be left unenrolled.",[1344,224779,224780,224784,224786],{},[1362,224781,224782],{},[941,224783,223922],{},[1362,224785,7670],{},[1362,224787,224581,224788,224790,224791,224793],{},[941,224789,14350],{}," implementation shipped in ",[941,224792,1086],{}," today (in-memory, test-only).",[937,224795,224114],{},[1111,224797,224798],{},[1114,224799,224800,964,224802,92388,224805,1520],{},[941,224801,14350],{},[941,224803,224804],{},"{ id, seal(secret), unseal(sealed) }",[1007,224806,224808,54003],{"href":224807},"#noydbsealer-interface",[941,224809,14350],{},[1022,224811,224813],{"id":224812},"threat-model-summary","Threat model summary",[1338,224815,224816,224831],{},[1341,224817,224818],{},[1344,224819,224820,224823,224826,224828],{},[1347,224821,224822],{},"Attack",[1347,224824,224825],{},"Standard mode",[1347,224827,55886],{},[1347,224829,224830],{},"Managed mode",[1357,224832,224833,224847,224861,224881,224895,224907,224922,224934,224948,224960],{},[1344,224834,224835,224838,224841,224844],{},[1362,224836,224837],{},"Brute-force the secret",[1362,224839,224840],{},"PBKDF2 600k iterations + phrase format (≥77 bits)",[1362,224842,224843],{},"Same 600k, over all three parts jointly",[1362,224845,224846],{},"Random 256 bits — infeasible",[1344,224848,224849,224852,224855,224858],{},[1362,224850,224851],{},"Phishing page imitating the unlock screen",[1362,224853,224854],{},"Nothing structural — one typed field looks like any other",[1362,224856,224857],{},"Defeated: the page cannot produce the echo, and no single-field entry exists for it to imitate",[1362,224859,224860],{},"Nothing to phish — the user types no secret",[1344,224862,224863,224866,224869,224878],{},[1362,224864,224865],{},"Stolen copy of the keyring file",[1362,224867,224868],{},"Ciphertext plus the PBKDF2 floor",[1362,224870,224871,224872,224874,224875,224877],{},"Same, plus offline guessing oracles on the prompt and the echo; a ",[941,224873,55954],{}," reveal keeps the echo unreadable, a ",[941,224876,55959],{}," one does not",[1362,224879,224880],{},"Same, plus a sealed secret only the sealing-key store can open",[1344,224882,224883,224886,224889,224892],{},[1362,224884,224885],{},"Stolen device with a platform passkey (tier 2)",[1362,224887,224888],{},"Tier-1 changes require an off-device 2FA factor",[1362,224890,224891],{},"Same",[1362,224893,224894],{},"Same; secret rotation is disabled entirely",[1344,224896,224897,224900,224903,224905],{},[1362,224898,224899],{},"Stolen device with a cached tier-3 PIN",[1362,224901,224902],{},"Idle timeout wipes the cache",[1362,224904,224891],{},[1362,224906,224891],{},[1344,224908,224909,224912,224917,224919],{},[1362,224910,224911],{},"Coerced user",[1362,224913,224914,224916],{},[941,224915,15984],{}," duress-secret wipe (plausible deniability)",[1362,224918,224891],{},[1362,224920,224921],{},"User has no secret to reveal",[1344,224923,224924,224927,224930,224932],{},[1362,224925,224926],{},"Lost passkey only",[1362,224928,224929],{},"Unlock via another tier-2 slot, or recovery",[1362,224931,224891],{},[1362,224933,224891],{},[1344,224935,224936,224939,224942,224945],{},[1362,224937,224938],{},"Forgotten secret",[1362,224940,224941],{},"Paper or Shamir recovery",[1362,224943,224944],{},"Same, and the revealed echo is itself a cue for the key",[1362,224946,224947],{},"Same, and recovery is mandatory",[1344,224949,224950,224953,224956,224958],{},[1362,224951,224952],{},"Compromised admin",[1362,224954,224955],{},"Cannot read user data (zero-knowledge); cannot rotate a user's secret",[1362,224957,224891],{},[1362,224959,224891],{},[1344,224961,224962,224965,224968,224970],{},[1362,224963,224964],{},"Forged keyring file",[1362,224966,224967],{},"Authenticated AES-GCM rejects modified envelopes",[1362,224969,224891],{},[1362,224971,224891],{},[1022,224973,224975],{"id":224974},"limits-and-gotchas","Limits and gotchas",[1111,224977,224978,224990,225005,225011,225027,225037,225048,225060,225066],{},[1114,224979,224980,224983,224984,224986,224987,1520],{},[950,224981,224982],{},"Tier-2 slots don't survive rotation"," unless you supply a ",[941,224985,16469],{}," ceremony per slot — see ",[1007,224988,220904],{"href":224989},"#rotate-and-recover-the-secret",[1114,224991,224992,224999,225000,997,225002,225004],{},[950,224993,224994,224995,997,224997],{},"Recovery profiles ",[941,224996,19627],{},[941,224998,19630],{}," have no implementation yet; only ",[941,225001,19603],{},[941,225003,325],{}," work.",[1114,225006,225007,225010],{},[950,225008,225009],{},"Per-keyring policy overrides"," are stored but not enforced — the vault-level policy is always the effective one.",[1114,225012,225013,953,225019,225021,225022,1625,225024,225026],{},[950,225014,225015,225016,225018],{},"Secret validation on first creation and on ",[941,225017,10831],{}," is opt-in",[941,225020,17438],{},"); on ",[941,225023,15216],{},[941,225025,16494],{}," it is on by default.",[1114,225028,225029,225034,225035,1520],{},[950,225030,223901,225031,225033],{},[941,225032,14350],{},"s for real platforms are not published yet"," — only the in-memory test provider ships in ",[941,225036,1086],{},[1114,225038,225039,225044,225045,225047],{},[950,225040,223901,225041,225043],{},[941,225042,221978],{},"s are equally unshipped"," — without one, echo keyrings enroll a ",[941,225046,55959],{}," reveal, which a file-holder who knows the prompt can decrypt.",[1114,225049,225050,225053,225054,225056,225057,225059],{},[950,225051,225052],{},"An echo keyring cannot be opened by any single string"," — including a ",[941,225055,3096],{},"-joined form. Tooling that assumes a one-field secret gets ",[941,225058,222429],{},"; there is no compatibility path, by design.",[1114,225061,225062,225065],{},[950,225063,225064],{},"The anti-phishing terminal state is the player's job."," The kernel exposes the reveal and keeps no attempt counter; showing the warning, refusing a skip affordance, and rate-limiting wrong prompts are all app-side obligations.",[1114,225067,225068,225071,225072,1520],{},[950,225069,225070],{},"Anti-autobiography guidance is documentation, never a validator"," — the hub cannot tell a private memory from a fact on someone's public profile. See the ",[1007,225073,222768],{"href":540},[1022,225075,2621],{"id":2620},[1111,225077,225078,225103,225110,225115,225120],{},[1114,225079,225080,225084,225085,968,225087,968,225089,968,225091,968,225093,968,225095,968,225097,968,225099,968,225101,957],{},[1007,225081,225082,9789],{"href":91889},[941,225083,4883],{}," — the full catalog of unlock\u002Fauth packages (",[941,225086,15944],{},[941,225088,15969],{},[941,225090,15953],{},[941,225092,15958],{},[941,225094,15974],{},[941,225096,15979],{},[941,225098,18152],{},[941,225100,15994],{},[941,225102,15984],{},[1114,225104,225105,225109],{},[1007,225106,225107],{"href":128438},[941,225108,645],{}," — the plaintext, unauthenticated vault metadata surface (separate from anything covered by session tiers).",[1114,225111,225112,225114],{},[1007,225113,539],{"href":540}," — how to choose the three parts, and what makes an echo unguessable.",[1114,225116,225117,225119],{},[1007,225118,463],{"href":464}," — the attacker positions behind every claim on this page, including the echo ceremony's honest limits.",[1114,225121,225122,225124],{},[1007,225123,589],{"href":590}," — the full tier-1\u002F2\u002F3 lifecycle end to end.",[2645,225126,225127],{},"html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}html pre.shiki code .sfNiH, html code.shiki .sfNiH{--shiki-light:#FF5370;--shiki-default:#FF9CAC;--shiki-dark:#FF9CAC}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sHdIc, html code.shiki .sHdIc{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#EEFFFF;--shiki-default-font-style:italic;--shiki-dark:#BABED8;--shiki-dark-font-style:italic}",{"title":1037,"searchDepth":1038,"depth":1038,"links":225129},[225130,225131,225132,225133,225173,225181,225182,225183],{"id":6241,"depth":1041,"text":6242},{"id":99863,"depth":1041,"text":99864},{"id":16992,"depth":1041,"text":16993},{"id":29892,"depth":1041,"text":29893,"children":225134},[225135,225140,225144,225145,225151,225155,225167],{"id":218761,"depth":1038,"text":218762,"children":225136},[225137,225138,225139],{"id":218857,"depth":2288,"text":218858},{"id":218968,"depth":2288,"text":218969},{"id":219161,"depth":2288,"text":219162},{"id":219185,"depth":1038,"text":219186,"children":225141},[225142,225143],{"id":219433,"depth":2288,"text":219434},{"id":219510,"depth":2288,"text":219511},{"id":219834,"depth":1038,"text":219835},{"id":220043,"depth":1038,"text":220044,"children":225146},[225147,225148,225149,225150],{"id":220264,"depth":2288,"text":220265},{"id":220440,"depth":2288,"text":220441},{"id":12969,"depth":2288,"text":12972},{"id":220885,"depth":2288,"text":220886},{"id":220903,"depth":1038,"text":220904,"children":225152},[225153,225154],{"id":220907,"depth":2288,"text":220908},{"id":221146,"depth":2288,"text":221147},{"id":221421,"depth":1038,"text":221422,"children":225156},[225157,225158,225159,225160,225161,225162,225163,225164,225165,225166],{"id":112267,"depth":2288,"text":221476},{"id":221772,"depth":2288,"text":221773},{"id":221998,"depth":2288,"text":221999},{"id":222320,"depth":2288,"text":222321},{"id":222433,"depth":2288,"text":222434},{"id":222510,"depth":2288,"text":222511},{"id":222890,"depth":2288,"text":222891},{"id":223238,"depth":2288,"text":223239},{"id":120264,"depth":2288,"text":120265},{"id":223644,"depth":2288,"text":463},{"id":223657,"depth":1038,"text":223658,"children":225168},[225169,225170,225172],{"id":223879,"depth":2288,"text":223880},{"id":223926,"depth":2288,"text":225171},"NoydbSealer interface",{"id":224014,"depth":2288,"text":463},{"id":102888,"depth":1041,"text":102889,"children":225174},[225175,225176,225177,225178,225179,225180],{"id":224022,"depth":1038,"text":224023},{"id":224143,"depth":1038,"text":224144},{"id":18170,"depth":1038,"text":17096},{"id":224341,"depth":1038,"text":224342},{"id":224444,"depth":1038,"text":224445},{"id":224744,"depth":1038,"text":224745},{"id":224812,"depth":1041,"text":224813},{"id":224974,"depth":1041,"text":224975},{"id":2620,"depth":1041,"text":2621},"The three-tier session-authentication ladder — secret, multi-slot, quick-resume — and the policy-gate DSL guarding sensitive vault operations.",{},{"title":844,"description":225184},"_DabOI_fPjST05PVaxeHyOeSAbFGc0ESK5MebvdEnng",{"id":225189,"title":848,"access":12,"apiToc":929,"body":225190,"category":1042,"description":226472,"extension":1044,"meta":226473,"navigation":1042,"nest":1047,"path":849,"seo":226474,"sinceVersion":1042,"status":1049,"stem":850,"subtitle":1042,"tier":1050,"tocExternal":1042,"__hash__":226475},"docs\u002Fservices\u002Fshadow.md",{"type":931,"value":225191,"toc":226450},[225192,225194,225197,225199,225228,225230,225244,225246,225268,225270,225407,225409,225413,225662,225665,225848,225854,225935,225950,225954,225957,226076,226088,226090,226098,226142,226150,226156,226288,226294,226378,226380,226390,226392,226405,226407,226419,226421,226448],[934,225193,848],{"id":848},[46871,225195],{":rows":225196},"[{\"label\":\"Subpath\",\"value\":\"`@noy-db\u002Fhub\u002Fshadow`\"},{\"label\":\"Factory\",\"value\":\"`withShadow()`\"},{\"label\":\"Layer\",\"value\":\"`with-fork`\"},{\"label\":\"Cluster\",\"value\":\"F — Snapshot & Portability\"},{\"label\":\"LOC cost\",\"value\":\"~129 (off-bundle when not opted in)\"}]",[1022,225198,29857],{"id":29856},[937,225200,225201,225202,225204,225205,225207,225208,225210,225211,225214,225215,225218,225219,225221,225222,225224,225225,1520],{},"This service adds a read-only view of a ",[950,225203,2694],{}," — noy-db's end-to-end-encrypted document store\n(see ",[1007,225206,65376],{"href":48},"). Once enabled, ",[941,225209,21505],{}," returns\na ",[941,225212,225213],{},"VaultFrame",": every read method on it delegates to the underlying live collection (same\nvalidation, locale handling, and caching as the real thing), while every write method throws\n",[941,225216,225217],{},"ReadOnlyFrameError"," instead of touching data. A frame is a ",[1092,225220,104079],{},", not a snapshot — it always\nreflects the vault's current state at the moment you read, not the state at the moment ",[941,225223,23750],{},"\nwas called. Because it's opt-in, the ~129 lines behind it never reach your application bundle\nunless you import ",[941,225226,225227],{},"withShadow",[1022,225229,111660],{"id":111659},[1111,225231,225232,225235,225238,225241],{},[1114,225233,225234],{},"Screen-sharing a live vault during a demo, without risking an accidental edit",[1114,225236,225237],{},"Compliance review where the reviewer must not be able to change data",[1114,225239,225240],{},"Test harnesses that want to assert a code path is read-only",[1114,225242,225243],{},"Wrapping a vault before passing it into an untrusted or lower-trust callsite",[1022,225245,70093],{"id":70092},[1111,225247,225248,225255],{},[1114,225249,225250,964,225252,225254],{},[950,225251,107493],{},[941,225253,21500],{}," alone enables read-only frames.",[1114,225256,225257,1324,225264,27678,225266,1520],{},[1092,225258,225259,225260,1261,225262,2311],{},"For frame ",[941,225261,158461],{},[941,225263,21843],{},[941,225265,84477],{},[1007,225267,697],{"href":698},[1022,225269,99864],{"id":99863},[2217,225271,225273],{"className":2219,"code":225272,"language":2221,"meta":1037,"style":1037},"import { createNoydb } from '@noy-db\u002Fhub'\nimport { withShadow } from '@noy-db\u002Fhub\u002Fshadow'\n\nconst db = await createNoydb({\n  user: 'firm',\n  secret: 'firm-secret-2026',   \u002F\u002F omit `store` to use the built-in in-memory store\n  shadowStrategy: withShadow(),\n})\nconst vault = await db.openVault('books')\n",[941,225274,225275,225293,225312,225316,225332,225346,225362,225375,225381],{"__ignoreMap":1037},[2225,225276,225277,225279,225281,225283,225285,225287,225289,225291],{"class":2227,"line":2228},[2225,225278,2232],{"class":2231},[2225,225280,2236],{"class":2235},[2225,225282,2744],{"class":2239},[2225,225284,2243],{"class":2235},[2225,225286,2246],{"class":2231},[2225,225288,2249],{"class":2235},[2225,225290,1086],{"class":2252},[2225,225292,2256],{"class":2235},[2225,225294,225295,225297,225299,225302,225304,225306,225308,225310],{"class":2227,"line":1041},[2225,225296,2232],{"class":2231},[2225,225298,2236],{"class":2235},[2225,225300,225301],{"class":2239}," withShadow",[2225,225303,2243],{"class":2235},[2225,225305,2246],{"class":2231},[2225,225307,2249],{"class":2235},[2225,225309,73367],{"class":2252},[2225,225311,2256],{"class":2235},[2225,225313,225314],{"class":2227,"line":1038},[2225,225315,2285],{"emptyLinePlaceholder":2284},[2225,225317,225318,225320,225322,225324,225326,225328,225330],{"class":2227,"line":2288},[2225,225319,2775],{"class":2774},[2225,225321,2778],{"class":2239},[2225,225323,2781],{"class":2235},[2225,225325,2784],{"class":2231},[2225,225327,2744],{"class":2291},[2225,225329,2789],{"class":2239},[2225,225331,4200],{"class":2235},[2225,225333,225334,225336,225338,225340,225342,225344],{"class":2227,"line":2304},[2225,225335,4205],{"class":2307},[2225,225337,2311],{"class":2235},[2225,225339,2249],{"class":2235},[2225,225341,39709],{"class":2252},[2225,225343,2805],{"class":2235},[2225,225345,1707],{"class":2235},[2225,225347,225348,225350,225352,225354,225356,225358,225360],{"class":2227,"line":2356},[2225,225349,10423],{"class":2307},[2225,225351,2311],{"class":2235},[2225,225353,2249],{"class":2235},[2225,225355,65709],{"class":2252},[2225,225357,2805],{"class":2235},[2225,225359,2298],{"class":2235},[2225,225361,158564],{"class":2763},[2225,225363,225364,225367,225369,225371,225373],{"class":2227,"line":2830},[2225,225365,225366],{"class":2307},"  shadowStrategy",[2225,225368,2311],{"class":2235},[2225,225370,225301],{"class":2291},[2225,225372,4228],{"class":2239},[2225,225374,1707],{"class":2235},[2225,225376,225377,225379],{"class":2227,"line":2861},[2225,225378,2359],{"class":2235},[2225,225380,2362],{"class":2239},[2225,225382,225383,225385,225387,225389,225391,225393,225395,225397,225399,225401,225403,225405],{"class":2227,"line":4246},[2225,225384,2775],{"class":2774},[2225,225386,2835],{"class":2239},[2225,225388,2781],{"class":2235},[2225,225390,2784],{"class":2231},[2225,225392,2842],{"class":2239},[2225,225394,1520],{"class":2235},[2225,225396,2847],{"class":2291},[2225,225398,2789],{"class":2239},[2225,225400,2805],{"class":2235},[2225,225402,65761],{"class":2252},[2225,225404,2805],{"class":2235},[2225,225406,2362],{"class":2239},[1022,225408,29893],{"id":29892},[1591,225410,225412],{"id":225411},"reading-through-a-frame","Reading through a frame",[2217,225414,225416],{"className":2219,"code":225415,"language":2221,"meta":1037,"style":1037},"interface Invoice { amount: number; status: string }\nconst invoices = vault.collection\u003CInvoice>('invoices')\nawait invoices.put('inv-1', { amount: 1000, status: 'sent' })\n\nconst frame = vault.frame()\nconst invoice = await frame.collection\u003CInvoice>('invoices').get('inv-1')\n\u002F\u002F → { amount: 1000, status: 'sent' }\n\nconst all = await frame.collection\u003CInvoice>('invoices').list()\n\u002F\u002F → [{ amount: 1000, status: 'sent' }]\n\nconst names = await frame.collections()\n\u002F\u002F → ['invoices']\n",[941,225417,225418,225442,225472,225516,225520,225537,225583,225588,225592,225630,225635,225639,225657],{"__ignoreMap":1037},[2225,225419,225420,225422,225424,225426,225428,225430,225432,225434,225436,225438,225440],{"class":2227,"line":2228},[2225,225421,6994],{"class":2774},[2225,225423,6343],{"class":6342},[2225,225425,2236],{"class":2235},[2225,225427,6351],{"class":2307},[2225,225429,2311],{"class":2235},[2225,225431,6356],{"class":6342},[2225,225433,3769],{"class":2235},[2225,225435,6361],{"class":2307},[2225,225437,2311],{"class":2235},[2225,225439,6366],{"class":6342},[2225,225441,6369],{"class":2235},[2225,225443,225444,225446,225448,225450,225452,225454,225456,225458,225460,225462,225464,225466,225468,225470],{"class":2227,"line":1041},[2225,225445,2775],{"class":2774},[2225,225447,6461],{"class":2239},[2225,225449,2781],{"class":2235},[2225,225451,6466],{"class":2239},[2225,225453,1520],{"class":2235},[2225,225455,6471],{"class":2291},[2225,225457,6474],{"class":2235},[2225,225459,6477],{"class":6342},[2225,225461,6480],{"class":2235},[2225,225463,2789],{"class":2239},[2225,225465,2805],{"class":2235},[2225,225467,6487],{"class":2252},[2225,225469,2805],{"class":2235},[2225,225471,2362],{"class":2239},[2225,225473,225474,225476,225478,225480,225482,225484,225486,225488,225490,225492,225494,225496,225498,225500,225502,225504,225506,225508,225510,225512,225514],{"class":2227,"line":1038},[2225,225475,6500],{"class":2231},[2225,225477,6503],{"class":2239},[2225,225479,1520],{"class":2235},[2225,225481,3161],{"class":2291},[2225,225483,2789],{"class":2239},[2225,225485,2805],{"class":2235},[2225,225487,6514],{"class":2252},[2225,225489,2805],{"class":2235},[2225,225491,2298],{"class":2235},[2225,225493,2236],{"class":2235},[2225,225495,6351],{"class":2307},[2225,225497,2311],{"class":2235},[2225,225499,26290],{"class":6527},[2225,225501,2298],{"class":2235},[2225,225503,6361],{"class":2307},[2225,225505,2311],{"class":2235},[2225,225507,2249],{"class":2235},[2225,225509,88283],{"class":2252},[2225,225511,2805],{"class":2235},[2225,225513,2243],{"class":2235},[2225,225515,2362],{"class":2239},[2225,225517,225518],{"class":2227,"line":2288},[2225,225519,2285],{"emptyLinePlaceholder":2284},[2225,225521,225522,225524,225527,225529,225531,225533,225535],{"class":2227,"line":2304},[2225,225523,2775],{"class":2774},[2225,225525,225526],{"class":2239}," frame ",[2225,225528,2781],{"class":2235},[2225,225530,6466],{"class":2239},[2225,225532,1520],{"class":2235},[2225,225534,40918],{"class":2291},[2225,225536,8146],{"class":2239},[2225,225538,225539,225541,225543,225545,225547,225549,225551,225553,225555,225557,225559,225561,225563,225565,225567,225569,225571,225573,225575,225577,225579,225581],{"class":2227,"line":2356},[2225,225540,2775],{"class":2774},[2225,225542,125221],{"class":2239},[2225,225544,2781],{"class":2235},[2225,225546,2784],{"class":2231},[2225,225548,40886],{"class":2239},[2225,225550,1520],{"class":2235},[2225,225552,6471],{"class":2291},[2225,225554,6474],{"class":2235},[2225,225556,6477],{"class":6342},[2225,225558,6480],{"class":2235},[2225,225560,2789],{"class":2239},[2225,225562,2805],{"class":2235},[2225,225564,6487],{"class":2252},[2225,225566,2805],{"class":2235},[2225,225568,1381],{"class":2239},[2225,225570,1520],{"class":2235},[2225,225572,3158],{"class":2291},[2225,225574,2789],{"class":2239},[2225,225576,2805],{"class":2235},[2225,225578,6514],{"class":2252},[2225,225580,2805],{"class":2235},[2225,225582,2362],{"class":2239},[2225,225584,225585],{"class":2227,"line":2830},[2225,225586,225587],{"class":2763},"\u002F\u002F → { amount: 1000, status: 'sent' }\n",[2225,225589,225590],{"class":2227,"line":2861},[2225,225591,2285],{"emptyLinePlaceholder":2284},[2225,225593,225594,225596,225598,225600,225602,225604,225606,225608,225610,225612,225614,225616,225618,225620,225622,225624,225626,225628],{"class":2227,"line":4246},[2225,225595,2775],{"class":2774},[2225,225597,104158],{"class":2239},[2225,225599,2781],{"class":2235},[2225,225601,2784],{"class":2231},[2225,225603,40886],{"class":2239},[2225,225605,1520],{"class":2235},[2225,225607,6471],{"class":2291},[2225,225609,6474],{"class":2235},[2225,225611,6477],{"class":6342},[2225,225613,6480],{"class":2235},[2225,225615,2789],{"class":2239},[2225,225617,2805],{"class":2235},[2225,225619,6487],{"class":2252},[2225,225621,2805],{"class":2235},[2225,225623,1381],{"class":2239},[2225,225625,1520],{"class":2235},[2225,225627,3167],{"class":2291},[2225,225629,8146],{"class":2239},[2225,225631,225632],{"class":2227,"line":4253},[2225,225633,225634],{"class":2763},"\u002F\u002F → [{ amount: 1000, status: 'sent' }]\n",[2225,225636,225637],{"class":2227,"line":4259},[2225,225638,2285],{"emptyLinePlaceholder":2284},[2225,225640,225641,225643,225645,225647,225649,225651,225653,225655],{"class":2227,"line":6548},[2225,225642,2775],{"class":2774},[2225,225644,123455],{"class":2239},[2225,225646,2781],{"class":2235},[2225,225648,2784],{"class":2231},[2225,225650,40886],{"class":2239},[2225,225652,1520],{"class":2235},[2225,225654,2698],{"class":2291},[2225,225656,8146],{"class":2239},[2225,225658,225659],{"class":2227,"line":6576},[2225,225660,225661],{"class":2763},"\u002F\u002F → ['invoices']\n",[937,225663,225664],{},"A frame always tracks the live vault — it is never stale:",[2217,225666,225668],{"className":2219,"code":225667,"language":2221,"meta":1037,"style":1037},"const payments = vault.collection\u003CInvoice>('payments')\nconst paymentsFrame = vault.frame()\n\nawait paymentsFrame.collection\u003CInvoice>('payments').list()\n\u002F\u002F → []  (nothing written yet)\n\nawait payments.put('pay-1', { amount: 750, status: 'pending' })\nawait paymentsFrame.collection\u003CInvoice>('payments').list()\n\u002F\u002F → [{ amount: 750, status: 'pending' }]   — the frame sees the write that happened after it was created\n",[941,225669,225670,225701,225718,225722,225755,225760,225764,225811,225843],{"__ignoreMap":1037},[2225,225671,225672,225674,225677,225679,225681,225683,225685,225687,225689,225691,225693,225695,225697,225699],{"class":2227,"line":2228},[2225,225673,2775],{"class":2774},[2225,225675,225676],{"class":2239}," payments ",[2225,225678,2781],{"class":2235},[2225,225680,6466],{"class":2239},[2225,225682,1520],{"class":2235},[2225,225684,6471],{"class":2291},[2225,225686,6474],{"class":2235},[2225,225688,6477],{"class":6342},[2225,225690,6480],{"class":2235},[2225,225692,2789],{"class":2239},[2225,225694,2805],{"class":2235},[2225,225696,32579],{"class":2252},[2225,225698,2805],{"class":2235},[2225,225700,2362],{"class":2239},[2225,225702,225703,225705,225708,225710,225712,225714,225716],{"class":2227,"line":1041},[2225,225704,2775],{"class":2774},[2225,225706,225707],{"class":2239}," paymentsFrame ",[2225,225709,2781],{"class":2235},[2225,225711,6466],{"class":2239},[2225,225713,1520],{"class":2235},[2225,225715,40918],{"class":2291},[2225,225717,8146],{"class":2239},[2225,225719,225720],{"class":2227,"line":1038},[2225,225721,2285],{"emptyLinePlaceholder":2284},[2225,225723,225724,225726,225729,225731,225733,225735,225737,225739,225741,225743,225745,225747,225749,225751,225753],{"class":2227,"line":2288},[2225,225725,6500],{"class":2231},[2225,225727,225728],{"class":2239}," paymentsFrame",[2225,225730,1520],{"class":2235},[2225,225732,6471],{"class":2291},[2225,225734,6474],{"class":2235},[2225,225736,6477],{"class":6342},[2225,225738,6480],{"class":2235},[2225,225740,2789],{"class":2239},[2225,225742,2805],{"class":2235},[2225,225744,32579],{"class":2252},[2225,225746,2805],{"class":2235},[2225,225748,1381],{"class":2239},[2225,225750,1520],{"class":2235},[2225,225752,3167],{"class":2291},[2225,225754,8146],{"class":2239},[2225,225756,225757],{"class":2227,"line":2304},[2225,225758,225759],{"class":2763},"\u002F\u002F → []  (nothing written yet)\n",[2225,225761,225762],{"class":2227,"line":2356},[2225,225763,2285],{"emptyLinePlaceholder":2284},[2225,225765,225766,225768,225771,225773,225775,225777,225779,225782,225784,225786,225788,225790,225792,225795,225797,225799,225801,225803,225805,225807,225809],{"class":2227,"line":2830},[2225,225767,6500],{"class":2231},[2225,225769,225770],{"class":2239}," payments",[2225,225772,1520],{"class":2235},[2225,225774,3161],{"class":2291},[2225,225776,2789],{"class":2239},[2225,225778,2805],{"class":2235},[2225,225780,225781],{"class":2252},"pay-1",[2225,225783,2805],{"class":2235},[2225,225785,2298],{"class":2235},[2225,225787,2236],{"class":2235},[2225,225789,6351],{"class":2307},[2225,225791,2311],{"class":2235},[2225,225793,225794],{"class":6527}," 750",[2225,225796,2298],{"class":2235},[2225,225798,6361],{"class":2307},[2225,225800,2311],{"class":2235},[2225,225802,2249],{"class":2235},[2225,225804,12192],{"class":2252},[2225,225806,2805],{"class":2235},[2225,225808,2243],{"class":2235},[2225,225810,2362],{"class":2239},[2225,225812,225813,225815,225817,225819,225821,225823,225825,225827,225829,225831,225833,225835,225837,225839,225841],{"class":2227,"line":2861},[2225,225814,6500],{"class":2231},[2225,225816,225728],{"class":2239},[2225,225818,1520],{"class":2235},[2225,225820,6471],{"class":2291},[2225,225822,6474],{"class":2235},[2225,225824,6477],{"class":6342},[2225,225826,6480],{"class":2235},[2225,225828,2789],{"class":2239},[2225,225830,2805],{"class":2235},[2225,225832,32579],{"class":2252},[2225,225834,2805],{"class":2235},[2225,225836,1381],{"class":2239},[2225,225838,1520],{"class":2235},[2225,225840,3167],{"class":2291},[2225,225842,8146],{"class":2239},[2225,225844,225845],{"class":2227,"line":4246},[2225,225846,225847],{"class":2763},"\u002F\u002F → [{ amount: 750, status: 'pending' }]   — the frame sees the write that happened after it was created\n",[1591,225849,225851,225852],{"id":225850},"writes-throw-readonlyframeerror","Writes throw ",[941,225853,225217],{},[2217,225855,225857],{"className":2219,"code":225856,"language":2221,"meta":1037,"style":1037},"await frame.collection\u003CInvoice>('invoices')\n  .put('inv-1', { amount: 2000, status: 'paid' })\n\u002F\u002F → throws ReadOnlyFrameError: \"Cannot put() on a vault frame — frames are read-only\n\u002F\u002F   presentations of the current vault\"\n",[941,225858,225859,225885,225925,225930],{"__ignoreMap":1037},[2225,225860,225861,225863,225865,225867,225869,225871,225873,225875,225877,225879,225881,225883],{"class":2227,"line":2228},[2225,225862,6500],{"class":2231},[2225,225864,40886],{"class":2239},[2225,225866,1520],{"class":2235},[2225,225868,6471],{"class":2291},[2225,225870,6474],{"class":2235},[2225,225872,6477],{"class":6342},[2225,225874,6480],{"class":2235},[2225,225876,2789],{"class":2239},[2225,225878,2805],{"class":2235},[2225,225880,6487],{"class":2252},[2225,225882,2805],{"class":2235},[2225,225884,2362],{"class":2239},[2225,225886,225887,225889,225891,225893,225895,225897,225899,225901,225903,225905,225907,225909,225911,225913,225915,225917,225919,225921,225923],{"class":2227,"line":1041},[2225,225888,26235],{"class":2235},[2225,225890,3161],{"class":2291},[2225,225892,2789],{"class":2239},[2225,225894,2805],{"class":2235},[2225,225896,6514],{"class":2252},[2225,225898,2805],{"class":2235},[2225,225900,2298],{"class":2235},[2225,225902,2236],{"class":2235},[2225,225904,6351],{"class":2307},[2225,225906,2311],{"class":2235},[2225,225908,159089],{"class":6527},[2225,225910,2298],{"class":2235},[2225,225912,6361],{"class":2307},[2225,225914,2311],{"class":2235},[2225,225916,2249],{"class":2235},[2225,225918,6539],{"class":2252},[2225,225920,2805],{"class":2235},[2225,225922,2243],{"class":2235},[2225,225924,2362],{"class":2239},[2225,225926,225927],{"class":2227,"line":1038},[2225,225928,225929],{"class":2763},"\u002F\u002F → throws ReadOnlyFrameError: \"Cannot put() on a vault frame — frames are read-only\n",[2225,225931,225932],{"class":2227,"line":2288},[2225,225933,225934],{"class":2763},"\u002F\u002F   presentations of the current vault\"\n",[937,225936,225937,225938,968,225940,968,225942,968,225944,968,225946,968,225948,1520],{},"Every write method throws the same way, with the attempted operation named in the message:\n",[941,225939,3161],{},[941,225941,3164],{},[941,225943,39210],{},[941,225945,24476],{},[941,225947,24681],{},[941,225949,159494],{},[1591,225951,225953],{"id":225952},"history-reads-still-work","History reads still work",[937,225955,225956],{},"History reads are allowed through a frame — a past version is data, not a live mutation:",[2217,225958,225960],{"className":2219,"code":225959,"language":2221,"meta":1037,"style":1037},"const history = await frame.collection\u003CInvoice>('invoices').history('inv-1')\n\u002F\u002F → [{ version: 1, timestamp: '2026-07-08T09:12:00.000Z', userId: 'firm',\n\u002F\u002F      record: { amount: 1000, status: 'sent' } }]\n\nconst v1 = await frame.collection\u003CInvoice>('invoices').getVersion('inv-1', 1)\n\u002F\u002F → { amount: 1000, status: 'sent' }\n",[941,225961,225962,226008,226013,226018,226022,226072],{"__ignoreMap":1037},[2225,225963,225964,225966,225968,225970,225972,225974,225976,225978,225980,225982,225984,225986,225988,225990,225992,225994,225996,225998,226000,226002,226004,226006],{"class":2227,"line":2228},[2225,225965,2775],{"class":2774},[2225,225967,59845],{"class":2239},[2225,225969,2781],{"class":2235},[2225,225971,2784],{"class":2231},[2225,225973,40886],{"class":2239},[2225,225975,1520],{"class":2235},[2225,225977,6471],{"class":2291},[2225,225979,6474],{"class":2235},[2225,225981,6477],{"class":6342},[2225,225983,6480],{"class":2235},[2225,225985,2789],{"class":2239},[2225,225987,2805],{"class":2235},[2225,225989,6487],{"class":2252},[2225,225991,2805],{"class":2235},[2225,225993,1381],{"class":2239},[2225,225995,1520],{"class":2235},[2225,225997,697],{"class":2291},[2225,225999,2789],{"class":2239},[2225,226001,2805],{"class":2235},[2225,226003,6514],{"class":2252},[2225,226005,2805],{"class":2235},[2225,226007,2362],{"class":2239},[2225,226009,226010],{"class":2227,"line":1041},[2225,226011,226012],{"class":2763},"\u002F\u002F → [{ version: 1, timestamp: '2026-07-08T09:12:00.000Z', userId: 'firm',\n",[2225,226014,226015],{"class":2227,"line":1038},[2225,226016,226017],{"class":2763},"\u002F\u002F      record: { amount: 1000, status: 'sent' } }]\n",[2225,226019,226020],{"class":2227,"line":2288},[2225,226021,2285],{"emptyLinePlaceholder":2284},[2225,226023,226024,226026,226028,226030,226032,226034,226036,226038,226040,226042,226044,226046,226048,226050,226052,226054,226056,226058,226060,226062,226064,226066,226068,226070],{"class":2227,"line":2304},[2225,226025,2775],{"class":2774},[2225,226027,159394],{"class":2239},[2225,226029,2781],{"class":2235},[2225,226031,2784],{"class":2231},[2225,226033,40886],{"class":2239},[2225,226035,1520],{"class":2235},[2225,226037,6471],{"class":2291},[2225,226039,6474],{"class":2235},[2225,226041,6477],{"class":6342},[2225,226043,6480],{"class":2235},[2225,226045,2789],{"class":2239},[2225,226047,2805],{"class":2235},[2225,226049,6487],{"class":2252},[2225,226051,2805],{"class":2235},[2225,226053,1381],{"class":2239},[2225,226055,1520],{"class":2235},[2225,226057,159405],{"class":2291},[2225,226059,2789],{"class":2239},[2225,226061,2805],{"class":2235},[2225,226063,6514],{"class":2252},[2225,226065,2805],{"class":2235},[2225,226067,2298],{"class":2235},[2225,226069,9377],{"class":6527},[2225,226071,2362],{"class":2239},[2225,226073,226074],{"class":2227,"line":2356},[2225,226075,225587],{"class":2763},[937,226077,226078,997,226080,226082,226083,226087],{},[941,226079,158461],{},[941,226081,21843],{}," are only meaningful when the vault also has\n",[1007,226084,226085],{"href":697},[941,226086,6719],{}," enabled; otherwise they throw the same way the live collection's\nmethods do.",[1022,226089,102889],{"id":102888},[1591,226091,226093,226095,226096],{"id":226092},"vaultframe-returned-by-vaultframe",[941,226094,225213],{}," — returned by ",[941,226097,21505],{},[1338,226099,226100,226110],{},[1341,226101,226102],{},[1344,226103,226104,226106,226108],{},[1347,226105,7831],{},[1347,226107,102906],{},[1347,226109,7834],{},[1357,226111,226112,226127],{},[1344,226113,226114,226119,226124],{},[1362,226115,226116],{},[941,226117,226118],{},"frame.collection\u003CT>(name)",[1362,226120,226121],{},[941,226122,226123],{},"CollectionFrame\u003CT>",[1362,226125,226126],{},"Read-only view of one collection.",[1344,226128,226129,226134,226139],{},[1362,226130,226131],{},[941,226132,226133],{},"frame.collections()",[1362,226135,226136],{},[941,226137,226138],{},"Promise\u003Cstring[]>",[1362,226140,226141],{},"List every collection name visible in the underlying vault.",[1591,226143,226145,226095,226147],{"id":226144},"collectionframet-returned-by-framecollectionname",[941,226146,226123],{},[941,226148,226149],{},"frame.collection(name)",[937,226151,226152,226153,226155],{},"Reads delegate to the underlying ",[941,226154,3121],{}," and behave identically to calling them directly:",[1338,226157,226158,226168],{},[1341,226159,226160],{},[1344,226161,226162,226164,226166],{},[1347,226163,7831],{},[1347,226165,102906],{},[1347,226167,7834],{},[1357,226169,226170,226184,226199,226224,226238,226256,226274],{},[1344,226171,226172,226177,226181],{},[1362,226173,226174],{},[941,226175,226176],{},"frameCollection.get(id, locale?)",[1362,226178,226179],{},[941,226180,160357],{},[1362,226182,226183],{},"Read one record.",[1344,226185,226186,226191,226196],{},[1362,226187,226188],{},[941,226189,226190],{},"frameCollection.list(locale?)",[1362,226192,226193],{},[941,226194,226195],{},"Promise\u003CT[]>",[1362,226197,226198],{},"Read every record in the collection.",[1344,226200,226201,226206,226210],{},[1362,226202,226203],{},[941,226204,226205],{},"frameCollection.query()",[1362,226207,226208],{},[941,226209,24785],{},[1362,226211,226212,226213,968,226215,968,226217,968,226219,968,226221,226223],{},"Chainable query builder (",[941,226214,24883],{},[941,226216,164984],{},[941,226218,164987],{},[941,226220,65247],{},[941,226222,21931],{},", …) — no write surface, so it's safe to expose directly.",[1344,226225,226226,226231,226235],{},[1362,226227,226228],{},[941,226229,226230],{},"frameCollection.query(predicate)",[1362,226232,226233],{},[941,226234,24430],{},[1362,226236,226237],{},"Inline-predicate overload, same as calling it on the live collection.",[1344,226239,226240,226245,226249],{},[1362,226241,226242],{},[941,226243,226244],{},"frameCollection.history(id, options?)",[1362,226246,226247],{},[941,226248,160336],{},[1362,226250,226251,226252,1520],{},"Prior versions, newest first — requires ",[1007,226253,226254],{"href":697},[941,226255,6719],{},[1344,226257,226258,226263,226267],{},[1362,226259,226260],{},[941,226261,226262],{},"frameCollection.getVersion(id, version)",[1362,226264,226265],{},[941,226266,160357],{},[1362,226268,226269,226270,1520],{},"One specific past version — requires ",[1007,226271,226272],{"href":697},[941,226273,6719],{},[1344,226275,226276,226281,226285],{},[1362,226277,226278],{},[941,226279,226280],{},"frameCollection.name",[1362,226282,226283],{},[941,226284,7259],{},[1362,226286,226287],{},"The underlying collection's name, captured at construction.",[937,226289,226290,226291,226293],{},"Write methods all reject with ",[941,226292,225217],{},", naming the attempted operation:",[1338,226295,226296,226304],{},[1341,226297,226298],{},[1344,226299,226300,226302],{},[1347,226301,7831],{},[1347,226303,155630],{},[1357,226305,226306,226318,226330,226342,226354,226366],{},[1344,226307,226308,226313],{},[1362,226309,226310],{},[941,226311,226312],{},"frameCollection.put(id, record)",[1362,226314,226315],{},[941,226316,226317],{},"ReadOnlyFrameError('put')",[1344,226319,226320,226325],{},[1362,226321,226322],{},[941,226323,226324],{},"frameCollection.delete(id)",[1362,226326,226327],{},[941,226328,226329],{},"ReadOnlyFrameError('delete')",[1344,226331,226332,226337],{},[1362,226333,226334],{},[941,226335,226336],{},"frameCollection.update(id, patch)",[1362,226338,226339],{},[941,226340,226341],{},"ReadOnlyFrameError('update')",[1344,226343,226344,226349],{},[1362,226345,226346],{},[941,226347,226348],{},"frameCollection.revert(id, version)",[1362,226350,226351],{},[941,226352,226353],{},"ReadOnlyFrameError('revert')",[1344,226355,226356,226361],{},[1362,226357,226358],{},[941,226359,226360],{},"frameCollection.putMany(entries)",[1362,226362,226363],{},[941,226364,226365],{},"ReadOnlyFrameError('putMany')",[1344,226367,226368,226373],{},[1362,226369,226370],{},[941,226371,226372],{},"frameCollection.deleteMany(ids)",[1362,226374,226375],{},[941,226376,226377],{},"ReadOnlyFrameError('deleteMany')",[1022,226379,101425],{"id":101424},[1111,226381,226382],{},[1114,226383,226384,165298,226386,997,226388],{},[941,226385,21505],{},[941,226387,73367],{},[941,226389,21500],{},[1022,226391,114684],{"id":114683},[1111,226393,226394],{},[1114,226395,226396,964,226400,22207,226402,226404],{},[950,226397,226398],{},[1007,226399,697],{"href":697},[941,226401,58310],{},[941,226403,160499],{},", the point-in-time\ncousin of a frame: same read-only contract (writes throw), but it reads a fixed past moment\ninstead of the vault's current state",[1022,226406,6825],{"id":6824},[1111,226408,226409,226416],{},[1114,226410,226411,226412,226415],{},"A frame is a JavaScript-layer guard, not a cryptographic boundary — the underlying keyring's\ndecryption keys are unchanged. A hostile caller in the same process could still reach the live\nvault directly and write to it; use a frame to prevent ",[1092,226413,226414],{},"accidental"," writes in a read-scoped code\npath, not as a security boundary against a hostile co-tenant.",[1114,226417,226418],{},"Frames are cheap to construct — create one per code path that should be read-only rather than\nsharing one broadly.",[1022,226420,2621],{"id":2620},[1111,226422,226423,226427,226434,226440],{},[1114,226424,226425,160785],{},[1007,226426,65376],{"href":48},[1114,226428,226429,226431,226432],{},[1007,226430,697],{"href":697}," — versioning, the audit ledger, and ",[941,226433,58310],{},[1114,226435,226436,1368,226438],{},[1007,226437,61743],{"href":598},[1007,226439,1019],{"href":1018},[1114,226441,101581,226442,1520],{},[1007,226443,226445],{"href":92703,"rel":226444},[3371],[941,226446,226447],{},"16-with-shadow",[2645,226449,126569],{},{"title":1037,"searchDepth":1038,"depth":1038,"links":226451},[226452,226453,226454,226455,226456,226462,226468,226469,226470,226471],{"id":29856,"depth":1041,"text":29857},{"id":111659,"depth":1041,"text":111660},{"id":70092,"depth":1041,"text":70093},{"id":99863,"depth":1041,"text":99864},{"id":29892,"depth":1041,"text":29893,"children":226457},[226458,226459,226461],{"id":225411,"depth":1038,"text":225412},{"id":225850,"depth":1038,"text":226460},"Writes throw ReadOnlyFrameError",{"id":225952,"depth":1038,"text":225953},{"id":102888,"depth":1041,"text":102889,"children":226463},[226464,226466],{"id":226092,"depth":1038,"text":226465},"VaultFrame — returned by vault.frame()",{"id":226144,"depth":1038,"text":226467},"CollectionFrame\u003CT> — returned by frame.collection(name)",{"id":101424,"depth":1041,"text":101425},{"id":114683,"depth":1041,"text":114684},{"id":6824,"depth":1041,"text":6825},{"id":2620,"depth":1041,"text":2621},"Read-only views of a live vault: `vault.frame()` delegates reads and throws on writes — for screen-sharing, demos, compliance review, and test harnesses.",{},{"title":848,"description":226472},"-yF0TpAQwv9tz-5Kuk5XRP2lyTqPKXZqWfgAaxoNFkE",{"id":226477,"title":852,"access":12,"apiToc":929,"body":226478,"category":1042,"description":226873,"extension":1044,"meta":226874,"navigation":1042,"nest":1047,"path":853,"seo":226875,"sinceVersion":38549,"status":38550,"stem":854,"subtitle":1042,"tier":1473,"tocExternal":1042,"__hash__":226876},"docs\u002Fservices\u002Fshare-link.md",{"type":931,"value":226479,"toc":226867},[226480,226482,226485,226487,226490,226496,226510,226527,226531,226551,226561,226563,226791,226830,226840,226842,226864],[934,226481,852],{"id":852},[46871,226483],{":rows":226484},"[{\"label\":\"Subpath\",\"value\":\"`@noy-db\u002Fhub\u002Fshare-link`\"},{\"label\":\"Factory\",\"value\":\"none — direct named imports\"},{\"label\":\"Kind\",\"value\":\"reference — a frozen link grammar, not a service\"},{\"label\":\"LOC cost\",\"value\":\"standalone: no crypto, no store, no hub floor\"}]",[1022,226486,29857],{"id":29856},[937,226488,226489],{},"One canonical URL shape for addressing a single record across every portal surface — the LIFF\npermalink, the installed PWA, and the vendor console all speak it:",[2217,226491,226494],{"className":226492,"code":226493,"language":5672},[5670],"\u002Fr\u002F{vaultHandle}\u002F{collection}\u002F{recordId}?period=…&v=…#g={token}\n",[941,226495,226493],{"__ignoreMap":1037},[937,226497,226498,226501,226502,226505,226506,226509],{},[941,226499,226500],{},"buildShareLink(parts, base?)"," renders that string; ",[941,226503,226504],{},"parseShareLink(input)"," reads one back and\nthrows ",[941,226507,226508],{},"ShareLinkParseError"," on anything malformed. That is the whole surface.",[937,226511,226512,226513,226516,226517,226519,226520,226522,226523,226526],{},"It is deliberately ",[950,226514,226515],{},"standalone",": pure string and ",[941,226518,70476],{}," code, with no crypto, no store, and no hub\nfloor behind it. You can import it from a LIFF shell, a PWA, or ",[1007,226521,1212],{"href":38},"\nwithout dragging the rest of the hub along. The export surface is a ",[950,226524,226525],{},"frozen contract",", pinned\nupstream by a golden test.",[1022,226528,226530],{"id":226529},"the-grant-token-lives-in-the-fragment-and-that-is-the-point","The grant token lives in the fragment, and that is the point",[7646,226532,226534],{"color":203757,"icon":226533},"i-lucide-shield-check",[937,226535,226536,226537,226539,226540,226543,226544,226547,226548,226550],{},"The single-use grant token travels ",[950,226538,1320],{}," in the URL fragment (",[941,226541,226542],{},"#g=…","). Fragments are never sent\nto the server, so the token cannot land in access logs, proxy logs, or a ",[941,226545,226546],{},"Referer"," header — the\nsame transport rule ",[1007,226549,15909],{"href":306}," follows.",[937,226552,226553,226554,226556,226557,226560],{},"Everything before the ",[941,226555,3096],{}," is addressing information and is safe to log. Everything after it is\ncapability. If you build links by string concatenation instead of ",[941,226558,226559],{},"buildShareLink",", that separation\nis the thing you are most likely to break.",[1022,226562,29893],{"id":29892},[2217,226564,226566],{"className":2219,"code":226565,"language":2221,"meta":1037,"style":1037},"import { buildShareLink, parseShareLink, ShareLinkParseError } from '@noy-db\u002Fhub\u002Fshare-link'\n\nconst url = buildShareLink({\n  vaultHandle: '01J1QDX9Y3Z4A5B6C7D8E9FGHJ',\n  collection: 'invoices',\n  recordId: 'inv-1',\n  period: '2026-Q1',   \u002F\u002F optional\n  version: 3,          \u002F\u002F optional\n  grantToken: 'nQ3f…', \u002F\u002F optional — rendered into the fragment\n}, 'https:\u002F\u002Fportal.example')\n\ntry {\n  const link = parseShareLink(url)\n  \u002F\u002F → { vaultHandle, collection, recordId, period?, version?, grantToken? }\n} catch (err) {\n  if (err instanceof ShareLinkParseError) err.code \u002F\u002F machine-readable discriminant\n}\n",[941,226567,226568,226598,226602,226616,226631,226645,226659,226678,226691,226710,226723,226727,226733,226749,226754,226764,226787],{"__ignoreMap":1037},[2225,226569,226570,226572,226574,226577,226579,226582,226584,226587,226589,226591,226593,226596],{"class":2227,"line":2228},[2225,226571,2232],{"class":2231},[2225,226573,2236],{"class":2235},[2225,226575,226576],{"class":2239}," buildShareLink",[2225,226578,2298],{"class":2235},[2225,226580,226581],{"class":2239}," parseShareLink",[2225,226583,2298],{"class":2235},[2225,226585,226586],{"class":2239}," ShareLinkParseError",[2225,226588,2243],{"class":2235},[2225,226590,2246],{"class":2231},[2225,226592,2249],{"class":2235},[2225,226594,226595],{"class":2252},"@noy-db\u002Fhub\u002Fshare-link",[2225,226597,2256],{"class":2235},[2225,226599,226600],{"class":2227,"line":1041},[2225,226601,2285],{"emptyLinePlaceholder":2284},[2225,226603,226604,226606,226608,226610,226612,226614],{"class":2227,"line":1038},[2225,226605,2775],{"class":2774},[2225,226607,69773],{"class":2239},[2225,226609,2781],{"class":2235},[2225,226611,226576],{"class":2291},[2225,226613,2789],{"class":2239},[2225,226615,4200],{"class":2235},[2225,226617,226618,226621,226623,226625,226627,226629],{"class":2227,"line":2288},[2225,226619,226620],{"class":2307},"  vaultHandle",[2225,226622,2311],{"class":2235},[2225,226624,2249],{"class":2235},[2225,226626,115287],{"class":2252},[2225,226628,2805],{"class":2235},[2225,226630,1707],{"class":2235},[2225,226632,226633,226635,226637,226639,226641,226643],{"class":2227,"line":2304},[2225,226634,30275],{"class":2307},[2225,226636,2311],{"class":2235},[2225,226638,2249],{"class":2235},[2225,226640,6487],{"class":2252},[2225,226642,2805],{"class":2235},[2225,226644,1707],{"class":2235},[2225,226646,226647,226649,226651,226653,226655,226657],{"class":2227,"line":2356},[2225,226648,186783],{"class":2307},[2225,226650,2311],{"class":2235},[2225,226652,2249],{"class":2235},[2225,226654,6514],{"class":2252},[2225,226656,2805],{"class":2235},[2225,226658,1707],{"class":2235},[2225,226660,226661,226664,226666,226668,226671,226673,226675],{"class":2227,"line":2830},[2225,226662,226663],{"class":2307},"  period",[2225,226665,2311],{"class":2235},[2225,226667,2249],{"class":2235},[2225,226669,226670],{"class":2252},"2026-Q1",[2225,226672,2805],{"class":2235},[2225,226674,2298],{"class":2235},[2225,226676,226677],{"class":2763},"   \u002F\u002F optional\n",[2225,226679,226680,226682,226684,226686,226688],{"class":2227,"line":2861},[2225,226681,158056],{"class":2307},[2225,226683,2311],{"class":2235},[2225,226685,14460],{"class":6527},[2225,226687,2298],{"class":2235},[2225,226689,226690],{"class":2763},"          \u002F\u002F optional\n",[2225,226692,226693,226696,226698,226700,226703,226705,226707],{"class":2227,"line":4246},[2225,226694,226695],{"class":2307},"  grantToken",[2225,226697,2311],{"class":2235},[2225,226699,2249],{"class":2235},[2225,226701,226702],{"class":2252},"nQ3f…",[2225,226704,2805],{"class":2235},[2225,226706,2298],{"class":2235},[2225,226708,226709],{"class":2763}," \u002F\u002F optional — rendered into the fragment\n",[2225,226711,226712,226714,226716,226719,226721],{"class":2227,"line":4253},[2225,226713,33127],{"class":2235},[2225,226715,2249],{"class":2235},[2225,226717,226718],{"class":2252},"https:\u002F\u002Fportal.example",[2225,226720,2805],{"class":2235},[2225,226722,2362],{"class":2239},[2225,226724,226725],{"class":2227,"line":4259},[2225,226726,2285],{"emptyLinePlaceholder":2284},[2225,226728,226729,226731],{"class":2227,"line":6548},[2225,226730,7554],{"class":2231},[2225,226732,2301],{"class":2235},[2225,226734,226735,226737,226739,226741,226743,226745,226747],{"class":2227,"line":6576},[2225,226736,8123],{"class":2774},[2225,226738,44523],{"class":2239},[2225,226740,6346],{"class":2235},[2225,226742,226581],{"class":2291},[2225,226744,2789],{"class":2307},[2225,226746,54796],{"class":2239},[2225,226748,2362],{"class":2307},[2225,226750,226751],{"class":2227,"line":6582},[2225,226752,226753],{"class":2763},"  \u002F\u002F → { vaultHandle, collection, recordId, period?, version?, grantToken? }\n",[2225,226755,226756,226758,226760,226762],{"class":2227,"line":6587},[2225,226757,2359],{"class":2235},[2225,226759,7598],{"class":2231},[2225,226761,7601],{"class":2239},[2225,226763,4200],{"class":2235},[2225,226765,226766,226768,226770,226772,226774,226776,226778,226780,226782,226784],{"class":2227,"line":7174},[2225,226767,7608],{"class":2231},[2225,226769,953],{"class":2307},[2225,226771,7617],{"class":2239},[2225,226773,19172],{"class":2235},[2225,226775,226586],{"class":6342},[2225,226777,8495],{"class":2307},[2225,226779,7617],{"class":2239},[2225,226781,1520],{"class":2235},[2225,226783,941],{"class":2239},[2225,226785,226786],{"class":2763}," \u002F\u002F machine-readable discriminant\n",[2225,226788,226789],{"class":2227,"line":7180},[2225,226790,7351],{"class":2235},[937,226792,226793,226796,226797,968,226800,968,226803,1707,226806,968,226809,968,226812,968,226815,1707,226818,968,226821,968,226824,968,226827,1520],{},[941,226794,226795],{},"ShareLinkParseError.code"," is a discriminant rather than a message, so a portal can branch on the\nfailure instead of matching prose: ",[941,226798,226799],{},"MALFORMED_URL",[941,226801,226802],{},"UNKNOWN_PREFIX",[941,226804,226805],{},"MALFORMED_PATH",[941,226807,226808],{},"MALFORMED_SEGMENT",[941,226810,226811],{},"INVALID_VAULT_HANDLE",[941,226813,226814],{},"MALFORMED_QUERY",[941,226816,226817],{},"DUPLICATE_QUERY_KEY",[941,226819,226820],{},"UNKNOWN_QUERY_KEY",[941,226822,226823],{},"INVALID_VERSION",[941,226825,226826],{},"MALFORMED_FRAGMENT",[941,226828,226829],{},"INVALID_PARTS",[937,226831,226832,226833,226836,226837,226839],{},"Note ",[941,226834,226835],{},"vaultHandle"," — the opaque ULID from the ",[1007,226838,195650],{"href":630},", not the vault's\nname. Links therefore leak no tenant name at the URL layer.",[1022,226841,2621],{"id":2620},[1111,226843,226844,226851,226856],{},[1114,226845,226846,964,226848,226850],{},[1007,226847,39924],{"href":236},[941,226849,37649],{}," ingests a share link from the current location",[1114,226852,226853,226855],{},[1007,226854,15909],{"href":306}," — the same fragment-only transport rule",[1114,226857,226858,226860,226861,226863],{},[1007,226859,629],{"href":630}," — where the ",[941,226862,226835],{}," ULID comes from",[2645,226865,226866],{},"html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":1037,"searchDepth":1038,"depth":1038,"links":226868},[226869,226870,226871,226872],{"id":29856,"depth":1041,"text":29857},{"id":226529,"depth":1041,"text":226530},{"id":29892,"depth":1041,"text":29893},{"id":2620,"depth":1041,"text":2621},"The portal deep-link grammar — one canonical URL shape addressing vault \u002F period \u002F collection \u002F record, with the grant token confined to the fragment.",{},{"title":852,"description":226873},"4DiIjGwTOSuS3v6kpgtkVduTyF2cWRTcwGBC6z_LkG0",{"id":226878,"title":856,"access":12,"apiToc":929,"body":226879,"category":1042,"description":228858,"extension":1044,"meta":228859,"navigation":1042,"nest":1047,"path":857,"seo":228860,"sinceVersion":1042,"status":1049,"stem":858,"subtitle":1042,"tier":1050,"tocExternal":1042,"__hash__":228861},"docs\u002Fservices\u002Fsnapshots.md",{"type":931,"value":226880,"toc":228833},[226881,226883,226886,226888,226904,226944,226946,226974,226976,226985,227249,227262,227264,227268,227277,227348,227358,227362,227368,227401,227549,227552,227561,227646,227662,227666,227682,227834,227892,227926,227929,227948,228108,228131,228133,228139,228146,228207,228212,228215,228257,228267,228272,228275,228280,228287,228293,228409,228425,228430,228484,228501,228506,228614,228616,228692,228694,228709,228715,228717,228771,228773,228830],[934,226882,856],{"id":856},[46871,226884],{":rows":226885},"[{\"label\":\"Subpath\",\"value\":\"`@noy-db\u002Fhub\u002Fsnapshots`\"},{\"label\":\"Factory\",\"value\":\"`withSnapshots({ store })`\"},{\"label\":\"Layer\",\"value\":\"`with-fork`\"},{\"label\":\"Cluster\",\"value\":\"F — Snapshot & Portability\"},{\"label\":\"LOC cost\",\"value\":\"~200 (off-bundle when not opted in)\"}]",[1022,226887,29857],{"id":29856},[937,226889,3289,226890,226892,226893,16238,226895,226897,226898,226900,226901,226903],{},[950,226891,2694],{}," is noy-db's unit of encrypted storage — one logical database made up of\ncollections of records, each individually encrypted before it ever reaches a\n",[1007,226894,6951],{"href":51},[1007,226896,172018],{"href":48},").\nThe snapshots service adds point-in-time backup and restore for a whole vault:\n",[941,226899,21271],{}," writes a checkpoint you can come back to later with ",[941,226902,21277],{},",\nindependent of whatever store the vault normally reads and writes through day to day.",[937,226905,226906,226907,226909,226910,226913,226914,226916,226917,1261,226920,1261,226923,1261,226926,111681,226929,17904,226931,226933,226934,17904,226936,226938,226939,226941,226942,1520],{},"Each snapshot is a ",[941,226908,1402],{}," bundle — the same portable ",[1007,226911,226912],{"href":630},"encrypted container format","\nused for exports — written to a second, separate ",[941,226915,49533],{}," (any adapter implementing\n",[941,226918,226919],{},"readBundle",[941,226921,226922],{},"writeBundle",[941,226924,226925],{},"deleteBundle",[941,226927,226928],{},"listBundles",[941,226930,49601],{},[941,226932,49504],{},"\nor ",[941,226935,47116],{},[941,226937,29876],{},"). Because the bundle already holds ciphertext, the\nsnapshot destination never sees plaintext, and restoring re-uses the vault's existing\n",[950,226940,2705],{}," (its wrapped decryption keys) — no credentials need to be re-supplied. Snapshots\nare whole-vault only (no per-collection snapshot), and restoring is a plain overwrite, not a\nmerge — see ",[1007,226943,68210],{"href":126812},[1022,226945,70093],{"id":70092},[1111,226947,226948],{},[1114,226949,226950,964,226953,226955,226956,953,226958,1625,226960,1625,226962,226964,226965,17904,226967,4688,226969,17904,226971,226973],{},[950,226951,226952],{},"A bundle-capable store",[941,226954,21266],{}," writes checkpoints to a ",[941,226957,49533],{},[941,226959,226919],{},[941,226961,226922],{},[941,226963,226928],{},"), e.g. ",[941,226966,47116],{},[941,226968,29876],{},[941,226970,49601],{},[941,226972,49504],{},". A plain record store cannot receive snapshots.",[1022,226975,99864],{"id":99863},[937,226977,226978,226980,226981,10313,226983,2311],{},[941,226979,21266],{}," designates the bundle store snapshots are written to, and is\npassed as ",[941,226982,73387],{},[941,226984,12780],{},[2217,226986,226988],{"className":2219,"code":226987,"language":2221,"meta":1037,"style":1037},"import { createNoydb } from '@noy-db\u002Fhub'\nimport { withSnapshots } from '@noy-db\u002Fhub\u002Fsnapshots'\nimport { toMemory } from '@noy-db\u002Fto-memory'\nimport { s3Bundle } from '@noy-db\u002Fto-aws-s3'\n\nconst db = await createNoydb({\n  store: toMemory(),\n  user: 'owner',\n  secret: 'correct horse battery staple',\n  snapshotsStrategy: withSnapshots({\n    store: s3Bundle({ bucket: 'my-backups', prefix: 'noydb', region: 'us-east-1' }),\n    retention: { keepLast: 10 },   \u002F\u002F optional; default keeps every on-demand snapshot\n  }),\n})\n\nawait db.openVault('acct')\n",[941,226989,226990,227008,227027,227045,227063,227067,227083,227095,227109,227123,227136,227189,227210,227218,227224,227228],{"__ignoreMap":1037},[2225,226991,226992,226994,226996,226998,227000,227002,227004,227006],{"class":2227,"line":2228},[2225,226993,2232],{"class":2231},[2225,226995,2236],{"class":2235},[2225,226997,2744],{"class":2239},[2225,226999,2243],{"class":2235},[2225,227001,2246],{"class":2231},[2225,227003,2249],{"class":2235},[2225,227005,1086],{"class":2252},[2225,227007,2256],{"class":2235},[2225,227009,227010,227012,227014,227017,227019,227021,227023,227025],{"class":2227,"line":1041},[2225,227011,2232],{"class":2231},[2225,227013,2236],{"class":2235},[2225,227015,227016],{"class":2239}," withSnapshots",[2225,227018,2243],{"class":2235},[2225,227020,2246],{"class":2231},[2225,227022,2249],{"class":2235},[2225,227024,73390],{"class":2252},[2225,227026,2256],{"class":2235},[2225,227028,227029,227031,227033,227035,227037,227039,227041,227043],{"class":2227,"line":1038},[2225,227030,2232],{"class":2231},[2225,227032,2236],{"class":2235},[2225,227034,68576],{"class":2239},[2225,227036,2243],{"class":2235},[2225,227038,2246],{"class":2231},[2225,227040,2249],{"class":2235},[2225,227042,9638],{"class":2252},[2225,227044,2256],{"class":2235},[2225,227046,227047,227049,227051,227053,227055,227057,227059,227061],{"class":2227,"line":2288},[2225,227048,2232],{"class":2231},[2225,227050,2236],{"class":2235},[2225,227052,72118],{"class":2239},[2225,227054,2243],{"class":2235},[2225,227056,2246],{"class":2231},[2225,227058,2249],{"class":2235},[2225,227060,29876],{"class":2252},[2225,227062,2256],{"class":2235},[2225,227064,227065],{"class":2227,"line":2304},[2225,227066,2285],{"emptyLinePlaceholder":2284},[2225,227068,227069,227071,227073,227075,227077,227079,227081],{"class":2227,"line":2356},[2225,227070,2775],{"class":2774},[2225,227072,2778],{"class":2239},[2225,227074,2781],{"class":2235},[2225,227076,2784],{"class":2231},[2225,227078,2744],{"class":2291},[2225,227080,2789],{"class":2239},[2225,227082,4200],{"class":2235},[2225,227084,227085,227087,227089,227091,227093],{"class":2227,"line":2830},[2225,227086,15608],{"class":2307},[2225,227088,2311],{"class":2235},[2225,227090,68576],{"class":2291},[2225,227092,4228],{"class":2239},[2225,227094,1707],{"class":2235},[2225,227096,227097,227099,227101,227103,227105,227107],{"class":2227,"line":2861},[2225,227098,4205],{"class":2307},[2225,227100,2311],{"class":2235},[2225,227102,2249],{"class":2235},[2225,227104,9898],{"class":2252},[2225,227106,2805],{"class":2235},[2225,227108,1707],{"class":2235},[2225,227110,227111,227113,227115,227117,227119,227121],{"class":2227,"line":4246},[2225,227112,10423],{"class":2307},[2225,227114,2311],{"class":2235},[2225,227116,2249],{"class":2235},[2225,227118,40260],{"class":2252},[2225,227120,2805],{"class":2235},[2225,227122,1707],{"class":2235},[2225,227124,227125,227128,227130,227132,227134],{"class":2227,"line":4253},[2225,227126,227127],{"class":2307},"  snapshotsStrategy",[2225,227129,2311],{"class":2235},[2225,227131,227016],{"class":2291},[2225,227133,2789],{"class":2239},[2225,227135,4200],{"class":2235},[2225,227137,227138,227140,227142,227144,227146,227148,227150,227152,227154,227157,227159,227161,227163,227165,227167,227169,227171,227173,227175,227177,227179,227181,227183,227185,227187],{"class":2227,"line":4259},[2225,227139,85589],{"class":2307},[2225,227141,2311],{"class":2235},[2225,227143,72118],{"class":2291},[2225,227145,2789],{"class":2239},[2225,227147,2792],{"class":2235},[2225,227149,29939],{"class":2307},[2225,227151,2311],{"class":2235},[2225,227153,2249],{"class":2235},[2225,227155,227156],{"class":2252},"my-backups",[2225,227158,2805],{"class":2235},[2225,227160,2298],{"class":2235},[2225,227162,8638],{"class":2307},[2225,227164,2311],{"class":2235},[2225,227166,2249],{"class":2235},[2225,227168,149],{"class":2252},[2225,227170,2805],{"class":2235},[2225,227172,2298],{"class":2235},[2225,227174,29953],{"class":2307},[2225,227176,2311],{"class":2235},[2225,227178,2249],{"class":2235},[2225,227180,47049],{"class":2252},[2225,227182,2805],{"class":2235},[2225,227184,2243],{"class":2235},[2225,227186,1381],{"class":2239},[2225,227188,1707],{"class":2235},[2225,227190,227191,227194,227196,227198,227201,227203,227205,227207],{"class":2227,"line":6548},[2225,227192,227193],{"class":2307},"    retention",[2225,227195,2311],{"class":2235},[2225,227197,2236],{"class":2235},[2225,227199,227200],{"class":2307}," keepLast",[2225,227202,2311],{"class":2235},[2225,227204,45554],{"class":6527},[2225,227206,10965],{"class":2235},[2225,227208,227209],{"class":2763},"   \u002F\u002F optional; default keeps every on-demand snapshot\n",[2225,227211,227212,227214,227216],{"class":2227,"line":6576},[2225,227213,19143],{"class":2235},[2225,227215,1381],{"class":2239},[2225,227217,1707],{"class":2235},[2225,227219,227220,227222],{"class":2227,"line":6582},[2225,227221,2359],{"class":2235},[2225,227223,2362],{"class":2239},[2225,227225,227226],{"class":2227,"line":6587},[2225,227227,2285],{"emptyLinePlaceholder":2284},[2225,227229,227230,227232,227234,227236,227238,227240,227242,227245,227247],{"class":2227,"line":7174},[2225,227231,6500],{"class":2231},[2225,227233,2842],{"class":2239},[2225,227235,1520],{"class":2235},[2225,227237,2847],{"class":2291},[2225,227239,2789],{"class":2239},[2225,227241,2805],{"class":2235},[2225,227243,227244],{"class":2252},"acct",[2225,227246,2805],{"class":2235},[2225,227248,2362],{"class":2239},[937,227250,100279,227251,227253,227254,227258,227259,227261],{},[941,227252,49533],{}," works as the snapshot destination — see ",[1007,227255,227257],{"href":227256},"#s3-bundle-store","S3 bundle store","\nbelow for the one shipped in the extended ",[941,227260,1779],{}," family.",[1022,227263,29893],{"id":29892},[1591,227265,227267],{"id":227266},"take-a-checkpoint","Take a checkpoint",[937,227269,227270,227273,227274,227276],{},[941,227271,227272],{},"db.snapshot(vaultId, opts?)"," requires the vault to already be open (",[941,227275,1749],{},"), and\nreturns the new snapshot's metadata:",[2217,227278,227280],{"className":2219,"code":227279,"language":2221,"meta":1037,"style":1037},"const snap = await db.snapshot('acct', { label: 'before-year-close', note: 'FY2026' })\n\u002F\u002F snap.version   → 'acct__snap_000001'   (pass this to restoreSnapshot)\n\u002F\u002F snap.integrity → 'verified'\n",[941,227281,227282,227338,227343],{"__ignoreMap":1037},[2225,227283,227284,227286,227288,227290,227292,227294,227296,227298,227300,227302,227304,227306,227308,227310,227312,227314,227316,227319,227321,227323,227325,227327,227329,227332,227334,227336],{"class":2227,"line":2228},[2225,227285,2775],{"class":2774},[2225,227287,166159],{"class":2239},[2225,227289,2781],{"class":2235},[2225,227291,2784],{"class":2231},[2225,227293,2842],{"class":2239},[2225,227295,1520],{"class":2235},[2225,227297,8882],{"class":2291},[2225,227299,2789],{"class":2239},[2225,227301,2805],{"class":2235},[2225,227303,227244],{"class":2252},[2225,227305,2805],{"class":2235},[2225,227307,2298],{"class":2235},[2225,227309,2236],{"class":2235},[2225,227311,95892],{"class":2307},[2225,227313,2311],{"class":2235},[2225,227315,2249],{"class":2235},[2225,227317,227318],{"class":2252},"before-year-close",[2225,227320,2805],{"class":2235},[2225,227322,2298],{"class":2235},[2225,227324,193013],{"class":2307},[2225,227326,2311],{"class":2235},[2225,227328,2249],{"class":2235},[2225,227330,227331],{"class":2252},"FY2026",[2225,227333,2805],{"class":2235},[2225,227335,2243],{"class":2235},[2225,227337,2362],{"class":2239},[2225,227339,227340],{"class":2227,"line":1041},[2225,227341,227342],{"class":2763},"\u002F\u002F snap.version   → 'acct__snap_000001'   (pass this to restoreSnapshot)\n",[2225,227344,227345],{"class":2227,"line":1038},[2225,227346,227347],{"class":2763},"\u002F\u002F snap.integrity → 'verified'\n",[937,227349,227350,227353,227354,227357],{},[941,227351,227352],{},"opts.label"," is a human-readable name (shown in a snapshot chooser UI); ",[941,227355,227356],{},"opts.note"," is a\nfreeform memo. Both are optional.",[1591,227359,227361],{"id":227360},"list-snapshots","List snapshots",[937,227363,227364,227367],{},[941,227365,227366],{},"db.listSnapshots(vaultId)"," reads only the sidecar index — it never downloads snapshot\nbytes — and returns metadata newest-first:",[2217,227369,227371],{"className":2219,"code":227370,"language":2221,"meta":1037,"style":1037},"const snaps = await db.listSnapshots('acct')\n",[941,227372,227373],{"__ignoreMap":1037},[2225,227374,227375,227377,227380,227382,227384,227386,227388,227391,227393,227395,227397,227399],{"class":2227,"line":2228},[2225,227376,2775],{"class":2774},[2225,227378,227379],{"class":2239}," snaps ",[2225,227381,2781],{"class":2235},[2225,227383,2784],{"class":2231},[2225,227385,2842],{"class":2239},[2225,227387,1520],{"class":2235},[2225,227389,227390],{"class":2291},"listSnapshots",[2225,227392,2789],{"class":2239},[2225,227394,2805],{"class":2235},[2225,227396,227244],{"class":2252},[2225,227398,2805],{"class":2235},[2225,227400,2362],{"class":2239},[2217,227402,227404],{"className":6604,"code":227403,"language":141,"meta":1037,"style":1037},"[\n  {\n    \"version\": \"acct__snap_000001\",\n    \"label\": \"before-year-close\",\n    \"note\": \"FY2026\",\n    \"exportedAt\": \"2026-01-15T10:03:22.000Z\",\n    \"exportedBy\": \"owner\",\n    \"size\": 4821,\n    \"integrity\": \"verified\"\n  }\n]\n",[941,227405,227406,227410,227414,227433,227451,227470,227490,227509,227524,227541,227545],{"__ignoreMap":1037},[2225,227407,227408],{"class":2227,"line":2228},[2225,227409,26349],{"class":2235},[2225,227411,227412],{"class":2227,"line":1041},[2225,227413,59879],{"class":2235},[2225,227415,227416,227418,227420,227422,227424,227426,227429,227431],{"class":2227,"line":1038},[2225,227417,14201],{"class":2235},[2225,227419,52564],{"class":2774},[2225,227421,6620],{"class":2235},[2225,227423,2311],{"class":2235},[2225,227425,6614],{"class":2235},[2225,227427,227428],{"class":2252},"acct__snap_000001",[2225,227430,6620],{"class":2235},[2225,227432,1707],{"class":2235},[2225,227434,227435,227437,227439,227441,227443,227445,227447,227449],{"class":2227,"line":2288},[2225,227436,14201],{"class":2235},[2225,227438,96028],{"class":2774},[2225,227440,6620],{"class":2235},[2225,227442,2311],{"class":2235},[2225,227444,6614],{"class":2235},[2225,227446,227318],{"class":2252},[2225,227448,6620],{"class":2235},[2225,227450,1707],{"class":2235},[2225,227452,227453,227455,227458,227460,227462,227464,227466,227468],{"class":2227,"line":2304},[2225,227454,14201],{"class":2235},[2225,227456,227457],{"class":2774},"note",[2225,227459,6620],{"class":2235},[2225,227461,2311],{"class":2235},[2225,227463,6614],{"class":2235},[2225,227465,227331],{"class":2252},[2225,227467,6620],{"class":2235},[2225,227469,1707],{"class":2235},[2225,227471,227472,227474,227477,227479,227481,227483,227486,227488],{"class":2227,"line":2356},[2225,227473,14201],{"class":2235},[2225,227475,227476],{"class":2774},"exportedAt",[2225,227478,6620],{"class":2235},[2225,227480,2311],{"class":2235},[2225,227482,6614],{"class":2235},[2225,227484,227485],{"class":2252},"2026-01-15T10:03:22.000Z",[2225,227487,6620],{"class":2235},[2225,227489,1707],{"class":2235},[2225,227491,227492,227494,227497,227499,227501,227503,227505,227507],{"class":2227,"line":2830},[2225,227493,14201],{"class":2235},[2225,227495,227496],{"class":2774},"exportedBy",[2225,227498,6620],{"class":2235},[2225,227500,2311],{"class":2235},[2225,227502,6614],{"class":2235},[2225,227504,9898],{"class":2252},[2225,227506,6620],{"class":2235},[2225,227508,1707],{"class":2235},[2225,227510,227511,227513,227515,227517,227519,227522],{"class":2227,"line":2861},[2225,227512,14201],{"class":2235},[2225,227514,79350],{"class":2774},[2225,227516,6620],{"class":2235},[2225,227518,2311],{"class":2235},[2225,227520,227521],{"class":6527}," 4821",[2225,227523,1707],{"class":2235},[2225,227525,227526,227528,227531,227533,227535,227537,227539],{"class":2227,"line":4246},[2225,227527,14201],{"class":2235},[2225,227529,227530],{"class":2774},"integrity",[2225,227532,6620],{"class":2235},[2225,227534,2311],{"class":2235},[2225,227536,6614],{"class":2235},[2225,227538,196404],{"class":2252},[2225,227540,9449],{"class":2235},[2225,227542,227543],{"class":2227,"line":4253},[2225,227544,7632],{"class":2235},[2225,227546,227547],{"class":2227,"line":4259},[2225,227548,26484],{"class":2235},[1591,227550,227551],{"id":100920},"Restore",[937,227553,227554,227557,227558,227560],{},[941,227555,227556],{},"db.restoreSnapshot(vaultId, version)"," restores the vault in place and runs\n",[941,227559,22875],{}," automatically. Collection handles obtained before the call are\nnow stale — re-obtain them afterward:",[2217,227562,227564],{"className":2219,"code":227563,"language":2221,"meta":1037,"style":1037},"await db.restoreSnapshot('acct', snap.version)\n\nconst invoices = db.vault('acct').collection\u003CInvoice>('invoices')   \u002F\u002F re-obtain after restore\n",[941,227565,227566,227595,227599],{"__ignoreMap":1037},[2225,227567,227568,227570,227572,227574,227577,227579,227581,227583,227585,227587,227590,227592],{"class":2227,"line":2228},[2225,227569,6500],{"class":2231},[2225,227571,2842],{"class":2239},[2225,227573,1520],{"class":2235},[2225,227575,227576],{"class":2291},"restoreSnapshot",[2225,227578,2789],{"class":2239},[2225,227580,2805],{"class":2235},[2225,227582,227244],{"class":2252},[2225,227584,2805],{"class":2235},[2225,227586,2298],{"class":2235},[2225,227588,227589],{"class":2239}," snap",[2225,227591,1520],{"class":2235},[2225,227593,227594],{"class":2239},"version)\n",[2225,227596,227597],{"class":2227,"line":1041},[2225,227598,2285],{"emptyLinePlaceholder":2284},[2225,227600,227601,227603,227605,227607,227609,227611,227613,227615,227617,227619,227621,227623,227625,227627,227629,227631,227633,227635,227637,227639,227641,227643],{"class":2227,"line":1038},[2225,227602,2775],{"class":2774},[2225,227604,6461],{"class":2239},[2225,227606,2781],{"class":2235},[2225,227608,2842],{"class":2239},[2225,227610,1520],{"class":2235},[2225,227612,2694],{"class":2291},[2225,227614,2789],{"class":2239},[2225,227616,2805],{"class":2235},[2225,227618,227244],{"class":2252},[2225,227620,2805],{"class":2235},[2225,227622,1381],{"class":2239},[2225,227624,1520],{"class":2235},[2225,227626,6471],{"class":2291},[2225,227628,6474],{"class":2235},[2225,227630,6477],{"class":6342},[2225,227632,6480],{"class":2235},[2225,227634,2789],{"class":2239},[2225,227636,2805],{"class":2235},[2225,227638,6487],{"class":2252},[2225,227640,2805],{"class":2235},[2225,227642,71781],{"class":2239},[2225,227644,227645],{"class":2763},"\u002F\u002F re-obtain after restore\n",[937,227647,16810,227648,102958,227651,227653,227654,4688,227656,227658,227659,227661],{},[941,227649,227650],{},"SnapshotNotFoundError",[941,227652,52564],{}," doesn't exist in the snapshot store (pruned by\nretention, or a typo). Throws ",[941,227655,22939],{},[941,227657,22935],{}," if\n",[941,227660,22875],{}," detects tampering.",[1022,227663,227665],{"id":227664},"automatic-cadence","Automatic cadence",[937,227667,227668,227671,227672,227674,227675,227678,227679,227681],{},[941,227669,227670],{},"withSnapshots({ snapshotPolicy })"," enables automatic whole-vault snapshots driven by vault\nwrites, in addition to on-demand ",[941,227673,21271],{},". Automatic snapshots overwrite a single\nrolling key (",[941,227676,227677],{},"\u003CvaultId>__auto",") and are exempt from retention — the cadence timer can never\nevict your labeled on-demand checkpoints, and on-demand ",[941,227680,150072],{}," calls preserve the\nrolling slot untouched.",[2217,227683,227685],{"className":2219,"code":227684,"language":2221,"meta":1037,"style":1037},"const db = await createNoydb({\n  store,\n  user: 'owner',\n  secret: 'correct horse battery staple',\n  snapshotsStrategy: withSnapshots({\n    store: snapshotStore,\n    snapshotPolicy: { mode: 'debounce', debounceMs: 60_000, minIntervalMs: 300_000 },\n    retention: { keepLast: 10 },   \u002F\u002F applies to on-demand checkpoints only\n  }),\n})\n",[941,227686,227687,227703,227709,227723,227737,227749,227760,227801,227820,227828],{"__ignoreMap":1037},[2225,227688,227689,227691,227693,227695,227697,227699,227701],{"class":2227,"line":2228},[2225,227690,2775],{"class":2774},[2225,227692,2778],{"class":2239},[2225,227694,2781],{"class":2235},[2225,227696,2784],{"class":2231},[2225,227698,2744],{"class":2291},[2225,227700,2789],{"class":2239},[2225,227702,4200],{"class":2235},[2225,227704,227705,227707],{"class":2227,"line":1041},[2225,227706,15608],{"class":2239},[2225,227708,1707],{"class":2235},[2225,227710,227711,227713,227715,227717,227719,227721],{"class":2227,"line":1038},[2225,227712,4205],{"class":2307},[2225,227714,2311],{"class":2235},[2225,227716,2249],{"class":2235},[2225,227718,9898],{"class":2252},[2225,227720,2805],{"class":2235},[2225,227722,1707],{"class":2235},[2225,227724,227725,227727,227729,227731,227733,227735],{"class":2227,"line":2288},[2225,227726,10423],{"class":2307},[2225,227728,2311],{"class":2235},[2225,227730,2249],{"class":2235},[2225,227732,40260],{"class":2252},[2225,227734,2805],{"class":2235},[2225,227736,1707],{"class":2235},[2225,227738,227739,227741,227743,227745,227747],{"class":2227,"line":2304},[2225,227740,227127],{"class":2307},[2225,227742,2311],{"class":2235},[2225,227744,227016],{"class":2291},[2225,227746,2789],{"class":2239},[2225,227748,4200],{"class":2235},[2225,227750,227751,227753,227755,227758],{"class":2227,"line":2356},[2225,227752,85589],{"class":2307},[2225,227754,2311],{"class":2235},[2225,227756,227757],{"class":2239}," snapshotStore",[2225,227759,1707],{"class":2235},[2225,227761,227762,227765,227767,227769,227771,227773,227775,227778,227780,227782,227785,227787,227789,227791,227794,227796,227799],{"class":2227,"line":2830},[2225,227763,227764],{"class":2307},"    snapshotPolicy",[2225,227766,2311],{"class":2235},[2225,227768,2236],{"class":2235},[2225,227770,32303],{"class":2307},[2225,227772,2311],{"class":2235},[2225,227774,2249],{"class":2235},[2225,227776,227777],{"class":2252},"debounce",[2225,227779,2805],{"class":2235},[2225,227781,2298],{"class":2235},[2225,227783,227784],{"class":2307}," debounceMs",[2225,227786,2311],{"class":2235},[2225,227788,19001],{"class":6527},[2225,227790,2298],{"class":2235},[2225,227792,227793],{"class":2307}," minIntervalMs",[2225,227795,2311],{"class":2235},[2225,227797,227798],{"class":6527}," 300_000",[2225,227800,2353],{"class":2235},[2225,227802,227803,227805,227807,227809,227811,227813,227815,227817],{"class":2227,"line":2861},[2225,227804,227193],{"class":2307},[2225,227806,2311],{"class":2235},[2225,227808,2236],{"class":2235},[2225,227810,227200],{"class":2307},[2225,227812,2311],{"class":2235},[2225,227814,45554],{"class":6527},[2225,227816,10965],{"class":2235},[2225,227818,227819],{"class":2763},"   \u002F\u002F applies to on-demand checkpoints only\n",[2225,227821,227822,227824,227826],{"class":2227,"line":4246},[2225,227823,19143],{"class":2235},[2225,227825,1381],{"class":2239},[2225,227827,1707],{"class":2235},[2225,227829,227830,227832],{"class":2227,"line":4253},[2225,227831,2359],{"class":2235},[2225,227833,2362],{"class":2239},[1338,227835,227836,227847],{},[1341,227837,227838],{},[1344,227839,227840,227844],{},[1347,227841,227842],{},[941,227843,120875],{},[1347,227845,227846],{},"Trigger",[1357,227848,227849,227860,227878],{},[1344,227850,227851,227855],{},[1362,227852,227853,91937],{},[941,227854,92133],{},[1362,227856,227857,227858,1293],{},"No timers — ",[941,227859,21271],{},[1344,227861,227862,227867],{},[1362,227863,227864],{},[941,227865,227866],{},"'debounce'",[1362,227868,227869,227870,227873,227874,227877],{},"Fires after ",[941,227871,227872],{},"debounceMs"," of write-idle, with a ",[941,227875,227876],{},"minIntervalMs"," floor between fires.",[1344,227879,227880,227885],{},[1362,227881,227882],{},[941,227883,227884],{},"'interval'",[1362,227886,227887,227888,227891],{},"Fires on a fixed ",[941,227889,227890],{},"intervalMs"," timer.",[937,227893,227894,30200,227897,227899,227900,227903,227904,227907,227908,227911,227912,227915,227916,227919,227920,227922,227923,227925],{},[941,227895,227896],{},"onUnload",[941,227898,11790],{}," for non-manual modes) flushes a pending auto-snapshot on tab-hide\nor process exit. The auto-snapshot appears first in ",[941,227901,227902],{},"listSnapshots()"," (flagged ",[941,227905,227906],{},"auto: true",")\nand restores like any other: ",[941,227909,227910],{},"db.restoreSnapshot(vaultId, '\u003CvaultId>__auto')",". The cadence is\nwired off the instance's post-write hook: a write marks its vault dirty, the scheduler fires\n",[941,227913,227914],{},"autoSnapshot()"," per dirty vault, and any error (or an optimistic-concurrency conflict) during\nthat call is logged, never thrown — the vault stays marked dirty so the next cadence tick\n(",[941,227917,227918],{},"interval",") or the next write (",[941,227921,227777],{},") retries. ",[941,227924,13331],{}," tears the scheduler down.",[1022,227927,227257],{"id":227928},"s3-bundle-store",[937,227930,227931,227933,227934,227936,227937,119346,227939,227941,227942,227944,227945,227947],{},[941,227932,29876],{}," (from the extended ",[941,227935,1779],{}," adapter family) ships ",[941,227938,47116],{},[941,227940,49533],{}," for whole-vault ",[941,227943,1402],{}," blobs, distinct from the per-record ",[941,227946,49160],{}," adapter,\nsuitable as a snapshot destination:",[2217,227949,227951],{"className":2219,"code":227950,"language":2221,"meta":1037,"style":1037},"import { s3Bundle } from '@noy-db\u002Fto-aws-s3'\n\nconst snapshotStore = s3Bundle({ bucket: 'my-backups', prefix: 'noydb', region: 'us-east-1' })\nconst db = await createNoydb({\n  store,\n  user: 'owner',\n  secret: 'correct horse battery staple',\n  snapshotsStrategy: withSnapshots({ store: snapshotStore }),\n})\n",[941,227952,227953,227971,227975,228028,228044,228050,228064,228078,228102],{"__ignoreMap":1037},[2225,227954,227955,227957,227959,227961,227963,227965,227967,227969],{"class":2227,"line":2228},[2225,227956,2232],{"class":2231},[2225,227958,2236],{"class":2235},[2225,227960,72118],{"class":2239},[2225,227962,2243],{"class":2235},[2225,227964,2246],{"class":2231},[2225,227966,2249],{"class":2235},[2225,227968,29876],{"class":2252},[2225,227970,2256],{"class":2235},[2225,227972,227973],{"class":2227,"line":1041},[2225,227974,2285],{"emptyLinePlaceholder":2284},[2225,227976,227977,227979,227982,227984,227986,227988,227990,227992,227994,227996,227998,228000,228002,228004,228006,228008,228010,228012,228014,228016,228018,228020,228022,228024,228026],{"class":2227,"line":1038},[2225,227978,2775],{"class":2774},[2225,227980,227981],{"class":2239}," snapshotStore ",[2225,227983,2781],{"class":2235},[2225,227985,72118],{"class":2291},[2225,227987,2789],{"class":2239},[2225,227989,2792],{"class":2235},[2225,227991,29939],{"class":2307},[2225,227993,2311],{"class":2235},[2225,227995,2249],{"class":2235},[2225,227997,227156],{"class":2252},[2225,227999,2805],{"class":2235},[2225,228001,2298],{"class":2235},[2225,228003,8638],{"class":2307},[2225,228005,2311],{"class":2235},[2225,228007,2249],{"class":2235},[2225,228009,149],{"class":2252},[2225,228011,2805],{"class":2235},[2225,228013,2298],{"class":2235},[2225,228015,29953],{"class":2307},[2225,228017,2311],{"class":2235},[2225,228019,2249],{"class":2235},[2225,228021,47049],{"class":2252},[2225,228023,2805],{"class":2235},[2225,228025,2243],{"class":2235},[2225,228027,2362],{"class":2239},[2225,228029,228030,228032,228034,228036,228038,228040,228042],{"class":2227,"line":2288},[2225,228031,2775],{"class":2774},[2225,228033,2778],{"class":2239},[2225,228035,2781],{"class":2235},[2225,228037,2784],{"class":2231},[2225,228039,2744],{"class":2291},[2225,228041,2789],{"class":2239},[2225,228043,4200],{"class":2235},[2225,228045,228046,228048],{"class":2227,"line":2304},[2225,228047,15608],{"class":2239},[2225,228049,1707],{"class":2235},[2225,228051,228052,228054,228056,228058,228060,228062],{"class":2227,"line":2356},[2225,228053,4205],{"class":2307},[2225,228055,2311],{"class":2235},[2225,228057,2249],{"class":2235},[2225,228059,9898],{"class":2252},[2225,228061,2805],{"class":2235},[2225,228063,1707],{"class":2235},[2225,228065,228066,228068,228070,228072,228074,228076],{"class":2227,"line":2830},[2225,228067,10423],{"class":2307},[2225,228069,2311],{"class":2235},[2225,228071,2249],{"class":2235},[2225,228073,40260],{"class":2252},[2225,228075,2805],{"class":2235},[2225,228077,1707],{"class":2235},[2225,228079,228080,228082,228084,228086,228088,228090,228092,228094,228096,228098,228100],{"class":2227,"line":2861},[2225,228081,227127],{"class":2307},[2225,228083,2311],{"class":2235},[2225,228085,227016],{"class":2291},[2225,228087,2789],{"class":2239},[2225,228089,2792],{"class":2235},[2225,228091,7564],{"class":2307},[2225,228093,2311],{"class":2235},[2225,228095,227981],{"class":2239},[2225,228097,2359],{"class":2235},[2225,228099,1381],{"class":2239},[2225,228101,1707],{"class":2235},[2225,228103,228104,228106],{"class":2227,"line":4246},[2225,228105,2359],{"class":2235},[2225,228107,2362],{"class":2239},[937,228109,228110,228111,228114,228115,228117,228118,38633,228120,228123,228124,228127,228128,228130],{},"Key scheme is ",[941,228112,228113],{},"{prefix}\u002F{vaultId}.noydb","; the version token is the object's S3 ETag. OCC uses\nS3 conditional writes (",[941,228116,9732],{}," on the ETag) — a lost race throws ",[941,228119,49924],{},[941,228121,228122],{},"listBundles()"," derives metadata from paginated ",[941,228125,228126],{},"ListObjectsV2"," calls (no per-object GET).\nRequires ",[941,228129,30131],{}," ≥ 3.696.",[1022,228132,102889],{"id":102888},[1591,228134,228136],{"id":228135},"withsnapshotsopts",[941,228137,228138],{},"withSnapshots(opts)",[937,228140,228141,228142,10313,228144,1520],{},"Enables the snapshots service. Pass the result as ",[941,228143,73387],{},[941,228145,12780],{},[1338,228147,228148,228158],{},[1341,228149,228150],{},[1344,228151,228152,228154,228156],{},[1347,228153,101060],{},[1347,228155,20194],{},[1347,228157,7834],{},[1357,228159,228160,228173,228188],{},[1344,228161,228162,228166,228170],{},[1362,228163,228164],{},[941,228165,6951],{},[1362,228167,228168],{},[941,228169,49533],{},[1362,228171,228172],{},"Where snapshot blobs and the sidecar index are written. Required.",[1344,228174,228175,228180,228185],{},[1362,228176,228177],{},[941,228178,228179],{},"retention",[1362,228181,228182],{},[941,228183,228184],{},"RetentionPolicy",[1362,228186,228187],{},"Pruning for on-demand snapshots. Default: keep all. Never affects the auto-snapshot slot.",[1344,228189,228190,228195,228200],{},[1362,228191,228192],{},[941,228193,228194],{},"snapshotPolicy",[1362,228196,228197],{},[941,228198,228199],{},"SnapshotPolicy",[1362,228201,228202,228203,228206],{},"Automatic-snapshot cadence. Default ",[941,228204,228205],{},"mode: 'manual'"," — no timers.",[1591,228208,228210],{"id":228209},"dbsnapshotvaultid-opts",[941,228211,227272],{},[937,228213,228214],{},"Takes an on-demand checkpoint of the given vault.",[1338,228216,228217,228229],{},[1341,228218,228219],{},[1344,228220,228221,228225,228227],{},[1347,228222,228223],{},[941,228224,8097],{},[1347,228226,20194],{},[1347,228228,7834],{},[1357,228230,228231,228244],{},[1344,228232,228233,228237,228241],{},[1362,228234,228235],{},[941,228236,96028],{},[1362,228238,228239],{},[941,228240,7259],{},[1362,228242,228243],{},"Human-readable name, e.g. for a chooser UI.",[1344,228245,228246,228250,228254],{},[1362,228247,228248],{},[941,228249,227457],{},[1362,228251,228252],{},[941,228253,7259],{},[1362,228255,228256],{},"Freeform memo.",[937,228258,179992,228259,102958,228261,228263,228264,228266],{},[941,228260,12126],{},[941,228262,73387],{}," isn't configured, or ",[941,228265,12881],{}," if the\nvault isn't open.",[1591,228268,228270],{"id":228269},"dblistsnapshotsvaultid",[941,228271,227366],{},[937,228273,228274],{},"Lists snapshots newest-first (auto-snapshot first, if present), from the sidecar index only.",[1591,228276,228278],{"id":228277},"dbrestoresnapshotvaultid-version",[941,228279,227556],{},[937,228281,228282,228283,228286],{},"Restores the vault to a prior snapshot in place. See ",[1007,228284,227551],{"href":228285},"#restore"," above for errors.",[1591,228288,228290],{"id":228289},"snapshotmeta",[941,228291,228292],{},"SnapshotMeta",[2217,228294,228296],{"className":2219,"code":228295,"language":2221,"meta":1037,"style":1037},"interface SnapshotMeta {\n  version: string          \u002F\u002F lookup key; pass to restoreSnapshot()\n  label?: string\n  note?: string\n  exportedAt: string       \u002F\u002F ISO 8601\n  exportedBy: string       \u002F\u002F the acting user (NoydbOptions.user) at snapshot time\n  size: number              \u002F\u002F bytes\n  integrity: 'verified' | 'legacy-unverifiable'\n  auto?: true               \u002F\u002F present only on the rolling auto-snapshot\n}\n",[941,228297,228298,228307,228318,228326,228335,228347,228359,228371,228393,228405],{"__ignoreMap":1037},[2225,228299,228300,228302,228305],{"class":2227,"line":2228},[2225,228301,6994],{"class":2774},[2225,228303,228304],{"class":6342}," SnapshotMeta",[2225,228306,2301],{"class":2235},[2225,228308,228309,228311,228313,228315],{"class":2227,"line":1041},[2225,228310,158056],{"class":2307},[2225,228312,2311],{"class":2235},[2225,228314,6366],{"class":6342},[2225,228316,228317],{"class":2763},"          \u002F\u002F lookup key; pass to restoreSnapshot()\n",[2225,228319,228320,228322,228324],{"class":2227,"line":1038},[2225,228321,82578],{"class":2307},[2225,228323,7011],{"class":2235},[2225,228325,7014],{"class":6342},[2225,228327,228328,228331,228333],{"class":2227,"line":2288},[2225,228329,228330],{"class":2307},"  note",[2225,228332,7011],{"class":2235},[2225,228334,7014],{"class":6342},[2225,228336,228337,228340,228342,228344],{"class":2227,"line":2304},[2225,228338,228339],{"class":2307},"  exportedAt",[2225,228341,2311],{"class":2235},[2225,228343,6366],{"class":6342},[2225,228345,228346],{"class":2763},"       \u002F\u002F ISO 8601\n",[2225,228348,228349,228352,228354,228356],{"class":2227,"line":2356},[2225,228350,228351],{"class":2307},"  exportedBy",[2225,228353,2311],{"class":2235},[2225,228355,6366],{"class":6342},[2225,228357,228358],{"class":2763},"       \u002F\u002F the acting user (NoydbOptions.user) at snapshot time\n",[2225,228360,228361,228364,228366,228368],{"class":2227,"line":2830},[2225,228362,228363],{"class":2307},"  size",[2225,228365,2311],{"class":2235},[2225,228367,6356],{"class":6342},[2225,228369,228370],{"class":2763},"              \u002F\u002F bytes\n",[2225,228372,228373,228376,228378,228380,228382,228384,228386,228388,228391],{"class":2227,"line":2861},[2225,228374,228375],{"class":2307},"  integrity",[2225,228377,2311],{"class":2235},[2225,228379,2249],{"class":2235},[2225,228381,196404],{"class":2252},[2225,228383,2805],{"class":2235},[2225,228385,7087],{"class":2235},[2225,228387,2249],{"class":2235},[2225,228389,228390],{"class":2252},"legacy-unverifiable",[2225,228392,2256],{"class":2235},[2225,228394,228395,228398,228400,228402],{"class":2227,"line":4246},[2225,228396,228397],{"class":2307},"  auto",[2225,228399,7011],{"class":2235},[2225,228401,2322],{"class":6342},[2225,228403,228404],{"class":2763},"               \u002F\u002F present only on the rolling auto-snapshot\n",[2225,228406,228407],{"class":2227,"line":4253},[2225,228408,7351],{"class":2235},[937,228410,228411,7356,228413,228416,228417,228420,228421,228424],{},[941,228412,227530],{},[941,228414,228415],{},"'verified'"," for every snapshot the current engine produces (the bundle embeds\nledger metadata that ",[941,228418,228419],{},"restoreSnapshot()"," checks); ",[941,228422,228423],{},"'legacy-unverifiable'"," is reserved for a\nfuture import path that reads pre-existing bundles with no ledger to verify against.",[1591,228426,228428],{"id":228427},"retentionpolicy",[941,228429,228184],{},[2217,228431,228433],{"className":2219,"code":228432,"language":2221,"meta":1037,"style":1037},"interface RetentionPolicy {\n  keepLast?: number     \u002F\u002F keep only the most recent N on-demand snapshots per vault\n  maxAgeDays?: number   \u002F\u002F delete on-demand snapshots older than N days\n  prune?: boolean       \u002F\u002F false → never delete; delegate expiry to store-level infra. Default true.\n}\n",[941,228434,228435,228444,228456,228468,228480],{"__ignoreMap":1037},[2225,228436,228437,228439,228442],{"class":2227,"line":2228},[2225,228438,6994],{"class":2774},[2225,228440,228441],{"class":6342}," RetentionPolicy",[2225,228443,2301],{"class":2235},[2225,228445,228446,228449,228451,228453],{"class":2227,"line":1041},[2225,228447,228448],{"class":2307},"  keepLast",[2225,228450,7011],{"class":2235},[2225,228452,6356],{"class":6342},[2225,228454,228455],{"class":2763},"     \u002F\u002F keep only the most recent N on-demand snapshots per vault\n",[2225,228457,228458,228461,228463,228465],{"class":2227,"line":1038},[2225,228459,228460],{"class":2307},"  maxAgeDays",[2225,228462,7011],{"class":2235},[2225,228464,6356],{"class":6342},[2225,228466,228467],{"class":2763},"   \u002F\u002F delete on-demand snapshots older than N days\n",[2225,228469,228470,228473,228475,228477],{"class":2227,"line":2288},[2225,228471,228472],{"class":2307},"  prune",[2225,228474,7011],{"class":2235},[2225,228476,7723],{"class":6342},[2225,228478,228479],{"class":2763},"       \u002F\u002F false → never delete; delegate expiry to store-level infra. Default true.\n",[2225,228481,228482],{"class":2227,"line":2304},[2225,228483,7351],{"class":2235},[937,228485,228486,228487,228489,228490,997,228493,228496,228497,228500],{},"Applied eagerly after each ",[941,228488,21271],{}," call. ",[941,228491,228492],{},"keepLast",[941,228494,228495],{},"maxAgeDays"," can be combined.\nSet ",[941,228498,228499],{},"prune: false"," to use S3 lifecycle rules or similar infra-level expiry instead.",[1591,228502,228504],{"id":228503},"snapshotpolicy",[941,228505,228199],{},[2217,228507,228509],{"className":2219,"code":228508,"language":2221,"meta":1037,"style":1037},"interface SnapshotPolicy {\n  mode?: 'manual' | 'debounce' | 'interval'   \u002F\u002F default 'manual'\n  debounceMs?: number     \u002F\u002F idle delay before firing, mode:'debounce'. Default 30_000.\n  intervalMs?: number     \u002F\u002F fixed timer period, mode:'interval'. Default 300_000.\n  minIntervalMs?: number  \u002F\u002F hard floor between auto-snapshots, any mode. Default 0.\n  onUnload?: boolean       \u002F\u002F flush a pending auto-snapshot on tab-hide\u002Fexit. Default true for non-manual modes.\n  label?: string           \u002F\u002F label applied to auto-snapshots. Default 'auto'.\n}\n",[941,228510,228511,228520,228551,228563,228575,228587,228599,228610],{"__ignoreMap":1037},[2225,228512,228513,228515,228518],{"class":2227,"line":2228},[2225,228514,6994],{"class":2774},[2225,228516,228517],{"class":6342}," SnapshotPolicy",[2225,228519,2301],{"class":2235},[2225,228521,228522,228524,228526,228528,228530,228532,228534,228536,228538,228540,228542,228544,228546,228548],{"class":2227,"line":1041},[2225,228523,145557],{"class":2307},[2225,228525,7011],{"class":2235},[2225,228527,2249],{"class":2235},[2225,228529,180353],{"class":2252},[2225,228531,2805],{"class":2235},[2225,228533,7087],{"class":2235},[2225,228535,2249],{"class":2235},[2225,228537,227777],{"class":2252},[2225,228539,2805],{"class":2235},[2225,228541,7087],{"class":2235},[2225,228543,2249],{"class":2235},[2225,228545,227918],{"class":2252},[2225,228547,2805],{"class":2235},[2225,228549,228550],{"class":2763},"   \u002F\u002F default 'manual'\n",[2225,228552,228553,228556,228558,228560],{"class":2227,"line":1038},[2225,228554,228555],{"class":2307},"  debounceMs",[2225,228557,7011],{"class":2235},[2225,228559,6356],{"class":6342},[2225,228561,228562],{"class":2763},"     \u002F\u002F idle delay before firing, mode:'debounce'. Default 30_000.\n",[2225,228564,228565,228568,228570,228572],{"class":2227,"line":2288},[2225,228566,228567],{"class":2307},"  intervalMs",[2225,228569,7011],{"class":2235},[2225,228571,6356],{"class":6342},[2225,228573,228574],{"class":2763},"     \u002F\u002F fixed timer period, mode:'interval'. Default 300_000.\n",[2225,228576,228577,228580,228582,228584],{"class":2227,"line":2304},[2225,228578,228579],{"class":2307},"  minIntervalMs",[2225,228581,7011],{"class":2235},[2225,228583,6356],{"class":6342},[2225,228585,228586],{"class":2763},"  \u002F\u002F hard floor between auto-snapshots, any mode. Default 0.\n",[2225,228588,228589,228592,228594,228596],{"class":2227,"line":2356},[2225,228590,228591],{"class":2307},"  onUnload",[2225,228593,7011],{"class":2235},[2225,228595,7723],{"class":6342},[2225,228597,228598],{"class":2763},"       \u002F\u002F flush a pending auto-snapshot on tab-hide\u002Fexit. Default true for non-manual modes.\n",[2225,228600,228601,228603,228605,228607],{"class":2227,"line":2830},[2225,228602,82578],{"class":2307},[2225,228604,7011],{"class":2235},[2225,228606,6366],{"class":6342},[2225,228608,228609],{"class":2763},"           \u002F\u002F label applied to auto-snapshots. Default 'auto'.\n",[2225,228611,228612],{"class":2227,"line":2861},[2225,228613,7351],{"class":2235},[1591,228615,120265],{"id":120264},[1338,228617,228618,228629],{},[1341,228619,228620],{},[1344,228621,228622,228625,228627],{},[1347,228623,228624],{},"Error class",[1347,228626,223542],{},[1347,228628,120276],{},[1357,228630,228631,228647,228661,228675],{},[1344,228632,228633,228637,228642],{},[1362,228634,228635],{},[941,228636,227650],{},[1362,228638,228639],{},[941,228640,228641],{},"SNAPSHOT_NOT_FOUND",[1362,228643,228644,228646],{},[941,228645,52564],{}," not in the snapshot store (pruned or invalid).",[1344,228648,228649,228653,228658],{},[1362,228650,228651],{},[941,228652,22939],{},[1362,228654,228655],{},[941,228656,228657],{},"BACKUP_CORRUPTED",[1362,228659,228660],{},"An envelope's hash doesn't match the ledger on restore (tamper detected).",[1344,228662,228663,228667,228672],{},[1362,228664,228665],{},[941,228666,22935],{},[1362,228668,228669],{},[941,228670,228671],{},"BACKUP_LEDGER",[1362,228673,228674],{},"The hash chain, or its head, doesn't verify on restore (tamper detected).",[1344,228676,228677,228681,228686],{},[1362,228678,228679],{},[941,228680,49924],{},[1362,228682,228683],{},[941,228684,228685],{},"BUNDLE_VERSION_CONFLICT",[1362,228687,228688,228689,228691],{},"A snapshot or index write lost an optimistic-concurrency race (e.g. S3 ",[941,228690,9732],{}," 412).",[1022,228693,101425],{"id":101424},[937,228695,90416,228696,968,228699,968,228701,2423,228703,228705,228706,228708],{},[941,228697,228698],{},"snapshotsStrategy: withSnapshots({ store })",[941,228700,21271],{},[941,228702,21274],{},[941,228704,21277],{}," all reject with an ",[941,228707,12126],{}," pointing at the fix:",[2217,228710,228713],{"className":228711,"code":228712,"language":5672},[5670],"Snapshots require the snapshot strategy. Import `{ withSnapshots }` from\n\"@noy-db\u002Fhub\u002Fsnapshots\" and pass it to\n`createNoydb({ snapshotsStrategy: withSnapshots({ store }) })`.\n",[941,228714,228712],{"__ignoreMap":1037},[1022,228716,68210],{"id":68209},[1111,228718,228719,228727,228733,228744,228760],{},[1114,228720,228721,228726],{},[950,228722,228723,228724],{},"Not a replacement for ",[1007,228725,697],{"href":698}," (per-record, intra-vault\npoint-in-time). Snapshots are external, whole-vault checkpoints.",[1114,228728,228729,228732],{},[950,228730,228731],{},"Whole-vault only"," — there is no per-collection snapshot.",[1114,228734,228735,1324,228738,228740,228741,228743],{},[950,228736,228737],{},"Restore is an overwrite, not a merge.",[941,228739,228419],{}," replaces vault state in place\nvia ",[941,228742,116579],{},"; the app is responsible for checking unsaved state before calling it.",[1114,228745,228746,228749,228750,228753,228754,228756,228757,228759],{},[950,228747,228748],{},"Single-writer safe by design, not by lock."," The on-demand snapshot counter\n(",[941,228751,228752],{},"__snap_N",") is derived from an index read that isn't cross-process-locked, so two\n",[941,228755,150072],{}," calls racing on the same vault can compute the same blob key and the losing\nwrite silently overwrites the winning one's bytes. Multi-writer deployments (server\nworkers, etc.) should serialize ",[941,228758,150072],{}," calls at the application layer.",[1114,228761,228762,228767,228768,228770],{},[950,228763,228764,228766],{},[941,228765,228179],{}," never touches the auto-snapshot slot"," — it prunes only the on-demand\n",[941,228769,856],{}," pool, so the cadence timer can't evict a labeled checkpoint and a labeled\ncheckpoint can't evict the rolling auto slot.",[1022,228772,2621],{"id":2620},[1111,228774,228775,228781,228788,228793,228798,228806,228816],{},[1114,228776,228777,1368,228779],{},[1007,228778,61743],{"href":598},[1007,228780,1019],{"href":1018},[1114,228782,228783,1169,228785,228787],{},[1007,228784,629],{"href":630},[941,228786,1402],{}," container format snapshots are written in",[1114,228789,228790,228792],{},[1007,228791,697],{"href":698}," — per-record, intra-vault point-in-time, the service snapshots complements rather than replaces",[1114,228794,228795,228797],{},[1007,228796,605],{"href":606}," — relocates cold records to a second store instead of checkpointing the whole vault; a different cold-storage tradeoff than a snapshot",[1114,228799,228800,228802,228803,228805],{},[1007,228801,16076],{"href":51}," — the store contract; ",[941,228804,49533],{}," is its whole-vault counterpart",[1114,228807,228808,964,228811,228813,228814],{},[1007,228809,228810],{"href":342},"families\u002Fto",[941,228812,1779],{}," adapters that implement ",[941,228815,49533],{},[1114,228817,141161,228818,997,228824,1520],{},[1007,228819,228821],{"href":93757,"rel":228820},[3371],[941,228822,228823],{},"93-with-snapshots",[1007,228825,228827],{"href":93808,"rel":228826},[3371],[941,228828,228829],{},"96-snapshots-auto-cadence",[2645,228831,228832],{},"html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}",{"title":1037,"searchDepth":1038,"depth":1038,"links":228834},[228835,228836,228837,228838,228843,228844,228845,228855,228856,228857],{"id":29856,"depth":1041,"text":29857},{"id":70092,"depth":1041,"text":70093},{"id":99863,"depth":1041,"text":99864},{"id":29892,"depth":1041,"text":29893,"children":228839},[228840,228841,228842],{"id":227266,"depth":1038,"text":227267},{"id":227360,"depth":1038,"text":227361},{"id":100920,"depth":1038,"text":227551},{"id":227664,"depth":1041,"text":227665},{"id":227928,"depth":1041,"text":227257},{"id":102888,"depth":1041,"text":102889,"children":228846},[228847,228848,228849,228850,228851,228852,228853,228854],{"id":228135,"depth":1038,"text":228138},{"id":228209,"depth":1038,"text":227272},{"id":228269,"depth":1038,"text":227366},{"id":228277,"depth":1038,"text":227556},{"id":228289,"depth":1038,"text":228292},{"id":228427,"depth":1038,"text":228184},{"id":228503,"depth":1038,"text":228199},{"id":120264,"depth":1038,"text":120265},{"id":101424,"depth":1041,"text":101425},{"id":68209,"depth":1041,"text":68210},{"id":2620,"depth":1041,"text":2621},"Point-in-time backup and restore: db.snapshot() writes an encrypted checkpoint to a separate bundle store; restoreSnapshot() restores in place.",{},{"title":856,"description":228858},"va6cOv_ztEZuCzNCA9qZUYEyFnszWBkMEMga__t2qmQ",{"id":228863,"title":860,"access":12,"apiToc":929,"body":228864,"category":1042,"description":229586,"extension":1044,"meta":229587,"navigation":1042,"nest":1047,"path":861,"seo":229588,"sinceVersion":1042,"status":1049,"stem":862,"subtitle":1042,"tier":1050,"tocExternal":1042,"__hash__":229589},"docs\u002Fservices\u002Fstorage-capability-matrix.md",{"type":931,"value":228865,"toc":229583},[228866,228869,228898,228970,229008,229029,229060,229549,229555,229557],[934,228867,860],{"id":228868},"store-capabilities",[937,228870,228871,228872,228876,228877,953,228880,228883,228884,228887,228888,228890,228891,228894,228895,228897],{},"Consult this page when choosing a store: it answers which store exposes which\nshape, whether concurrent same-record writes are CAS-protected, and whether\n",[1007,228873,228874],{"href":877},[941,228875,47889],{}," can commit through one atomic\nstore-level batch. Generated from ",[941,228878,228879],{},"features.yaml",[941,228881,228882],{},"adapters[].capabilities",",\nthe 3 essential stores) and ",[941,228885,228886],{},"registry\u002Fgenerated\u002Fto-capabilities.json","\n(the 19 extended ",[941,228889,5351],{}," stores, itself generated by\n",[941,228892,228893],{},"registry\u002Fscan-to-capabilities.mjs"," statically scanning the ",[941,228896,5351],{},"\nsource — never hand-typed).",[937,228899,228900,228903,228904,228910,228911,228914,228915,5502,228918,17904,228920,228923,228924,228927,228928,968,228930,2462,228932,228935,228936,228938,228939,228941,228942,228944,228945,228947,228948,228952,228953,1625,228956,228958,228959,228961,228962,228964,228965,228967,228968,1520],{},[950,228901,228902],{},"Scope:"," this matrix covers all ",[950,228905,228906,228907,228909],{},"22 ",[941,228908,1779],{}," stores across the family"," — the\n",[950,228912,228913],{},"3 essential stores"," (2 ship with noy-db itself, ",[941,228916,228917],{},"@noy-db core",[941,228919,5125],{},[941,228921,228922],{},"noy-db-in","), and the\n",[950,228925,228926],{},"19 extended stores"," — cloud, SQL, server, and remote-filesystem backends such as\n",[941,228929,5355],{},[941,228931,5358],{},[941,228933,228934],{},"to-supabase"," — that ship from the separate\n",[941,228937,5351],{}," repository under the same ",[941,228940,5129],{}," scope, binding the same\n",[941,228943,3299],{}," contract. The ",[950,228946,34564],{}," column names which repo ships each\nrow; browse the full catalog at the ",[1007,228949,228950,5141],{"href":115},[941,228951,1779],{},". Extended\nrows show a ",[941,228954,228955],{},"Use case",[941,228957,48731],{}," tag only where ",[941,228960,228879],{}," still carries an\nentry for them — today just ",[941,228963,2992],{},", which kept its entry when it relocated\nout of core; ",[941,228966,5351],{}," tracks neither field, so the rest show ",[941,228969,3610],{},[1111,228971,228972,228988],{},[1114,228973,228974,228976,228977,2085,228979,1261,228981,1261,228983,1261,228985,228987],{},[950,228975,24158],{}," — per-record key-value store (",[941,228978,3154],{},[941,228980,3158],{},[941,228982,3161],{},[941,228984,3167],{},[941,228986,3170],{},"). The default shape for databases and object stores.",[1114,228989,228990,228992,228993,2085,228995,1261,228997,1261,228999,229001,229002,229004,229005,229007],{},[950,228991,2694],{}," — whole-vault bundle store (",[941,228994,49533],{},[941,228996,226919],{},[941,228998,226922],{},[941,229000,226928],{},"). One encrypted ",[941,229003,1402],{}," blob per vault; the destination for ",[941,229006,73384],{}," and bundle-mode sync.",[937,229009,229010,229011,229013,229014,229016,229017,229019,229020,229022,229023,229025,229026,229028],{},"A backend may expose ",[950,229012,31219],{}," shapes — in the extended family,\n",[941,229015,29876],{}," ships ",[941,229018,49160],{}," for records and ",[941,229021,47116],{}," for\nwhole-vault bundles. ",[941,229024,9539],{}," is shown as ",[941,229027,37998],{}," for vault-only stores\n(concurrency there is per-bundle OCC, not per-record CAS).",[937,229030,229031,229033,229034,229040,229041,229044,229045,229047,229048,229050,229051,229053,229054,229056,229057,229059],{},[941,229032,24565],{}," reports whether the store declares the capability ",[1007,229035,229037,229039],{"href":229036},"\u002Fservices\u002Ftransactions#atomicity-guarantee",[941,229038,47889],{},"\ndelegates to"," for a statically-safe\nbatch: ✅ full contract, ",[941,229042,229043],{},"conditional"," when it depends on a runtime detail (only\n",[941,229046,52781],{}," today — whether the injected client exposes ",[941,229049,115929],{},"), ❌ no (every\ntransaction on this store takes the per-record OCC path), ",[941,229052,37998],{}," for vault-only\nstores (no ",[941,229055,3154],{},", so no per-record ",[941,229058,9749],{}," to declare).",[1338,229061,229062,229083],{},[1341,229063,229064],{},[1344,229065,229066,229069,229071,229073,229075,229077,229079,229081],{},[1347,229067,229068],{},"Adapter",[1347,229070,34564],{},[1347,229072,24158],{"align":9536},[1347,229074,2694],{"align":9536},[1347,229076,9539],{"align":9536},[1347,229078,24565],{"align":9536},[1347,229080,228955],{},[1347,229082,48731],{},[1357,229084,229085,229106,229127,229148,229169,229190,229211,229232,229254,229275,229296,229317,229338,229359,229380,229400,229421,229443,229464,229486,229507,229528],{},[1344,229086,229087,229091,229093,229095,229097,229099,229101,229104],{},[1362,229088,229089],{},[941,229090,9710],{},[1362,229092,5351],{},[1362,229094,55189],{"align":9536},[1362,229096,104927],{"align":9536},[1362,229098,55189],{"align":9536},[1362,229100,55189],{"align":9536},[1362,229102,229103],{},"serverless-cloud-kv",[1362,229105,1049],{},[1344,229107,229108,229112,229114,229116,229118,229120,229122,229125],{},[1362,229109,229110],{},[941,229111,5358],{},[1362,229113,5351],{},[1362,229115,55189],{"align":9536},[1362,229117,55189],{"align":9536},[1362,229119,55189],{"align":9536},[1362,229121,104927],{"align":9536},[1362,229123,229124],{},"object-store-and-bundles",[1362,229126,1049],{},[1344,229128,229129,229133,229135,229137,229139,229141,229143,229146],{},[1362,229130,229131],{},[941,229132,70155],{},[1362,229134,5351],{},[1362,229136,55189],{"align":9536},[1362,229138,104927],{"align":9536},[1362,229140,104927],{"align":9536},[1362,229142,104927],{"align":9536},[1362,229144,229145],{},"browser-local-directory",[1362,229147,1049],{},[1344,229149,229150,229154,229156,229158,229160,229162,229164,229167],{},[1362,229151,229152],{},[941,229153,9507],{},[1362,229155,228917],{},[1362,229157,55189],{"align":9536},[1362,229159,104927],{"align":9536},[1362,229161,55189],{"align":9536},[1362,229163,104927],{"align":9536},[1362,229165,229166],{},"pwa-offline-first",[1362,229168,1049],{},[1344,229170,229171,229175,229177,229179,229181,229183,229185,229188],{},[1362,229172,229173],{},[941,229174,9698],{},[1362,229176,5351],{},[1362,229178,55189],{"align":9536},[1362,229180,104927],{"align":9536},[1362,229182,104927],{"align":9536},[1362,229184,104927],{"align":9536},[1362,229186,229187],{},"browser-small-vault",[1362,229189,1049],{},[1344,229191,229192,229196,229198,229200,229202,229204,229206,229209],{},[1362,229193,229194],{},[941,229195,9772],{},[1362,229197,5351],{},[1362,229199,55189],{"align":9536},[1362,229201,104927],{"align":9536},[1362,229203,55189],{"align":9536},[1362,229205,55189],{"align":9536},[1362,229207,229208],{},"edge-sql",[1362,229210,1049],{},[1344,229212,229213,229217,229219,229221,229223,229225,229227,229230],{},[1362,229214,229215],{},[941,229216,9760],{},[1362,229218,5351],{},[1362,229220,55189],{"align":9536},[1362,229222,104927],{"align":9536},[1362,229224,55189],{"align":9536},[1362,229226,104927],{"align":9536},[1362,229228,229229],{},"edge-object-store",[1362,229231,1049],{},[1344,229233,229234,229239,229241,229243,229245,229247,229249,229252],{},[1362,229235,229236],{},[941,229237,229238],{},"to-drive",[1362,229240,5351],{},[1362,229242,104927],{"align":9536},[1362,229244,55189],{"align":9536},[1362,229246,37998],{"align":9536},[1362,229248,37998],{"align":9536},[1362,229250,229251],{},"personal-drive-bundles",[1362,229253,1049],{},[1344,229255,229256,229260,229262,229264,229266,229268,229270,229273],{},[1362,229257,229258],{},[941,229259,7461],{},[1362,229261,228917],{},[1362,229263,55189],{"align":9536},[1362,229265,104927],{"align":9536},[1362,229267,104927],{"align":9536},[1362,229269,104927],{"align":9536},[1362,229271,229272],{},"usb-or-local-disk",[1362,229274,1049],{},[1344,229276,229277,229281,229283,229285,229287,229289,229291,229294],{},[1362,229278,229279],{},[941,229280,48818],{},[1362,229282,5351],{},[1362,229284,104927],{"align":9536},[1362,229286,55189],{"align":9536},[1362,229288,37998],{"align":9536},[1362,229290,37998],{"align":9536},[1362,229292,229293],{},"apple-personal-bundles",[1362,229295,1049],{},[1344,229297,229298,229302,229304,229306,229308,229310,229312,229315],{},[1362,229299,229300],{},[941,229301,2992],{},[1362,229303,5351],{},[1362,229305,55189],{"align":9536},[1362,229307,104927],{"align":9536},[1362,229309,55189],{"align":9536},[1362,229311,55189],{"align":9536},[1362,229313,229314],{},"testing",[1362,229316,1049],{},[1344,229318,229319,229323,229325,229327,229329,229331,229333,229336],{},[1362,229320,229321],{},[941,229322,5125],{},[1362,229324,228922],{},[1362,229326,55189],{"align":9536},[1362,229328,104927],{"align":9536},[1362,229330,104927],{"align":9536},[1362,229332,104927],{"align":9536},[1362,229334,229335],{},"observability",[1362,229337,1049],{},[1344,229339,229340,229344,229346,229348,229350,229352,229354,229357],{},[1362,229341,229342],{},[941,229343,7464],{},[1362,229345,5351],{},[1362,229347,55189],{"align":9536},[1362,229349,104927],{"align":9536},[1362,229351,55189],{"align":9536},[1362,229353,55189],{"align":9536},[1362,229355,229356],{},"self-hosted-sql",[1362,229358,1049],{},[1344,229360,229361,229365,229367,229369,229371,229373,229375,229378],{},[1362,229362,229363],{},[941,229364,53945],{},[1362,229366,5351],{},[1362,229368,55189],{"align":9536},[1362,229370,104927],{"align":9536},[1362,229372,104927],{"align":9536},[1362,229374,104927],{"align":9536},[1362,229376,229377],{},"network-filesystem",[1362,229379,1049],{},[1344,229381,229382,229386,229388,229390,229392,229394,229396,229398],{},[1362,229383,229384],{},[941,229385,5355],{},[1362,229387,5351],{},[1362,229389,55189],{"align":9536},[1362,229391,104927],{"align":9536},[1362,229393,55189],{"align":9536},[1362,229395,55189],{"align":9536},[1362,229397,229356],{},[1362,229399,1049],{},[1344,229401,229402,229406,229408,229410,229412,229414,229416,229419],{},[1362,229403,229404],{},[941,229405,48670],{},[1362,229407,5351],{},[1362,229409,55189],{"align":9536},[1362,229411,104927],{"align":9536},[1362,229413,55189],{"align":9536},[1362,229415,104927],{"align":9536},[1362,229417,229418],{},"remote-http-store",[1362,229420,1049],{},[1344,229422,229423,229428,229430,229432,229434,229436,229438,229441],{},[1362,229424,229425],{},[941,229426,229427],{},"to-smb",[1362,229429,5351],{},[1362,229431,55189],{"align":9536},[1362,229433,104927],{"align":9536},[1362,229435,104927],{"align":9536},[1362,229437,104927],{"align":9536},[1362,229439,229440],{},"windows-network-share",[1362,229442,1049],{},[1344,229444,229445,229449,229451,229453,229455,229457,229459,229462],{},[1362,229446,229447],{},[941,229448,5361],{},[1362,229450,5351],{},[1362,229452,55189],{"align":9536},[1362,229454,104927],{"align":9536},[1362,229456,55189],{"align":9536},[1362,229458,55189],{"align":9536},[1362,229460,229461],{},"embedded-sql",[1362,229463,1049],{},[1344,229465,229466,229471,229473,229475,229477,229479,229481,229484],{},[1362,229467,229468],{},[941,229469,229470],{},"to-ssh",[1362,229472,5351],{},[1362,229474,55189],{"align":9536},[1362,229476,104927],{"align":9536},[1362,229478,104927],{"align":9536},[1362,229480,104927],{"align":9536},[1362,229482,229483],{},"remote-filesystem-over-ssh",[1362,229485,1049],{},[1344,229487,229488,229492,229494,229496,229498,229500,229502,229505],{},[1362,229489,229490],{},[941,229491,228934],{},[1362,229493,5351],{},[1362,229495,55189],{"align":9536},[1362,229497,104927],{"align":9536},[1362,229499,55189],{"align":9536},[1362,229501,55189],{"align":9536},[1362,229503,229504],{},"managed-postgres",[1362,229506,1049],{},[1344,229508,229509,229513,229515,229517,229519,229521,229523,229526],{},[1362,229510,229511],{},[941,229512,52781],{},[1362,229514,5351],{},[1362,229516,55189],{"align":9536},[1362,229518,104927],{"align":9536},[1362,229520,55189],{"align":9536},[1362,229522,229043],{"align":9536},[1362,229524,229525],{},"edge-sqlite",[1362,229527,1049],{},[1344,229529,229530,229534,229536,229538,229540,229542,229544,229547],{},[1362,229531,229532],{},[941,229533,52778],{},[1362,229535,5351],{},[1362,229537,55189],{"align":9536},[1362,229539,104927],{"align":9536},[1362,229541,104927],{"align":9536},[1362,229543,104927],{"align":9536},[1362,229545,229546],{},"webdav-share",[1362,229548,1049],{},[937,229550,229551,229554],{},[950,229552,229553],{},"Totals:"," 22 adapters (3 essential + 19 extended) — 20 record-capable, 3 vault-capable (1 both).",[1022,229556,2621],{"id":2620},[1111,229558,229559,229568,229573],{},[1114,229560,229561,1169,229563,1261,229565,229567],{},[1007,229562,6891],{"href":51},[941,229564,3154],{},[941,229566,49533],{}," contracts these adapters implement, and the two-repo split",[1114,229569,229570,229572],{},[1007,229571,47064],{"href":342}," — the full storage-destination catalog, including the extended cloud\u002FSQL\u002Fremote-FS stores",[1114,229574,229575,1169,229577,229579,229580,229582],{},[1007,229576,876],{"href":877},[941,229578,47889],{}," atomicity guarantee this ",[941,229581,24565],{}," column feeds",{"title":1037,"searchDepth":1038,"depth":1038,"links":229584},[229585],{"id":2620,"depth":1041,"text":2621},"Which essential or extended to-* store exposes which shape, its per-record CAS atomicity, and whether db.transaction() can commit atomically.",{},{"title":860,"description":229586},"GmJNNrhjjAqKaKtB0HveLwIwGwHTttG1bLVaoh780x0",{"id":229591,"title":864,"access":12,"apiToc":929,"body":229592,"category":1042,"description":232815,"extension":1044,"meta":232816,"navigation":1042,"nest":1047,"path":865,"seo":232817,"sinceVersion":1042,"status":1049,"stem":866,"subtitle":1042,"tier":1050,"tocExternal":1042,"__hash__":232818},"docs\u002Fservices\u002Fsync.md",{"type":931,"value":229593,"toc":232787},[229594,229596,229599,229601,229621,229623,229637,229639,229671,229673,229834,229840,229986,230016,230018,230022,230165,230220,230238,230270,230324,230331,230362,230431,230450,230491,230616,230620,230637,230669,230678,230682,230825,230888,230910,230933,230937,230995,231009,231020,231024,231030,231158,231216,231219,231282,231314,231318,231354,231374,231393,231397,231429,231521,231542,231545,231557,231657,231663,231723,231736,231860,231898,231933,231935,231947,232109,232202,232232,232234,232330,232332,232426,232431,232487,232489,232587,232591,232610,232612,232653,232655,232689,232691,232748,232750,232785],[934,229595,864],{"id":864},[46871,229597],{":rows":229598},"[{\"label\":\"Subpath\",\"value\":\"`@noy-db\u002Fhub\u002Fsync`\"},{\"label\":\"Factory\",\"value\":\"`withSync()`\"},{\"label\":\"Layer\",\"value\":\"`with-sync`\"},{\"label\":\"Cluster\",\"value\":\"G — Collaboration & Auth\"},{\"label\":\"LOC cost\",\"value\":\"~856 (off-bundle when not opted in)\"}]",[1022,229600,29857],{"id":29856},[937,229602,229603,229604,229606,229607,229609,229610,229612,229613,229615,229616,65433,229618,229620],{},"Sync replicates a ",[950,229605,2694],{}," — noy-db's end-to-end-encrypted document store (see\n",[1007,229608,65376],{"href":48},") — between a local store and one or more\nremote stores. It tracks which records changed locally since the last push (\"dirty tracking\"),\nsends only those changes to the remote, pulls back whatever the remote has that's newer, and\nresolves conflicts when both sides changed the same record. Everything that crosses the wire is\nthe same encrypted envelope your collections already write (see\n",[1007,229611,1516],{"href":66},"), so a remote store you don't fully trust — a cloud\nbucket, a peer's device — never sees plaintext. On top of replication, sync also exposes\n",[950,229614,91292],{},": short-lived \"who's looking at this collection right now\" awareness, broadcast over\nan ephemeral channel whose key is derived from the collection's data-encryption key (its ",[950,229617,5627],{},[1007,229619,1516],{"href":66},") via HKDF, a one-way key-derivation function. When\nthe store adapter supports real-time pub\u002Fsub, the whole presence message — user ID and payload\ntogether — travels encrypted under that key, so the adapter can't identify users either. Without\npub\u002Fsub support, presence falls back to polling records written to the store, and that fallback\nwrites the user ID in the clear (only the payload is encrypted) — see the storage-poll note below.",[1022,229622,111660],{"id":111659},[1111,229624,229625,229628,229631,229634],{},[1114,229626,229627],{},"Multi-device single-user apps (laptop + phone sharing one vault)",[1114,229629,229630],{},"Multi-user collaboration where each device keeps a local cache plus a shared cloud target",[1114,229632,229633],{},"Active backup \u002F archive replication to a second store",[1114,229635,229636],{},"Live \"who's viewing this\" presence in a collaborative UI",[1022,229638,70093],{"id":70092},[1111,229640,229641,229658],{},[1114,229642,229643,229646,229647,229649,229650,229652,229653,229655,229656,1520],{},[950,229644,229645],{},"A shared sync adapter"," — pass a store both devices can reach as the ",[941,229648,20587],{}," option; ",[941,229651,4676],{}," without it (or ",[941,229654,20587],{}," without the strategy) throws on the first ",[941,229657,1749],{},[1114,229659,229660,229663,229664,1261,229666,5502,229668,229670],{},[1092,229661,229662],{},"For real-time presence:"," the adapter must implement ",[941,229665,91537],{},[941,229667,91540],{},[941,229669,21307],{}," also always requires the strategy itself.",[1022,229672,99864],{"id":99863},[2217,229674,229676],{"className":2219,"code":229675,"language":2221,"meta":1037,"style":1037},"import { createNoydb } from '@noy-db\u002Fhub'\nimport { withSync } from '@noy-db\u002Fhub\u002Fsync'\n\nconst db = await createNoydb({\n  store: localStore,       \u002F\u002F local cache — see Core 05: Stores\n  sync: remoteStore,       \u002F\u002F primary sync target\n  user: 'alice',\n  secret: 'alice-secret-2026',\n  syncStrategy: withSync(),\n})\nconst vault = await db.openVault('notes')\n",[941,229677,229678,229696,229714,229718,229734,229748,229762,229776,229790,229802,229808],{"__ignoreMap":1037},[2225,229679,229680,229682,229684,229686,229688,229690,229692,229694],{"class":2227,"line":2228},[2225,229681,2232],{"class":2231},[2225,229683,2236],{"class":2235},[2225,229685,2744],{"class":2239},[2225,229687,2243],{"class":2235},[2225,229689,2246],{"class":2231},[2225,229691,2249],{"class":2235},[2225,229693,1086],{"class":2252},[2225,229695,2256],{"class":2235},[2225,229697,229698,229700,229702,229704,229706,229708,229710,229712],{"class":2227,"line":1041},[2225,229699,2232],{"class":2231},[2225,229701,2236],{"class":2235},[2225,229703,89579],{"class":2239},[2225,229705,2243],{"class":2235},[2225,229707,2246],{"class":2231},[2225,229709,2249],{"class":2235},[2225,229711,4672],{"class":2252},[2225,229713,2256],{"class":2235},[2225,229715,229716],{"class":2227,"line":1038},[2225,229717,2285],{"emptyLinePlaceholder":2284},[2225,229719,229720,229722,229724,229726,229728,229730,229732],{"class":2227,"line":2288},[2225,229721,2775],{"class":2774},[2225,229723,2778],{"class":2239},[2225,229725,2781],{"class":2235},[2225,229727,2784],{"class":2231},[2225,229729,2744],{"class":2291},[2225,229731,2789],{"class":2239},[2225,229733,4200],{"class":2235},[2225,229735,229736,229738,229740,229743,229745],{"class":2227,"line":2304},[2225,229737,15608],{"class":2307},[2225,229739,2311],{"class":2235},[2225,229741,229742],{"class":2239}," localStore",[2225,229744,2298],{"class":2235},[2225,229746,229747],{"class":2763},"       \u002F\u002F local cache — see Core 05: Stores\n",[2225,229749,229750,229752,229754,229757,229759],{"class":2227,"line":2356},[2225,229751,34919],{"class":2307},[2225,229753,2311],{"class":2235},[2225,229755,229756],{"class":2239}," remoteStore",[2225,229758,2298],{"class":2235},[2225,229760,229761],{"class":2763},"       \u002F\u002F primary sync target\n",[2225,229763,229764,229766,229768,229770,229772,229774],{"class":2227,"line":2830},[2225,229765,4205],{"class":2307},[2225,229767,2311],{"class":2235},[2225,229769,2249],{"class":2235},[2225,229771,6408],{"class":2252},[2225,229773,2805],{"class":2235},[2225,229775,1707],{"class":2235},[2225,229777,229778,229780,229782,229784,229786,229788],{"class":2227,"line":2861},[2225,229779,10423],{"class":2307},[2225,229781,2311],{"class":2235},[2225,229783,2249],{"class":2235},[2225,229785,107607],{"class":2252},[2225,229787,2805],{"class":2235},[2225,229789,1707],{"class":2235},[2225,229791,229792,229794,229796,229798,229800],{"class":2227,"line":4246},[2225,229793,89901],{"class":2307},[2225,229795,2311],{"class":2235},[2225,229797,89579],{"class":2291},[2225,229799,4228],{"class":2239},[2225,229801,1707],{"class":2235},[2225,229803,229804,229806],{"class":2227,"line":4253},[2225,229805,2359],{"class":2235},[2225,229807,2362],{"class":2239},[2225,229809,229810,229812,229814,229816,229818,229820,229822,229824,229826,229828,229830,229832],{"class":2227,"line":4259},[2225,229811,2775],{"class":2774},[2225,229813,2835],{"class":2239},[2225,229815,2781],{"class":2235},[2225,229817,2784],{"class":2231},[2225,229819,2842],{"class":2239},[2225,229821,1520],{"class":2235},[2225,229823,2847],{"class":2291},[2225,229825,2789],{"class":2239},[2225,229827,2805],{"class":2235},[2225,229829,2854],{"class":2252},[2225,229831,2805],{"class":2235},[2225,229833,2362],{"class":2239},[937,229835,229836,229837,229839],{},"Multiple targets — a bidirectional peer plus a push-only archive — pass an array of\n",[941,229838,48468],{}," pairs instead of a bare store:",[2217,229841,229843],{"className":2219,"code":229842,"language":2221,"meta":1037,"style":1037},"const db = await createNoydb({\n  store: localStore,\n  sync: [\n    { store: peerStore, role: 'sync-peer' },   \u002F\u002F bidirectional\n    { store: archiveStore, role: 'archive' },  \u002F\u002F push-only\n  ],\n  user: 'alice',\n  secret: 'alice-secret-2026',\n  syncStrategy: withSync(),\n})\n",[941,229844,229845,229861,229871,229879,229906,229934,229940,229954,229968,229980],{"__ignoreMap":1037},[2225,229846,229847,229849,229851,229853,229855,229857,229859],{"class":2227,"line":2228},[2225,229848,2775],{"class":2774},[2225,229850,2778],{"class":2239},[2225,229852,2781],{"class":2235},[2225,229854,2784],{"class":2231},[2225,229856,2744],{"class":2291},[2225,229858,2789],{"class":2239},[2225,229860,4200],{"class":2235},[2225,229862,229863,229865,229867,229869],{"class":2227,"line":1041},[2225,229864,15608],{"class":2307},[2225,229866,2311],{"class":2235},[2225,229868,229742],{"class":2239},[2225,229870,1707],{"class":2235},[2225,229872,229873,229875,229877],{"class":2227,"line":1038},[2225,229874,34919],{"class":2307},[2225,229876,2311],{"class":2235},[2225,229878,32491],{"class":2239},[2225,229880,229881,229883,229885,229887,229889,229891,229893,229895,229897,229899,229901,229903],{"class":2227,"line":2288},[2225,229882,32496],{"class":2235},[2225,229884,7564],{"class":2307},[2225,229886,2311],{"class":2235},[2225,229888,34782],{"class":2239},[2225,229890,2298],{"class":2235},[2225,229892,34944],{"class":2307},[2225,229894,2311],{"class":2235},[2225,229896,2249],{"class":2235},[2225,229898,34951],{"class":2252},[2225,229900,2805],{"class":2235},[2225,229902,10965],{"class":2235},[2225,229904,229905],{"class":2763},"   \u002F\u002F bidirectional\n",[2225,229907,229908,229910,229912,229914,229917,229919,229921,229923,229925,229927,229929,229931],{"class":2227,"line":2304},[2225,229909,32496],{"class":2235},[2225,229911,7564],{"class":2307},[2225,229913,2311],{"class":2235},[2225,229915,229916],{"class":2239}," archiveStore",[2225,229918,2298],{"class":2235},[2225,229920,34944],{"class":2307},[2225,229922,2311],{"class":2235},[2225,229924,2249],{"class":2235},[2225,229926,605],{"class":2252},[2225,229928,2805],{"class":2235},[2225,229930,10965],{"class":2235},[2225,229932,229933],{"class":2763},"  \u002F\u002F push-only\n",[2225,229935,229936,229938],{"class":2227,"line":2356},[2225,229937,32588],{"class":2239},[2225,229939,1707],{"class":2235},[2225,229941,229942,229944,229946,229948,229950,229952],{"class":2227,"line":2830},[2225,229943,4205],{"class":2307},[2225,229945,2311],{"class":2235},[2225,229947,2249],{"class":2235},[2225,229949,6408],{"class":2252},[2225,229951,2805],{"class":2235},[2225,229953,1707],{"class":2235},[2225,229955,229956,229958,229960,229962,229964,229966],{"class":2227,"line":2861},[2225,229957,10423],{"class":2307},[2225,229959,2311],{"class":2235},[2225,229961,2249],{"class":2235},[2225,229963,107607],{"class":2252},[2225,229965,2805],{"class":2235},[2225,229967,1707],{"class":2235},[2225,229969,229970,229972,229974,229976,229978],{"class":2227,"line":4246},[2225,229971,89901],{"class":2307},[2225,229973,2311],{"class":2235},[2225,229975,89579],{"class":2291},[2225,229977,4228],{"class":2239},[2225,229979,1707],{"class":2235},[2225,229981,229982,229984],{"class":2227,"line":4253},[2225,229983,2359],{"class":2235},[2225,229985,2362],{"class":2239},[937,229987,229988,229989,229991,229992,997,229994,229996,229997,1625,229999,230001,230002,230004,230005,230007,230008,1707,230010,230012,230013,230015],{},"Roles: ",[941,229990,190253],{}," pushes and pulls; ",[941,229993,190175],{},[941,229995,190178],{}," only ever receive pushes — they\nnever feed data back into the local store. This holds even when a ",[941,229998,190175],{},[941,230000,190178],{}," target\nends up elected as the ",[1092,230003,47925],{}," (a config with no ",[941,230006,190253],{}," at all): since ",[941,230009,56991],{},[941,230011,20594],{}," against such a primary is a no-op returning an empty result, and ",[941,230014,20597],{}," runs\npush-only against it — a backup sink is never pulled from.",[1022,230017,29893],{"id":29892},[1591,230019,230021],{"id":230020},"push-pull-and-status","Push, pull, and status",[2217,230023,230025],{"className":2219,"code":230024,"language":2221,"meta":1037,"style":1037},"interface Note { title: string; body: string }\nconst notes = vault.collection\u003CNote>('notes')\n\nawait notes.put('n1', { title: 'Grocery list', body: 'eggs, milk' })\n\nconst pushResult = await db.push('notes')\n",[941,230026,230027,230051,230081,230085,230134,230138],{"__ignoreMap":1037},[2225,230028,230029,230031,230033,230035,230037,230039,230041,230043,230045,230047,230049],{"class":2227,"line":2228},[2225,230030,6994],{"class":2774},[2225,230032,86655],{"class":6342},[2225,230034,2236],{"class":2235},[2225,230036,74169],{"class":2307},[2225,230038,2311],{"class":2235},[2225,230040,6366],{"class":6342},[2225,230042,3769],{"class":2235},[2225,230044,42791],{"class":2307},[2225,230046,2311],{"class":2235},[2225,230048,6366],{"class":6342},[2225,230050,6369],{"class":2235},[2225,230052,230053,230055,230057,230059,230061,230063,230065,230067,230069,230071,230073,230075,230077,230079],{"class":2227,"line":1041},[2225,230054,2775],{"class":2774},[2225,230056,42762],{"class":2239},[2225,230058,2781],{"class":2235},[2225,230060,6466],{"class":2239},[2225,230062,1520],{"class":2235},[2225,230064,6471],{"class":2291},[2225,230066,6474],{"class":2235},[2225,230068,86814],{"class":6342},[2225,230070,6480],{"class":2235},[2225,230072,2789],{"class":2239},[2225,230074,2805],{"class":2235},[2225,230076,2854],{"class":2252},[2225,230078,2805],{"class":2235},[2225,230080,2362],{"class":2239},[2225,230082,230083],{"class":2227,"line":1038},[2225,230084,2285],{"emptyLinePlaceholder":2284},[2225,230086,230087,230089,230091,230093,230095,230097,230099,230101,230103,230105,230107,230109,230111,230113,230116,230118,230120,230122,230124,230126,230128,230130,230132],{"class":2227,"line":2288},[2225,230088,6500],{"class":2231},[2225,230090,42817],{"class":2239},[2225,230092,1520],{"class":2235},[2225,230094,3161],{"class":2291},[2225,230096,2789],{"class":2239},[2225,230098,2805],{"class":2235},[2225,230100,129502],{"class":2252},[2225,230102,2805],{"class":2235},[2225,230104,2298],{"class":2235},[2225,230106,2236],{"class":2235},[2225,230108,74169],{"class":2307},[2225,230110,2311],{"class":2235},[2225,230112,2249],{"class":2235},[2225,230114,230115],{"class":2252},"Grocery list",[2225,230117,2805],{"class":2235},[2225,230119,2298],{"class":2235},[2225,230121,42791],{"class":2307},[2225,230123,2311],{"class":2235},[2225,230125,2249],{"class":2235},[2225,230127,86897],{"class":2252},[2225,230129,2805],{"class":2235},[2225,230131,2243],{"class":2235},[2225,230133,2362],{"class":2239},[2225,230135,230136],{"class":2227,"line":2304},[2225,230137,2285],{"emptyLinePlaceholder":2284},[2225,230139,230140,230142,230145,230147,230149,230151,230153,230155,230157,230159,230161,230163],{"class":2227,"line":2356},[2225,230141,2775],{"class":2774},[2225,230143,230144],{"class":2239}," pushResult ",[2225,230146,2781],{"class":2235},[2225,230148,2784],{"class":2231},[2225,230150,2842],{"class":2239},[2225,230152,1520],{"class":2235},[2225,230154,82367],{"class":2291},[2225,230156,2789],{"class":2239},[2225,230158,2805],{"class":2235},[2225,230160,2854],{"class":2252},[2225,230162,2805],{"class":2235},[2225,230164,2362],{"class":2239},[2217,230166,230168],{"className":6604,"code":230167,"language":141,"meta":1037,"style":1037},"{ \"pushed\": 1, \"conflicts\": [], \"errors\": [], \"erasures\": [] }\n",[941,230169,230170],{"__ignoreMap":1037},[2225,230171,230172,230174,230176,230179,230181,230183,230185,230187,230189,230191,230193,230195,230197,230199,230201,230203,230205,230207,230209,230212,230214,230216,230218],{"class":2227,"line":2228},[2225,230173,2792],{"class":2235},[2225,230175,6614],{"class":2235},[2225,230177,230178],{"class":2774},"pushed",[2225,230180,6620],{"class":2235},[2225,230182,2311],{"class":2235},[2225,230184,9377],{"class":6527},[2225,230186,2298],{"class":2235},[2225,230188,6614],{"class":2235},[2225,230190,91240],{"class":2774},[2225,230192,6620],{"class":2235},[2225,230194,2311],{"class":2235},[2225,230196,203954],{"class":2235},[2225,230198,6614],{"class":2235},[2225,230200,120264],{"class":2774},[2225,230202,6620],{"class":2235},[2225,230204,2311],{"class":2235},[2225,230206,203954],{"class":2235},[2225,230208,6614],{"class":2235},[2225,230210,230211],{"class":2774},"erasures",[2225,230213,6620],{"class":2235},[2225,230215,2311],{"class":2235},[2225,230217,66698],{"class":2235},[2225,230219,6369],{"class":2235},[937,230221,230222,230225,230226,230228,230229,230231,230232,5584,230234,230237],{},[941,230223,230224],{},"db.push(vault, options?)"," sends only ",[950,230227,71061],{}," records — ones written locally since the last\nsuccessful push — to the vault's primary sync target (the first ",[941,230230,190253],{},", or the first\nconfigured target if none is marked ",[941,230233,190253],{},[941,230235,230236],{},"db.pull(vault, options?)"," is the mirror: it\nreads the remote's full snapshot and applies anything newer than the local copy.",[2217,230239,230241],{"className":2219,"code":230240,"language":2221,"meta":1037,"style":1037},"const pullResult = await db.pull('notes')\n",[941,230242,230243],{"__ignoreMap":1037},[2225,230244,230245,230247,230250,230252,230254,230256,230258,230260,230262,230264,230266,230268],{"class":2227,"line":2228},[2225,230246,2775],{"class":2774},[2225,230248,230249],{"class":2239}," pullResult ",[2225,230251,2781],{"class":2235},[2225,230253,2784],{"class":2231},[2225,230255,2842],{"class":2239},[2225,230257,1520],{"class":2235},[2225,230259,91149],{"class":2291},[2225,230261,2789],{"class":2239},[2225,230263,2805],{"class":2235},[2225,230265,2854],{"class":2252},[2225,230267,2805],{"class":2235},[2225,230269,2362],{"class":2239},[2217,230271,230273],{"className":6604,"code":230272,"language":141,"meta":1037,"style":1037},"{ \"pulled\": 3, \"conflicts\": [], \"errors\": [], \"erasures\": [] }\n",[941,230274,230275],{"__ignoreMap":1037},[2225,230276,230277,230279,230281,230284,230286,230288,230290,230292,230294,230296,230298,230300,230302,230304,230306,230308,230310,230312,230314,230316,230318,230320,230322],{"class":2227,"line":2228},[2225,230278,2792],{"class":2235},[2225,230280,6614],{"class":2235},[2225,230282,230283],{"class":2774},"pulled",[2225,230285,6620],{"class":2235},[2225,230287,2311],{"class":2235},[2225,230289,14460],{"class":6527},[2225,230291,2298],{"class":2235},[2225,230293,6614],{"class":2235},[2225,230295,91240],{"class":2774},[2225,230297,6620],{"class":2235},[2225,230299,2311],{"class":2235},[2225,230301,203954],{"class":2235},[2225,230303,6614],{"class":2235},[2225,230305,120264],{"class":2774},[2225,230307,6620],{"class":2235},[2225,230309,2311],{"class":2235},[2225,230311,203954],{"class":2235},[2225,230313,6614],{"class":2235},[2225,230315,230211],{"class":2774},[2225,230317,6620],{"class":2235},[2225,230319,2311],{"class":2235},[2225,230321,66698],{"class":2235},[2225,230323,6369],{"class":2235},[937,230325,230326,230327,230330],{},"Check what's outstanding at any time with ",[941,230328,230329],{},"db.syncStatus(vault)"," — it reads the engine's tracked\ndirty log, no network call:",[2217,230332,230334],{"className":2219,"code":230333,"language":2221,"meta":1037,"style":1037},"const status = db.syncStatus('notes')\n",[941,230335,230336],{"__ignoreMap":1037},[2225,230337,230338,230340,230343,230345,230347,230349,230352,230354,230356,230358,230360],{"class":2227,"line":2228},[2225,230339,2775],{"class":2774},[2225,230341,230342],{"class":2239}," status ",[2225,230344,2781],{"class":2235},[2225,230346,2842],{"class":2239},[2225,230348,1520],{"class":2235},[2225,230350,230351],{"class":2291},"syncStatus",[2225,230353,2789],{"class":2239},[2225,230355,2805],{"class":2235},[2225,230357,2854],{"class":2252},[2225,230359,2805],{"class":2235},[2225,230361,2362],{"class":2239},[2217,230363,230365],{"className":6604,"code":230364,"language":141,"meta":1037,"style":1037},"{ \"dirty\": 0, \"lastPush\": \"2026-07-08T10:02:00.000Z\", \"lastPull\": \"2026-07-08T10:02:05.000Z\", \"online\": true }\n",[941,230366,230367],{"__ignoreMap":1037},[2225,230368,230369,230371,230373,230375,230377,230379,230381,230383,230385,230388,230390,230392,230394,230397,230399,230401,230403,230406,230408,230410,230412,230415,230417,230419,230421,230423,230425,230427,230429],{"class":2227,"line":2228},[2225,230370,2792],{"class":2235},[2225,230372,6614],{"class":2235},[2225,230374,71061],{"class":2774},[2225,230376,6620],{"class":2235},[2225,230378,2311],{"class":2235},[2225,230380,8464],{"class":6527},[2225,230382,2298],{"class":2235},[2225,230384,6614],{"class":2235},[2225,230386,230387],{"class":2774},"lastPush",[2225,230389,6620],{"class":2235},[2225,230391,2311],{"class":2235},[2225,230393,6614],{"class":2235},[2225,230395,230396],{"class":2252},"2026-07-08T10:02:00.000Z",[2225,230398,6620],{"class":2235},[2225,230400,2298],{"class":2235},[2225,230402,6614],{"class":2235},[2225,230404,230405],{"class":2774},"lastPull",[2225,230407,6620],{"class":2235},[2225,230409,2311],{"class":2235},[2225,230411,6614],{"class":2235},[2225,230413,230414],{"class":2252},"2026-07-08T10:02:05.000Z",[2225,230416,6620],{"class":2235},[2225,230418,2298],{"class":2235},[2225,230420,6614],{"class":2235},[2225,230422,40013],{"class":2774},[2225,230424,6620],{"class":2235},[2225,230426,2311],{"class":2235},[2225,230428,2322],{"class":2235},[2225,230430,6369],{"class":2235},[937,230432,230433,230436,230437,1625,230439,230441,230442,230444,230445,1625,230447,230449],{},[941,230434,230435],{},"db.sync(vault, options?)"," is a convenience that pulls then pushes in one call and, unlike\n",[941,230438,82367],{},[941,230440,91149],{}," on their own, fans out to every configured target: ",[941,230443,190253],{}," targets get a full\npull+push, ",[941,230446,190175],{},[941,230448,190178],{}," targets get push-only.",[2217,230451,230453],{"className":2219,"code":230452,"language":2221,"meta":1037,"style":1037},"const { pull, push } = await db.sync('notes')\n",[941,230454,230455],{"__ignoreMap":1037},[2225,230456,230457,230459,230461,230464,230466,230469,230471,230473,230475,230477,230479,230481,230483,230485,230487,230489],{"class":2227,"line":2228},[2225,230458,2775],{"class":2774},[2225,230460,2236],{"class":2235},[2225,230462,230463],{"class":2239}," pull",[2225,230465,2298],{"class":2235},[2225,230467,230468],{"class":2239}," push ",[2225,230470,2359],{"class":2235},[2225,230472,6346],{"class":2235},[2225,230474,2784],{"class":2231},[2225,230476,2842],{"class":2239},[2225,230478,1520],{"class":2235},[2225,230480,864],{"class":2291},[2225,230482,2789],{"class":2239},[2225,230484,2805],{"class":2235},[2225,230486,2854],{"class":2252},[2225,230488,2805],{"class":2235},[2225,230490,2362],{"class":2239},[2217,230492,230494],{"className":6604,"code":230493,"language":141,"meta":1037,"style":1037},"{\n  \"pull\": { \"pulled\": 3, \"conflicts\": [], \"errors\": [], \"erasures\": [] },\n  \"push\": { \"pushed\": 1, \"conflicts\": [], \"errors\": [], \"erasures\": [] }\n}\n",[941,230495,230496,230500,230556,230612],{"__ignoreMap":1037},[2225,230497,230498],{"class":2227,"line":2228},[2225,230499,4200],{"class":2235},[2225,230501,230502,230504,230506,230508,230510,230512,230514,230516,230518,230520,230522,230524,230526,230528,230530,230532,230534,230536,230538,230540,230542,230544,230546,230548,230550,230552,230554],{"class":2227,"line":1041},[2225,230503,9368],{"class":2235},[2225,230505,91149],{"class":2774},[2225,230507,6620],{"class":2235},[2225,230509,2311],{"class":2235},[2225,230511,2236],{"class":2235},[2225,230513,6614],{"class":2235},[2225,230515,230283],{"class":6342},[2225,230517,6620],{"class":2235},[2225,230519,2311],{"class":2235},[2225,230521,14460],{"class":6527},[2225,230523,2298],{"class":2235},[2225,230525,6614],{"class":2235},[2225,230527,91240],{"class":6342},[2225,230529,6620],{"class":2235},[2225,230531,2311],{"class":2235},[2225,230533,203954],{"class":2235},[2225,230535,6614],{"class":2235},[2225,230537,120264],{"class":6342},[2225,230539,6620],{"class":2235},[2225,230541,2311],{"class":2235},[2225,230543,203954],{"class":2235},[2225,230545,6614],{"class":2235},[2225,230547,230211],{"class":6342},[2225,230549,6620],{"class":2235},[2225,230551,2311],{"class":2235},[2225,230553,66698],{"class":2235},[2225,230555,2353],{"class":2235},[2225,230557,230558,230560,230562,230564,230566,230568,230570,230572,230574,230576,230578,230580,230582,230584,230586,230588,230590,230592,230594,230596,230598,230600,230602,230604,230606,230608,230610],{"class":2227,"line":1038},[2225,230559,9368],{"class":2235},[2225,230561,82367],{"class":2774},[2225,230563,6620],{"class":2235},[2225,230565,2311],{"class":2235},[2225,230567,2236],{"class":2235},[2225,230569,6614],{"class":2235},[2225,230571,230178],{"class":6342},[2225,230573,6620],{"class":2235},[2225,230575,2311],{"class":2235},[2225,230577,9377],{"class":6527},[2225,230579,2298],{"class":2235},[2225,230581,6614],{"class":2235},[2225,230583,91240],{"class":6342},[2225,230585,6620],{"class":2235},[2225,230587,2311],{"class":2235},[2225,230589,203954],{"class":2235},[2225,230591,6614],{"class":2235},[2225,230593,120264],{"class":6342},[2225,230595,6620],{"class":2235},[2225,230597,2311],{"class":2235},[2225,230599,203954],{"class":2235},[2225,230601,6614],{"class":2235},[2225,230603,230211],{"class":6342},[2225,230605,6620],{"class":2235},[2225,230607,2311],{"class":2235},[2225,230609,66698],{"class":2235},[2225,230611,6369],{"class":2235},[2225,230613,230614],{"class":2227,"line":2288},[2225,230615,7351],{"class":2235},[1591,230617,230619],{"id":230618},"the-automation-ladder","The automation ladder",[937,230621,230622,230623,230626,230627,968,230629,52185,230631,230633,230634,230636],{},"Everything above is ",[950,230624,230625],{},"rung 1 — explicit",": replication happens when you call ",[941,230628,82367],{},[941,230630,91149],{},[941,230632,864],{},", and at no other time. That is the whole behavior you get from ",[941,230635,20587],{}," alone, and it is\nwhere you stay unless you do the one thing below.",[7646,230638,230639],{"color":10789,"icon":39006},[937,230640,230641,230646,230647,230650,230651,230654,230655,230658,230659,230661,230662,230665,230666,230668],{},[950,230642,117292,230643,230645],{},[941,230644,20562],{}," is the opt-in."," noy-db resolves a policy from the store category —\nper-record stores get ",[941,230648,230649],{},"INDEXED_STORE_POLICY",", pod stores get ",[941,230652,230653],{},"POD_STORE_POLICY"," — but ",[1092,230656,230657],{},"resolving","\none does not start anything. The scheduler starts only for a policy you ",[950,230660,95997],{},", via\n",[941,230663,230664],{},"createNoydb({ syncPolicy })"," or a per-target ",[941,230667,796],{},". Fall back to the resolved default and you\nstay on rung 1 forever.",[937,230670,230671,230672,230675,230676,1520],{},"That distinction is deliberate rather than an oversight. Starting automation on the resolved\ndefault was tried and rejected: ",[941,230673,230674],{},"push: 'on-change'"," fires an unawaited push, which races anything\nelse touching the remote, so turning it on by inference made previously deterministic behavior\nracy — including a conflict-resolution ",[1092,230677,67177],{},[24204,230679,230681],{"id":230680},"rung-2-declare-a-policy","Rung 2 — declare a policy",[2217,230683,230685],{"className":2219,"code":230684,"language":2221,"meta":1037,"style":1037},"const db = await createNoydb({\n  user: 'me',\n  store: \u002F* … *\u002F,\n  sync: remote,\n  syncPolicy: {\n    push: { mode: 'on-change', minIntervalMs: 0, onUnload: true },\n    pull: { mode: 'interval', intervalMs: 60_000 },\n  },\n})\n",[941,230686,230687,230703,230717,230727,230737,230746,230785,230815,230819],{"__ignoreMap":1037},[2225,230688,230689,230691,230693,230695,230697,230699,230701],{"class":2227,"line":2228},[2225,230690,2775],{"class":2774},[2225,230692,2778],{"class":2239},[2225,230694,2781],{"class":2235},[2225,230696,2784],{"class":2231},[2225,230698,2744],{"class":2291},[2225,230700,2789],{"class":2239},[2225,230702,4200],{"class":2235},[2225,230704,230705,230707,230709,230711,230713,230715],{"class":2227,"line":1041},[2225,230706,4205],{"class":2307},[2225,230708,2311],{"class":2235},[2225,230710,2249],{"class":2235},[2225,230712,4212],{"class":2252},[2225,230714,2805],{"class":2235},[2225,230716,1707],{"class":2235},[2225,230718,230719,230721,230723,230725],{"class":2227,"line":1038},[2225,230720,15608],{"class":2307},[2225,230722,2311],{"class":2235},[2225,230724,10968],{"class":2763},[2225,230726,1707],{"class":2235},[2225,230728,230729,230731,230733,230735],{"class":2227,"line":2288},[2225,230730,34919],{"class":2307},[2225,230732,2311],{"class":2235},[2225,230734,89831],{"class":2239},[2225,230736,1707],{"class":2235},[2225,230738,230739,230742,230744],{"class":2227,"line":2304},[2225,230740,230741],{"class":2307},"  syncPolicy",[2225,230743,2311],{"class":2235},[2225,230745,2301],{"class":2235},[2225,230747,230748,230751,230753,230755,230757,230759,230761,230764,230766,230768,230770,230772,230774,230776,230779,230781,230783],{"class":2227,"line":2356},[2225,230749,230750],{"class":2307},"    push",[2225,230752,2311],{"class":2235},[2225,230754,2236],{"class":2235},[2225,230756,32303],{"class":2307},[2225,230758,2311],{"class":2235},[2225,230760,2249],{"class":2235},[2225,230762,230763],{"class":2252},"on-change",[2225,230765,2805],{"class":2235},[2225,230767,2298],{"class":2235},[2225,230769,227793],{"class":2307},[2225,230771,2311],{"class":2235},[2225,230773,8464],{"class":6527},[2225,230775,2298],{"class":2235},[2225,230777,230778],{"class":2307}," onUnload",[2225,230780,2311],{"class":2235},[2225,230782,2322],{"class":2321},[2225,230784,2353],{"class":2235},[2225,230786,230787,230790,230792,230794,230796,230798,230800,230802,230804,230806,230809,230811,230813],{"class":2227,"line":2830},[2225,230788,230789],{"class":2307},"    pull",[2225,230791,2311],{"class":2235},[2225,230793,2236],{"class":2235},[2225,230795,32303],{"class":2307},[2225,230797,2311],{"class":2235},[2225,230799,2249],{"class":2235},[2225,230801,227918],{"class":2252},[2225,230803,2805],{"class":2235},[2225,230805,2298],{"class":2235},[2225,230807,230808],{"class":2307}," intervalMs",[2225,230810,2311],{"class":2235},[2225,230812,19001],{"class":6527},[2225,230814,2353],{"class":2235},[2225,230816,230817],{"class":2227,"line":2861},[2225,230818,19017],{"class":2235},[2225,230820,230821,230823],{"class":2227,"line":4246},[2225,230822,2359],{"class":2235},[2225,230824,2362],{"class":2239},[1338,230826,230827,230837],{},[1341,230828,230829],{},[1344,230830,230831,230834],{},[1347,230832,230833],{},"Half",[1347,230835,230836],{},"Modes",[1357,230838,230839,230865],{},[1344,230840,230841,230845],{},[1362,230842,230843],{},[941,230844,82367],{},[1362,230846,230847,1368,230849,230851,230852,230854,230855,230857,230858,230860,230861,230854,230863],{},[941,230848,180353],{},[941,230850,230763],{}," (after every local write, respecting ",[941,230853,227876],{},") · ",[941,230856,227777],{}," (after ",[941,230859,227872],{}," of quiet, default ",[941,230862,204754],{},[941,230864,227918],{},[1344,230866,230867,230871],{},[1362,230868,230869],{},[941,230870,91149],{},[1362,230872,230873,1368,230875,953,230877,41109,230879,230854,230881,230884,230885],{},[941,230874,180353],{},[941,230876,227918],{},[941,230878,227890],{},[941,230880,204792],{},[941,230882,230883],{},"on-focus"," (tab regains visibility) · ",[941,230886,230887],{},"phased",[937,230889,230890,30200,230893,230895,230896,2601,230899,230902,230903,230906,230907,1520],{},[941,230891,230892],{},"push.onUnload",[941,230894,11790],{}," for non-manual modes) forces a final push on ",[941,230897,230898],{},"pagehide",[941,230900,230901],{},"visibilitychange → hidden"," in browsers and ",[941,230904,230905],{},"beforeExit"," in Node. The escape hatch — automation\noff, but declared rather than inferred — is\n",[941,230908,230909],{},"{ push: { mode: 'manual' }, pull: { mode: 'manual' } }",[937,230911,230912,230915,230916,230918,230919,226933,230921,230923,230924,230926,230927,230929,230930,230932],{},[950,230913,230914],{},"Scheduler-initiated pulls are role-gated."," Only a ",[941,230917,190253],{}," target auto-pulls; a ",[941,230920,190175],{},[941,230922,190178],{}," target with an ",[941,230925,227918],{}," pull policy does ",[950,230928,1004],{},", because a backup sink has no\nbusiness feeding data back. An explicit ",[941,230931,20594],{}," still pulls for every role.",[24204,230934,230936],{"id":230935},"rung-3-phased-pull","Rung 3 — phased pull",[2217,230938,230940],{"className":2219,"code":230939,"language":2221,"meta":1037,"style":1037},"pull: { mode: 'phased', sequence: ['clients', 'invoices', 'attachments'] }\n",[941,230941,230942],{"__ignoreMap":1037},[2225,230943,230944,230946,230948,230950,230952,230954,230956,230958,230960,230962,230965,230967,230969,230971,230973,230975,230977,230979,230981,230983,230985,230987,230989,230991,230993],{"class":2227,"line":2228},[2225,230945,91149],{"class":6342},[2225,230947,2311],{"class":2235},[2225,230949,2236],{"class":2235},[2225,230951,32303],{"class":6342},[2225,230953,2311],{"class":2235},[2225,230955,2249],{"class":2235},[2225,230957,230887],{"class":2252},[2225,230959,2805],{"class":2235},[2225,230961,2298],{"class":2235},[2225,230963,230964],{"class":6342}," sequence",[2225,230966,2311],{"class":2235},[2225,230968,8667],{"class":2307},[2225,230970,2805],{"class":2235},[2225,230972,23926],{"class":2252},[2225,230974,2805],{"class":2235},[2225,230976,2298],{"class":2235},[2225,230978,2249],{"class":2235},[2225,230980,6487],{"class":2252},[2225,230982,2805],{"class":2235},[2225,230984,2298],{"class":2235},[2225,230986,2249],{"class":2235},[2225,230988,78159],{"class":2252},[2225,230990,2805],{"class":2235},[2225,230992,8890],{"class":2307},[2225,230994,7351],{"class":2235},[937,230996,230997,230998,231001,231002,231005,231006,231008],{},"Collections are pulled ",[950,230999,231000],{},"one at a time, in the order given"," — each phase an ordinary\n",[941,231003,231004],{},"pull({ collections: [name] })",". It is strictly sequential; running phases concurrently would\ndefeat the prioritisation that is the entire point. A failing phase does not abort the ones behind\nit. When the sequence drains, the scheduler settles into steady state — ",[941,231007,227890],{}," if you gave\none, idle if you didn't — so bootstrap and steady-state sync are one flow rather than two APIs.",[937,231010,231011,231013,231014,231016,231017,1520],{},[941,231012,836],{}," entries must be non-empty and unique, and ",[941,231015,836],{}," is rejected unless\n",[941,231018,231019],{},"mode: 'phased'",[24204,231021,231023],{"id":231022},"rung-4-readiness","Rung 4 — readiness",[937,231025,231026,231027,231029],{},"Under a phased pull, a ",[941,231028,14639],{}," miss is ambiguous: the record may not exist, or its collection may\nsimply not have arrived yet. Readiness is how you tell those apart.",[2217,231031,231033],{"className":2219,"code":231032,"language":2221,"meta":1037,"style":1037},"const inv = await invoices.get('inv-1')\nconst { readiness } = db.syncStatus('acme')\n\nif (inv === null && readiness?.get('invoices') !== 'live') showSkeleton()\nelse if (inv === null) showNotFound()\n",[941,231034,231035,231061,231090,231094,231139],{"__ignoreMap":1037},[2225,231036,231037,231039,231041,231043,231045,231047,231049,231051,231053,231055,231057,231059],{"class":2227,"line":2228},[2225,231038,2775],{"class":2774},[2225,231040,6553],{"class":2239},[2225,231042,2781],{"class":2235},[2225,231044,2784],{"class":2231},[2225,231046,6503],{"class":2239},[2225,231048,1520],{"class":2235},[2225,231050,3158],{"class":2291},[2225,231052,2789],{"class":2239},[2225,231054,2805],{"class":2235},[2225,231056,6514],{"class":2252},[2225,231058,2805],{"class":2235},[2225,231060,2362],{"class":2239},[2225,231062,231063,231065,231067,231070,231072,231074,231076,231078,231080,231082,231084,231086,231088],{"class":2227,"line":1041},[2225,231064,2775],{"class":2774},[2225,231066,2236],{"class":2235},[2225,231068,231069],{"class":2239}," readiness ",[2225,231071,2359],{"class":2235},[2225,231073,6346],{"class":2235},[2225,231075,2842],{"class":2239},[2225,231077,1520],{"class":2235},[2225,231079,230351],{"class":2291},[2225,231081,2789],{"class":2239},[2225,231083,2805],{"class":2235},[2225,231085,6450],{"class":2252},[2225,231087,2805],{"class":2235},[2225,231089,2362],{"class":2239},[2225,231091,231092],{"class":2227,"line":1038},[2225,231093,2285],{"emptyLinePlaceholder":2284},[2225,231095,231096,231098,231101,231103,231105,231107,231110,231112,231114,231116,231118,231120,231122,231124,231126,231128,231130,231132,231134,231137],{"class":2227,"line":2288},[2225,231097,35129],{"class":2231},[2225,231099,231100],{"class":2239}," (inv ",[2225,231102,33083],{"class":2235},[2225,231104,7090],{"class":2235},[2225,231106,8486],{"class":2235},[2225,231108,231109],{"class":2239}," readiness",[2225,231111,8457],{"class":2235},[2225,231113,3158],{"class":2291},[2225,231115,2789],{"class":2239},[2225,231117,2805],{"class":2235},[2225,231119,6487],{"class":2252},[2225,231121,2805],{"class":2235},[2225,231123,8495],{"class":2239},[2225,231125,32545],{"class":2235},[2225,231127,2249],{"class":2235},[2225,231129,727],{"class":2252},[2225,231131,2805],{"class":2235},[2225,231133,8495],{"class":2239},[2225,231135,231136],{"class":2291},"showSkeleton",[2225,231138,8146],{"class":2239},[2225,231140,231141,231143,231145,231147,231149,231151,231153,231156],{"class":2227,"line":2304},[2225,231142,46145],{"class":2231},[2225,231144,46148],{"class":2231},[2225,231146,231100],{"class":2239},[2225,231148,33083],{"class":2235},[2225,231150,7090],{"class":2235},[2225,231152,8495],{"class":2239},[2225,231154,231155],{"class":2291},"showNotFound",[2225,231157,8146],{"class":2239},[1338,231159,231160,231169],{},[1341,231161,231162],{},[1344,231163,231164,231166],{},[1347,231165,37666],{},[1347,231167,231168],{},"What it licenses",[1357,231170,231171,231180,231204],{},[1344,231172,231173,231177],{},[1362,231174,231175],{},[941,231176,204494],{},[1362,231178,231179],{},"Not pulled yet, or its phase did not finish cleanly — a miss proves nothing",[1344,231181,231182,231187],{},[1362,231183,231184],{},[941,231185,231186],{},"'pulling'",[1362,231188,231189,231190,231193,231194,231197,231198,231200,231201,231203],{},"Phase in flight. ",[950,231191,231192],{},"Never terminal",": it always resolves to ",[941,231195,231196],{},"'live'"," or back to ",[941,231199,204494],{},", so a stuck ",[941,231202,231186],{}," can't leave a permanent skeleton",[1344,231205,231206,231210],{},[1362,231207,231208],{},[941,231209,231196],{},[1362,231211,231212,231213,231215],{},"Phase completed cleanly, so a miss ",[950,231214,3082],{}," a real absence",[937,231217,231218],{},"Three rules are easy to get wrong:",[1111,231220,231221,231250,231261],{},[1114,231222,231223,231228,231229,203399,231231,231234,231235,231238,231239,1625,231241,231244,231245,1625,231247,231249],{},[950,231224,231225,231226,1520],{},"Only a clean phase reaches ",[941,231227,231196],{}," A pull that reports errors leaves the collection\n",[941,231230,204494],{},[941,231232,231233],{},"PullResult.errors"," accumulates ",[1092,231236,231237],{},"without throwing",", so a partial failure is\ninvisible to a ",[941,231240,7554],{},[941,231242,231243],{},"catch",". So does a pull that throws, and one skipped by the role gate on a\n",[941,231246,50702],{},[941,231248,605],{}," target. Completeness is never claimed by a target that never read.",[1114,231251,231252,1324,231255,140633,231257,231260],{},[950,231253,231254],{},"A collection the sequence never names is absent from the map.",[941,231256,18316],{},[1092,231258,231259],{},"\"no claim\nmade\"",", which is never a reason to gate a UI.",[1114,231262,231263,7356,231266,231269,231270,231272,231273,997,231276,70057,231278,231281],{},[941,231264,231265],{},"phase",[941,231267,231268],{},"{ index, total }"," (1-based) while the sequence runs and ",[941,231271,6651],{}," once it drains.\nBoth ",[941,231274,231275],{},"readiness",[941,231277,231265],{},[950,231279,231280],{},"absent entirely"," for non-phased policies.",[7646,231283,231284],{"color":11278,"icon":139875},[937,231285,231286,231287,997,231289,231291,231292,231295,231296,231299,231300,231303,231304,231307,231308,231310,231311,231313],{},"Rungs 3 and 4 — ",[941,231288,231019],{},[941,231290,231275],{}," — arrived in ",[941,231293,231294],{},"0.4.0-pre.10",", and so did the\nscheduler running at all: below that version ",[941,231297,231298],{},"startScheduler()"," had no callers and\n",[941,231301,231302],{},"notifyChange()"," returned at its ",[941,231305,231306],{},"if (!this.started)"," guard, so ",[950,231309,11313],{}," policy ran automatically.\nEvery published version from ",[941,231312,231294],{}," on has all four rungs — the rung you get is decided by\nthe hub version you install, not by which dist-tag you install from.",[1591,231315,231317],{"id":231316},"deletes-converge-as-delete-markers","Deletes converge as delete markers",[937,231319,10993,231320,968,231322,56825,231325,231327,231328,231330,231331,231333,231334,1625,231336,231338,231339,2557,231341,231343,231344,231347,231348,231350,231351,231353],{},[941,231321,9471],{},[941,231323,231324],{},"collection.delete()",[950,231326,56841],{}," vault\nno longer physically removes the envelope. It writes a version-ordered ",[950,231329,9463],{}," instead —\nan envelope with ",[941,231332,9459],{}," and an empty body — so the delete travels through ",[941,231335,82367],{},[941,231337,91149],{}," like\nany other write and an offline peer can no longer resurrect a deleted record by pushing its stale\ncopy back. Reads treat a marker as absent (",[941,231340,14639],{},[941,231342,6651],{},"), and a legitimate re-create at a\nhigher version still resurrects the id — guaranteed ",[1092,231345,231346],{},"non","-resurrection remains\n",[1007,231349,685],{"href":686},"'s job, not ",[941,231352,9467],{},"'s. A concurrent same-version delete-vs-edit\nresolves through the collection's conflict resolver when one is configured; without one, the\ndelete wins. Local-only (non-synced) collections keep physical deletes — nothing changes there.",[937,231355,231356,231357,231359,231360,231362,231363,231365,231366,231368,231369,231371,231372,1520],{},"The marker rides the store contract as an optional ",[941,231358,15087],{}," field on the encrypted envelope (see\n",[1007,231361,6891],{"href":51},") — additive, and every ",[941,231364,1779],{}," store round-trips it (it is part of the\nshared adapter-conformance suite). Markers accumulate until explicitly reclaimed: the\n",[1007,231367,776],{"href":777}," service purges a closed period's markers locally with\n",[941,231370,56997],{}," and sweeps them off push-only sync targets with\n",[941,231373,57004],{},[937,231375,231376,231377,231379,231380,231382,231383,231385,231386,231388,231389,1520],{},"Reserved dictionary collections (",[941,231378,6796],{},", backing ",[1007,231381,731],{"href":732}," fields) participate\nin sync the same way since ",[941,231384,111464],{},": dictionary writes enter the dirty log, ",[941,231387,56862],{}," applies\nthem through the ordinary path, and dictionary deletes travel as the same delete markers — see\n",[1007,231390,231392],{"href":231391},"\u002Fservices\u002Flookup#reserved-tier-sync-dictionaries-travel","lookup → reserved-tier sync",[1591,231394,231396],{"id":231395},"erasures-are-terminal","Erasures are terminal",[937,231398,231399,231400,231402,231403,231405,231406,231408,231409,231411,231412,231414,231415,231417,231418,231420,231421,231424,231425,231428],{},"A record erased with ",[1007,231401,685],{"href":686}," is crypto-shredded down to a tombstone, and since\n",[941,231404,9471],{}," sync treats that tombstone as ",[950,231407,56895],{}," for its record id: ",[941,231410,56862],{}," never\noverwrites a tombstone with a live remote envelope regardless of version — it re-asserts the shred\noutward instead — and ",[941,231413,56859],{}," asserts tombstones unconditionally, bypassing conflict resolvers\nentirely (an erasure cannot be overruled by a merge function). Forget tombstones also enter the\nsync dirty log, so the shred itself propagates on the next ",[941,231416,56859],{},". Every suppressed live envelope\nis reported on the ",[941,231419,230211],{}," array of the push\u002Fpull result (the ",[941,231422,231423],{},"ErasureEnforcement"," shape under\nTypes below) and through the ",[941,231426,231427],{},"sync:erasure"," event:",[2217,231430,231432],{"className":2219,"code":231431,"language":2221,"meta":1037,"style":1037},"db.on('sync:erasure', (e) => {\n  console.log(`${e.direction}: erased ${e.collection}\u002F${e.id} won over a live edit`)\n})\n",[941,231433,231434,231462,231515],{"__ignoreMap":1037},[2225,231435,231436,231438,231440,231442,231444,231446,231448,231450,231452,231454,231456,231458,231460],{"class":2227,"line":2228},[2225,231437,6590],{"class":2239},[2225,231439,1520],{"class":2235},[2225,231441,4883],{"class":2291},[2225,231443,2789],{"class":2239},[2225,231445,2805],{"class":2235},[2225,231447,231427],{"class":2252},[2225,231449,2805],{"class":2235},[2225,231451,2298],{"class":2235},[2225,231453,953],{"class":2235},[2225,231455,12450],{"class":7052},[2225,231457,1381],{"class":2235},[2225,231459,8116],{"class":2774},[2225,231461,2301],{"class":2235},[2225,231463,231464,231466,231468,231470,231472,231474,231476,231478,231481,231483,231486,231488,231490,231492,231494,231496,231498,231500,231502,231504,231506,231508,231511,231513],{"class":2227,"line":1041},[2225,231465,27095],{"class":2239},[2225,231467,1520],{"class":2235},[2225,231469,27100],{"class":2291},[2225,231471,2789],{"class":2307},[2225,231473,91449],{"class":2235},[2225,231475,12450],{"class":2239},[2225,231477,1520],{"class":2235},[2225,231479,231480],{"class":2239},"direction",[2225,231482,2359],{"class":2235},[2225,231484,231485],{"class":2252},": erased ",[2225,231487,8197],{"class":2235},[2225,231489,12450],{"class":2239},[2225,231491,1520],{"class":2235},[2225,231493,6471],{"class":2239},[2225,231495,2359],{"class":2235},[2225,231497,1625],{"class":2252},[2225,231499,8197],{"class":2235},[2225,231501,12450],{"class":2239},[2225,231503,1520],{"class":2235},[2225,231505,6983],{"class":2239},[2225,231507,2359],{"class":2235},[2225,231509,231510],{"class":2252}," won over a live edit",[2225,231512,44476],{"class":2235},[2225,231514,2362],{"class":2307},[2225,231516,231517,231519],{"class":2227,"line":1038},[2225,231518,2359],{"class":2235},[2225,231520,2362],{"class":2239},[937,231522,231523,231524,968,231526,51161,231528,231530,231531,1625,231533,231535,231536,231538,231539,231541],{},"Sync-applied writes also refresh the collection's in-memory caches, so a reader in the same\nsession sees pull results immediately — and never a decrypted residue of a shredded record. On\nvaults using ",[1007,231525,665],{"href":666},[1007,231527,124625],{"href":740},[1007,231529,637],{"href":638}," fields, a batched dispatch wave at the end of each ",[941,231532,56862],{},[941,231534,56859],{},"\nrecomputes affected derived targets exactly once per target (since ",[941,231537,111464],{}," — see the\ndependency graph on the ",[1007,231540,896],{"href":897}," page).",[1591,231543,95935],{"id":231544},"conflict-resolution",[937,231546,231547,231548,1625,231550,231552,231553,231556],{},"When both sides changed the same record, ",[941,231549,82367],{},[941,231551,91149],{}," resolve the conflict instead of failing.\nThe database-level default is ",[941,231554,231555],{},"conflict: 'version'"," — whichever side has the higher internal\nrecord version wins:",[2217,231558,231560],{"className":2219,"code":231559,"language":2221,"meta":1037,"style":1037},"const db = await createNoydb({\n  store: localStore, sync: remoteStore, user: 'alice', secret: 'alice-secret-2026',\n  syncStrategy: withSync(),\n  conflict: 'remote-wins',   \u002F\u002F or 'local-wins', or (conflict) => 'local' | 'remote'\n})\n",[941,231561,231562,231578,231620,231632,231651],{"__ignoreMap":1037},[2225,231563,231564,231566,231568,231570,231572,231574,231576],{"class":2227,"line":2228},[2225,231565,2775],{"class":2774},[2225,231567,2778],{"class":2239},[2225,231569,2781],{"class":2235},[2225,231571,2784],{"class":2231},[2225,231573,2744],{"class":2291},[2225,231575,2789],{"class":2239},[2225,231577,4200],{"class":2235},[2225,231579,231580,231582,231584,231586,231588,231590,231592,231594,231596,231598,231600,231602,231604,231606,231608,231610,231612,231614,231616,231618],{"class":2227,"line":1041},[2225,231581,15608],{"class":2307},[2225,231583,2311],{"class":2235},[2225,231585,229742],{"class":2239},[2225,231587,2298],{"class":2235},[2225,231589,129892],{"class":2307},[2225,231591,2311],{"class":2235},[2225,231593,229756],{"class":2239},[2225,231595,2298],{"class":2235},[2225,231597,2795],{"class":2307},[2225,231599,2311],{"class":2235},[2225,231601,2249],{"class":2235},[2225,231603,6408],{"class":2252},[2225,231605,2805],{"class":2235},[2225,231607,2298],{"class":2235},[2225,231609,2810],{"class":2307},[2225,231611,2311],{"class":2235},[2225,231613,2249],{"class":2235},[2225,231615,107607],{"class":2252},[2225,231617,2805],{"class":2235},[2225,231619,1707],{"class":2235},[2225,231621,231622,231624,231626,231628,231630],{"class":2227,"line":1038},[2225,231623,89901],{"class":2307},[2225,231625,2311],{"class":2235},[2225,231627,89579],{"class":2291},[2225,231629,4228],{"class":2239},[2225,231631,1707],{"class":2235},[2225,231633,231634,231637,231639,231641,231644,231646,231648],{"class":2227,"line":2288},[2225,231635,231636],{"class":2307},"  conflict",[2225,231638,2311],{"class":2235},[2225,231640,2249],{"class":2235},[2225,231642,231643],{"class":2252},"remote-wins",[2225,231645,2805],{"class":2235},[2225,231647,2298],{"class":2235},[2225,231649,231650],{"class":2763},"   \u002F\u002F or 'local-wins', or (conflict) => 'local' | 'remote'\n",[2225,231652,231653,231655],{"class":2227,"line":2304},[2225,231654,2359],{"class":2235},[2225,231656,2362],{"class":2239},[937,231658,231659,231660,231662],{},"Override per collection with ",[941,231661,91981],{},", which offers richer choices — a custom function here\nreceives and returns the record content directly, not just a side:",[2217,231664,231666],{"className":2219,"code":231665,"language":2221,"meta":1037,"style":1037},"const notes = vault.collection\u003CNote>('notes', {\n  conflictPolicy: 'last-writer-wins',   \u002F\u002F or 'first-writer-wins', 'manual', or (local, remote) => merged\n})\n",[941,231667,231668,231700,231717],{"__ignoreMap":1037},[2225,231669,231670,231672,231674,231676,231678,231680,231682,231684,231686,231688,231690,231692,231694,231696,231698],{"class":2227,"line":2228},[2225,231671,2775],{"class":2774},[2225,231673,42762],{"class":2239},[2225,231675,2781],{"class":2235},[2225,231677,6466],{"class":2239},[2225,231679,1520],{"class":2235},[2225,231681,6471],{"class":2291},[2225,231683,6474],{"class":2235},[2225,231685,86814],{"class":6342},[2225,231687,6480],{"class":2235},[2225,231689,2789],{"class":2239},[2225,231691,2805],{"class":2235},[2225,231693,2854],{"class":2252},[2225,231695,2805],{"class":2235},[2225,231697,2298],{"class":2235},[2225,231699,2301],{"class":2235},[2225,231701,231702,231704,231706,231708,231710,231712,231714],{"class":2227,"line":1041},[2225,231703,92072],{"class":2307},[2225,231705,2311],{"class":2235},[2225,231707,2249],{"class":2235},[2225,231709,92079],{"class":2252},[2225,231711,2805],{"class":2235},[2225,231713,2298],{"class":2235},[2225,231715,231716],{"class":2763},"   \u002F\u002F or 'first-writer-wins', 'manual', or (local, remote) => merged\n",[2225,231718,231719,231721],{"class":2227,"line":1038},[2225,231720,2359],{"class":2235},[2225,231722,2362],{"class":2239},[937,231724,231725,231726,231728,231729,1625,231732,231735],{},"Every resolved conflict is also reported on the ",[941,231727,91240],{}," array of the ",[941,231730,231731],{},"PushResult",[941,231733,231734],{},"PullResult","\nshown above, with both sides' record version:",[2217,231737,231739],{"className":6604,"code":231738,"language":141,"meta":1037,"style":1037},"{\n  \"vault\": \"notes\", \"collection\": \"notes\", \"id\": \"n1\",\n  \"localVersion\": 2, \"remoteVersion\": 3,\n  \"local\": \"{ encrypted envelope }\", \"remote\": \"{ encrypted envelope }\"\n}\n",[941,231740,231741,231745,231795,231823,231856],{"__ignoreMap":1037},[2225,231742,231743],{"class":2227,"line":2228},[2225,231744,4200],{"class":2235},[2225,231746,231747,231749,231751,231753,231755,231757,231759,231761,231763,231765,231767,231769,231771,231773,231775,231777,231779,231781,231783,231785,231787,231789,231791,231793],{"class":2227,"line":1041},[2225,231748,9368],{"class":2235},[2225,231750,2694],{"class":2774},[2225,231752,6620],{"class":2235},[2225,231754,2311],{"class":2235},[2225,231756,6614],{"class":2235},[2225,231758,2854],{"class":2252},[2225,231760,6620],{"class":2235},[2225,231762,2298],{"class":2235},[2225,231764,6614],{"class":2235},[2225,231766,6471],{"class":2774},[2225,231768,6620],{"class":2235},[2225,231770,2311],{"class":2235},[2225,231772,6614],{"class":2235},[2225,231774,2854],{"class":2252},[2225,231776,6620],{"class":2235},[2225,231778,2298],{"class":2235},[2225,231780,6614],{"class":2235},[2225,231782,6983],{"class":2774},[2225,231784,6620],{"class":2235},[2225,231786,2311],{"class":2235},[2225,231788,6614],{"class":2235},[2225,231790,129502],{"class":2252},[2225,231792,6620],{"class":2235},[2225,231794,1707],{"class":2235},[2225,231796,231797,231799,231802,231804,231806,231808,231810,231812,231815,231817,231819,231821],{"class":2227,"line":1038},[2225,231798,9368],{"class":2235},[2225,231800,231801],{"class":2774},"localVersion",[2225,231803,6620],{"class":2235},[2225,231805,2311],{"class":2235},[2225,231807,18961],{"class":6527},[2225,231809,2298],{"class":2235},[2225,231811,6614],{"class":2235},[2225,231813,231814],{"class":2774},"remoteVersion",[2225,231816,6620],{"class":2235},[2225,231818,2311],{"class":2235},[2225,231820,14460],{"class":6527},[2225,231822,1707],{"class":2235},[2225,231824,231825,231827,231829,231831,231833,231835,231838,231840,231842,231844,231846,231848,231850,231852,231854],{"class":2227,"line":2288},[2225,231826,9368],{"class":2235},[2225,231828,49766],{"class":2774},[2225,231830,6620],{"class":2235},[2225,231832,2311],{"class":2235},[2225,231834,6614],{"class":2235},[2225,231836,231837],{"class":2252},"{ encrypted envelope }",[2225,231839,6620],{"class":2235},[2225,231841,2298],{"class":2235},[2225,231843,6614],{"class":2235},[2225,231845,70066],{"class":2774},[2225,231847,6620],{"class":2235},[2225,231849,2311],{"class":2235},[2225,231851,6614],{"class":2235},[2225,231853,231837],{"class":2252},[2225,231855,9449],{"class":2235},[2225,231857,231858],{"class":2227,"line":2304},[2225,231859,7351],{"class":2235},[937,231861,231862,231867,231868,231870,231871,231874,231875,231880,231881,231883,231884,231887,231888,231890,231891,231894,231895,14388],{},[950,231863,231864,231865,957],{},"Same-version ties converge (since hub ",[941,231866,24551],{}," When a push CAS conflict resolves local-wins and the winning envelope's ",[941,231869,1700],{}," does not already exceed the remote's — the concurrent-edit tie — the engine re-stamps the winner at ",[941,231872,231873],{},"remote._v + 1"," and mirrors the advanced envelope into the local store (",[1007,231876,231879],{"href":231877,"rel":231878},"https:\u002F\u002Fgithub.com\u002Fnoy-db\u002Fcore\u002Fissues\u002F936",[3371],"noy-db#936","). The losing peer's next ",[941,231882,56862],{}," then sees a genuine version advance and adopts the winner through the ordinary pull path, instead of reporting ",[941,231885,231886],{},"pulled: 0"," while both peers hold different content at the same ",[941,231889,1700],{},". (A ",[941,231892,231893],{},"'merged'"," resolution already stamped ",[941,231896,231897],{},"max(local, remote) + 1",[937,231899,231900,231903,231904,231907,231908,231910,231911,231913,231914,1261,231917,231919,231920,968,231922,968,231924,231926,231927,231929,231930,231932],{},[950,231901,231902],{},"A delete marker does not always beat a concurrent edit."," For an ",[1092,231905,231906],{},"ordinary"," delete marker (from\n",[941,231909,9467],{}," — distinct from a terminal forget tombstone, which is never overruled), whether the\ndelete or the edit wins depends on the policy. A custom-function ",[941,231912,91981],{}," and the CRDT\nmodes ",[941,231915,231916],{},"'lww-map'",[941,231918,89445],{}," can never keep an edit over a delete: the shared resolver checks for a\ndeleted side first and returns it unconditionally, before the merge function or the CRDT merge runs.\n",[941,231921,92108],{},[941,231923,92120],{},[941,231925,92133],{},", and CRDT mode ",[941,231928,89448],{}," (a raw version\ncompare, no decrypt) do ",[950,231931,1004],{}," special-case delete markers — so a higher-versioned edit can win\nover the delete under those.",[1591,231934,25646],{"id":91292},[937,231936,231937,231940,231941,231943,231944,231946],{},[941,231938,231939],{},"collection.presence\u003CP>(opts?)"," returns a handle scoped to one collection. ",[941,231942,39249],{}," announces\nyour own payload; ",[941,231945,25045],{}," receives everyone else's:",[2217,231948,231950],{"className":2219,"code":231949,"language":2221,"meta":1037,"style":1037},"interface Cursor { path: string; action: 'viewing' | 'editing' }\nconst presence = notes.presence\u003CCursor>()\n\nconst unsubscribe = presence.subscribe((peers) => {\n  console.log(peers)\n})\n\nawait presence.update({ path: 'notes\u002Fn1', action: 'editing' })\n",[941,231951,231952,231991,232014,232018,232044,232058,232064,232068],{"__ignoreMap":1037},[2225,231953,231954,231956,231959,231961,231963,231965,231967,231969,231971,231973,231975,231978,231980,231982,231984,231987,231989],{"class":2227,"line":2228},[2225,231955,6994],{"class":2774},[2225,231957,231958],{"class":6342}," Cursor",[2225,231960,2236],{"class":2235},[2225,231962,53037],{"class":2307},[2225,231964,2311],{"class":2235},[2225,231966,6366],{"class":6342},[2225,231968,3769],{"class":2235},[2225,231970,210073],{"class":2307},[2225,231972,2311],{"class":2235},[2225,231974,2249],{"class":2235},[2225,231976,231977],{"class":2252},"viewing",[2225,231979,2805],{"class":2235},[2225,231981,7087],{"class":2235},[2225,231983,2249],{"class":2235},[2225,231985,231986],{"class":2252},"editing",[2225,231988,2805],{"class":2235},[2225,231990,6369],{"class":2235},[2225,231992,231993,231995,231997,231999,232001,232003,232005,232007,232010,232012],{"class":2227,"line":1041},[2225,231994,2775],{"class":2774},[2225,231996,91265],{"class":2239},[2225,231998,2781],{"class":2235},[2225,232000,42817],{"class":2239},[2225,232002,1520],{"class":2235},[2225,232004,91292],{"class":2291},[2225,232006,6474],{"class":2235},[2225,232008,232009],{"class":6342},"Cursor",[2225,232011,6480],{"class":2235},[2225,232013,8146],{"class":2239},[2225,232015,232016],{"class":2227,"line":1038},[2225,232017,2285],{"emptyLinePlaceholder":2284},[2225,232019,232020,232022,232024,232026,232028,232030,232032,232034,232036,232038,232040,232042],{"class":2227,"line":2288},[2225,232021,2775],{"class":2774},[2225,232023,91395],{"class":2239},[2225,232025,2781],{"class":2235},[2225,232027,91339],{"class":2239},[2225,232029,1520],{"class":2235},[2225,232031,25042],{"class":2291},[2225,232033,2789],{"class":2239},[2225,232035,2789],{"class":2235},[2225,232037,91410],{"class":7052},[2225,232039,1381],{"class":2235},[2225,232041,8116],{"class":2774},[2225,232043,2301],{"class":2235},[2225,232045,232046,232048,232050,232052,232054,232056],{"class":2227,"line":2304},[2225,232047,27095],{"class":2239},[2225,232049,1520],{"class":2235},[2225,232051,27100],{"class":2291},[2225,232053,2789],{"class":2307},[2225,232055,91410],{"class":2239},[2225,232057,2362],{"class":2307},[2225,232059,232060,232062],{"class":2227,"line":2356},[2225,232061,2359],{"class":2235},[2225,232063,2362],{"class":2239},[2225,232065,232066],{"class":2227,"line":2830},[2225,232067,2285],{"emptyLinePlaceholder":2284},[2225,232069,232070,232072,232074,232076,232078,232080,232082,232084,232086,232088,232091,232093,232095,232097,232099,232101,232103,232105,232107],{"class":2227,"line":2861},[2225,232071,6500],{"class":2231},[2225,232073,91339],{"class":2239},[2225,232075,1520],{"class":2235},[2225,232077,39210],{"class":2291},[2225,232079,2789],{"class":2239},[2225,232081,2792],{"class":2235},[2225,232083,53037],{"class":2307},[2225,232085,2311],{"class":2235},[2225,232087,2249],{"class":2235},[2225,232089,232090],{"class":2252},"notes\u002Fn1",[2225,232092,2805],{"class":2235},[2225,232094,2298],{"class":2235},[2225,232096,210073],{"class":2307},[2225,232098,2311],{"class":2235},[2225,232100,2249],{"class":2235},[2225,232102,231986],{"class":2252},[2225,232104,2805],{"class":2235},[2225,232106,2243],{"class":2235},[2225,232108,2362],{"class":2239},[2217,232110,232112],{"className":6604,"code":232111,"language":141,"meta":1037,"style":1037},"[\n  { \"userId\": \"bob\", \"payload\": { \"path\": \"notes\u002Fn1\", \"action\": \"viewing\" }, \"lastSeen\": \"2026-07-08T10:03:40.000Z\" }\n]\n",[941,232113,232114,232118,232198],{"__ignoreMap":1037},[2225,232115,232116],{"class":2227,"line":2228},[2225,232117,26349],{"class":2235},[2225,232119,232120,232122,232124,232126,232128,232130,232132,232134,232136,232138,232140,232142,232144,232146,232148,232150,232152,232154,232156,232158,232160,232162,232164,232166,232168,232170,232172,232174,232176,232178,232180,232182,232185,232187,232189,232191,232194,232196],{"class":2227,"line":1041},[2225,232121,26354],{"class":2235},[2225,232123,6614],{"class":2235},[2225,232125,10749],{"class":2774},[2225,232127,6620],{"class":2235},[2225,232129,2311],{"class":2235},[2225,232131,6614],{"class":2235},[2225,232133,91596],{"class":2252},[2225,232135,6620],{"class":2235},[2225,232137,2298],{"class":2235},[2225,232139,6614],{"class":2235},[2225,232141,66738],{"class":2774},[2225,232143,6620],{"class":2235},[2225,232145,2311],{"class":2235},[2225,232147,2236],{"class":2235},[2225,232149,6614],{"class":2235},[2225,232151,52937],{"class":6342},[2225,232153,6620],{"class":2235},[2225,232155,2311],{"class":2235},[2225,232157,6614],{"class":2235},[2225,232159,232090],{"class":2252},[2225,232161,6620],{"class":2235},[2225,232163,2298],{"class":2235},[2225,232165,6614],{"class":2235},[2225,232167,71177],{"class":6342},[2225,232169,6620],{"class":2235},[2225,232171,2311],{"class":2235},[2225,232173,6614],{"class":2235},[2225,232175,231977],{"class":2252},[2225,232177,6620],{"class":2235},[2225,232179,10965],{"class":2235},[2225,232181,6614],{"class":2235},[2225,232183,232184],{"class":2774},"lastSeen",[2225,232186,6620],{"class":2235},[2225,232188,2311],{"class":2235},[2225,232190,6614],{"class":2235},[2225,232192,232193],{"class":2252},"2026-07-08T10:03:40.000Z",[2225,232195,6620],{"class":2235},[2225,232197,6369],{"class":2235},[2225,232199,232200],{"class":2227,"line":1038},[2225,232201,26484],{"class":2235},[937,232203,54460,232204,232207,232208,232211,232212,1625,232214,232216,232217,232219,232220,232223,232224,10750,232226,232228,232229,232231],{},[941,232205,232206],{},"unsubscribe()"," to stop receiving updates, and ",[941,232209,232210],{},"presence.stop()"," to release the handle\nentirely (stops polling, drops the pub\u002Fsub listener, clears subscribers). When the store adapter\nimplements ",[941,232213,91537],{},[941,232215,91540],{},", presence is real-time; otherwise it falls back to\npolling a reserved ",[941,232218,6780],{}," collection on the sync target (or the local store if\nnone is configured) every ",[941,232221,232222],{},"pollIntervalMs"," (default 5,000 ms). Each polled record stores ",[941,232225,10749],{},[941,232227,232184],{}," as plain fields alongside the encrypted ",[941,232230,66738],{}," — the storage-poll fallback trades\nadapter-side user-ID privacy for working on any adapter, unlike the pub\u002Fsub path above.",[1022,232233,102889],{"id":102888},[1338,232235,232236,232246],{},[1341,232237,232238],{},[1344,232239,232240,232242,232244],{},[1347,232241,7831],{},[1347,232243,102906],{},[1347,232245,7834],{},[1357,232247,232248,232262,232276,232297,232311],{},[1344,232249,232250,232254,232259],{},[1362,232251,232252],{},[941,232253,230224],{},[1362,232255,232256],{},[941,232257,232258],{},"Promise\u003CPushResult>",[1362,232260,232261],{},"Send dirty local records to the vault's primary sync target.",[1344,232263,232264,232268,232273],{},[1362,232265,232266],{},[941,232267,230236],{},[1362,232269,232270],{},[941,232271,232272],{},"Promise\u003CPullResult>",[1362,232274,232275],{},"Apply newer remote records to the local store.",[1344,232277,232278,232282,232287],{},[1362,232279,232280],{},[941,232281,230435],{},[1362,232283,232284],{},[941,232285,232286],{},"Promise\u003C{ pull: PullResult, push: PushResult }>",[1362,232288,232289,232290,1625,232292,232294,232295,1520],{},"Pull then push against the primary target; also pushes to every ",[941,232291,190175],{},[941,232293,190178],{}," target and syncs every other ",[941,232296,190253],{},[1344,232298,232299,232303,232308],{},[1362,232300,232301],{},[941,232302,230329],{},[1362,232304,232305],{},[941,232306,232307],{},"SyncStatus",[1362,232309,232310],{},"Current dirty count and last push\u002Fpull timestamps.",[1344,232312,232313,232317,232322],{},[1362,232314,232315],{},[941,232316,231939],{},[1362,232318,232319],{},[941,232320,232321],{},"PresenceHandle\u003CP>",[1362,232323,232324,232325,2085,232327,1520],{},"A presence channel scoped to this collection. ",[941,232326,8097],{},[941,232328,232329],{},"{ staleMs?, pollIntervalMs? }",[1591,232331,204640],{"id":16362},[1338,232333,232334,232345],{},[1341,232335,232336],{},[1344,232337,232338,232341,232343],{},[1347,232339,232340],{},"Option bag",[1347,232342,101104],{},[1347,232344,20200],{},[1357,232346,232347,232362,232376,232391,232411],{},[1344,232348,232349,232354,232359],{},[1362,232350,232351],{},[941,232352,232353],{},"PushOptions",[1362,232355,232356],{},[941,232357,232358],{},"collections?: string[]",[1362,232360,232361],{},"Push only these collections; omit to push everything dirty.",[1344,232363,232364,232369,232373],{},[1362,232365,232366],{},[941,232367,232368],{},"PullOptions",[1362,232370,232371],{},[941,232372,232358],{},[1362,232374,232375],{},"Pull only these collections; omit to pull everything.",[1344,232377,232378,232380,232385],{},[1362,232379],{},[1362,232381,232382],{},[941,232383,232384],{},"modifiedSince?: string",[1362,232386,232387,232388,232390],{},"Only pull records with ",[941,232389,1703],{}," strictly after this ISO timestamp.",[1344,232392,232393,232401,232406],{},[1362,232394,232395,192640,232398,1381],{},[941,232396,232397],{},"PresenceHandleOpts",[941,232399,232400],{},"presence(opts)",[1362,232402,232403],{},[941,232404,232405],{},"staleMs?: number",[1362,232407,232408,232409,1520],{},"How long before a peer is considered gone. Default ",[941,232410,204754],{},[1344,232412,232413,232415,232420],{},[1362,232414],{},[1362,232416,232417],{},[941,232418,232419],{},"pollIntervalMs?: number",[1362,232421,232422,232423,1520],{},"Poll interval for the storage-poll fallback. Default ",[941,232424,232425],{},"5_000",[1591,232427,232429],{"id":232428},"presencehandlep",[941,232430,232321],{},[1338,232432,232433,232443],{},[1341,232434,232435],{},[1344,232436,232437,232439,232441],{},[1347,232438,7831],{},[1347,232440,102906],{},[1347,232442,7834],{},[1357,232444,232445,232459,232474],{},[1344,232446,232447,232452,232456],{},[1362,232448,232449],{},[941,232450,232451],{},"update(payload: P)",[1362,232453,232454],{},[941,232455,116014],{},[1362,232457,232458],{},"Encrypt and publish your presence payload.",[1344,232460,232461,232466,232471],{},[1362,232462,232463],{},[941,232464,232465],{},"subscribe(cb: (peers: PresencePeer\u003CP>[]) => void)",[1362,232467,232468],{},[941,232469,232470],{},"() => void",[1362,232472,232473],{},"Register for peer updates; returns an unsubscribe function.",[1344,232475,232476,232480,232484],{},[1362,232477,232478],{},[941,232479,202350],{},[1362,232481,232482],{},[941,232483,7165],{},[1362,232485,232486],{},"Release the handle — stops polling\u002Flistening, clears subscribers.",[1591,232488,103205],{"id":128135},[1111,232490,232491,232498,232505,232512,232526,232547,232555,232562,232576],{},[1114,232492,232493,964,232495],{},[941,232494,231731],{},[941,232496,232497],{},"{ pushed: number, conflicts: Conflict[], errors: Error[], erasures: ErasureEnforcement[] }",[1114,232499,232500,964,232502],{},[941,232501,231734],{},[941,232503,232504],{},"{ pulled: number, conflicts: Conflict[], errors: Error[], erasures: ErasureEnforcement[] }",[1114,232506,232507,964,232509],{},[941,232508,232307],{},[941,232510,232511],{},"{ dirty: number, lastPush: string | null, lastPull: string | null, online: boolean }",[1114,232513,232514,964,232517,66237,232520,232522,232523],{},[941,232515,232516],{},"Conflict",[941,232518,232519],{},"{ vault, collection, id, local, remote, localVersion, remoteVersion, resolve? }",[941,232521,54399],{}," is only present under ",[941,232524,232525],{},"conflictPolicy: 'manual'",[1114,232527,232528,964,232530,232533,232534,232536,232537,232540,232541,232543,232544,232546],{},[941,232529,231423],{},[941,232531,232532],{},"{ vault, collection, id, tombstone, suppressed, direction: 'pull' | 'push' }","\n— reported whenever push\u002Fpull encounters a record erased with ",[1007,232535,685],{"href":686}," on one\nside and a live edit on the other; the tombstone always wins, ",[941,232538,232539],{},"suppressed"," carries the envelope\nit overwrote (also emitted per enforcement as the ",[941,232542,231427],{}," event). Empty (",[941,232545,7262],{},") unless a\nsync run actually re-asserts an erasure.",[1114,232548,232549,964,232552],{},[941,232550,232551],{},"PresencePeer\u003CP>",[941,232553,232554],{},"{ userId: string, payload: P, lastSeen: string }",[1114,232556,232557,964,232559],{},[941,232558,34695],{},[941,232560,232561],{},"{ store, role: 'sync-peer' | 'backup' | 'archive', policy?, label? }",[1114,232563,232564,232566,232567,232569,232570,232572,232573],{},[941,232565,20529],{}," (db-level ",[941,232568,20524],{}," option, default ",[941,232571,20534],{},") —\n",[941,232574,232575],{},"'local-wins' | 'remote-wins' | 'version' | ((conflict: Conflict) => 'local' | 'remote')",[1114,232577,232578,232580,232581,232583,232584],{},[941,232579,91984],{}," (collection-level ",[941,232582,91981],{}," option) —\n",[941,232585,232586],{},"'last-writer-wins' | 'first-writer-wins' | 'manual' | ((local: T, remote: T) => T)",[1591,232588,232590],{"id":232589},"lower-level-exports","Lower-level exports",[937,232592,232593,968,232595,2462,232597,232599,232600,232602,232603,232605,232606,1261,232608,1520],{},[941,232594,4684],{},[941,232596,4687],{},[941,232598,4691],{}," — the classes ",[941,232601,4676],{}," constructs — are\nalso exported from ",[941,232604,4680],{}," for advanced consumers that want to construct them directly\ninstead of going through ",[941,232607,12780],{},[941,232609,21307],{},[1022,232611,101425],{"id":101424},[1111,232613,232614,232628,232642],{},[1114,232615,232616,232617,21348,232620,232622,232623,232625,232626,1520],{},"Configuring ",[941,232618,232619],{},"sync: ...",[941,232621,89280],{}," throws on the first ",[941,232624,1749],{},"\ncall, pointing at ",[941,232627,4672],{},[1114,232629,232630,1261,232632,232634,232635,232637,232638,232641],{},[941,232631,90941],{},[941,232633,90945],{}," on a vault with no ",[941,232636,864],{}," target configured throw\n(",[941,232639,232640],{},"\"No sync adapter configured. Pass a \\","sync` adapter to createNoydb().\"`).",[1114,232643,232644,232646,232647,232649,232650,232652],{},[941,232645,21307],{}," always requires ",[941,232648,89280],{}," — even a vault with no\nremote ",[941,232651,864],{}," target can use presence (it falls back to polling the local store), but without\nthe strategy it throws.",[1022,232654,114684],{"id":114683},[1111,232656,232657,232669,232682],{},[1114,232658,232659,232663,232664,1625,232666,232668],{},[950,232660,232661],{},[1007,232662,653],{"href":653}," — sync defers to CRDT merge semantics for collections that opt in; conflicts\nresolve inside the CRDT merge instead of the ",[941,232665,20524],{},[941,232667,91981],{}," strategies above",[1114,232670,232671,964,232675,1625,232677,1625,232679,232681],{},[950,232672,232673],{},[1007,232674,868],{"href":868},[941,232676,10319],{},[941,232678,10322],{},[941,232680,10325],{}," manage vault membership\nindependently of this service; multi-user vaults typically pair the two so every device can\npush\u002Fpull the shared collections",[1114,232683,232684,232688],{},[950,232685,232686],{},[1007,232687,840],{"href":840}," — long-lived sessions suit always-on, frequently-syncing devices",[1022,232690,6825],{"id":6824},[1111,232692,232693,232696,232707,232730,232738],{},[1114,232694,232695],{},"Presence keys are derived from the collection DEK via HKDF — revoked users lose presence access\non the next DEK rotation, with no change needed to the presence code itself.",[1114,232697,232698,232699,1625,232701,232703,232704,232706],{},"Real-time presence needs a store adapter that implements ",[941,232700,91537],{},[941,232702,91540],{},";\nadapters that don't fall back to polling, trading latency (up to ",[941,232705,232222],{},") for working\neverywhere.",[1114,232708,232709,1625,232712,232715,232716,232718,232719,232721,232722,1625,232724,232726,232727,1520],{},[941,232710,232711],{},"db.push",[941,232713,232714],{},"db.pull"," only touch the vault's ",[1092,232717,47925],{}," target (the first ",[941,232720,190253],{},", or the\nfirst target if none is a peer). Reach ",[941,232723,190175],{},[941,232725,190178],{}," targets via ",[941,232728,232729],{},"db.sync(vault)",[1114,232731,232732,232733,968,232735,232737],{},"When the elected primary is not a ",[941,232734,190253],{},[941,232736,20594],{}," returns an empty result without\ntouching the target — the role→direction policy applies to the primary the same way it always\napplied to the secondary fan-out.",[1114,232739,232740,232741,1261,232743,1617,232745,232747],{},"Delete markers are how deletes converge, not how space is reclaimed — a heavily-deleting synced\nvault should close periods and run ",[941,232742,56997],{},[941,232744,57004],{},[1007,232746,776],{"href":777},") to physically purge converged markers.",[1022,232749,2621],{"id":2620},[1111,232751,232752,232757,232762,232771,232777],{},[1114,232753,232754,232756],{},[1007,232755,65376],{"href":48}," — the vault\u002Fcollection model sync\nreplicates",[1114,232758,232759,232761],{},[1007,232760,1516],{"href":66}," — the encrypted envelope format sync moves as-is",[1114,232763,232764,1368,232766,1368,232768,232770],{},[1007,232765,653],{"href":653},[1007,232767,868],{"href":868},[1007,232769,840],{"href":840}," — related services",[1114,232772,232773,1368,232775],{},[1007,232774,61743],{"href":598},[1007,232776,1019],{"href":1018},[1114,232778,101581,232779,1520],{},[1007,232780,232782],{"href":92284,"rel":232781},[3371],[941,232783,232784],{},"15-with-sync",[2645,232786,153960],{},{"title":1037,"searchDepth":1038,"depth":1038,"links":232788},[232789,232790,232791,232792,232793,232805,232811,232812,232813,232814],{"id":29856,"depth":1041,"text":29857},{"id":111659,"depth":1041,"text":111660},{"id":70092,"depth":1041,"text":70093},{"id":99863,"depth":1041,"text":99864},{"id":29892,"depth":1041,"text":29893,"children":232794},[232795,232796,232801,232802,232803,232804],{"id":230020,"depth":1038,"text":230021},{"id":230618,"depth":1038,"text":230619,"children":232797},[232798,232799,232800],{"id":230680,"depth":2288,"text":230681},{"id":230935,"depth":2288,"text":230936},{"id":231022,"depth":2288,"text":231023},{"id":231316,"depth":1038,"text":231317},{"id":231395,"depth":1038,"text":231396},{"id":231544,"depth":1038,"text":95935},{"id":91292,"depth":1038,"text":25646},{"id":102888,"depth":1041,"text":102889,"children":232806},[232807,232808,232809,232810],{"id":16362,"depth":1038,"text":204640},{"id":232428,"depth":1038,"text":232321},{"id":128135,"depth":1038,"text":103205},{"id":232589,"depth":1038,"text":232590},{"id":101424,"depth":1041,"text":101425},{"id":114683,"depth":1041,"text":114684},{"id":6824,"depth":1041,"text":6825},{"id":2620,"depth":1041,"text":2621},"Replication engine — dirty tracking, push\u002Fpull between a local store and remote targets, conflict resolution — plus live presence over an encrypted channel.",{},{"title":864,"description":232815},"gYZ3nNUiTw9w834DsA2j2NpuV7DIdyJr8b6wytPHgP4",{"id":232820,"title":868,"access":12,"apiToc":929,"body":232821,"category":1042,"description":233739,"extension":1044,"meta":233740,"navigation":1042,"nest":1047,"path":869,"seo":233741,"sinceVersion":1042,"status":1049,"stem":870,"subtitle":1042,"tier":1050,"tocExternal":1042,"__hash__":233742},"docs\u002Fservices\u002Fteam.md",{"type":931,"value":232822,"toc":233723},[232823,232825,232828,232830,232839,232841,232855,232857,232884,232886,233009,233033,233035,233039,233203,233207,233276,233285,233300,233322,233328,233332,233349,233351,233446,233448,233452,233556,233560,233606,233608,233681,233683,233721],[934,232824,868],{"id":868},[46871,232826],{":rows":232827},"[{\"label\":\"Subpath\",\"value\":\"`@noy-db\u002Fhub\u002Fteam`\"},{\"label\":\"Factory\",\"value\":\"`withTeam()`\"},{\"label\":\"Layer\",\"value\":\"`with-party`\"},{\"label\":\"Cluster\",\"value\":\"G — Collaboration & Auth\"},{\"label\":\"LOC cost\",\"value\":\"~1,000 (off-bundle when not opted in)\"}]",[1022,232829,29857],{"id":29856},[937,232831,232832,232833,232835,232836,232838],{},"Every vault encrypts each collection with its own DEK (data-encryption key) — see\n",[1007,232834,6898],{"href":54}," for the full key hierarchy.\nWrapping that DEK for a single owner is the minimum every vault ships with, always on, no\nopt-in required. The ",[950,232837,868],{}," service is what you add when a vault has more than one user:\ngranting a second person a wrapped copy of the DEK, revoking it, rotating it, and everything\nthat follows from more than one keyring existing in the same vault — magic-link viewer\nsessions, sub-permission delegation, and hierarchical permission tiers.",[1022,232840,111660],{"id":111659},[1111,232842,232843,232846,232849,232852],{},[1114,232844,232845],{},"Vaults shared between two or more users with role-based access",[1114,232847,232848],{},"Time-boxed read-only viewer sessions (auditors, reviewers)",[1114,232850,232851],{},"Operators with a sub-permission delegated by an admin",[1114,232853,232854],{},"Hierarchical access (tiers 0..N where higher tiers see lower tiers but not vice versa)",[1022,232856,70093],{"id":70092},[1111,232858,232859,232871],{},[1114,232860,232861,964,232863,107496,232865,1261,232867,1261,232869,1520],{},[950,232862,107493],{},[941,232864,3197],{},[941,232866,10493],{},[941,232868,10653],{},[941,232870,10859],{},[1114,232872,232873,232876,232877,232880,232881,232883],{},[1092,232874,232875],{},"For tier elevation on a handle:"," the target collection must declare the tier and ",[941,232878,232879],{},"tiersStrategy: withTiers()"," (a separate opt-in from ",[941,232882,73343],{},") must be passed too.",[1022,232885,99864],{"id":99863},[2217,232887,232889],{"className":2219,"code":232888,"language":2221,"meta":1037,"style":1037},"import { createNoydb } from '@noy-db\u002Fhub'\nimport { toMemory } from '@noy-db\u002Fto-memory'\nimport { withTeam } from '@noy-db\u002Fhub\u002Fteam'\n\nconst db = await createNoydb({\n  store: toMemory(),\n  user: 'alice',\n  teamStrategy: withTeam(),\n})\n",[941,232890,232891,232909,232927,232945,232949,232965,232977,232991,233003],{"__ignoreMap":1037},[2225,232892,232893,232895,232897,232899,232901,232903,232905,232907],{"class":2227,"line":2228},[2225,232894,2232],{"class":2231},[2225,232896,2236],{"class":2235},[2225,232898,2744],{"class":2239},[2225,232900,2243],{"class":2235},[2225,232902,2246],{"class":2231},[2225,232904,2249],{"class":2235},[2225,232906,1086],{"class":2252},[2225,232908,2256],{"class":2235},[2225,232910,232911,232913,232915,232917,232919,232921,232923,232925],{"class":2227,"line":1041},[2225,232912,2232],{"class":2231},[2225,232914,2236],{"class":2235},[2225,232916,68576],{"class":2239},[2225,232918,2243],{"class":2235},[2225,232920,2246],{"class":2231},[2225,232922,2249],{"class":2235},[2225,232924,9638],{"class":2252},[2225,232926,2256],{"class":2235},[2225,232928,232929,232931,232933,232935,232937,232939,232941,232943],{"class":2227,"line":1038},[2225,232930,2232],{"class":2231},[2225,232932,2236],{"class":2235},[2225,232934,10373],{"class":2239},[2225,232936,2243],{"class":2235},[2225,232938,2246],{"class":2231},[2225,232940,2249],{"class":2235},[2225,232942,4680],{"class":2252},[2225,232944,2256],{"class":2235},[2225,232946,232947],{"class":2227,"line":2288},[2225,232948,2285],{"emptyLinePlaceholder":2284},[2225,232950,232951,232953,232955,232957,232959,232961,232963],{"class":2227,"line":2304},[2225,232952,2775],{"class":2774},[2225,232954,2778],{"class":2239},[2225,232956,2781],{"class":2235},[2225,232958,2784],{"class":2231},[2225,232960,2744],{"class":2291},[2225,232962,2789],{"class":2239},[2225,232964,4200],{"class":2235},[2225,232966,232967,232969,232971,232973,232975],{"class":2227,"line":2356},[2225,232968,15608],{"class":2307},[2225,232970,2311],{"class":2235},[2225,232972,68576],{"class":2291},[2225,232974,4228],{"class":2239},[2225,232976,1707],{"class":2235},[2225,232978,232979,232981,232983,232985,232987,232989],{"class":2227,"line":2830},[2225,232980,4205],{"class":2307},[2225,232982,2311],{"class":2235},[2225,232984,2249],{"class":2235},[2225,232986,6408],{"class":2252},[2225,232988,2805],{"class":2235},[2225,232990,1707],{"class":2235},[2225,232992,232993,232995,232997,232999,233001],{"class":2227,"line":2861},[2225,232994,10439],{"class":2307},[2225,232996,2311],{"class":2235},[2225,232998,10373],{"class":2291},[2225,233000,4228],{"class":2239},[2225,233002,1707],{"class":2235},[2225,233004,233005,233007],{"class":2227,"line":4246},[2225,233006,2359],{"class":2235},[2225,233008,2362],{"class":2239},[937,233010,90416,233011,968,233013,968,233015,2462,233017,66005,233019,233021,233022,233024,233025,968,233028,1707,233030,233032],{},[941,233012,10312],{},[941,233014,10319],{},[941,233016,10322],{},[941,233018,10325],{},[941,233020,10329],{}," — the floor default rejects every call so a single-user vault never\npays the bundle cost of the keyring-grant engine. Single-user primitives stay ungated with or\nwithout ",[941,233023,3197],{},": the owner keyring itself, unlock, ",[941,233026,233027],{},"db.listUsers",[941,233029,197106],{},[941,233031,218279],{},", and secret rotate\u002Frecover.",[1022,233034,29893],{"id":29892},[1591,233036,233038],{"id":233037},"grant-then-revoke","Grant, then revoke",[2217,233040,233042],{"className":2219,"code":233041,"language":2221,"meta":1037,"style":1037},"await db.grant('acme', {\n  userId: 'bob',\n  displayName: 'Bob',\n  role: 'operator',\n  secret: 'glasses cabinet bicycle umbrella thunder velvet',\n  permissions: { invoices: 'rw' },\n})\n\u002F\u002F → resolves once bob's keyring is written with a wrapped DEK for `invoices`\n\nawait db.revoke('acme', { userId: 'bob' })\n\u002F\u002F → resolves once bob's wrapped DEKs are removed. `cascade: 'strict'` (the default) also\n\u002F\u002F   revokes any admins bob had granted; pass `cascade: 'warn'` to log them instead\n",[941,233043,233044,233066,233080,233094,233108,233122,233142,233148,233153,233157,233193,233198],{"__ignoreMap":1037},[2225,233045,233046,233048,233050,233052,233054,233056,233058,233060,233062,233064],{"class":2227,"line":2228},[2225,233047,6500],{"class":2231},[2225,233049,2842],{"class":2239},[2225,233051,1520],{"class":2235},[2225,233053,10493],{"class":2291},[2225,233055,2789],{"class":2239},[2225,233057,2805],{"class":2235},[2225,233059,6450],{"class":2252},[2225,233061,2805],{"class":2235},[2225,233063,2298],{"class":2235},[2225,233065,2301],{"class":2235},[2225,233067,233068,233070,233072,233074,233076,233078],{"class":2227,"line":1041},[2225,233069,10510],{"class":2307},[2225,233071,2311],{"class":2235},[2225,233073,2249],{"class":2235},[2225,233075,91596],{"class":2252},[2225,233077,2805],{"class":2235},[2225,233079,1707],{"class":2235},[2225,233081,233082,233084,233086,233088,233090,233092],{"class":2227,"line":1038},[2225,233083,10526],{"class":2307},[2225,233085,2311],{"class":2235},[2225,233087,2249],{"class":2235},[2225,233089,91611],{"class":2252},[2225,233091,2805],{"class":2235},[2225,233093,1707],{"class":2235},[2225,233095,233096,233098,233100,233102,233104,233106],{"class":2227,"line":2288},[2225,233097,10542],{"class":2307},[2225,233099,2311],{"class":2235},[2225,233101,2249],{"class":2235},[2225,233103,9907],{"class":2252},[2225,233105,2805],{"class":2235},[2225,233107,1707],{"class":2235},[2225,233109,233110,233112,233114,233116,233118,233120],{"class":2227,"line":2304},[2225,233111,10423],{"class":2307},[2225,233113,2311],{"class":2235},[2225,233115,2249],{"class":2235},[2225,233117,197163],{"class":2252},[2225,233119,2805],{"class":2235},[2225,233121,1707],{"class":2235},[2225,233123,233124,233126,233128,233130,233132,233134,233136,233138,233140],{"class":2227,"line":2356},[2225,233125,10572],{"class":2307},[2225,233127,2311],{"class":2235},[2225,233129,2236],{"class":2235},[2225,233131,6503],{"class":2307},[2225,233133,2311],{"class":2235},[2225,233135,2249],{"class":2235},[2225,233137,10026],{"class":2252},[2225,233139,2805],{"class":2235},[2225,233141,2353],{"class":2235},[2225,233143,233144,233146],{"class":2227,"line":2830},[2225,233145,2359],{"class":2235},[2225,233147,2362],{"class":2239},[2225,233149,233150],{"class":2227,"line":2861},[2225,233151,233152],{"class":2763},"\u002F\u002F → resolves once bob's keyring is written with a wrapped DEK for `invoices`\n",[2225,233154,233155],{"class":2227,"line":4246},[2225,233156,2285],{"emptyLinePlaceholder":2284},[2225,233158,233159,233161,233163,233165,233167,233169,233171,233173,233175,233177,233179,233181,233183,233185,233187,233189,233191],{"class":2227,"line":4253},[2225,233160,6500],{"class":2231},[2225,233162,2842],{"class":2239},[2225,233164,1520],{"class":2235},[2225,233166,10653],{"class":2291},[2225,233168,2789],{"class":2239},[2225,233170,2805],{"class":2235},[2225,233172,6450],{"class":2252},[2225,233174,2805],{"class":2235},[2225,233176,2298],{"class":2235},[2225,233178,2236],{"class":2235},[2225,233180,10668],{"class":2307},[2225,233182,2311],{"class":2235},[2225,233184,2249],{"class":2235},[2225,233186,91596],{"class":2252},[2225,233188,2805],{"class":2235},[2225,233190,2243],{"class":2235},[2225,233192,2362],{"class":2239},[2225,233194,233195],{"class":2227,"line":4259},[2225,233196,233197],{"class":2763},"\u002F\u002F → resolves once bob's wrapped DEKs are removed. `cascade: 'strict'` (the default) also\n",[2225,233199,233200],{"class":2227,"line":6548},[2225,233201,233202],{"class":2763},"\u002F\u002F   revokes any admins bob had granted; pass `cascade: 'warn'` to log them instead\n",[1591,233204,233206],{"id":233205},"rotate-keys","Rotate keys",[2217,233208,233210],{"className":2219,"code":233209,"language":2221,"meta":1037,"style":1037},"const { needsRegrant, unverified } = await db.rotate('acme', ['invoices', 'payments'])\n\u002F\u002F → resolves once fresh DEKs are generated for `invoices` and `payments` and every existing\n\u002F\u002F   record in those collections is decrypted under the old DEK and re-encrypted under the new one\n",[941,233211,233212,233266,233271],{"__ignoreMap":1037},[2225,233213,233214,233216,233218,233221,233223,233226,233228,233230,233232,233234,233236,233238,233240,233242,233244,233246,233248,233250,233252,233254,233256,233258,233260,233262,233264],{"class":2227,"line":2228},[2225,233215,2775],{"class":2774},[2225,233217,2236],{"class":2235},[2225,233219,233220],{"class":2239}," needsRegrant",[2225,233222,2298],{"class":2235},[2225,233224,233225],{"class":2239}," unverified ",[2225,233227,2359],{"class":2235},[2225,233229,6346],{"class":2235},[2225,233231,2784],{"class":2231},[2225,233233,2842],{"class":2239},[2225,233235,1520],{"class":2235},[2225,233237,10859],{"class":2291},[2225,233239,2789],{"class":2239},[2225,233241,2805],{"class":2235},[2225,233243,6450],{"class":2252},[2225,233245,2805],{"class":2235},[2225,233247,2298],{"class":2235},[2225,233249,8667],{"class":2239},[2225,233251,2805],{"class":2235},[2225,233253,6487],{"class":2252},[2225,233255,2805],{"class":2235},[2225,233257,2298],{"class":2235},[2225,233259,2249],{"class":2235},[2225,233261,32579],{"class":2252},[2225,233263,2805],{"class":2235},[2225,233265,10880],{"class":2239},[2225,233267,233268],{"class":2227,"line":1041},[2225,233269,233270],{"class":2763},"\u002F\u002F → resolves once fresh DEKs are generated for `invoices` and `payments` and every existing\n",[2225,233272,233273],{"class":2227,"line":1038},[2225,233274,233275],{"class":2763},"\u002F\u002F   record in those collections is decrypted under the old DEK and re-encrypted under the new one\n",[937,233277,233278,233280,233281,233284],{},[941,233279,10859],{}," keeps membership unchanged — but ",[950,233282,233283],{},"every other member loses their keys"," for the\nrotated collections. A member's DEKs are wrapped under that member's KEK, and a KEK derives\nonly from that member's secret, so the caller cannot re-wrap a fresh DEK for anyone else.\nRotation therefore drops the rotated collections from every other member's keyring; they keep\ntheir membership and their other collections.",[937,233286,233287,233289,233290,233293,233294,233296,233297,1520],{},[941,233288,10985],{}," names each ",[941,233291,233292],{},"(userId, collection)"," pair that was dropped — re-run ",[941,233295,10831],{}," for\nthose pairs or they stay locked out. See\n",[1007,233298,10786],{"href":233299},"..\u002Fcore\u002F06-permissions-and-keyring#rotation-drops-every-other-members-access",[937,233301,233302,233305,233306,233308,233309,233312,233313,233315,233316,233318,233319,233321],{},[941,233303,233304],{},"unverified"," names members skipped because their ",[941,233307,6709],{}," file failed roster verification.\nSkipping gives them ",[950,233310,233311],{},"less",", never more — the file is left untouched and its wrappings for the\nrotated collections go stale. It is the same locked-out end state as ",[941,233314,10985],{}," but needs a\ndifferent remedy: a ",[941,233317,10985],{}," entry is fixed by ",[941,233320,10831],{},", while an unverified file no longer\nauthenticates and cannot be repaired by granting over it.",[937,233323,233324,233325,233327],{},"To remove a member, use ",[941,233326,10726],{}," — revocation always rotates.",[1591,233329,233331],{"id":233330},"scoped-tier-elevation","Scoped tier elevation",[937,233333,233334,233335,968,233338,968,233340,233343,233344,233348],{},"Record-tier elevation — ",[941,233336,233337],{},"vault.elevate()",[941,233339,21993],{},[941,233341,233342],{},"delegate()",", tier DEKs — has its own\nservice page: ",[950,233345,233346],{},[1007,233347,872],{"href":873},". Grants and tiers compose: the tier DEK a delegate\nreceives is scoped by the same keyring this page describes.",[1022,233350,101012],{"id":41259},[1111,233352,233353,233370,233376,233382,233393,233401,233425],{},[1114,233354,233355,233357,233358,10313,233360,233362,233363,1625,233365,1625,233367,233369],{},[941,233356,3197],{}," — factory; pass as ",[941,233359,73278],{},[941,233361,12780],{}," to make ",[941,233364,10493],{},[941,233366,10653],{},[941,233368,10859],{}," live.",[1114,233371,233372,233375],{},[941,233373,233374],{},"db.grant(vault: string, options: GrantOptions, factors?: FactorProofBundle): Promise\u003Cvoid>"," — wrap a DEK for a new user.",[1114,233377,233378,233381],{},[941,233379,233380],{},"db.revoke(vault: string, options: RevokeOptions, factors?: FactorProofBundle): Promise\u003Cvoid>"," — remove a user's access; cascades to admins they granted by default.",[1114,233383,233384,233387,233388,51566,233390,233392],{},[941,233385,233386],{},"db.rotate(vault: string, collections: string[]): Promise\u003Cvoid>"," — regenerate DEKs for the given collections, re-encrypt their records, and re-wrap for remaining members. Throws ",[941,233389,11395],{},[941,233391,9904],{}," caller — rotation is owner\u002Fadmin-only.",[1114,233394,233395,233398,233399,1520],{},[941,233396,233397],{},"db.changeSecret(vault: string, newSecret: string, options?): Promise\u003Cvoid>"," — change the caller's own secret; not gated by ",[941,233400,3197],{},[1114,233402,233403,233404,964,233406,968,233409,968,233412,968,233415,968,233418,968,233421,233424],{},"Magic-link grant helpers, imported from ",[941,233405,4680],{},[941,233407,233408],{},"writeMagicLinkGrant",[941,233410,233411],{},"readMagicLinkGrantRecord",[941,233413,233414],{},"listMagicLinkGrants",[941,233416,233417],{},"unwrapMagicLinkGrant",[941,233419,233420],{},"revokeMagicLinkGrant",[941,233422,233423],{},"isMagicLinkGrantExpired"," — one-shot, secret-less read-only viewer sessions bound to a magic-link token.",[1114,233426,233427,233428,1261,233431,1261,233433,1261,233435,1261,233438,233441,233442,1520],{},"Record-tier elevation and cross-tier delegation (",[941,233429,233430],{},"vault.elevate",[941,233432,14609],{},[941,233434,75216],{},[941,233436,233437],{},"refreshDelegations",[941,233439,233440],{},"revokeDelegation",") moved to ",[950,233443,233444],{},[1007,233445,872],{"href":873},[1591,233447,204640],{"id":16362},[937,233449,233450,2311],{},[941,233451,137077],{},[1338,233453,233454,233464],{},[1341,233455,233456],{},[1344,233457,233458,233460,233462],{},[1347,233459,101104],{},[1347,233461,20194],{},[1347,233463,9542],{},[1357,233465,233466,233478,233490,233505,233520,233539],{},[1344,233467,233468,233472,233476],{},[1362,233469,233470],{},[941,233471,10749],{},[1362,233473,233474],{},[941,233475,7259],{},[1362,233477,45375],{},[1344,233479,233480,233484,233488],{},[1362,233481,233482],{},[941,233483,16791],{},[1362,233485,233486],{},[941,233487,7259],{},[1362,233489,45375],{},[1344,233491,233492,233496,233500],{},[1362,233493,233494],{},[941,233495,16785],{},[1362,233497,233498],{},[941,233499,2970],{},[1362,233501,233502],{},[941,233503,233504],{},"'owner' | 'admin' | 'operator' | 'viewer' | 'client'",[1344,233506,233507,233511,233515],{},[1362,233508,233509],{},[941,233510,10695],{},[1362,233512,233513],{},[941,233514,7259],{},[1362,233516,233517,233518],{},"the new user's own secret; not checked against a strength policy by default — validate it yourself before calling ",[941,233519,10493],{},[1344,233521,233522,233526,233531],{},[1362,233523,233524],{},[941,233525,16800],{},[1362,233527,233528],{},[941,233529,233530],{},"Permissions?",[1362,233532,233533,233535,233536,233538],{},[941,233534,9919],{},"; defaults from ",[941,233537,16785],{}," when omitted",[1344,233540,233541,233545,233550],{},[1362,233542,233543],{},[941,233544,16369],{},[1362,233546,233547],{},[941,233548,233549],{},"boolean?",[1362,233551,233552,233553,233555],{},"reserved for an internal strength check that ",[941,233554,10319],{}," does not currently enable — has no effect through the public API today",[937,233557,233558,2311],{},[941,233559,10745],{},[1338,233561,233562,233572],{},[1341,233563,233564],{},[1344,233565,233566,233568,233570],{},[1347,233567,101104],{},[1347,233569,20194],{},[1347,233571,9542],{},[1357,233573,233574,233586],{},[1344,233575,233576,233580,233584],{},[1362,233577,233578],{},[941,233579,10749],{},[1362,233581,233582],{},[941,233583,7259],{},[1362,233585,45375],{},[1344,233587,233588,233592,233597],{},[1362,233589,233590],{},[941,233591,10753],{},[1362,233593,233594],{},[941,233595,233596],{},"'strict' | 'warn'?",[1362,233598,233599,233600,233602,233603,233605],{},"when the revoked user is an admin, ",[941,233601,150663],{}," (default) recursively revokes admins they granted; ",[941,233604,51655],{}," logs them instead",[1022,233607,68210],{"id":68209},[1111,233609,233610,233625,233630,233638,233648,233659],{},[1114,233611,233612,233613,233616,233617,233619,233620,183145,233622,233624],{},"Roles, most to least privileged: ",[941,233614,233615],{},"owner > admin > operator > viewer > client",". Owner and\nadmin can grant\u002Frevoke; viewer and client are read-only. A ",[941,233618,9904],{}," role — normally\nminted via the ",[1007,233621,661],{"href":661},[941,233623,135884],{}," — operates every\ncollection but can never grant, revoke, or rotate, even if held by an admin-equivalent\ncaller.",[1114,233626,233627,233629],{},[941,233628,10325],{}," re-encrypts every record in the given collections under a fresh DEK — cost is\nO(records in those collections), not a metadata-only rewrap.",[1114,233631,233632,233633,233635,233636,1520],{},"Only one tier elevation can be active per vault at a time; a second ",[941,233634,75009],{},"\ncall while one is live throws ",[941,233637,75012],{},[1114,233639,233640,233641,2514,233644,233647],{},"The export gates (",[941,233642,233643],{},"vault.assertCanExport('plaintext', fmt)",[941,233645,233646],{},"vault.assertCanExport('bundle')",") are not bypassed by elevation — it projects a tier,\nnot a privilege escalation.",[1114,233649,233650,233651,233654,233655,6789,233657,1520],{},"Owners, admins, and custodians can elevate to any tier (auto-minting the DEK at write\ntime); other roles must already hold a ",[941,233652,233653],{},"*#\u003Ctier>"," DEK on their keyring — via a prior grant\nor an active delegation — otherwise ",[941,233656,14606],{},[941,233658,109129],{},[1114,233660,10993,233661,233663,233664,233667,233668,233670,233671,233673,233674,233676,233677,1520],{},[941,233662,56948],{},", a record's ",[950,233665,233666],{},"blob attachments follow its tier",": on ",[941,233669,21990],{}," they\nre-home under the tier DEK and become invisible to a tier-0 caller (readable via\n",[941,233672,73914],{}," by a cleared caller), and ",[941,233675,21993],{}," restores them. See\n",[1007,233678,233680],{"href":233679},"\u002Fservices\u002Fblobs#tiered-blobs-content-follows-the-records-tier","blobs → Tiered blobs",[1022,233682,2621],{"id":2620},[1111,233684,233685,233690,233695,233700,233705,233713],{},[1114,233686,233687,233689],{},[1007,233688,6898],{"href":54}," — the always-on single-owner\nkeyring this service extends",[1114,233691,233692,233694],{},[1007,233693,864],{"href":864}," — grants flow over sync; rotated DEKs propagate to peers",[1114,233696,233697,233699],{},[1007,233698,840],{"href":840}," — token-based sessions enforce grant policies",[1114,233701,233702,233704],{},[1007,233703,697],{"href":697}," — every grant \u002F revoke writes a ledger entry",[1114,233706,233707,233709,233710,233712],{},[1007,233708,661],{"href":661}," — the owner-only ",[941,233711,9904],{}," role and grant\u002Frevoke-custodian API",[1114,233714,101581,233715,1520],{},[1007,233716,233718],{"href":92545,"rel":233717},[3371],[941,233719,233720],{},"06-multi-user",[2645,233722,32928],{},{"title":1037,"searchDepth":1038,"depth":1038,"links":233724},[233725,233726,233727,233728,233729,233734,233737,233738],{"id":29856,"depth":1041,"text":29857},{"id":111659,"depth":1041,"text":111660},{"id":70092,"depth":1041,"text":70093},{"id":99863,"depth":1041,"text":99864},{"id":29892,"depth":1041,"text":29893,"children":233730},[233731,233732,233733],{"id":233037,"depth":1038,"text":233038},{"id":233205,"depth":1038,"text":233206},{"id":233330,"depth":1038,"text":233331},{"id":41259,"depth":1041,"text":101012,"children":233735},[233736],{"id":16362,"depth":1038,"text":204640},{"id":68209,"depth":1041,"text":68210},{"id":2620,"depth":1041,"text":2621},"Multi-user collaboration — grant\u002Frevoke\u002Frotate wrapped DEKs via withTeam(), plus magic-link sessions; delegation and tier elevation moved to \u002Fservices\u002Ftiers.",{},{"title":868,"description":233739},"sJbPu0C1kBdcxl9sl0BkyGCJdvrD5p4BsKkwyCT_Za0",{"id":233744,"title":872,"access":12,"apiToc":929,"body":233745,"category":1042,"description":235058,"extension":1044,"meta":235059,"navigation":1042,"nest":1047,"path":873,"seo":235060,"sinceVersion":235061,"status":1049,"stem":874,"subtitle":1042,"tier":1050,"tocExternal":1042,"__hash__":235062},"docs\u002Fservices\u002Ftiers.md",{"type":931,"value":233746,"toc":235037},[233747,233749,233770,233773,233777,233788,233806,233825,233827,233862,233864,233873,234061,234067,234082,234212,234301,234325,234334,234339,234383,234391,234399,234445,234451,234454,234498,234514,234543,234547,234559,234605,234625,234634,234638,234654,234665,234674,234678,234689,234738,234798,234802,234809,234856,234860,234863,235000,235002,235034],[934,233748,872],{"id":872},[1061,233750,233751],{},[937,233752,233753,233756,233757,233759,233760,1625,233762,233764,233765,233767,233768,1520],{},[950,233754,233755],{},"Record tiers, not session tiers."," This page covers per-record ",[950,233758,8673],{}," tiers —\n",[941,233761,14606],{},[941,233763,14609],{},", per-tier DEKs, tier-gated reads. For the session ",[950,233766,16960],{},"\nladder (Tier 1 secret \u002F Tier 2 multi-slot \u002F Tier 3 quick-unlock), see\n",[1007,233769,844],{"href":845},[46871,233771],{":rows":233772},"[{\"label\":\"Subpath\",\"value\":\"`@noy-db\u002Fhub\u002Ftiers`\"},{\"label\":\"Factory\",\"value\":\"`withTiers()`\"},{\"label\":\"Layer\",\"value\":\"`with-audit`\"},{\"label\":\"Cluster\",\"value\":\"G — Collaboration & Auth\"}]",[1022,233774,233776],{"id":233775},"mental-model","Mental model",[937,233778,233779,233780,233783,233784,233787],{},"Every record in a tiered collection lives at a ",[950,233781,233782],{},"numeric access tier",". Tier 0 is the\ndefault — the floor everyone with the collection DEK can read. A collection opts into higher\ntiers by declaring them: ",[941,233785,233786],{},"vault.collection(name, { tiers: [0, 1, 2] })",". Higher tiers see\nlower tiers; a lower tier cannot see a record parked above it.",[937,233789,233790,233791,233794,233795,233797,233798,233801,233802,233805],{},"Each tier wraps ",[950,233792,233793],{},"its own DEK",". A record's body is encrypted under a content-encryption key\n(CEK, see ",[1007,233796,210590],{"href":154040},"); the CEK is\nAES-KW-wrapped under the ",[950,233799,233800],{},"tier's"," DEK, not one flat collection key. So \"which tier a record\nis at\" is a fact about ",[1092,233803,233804],{},"which DEK can unwrap its body"," — a key-holder for tier 0 simply cannot\ndecrypt a record whose CEK is wrapped under the tier-2 DEK.",[937,233807,233808,233811,233812,1625,233814,233816,233817,233819,233820,957],{},[950,233809,233810],{},"Tiers compose with per-record keys."," Moving a record between tiers (",[941,233813,14606],{},[941,233815,14609],{},")\nre-wraps the ",[1092,233818,4664],{}," CEK from the source-tier DEK to the target-tier DEK — the body key is\nunchanged, so the ciphertext body is not rewritten, only its wrapped-CEK header. This is the\nproperty that makes a tier move cheap and reversible (source of truth:\n",[1007,233821,233822],{"href":154040},[941,233823,233824],{},"core\u002Fnoydb",[1022,233826,70093],{"id":70092},[1111,233828,233829,233851],{},[1114,233830,233831,216245,233835,233837,233838,233840,233841,233843,233844,233847,233848,233850],{},[950,233832,233833],{},[941,233834,232879],{},[941,233836,12780],{}," — without it every record-tier\noperation throws ",[941,233839,22156],{},", and the tier cores stay out of the bundle. The\n",[941,233842,233337],{}," handle mint is the one exception: it is always-on core, but ",[1092,233845,233846],{},"writing","\nthrough the handle still needs ",[941,233849,21976],{}," and a tier-declaring collection.",[1114,233852,233853,964,233856,233858,233859,233861],{},[950,233854,233855],{},"The collection must declare its tiers",[941,233857,233786],{},".\nA collection that never declares ",[941,233860,872],{}," is a plain tier-0 collection and pays nothing.",[1022,233863,101012],{"id":41259},[937,233865,233866,233867,233869,233870,233872],{},"Record-tier operations are gated behind ",[941,233868,232879],{},". Without it every\ntier-aware call throws ",[941,233871,22156],{}," — the machinery stays out of the bundle.",[2217,233874,233876],{"className":2219,"code":233875,"language":2221,"meta":1037,"style":1037},"import { createNoydb } from '@noy-db\u002Fhub'\nimport { withTiers } from '@noy-db\u002Fhub\u002Ftiers'\n\nconst db = await createNoydb({\n  store: \u002F* your store adapter *\u002F,\n  user: 'alice',\n  tiersStrategy: withTiers(),\n})\n\nconst vault = await db.openVault('acme')\nconst docs = vault.collection\u003CDoc>('docs', { tiers: [0, 1, 2] })\n",[941,233877,233878,233896,233914,233918,233934,233945,233959,233971,233977,233981,234007],{"__ignoreMap":1037},[2225,233879,233880,233882,233884,233886,233888,233890,233892,233894],{"class":2227,"line":2228},[2225,233881,2232],{"class":2231},[2225,233883,2236],{"class":2235},[2225,233885,2744],{"class":2239},[2225,233887,2243],{"class":2235},[2225,233889,2246],{"class":2231},[2225,233891,2249],{"class":2235},[2225,233893,1086],{"class":2252},[2225,233895,2256],{"class":2235},[2225,233897,233898,233900,233902,233904,233906,233908,233910,233912],{"class":2227,"line":1041},[2225,233899,2232],{"class":2231},[2225,233901,2236],{"class":2235},[2225,233903,73968],{"class":2239},[2225,233905,2243],{"class":2235},[2225,233907,2246],{"class":2231},[2225,233909,2249],{"class":2235},[2225,233911,73343],{"class":2252},[2225,233913,2256],{"class":2235},[2225,233915,233916],{"class":2227,"line":1038},[2225,233917,2285],{"emptyLinePlaceholder":2284},[2225,233919,233920,233922,233924,233926,233928,233930,233932],{"class":2227,"line":2288},[2225,233921,2775],{"class":2774},[2225,233923,2778],{"class":2239},[2225,233925,2781],{"class":2235},[2225,233927,2784],{"class":2231},[2225,233929,2744],{"class":2291},[2225,233931,2789],{"class":2239},[2225,233933,4200],{"class":2235},[2225,233935,233936,233938,233940,233943],{"class":2227,"line":2304},[2225,233937,15608],{"class":2307},[2225,233939,2311],{"class":2235},[2225,233941,233942],{"class":2763}," \u002F* your store adapter *\u002F",[2225,233944,1707],{"class":2235},[2225,233946,233947,233949,233951,233953,233955,233957],{"class":2227,"line":2356},[2225,233948,4205],{"class":2307},[2225,233950,2311],{"class":2235},[2225,233952,2249],{"class":2235},[2225,233954,6408],{"class":2252},[2225,233956,2805],{"class":2235},[2225,233958,1707],{"class":2235},[2225,233960,233961,233963,233965,233967,233969],{"class":2227,"line":2830},[2225,233962,74294],{"class":2307},[2225,233964,2311],{"class":2235},[2225,233966,73968],{"class":2291},[2225,233968,4228],{"class":2239},[2225,233970,1707],{"class":2235},[2225,233972,233973,233975],{"class":2227,"line":2861},[2225,233974,2359],{"class":2235},[2225,233976,2362],{"class":2239},[2225,233978,233979],{"class":2227,"line":4246},[2225,233980,2285],{"emptyLinePlaceholder":2284},[2225,233982,233983,233985,233987,233989,233991,233993,233995,233997,233999,234001,234003,234005],{"class":2227,"line":4253},[2225,233984,2775],{"class":2774},[2225,233986,2835],{"class":2239},[2225,233988,2781],{"class":2235},[2225,233990,2784],{"class":2231},[2225,233992,2842],{"class":2239},[2225,233994,1520],{"class":2235},[2225,233996,2847],{"class":2291},[2225,233998,2789],{"class":2239},[2225,234000,2805],{"class":2235},[2225,234002,6450],{"class":2252},[2225,234004,2805],{"class":2235},[2225,234006,2362],{"class":2239},[2225,234008,234009,234011,234013,234015,234017,234019,234021,234023,234025,234027,234029,234031,234033,234035,234037,234039,234041,234043,234045,234047,234049,234051,234053,234055,234057,234059],{"class":2227,"line":4259},[2225,234010,2775],{"class":2774},[2225,234012,90113],{"class":2239},[2225,234014,2781],{"class":2235},[2225,234016,6466],{"class":2239},[2225,234018,1520],{"class":2235},[2225,234020,6471],{"class":2291},[2225,234022,6474],{"class":2235},[2225,234024,143086],{"class":6342},[2225,234026,6480],{"class":2235},[2225,234028,2789],{"class":2239},[2225,234030,2805],{"class":2235},[2225,234032,69487],{"class":2252},[2225,234034,2805],{"class":2235},[2225,234036,2298],{"class":2235},[2225,234038,2236],{"class":2235},[2225,234040,74501],{"class":2307},[2225,234042,2311],{"class":2235},[2225,234044,8667],{"class":2239},[2225,234046,5402],{"class":6527},[2225,234048,2298],{"class":2235},[2225,234050,9377],{"class":6527},[2225,234052,2298],{"class":2235},[2225,234054,18961],{"class":6527},[2225,234056,8890],{"class":2239},[2225,234058,2359],{"class":2235},[2225,234060,2362],{"class":2239},[1591,234062,234064,234065],{"id":234063},"scoped-session-elevation-vaultelevate","Scoped session elevation — ",[941,234066,233337],{},[937,234068,234069,234070,234072,234073,139857,234076,234078,234079,234081],{},"When a workflow needs a lower-tier session to briefly act at a higher tier — approving a gated\nwrite, running a plaintext export — ",[941,234071,73872],{}," returns an\n",[941,234074,234075],{},"ElevatedHandle",[950,234077,74815],{}," land at the elevated tier. Reads on the original ",[941,234080,2694],{},"\nstay at the caller's inherent tier; only the returned handle is privileged.",[2217,234083,234085],{"className":2219,"code":234084,"language":2221,"meta":1037,"style":1037},"const elevated = await vault.elevate(2, {\n  ttlMs: 15 * 60_000,\n  reason: 'plaintext export',\n})\n\u002F\u002F → ElevatedHandle { tier: 2, reason: 'plaintext export', expiresAt: \u003Cnow + ttlMs> }\n\nawait elevated.collection\u003CDoc>('docs').put('d1', record)\nawait elevated.release() \u002F\u002F or let ttlMs elapse\n",[941,234086,234087,234111,234125,234140,234146,234151,234155,234197],{"__ignoreMap":1037},[2225,234088,234089,234091,234093,234095,234097,234099,234101,234103,234105,234107,234109],{"class":2227,"line":2228},[2225,234090,2775],{"class":2774},[2225,234092,74828],{"class":2239},[2225,234094,2781],{"class":2235},[2225,234096,2784],{"class":2231},[2225,234098,6466],{"class":2239},[2225,234100,1520],{"class":2235},[2225,234102,14606],{"class":2291},[2225,234104,2789],{"class":2239},[2225,234106,5470],{"class":6527},[2225,234108,2298],{"class":2235},[2225,234110,2301],{"class":2235},[2225,234112,234113,234115,234117,234119,234121,234123],{"class":2227,"line":1041},[2225,234114,69818],{"class":2307},[2225,234116,2311],{"class":2235},[2225,234118,45248],{"class":6527},[2225,234120,2263],{"class":2235},[2225,234122,19001],{"class":6527},[2225,234124,1707],{"class":2235},[2225,234126,234127,234129,234131,234133,234136,234138],{"class":2227,"line":1038},[2225,234128,74864],{"class":2307},[2225,234130,2311],{"class":2235},[2225,234132,2249],{"class":2235},[2225,234134,234135],{"class":2252},"plaintext export",[2225,234137,2805],{"class":2235},[2225,234139,1707],{"class":2235},[2225,234141,234142,234144],{"class":2227,"line":2288},[2225,234143,2359],{"class":2235},[2225,234145,2362],{"class":2239},[2225,234147,234148],{"class":2227,"line":2304},[2225,234149,234150],{"class":2763},"\u002F\u002F → ElevatedHandle { tier: 2, reason: 'plaintext export', expiresAt: \u003Cnow + ttlMs> }\n",[2225,234152,234153],{"class":2227,"line":2356},[2225,234154,2285],{"emptyLinePlaceholder":2284},[2225,234156,234157,234159,234161,234163,234165,234167,234169,234171,234173,234175,234177,234179,234181,234183,234185,234187,234189,234191,234193,234195],{"class":2227,"line":2830},[2225,234158,6500],{"class":2231},[2225,234160,74897],{"class":2239},[2225,234162,1520],{"class":2235},[2225,234164,6471],{"class":2291},[2225,234166,6474],{"class":2235},[2225,234168,143086],{"class":6342},[2225,234170,6480],{"class":2235},[2225,234172,2789],{"class":2239},[2225,234174,2805],{"class":2235},[2225,234176,69487],{"class":2252},[2225,234178,2805],{"class":2235},[2225,234180,1381],{"class":2239},[2225,234182,1520],{"class":2235},[2225,234184,3161],{"class":2291},[2225,234186,2789],{"class":2239},[2225,234188,2805],{"class":2235},[2225,234190,154976],{"class":2252},[2225,234192,2805],{"class":2235},[2225,234194,2298],{"class":2235},[2225,234196,43833],{"class":2239},[2225,234198,234199,234201,234203,234205,234207,234209],{"class":2227,"line":2861},[2225,234200,6500],{"class":2231},[2225,234202,74897],{"class":2239},[2225,234204,1520],{"class":2235},[2225,234206,75043],{"class":2291},[2225,234208,19132],{"class":2239},[2225,234210,234211],{"class":2763},"\u002F\u002F or let ttlMs elapse\n",[1111,234213,234214,234223,234241,234257,234271,234288],{},[1114,234215,234216,234219,234220,234222],{},[941,234217,234218],{},"vault.elevate(tier, { ttlMs, reason }): Promise\u003CElevatedHandle>"," — the handle mint itself is\nalways-on core; writing through the handle requires the target collection to declare the tier\nand ",[941,234221,21976],{}," to be wired.",[1114,234224,234225,10746,234227,968,234229,968,234231,234233,234234,2462,234237,234240],{},[941,234226,234075],{},[941,234228,17414],{},[941,234230,43668],{},[941,234232,44339],{},", exposes ",[941,234235,234236],{},"collection\u003CT>(name).put(id, record)",[941,234238,234239],{},"release(): Promise\u003Cvoid>"," (idempotent — reverts the elevation and frees the\nvault's one active-elevation slot).",[1114,234242,234243,234246,234247,234249,234250,234252,234253,234256],{},[950,234244,234245],{},"TTL is enforced lazily."," The handle checks ",[941,234248,44339],{}," on every operation, so a forgotten\n",[941,234251,75019],{}," cannot keep elevated writes alive — the next write after expiry throws\n",[941,234254,234255],{},"ElevationExpiredError",". Reads on the original vault are unaffected.",[1114,234258,234259,234262,234263,234265,234266,1952,234268,234270],{},[950,234260,234261],{},"One elevation per vault."," A second ",[941,234264,75009],{}," while one is live throws\n",[941,234267,75012],{},[941,234269,75019],{}," the current handle first.",[1114,234272,234273,234276,234277,1625,234279,1625,234281,234283,234284,6789,234286,1520],{},[950,234274,234275],{},"Clearance is required."," The keyring must already carry a wrap for the target tier on at\nleast one collection, or the caller must be ",[941,234278,9898],{},[941,234280,9901],{},[941,234282,9904],{}," (who auto-mint the\ntier DEK at write time). Otherwise ",[941,234285,14606],{},[941,234287,109129],{},[1114,234289,234290,234293,234294,10750,234296,25809,234298,234300],{},[950,234291,234292],{},"Not a privilege escalation."," The export gates (",[941,234295,233643],{},[941,234297,233646],{},[1092,234299,1004],{}," bypassed by elevation — it projects a\ntier, not a role.",[937,234302,234303,234304,234307,234308,234311,234312,28806,234315,234318,234319,234321,234322,1520],{},"Each elevation writes one audit envelope to the reserved ",[941,234305,234306],{},"_elevation_audit"," collection\n(",[941,234309,234310],{},"{ id, actor, tier, reason, ttlMs, startedAt, expiresAt }","), and every write through the\nhandle fires a ",[941,234313,234314],{},"CrossTierAccessEvent",[941,234316,234317],{},"authorization: 'elevation'",", stamped with ",[941,234320,43668],{},".\nSubscribe with ",[941,234323,234324],{},"vault.onCrossTierAccess(listener)",[1591,234326,234328,234329,1261,234332],{"id":234327},"per-record-moves-collectionelevate-demote","Per-record moves — ",[941,234330,234331],{},"collection.elevate()",[941,234333,21993],{},[937,234335,234336,234337,2311],{},"For moving an individual record's home tier (rather than opening a session handle), the\ncollection exposes the tier-move primitives — all gated behind ",[941,234338,21976],{},[1111,234340,234341,234350,234362],{},[1114,234342,234343,234346,234347,234349],{},[941,234344,234345],{},"collection.elevate(id, toTier): Promise\u003CTierMoveResult>"," — re-encrypts the record under the\ntarget tier's DEK and stamps ",[941,234348,1719],{}," with the caller id. The caller must hold DEKs for\nboth the current tier (to decrypt) and the target tier (to re-encrypt).",[1114,234351,234352,234355,234356,234358,234359,1520],{},[941,234353,234354],{},"collection.demote(id, toTier): Promise\u003CTierMoveResult>"," — the reverse. Allowed ",[950,234357,1320],{}," for\nthe user who performed the last elevation, or an owner; anyone else gets\n",[941,234360,234361],{},"TierDemoteDeniedError",[1114,234363,234364,10746,234367,234370,234371,234374,234375,1625,234377,234379,234380,234382],{},[941,234365,234366],{},"TierMoveResult",[941,234368,234369],{},"searchResidue: boolean"," — the record move (indexes, cache, ledger,\nderived outputs, history, blobs) always completes; ",[941,234372,234373],{},"searchResidue: true"," signals only that the\nsearch-index side (",[941,234376,75373],{},[941,234378,35628],{},") was left needing a retry, mirroring ",[941,234381,2542],{},"'s\nresidue posture rather than aborting the move.",[1591,234384,234386,234387,1261,234389],{"id":234385},"cleared-reads-getattier-listattier","Cleared reads — ",[941,234388,21984],{},[941,234390,21987],{},[937,234392,234393,234394,234398],{},"Tier-0 read surfaces treat an elevated record as absent (see ",[1007,234395,234397],{"href":234396},"#readwrite-gating","gating","). The\nsanctioned way for a cleared caller to read above their inherent tier:",[1111,234400,234401,234423,234434],{},[1114,234402,234403,234406,234407,1617,234409,234412,234413,1617,234416,234419,234420,234422],{},[941,234404,234405],{},"collection.getAtTier(id): Promise\u003CT | GhostRecord | null>"," — a fully-cleared read returns\nthe plaintext record and fires a cross-tier event; an uncleared read returns ",[941,234408,6651],{},[941,234410,234411],{},"'invisibility'"," mode, the default) or a ",[941,234414,234415],{},"GhostRecord { _ghost, _tier, canElevateFrom? }",[941,234417,234418],{},"'ghost'"," mode, selected with the collection's ",[941,234421,22782],{}," option).",[1114,234424,234425,234428,234429,234431,234432,1520],{},[941,234426,234427],{},"collection.listAtTier(): Promise\u003CArray\u003C{ id; tier; readable }>>"," — ids grouped by the\ncaller's readability; above-tier ids are omitted under ",[941,234430,234411],{}," and included with\ntier metadata under ",[941,234433,234418],{},[1114,234435,234436,234439,234440,234442,234443,957],{},[941,234437,234438],{},"collection.putAtTier(id, record, tier, opts?): Promise\u003CTierMoveResult>"," — a direct tiered\nwrite. It asserts access to the record's ",[950,234441,80763],{}," tier as well as the target, so a caller\ncleared only for the target cannot move a record parked at a tier they were never granted\n(",[941,234444,109129],{},[1591,234446,234448,234449],{"id":234447},"cross-tier-delegation-vaultdelegate","Cross-tier delegation — ",[941,234450,73882],{},[937,234452,234453],{},"Delegation hands another user time-boxed access to a tier without a full grant:",[1111,234455,234456,234479,234488],{},[1114,234457,234458,234461,234462,234465,234466,168285,234469,7356,234472,234475,234476,1520],{},[941,234459,234460],{},"vault.delegate(opts: IssueDelegationOptions): Promise\u003CDelegationToken>"," — the caller must\nhold the tier DEK for the requested ",[941,234463,234464],{},"{ tier, collection? }","; it wraps that DEK into the\nreserved ",[941,234467,234468],{},"_delegations",[941,234470,234471],{},"IssueDelegationOptions",[941,234473,234474],{},"{ toUser, tier, collection?, record?, until: Date | string }",". A target with no keyring in the vault raises\n",[941,234477,234478],{},"DelegationTargetMissingError",[1114,234480,234481,1169,234484,234487],{},[941,234482,234483],{},"vault.refreshDelegations(now?: Date): Promise\u003CDelegationToken[]>",[950,234485,234486],{},"read half",". Merges\nevery live delegation addressed to the calling user into their keyring and returns the ones it\nmerged.",[1114,234489,234490,234493,234494,234497],{},[941,234491,234492],{},"vault.revokeDelegation(id: string): Promise\u003Cvoid>"," — revoke by id; safe to call for an id\nthat does not exist. Revocation is the deletion of the ",[941,234495,234496],{},"_delegations\u002F\u003Cid>"," envelope.",[937,234499,234500,234501,234504,234505,234508,234509,28806,234511,1520],{},"The returned ",[941,234502,234503],{},"DelegationToken"," records ",[941,234506,234507],{},"{ id, toUser, fromUser, tier, collection, record?, until, wrappedDek?, wrappedDeks?, sealedCek?, createdAt }",". A read or write via an active\ndelegation fires a ",[941,234510,234314],{},[941,234512,234513],{},"authorization: 'delegation'",[7646,234515,234516],{"color":10789,"icon":122437},[937,234517,234518,234521,234524,234525,234528,234529,234532,234533,234535,234536,234539,234540,234542],{},[950,234519,234520],{},"Merging is EXPLICIT — it does not happen on open.",[941,234522,234523],{},"refreshDelegations()"," must be called; opening the vault does not merge anything, and neither\ndoes a read. As of hub 0.9.0 that is deliberate rather than pending: an automatic scan would\ncall ",[941,234526,234527],{},"getDEK('_delegations')",", which ",[950,234530,234531],{},"mints"," a DEK when absent and persists the keyring — so\nmerely opening a vault would write to it, and on a vault whose shared ",[941,234534,234468],{}," DEK the\ncaller does not hold it would mint a ",[1092,234537,234538],{},"wrong"," one and then fail to decrypt every token. Call it\nafter open, and on whatever interval your app wants; it lists first and returns ",[941,234541,7262],{}," when\nnothing is written.",[24204,234544,234546],{"id":234545},"per-collection-or-collection-wide","Per-collection or collection-wide",[937,234548,234549,234551,234552,234554,234555,234558],{},[941,234550,6471],{}," is optional on the way in and ",[941,234553,110591],{}," on the way out — omit it and the\ntoken covers ",[950,234556,234557],{},"every collection the grantor held at that tier",". The two forms carry different\npayloads, which is why both fields are optional:",[1338,234560,234561,234573],{},[1341,234562,234563],{},[1344,234564,234565,234569,234571],{},[1347,234566,234567],{},[941,234568,6471],{},[1347,234570,66738],{},[1347,234572,82055],{},[1357,234574,234575,234587],{},[1344,234576,234577,234580,234584],{},[1362,234578,234579],{},"a name",[1362,234581,234582],{},[941,234583,75323],{},[1362,234585,234586],{},"one wrapped tier DEK",[1344,234588,234589,234594,234598],{},[1362,234590,234591,234592],{},"omitted → ",[941,234593,6651],{},[1362,234595,234596],{},[941,234597,75330],{},[1362,234599,234600,234601,234604],{},"a map, ",[941,234602,234603],{},"\u003Ccollection>#\u003Ctier>"," → wrapped DEK, one entry per collection",[937,234606,46418,234607,234610,234611,997,234614,234617,234618,234620,234621,234624],{},[950,234608,234609],{},"A collection-wide token is a MAP, not a wildcard key."," Every collection has its own tier\nDEK — ",[941,234612,234613],{},"docs#1",[941,234615,234616],{},"ledger#1"," are different keys — so one key cannot decrypt many collections.\nEach entry merges under its real ",[941,234619,234603],{}," slot, and the access gate needs no\nwildcard case and gains no new privilege surface. (An earlier design tried a single\n",[941,234622,234623],{},"__any#\u003Ctier>"," slot; it was removed before release because the gate would have said yes and\ndecryption would then have failed.)",[937,234626,1649,234627,234630,234631,234633],{},[950,234628,234629],{},"\"Every collection\" means every collection AS OF ISSUE TIME."," The map is a snapshot taken\nwhen ",[941,234632,233342],{}," ran: a collection created afterwards is not in it and is not granted by it,\nbecause the grantor had no DEK to wrap. If a grantee needs access to collections that do not\nexist yet, re-issue — there is no form of this token that widens itself later.",[24204,234635,234637],{"id":234636},"what-the-grantor-needs-from-the-target","What the grantor needs from the target",[937,234639,234640,234641,234643,234644,234647,234648,234650,234651,234653],{},"Since hub 0.9.0 (core#65): only the target's ",[950,234642,12],{}," half. The tier DEKs are AES-KW-wrapped\nunder a per-token ",[1092,234645,234646],{},"content key",", and that key is RSA-OAEP-sealed to the target's inbox public\nhalf in ",[941,234649,75341],{},", so a grantor can delegate to a member whose secret it does not know.\nOpening one needs the target's inbox ",[1092,234652,196276],{}," half, which is sealed in their own keyring under\na DEK only their KEK unwraps.",[937,234655,1649,234656,234661,234662,234664],{},[950,234657,234658,234659],{},"A token written before 0.9.0 has no ",[941,234660,75341],{}," and is wrapped under the grantor's own\nKEK. Those still load for a target that shares that KEK — the only case that ever worked — and\nare skipped for anyone else, exactly as before. So ",[941,234663,75341],{}," being absent identifies an older\ntoken, never a broken one.",[937,234666,234667,234668,234670,234671,234673],{},"⭐ The slot ",[950,234669,71352],{}," stay readable to any member holding the ",[941,234672,234468],{}," DEK while the keys\ndo not: an operator can enumerate active delegations for audit without being able to use them.",[1022,234675,234677],{"id":234676},"readwrite-gating","Read\u002Fwrite gating",[937,234679,234680,234681,234684,234685,234688],{},"Tiers are enforced at two choke points — a ",[950,234682,234683],{},"read gate"," that makes above-tier records\ninvisible, and a ",[950,234686,234687],{},"write ring"," that refuses tier-0 writes onto elevated records.",[937,234690,234691,234694,234695,968,234697,1707,234699,2462,234701,234703,234704,234706,234707,234709,234710,234712,234713,234715,234716,234718,234719,22207,234721,234724,234725,1707,234727,234729,234730,234734,234735,234737],{},[950,234692,234693],{},"Reads are gated at runtime, before any decrypt."," On a tiered collection, ",[941,234696,14639],{},[941,234698,24407],{},[941,234700,24715],{},[941,234702,25639],{}," treat an ",[950,234705,109109],{}," record as nonexistent for a lower-tier caller —\nthe check runs off the record's live ",[941,234708,1716],{}," envelope header before any key material is\nresolved. The ",[941,234711,22782],{}," option chooses the shape: ",[941,234714,234411],{}," (default) returns\n",[941,234717,6651],{}," \u002F omits the id; ",[941,234720,234418],{},[941,234722,234723],{},"GhostRecord"," placeholder that reveals the record\nexists and its tier without its contents. The only sanctioned cleared paths are ",[941,234726,21984],{},[941,234728,21987],{},", and — for attachments — ",[1007,234731,234732],{"href":233679},[941,234733,73914],{},";\nan ungranted cleared read throws ",[941,234736,109129],{}," and mints no key.",[937,234739,234740,234743,234744,234746,234747,4688,234749,234751,234752,177793,234755,964,234758,234761,234762,1625,234764,234766,234767,234769,234770,234772,234773,234775,234776,1261,234778,1261,234780,234782,234783,1952,234785,234787,234788,234790,234791,234793,234794,234797],{},[950,234741,234742],{},"The write ring refuses tier-0 writes onto elevated records."," On a collection that declares\n",[941,234745,872],{},", a tier-0 ",[941,234748,20953],{},[941,234750,9467],{}," targeting a record whose live envelope is elevated\n(",[941,234753,234754],{},"_tier > 0",[941,234756,234757],{},"TierWriteRefusedError",[1092,234759,234760],{},"even for a caller who holds the tier's DEK",".\nThe refusal is not about clearance; ",[941,234763,20953],{},[941,234765,9467],{}," are simply the wrong API for an elevated\nrecord: a tier-0 ",[941,234768,20953],{}," over one would silently demote it (an elevated record reads as absent,\nso ",[941,234771,20953],{}," treats the id as a create), and a tier-0 ",[941,234774,9467],{}," would write a tier-less marker\nthat erases the elevation signal. Use ",[941,234777,73849],{},[941,234779,21990],{},[941,234781,21993],{}," — the tier-aware\npaths — which are unaffected, as are tier-0 records and any collection that never declares\n",[941,234784,872],{},[941,234786,12205],{}," hooks still fire for a refused ",[941,234789,3161],{},", receiving a ",[950,234792,6651],{}," prior for the\nelevated record (never its plaintext); ",[941,234795,234796],{},"beforePut"," gate handlers do not fire at all — the\nrefusal precedes the gate bus.",[1022,234799,234801],{"id":234800},"what-tiers-do-and-dont-protect","What tiers do and don't protect",[937,234803,234804,234805,234808],{},"Tiers are a ",[950,234806,234807],{},"confidentiality"," boundary keyed by DEK, not an access-control policy layer.\nHonestly, that means:",[1111,234810,234811,234817],{},[1114,234812,234813,234816],{},[950,234814,234815],{},"What they protect."," An elevated record's body, its per-tier CEK, and — on a tiered blob\ncollection — its attachment content, chunk addresses, and slot metadata are all encrypted\nunder the tier DEK. A tier-0 key holder, including one with raw read access to the storage\nbackend, cannot decrypt them at rest. Above-tier records are invisible on tier-0 read\nsurfaces before any decrypt, and the write ring stops a tier-0 caller from silently demoting\nor erasing them.",[1114,234818,234819,234822,234823,234826,234827,2514,234829,234831,234832,234834,234835,234838,234839,234841,234842,234845,234846,234848,234849,234851,234852,234855],{},[950,234820,234821],{},"What they don't."," Tiers protect record ",[1092,234824,234825],{},"bodies",", not the envelope protocol header. The\nfields a store, the sync engine, and history read without keys — including ",[941,234828,1716],{},[941,234830,1719],{}," — are ",[950,234833,1004],{}," encrypted (see\n",[1007,234836,234837],{"href":26},"forking the enclave","): an observer with backend\naccess learns ",[1092,234840,53557],{}," records are elevated and ",[1092,234843,234844],{},"to what tier",", just not their contents. Tier\nmembership therefore leaks record count and tier distribution. Owners, admins, and custodians\nauto-mint tier DEKs, so tiers are not a defense against a compromised admin keyring. And two\ndocumented at-rest residues survive: a ",[941,234847,111432],{}," shared blob stays decryptable\nunder the flat ",[941,234850,15122],{}," DEK, and a demoted record's ",[1092,234853,234854],{},"prior"," history versions remain\ndecryptable at their original tier until the history keys are rewrapped. Tiers are a graded\nconfidentiality tool for one vault's own key-holders — not a substitute for revoking a\nkeyring you no longer trust.",[1022,234857,234859],{"id":234858},"tiers-across-the-system","Tiers across the system",[937,234861,234862],{},"Tier behavior is documented in each feature's own home; this table is the map.",[1338,234864,234865,234877],{},[1341,234866,234867],{},[1344,234868,234869,234872,234874],{},[1347,234870,234871],{},"Area",[1347,234873,56173],{},[1347,234875,234876],{},"One-line behavior",[1357,234878,234879,234895,234912,234933,234949,234961,234983],{},[1344,234880,234881,234884,234888],{},[1362,234882,234883],{},"Crypto composition",[1362,234885,234886],{},[1007,234887,233824],{"href":154040},[1362,234889,234890,1625,234892,234894],{},[941,234891,14606],{},[941,234893,14609],{}," re-wrap the same CEK from source-tier DEK to target-tier DEK; body key unchanged.",[1344,234896,234897,234899,234903],{},[1362,234898,111454],{},[1362,234900,234901],{},[1007,234902,233680],{"href":233679},[1362,234904,234905,234906,234909,234910,1520],{},"A blob's storage tier follows its owning record; ",[941,234907,234908],{},"blobTierPolicy: 'isolate' | 'dedup'"," governs shared content; cleared read via ",[941,234911,73914],{},[1344,234913,234914,234917,234923],{},[1362,234915,234916],{},"Search & embeddings",[1362,234918,234919],{},[1007,234920,234922],{"href":234921},"\u002Fservices\u002Fembeddings#rebuilding-vectors-rebuildembeddings","embeddings → rebuildEmbeddings",[1362,234924,234925,234926,234929,234930,234932],{},"Elevated records are ",[950,234927,234928],{},"skipped, not refused"," — a re-derive counts them in ",[941,234931,75655],{}," rather than writing a searchable sidecar above tier 0.",[1344,234934,234935,234937,234942],{},[1362,234936,56906],{},[1362,234938,234939],{},[1007,234940,234941],{"href":76455},"glossary\u002Fdeletion → Erasure residue",[1362,234943,234944,234946,234947,1520],{},[941,234945,2542],{}," of an elevated owner shreds under its pre-tombstone tier; an undecodable MV output row surfaces as ",[941,234948,56955],{},[1344,234950,234951,234954,234958],{},[1362,234952,234953],{},"Right-to-erasure",[1362,234955,234956],{},[1007,234957,685],{"href":686},[1362,234959,234960],{},"Crypto-shred honors tier: an elevated blob-owner shreds under the record's live tier.",[1344,234962,234963,234966,234970],{},[1362,234964,234965],{},"Policy errors",[1362,234967,234968],{},[1007,234969,844],{"href":845},[1362,234971,5093,234972,1324,234975,234977,234978,34689,234980,234982],{},[941,234973,234974],{},"insufficient-tier",[941,234976,13025],{}," (e.g. from ",[1007,234979,884],{"href":885},[941,234981,98879],{}," gate) belongs to the session authentication ladder, not this page — the caller is expected to authenticate further, not elevate a record.",[1344,234984,234985,234988,234993],{},[1362,234986,234987],{},"Wire fields",[1362,234989,234990],{},[1007,234991,234992],{"href":26},"architecture → forking the enclave",[1362,234994,234995,997,234997,234999],{},[941,234996,1716],{},[941,234998,1719],{}," are family-owned protocol-header fields, readable without keys.",[1022,235001,2621],{"id":2620},[1111,235003,235004,235009,235014,235020,235028],{},[1114,235005,235006,235008],{},[1007,235007,868],{"href":869}," — grants and tiers compose: a delegate's tier DEK is scoped by the\nkeyring the team service manages",[1114,235010,235011,235013],{},[1007,235012,21],{"href":66}," — the per-record CEK and tier-DEK re-wrap this page builds on",[1114,235015,235016,76470,235018],{},[1007,235017,621],{"href":622},[941,235019,73911],{},[1114,235021,235022,235024,235025,235027],{},[1007,235023,844],{"href":845}," — the unrelated session ",[950,235026,16960],{}," ladder",[1114,235029,235030,1368,235032],{},[1007,235031,61743],{"href":598},[1007,235033,1019],{"href":1018},[2645,235035,235036],{},"html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":1037,"searchDepth":1038,"depth":1038,"links":235038},[235039,235040,235041,235054,235055,235056,235057],{"id":233775,"depth":1041,"text":233776},{"id":70092,"depth":1041,"text":70093},{"id":41259,"depth":1041,"text":101012,"children":235042},[235043,235045,235047,235049],{"id":234063,"depth":1038,"text":235044},"Scoped session elevation — vault.elevate()",{"id":234327,"depth":1038,"text":235046},"Per-record moves — collection.elevate() \u002F demote()",{"id":234385,"depth":1038,"text":235048},"Cleared reads — getAtTier() \u002F listAtTier()",{"id":234447,"depth":1038,"text":235050,"children":235051},"Cross-tier delegation — vault.delegate()",[235052,235053],{"id":234545,"depth":2288,"text":234546},{"id":234636,"depth":2288,"text":234637},{"id":234676,"depth":1041,"text":234677},{"id":234800,"depth":1041,"text":234801},{"id":234858,"depth":1041,"text":234859},{"id":2620,"depth":1041,"text":2621},"Record tiers — scoped elevation with TTL + reason, per-tier DEKs, tier-gated reads, and how blobs, search, and forget behave across tiers.",{},{"title":872,"description":235058},"0.1.0-pre.3","mm2u5Trd1H1xXFYhk2B00I_PRVw2hmB8huZplHQyBTc",{"id":235064,"title":876,"access":12,"apiToc":929,"body":235065,"category":1042,"description":236715,"extension":1044,"meta":236716,"navigation":1042,"nest":1047,"path":877,"seo":236717,"sinceVersion":1042,"status":1049,"stem":878,"subtitle":1042,"tier":1050,"tocExternal":1042,"__hash__":236718},"docs\u002Fservices\u002Ftransactions.md",{"type":931,"value":235066,"toc":236692},[235067,235069,235072,235074,235097,235099,235116,235118,235162,235164,235301,235303,235687,235696,235700,235716,236065,236098,236101,236129,236136,236218,236221,236225,236271,236280,236299,236301,236307,236328,236338,236344,236347,236369,236374,236385,236390,236416,236422,236463,236482,236501,236503,236546,236548,236598,236600,236644,236646,236689],[934,235068,876],{"id":876},[46871,235070],{":rows":235071},"[{\"label\":\"Subpath\",\"value\":\"`@noy-db\u002Fhub\u002Ftransactions`\"},{\"label\":\"Factory\",\"value\":\"`withTransactions()`\"},{\"label\":\"Layer\",\"value\":\"`with-commit`\"}]",[1022,235073,29857],{"id":29856},[937,235075,235076,235077,1625,235079,56825,235081,235083,235084,235087,235088,235091,235092,1625,235094,235096],{},"By default, noy-db writes one record at a time: every ",[941,235078,20953],{},[941,235080,9467],{},[1007,235082,6471],{"href":58262}," commits on its own and is visible immediately. The transactions service adds ",[950,235085,235086],{},"multi-record atomic writes"," on top of that: ",[941,235089,235090],{},"db.transaction(async (tx) => { ... })"," runs a callback whose body stages ",[941,235093,3161],{},[941,235095,3164],{}," operations across any number of collections or vaults, and the hub commits every staged op together — or none of them — when the callback returns or throws. Reads inside the body see your own staged writes (\"read-your-writes\") even though nothing is persisted until commit. The service is a tree-shake seam: ~280 lines of transaction-executor code only reach your bundle once you opt in.",[1022,235098,111660],{"id":111659},[1111,235100,235101,235104,235107,235113],{},[1114,235102,235103],{},"Cross-collection invariants (an invoice and its payment must both succeed, or neither does)",[1114,235105,235106],{},"Cross-vault writes (the same logical operation touching two tenants)",[1114,235108,235109,235110,235112],{},"Optimistic concurrency control via ",[941,235111,8477],{}," on staged ops",[1114,235114,235115],{},"Workflows where a partial write would corrupt downstream state",[1022,235117,70093],{"id":70092},[1111,235119,235120,235128,235146],{},[1114,235121,235122,964,235124,107496,235126,1520],{},[950,235123,107493],{},[941,235125,21246],{},[941,235127,7989],{},[1114,235129,235130,235133,235134,235136,235137,968,235139,4765,235141,235143,235144,1520],{},[1092,235131,235132],{},"For the amendment form:"," guard declarations (",[941,235135,154296],{},") plus ",[941,235138,9901],{},[941,235140,9898],{},[941,235142,9904],{}," role on every vault the body touches — see ",[1007,235145,693],{"href":694},[1114,235147,235148,235151,235152,1324,235154,235156,235157,235161],{},[1092,235149,235150],{},"Atomicity note:"," true all-or-nothing storage atomicity needs a store that declares ",[941,235153,24565],{},[950,235155,14128],{}," a batch that is statically safe — see ",[1007,235158,235160],{"href":235159},"#atomicity-guarantee","Atomicity guarantee"," below. Every other transaction falls back to the per-record OCC executor described in this page, with best-effort unwind.",[1022,235163,99864],{"id":99863},[2217,235165,235167],{"className":2219,"code":235166,"language":2221,"meta":1037,"style":1037},"import { createNoydb } from '@noy-db\u002Fhub'\nimport { withTransactions } from '@noy-db\u002Fhub\u002Ftransactions'\nimport { toMemory } from '@noy-db\u002Fto-memory'\n\nconst db = await createNoydb({\n  store: toMemory(),\n  user: 'alice',\n  secret: 'a-strong-secret',\n  transactionsStrategy: withTransactions(),\n})\n",[941,235168,235169,235187,235205,235223,235227,235243,235255,235269,235283,235295],{"__ignoreMap":1037},[2225,235170,235171,235173,235175,235177,235179,235181,235183,235185],{"class":2227,"line":2228},[2225,235172,2232],{"class":2231},[2225,235174,2236],{"class":2235},[2225,235176,2744],{"class":2239},[2225,235178,2243],{"class":2235},[2225,235180,2246],{"class":2231},[2225,235182,2249],{"class":2235},[2225,235184,1086],{"class":2252},[2225,235186,2256],{"class":2235},[2225,235188,235189,235191,235193,235195,235197,235199,235201,235203],{"class":2227,"line":1041},[2225,235190,2232],{"class":2231},[2225,235192,2236],{"class":2235},[2225,235194,154396],{"class":2239},[2225,235196,2243],{"class":2235},[2225,235198,2246],{"class":2231},[2225,235200,2249],{"class":2235},[2225,235202,154405],{"class":2252},[2225,235204,2256],{"class":2235},[2225,235206,235207,235209,235211,235213,235215,235217,235219,235221],{"class":2227,"line":1038},[2225,235208,2232],{"class":2231},[2225,235210,2236],{"class":2235},[2225,235212,68576],{"class":2239},[2225,235214,2243],{"class":2235},[2225,235216,2246],{"class":2231},[2225,235218,2249],{"class":2235},[2225,235220,9638],{"class":2252},[2225,235222,2256],{"class":2235},[2225,235224,235225],{"class":2227,"line":2288},[2225,235226,2285],{"emptyLinePlaceholder":2284},[2225,235228,235229,235231,235233,235235,235237,235239,235241],{"class":2227,"line":2304},[2225,235230,2775],{"class":2774},[2225,235232,2778],{"class":2239},[2225,235234,2781],{"class":2235},[2225,235236,2784],{"class":2231},[2225,235238,2744],{"class":2291},[2225,235240,2789],{"class":2239},[2225,235242,4200],{"class":2235},[2225,235244,235245,235247,235249,235251,235253],{"class":2227,"line":2356},[2225,235246,15608],{"class":2307},[2225,235248,2311],{"class":2235},[2225,235250,68576],{"class":2291},[2225,235252,4228],{"class":2239},[2225,235254,1707],{"class":2235},[2225,235256,235257,235259,235261,235263,235265,235267],{"class":2227,"line":2830},[2225,235258,4205],{"class":2307},[2225,235260,2311],{"class":2235},[2225,235262,2249],{"class":2235},[2225,235264,6408],{"class":2252},[2225,235266,2805],{"class":2235},[2225,235268,1707],{"class":2235},[2225,235270,235271,235273,235275,235277,235279,235281],{"class":2227,"line":2861},[2225,235272,10423],{"class":2307},[2225,235274,2311],{"class":2235},[2225,235276,2249],{"class":2235},[2225,235278,6421],{"class":2252},[2225,235280,2805],{"class":2235},[2225,235282,1707],{"class":2235},[2225,235284,235285,235287,235289,235291,235293],{"class":2227,"line":4246},[2225,235286,154866],{"class":2307},[2225,235288,2311],{"class":2235},[2225,235290,154396],{"class":2291},[2225,235292,4228],{"class":2239},[2225,235294,1707],{"class":2235},[2225,235296,235297,235299],{"class":2227,"line":4253},[2225,235298,2359],{"class":2235},[2225,235300,2362],{"class":2239},[1022,235302,29893],{"id":29892},[2217,235304,235306],{"className":2219,"code":235305,"language":2221,"meta":1037,"style":1037},"type Invoice = { amount: number; status: string }\ntype Payment = { invoiceId: string; amount: number; paidAt: string }\n\nawait db.openVault('acme')\n\nconst result = await db.transaction(async (tx) => {\n  const inv = tx.vault('acme').collection\u003CInvoice>('invoices')\n  const pay = tx.vault('acme').collection\u003CPayment>('payments')\n\n  inv.put('inv-1', { amount: 100, status: 'paid' })\n  pay.put('pay-1', { invoiceId: 'inv-1', amount: 100, paidAt: '2026-07-08T00:00:00Z' })\n\n  \u002F\u002F Read-your-writes: sees the staged put above even though nothing\n  \u002F\u002F has been committed yet.\n  const staged = await inv.get('inv-1')\n\n  return staged.amount \u002F\u002F the body's return value flows through\n})\n\u002F\u002F → 100\n",[941,235307,235308,235334,235368,235372,235392,235396,235426,235471,235516,235520,235563,235619,235623,235628,235633,235660,235664,235677,235683],{"__ignoreMap":1037},[2225,235309,235310,235312,235314,235316,235318,235320,235322,235324,235326,235328,235330,235332],{"class":2227,"line":2228},[2225,235311,6339],{"class":2774},[2225,235313,6343],{"class":6342},[2225,235315,6346],{"class":2235},[2225,235317,2236],{"class":2235},[2225,235319,6351],{"class":2307},[2225,235321,2311],{"class":2235},[2225,235323,6356],{"class":6342},[2225,235325,3769],{"class":2235},[2225,235327,6361],{"class":2307},[2225,235329,2311],{"class":2235},[2225,235331,6366],{"class":6342},[2225,235333,6369],{"class":2235},[2225,235335,235336,235338,235340,235342,235344,235346,235348,235350,235352,235354,235356,235358,235360,235362,235364,235366],{"class":2227,"line":1041},[2225,235337,6339],{"class":2774},[2225,235339,27760],{"class":6342},[2225,235341,6346],{"class":2235},[2225,235343,2236],{"class":2235},[2225,235345,154999],{"class":2307},[2225,235347,2311],{"class":2235},[2225,235349,6366],{"class":6342},[2225,235351,3769],{"class":2235},[2225,235353,6351],{"class":2307},[2225,235355,2311],{"class":2235},[2225,235357,6356],{"class":6342},[2225,235359,3769],{"class":2235},[2225,235361,27789],{"class":2307},[2225,235363,2311],{"class":2235},[2225,235365,6366],{"class":6342},[2225,235367,6369],{"class":2235},[2225,235369,235370],{"class":2227,"line":1038},[2225,235371,2285],{"emptyLinePlaceholder":2284},[2225,235373,235374,235376,235378,235380,235382,235384,235386,235388,235390],{"class":2227,"line":2288},[2225,235375,6500],{"class":6342},[2225,235377,2842],{"class":6342},[2225,235379,1520],{"class":2235},[2225,235381,2847],{"class":6342},[2225,235383,2789],{"class":2239},[2225,235385,2805],{"class":2235},[2225,235387,6450],{"class":2252},[2225,235389,2805],{"class":2235},[2225,235391,2362],{"class":2239},[2225,235393,235394],{"class":2227,"line":2304},[2225,235395,2285],{"emptyLinePlaceholder":2284},[2225,235397,235398,235400,235402,235404,235406,235408,235410,235412,235414,235416,235418,235420,235422,235424],{"class":2227,"line":2356},[2225,235399,2775],{"class":2774},[2225,235401,36920],{"class":2239},[2225,235403,2781],{"class":2235},[2225,235405,2784],{"class":2231},[2225,235407,2842],{"class":2239},[2225,235409,1520],{"class":2235},[2225,235411,155411],{"class":2291},[2225,235413,2789],{"class":2239},[2225,235415,70491],{"class":2774},[2225,235417,953],{"class":2235},[2225,235419,4399],{"class":7052},[2225,235421,1381],{"class":2235},[2225,235423,8116],{"class":2774},[2225,235425,2301],{"class":2235},[2225,235427,235428,235430,235432,235434,235437,235439,235441,235443,235445,235447,235449,235451,235453,235455,235457,235459,235461,235463,235465,235467,235469],{"class":2227,"line":2830},[2225,235429,8123],{"class":2774},[2225,235431,27000],{"class":2239},[2225,235433,6346],{"class":2235},[2225,235435,235436],{"class":2239}," tx",[2225,235438,1520],{"class":2235},[2225,235440,2694],{"class":2291},[2225,235442,2789],{"class":2307},[2225,235444,2805],{"class":2235},[2225,235446,6450],{"class":2252},[2225,235448,2805],{"class":2235},[2225,235450,1381],{"class":2307},[2225,235452,1520],{"class":2235},[2225,235454,6471],{"class":2291},[2225,235456,6474],{"class":2235},[2225,235458,6477],{"class":6342},[2225,235460,6480],{"class":2235},[2225,235462,2789],{"class":2307},[2225,235464,2805],{"class":2235},[2225,235466,6487],{"class":2252},[2225,235468,2805],{"class":2235},[2225,235470,2362],{"class":2307},[2225,235472,235473,235475,235478,235480,235482,235484,235486,235488,235490,235492,235494,235496,235498,235500,235502,235504,235506,235508,235510,235512,235514],{"class":2227,"line":2861},[2225,235474,8123],{"class":2774},[2225,235476,235477],{"class":2239}," pay",[2225,235479,6346],{"class":2235},[2225,235481,235436],{"class":2239},[2225,235483,1520],{"class":2235},[2225,235485,2694],{"class":2291},[2225,235487,2789],{"class":2307},[2225,235489,2805],{"class":2235},[2225,235491,6450],{"class":2252},[2225,235493,2805],{"class":2235},[2225,235495,1381],{"class":2307},[2225,235497,1520],{"class":2235},[2225,235499,6471],{"class":2291},[2225,235501,6474],{"class":2235},[2225,235503,138724],{"class":6342},[2225,235505,6480],{"class":2235},[2225,235507,2789],{"class":2307},[2225,235509,2805],{"class":2235},[2225,235511,32579],{"class":2252},[2225,235513,2805],{"class":2235},[2225,235515,2362],{"class":2307},[2225,235517,235518],{"class":2227,"line":4246},[2225,235519,2285],{"emptyLinePlaceholder":2284},[2225,235521,235522,235525,235527,235529,235531,235533,235535,235537,235539,235541,235543,235545,235547,235549,235551,235553,235555,235557,235559,235561],{"class":2227,"line":4253},[2225,235523,235524],{"class":2239},"  inv",[2225,235526,1520],{"class":2235},[2225,235528,3161],{"class":2291},[2225,235530,2789],{"class":2307},[2225,235532,2805],{"class":2235},[2225,235534,6514],{"class":2252},[2225,235536,2805],{"class":2235},[2225,235538,2298],{"class":2235},[2225,235540,2236],{"class":2235},[2225,235542,6351],{"class":2307},[2225,235544,2311],{"class":2235},[2225,235546,6528],{"class":6527},[2225,235548,2298],{"class":2235},[2225,235550,6361],{"class":2307},[2225,235552,2311],{"class":2235},[2225,235554,2249],{"class":2235},[2225,235556,6539],{"class":2252},[2225,235558,2805],{"class":2235},[2225,235560,2243],{"class":2235},[2225,235562,2362],{"class":2307},[2225,235564,235565,235568,235570,235572,235574,235576,235578,235580,235582,235584,235586,235588,235590,235592,235594,235596,235598,235600,235602,235604,235606,235608,235610,235613,235615,235617],{"class":2227,"line":4259},[2225,235566,235567],{"class":2239},"  pay",[2225,235569,1520],{"class":2235},[2225,235571,3161],{"class":2291},[2225,235573,2789],{"class":2307},[2225,235575,2805],{"class":2235},[2225,235577,225781],{"class":2252},[2225,235579,2805],{"class":2235},[2225,235581,2298],{"class":2235},[2225,235583,2236],{"class":2235},[2225,235585,154999],{"class":2307},[2225,235587,2311],{"class":2235},[2225,235589,2249],{"class":2235},[2225,235591,6514],{"class":2252},[2225,235593,2805],{"class":2235},[2225,235595,2298],{"class":2235},[2225,235597,6351],{"class":2307},[2225,235599,2311],{"class":2235},[2225,235601,6528],{"class":6527},[2225,235603,2298],{"class":2235},[2225,235605,27789],{"class":2307},[2225,235607,2311],{"class":2235},[2225,235609,2249],{"class":2235},[2225,235611,235612],{"class":2252},"2026-07-08T00:00:00Z",[2225,235614,2805],{"class":2235},[2225,235616,2243],{"class":2235},[2225,235618,2362],{"class":2307},[2225,235620,235621],{"class":2227,"line":6548},[2225,235622,2285],{"emptyLinePlaceholder":2284},[2225,235624,235625],{"class":2227,"line":6576},[2225,235626,235627],{"class":2763},"  \u002F\u002F Read-your-writes: sees the staged put above even though nothing\n",[2225,235629,235630],{"class":2227,"line":6582},[2225,235631,235632],{"class":2763},"  \u002F\u002F has been committed yet.\n",[2225,235634,235635,235637,235640,235642,235644,235646,235648,235650,235652,235654,235656,235658],{"class":2227,"line":6587},[2225,235636,8123],{"class":2774},[2225,235638,235639],{"class":2239}," staged",[2225,235641,6346],{"class":2235},[2225,235643,2784],{"class":2231},[2225,235645,27000],{"class":2239},[2225,235647,1520],{"class":2235},[2225,235649,3158],{"class":2291},[2225,235651,2789],{"class":2307},[2225,235653,2805],{"class":2235},[2225,235655,6514],{"class":2252},[2225,235657,2805],{"class":2235},[2225,235659,2362],{"class":2307},[2225,235661,235662],{"class":2227,"line":7174},[2225,235663,2285],{"emptyLinePlaceholder":2284},[2225,235665,235666,235668,235670,235672,235674],{"class":2227,"line":7180},[2225,235667,8220],{"class":2231},[2225,235669,235639],{"class":2239},[2225,235671,1520],{"class":2235},[2225,235673,6617],{"class":2239},[2225,235675,235676],{"class":2763}," \u002F\u002F the body's return value flows through\n",[2225,235678,235679,235681],{"class":2227,"line":7220},[2225,235680,2359],{"class":2235},[2225,235682,2362],{"class":2239},[2225,235684,235685],{"class":2227,"line":7225},[2225,235686,209483],{"class":2763},[937,235688,235689,235690,235692,235693,235695],{},"If the body throws before returning, nothing is persisted. If a staged op carries an ",[941,235691,8477],{}," that no longer matches the stored record, the transaction throws ",[941,235694,6860],{}," before any writes happen (a pre-flight compare-and-swap check).",[1591,235697,235699],{"id":235698},"commit-time-invariants","Commit-time invariants",[937,235701,235702,235705,235706,235708,235709,235712,235713,235715],{},[941,235703,235704],{},"withTransactions({ invariants: [...] })"," declares set-level invariants that run at commit, over the change-set of an ordinary ",[941,235707,47889],{}," (and of amendment transactions — see ",[1007,235710,693],{"href":235711},".\u002Fguards#amendment-flow","). There is no role gate: this generalizes the guard service's ",[941,235714,155606],{}," to plain transactions.",[2217,235717,235719],{"className":2219,"code":235718,"language":2221,"meta":1037,"style":1037},"import { InvariantError } from '@noy-db\u002Fhub'\nimport { withTransactions } from '@noy-db\u002Fhub\u002Ftransactions'\n\nconst db = await createNoydb({\n  store: toMemory(),\n  user: 'alice',\n  secret: 'a-strong-secret',\n  transactionsStrategy: withTransactions({\n    invariants: [{\n      scope: 'lines', \u002F\u002F collection name\n      check: (changes, ctx) => {\n        const total = (side: 'before' | 'after') =>\n          changes.reduce((sum, c) => sum + ((c[side] as { amount?: number } | null)?.amount ?? 0), 0)\n        if (total('before') !== total('after')) {\n          throw new InvariantError('lines total must be preserved')\n        }\n      },\n    }],\n  }),\n})\n",[941,235720,235721,235739,235757,235761,235777,235789,235803,235817,235829,235840,235858,235879,235911,235982,236016,236035,236039,236043,236051,236059],{"__ignoreMap":1037},[2225,235722,235723,235725,235727,235729,235731,235733,235735,235737],{"class":2227,"line":2228},[2225,235724,2232],{"class":2231},[2225,235726,2236],{"class":2235},[2225,235728,154377],{"class":2239},[2225,235730,2243],{"class":2235},[2225,235732,2246],{"class":2231},[2225,235734,2249],{"class":2235},[2225,235736,1086],{"class":2252},[2225,235738,2256],{"class":2235},[2225,235740,235741,235743,235745,235747,235749,235751,235753,235755],{"class":2227,"line":1041},[2225,235742,2232],{"class":2231},[2225,235744,2236],{"class":2235},[2225,235746,154396],{"class":2239},[2225,235748,2243],{"class":2235},[2225,235750,2246],{"class":2231},[2225,235752,2249],{"class":2235},[2225,235754,154405],{"class":2252},[2225,235756,2256],{"class":2235},[2225,235758,235759],{"class":2227,"line":1038},[2225,235760,2285],{"emptyLinePlaceholder":2284},[2225,235762,235763,235765,235767,235769,235771,235773,235775],{"class":2227,"line":2288},[2225,235764,2775],{"class":2774},[2225,235766,2778],{"class":2239},[2225,235768,2781],{"class":2235},[2225,235770,2784],{"class":2231},[2225,235772,2744],{"class":2291},[2225,235774,2789],{"class":2239},[2225,235776,4200],{"class":2235},[2225,235778,235779,235781,235783,235785,235787],{"class":2227,"line":2304},[2225,235780,15608],{"class":2307},[2225,235782,2311],{"class":2235},[2225,235784,68576],{"class":2291},[2225,235786,4228],{"class":2239},[2225,235788,1707],{"class":2235},[2225,235790,235791,235793,235795,235797,235799,235801],{"class":2227,"line":2356},[2225,235792,4205],{"class":2307},[2225,235794,2311],{"class":2235},[2225,235796,2249],{"class":2235},[2225,235798,6408],{"class":2252},[2225,235800,2805],{"class":2235},[2225,235802,1707],{"class":2235},[2225,235804,235805,235807,235809,235811,235813,235815],{"class":2227,"line":2830},[2225,235806,10423],{"class":2307},[2225,235808,2311],{"class":2235},[2225,235810,2249],{"class":2235},[2225,235812,6421],{"class":2252},[2225,235814,2805],{"class":2235},[2225,235816,1707],{"class":2235},[2225,235818,235819,235821,235823,235825,235827],{"class":2227,"line":2861},[2225,235820,154866],{"class":2307},[2225,235822,2311],{"class":2235},[2225,235824,154396],{"class":2291},[2225,235826,2789],{"class":2239},[2225,235828,4200],{"class":2235},[2225,235830,235831,235834,235836,235838],{"class":2227,"line":4246},[2225,235832,235833],{"class":2307},"    invariants",[2225,235835,2311],{"class":2235},[2225,235837,8667],{"class":2239},[2225,235839,4200],{"class":2235},[2225,235841,235842,235845,235847,235849,235851,235853,235855],{"class":2227,"line":4253},[2225,235843,235844],{"class":2307},"      scope",[2225,235846,2311],{"class":2235},[2225,235848,2249],{"class":2235},[2225,235850,121152],{"class":2252},[2225,235852,2805],{"class":2235},[2225,235854,2298],{"class":2235},[2225,235856,235857],{"class":2763}," \u002F\u002F collection name\n",[2225,235859,235860,235863,235865,235867,235869,235871,235873,235875,235877],{"class":2227,"line":4259},[2225,235861,235862],{"class":2291},"      check",[2225,235864,2311],{"class":2235},[2225,235866,953],{"class":2235},[2225,235868,154638],{"class":7052},[2225,235870,2298],{"class":2235},[2225,235872,138692],{"class":7052},[2225,235874,1381],{"class":2235},[2225,235876,8116],{"class":2774},[2225,235878,2301],{"class":2235},[2225,235880,235881,235883,235885,235887,235889,235891,235893,235895,235897,235899,235901,235903,235905,235907,235909],{"class":2227,"line":6548},[2225,235882,8818],{"class":2774},[2225,235884,27452],{"class":2239},[2225,235886,6346],{"class":2235},[2225,235888,953],{"class":2235},[2225,235890,170523],{"class":7052},[2225,235892,2311],{"class":2235},[2225,235894,2249],{"class":2235},[2225,235896,11357],{"class":2252},[2225,235898,2805],{"class":2235},[2225,235900,7087],{"class":2235},[2225,235902,2249],{"class":2235},[2225,235904,10761],{"class":2252},[2225,235906,2805],{"class":2235},[2225,235908,1381],{"class":2235},[2225,235910,71229],{"class":2774},[2225,235912,235913,235916,235918,235920,235922,235924,235926,235928,235930,235932,235934,235936,235938,235940,235942,235944,235946,235948,235950,235952,235954,235956,235958,235960,235962,235964,235966,235968,235970,235972,235974,235976,235978,235980],{"class":2227,"line":6576},[2225,235914,235915],{"class":2239},"          changes",[2225,235917,1520],{"class":2235},[2225,235919,804],{"class":2291},[2225,235921,2789],{"class":2307},[2225,235923,2789],{"class":2235},[2225,235925,28196],{"class":7052},[2225,235927,2298],{"class":2235},[2225,235929,8169],{"class":7052},[2225,235931,1381],{"class":2235},[2225,235933,8116],{"class":2774},[2225,235935,27298],{"class":2239},[2225,235937,154705],{"class":2235},[2225,235939,154708],{"class":2307},[2225,235941,8200],{"class":2239},[2225,235943,8885],{"class":2307},[2225,235945,170523],{"class":2239},[2225,235947,8890],{"class":2307},[2225,235949,4899],{"class":2231},[2225,235951,2236],{"class":2235},[2225,235953,6351],{"class":2307},[2225,235955,7011],{"class":2235},[2225,235957,6356],{"class":6342},[2225,235959,2243],{"class":2235},[2225,235961,7087],{"class":2235},[2225,235963,7090],{"class":6342},[2225,235965,1381],{"class":2307},[2225,235967,8457],{"class":2235},[2225,235969,6617],{"class":2239},[2225,235971,8239],{"class":2235},[2225,235973,8464],{"class":6527},[2225,235975,1381],{"class":2307},[2225,235977,2298],{"class":2235},[2225,235979,8464],{"class":6527},[2225,235981,2362],{"class":2307},[2225,235983,235984,235986,235988,235990,235992,235994,235996,235998,236000,236002,236004,236006,236008,236010,236012,236014],{"class":2227,"line":6582},[2225,235985,8857],{"class":2231},[2225,235987,953],{"class":2307},[2225,235989,64200],{"class":2291},[2225,235991,2789],{"class":2307},[2225,235993,2805],{"class":2235},[2225,235995,11357],{"class":2252},[2225,235997,2805],{"class":2235},[2225,235999,8495],{"class":2307},[2225,236001,32545],{"class":2235},[2225,236003,27452],{"class":2291},[2225,236005,2789],{"class":2307},[2225,236007,2805],{"class":2235},[2225,236009,10761],{"class":2252},[2225,236011,2805],{"class":2235},[2225,236013,7620],{"class":2307},[2225,236015,4200],{"class":2235},[2225,236017,236018,236020,236022,236024,236026,236028,236031,236033],{"class":2227,"line":6587},[2225,236019,155978],{"class":2231},[2225,236021,8130],{"class":2235},[2225,236023,154377],{"class":2291},[2225,236025,2789],{"class":2307},[2225,236027,2805],{"class":2235},[2225,236029,236030],{"class":2252},"lines total must be preserved",[2225,236032,2805],{"class":2235},[2225,236034,2362],{"class":2307},[2225,236036,236037],{"class":2227,"line":7174},[2225,236038,9180],{"class":2235},[2225,236040,236041],{"class":2227,"line":7180},[2225,236042,60068],{"class":2235},[2225,236044,236045,236047,236049],{"class":2227,"line":7220},[2225,236046,80146],{"class":2235},[2225,236048,8803],{"class":2239},[2225,236050,1707],{"class":2235},[2225,236052,236053,236055,236057],{"class":2227,"line":7225},[2225,236054,19143],{"class":2235},[2225,236056,1381],{"class":2239},[2225,236058,1707],{"class":2235},[2225,236060,236061,236063],{"class":2227,"line":7231},[2225,236062,2359],{"class":2235},[2225,236064,2362],{"class":2239},[937,236066,236067,236069,236070,236072,236073,236076,236077,236080,236081,236083,236084,236086,236087,236089,236090,236092,236093,236095,236096,1520],{},[941,236068,13544],{}," is the collection name the invariant watches — ",[941,236071,154924],{}," only fires when the committed transaction wrote at least one record in that collection. ",[941,236074,236075],{},"check(changes, ctx)"," receives a ",[941,236078,236079],{},"ReadonlyArray\u003CGuardChange>"," for that scope: ",[941,236082,11357],{}," is the plaintext prior record (",[941,236085,6651],{}," on insert), ",[941,236088,10761],{}," is the written record (",[941,236091,6651],{}," on delete). The plaintext ",[941,236094,11357],{}," is captured before the writes execute, so invariants see the true prior state. A throw reverts the whole transaction and surfaces as ",[941,236097,155701],{},[1022,236099,235160],{"id":236100},"atomicity-guarantee",[937,236102,236103,236105,236106,236112,236113,236115,236116,8495,236118,236121,236122,236125,236126,236128],{},[941,236104,7989],{}," commits ",[950,236107,236108,236109,236111],{},"all-or-nothing through one ",[941,236110,24560],{}," batch"," when the store declares ",[941,236114,24565],{}," (and implements ",[941,236117,9749],{},[950,236119,236120],{},"and the batch is statically safe",". Otherwise it falls back to the per-record OCC executor described above, with best-effort unwind — unchanged from prior behavior. The fallback is ",[950,236123,236124],{},"silent by design",": nothing in the return value of ",[941,236127,47889],{}," tells you which path a given call took.",[937,236130,236131,236132,236135],{},"A batch is ",[950,236133,236134],{},"statically safe"," only when ALL of the following hold:",[1111,236137,236138,236145,236168,236205],{},[1114,236139,236140,236141,236144],{},"No touched collection has a ",[950,236142,236143],{},"derivation or materialized-view source",", at any lifecycle — lazy included.",[1114,236146,236147,236148,236151,236152,236155,236156,236158,236159,236161,236162,236167],{},"No touched collection is ",[950,236149,236150],{},"CRDT-mode",", has ",[950,236153,236154],{},"unique constraints",", or has ",[950,236157,22753],{}," on the write direction. Since hub ",[941,236160,24551],{},", a delete no longer forces the OCC path by itself (",[1007,236163,236166],{"href":236164,"rel":236165},"https:\u002F\u002Fgithub.com\u002Fnoy-db\u002Fcore\u002Fissues\u002F922",[3371],"noy-db#922","): the eligibility check consults every source a delete cascade can fire from — lookup-ref edges, classic inbound refs, and managed-link endpoints — and only a collection one of those touches falls back to OCC (the cascades run during prepare, which is not abortable). A genuinely refs-free collection's deletes commit all-or-nothing alongside puts.",[1114,236169,236170,236171,236176,236177,236179,236180,236182,236183,236188,236189,236191,236192,1625,236195,236198,236199,236201,236202,236204],{},"No live ",[950,236172,236173,236175],{},[941,236174,12205],{}," hook"," — the refusal-capable kind — is registered anywhere on the ",[941,236178,62],{}," instance (a db-global signal). Since hub ",[941,236181,24551],{},", after-write observers no longer forfeit the atomic path (",[1007,236184,236187],{"href":236185,"rel":236186},"https:\u002F\u002Fgithub.com\u002Fnoy-db\u002Fcore\u002Fissues\u002F931",[3371],"noy-db#931","): user ",[941,236190,12212],{}," hooks and the ",[941,236193,236194],{},"afterPut",[941,236196,236197],{},"afterDelete"," observe bus fire per op on the atomic path too, after each leg's finalize — so the auto-registered cross-tab write relay (",[941,236200,16061],{},") or a ",[941,236203,73758],{}," subject index no longer pushes every batch onto OCC. Ordering note: on the atomic path these observers fire after the batch is durable (in staged order), where the OCC loop interleaves them per op.",[1114,236206,236207,236208,236211,236212,236217],{},"The transaction is ",[950,236209,236210],{},"not amendment mode",", and the batch has no ",[950,236213,236214,236215],{},"duplicate ",[941,236216,6299],{}," target.",[937,236219,236220],{},"Schema gates (update-gate + fence) are asserted per-op the same way on both paths.",[1591,236222,236224],{"id":236223},"what-differs-between-the-two-paths","What differs between the two paths",[1111,236226,236227,236247,236265],{},[1114,236228,236229,236232,236233,236235,236236,1625,236238,236240,236241,236243,236244,236246],{},[950,236230,236231],{},"Commit ordering."," On the atomic path, history snapshots, ledger appends, change events, after-write observers (",[941,236234,12212],{}," hooks + the ",[941,236237,236194],{},[941,236239,236197],{}," bus, since hub ",[941,236242,24551],{},"), and dirty-marking all fire per-op in staged order ",[950,236245,10761],{}," the batch commits. On the OCC path they interleave with execution, per op, as before. Outcome-identical on success; observable to anything subscribing to events mid-transaction.",[1114,236248,236249,236252,236253,236255,236256,236258,236259,236262,236263,1520],{},[950,236250,236251],{},"Failure behavior."," A rejected atomic batch — including a store-side ",[941,236254,8477],{}," CAS rejection — applies ",[950,236257,18459],{},": no ledger entries, no events, no revert pass, because nothing landed. A ",[1092,236260,236261],{},"post-commit"," failure (finalize\u002Finvariant) reverts through the atomic-revert path on stores that support it. The OCC path keeps its existing best-effort revert — see ",[1007,236264,68210],{"href":126812},[1114,236266,236267,236270],{},[950,236268,236269],{},"Crash window."," The batch write itself is atomic on the atomic path, but the audit artifacts (history\u002Fledger) written after it are not covered by that same batch: a crash between commit and finalize can lose audit entries for a batch that already committed. That window is narrower than the OCC path's — where a crash mid-batch can leave a partially-applied write on disk — but it is a real window, not full audit atomicity, and worth stating plainly rather than implying otherwise.",[937,236272,236273,236274,236276,236277,236279],{},"Which stores currently qualify for the atomic path (and any per-store caveats) is tracked on the ",[1007,236275,24587],{"href":861}," — treat \"all-or-nothing\" as a store-dependent property of ",[941,236278,47889],{},", not a blanket one.",[937,236281,2123,236282,968,236284,236287,236288,236290,236291,184088,236294,1520],{},[941,236283,24551],{},[941,236285,236286],{},"collection.putMany({ atomic: true })"," commits through the same one-batch ",[941,236289,24560],{}," path on an eligible batch (",[1007,236292,24570],{"href":24568,"rel":236293},[3371],[1007,236295,236297],{"href":236296},"..\u002Fcore\u002Fcollection\u002Fmethods#putmany",[941,236298,24466],{},[1022,236300,101012],{"id":41259},[1591,236302,236304],{"id":236303},"withtransactionsoptions",[941,236305,236306],{},"withTransactions(options?)",[2217,236308,236310],{"className":2219,"code":236309,"language":2221,"meta":1037,"style":1037},"withTransactions(options?: TransactionStrategyOptions): TxStrategy\n\u002F\u002F TransactionStrategyOptions = { invariants?: TransactionInvariant[] }\n",[941,236311,236312,236323],{"__ignoreMap":1037},[2225,236313,236314,236316,236318,236320],{"class":2227,"line":2228},[2225,236315,140657],{"class":2291},[2225,236317,101162],{"class":2239},[2225,236319,7011],{"class":2235},[2225,236321,236322],{"class":2239}," TransactionStrategyOptions): TxStrategy\n",[2225,236324,236325],{"class":2227,"line":1041},[2225,236326,236327],{"class":2763},"\u002F\u002F TransactionStrategyOptions = { invariants?: TransactionInvariant[] }\n",[937,236329,236330,236331,1952,236334,236337],{},"Builds the strategy passed to ",[941,236332,236333],{},"createNoydb({ transactionsStrategy })",[941,236335,236336],{},"options.invariants"," registers the commit-time changeset invariants described above; omit it for plain multi-record atomicity with no extra checks.",[1591,236339,236341],{"id":236340},"txcontext",[941,236342,236343],{},"TxContext",[937,236345,236346],{},"The handle passed into the transaction body.",[1111,236348,236349,236355],{},[1114,236350,236351,236354],{},[941,236352,236353],{},"tx.txId"," — a stable ULID shared by every write the transaction performs.",[1114,236356,236357,236359,236360,236363,236364,236366,236367,957],{},[941,236358,157211],{}," — scope subsequent calls to a named vault; returns a ",[941,236361,236362],{},"TxVault",". The vault must already be open on the ",[941,236365,62],{}," instance (via ",[941,236368,188618],{},[1591,236370,236372],{"id":236371},"txvault",[941,236373,236362],{},[1111,236375,236376],{},[1114,236377,236378,236381,236382,1520],{},[941,236379,236380],{},".collection\u003CT>(name)"," — scope subsequent calls to a named collection; returns a ",[941,236383,236384],{},"TxCollection\u003CT>",[1591,236386,236388],{"id":236387},"txcollectiont",[941,236389,236384],{},[1111,236391,236392,236401,236410],{},[1114,236393,236394,236397,236398,236400],{},[941,236395,236396],{},".get(id): Promise\u003CT | null>"," — reads the most recently staged value for ",[941,236399,6983],{}," in this transaction if one exists, otherwise the current committed value.",[1114,236402,236403,236406,236407,236409],{},[941,236404,236405],{},".put(id, record, options?: { expectedVersion?: number; reason?: string }): void"," — stages a put; nothing is written until the body returns. ",[941,236408,43668],{}," is forwarded to the resulting ledger entry.",[1114,236411,236412,236415],{},[941,236413,236414],{},".delete(id, options?: { expectedVersion?: number }): void"," — stages a delete.",[1591,236417,236419],{"id":236418},"amendmenttxoptions",[941,236420,236421],{},"AmendmentTxOptions",[2217,236423,236425],{"className":2219,"code":236424,"language":2221,"meta":1037,"style":1037},"interface AmendmentTxOptions {\n  readonly amendment: true\n  readonly reason: string \u002F\u002F required, recorded in the ledger entry\n}\n",[941,236426,236427,236436,236446,236459],{"__ignoreMap":1037},[2225,236428,236429,236431,236434],{"class":2227,"line":2228},[2225,236430,6994],{"class":2774},[2225,236432,236433],{"class":6342}," AmendmentTxOptions",[2225,236435,2301],{"class":2235},[2225,236437,236438,236440,236442,236444],{"class":2227,"line":1041},[2225,236439,53580],{"class":2774},[2225,236441,155418],{"class":2307},[2225,236443,2311],{"class":2235},[2225,236445,71026],{"class":6342},[2225,236447,236448,236450,236452,236454,236456],{"class":2227,"line":1038},[2225,236449,53580],{"class":2774},[2225,236451,155427],{"class":2307},[2225,236453,2311],{"class":2235},[2225,236455,6366],{"class":6342},[2225,236457,236458],{"class":2763}," \u002F\u002F required, recorded in the ledger entry\n",[2225,236460,236461],{"class":2227,"line":2288},[2225,236462,7351],{"class":2235},[937,236464,236465,236466,236468,236469,27493,236471,2462,236473,968,236475,4765,236477,236479,236480,23897],{},"The options bag for ",[941,236467,155382],{}," — a privileged transaction form that skips ordinary guard checks in favor of a role-gated invariant review. Requires ",[941,236470,21246],{},[941,236472,154296],{},[941,236474,9901],{},[941,236476,9898],{},[941,236478,9904],{}," role on every vault the body touches. Full walkthrough on the ",[1007,236481,693],{"href":235711},[937,236483,236484,968,236487,2462,236490,236493,236494,236496,236497,997,236499,1520],{},[941,236485,236486],{},"runTransaction",[941,236488,236489],{},"TxStrategy",[941,236491,236492],{},"TransactionInvariant"," are also exported from ",[941,236495,154405],{}," for type annotations and custom strategy composition; application code normally only calls ",[941,236498,7989],{},[941,236500,21246],{},[1022,236502,101425],{"id":101424},[1111,236504,236505,236527,236543],{},[1114,236506,236507,968,236509,2462,236511,236514,236515],{},[941,236508,7989],{},[941,236510,155382],{},[941,236512,236513],{},"db.transaction({ dryRun: true }, fn)"," all throw immediately: ",[1092,236516,236517,236518,17904,236521,202216,236524,202220],{},"\"Multi-record transactions require the tx strategy. Import ",[941,236519,236520],{},"{ withTransactions }",[941,236522,236523],{},"\"@noy-db\u002Fhub\u002Ftransactions\"",[941,236525,236526],{},"createNoydb({ transactionsStrategy: withTransactions() })",[1114,236528,236529,236532,236533,236535,236536,236540,236541,167990],{},[941,236530,236531],{},"db.transaction(vaultName)"," — the sync-peer batch transaction (",[941,236534,4687],{},") — is a distinct overload gated by ",[1007,236537,236538],{"href":174727},[941,236539,4676],{},", not by this service; it is unaffected by whether ",[941,236542,21246],{},[1114,236544,236545],{},"None of the ~280 lines of transaction-executor code reach the bundle until you opt in.",[1022,236547,68210],{"id":68209},[1111,236549,236550,236561,236567,236576,236584,236595],{},[1114,236551,236552,236555,236556,236558,236559,1520],{},[950,236553,236554],{},"Crash window (OCC path)",": on a store\u002Fbatch that doesn't qualify for the atomic path (see ",[1007,236557,235160],{"href":235159},"), commit is pre-flight-check, then execute-each-op, then best-effort revert on failure — it is not a single storage-layer transaction. If the process dies between two already-executed ops, on-disk state can be left partial. The atomic path has its own, narrower crash window — audit artifacts written after the batch commits, not the batch itself — see ",[1007,236560,235160],{"href":235159},[1114,236562,236563,236566],{},[950,236564,236565],{},"Revert is best-effort, not audited (OCC path)",": a mid-commit failure restores each already-written record to its prior envelope, but the ledger is not rewritten to erase the intermediate writes — the audit trail preserves the partial commit and the revert.",[1114,236568,236569,2085,236572,236575],{},[950,236570,236571],{},"Version numbers inside the body are pre-transaction",[941,236573,236574],{},"tx.collection().get(id)"," returns the most-recently-staged value, but its version reflects the state before the transaction — staged puts don't have a version yet.",[1114,236577,236578,83183,236581,236583],{},[950,236579,236580],{},"Cross-vault transactions need both vaults already open",[941,236582,62],{}," instance before the body runs.",[1114,236585,236586,236589,236590,953,236592,236594],{},[950,236587,236588],{},"Not cross-peer",": a transaction commits against the primary store only; the sync engine still pushes on its normal schedule. For atomic writes across sync peers, use ",[941,236591,236531],{},[941,236593,4687],{},") instead.",[1114,236596,236597],{},"Body errors propagate — wrap the call in try\u002Fcatch if you want a graceful UI response instead of an unhandled rejection.",[1022,236599,114684],{"id":114683},[1111,236601,236602,236609,236616,236623,236634],{},[1114,236603,236604,236608],{},[950,236605,236606],{},[1007,236607,697],{"href":140989}," — every committed op fires a ledger entry per op after commit",[1114,236610,236611,236615],{},[950,236612,236613],{},[1007,236614,864],{"href":174727}," — staged ops still flow through dirty tracking once committed",[1114,236617,236618,236622],{},[950,236619,236620],{},[1007,236621,653],{"href":174721}," — staged ops on CRDT collections merge through the CRDT strategy",[1114,236624,236625,236629,236630,236633],{},[950,236626,236627],{},[1007,236628,693],{"href":124814}," — amendment-mode transactions (",[941,236631,236632],{},"db.transaction({ amendment: true }, fn)",") are the privileged override path for guard-locked records",[1114,236635,236636,236638,236639,236641,236642,1520],{},[950,236637,22753],{}," — cascade-delete (ref mode ",[941,236640,150705],{},") is transaction-atomic: cascaded child deletes register onto the active transaction and roll back with the parent on a mid-batch failure. See ",[1007,236643,681],{"href":168276},[1022,236645,2621],{"id":2620},[1111,236647,236648,236652,236656,236661,236666,236673,236681],{},[1114,236649,236650],{},[1007,236651,3112],{"href":58262},[1114,236653,236654],{},[1007,236655,681],{"href":168276},[1114,236657,236658,236660],{},[1007,236659,693],{"href":124814}," — amendment transactions, record locks, invariants",[1114,236662,236663,236665],{},[1007,236664,697],{"href":140989}," — the audit ledger transactions write to",[1114,236667,236668,964,236670,236672],{},[1007,236669,864],{"href":174727},[941,236671,236531],{}," cross-peer batches",[1114,236674,236675,236678,236679],{},[1007,236676,860],{"href":236677},".\u002Fstorage-capability-matrix"," — which stores declare ",[941,236680,24565],{},[1114,236682,101581,236683,1520],{},[1007,236684,236686],{"href":92772,"rel":236685},[3371],[941,236687,236688],{},"20-with-transactions",[2645,236690,236691],{},"html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sHdIc, html code.shiki .sHdIc{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#EEFFFF;--shiki-default-font-style:italic;--shiki-dark:#BABED8;--shiki-dark-font-style:italic}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}",{"title":1037,"searchDepth":1038,"depth":1038,"links":236693},[236694,236695,236696,236697,236698,236701,236704,236711,236712,236713,236714],{"id":29856,"depth":1041,"text":29857},{"id":111659,"depth":1041,"text":111660},{"id":70092,"depth":1041,"text":70093},{"id":99863,"depth":1041,"text":99864},{"id":29892,"depth":1041,"text":29893,"children":236699},[236700],{"id":235698,"depth":1038,"text":235699},{"id":236100,"depth":1041,"text":235160,"children":236702},[236703],{"id":236223,"depth":1038,"text":236224},{"id":41259,"depth":1041,"text":101012,"children":236705},[236706,236707,236708,236709,236710],{"id":236303,"depth":1038,"text":236306},{"id":236340,"depth":1038,"text":236343},{"id":236371,"depth":1038,"text":236362},{"id":236387,"depth":1038,"text":236384},{"id":236418,"depth":1038,"text":236421},{"id":101424,"depth":1041,"text":101425},{"id":68209,"depth":1041,"text":68210},{"id":114683,"depth":1041,"text":114684},{"id":2620,"depth":1041,"text":2621},"Multi-record atomic writes: db.transaction() stages puts\u002Fdeletes across collections and commits all-or-nothing, with optional changeset invariants.",{},{"title":876,"description":236715},"tdSNHR92JlGVjZ6Dwxc1ou7nJ8-JaV3oo4qcR_G1H-w",{"id":236720,"title":14626,"access":12,"apiToc":929,"body":236721,"category":1042,"description":238321,"extension":1044,"meta":238322,"navigation":238323,"nest":1047,"path":881,"seo":238324,"sinceVersion":1042,"status":1049,"stem":882,"subtitle":1042,"tier":1050,"tocExternal":1042,"__hash__":238325},"docs\u002Fservices\u002Ftransferable-partitions.md",{"type":931,"value":236722,"toc":238306},[236723,236725,236728,236730,236760,236762,236773,236775,236798,236800,236830,237005,237007,237017,237540,237542,237548,237568,237570,237994,237996,238043,238047,238089,238092,238126,238130,238145,238178,238180,238261,238263,238303],[934,236724,14626],{"id":116737},[46871,236726],{":rows":236727},"[{\"label\":\"Subpath\",\"value\":\"`@noy-db\u002Fhub\u002Fcargo`, on every published version since the `\u002Fpod` + `\u002Fcargo` split (noy-db#833, #812) — `\u002Fcargo` carries `describeExtraction`, `extractPartition`, `adoptPartition`, `createOwnerOnAdoptedPartition` and `walkClosure`. The old `@noy-db\u002Fhub\u002Fbundle` was last present in 0.3.0 and is gone from both rails, so there is no longer a version where it is the right import. (`\u002Fbundle` also re-exported the unrelated `.noydb` container helpers; those live on `\u002Fpod` — see [bundle](.\u002Fbundle.md).)\"},{\"label\":\"Factory\",\"value\":\"`withCargo()` gates the source-side `extractPartition`; `describeExtraction`, `adoptPartition`, `createOwnerOnAdoptedPartition`, and `walkClosure` are ungated named imports. `withCargo()` itself is exported from `@noy-db\u002Fhub\u002Fcargo`.\"},{\"label\":\"Layer\",\"value\":\"`with-cargo`\"},{\"label\":\"Cluster\",\"value\":\"F — Snapshot & Portability\"},{\"label\":\"LOC cost\",\"value\":\"off-bundle when not opted in — the extraction crypto is dynamically imported only once `withCargo()` is passed to `createNoydb()`\"}]",[1022,236729,29857],{"id":29856},[937,236731,3289,236732,236734,236735,236737,236738,236741,236742,236745,236746,236749,236750,236753,236754,236756,236757,236759],{},[950,236733,2694],{}," is noy-db's end-to-end-encrypted, per-tenant data space (see\n",[1007,236736,65376],{"href":48},"). Transferable partitions let an owner\ncarve a subset of one vault's records out into a ",[950,236739,236740],{},"new, independently-owned vault"," — an\nowner-transfer ceremony, not a data export. Starting from a seed predicate (e.g. \"every client row\nwhere ",[941,236743,236744],{},"operatorUserId"," equals X\"), it walks the foreign-key closure so every reference resolves,\nre-encrypts the selected records under ",[950,236747,236748],{},"fresh per-collection data-encryption keys (DEKs)",", and\nseals those DEKs under a ",[950,236751,236752],{},"one-time transfer key",". The recipient adopts the resulting bundle into\ntheir own store and mints the first owner ",[950,236755,2705],{}," (the per-user file of wrapped encryption keys\n— see ",[1007,236758,107337],{"href":54},"); from that point they\nhold the only keys, and the source owner has no path back into the partition. The source vault is left untouched (aside from an\noptional audit entry) — extraction is a copy, not a move.",[1022,236761,111660],{"id":111659},[1111,236763,236764,236767,236770],{},[1114,236765,236766],{},"A firm spins a sub-portfolio off into a dedicated department's own vault — for example, handing\none client group's records (bills, receipts, worker rosters) to a new owner when that department\nsplits off.",[1114,236768,236769],{},"A client exercises data portability — taking the records they control to a new provider, with\naudit and schema fidelity intact, as a cryptographic right rather than a vendor favour.",[1114,236771,236772],{},"Splitting a shared vault along an ownership boundary without a manual dump-and-replay, which loses\nthe FK closure, schema, and audit chain.",[1022,236774,70093],{"id":70092},[1111,236776,236777,236790],{},[1114,236778,236779,236785,236786,236789],{},[950,236780,236781,236784],{},[941,236782,236783],{},"cargoStrategy: withCargo()"," on the source vault's client"," — it gates ",[941,236787,236788],{},"extractPartition"," only; the recipient side adopts with ungated named imports and needs no opt-in.",[1114,236791,236792,1169,236795,236797],{},[950,236793,236794],{},"The mandated Shamir recovery profile at partition creation",[941,236796,45751],{}," provider must be injected when the partition is created, or it cannot be extracted later.",[1022,236799,99864],{"id":99863},[937,236801,236802,968,236805,968,236808,2462,236810,236813,236814,236816,236817,10313,236819,83183,236821,236823,236824,236826,236827,236829],{},[941,236803,236804],{},"describeExtraction",[941,236806,236807],{},"adoptPartition",[941,236809,195366],{},[941,236811,236812],{},"walkClosure"," tree-shake\nnaturally via direct named imports — no strategy to wire in. The source-side ",[941,236815,236788],{}," is\nthe one gated call: pass ",[941,236818,236783],{},[941,236820,2134],{},[950,236822,22723],{}," vault's\nclient, or it throws ",[941,236825,21794],{},". The recipient side never needs ",[941,236828,21701],{}," — adoption\ncarries no source instance to gate against.",[2217,236831,236833],{"className":2219,"code":236832,"language":2221,"meta":1037,"style":1037},"import { createNoydb } from '@noy-db\u002Fhub'\nimport { toMemory } from '@noy-db\u002Fto-memory'\nimport { withCargo } from '@noy-db\u002Fhub\u002Fcargo'\n\n\u002F\u002F ── Source side (owner only) ──────────────────────────────────────────\nconst source = await createNoydb({\n  store: toMemory(),\n  user: 'alice',\n  secret: 'source-owner-secret',\n  cargoStrategy: withCargo(), \u002F\u002F required for extractPartition below\n})\nconst sourceVault = await source.openVault('acme')\n",[941,236834,236835,236853,236871,236890,236894,236899,236915,236927,236941,236956,236972,236978],{"__ignoreMap":1037},[2225,236836,236837,236839,236841,236843,236845,236847,236849,236851],{"class":2227,"line":2228},[2225,236838,2232],{"class":2231},[2225,236840,2236],{"class":2235},[2225,236842,2744],{"class":2239},[2225,236844,2243],{"class":2235},[2225,236846,2246],{"class":2231},[2225,236848,2249],{"class":2235},[2225,236850,1086],{"class":2252},[2225,236852,2256],{"class":2235},[2225,236854,236855,236857,236859,236861,236863,236865,236867,236869],{"class":2227,"line":1041},[2225,236856,2232],{"class":2231},[2225,236858,2236],{"class":2235},[2225,236860,68576],{"class":2239},[2225,236862,2243],{"class":2235},[2225,236864,2246],{"class":2231},[2225,236866,2249],{"class":2235},[2225,236868,9638],{"class":2252},[2225,236870,2256],{"class":2235},[2225,236872,236873,236875,236877,236880,236882,236884,236886,236888],{"class":2227,"line":1038},[2225,236874,2232],{"class":2231},[2225,236876,2236],{"class":2235},[2225,236878,236879],{"class":2239}," withCargo",[2225,236881,2243],{"class":2235},[2225,236883,2246],{"class":2231},[2225,236885,2249],{"class":2235},[2225,236887,1220],{"class":2252},[2225,236889,2256],{"class":2235},[2225,236891,236892],{"class":2227,"line":2288},[2225,236893,2285],{"emptyLinePlaceholder":2284},[2225,236895,236896],{"class":2227,"line":2304},[2225,236897,236898],{"class":2763},"\u002F\u002F ── Source side (owner only) ──────────────────────────────────────────\n",[2225,236900,236901,236903,236905,236907,236909,236911,236913],{"class":2227,"line":2356},[2225,236902,2775],{"class":2774},[2225,236904,112580],{"class":2239},[2225,236906,2781],{"class":2235},[2225,236908,2784],{"class":2231},[2225,236910,2744],{"class":2291},[2225,236912,2789],{"class":2239},[2225,236914,4200],{"class":2235},[2225,236916,236917,236919,236921,236923,236925],{"class":2227,"line":2830},[2225,236918,15608],{"class":2307},[2225,236920,2311],{"class":2235},[2225,236922,68576],{"class":2291},[2225,236924,4228],{"class":2239},[2225,236926,1707],{"class":2235},[2225,236928,236929,236931,236933,236935,236937,236939],{"class":2227,"line":2861},[2225,236930,4205],{"class":2307},[2225,236932,2311],{"class":2235},[2225,236934,2249],{"class":2235},[2225,236936,6408],{"class":2252},[2225,236938,2805],{"class":2235},[2225,236940,1707],{"class":2235},[2225,236942,236943,236945,236947,236949,236952,236954],{"class":2227,"line":4246},[2225,236944,10423],{"class":2307},[2225,236946,2311],{"class":2235},[2225,236948,2249],{"class":2235},[2225,236950,236951],{"class":2252},"source-owner-secret",[2225,236953,2805],{"class":2235},[2225,236955,1707],{"class":2235},[2225,236957,236958,236961,236963,236965,236967,236969],{"class":2227,"line":4253},[2225,236959,236960],{"class":2307},"  cargoStrategy",[2225,236962,2311],{"class":2235},[2225,236964,236879],{"class":2291},[2225,236966,4228],{"class":2239},[2225,236968,2298],{"class":2235},[2225,236970,236971],{"class":2763}," \u002F\u002F required for extractPartition below\n",[2225,236973,236974,236976],{"class":2227,"line":4259},[2225,236975,2359],{"class":2235},[2225,236977,2362],{"class":2239},[2225,236979,236980,236982,236985,236987,236989,236991,236993,236995,236997,236999,237001,237003],{"class":2227,"line":6548},[2225,236981,2775],{"class":2774},[2225,236983,236984],{"class":2239}," sourceVault ",[2225,236986,2781],{"class":2235},[2225,236988,2784],{"class":2231},[2225,236990,95077],{"class":2239},[2225,236992,1520],{"class":2235},[2225,236994,2847],{"class":2291},[2225,236996,2789],{"class":2239},[2225,236998,2805],{"class":2235},[2225,237000,6450],{"class":2252},[2225,237002,2805],{"class":2235},[2225,237004,2362],{"class":2239},[1022,237006,29893],{"id":29892},[937,237008,237009,237010,237012,237013,237016],{},"Continuing from the ",[941,237011,22723],{}," client and ",[941,237014,237015],{},"sourceVault"," opened above, the transfer runs in four\nsteps: preview, extract (source side), adopt, then mint the recipient's owner (recipient side).",[2217,237018,237020],{"className":2219,"code":237019,"language":2221,"meta":1037,"style":1037},"import {\n  describeExtraction,\n  extractPartition,\n  adoptPartition,\n  createOwnerOnAdoptedPartition,\n} from '@noy-db\u002Fhub\u002Fcargo'\n\nconst preview = await describeExtraction(sourceVault, {\n  seeds: { clients: (c) => c.operatorUserId === 'ops-team-42' },\n})\n\u002F\u002F → {\n\u002F\u002F     totalRecords: 128,\n\u002F\u002F     totalBytes: 41230,\n\u002F\u002F     byCollection: [\n\u002F\u002F       { name: 'bills', recordCount: 54, bytes: 18120, oldestTs: '2026-01-04T…', newestTs: '2026-06-30T…' },\n\u002F\u002F       { name: 'clients', recordCount: 12, bytes: 3110 },\n\u002F\u002F       { name: 'workers', recordCount: 62, bytes: 20000 },\n\u002F\u002F     ],\n\u002F\u002F     graph: { depth: 2, cyclesDetected: false },\n\u002F\u002F     inaccessible: [],\n\u002F\u002F   }\n\u002F\u002F Computed from raw encrypted envelopes — decrypts nothing.\n\nconst { bundleBytes, transferKey, sealId } = await extractPartition(sourceVault, {\n  seeds: { clients: (c) => c.operatorUserId === 'ops-team-42' },\n  carrySchemas: true, \u002F\u002F optional — carry persisted JSON Schemas (default false)\n  carryLedger: true,  \u002F\u002F optional — carry the re-chained audit ledger (default false)\n})\n\u002F\u002F → { bundleBytes: Uint8Array(…), transferKey: Uint8Array(32), sealId: 'nQ3f…' }\n\u002F\u002F Deliver `transferKey` out-of-band; it never travels inside `bundleBytes`.\n\n\u002F\u002F ── Recipient side (ungated) ────────────────────────────────────────────\nconst destinationStore = toMemory()\nawait adoptPartition(bundleBytes, { transferKey, destinationStore, vaultName: 'dept-spinoff' })\n\u002F\u002F → { vaultName: 'dept-spinoff', needsOwner: true, sealId: 'nQ3f…' }\n\nawait createOwnerOnAdoptedPartition(destinationStore, 'dept-spinoff', {\n  userId: 'bob',\n  secret: 'recipient-owner-secret',\n  transferKey,\n})\n\u002F\u002F → { vaultName: 'dept-spinoff', userId: 'bob' }\n\n\u002F\u002F Now open it normally — a keyring exists.\nconst recipient = await createNoydb({ store: destinationStore, user: 'bob', secret: 'recipient-owner-secret' })\nconst deptVault = await recipient.openVault('dept-spinoff')\n",[941,237021,237022,237028,237035,237042,237049,237056,237068,237072,237092,237131,237137,237141,237146,237151,237156,237161,237166,237171,237176,237181,237186,237190,237195,237199,237233,237269,237283,237297,237303,237308,237313,237317,237322,237335,237374,237379,237383,237405,237419,237434,237441,237447,237452,237456,237461,237512],{"__ignoreMap":1037},[2225,237023,237024,237026],{"class":2227,"line":2228},[2225,237025,2232],{"class":2231},[2225,237027,2301],{"class":2235},[2225,237029,237030,237033],{"class":2227,"line":1041},[2225,237031,237032],{"class":2239},"  describeExtraction",[2225,237034,1707],{"class":2235},[2225,237036,237037,237040],{"class":2227,"line":1038},[2225,237038,237039],{"class":2239},"  extractPartition",[2225,237041,1707],{"class":2235},[2225,237043,237044,237047],{"class":2227,"line":2288},[2225,237045,237046],{"class":2239},"  adoptPartition",[2225,237048,1707],{"class":2235},[2225,237050,237051,237054],{"class":2227,"line":2304},[2225,237052,237053],{"class":2239},"  createOwnerOnAdoptedPartition",[2225,237055,1707],{"class":2235},[2225,237057,237058,237060,237062,237064,237066],{"class":2227,"line":2356},[2225,237059,2359],{"class":2235},[2225,237061,2246],{"class":2231},[2225,237063,2249],{"class":2235},[2225,237065,1220],{"class":2252},[2225,237067,2256],{"class":2235},[2225,237069,237070],{"class":2227,"line":2830},[2225,237071,2285],{"emptyLinePlaceholder":2284},[2225,237073,237074,237076,237078,237080,237082,237085,237088,237090],{"class":2227,"line":2861},[2225,237075,2775],{"class":2774},[2225,237077,108543],{"class":2239},[2225,237079,2781],{"class":2235},[2225,237081,2784],{"class":2231},[2225,237083,237084],{"class":2291}," describeExtraction",[2225,237086,237087],{"class":2239},"(sourceVault",[2225,237089,2298],{"class":2235},[2225,237091,2301],{"class":2235},[2225,237093,237094,237097,237099,237101,237103,237105,237107,237109,237111,237113,237115,237117,237120,237122,237124,237127,237129],{"class":2227,"line":4246},[2225,237095,237096],{"class":2307},"  seeds",[2225,237098,2311],{"class":2235},[2225,237100,2236],{"class":2235},[2225,237102,62689],{"class":2291},[2225,237104,2311],{"class":2235},[2225,237106,953],{"class":2235},[2225,237108,8200],{"class":7052},[2225,237110,1381],{"class":2235},[2225,237112,8116],{"class":2774},[2225,237114,8169],{"class":2239},[2225,237116,1520],{"class":2235},[2225,237118,237119],{"class":2239},"operatorUserId ",[2225,237121,33083],{"class":2235},[2225,237123,2249],{"class":2235},[2225,237125,237126],{"class":2252},"ops-team-42",[2225,237128,2805],{"class":2235},[2225,237130,2353],{"class":2235},[2225,237132,237133,237135],{"class":2227,"line":4253},[2225,237134,2359],{"class":2235},[2225,237136,2362],{"class":2239},[2225,237138,237139],{"class":2227,"line":4259},[2225,237140,164681],{"class":2763},[2225,237142,237143],{"class":2227,"line":6548},[2225,237144,237145],{"class":2763},"\u002F\u002F     totalRecords: 128,\n",[2225,237147,237148],{"class":2227,"line":6576},[2225,237149,237150],{"class":2763},"\u002F\u002F     totalBytes: 41230,\n",[2225,237152,237153],{"class":2227,"line":6582},[2225,237154,237155],{"class":2763},"\u002F\u002F     byCollection: [\n",[2225,237157,237158],{"class":2227,"line":6587},[2225,237159,237160],{"class":2763},"\u002F\u002F       { name: 'bills', recordCount: 54, bytes: 18120, oldestTs: '2026-01-04T…', newestTs: '2026-06-30T…' },\n",[2225,237162,237163],{"class":2227,"line":7174},[2225,237164,237165],{"class":2763},"\u002F\u002F       { name: 'clients', recordCount: 12, bytes: 3110 },\n",[2225,237167,237168],{"class":2227,"line":7180},[2225,237169,237170],{"class":2763},"\u002F\u002F       { name: 'workers', recordCount: 62, bytes: 20000 },\n",[2225,237172,237173],{"class":2227,"line":7220},[2225,237174,237175],{"class":2763},"\u002F\u002F     ],\n",[2225,237177,237178],{"class":2227,"line":7225},[2225,237179,237180],{"class":2763},"\u002F\u002F     graph: { depth: 2, cyclesDetected: false },\n",[2225,237182,237183],{"class":2227,"line":7231},[2225,237184,237185],{"class":2763},"\u002F\u002F     inaccessible: [],\n",[2225,237187,237188],{"class":2227,"line":7267},[2225,237189,164715],{"class":2763},[2225,237191,237192],{"class":2227,"line":7272},[2225,237193,237194],{"class":2763},"\u002F\u002F Computed from raw encrypted envelopes — decrypts nothing.\n",[2225,237196,237197],{"class":2227,"line":7278},[2225,237198,2285],{"emptyLinePlaceholder":2284},[2225,237200,237201,237203,237205,237208,237210,237213,237215,237218,237220,237222,237224,237227,237229,237231],{"class":2227,"line":7303},[2225,237202,2775],{"class":2774},[2225,237204,2236],{"class":2235},[2225,237206,237207],{"class":2239}," bundleBytes",[2225,237209,2298],{"class":2235},[2225,237211,237212],{"class":2239}," transferKey",[2225,237214,2298],{"class":2235},[2225,237216,237217],{"class":2239}," sealId ",[2225,237219,2359],{"class":2235},[2225,237221,6346],{"class":2235},[2225,237223,2784],{"class":2231},[2225,237225,237226],{"class":2291}," extractPartition",[2225,237228,237087],{"class":2239},[2225,237230,2298],{"class":2235},[2225,237232,2301],{"class":2235},[2225,237234,237235,237237,237239,237241,237243,237245,237247,237249,237251,237253,237255,237257,237259,237261,237263,237265,237267],{"class":2227,"line":7308},[2225,237236,237096],{"class":2307},[2225,237238,2311],{"class":2235},[2225,237240,2236],{"class":2235},[2225,237242,62689],{"class":2291},[2225,237244,2311],{"class":2235},[2225,237246,953],{"class":2235},[2225,237248,8200],{"class":7052},[2225,237250,1381],{"class":2235},[2225,237252,8116],{"class":2774},[2225,237254,8169],{"class":2239},[2225,237256,1520],{"class":2235},[2225,237258,237119],{"class":2239},[2225,237260,33083],{"class":2235},[2225,237262,2249],{"class":2235},[2225,237264,237126],{"class":2252},[2225,237266,2805],{"class":2235},[2225,237268,2353],{"class":2235},[2225,237270,237271,237274,237276,237278,237280],{"class":2227,"line":7314},[2225,237272,237273],{"class":2307},"  carrySchemas",[2225,237275,2311],{"class":2235},[2225,237277,2322],{"class":2321},[2225,237279,2298],{"class":2235},[2225,237281,237282],{"class":2763}," \u002F\u002F optional — carry persisted JSON Schemas (default false)\n",[2225,237284,237285,237288,237290,237292,237294],{"class":2227,"line":7348},[2225,237286,237287],{"class":2307},"  carryLedger",[2225,237289,2311],{"class":2235},[2225,237291,2322],{"class":2321},[2225,237293,2298],{"class":2235},[2225,237295,237296],{"class":2763},"  \u002F\u002F optional — carry the re-chained audit ledger (default false)\n",[2225,237298,237299,237301],{"class":2227,"line":8613},[2225,237300,2359],{"class":2235},[2225,237302,2362],{"class":2239},[2225,237304,237305],{"class":2227,"line":8633},[2225,237306,237307],{"class":2763},"\u002F\u002F → { bundleBytes: Uint8Array(…), transferKey: Uint8Array(32), sealId: 'nQ3f…' }\n",[2225,237309,237310],{"class":2227,"line":8662},[2225,237311,237312],{"class":2763},"\u002F\u002F Deliver `transferKey` out-of-band; it never travels inside `bundleBytes`.\n",[2225,237314,237315],{"class":2227,"line":8747},[2225,237316,2285],{"emptyLinePlaceholder":2284},[2225,237318,237319],{"class":2227,"line":8752},[2225,237320,237321],{"class":2763},"\u002F\u002F ── Recipient side (ungated) ────────────────────────────────────────────\n",[2225,237323,237324,237326,237329,237331,237333],{"class":2227,"line":8768},[2225,237325,2775],{"class":2774},[2225,237327,237328],{"class":2239}," destinationStore ",[2225,237330,2781],{"class":2235},[2225,237332,68576],{"class":2291},[2225,237334,8146],{"class":2239},[2225,237336,237337,237339,237342,237345,237347,237349,237351,237353,237356,237358,237361,237363,237365,237368,237370,237372],{"class":2227,"line":8785},[2225,237338,6500],{"class":2231},[2225,237340,237341],{"class":2291}," adoptPartition",[2225,237343,237344],{"class":2239},"(bundleBytes",[2225,237346,2298],{"class":2235},[2225,237348,2236],{"class":2235},[2225,237350,237212],{"class":2239},[2225,237352,2298],{"class":2235},[2225,237354,237355],{"class":2239}," destinationStore",[2225,237357,2298],{"class":2235},[2225,237359,237360],{"class":2307}," vaultName",[2225,237362,2311],{"class":2235},[2225,237364,2249],{"class":2235},[2225,237366,237367],{"class":2252},"dept-spinoff",[2225,237369,2805],{"class":2235},[2225,237371,2243],{"class":2235},[2225,237373,2362],{"class":2239},[2225,237375,237376],{"class":2227,"line":8815},[2225,237377,237378],{"class":2763},"\u002F\u002F → { vaultName: 'dept-spinoff', needsOwner: true, sealId: 'nQ3f…' }\n",[2225,237380,237381],{"class":2227,"line":8854},[2225,237382,2285],{"emptyLinePlaceholder":2284},[2225,237384,237385,237387,237390,237393,237395,237397,237399,237401,237403],{"class":2227,"line":8874},[2225,237386,6500],{"class":2231},[2225,237388,237389],{"class":2291}," createOwnerOnAdoptedPartition",[2225,237391,237392],{"class":2239},"(destinationStore",[2225,237394,2298],{"class":2235},[2225,237396,2249],{"class":2235},[2225,237398,237367],{"class":2252},[2225,237400,2805],{"class":2235},[2225,237402,2298],{"class":2235},[2225,237404,2301],{"class":2235},[2225,237406,237407,237409,237411,237413,237415,237417],{"class":2227,"line":8911},[2225,237408,10510],{"class":2307},[2225,237410,2311],{"class":2235},[2225,237412,2249],{"class":2235},[2225,237414,91596],{"class":2252},[2225,237416,2805],{"class":2235},[2225,237418,1707],{"class":2235},[2225,237420,237421,237423,237425,237427,237430,237432],{"class":2227,"line":8916},[2225,237422,10423],{"class":2307},[2225,237424,2311],{"class":2235},[2225,237426,2249],{"class":2235},[2225,237428,237429],{"class":2252},"recipient-owner-secret",[2225,237431,2805],{"class":2235},[2225,237433,1707],{"class":2235},[2225,237435,237436,237439],{"class":2227,"line":8924},[2225,237437,237438],{"class":2239},"  transferKey",[2225,237440,1707],{"class":2235},[2225,237442,237443,237445],{"class":2227,"line":8929},[2225,237444,2359],{"class":2235},[2225,237446,2362],{"class":2239},[2225,237448,237449],{"class":2227,"line":8949},[2225,237450,237451],{"class":2763},"\u002F\u002F → { vaultName: 'dept-spinoff', userId: 'bob' }\n",[2225,237453,237454],{"class":2227,"line":8955},[2225,237455,2285],{"emptyLinePlaceholder":2284},[2225,237457,237458],{"class":2227,"line":8983},[2225,237459,237460],{"class":2763},"\u002F\u002F Now open it normally — a keyring exists.\n",[2225,237462,237463,237465,237468,237470,237472,237474,237476,237478,237480,237482,237484,237486,237488,237490,237492,237494,237496,237498,237500,237502,237504,237506,237508,237510],{"class":2227,"line":9016},[2225,237464,2775],{"class":2774},[2225,237466,237467],{"class":2239}," recipient ",[2225,237469,2781],{"class":2235},[2225,237471,2784],{"class":2231},[2225,237473,2744],{"class":2291},[2225,237475,2789],{"class":2239},[2225,237477,2792],{"class":2235},[2225,237479,7564],{"class":2307},[2225,237481,2311],{"class":2235},[2225,237483,237355],{"class":2239},[2225,237485,2298],{"class":2235},[2225,237487,2795],{"class":2307},[2225,237489,2311],{"class":2235},[2225,237491,2249],{"class":2235},[2225,237493,91596],{"class":2252},[2225,237495,2805],{"class":2235},[2225,237497,2298],{"class":2235},[2225,237499,2810],{"class":2307},[2225,237501,2311],{"class":2235},[2225,237503,2249],{"class":2235},[2225,237505,237429],{"class":2252},[2225,237507,2805],{"class":2235},[2225,237509,2243],{"class":2235},[2225,237511,2362],{"class":2239},[2225,237513,237514,237516,237519,237521,237523,237526,237528,237530,237532,237534,237536,237538],{"class":2227,"line":9063},[2225,237515,2775],{"class":2774},[2225,237517,237518],{"class":2239}," deptVault ",[2225,237520,2781],{"class":2235},[2225,237522,2784],{"class":2231},[2225,237524,237525],{"class":2239}," recipient",[2225,237527,1520],{"class":2235},[2225,237529,2847],{"class":2291},[2225,237531,2789],{"class":2239},[2225,237533,2805],{"class":2235},[2225,237535,237367],{"class":2252},[2225,237537,2805],{"class":2235},[2225,237539,2362],{"class":2239},[1022,237541,6304],{"id":6303},[2217,237543,237546],{"className":237544,"code":237545,"language":5672},[5670],"source vault ──extractPartition──▶ extracted bundle ──adoptPartition──▶ ADOPTED, UNOWNED ──createOwnerOnAdoptedPartition──▶ OWNED\n (owner only)  re-keyed, sealed,    _meta\u002Fadoption,    recipient keyring,\n               bundleKind header     keyring empty       seal destroyed\n",[941,237547,237545],{"__ignoreMap":1037},[937,237549,237550,237551,237559,237560,237563,237564,237567],{},"The FK graph is ",[950,237552,237553,237554],{},"auto-derived from ",[1007,237555,237556,237558],{"href":682},[941,237557,62282],{}," declarations"," (tracked\ninternally as the ",[941,237561,237562],{},"RefRegistry",") — you supply only ",[941,237565,237566],{},"seeds",", not a hand-written edge list. The walk is\ntwo-phase: inbound expansion grows the scope from the seeds (a client's bills travel with it), then\noutbound completion pulls referenced parents so no FK dangles (without re-expanding those parents\ninto unrelated siblings).",[1022,237569,101012],{"id":41259},[1111,237571,237572,237594,237744,237752,237924,237935,237947,237959,237968],{},[1114,237573,237574,1746,237577,237580,237581,237583,237584,237587,237588,1625,237590,214459,237592,13528],{},[941,237575,237576],{},"describeExtraction(vault, { seeds, maxDepth? })",[941,237578,237579],{},"ExtractionPreview"," — read-only dry-run\n(counts, bytes, ",[941,237582,1703],{}," span, graph depth, ",[941,237585,237586],{},"inaccessible[]","); writes nothing. Takes the same\n",[941,237589,237566],{},[941,237591,140905],{},[941,237593,236812],{},[1114,237595,237596,1746,237599,964,237602,237604,237605,237608,237609,1520,237611],{},[941,237597,237598],{},"extractPartition(vault, options)",[941,237600,237601],{},"{ bundleBytes, transferKey, sealId }",[950,237603,142244],{},";\nnon-destructive on the source. Routes through the vault's ",[941,237606,237607],{},"cargoStrategy",", so an un-opted-in\ncaller throws ",[941,237610,21794],{},[1338,237612,237613,237625],{},[1341,237614,237615],{},[1344,237616,237617,237619,237621,237623],{},[1347,237618,101060],{},[1347,237620,20194],{},[1347,237622,20197],{},[1347,237624,7834],{},[1357,237626,237627,237643,237662,237679,237704,237724],{},[1344,237628,237629,237633,237638,237640],{},[1362,237630,237631],{},[941,237632,237566],{},[1362,237634,237635],{},[941,237636,237637],{},"Record\u003Cstring, (record) => boolean | Promise\u003Cboolean>>",[1362,237639,45375],{},[1362,237641,237642],{},"Per-collection predicate selecting the root records the closure grows from.",[1344,237644,237645,237649,237653,237657],{},[1362,237646,237647],{},[941,237648,140905],{},[1362,237650,237651],{},[941,237652,20636],{},[1362,237654,237655],{},[941,237656,80546],{},[1362,237658,237659,237660,1520],{},"Max fixed-point iterations of the inbound\u002Foutbound walk before throwing ",[941,237661,136289],{},[1344,237663,237664,237668,237672,237676],{},[1362,237665,237666],{},[941,237667,110539],{},[1362,237669,237670],{},[941,237671,116072],{},[1362,237673,237674],{},[941,237675,116077],{},[1362,237677,237678],{},"Bundle-byte compression.",[1344,237680,237681,237686,237690,237694],{},[1362,237682,237683],{},[941,237684,237685],{},"carrySchemas",[1362,237687,237688],{},[941,237689,16861],{},[1362,237691,237692],{},[941,237693,2560],{},[1362,237695,237696,237697,237699,237700,1520],{},"Re-key and carry ",[941,237698,166841],{}," for the closure — see ",[1007,237701,237703],{"href":237702},"#carry-opt-ins","Carry opt-ins",[1344,237705,237706,237711,237715,237719],{},[1362,237707,237708],{},[941,237709,237710],{},"carryLedger",[1362,237712,237713],{},[941,237714,16861],{},[1362,237716,237717],{},[941,237718,2560],{},[1362,237720,237721,237722,1520],{},"Filter, re-chain, and carry the audit ledger for the closure — see ",[1007,237723,237703],{"href":237702},[1344,237725,237726,237731,237736,237738],{},[1362,237727,237728],{},[941,237729,237730],{},"fieldProjection",[1362,237732,237733],{},[941,237734,237735],{},"Record\u003Cstring, readonly string[]>",[1362,237737,8292],{},[1362,237739,237740,237741,237743],{},"Per-collection allow-list of fields to keep; non-listed fields are dropped before re-encryption (",[941,237742,6983],{}," is always kept). A projected collection's schema is not carried.",[1114,237745,237746,68475,237749,1520],{},[941,237747,237748],{},"adoptPartition(bundleBytes, { transferKey, destinationStore, vaultName })",[941,237750,237751],{},"{ vaultName, needsOwner: true, sealId }",[1114,237753,237754,1746,237757,237760,237761,237763,237764,237820],{},[941,237755,237756],{},"createOwnerOnAdoptedPartition(store, vaultName, options)",[941,237758,237759],{},"{ vaultName, userId }"," — mints the\nfirst owner and destroys the transfer seal. ",[941,237762,16362],{}," is one of two shapes:",[1338,237765,237766,237776],{},[1341,237767,237768],{},[1344,237769,237770,237772,237774],{},[1347,237771,224825],{},[1347,237773,20194],{},[1347,237775,7834],{},[1357,237777,237778,237791,237804],{},[1344,237779,237780,237784,237788],{},[1362,237781,237782],{},[941,237783,10749],{},[1362,237785,237786],{},[941,237787,7259],{},[1362,237789,237790],{},"The recipient's user id.",[1344,237792,237793,237797,237801],{},[1362,237794,237795],{},[941,237796,10695],{},[1362,237798,237799],{},[941,237800,7259],{},[1362,237802,237803],{},"The recipient owner's secret.",[1344,237805,237806,237811,237815],{},[1362,237807,237808],{},[941,237809,237810],{},"transferKey",[1362,237812,237813],{},[941,237814,45820],{},[1362,237816,237817,237818,1520],{},"The 32-byte key delivered out-of-band from ",[941,237819,236788],{},[1338,237821,237822,237832],{},[1341,237823,237824],{},[1344,237825,237826,237828,237830],{},[1347,237827,224830],{},[1347,237829,20194],{},[1347,237831,7834],{},[1357,237833,237834,237846,237859,237875,237895,237910],{},[1344,237835,237836,237840,237844],{},[1362,237837,237838],{},[941,237839,10749],{},[1362,237841,237842],{},[941,237843,7259],{},[1362,237845,237790],{},[1344,237847,237848,237852,237856],{},[1362,237849,237850],{},[941,237851,20272],{},[1362,237853,237854],{},[941,237855,20293],{},[1362,237857,237858],{},"Selects managed mode instead of a caller-supplied secret.",[1344,237860,237861,237865,237869],{},[1362,237862,237863],{},[941,237864,13515],{},[1362,237866,237867],{},[941,237868,14350],{},[1362,237870,237871,237872,237874],{},"Seals the minted secret (e.g. an ",[941,237873,1139],{}," OS keychain) so the partition auto-unlocks on the recipient's device.",[1344,237876,237877,237881,237886],{},[1362,237878,237879],{},[941,237880,321],{},[1362,237882,237883],{},[941,237884,237885],{},"ReadonlyArray\u003CRecoveryEnrollmentInput>",[1362,237887,237888,237889,237891,237892,957],{},"Recovery profiles to enroll; must include at least one ",[941,237890,325],{}," profile (see ",[1007,237893,6825],{"href":237894},"#edge-cases-limits",[1344,237896,237897,237901,237905],{},[1362,237898,237899],{},[941,237900,20330],{},[1362,237902,237903],{},[941,237904,19615],{},[1362,237906,960,237907,237909],{},[941,237908,45751],{}," provider backing the mandated Shamir profile.",[1344,237911,237912,237916,237920],{},[1362,237913,237914],{},[941,237915,237810],{},[1362,237917,237918],{},[941,237919,45820],{},[1362,237921,237817,237922,1520],{},[941,237923,236788],{},[1114,237925,237926,1746,237929,237932,237933,957],{},[941,237927,237928],{},"walkClosure(vault, { seeds, maxDepth? })",[941,237930,237931],{},"{ closure, graph }"," — the underlying FK-closure\nprimitive (also used by ",[941,237934,236804],{},[1114,237936,237937,1746,237940,237943,237944,237946],{},[941,237938,237939],{},"unsealDeks(seal, transferKey)",[941,237941,237942],{},"Map\u003Cstring, CryptoKey>"," — the reverse of the internal DEK-seal\nstep; used internally by ",[941,237945,236807],{}," and exported for callers that need to validate a\ntransfer key without adopting.",[1114,237948,237949,1746,237952,237955,237956,237958],{},[941,237950,237951],{},"decryptExtractedPartition(bundleBytes, transferKey)",[941,237953,237954],{},"Record\u003Cstring, DecryptedRecord[]>"," —\nread-side counterpart to ",[941,237957,236788],{},": decrypts every record of an extracted-partition\nbundle to plaintext (grouped by collection) without adopting it into a vault. Throws on a bundle\nthat isn't an extracted partition, or a wrong transfer key.",[1114,237960,237961,237962,32975,237965,237967],{},"Header additions (read pre-decryption by cloud listers): ",[941,237963,237964],{},"bundleKind: 'extracted-partition'",[941,237966,195197],{}," indicator.",[1114,237969,237970,1324,237973,968,237975,2462,237978,237981,237982,237984,237985,237987,237988,237990,237991,1520],{},[950,237971,237972],{},"Errors:",[941,237974,136289],{},[941,237976,237977],{},"TransferSealError",[941,237979,237980],{},"AdoptionStateError"," are\nre-exported alongside the functions above, from ",[941,237983,1220],{}," — the same subpath as\n",[941,237986,21701],{}," and as ",[941,237989,21794],{},", which has always lived there:\n",[941,237992,237993],{},"import { CargoNotEnabledError } from '@noy-db\u002Fhub\u002Fcargo'",[1022,237995,101425],{"id":101424},[937,237997,237998,238000,238001,238003,238004,238006,238007,40603,238009,238011,238012,238014,238015,1625,238017,5502,238019,238021,238022,238024,238025,6030,238027,238030,238031,1625,238033,238035,238036,238038,238039,238042],{},[941,237999,236788],{}," is the only gated call: without ",[941,238002,236783],{}," on the source\nvault's client, it throws ",[941,238005,21794],{}," pointing you at ",[941,238008,21701],{},[941,238010,1220],{},". The recipient-side functions need no opt-in. An ordinary backup (",[941,238013,35759],{},")\nhas no ",[941,238016,196179],{},[941,238018,195197],{},[941,238020,236807],{}," rejects it with a ",[941,238023,12881],{}," pointing\nyou to ",[941,238026,70321],{},[941,238028,238029],{},"vault.load"," instead. ",[941,238032,237685],{},[941,238034,237710],{}," default to ",[941,238037,2560],{},", producing\na clean partition with no ",[941,238040,238041],{},"_internal"," payload.",[1022,238044,238046],{"id":238045},"security-model","Security model",[1111,238048,238049,238055,238063,238077,238083],{},[1114,238050,238051,238054],{},[950,238052,238053],{},"Owner-only extraction."," Producing a re-keyed standalone vault is an ownership operation; a\nnon-owner caller (including a custodian) is rejected.",[1114,238056,238057,1324,238060,238062],{},[950,238058,238059],{},"The transfer key never travels in the bundle.",[941,238061,236788],{}," mints a random 32-byte key\nand seals the DEK set under it; the bundle bytes alone are inert. Deliver the key out-of-band —\nin person, through a secrets manager, or any channel outside the bundle transport.",[1114,238064,238065,1324,238068,238070,238071,238073,238074,238076],{},[950,238066,238067],{},"DEKs stay sealed at rest after adoption.",[941,238069,236807],{}," validates the key by unsealing in\nmemory only; the sealed payload persists in ",[941,238072,195186],{}," until ",[941,238075,195366],{},"\nwraps the DEKs under the recipient's KEK and destroys the seal.",[1114,238078,238079,238082],{},[950,238080,238081],{},"One-time per destination."," Re-adopting the same bundle into the same store is rejected; into a\ndifferent store it's allowed (the bundle is immutable — the consumed marker is local).",[1114,238084,238085,238088],{},[950,238086,238087],{},"Cryptographic ownership."," The recipient's control is that they hold the only DEKs, not a policy\nflag. The source owner cannot read the adopted partition.",[1022,238090,237703],{"id":238091},"carry-opt-ins",[1111,238093,238094,238107],{},[1114,238095,238096,238100,238101,238103,238104,238106],{},[950,238097,238098],{},[941,238099,237685],{}," — re-keys ",[941,238102,166841],{}," for the closure under the destination DEKs,\nso the recipient validates against the same contract and ",[941,238105,36137],{}," works on the partition.",[1114,238108,238109,238113,238114,238116,238117,238119,238120,238122,238123,238125],{},[950,238110,238111],{},[941,238112,237710],{}," — filters the source audit chain to the closure, re-chains it (fresh\nindices\u002F",[941,238115,158910],{},"), recomputes each ",[941,238118,22982],{}," against the re-keyed ciphertext for the latest\nput per record, and re-encrypts under a fresh ",[941,238121,6724],{}," DEK (sealed with the rest). The\nrecipient's ",[941,238124,22875],{}," passes over the re-keyed data. (Open the vault with a\nhistory strategy to exercise it.)",[1022,238127,238129],{"id":238128},"audit-trail","Audit trail",[937,238131,238132,238133,238136,238137,1625,238139,238141,238142,238144],{},"All three lifecycle events ride a generic ",[941,238134,238135],{},"'lifecycle'"," ledger op (empty ",[941,238138,6471],{},[941,238140,6983],{},", detail\nin ",[941,238143,43668],{},"; skipped by the data cross-check, present in the tamper-evident chain):",[1111,238146,238147,238159],{},[1114,238148,238149,1324,238152,238154,238155,238158],{},[950,238150,238151],{},"Source:",[941,238153,236788],{}," appends ",[941,238156,238157],{},"partition-handed-over:\u003CsealId>"," (the owner's record that\ndata left). No-op without a source history strategy.",[1114,238160,238161,238164,238165,238167,238168,238170,238171,6030,238174,238177],{},[950,238162,238163],{},"Destination"," (when ",[941,238166,237710],{}," was used): ",[941,238169,195366],{}," appends\n",[941,238172,238173],{},"creation-of-new-owner:\u003CuserId>",[941,238175,238176],{},"transfer-seal-consumed:\u003CsealId>"," to the carried chain.",[1022,238179,6825],{"id":6824},[1111,238181,238182,238215,238229,238235,238250],{},[1114,238183,238184,238187,238188,77939,238191,238193,238194,238196,238197,238200,238201,238203,238204,238206,238207,238210,238211,6030,238213,1520],{},[950,238185,238186],{},"Managed mode at owner-create"," is supported: pass\n",[941,238189,238190],{},"{ secretMode: 'managed', sealingKey, recovery, shamirRecovery }",[941,238192,195366],{}," and the partition auto-unlocks on the recipient's device (e.g. via\nan ",[941,238195,1139],{}," OS keychain) with no secret. Managed mode ",[950,238198,238199],{},"mandates a strong (Shamir) recovery\nprofile"," at creation — the ",[941,238202,20330],{}," provider (",[941,238205,45751],{},") must be injected. A\nhalf-completed managed adoption (keyring + sealed secret written, recovery enrollment\ninterrupted) is ",[950,238208,238209],{},"idempotent under retry"," — re-run with the same ",[941,238212,13515],{},[941,238214,321],{},[1114,238216,238217,238222,238223,238225,238226,238228],{},[950,238218,238219,238221],{},[941,238220,236812],{}," performance"," is O(frontier · inbound-collections · records) per depth — it\nre-scans each inbound collection for every frontier element, which is fine at consumer-firm scale\nand would need an index or pagination to stay fast over very large vaults; ",[941,238224,140905],{}," (default 16)\nthrows ",[941,238227,136289],{}," on runaway graphs.",[1114,238230,238231,238234],{},[950,238232,238233],{},"No source-side delete."," The ceremony is non-destructive of source records; deleting the\nwithdrawn records from the source afterward is a separate step the caller performs explicitly —\nextraction never does it for you.",[1114,238236,238237,964,238244,238246,238247,238249],{},[950,238238,238239,10746,238241,238243],{},[941,238240,237710],{},[941,238242,6724],{}," entries only",[941,238245,6727],{}," (historical versions) and\n",[941,238248,6715],{}," snapshots do not travel with the partition.",[1114,238251,238252,238257,238258,238260],{},[950,238253,238254,238255,123092],{},"Records need string ",[941,238256,6983],{}," — a non-string id fails loud (",[941,238259,136289],{},") rather\nthan silently dropping (which would dangle an FK).",[1022,238262,2621],{"id":2620},[1111,238264,238265,238287,238295],{},[1114,238266,238267,238268,2878,238270,238272,238273,1617,238275,1884,238277,953,238279,1884,238281,238284,238285],{},"Pairs with: ",[1007,238269,629],{"href":195435},[941,238271,1402],{}," container format), ",[1007,238274,697],{"href":140989},[941,238276,237710],{},[1007,238278,780],{"href":207528},[941,238280,237685],{},[1007,238282,661],{"href":238283},".\u002Fcustody","\n(ownership and the custodian role), ",[1007,238286,868],{"href":198512},[1114,238288,238289,238291,238292,238294],{},[1007,238290,6898],{"href":54}," — the key hierarchy that\n",[941,238293,195366],{}," bootstraps on the recipient side",[1114,238296,101581,238297,1520],{},[1007,238298,238300],{"href":93672,"rel":238299},[3371],[941,238301,238302],{},"88-transferable-partition",[2645,238304,238305],{},"html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sHdIc, html code.shiki .sHdIc{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#EEFFFF;--shiki-default-font-style:italic;--shiki-dark:#BABED8;--shiki-dark-font-style:italic}html pre.shiki code .sfNiH, html code.shiki .sfNiH{--shiki-light:#FF5370;--shiki-default:#FF9CAC;--shiki-dark:#FF9CAC}",{"title":1037,"searchDepth":1038,"depth":1038,"links":238307},[238308,238309,238310,238311,238312,238313,238314,238315,238316,238317,238318,238319,238320],{"id":29856,"depth":1041,"text":29857},{"id":111659,"depth":1041,"text":111660},{"id":70092,"depth":1041,"text":70093},{"id":99863,"depth":1041,"text":99864},{"id":29892,"depth":1041,"text":29893},{"id":6303,"depth":1041,"text":6304},{"id":41259,"depth":1041,"text":101012},{"id":101424,"depth":1041,"text":101425},{"id":238045,"depth":1041,"text":238046},{"id":238091,"depth":1041,"text":237703},{"id":238128,"depth":1041,"text":238129},{"id":6824,"depth":1041,"text":6825},{"id":2620,"depth":1041,"text":2621},"Extract a referentially-complete, re-keyed slice of a vault into a standalone bundle and hand it to a new owner — a cryptographic ownership transfer.",{},{"title":880},{"title":14626,"description":238321},"wOKQ3gFSzWCUCfqSa3bighIONMsAnTAG7dQsb3pkyPk",{"id":238327,"title":884,"access":12,"apiToc":929,"body":238328,"category":1042,"description":240454,"extension":1044,"meta":240455,"navigation":1042,"nest":1047,"path":885,"seo":240456,"sinceVersion":1042,"status":1049,"stem":886,"subtitle":1042,"tier":1050,"tocExternal":1042,"__hash__":240457},"docs\u002Fservices\u002Fuser-envelope.md",{"type":931,"value":238329,"toc":240429},[238330,238332,238335,238337,238357,238390,238392,238406,238408,238417,238614,238616,238620,238628,238680,238684,238695,238741,238745,238751,238885,238887,238891,239024,239040,239043,239138,239151,239190,239195,239240,239245,239303,239317,239320,239404,239421,239425,239458,239464,239624,239630,239723,239740,239742,239756,239760,239767,239849,239865,239888,239892,239895,239950,239956,239976,239981,240003,240008,240015,240017,240082,240084,240124,240126,240195,240199,240204,240347,240369,240381,240387,240389,240426],[934,238331,884],{"id":884},[46871,238333],{":rows":238334},"[{\"label\":\"Subpath\",\"value\":\"`@noy-db\u002Fhub` (root export)\"},{\"label\":\"Type\",\"value\":\"Core feature — exposed as `vault.user.*`, always available.\"}]",[1022,238336,29857],{"id":29856},[937,238338,238339,238340,238344,238345,238347,238348,238350,238351,238354,238355,2311],{},"Every noy-db vault can hold more than one user. Each user unlocks the vault with their own\n",[1007,238341,238342],{"href":54},[950,238343,2705],{}," — the per-principal set of wrapped keys that\ngrants them access — and this service gives every keyring a small JSON object to store its own\nprofile and preferences (display name, theme, locale, whatever the app needs), living alongside\nit in the vault. That object is called a ",[950,238346,97731],{},": it's stored at ",[941,238349,97735],{},",\nencrypted under a DEK (data-encryption key) shared by every keyring in the vault, and only ever\nwritten by the user it belongs to — hub commits only to ",[941,238352,238353],{},"userId === keyringId"," and where the\nenvelope lives; the shape of the data inside is entirely up to the app. Three method families on\n",[941,238356,21609],{},[1111,238358,238359,238368,238381],{},[1114,238360,238361,964,238364,238367],{},[950,238362,238363],{},"Write-self",[941,238365,238366],{},"me() \u002F updateMe(patch) \u002F setMe(payload)",". Always target the writer's own keyringId; there is no API method to write someone else's envelope.",[1114,238369,238370,964,238373,238376,238377,30200,238379,957],{},[950,238371,238372],{},"Read-anyone",[941,238374,238375],{},"get(keyringId) \u002F list()",". Gated by ",[941,238378,98879],{},[941,238380,220372],{},[1114,238382,238383,964,238386,238389],{},[950,238384,238385],{},"Reactive",[941,238387,238388],{},"subscribe(id, cb) \u002F live(id)",". In-process event emission on local writes.",[1022,238391,111660],{"id":111659},[1111,238393,238394,238397,238400,238403],{},[1114,238395,238396],{},"A multi-user vault where each user wants their own UI preferences (theme, locale) that follow them across devices via the team\u002Fsync engine.",[1114,238398,238399],{},"A team-aware app rendering \"who's online\" or \"who edited this record\" with display names + avatars sourced consistently from one place.",[1114,238401,238402],{},"An accounting-style app where each user has a profile (signature, default currency map, per-client overrides) that travels with the vault.",[1114,238404,238405],{},"Admin onboarding flows that pre-fill a teammate's display name and locale at invite time.",[1022,238407,58394],{"id":58393},[937,238409,238410,238411,238413,238414,238416],{},"None — ",[941,238412,21055],{}," is always available on every ",[941,238415,94],{},". The first write lazily provisions any state the underlying primitive needs:",[2217,238418,238420],{"className":2219,"code":238419,"language":2221,"meta":1037,"style":1037},"import { createNoydb } from '@noy-db\u002Fhub'\n\n\u002F\u002F No store → the kernel's built-in in-memory default (0.3).\nconst db = await createNoydb({ user: 'alice', secret: 'pass-2026' })\nconst vault = await db.openVault('demo')\n\nawait vault.user.updateMe({\n  profile: { displayName: 'Alice' },\n  preferences: { theme: 'dark' },\n})\n\u002F\u002F → {\n\u002F\u002F     keyringId: 'alice',\n\u002F\u002F     data: { profile: { displayName: 'Alice' }, preferences: { theme: 'dark' } },\n\u002F\u002F     _v: 1,\n\u002F\u002F     _ts: '2026-05-05T10:00:00.000Z',\n\u002F\u002F   }\n",[941,238421,238422,238440,238444,238449,238492,238518,238522,238540,238560,238580,238586,238590,238595,238600,238605,238610],{"__ignoreMap":1037},[2225,238423,238424,238426,238428,238430,238432,238434,238436,238438],{"class":2227,"line":2228},[2225,238425,2232],{"class":2231},[2225,238427,2236],{"class":2235},[2225,238429,2744],{"class":2239},[2225,238431,2243],{"class":2235},[2225,238433,2246],{"class":2231},[2225,238435,2249],{"class":2235},[2225,238437,1086],{"class":2252},[2225,238439,2256],{"class":2235},[2225,238441,238442],{"class":2227,"line":1041},[2225,238443,2285],{"emptyLinePlaceholder":2284},[2225,238445,238446],{"class":2227,"line":1038},[2225,238447,238448],{"class":2763},"\u002F\u002F No store → the kernel's built-in in-memory default (0.3).\n",[2225,238450,238451,238453,238455,238457,238459,238461,238463,238465,238467,238469,238471,238473,238475,238477,238479,238481,238483,238486,238488,238490],{"class":2227,"line":2288},[2225,238452,2775],{"class":2774},[2225,238454,2778],{"class":2239},[2225,238456,2781],{"class":2235},[2225,238458,2784],{"class":2231},[2225,238460,2744],{"class":2291},[2225,238462,2789],{"class":2239},[2225,238464,2792],{"class":2235},[2225,238466,2795],{"class":2307},[2225,238468,2311],{"class":2235},[2225,238470,2249],{"class":2235},[2225,238472,6408],{"class":2252},[2225,238474,2805],{"class":2235},[2225,238476,2298],{"class":2235},[2225,238478,2810],{"class":2307},[2225,238480,2311],{"class":2235},[2225,238482,2249],{"class":2235},[2225,238484,238485],{"class":2252},"pass-2026",[2225,238487,2805],{"class":2235},[2225,238489,2243],{"class":2235},[2225,238491,2362],{"class":2239},[2225,238493,238494,238496,238498,238500,238502,238504,238506,238508,238510,238512,238514,238516],{"class":2227,"line":2304},[2225,238495,2775],{"class":2774},[2225,238497,2835],{"class":2239},[2225,238499,2781],{"class":2235},[2225,238501,2784],{"class":2231},[2225,238503,2842],{"class":2239},[2225,238505,1520],{"class":2235},[2225,238507,2847],{"class":2291},[2225,238509,2789],{"class":2239},[2225,238511,2805],{"class":2235},[2225,238513,97914],{"class":2252},[2225,238515,2805],{"class":2235},[2225,238517,2362],{"class":2239},[2225,238519,238520],{"class":2227,"line":2356},[2225,238521,2285],{"emptyLinePlaceholder":2284},[2225,238523,238524,238526,238528,238530,238532,238534,238536,238538],{"class":2227,"line":2830},[2225,238525,6500],{"class":2231},[2225,238527,6466],{"class":2239},[2225,238529,1520],{"class":2235},[2225,238531,16225],{"class":2239},[2225,238533,1520],{"class":2235},[2225,238535,98160],{"class":2291},[2225,238537,2789],{"class":2239},[2225,238539,4200],{"class":2235},[2225,238541,238542,238544,238546,238548,238550,238552,238554,238556,238558],{"class":2227,"line":2861},[2225,238543,97941],{"class":2307},[2225,238545,2311],{"class":2235},[2225,238547,2236],{"class":2235},[2225,238549,75153],{"class":2307},[2225,238551,2311],{"class":2235},[2225,238553,2249],{"class":2235},[2225,238555,10533],{"class":2252},[2225,238557,2805],{"class":2235},[2225,238559,2353],{"class":2235},[2225,238561,238562,238564,238566,238568,238570,238572,238574,238576,238578],{"class":2227,"line":4246},[2225,238563,97999],{"class":2307},[2225,238565,2311],{"class":2235},[2225,238567,2236],{"class":2235},[2225,238569,98216],{"class":2307},[2225,238571,2311],{"class":2235},[2225,238573,2249],{"class":2235},[2225,238575,98024],{"class":2252},[2225,238577,2805],{"class":2235},[2225,238579,2353],{"class":2235},[2225,238581,238582,238584],{"class":2227,"line":4253},[2225,238583,2359],{"class":2235},[2225,238585,2362],{"class":2239},[2225,238587,238588],{"class":2227,"line":4259},[2225,238589,164681],{"class":2763},[2225,238591,238592],{"class":2227,"line":6548},[2225,238593,238594],{"class":2763},"\u002F\u002F     keyringId: 'alice',\n",[2225,238596,238597],{"class":2227,"line":6576},[2225,238598,238599],{"class":2763},"\u002F\u002F     data: { profile: { displayName: 'Alice' }, preferences: { theme: 'dark' } },\n",[2225,238601,238602],{"class":2227,"line":6582},[2225,238603,238604],{"class":2763},"\u002F\u002F     _v: 1,\n",[2225,238606,238607],{"class":2227,"line":6587},[2225,238608,238609],{"class":2763},"\u002F\u002F     _ts: '2026-05-05T10:00:00.000Z',\n",[2225,238611,238612],{"class":2227,"line":7174},[2225,238613,164715],{"class":2763},[1022,238615,29893],{"id":29892},[1591,238617,238619],{"id":238618},"read-your-own-envelope","Read your own envelope",[937,238621,238622,238624,238625,238627],{},[941,238623,98397],{}," returns the writer's own envelope, or ",[941,238626,6651],{}," if they've never written one — it's never\ngated, since reading your own data needs no permission check:",[2217,238629,238631],{"className":2219,"code":238630,"language":2221,"meta":1037,"style":1037},"const mine = await vault.user.me()\n\u002F\u002F → {\n\u002F\u002F     keyringId: 'alice',\n\u002F\u002F     data: { profile: { displayName: 'Alice' }, preferences: { theme: 'dark' } },\n\u002F\u002F     _v: 1,\n\u002F\u002F     _ts: '2026-05-05T10:00:00.000Z',\n\u002F\u002F   }\n",[941,238632,238633,238656,238660,238664,238668,238672,238676],{"__ignoreMap":1037},[2225,238634,238635,238637,238640,238642,238644,238646,238648,238650,238652,238654],{"class":2227,"line":2228},[2225,238636,2775],{"class":2774},[2225,238638,238639],{"class":2239}," mine ",[2225,238641,2781],{"class":2235},[2225,238643,2784],{"class":2231},[2225,238645,6466],{"class":2239},[2225,238647,1520],{"class":2235},[2225,238649,16225],{"class":2239},[2225,238651,1520],{"class":2235},[2225,238653,4212],{"class":2291},[2225,238655,8146],{"class":2239},[2225,238657,238658],{"class":2227,"line":1041},[2225,238659,164681],{"class":2763},[2225,238661,238662],{"class":2227,"line":1038},[2225,238663,238594],{"class":2763},[2225,238665,238666],{"class":2227,"line":2288},[2225,238667,238599],{"class":2763},[2225,238669,238670],{"class":2227,"line":2304},[2225,238671,238604],{"class":2763},[2225,238673,238674],{"class":2227,"line":2356},[2225,238675,238609],{"class":2763},[2225,238677,238678],{"class":2227,"line":2830},[2225,238679,164715],{"class":2763},[1591,238681,238683],{"id":238682},"read-a-teammates-envelope","Read a teammate's envelope",[937,238685,238686,238689,238690,30200,238692,238694],{},[941,238687,238688],{},"get(keyringId)"," reads any principal's envelope by id. Reading someone else's envelope is\ngated by ",[941,238691,98879],{},[941,238693,220372],{},") — reading your own is not:",[2217,238696,238698],{"className":2219,"code":238697,"language":2221,"meta":1037,"style":1037},"const bobs = await vault.user.get('bob')\n\u002F\u002F → { keyringId: 'bob', data: { profile: { displayName: 'Bob' } }, _v: 1, _ts: '2026-05-05T10:05:00.000Z' }\n\u002F\u002F throws PolicyDeniedError if the caller's session tier is below view-team-profiles' minTier\n",[941,238699,238700,238731,238736],{"__ignoreMap":1037},[2225,238701,238702,238704,238707,238709,238711,238713,238715,238717,238719,238721,238723,238725,238727,238729],{"class":2227,"line":2228},[2225,238703,2775],{"class":2774},[2225,238705,238706],{"class":2239}," bobs ",[2225,238708,2781],{"class":2235},[2225,238710,2784],{"class":2231},[2225,238712,6466],{"class":2239},[2225,238714,1520],{"class":2235},[2225,238716,16225],{"class":2239},[2225,238718,1520],{"class":2235},[2225,238720,3158],{"class":2291},[2225,238722,2789],{"class":2239},[2225,238724,2805],{"class":2235},[2225,238726,91596],{"class":2252},[2225,238728,2805],{"class":2235},[2225,238730,2362],{"class":2239},[2225,238732,238733],{"class":2227,"line":1041},[2225,238734,238735],{"class":2763},"\u002F\u002F → { keyringId: 'bob', data: { profile: { displayName: 'Bob' } }, _v: 1, _ts: '2026-05-05T10:05:00.000Z' }\n",[2225,238737,238738],{"class":2227,"line":1038},[2225,238739,238740],{"class":2763},"\u002F\u002F throws PolicyDeniedError if the caller's session tier is below view-team-profiles' minTier\n",[1591,238742,238744],{"id":238743},"react-to-local-writes","React to local writes",[937,238746,238747,238750],{},[941,238748,238749],{},"subscribe(keyringId, cb)"," fires synchronously whenever that principal writes their own\nenvelope from this process:",[2217,238752,238754],{"className":2219,"code":238753,"language":2221,"meta":1037,"style":1037},"const unsubscribe = vault.user.subscribe('alice', (env) => {\n  console.log(env?.data.preferences?.theme)\n})\n\nawait vault.user.updateMe({ preferences: { theme: 'light' } })\n\u002F\u002F → the subscriber above fires synchronously: logs 'light'\n\nunsubscribe()\n",[941,238755,238756,238794,238820,238826,238830,238870,238875,238879],{"__ignoreMap":1037},[2225,238757,238758,238760,238762,238764,238766,238768,238770,238772,238774,238776,238778,238780,238782,238784,238786,238788,238790,238792],{"class":2227,"line":2228},[2225,238759,2775],{"class":2774},[2225,238761,91395],{"class":2239},[2225,238763,2781],{"class":2235},[2225,238765,6466],{"class":2239},[2225,238767,1520],{"class":2235},[2225,238769,16225],{"class":2239},[2225,238771,1520],{"class":2235},[2225,238773,25042],{"class":2291},[2225,238775,2789],{"class":2239},[2225,238777,2805],{"class":2235},[2225,238779,6408],{"class":2252},[2225,238781,2805],{"class":2235},[2225,238783,2298],{"class":2235},[2225,238785,953],{"class":2235},[2225,238787,183],{"class":7052},[2225,238789,1381],{"class":2235},[2225,238791,8116],{"class":2774},[2225,238793,2301],{"class":2235},[2225,238795,238796,238798,238800,238802,238804,238806,238808,238810,238812,238814,238816,238818],{"class":2227,"line":1041},[2225,238797,27095],{"class":2239},[2225,238799,1520],{"class":2235},[2225,238801,27100],{"class":2291},[2225,238803,2789],{"class":2307},[2225,238805,183],{"class":2239},[2225,238807,8457],{"class":2235},[2225,238809,8673],{"class":2239},[2225,238811,1520],{"class":2235},[2225,238813,98383],{"class":2239},[2225,238815,8457],{"class":2235},[2225,238817,98497],{"class":2239},[2225,238819,2362],{"class":2307},[2225,238821,238822,238824],{"class":2227,"line":1038},[2225,238823,2359],{"class":2235},[2225,238825,2362],{"class":2239},[2225,238827,238828],{"class":2227,"line":2288},[2225,238829,2285],{"emptyLinePlaceholder":2284},[2225,238831,238832,238834,238836,238838,238840,238842,238844,238846,238848,238850,238852,238854,238856,238858,238860,238862,238864,238866,238868],{"class":2227,"line":2304},[2225,238833,6500],{"class":2231},[2225,238835,6466],{"class":2239},[2225,238837,1520],{"class":2235},[2225,238839,16225],{"class":2239},[2225,238841,1520],{"class":2235},[2225,238843,98160],{"class":2291},[2225,238845,2789],{"class":2239},[2225,238847,2792],{"class":2235},[2225,238849,99096],{"class":2307},[2225,238851,2311],{"class":2235},[2225,238853,2236],{"class":2235},[2225,238855,98216],{"class":2307},[2225,238857,2311],{"class":2235},[2225,238859,2249],{"class":2235},[2225,238861,98015],{"class":2252},[2225,238863,2805],{"class":2235},[2225,238865,2243],{"class":2235},[2225,238867,2243],{"class":2235},[2225,238869,2362],{"class":2239},[2225,238871,238872],{"class":2227,"line":2356},[2225,238873,238874],{"class":2763},"\u002F\u002F → the subscriber above fires synchronously: logs 'light'\n",[2225,238876,238877],{"class":2227,"line":2830},[2225,238878,2285],{"emptyLinePlaceholder":2284},[2225,238880,238881,238883],{"class":2227,"line":2861},[2225,238882,91505],{"class":2291},[2225,238884,8146],{"class":2239},[1022,238886,101012],{"id":41259},[1591,238888,238890],{"id":238889},"write-self-own-envelope-only","Write-self (own envelope only)",[2217,238892,238894],{"className":2219,"code":238893,"language":2221,"meta":1037,"style":1037},"vault.user.me\u003CT>(): Promise\u003CUserEnvelope\u003CT> | null>\nvault.user.updateMe\u003CT>(patch: DeepPartial\u003CT>, presented?): Promise\u003CUserEnvelope\u003CT>>\nvault.user.setMe\u003CT>(payload: T, presented?): Promise\u003CUserEnvelope\u003CT>>\n",[941,238895,238896,238933,238981],{"__ignoreMap":1037},[2225,238897,238898,238900,238902,238904,238906,238908,238910,238912,238914,238916,238918,238920,238923,238925,238927,238929,238931],{"class":2227,"line":2228},[2225,238899,2694],{"class":2239},[2225,238901,1520],{"class":2235},[2225,238903,16225],{"class":2239},[2225,238905,1520],{"class":2235},[2225,238907,4212],{"class":2291},[2225,238909,6474],{"class":2235},[2225,238911,11968],{"class":6342},[2225,238913,6480],{"class":2235},[2225,238915,12809],{"class":2239},[2225,238917,11740],{"class":6342},[2225,238919,6474],{"class":2235},[2225,238921,238922],{"class":2239},"UserEnvelope",[2225,238924,6474],{"class":2235},[2225,238926,11968],{"class":2239},[2225,238928,6480],{"class":2235},[2225,238930,7087],{"class":2235},[2225,238932,24020],{"class":2235},[2225,238934,238935,238937,238939,238941,238943,238945,238947,238949,238951,238954,238956,238958,238960,238963,238965,238967,238969,238971,238973,238975,238977,238979],{"class":2227,"line":1041},[2225,238936,2694],{"class":2239},[2225,238938,1520],{"class":2235},[2225,238940,16225],{"class":2239},[2225,238942,1520],{"class":2235},[2225,238944,98160],{"class":2291},[2225,238946,6474],{"class":2235},[2225,238948,11968],{"class":6342},[2225,238950,6480],{"class":2235},[2225,238952,238953],{"class":2239},"(patch: DeepPartial",[2225,238955,6474],{"class":2235},[2225,238957,11968],{"class":2239},[2225,238959,17840],{"class":2235},[2225,238961,238962],{"class":2239}," presented",[2225,238964,35940],{"class":2235},[2225,238966,1381],{"class":2239},[2225,238968,2311],{"class":2235},[2225,238970,7079],{"class":6342},[2225,238972,6474],{"class":2235},[2225,238974,238922],{"class":2239},[2225,238976,6474],{"class":2235},[2225,238978,11968],{"class":2239},[2225,238980,17688],{"class":2235},[2225,238982,238983,238985,238987,238989,238991,238993,238995,238997,238999,239002,239004,239006,239008,239010,239012,239014,239016,239018,239020,239022],{"class":2227,"line":1038},[2225,238984,2694],{"class":2239},[2225,238986,1520],{"class":2235},[2225,238988,16225],{"class":2239},[2225,238990,1520],{"class":2235},[2225,238992,98553],{"class":2291},[2225,238994,6474],{"class":2235},[2225,238996,11968],{"class":6342},[2225,238998,6480],{"class":2235},[2225,239000,239001],{"class":2239},"(payload: T",[2225,239003,2298],{"class":2235},[2225,239005,238962],{"class":2239},[2225,239007,35940],{"class":2235},[2225,239009,1381],{"class":2239},[2225,239011,2311],{"class":2235},[2225,239013,7079],{"class":6342},[2225,239015,6474],{"class":2235},[2225,239017,238922],{"class":2239},[2225,239019,6474],{"class":2235},[2225,239021,11968],{"class":2239},[2225,239023,17688],{"class":2235},[937,239025,239026,239027,239029,239030,239032,239033,239036,239037,239039],{},"Each call goes through the ",[941,239028,98678],{}," policy gate (default ",[941,239031,198448],{},"). Pass ",[941,239034,239035],{},"presented: { factors, sharedDevice }"," to satisfy tightened policies (e.g. ",[941,239038,12834],{}," requires a TOTP or email-OTP proof for profile edits).",[1591,239041,238372],{"id":239042},"read-anyone",[2217,239044,239046],{"className":2219,"code":239045,"language":2221,"meta":1037,"style":1037},"vault.user.get\u003CT>(keyringId: string, presented?): Promise\u003CUserEnvelope\u003CT> | null>\nvault.user.list\u003CT>(presented?): Promise\u003CUserEnvelope\u003CT>[]>\n",[941,239047,239048,239095],{"__ignoreMap":1037},[2225,239049,239050,239052,239054,239056,239058,239060,239062,239064,239066,239069,239071,239073,239075,239077,239079,239081,239083,239085,239087,239089,239091,239093],{"class":2227,"line":2228},[2225,239051,2694],{"class":2239},[2225,239053,1520],{"class":2235},[2225,239055,16225],{"class":2239},[2225,239057,1520],{"class":2235},[2225,239059,3158],{"class":2291},[2225,239061,6474],{"class":2235},[2225,239063,11968],{"class":6342},[2225,239065,6480],{"class":2235},[2225,239067,239068],{"class":2239},"(keyringId: string",[2225,239070,2298],{"class":2235},[2225,239072,238962],{"class":2239},[2225,239074,35940],{"class":2235},[2225,239076,1381],{"class":2239},[2225,239078,2311],{"class":2235},[2225,239080,7079],{"class":6342},[2225,239082,6474],{"class":2235},[2225,239084,238922],{"class":2239},[2225,239086,6474],{"class":2235},[2225,239088,11968],{"class":2239},[2225,239090,6480],{"class":2235},[2225,239092,7087],{"class":2235},[2225,239094,24020],{"class":2235},[2225,239096,239097,239099,239101,239103,239105,239107,239109,239111,239113,239116,239118,239120,239122,239124,239126,239128,239130,239132,239134,239136],{"class":2227,"line":1041},[2225,239098,2694],{"class":2239},[2225,239100,1520],{"class":2235},[2225,239102,16225],{"class":2239},[2225,239104,1520],{"class":2235},[2225,239106,3167],{"class":2291},[2225,239108,6474],{"class":2235},[2225,239110,11968],{"class":6342},[2225,239112,6480],{"class":2235},[2225,239114,239115],{"class":2239},"(presented",[2225,239117,35940],{"class":2235},[2225,239119,1381],{"class":2239},[2225,239121,2311],{"class":2235},[2225,239123,7079],{"class":6342},[2225,239125,6474],{"class":2235},[2225,239127,238922],{"class":2239},[2225,239129,6474],{"class":2235},[2225,239131,11968],{"class":2239},[2225,239133,6480],{"class":2235},[2225,239135,7262],{"class":2239},[2225,239137,7093],{"class":2235},[937,239139,239140,239142,239143,1324,239145,239150],{},[941,239141,238688],{}," is gated by ",[941,239144,98879],{},[1092,239146,180122,239147],{},[941,239148,239149],{},"keyringId !== self"," — reading your own envelope is never gated.",[2217,239152,239154],{"className":2219,"code":239153,"language":2221,"meta":1037,"style":1037},"const bob = await vault.user.get('bob')\n\u002F\u002F → { keyringId: 'bob', data: { profile: { displayName: 'Bob' } }, _v: 1, _ts: '2026-05-05T10:05:00.000Z' }\n",[941,239155,239156,239186],{"__ignoreMap":1037},[2225,239157,239158,239160,239162,239164,239166,239168,239170,239172,239174,239176,239178,239180,239182,239184],{"class":2227,"line":2228},[2225,239159,2775],{"class":2774},[2225,239161,98897],{"class":2239},[2225,239163,2781],{"class":2235},[2225,239165,2784],{"class":2231},[2225,239167,6466],{"class":2239},[2225,239169,1520],{"class":2235},[2225,239171,16225],{"class":2239},[2225,239173,1520],{"class":2235},[2225,239175,3158],{"class":2291},[2225,239177,2789],{"class":2239},[2225,239179,2805],{"class":2235},[2225,239181,91596],{"class":2252},[2225,239183,2805],{"class":2235},[2225,239185,2362],{"class":2239},[2225,239187,239188],{"class":2227,"line":1041},[2225,239189,238735],{"class":2763},[937,239191,239192,239194],{},[941,239193,24407],{}," reads every persisted envelope in the vault (store-defined order):",[2217,239196,239198],{"className":2219,"code":239197,"language":2221,"meta":1037,"style":1037},"const all = await vault.user.list()\n\u002F\u002F → [\n\u002F\u002F     { keyringId: 'alice', data: { profile: { displayName: 'Alice' } }, _v: 1, _ts: '2026-05-05T10:00:00.000Z' },\n\u002F\u002F     { keyringId: 'bob',   data: { profile: { displayName: 'Bob' } },   _v: 1, _ts: '2026-05-05T10:05:00.000Z' },\n\u002F\u002F   ]\n",[941,239199,239200,239222,239226,239231,239236],{"__ignoreMap":1037},[2225,239201,239202,239204,239206,239208,239210,239212,239214,239216,239218,239220],{"class":2227,"line":2228},[2225,239203,2775],{"class":2774},[2225,239205,104158],{"class":2239},[2225,239207,2781],{"class":2235},[2225,239209,2784],{"class":2231},[2225,239211,6466],{"class":2239},[2225,239213,1520],{"class":2235},[2225,239215,16225],{"class":2239},[2225,239217,1520],{"class":2235},[2225,239219,3167],{"class":2291},[2225,239221,8146],{"class":2239},[2225,239223,239224],{"class":2227,"line":1041},[2225,239225,100852],{"class":2763},[2225,239227,239228],{"class":2227,"line":1038},[2225,239229,239230],{"class":2763},"\u002F\u002F     { keyringId: 'alice', data: { profile: { displayName: 'Alice' } }, _v: 1, _ts: '2026-05-05T10:00:00.000Z' },\n",[2225,239232,239233],{"class":2227,"line":2288},[2225,239234,239235],{"class":2763},"\u002F\u002F     { keyringId: 'bob',   data: { profile: { displayName: 'Bob' } },   _v: 1, _ts: '2026-05-05T10:05:00.000Z' },\n",[2225,239237,239238],{"class":2227,"line":2304},[2225,239239,100867],{"class":2763},[937,239241,239242,239244],{},[941,239243,24407],{}," has a deliberate asymmetry on denial:",[1338,239246,239247,239256],{},[1341,239248,239249],{},[1344,239250,239251,239254],{},[1347,239252,239253],{},"Denial reason",[1347,239255,124024],{},[1357,239257,239258,239270,239281,239292],{},[1344,239259,239260,239265],{},[1362,239261,239262],{},[941,239263,239264],{},"disabled",[1362,239266,239267,239268,1293],{},"Silent self-fallback — returns ",[941,239269,99038],{},[1344,239271,239272,239276],{},[1362,239273,239274],{},[941,239275,234974],{},[1362,239277,16810,239278,239280],{},[941,239279,13025],{}," (caller is expected to elevate)",[1344,239282,239283,239288],{},[1362,239284,239285],{},[941,239286,239287],{},"missing-factor",[1362,239289,16810,239290],{},[941,239291,13025],{},[1344,239293,239294,239299],{},[1362,239295,239296],{},[941,239297,239298],{},"stale-proof",[1362,239300,16810,239301],{},[941,239302,13025],{},[937,239304,960,239305,239307,239308,239310,239311,239314,239315,1520],{},[941,239306,99032],{}," path is the privacy-strict opt-out (\"nobody sees teammate profiles in this app\"); the others are authentication concerns. An ",[941,239309,234974],{}," denial here asks you to authenticate further up the ",[1007,239312,239313],{"href":845},"session ladder"," — it is not the per-record elevation documented on ",[1007,239316,872],{"href":873},[1591,239318,238385],{"id":239319},"reactive",[2217,239321,239323],{"className":2219,"code":239322,"language":2221,"meta":1037,"style":1037},"vault.user.subscribe\u003CT>(keyringId: string, cb: (env: UserEnvelope\u003CT> | null) => void): Unsubscribe\nvault.user.live\u003CT>(keyringId: string): LiveUserEnvelope\u003CT>\n",[941,239324,239325,239377],{"__ignoreMap":1037},[2225,239326,239327,239329,239331,239333,239335,239337,239339,239341,239343,239345,239347,239350,239352,239354,239356,239359,239361,239363,239365,239367,239369,239371,239373,239375],{"class":2227,"line":2228},[2225,239328,2694],{"class":2239},[2225,239330,1520],{"class":2235},[2225,239332,16225],{"class":2239},[2225,239334,1520],{"class":2235},[2225,239336,25042],{"class":2291},[2225,239338,6474],{"class":2235},[2225,239340,11968],{"class":6342},[2225,239342,6480],{"class":2235},[2225,239344,239068],{"class":2239},[2225,239346,2298],{"class":2235},[2225,239348,239349],{"class":2239}," cb: ",[2225,239351,2789],{"class":2235},[2225,239353,183],{"class":7052},[2225,239355,2311],{"class":2235},[2225,239357,239358],{"class":6342}," UserEnvelope",[2225,239360,6474],{"class":2235},[2225,239362,11968],{"class":6342},[2225,239364,6480],{"class":2235},[2225,239366,7087],{"class":2235},[2225,239368,7090],{"class":6342},[2225,239370,1381],{"class":2235},[2225,239372,8116],{"class":2774},[2225,239374,12717],{"class":2235},[2225,239376,12720],{"class":2239},[2225,239378,239379,239381,239383,239385,239387,239389,239391,239393,239395,239398,239400,239402],{"class":2227,"line":1041},[2225,239380,2694],{"class":2239},[2225,239382,1520],{"class":2235},[2225,239384,16225],{"class":2239},[2225,239386,1520],{"class":2235},[2225,239388,727],{"class":2291},[2225,239390,6474],{"class":2235},[2225,239392,11968],{"class":6342},[2225,239394,6480],{"class":2235},[2225,239396,239397],{"class":2239},"(keyringId: string): LiveUserEnvelope",[2225,239399,6474],{"class":2235},[2225,239401,11968],{"class":2239},[2225,239403,7093],{"class":2235},[937,239405,239406,239407,1625,239409,239411,239412,239414,239415,1625,239418,239420],{},"Subscribers fire synchronously after every successful local ",[941,239408,98160],{},[941,239410,98553],{}," on the matching keyringId. Pass ",[941,239413,126292],{}," to fire on every change. Cross-instance changes (a teammate edits their profile on their device, the sync engine pulls onto this device) flow through the team\u002Fsync engine and will fire subscribers when sync replays the write through the API. In v1, raw store-level changes do not fire subscribers — wire your sync layer to call back through ",[941,239416,239417],{},"vault.user.setMe",[941,239419,98160],{}," if you need that behavior.",[1591,239422,239424],{"id":239423},"lifecycle-hooks","Lifecycle hooks",[2217,239426,239428],{"className":2219,"code":239427,"language":2221,"meta":1037,"style":1037},"db.grant(vault, { ..., initialProfile?: T })\n",[941,239429,239430],{"__ignoreMap":1037},[2225,239431,239432,239434,239436,239438,239440,239442,239444,239447,239450,239452,239454,239456],{"class":2227,"line":2228},[2225,239433,6590],{"class":2239},[2225,239435,1520],{"class":2235},[2225,239437,10493],{"class":2291},[2225,239439,30266],{"class":2239},[2225,239441,2298],{"class":2235},[2225,239443,2236],{"class":2235},[2225,239445,239446],{"class":2235}," ...,",[2225,239448,239449],{"class":2239}," initialProfile?",[2225,239451,2311],{"class":2235},[2225,239453,24640],{"class":2239},[2225,239455,2359],{"class":2235},[2225,239457,2362],{"class":2239},[937,239459,239460,239461,239463],{},"Admin pre-fill at grant time. Hub seeds the new principal's first envelope under the caller's ",[941,239462,141843],{}," DEK so the new user can decrypt it on first open. Once the user activates and writes their own envelope, the own-only write rule prevents further admin edits — bootstrap-only by construction.",[2217,239465,239467],{"className":2219,"code":239466,"language":2221,"meta":1037,"style":1037},"await db.grant('acme', {\n  userId: 'bob',\n  displayName: 'Bob',\n  role: 'operator',\n  secret: 'glasses cabinet bicycle umbrella thunder velvet',\n  permissions: { invoices: 'rw' },\n  initialProfile: { profile: { displayName: 'Bob' }, preferences: { theme: 'light' } },\n})\n\u002F\u002F → bob's `_users\u002Fbob` envelope exists, pre-filled, before bob ever opens the vault\n",[941,239468,239469,239491,239505,239519,239533,239547,239567,239613,239619],{"__ignoreMap":1037},[2225,239470,239471,239473,239475,239477,239479,239481,239483,239485,239487,239489],{"class":2227,"line":2228},[2225,239472,6500],{"class":2231},[2225,239474,2842],{"class":2239},[2225,239476,1520],{"class":2235},[2225,239478,10493],{"class":2291},[2225,239480,2789],{"class":2239},[2225,239482,2805],{"class":2235},[2225,239484,6450],{"class":2252},[2225,239486,2805],{"class":2235},[2225,239488,2298],{"class":2235},[2225,239490,2301],{"class":2235},[2225,239492,239493,239495,239497,239499,239501,239503],{"class":2227,"line":1041},[2225,239494,10510],{"class":2307},[2225,239496,2311],{"class":2235},[2225,239498,2249],{"class":2235},[2225,239500,91596],{"class":2252},[2225,239502,2805],{"class":2235},[2225,239504,1707],{"class":2235},[2225,239506,239507,239509,239511,239513,239515,239517],{"class":2227,"line":1038},[2225,239508,10526],{"class":2307},[2225,239510,2311],{"class":2235},[2225,239512,2249],{"class":2235},[2225,239514,91611],{"class":2252},[2225,239516,2805],{"class":2235},[2225,239518,1707],{"class":2235},[2225,239520,239521,239523,239525,239527,239529,239531],{"class":2227,"line":2288},[2225,239522,10542],{"class":2307},[2225,239524,2311],{"class":2235},[2225,239526,2249],{"class":2235},[2225,239528,9907],{"class":2252},[2225,239530,2805],{"class":2235},[2225,239532,1707],{"class":2235},[2225,239534,239535,239537,239539,239541,239543,239545],{"class":2227,"line":2304},[2225,239536,10423],{"class":2307},[2225,239538,2311],{"class":2235},[2225,239540,2249],{"class":2235},[2225,239542,197163],{"class":2252},[2225,239544,2805],{"class":2235},[2225,239546,1707],{"class":2235},[2225,239548,239549,239551,239553,239555,239557,239559,239561,239563,239565],{"class":2227,"line":2356},[2225,239550,10572],{"class":2307},[2225,239552,2311],{"class":2235},[2225,239554,2236],{"class":2235},[2225,239556,6503],{"class":2307},[2225,239558,2311],{"class":2235},[2225,239560,2249],{"class":2235},[2225,239562,10026],{"class":2252},[2225,239564,2805],{"class":2235},[2225,239566,2353],{"class":2235},[2225,239568,239569,239571,239573,239575,239577,239579,239581,239583,239585,239587,239589,239591,239593,239595,239597,239599,239601,239603,239605,239607,239609,239611],{"class":2227,"line":2830},[2225,239570,98785],{"class":2307},[2225,239572,2311],{"class":2235},[2225,239574,2236],{"class":2235},[2225,239576,72016],{"class":2307},[2225,239578,2311],{"class":2235},[2225,239580,2236],{"class":2235},[2225,239582,75153],{"class":2307},[2225,239584,2311],{"class":2235},[2225,239586,2249],{"class":2235},[2225,239588,91611],{"class":2252},[2225,239590,2805],{"class":2235},[2225,239592,10965],{"class":2235},[2225,239594,99096],{"class":2307},[2225,239596,2311],{"class":2235},[2225,239598,2236],{"class":2235},[2225,239600,98216],{"class":2307},[2225,239602,2311],{"class":2235},[2225,239604,2249],{"class":2235},[2225,239606,98015],{"class":2252},[2225,239608,2805],{"class":2235},[2225,239610,2243],{"class":2235},[2225,239612,2353],{"class":2235},[2225,239614,239615,239617],{"class":2227,"line":2861},[2225,239616,2359],{"class":2235},[2225,239618,2362],{"class":2239},[2225,239620,239621],{"class":2227,"line":4246},[2225,239622,239623],{"class":2763},"\u002F\u002F → bob's `_users\u002Fbob` envelope exists, pre-filled, before bob ever opens the vault\n",[1591,239625,239627],{"id":239626},"listuserswithenvelopesstore-vault-dek-callerrole",[941,239628,239629],{},"listUsersWithEnvelopes(store, vault, dek, callerRole)",[2217,239631,239633],{"className":2219,"code":239632,"language":2221,"meta":1037,"style":1037},"import { listUsersWithEnvelopes } from '@noy-db\u002Fhub'\n\nconst rows = await listUsersWithEnvelopes\u003CUserShape>(store, vault, dek, callerRole)\n\u002F\u002F rows: Array\u003C{ user: UserInfo; envelope: UserEnvelope\u003CUserShape> | null }>\n\u002F\u002F → [\n\u002F\u002F     { user: { userId: 'alice', displayName: 'Alice', role: 'owner', ... },\n\u002F\u002F       envelope: { keyringId: 'alice', data: { profile: { displayName: 'Alice' } }, _v: 1, _ts: '...' } },\n\u002F\u002F     { user: { userId: 'bob', displayName: 'Bob', role: 'operator', ... },\n\u002F\u002F       envelope: null },\n\u002F\u002F   ]\n",[941,239634,239635,239653,239657,239690,239695,239699,239704,239709,239714,239719],{"__ignoreMap":1037},[2225,239636,239637,239639,239641,239643,239645,239647,239649,239651],{"class":2227,"line":2228},[2225,239638,2232],{"class":2231},[2225,239640,2236],{"class":2235},[2225,239642,141867],{"class":2239},[2225,239644,2243],{"class":2235},[2225,239646,2246],{"class":2231},[2225,239648,2249],{"class":2235},[2225,239650,1086],{"class":2252},[2225,239652,2256],{"class":2235},[2225,239654,239655],{"class":2227,"line":1041},[2225,239656,2285],{"emptyLinePlaceholder":2284},[2225,239658,239659,239661,239663,239665,239667,239669,239671,239673,239675,239677,239679,239681,239683,239685,239687],{"class":2227,"line":1038},[2225,239660,2775],{"class":2774},[2225,239662,42003],{"class":2239},[2225,239664,2781],{"class":2235},[2225,239666,2784],{"class":2231},[2225,239668,141867],{"class":2291},[2225,239670,6474],{"class":2235},[2225,239672,98165],{"class":6342},[2225,239674,6480],{"class":2235},[2225,239676,39702],{"class":2239},[2225,239678,2298],{"class":2235},[2225,239680,6466],{"class":2239},[2225,239682,2298],{"class":2235},[2225,239684,193923],{"class":2239},[2225,239686,2298],{"class":2235},[2225,239688,239689],{"class":2239}," callerRole)\n",[2225,239691,239692],{"class":2227,"line":2288},[2225,239693,239694],{"class":2763},"\u002F\u002F rows: Array\u003C{ user: UserInfo; envelope: UserEnvelope\u003CUserShape> | null }>\n",[2225,239696,239697],{"class":2227,"line":2304},[2225,239698,100852],{"class":2763},[2225,239700,239701],{"class":2227,"line":2356},[2225,239702,239703],{"class":2763},"\u002F\u002F     { user: { userId: 'alice', displayName: 'Alice', role: 'owner', ... },\n",[2225,239705,239706],{"class":2227,"line":2830},[2225,239707,239708],{"class":2763},"\u002F\u002F       envelope: { keyringId: 'alice', data: { profile: { displayName: 'Alice' } }, _v: 1, _ts: '...' } },\n",[2225,239710,239711],{"class":2227,"line":2861},[2225,239712,239713],{"class":2763},"\u002F\u002F     { user: { userId: 'bob', displayName: 'Bob', role: 'operator', ... },\n",[2225,239715,239716],{"class":2227,"line":4246},[2225,239717,239718],{"class":2763},"\u002F\u002F       envelope: null },\n",[2225,239720,239721],{"class":2227,"line":4253},[2225,239722,100867],{"class":2763},[937,239724,239725,239726,1625,239728,239730,239731,1625,239733,968,239735,239737,239738,1520],{},"Convenience for admin UIs that want to render team-member lists with profile data in a single pass — ",[941,239727,6951],{},[941,239729,2694],{}," are the same values passed to ",[941,239732,12780],{},[941,239734,2847],{},[941,239736,142508],{}," is the caller's own role, and it drives the directory-visibility rules below. Principals without a persisted envelope (legacy keyrings predating this feature) come back with ",[941,239739,142520],{},[1022,239741,178796],{"id":178795},[937,239743,239744,239746,239747,239749,239750,239752,239753,1520],{},[941,239745,21055],{}," is always present — there is no opt-in to skip. Vaults that never call ",[941,239748,21609],{}," work identically to today; the ",[941,239751,141843],{}," collection is reserved on grant but envelopes are empty until first ",[941,239754,239755],{},"updateMe()",[1022,239757,239759],{"id":239758},"storage-shape","Storage shape",[937,239761,239762,239763,239766],{},"Each principal has one envelope at the store key ",[941,239764,239765],{},"(_users, \u003CkeyringId>)",". The envelope uses the standard noy-db form:",[2217,239768,239770],{"className":6604,"code":239769,"language":141,"meta":1037,"style":1037},"{ \"_noydb\": 1, \"_v\": 7, \"_ts\": \"2026-05-05T10:00:00.000Z\", \"_iv\": \"\u003Cbase64>\", \"_data\": \"\u003Cbase64 ciphertext>\" }\n",[941,239771,239772],{"__ignoreMap":1037},[2225,239773,239774,239776,239778,239780,239782,239784,239786,239788,239790,239792,239794,239796,239798,239800,239802,239804,239806,239808,239810,239813,239815,239817,239819,239821,239823,239825,239827,239829,239831,239833,239835,239837,239839,239841,239843,239845,239847],{"class":2227,"line":2228},[2225,239775,2792],{"class":2235},[2225,239777,6614],{"class":2235},[2225,239779,1697],{"class":2774},[2225,239781,6620],{"class":2235},[2225,239783,2311],{"class":2235},[2225,239785,9377],{"class":6527},[2225,239787,2298],{"class":2235},[2225,239789,6614],{"class":2235},[2225,239791,1700],{"class":2774},[2225,239793,6620],{"class":2235},[2225,239795,2311],{"class":2235},[2225,239797,44442],{"class":6527},[2225,239799,2298],{"class":2235},[2225,239801,6614],{"class":2235},[2225,239803,1703],{"class":2774},[2225,239805,6620],{"class":2235},[2225,239807,2311],{"class":2235},[2225,239809,6614],{"class":2235},[2225,239811,239812],{"class":2252},"2026-05-05T10:00:00.000Z",[2225,239814,6620],{"class":2235},[2225,239816,2298],{"class":2235},[2225,239818,6614],{"class":2235},[2225,239820,1143],{"class":2774},[2225,239822,6620],{"class":2235},[2225,239824,2311],{"class":2235},[2225,239826,6614],{"class":2235},[2225,239828,14223],{"class":2252},[2225,239830,6620],{"class":2235},[2225,239832,2298],{"class":2235},[2225,239834,6614],{"class":2235},[2225,239836,1146],{"class":2774},[2225,239838,6620],{"class":2235},[2225,239840,2311],{"class":2235},[2225,239842,6614],{"class":2235},[2225,239844,14242],{"class":2252},[2225,239846,6620],{"class":2235},[2225,239848,6369],{"class":2235},[937,239850,239851,239853,239854,239856,239857,239860,239861,239864],{},[941,239852,1146],{}," decrypts (under the ",[941,239855,141843],{}," DEK) to an opaque JSON object. Hub does not reserve any keys inside the payload. The reference shape ",[941,239858,239859],{},"{ profile, preferences, app }"," lives in the ",[941,239862,239863],{},"recipe-user-preferences"," recipe as copy-paste material — apps adopt, modify, or replace it.",[937,239866,960,239867,239869,239870,2419,239873,239876,239877,239880,239881,239884,239885,239887],{},[941,239868,141843],{}," DEK is ",[950,239871,239872],{},"eager-provisioned",[941,239874,239875],{},"createOwnerKeyring()"," for new vaults, and propagated to every granted keyring via the existing system-collection branch (",[941,239878,239879],{},"collName.startsWith('_')",") in ",[941,239882,239883],{},"team\u002Fkeyring.ts",". Pre-existing vaults (provisioned before this feature) get the DEK lazily on first ",[941,239886,21609],{}," access — see \"Edge cases & limits\" below.",[1022,239889,239891],{"id":239890},"permission-model","Permission model",[937,239893,239894],{},"Two new built-in policy gates, registered alongside the existing tier-2\u002Ftier-3 gates:",[1338,239896,239897,239911],{},[1341,239898,239899],{},[1344,239900,239901,239903,239906,239909],{},[1347,239902,104381],{},[1347,239904,239905],{},"Default (PERSONAL)",[1347,239907,239908],{},"Default (STRICT)",[1347,239910,7834],{},[1357,239912,239913,239932],{},[1344,239914,239915,239919,239924,239929],{},[1362,239916,239917],{},[941,239918,98678],{},[1362,239920,239921],{},[941,239922,239923],{},"{ minTier: 3 }",[1362,239925,239926],{},[941,239927,239928],{},"{ minTier: 2, factors: [{ anyOf: ['totp', 'email-otp'] }] }",[1362,239930,239931],{},"Authorize a write to one's own envelope.",[1344,239933,239934,239938,239943,239947],{},[1362,239935,239936],{},[941,239937,98879],{},[1362,239939,239940],{},[941,239941,239942],{},"{ minTier: 2 }",[1362,239944,239945],{},[941,239946,239942],{},[1362,239948,239949],{},"Authorize reading other principals' envelopes.",[937,239951,239952,239955],{},[950,239953,239954],{},"Structural rules"," (cannot be relaxed by policy):",[1234,239957,239958,239973],{},[1114,239959,239960,1261,239963,239965,239966,239969,239970,239972],{},[941,239961,239962],{},"vault.user.updateMe",[941,239964,98553],{}," always target ",[941,239967,239968],{},"writerKeyringId",". Runtime check throws ",[941,239971,11395],{}," if the resolved target keyringId does not equal the writer's.",[1114,239974,239975],{},"The API exposes no method that accepts a target keyringId for writes. There is no path by which an admin or owner can write a teammate's envelope.",[937,239977,239978],{},[950,239979,239980],{},"Tightening (allowed):",[1111,239982,239983,239989],{},[1114,239984,239985,239986,239988],{},"A strict app sets ",[941,239987,98678],{}," to require a fresh TOTP for any profile change.",[1114,239990,239991,239992,1746,239995,239997,239998,6789,240001,1520],{},"A privacy-strict app sets ",[941,239993,239994],{},"view-team-profiles.enabled: false",[941,239996,24407],{}," returns only self; ",[941,239999,240000],{},"get(other)",[941,240002,13025],{},[937,240004,240005],{},[950,240006,240007],{},"Relaxing (disallowed):",[1111,240009,240010],{},[1114,240011,218175,240012,240014],{},[941,240013,98678],{}," to permit cross-principal writes is impossible. Policy controls authentication, not authorization. The own-only rule is structural.",[1022,240016,6304],{"id":6303},[1111,240018,240019,240038,240057,240063,240076],{},[1114,240020,240021,240024,240025,240028,240029,240031,240032,240035,240036,957],{},[950,240022,240023],{},"Created on grant."," When ",[941,240026,240027],{},"team\u002Fgrant.ts"," provisions a new keyring, it also creates a ",[941,240030,97735],{}," envelope, seeded with ",[941,240033,240034],{},"initialProfile?: T"," if present, else empty (",[941,240037,9261],{},[1114,240039,240040,240043,240044,240047,240048,240051,240052,240054,240055,1293],{},[950,240041,240042],{},"Magic-link grants."," The existing ",[941,240045,240046],{},"team\u002Fmagic-link-grant.ts"," is for ",[950,240049,240050],{},"tier delegation"," (auditor gets 48h access to a collection), not user creation; it does not produce a persistent keyring. Bootstrap ",[941,240053,98695],{}," flows through ",[941,240056,21197],{},[1114,240058,240059,240062],{},[950,240060,240061],{},"Deleted on revoke."," Cascade-revoke removes the envelope alongside the keyring file. Idempotent: no error if the envelope is already absent.",[1114,240064,240065,240068,240069,240072,240073,240075],{},[950,240066,240067],{},"Rotated on tier-1 rotation."," The DEK rotation path re-encrypts every ",[941,240070,240071],{},"_users\u002F*"," envelope under the fresh DEK — the user-envelope feature inherits this for free since ",[941,240074,141843],{}," is in the affected collections set.",[1114,240077,240078,240081],{},[950,240079,240080],{},"Untouched on tier-2 add\u002Fremove."," Authenticator slot changes do not touch the envelope.",[1022,240083,114684],{"id":114683},[1111,240085,240086,240095,240104,240112],{},[1114,240087,240088,240090,240091,240094],{},[941,240089,868],{}," — joined ",[941,240092,240093],{},"listUsersWithEnvelopes()"," for admin UIs; cascade-revoke deletes envelopes alongside keyrings.",[1114,240096,240097,1169,240099,997,240101,240103],{},[941,240098,872],{},[941,240100,98678],{},[941,240102,98879],{}," gates plug into the same policy-gate DSL as every other built-in gate.",[1114,240105,240106,240108,240109,240111],{},[941,240107,864],{}," — own-write changes propagate to teammates' clients on the next sync round-trip; ",[941,240110,25045],{}," fires when the sync layer replays the write through the API.",[1114,240113,240114,240116,240117,240120,240121,180228],{},[941,240115,701],{}," — apps storing ",[941,240118,240119],{},"profile.locale"," use it to drive ",[941,240122,240123],{},"vault.t()",[1022,240125,6825],{"id":6824},[1111,240127,240128,240140,240165,240171,240181],{},[1114,240129,240130,240133,240134,240136,240137,957],{},[950,240131,240132],{},"Soft size cap: 64 KiB per envelope."," Larger payloads throw ",[941,240135,98117],{},". UTF-8 byte-aware (multi-byte characters are counted via TextEncoder, not ",[941,240138,240139],{},"String.length",[1114,240141,240142,240145,240146,240148,240149,240151,240152,240155,240156,240159,240160,240162,240163,1520],{},[950,240143,240144],{},"Pre-existing vaults — single-principal lazy DEK."," Vaults provisioned before this feature do not have a ",[941,240147,141843],{}," DEK in any keyring. The first call to ",[941,240150,21609],{}," lazily creates the DEK in ",[1092,240153,240154],{},"that user's"," keyring only. Other principals on a different machine that open the vault later will then create their own (different) DEK — and cross-principal reads will fail until the vault rotates the keys. Workaround: call ",[941,240157,240158],{},"db.rotate(vault, ['_users'])"," after the first user calls ",[941,240161,21609],{}," to propagate the DEK to all keyrings. New vaults (post-feature) avoid this entirely via eager provisioning in ",[941,240164,239875],{},[1114,240166,240167,240170],{},[950,240168,240169],{},"Subscribe \u002F live in v1 fire only on local writes."," Cross-instance changes flow via team\u002Fsync; subscribers there see the change when sync replays through the API. Wiring raw store-level changes into subscribers is a follow-up.",[1114,240172,240173,240176,240177,240180],{},[950,240174,240175],{},"No managed-mode identity-provider integration yet."," A future ",[941,240178,240179],{},"UserProfileProvider"," interface for syncing profile data from an external IdP is planned but not implemented — there is no such export, constructor option, or runtime hook in the current API.",[1114,240182,240183,240191,240192,240194],{},[950,240184,240185,240187,240188,240190],{},[941,240186,141235],{}," requires the ",[941,240189,141843],{}," DEK, which has no public accessor yet."," The function's ",[941,240193,194035],{}," parameter must currently be sourced from vault internals; there is no supported public method that returns it. Until one ships, the joined enumeration is reachable from code with direct access to those internals (e.g. within the hub itself), not from ordinary app code.",[1022,240196,240198],{"id":240197},"directory-visibility","Directory visibility",[937,240200,240201,240202,2311],{},"Two complementary opt-outs on top of ",[941,240203,141235],{},[1111,240205,240206,240293],{},[1114,240207,240208,240213,240214,240216,240217,1625,240219,240221,240222,240225,240226,240230,240231,240233,240234],{},[950,240209,240210],{},[941,240211,240212],{},"vault.user.setMyVisibility({ hidden: true })"," — per-user opt-out.\nPersisted as a plaintext sidecar at ",[941,240215,142146],{},".\nHidden users are filtered out of the default listing; ",[941,240218,9898],{},[941,240220,9901],{},"\ncallers can pass ",[941,240223,240224],{},"{ includeHidden: true }"," to see them. Own-only by\nconstruction (no method to hide another principal). Read it back with\n",[950,240227,240228],{},[941,240229,142348],{},", which returns ",[941,240232,142303],{},"\nwhen nothing has been persisted yet.",[2217,240235,240237],{"className":2219,"code":240236,"language":2221,"meta":1037,"style":1037},"await vault.user.setMyVisibility({ hidden: true })\nconst mine = await vault.user.getMyVisibility()\n\u002F\u002F → { hidden: true }\n",[941,240238,240239,240267,240289],{"__ignoreMap":1037},[2225,240240,240241,240243,240245,240247,240249,240251,240253,240255,240257,240259,240261,240263,240265],{"class":2227,"line":2228},[2225,240242,6500],{"class":2231},[2225,240244,6466],{"class":2239},[2225,240246,1520],{"class":2235},[2225,240248,16225],{"class":2239},[2225,240250,1520],{"class":2235},[2225,240252,141282],{"class":2291},[2225,240254,2789],{"class":2239},[2225,240256,2792],{"class":2235},[2225,240258,141733],{"class":2307},[2225,240260,2311],{"class":2235},[2225,240262,2322],{"class":2321},[2225,240264,2243],{"class":2235},[2225,240266,2362],{"class":2239},[2225,240268,240269,240271,240273,240275,240277,240279,240281,240283,240285,240287],{"class":2227,"line":1041},[2225,240270,2775],{"class":2774},[2225,240272,238639],{"class":2239},[2225,240274,2781],{"class":2235},[2225,240276,2784],{"class":2231},[2225,240278,6466],{"class":2239},[2225,240280,1520],{"class":2235},[2225,240282,16225],{"class":2239},[2225,240284,1520],{"class":2235},[2225,240286,141756],{"class":2291},[2225,240288,8146],{"class":2239},[2225,240290,240291],{"class":2227,"line":1038},[2225,240292,141763],{"class":2763},[1114,240294,240295,240300,240301,240303,240304,6789,240306,240308,240309,139707,240311,1520,240313],{},[950,240296,240297],{},[941,240298,240299],{},"db.setDirectoryEnabled(vault, false)"," — vault-level toggle.\nOwner-only. Persisted at ",[941,240302,16900],{},". When disabled,\n",[941,240305,141235],{},[941,240307,16907],{}," for any\ncaller whose role is neither ",[941,240310,9898],{},[941,240312,9901],{},[2217,240314,240316],{"className":2219,"code":240315,"language":2221,"meta":1037,"style":1037},"await db.setDirectoryEnabled('acme', false)\n\u002F\u002F → any admin\u002Fnon-owner call to listUsersWithEnvelopes now throws DirectoryDisabledError\n",[941,240317,240318,240342],{"__ignoreMap":1037},[2225,240319,240320,240322,240324,240326,240328,240330,240332,240334,240336,240338,240340],{"class":2227,"line":2228},[2225,240321,6500],{"class":2231},[2225,240323,2842],{"class":2239},[2225,240325,1520],{"class":2235},[2225,240327,16868],{"class":2291},[2225,240329,2789],{"class":2239},[2225,240331,2805],{"class":2235},[2225,240333,6450],{"class":2252},[2225,240335,2805],{"class":2235},[2225,240337,2298],{"class":2235},[2225,240339,2341],{"class":2321},[2225,240341,2362],{"class":2239},[2225,240343,240344],{"class":2227,"line":1041},[2225,240345,240346],{"class":2763},"\u002F\u002F → any admin\u002Fnon-owner call to listUsersWithEnvelopes now throws DirectoryDisabledError\n",[937,240348,240349,240350,240352,240353,240356,240357,240359,240360,240362,240363,1520],{},"Both flags live in ",[941,240351,117002],{}," rather than inside the encrypted\n",[941,240354,240355],{},"UserEnvelope\u003CT>.data"," payload — ",[941,240358,8673],{}," is opaque-to-hub by contract,\nand the directory filter must work even when an envelope decryption\nfails (legacy keyrings predating the envelope feature, or a corrupted\nenvelope). Storing them as plaintext sidecars matches the\n",[941,240361,12784],{}," pattern documented in\n",[1007,240364,240366],{"href":240365},".\u002Fplaintext-bypass",[941,240367,240368],{},"plaintext-bypass.md",[937,240370,240371,240374,240375,240377,240378,240380],{},[950,240372,240373],{},"Honest caveat — this is a UX flag, not a privacy guarantee."," The\nkeyring file at ",[941,240376,9871],{}," is still listed (an attacker with\ndirect store read access can count keyrings and read role +\npermissions metadata). The envelope ciphertext at\n",[941,240379,97735],{}," is still present in the store. The visibility +\ndirectory flags only gate the hub-level enumeration helper — they\nprevent admin-UIs from accidentally rendering a hidden user, not a\ndetermined adversary from learning who has access. Apps that need\nreal principal anonymity should use a dedicated identity store\nexternal to the vault.",[937,240382,240383,240384,240386],{},"Peer-recovery preserves both flags: ",[941,240385,198030],{}," only rewrites the\nkeyring file, so the user's visibility doc and the vault's directory\ndoc both survive the rotation.",[1022,240388,2621],{"id":2620},[1111,240390,240391,240399,240407,240412,240417],{},[1114,240392,240393,240398],{},[1007,240394,240396],{"href":93364,"rel":240395},[3371],[941,240397,93368],{}," — Hub API end-to-end",[1114,240400,240401,240406],{},[1007,240402,240404],{"href":94526,"rel":240403},[3371],[941,240405,94530],{}," — reference shape pattern + device-local pattern",[1114,240408,240409,240411],{},[941,240410,218349],{}," — policy gate DSL the new gates plug into",[1114,240413,240414,240416],{},[941,240415,232820],{}," — multi-user grant\u002Frevoke flows the lifecycle binds to",[1114,240418,240419,964,240421,997,240423,240425],{},[941,240420,194526],{},[941,240422,16900],{},[941,240424,142146],{}," are documented bypasses",[2645,240427,240428],{},"html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sHdIc, html code.shiki .sHdIc{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#EEFFFF;--shiki-default-font-style:italic;--shiki-dark:#BABED8;--shiki-dark-font-style:italic}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}html pre.shiki code .sfNiH, html code.shiki .sfNiH{--shiki-light:#FF5370;--shiki-default:#FF9CAC;--shiki-dark:#FF9CAC}",{"title":1037,"searchDepth":1038,"depth":1038,"links":240430},[240431,240432,240433,240434,240439,240446,240447,240448,240449,240450,240451,240452,240453],{"id":29856,"depth":1041,"text":29857},{"id":111659,"depth":1041,"text":111660},{"id":58393,"depth":1041,"text":58394},{"id":29892,"depth":1041,"text":29893,"children":240435},[240436,240437,240438],{"id":238618,"depth":1038,"text":238619},{"id":238682,"depth":1038,"text":238683},{"id":238743,"depth":1038,"text":238744},{"id":41259,"depth":1041,"text":101012,"children":240440},[240441,240442,240443,240444,240445],{"id":238889,"depth":1038,"text":238890},{"id":239042,"depth":1038,"text":238372},{"id":239319,"depth":1038,"text":238385},{"id":239423,"depth":1038,"text":239424},{"id":239626,"depth":1038,"text":239629},{"id":178795,"depth":1041,"text":178796},{"id":239758,"depth":1041,"text":239759},{"id":239890,"depth":1041,"text":239891},{"id":6303,"depth":1041,"text":6304},{"id":114683,"depth":1041,"text":114684},{"id":6824,"depth":1041,"text":6825},{"id":240197,"depth":1041,"text":240198},{"id":2620,"depth":1041,"text":2621},"Per-principal profile storage: every keyring gets a small encrypted JSON object at `_users\u002F\u003CkeyringId>` for its own display name, theme, and preferences.",{},{"title":884,"description":240454},"XD28HhnTtYzpbHA12mdsE9KBuINChoFlzHmpiomBh8E",{"id":240459,"title":888,"access":12,"apiToc":929,"body":240460,"category":1042,"description":240490,"extension":1044,"meta":240491,"navigation":240492,"nest":1047,"path":889,"seo":240493,"sinceVersion":1042,"status":183052,"stem":890,"subtitle":1042,"tier":1050,"tocExternal":1042,"__hash__":240494},"docs\u002Fservices\u002Fvalidation.md",{"type":931,"value":240461,"toc":240487},[240462,240464,240467,240477,240479],[934,240463,888],{"id":888},[46871,240465],{":rows":240466},"[{\"label\":\"Subpath (reserved, not yet implemented)\",\"value\":\"`@noy-db\u002Fhub\u002Fvalidation`\"},{\"label\":\"Factory\",\"value\":\"none yet\"},{\"label\":\"Layer\",\"value\":\"not yet assigned (reserved slot)\"}]",[937,240468,240469,240470,240473,240474,240476],{},"Validation is a reserved service name for richer runtime validators —\nZod-style or JSON-Schema checks beyond what the core schema layer\nenforces. It does not exist yet: there is no factory and no\n",[941,240471,240472],{},"@noy-db\u002Fhub\u002Fvalidation"," subpath export. Schema and reference integrity\nare core today (see ",[1007,240475,23934],{"href":112},"); deeper validation would\nsplit out into this slot if it lands.",[1022,240478,2621],{"id":2620},[1111,240480,240481],{},[1114,240482,240483,1368,240485],{},[1007,240484,61743],{"href":598},[1007,240486,1019],{"href":1018},{"title":1037,"searchDepth":1038,"depth":1038,"links":240488},[240489],{"id":2620,"depth":1041,"text":2621},"Richer runtime validators (Zod-style, JSON-schema). Today schema\u002Frefs is core; deeper validation could split out.",{},{"badge":754},{"title":888,"description":240490},"yEDZWDmoxETzFLGPf5kGh2rd0yp2VMkFGEQbPxRplE8",{"id":240496,"title":892,"access":12,"apiToc":929,"body":240497,"category":1042,"description":241130,"extension":1044,"meta":241131,"navigation":1042,"nest":1047,"path":893,"seo":241132,"sinceVersion":1042,"status":1049,"stem":894,"subtitle":1042,"tier":1473,"tocExternal":1042,"__hash__":241133},"docs\u002Fservices\u002Fvault-head.md",{"type":931,"value":240498,"toc":241119},[240499,240501,240504,240506,240512,240517,240530,240540,240544,240551,240557,240576,240578,240601,240605,240817,240821,240850,240897,240903,240909,240913,240962,240973,240977,241045,241086,241088,241117],[934,240500,892],{"id":892},[46871,240502],{":rows":240503},"[{\"label\":\"Subpath\",\"value\":\"`@noy-db\u002Fhub\u002Fvault-head`\"},{\"label\":\"Factory\",\"value\":\"`withVaultHead()`\"},{\"label\":\"Layer\",\"value\":\"`with-commit`\"}]",[1022,240505,29857],{"id":29856},[937,240507,240508,240509,240511],{},"A record's envelope authenticates itself. Its identity — collection, id, tier, author — and its\nversion are bound into the AEAD, so an untrusted store cannot relocate a record, re-tier it,\nre-author it, splice another body into it, or present it at a version it was not sealed at. The\nsync merge rejects an envelope that fails to authenticate ",[1092,240510,11357],{}," committing it.",[937,240513,240514],{},[950,240515,240516],{},"None of that can see absence.",[937,240518,240519,240520,182638,240523,240526,240527,240529],{},"A store serving a genuine, unmodified ",[941,240521,240522],{},"v1",[941,240524,240525],{},"v7"," exists is serving a real record this client\nreally wrote — nothing about the bytes is wrong. A store serving ",[1092,240528,18459],{}," is indistinguishable\nfrom a record that never existed. You cannot detect a missing thing by examining the things\npresent.",[937,240531,240532,240535,240536,240539],{},[941,240533,240534],{},"withVaultHead()"," supplies the missing external knowledge: an authenticated ",[941,240537,240538],{},"{id → version}","\nmanifest the client writes and the store cannot forge.",[1022,240541,240543],{"id":240542},"why-it-is-opt-in","Why it is opt-in",[937,240545,240546,240547,240550],{},"Identity binding costs nothing to coordinate and closes ",[1092,240548,240549],{},"alteration",", so it is unconditional. The\nhead costs a write per commit and needs anti-entropy. On a single-device offline vault it defends\nagainst nothing — there is no second party to withhold anything — so making it mandatory would tax\nevery user for a multi-writer property.",[937,240552,240553,240554,240556],{},"That split is what lets the security boundary be stated as a narrower ",[950,240555,11790],{}," claim rather than a\nconcession:",[7646,240558,240559],{"color":11278,"icon":226533},[937,240560,240561,240562,240564,240565,137767,240568,240570,240571,4688,240574,1520],{},"A remote store — or a ",[941,240563,16057],{}," peer — ",[950,240566,240567],{},"cannot alter, relocate, re-tier, re-author, splice or\nre-version any record it serves.",[941,240569,240534],{},", it can still ",[950,240572,240573],{},"withhold",[950,240575,119893],{},[1022,240577,70093],{"id":70092},[1111,240579,240580,240592,240598],{},[1114,240581,240582,240587,240588,240591],{},[950,240583,240584,240585],{},"A store that advertises ",[941,240586,160746],{}," — the head is written with a\ncompare-and-set. Without it a sweep reports ",[941,240589,240590],{},"'unverifiable'"," rather than a false clean.",[1114,240593,240594,240597],{},[950,240595,240596],{},"A multi-writer or remote deployment for it to be worth enabling"," — on a single-device\noffline vault there is no second party to withhold anything.",[1114,240599,240600],{},"Nothing else: the head travels as ordinary records over the same store contract, so no adapter\nneeds new methods to support it.",[1022,240602,240604],{"id":240603},"using-it","Using it",[2217,240606,240608],{"className":2219,"code":240607,"language":2221,"meta":1037,"style":1037},"import { createNoydb } from '@noy-db\u002Fhub'\nimport { withVaultHead, verifyVaultHead } from '@noy-db\u002Fhub\u002Fvault-head'\n\nconst head = withVaultHead()\nconst db = await createNoydb({ vaultHeadStrategy: head, store, user, secret })\nconst vault = await db.openVault('acme')\n\n\u002F\u002F …writes proceed normally; the head records each one…\n\nconst { adapter, getDEK } = vault._introspectState()\nconst result = await verifyVaultHead(head, adapter, 'acme', getDEK, 'invoices')\n",[941,240609,240610,240628,240653,240657,240670,240710,240736,240740,240745,240749,240775],{"__ignoreMap":1037},[2225,240611,240612,240614,240616,240618,240620,240622,240624,240626],{"class":2227,"line":2228},[2225,240613,2232],{"class":2231},[2225,240615,2236],{"class":2235},[2225,240617,2744],{"class":2239},[2225,240619,2243],{"class":2235},[2225,240621,2246],{"class":2231},[2225,240623,2249],{"class":2235},[2225,240625,1086],{"class":2252},[2225,240627,2256],{"class":2235},[2225,240629,240630,240632,240634,240637,240639,240642,240644,240646,240648,240651],{"class":2227,"line":1041},[2225,240631,2232],{"class":2231},[2225,240633,2236],{"class":2235},[2225,240635,240636],{"class":2239}," withVaultHead",[2225,240638,2298],{"class":2235},[2225,240640,240641],{"class":2239}," verifyVaultHead",[2225,240643,2243],{"class":2235},[2225,240645,2246],{"class":2231},[2225,240647,2249],{"class":2235},[2225,240649,240650],{"class":2252},"@noy-db\u002Fhub\u002Fvault-head",[2225,240652,2256],{"class":2235},[2225,240654,240655],{"class":2227,"line":1038},[2225,240656,2285],{"emptyLinePlaceholder":2284},[2225,240658,240659,240661,240664,240666,240668],{"class":2227,"line":2288},[2225,240660,2775],{"class":2774},[2225,240662,240663],{"class":2239}," head ",[2225,240665,2781],{"class":2235},[2225,240667,240636],{"class":2291},[2225,240669,8146],{"class":2239},[2225,240671,240672,240674,240676,240678,240680,240682,240684,240686,240689,240691,240694,240696,240698,240700,240702,240704,240706,240708],{"class":2227,"line":2304},[2225,240673,2775],{"class":2774},[2225,240675,2778],{"class":2239},[2225,240677,2781],{"class":2235},[2225,240679,2784],{"class":2231},[2225,240681,2744],{"class":2291},[2225,240683,2789],{"class":2239},[2225,240685,2792],{"class":2235},[2225,240687,240688],{"class":2307}," vaultHeadStrategy",[2225,240690,2311],{"class":2235},[2225,240692,240693],{"class":2239}," head",[2225,240695,2298],{"class":2235},[2225,240697,7564],{"class":2239},[2225,240699,2298],{"class":2235},[2225,240701,2795],{"class":2239},[2225,240703,2298],{"class":2235},[2225,240705,46330],{"class":2239},[2225,240707,2359],{"class":2235},[2225,240709,2362],{"class":2239},[2225,240711,240712,240714,240716,240718,240720,240722,240724,240726,240728,240730,240732,240734],{"class":2227,"line":2356},[2225,240713,2775],{"class":2774},[2225,240715,2835],{"class":2239},[2225,240717,2781],{"class":2235},[2225,240719,2784],{"class":2231},[2225,240721,2842],{"class":2239},[2225,240723,1520],{"class":2235},[2225,240725,2847],{"class":2291},[2225,240727,2789],{"class":2239},[2225,240729,2805],{"class":2235},[2225,240731,6450],{"class":2252},[2225,240733,2805],{"class":2235},[2225,240735,2362],{"class":2239},[2225,240737,240738],{"class":2227,"line":2830},[2225,240739,2285],{"emptyLinePlaceholder":2284},[2225,240741,240742],{"class":2227,"line":2861},[2225,240743,240744],{"class":2763},"\u002F\u002F …writes proceed normally; the head records each one…\n",[2225,240746,240747],{"class":2227,"line":4246},[2225,240748,2285],{"emptyLinePlaceholder":2284},[2225,240750,240751,240753,240755,240757,240759,240762,240764,240766,240768,240770,240773],{"class":2227,"line":4253},[2225,240752,2775],{"class":2774},[2225,240754,2236],{"class":2235},[2225,240756,96324],{"class":2239},[2225,240758,2298],{"class":2235},[2225,240760,240761],{"class":2239}," getDEK ",[2225,240763,2359],{"class":2235},[2225,240765,6346],{"class":2235},[2225,240767,6466],{"class":2239},[2225,240769,1520],{"class":2235},[2225,240771,240772],{"class":2291},"_introspectState",[2225,240774,8146],{"class":2239},[2225,240776,240777,240779,240781,240783,240785,240787,240790,240792,240794,240796,240798,240800,240802,240804,240807,240809,240811,240813,240815],{"class":2227,"line":4259},[2225,240778,2775],{"class":2774},[2225,240780,36920],{"class":2239},[2225,240782,2781],{"class":2235},[2225,240784,2784],{"class":2231},[2225,240786,240641],{"class":2291},[2225,240788,240789],{"class":2239},"(head",[2225,240791,2298],{"class":2235},[2225,240793,96324],{"class":2239},[2225,240795,2298],{"class":2235},[2225,240797,2249],{"class":2235},[2225,240799,6450],{"class":2252},[2225,240801,2805],{"class":2235},[2225,240803,2298],{"class":2235},[2225,240805,240806],{"class":2239}," getDEK",[2225,240808,2298],{"class":2235},[2225,240810,2249],{"class":2235},[2225,240812,6487],{"class":2252},[2225,240814,2805],{"class":2235},[2225,240816,2362],{"class":2239},[1022,240818,240820],{"id":240819},"the-verdict-is-three-way-and-the-middle-value-is-the-point","The verdict is three-way, and the middle value is the point",[2217,240822,240824],{"className":2219,"code":240823,"language":2221,"meta":1037,"style":1037},"result.verdict  \u002F\u002F 'verified' | 'unverifiable' | 'tampered'\nresult.because  \u002F\u002F why it could not conclude — non-empty only when 'unverifiable'\n",[941,240825,240826,240838],{"__ignoreMap":1037},[2225,240827,240828,240830,240832,240835],{"class":2227,"line":2228},[2225,240829,11195],{"class":2239},[2225,240831,1520],{"class":2235},[2225,240833,240834],{"class":2239},"verdict  ",[2225,240836,240837],{"class":2763},"\u002F\u002F 'verified' | 'unverifiable' | 'tampered'\n",[2225,240839,240840,240842,240844,240847],{"class":2227,"line":1041},[2225,240841,11195],{"class":2239},[2225,240843,1520],{"class":2235},[2225,240845,240846],{"class":2239},"because  ",[2225,240848,240849],{"class":2763},"\u002F\u002F why it could not conclude — non-empty only when 'unverifiable'\n",[1338,240851,240852,240860],{},[1341,240853,240854],{},[1344,240855,240856,240858],{},[1347,240857,61655],{},[1347,240859,82055],{},[1357,240861,240862,240871,240883],{},[1344,240863,240864,240868],{},[1362,240865,240866],{},[941,240867,228415],{},[1362,240869,240870],{},"the head held expectations, every one was met, and the store can serialize head writes",[1344,240872,240873,240877],{},[1362,240874,240875],{},[941,240876,240590],{},[1362,240878,240879,240880],{},"the sweep could not reach a conclusion — see ",[941,240881,240882],{},"because",[1344,240884,240885,240890],{},[1362,240886,240887],{},[941,240888,240889],{},"'tampered'",[1362,240891,240892,240893,240896],{},"at least one discrepancy. Positive evidence, so it wins over any ",[941,240894,240895],{},"unverifiable"," reason present at the same time",[937,240898,240899,240902],{},[950,240900,240901],{},"Collapsing the middle value is wrong in a different direction each way."," Folded into \"clean\" it\nhides withholding, which is the one thing this service exists to catch. Folded into \"tampered\" it\ncries wolf on a vault that is merely unexamined.",[937,240904,240905,240906,240908],{},"That is not hypothetical: this service's own first bug was a head that recorded ",[950,240907,18459],{}," and\nswept perfectly clean — the degraded state rendered identically to a healthy one. Assert on the\ncount of records checked, not merely on the absence of an error.",[1591,240910,240912],{"id":240911},"why-a-sweep-may-be-unverifiable","Why a sweep may be unverifiable",[1338,240914,240915,240925],{},[1341,240916,240917],{},[1344,240918,240919,240923],{},[1347,240920,240921],{},[941,240922,240882],{},[1347,240924,82055],{},[1357,240926,240927,240949],{},[1344,240928,240929,240934],{},[1362,240930,240931],{},[941,240932,240933],{},"'no-expectations'",[1362,240935,240936,240937,953,240940,7356,240943,240945,240946,240948],{},"the head holds nothing for this collection — a fresh vault, a head switched on after the fact, or a ",[950,240938,240939],{},"restore from a snapshot",[941,240941,240942],{},"_head",[941,240944,6702],{},"-prefixed, so ",[941,240947,3170],{}," excludes it and a restored vault has no head to compare against)",[1344,240950,240951,240956],{},[1362,240952,240953],{},[941,240954,240955],{},"'store-cannot-cas'",[1362,240957,240958,240959,240961],{},"the store does not advertise ",[941,240960,160746],{},", so two writers racing on one bucket can silently lose an entry — and a lost entry is a record the sweep stops expecting, which is a false clean",[7646,240963,240964],{"color":10789,"icon":39006},[937,240965,240966,240969,240970,240972],{},[950,240967,240968],{},"Capability honesty is an integrity property here, not a lost-update one."," The head is written\nwith a compare-and-set. A store that under-reports ",[941,240971,9539],{}," degrades the very manifest that\nexists to detect that store — the attack disables its own detector.",[1022,240974,240976],{"id":240975},"discrepancies","Discrepancies",[2217,240978,240980],{"className":2219,"code":240979,"language":2221,"meta":1037,"style":1037},"if (result.verdict === 'tampered') {\n  for (const d of result.discrepancies) {\n    \u002F\u002F d.expected — what the client last committed\n    \u002F\u002F d.actual   — what the store serves now, or null when it serves nothing\n  }\n}\n",[941,240981,240982,241005,241027,241032,241037,241041],{"__ignoreMap":1037},[2225,240983,240984,240986,240988,240990,240993,240995,240997,240999,241001,241003],{"class":2227,"line":2228},[2225,240985,35129],{"class":2231},[2225,240987,43840],{"class":2239},[2225,240989,1520],{"class":2235},[2225,240991,240992],{"class":2239},"verdict ",[2225,240994,33083],{"class":2235},[2225,240996,2249],{"class":2235},[2225,240998,196452],{"class":2252},[2225,241000,2805],{"class":2235},[2225,241002,8495],{"class":2239},[2225,241004,4200],{"class":2235},[2225,241006,241007,241009,241011,241013,241015,241017,241019,241021,241023,241025],{"class":2227,"line":1041},[2225,241008,80016],{"class":2231},[2225,241010,953],{"class":2307},[2225,241012,2775],{"class":2774},[2225,241014,148707],{"class":2239},[2225,241016,8806],{"class":2235},[2225,241018,40906],{"class":2239},[2225,241020,1520],{"class":2235},[2225,241022,240975],{"class":2239},[2225,241024,8495],{"class":2307},[2225,241026,4200],{"class":2235},[2225,241028,241029],{"class":2227,"line":1038},[2225,241030,241031],{"class":2763},"    \u002F\u002F d.expected — what the client last committed\n",[2225,241033,241034],{"class":2227,"line":2288},[2225,241035,241036],{"class":2763},"    \u002F\u002F d.actual   — what the store serves now, or null when it serves nothing\n",[2225,241038,241039],{"class":2227,"line":2304},[2225,241040,7632],{"class":2235},[2225,241042,241043],{"class":2227,"line":2356},[2225,241044,7351],{"class":2235},[1338,241046,241047,241057],{},[1341,241048,241049],{},[1344,241050,241051,241055],{},[1347,241052,241053],{},[941,241054,27895],{},[1347,241056,82055],{},[1357,241058,241059,241072],{},[1344,241060,241061,241066],{},[1362,241062,241063],{},[941,241064,241065],{},"'withheld'",[1362,241067,241068,241069],{},"the store serves nothing for a record we wrote — ",[950,241070,241071],{},"suppression",[1344,241073,241074,241079],{},[1362,241075,241076],{},[941,241077,241078],{},"'rolled-back'",[1362,241080,241081,241082,241085],{},"it serves an authentic ",[950,241083,241084],{},"older"," version in place of the current one — the case no amount of per-envelope authentication can see",[1022,241087,68210],{"id":68209},[1111,241089,241090,241100,241108],{},[1114,241091,241092,241095,241096,241099],{},[950,241093,241094],{},"Verification is client-side and opt-in."," A store cannot be made to prove faithful service;\nthe head only lets a client that opted in ",[1092,241097,241098],{},"notice",". In a relay or multi-writer deployment, the\nparty that must enable it is the party the relay does not control.",[1114,241101,241102,1324,241105,241107],{},[950,241103,241104],{},"Keyring writes are not covered.",[941,241106,6709],{}," files are not records under the head, so a\nreplayed genuine roster is outside what this detects.",[1114,241109,241110,241113,241114,241116],{},[950,241111,241112],{},"A restore from a snapshot starts with no expectations"," and reports ",[941,241115,240590],{}," until the\nhead reaccumulates — correct, not a fault.",[2645,241118,30448],{},{"title":1037,"searchDepth":1038,"depth":1038,"links":241120},[241121,241122,241123,241124,241125,241128,241129],{"id":29856,"depth":1041,"text":29857},{"id":240542,"depth":1041,"text":240543},{"id":70092,"depth":1041,"text":70093},{"id":240603,"depth":1041,"text":240604},{"id":240819,"depth":1041,"text":240820,"children":241126},[241127],{"id":240911,"depth":1038,"text":240912},{"id":240975,"depth":1041,"text":240976},{"id":68209,"depth":1041,"text":68210},"An authenticated {id → version} manifest that detects a store withholding or rewinding records — the one attack per-envelope authentication cannot see.",{},{"title":892,"description":241130},"6WNPm4-QCoLh0nywRE_g9HRZnZtMTjnami36bY9Vl1g",{"id":241135,"title":896,"access":12,"apiToc":929,"body":241136,"category":1042,"description":242540,"extension":1044,"meta":242541,"navigation":1042,"nest":2635,"path":897,"seo":242542,"sinceVersion":111464,"status":38550,"stem":898,"subtitle":242543,"tier":1050,"tocExternal":1042,"__hash__":242544},"docs\u002Fservices\u002Fvia.md",{"type":931,"value":241137,"toc":242527},[241138,241140,241143,241145,241155,241184,241194,241202,241217,241619,241625,241786,241798,241802,241828,241989,242001,242005,242147,242165,242169,242172,242178,242181,242185,242215,242268,242272,242302,242325,242337,242373,242378,242380,242487,242489,242524],[934,241139,896],{"id":896},[46871,241141],{":rows":241142},"[{\"label\":\"Subpath\",\"value\":\"`@noy-db\u002Fhub` (root export — `via`, the feature factories, and the posture errors)\"},{\"label\":\"Wiring\",\"value\":\"no `with*()` strategy — declare `viaFields: { field: via(...) }` on `vault.collection()`\"},{\"label\":\"Kind\",\"value\":\"core feature — the kernel's unified field-feature declaration surface\"}]",[1022,241144,29857],{"id":29856},[937,241146,241147,241148,241150,241151,241154],{},"Every capability a ",[950,241149,25197],{}," can have in noy-db — exact-decimal money, translated text, a\nreference to a lookup dimension, a computed value, a sealed classified value, an externalized\nblob — is a ",[950,241152,241153],{},"via-feature",": a declaration on the field that plugs behavior into one unified,\nkernel-run pipeline. You start with plain JSON and adopt one feature at a time; each feature is a\nseparately tree-shaken chunk, so anything you don't declare doesn't exist in your bundle.",[937,241156,241157,241158,241160,241161,241163,241164,953,241166,241168,241169,241171,241172,241174,241175,241177,241178,241180,241181,241183],{},"The name is grammar: family packages read as two-letter prepositions (data goes ",[1092,241159,4879],{}," a store,\nunlock ",[1092,241162,4883],{}," a method), vault services read as ",[1092,241165,5801],{},[941,241167,4676],{}," — the vault comes ",[1092,241170,5801],{},"\nsync), and field features read as ",[1092,241173,896],{}," — the value passes ",[1092,241176,896],{}," the formula, ",[1092,241179,896],{}," the seal,\n",[1092,241182,896],{}," the dictionary. Same product, three grains: package, vault, field.",[937,241185,241186,241187,241189,241190,241193],{},"Underneath, every feature's promises are enforced rather than documented: each binding declares a\n",[950,241188,117217],{},", the kernel refuses queries and redacts exports that would violate it, and a\nper-vault ",[950,241191,241192],{},"dependency graph"," makes a derived field inherit the strictest posture of its sources\n— so a stale rollup, a dangling reference, or a leaked derivative of a sealed value is a\nkernel-refused state, not a bug found in production.",[1022,241195,241197,241198,997,241200],{"id":241196},"declaring-features-via-and-viafields","Declaring features — ",[941,241199,3040],{},[941,241201,5885],{},[937,241203,241204,2993,241206,241209,241210,241212,241213,241216],{},[941,241205,5885],{},[950,241207,241208],{},"sibling"," collection option next to ",[941,241211,23934],{}," — never a key inside it — holding\none ",[941,241214,241215],{},"via(...)"," per declared field:",[2217,241218,241220],{"className":2219,"code":241219,"language":2221,"meta":1037,"style":1037},"import { createNoydb, via, money, i18nText } from '@noy-db\u002Fhub'\nimport { toMemory } from '@noy-db\u002Fto-memory'\nimport { z } from 'zod'\n\nconst db = await createNoydb({ store: toMemory(), user: 'alice', secret: 'a-strong-secret' })\nconst vault = await db.openVault('books')\n\nconst invoices = vault.collection('invoices', {\n  schema: z.object({\n    id: z.string(),\n    total: z.union([z.number(), z.string()]),\n    note: z.record(z.string(), z.string()),\n  }),\n  viaFields: {\n    total: via(money({ currency: 'EUR', scale: 2 })),\n    note: via(i18nText({ languages: ['en', 'th'], required: 'all' })),\n  },\n})\n",[941,241221,241222,241252,241270,241288,241292,241344,241370,241374,241400,241416,241432,241464,241497,241505,241513,241553,241609,241613],{"__ignoreMap":1037},[2225,241223,241224,241226,241228,241230,241232,241234,241236,241238,241240,241242,241244,241246,241248,241250],{"class":2227,"line":2228},[2225,241225,2232],{"class":2231},[2225,241227,2236],{"class":2235},[2225,241229,2744],{"class":2239},[2225,241231,2298],{"class":2235},[2225,241233,122163],{"class":2239},[2225,241235,2298],{"class":2235},[2225,241237,123521],{"class":2239},[2225,241239,2298],{"class":2235},[2225,241241,58819],{"class":2239},[2225,241243,2243],{"class":2235},[2225,241245,2246],{"class":2231},[2225,241247,2249],{"class":2235},[2225,241249,1086],{"class":2252},[2225,241251,2256],{"class":2235},[2225,241253,241254,241256,241258,241260,241262,241264,241266,241268],{"class":2227,"line":1041},[2225,241255,2232],{"class":2231},[2225,241257,2236],{"class":2235},[2225,241259,68576],{"class":2239},[2225,241261,2243],{"class":2235},[2225,241263,2246],{"class":2231},[2225,241265,2249],{"class":2235},[2225,241267,9638],{"class":2252},[2225,241269,2256],{"class":2235},[2225,241271,241272,241274,241276,241278,241280,241282,241284,241286],{"class":2227,"line":1038},[2225,241273,2232],{"class":2231},[2225,241275,2236],{"class":2235},[2225,241277,28314],{"class":2239},[2225,241279,2243],{"class":2235},[2225,241281,2246],{"class":2231},[2225,241283,2249],{"class":2235},[2225,241285,28323],{"class":2252},[2225,241287,2256],{"class":2235},[2225,241289,241290],{"class":2227,"line":2288},[2225,241291,2285],{"emptyLinePlaceholder":2284},[2225,241293,241294,241296,241298,241300,241302,241304,241306,241308,241310,241312,241314,241316,241318,241320,241322,241324,241326,241328,241330,241332,241334,241336,241338,241340,241342],{"class":2227,"line":2304},[2225,241295,2775],{"class":2774},[2225,241297,2778],{"class":2239},[2225,241299,2781],{"class":2235},[2225,241301,2784],{"class":2231},[2225,241303,2744],{"class":2291},[2225,241305,2789],{"class":2239},[2225,241307,2792],{"class":2235},[2225,241309,7564],{"class":2307},[2225,241311,2311],{"class":2235},[2225,241313,68576],{"class":2291},[2225,241315,4228],{"class":2239},[2225,241317,2298],{"class":2235},[2225,241319,2795],{"class":2307},[2225,241321,2311],{"class":2235},[2225,241323,2249],{"class":2235},[2225,241325,6408],{"class":2252},[2225,241327,2805],{"class":2235},[2225,241329,2298],{"class":2235},[2225,241331,2810],{"class":2307},[2225,241333,2311],{"class":2235},[2225,241335,2249],{"class":2235},[2225,241337,6421],{"class":2252},[2225,241339,2805],{"class":2235},[2225,241341,2243],{"class":2235},[2225,241343,2362],{"class":2239},[2225,241345,241346,241348,241350,241352,241354,241356,241358,241360,241362,241364,241366,241368],{"class":2227,"line":2356},[2225,241347,2775],{"class":2774},[2225,241349,2835],{"class":2239},[2225,241351,2781],{"class":2235},[2225,241353,2784],{"class":2231},[2225,241355,2842],{"class":2239},[2225,241357,1520],{"class":2235},[2225,241359,2847],{"class":2291},[2225,241361,2789],{"class":2239},[2225,241363,2805],{"class":2235},[2225,241365,65761],{"class":2252},[2225,241367,2805],{"class":2235},[2225,241369,2362],{"class":2239},[2225,241371,241372],{"class":2227,"line":2830},[2225,241373,2285],{"emptyLinePlaceholder":2284},[2225,241375,241376,241378,241380,241382,241384,241386,241388,241390,241392,241394,241396,241398],{"class":2227,"line":2861},[2225,241377,2775],{"class":2774},[2225,241379,6461],{"class":2239},[2225,241381,2781],{"class":2235},[2225,241383,6466],{"class":2239},[2225,241385,1520],{"class":2235},[2225,241387,6471],{"class":2291},[2225,241389,2789],{"class":2239},[2225,241391,2805],{"class":2235},[2225,241393,6487],{"class":2252},[2225,241395,2805],{"class":2235},[2225,241397,2298],{"class":2235},[2225,241399,2301],{"class":2235},[2225,241401,241402,241404,241406,241408,241410,241412,241414],{"class":2227,"line":4246},[2225,241403,28618],{"class":2307},[2225,241405,2311],{"class":2235},[2225,241407,28314],{"class":2239},[2225,241409,1520],{"class":2235},[2225,241411,25505],{"class":2291},[2225,241413,2789],{"class":2239},[2225,241415,4200],{"class":2235},[2225,241417,241418,241420,241422,241424,241426,241428,241430],{"class":2227,"line":4253},[2225,241419,60558],{"class":2307},[2225,241421,2311],{"class":2235},[2225,241423,28314],{"class":2239},[2225,241425,1520],{"class":2235},[2225,241427,7259],{"class":2291},[2225,241429,4228],{"class":2239},[2225,241431,1707],{"class":2235},[2225,241433,241434,241436,241438,241440,241442,241444,241446,241448,241450,241452,241454,241456,241458,241460,241462],{"class":2227,"line":4259},[2225,241435,64102],{"class":2307},[2225,241437,2311],{"class":2235},[2225,241439,28314],{"class":2239},[2225,241441,1520],{"class":2235},[2225,241443,123641],{"class":2291},[2225,241445,123644],{"class":2239},[2225,241447,1520],{"class":2235},[2225,241449,20636],{"class":2291},[2225,241451,4228],{"class":2239},[2225,241453,2298],{"class":2235},[2225,241455,28314],{"class":2239},[2225,241457,1520],{"class":2235},[2225,241459,7259],{"class":2291},[2225,241461,123661],{"class":2239},[2225,241463,1707],{"class":2235},[2225,241465,241466,241469,241471,241473,241475,241477,241479,241481,241483,241485,241487,241489,241491,241493,241495],{"class":2227,"line":6548},[2225,241467,241468],{"class":2307},"    note",[2225,241470,2311],{"class":2235},[2225,241472,28314],{"class":2239},[2225,241474,1520],{"class":2235},[2225,241476,24158],{"class":2291},[2225,241478,193541],{"class":2239},[2225,241480,1520],{"class":2235},[2225,241482,7259],{"class":2291},[2225,241484,4228],{"class":2239},[2225,241486,2298],{"class":2235},[2225,241488,28314],{"class":2239},[2225,241490,1520],{"class":2235},[2225,241492,7259],{"class":2291},[2225,241494,145147],{"class":2239},[2225,241496,1707],{"class":2235},[2225,241498,241499,241501,241503],{"class":2227,"line":6576},[2225,241500,19143],{"class":2235},[2225,241502,1381],{"class":2239},[2225,241504,1707],{"class":2235},[2225,241506,241507,241509,241511],{"class":2227,"line":6582},[2225,241508,122240],{"class":2307},[2225,241510,2311],{"class":2235},[2225,241512,2301],{"class":2235},[2225,241514,241515,241517,241519,241521,241523,241525,241527,241529,241531,241533,241535,241537,241539,241541,241543,241545,241547,241549,241551],{"class":2227,"line":6587},[2225,241516,64102],{"class":2307},[2225,241518,2311],{"class":2235},[2225,241520,122163],{"class":2291},[2225,241522,2789],{"class":2239},[2225,241524,760],{"class":2291},[2225,241526,2789],{"class":2239},[2225,241528,2792],{"class":2235},[2225,241530,122489],{"class":2307},[2225,241532,2311],{"class":2235},[2225,241534,2249],{"class":2235},[2225,241536,122902],{"class":2252},[2225,241538,2805],{"class":2235},[2225,241540,2298],{"class":2235},[2225,241542,122909],{"class":2307},[2225,241544,2311],{"class":2235},[2225,241546,18961],{"class":6527},[2225,241548,2243],{"class":2235},[2225,241550,8454],{"class":2239},[2225,241552,1707],{"class":2235},[2225,241554,241555,241557,241559,241561,241563,241565,241567,241569,241571,241573,241575,241577,241579,241581,241583,241585,241587,241589,241591,241593,241595,241597,241599,241601,241603,241605,241607],{"class":2227,"line":7174},[2225,241556,241468],{"class":2307},[2225,241558,2311],{"class":2235},[2225,241560,122163],{"class":2291},[2225,241562,2789],{"class":2239},[2225,241564,20943],{"class":2291},[2225,241566,2789],{"class":2239},[2225,241568,2792],{"class":2235},[2225,241570,59325],{"class":2307},[2225,241572,2311],{"class":2235},[2225,241574,8667],{"class":2239},[2225,241576,2805],{"class":2235},[2225,241578,59334],{"class":2252},[2225,241580,2805],{"class":2235},[2225,241582,2298],{"class":2235},[2225,241584,2249],{"class":2235},[2225,241586,1347],{"class":2252},[2225,241588,2805],{"class":2235},[2225,241590,8803],{"class":2239},[2225,241592,2298],{"class":2235},[2225,241594,8299],{"class":2307},[2225,241596,2311],{"class":2235},[2225,241598,2249],{"class":2235},[2225,241600,59366],{"class":2252},[2225,241602,2805],{"class":2235},[2225,241604,2243],{"class":2235},[2225,241606,8454],{"class":2239},[2225,241608,1707],{"class":2235},[2225,241610,241611],{"class":2227,"line":7180},[2225,241612,19017],{"class":2235},[2225,241614,241615,241617],{"class":2227,"line":7220},[2225,241616,2359],{"class":2235},[2225,241618,2362],{"class":2239},[937,241620,241621,241622,241624],{},"A field can stack more than one feature in a single ",[941,241623,241215],{}," call — for example a computed\nmoney total:",[2217,241626,241629],{"className":2219,"code":241627,"language":2221,"meta":241628,"style":1037},"\u002F\u002F A fragment: `viaFields` is shown out of its `collection(...)` context, so it\n\u002F\u002F is deliberately not standalone TypeScript. Marked no-check so it does not\n\u002F\u002F suppress semantic analysis for every other snippet — see registry\u002Fvalidate-snippets.mjs.\nimport { computed } from '@noy-db\u002Fhub'\n\nviaFields: {\n  total: via(\n    computed((r) => (r.subtotal as number) * 1.07, { deps: ['subtotal'] }),\n    money({ currency: 'EUR', scale: 2 }),\n  ),\n}\n","no-check",[941,241630,241631,241636,241641,241646,241664,241668,241676,241686,241744,241776,241782],{"__ignoreMap":1037},[2225,241632,241633],{"class":2227,"line":2228},[2225,241634,241635],{"class":2763},"\u002F\u002F A fragment: `viaFields` is shown out of its `collection(...)` context, so it\n",[2225,241637,241638],{"class":2227,"line":1041},[2225,241639,241640],{"class":2763},"\u002F\u002F is deliberately not standalone TypeScript. Marked no-check so it does not\n",[2225,241642,241643],{"class":2227,"line":1038},[2225,241644,241645],{"class":2763},"\u002F\u002F suppress semantic analysis for every other snippet — see registry\u002Fvalidate-snippets.mjs.\n",[2225,241647,241648,241650,241652,241654,241656,241658,241660,241662],{"class":2227,"line":2288},[2225,241649,2232],{"class":2231},[2225,241651,2236],{"class":2235},[2225,241653,122168],{"class":2239},[2225,241655,2243],{"class":2235},[2225,241657,2246],{"class":2231},[2225,241659,2249],{"class":2235},[2225,241661,1086],{"class":2252},[2225,241663,2256],{"class":2235},[2225,241665,241666],{"class":2227,"line":2304},[2225,241667,2285],{"emptyLinePlaceholder":2284},[2225,241669,241670,241672,241674],{"class":2227,"line":2356},[2225,241671,5885],{"class":6342},[2225,241673,2311],{"class":2235},[2225,241675,2301],{"class":2235},[2225,241677,241678,241680,241682,241684],{"class":2227,"line":2830},[2225,241679,121085],{"class":6342},[2225,241681,2311],{"class":2235},[2225,241683,122163],{"class":2291},[2225,241685,11683],{"class":2307},[2225,241687,241688,241690,241692,241694,241696,241698,241700,241702,241704,241706,241709,241711,241713,241715,241717,241720,241722,241724,241726,241728,241730,241732,241734,241736,241738,241740,241742],{"class":2227,"line":2861},[2225,241689,122587],{"class":2291},[2225,241691,2789],{"class":2307},[2225,241693,2789],{"class":2235},[2225,241695,13217],{"class":7052},[2225,241697,1381],{"class":2235},[2225,241699,8116],{"class":2774},[2225,241701,953],{"class":2307},[2225,241703,13217],{"class":2239},[2225,241705,1520],{"class":2235},[2225,241707,241708],{"class":2239},"subtotal",[2225,241710,2266],{"class":2231},[2225,241712,6356],{"class":6342},[2225,241714,8495],{"class":2307},[2225,241716,121345],{"class":2235},[2225,241718,241719],{"class":6527}," 1.07",[2225,241721,2298],{"class":2235},[2225,241723,2236],{"class":2235},[2225,241725,122290],{"class":2307},[2225,241727,2311],{"class":2235},[2225,241729,8667],{"class":2307},[2225,241731,2805],{"class":2235},[2225,241733,241708],{"class":2252},[2225,241735,2805],{"class":2235},[2225,241737,8890],{"class":2307},[2225,241739,2359],{"class":2235},[2225,241741,1381],{"class":2307},[2225,241743,1707],{"class":2235},[2225,241745,241746,241748,241750,241752,241754,241756,241758,241760,241762,241764,241766,241768,241770,241772,241774],{"class":2227,"line":4246},[2225,241747,122562],{"class":2291},[2225,241749,2789],{"class":2307},[2225,241751,2792],{"class":2235},[2225,241753,122489],{"class":2307},[2225,241755,2311],{"class":2235},[2225,241757,2249],{"class":2235},[2225,241759,122902],{"class":2252},[2225,241761,2805],{"class":2235},[2225,241763,2298],{"class":2235},[2225,241765,122909],{"class":2307},[2225,241767,2311],{"class":2235},[2225,241769,18961],{"class":6527},[2225,241771,2243],{"class":2235},[2225,241773,1381],{"class":2307},[2225,241775,1707],{"class":2235},[2225,241777,241778,241780],{"class":2227,"line":4253},[2225,241779,122699],{"class":2307},[2225,241781,1707],{"class":2235},[2225,241783,241784],{"class":2227,"line":4259},[2225,241785,7351],{"class":2235},[937,241787,241788,241789,241791,241792,241794,241795,241797],{},"The stack order features ",[1092,241790,61526],{}," in is deterministic and kernel-pinned (money → i18n → classified →\nblob → computed — ",[941,241793,637],{}," always compiles last, so its formula can read other fields'\nalready-decoded values); see ",[1007,241796,637],{"href":638}," for what that ordering means when\ncomputed stacks with money on the same field.",[1591,241799,241801],{"id":241800},"sugar-equivalence-the-existing-spellings-are-the-same-thing","Sugar equivalence — the existing spellings are the same thing",[937,241803,241804,241805,968,241807,968,241809,968,241811,1707,241813,241815,241816,12582,241818,241821,241822,241824,241825,241827],{},"The feature-specific sugar keys — ",[941,241806,22765],{},[941,241808,22756],{},[941,241810,22759],{},[941,241812,22762],{},[941,241814,637],{}," — compile to the same internal bindings as ",[941,241817,5885],{},[950,241819,241820],{},"byte-identical"," stored\nenvelopes and identical ",[941,241823,3926],{}," output. Existing code keeps working unchanged; ",[941,241826,3040],{}," is\nthe canonical composable spelling, not a replacement:",[2217,241829,241831],{"className":2219,"code":241830,"language":2221,"meta":1037,"style":1037},"\u002F\u002F Sugar spelling:\nvault.collection('invoices', { schema, moneyFields: { total: money({ currency: 'EUR', scale: 2 }) } })\n\n\u002F\u002F via() spelling — identical internals, envelopes, and introspection:\nvault.collection('invoices', { schema, viaFields: { total: via(money({ currency: 'EUR', scale: 2 })) } })\n",[941,241832,241833,241838,241907,241911,241916],{"__ignoreMap":1037},[2225,241834,241835],{"class":2227,"line":2228},[2225,241836,241837],{"class":2763},"\u002F\u002F Sugar spelling:\n",[2225,241839,241840,241842,241844,241846,241848,241850,241852,241854,241856,241858,241860,241862,241865,241867,241869,241871,241873,241875,241877,241879,241881,241883,241885,241887,241889,241891,241893,241895,241897,241899,241901,241903,241905],{"class":2227,"line":1041},[2225,241841,2694],{"class":2239},[2225,241843,1520],{"class":2235},[2225,241845,6471],{"class":2291},[2225,241847,2789],{"class":2239},[2225,241849,2805],{"class":2235},[2225,241851,6487],{"class":2252},[2225,241853,2805],{"class":2235},[2225,241855,2298],{"class":2235},[2225,241857,2236],{"class":2235},[2225,241859,51997],{"class":2239},[2225,241861,2298],{"class":2235},[2225,241863,241864],{"class":2307}," moneyFields",[2225,241866,2311],{"class":2235},[2225,241868,2236],{"class":2235},[2225,241870,27452],{"class":2307},[2225,241872,2311],{"class":2235},[2225,241874,123521],{"class":2291},[2225,241876,2789],{"class":2239},[2225,241878,2792],{"class":2235},[2225,241880,122489],{"class":2307},[2225,241882,2311],{"class":2235},[2225,241884,2249],{"class":2235},[2225,241886,122902],{"class":2252},[2225,241888,2805],{"class":2235},[2225,241890,2298],{"class":2235},[2225,241892,122909],{"class":2307},[2225,241894,2311],{"class":2235},[2225,241896,18961],{"class":6527},[2225,241898,2243],{"class":2235},[2225,241900,8495],{"class":2239},[2225,241902,2359],{"class":2235},[2225,241904,2243],{"class":2235},[2225,241906,2362],{"class":2239},[2225,241908,241909],{"class":2227,"line":1038},[2225,241910,2285],{"emptyLinePlaceholder":2284},[2225,241912,241913],{"class":2227,"line":2288},[2225,241914,241915],{"class":2763},"\u002F\u002F via() spelling — identical internals, envelopes, and introspection:\n",[2225,241917,241918,241920,241922,241924,241926,241928,241930,241932,241934,241936,241938,241940,241943,241945,241947,241949,241951,241953,241955,241957,241959,241961,241963,241965,241967,241969,241971,241973,241975,241977,241979,241981,241983,241985,241987],{"class":2227,"line":2304},[2225,241919,2694],{"class":2239},[2225,241921,1520],{"class":2235},[2225,241923,6471],{"class":2291},[2225,241925,2789],{"class":2239},[2225,241927,2805],{"class":2235},[2225,241929,6487],{"class":2252},[2225,241931,2805],{"class":2235},[2225,241933,2298],{"class":2235},[2225,241935,2236],{"class":2235},[2225,241937,51997],{"class":2239},[2225,241939,2298],{"class":2235},[2225,241941,241942],{"class":2307}," viaFields",[2225,241944,2311],{"class":2235},[2225,241946,2236],{"class":2235},[2225,241948,27452],{"class":2307},[2225,241950,2311],{"class":2235},[2225,241952,122163],{"class":2291},[2225,241954,2789],{"class":2239},[2225,241956,760],{"class":2291},[2225,241958,2789],{"class":2239},[2225,241960,2792],{"class":2235},[2225,241962,122489],{"class":2307},[2225,241964,2311],{"class":2235},[2225,241966,2249],{"class":2235},[2225,241968,122902],{"class":2252},[2225,241970,2805],{"class":2235},[2225,241972,2298],{"class":2235},[2225,241974,122909],{"class":2307},[2225,241976,2311],{"class":2235},[2225,241978,18961],{"class":6527},[2225,241980,2243],{"class":2235},[2225,241982,7620],{"class":2239},[2225,241984,2359],{"class":2235},[2225,241986,2243],{"class":2235},[2225,241988,2362],{"class":2239},[937,241990,241991,241992,241995,241996,6789,241998,242000],{},"Declaring the ",[950,241993,241994],{},"same field"," under both a sugar key and ",[941,241997,5885],{},[941,241999,12881],{},"\nnaming the field — one declaration site per field.",[1022,242002,242004],{"id":242003},"the-feature-catalog","The feature catalog",[1338,242006,242007,242022],{},[1341,242008,242009],{},[1344,242010,242011,242014,242017,242020],{},[1347,242012,242013],{},"Feature",[1347,242015,242016],{},"Factories",[1347,242018,242019],{},"Declared via",[1347,242021,5721],{},[1357,242023,242024,242043,242068,242092,242113,242132],{},[1344,242025,242026,242029,242033,242039],{},[1362,242027,242028],{},"Money",[1362,242030,242031],{},[941,242032,183119],{},[1362,242034,242035,4688,242037],{},[941,242036,241215],{},[941,242038,22765],{},[1362,242040,242041],{},[1007,242042,760],{"href":761},[1344,242044,242045,242048,242056,242064],{},[1362,242046,242047],{},"I18n text + dict codes",[1362,242049,242050,968,242052,968,242054],{},[941,242051,58358],{},[941,242053,58348],{},[941,242055,163843],{},[1362,242057,242058,4688,242060,1261,242062],{},[941,242059,241215],{},[941,242061,22756],{},[941,242063,22759],{},[1362,242065,242066],{},[1007,242067,701],{"href":702},[1344,242069,242070,242072,242082,242088],{},[1362,242071,152810],{},[1362,242073,242074,968,242076,1261,242078,968,242080],{},[941,242075,163865],{},[941,242077,163868],{},[941,242079,28421],{},[941,242081,163871],{},[1362,242083,242084,4688,242086],{},[941,242085,241215],{},[941,242087,22762],{},[1362,242089,242090],{},[1007,242091,731],{"href":732},[1344,242093,242094,242097,242102,242109],{},[1362,242095,242096],{},"Computed values",[1362,242098,242099],{},[941,242100,242101],{},"computed(fn, { deps, mode })",[1362,242103,242104,242106,242107,179705],{},[941,242105,241215],{}," or the ",[941,242108,637],{},[1362,242110,242111],{},[1007,242112,637],{"href":638},[1344,242114,242115,242118,242123,242128],{},[1362,242116,242117],{},"Classified (sealed) fields",[1362,242119,3742,242120,242122],{},[941,242121,119797],{}," presets",[1362,242124,242125,242127],{},[941,242126,22770],{}," (sugar only — see below)",[1362,242129,242130],{},[1007,242131,633],{"href":634},[1344,242133,242134,242136,242139,242143],{},[1362,242135,72479],{},[1362,242137,242138],{},"slot policies",[1362,242140,242141,242127],{},[941,242142,22785],{},[1362,242144,242145],{},[1007,242146,621],{"href":622},[937,242148,242149,242150,1261,242152,242154,242155,242157,242158,2601,242161,242164],{},"Classified fields and blobs are full via-features internally — they carry a binding, a posture,\nand an erasure hook like every other feature — but they are declared through their own sugar keys\n(",[941,242151,22770],{},[941,242153,22785],{},"), not through the ",[941,242156,3040],{}," composer: ",[941,242159,242160],{},"via(classified())",[941,242162,242163],{},"via(blob())"," spellings do not exist yet.",[1022,242166,242168],{"id":242167},"the-phased-pipeline","The phased pipeline",[937,242170,242171],{},"All field features run in a kernel-orchestrated phased pipeline, so cross-feature ordering is\npinned in one place rather than hand-wired per feature:",[2217,242173,242176],{"className":242174,"code":242175,"language":5672},[5670],"WRITE:  derive (computed) → normalize (money\u002Fi18n\u002Flookup) → validate (kernel) → encode (classified) → store\nREAD:   load → decode (classified) → present (money\u002Fi18n\u002Flookup\u002Fcomputed-virtual)\n",[941,242177,242175],{"__ignoreMap":1037},[937,242179,242180],{},"Fields with no via declaration skip the pipeline entirely — an identity fast path, so plain\ndocuments pay nothing.",[1022,242182,242184],{"id":242183},"posture-a-features-security-contract-enforced","Posture — a feature's security contract, enforced",[937,242186,242187,242188,242191,242192,1617,242195,242198,242199,242201,242202,953,242205,1884,242208,242211,242212,242214],{},"Every binding declares a ",[941,242189,242190],{},"ViaPosture"," across four axes: ",[941,242193,242194],{},"encryptedAtRest",[941,242196,242197],{},"'envelope'"," — the normal record encryption — or ",[941,242200,116601],{}," — its own slot under per-record key\nmaterial), ",[941,242203,242204],{},"queryable",[941,242206,242207],{},"'none' | 'det-exact' | 'ordered' | 'full'",[941,242209,242210],{},"exportable"," (boolean), and\n",[941,242213,119328],{}," (boolean). The kernel consults posture generically — no per-feature special cases:",[1111,242216,242217,242235,242251],{},[1114,242218,242219,242221,242222,242224,242225,242227,242228,242230,242231,242234],{},[950,242220,46896],{}," — the query DSL refuses a ",[941,242223,111468],{}," field with ",[941,242226,111478],{},"\n(e.g. a ",[941,242229,22785],{}," slot, or a virtual computed field); a classified field keeps its\ndeterministic exact-match behavior (",[941,242232,242233],{},"det-exact",") unchanged.",[1114,242236,242237,964,242239,1261,242241,242243,242244,242247,242248,242250],{},[950,242238,72632],{},[941,242240,120667],{},[941,242242,23185],{}," redact an ",[941,242245,242246],{},"exportable: false"," field to the\nliteral string ",[941,242249,120673],{}," on the record itself, before any exporter sees it.",[1114,242252,242253,964,242255,242259,242260,242262,242263,2601,242265,242267],{},[950,242254,146655],{},[1007,242256,242257],{"href":686},[941,242258,2546],{}," consults ",[941,242261,119328],{}," and folds each\nsealed-posture binding's erasure hook into its report (the ",[941,242264,119334],{},[941,242266,119338],{}," accounting).",[1022,242269,242271],{"id":242270},"the-dependency-graph-derived-fields-inherit-the-strictest-source-posture","The dependency graph — derived fields inherit the strictest source posture",[937,242273,242274,242275,242277,242278,2462,242280,242282,242283,242286,242287,242289,242290,242292,242293,5502,242296,242298,242299,242301],{},"A per-vault dependency graph connects every ",[1007,242276,162301],{"href":666},", rollup,\n",[1007,242279,134318],{"href":740},[1007,242281,637],{"href":638}," field to the\nsources it reads. Its ",[950,242284,242285],{},"taint algebra"," folds two postures to the strictest per-axis result —\n",[941,242288,242194],{},": sealed wins; ",[941,242291,242204],{},": the least-capable rung of\n",[941,242294,242295],{},"none \u003C det-exact \u003C ordered \u003C full",[941,242297,242210],{},": logical AND; ",[941,242300,119328],{},": logical OR:",[2217,242303,242305],{"className":2219,"code":242304,"language":2221,"meta":1037,"style":1037},"foldPosture(CLASSIFIED, MONEY)\n\u002F\u002F → { encryptedAtRest: 'sealed', queryable: 'det-exact', exportable: false, forgettable: true }\n",[941,242306,242307,242320],{"__ignoreMap":1037},[2225,242308,242309,242312,242315,242317],{"class":2227,"line":2228},[2225,242310,242311],{"class":2291},"foldPosture",[2225,242313,242314],{"class":2239},"(CLASSIFIED",[2225,242316,2298],{"class":2235},[2225,242318,242319],{"class":2239}," MONEY)\n",[2225,242321,242322],{"class":2227,"line":1041},[2225,242323,242324],{"class":2763},"\u002F\u002F → { encryptedAtRest: 'sealed', queryable: 'det-exact', exportable: false, forgettable: true }\n",[937,242326,242327,242328,242330,242331,242333,242334,242336],{},"The fold is load-bearing: a computed field whose ",[941,242329,117230],{}," include a classified source is actually\nsealed at rest (materialized mode) or redacted on every read (virtual mode) — it can no longer\nsilently copy sealed plaintext into an ordinary field. The same fold reaches whole-record formula\noutputs: a ",[1007,242332,162301],{"href":666},", rollup, or\n",[1007,242335,134318],{"href":740}," that reads a classified-bearing source now seals\nits output field(s) at rest, refuses them to queries, and redacts them from exports by default — a\ncompute function can no longer land a copied classified plaintext in the open. The graph also\npowers three cross-cutting behaviors:",[1111,242338,242339,242350,242363],{},[1114,242340,242341,242344,242345,1625,242347,242349],{},[950,242342,242343],{},"Sync dispatch"," — writes applied by ",[941,242346,56862],{},[941,242348,56859],{},", schema cutover, or restore dispatch\nderivations in a batched, per-target-deduped wave (N synced children of one rollup parent\nrecompute the parent once, not N times); a collection with no dependents is skipped at zero\ndecrypt cost.",[1114,242351,242352,242355,242356,242358,242359,242362],{},[950,242353,242354],{},"Frozen-output rule"," — a derivation output landing in a period closed by\n",[1007,242357,776],{"href":777}," is skipped (the historical output stands) and audited via the\n",[941,242360,242361],{},"derivation:skipped-frozen"," event, instead of failing the legal source write that triggered it.",[1114,242364,242365,964,242368,242372],{},[950,242366,242367],{},"Forget fan-out",[1007,242369,242370],{"href":686},[941,242371,2546],{}," erases record-grain derived copies and\nrecomputes aggregate-grain rollups without the forgotten contribution.",[937,242374,46939,242375,242377],{},[1007,242376,637],{"href":638}," for the worked taint examples, the declare-time dependency\nguard and its limits, and the virtual\u002Fmaterialized mode semantics.",[1022,242379,68210],{"id":68209},[1111,242381,242382,242393,242415,242421,242457],{},[1114,242383,242384,242387,242388,242390,242391,1520],{},[950,242385,242386],{},"One declaration site per field"," — the same field under both a sugar key and ",[941,242389,5885],{},"\nthrows ",[941,242392,12881],{},[1114,242394,242395,242402,242403,1261,242405,242407,242408,1625,242410,1625,242412,242414],{},[950,242396,242397,1261,242399,242401],{},[941,242398,242160],{},[941,242400,242163],{}," don't exist"," — classified fields and blobs are declared\nthrough ",[941,242404,22770],{},[941,242406,22785],{}," only; the composer covers money, i18n\n(",[941,242409,20943],{},[941,242411,21456],{},[941,242413,162565],{},"), lookup, and computed.",[1114,242416,242417,242420],{},[950,242418,242419],{},"Posture inheritance is a behavior change"," for pre-existing configurations that computed a\nvalue from — or ran a derivation, rollup, or materialized view over — a classified field: what\nused to read back (and export, and query) as plaintext is now sealed or redacted. This is a\ndeliberate security fix, shipped pre-1.0.",[1114,242422,242423,242426,242427,242431,242432,997,242435,242437,242438,242441,242442,1261,242444,242446,242447,242449,242450,968,242452,2462,242454,242456],{},[950,242424,242425],{},"Scoped forget purge is opt-in."," By default, ",[1007,242428,242429],{"href":686},[941,242430,2546],{},"'s vault-level\nsealed-key and blob purges run unconditionally across every collection — a field declaration is a\nnecessary-but-not-sufficient signal, since ",[941,242433,242434],{},"sealRecordToHost()",[941,242436,108814],{}," work on\ncollections that never declared a classified or blob field. Passing\n",[941,242439,242440],{},"withForget({ subjects, scopedPurge: true })"," instead gates both purges on whether the\ncollection declared ",[941,242443,22770],{},[941,242445,22785],{},", and reports every skipped collection\nthrough ",[941,242448,108847],{}," rather than narrowing the erasure promise silently. See\n",[1007,242451,685],{"href":686},[1007,242453,633],{"href":634},[1007,242455,621],{"href":622}," for\nthe full semantics.",[1114,242458,242459,242462,242463,242465,242466,1261,242468,1261,242470,242472,242473,134813,242475,242477,242478,242480,242481,242483,242484,242486],{},[950,242460,242461],{},"Export bytes changed for redacted fields",": the default (non-",[941,242464,120660],{},"-option) export of a\nclassified field through ",[941,242467,30472],{},[941,242469,32192],{},[941,242471,32730],{}," now emits the\nplain string ",[941,242474,120673],{},[941,242476,117966],{}," CSV cell; a ",[941,242479,5672],{}," SQL column) where it previously\nfell through to JSON-stringified output of a live sealed handle (",[941,242482,120690],{}," in a CSV\ncell; a ",[941,242485,50501],{}," column) — the new output is the intended one, and closes an accidental path\nwhere a revealable handle reached an export stream.",[1022,242488,2621],{"id":2620},[1111,242490,242491,242506,242513,242518],{},[1114,242492,242493,1368,242495,1368,242497,103376,242499,1368,242501,1368,242503,242505],{},[1007,242494,760],{"href":761},[1007,242496,701],{"href":702},[1007,242498,731],{"href":732},[1007,242500,637],{"href":638},[1007,242502,633],{"href":634},[1007,242504,621],{"href":622},"\n— the individual features",[1114,242507,242508,1368,242510,242512],{},[1007,242509,665],{"href":666},[1007,242511,739],{"href":740}," —\nthe cross-record derived data the dependency graph also covers",[1114,242514,242515,242517],{},[1007,242516,685],{"href":686}," — the erasure report the posture and graph fan-outs feed",[1114,242519,242520,1368,242522],{},[1007,242521,61743],{"href":598},[1007,242523,1019],{"href":1018},[2645,242525,242526],{},"html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sHdIc, html code.shiki .sHdIc{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#EEFFFF;--shiki-default-font-style:italic;--shiki-dark:#BABED8;--shiki-dark-font-style:italic}",{"title":1037,"searchDepth":1038,"depth":1038,"links":242528},[242529,242530,242534,242535,242536,242537,242538,242539],{"id":29856,"depth":1041,"text":29857},{"id":241196,"depth":1041,"text":242531,"children":242532},"Declaring features — via() and viaFields",[242533],{"id":241800,"depth":1038,"text":241801},{"id":242003,"depth":1041,"text":242004},{"id":242167,"depth":1041,"text":242168},{"id":242183,"depth":1041,"text":242184},{"id":242270,"depth":1041,"text":242271},{"id":68209,"depth":1041,"text":68210},{"id":2620,"depth":1041,"text":2621},"The via() field-feature composer: declare what a field can do — money, i18n, lookup, computed, classified, blob — as one stacked declaration per field.",{},{"title":896,"description":242540},"Field features — one declaration surface, one pipeline, one dependency graph","KJXhLrybYhG3aeK2UlHB8mGvH3pr2EP94tVoIOsibRk",{"id":242546,"title":900,"access":12,"apiToc":929,"body":242547,"category":1042,"description":244558,"extension":1044,"meta":244559,"navigation":1042,"nest":1047,"path":901,"seo":244560,"sinceVersion":1042,"status":1049,"stem":902,"subtitle":1042,"tier":1050,"tocExternal":1042,"__hash__":244561},"docs\u002Fservices\u002Fwithdrawal.md",{"type":931,"value":242548,"toc":244536},[242549,242551,242553,242569,242571,242591,242594,242623,242625,242643,242645,242673,242675,242700,242710,242714,242722,242728,242836,242849,242855,242899,243070,243075,243224,243236,243240,243252,243368,243474,243476,243582,243624,243632,243636,243642,243676,243739,243747,243749,243768,243841,243850,243852,243869,243875,243884,243987,243993,243999,244079,244085,244091,244252,244258,244264,244270,244273,244275,244325,244327,244338,244402,244412,244414,244465,244467,244533],[934,242550,900],{"id":900},[46871,242552],{":rows":198560},[7646,242554,242555],{"color":11278,"icon":369},[937,242556,242557,242559,242560,198571,242562,242564,242565,1520],{},[950,242558,198567],{}," Withdrawal and export are the two workflows of the single ",[941,242561,21604],{},[950,242563,900],{}," — the destructive half (export-then-dispose). For a non-destructive portable copy and the shared opt-in setup, see ",[950,242566,242567],{},[1007,242568,800],{"href":801},[1022,242570,29857],{"id":29856},[937,242572,242573,242574,242576,242577,242579,242580,242582,242583,4688,242585,242587,242588,242590],{},"Withdrawal lets a ",[950,242575,198621],{}," remove ",[950,242578,17470],{}," accessible data from a shared\n",[1007,242581,2694],{"href":198512}," — always building a re-keyed portable copy first, then disposing of\nthe source (",[941,242584,3164],{},[941,242586,56994],{},"). Like ",[1007,242589,8080],{"href":801},", it is scoped\ncryptographically: it operates only on collections whose DEKs the caller's keyring holds.",[937,242592,242593],{},"There are two workflows, by role:",[1111,242595,242596,242607],{},[1114,242597,242598,964,242601,242603,242604,242606],{},[950,242599,242600],{},"One-party",[941,242602,21616],{}," (destructive, disabled by default behind a\nfail-closed policy gate). For an ",[941,242605,9907],{}," with read-write grants who may remove their\nown slice in one step.",[1114,242608,242609,964,242612,242614,242615,242617,242618,226933,242620,242622],{},[950,242610,242611],{},"Two-party",[941,242613,21619],{}," files a durable request; an owner or admin\nreviews it with ",[941,242616,21622],{}," and settles it with ",[941,242619,21625],{},[941,242621,21628],{},". This is the path for read-only roles that cannot delete\nanything themselves.",[1022,242624,111660],{"id":111659},[1111,242626,242627,242633,242640],{},[1114,242628,198644,242629,242632],{},[950,242630,242631],{},"erasure"," (Art. 17) for the clients of a firm-run vault — a departing\nclient's records leave the vault after they take their copy.",[1114,242634,242635,242636,242639],{},"Letting a read-only client ",[1092,242637,242638],{},"request"," deletion that the firm then executes under its\nown authority, with every step recorded in the tamper-evident ledger.",[1114,242641,242642],{},"Regulated offboarding where the firm must retain a frozen, hash-pinned snapshot of the\nwithdrawn records instead of deleting them outright.",[1022,242644,70093],{"id":70092},[1111,242646,242647,242659,242670],{},[1114,242648,242649,242653,242654,242656,242657,1520],{},[950,242650,242651],{},[941,242652,83182],{}," — withdrawal is part of the same opt-in as ",[1007,242655,800],{"href":801},"; in a multi-user vault add ",[941,242658,10312],{},[1114,242660,242661,242664,242665,53922,242667,242669],{},[1092,242662,242663],{},"For the one-party (self-serve destructive) path:"," the firm must enable the policy gate described below, and the withdrawing user needs ",[941,242666,10026],{},[950,242668,2119],{}," withdrawn collection.",[1114,242671,242672],{},"The two-party path is available to any granted member with no extra configuration.",[1022,242674,99864],{"id":99863},[937,242676,242677,242678,242680,242681,242685,242686,242688,242689,242691,242692,242694,242695,242699],{},"Withdrawal is enabled by the same ",[941,242679,83182],{}," opt-in as\nexport — see ",[1007,242682,242684],{"href":242683},"\u002Fservices\u002Fportability#opt-in","portability → Opt in"," for the full\n",[941,242687,2134],{}," setup. Without it, every ",[941,242690,21609],{}," method throws\n",[941,242693,21786],{},". The multi-user scenarios below assume a vault provisioned\nwith ",[1007,242696,242697],{"href":198512},[941,242698,3197],{},", where the firm (owner) has granted the caller a keyring.",[937,242701,960,242702,242705,242706,242709],{},[950,242703,242704],{},"one-party"," path additionally requires the firm to enable a policy gate (below);\nthe ",[950,242707,242708],{},"two-party"," path is available to any granted member with no extra configuration.",[1022,242711,242713],{"id":242712},"two-party-withdrawal-request-approvereject","Two-party withdrawal (request → approve\u002Freject)",[937,242715,242716,242717,4688,242719,242721],{},"The conservative path: the requester files a durable request; a firm-side owner or\nadmin decides it. A read-only ",[941,242718,9913],{},[941,242720,9910],{}," — who can never delete records\nthemselves — uses exactly this flow.",[937,242723,242724,242727],{},[950,242725,242726],{},"Requester side"," — file the request:",[2217,242729,242731],{"className":2219,"code":242730,"language":2221,"meta":1037,"style":1037},"\u002F\u002F clientVault was opened by a read-only `client` keyring\nconst req = await clientVault.user.requestWithdrawal({\n  disposition: 'freeze',          \u002F\u002F 'delete' (default) or 'freeze'\n  legalBasis: 'gdpr-art-17',\n  expiresInMs: 14 * 24 * 3600_000, \u002F\u002F optional: request lapses after 14 days\n})\n\u002F\u002F → { requestId: 'wr-9f2c41d0be57a6e83d1f0a2b', status: 'pending',\n\u002F\u002F     expiresAt: '2026-07-22T09:00:00.000Z' }\n",[941,242732,242733,242738,242764,242782,242797,242820,242826,242831],{"__ignoreMap":1037},[2225,242734,242735],{"class":2227,"line":2228},[2225,242736,242737],{"class":2763},"\u002F\u002F clientVault was opened by a read-only `client` keyring\n",[2225,242739,242740,242742,242745,242747,242749,242752,242754,242756,242758,242760,242762],{"class":2227,"line":1041},[2225,242741,2775],{"class":2774},[2225,242743,242744],{"class":2239}," req ",[2225,242746,2781],{"class":2235},[2225,242748,2784],{"class":2231},[2225,242750,242751],{"class":2239}," clientVault",[2225,242753,1520],{"class":2235},[2225,242755,16225],{"class":2239},[2225,242757,1520],{"class":2235},[2225,242759,199190],{"class":2291},[2225,242761,2789],{"class":2239},[2225,242763,4200],{"class":2235},[2225,242765,242766,242769,242771,242773,242775,242777,242779],{"class":2227,"line":1038},[2225,242767,242768],{"class":2307},"  disposition",[2225,242770,2311],{"class":2235},[2225,242772,2249],{"class":2235},[2225,242774,56994],{"class":2252},[2225,242776,2805],{"class":2235},[2225,242778,2298],{"class":2235},[2225,242780,242781],{"class":2763},"          \u002F\u002F 'delete' (default) or 'freeze'\n",[2225,242783,242784,242786,242788,242790,242793,242795],{"class":2227,"line":2288},[2225,242785,136614],{"class":2307},[2225,242787,2311],{"class":2235},[2225,242789,2249],{"class":2235},[2225,242791,242792],{"class":2252},"gdpr-art-17",[2225,242794,2805],{"class":2235},[2225,242796,1707],{"class":2235},[2225,242798,242799,242802,242804,242806,242808,242810,242812,242815,242817],{"class":2227,"line":2304},[2225,242800,242801],{"class":2307},"  expiresInMs",[2225,242803,2311],{"class":2235},[2225,242805,45037],{"class":6527},[2225,242807,2263],{"class":2235},[2225,242809,44447],{"class":6527},[2225,242811,2263],{"class":2235},[2225,242813,242814],{"class":6527}," 3600_000",[2225,242816,2298],{"class":2235},[2225,242818,242819],{"class":2763}," \u002F\u002F optional: request lapses after 14 days\n",[2225,242821,242822,242824],{"class":2227,"line":2356},[2225,242823,2359],{"class":2235},[2225,242825,2362],{"class":2239},[2225,242827,242828],{"class":2227,"line":2830},[2225,242829,242830],{"class":2763},"\u002F\u002F → { requestId: 'wr-9f2c41d0be57a6e83d1f0a2b', status: 'pending',\n",[2225,242832,242833],{"class":2227,"line":2861},[2225,242834,242835],{"class":2763},"\u002F\u002F     expiresAt: '2026-07-22T09:00:00.000Z' }\n",[937,242837,242838,242839,242841,242842,242844,242845,242848],{},"The requester's accessible collections are recorded automatically from their grants. A\nrequester whose role can read ",[1092,242840,2119],{}," collection (e.g. a ",[941,242843,9910],{},") must name the\ncollections explicitly via ",[941,242846,242847],{},"scope.collections"," — an all-collection withdrawal request\nis rejected as too broad.",[937,242850,242851,242854],{},[950,242852,242853],{},"Owner side"," — review the queue:",[2217,242856,242858],{"className":2219,"code":242857,"language":2221,"meta":1037,"style":1037},"const pending = await ownerVault.user.listWithdrawalRequests({ status: 'pending' })\n",[941,242859,242860],{"__ignoreMap":1037},[2225,242861,242862,242864,242866,242868,242870,242873,242875,242877,242879,242881,242883,242885,242887,242889,242891,242893,242895,242897],{"class":2227,"line":2228},[2225,242863,2775],{"class":2774},[2225,242865,186338],{"class":2239},[2225,242867,2781],{"class":2235},[2225,242869,2784],{"class":2231},[2225,242871,242872],{"class":2239}," ownerVault",[2225,242874,1520],{"class":2235},[2225,242876,16225],{"class":2239},[2225,242878,1520],{"class":2235},[2225,242880,199193],{"class":2291},[2225,242882,2789],{"class":2239},[2225,242884,2792],{"class":2235},[2225,242886,6361],{"class":2307},[2225,242888,2311],{"class":2235},[2225,242890,2249],{"class":2235},[2225,242892,12192],{"class":2252},[2225,242894,2805],{"class":2235},[2225,242896,2243],{"class":2235},[2225,242898,2362],{"class":2239},[2217,242900,242902],{"className":6604,"code":242901,"language":141,"meta":1037,"style":1037},"[\n  {\n    \"requestId\": \"wr-9f2c41d0be57a6e83d1f0a2b\",\n    \"requester\": \"client1\",\n    \"collections\": [\"invoices\"],\n    \"disposition\": \"freeze\",\n    \"legalBasis\": \"gdpr-art-17\",\n    \"status\": \"pending\",\n    \"requestedAt\": \"2026-07-08T09:00:00.000Z\",\n    \"expiresAt\": \"2026-07-22T09:00:00.000Z\"\n  }\n]\n",[941,242903,242904,242908,242912,242932,242951,242971,242990,243008,243026,243045,243062,243066],{"__ignoreMap":1037},[2225,242905,242906],{"class":2227,"line":2228},[2225,242907,26349],{"class":2235},[2225,242909,242910],{"class":2227,"line":1041},[2225,242911,59879],{"class":2235},[2225,242913,242914,242916,242919,242921,242923,242925,242928,242930],{"class":2227,"line":1038},[2225,242915,14201],{"class":2235},[2225,242917,242918],{"class":2774},"requestId",[2225,242920,6620],{"class":2235},[2225,242922,2311],{"class":2235},[2225,242924,6614],{"class":2235},[2225,242926,242927],{"class":2252},"wr-9f2c41d0be57a6e83d1f0a2b",[2225,242929,6620],{"class":2235},[2225,242931,1707],{"class":2235},[2225,242933,242934,242936,242939,242941,242943,242945,242947,242949],{"class":2227,"line":2288},[2225,242935,14201],{"class":2235},[2225,242937,242938],{"class":2774},"requester",[2225,242940,6620],{"class":2235},[2225,242942,2311],{"class":2235},[2225,242944,6614],{"class":2235},[2225,242946,198793],{"class":2252},[2225,242948,6620],{"class":2235},[2225,242950,1707],{"class":2235},[2225,242952,242953,242955,242957,242959,242961,242963,242965,242967,242969],{"class":2227,"line":2304},[2225,242954,14201],{"class":2235},[2225,242956,2698],{"class":2774},[2225,242958,6620],{"class":2235},[2225,242960,2311],{"class":2235},[2225,242962,8667],{"class":2235},[2225,242964,6620],{"class":2235},[2225,242966,6487],{"class":2252},[2225,242968,6620],{"class":2235},[2225,242970,66199],{"class":2235},[2225,242972,242973,242975,242978,242980,242982,242984,242986,242988],{"class":2227,"line":2356},[2225,242974,14201],{"class":2235},[2225,242976,242977],{"class":2774},"disposition",[2225,242979,6620],{"class":2235},[2225,242981,2311],{"class":2235},[2225,242983,6614],{"class":2235},[2225,242985,56994],{"class":2252},[2225,242987,6620],{"class":2235},[2225,242989,1707],{"class":2235},[2225,242991,242992,242994,242996,242998,243000,243002,243004,243006],{"class":2227,"line":2830},[2225,242993,14201],{"class":2235},[2225,242995,137378],{"class":2774},[2225,242997,6620],{"class":2235},[2225,242999,2311],{"class":2235},[2225,243001,6614],{"class":2235},[2225,243003,242792],{"class":2252},[2225,243005,6620],{"class":2235},[2225,243007,1707],{"class":2235},[2225,243009,243010,243012,243014,243016,243018,243020,243022,243024],{"class":2227,"line":2861},[2225,243011,14201],{"class":2235},[2225,243013,6631],{"class":2774},[2225,243015,6620],{"class":2235},[2225,243017,2311],{"class":2235},[2225,243019,6614],{"class":2235},[2225,243021,12192],{"class":2252},[2225,243023,6620],{"class":2235},[2225,243025,1707],{"class":2235},[2225,243027,243028,243030,243033,243035,243037,243039,243041,243043],{"class":2227,"line":4246},[2225,243029,14201],{"class":2235},[2225,243031,243032],{"class":2774},"requestedAt",[2225,243034,6620],{"class":2235},[2225,243036,2311],{"class":2235},[2225,243038,6614],{"class":2235},[2225,243040,98542],{"class":2252},[2225,243042,6620],{"class":2235},[2225,243044,1707],{"class":2235},[2225,243046,243047,243049,243051,243053,243055,243057,243060],{"class":2227,"line":4253},[2225,243048,14201],{"class":2235},[2225,243050,44339],{"class":2774},[2225,243052,6620],{"class":2235},[2225,243054,2311],{"class":2235},[2225,243056,6614],{"class":2235},[2225,243058,243059],{"class":2252},"2026-07-22T09:00:00.000Z",[2225,243061,9449],{"class":2235},[2225,243063,243064],{"class":2227,"line":4259},[2225,243065,7632],{"class":2235},[2225,243067,243068],{"class":2227,"line":6548},[2225,243069,26484],{"class":2235},[937,243071,243072,243074],{},[950,243073,242853],{}," — approve (extract-and-dispose under firm authority) or reject:",[2217,243076,243078],{"className":2219,"code":243077,"language":2221,"meta":1037,"style":1037},"const { bundle, snapshot } = await ownerVault.user.approveWithdrawal(\n  'wr-9f2c41d0be57a6e83d1f0a2b',\n  { reKey: { secret: 'handover-secret' } }, \u002F\u002F conveyed to the requester out-of-band\n)\n\u002F\u002F bundle   → Uint8Array — the requester's re-keyed portable copy\n\u002F\u002F snapshot → { withdrawalId, sha256, recordCount, frozenAt }  (freeze disposition only)\n\n\u002F\u002F …or decline; no data is touched:\nawait ownerVault.user.rejectWithdrawal('wr-9f2c41d0be57a6e83d1f0a2b', {\n  reason: 'statutory retention period still running',\n})\n\u002F\u002F → the request record with status: 'rejected', decidedBy, decidedAt, rejectReason\n",[941,243079,243080,243110,243120,243149,243153,243158,243163,243167,243172,243198,243213,243219],{"__ignoreMap":1037},[2225,243081,243082,243084,243086,243088,243090,243092,243094,243096,243098,243100,243102,243104,243106,243108],{"class":2227,"line":2228},[2225,243083,2775],{"class":2774},[2225,243085,2236],{"class":2235},[2225,243087,83152],{"class":2239},[2225,243089,2298],{"class":2235},[2225,243091,160119],{"class":2239},[2225,243093,2359],{"class":2235},[2225,243095,6346],{"class":2235},[2225,243097,2784],{"class":2231},[2225,243099,242872],{"class":2239},[2225,243101,1520],{"class":2235},[2225,243103,16225],{"class":2239},[2225,243105,1520],{"class":2235},[2225,243107,199196],{"class":2291},[2225,243109,11683],{"class":2239},[2225,243111,243112,243114,243116,243118],{"class":2227,"line":1041},[2225,243113,124511],{"class":2235},[2225,243115,242927],{"class":2252},[2225,243117,2805],{"class":2235},[2225,243119,1707],{"class":2235},[2225,243121,243122,243124,243127,243129,243131,243133,243135,243137,243140,243142,243144,243146],{"class":2227,"line":1038},[2225,243123,26354],{"class":2235},[2225,243125,243126],{"class":2307}," reKey",[2225,243128,2311],{"class":2235},[2225,243130,2236],{"class":2235},[2225,243132,2810],{"class":2307},[2225,243134,2311],{"class":2235},[2225,243136,2249],{"class":2235},[2225,243138,243139],{"class":2252},"handover-secret",[2225,243141,2805],{"class":2235},[2225,243143,2243],{"class":2235},[2225,243145,10965],{"class":2235},[2225,243147,243148],{"class":2763}," \u002F\u002F conveyed to the requester out-of-band\n",[2225,243150,243151],{"class":2227,"line":2288},[2225,243152,2362],{"class":2239},[2225,243154,243155],{"class":2227,"line":2304},[2225,243156,243157],{"class":2763},"\u002F\u002F bundle   → Uint8Array — the requester's re-keyed portable copy\n",[2225,243159,243160],{"class":2227,"line":2356},[2225,243161,243162],{"class":2763},"\u002F\u002F snapshot → { withdrawalId, sha256, recordCount, frozenAt }  (freeze disposition only)\n",[2225,243164,243165],{"class":2227,"line":2830},[2225,243166,2285],{"emptyLinePlaceholder":2284},[2225,243168,243169],{"class":2227,"line":2861},[2225,243170,243171],{"class":2763},"\u002F\u002F …or decline; no data is touched:\n",[2225,243173,243174,243176,243178,243180,243182,243184,243186,243188,243190,243192,243194,243196],{"class":2227,"line":4246},[2225,243175,6500],{"class":2231},[2225,243177,242872],{"class":2239},[2225,243179,1520],{"class":2235},[2225,243181,16225],{"class":2239},[2225,243183,1520],{"class":2235},[2225,243185,199199],{"class":2291},[2225,243187,2789],{"class":2239},[2225,243189,2805],{"class":2235},[2225,243191,242927],{"class":2252},[2225,243193,2805],{"class":2235},[2225,243195,2298],{"class":2235},[2225,243197,2301],{"class":2235},[2225,243199,243200,243202,243204,243206,243209,243211],{"class":2227,"line":4253},[2225,243201,74864],{"class":2307},[2225,243203,2311],{"class":2235},[2225,243205,2249],{"class":2235},[2225,243207,243208],{"class":2252},"statutory retention period still running",[2225,243210,2805],{"class":2235},[2225,243212,1707],{"class":2235},[2225,243214,243215,243217],{"class":2227,"line":4259},[2225,243216,2359],{"class":2235},[2225,243218,2362],{"class":2239},[2225,243220,243221],{"class":2227,"line":6548},[2225,243222,243223],{"class":2763},"\u002F\u002F → the request record with status: 'rejected', decidedBy, decidedAt, rejectReason\n",[937,243225,960,243226,243228,243229,243231,243232,243235],{},[941,243227,242977],{}," the requester chose flows through end-to-end — approval runs the same\nextract-and-dispose engine as ",[941,243230,199187],{},". The request record itself is\nplaintext metadata (collection names, disposition, legal basis — nothing the owner\ncannot already see) and carries ",[950,243233,243234],{},"no secret",": the re-key secret is supplied by\nthe approver at approval time and handed to the requester out-of-band, so no secret is\never stored at rest.",[1022,243237,243239],{"id":243238},"one-party-withdrawal-operator-self-serve","One-party withdrawal (operator self-serve)",[937,243241,5093,243242,243244,243245,243247,243248,243251],{},[941,243243,9907],{}," — a member with read-write grants on their collections — can withdraw in\none step, if the firm has enabled the ",[941,243246,198091],{}," gate. The gate is\n",[950,243249,243250],{},"disabled by default and fails closed","; the firm opts in at vault creation, per\njurisdiction or contract:",[2217,243253,243255],{"className":2219,"code":243254,"language":2221,"meta":1037,"style":1037},"const owner = await createNoydb({\n  user: 'firm',\n  secret: 'owner-pw-long-enough',\n  teamStrategy: withTeam(),\n  policy: {\n    gates: { 'client-unilateral-withdraw': { enabled: true, minTier: 1 } },\n  },\n})\n",[941,243256,243257,243273,243287,243302,243314,243322,243358,243362],{"__ignoreMap":1037},[2225,243258,243259,243261,243263,243265,243267,243269,243271],{"class":2227,"line":2228},[2225,243260,2775],{"class":2774},[2225,243262,141385],{"class":2239},[2225,243264,2781],{"class":2235},[2225,243266,2784],{"class":2231},[2225,243268,2744],{"class":2291},[2225,243270,2789],{"class":2239},[2225,243272,4200],{"class":2235},[2225,243274,243275,243277,243279,243281,243283,243285],{"class":2227,"line":1041},[2225,243276,4205],{"class":2307},[2225,243278,2311],{"class":2235},[2225,243280,2249],{"class":2235},[2225,243282,39709],{"class":2252},[2225,243284,2805],{"class":2235},[2225,243286,1707],{"class":2235},[2225,243288,243289,243291,243293,243295,243298,243300],{"class":2227,"line":1038},[2225,243290,10423],{"class":2307},[2225,243292,2311],{"class":2235},[2225,243294,2249],{"class":2235},[2225,243296,243297],{"class":2252},"owner-pw-long-enough",[2225,243299,2805],{"class":2235},[2225,243301,1707],{"class":2235},[2225,243303,243304,243306,243308,243310,243312],{"class":2227,"line":2288},[2225,243305,10439],{"class":2307},[2225,243307,2311],{"class":2235},[2225,243309,10373],{"class":2291},[2225,243311,4228],{"class":2239},[2225,243313,1707],{"class":2235},[2225,243315,243316,243318,243320],{"class":2227,"line":2304},[2225,243317,18899],{"class":2307},[2225,243319,2311],{"class":2235},[2225,243321,2301],{"class":2235},[2225,243323,243324,243326,243328,243330,243332,243334,243336,243338,243340,243342,243344,243346,243348,243350,243352,243354,243356],{"class":2227,"line":2356},[2225,243325,18917],{"class":2307},[2225,243327,2311],{"class":2235},[2225,243329,2236],{"class":2235},[2225,243331,2249],{"class":2235},[2225,243333,198091],{"class":2307},[2225,243335,2805],{"class":2235},[2225,243337,2311],{"class":2235},[2225,243339,2236],{"class":2235},[2225,243341,135470],{"class":2307},[2225,243343,2311],{"class":2235},[2225,243345,2322],{"class":2321},[2225,243347,2298],{"class":2235},[2225,243349,18956],{"class":2307},[2225,243351,2311],{"class":2235},[2225,243353,9377],{"class":6527},[2225,243355,2243],{"class":2235},[2225,243357,2353],{"class":2235},[2225,243359,243360],{"class":2227,"line":2830},[2225,243361,19017],{"class":2235},[2225,243363,243364,243366],{"class":2227,"line":2861},[2225,243365,2359],{"class":2235},[2225,243367,2362],{"class":2239},[2217,243369,243371],{"className":2219,"code":243370,"language":2221,"meta":1037,"style":1037},"\u002F\u002F operatorVault was opened by an `operator` keyring with rw grants\nconst { bundle, snapshot } = await operatorVault.user.unilateralWithdrawal({\n  legalBasis: 'gdpr-art-17',\n  disposition: 'delete',                       \u002F\u002F default\n  reKey: { secret: 'takeaway-secret' },\n})\n\u002F\u002F → { bundle: Uint8Array } — the re-keyed copy; the source records are gone\n",[941,243372,243373,243378,243411,243425,243442,243463,243469],{"__ignoreMap":1037},[2225,243374,243375],{"class":2227,"line":2228},[2225,243376,243377],{"class":2763},"\u002F\u002F operatorVault was opened by an `operator` keyring with rw grants\n",[2225,243379,243380,243382,243384,243386,243388,243390,243392,243394,243396,243399,243401,243403,243405,243407,243409],{"class":2227,"line":1041},[2225,243381,2775],{"class":2774},[2225,243383,2236],{"class":2235},[2225,243385,83152],{"class":2239},[2225,243387,2298],{"class":2235},[2225,243389,160119],{"class":2239},[2225,243391,2359],{"class":2235},[2225,243393,6346],{"class":2235},[2225,243395,2784],{"class":2231},[2225,243397,243398],{"class":2239}," operatorVault",[2225,243400,1520],{"class":2235},[2225,243402,16225],{"class":2239},[2225,243404,1520],{"class":2235},[2225,243406,199187],{"class":2291},[2225,243408,2789],{"class":2239},[2225,243410,4200],{"class":2235},[2225,243412,243413,243415,243417,243419,243421,243423],{"class":2227,"line":1038},[2225,243414,136614],{"class":2307},[2225,243416,2311],{"class":2235},[2225,243418,2249],{"class":2235},[2225,243420,242792],{"class":2252},[2225,243422,2805],{"class":2235},[2225,243424,1707],{"class":2235},[2225,243426,243427,243429,243431,243433,243435,243437,243439],{"class":2227,"line":2288},[2225,243428,242768],{"class":2307},[2225,243430,2311],{"class":2235},[2225,243432,2249],{"class":2235},[2225,243434,3164],{"class":2252},[2225,243436,2805],{"class":2235},[2225,243438,2298],{"class":2235},[2225,243440,243441],{"class":2763},"                       \u002F\u002F default\n",[2225,243443,243444,243446,243448,243450,243452,243454,243456,243459,243461],{"class":2227,"line":2304},[2225,243445,84124],{"class":2307},[2225,243447,2311],{"class":2235},[2225,243449,2236],{"class":2235},[2225,243451,2810],{"class":2307},[2225,243453,2311],{"class":2235},[2225,243455,2249],{"class":2235},[2225,243457,243458],{"class":2252},"takeaway-secret",[2225,243460,2805],{"class":2235},[2225,243462,2353],{"class":2235},[2225,243464,243465,243467],{"class":2227,"line":2356},[2225,243466,2359],{"class":2235},[2225,243468,2362],{"class":2239},[2225,243470,243471],{"class":2227,"line":2830},[2225,243472,243473],{"class":2763},"\u002F\u002F → { bundle: Uint8Array } — the re-keyed copy; the source records are gone\n",[1022,243475,199005],{"id":199004},[1338,243477,243478,243499],{},[1341,243479,243480],{},[1344,243481,243482,243484,243488,243492],{},[1347,243483,2970],{},[1347,243485,243486],{},[941,243487,199187],{},[1347,243489,243490],{},[941,243491,199190],{},[1347,243493,243494,1261,243497],{},[941,243495,243496],{},"approve",[941,243498,210100],{},[1357,243500,243501,243526,243542,243563],{},[1344,243502,243503,243509,243516,243521],{},[1362,243504,243505,1261,243507],{},[941,243506,9898],{},[941,243508,9901],{},[1362,243510,243511,243512],{},"no — use ",[1007,243513,243514],{"href":195453},[941,243515,236788],{},[1362,243517,243518,243519],{},"needs explicit ",[941,243520,242847],{},[1362,243522,243523,243525],{},[950,243524,14852],{}," (the only roles that can)",[1344,243527,243528,243532,243538,243540],{},[1362,243529,243530],{},[941,243531,9907],{},[1362,243533,243534,243535,243537],{},"yes — ",[941,243536,10026],{}," grants only, gate must be enabled",[1362,243539,14852],{},[1362,243541,11313],{},[1344,243543,243544,243550,243553,243561],{},[1362,243545,243546,1261,243548],{},[941,243547,9913],{},[941,243549,9910],{},[1362,243551,243552],{},"no — read-only by construction",[1362,243554,243555,243556,243558,243559,1381],{},"yes — this is their path (a ",[941,243557,9910],{}," must name ",[941,243560,242847],{},[1362,243562,11313],{},[1344,243564,243565,243569,243576,243580],{},[1362,243566,243567],{},[941,243568,9904],{},[1362,243570,243571,243572,1324,243574,77626],{},"no — use the audited ",[1007,243573,661],{"href":238283},[941,243575,21231],{},[1362,243577,243518,243578],{},[941,243579,242847],{},[1362,243581,11313],{},[1111,243583,243584,243599,243610,243613],{},[1114,243585,243586,243587,1625,243589,243591,243592,128330,243594,243596,243597,1520],{},"Read-only roles can never delete anything, regardless of policy — a ",[941,243588,9913],{},[941,243590,9910],{},"\ncalling ",[941,243593,199187],{},[941,243595,11361],{}," pointing at ",[941,243598,199190],{},[1114,243600,243601,243602,53922,243604,243606,243607,243609],{},"Destructive self-serve requires ",[941,243603,10026],{},[950,243605,2119],{}," withdrawn collection; the export\nscope of a destructive op is the caller's ",[941,243608,10026],{}," grants only.",[1114,243611,243612],{},"Owner-class roles hold blanket authority over the vault, so \"withdrawing my slice\" is\nthe wrong tool for them; they extract and hand over whole partitions instead.",[1114,243614,243615,243616,4688,243618,243620,243621,243623],{},"Approving\u002Frejecting requires an ",[941,243617,9898],{},[941,243619,9901],{}," keyring in addition to the policy\ngate — a ",[941,243622,9904],{}," is deliberately excluded, because approving a destructive\nextract-and-dispose is a governance act on par with grant\u002Frevoke.",[937,243625,243626,243627,243631],{},"(For who may take a non-destructive copy, see ",[1007,243628,243630],{"href":243629},"\u002Fservices\u002Fportability#who-can-do-what","portability → Who can do what"," — export is open to every role.)",[1022,243633,243635],{"id":243634},"source-dispositions","Source dispositions",[937,243637,243638,243639,243641],{},"Both withdrawal paths accept a ",[941,243640,242977],{}," deciding what happens to the source\nrecords after the portable copy is built:",[1111,243643,243644,243658],{},[1114,243645,243646,243650,243651,243654,243655,243657],{},[950,243647,243648],{},[941,243649,3164],{}," (default) — ",[1092,243652,243653],{},"delete-closure",": the records leave the vault entirely\n(GDPR Art. 17-style erasure). The re-keyed bundle is always produced ",[950,243656,3233],{},", so a\ncrash before deletion loses nothing; deletion is idempotent, so a crashed run can be\nre-run safely.",[1114,243659,243660,243664,243665,243668,243669,243672,243673,2311],{},[950,243661,243662],{},[941,243663,56994],{}," — the firm retains a ",[950,243666,243667],{},"write-once",", hash-pinned snapshot of the original\nciphertext envelopes at ",[941,243670,243671],{},"_frozen_snapshots\u002F\u003CwithdrawalId>"," (already under the firm's\nown DEKs — no re-key), then delete-closures the live records. The result includes a\n",[941,243674,243675],{},"FrozenSnapshotRef",[2217,243677,243679],{"className":2219,"code":243678,"language":2221,"meta":1037,"style":1037},"interface FrozenSnapshotRef {\n  withdrawalId: string   \u002F\u002F 'wd-…'\n  sha256: string         \u002F\u002F hex digest of the snapshot body\n  recordCount: number\n  frozenAt: string       \u002F\u002F ISO timestamp\n}\n",[941,243680,243681,243690,243702,243714,243723,243735],{"__ignoreMap":1037},[2225,243682,243683,243685,243688],{"class":2227,"line":2228},[2225,243684,6994],{"class":2774},[2225,243686,243687],{"class":6342}," FrozenSnapshotRef",[2225,243689,2301],{"class":2235},[2225,243691,243692,243695,243697,243699],{"class":2227,"line":1041},[2225,243693,243694],{"class":2307},"  withdrawalId",[2225,243696,2311],{"class":2235},[2225,243698,6366],{"class":6342},[2225,243700,243701],{"class":2763},"   \u002F\u002F 'wd-…'\n",[2225,243703,243704,243707,243709,243711],{"class":2227,"line":1038},[2225,243705,243706],{"class":2307},"  sha256",[2225,243708,2311],{"class":2235},[2225,243710,6366],{"class":6342},[2225,243712,243713],{"class":2763},"         \u002F\u002F hex digest of the snapshot body\n",[2225,243715,243716,243719,243721],{"class":2227,"line":2288},[2225,243717,243718],{"class":2307},"  recordCount",[2225,243720,2311],{"class":2235},[2225,243722,12024],{"class":6342},[2225,243724,243725,243728,243730,243732],{"class":2227,"line":2304},[2225,243726,243727],{"class":2307},"  frozenAt",[2225,243729,2311],{"class":2235},[2225,243731,6366],{"class":6342},[2225,243733,243734],{"class":2763},"       \u002F\u002F ISO timestamp\n",[2225,243736,243737],{"class":2227,"line":2356},[2225,243738,7351],{"class":2235},[937,243740,960,243741,243743,243744,243746],{},[941,243742,136678],{}," is pinned into the tamper-evident ledger, making the retained snapshot\nprovably unaltered. The snapshot write is compare-and-swap \"create-only\", so re-running\nan interrupted withdrawal with the same ",[941,243745,136659],{}," never overwrites it.",[1022,243748,220044],{"id":220043},[937,243750,243751,243752,243754,243755,243757,243758,243761,243762,243764,243765,243767],{},"Policy ",[950,243753,12826],{}," are named checkpoints evaluated against the caller's session strength —\nits ",[1007,243756,196621],{"href":129031}," (tier 1 = full secret unlock, tier 2 =\nauthenticated login, tier 3 = quick resume) — plus any extra factor proofs the vault's\npolicy demands (see ",[1007,243759,796],{"href":243760},".\u002Fpolicy","). Built-in gates ",[950,243763,2200],{}," when the\npolicy does not configure them; only developer-defined ",[941,243766,13045],{}," gates default to allow.",[1338,243769,243770,243787],{},[1341,243771,243772],{},[1344,243773,243774,243776,243779,243783],{},[1347,243775,104381],{},[1347,243777,243778],{},"Guards",[1347,243780,243781,36571],{},[941,243782,12830],{},[1347,243784,243785,36571],{},[941,243786,12834],{},[1357,243788,243789,243808,243823],{},[1344,243790,243791,243795,243799,243803],{},[1362,243792,243793],{},[941,243794,198091],{},[1362,243796,243797],{},[941,243798,199187],{},[1362,243800,243801],{},[950,243802,239264],{},[1362,243804,243805,243807],{},[950,243806,239264],{},"; if a firm enables it, it inherits a two-factor proof (TOTP\u002Femail-OTP ×2) + shared-device block",[1344,243809,243810,243814,243818,243821],{},[1362,243811,243812],{},[941,243813,198094],{},[1362,243815,243816],{},[941,243817,199190],{},[1362,243819,243820],{},"enabled, tier 1",[1362,243822,243820],{},[1344,243824,243825,243829,243835,243838],{},[1362,243826,243827],{},[941,243828,198097],{},[1362,243830,243831,1261,243833],{},[941,243832,199196],{},[941,243834,199199],{},[1362,243836,243837],{},"enabled, tier 2",[1362,243839,243840],{},"tier 2 + TOTP\u002Femail-OTP factor + shared-device block",[937,243842,243843,243844,31984,243846,243849],{},"Non-destructive ",[1007,243845,8080],{"href":801},[950,243847,243848],{},"no gate"," by design: a portable\ncopy of data the caller can already decrypt cannot be denied — it is only audited.",[1022,243851,102889],{"id":102888},[937,243853,243854,243855,243857,243858,243862,243863,243865,243866,14388],{},"All five withdrawal methods live on ",[941,243856,21609],{}," once the shared\n",[1007,243859,243860],{"href":242683},[941,243861,21604],{}," strategy is wired. (The\nnon-destructive ",[941,243864,84115],{}," lives on\n",[1007,243867,800],{"href":243868},"\u002Fservices\u002Fportability#api-reference",[1591,243870,243872],{"id":243871},"vaultuserunilateralwithdrawalopts",[941,243873,243874],{},"vault.user.unilateralWithdrawal(opts)",[937,243876,243877,243878,33690,243881,1520],{},"Destructive one-party export-and-dispose. ",[941,243879,243880],{},"WithdrawResult",[941,243882,243883],{},"{ bundle: Uint8Array; snapshot?: FrozenSnapshotRef }",[1338,243885,243886,243898],{},[1341,243887,243888],{},[1344,243889,243890,243892,243894,243896],{},[1347,243891,101060],{},[1347,243893,20194],{},[1347,243895,20197],{},[1347,243897,20200],{},[1357,243899,243900,243919,243935,243953,243971],{},[1344,243901,243902,243906,243910,243914],{},[1362,243903,243904],{},[941,243905,137378],{},[1362,243907,243908],{},[941,243909,7259],{},[1362,243911,243912],{},[950,243913,45375],{},[1362,243915,137385,243916,1381],{},[941,243917,243918],{},"'gdpr-art-17'",[1344,243920,243921,243925,243929,243932],{},[1362,243922,243923],{},[941,243924,198995],{},[1362,243926,243927],{},[941,243928,199141],{},[1362,243930,243931],{},"inherit",[1362,243933,243934],{},"Re-key the exported bundle",[1344,243936,243937,243941,243946,243950],{},[1362,243938,243939],{},[941,243940,242977],{},[1362,243942,243943],{},[941,243944,243945],{},"'delete' | 'freeze'",[1362,243947,243948],{},[941,243949,180275],{},[1362,243951,243952],{},"What happens to the source",[1344,243954,243955,243959,243963,243968],{},[1362,243956,243957],{},[941,243958,13544],{},[1362,243960,243961],{},[941,243962,199158],{},[1362,243964,214069,243965,243967],{},[941,243966,10026],{}," grants",[1362,243969,243970],{},"Narrow the withdrawal",[1344,243972,243973,243977,243981,243984],{},[1362,243974,243975],{},[941,243976,136659],{},[1362,243978,243979],{},[941,243980,7259],{},[1362,243982,243983],{},"random",[1362,243985,243986],{},"Stable id for idempotent resume after a crash",[1591,243988,243990],{"id":243989},"vaultuserrequestwithdrawalopts",[941,243991,243992],{},"vault.user.requestWithdrawal(opts?)",[937,243994,243995,243996,1520],{},"Files a durable pending request; returns ",[941,243997,243998],{},"{ requestId, status, expiresAt? }",[1338,244000,244001,244013],{},[1341,244002,244003],{},[1344,244004,244005,244007,244009,244011],{},[1347,244006,101060],{},[1347,244008,20194],{},[1347,244010,20197],{},[1347,244012,20200],{},[1357,244014,244015,244031,244048,244063],{},[1344,244016,244017,244021,244025,244028],{},[1362,244018,244019],{},[941,244020,13544],{},[1362,244022,244023],{},[941,244024,199158],{},[1362,244026,244027],{},"caller's grants",[1362,244029,244030],{},"Required when the caller can read every collection",[1344,244032,244033,244037,244041,244045],{},[1362,244034,244035],{},[941,244036,242977],{},[1362,244038,244039],{},[941,244040,243945],{},[1362,244042,244043],{},[941,244044,180275],{},[1362,244046,244047],{},"Requested source disposition",[1344,244049,244050,244054,244058,244060],{},[1362,244051,244052],{},[941,244053,137378],{},[1362,244055,244056],{},[941,244057,7259],{},[1362,244059,3610],{},[1362,244061,244062],{},"Recorded on the request",[1344,244064,244065,244070,244074,244076],{},[1362,244066,244067],{},[941,244068,244069],{},"expiresInMs",[1362,244071,244072],{},[941,244073,20636],{},[1362,244075,18391],{},[1362,244077,244078],{},"After this, the request can no longer be approved",[1591,244080,244082],{"id":244081},"vaultuserlistwithdrawalrequests-status",[941,244083,244084],{},"vault.user.listWithdrawalRequests({ status? })",[937,244086,244087,244088,1520],{},"Owner side. Optionally filter by ",[941,244089,244090],{},"status: 'pending' | 'approved' | 'rejected'",[2217,244092,244094],{"className":2219,"code":244093,"language":2221,"meta":1037,"style":1037},"interface WithdrawalRequest {\n  requestId: string\n  requester: string\n  collections: readonly string[]\n  disposition: 'delete' | 'freeze'\n  legalBasis?: string\n  status: 'pending' | 'approved' | 'rejected'\n  requestedAt: string\n  expiresAt?: string\n  decidedAt?: string\n  decidedBy?: string\n  rejectReason?: string\n  snapshotSha256?: string   \u002F\u002F set on approved freeze-disposition requests\n}\n",[941,244095,244096,244105,244114,244123,244135,244155,244163,244192,244201,244209,244218,244227,244236,244248],{"__ignoreMap":1037},[2225,244097,244098,244100,244103],{"class":2227,"line":2228},[2225,244099,6994],{"class":2774},[2225,244101,244102],{"class":6342}," WithdrawalRequest",[2225,244104,2301],{"class":2235},[2225,244106,244107,244110,244112],{"class":2227,"line":1041},[2225,244108,244109],{"class":2307},"  requestId",[2225,244111,2311],{"class":2235},[2225,244113,7014],{"class":6342},[2225,244115,244116,244119,244121],{"class":2227,"line":1038},[2225,244117,244118],{"class":2307},"  requester",[2225,244120,2311],{"class":2235},[2225,244122,7014],{"class":6342},[2225,244124,244125,244127,244129,244131,244133],{"class":2227,"line":2288},[2225,244126,32324],{"class":2307},[2225,244128,2311],{"class":2235},[2225,244130,112906],{"class":2774},[2225,244132,6366],{"class":6342},[2225,244134,78511],{"class":2239},[2225,244136,244137,244139,244141,244143,244145,244147,244149,244151,244153],{"class":2227,"line":2304},[2225,244138,242768],{"class":2307},[2225,244140,2311],{"class":2235},[2225,244142,2249],{"class":2235},[2225,244144,3164],{"class":2252},[2225,244146,2805],{"class":2235},[2225,244148,7087],{"class":2235},[2225,244150,2249],{"class":2235},[2225,244152,56994],{"class":2252},[2225,244154,2256],{"class":2235},[2225,244156,244157,244159,244161],{"class":2227,"line":2356},[2225,244158,136614],{"class":2307},[2225,244160,7011],{"class":2235},[2225,244162,7014],{"class":6342},[2225,244164,244165,244167,244169,244171,244173,244175,244177,244179,244182,244184,244186,244188,244190],{"class":2227,"line":2830},[2225,244166,26171],{"class":2307},[2225,244168,2311],{"class":2235},[2225,244170,2249],{"class":2235},[2225,244172,12192],{"class":2252},[2225,244174,2805],{"class":2235},[2225,244176,7087],{"class":2235},[2225,244178,2249],{"class":2235},[2225,244180,244181],{"class":2252},"approved",[2225,244183,2805],{"class":2235},[2225,244185,7087],{"class":2235},[2225,244187,2249],{"class":2235},[2225,244189,207505],{"class":2252},[2225,244191,2256],{"class":2235},[2225,244193,244194,244197,244199],{"class":2227,"line":2861},[2225,244195,244196],{"class":2307},"  requestedAt",[2225,244198,2311],{"class":2235},[2225,244200,7014],{"class":6342},[2225,244202,244203,244205,244207],{"class":2227,"line":4246},[2225,244204,211106],{"class":2307},[2225,244206,7011],{"class":2235},[2225,244208,7014],{"class":6342},[2225,244210,244211,244214,244216],{"class":2227,"line":4253},[2225,244212,244213],{"class":2307},"  decidedAt",[2225,244215,7011],{"class":2235},[2225,244217,7014],{"class":6342},[2225,244219,244220,244223,244225],{"class":2227,"line":4259},[2225,244221,244222],{"class":2307},"  decidedBy",[2225,244224,7011],{"class":2235},[2225,244226,7014],{"class":6342},[2225,244228,244229,244232,244234],{"class":2227,"line":6548},[2225,244230,244231],{"class":2307},"  rejectReason",[2225,244233,7011],{"class":2235},[2225,244235,7014],{"class":6342},[2225,244237,244238,244241,244243,244245],{"class":2227,"line":6576},[2225,244239,244240],{"class":2307},"  snapshotSha256",[2225,244242,7011],{"class":2235},[2225,244244,6366],{"class":6342},[2225,244246,244247],{"class":2763},"   \u002F\u002F set on approved freeze-disposition requests\n",[2225,244249,244250],{"class":2227,"line":6582},[2225,244251,7351],{"class":2235},[1591,244253,244255],{"id":244254},"vaultuserapprovewithdrawalrequestid-rekey",[941,244256,244257],{},"vault.user.approveWithdrawal(requestId, { reKey? })",[937,244259,244260,244261,244263],{},"Owner side. Extracts the requester's recorded scope under firm authority, disposes of\nthe source per the request's ",[941,244262,242977],{},", marks the request approved, and returns the\nre-keyed bundle to hand back.",[1591,244265,244267],{"id":244266},"vaultuserrejectwithdrawalrequestid-reason",[941,244268,244269],{},"vault.user.rejectWithdrawal(requestId, { reason? })",[937,244271,244272],{},"Owner side. Declines a pending request — no data is touched; returns the updated record.",[1022,244274,120265],{"id":120264},[1338,244276,244277,244285],{},[1341,244278,244279],{},[1344,244280,244281,244283],{},[1347,244282,12126],{},[1347,244284,120276],{},[1357,244286,244287,244298,244307,244316],{},[1344,244288,244289,244293],{},[1362,244290,244291],{},[941,244292,21786],{},[1362,244294,244295,244296],{},"Any method called without ",[941,244297,83182],{},[1344,244299,244300,244304],{},[1362,244301,244302],{},[941,244303,13025],{},[1362,244305,244306],{},"The guarding gate is disabled, the session tier is too low, or a required factor is missing",[1344,244308,244309,244313],{},[1362,244310,244311],{},[941,244312,11361],{},[1362,244314,244315],{},"A role without the required write authority attempts a destructive op",[1344,244317,244318,244322],{},[1362,244319,244320],{},[941,244321,199241],{},[1362,244323,244324],{},"Request not found, already decided, expired, or approve\u002Freject called by a non-owner\u002Fadmin",[1022,244326,199249],{"id":119664},[937,244328,244329,244330,244332,244333,244335,244336,2311],{},"Every operation appends an ",[941,244331,199255],{}," entry to the tamper-evident ledger when the\nvault has ",[1007,244334,697],{"href":140989}," enabled (without it, auditing is a silent no-op). The\nentries participate in the ledger's hash chain but carry no data payload — only a\nstructured ",[941,244337,43668],{},[1338,244339,244340,244350],{},[1341,244341,244342],{},[1344,244343,244344,244346],{},[1347,244345,199270],{},[1347,244347,244348],{},[941,244349,43668],{},[1357,244351,244352,244362,244372,244382,244392],{},[1344,244353,244354,244357],{},[1362,244355,244356],{},"One-party withdrawal",[1362,244358,244359],{},[941,244360,244361],{},"user-unilateral-withdrawal:\u003CuserId>:\u003Cdisposition>:\u003ClegalBasis>",[1344,244363,244364,244367],{},[1362,244365,244366],{},"Request filed",[1362,244368,244369],{},[941,244370,244371],{},"user-withdrawal-request:\u003CrequestId>:\u003CuserId>",[1344,244373,244374,244377],{},[1362,244375,244376],{},"Request approved",[1362,244378,244379],{},[941,244380,244381],{},"user-withdrawal-approved:\u003CrequestId>:\u003Crequester>:\u003Cdisposition>",[1344,244383,244384,244387],{},[1362,244385,244386],{},"Request rejected",[1362,244388,244389],{},[941,244390,244391],{},"user-withdrawal-rejected:\u003CrequestId>:\u003Crequester>",[1344,244393,244394,244397],{},[1362,244395,244396],{},"Freeze snapshot pinned",[1362,244398,244399],{},[941,244400,244401],{},"withdrawal-frozen-snapshot:\u003CwithdrawalId>:\u003Csha256>",[937,244403,244404,244405,244407,244408,14388],{},"(The non-destructive export entry, ",[941,244406,199285],{},", is on\n",[1007,244409,244411],{"href":244410},"\u002Fservices\u002Fportability#audit","portability → Audit",[1022,244413,68210],{"id":68209},[1111,244415,244416,244426,244437,244445,244451,244459],{},[1114,244417,244418,244419,244421,244422,244425],{},"Withdrawal requests live as ",[950,244420,128480],{}," in the reserved\n",[941,244423,244424],{},"_user_withdrawal_requests"," collection — collection names, disposition, legal basis.\nNothing secret in this trust model (the owner sees the data anyway), and no\nsecret is ever stored.",[1114,244427,3289,244428,244430,244431,244433,244434,244436],{},[941,244429,56994],{}," snapshot stays under the firm's own DEKs — it is ",[950,244432,228179],{},", not\nerasure: the firm can still read it. Use ",[941,244435,3164],{}," when the goal is that the data is\ngone.",[1114,244438,244439,244441,244442,244444],{},[941,244440,199187],{}," is unusable until the firm enables\n",[941,244443,198091],{}," in the vault policy — the gate fails closed by default.",[1114,244446,244447,244448,244450],{},"A requester with all-collection read access must pass an explicit\n",[941,244449,242847],{},"; the ceremony refuses an unbounded \"withdraw everything\" request.",[1114,244452,244453,244454,244456,244457,1520],{},"Expired requests (past ",[941,244455,44339],{},") can no longer be approved or rejected —\n",[941,244458,199241],{},[1114,244460,199302,244461,199307],{},[1007,244462,244463],{"href":140989},[941,244464,6719],{},[1022,244466,2621],{"id":2620},[1111,244468,244469,244479,244488,244492,244499,244507,244515,244521,244526],{},[1114,244470,244471,244473,244474,2085,244476,244478],{},[1007,244472,800],{"href":801}," — the non-destructive other half of ",[941,244475,21604],{},[941,244477,84115],{}," and the shared opt-in.",[1114,244480,244481,1169,244483,195438,244485,244487],{},[1007,244482,629],{"href":195435},[941,244484,1402],{},[941,244486,116870],{}," to open a withdrawn bundle.",[1114,244489,244490,199333],{},[1007,244491,868],{"href":198512},[1114,244493,244494,1261,244496,244498],{},[1007,244495,844],{"href":129031},[1007,244497,796],{"href":243760}," — the tier ladder and the gate DSL the withdrawal gates are built on.",[1114,244500,244501,244503,244504,244506],{},[1007,244502,116737],{"href":195453}," — owner-side ",[941,244505,236788],{}," for handing over whole partitions.",[1114,244508,244509,244511,244512,244514],{},[1007,244510,661],{"href":238283}," — the custodian role and the audited ",[941,244513,21231],{}," ceremony.",[1114,244516,244517,244520],{},[1007,244518,685],{"href":244519},".\u002Fforget"," — per-record crypto-shred erasure by subject key.",[1114,244522,244523,244525],{},[1007,244524,697],{"href":140989}," — the tamper-evident ledger the audit entries land in.",[1114,244527,101581,244528,1520],{},[1007,244529,244531],{"href":94180,"rel":244530},[3371],[941,244532,199346],{},[2645,244534,244535],{},"html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sfNiH, html code.shiki .sfNiH{--shiki-light:#FF5370;--shiki-default:#FF9CAC;--shiki-dark:#FF9CAC}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}",{"title":1037,"searchDepth":1038,"depth":1038,"links":244537},[244538,244539,244540,244541,244542,244543,244544,244545,244546,244547,244554,244555,244556,244557],{"id":29856,"depth":1041,"text":29857},{"id":111659,"depth":1041,"text":111660},{"id":70092,"depth":1041,"text":70093},{"id":99863,"depth":1041,"text":99864},{"id":242712,"depth":1041,"text":242713},{"id":243238,"depth":1041,"text":243239},{"id":199004,"depth":1041,"text":199005},{"id":243634,"depth":1041,"text":243635},{"id":220043,"depth":1041,"text":220044},{"id":102888,"depth":1041,"text":102889,"children":244548},[244549,244550,244551,244552,244553],{"id":243871,"depth":1038,"text":243874},{"id":243989,"depth":1038,"text":243992},{"id":244081,"depth":1038,"text":244084},{"id":244254,"depth":1038,"text":244257},{"id":244266,"depth":1038,"text":244269},{"id":120264,"depth":1041,"text":120265},{"id":119664,"depth":1041,"text":199249},{"id":68209,"depth":1041,"text":68210},{"id":2620,"depth":1041,"text":2621},"Removal half of `withPortability()`: a non-owner withdraws the data their keyring can decrypt — self-serve (gated) or via a two-party ceremony.",{},{"title":900,"description":244558},"CnASfaJbH-wkOyhcXMwaCB20p8Nor2QlWGZBq9Pb-Xw",{"id":244563,"title":912,"access":12,"apiToc":929,"body":244564,"category":1042,"description":244897,"extension":1044,"meta":244898,"navigation":1042,"nest":2635,"path":913,"seo":244899,"sinceVersion":1042,"status":1049,"stem":914,"subtitle":1042,"tier":6922,"tocExternal":1042,"__hash__":244900},"docs\u002Fstart\u002F1.install.md",{"type":931,"value":244565,"toc":244891},[244566,244569,244578,244582,244585,244608,244638,244652,244691,244724,244728,244731,244745,244751,244825,244849,244853,244863,244881,244883,244888],[934,244567,912],{"id":244568},"install",[937,244570,244571,244572,244574,244575,244577],{},"noy-db is an encrypted, offline-first document store: your data is encrypted on the client before it ever reaches a storage backend, so no server or disk you use has to be trusted with your plaintext — a property called ",[950,244573,29021],{}," (the store keeps zero knowledge of the data it's holding). ",[941,244576,1086],{}," is the single package that gives you this: install it and you have a working, encrypted database instance in two lines of code.",[1022,244579,244581],{"id":244580},"scaffold-a-project-recommended","Scaffold a project (recommended)",[937,244583,244584],{},"One command, one question:",[2217,244586,244588],{"className":33763,"code":244587,"language":33765,"meta":1037,"style":1037},"npm create noy-db@latest\npnpm create noy-db\n",[941,244589,244590,244599],{"__ignoreMap":1037},[2225,244591,244592,244594,244596],{"class":2227,"line":2228},[2225,244593,13678],{"class":6342},[2225,244595,43000],{"class":2252},[2225,244597,244598],{"class":2252}," noy-db@latest\n",[2225,244600,244601,244603,244605],{"class":2227,"line":1041},[2225,244602,41169],{"class":6342},[2225,244604,43000],{"class":2252},[2225,244606,244607],{"class":2252}," noy-db\n",[937,244609,244610,244611,244614,244615,244618,244619,244621,244622,244624,244625,244628,244629,244631,244632,244634,244635,244637],{},"Run in an empty directory, the wizard asks ",[1092,244612,244613],{},"\"Use the recommended setup?\""," — yes scaffolds the ",[941,244616,244617],{},"minimal"," template: ",[941,244620,1086],{}," plus browser storage (",[941,244623,9507],{},"), one ",[941,244626,244627],{},"main.ts",", nothing else. Answer no (or pass any explicit flag) and the full prompts open: Nuxt 4 + Pinia, Vite + Vue 3 + Pinia, Vite + TypeScript, or Electron + Vue 3, with a storage adapter, sample data and a sync target. Run inside an existing Nuxt 4 project, it detects your ",[941,244630,36286],{}," and offers to patch it in place, showing a diff before writing anything. Nothing is installed automatically; the closing lines print the ",[941,244633,92355],{}," \u002F install \u002F ",[941,244636,15645],{}," steps.",[937,244639,244640,244641,244645,244646,244648,244649,244651],{},"Every scaffolded project carries the ",[950,244642,244643],{},[941,244644,1072],{}," command line (from ",[941,244647,5209],{},", since ",[941,244650,7399],{},"). Two of its verbs are the rest of the first hour:",[2217,244653,244655],{"className":33763,"code":244654,"language":33765,"meta":1037,"style":1037},"noy-db doctor               # what is configured, what is missing, what to do about it\nnoy-db add --list           # every store, framework binding, unlock method and export, by layer\nnoy-db add to-aws-s3        # install one, wire it where a config can be edited, and read the trust line it crosses\n",[941,244656,244657,244667,244679],{"__ignoreMap":1037},[2225,244658,244659,244661,244664],{"class":2227,"line":2228},[2225,244660,1072],{"class":6342},[2225,244662,244663],{"class":2252}," doctor",[2225,244665,244666],{"class":2763},"               # what is configured, what is missing, what to do about it\n",[2225,244668,244669,244671,244673,244676],{"class":2227,"line":1041},[2225,244670,1072],{"class":6342},[2225,244672,41172],{"class":2252},[2225,244674,244675],{"class":2252}," --list",[2225,244677,244678],{"class":2763},"           # every store, framework binding, unlock method and export, by layer\n",[2225,244680,244681,244683,244685,244688],{"class":2227,"line":1038},[2225,244682,1072],{"class":6342},[2225,244684,41172],{"class":2252},[2225,244686,244687],{"class":2252}," to-aws-s3",[2225,244689,244690],{"class":2763},"        # install one, wire it where a config can be edited, and read the trust line it crosses\n",[937,244692,244693,244695,244696,244699,244700,36218,244703,244706,244707,1952,244710,244712,244713,244715,244716,244718,244719,244721,244722,1520],{},[941,244694,36199],{}," needs no secret and prints stable check ids (",[941,244697,244698],{},"NOY-S…"," secret, ",[941,244701,244702],{},"NOY-R…",[941,244704,244705],{},"NOY-T…"," topology) so a CI step can gate on ",[941,244708,244709],{},"--mode=prod --fail-level=warn",[941,244711,36269],{}," prints, for every package, what its layer does to your trust boundary — a ",[941,244714,1779],{}," store sees ciphertext only, an ",[941,244717,1791],{}," export sees plaintext by design, an ",[941,244720,1139],{}," provider lets a host you control decrypt a slice — because the prefix is the only tell. The full command list is on ",[1007,244723,213],{"href":214},[1022,244725,244727],{"id":244726},"add-the-library-manually","Add the library manually",[937,244729,244730],{},"Prefer to wire things up yourself, or aren't using Nuxt? Add the core library directly:",[2217,244732,244734],{"className":33763,"code":244733,"language":33765,"meta":1037,"style":1037},"pnpm add @noy-db\u002Fhub\n",[941,244735,244736],{"__ignoreMap":1037},[2225,244737,244738,244740,244742],{"class":2227,"line":2228},[2225,244739,41169],{"class":6342},[2225,244741,41172],{"class":2252},[2225,244743,244744],{"class":2252}," @noy-db\u002Fhub\n",[937,244746,244747,244748,244750],{},"A storage backend is optional to get started — ",[941,244749,1086],{}," ships a built-in in-memory default, so there's nothing else to install for a first run:",[2217,244752,244754],{"className":2219,"code":244753,"language":2221,"meta":1037,"style":1037},"import { createNoydb } from '@noy-db\u002Fhub'\n\nconst db = await createNoydb({ user: 'me', secret: 'correct horse battery staple' })\n\u002F\u002F in-memory, encrypted, zero-knowledge — no store package required\n",[941,244755,244756,244774,244778,244820],{"__ignoreMap":1037},[2225,244757,244758,244760,244762,244764,244766,244768,244770,244772],{"class":2227,"line":2228},[2225,244759,2232],{"class":2231},[2225,244761,2236],{"class":2235},[2225,244763,2744],{"class":2239},[2225,244765,2243],{"class":2235},[2225,244767,2246],{"class":2231},[2225,244769,2249],{"class":2235},[2225,244771,1086],{"class":2252},[2225,244773,2256],{"class":2235},[2225,244775,244776],{"class":2227,"line":1041},[2225,244777,2285],{"emptyLinePlaceholder":2284},[2225,244779,244780,244782,244784,244786,244788,244790,244792,244794,244796,244798,244800,244802,244804,244806,244808,244810,244812,244814,244816,244818],{"class":2227,"line":1038},[2225,244781,2775],{"class":2774},[2225,244783,2778],{"class":2239},[2225,244785,2781],{"class":2235},[2225,244787,2784],{"class":2231},[2225,244789,2744],{"class":2291},[2225,244791,2789],{"class":2239},[2225,244793,2792],{"class":2235},[2225,244795,2795],{"class":2307},[2225,244797,2311],{"class":2235},[2225,244799,2249],{"class":2235},[2225,244801,4212],{"class":2252},[2225,244803,2805],{"class":2235},[2225,244805,2298],{"class":2235},[2225,244807,2810],{"class":2307},[2225,244809,2311],{"class":2235},[2225,244811,2249],{"class":2235},[2225,244813,40260],{"class":2252},[2225,244815,2805],{"class":2235},[2225,244817,2243],{"class":2235},[2225,244819,2362],{"class":2239},[2225,244821,244822],{"class":2227,"line":2288},[2225,244823,244824],{"class":2763},"\u002F\u002F in-memory, encrypted, zero-knowledge — no store package required\n",[937,244826,244827,244829,244830,244832,244833,244835,244836,964,244838,244840,244841,244843,244844,244846,244847,957],{},[941,244828,2134],{}," resolves that in-memory default whenever ",[941,244831,6951],{}," is omitted. It's the same shape as ",[941,244834,9638],{},", just built in — fine for a first run, tests, and prototypes. When you need data to persist, pass an explicit ",[941,244837,6951],{},[941,244839,7461],{}," for local disk, ",[941,244842,9507],{}," for the browser, or any other backend from the ",[941,244845,1779],{}," family (see ",[1007,244848,16076],{"href":51},[1022,244850,244852],{"id":244851},"zero-knowledge-encryption-is-always-on","Zero-knowledge encryption is always on",[937,244854,244855,244856,244859,244860,244862],{},"This holds no matter which store you pick: ",[950,244857,244858],{},"the store never sees plaintext."," Your ",[941,244861,10695],{}," (the secret above) derives a key that encrypts every record before it reaches the store; the store is handed ciphertext envelopes and hands ciphertext envelopes back — it can't run your queries or read your data. That's the mechanism behind \"zero-knowledge\": the party holding the bytes never holds the key.",[937,244864,244865,186701,244867,244869,244870,244872,244873,244875,244876,203399,244878,244880],{},[941,244866,2134],{},[941,244868,10695],{}," (or a ",[941,244871,9953],{}," callback for custom unlock flows) up front — encryption can't be switched off by omission, only explicitly with ",[941,244874,11453],{}," (testing only, no privacy guarantee). The full key hierarchy and envelope format are covered in ",[1007,244877,1516],{"href":66},[1007,244879,924],{"href":925}," lets you verify the property on disk in one minute.",[1022,244882,1455],{"id":1454},[937,244884,244885],{},[1007,244886,244887],{"href":917},"Open your first vault →",[2645,244889,244890],{},"html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}",{"title":1037,"searchDepth":1038,"depth":1038,"links":244892},[244893,244894,244895,244896],{"id":244580,"depth":1041,"text":244581},{"id":244726,"depth":1041,"text":244727},{"id":244851,"depth":1041,"text":244852},{"id":1454,"depth":1041,"text":1455},"Install @noy-db\u002Fhub and create a working, encrypted, zero-knowledge database instance in two lines of code.",{},{"title":912,"description":244897},"pCFuF4kZCKnrL_QSNiompXVdAh-0SG39wpmbIPvTmyI",{"id":244902,"title":916,"access":12,"apiToc":929,"body":244903,"category":1042,"description":245340,"extension":1044,"meta":245341,"navigation":1042,"nest":2635,"path":917,"seo":245342,"sinceVersion":1042,"status":1049,"stem":918,"subtitle":1042,"tier":6922,"tocExternal":1042,"__hash__":245343},"docs\u002Fstart\u002F2.first-vault.md",{"type":931,"value":244904,"toc":245336},[244905,244908,244933,244939,245131,245152,245156,245165,245225,245286,245326,245328,245333],[934,244906,916],{"id":244907},"your-first-vault",[937,244909,244910,244911,244913,244914,244918,244919,968,244921,244923,244924,244926,244927,968,244930,957],{},"Every record lives at a ",[941,244912,6299],{}," triple. A ",[1007,244915,244916],{"href":484},[950,244917,2694],{}," is a tenant \u002F company namespace (",[941,244920,56696],{},[941,244922,56699],{},"); a ",[950,244925,6471],{}," is a typed record set inside it (",[941,244928,244929],{},"\"invoices\"",[941,244931,244932],{},"\"clients\"",[937,244934,244935,244936,244938],{},"Continuing from ",[1007,244937,912],{"href":913},", open a vault and create a typed collection:",[2217,244940,244942],{"className":2219,"code":244941,"language":2221,"meta":1037,"style":1037},"type Invoice = { id: string; amount: number; customer: string }\n\nconst vault = await db.openVault('acme')\nconst invoices = vault.collection\u003CInvoice>('invoices')\n\nawait invoices.put('inv-1', { id: 'inv-1', amount: 8500, customer: 'ABC Trading' })\nconst inv = await invoices.get('inv-1')\n\u002F\u002F → { id: 'inv-1', amount: 8500, customer: 'ABC Trading' }\n",[941,244943,244944,244978,244982,245008,245038,245042,245100,245126],{"__ignoreMap":1037},[2225,244945,244946,244948,244950,244952,244954,244956,244958,244960,244962,244964,244966,244968,244970,244972,244974,244976],{"class":2227,"line":2228},[2225,244947,6339],{"class":2774},[2225,244949,6343],{"class":6342},[2225,244951,6346],{"class":2235},[2225,244953,2236],{"class":2235},[2225,244955,7070],{"class":2307},[2225,244957,2311],{"class":2235},[2225,244959,6366],{"class":6342},[2225,244961,3769],{"class":2235},[2225,244963,6351],{"class":2307},[2225,244965,2311],{"class":2235},[2225,244967,6356],{"class":6342},[2225,244969,3769],{"class":2235},[2225,244971,88787],{"class":2307},[2225,244973,2311],{"class":2235},[2225,244975,6366],{"class":6342},[2225,244977,6369],{"class":2235},[2225,244979,244980],{"class":2227,"line":1041},[2225,244981,2285],{"emptyLinePlaceholder":2284},[2225,244983,244984,244986,244988,244990,244992,244994,244996,244998,245000,245002,245004,245006],{"class":2227,"line":1038},[2225,244985,2775],{"class":2774},[2225,244987,2835],{"class":2239},[2225,244989,2781],{"class":2235},[2225,244991,2784],{"class":2231},[2225,244993,2842],{"class":2239},[2225,244995,1520],{"class":2235},[2225,244997,2847],{"class":2291},[2225,244999,2789],{"class":2239},[2225,245001,2805],{"class":2235},[2225,245003,6450],{"class":2252},[2225,245005,2805],{"class":2235},[2225,245007,2362],{"class":2239},[2225,245009,245010,245012,245014,245016,245018,245020,245022,245024,245026,245028,245030,245032,245034,245036],{"class":2227,"line":2288},[2225,245011,2775],{"class":2774},[2225,245013,6461],{"class":2239},[2225,245015,2781],{"class":2235},[2225,245017,6466],{"class":2239},[2225,245019,1520],{"class":2235},[2225,245021,6471],{"class":2291},[2225,245023,6474],{"class":2235},[2225,245025,6477],{"class":6342},[2225,245027,6480],{"class":2235},[2225,245029,2789],{"class":2239},[2225,245031,2805],{"class":2235},[2225,245033,6487],{"class":2252},[2225,245035,2805],{"class":2235},[2225,245037,2362],{"class":2239},[2225,245039,245040],{"class":2227,"line":2304},[2225,245041,2285],{"emptyLinePlaceholder":2284},[2225,245043,245044,245046,245048,245050,245052,245054,245056,245058,245060,245062,245064,245066,245068,245070,245072,245074,245076,245078,245080,245083,245085,245087,245089,245091,245094,245096,245098],{"class":2227,"line":2356},[2225,245045,6500],{"class":2231},[2225,245047,6503],{"class":2239},[2225,245049,1520],{"class":2235},[2225,245051,3161],{"class":2291},[2225,245053,2789],{"class":2239},[2225,245055,2805],{"class":2235},[2225,245057,6514],{"class":2252},[2225,245059,2805],{"class":2235},[2225,245061,2298],{"class":2235},[2225,245063,2236],{"class":2235},[2225,245065,7070],{"class":2307},[2225,245067,2311],{"class":2235},[2225,245069,2249],{"class":2235},[2225,245071,6514],{"class":2252},[2225,245073,2805],{"class":2235},[2225,245075,2298],{"class":2235},[2225,245077,6351],{"class":2307},[2225,245079,2311],{"class":2235},[2225,245081,245082],{"class":6527}," 8500",[2225,245084,2298],{"class":2235},[2225,245086,88787],{"class":2307},[2225,245088,2311],{"class":2235},[2225,245090,2249],{"class":2235},[2225,245092,245093],{"class":2252},"ABC Trading",[2225,245095,2805],{"class":2235},[2225,245097,2243],{"class":2235},[2225,245099,2362],{"class":2239},[2225,245101,245102,245104,245106,245108,245110,245112,245114,245116,245118,245120,245122,245124],{"class":2227,"line":2830},[2225,245103,2775],{"class":2774},[2225,245105,6553],{"class":2239},[2225,245107,2781],{"class":2235},[2225,245109,2784],{"class":2231},[2225,245111,6503],{"class":2239},[2225,245113,1520],{"class":2235},[2225,245115,3158],{"class":2291},[2225,245117,2789],{"class":2239},[2225,245119,2805],{"class":2235},[2225,245121,6514],{"class":2252},[2225,245123,2805],{"class":2235},[2225,245125,2362],{"class":2239},[2225,245127,245128],{"class":2227,"line":2861},[2225,245129,245130],{"class":2763},"\u002F\u002F → { id: 'inv-1', amount: 8500, customer: 'ABC Trading' }\n",[937,245132,245133,245135,245136,28935,245139,968,245141,968,245143,2462,245145,1952,245147,6648,245149,245151],{},[941,245134,1749],{}," opens and caches a vault — calling it again with the same name is idempotent. The type parameter on ",[941,245137,245138],{},"collection\u003CInvoice>()",[941,245140,3158],{},[941,245142,3161],{},[941,245144,5286],{},[941,245146,3239],{},[941,245148,14639],{},[941,245150,6651],{}," if no record exists at that id — the store and any transport in between only ever handled the encrypted form.",[1022,245153,245155],{"id":245154},"query-it","Query it",[937,245157,245158,245159,1261,245161,1261,245163,2311],{},"The collection's chainable query builder gives you ",[941,245160,3016],{},[941,245162,3019],{},[941,245164,3022],{},[2217,245166,245168],{"className":2219,"code":245167,"language":2221,"meta":1037,"style":1037},"const bigInvoices = await invoices.query()\n  .where('amount', '>', 1000)\n  .toArray()\n",[941,245169,245170,245189,245217],{"__ignoreMap":1037},[2225,245171,245172,245174,245177,245179,245181,245183,245185,245187],{"class":2227,"line":2228},[2225,245173,2775],{"class":2774},[2225,245175,245176],{"class":2239}," bigInvoices ",[2225,245178,2781],{"class":2235},[2225,245180,2784],{"class":2231},[2225,245182,6503],{"class":2239},[2225,245184,1520],{"class":2235},[2225,245186,5286],{"class":2291},[2225,245188,8146],{"class":2239},[2225,245190,245191,245193,245195,245197,245199,245201,245203,245205,245207,245209,245211,245213,245215],{"class":2227,"line":1041},[2225,245192,26235],{"class":2235},[2225,245194,3016],{"class":2291},[2225,245196,2789],{"class":2239},[2225,245198,2805],{"class":2235},[2225,245200,6617],{"class":2252},[2225,245202,2805],{"class":2235},[2225,245204,2298],{"class":2235},[2225,245206,2249],{"class":2235},[2225,245208,6480],{"class":2252},[2225,245210,2805],{"class":2235},[2225,245212,2298],{"class":2235},[2225,245214,26290],{"class":6527},[2225,245216,2362],{"class":2239},[2225,245218,245219,245221,245223],{"class":2227,"line":1038},[2225,245220,26235],{"class":2235},[2225,245222,3230],{"class":2291},[2225,245224,8146],{"class":2239},[2217,245226,245228],{"className":6604,"code":245227,"language":141,"meta":1037,"style":1037},"[\n  { \"id\": \"inv-1\", \"amount\": 8500, \"customer\": \"ABC Trading\" }\n]\n",[941,245229,245230,245234,245282],{"__ignoreMap":1037},[2225,245231,245232],{"class":2227,"line":2228},[2225,245233,26349],{"class":2235},[2225,245235,245236,245238,245240,245242,245244,245246,245248,245250,245252,245254,245256,245258,245260,245262,245264,245266,245268,245270,245272,245274,245276,245278,245280],{"class":2227,"line":1041},[2225,245237,26354],{"class":2235},[2225,245239,6614],{"class":2235},[2225,245241,6983],{"class":2774},[2225,245243,6620],{"class":2235},[2225,245245,2311],{"class":2235},[2225,245247,6614],{"class":2235},[2225,245249,6514],{"class":2252},[2225,245251,6620],{"class":2235},[2225,245253,2298],{"class":2235},[2225,245255,6614],{"class":2235},[2225,245257,6617],{"class":2774},[2225,245259,6620],{"class":2235},[2225,245261,2311],{"class":2235},[2225,245263,245082],{"class":6527},[2225,245265,2298],{"class":2235},[2225,245267,6614],{"class":2235},[2225,245269,88649],{"class":2774},[2225,245271,6620],{"class":2235},[2225,245273,2311],{"class":2235},[2225,245275,6614],{"class":2235},[2225,245277,245093],{"class":2252},[2225,245279,6620],{"class":2235},[2225,245281,6369],{"class":2235},[2225,245283,245284],{"class":2227,"line":1038},[2225,245285,26484],{"class":2235},[937,245287,245288,245290,245291,968,245293,968,245295,968,245297,968,245299,968,245301,968,245303,968,245305,968,245308,2462,245310,245312,245313,1261,245315,1261,245317,162552,245319,245321,245322,1261,245324,33515],{},[941,245289,26648],{}," supports ",[941,245292,21901],{},[941,245294,181194],{},[941,245296,6474],{},[941,245298,100227],{},[941,245300,6480],{},[941,245302,26921],{},[941,245304,4891],{},[941,245306,245307],{},"contains",[941,245309,8705],{},[941,245311,28291],{},". Chain ",[941,245314,41940],{},[941,245316,200151],{},[941,245318,200154],{},[941,245320,164984],{},", or call ",[941,245323,164987],{},[941,245325,65247],{},[1022,245327,1455],{"id":1454},[937,245329,245330],{},[1007,245331,245332],{"href":921},"Opt into your first service →",[2645,245334,245335],{},"html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":1037,"searchDepth":1038,"depth":1038,"links":245337},[245338,245339],{"id":245154,"depth":1041,"text":245155},{"id":1454,"depth":1041,"text":1455},"Open a vault, create a typed collection, put and get a record, and run a basic query.",{},{"title":916,"description":245340},"Pt94VFYeiYPOm-nrGlhfI5rBOUDV-3lNc359szKF0YY",{"id":245345,"title":920,"access":12,"apiToc":929,"body":245346,"category":1042,"description":245937,"extension":1044,"meta":245938,"navigation":1042,"nest":1047,"path":921,"seo":245939,"sinceVersion":1042,"status":1049,"stem":922,"subtitle":1042,"tier":6922,"tocExternal":1042,"__hash__":245940},"docs\u002Fstart\u002F3.first-service.md",{"type":931,"value":245347,"toc":245934},[245348,245351,245369,245379,245731,245849,245874,245877,245906,245923,245925,245931],[934,245349,920],{"id":245350},"your-first-service",[937,245352,245353,245354,245357,245358,245360,245361,245363,245364,245366,245367,1520],{},"The vault and collection you used on the ",[1007,245355,245356],{"href":917},"previous page"," are the always-on ",[950,245359,2635],{}," — every install ships them. Everything past that is opt-in: a ",[950,245362,1047],{}," is a tree-shakeable module behind a ",[941,245365,943],{}," strategy factory, and its code only enters your bundle once you pass that factory to ",[941,245368,2134],{},[937,245370,245371,245373,245374,245376,245377,2311],{},[941,245372,697],{}," is a good first one to try: it keeps a full audit trail of every write to a collection — a snapshot of each record's prior state, plus a tamper-evident log — the ",[950,245375,20923],{}," — of who changed what and when. Opt in with ",[941,245378,6719],{},[2217,245380,245382],{"className":2219,"code":245381,"language":2221,"meta":1037,"style":1037},"import { createNoydb } from '@noy-db\u002Fhub'\nimport { withHistory } from '@noy-db\u002Fhub\u002Fhistory'\n\ntype Invoice = { id: string; amount: number; customer: string }\n\nconst db = await createNoydb({\n  user: 'me',\n  secret: 'correct horse battery staple',\n  historyStrategy: withHistory(),\n})\n\nconst vault = await db.openVault('acme')\nconst invoices = vault.collection\u003CInvoice>('invoices')\n\nawait invoices.put('inv-1', { id: 'inv-1', amount: 8500, customer: 'ABC Trading' })\nawait invoices.put('inv-1', { id: 'inv-1', amount: 9000, customer: 'ABC Trading' })\n\nconst versions = await invoices.history('inv-1')\n",[941,245383,245384,245402,245420,245424,245458,245462,245478,245492,245506,245518,245524,245528,245554,245584,245588,245644,245701,245705],{"__ignoreMap":1037},[2225,245385,245386,245388,245390,245392,245394,245396,245398,245400],{"class":2227,"line":2228},[2225,245387,2232],{"class":2231},[2225,245389,2236],{"class":2235},[2225,245391,2744],{"class":2239},[2225,245393,2243],{"class":2235},[2225,245395,2246],{"class":2231},[2225,245397,2249],{"class":2235},[2225,245399,1086],{"class":2252},[2225,245401,2256],{"class":2235},[2225,245403,245404,245406,245408,245410,245412,245414,245416,245418],{"class":2227,"line":1041},[2225,245405,2232],{"class":2231},[2225,245407,2236],{"class":2235},[2225,245409,4142],{"class":2239},[2225,245411,2243],{"class":2235},[2225,245413,2246],{"class":2231},[2225,245415,2249],{"class":2235},[2225,245417,4151],{"class":2252},[2225,245419,2256],{"class":2235},[2225,245421,245422],{"class":2227,"line":1038},[2225,245423,2285],{"emptyLinePlaceholder":2284},[2225,245425,245426,245428,245430,245432,245434,245436,245438,245440,245442,245444,245446,245448,245450,245452,245454,245456],{"class":2227,"line":2288},[2225,245427,6339],{"class":2774},[2225,245429,6343],{"class":6342},[2225,245431,6346],{"class":2235},[2225,245433,2236],{"class":2235},[2225,245435,7070],{"class":2307},[2225,245437,2311],{"class":2235},[2225,245439,6366],{"class":6342},[2225,245441,3769],{"class":2235},[2225,245443,6351],{"class":2307},[2225,245445,2311],{"class":2235},[2225,245447,6356],{"class":6342},[2225,245449,3769],{"class":2235},[2225,245451,88787],{"class":2307},[2225,245453,2311],{"class":2235},[2225,245455,6366],{"class":6342},[2225,245457,6369],{"class":2235},[2225,245459,245460],{"class":2227,"line":2304},[2225,245461,2285],{"emptyLinePlaceholder":2284},[2225,245463,245464,245466,245468,245470,245472,245474,245476],{"class":2227,"line":2356},[2225,245465,2775],{"class":2774},[2225,245467,2778],{"class":2239},[2225,245469,2781],{"class":2235},[2225,245471,2784],{"class":2231},[2225,245473,2744],{"class":2291},[2225,245475,2789],{"class":2239},[2225,245477,4200],{"class":2235},[2225,245479,245480,245482,245484,245486,245488,245490],{"class":2227,"line":2830},[2225,245481,4205],{"class":2307},[2225,245483,2311],{"class":2235},[2225,245485,2249],{"class":2235},[2225,245487,4212],{"class":2252},[2225,245489,2805],{"class":2235},[2225,245491,1707],{"class":2235},[2225,245493,245494,245496,245498,245500,245502,245504],{"class":2227,"line":2861},[2225,245495,10423],{"class":2307},[2225,245497,2311],{"class":2235},[2225,245499,2249],{"class":2235},[2225,245501,40260],{"class":2252},[2225,245503,2805],{"class":2235},[2225,245505,1707],{"class":2235},[2225,245507,245508,245510,245512,245514,245516],{"class":2227,"line":4246},[2225,245509,4221],{"class":2307},[2225,245511,2311],{"class":2235},[2225,245513,4142],{"class":2291},[2225,245515,4228],{"class":2239},[2225,245517,1707],{"class":2235},[2225,245519,245520,245522],{"class":2227,"line":4253},[2225,245521,2359],{"class":2235},[2225,245523,2362],{"class":2239},[2225,245525,245526],{"class":2227,"line":4259},[2225,245527,2285],{"emptyLinePlaceholder":2284},[2225,245529,245530,245532,245534,245536,245538,245540,245542,245544,245546,245548,245550,245552],{"class":2227,"line":6548},[2225,245531,2775],{"class":2774},[2225,245533,2835],{"class":2239},[2225,245535,2781],{"class":2235},[2225,245537,2784],{"class":2231},[2225,245539,2842],{"class":2239},[2225,245541,1520],{"class":2235},[2225,245543,2847],{"class":2291},[2225,245545,2789],{"class":2239},[2225,245547,2805],{"class":2235},[2225,245549,6450],{"class":2252},[2225,245551,2805],{"class":2235},[2225,245553,2362],{"class":2239},[2225,245555,245556,245558,245560,245562,245564,245566,245568,245570,245572,245574,245576,245578,245580,245582],{"class":2227,"line":6576},[2225,245557,2775],{"class":2774},[2225,245559,6461],{"class":2239},[2225,245561,2781],{"class":2235},[2225,245563,6466],{"class":2239},[2225,245565,1520],{"class":2235},[2225,245567,6471],{"class":2291},[2225,245569,6474],{"class":2235},[2225,245571,6477],{"class":6342},[2225,245573,6480],{"class":2235},[2225,245575,2789],{"class":2239},[2225,245577,2805],{"class":2235},[2225,245579,6487],{"class":2252},[2225,245581,2805],{"class":2235},[2225,245583,2362],{"class":2239},[2225,245585,245586],{"class":2227,"line":6582},[2225,245587,2285],{"emptyLinePlaceholder":2284},[2225,245589,245590,245592,245594,245596,245598,245600,245602,245604,245606,245608,245610,245612,245614,245616,245618,245620,245622,245624,245626,245628,245630,245632,245634,245636,245638,245640,245642],{"class":2227,"line":6587},[2225,245591,6500],{"class":2231},[2225,245593,6503],{"class":2239},[2225,245595,1520],{"class":2235},[2225,245597,3161],{"class":2291},[2225,245599,2789],{"class":2239},[2225,245601,2805],{"class":2235},[2225,245603,6514],{"class":2252},[2225,245605,2805],{"class":2235},[2225,245607,2298],{"class":2235},[2225,245609,2236],{"class":2235},[2225,245611,7070],{"class":2307},[2225,245613,2311],{"class":2235},[2225,245615,2249],{"class":2235},[2225,245617,6514],{"class":2252},[2225,245619,2805],{"class":2235},[2225,245621,2298],{"class":2235},[2225,245623,6351],{"class":2307},[2225,245625,2311],{"class":2235},[2225,245627,245082],{"class":6527},[2225,245629,2298],{"class":2235},[2225,245631,88787],{"class":2307},[2225,245633,2311],{"class":2235},[2225,245635,2249],{"class":2235},[2225,245637,245093],{"class":2252},[2225,245639,2805],{"class":2235},[2225,245641,2243],{"class":2235},[2225,245643,2362],{"class":2239},[2225,245645,245646,245648,245650,245652,245654,245656,245658,245660,245662,245664,245666,245668,245670,245672,245674,245676,245678,245680,245682,245685,245687,245689,245691,245693,245695,245697,245699],{"class":2227,"line":7174},[2225,245647,6500],{"class":2231},[2225,245649,6503],{"class":2239},[2225,245651,1520],{"class":2235},[2225,245653,3161],{"class":2291},[2225,245655,2789],{"class":2239},[2225,245657,2805],{"class":2235},[2225,245659,6514],{"class":2252},[2225,245661,2805],{"class":2235},[2225,245663,2298],{"class":2235},[2225,245665,2236],{"class":2235},[2225,245667,7070],{"class":2307},[2225,245669,2311],{"class":2235},[2225,245671,2249],{"class":2235},[2225,245673,6514],{"class":2252},[2225,245675,2805],{"class":2235},[2225,245677,2298],{"class":2235},[2225,245679,6351],{"class":2307},[2225,245681,2311],{"class":2235},[2225,245683,245684],{"class":6527}," 9000",[2225,245686,2298],{"class":2235},[2225,245688,88787],{"class":2307},[2225,245690,2311],{"class":2235},[2225,245692,2249],{"class":2235},[2225,245694,245093],{"class":2252},[2225,245696,2805],{"class":2235},[2225,245698,2243],{"class":2235},[2225,245700,2362],{"class":2239},[2225,245702,245703],{"class":2227,"line":7180},[2225,245704,2285],{"emptyLinePlaceholder":2284},[2225,245706,245707,245709,245711,245713,245715,245717,245719,245721,245723,245725,245727,245729],{"class":2227,"line":7220},[2225,245708,2775],{"class":2774},[2225,245710,159166],{"class":2239},[2225,245712,2781],{"class":2235},[2225,245714,2784],{"class":2231},[2225,245716,6503],{"class":2239},[2225,245718,1520],{"class":2235},[2225,245720,697],{"class":2291},[2225,245722,2789],{"class":2239},[2225,245724,2805],{"class":2235},[2225,245726,6514],{"class":2252},[2225,245728,2805],{"class":2235},[2225,245730,2362],{"class":2239},[2217,245732,245734],{"className":6604,"code":245733,"language":141,"meta":1037,"style":1037},"[\n  { \"version\": 1, \"timestamp\": \"2026-07-08T10:01:00.000Z\", \"userId\": \"me\", \"record\": { \"id\": \"inv-1\", \"amount\": 8500, \"customer\": \"ABC Trading\" } }\n]\n",[941,245735,245736,245740,245845],{"__ignoreMap":1037},[2225,245737,245738],{"class":2227,"line":2228},[2225,245739,26349],{"class":2235},[2225,245741,245742,245744,245746,245748,245750,245752,245754,245756,245758,245760,245762,245764,245766,245769,245771,245773,245775,245777,245779,245781,245783,245785,245787,245789,245791,245793,245795,245797,245799,245801,245803,245805,245807,245809,245811,245813,245815,245817,245819,245821,245823,245825,245827,245829,245831,245833,245835,245837,245839,245841,245843],{"class":2227,"line":1041},[2225,245743,26354],{"class":2235},[2225,245745,6614],{"class":2235},[2225,245747,52564],{"class":2774},[2225,245749,6620],{"class":2235},[2225,245751,2311],{"class":2235},[2225,245753,9377],{"class":6527},[2225,245755,2298],{"class":2235},[2225,245757,6614],{"class":2235},[2225,245759,59900],{"class":2774},[2225,245761,6620],{"class":2235},[2225,245763,2311],{"class":2235},[2225,245765,6614],{"class":2235},[2225,245767,245768],{"class":2252},"2026-07-08T10:01:00.000Z",[2225,245770,6620],{"class":2235},[2225,245772,2298],{"class":2235},[2225,245774,6614],{"class":2235},[2225,245776,10749],{"class":2774},[2225,245778,6620],{"class":2235},[2225,245780,2311],{"class":2235},[2225,245782,6614],{"class":2235},[2225,245784,4212],{"class":2252},[2225,245786,6620],{"class":2235},[2225,245788,2298],{"class":2235},[2225,245790,6614],{"class":2235},[2225,245792,24158],{"class":2774},[2225,245794,6620],{"class":2235},[2225,245796,2311],{"class":2235},[2225,245798,2236],{"class":2235},[2225,245800,6614],{"class":2235},[2225,245802,6983],{"class":6342},[2225,245804,6620],{"class":2235},[2225,245806,2311],{"class":2235},[2225,245808,6614],{"class":2235},[2225,245810,6514],{"class":2252},[2225,245812,6620],{"class":2235},[2225,245814,2298],{"class":2235},[2225,245816,6614],{"class":2235},[2225,245818,6617],{"class":6342},[2225,245820,6620],{"class":2235},[2225,245822,2311],{"class":2235},[2225,245824,245082],{"class":6527},[2225,245826,2298],{"class":2235},[2225,245828,6614],{"class":2235},[2225,245830,88649],{"class":6342},[2225,245832,6620],{"class":2235},[2225,245834,2311],{"class":2235},[2225,245836,6614],{"class":2235},[2225,245838,245093],{"class":2252},[2225,245840,6620],{"class":2235},[2225,245842,2243],{"class":2235},[2225,245844,6369],{"class":2235},[2225,245846,245847],{"class":2227,"line":1038},[2225,245848,26484],{"class":2235},[937,245850,245851,245853,245854,245856,245857,245860,245861,245864,245865,245867,245868,245870,245871,245873],{},[941,245852,158461],{}," returns only ",[1092,245855,234854],{}," versions, newest first — the current live version (",[941,245858,245859],{},"amount: 9000",") isn't in the list; read it with ",[941,245862,245863],{},"invoices.get('inv-1')"," as usual. That's why one ",[941,245866,3161],{}," before it produced zero history entries and this second ",[941,245869,3161],{}," produced exactly one: each ",[941,245872,3161],{}," snapshots the version it's about to overwrite.",[937,245875,245876],{},"That snapshot-plus-ledger write is what every other history capability reads from:",[1111,245878,245879,245885,245891,245900],{},[1114,245880,245881,245884],{},[941,245882,245883],{},"invoices.getVersion('inv-1', 1)"," — load one past version's content directly.",[1114,245886,245887,245890],{},[941,245888,245889],{},"invoices.diff('inv-1', 1)"," — field-level diff between version 1 and the current record.",[1114,245892,245893,245896,245897,245899],{},[941,245894,245895],{},"invoices.revert('inv-1', 1)"," — re-",[941,245898,3161],{}," version 1's content as a brand-new version.",[1114,245901,245902,245905],{},[941,245903,245904],{},"vault.at('2026-07-08T10:01:30Z')"," — a read-only view of the whole vault as it stood at that instant.",[937,245907,90416,245908,968,245910,968,245912,2462,245914,245916,245917,245919,245920,245922],{},[941,245909,20172],{},[941,245911,158461],{},[941,245913,21843],{},[941,245915,21849],{}," throw pointing back to ",[941,245918,4151],{},", and none of that code reaches your bundle. See ",[1007,245921,697],{"href":61702}," for the full API — including the ledger, retention, and point-in-time reads in depth.",[1022,245924,1455],{"id":1454},[937,245926,245927,245930],{},[1007,245928,245929],{"href":925},"See the ciphertext →"," — persist the vault to disk and verify with your own eyes what the store holds.",[2645,245932,245933],{},"html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":1037,"searchDepth":1038,"depth":1038,"links":245935},[245936],{"id":1454,"depth":1041,"text":1455},"Opt into the history service with withHistory() and read back a record's past versions.",{},{"title":920,"description":245937},"rGw7GlqLp0lyVHkq-nkxGB92WfBFgSRII6z9d2W-fJ8",{"id":245942,"title":924,"access":12,"apiToc":929,"body":245943,"category":1042,"description":246583,"extension":1044,"meta":246584,"navigation":1042,"nest":2635,"path":925,"seo":246585,"sinceVersion":1042,"status":1049,"stem":926,"subtitle":1042,"tier":6922,"tocExternal":1042,"__hash__":246586},"docs\u002Fstart\u002F4.see-the-ciphertext.md",{"type":931,"value":245944,"toc":246578},[245945,245948,245957,245971,246204,246210,246324,246358,246371,246375,246381,246531,246542,246546,246557,246559,246575],[934,245946,924],{"id":245947},"see-the-ciphertext",[937,245949,960,245950,245953,245954,245956],{},[1007,245951,245952],{"href":913},"Install page"," claims the store never sees your plaintext. Don't take the claim — look at the bytes. Swap the in-memory default for ",[941,245955,7461],{},", which persists each record as a JSON file you can open:",[2217,245958,245960],{"className":33763,"code":245959,"language":33765,"meta":1037,"style":1037},"pnpm add @noy-db\u002Fto-file\n",[941,245961,245962],{"__ignoreMap":1037},[2225,245963,245964,245966,245968],{"class":2227,"line":2228},[2225,245965,41169],{"class":6342},[2225,245967,41172],{"class":2252},[2225,245969,245970],{"class":2252}," @noy-db\u002Fto-file\n",[2217,245972,245974],{"className":2219,"code":245973,"language":2221,"meta":1037,"style":1037},"import { createNoydb } from '@noy-db\u002Fhub'\nimport { toFile } from '@noy-db\u002Fto-file'\n\nconst db = await createNoydb({\n  store: toFile({ dir: '.\u002Fdata' }),\n  user: 'me',\n  secret: 'correct horse battery staple',\n})\n\nconst vault = await db.openVault('acme')\nconst invoices = vault.collection('invoices')\nawait invoices.put('inv-1', { id: 'inv-1', amount: 8500, customer: 'ABC Trading' })\n",[941,245975,245976,245994,246012,246016,246032,246060,246074,246088,246094,246098,246124,246148],{"__ignoreMap":1037},[2225,245977,245978,245980,245982,245984,245986,245988,245990,245992],{"class":2227,"line":2228},[2225,245979,2232],{"class":2231},[2225,245981,2236],{"class":2235},[2225,245983,2744],{"class":2239},[2225,245985,2243],{"class":2235},[2225,245987,2246],{"class":2231},[2225,245989,2249],{"class":2235},[2225,245991,1086],{"class":2252},[2225,245993,2256],{"class":2235},[2225,245995,245996,245998,246000,246002,246004,246006,246008,246010],{"class":2227,"line":1041},[2225,245997,2232],{"class":2231},[2225,245999,2236],{"class":2235},[2225,246001,15573],{"class":2239},[2225,246003,2243],{"class":2235},[2225,246005,2246],{"class":2231},[2225,246007,2249],{"class":2235},[2225,246009,9551],{"class":2252},[2225,246011,2256],{"class":2235},[2225,246013,246014],{"class":2227,"line":1038},[2225,246015,2285],{"emptyLinePlaceholder":2284},[2225,246017,246018,246020,246022,246024,246026,246028,246030],{"class":2227,"line":2288},[2225,246019,2775],{"class":2774},[2225,246021,2778],{"class":2239},[2225,246023,2781],{"class":2235},[2225,246025,2784],{"class":2231},[2225,246027,2744],{"class":2291},[2225,246029,2789],{"class":2239},[2225,246031,4200],{"class":2235},[2225,246033,246034,246036,246038,246040,246042,246044,246046,246048,246050,246052,246054,246056,246058],{"class":2227,"line":2304},[2225,246035,15608],{"class":2307},[2225,246037,2311],{"class":2235},[2225,246039,15573],{"class":2291},[2225,246041,2789],{"class":2239},[2225,246043,2792],{"class":2235},[2225,246045,15619],{"class":2307},[2225,246047,2311],{"class":2235},[2225,246049,2249],{"class":2235},[2225,246051,15626],{"class":2252},[2225,246053,2805],{"class":2235},[2225,246055,2243],{"class":2235},[2225,246057,1381],{"class":2239},[2225,246059,1707],{"class":2235},[2225,246061,246062,246064,246066,246068,246070,246072],{"class":2227,"line":2356},[2225,246063,4205],{"class":2307},[2225,246065,2311],{"class":2235},[2225,246067,2249],{"class":2235},[2225,246069,4212],{"class":2252},[2225,246071,2805],{"class":2235},[2225,246073,1707],{"class":2235},[2225,246075,246076,246078,246080,246082,246084,246086],{"class":2227,"line":2830},[2225,246077,10423],{"class":2307},[2225,246079,2311],{"class":2235},[2225,246081,2249],{"class":2235},[2225,246083,40260],{"class":2252},[2225,246085,2805],{"class":2235},[2225,246087,1707],{"class":2235},[2225,246089,246090,246092],{"class":2227,"line":2861},[2225,246091,2359],{"class":2235},[2225,246093,2362],{"class":2239},[2225,246095,246096],{"class":2227,"line":4246},[2225,246097,2285],{"emptyLinePlaceholder":2284},[2225,246099,246100,246102,246104,246106,246108,246110,246112,246114,246116,246118,246120,246122],{"class":2227,"line":4253},[2225,246101,2775],{"class":2774},[2225,246103,2835],{"class":2239},[2225,246105,2781],{"class":2235},[2225,246107,2784],{"class":2231},[2225,246109,2842],{"class":2239},[2225,246111,1520],{"class":2235},[2225,246113,2847],{"class":2291},[2225,246115,2789],{"class":2239},[2225,246117,2805],{"class":2235},[2225,246119,6450],{"class":2252},[2225,246121,2805],{"class":2235},[2225,246123,2362],{"class":2239},[2225,246125,246126,246128,246130,246132,246134,246136,246138,246140,246142,246144,246146],{"class":2227,"line":4259},[2225,246127,2775],{"class":2774},[2225,246129,6461],{"class":2239},[2225,246131,2781],{"class":2235},[2225,246133,6466],{"class":2239},[2225,246135,1520],{"class":2235},[2225,246137,6471],{"class":2291},[2225,246139,2789],{"class":2239},[2225,246141,2805],{"class":2235},[2225,246143,6487],{"class":2252},[2225,246145,2805],{"class":2235},[2225,246147,2362],{"class":2239},[2225,246149,246150,246152,246154,246156,246158,246160,246162,246164,246166,246168,246170,246172,246174,246176,246178,246180,246182,246184,246186,246188,246190,246192,246194,246196,246198,246200,246202],{"class":2227,"line":6548},[2225,246151,6500],{"class":2231},[2225,246153,6503],{"class":2239},[2225,246155,1520],{"class":2235},[2225,246157,3161],{"class":2291},[2225,246159,2789],{"class":2239},[2225,246161,2805],{"class":2235},[2225,246163,6514],{"class":2252},[2225,246165,2805],{"class":2235},[2225,246167,2298],{"class":2235},[2225,246169,2236],{"class":2235},[2225,246171,7070],{"class":2307},[2225,246173,2311],{"class":2235},[2225,246175,2249],{"class":2235},[2225,246177,6514],{"class":2252},[2225,246179,2805],{"class":2235},[2225,246181,2298],{"class":2235},[2225,246183,6351],{"class":2307},[2225,246185,2311],{"class":2235},[2225,246187,245082],{"class":6527},[2225,246189,2298],{"class":2235},[2225,246191,88787],{"class":2307},[2225,246193,2311],{"class":2235},[2225,246195,2249],{"class":2235},[2225,246197,245093],{"class":2252},[2225,246199,2805],{"class":2235},[2225,246201,2243],{"class":2235},[2225,246203,2362],{"class":2239},[937,246205,246206,246207,2311],{},"Now open what the store wrote — ",[941,246208,246209],{},"cat .\u002Fdata\u002Facme\u002Finvoices\u002Finv-1.json",[2217,246211,246213],{"className":6604,"code":246212,"language":141,"meta":1037,"style":1037},"{\n  \"_noydb\": 1,\n  \"_v\": 1,\n  \"_ts\": \"2026-07-15T15:31:54.149Z\",\n  \"_iv\": \"OIGbecaDduQOQqO+\",\n  \"_data\": \"ruPaj9T50xSQoRfFM3y8NfmlXApnBRzz\u002FYm3XaqGlqZzao93e\u002FLR4Pox+d0N1nJoWmhypqXAIaEaJZRBcITEmsY9jiFD\",\n  \"_by\": \"me\"\n}\n",[941,246214,246215,246219,246233,246247,246266,246285,246304,246320],{"__ignoreMap":1037},[2225,246216,246217],{"class":2227,"line":2228},[2225,246218,4200],{"class":2235},[2225,246220,246221,246223,246225,246227,246229,246231],{"class":2227,"line":1041},[2225,246222,9368],{"class":2235},[2225,246224,1697],{"class":2774},[2225,246226,6620],{"class":2235},[2225,246228,2311],{"class":2235},[2225,246230,9377],{"class":6527},[2225,246232,1707],{"class":2235},[2225,246234,246235,246237,246239,246241,246243,246245],{"class":2227,"line":1038},[2225,246236,9368],{"class":2235},[2225,246238,1700],{"class":2774},[2225,246240,6620],{"class":2235},[2225,246242,2311],{"class":2235},[2225,246244,9377],{"class":6527},[2225,246246,1707],{"class":2235},[2225,246248,246249,246251,246253,246255,246257,246259,246262,246264],{"class":2227,"line":2288},[2225,246250,9368],{"class":2235},[2225,246252,1703],{"class":2774},[2225,246254,6620],{"class":2235},[2225,246256,2311],{"class":2235},[2225,246258,6614],{"class":2235},[2225,246260,246261],{"class":2252},"2026-07-15T15:31:54.149Z",[2225,246263,6620],{"class":2235},[2225,246265,1707],{"class":2235},[2225,246267,246268,246270,246272,246274,246276,246278,246281,246283],{"class":2227,"line":2304},[2225,246269,9368],{"class":2235},[2225,246271,1143],{"class":2774},[2225,246273,6620],{"class":2235},[2225,246275,2311],{"class":2235},[2225,246277,6614],{"class":2235},[2225,246279,246280],{"class":2252},"OIGbecaDduQOQqO+",[2225,246282,6620],{"class":2235},[2225,246284,1707],{"class":2235},[2225,246286,246287,246289,246291,246293,246295,246297,246300,246302],{"class":2227,"line":2356},[2225,246288,9368],{"class":2235},[2225,246290,1146],{"class":2774},[2225,246292,6620],{"class":2235},[2225,246294,2311],{"class":2235},[2225,246296,6614],{"class":2235},[2225,246298,246299],{"class":2252},"ruPaj9T50xSQoRfFM3y8NfmlXApnBRzz\u002FYm3XaqGlqZzao93e\u002FLR4Pox+d0N1nJoWmhypqXAIaEaJZRBcITEmsY9jiFD",[2225,246301,6620],{"class":2235},[2225,246303,1707],{"class":2235},[2225,246305,246306,246308,246310,246312,246314,246316,246318],{"class":2227,"line":2830},[2225,246307,9368],{"class":2235},[2225,246309,1706],{"class":2774},[2225,246311,6620],{"class":2235},[2225,246313,2311],{"class":2235},[2225,246315,6614],{"class":2235},[2225,246317,4212],{"class":2252},[2225,246319,9449],{"class":2235},[2225,246321,246322],{"class":2227,"line":2861},[2225,246323,7351],{"class":2235},[937,246325,246326,246327,246329,246330,246332,246333,968,246335,246337,246338,246340,246341,246343,246344,246346,246347,968,246349,968,246351,246353,246354,246357],{},"That file is an ",[950,246328,1659],{}," — the only shape any ",[941,246331,1779],{}," store ever handles. The record's fields (",[941,246334,6617],{},[941,246336,88649],{},", even the field ",[1092,246339,71352],{},") are inside ",[941,246342,1146],{},", encrypted; ",[941,246345,1143],{}," is the per-write initialization vector; the cleartext parts (",[941,246348,1700],{},[941,246350,1703],{},[941,246352,1706],{},") are versioning metadata the store needs to do its job. ",[941,246355,246356],{},"grep 8500 -r .\u002Fdata"," finds nothing.",[937,246359,246360,246361,246364,246365,203399,246367,246370],{},"The same directory also holds ",[941,246362,246363],{},"acme\u002F_keyring\u002Fme.json"," — your keyring, whose per-collection data keys are themselves wrapped under a key derived from your ",[941,246366,10695],{},[941,246368,246369],{},"acme\u002F_meta\u002Fpolicy.json",", the deliberately-plaintext unlock policy. Nothing readable requires anything readable to be stored: whoever holds this directory (a disk, a cloud bucket, a backup) holds ciphertext and can neither run your queries nor read your records.",[1022,246372,246374],{"id":246373},"the-negative-control","The negative control",[937,246376,246377,246378,246380],{},"Encryption is on by default and can only be disabled explicitly. Run the same code with ",[941,246379,11453],{}," (testing only — no privacy guarantee) and read the file again:",[2217,246382,246384],{"className":6604,"code":246383,"language":141,"meta":1037,"style":1037},"{\n  \"_noydb\": 1,\n  \"_v\": 1,\n  \"_ts\": \"2026-07-15T15:32:09.970Z\",\n  \"_iv\": \"\",\n  \"_data\": \"{\\\"id\\\":\\\"inv-1\\\",\\\"amount\\\":8500,\\\"customer\\\":\\\"ABC Trading\\\"}\",\n  \"_by\": \"me\"\n}\n",[941,246385,246386,246390,246404,246418,246437,246451,246511,246527],{"__ignoreMap":1037},[2225,246387,246388],{"class":2227,"line":2228},[2225,246389,4200],{"class":2235},[2225,246391,246392,246394,246396,246398,246400,246402],{"class":2227,"line":1041},[2225,246393,9368],{"class":2235},[2225,246395,1697],{"class":2774},[2225,246397,6620],{"class":2235},[2225,246399,2311],{"class":2235},[2225,246401,9377],{"class":6527},[2225,246403,1707],{"class":2235},[2225,246405,246406,246408,246410,246412,246414,246416],{"class":2227,"line":1038},[2225,246407,9368],{"class":2235},[2225,246409,1700],{"class":2774},[2225,246411,6620],{"class":2235},[2225,246413,2311],{"class":2235},[2225,246415,9377],{"class":6527},[2225,246417,1707],{"class":2235},[2225,246419,246420,246422,246424,246426,246428,246430,246433,246435],{"class":2227,"line":2288},[2225,246421,9368],{"class":2235},[2225,246423,1703],{"class":2774},[2225,246425,6620],{"class":2235},[2225,246427,2311],{"class":2235},[2225,246429,6614],{"class":2235},[2225,246431,246432],{"class":2252},"2026-07-15T15:32:09.970Z",[2225,246434,6620],{"class":2235},[2225,246436,1707],{"class":2235},[2225,246438,246439,246441,246443,246445,246447,246449],{"class":2227,"line":2304},[2225,246440,9368],{"class":2235},[2225,246442,1143],{"class":2774},[2225,246444,6620],{"class":2235},[2225,246446,2311],{"class":2235},[2225,246448,60469],{"class":2235},[2225,246450,1707],{"class":2235},[2225,246452,246453,246455,246457,246459,246461,246463,246465,246468,246470,246472,246474,246476,246478,246480,246482,246484,246486,246488,246491,246493,246495,246497,246499,246501,246503,246505,246507,246509],{"class":2227,"line":2356},[2225,246454,9368],{"class":2235},[2225,246456,1146],{"class":2774},[2225,246458,6620],{"class":2235},[2225,246460,2311],{"class":2235},[2225,246462,6614],{"class":2235},[2225,246464,2792],{"class":2252},[2225,246466,246467],{"class":2239},"\\\"",[2225,246469,6983],{"class":2252},[2225,246471,246467],{"class":2239},[2225,246473,2311],{"class":2252},[2225,246475,246467],{"class":2239},[2225,246477,6514],{"class":2252},[2225,246479,246467],{"class":2239},[2225,246481,2298],{"class":2252},[2225,246483,246467],{"class":2239},[2225,246485,6617],{"class":2252},[2225,246487,246467],{"class":2239},[2225,246489,246490],{"class":2252},":8500,",[2225,246492,246467],{"class":2239},[2225,246494,88649],{"class":2252},[2225,246496,246467],{"class":2239},[2225,246498,2311],{"class":2252},[2225,246500,246467],{"class":2239},[2225,246502,245093],{"class":2252},[2225,246504,246467],{"class":2239},[2225,246506,2359],{"class":2252},[2225,246508,6620],{"class":2235},[2225,246510,1707],{"class":2235},[2225,246512,246513,246515,246517,246519,246521,246523,246525],{"class":2227,"line":2830},[2225,246514,9368],{"class":2235},[2225,246516,1706],{"class":2774},[2225,246518,6620],{"class":2235},[2225,246520,2311],{"class":2235},[2225,246522,6614],{"class":2235},[2225,246524,4212],{"class":2252},[2225,246526,9449],{"class":2235},[2225,246528,246529],{"class":2227,"line":2861},[2225,246530,7351],{"class":2235},[937,246532,246533,246534,15696,246536,246538,246539,246541],{},"Same envelope shape, plaintext ",[941,246535,1146],{},[941,246537,1143],{}," — that's the entire difference the ",[941,246540,10695],{}," makes, and it's the difference between a store you must trust and a store you don't have to.",[1022,246543,246545],{"id":246544},"in-the-browser","In the browser",[937,246547,246548,246550,246551,246553,246554,246556],{},[941,246549,9573],{}," gives you the same experiment in a web app: write a record, open DevTools → Application → IndexedDB, and inspect the stored envelopes — ",[941,246552,1146],{}," is the same opaque ciphertext there. See ",[1007,246555,16076],{"href":51}," for the store catalog.",[1022,246558,1455],{"id":1454},[1111,246560,246561,246570],{},[1114,246562,246563,246566,246567,246569],{},[1007,246564,246565],{"href":1018},"Orientation"," — pick the capabilities your app needs and compose your ",[941,246568,2134],{}," build.",[1114,246571,246572,246574],{},[1007,246573,56109],{"href":66}," — the full key hierarchy and envelope format behind what you just saw.",[2645,246576,246577],{},"html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}",{"title":1037,"searchDepth":1038,"depth":1038,"links":246579},[246580,246581,246582],{"id":246373,"depth":1041,"text":246374},{"id":246544,"depth":1041,"text":246545},{"id":1454,"depth":1041,"text":1455},"Persist your vault to disk with to-file, open the stored file, and see for yourself that the store only ever holds ciphertext.",{},{"title":924,"description":246583},"LlQFR8GnP3-Wb7neZPb6jPaUVEWb10qb1TShIA_2eoI",{"id":246588,"title":94685,"access":12,"apiToc":929,"body":246589,"category":1042,"description":246652,"extension":1044,"meta":246653,"navigation":246654,"nest":1042,"path":905,"seo":246655,"sinceVersion":1042,"status":1049,"stem":909,"subtitle":1042,"tier":6922,"tocExternal":1042,"__hash__":246656},"docs\u002Fstart\u002Findex.md",{"type":931,"value":246590,"toc":246650},[246591,246594,246604,246607,246644],[934,246592,94685],{"id":246593},"start-here",[1061,246595,246596],{},[937,246597,246598,246599,1520],{},"Reading this on GitHub? The links below are site paths and resolve only on the hosted docs — read these pages at ",[1007,246600,246603],{"href":246601,"rel":246602},"https:\u002F\u002Fnoy-db.lanna.ai\u002Fstart",[3371],"noy-db.lanna.ai\u002Fstart",[937,246605,246606],{},"Four steps to a running, encrypted, zero-knowledge document store — proof included:",[1234,246608,246609,246623,246631,246639],{},[1114,246610,246611,964,246613,246616,246617,246619,246620,246622],{},[1007,246612,912],{"href":913},[941,246614,246615],{},"npm create noy-db@latest",", or add ",[941,246618,1086],{}," by hand and create your first ",[941,246621,62],{}," instance. No storage backend required to start.",[1114,246624,246625,246627,246628,246630],{},[1007,246626,916],{"href":917}," — open a vault, create a typed collection, ",[941,246629,3161],{}," a record, and run a basic query.",[1114,246632,246633,246635,246636,246638],{},[1007,246634,920],{"href":921}," — opt into one ",[941,246637,943],{}," service (history) and see what it adds.",[1114,246640,246641,246643],{},[1007,246642,924],{"href":925}," — persist to disk and open the stored file: the store holds only ciphertext.",[937,246645,246646,246647,246649],{},"Once you've got a vault running, jump into the ",[1007,246648,1019],{"href":1018}," to pick the rest of the capabilities your app needs and land on the exact pages for them.",{"title":1037,"searchDepth":1038,"depth":1038,"links":246651},[],"Install, create a vault, opt into your first service.",{},{"title":10,"icon":910},{"title":94685,"description":246652},"bhqjckQ8fwm-tMZDQFYFBpFkGGa5t-husx91WuuODVw",{"id":246658,"title":246659,"access":12,"apiToc":929,"body":246660,"category":1042,"description":247580,"extension":1044,"meta":247581,"navigation":929,"nest":1042,"path":32989,"seo":247582,"sinceVersion":1042,"status":1049,"stem":247583,"subtitle":1042,"tier":1473,"tocExternal":1042,"__hash__":247584},"docs\u002Fzip-interop.md","WinZip-AES-256 Interoperability",{"type":931,"value":246661,"toc":247574},[246662,246665,246695,246706,246710,246713,246727,246738,246741,246784,246788,246796,246831,246838,246861,246864,246867,246973,246976,247019,247023,247029,247043,247077,247080,247209,247403,247424,247446,247454,247535,247537,247571],[934,246663,246659],{"id":246664},"winzip-aes-256-interoperability",[937,246666,246667,246672,246673,246675,246676,246681,246682,246684,246685,246687,246688,246691,246692,246694],{},[1007,246668,246670],{"href":246669},".\u002Ffamilies\u002Fas\u002Fzip",[941,246671,32422],{}," exports a collection's records and\nattachments from a noy-db ",[950,246674,2694],{}," (an encrypted document store — see the\n",[1007,246677,246679,107326],{"href":246678},".\u002Ffamilies\u002Fas\u002F",[941,246680,1791],{},") as a single ",[941,246683,31159],{}," file. When you pass a\n",[941,246686,313],{},", every entry in that archive is encrypted with ",[950,246689,246690],{},"WinZip-AES-256"," —\na widely implemented extension to the ZIP format — so the recipient can open it\nwith an ordinary archive tool instead of needing noy-db installed. This page\nstates which third-party tools ",[941,246693,32422],{},"'s automated interop test\nsuite currently exercises, in both directions: archives noy-db writes being\nopened by other tools, and archives other tools write being read back by\nnoy-db.",[7646,246696,246697],{"color":11278},[937,246698,246699,246700,246703,246704,33515],{},"WinZip-AES is the ",[950,246701,246702],{},"interop"," layer — a portable handoff to standard archive\ntools, protected by a single shared secret. For multi-recipient, revocable,\naudited egress of noy-db state, use the encrypted-bundle exporter\n",[941,246705,31967],{},[1022,246707,246709],{"id":246708},"what-the-format-guarantees","What the format guarantees",[937,246711,246712],{},"Two vendor versions of WinZip-AES exist, and both appear in the wild:",[1111,246714,246715,246721],{},[1114,246716,246717,246720],{},[950,246718,246719],{},"AE-1"," — the entry's CRC-32 checksum is stored alongside the ciphertext.",[1114,246722,246723,246726],{},[950,246724,246725],{},"AE-2"," — the CRC-32 field is zeroed; integrity is covered by the\nencryption's own authentication code instead.",[937,246728,246729,1324,246731,997,246734,246737],{},[941,246730,32422],{},[950,246732,246733],{},"writes AE-2",[950,246735,246736],{},"reads both AE-1 and AE-2",". Each\nencrypted entry carries a 16-byte random salt, a 2-byte password verifier\n(fails fast on a wrong secret), the AES-CTR ciphertext, and a 10-byte\nHMAC-SHA1 authentication code.",[937,246739,246740],{},"Deliberate scope limits, enforced at both write and read time:",[1338,246742,246743,246752],{},[1341,246744,246745],{},[1344,246746,246747,246750],{},[1347,246748,246749],{},"Constraint",[1347,246751,124024],{},[1357,246753,246754,246765,246776],{},[1344,246755,246756,246759],{},[1362,246757,246758],{},"AES-256 only",[1362,246760,246761,246762],{},"AES-128\u002F192 and legacy ZipCrypto archives are refused with ",[941,246763,246764],{},"ZipCipherError",[1344,246766,246767,246770],{},[1362,246768,246769],{},"STORE only (no compression)",[1362,246771,246772,246773],{},"DEFLATE\u002Fbzip2\u002Flzma entries are refused with ",[941,246774,246775],{},"ZipReadError",[1344,246777,246778,246781],{},[1362,246779,246780],{},"Single-disk, no Zip64",[1362,246782,246783],{},"Entries larger than 4 GiB are not supported",[1022,246785,246787],{"id":246786},"opening-a-noy-db-archive-in-other-tools","Opening a noy-db archive in other tools",[937,246789,246790,246792,246793,2311],{},[941,246791,32422],{}," ships an automated interop suite that writes a\npassword-protected archive with this package and then extracts it with a\nthird-party tool, byte-for-byte, across four payload shapes: a 1-byte entry,\na 16-byte entry (exactly one AES block), a non-ASCII entry path, and a 1 MiB\npayload. It runs against whichever of these tools is on the machine's ",[941,246794,246795],{},"PATH",[1338,246797,246798,246807],{},[1341,246799,246800],{},[1344,246801,246802,246804],{},[1347,246803,106516],{},[1347,246805,246806],{},"Installed on, in the release-time check",[1357,246808,246809,246823],{},[1344,246810,246811,246820],{},[1362,246812,246813,246814,1261,246817,1381],{},"7-Zip (",[941,246815,246816],{},"7z",[941,246818,246819],{},"7zz",[1362,246821,246822],{},"Linux, macOS, Windows",[1344,246824,246825,246828],{},[1362,246826,246827],{},"unar (The Unarchiver)",[1362,246829,246830],{},"macOS only",[937,246832,246833,246834,246837],{},"That release-time check runs the suite on Linux, macOS, and Windows runners\nas an ",[950,246835,246836],{},"advisory"," step — it reports cross-tool regressions but does not\nblock a release, so treat it as a point-in-time result rather than a\nstanding guarantee. You can reproduce it yourself with the relevant tool\ninstalled:",[2217,246839,246841],{"className":33763,"code":246840,"language":33765,"meta":1037,"style":1037},"cd packages\u002Fas-zip\npnpm vitest run __tests__\u002Finterop.test.ts\n",[941,246842,246843,246850],{"__ignoreMap":1037},[2225,246844,246845,246847],{"class":2227,"line":2228},[2225,246846,92355],{"class":2291},[2225,246848,246849],{"class":2252}," packages\u002Fas-zip\n",[2225,246851,246852,246854,246856,246858],{"class":2227,"line":1041},[2225,246853,41169],{"class":6342},[2225,246855,92418],{"class":2252},[2225,246857,92421],{"class":2252},[2225,246859,246860],{"class":2252}," __tests__\u002Finterop.test.ts\n",[937,246862,246863],{},"Archive Utility (macOS's built-in unzip) and WinRAR both implement the\nWinZip-AES extension and are expected to accept these archives, but neither\nis exercised by the automated suite.",[937,246865,246866],{},"Producing an encrypted archive takes one extra option:",[2217,246868,246870],{"className":2219,"code":246869,"language":2221,"meta":1037,"style":1037},"import { toBytes } from '@noy-db\u002Fas-zip'\n\n\u002F\u002F vault: an unlocked noy-db Vault whose keyring grants\n\u002F\u002F exportCapability { plaintext: ['zip'] } to the caller.\nconst archive = await toBytes(vault, {\n  records: { collection: 'invoices' },\n  password: 'shared-with-recipient-2026',\n})\n\u002F\u002F → Uint8Array — a standard single-disk .zip; WinZip-AES-aware\n\u002F\u002F   tools prompt for the password on extract\n",[941,246871,246872,246890,246894,246899,246904,246923,246943,246957,246963,246968],{"__ignoreMap":1037},[2225,246873,246874,246876,246878,246880,246882,246884,246886,246888],{"class":2227,"line":2228},[2225,246875,2232],{"class":2231},[2225,246877,2236],{"class":2235},[2225,246879,30223],{"class":2239},[2225,246881,2243],{"class":2235},[2225,246883,2246],{"class":2231},[2225,246885,2249],{"class":2235},[2225,246887,32422],{"class":2252},[2225,246889,2256],{"class":2235},[2225,246891,246892],{"class":2227,"line":1041},[2225,246893,2285],{"emptyLinePlaceholder":2284},[2225,246895,246896],{"class":2227,"line":1038},[2225,246897,246898],{"class":2763},"\u002F\u002F vault: an unlocked noy-db Vault whose keyring grants\n",[2225,246900,246901],{"class":2227,"line":2288},[2225,246902,246903],{"class":2763},"\u002F\u002F exportCapability { plaintext: ['zip'] } to the caller.\n",[2225,246905,246906,246908,246911,246913,246915,246917,246919,246921],{"class":2227,"line":2304},[2225,246907,2775],{"class":2774},[2225,246909,246910],{"class":2239}," archive ",[2225,246912,2781],{"class":2235},[2225,246914,2784],{"class":2231},[2225,246916,30223],{"class":2291},[2225,246918,30266],{"class":2239},[2225,246920,2298],{"class":2235},[2225,246922,2301],{"class":2235},[2225,246924,246925,246927,246929,246931,246933,246935,246937,246939,246941],{"class":2227,"line":2356},[2225,246926,33046],{"class":2307},[2225,246928,2311],{"class":2235},[2225,246930,2236],{"class":2235},[2225,246932,7061],{"class":2307},[2225,246934,2311],{"class":2235},[2225,246936,2249],{"class":2235},[2225,246938,6487],{"class":2252},[2225,246940,2805],{"class":2235},[2225,246942,2353],{"class":2235},[2225,246944,246945,246947,246949,246951,246953,246955],{"class":2227,"line":2830},[2225,246946,33135],{"class":2307},[2225,246948,2311],{"class":2235},[2225,246950,2249],{"class":2235},[2225,246952,33142],{"class":2252},[2225,246954,2805],{"class":2235},[2225,246956,1707],{"class":2235},[2225,246958,246959,246961],{"class":2227,"line":2861},[2225,246960,2359],{"class":2235},[2225,246962,2362],{"class":2239},[2225,246964,246965],{"class":2227,"line":4246},[2225,246966,246967],{"class":2763},"\u002F\u002F → Uint8Array — a standard single-disk .zip; WinZip-AES-aware\n",[2225,246969,246970],{"class":2227,"line":4253},[2225,246971,246972],{"class":2763},"\u002F\u002F   tools prompt for the password on extract\n",[937,246974,246975],{},"The recipient extracts it with their tool of choice:",[2217,246977,246979],{"className":33763,"code":246978,"language":33765,"meta":1037,"style":1037},"7z x -p'shared-with-recipient-2026' invoices.zip\nunar -p 'shared-with-recipient-2026' invoices.zip\n# Archive Utility \u002F Finder: double-click, enter the password when prompted\n",[941,246980,246981,246999,247014],{"__ignoreMap":1037},[2225,246982,246983,246985,246987,246990,246992,246994,246996],{"class":2227,"line":2228},[2225,246984,246816],{"class":6342},[2225,246986,91304],{"class":2252},[2225,246988,246989],{"class":2252}," -p",[2225,246991,2805],{"class":2235},[2225,246993,33142],{"class":2252},[2225,246995,2805],{"class":2235},[2225,246997,246998],{"class":2252}," invoices.zip\n",[2225,247000,247001,247004,247006,247008,247010,247012],{"class":2227,"line":1041},[2225,247002,247003],{"class":6342},"unar",[2225,247005,246989],{"class":2252},[2225,247007,2249],{"class":2235},[2225,247009,33142],{"class":2252},[2225,247011,2805],{"class":2235},[2225,247013,246998],{"class":2252},[2225,247015,247016],{"class":2227,"line":1038},[2225,247017,247018],{"class":2763},"# Archive Utility \u002F Finder: double-click, enter the password when prompted\n",[1022,247020,247022],{"id":247021},"reading-third-party-archives-back-into-noy-db","Reading third-party archives back into noy-db",[937,247024,247025,247026,247028],{},"The reverse direction is exercised the same way, with 7-Zip as the writer:\nthe suite creates an archive with 7-Zip using AES-256 and the STORE method,\nthen reads it back with ",[941,247027,32422],{},"'s own reader, on the same\nLinux\u002FmacOS\u002FWindows runners described above. Two things to know when\nproducing such an archive:",[1111,247030,247031,247037],{},[1114,247032,247033,247036],{},[950,247034,247035],{},"7-Zip writes AE-1 by default."," That is fine — the reader accepts both\nvendor versions.",[1114,247038,247039,247042],{},[950,247040,247041],{},"7-Zip compresses with DEFLATE by default."," The reader is STORE-only, so\nforce stored (uncompressed) entries when creating the archive:",[2217,247044,247046],{"className":33763,"code":247045,"language":33765,"meta":1037,"style":1037},"7z a -tzip -mm=Copy -mem=AES256 -p'shared-with-recipient-2026' data.zip records.json\n",[941,247047,247048],{"__ignoreMap":1037},[2225,247049,247050,247052,247054,247057,247060,247063,247065,247067,247069,247071,247074],{"class":2227,"line":2228},[2225,247051,246816],{"class":6342},[2225,247053,79316],{"class":2252},[2225,247055,247056],{"class":2252}," -tzip",[2225,247058,247059],{"class":2252}," -mm=Copy",[2225,247061,247062],{"class":2252}," -mem=AES256",[2225,247064,246989],{"class":2252},[2225,247066,2805],{"class":2235},[2225,247068,33142],{"class":2252},[2225,247070,2805],{"class":2235},[2225,247072,247073],{"class":2252}," data.zip",[2225,247075,247076],{"class":2252}," records.json\n",[937,247078,247079],{},"Reading it back:",[2217,247081,247083],{"className":2219,"code":247082,"language":2221,"meta":1037,"style":1037},"import { fromBytes } from '@noy-db\u002Fas-zip'\n\nconst plan = await fromBytes(vault, archiveBytes, {\n  collection: 'invoices',\n  password: 'shared-with-recipient-2026',\n})\n\n\u002F\u002F Nothing is written yet. plan.plan is a VaultDiff — every record that\n\u002F\u002F would be added, modified, or deleted to bring the vault to match the\n\u002F\u002F archive, computed against the caller's own current state:\nconsole.log(plan.plan.summary)\n\u002F\u002F → { add: 2, modify: 1, delete: 0, total: 3 }\n",[941,247084,247085,247103,247107,247130,247144,247158,247164,247168,247173,247178,247183,247204],{"__ignoreMap":1037},[2225,247086,247087,247089,247091,247093,247095,247097,247099,247101],{"class":2227,"line":2228},[2225,247088,2232],{"class":2231},[2225,247090,2236],{"class":2235},[2225,247092,30344],{"class":2239},[2225,247094,2243],{"class":2235},[2225,247096,2246],{"class":2231},[2225,247098,2249],{"class":2235},[2225,247100,32422],{"class":2252},[2225,247102,2256],{"class":2235},[2225,247104,247105],{"class":2227,"line":1041},[2225,247106,2285],{"emptyLinePlaceholder":2284},[2225,247108,247109,247111,247113,247115,247117,247119,247121,247123,247126,247128],{"class":2227,"line":1038},[2225,247110,2775],{"class":2774},[2225,247112,30365],{"class":2239},[2225,247114,2781],{"class":2235},[2225,247116,2784],{"class":2231},[2225,247118,30344],{"class":2291},[2225,247120,30266],{"class":2239},[2225,247122,2298],{"class":2235},[2225,247124,247125],{"class":2239}," archiveBytes",[2225,247127,2298],{"class":2235},[2225,247129,2301],{"class":2235},[2225,247131,247132,247134,247136,247138,247140,247142],{"class":2227,"line":2288},[2225,247133,30275],{"class":2307},[2225,247135,2311],{"class":2235},[2225,247137,2249],{"class":2235},[2225,247139,6487],{"class":2252},[2225,247141,2805],{"class":2235},[2225,247143,1707],{"class":2235},[2225,247145,247146,247148,247150,247152,247154,247156],{"class":2227,"line":2304},[2225,247147,33135],{"class":2307},[2225,247149,2311],{"class":2235},[2225,247151,2249],{"class":2235},[2225,247153,33142],{"class":2252},[2225,247155,2805],{"class":2235},[2225,247157,1707],{"class":2235},[2225,247159,247160,247162],{"class":2227,"line":2356},[2225,247161,2359],{"class":2235},[2225,247163,2362],{"class":2239},[2225,247165,247166],{"class":2227,"line":2830},[2225,247167,2285],{"emptyLinePlaceholder":2284},[2225,247169,247170],{"class":2227,"line":2861},[2225,247171,247172],{"class":2763},"\u002F\u002F Nothing is written yet. plan.plan is a VaultDiff — every record that\n",[2225,247174,247175],{"class":2227,"line":4246},[2225,247176,247177],{"class":2763},"\u002F\u002F would be added, modified, or deleted to bring the vault to match the\n",[2225,247179,247180],{"class":2227,"line":4253},[2225,247181,247182],{"class":2763},"\u002F\u002F archive, computed against the caller's own current state:\n",[2225,247184,247185,247187,247189,247191,247194,247196,247199,247201],{"class":2227,"line":4259},[2225,247186,28031],{"class":2239},[2225,247188,1520],{"class":2235},[2225,247190,27100],{"class":2291},[2225,247192,247193],{"class":2239},"(plan",[2225,247195,1520],{"class":2235},[2225,247197,247198],{"class":2239},"plan",[2225,247200,1520],{"class":2235},[2225,247202,247203],{"class":2239},"summary)\n",[2225,247205,247206],{"class":2227,"line":6548},[2225,247207,247208],{"class":2763},"\u002F\u002F → { add: 2, modify: 1, delete: 0, total: 3 }\n",[2217,247210,247212],{"className":6604,"code":247211,"language":141,"meta":1037,"style":1037},"{\n  \"added\": [\n    { \"collection\": \"invoices\", \"id\": \"inv-104\", \"record\": { \"id\": \"inv-104\", \"amount\": 4200 } }\n  ],\n  \"modified\": [\n    { \"collection\": \"invoices\", \"id\": \"inv-101\", \"fieldsChanged\": [\"amount\"] }\n  ],\n  \"deleted\": []\n}\n",[941,247213,247214,247218,247231,247308,247312,247325,247383,247387,247399],{"__ignoreMap":1037},[2225,247215,247216],{"class":2227,"line":2228},[2225,247217,4200],{"class":2235},[2225,247219,247220,247222,247225,247227,247229],{"class":2227,"line":1041},[2225,247221,9368],{"class":2235},[2225,247223,247224],{"class":2774},"added",[2225,247226,6620],{"class":2235},[2225,247228,2311],{"class":2235},[2225,247230,32491],{"class":2235},[2225,247232,247233,247235,247237,247239,247241,247243,247245,247247,247249,247251,247253,247255,247257,247259,247261,247264,247266,247268,247270,247272,247274,247276,247278,247280,247282,247284,247286,247288,247290,247292,247294,247296,247298,247300,247302,247304,247306],{"class":2227,"line":1038},[2225,247234,32496],{"class":2235},[2225,247236,6614],{"class":2235},[2225,247238,6471],{"class":6342},[2225,247240,6620],{"class":2235},[2225,247242,2311],{"class":2235},[2225,247244,6614],{"class":2235},[2225,247246,6487],{"class":2252},[2225,247248,6620],{"class":2235},[2225,247250,2298],{"class":2235},[2225,247252,6614],{"class":2235},[2225,247254,6983],{"class":6342},[2225,247256,6620],{"class":2235},[2225,247258,2311],{"class":2235},[2225,247260,6614],{"class":2235},[2225,247262,247263],{"class":2252},"inv-104",[2225,247265,6620],{"class":2235},[2225,247267,2298],{"class":2235},[2225,247269,6614],{"class":2235},[2225,247271,24158],{"class":6342},[2225,247273,6620],{"class":2235},[2225,247275,2311],{"class":2235},[2225,247277,2236],{"class":2235},[2225,247279,6614],{"class":2235},[2225,247281,6983],{"class":6527},[2225,247283,6620],{"class":2235},[2225,247285,2311],{"class":2235},[2225,247287,6614],{"class":2235},[2225,247289,247263],{"class":2252},[2225,247291,6620],{"class":2235},[2225,247293,2298],{"class":2235},[2225,247295,6614],{"class":2235},[2225,247297,6617],{"class":6527},[2225,247299,6620],{"class":2235},[2225,247301,2311],{"class":2235},[2225,247303,26021],{"class":6527},[2225,247305,2243],{"class":2235},[2225,247307,6369],{"class":2235},[2225,247309,247310],{"class":2227,"line":2288},[2225,247311,67686],{"class":2235},[2225,247313,247314,247316,247319,247321,247323],{"class":2227,"line":2304},[2225,247315,9368],{"class":2235},[2225,247317,247318],{"class":2774},"modified",[2225,247320,6620],{"class":2235},[2225,247322,2311],{"class":2235},[2225,247324,32491],{"class":2235},[2225,247326,247327,247329,247331,247333,247335,247337,247339,247341,247343,247345,247347,247349,247351,247353,247355,247358,247360,247362,247364,247367,247369,247371,247373,247375,247377,247379,247381],{"class":2227,"line":2356},[2225,247328,32496],{"class":2235},[2225,247330,6614],{"class":2235},[2225,247332,6471],{"class":6342},[2225,247334,6620],{"class":2235},[2225,247336,2311],{"class":2235},[2225,247338,6614],{"class":2235},[2225,247340,6487],{"class":2252},[2225,247342,6620],{"class":2235},[2225,247344,2298],{"class":2235},[2225,247346,6614],{"class":2235},[2225,247348,6983],{"class":6342},[2225,247350,6620],{"class":2235},[2225,247352,2311],{"class":2235},[2225,247354,6614],{"class":2235},[2225,247356,247357],{"class":2252},"inv-101",[2225,247359,6620],{"class":2235},[2225,247361,2298],{"class":2235},[2225,247363,6614],{"class":2235},[2225,247365,247366],{"class":6342},"fieldsChanged",[2225,247368,6620],{"class":2235},[2225,247370,2311],{"class":2235},[2225,247372,8667],{"class":2235},[2225,247374,6620],{"class":2235},[2225,247376,6617],{"class":2252},[2225,247378,6620],{"class":2235},[2225,247380,8803],{"class":2235},[2225,247382,6369],{"class":2235},[2225,247384,247385],{"class":2227,"line":2830},[2225,247386,67686],{"class":2235},[2225,247388,247389,247391,247393,247395,247397],{"class":2227,"line":2861},[2225,247390,9368],{"class":2235},[2225,247392,140494],{"class":2774},[2225,247394,6620],{"class":2235},[2225,247396,2311],{"class":2235},[2225,247398,80680],{"class":2235},[2225,247400,247401],{"class":2227,"line":4246},[2225,247402,7351],{"class":2235},[937,247404,247405,26623,247408,247411,247412,247415,247416,247419,247420,247423],{},[941,247406,247407],{},"plan.policy",[941,247409,247410],{},"'merge'",": added and modified records are\nwritten, deleted records are left alone (pass ",[941,247413,247414],{},"{ policy: 'replace' }"," to\nalso delete, or ",[941,247417,247418],{},"{ policy: 'insert-only' }"," to skip modifications).\nNothing is committed until ",[941,247421,247422],{},"apply()"," runs, and the whole batch is\ntransactional — if any write in the plan fails, none of it is kept:",[2217,247425,247427],{"className":2219,"code":247426,"language":2221,"meta":1037,"style":1037},"await plan.apply()\n\u002F\u002F → Promise\u003Cvoid> — added + modified records committed in one transaction\n",[941,247428,247429,247441],{"__ignoreMap":1037},[2225,247430,247431,247433,247435,247437,247439],{"class":2227,"line":2228},[2225,247432,6500],{"class":2231},[2225,247434,30419],{"class":2239},[2225,247436,1520],{"class":2235},[2225,247438,30424],{"class":2291},[2225,247440,8146],{"class":2239},[2225,247442,247443],{"class":2227,"line":1041},[2225,247444,247445],{"class":2763},"\u002F\u002F → Promise\u003Cvoid> — added + modified records committed in one transaction\n",[937,247447,247448,247449,247451,247452,2311],{},"A wrong password, or ciphertext that was tampered with, surfaces as\n",[941,247450,246764],{},"; structural problems (truncated file, unsupported compression\nmethod, missing AES metadata) surface as ",[941,247453,246775],{},[2217,247455,247457],{"className":2219,"code":247456,"language":2221,"meta":1037,"style":1037},"import { ZipCipherError, ZipReadError } from '@noy-db\u002Fas-zip'\n\nawait fromBytes(vault, archiveBytes, { collection: 'invoices', password: 'wrong' })\n\u002F\u002F → throws ZipCipherError (code 'ZIP_CIPHER_ERROR')\n",[941,247458,247459,247483,247487,247530],{"__ignoreMap":1037},[2225,247460,247461,247463,247465,247468,247470,247473,247475,247477,247479,247481],{"class":2227,"line":2228},[2225,247462,2232],{"class":2231},[2225,247464,2236],{"class":2235},[2225,247466,247467],{"class":2239}," ZipCipherError",[2225,247469,2298],{"class":2235},[2225,247471,247472],{"class":2239}," ZipReadError",[2225,247474,2243],{"class":2235},[2225,247476,2246],{"class":2231},[2225,247478,2249],{"class":2235},[2225,247480,32422],{"class":2252},[2225,247482,2256],{"class":2235},[2225,247484,247485],{"class":2227,"line":1041},[2225,247486,2285],{"emptyLinePlaceholder":2284},[2225,247488,247489,247491,247493,247495,247497,247499,247501,247503,247505,247507,247509,247511,247513,247515,247518,247520,247522,247524,247526,247528],{"class":2227,"line":1038},[2225,247490,6500],{"class":2231},[2225,247492,30344],{"class":2291},[2225,247494,30266],{"class":2239},[2225,247496,2298],{"class":2235},[2225,247498,247125],{"class":2239},[2225,247500,2298],{"class":2235},[2225,247502,2236],{"class":2235},[2225,247504,7061],{"class":2307},[2225,247506,2311],{"class":2235},[2225,247508,2249],{"class":2235},[2225,247510,6487],{"class":2252},[2225,247512,2805],{"class":2235},[2225,247514,2298],{"class":2235},[2225,247516,247517],{"class":2307}," password",[2225,247519,2311],{"class":2235},[2225,247521,2249],{"class":2235},[2225,247523,234538],{"class":2252},[2225,247525,2805],{"class":2235},[2225,247527,2243],{"class":2235},[2225,247529,2362],{"class":2239},[2225,247531,247532],{"class":2227,"line":2288},[2225,247533,247534],{"class":2763},"\u002F\u002F → throws ZipCipherError (code 'ZIP_CIPHER_ERROR')\n",[1022,247536,2621],{"id":2620},[1111,247538,247539,247549],{},[1114,247540,247541,247545,247546,247548],{},[1007,247542,247543,107326],{"href":246678},[941,247544,1791],{}," — the export authorisation model\n(capability tiers, ",[941,247547,11422],{}," grants).",[1114,247550,247551,22284,247555,247557,247558,968,247561,968,247564,968,247566,247568,247569,1520],{},[1007,247552,247553,9789],{"href":246678},[941,247554,1791],{},[941,247556,32422],{}," API\n(",[941,247559,247560],{},"toBytes",[941,247562,247563],{},"download",[941,247565,70773],{},[941,247567,23410],{},") alongside its sibling\nexporters, including the encrypted-bundle alternative ",[941,247570,31967],{},[2645,247572,247573],{},"html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}",{"title":1037,"searchDepth":1038,"depth":1038,"links":247575},[247576,247577,247578,247579],{"id":246708,"depth":1041,"text":246709},{"id":246786,"depth":1041,"text":246787},{"id":247021,"depth":1041,"text":247022},{"id":2620,"depth":1041,"text":2621},"Which third-party archive tools can open password-protected archives produced by @noy-db\u002Fas-zip, and which third-party archives its reader accepts.",{},{"title":246659,"description":247580},"zip-interop","PPKdPA45CXAn3mUHSiIB-NVN_g3ql15Hu3IEoPvWxoc",1791896600854]