Tamper-evident AI agent memory: RFC 6962 append-only Merkle log with signed tree heads and provenance
Project description
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 barememattestwill not resolve; an absolute path also defeatsPATHhijacking (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
SessionStarthook runsmemattest hook session-start, which verifies the log and delivers the result as hook JSON:additionalContextplaces the report in the agent's context, andsystemMessageshows the result in your console on every session start (a one-lineOK N entries verifiedon 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. Thehook session-startsubcommand always exits 0 by design, because Claude Code discards a SessionStart hook's stdout on a non-zero exit. It wrapsverifyfor exactly this reason: the plainmemattest verifycommand 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
PostToolUsehook (matchingWrite|Edit) runsmemattest hook post-tool-useto append a log entry after every memory write. -
A
PreToolUsehook (matchingBash|PowerShell|Write|Edit) runsmemattest hook pre-tool-use, which denies three kinds of proposed tool call. First, any command that invokesmemattest adopt, including quoted or path-prefixed spellings such as& "C:/.../memattest" adoptthat permission glob rules cannot match. Second, any command that invokesmemattest 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/Editcalls 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 thedisableAllHooksflag — 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
adoptentry is what keeps that path from being silent (see Security limitations). -
Permission
denyrules (Bash(*memattest adopt*),Bash(*memattest install*), and theirPowerShelltwins) remain as a second layer, but in my testing thePreToolUsehook 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
adoptcommand 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 thekeyringpackage: 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 fileencrypts the key in a file (scrypt-derived AES-256-GCM, written with0600permissions) 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 theMEMATTEST_PASSPHRASEenvironment 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 Nprints the inclusion proof for entry N — the audit path, a JSON array of hex-encoded hashes.--old-size Kprints 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 —0700on 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
.venvthat you apply to the memory directory. - The backend keystore is the trust anchor.
verifyre-derives the public key from the keystore-held signing seed and cross-checks the disk copy, so your OS credential store (or theMEMATTEST_PASSPHRASEfor the file backend keystore) is part of the trust surface. Akey-missingfinding 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 barememattestresolves throughPATH, and a writable directory earlier onPATHis 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/memattestor/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
adoptguard. The template'sPreToolUseguard 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.jsonfor 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": truein any scope — including the gitignoredsettings.local.jsonor 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 (inCLAUDE.mdorAGENTS.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_PASSPHRASElike 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
adoptcannot be silent. Even if same-user malware unseals the key and callsadoptto launder a bad state into the log, it still leaves a permanent, signedadoptentry, 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 withmemattest log, even though it wasn't blocked. - The
adoptterminal check can be circumvented.adoptrefuses to run when stdin reports itself as non-interactive, but Python'sisatty()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 asscript,winpty, orexpectcan satisfy the check deliberately. It's more of a limited protection against accidental scripting. You should rely on the harness-levelPreToolUseguard to keep agents away fromadopt; the signedadoptentry (previous bullet) is what keeps even a successful bypass from being silent. Theinstallcommand carries the identical best-effort check and the identical answer: thePreToolUseguard is the layer that keeps agents away from it, and unlikeadopta bypassed install leaves no signed log entry — only the settings-file diff. - Trust anchor.
verifyre-derives the public key from the signing seed in the backend keystore and cross-checkspubkey.ed25519on disk, so an attacker with write access to the memory directory who swaps the pubkey and re-signs history is reported (key-mismatch, plusbad-signatureon 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. Akey-missinglog'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
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2f1485420244f5231f57a3e36cab9a4438db507b0d49af7b46ebcf194981db3a
|
|
| MD5 |
baef1dd24832053f683cc4023ada83e4
|
|
| BLAKE2b-256 |
ead3d377a1029d17c690fd3d609a9849f06a4bc3ba64558a1d95eeaca09c77a7
|
Provenance
The following attestation bundles were made for memattest-1.0.2.tar.gz:
Publisher:
python-publish.yml on johnlatino/memattest
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
memattest-1.0.2.tar.gz -
Subject digest:
2f1485420244f5231f57a3e36cab9a4438db507b0d49af7b46ebcf194981db3a - Sigstore transparency entry: 2263050632
- Sigstore integration time:
-
Permalink:
johnlatino/memattest@cd67660e55f5f4381e59c8106f5ee95b932a5a85 -
Branch / Tag:
refs/tags/v1.0.2 - Owner: https://github.com/johnlatino
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
python-publish.yml@cd67660e55f5f4381e59c8106f5ee95b932a5a85 -
Trigger Event:
release
-
Statement type:
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
969f9ca64043e3fb5673e1a152688a8d6d0d40e954c57b29862ccaf029ed3409
|
|
| MD5 |
ef781657f30d7e660c62d70253354773
|
|
| BLAKE2b-256 |
31f409201b4e1df44d113b3b5ba149e9fbb92df1519efaab5c36e9bc0c521d02
|
Provenance
The following attestation bundles were made for memattest-1.0.2-py3-none-any.whl:
Publisher:
python-publish.yml on johnlatino/memattest
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
memattest-1.0.2-py3-none-any.whl -
Subject digest:
969f9ca64043e3fb5673e1a152688a8d6d0d40e954c57b29862ccaf029ed3409 - Sigstore transparency entry: 2263050816
- Sigstore integration time:
-
Permalink:
johnlatino/memattest@cd67660e55f5f4381e59c8106f5ee95b932a5a85 -
Branch / Tag:
refs/tags/v1.0.2 - Owner: https://github.com/johnlatino
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
python-publish.yml@cd67660e55f5f4381e59c8106f5ee95b932a5a85 -
Trigger Event:
release
-
Statement type: