The message and conversation stores hold only delivery metadata (identifiers, timestamps, ciphertext, nonce, key ids, delivery state) and a retention period is declared for them. No AI engine is involved: the verdict is reproducible from the source alone.
Inputs
Non-test code files of the checkout (JavaScript, TypeScript, Python, PHP) plus Prisma, SQL, YAML and Markdown files. The unit of analysis is the SCHEMA of a message or conversation store, located by its name (message, chat message, direct message, conversation, chat, room, thread, channel) and parsed field by field with the static analysis kit: object keys of a Mongoose (new Schema({…}), mongoose.model), Sequelize (define, init) or Drizzle (pgTable) definition, columns of a Knex createTable, properties of a TypeORM or decorated entity class, attributes of a Django, SQLAlchemy or SQLModel class, columns of a Laravel migration (Schema::create with $table->type('name'), timestamps, softDeletes) or an Eloquent $fillable, fields of a Prisma model and columns of a SQL CREATE TABLE. The purpose of a field is read from the comments of the same file (a comment on the field or the two lines above it, a Prisma doc comment, a Python docstring, or any comment naming the field with 'needed for', 'required for', 'used for', 'purpose:', 'why:', 'reason:'). Retention declarations are looked for in every file (TTL index, expireAfterSeconds, prune, purge, cron cleanup, disappearing messages, retention document). Paths listed in excludes (test, spec and tmp files, __tests__, __mocks__, fixtures, test, tests and audit-scripts folders) are never analysed. Nothing of the checkout is executed.
Decision rule
Not applicable when the checkout has no messaging feature (a message or conversation model, a chat transport, a messaging protocol library, a chat UI or end-to-end keys in a messaging folder; vocabulary alone is not-applicable with the reason) or when the messaging feature declares no message or conversation schema in the checkout. FAIL (HIGH, with the field line) when a messaging schema declares an IP address, user agent, location, device name or model, phone, e-mail, plaintext, text, body, content, preview, snippet, subject, summary, transcript or search text. Every other field outside the delivery set (identifiers of message, conversation, sender, recipient, device, user and group; timestamps; ciphertext, nonce, iv, mac, tag, envelope, key ids, ephemeral and ratchet keys; delivery and read state; type, status, version, sequence; reply, edit and delete markers; members, name, title, avatar) is MEDIUM (excessive-metadata; blocks in Extended suites) unless its purpose is declared in a comment, doc comment or docstring of the same file. No retention declaration anywhere in the checkout is MEDIUM (retention-undeclared). PASS when every field of every messaging schema is in the delivery set or declared, and a retention declaration exists; the summary lists each schema with its fields outside the set. Not judged by this check (declared): whether a declared purpose is legitimate (the declaration is attested, not its truth), whether the retention declared is enforced, stores defined outside the checkout (a managed database, a migration living elsewhere), and fields added by ORM mixins or base classes the schema inherits.
Type
deterministic
1.1.0: deterministic: the rule runs over the static analysis kit (tokens, function units, calls, imports, routes) with no AI engine; exact decisionRule and languages published; not-applicable with the reason when the checkout gives nothing to evaluate. The unit is the message or conversation SCHEMA parsed field by field (Mongoose, Sequelize, Drizzle, Knex, TypeORM, Django, SQLAlchemy, Laravel migrations and $fillable, Prisma, SQL): a sensitive field is HIGH; a field outside the delivery set is MEDIUM unless its purpose is declared in a comment, doc comment or docstring; no retention declaration is MEDIUM. Not judged: truth of the declared purpose, enforcement of the retention, stores outside the checkout. (1.0.2: never analyses temporary files (*.tmp.*; spec.excludes follows AUDITOR_ANALYSIS_EXCLUDES). (1.0.1: never analyses test, spec, fixture and mock paths nor the auditor's own scripts (spec.excludes = AUDITOR_ANALYSIS_EXCLUDES); a fixture-looking secret (sk_test_, example, dummy) in real code is LOW, informative; every model call stays under the engine prompt cap (spec.promptMaxChars = AUDITOR_AI_PROMPT_MAX_CHARS): assess mode sends the candidate set in parts and aggregates the verdicts (a HIGH in any part is a FAIL, an unsatisfied part is a FAIL).))
// the unit of analysis is the SCHEMA of a message or conversation store, parsed field by field (object keys, entity properties, model attributes, migration columns, Prisma fields, SQL columns)
const SENSITIVE_META = /^(?:ip|ip_?address|user_?agent|latitude|longitude|location|device_?name|phone|email|plaintext|text|body|content|preview|snippet|search_?text)$/i; // in a message schema -> HIGH
const PURPOSE_NOTE = /\b(?:needed|required|used|kept)\s+(?:for|to|by)\b|\bpurpose\s*:|\bwhy\s*:|\breason\s*:/i; // a field outside the set without it (comment on the field, the lines above, or a note naming it) -> MEDIUM
// no TTL index, expireAfterSeconds, prune job, cron cleanup, disappearing messages or retention document anywhere -> MEDIUM
The full script is disclosed on request in a read-only viewer (never published on GitHub); the attestation binds to this exact hash.