[]'s indicate lists.  List items can be marked with a '?', meaning
 they are optional; or a '*', meaning there can be zero or more of that
 item.  {}'s indicate an object that maps from field identifiers to
-values.  An object field marked '?' is optional.  |'s indicate
-alternates (e.g., int|string means something can be an int or a
-string).
+values.  An object field marked '?' is optional; one marked with '*'
+can repeat (with a different name). |'s indicate alternates (e.g.,
+int|string means something can be an int or a string).
 
 For S-Expression output, lists are printed delimited by () instead of
 []. Objects are printed as p-lists, i.e. lists where the keys and values
 # Message ID, sans "id:"
 messageid = string
 
+# E-mail header name, sans trailing colon, like "Subject" or "In-Reply-To"
+header_name = string
+
 notmuch show schema
 -------------------
 
                   status:      sigstatus,
                   # was the set of signatures described under encrypted cover?
                   encrypted:   bool,
+                  # which of the headers is covered by sigstatus?
+                  headers:     [header_name*]
                 },
     decrypted?: {
                   status: msgdecstatus,
+                  # map encrypted headers that differed from the outside headers.
+                  # the value of each item in the map is what that field showed externally
+                  # (maybe null if it was not present in the external headers).
+                  header-mask:  { header_name*: string|null }
                 }
 }
 
 
                        sp->map_key (sp, "encrypted");
                        sp->boolean (sp, msg_crypto->signature_encrypted);
                    }
+                   if (msg_crypto->payload_subject) {
+                       sp->map_key (sp, "headers");
+                       sp->begin_list (sp);
+                       sp->string (sp, "Subject");
+                       sp->end (sp);
+                   }
                    sp->end (sp);
                }
                if (msg_crypto->decryption_status != NOTMUCH_MESSAGE_DECRYPTED_NONE) {
                    sp->begin_map (sp);
                    sp->map_key (sp, "status");
                    sp->string (sp, msg_crypto->decryption_status == NOTMUCH_MESSAGE_DECRYPTED_FULL ? "full" : "partial");
+
+                   if (msg_crypto->payload_subject) {
+                       const char *subject = g_mime_message_get_subject GMIME_MESSAGE (node->part);
+                       if (subject == NULL || strcmp (subject, msg_crypto->payload_subject)) {
+                           /* protected subject differs from the external header */
+                           sp->map_key (sp, "header-mask");
+                           sp->begin_map (sp);
+                           sp->map_key (sp, "Subject");
+                           if (subject == NULL)
+                               sp->null (sp);
+                           else
+                               sp->string (sp, subject);
+                           sp->end (sp);
+                       }
+                   }
                    sp->end (sp);
                }
            }
 
 test_begin_subtest "verify protected header is visible with decryption"
 output=$(notmuch show --decrypt=true --format=json id:protected-header@crypto.notmuchmail.org)
 test_json_nodes <<<"$output" \
-                'crypto:[0][0][0]["crypto"]={"decrypted": {"status": "full"}}' \
+                'crypto:[0][0][0]["crypto"]={"decrypted": {"status": "full", "header-mask": {"Subject": "Subject Unavailable"}}}' \
                 'subject:[0][0][0]["headers"]["Subject"]="This is a protected header"'
 
 test_begin_subtest "misplaced protected headers should not be made visible during decryption"
 test_begin_subtest "verify nested message/rfc822 protected header is visible"
 output=$(notmuch show --decrypt=true --format=json id:nested-rfc822-message@crypto.notmuchmail.org)
 test_json_nodes <<<"$output" \
-                'crypto:[0][0][0]["crypto"]={"decrypted": {"status": "full"}}' \
+                'crypto:[0][0][0]["crypto"]={"decrypted": {"status": "full", "header-mask": {"Subject": "Subject Unavailable"}}}' \
                 'subject:[0][0][0]["headers"]["Subject"]="This is a message using draft-melnikov-smime-header-signing"'
 
 test_done