powerstamp
Energy measurement that reports its own measurement uncertainty.
powerstamp measures the energy consumed by a piece of work and reports the
joule figure together with the sampling rate it actually achieved, the rails and
domains it actually covered, and a quantified uncertainty. When the measurement
cannot support a confident number, it says so instead of printing one.
The field this serves reports "joules per token" and "energy per inference" constantly, and almost never states the sampling rate, the rails covered, or an error bar. Numbers reported that way are not comparable between papers. The first class output of this tool is therefore not the joule figure. It is the joule figure with its rate, its rails and its uncertainty attached.
Status: version 0.1.0, not published to PyPI and not tagged. See
RELEASE_CHECKLIST.md.
Why this exists
Two findings from the author's own prior work set the shape of the tool.
The first is a measurement error that was caught and corrected. In the author's
Raspberry Pi 5 energy work, the PMIC's effective sampling rate turned out to be
21.2 Hz, not the 43 to 44 Hz that a naive read count suggested, because the
apparent figure was twice a per rail read count: vcgencmd pmic_read_adc returns
a voltage channel and a current channel for each rail, and counting both as
samples doubles the apparent rate. The paper's conclusions survived with better
than a six times margin, but the rate was wrong in an earlier draft and had to be
corrected. A reported rate that is an artefact of how the reads were counted is
exactly the class of error this tool exists to prevent, so powerstamp measures
the rate from timestamps on completed samples and reports the raw channel read
rate as a separate, separately named field.
The second is why the rate matters at all. That line of work found that the latency-optimal configuration is not the energy-optimal one. That is only a meaningful claim if the energy measurement is trustworthy, which means the rate, the rails and the error bar have to travel with the number.
Install
Not on PyPI yet. From a checkout:
pip install -e .
pip install -e ".[nvml]" # adds nvidia-ml-py for the NVIDIA backend
pip install -e ".[test]" # adds pytest
Pure standard library at runtime. The NVIDIA backend is the only optional dependency, and its absence is reported as an unavailable backend rather than an import error.
Usage
Context manager
from powerstamp import Powerstamp
with Powerstamp(sample_hz=20, label="decode") as ps:
decode(frames)
print(ps.result.summary())
print(ps.result.to_json())
A refusal is a result, not an exception. ps.result is always present, and its
verdict is PASS, INVALID, UNSUPPORTED or UNKNOWN.
Decorator
from powerstamp import powerstamp
@powerstamp(sample_hz=20, baseline_s=2.0)
def decode(frames):
...
decode(frames)
print(decode.last_powerstamp.summary())
for r in decode.powerstamp_results:
print(r.joules, r.verdict)
The wrapped function returns its own value unchanged, so the decorator never changes a call signature.
Function
from powerstamp import measure
value, result = measure(decode, frames, sample_hz=20, baseline_w=3.1)
CLI
powerstamp detect # which backends are available, and why not
powerstamp run --sample-hz 20 -- python bench.py
powerstamp run --sample-hz 20 --baseline-seconds 2 -o result.json -- ./bench
powerstamp reasons # every reason code the tool can emit
powerstamp schema # the output envelope
Exit codes: 0 PASS, 1 INVALID, 2 UNSUPPORTED, 3 UNKNOWN, 4 usage error
or, with --propagate, a failure of the measured command itself. A non-zero exit
on INVALID is deliberate. A CI job that treats a refusal as a success is back to
publishing numbers the measurement cannot support. --allow-invalid exits 0
anyway; it does not change the verdict in the JSON.
Real output from a machine with no energy interface at all:
$ powerstamp detect
rapl unavailable no powercap sysfs at /sys/class/powercap
nvml unavailable pynvml is not importable
rpi_pmic unavailable vcgencmd is not on PATH
android_fuel_gauge unavailable no power supply node under /sys/class/power_supply exposes both charge_counter and voltage_now
No backend is available, so powerstamp would report UNSUPPORTED. It does not
estimate energy from CPU utilization and present it as a measurement.
GitHub Action
action.yml sits at the repository root, so the Action is usable directly:
- uses: manunicholasjacob/powerstamp@v0.1.0
with:
run: python bench.py
sample-hz: '20'
baseline-seconds: '2'
output: powerstamp.json
Outputs: joules, watts-mean, achieved-hz, sample-count,
expanded-uncertainty-j, verdict, kind, backend, json-path.
GitHub hosted runners do not expose a readable RAPL counter, so on a hosted
runner the Action will report UNSUPPORTED and, by default, not fail the job.
It is useful as it stands on a self-hosted runner with a readable energy
interface, and on a hosted runner it is an honest UNSUPPORTED record rather
than a fabricated number. fail-on controls that: see action.yml.
Output schema
The outer envelope is a shared provenance record. Everything specific to energy
measurement lives under measurement.
{
"schema": "measurement-provenance/1",
"tool": {"name": "powerstamp", "version": "0.1.0"},
"taken_at_utc": "2026-11-01T00:00:00.000000Z",
"duration_s": 2.9716981132075473,
"platform": {"system": "", "release": "", "machine": "", "python": ""},
"verdict": "PASS",
"reasons": [{"code": "SAMPLE_COUNT_MARGINAL", "severity": "warning", "detail": "..."}],
"unreadable": ["pmic:EXT5V (voltage only, no current channel)"],
"measurement": { "...": "..." }
}
Top level keys, all nine always present:
| Key | Meaning |
|---|---|
schema |
measurement-provenance/1 |
tool |
{name, version} |
taken_at_utc |
ISO 8601 UTC with a Z suffix |
duration_s |
wall clock duration of the measured region |
platform |
system, release, machine, python |
verdict |
PASS, INVALID, UNSUPPORTED or UNKNOWN |
reasons |
list of {code, severity, detail}; severity is invalidating, warning or info |
unreadable |
every domain or rail that could not be read, so a consumer sees an absence rather than a zero |
measurement |
the energy payload |
The unreadable list is the one that matters most here. A package-only RAPL
reading must not be presentable as a system reading, and this is where that is
recorded.
Inside measurement:
| Field | Meaning |
|---|---|
kind |
MEASUREMENT, ESTIMATE or NONE. The measurement-versus-estimate flag, as a field |
is_measurement |
boolean form of the same flag, for consumers that prefer a boolean |
backend, backend_detail, backend_coverage |
which backend ran, what it found, and what it physically covers |
reduction |
counter (a hardware energy counter delta) or sampler (an integral of sampled power) |
joules, watts_mean |
the figure, and the figure divided by the duration. null when UNSUPPORTED |
rate |
requested_hz, achieved_hz, sample_count, interval_count, span_s, and the interval mean, standard deviation, minimum, maximum and relative standard deviation |
channel_read_hz, mean_reads_per_sample |
the raw hardware read rate, kept strictly separate from the sample rate |
uncertainty |
standard_uncertainty_j, expanded_uncertainty_j, coverage_factor, relative_expanded, the three components_j, an excludes list and free-text notes |
refusal_rule |
the thresholds in force, and the duration floor they imply at the achieved rate |
per_domain_j |
joules per domain or per rail |
baseline |
subtracted, baseline_w, source, joules_before_baseline |
backends_rejected |
every backend that was tried and why it could not run |
docs/example_envelope.json holds a complete record. It was produced by
replaying the recorded Raspberry Pi 5 pmic_read_adc capture in
tests/fixtures/ through the real code path at a 21.2 Hz sample timeline, so the
structure and the arithmetic are real; the Type A component is zero in it because
a replayed constant capture has no scatter, and the rail currents are a single
snapshot rather than a live run.
reasons[].code values
Sample sufficiency and timing:
| Code | Severity | Meaning |
|---|---|---|
NO_SAMPLES |
invalidating | Fewer than two samples, so no interval exists |
SAMPLE_COUNT_TOO_LOW |
invalidating | Fewer complete samples than the documented minimum |
SAMPLE_COUNT_MARGINAL |
warning | Over the minimum but under the marginal band, so the error bar is itself loose |
RATE_SHORTFALL |
warning | The achieved rate fell below 80 percent of the requested rate |
SAMPLER_JITTER_HIGH |
warning | Interval relative standard deviation above 0.25 |
Counters:
| Code | Severity | Meaning |
|---|---|---|
COUNTER_WRAPAROUND |
info | A counter rolled over and the rollover was corrected |
COUNTER_WRAPAROUND_AMBIGUOUS |
invalidating | A single interval could have held more than one rollover, so the total is not recoverable |
ENERGY_FROM_HARDWARE_COUNTER |
info | The figure is a counter delta |
ENERGY_FROM_POWER_INTEGRAL |
info | The figure is an integral of sampled power, which is a different measurement |
Coverage:
| Code | Severity | Meaning |
|---|---|---|
PARTIAL_DOMAIN_COVERAGE |
warning | One or more domains or rails could not be read |
PACKAGE_ONLY_COVERAGE |
warning | Only package domains were readable, so this is not a system figure |
PERMISSION_DENIED |
warning | A counter file exists but is not readable with the current privileges |
NO_READABLE_DOMAINS |
invalidating | The backend ran but no domain or rail produced a reading |
PMIC_VOLTAGE_ONLY_CHANNELS |
info | Some PMIC channels have no matching current channel and contribute no power term |
PMIC_EXCLUDES_EXTERNAL_5V |
warning | PMIC rail readings exclude USB current and anything else on the 5V supply |
BATTERY_SIDE_READING |
warning | A battery side reading includes the display, the modem and everything else |
GPU_BOARD_SCOPE_UNSTATED |
warning | The vendor API does not state which internal rails the device figure covers |
Baseline and support:
| Code | Severity | Meaning |
|---|---|---|
BASELINE_SUBTRACTED |
info | An idle baseline was subtracted |
BASELINE_NOT_SUBTRACTED |
info | No baseline was subtracted, so idle draw is included |
BASELINE_EXCEEDS_MEASUREMENT |
invalidating | The baseline was not less than the measurement, so the net would be negative |
NO_BACKEND |
invalidating | No backend is available on this platform |
BACKEND_UNAVAILABLE |
invalidating | A backend was requested by name and is not available here |
ESTIMATE_NOT_MEASUREMENT |
warning | The figure is a model based estimate and can never earn PASS |
The verdict rule, in order: no backend gives UNSUPPORTED; any invalidating
reason gives INVALID; an ESTIMATE caps at UNKNOWN; otherwise PASS.
Backend matrix
What each backend does and, just as importantly, does not cover.
| Backend | Source | Reduction | Covers | Does not cover | Notes |
|---|---|---|---|---|---|
rapl |
/sys/class/powercap/intel-rapl*/energy_uj and subzones, plus amd-rapl* |
counter | whichever of package, core, uncore, dram, psys the kernel exposes and the process can read | discrete GPU, storage, network interface, display, power supply conversion loss | Root only since kernel commit 949dd0104c49. Rollover handled at max_energy_range_uj. Which domains were readable is reported; a package-only reading is flagged as such |
nvml |
nvmlDeviceGetTotalEnergyConsumption, falling back to nvmlDeviceGetPowerUsage |
counter, or sampler on fallback | the NVIDIA device as NVML reports it | host CPU, host memory, anything off the card | Which of the two sources was used is reported, because the integral of sampled power is not the same measurement as a hardware energy counter. The counter is documented on Volta or newer. NVML states the figure is for "this GPU" without naming the internal rails, so the scope is reported as unstated rather than asserted to be whole board |
rpi_pmic |
vcgencmd pmic_read_adc |
sampler | the PMIC supply rails that expose both a current and a voltage channel | USB current and anything else drawing directly on the 5V supply, power supply conversion loss, voltage-only channels | Per rail read structure made explicit: complete sweeps and raw channel reads are two separate reported rates. Pi 5 only |
android_fuel_gauge |
/sys/class/power_supply/*/charge_counter and voltage_now; dumpsys batterystats read as context |
counter | the whole device at the battery terminals | nothing is excluded, which is the problem: the display, the modem and the radios are all included | Always emits BATTERY_SIDE_READING at warning severity. No option removes it. Refuses when the gauge took fewer than three distinct counter values, and when the counter rose, which means the device was charging |
| everything else | none | n/a | nothing | everything | Reports UNSUPPORTED. Does not estimate energy from CPU utilization and present it as a measurement |
model_estimate |
process CPU time times an assumed watts per busy core | sampler | nothing physical | everything | Off unless allow_estimate is passed. Sets measurement.kind to ESTIMATE in the record itself and can never reach PASS. The assumed watts figure has no hardware basis and the record says so |
On the RAPL permission restriction
energy_uj is not world readable on a current kernel. Commit
949dd0104c49, "powercap: restrict energy meter to root access" (Len Brown,
10 November 2020, present in v5.10-rc4 and backported to stable), changed the
attribute mode from S_IWUSR | S_IRUGO to S_IWUSR | S_IRUSR in response to
the PLATYPUS software power side channel, tracked as CVE-2020-8694 for the Intel
and kernel side, CVE-2020-8695 for the related Intel issue, and CVE-2020-12912
for the AMD counterpart. An unprivileged process therefore typically sees the
files and cannot read them, and powerstamp says exactly that and cites the
commit. The kernel powercap sysfs documentation still describes the attribute as
readable and has not been updated to match the code, so do not cite the
documentation for the permission.
The uncertainty budget
The budget follows the structure of JCGM 100:2008, the GUM: one Type A component
evaluated from observed scatter, and Type B components evaluated from bounds on
effects the samples cannot show. A rectangular Type B bound of half width a
contributes a / sqrt(3). The components combine in quadrature and are reported
at k = 1 and at k = 2.
Type A, sample scatter. The standard error of the mean power is s / sqrt(n),
and energy is mean power times duration, so the term on the energy is
duration * s / sqrt(n). With fewer than two samples there is no scatter to
evaluate and the term is zero, which is an absent uncertainty and not a small
one. The record says so in uncertainty.notes.
Type B, region boundary. The start and the end of the region each fall somewhere
inside one sampling interval, so each boundary carries a rectangular error of
half width P * dt / 2 and contributes P * dt / (2 * sqrt(3)). Two independent
boundaries combine to P * dt * sqrt(2) / (2 * sqrt(3)). This term is the direct
cost of a low sampling rate, and it is why the achieved rate belongs next to the
joule figure.
Type B, quantization. One read of an integer counter carries a rectangular error
of half width q / 2 and contributes q / sqrt(12); two reads give
q * sqrt(2) / sqrt(12). q is 1 uJ for powercap, 1 mJ for the NVML counter,
one observed charge step at a nominal 3.85 V for the Android fuel gauge, and
zero for the Pi PMIC because Raspberry Pi does not publish the ADC resolution and
the eight printed decimal places are print precision rather than resolution.
What the budget deliberately excludes, named in the record under
uncertainty.excludes because omitting it silently would be the same failure the
tool exists to prevent:
- the absolute accuracy of the underlying counter or ADC, which no vendor in the matrix publishes for its energy path;
- any error from the measured region not being the only load on the machine;
- for a battery side reading, the share of the energy belonging to the display, the modem or the radios.
So it is a repeatability and discretisation budget, not a calibration budget.
The refusal rule
A confident figure needs at least 30 complete samples. Below that the
verdict is INVALID and the reason is SAMPLE_COUNT_TOO_LOW. Between 30 and
100 the figure is reported with SAMPLE_COUNT_MARGINAL at warning severity.
How 30 was chosen, both parts checkable:
- The Type A term uses a sample standard deviation, and the relative standard
error of a standard deviation is approximately
1 / sqrt(2 * (n - 1)). That is about 13 percent atn = 30, about 7 percent atn = 100, and about 24 percent atn = 10. At 10 samples the error bar is less trustworthy than the number it is supposed to qualify. - It forces the duration floor into the open. The floor is
min_samples / achieved_hz, so at the 21.2 Hz measured on the Raspberry Pi 5 the shortest run that can earn a confident verdict is about 1.42 s. A tool that accepts a 50 ms run on that hardware is reporting one or two samples and an error bar it invented.
The thresholds are published in every record under measurement.refusal_rule,
including the duration floor they imply at the rate actually achieved, so a
reader can check the rule against the run rather than take it on trust.
A sample means one complete read of every channel the backend covers, never one
hardware read. On the Pi PMIC a single sweep is 26 channel reads, and both rates
are reported separately: rate.achieved_hz for sweeps and channel_read_hz for
raw reads. Each powered rail contributes two channel reads, a voltage and a
current, so counting per rail reads instead of sweeps reports twice the real
rate, which is the specific error that put 43 to 44 Hz in an earlier draft of the
Raspberry Pi 5 work where the effective rate was 21.2 Hz.
Related work
Each of the following was checked against its own source or documentation before being described here. The four properties compared are: A reports the rate actually achieved rather than the configured one, B reports explicit rail or domain coverage, C reports a quantified uncertainty on the energy number, D refuses to emit a confident number the measurement cannot support.
| Tool | A achieved rate | B rail coverage | C uncertainty | D refusal |
|---|---|---|---|---|
| pyRAPL | no | no | no | no |
| pyJoules | no | no | no | partial |
| CodeCarbon | no | partial | no | no |
| scaphandre | no | no | no | no |
| powerstat | no | yes | partial | yes |
| turbostat | no | partial | no | no |
perf stat -e power/energy-pkg/ |
not applicable | no | partial | no |
| Zeus | no | partial | no | no |
| nvidia-smi and NVML | not applicable | no | no | no |
Notes, tool by tool:
- pyRAPL selects devices but does not report which were readable, and discloses one qualitative caveat with no number.
- pyJoules is the only Python library with a partial D: it returns null
values when the measurement period is too short. It is incomplete, because on
counter overflow it adds
max_energy_range_ujto a negative delta without refusing, and its own documentation notes the result is wrong if a counter overflows twice in one measurement. It states that devices have a hardware-dependent measurement frequency without stating that frequency for any device. - CodeCarbon has the strongest B among the Python tools: it logs the chosen RAPL domain at INFO level. That is a log line, not a field in the emitted record. Its D is the opposite of a refusal: when RAPL is unavailable it falls back on constant power models for unsupported hardware and emits a confident-looking number.
- scaphandre reads package, per-socket and sometimes DRAM consumption without reporting which domains it managed to read, samples on demand with no fixed or reported rate, and derives per-process figures from CPU-time ratios.
- powerstat (Canonical) is the closest of the nine and the best prior art for B and D. It prints the RAPL domains it found, prints a per-domain column, states that the readings do not cover all the hardware in the device, and names its failure paths including the root-permission one. It refuses: it exits on "Number of readings too low", enforces a minimum count, marks an inaccurate summary value as not available and suffixes an inaccurate value, and reports skipped samples. Its C is partial: it gives the mean, standard deviation, minimum, maximum and geometric mean of the power samples, which is the dispersion of the samples rather than a propagated uncertainty on the energy total. It has no A at all: it prints the configured interval and divides by that nominal value, using elapsed wall time only for scheduling.
- turbostat names its domains per column, which is better than one opaque
number, and exposes optional
usecandTime_Of_Day_Secondscolumns from which a caller could derive an interval. It still never reports an achieved rate, and--forceruns on unsupported hardware. perf stat -e power/energy-pkg/reads a counter at the start and the end, so it has no sampling rate to achieve.--repeatgives a run-to-run standard deviation across repetitions, which is not an uncertainty on a single energy reading.- Zeus is the most sophisticated of the nine and the most instructive near
miss. Its
end_windowdetects exactly the condition in D, warning that the measurement window was shorter than the GPU's energy counter update period, and then substitutes an approximation whose own docstring concedes it is still an approximation. Its coverage reporting is device-level, signalled by nullable fields, rather than rail-level. Its polling loop records neither the achieved rate nor the sample count. - nvidia-smi and NVML expose a cumulative counter with no rail breakdown and
no published uncertainty on the energy call.
NVML_ERROR_NOT_SUPPORTEDis capability detection, not a reliability refusal. The only published error bound anywhere in this survey is NVML's plus or minus 5 percent fornvmlDeviceGetPowerUsageon Fermi and Kepler, which is a power API on two old architectures and does not transfer to the energy counter.
None of the nine does all four. powerstat does B and D and a partial C, and
is the closest; nothing in the set reports an achieved sampling rate, and nothing
reports a quantified uncertainty on the energy number. So what powerstamp adds
is the combination: a measured rather than assumed sampling rate, explicit rail
and domain coverage in the emitted record rather than in a log line, an
uncertainty budget with its components and its exclusions named, and a refusal
that produces no confident number instead of an approximation.
Where powerstamp is behind: powerstat has far more mature RAPL handling and
a histogram view; turbostat reports frequency, idle residency and far more
besides; CodeCarbon does carbon accounting, which this tool does not attempt;
Zeus integrates with training loops and supports AMD GPUs, which this tool does
not. None of those is a reason to use this tool instead of those; the reason to
use this one is the provenance of the number.
A note on history: a prior pull request by this author to Zeus was closed. Zeus is cited here as related work and nothing more.
Related tools
powerstamp is one of three tools that emit the same measurement-provenance/1
envelope, so a consumer can read the provenance fields of any of them without
knowing which produced the record. The other two are benchguard, which decides
whether a measurement was valid from cgroup throttling, thermal counters, the
governor, major faults and competing load, and membw, which measures achieved
memory bandwidth against theoretical peak per core cluster. benchguard owns the
verdict vocabulary for the shared envelope. Neither is published, and this
section makes no claim that either ever will be.
Limitations
Stated plainly, because a tool about measurement honesty cannot be coy about its own gaps.
- No calibration. The uncertainty budget is a repeatability and discretisation budget. It does not include the absolute accuracy of any counter or ADC, because no vendor in the matrix publishes one for its energy path. A systematic error in the hardware passes straight through.
- No attribution. The energy of the measured region is the energy the machine drew during the region. Nothing isolates the caller's work from anything else running. The baseline subtraction helps with steady idle draw and nothing else, and whether it was applied is recorded.
- Counter update periods are mostly unpublished. Neither NVIDIA for the NVML energy counter nor Raspberry Pi for the PMIC ADC publishes an update period, so the region boundary term is bounded by the sampling interval rather than by the hardware's own refresh. Where the hardware is slower than the sampler, the real boundary uncertainty is larger than reported.
- The Pi PMIC quantization term is missing, not zero. It is reported as zero with a note saying why, rather than inferred from the printed decimal places.
- The Android path is battery side and that cannot be fixed by software here. It includes the display, the modem and the radios. Control for them or do not use the number.
- The 30 sample threshold is a judgement. The rationale is in this README and
in
powerstamp/uncertainty.py, the thresholds are published in every record, and a different field may want a different number. It is defended, not derived from first principles. - No RAPL MSR path and no
perfPMU path. Only powercap sysfs, so a system where powercap is absent but the MSR is readable reportsUNSUPPORTED. - AMD GPUs are not supported. There is no
amd-smior ROCm backend. - Apple Silicon is not supported. There is no
powermetricsor IOReport backend. - The sampler is a Python thread. At high requested rates the achieved rate
will fall short, which is reported as
RATE_SHORTFALLrather than hidden. - The test suite runs against synthetic fixtures. The RAPL, NVML, PMIC and
Android paths are exercised through injected readers, a fake NVML module and a
recorded
vcgencmdcapture. The logic is tested. Agreement with real hardware on a real machine is not yet established, and no claim is made that it is.
Testing
pip install -e ".[test]"
python -m pytest -v
135 tests, all passing on Windows with Python 3.14. They cover counter rollover
including a rollover mid measurement and a rollover that cannot be
disambiguated, partial domain availability with the unreadable domains reported
as absent rather than zero, the root-only energy_uj path, runs too short for
the achieved rate, the UNSUPPORTED path, the opt-in ESTIMATE path and its
inability to reach PASS, the separation of sweep rate from channel read rate,
every arm of the uncertainty budget against its documented formula, baseline
subtraction including a baseline larger than the measurement, the envelope shape,
and the CLI.
License
MIT. Copyright (c) 2026 Manu Nicholas Jacob.
Citation
See CITATION.cff.
Author
Manu Nicholas Jacob, Independent Researcher. ORCID 0009-0007-6589-6572. GitHub @manunicholasjacob.
Metadata
Release files for powerstamp 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| powerstamp-0.1.0.tar.gz | 72.9 kB | With provenance | Details for powerstamp-0.1.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| powerstamp-0.1.0-py3-none-any.whl | Python 3 | none | any | Details for powerstamp-0.1.0-py3-none-any.whl |
Total release size: 127.0 kB
Release files / powerstamp-0.1.0.tar.gz
| Download URL | powerstamp-0.1.0.tar.gz |
|---|---|
| Size | 72.9 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
f61da357a89a2aea8f0c9ec4333e07e099f4f6ff91bb241edd4ae51cb08a3a88
|
|
BLAKE2b-256 checksum How to use checksums |
a3a3b72dc2ec16683ed8e1e880cbac2faee2d39279d08bbe92c9e5de266efc32
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Nov 1, 2026.
Transparency logRelease files / powerstamp-0.1.0-py3-none-any.whl
| Download URL | powerstamp-0.1.0-py3-none-any.whl |
|---|---|
| Size | 54.1 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
28534fa86cd57a3a5752ed9590ca16fee9b28476e6d1be622f21665ce7fff4c0
|
|
BLAKE2b-256 checksum How to use checksums |
208433f2ed68b399c553f9e1a8d525faeece5e067687114385054f89a37df757
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Nov 1, 2026.
Transparency logSecurity for powerstamp 0.1.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for powerstamp-0.1.0.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Nov 1, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.