5. Security guidelines

H — Trust boundary

IDRule
H1MUST access vault data only via @nt2/vault-sdkMUST NOT touch window.parent.localStorage, indexedDB, OPFS, __TAURI_INTERNALS__, or host plugins.
H2MUST NOT attempt to export vault CryptoKey material via crypto.subtle.exportKey or any other path.
H3MUST NOT load remote JavaScript at runtime (import('https://…'), dynamic <script src>, eval of remote strings).
H4MUST NOT embed cross-origin iframes to external domains.
H5MUST NOT put master password, keys, or decrypted secrets in SDK request payloads — send structured item fields only; kernel handles crypto.

The sandbox limits blast radius but does not sanitize your HTML — you are responsible for DOM XSS in your own bundle.

I — Minimal permissions

IDRule
I1MUST list only permissions the app actually calls.
I2MUST document per-slug rationale in catalog metadata.
I3MUST NOT use wildcard category permissions.
I4SHOULD prefer category-specific slugs (items:list:note) over broader access.
I5SHOULD pin sdkVersion and handle PROTOCOL_MISMATCH (see L3).

J — No secret exfiltration

IDRule
J1MUST NOT log decrypted payloads or sensitive user input in production builds.
J2MUST NOT send vault data to external servers — local-first and zero-knowledge apply to your app too ().
J3MUST clear in-memory secrets on LOCKED / teardown.
J4MUST NOT embed API keys or credentials in the bundle (users can inspect local files).
J5SHOULD NOT persist SDK-returned plaintext in localStorage / sessionStorage.

K — Sensitive data handling

IDRule
K1MUST NOT persist decrypted SDK payloads across sessions — re-fetch after unlock.
K2MUST NOT copy sensitive values to clipboard without explicit user action and clear feedback.
K3MUST treat LOCKED as a hard stop — halt in-flight calls; do not auto-retry writes.
K4SHOULD auto-clear clipboard after 30–60 s when copying secrets (N12).
K5SHOULD obscure sensitive fields when document.visibilityState === 'hidden'.

L — Supply chain and bundle integrity

IDRule
L1MUST set entry.integrity to a valid SHA-384 SRI hash (sha384-…) of the entry file; host rejects mismatch at install.
L2MUST sign the manifest with your publisher Vault Key DID; host verifies before install ().
L3MUST pin sdkVersion to the @nt2/vault-sdk version you tested against; show a user-readable “please update” message on PROTOCOL_MISMATCH.
L4MUST NOT ship a vendored @nt2/vault-sdk (or bundled @nt2/vault-sdk-protocol) older than the host’s supported protocol at submission time.
L5SHOULD ship CHANGELOG.md in your source repo for reviewer diffs.

Compute SHA-384 integrity (example):

# Node one-liner for entry file bytes → base64 digest → sha384- prefix
node -e "
const fs=require('fs');const c=fs.readFileSync('index.html');
const h=require('crypto').createHash('sha384').update(c).digest('base64');
console.log('sha384-'+h);
"

M — Writer awareness

Only one browser tab is the Writer for mutations.

IDRule
M1MUST handle NOT_WRITER with clear copy and MUST NOT retry mutations in a tight loop.
M2MUST NOT assume the current tab is always Writer — check GET_STATUS / error codes.
M3SHOULD offer a manual Retry button after NOT_WRITER.

Suggested copy: “Another tab is editing this vault. Switch to that tab or close extra tabs, then try again.”

N — Input validation

IDRule
N1MUST validate required fields before ITEMS_CREATE / ITEMS_UPDATE — prevent BAD_REQUEST via client checks.
N2MUST escape or sanitize user-controlled HTML before inserting into the DOM.
N3SHOULD show inline field errors instead of generic toasts.

See also UX integration norms in the Quick start and SDK reference.