Skip to main content

Tamper-evident AI agent memory: RFC 6962 append-only Merkle log with signed tree heads and provenance

Project description

memAttest logo

memattest

memattest provides AI agent memory attestation. It makes an AI agent's persistent memory tamper-evident.

Agent memory is the information an AI agent carries between sessions- notes, learned preferences, project context, and decisions, typically stored as plain files that the agent reads back into context each time it starts. It shapes everything the agent does next, but it's vulnerable: anyone or anything that can write to those files can rewrite the agent's past, and the agent will trust the result.

Making memory tamper-evident addresses this vulnerability. The agent (and the human dev running it) can detect that a memory was altered, planted, deleted, or reordered after the fact- before acting on it. Tampering isn't prevented, but it can no longer succeed silently, so you and the agent will know that the memory was tampered with.

memattest delivers this as a small library and CLI that guard a memory directory: hooks record every legitimate memory write into a cryptographic log, and a verification pass at session start compares what's on disk against what the log says should be there, reporting any divergence.

The gory details: every write to a guarded memory file becomes a signed, provenance-stamped leaf in an append-only Merkle log, and every leaf is sealed under a signed tree head (STH)- an Ed25519 signature over the current root hash, chained to the previous STH by a consistency proof. Verification recomputes the tree from the entries on disk, checks that every STH in the chain is both correctly signed and a consistent extension of the one before it, and diffs the log's derived expected state against the actual files in the memory directory. Any out-of-band modification to a guarded file is detected and reported with name and details: which file, what hash was expected, what hash was found, and at which log entry the file was last known-good. Any attempt to reorder, truncate, or rewrite history is likewise caught by the consistency-proof check, because the log format makes silently editing the past mathematically distinguishable from validly extending it. Note that rollback detection will be implemented in v2; see the rollback limitation below.

memattest proves integrity and provenance: that a memory is unaltered since it was recorded, that it was recorded by a specific installation, and that it sits at a specific, unforgeable position in the write history.

What memattest does NOT do

memattest does not prove that a memory's content is true, safe, or free of manipulation. If a compromised agent writes a poisoned memory through the normal, legitimate write path, memattest will record that write faithfully. The hash, timestamp, provenance, and signature will all be valid, because nothing about the write was out-of-band. memattest does not classify or validate the memory content. That's deliberately out of scope. For screening memory content (prompt injection, PII, policy violations, and similar), pair memattest with a complementary tool such as OWASP Agent Memory Guard; memattest is designed to sit beneath such tools as the tamper-evidence and sequencing layer, not to replace them.

Quickstart

memattest requires Python 3.12 or newer and is published on PyPI. Install it into a virtual environment:

python -m venv .venv
.venv\Scripts\activate    # Windows; use `source .venv/bin/activate` on Linux/macOS
pip install memattest

One Windows note for the block above: if your shell is Git Bash, the activation path is the Windows layout with POSIX syntax: source .venv/Scripts/activate.

To work from source instead (for development, or to pick up unreleased changes), clone this repository and install it from the repository root (the directory containing pyproject.toml):

cd <path-to-memattest-repo>
python -m venv .venv
.venv\Scripts\activate
pip install -e .

Next you need the path to the memory directory you want to guard- the <MEMORY_DIR> placeholder. memattest works with any directory of files so it can support any agent or agent harness, and their memory folder paths will differ from each other. As a concrete example, Claude Code keeps each project's persistent memory under the user profile at:

<home>/.claude/projects/<my-project>/memory/

where <my-project> is derived from the project's working directory with separators replaced by dashes. For example, the project C:\source\myproject maps to C--source-myproject, giving C:/Users/<you>/.claude/projects/C--source-myproject/memory. Inside, the directory named memory typically holds MEMORY.md (an index the agent loads every session) plus one Markdown file per remembered fact. Point memattest at the memory directory itself, not the <my-project> directory above it; that parent also holds session data that changes constantly and would drown verification in false alarms.

[!IMPORTANT] Just a reminder- this example is for Claude Code. The memory directory will differ with other agents and agent harnesses. For example, in Codex it's ~/.codex/memories/

Initialize the memory directory. This generates a per-installation Ed25519 signing key, seals it in the OS keystore, and adopts any pre-existing files in the directory as the trusted baseline:

memattest init --memory-dir <MEMORY_DIR>

Double-check the path you pass: init guards whatever directory you point it at, recursively, and cannot know your intent. It reports how many pre-existing files it adopted — if that count surprises you, you initialized the wrong directory. To undo an init (or to uninstall memattest's state for a directory): delete the <MEMORY_DIR>/.memattest directory, then remove the sealed signing key, which is stored under the directory's resolved absolute path:

python -c "import keyring; keyring.delete_password('memattest', r'C:\full\resolved\path\to\MEMORY_DIR')"

To wire memattest into Claude Code, run the installer from your project directory and follow its prompts:

cd <YOUR_PROJECT>
memattest install

One interactive run performs the whole onboarding: it locates the project's Claude Code memory directory (shown in the printed plan for you to confirm; pass --memory-dir for custom layouts), runs init if the directory isn't initialized yet, merges the three hooks and the permission deny rules into the settings file you choose (shared is recommended; existing unrelated entries are preserved, and re-running updates the memattest entries in place), and finishes with a verify. Like adopt, it only runs from an interactive terminal, prints its full plan, and asks for typed confirmation before writing anything — and the PreToolUse guard denies agent-run memattest install invocations, since the command rewrites the same trust surface the guard protects.

Prefer to wire things by hand, or use another harness? Copy the hooks and permission rules from src/memattest/integrations/claude_code/settings-snippet.json into your project's .claude/settings.json, substituting two placeholders:

  • <MEMATTEST_BIN> — the absolute path to the venv's console script (e.g. C:/path/to/repo/.venv/Scripts/memattest). Hooks run in a fresh shell without your venv activated, so a bare memattest will not resolve; an absolute path also defeats PATH hijacking (see Hardening).
  • <MEMORY_DIR> — your memory directory as an absolute path. On Windows use drive notation with forward slashes (C:/Users/you/...), not Git Bash notation (/c/Users/...), which other shells and Python misinterpret.

The template configures three hooks and two permission rules:

  • A SessionStart hook runs memattest hook session-start, which verifies the log and delivers the result as hook JSON: additionalContext places the report in the agent's context, and systemMessage shows the result in your console on every session start (a one-line OK N entries verified on a clean log, the full report on a problem). Seeing that line each session is how you tell a healthy session from one where the hook was removed. The hook session-start subcommand always exits 0 by design, because Claude Code discards a SessionStart hook's stdout on a non-zero exit. It wraps verify for exactly this reason: the plain memattest verify command exits non-zero when it finds a problem, so wiring it directly as the hook would let Claude Code discard the tamper report before the agent, the party about to act on the memory, ever saw it.

  • A PostToolUse hook (matching Write|Edit) runs memattest hook post-tool-use to append a log entry after every memory write.

  • A PreToolUse hook (matching Bash|PowerShell|Write|Edit) runs memattest hook pre-tool-use, which denies three kinds of proposed tool call. First, any command that invokes memattest adopt, including quoted or path-prefixed spellings such as & "C:/.../memattest" adopt that permission glob rules cannot match. Second, any command that invokes memattest install — the installer rewrites the hook configuration, so only a human at a terminal may run it. Third, any agent edit of the trust surface itself: Write/Edit calls targeting a Claude Code settings file (.claude/settings.json, .claude/settings.local.json, or the user-level one), and shell commands that reference those files or the disableAllHooks flag — the routes by which an agent could un-hook memattest for its next session. Humans editing settings in their own editor are unaffected.

    [!IMPORTANT] all three matches are deliberately broad (fail-closed), so a command that merely mentions the phrases, such as a commit message, is denied too.

    [!IMPORTANT] An agent that can execute arbitrary Python can still reach the adopt API directly, which no command filter can catch; the signed, provenance-stamped adopt entry is what keeps that path from being silent (see Security limitations).

  • Permission deny rules (Bash(*memattest adopt*), Bash(*memattest install*), and their PowerShell twins) remain as a second layer, but in my testing the PreToolUse hook is the layer that actually catches real invocations.

Claude Code snapshots hook configuration at session start, so the hooks take effect in the next session, not the one where you edit the settings.

Run verification at any time, not just from a hook:

memattest verify --memory-dir <MEMORY_DIR>

A clean log prints a single OK <n> entries verified line and exits 0. A compromised log prints one PROBLEM line per finding (kind, path, detail, last known good entry) and exits 1 (or 3 if entries use an unknown scheme version— see Exit codes below).

If you edit a guarded memory file by hand between sessions (a legitimate out-of-band change, not tampering), verification will correctly report it as a divergence. Reconcile it with adopt, which appends a new signed entry recording the file's current hash together with a required justification:

memattest adopt --path <MEMORY_DIR>/notes.md --reason "manual correction of stale project name"

adopt and record locate the guarded directory from the file path itself: the file's containing folder must hold the .memattest state directory. In scenarios where it doesn't, like when the memory file is in a subdirectory of the memory directory, or when the containing folder is not the guarded root, pass --memory-dir to explicitly specify the memory directory. Example:

memattest adopt --path <MEMORY_DIR>/subfolder/notes.md --memory-dir <MEMORY_DIR> --reason "manual correction of stale project name"

The confirmation prompt always names the directory being adopted into, so check it before typing adopt.

adopt only runs from an interactive terminal and asks for typed confirmation; there is no non-interactive or --yes flag, by design. The terminal check is best-effort, which is why the Claude Code template also blocks adopt at the hook layer (see Security limitations below).

[!IMPORTANT] The adopt command is designed to be run manually and interactively. It's security-critical and should not be automated.

One Windows-specific note for anyone testing the stdin-reading hook subcommands (hook post-tool-use, hook pre-tool-use) by hand: piping JSON into memattest.exe from Windows PowerShell 5.1 does not reliably deliver the payload on stdin, and PowerShell can also prepend a byte-order mark that breaks JSON parsing. Use Git Bash (or WSL) for manual hook testing; Claude Code's own hook invocation does not go through PowerShell and is unaffected.

Keystores

The Ed25519 signing key never touches disk unencrypted; it is always sealed behind a KeyStore backend, selected with --keystore.

  • --keystore keyring (the default) uses the OS-native credential store via the keyring package: DPAPI on Windows, Secret Service (or the matching desktop keyring) on Linux, and Keychain on macOS. These require a user session, and Secret Service actually needs an active desktop session.
  • --keystore file encrypts the key in a file (scrypt-derived AES-256-GCM, written with 0600 permissions) for headless hosts where no OS keyring is available — CI runners, containers, servers without a logged-in desktop session. Of course, it can also be used in a desktop session, as an alternative to the keyring approach. It requires the MEMATTEST_PASSPHRASE environment variable to be set on every invocation; there is no default or embedded passphrase.
MEMATTEST_PASSPHRASE="correct horse battery staple" \
  memattest init --memory-dir <MEMORY_DIR> --keystore file

The choice is recorded in the log's .memattest/config.toml at init, and the config is authoritative from then on: later commands need no --keystore flag, and passing one that contradicts the config is an operational error rather than a lookup in the wrong backend keystore (which used to end in a false key-missing alarm). Logs initialized before this feature record their config automatically on their next successful append. Each backend keystore seals the key under a name derived from the memory directory's resolved path, so there is no way to move a key between backend keystores after init. If you need to work around this (e.g., if the config is wrong), edit config.toml by hand. The config file ships unsigned, so it is possible to add invalid or incorrect entries. However, there is sufficient validation code to cover every invalid condition, which will produce error messages to the console (and stderr) at the next session start. Cryptographic sealing is planned together with the watch list for a future update. Be careful about not exposing MEMATTEST_PASSPHRASE- doing so can weaken keyring-anchored logs because a memory-directory writer who knows it can redirect the config to a planted file backend keystore.

Every verify (including the session-start hook) cross-checks pubkey.ed25519 on disk against the public key re-derived from the signing seed in the backend keystore. To audit a copied log on a machine that never had the key- a restored backup before re-initializing, incident response on a clean machine, a third-party or CI audit— pass --no-key-check:

memattest verify --memory-dir <COPY_OF_MEMORY_DIR> --no-key-check

This skips only the backend-keystore cross-check; signatures, tree consistency, and file state are still fully verified against the pubkey file that travels with the log. After restoring a backup onto a new machine, verify with --no-key-check first and re-init only once the report is clean and you have reviewed the memory contents — re-init adopts whatever is on disk. A copied log verified on another machine will report its watched files as missing, since their recorded paths are absolute and local to the origin machine; this is expected, because the watched files themselves do not travel with the log.

Auditing with proofs

memattest prove emits the RFC 6962 proofs that let someone else check your log without trusting your machine:

  • --index N prints the inclusion proof for entry N — the audit path, a JSON array of hex-encoded hashes.
  • --old-size K prints the consistency proof between the K-entry tree and the current tree — evidence that the log grew append-only, with nothing rewritten or reordered.

You never need prove for your own log: verify already recomputes the full tree and checks every entry directly. prove exists for other parties — an auditor holding a snapshot you handed them, or, once external root anchoring lands (planned), anyone checking that today's log is an append-only extension of a previously published tree head, which is what makes rollback detectable.

A small example. With three entries in the log:

$ memattest prove --memory-dir <MEMORY_DIR> --index 1
["a7f3c2…", "5d90ee…"]

(hashes shortened here). An auditor holding entry 1's bytes, this audit path, and a signed tree head recomputes the root — hash the entry, then combine it pairwise with each hash in the path — and compares the result against the root in the tree head. A match proves the entry is in the tree that head commits to; they never need the rest of the log.

Watching the trust surface

memattest guards the files in your memory directory, but the hook configuration that makes it run- the Claude Code settings file, and instruction files like CLAUDE.md- lives outside that directory. The watch list extends coverage to designated external files (outside the memory dir): an out-of-band edit to a watched file is reported at the next session start, exactly like a tampered memory file.

memattest install automatically watches the settings file it writes when that is the shared settings.json, so the hook configuration is covered out of the box. It does not watch settings.local.json, because Claude Code writes permission decisions into that file and watching it whole would report those routine writes as tampering; a hooks-only watch is the planned way to cover a local settings file. To watch another file, adopt it- a path outside the memory directory is recorded as a watched file rather than a memory file:

memattest adopt --path <PROJECT>/CLAUDE.md --project <PROJECT> --reason "baseline project instructions"

--project is a Claude Code convenience: it derives the memory directory from the project path using Claude Code's directory layout, so you do not type the full path. With any other harness (Codex and the like) or a custom layout, pass --memory-dir with the memory directory instead. Use one or the other, not both.

When a watched file legitimately changes, re-baseline it by adopting it again with a reason. To stop watching a file (or to clear the report for one you deliberately deleted), use unwatch:

memattest unwatch --path <PROJECT>/CLAUDE.md --project <PROJECT> --reason "no longer used"

Both adopt and unwatch run only from an interactive terminal with typed confirmation, and the PreToolUse guard denies agent-run invocations, since changing what is watched changes your tamper-detection coverage.

KNOWN LIMITATIONS:

LIMITATION 1: If someone removes the memattest hook from the settings file entirely, verification won't run and the watch on that file won't fire. If you have previously enabled memattest and you stop seeing the message at session start (no OK N entries verified line), that may indicate malicious disabling by removal of the hook.

LIMITATION 2: An attacker who can write the state directory can delete the newest entries together with their signed tree heads: this needs only file access, not the signing key. Verify won't detect this change because the surviving tree heads still check out. A silently dropped watch entry essentially leaves no trace.

Both will be addressed by the planned implementation of a validator running under a separate account, which will also secure the latest tree head against rollback.

Hardening your installation

memattest detects tampering; it does not prevent it. Filesystem access control is critical to prevent things like malicious removal of the memattest hook.

[!IMPORTANT] Before relying on memattest, secure the memory and installation folders!

In rough priority order:

  • Restrict write access to the memory directory. The primary adversary is an unprivileged process or another user writing to your memory files out-of-band. Fewer possible writers means both less exposure and fewer false alarms. Keep <MEMORY_DIR> (including .memattest/) inside your own user profile with default private permissions — 0700 on Linux/Mac and the standard user-profile ACLs on Windows. Never use a world-writable or group-shared location.
  • Protect the memattest installation itself. The hooks execute memattest at every session start, so whoever can modify the installed code can disable or subvert verification. Apply the same access restrictions to your synced memattest repo and its .venv that you apply to the memory directory.
  • The backend keystore is the trust anchor. verify re-derives the public key from the keystore-held signing seed and cross-checks the disk copy, so your OS credential store (or the MEMATTEST_PASSPHRASE for the file backend keystore) is part of the trust surface. A key-missing finding at session start means the keystore entry is gone and the log's authorship can no longer be established locally — treat the memory contents as untrusted and review them manually before re-initializing.
  • Invoke the hook commands by absolute path. The settings template's <MEMATTEST_BIN> placeholder exists for exactly this reason: a bare memattest resolves through PATH, and a writable directory earlier on PATH is a classic hijack point. Fill the placeholder with the absolute path to the venv's console script (e.g. C:/path/to/repo/.venv/Scripts/memattest or /path/to/repo/.venv/bin/memattest) and don't replace it with a bare command name.
  • Treat the hook configuration as part of the trust surface. An attacker who can edit the settings file can remove the verify hook or the adopt guard. The template's PreToolUse guard blocks the agent's routes to those files (file tools and shell alike), but an attacker outside the session is not so constrained. Prefer the shared .claude/settings.json for the memattest hooks where practical— it normally lives in the repository, so edits leave a visible trail. Hooks defined there can't be selectively overridden from another settings file, but they can be globally silenced: "disableAllHooks": true in any scope — including the gitignored settings.local.json or the user-level ~/.claude/settings.json, both outside the repository's visibility — turns off every hook at the next session start. The installer refuses to add memattest hooks to a settings file when they already exist in another scope, so it will not silently create the duplicate registration that makes each hook fire more than once per event. Also keep in mind that memattest only emits messages when a hook invokes it, so removal of the hook will be observed as silence, not a report. So, if you don't see any message from memattest (e.g., memattest: OK) when starting your agent, treat the session as suspect. Instruct the agent (in CLAUDE.md or AGENTS.md) to be suspicious if it doesn't see a memattest status when it starts as well.
  • Keep the memory directory out of cloud-synced folders. Sync services (OneDrive, Dropbox, iCloud) rewrite files out-of-band by design, so sync conflicts or restores become indistinguishable from tampering.
  • Handle MEMATTEST_PASSPHRASE like the secret it is (file backend only). Don't hardcode it in shell profiles or scripts that get committed; inject it at invocation time from a secret manager or the environment of a protected service account.
  • Keep a backup of the memory directory (including .memattest/). If the memories are somehow tampered, you'll at least have a known-good copy to restore.

Extending provenance

Every recorded entry carries a provenance block built by collecting claims from a set of providers. memattest has built-in providers for the agent harness, the writing process (pid, executable, parent-process chain), the machine (hostname, a stable machine id), and the session (user, session id, whether the write happened at an interactive TTY). Third parties can add their own claims without touching memattest's source by registering a Python entry point in the memattest.providers group. A provider is any zero-argument callable that returns a JSON-serializable dict; its return value is recorded under the entry point's name in every subsequent entry.

The canonical example is a git workspace provider, which ties a memory write to the exact code state the agent was working in:

# in your package
def git_workspace_claims() -> dict:
    import subprocess
    def _git(*args):
        return subprocess.check_output(["git", *args], text=True).strip()
    return {
        "repo": _git("rev-parse", "--show-toplevel"),
        "branch": _git("rev-parse", "--abbrev-ref", "HEAD"),
        "head": _git("rev-parse", "HEAD"),
    }
# in your package's pyproject.toml
[project.entry-points."memattest.providers"]
git_workspace = "your_package.providers:git_workspace_claims"

Once installed, every memattest entry recorded in that environment will carry a git_workspace claim alongside the built-ins. A provider that raises an exception cannot abort the write or take down the other providers: memattest isolates each provider call and records {"error": "..."} for the failing one instead, so a broken plugin degrades provenance richness rather than availability.

Security limitations

memattest v1's threat model is deliberately scoped. It protects against an unprivileged process or a different OS user tampering with memory files on disk out-of-band, and against a compromised or prompt-injected agent trying to alter, delete, or reorder its own past memories through the log itself. It does not protect against a fully privileged attacker running as the same user memattest itself runs as:

Malware running as your own OS user can unseal the signing key and forge history. memattest v1 protects against other users' processes and against agents rewriting their own history — not against same-user malware. A v2 resident validator service under a separate account is the planned mitigation.

A few related boundaries are worth calling out:

  • Admin/SYSTEM-level attackers are out of scope. Defending against an attacker with administrative privileges would require TPM-backed key sealing or an external root of trust; v1 has neither.
  • Remote or synced memory stores are out of scope. Key distribution and multi-device identity are unsolved in this version; memattest assumes a single local installation with a single signing identity.
  • Same-user tampering routed through adopt cannot be silent. Even if same-user malware unseals the key and calls adopt to launder a bad state into the log, it still leaves a permanent, signed adopt entry, including the parent-process chain and whether the call came from an interactive TTY. The event remains visible to anyone who later inspects the log with memattest log, even though it wasn't blocked.
  • The adopt terminal check can be circumvented. adopt refuses to run when stdin reports itself as non-interactive, but Python's isatty() can return true in environments that are not meaningfully interactive (like an agent's Git Bash shell tool on Windows) and a pty wrapper such as script, winpty, or expect can satisfy the check deliberately. It's more of a limited protection against accidental scripting. You should rely on the harness-level PreToolUse guard to keep agents away from adopt; the signed adopt entry (previous bullet) is what keeps even a successful bypass from being silent. The install command carries the identical best-effort check and the identical answer: the PreToolUse guard is the layer that keeps agents away from it, and unlike adopt a bypassed install leaves no signed log entry — only the settings-file diff.
  • Trust anchor. verify re-derives the public key from the signing seed in the backend keystore and cross-checks pubkey.ed25519 on disk, so an attacker with write access to the memory directory who swaps the pubkey and re-signs history is reported (key-mismatch, plus bad-signature on the forged tree heads), and a deleted keystore entry is reported (key-missing) at the next session start instead of surfacing later as a failed append. A key-missing log's authorship cannot be established — accidental key loss and a hostile rewrite that also deleted the keystore entry are indistinguishable — so review memory contents manually before re-adopting them under a new key. Same-user malware can rewrite the keystore entry itself and defeat the cross-check; that gap remains until the v2 validator service, as does rollback (next bullet). External root anchoring (v2) hardens this further.
  • Rollback. Because every append seals a valid tree head, an attacker who deletes a suffix of entries together with their covering tree heads and the created files reverts the log to an earlier, fully valid sealed state undetected. Detecting rollback requires an external anchor for the latest tree head (v2).

Exit codes

Code Meaning
0 Clean — no tampering or inconsistency detected
1 Tamper detected — see the printed PROBLEM lines for file, hashes, and last-valid entry; includes key-mismatch and key-missing from the signing-key cross-check
2 Operational error — e.g. not initialized, backend keystore unreachable for the signing-key cross-check (--no-key-check skips it when auditing a copied log), malformed hook payload; appends fail closed rather than record an unverifiable entry
3 Unknown scheme version — an entry was written by a newer scheme than this verifier understands, and is refused rather than guessed at

memattest hook session-start is a deliberate exception: it exits 0 for clean, tampered, and operational-error outcomes alike, delivering each through hook JSON, because Claude Code discards a SessionStart hook's stdout on any non-zero exit. A failing verify also prints a one-line verification FAILED alert to stderr, so a harness that surfaces only the stderr of a failed hook still says something useful.

License

MIT — see LICENSE.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

memattest-1.0.2.tar.gz (79.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

memattest-1.0.2-py3-none-any.whl (40.8 kB view details)

Uploaded Python 3

File details

Details for the file memattest-1.0.2.tar.gz.

File metadata

  • Download URL: memattest-1.0.2.tar.gz
  • Upload date:
  • Size: 79.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.14

File hashes

Hashes for memattest-1.0.2.tar.gz
Algorithm Hash digest
SHA256 2f1485420244f5231f57a3e36cab9a4438db507b0d49af7b46ebcf194981db3a
MD5 baef1dd24832053f683cc4023ada83e4
BLAKE2b-256 ead3d377a1029d17c690fd3d609a9849f06a4bc3ba64558a1d95eeaca09c77a7

See more details on using hashes here.

Provenance

The following attestation bundles were made for memattest-1.0.2.tar.gz:

Publisher: python-publish.yml on johnlatino/memattest

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file memattest-1.0.2-py3-none-any.whl.

File metadata

  • Download URL: memattest-1.0.2-py3-none-any.whl
  • Upload date:
  • Size: 40.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.14

File hashes

Hashes for memattest-1.0.2-py3-none-any.whl
Algorithm Hash digest
SHA256 969f9ca64043e3fb5673e1a152688a8d6d0d40e954c57b29862ccaf029ed3409
MD5 ef781657f30d7e660c62d70253354773
BLAKE2b-256 31f409201b4e1df44d113b3b5ba149e9fbb92df1519efaab5c36e9bc0c521d02

See more details on using hashes here.

Provenance

The following attestation bundles were made for memattest-1.0.2-py3-none-any.whl:

Publisher: python-publish.yml on johnlatino/memattest

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page