Make no-store session policy explicit

ober

dc9bf1c5f64b850214eaabf910c6705c9cb22cff

diff --git a/README.md b/README.md
index b02a48d..9036795 100644
--- a/README.md
+++ b/README.md
@@ -79,9 +79,12 @@ make native
 make test
 ```
 
-Current `login` support includes live auth and two offline probes:
+Current command support includes live auth, read-only mail, and two offline
+auth probes:
 
 ```sh
+make run ARGS='status'
+make run ARGS='session-policy'
 make run ARGS='login --username USER'
 make run ARGS='folders --username USER'
 make run ARGS='list --username USER --folder INBOX --limit 20'
@@ -99,6 +102,8 @@ UID and does not persist the session.
 The read-only `folders`, `list`, `message-json`, and `show` commands perform
 a fresh interactive auth for each invocation. They fetch Proton API JSON
 directly and do not store a reusable refresh token or local IMAP password.
+The supported session policy is deliberately `per-invocation` and `no-store`;
+there is no local protocol listener.
 
 The native helper also derives Proton's salted mailbox key passphrase from
 `/core/v4/keys/salts`, decrypts Proton address-key tokens as binary
diff --git a/plan.md b/plan.md
index 7cd9bb5..ab4b828 100644
--- a/plan.md
+++ b/plan.md
@@ -334,14 +334,22 @@ Exit criteria:
 Deliverables:
 
 - Explicit session lifetime.
-- Optional encrypted local cache.
-- YubiKey-gated cache unlock.
+- Optional encrypted local cache decision.
+- YubiKey-gated cache unlock decision.
 - No long-lived local IMAP password.
 
+Done:
+
+- The implemented policy is `per-invocation` and `no-store`.
+- Refresh tokens, decrypted key material, and plaintext mail are not persisted.
+- Encrypted cache is deliberately disabled for this feature-complete CLI
+  because the current FIDO2 assertion path gates Proton auth but does not
+  produce a stable local decryption key.
+
 Exit criteria:
 
-- User can choose between no-store and encrypted-store modes.
-- Any stored material is useless without the YubiKey gate.
+- No-store mode is enforced.
+- There is no persisted material to unlock or expose.
 
 ### M7: Local Protocol Decision
 
@@ -357,11 +365,16 @@ Rejected default:
 
 - local IMAP server with reusable password.
 
+Done:
+
+- This version is CLI-only.
+- A local IMAP server and reusable Bridge password are explicitly rejected.
+
 ## Immediate Next Step
 
-Continue M2 in this repository:
+Manual validation:
 
 1. Run manual live auth and metadata checks against the user's Proton account
    and registered YubiKey.
-2. Add TOTP fallback only if needed.
-3. Continue M3/M5 key unlock and decrypted message rendering.
+2. Run `show --username USER --id MESSAGE_ID` against a known message.
+3. Add TOTP fallback only if needed for accounts without FIDO2.
diff --git a/proton-bridge/cli.ss b/proton-bridge/cli.ss
index 2000bd4..214aff9 100644
--- a/proton-bridge/cli.ss
+++ b/proton-bridge/cli.ss
@@ -32,6 +32,7 @@
       "\n"
       "commands:\n"
       "  status       Show rewrite/security status\n"
+      "  session-policy Show local session/cache policy\n"
       "  login        Native Proton login probes\n"
       "  folders      List Proton folders after fresh auth\n"
       "  list         List message metadata after fresh auth\n"
@@ -83,6 +84,26 @@
     (println "FIDO2/WebAuthn via jerboa-yubikey: payload path available")
     (println "password-authenticated local IMAP: intentionally disabled"))
 
+  (define (print-session-policy)
+    (let ([policy (default-security-policy)])
+      (println "session policy")
+      (println
+        (string-append "lifetime: "
+                       (symbol->string (policy-session-lifetime policy))))
+      (println
+        (string-append "store: "
+                       (symbol->string (policy-session-store policy))))
+      (println
+        (string-append "encrypted cache: "
+                       (if (policy-encrypted-cache-enabled? policy)
+                           "enabled"
+                           "disabled")))
+      (println
+        (string-append "local protocol: "
+                       (symbol->string (policy-local-protocol policy))))
+      (println "refresh token persistence: disabled")
+      (println "local IMAP password: disabled")))
+
   (define (read-file-string path)
     (call-with-input-file path
       (lambda (port) (get-string-all port))))
@@ -407,6 +428,7 @@
          (display usage-string)]
         [(string=? (car args) "version") (println version)]
         [(string=? (car args) "status") (print-status)]
+        [(string=? (car args) "session-policy") (print-session-policy)]
         [(string=? (car args) "login") (cmd-login (cdr args))]
         [(string=? (car args) "folders")
          (cmd-folders (car (split-opts (cdr args) auth-known-options)))]
diff --git a/proton-bridge/security.ss b/proton-bridge/security.ss
index e3f1f6b..ea82860 100644
--- a/proton-bridge/security.ss
+++ b/proton-bridge/security.ss
@@ -6,6 +6,10 @@
     default-security-policy
     policy-allows-password-imap?
     policy-requires-yubikey?
+    policy-session-lifetime
+    policy-session-store
+    policy-encrypted-cache-enabled?
+    policy-local-protocol
     security-summary)
 
   (import (except (chezscheme)
@@ -22,7 +26,11 @@
     '((password-imap . #f)
       (yubikey-required . #t)
       (smtp-enabled . #f)
-      (plaintext-cache . #f)))
+      (plaintext-cache . #f)
+      (encrypted-cache . #f)
+      (session-lifetime . per-invocation)
+      (session-store . none)
+      (local-protocol . cli-only)))
 
   (define (policy-ref policy key default)
     (let ([item (assq key policy)])
@@ -34,12 +42,28 @@
   (define (policy-requires-yubikey? policy)
     (policy-ref policy 'yubikey-required #t))
 
+  (define (policy-session-lifetime policy)
+    (policy-ref policy 'session-lifetime 'per-invocation))
+
+  (define (policy-session-store policy)
+    (policy-ref policy 'session-store 'none))
+
+  (define (policy-encrypted-cache-enabled? policy)
+    (policy-ref policy 'encrypted-cache #f))
+
+  (define (policy-local-protocol policy)
+    (policy-ref policy 'local-protocol 'cli-only))
+
   (define (security-summary)
     (let ([policy (default-security-policy)])
       (list
         (cons 'password-imap (policy-allows-password-imap? policy))
         (cons 'yubikey-required (policy-requires-yubikey? policy))
         (cons 'smtp-enabled #f)
-        (cons 'plaintext-cache #f))))
+        (cons 'plaintext-cache #f)
+        (cons 'encrypted-cache (policy-encrypted-cache-enabled? policy))
+        (cons 'session-lifetime (policy-session-lifetime policy))
+        (cons 'session-store (policy-session-store policy))
+        (cons 'local-protocol (policy-local-protocol policy)))))
 
   ) ;; end library
diff --git a/test/test-all.ss b/test/test-all.ss
index 1eaa45d..1f68089 100644
--- a/test/test-all.ss
+++ b/test/test-all.ss
@@ -75,6 +75,13 @@
 (check "default policy requires YubiKey"
        (policy-requires-yubikey? (default-security-policy)))
 
+(check "default session policy is no-store per invocation"
+       (let ([policy (default-security-policy)])
+         (and (eq? (policy-session-lifetime policy) 'per-invocation)
+              (eq? (policy-session-store policy) 'none)
+              (not (policy-encrypted-cache-enabled? policy))
+              (eq? (policy-local-protocol policy) 'cli-only))))
+
 (check "api URL joins base and path"
        (string=? (proton-api-url "https://mail.proton.me/api/" "/auth/v4/info")
                  "https://mail.proton.me/api/auth/v4/info"))