tf_tree
A transform tree engine: store time-stamped rigid-body transforms between named coordinate frames and answer "where was frame A relative to frame B at time t?" — fast enough to sit inside a control loop, with diagnostics good enough to debug at 3 a.m.
Jump to: Install · Start with a bag you already
have · First five
minutes · Where it
fits · Status · Is this a
tf2 replacement?
What you get
- A compiled query.
plan()resolves the topology and folds static edges once.at()then does d binary searches, d interpolations and d−1 compositions, and nothing else — no allocation, no hashing, no lock. Frames are interned to integer ids at plan time, so no lookup hashes a string. - One tree, not one copy per process. The arena holds no pointers — every
internal reference is an offset — so the same bytes map into every reader:
threads in one process, cooperating processes on a host, or sixteen dataloader
workers on a frozen
.tftfile. Consumers attach read-only by default, and the MMU is what enforces it, not convention. - Failures you can act on. Every error is a
Copyidentifier that names the offending edge as data rather than as a formatted string, andtf_tree doctorruns theTFT001–TFT019catalogue against a live arena, a frozen index, or an MCAP recording you already have. Nineteen ids are reported; seventeen can detect today, and the two that cannot say so rather than reporting a pass.
Linux-first. The single-process engine is portable Rust and much of it
compiles elsewhere; everything that maps memory — attaching to a live arena, the
frozen .tft backend, tf_tree freeze — is Linux-only and behind a
default-off shm feature. That sentence is here rather than in
SUPPORT.md alone because nobody should meet it as a build
error.
Install
| What you want | How to get it | Where it comes from |
|---|---|---|
| The Rust engine | cargo add tf_tree |
crates.io |
| The Python bindings | pip install transform_tree, then import tf_tree |
PyPI |
The tf_tree CLI |
a prebuilt Linux binary from the latest release, or cargo install --path crates/tf_tree_cli --features shm from a clone |
GitHub Releases (x86_64/aarch64, gnu and static musl); the CLI is publish = false, so not crates.io |
| C ABI, C++ header, ROS 2 bridge | just c-abi-check, just cpp-check, just ros-build |
source |
Three notes on that table, each of which surprises somebody:
- The distribution name is not the import name. PyPI refuses
tf_treeas too close to the existingtftree, so the wheel istransform_treeand the module staystf_tree(0008records the measurement). cargo add tf_treegives you the portable engine. Shared memory and the frozen.tftreader need--features shm, on Linux.cargo install tf_treeinstalls no command, and does not fail either: it exits 0 with a warning naming--features shm. Adding that flag does installtf_tree_rendezvous_child, which is a test helper and not a tool — the crate's own page has the whole story. The CLI is a separate, unpublished crate: take the prebuilt binary from the releases page, or build it from a checkout.publish = falseis about the crates.io index, not about whether it is shipped — three of its dependencies are path-only, so it has no version to publish against.
0.0.x promises nothing between releases. Cargo treats every 0.0.x as
incompatible with every other, so pin exactly and expect a later release to
break. That is the whole promise; see CHANGELOG.md.
Start where you change nothing
Point it at a recording you already have. No node joins anyone's launch file, no
robot is redeployed, and doctor --from-bag needs no features at all:
# No clone and no Rust toolchain. The musl build is static: it needs no system
# library, so it runs on Ubuntu 20.04 and in a distroless container alike.
TAG=$(curl -fsSL https://api.github.com/repos/NoeFontana/tf_tree/releases/latest \
| grep -m1 '"tag_name"' | cut -d'"' -f4)
curl -fsSL "https://github.com/NoeFontana/tf_tree/releases/download/${TAG}/tf_tree-${TAG}-x86_64-unknown-linux-musl.tar.gz" | tar xz
tft=./tf_tree-${TAG}-x86_64-unknown-linux-musl/tf_tree # stay where your recording is
$tft doctor --from-bag drive.mcap # what is wrong with this /tf traffic
$tft freeze --from-bag drive.mcap -o drive.tft # keep the answer
Both released builds carry --features shm, so the same binary also attaches to
a robot that is already running (--attach, tf_tree top, tf_tree participants). The -gnu archive is the dynamically-linked build and needs
glibc 2.34 or newer (Ubuntu 22.04 / ROS 2 Humble and later); take it if you
would rather a glibc security fix reach this binary through your distribution
than wait for a new release. Otherwise take musl. From a clone the equivalent is
cargo install --path crates/tf_tree_cli --features shm.
The same two steps run from Python, with no CLI and no clone — which is what
tf_tree_ingest was made a library crate for
(0046):
import tf_tree
tree = tf_tree.ingest_bag("drive.mcap") # returns the ordinary Tree
tree.freeze("drive.tft") # records drive.mcap's BLAKE3 digest
print(tree.source["transforms"], tree.source["digest"][:16])
tree.source is how a .tft stays traceable to the recording it came from, and
freeze writes that digest for you — so there is no second freeze_bag call to
remember. Taking a publisher on the tree drops it, because from that point the
tree may hold samples the recording does not.
drive.tft is a frozen transform index, and it is the arena itself written
to disk. There are no pointers anywhere in the arena — every internal reference
is an offset — so opening one is an mmap, with no parsing, no deserialization
and no fixups (PHASE5 §2.1). Sixteen dataloader workers map
the same file, the kernel charges the shared clean pages exactly once and the
untouched ones not at all (measured basis: PHASE2 §3.8),
and each worker queries in its own address space: no IPC, and no ROS node inside
the training loop.
import numpy as np, tf_tree
# Open per worker, after the fork/spawn — docs/PHASE5.md §4.3 says why.
tree = tf_tree.open_file("drive.tft")
plan = tree.plan("base_link", "lidar_top") # compile the route once
stamps = np.asarray(batch_stamps_ns, dtype=np.int64) # integer nanoseconds
poses = plan.at(stamps, layout="quat_twist") # (N, 13) float64
The whole batch is one call into Rust. layout="quat_twist" appends the body
twist to each pose, in the plan's source frame: it is the analytic derivative of
the same interpolation the pose came from, not a finite difference between two
lookups.
docs/PHASE5.md §2.2 is where that argument is made in the
project's own words. A perception dataloader today does one of three bad things
— re-parses the bag in every worker, precomputes poses into a pickle and loses
the ability to query at arbitrary times, or runs a ROS node to serve transforms
during training. This replaces all three and asks nobody to migrate anything,
which is also why it is the part that shipped first.
Running it inside a loop instead? just control-loop is the other half of
this page — cargo run --release -p tf_tree --features shm --example control_loop.
It is a 1 kHz controller against a 200 Hz estimate, showing the four things a
runtime consumer has to get right (compile the plan once, hoist the guard,
extrapolate on purpose and read how far it reached, treat a contended slot as
data) and printing the tail a deadline is set against. The offline path above
asks nobody to change their robot; this one is what happens when they do.
Two processes, not two threads? just two-processes — the capability this
page leads with, as something you can run. It spawns a publisher and a consumer
as separate processes and prints what each saw: the publisher declares the
topology (including a static sensor mount, which folds to one multiply and
needs no publisher ever), the consumer waits for it with Open::await_open
rather than depending on launch order, and then reads a transform and how far
past the newest sample it had to reach.
Numbers belong where they can be reproduced, not in this section.
just bench-report measures your host and writes
report/{results.json,index.html}; the standing figures and their caveats are in
docs/benchmarks/. Benchmarks, and what they are worth
below explains why a row there may legitimately read UNAVAILABLE.
First five minutes, with no data at all
The block above assumes you have a recording. This one assumes only the clone:
just quickstart # uv-managed interpreter + venv, with the extension installed into it
.venv/bin/python
import tf_tree
tree = tf_tree.build([("map", "base"), ("base", "cam")])
# stamp in integer nanoseconds; pose is [qw, qx, qy, qz, x, y, z]
tf_tree.push(tree, "base", "map", 1_000, [1.0, 0.0, 0.0, 0.0, 1.0, 2.0, 3.0])
tf_tree.push(tree, "base", "map", 2_000, [1.0, 0.0, 0.0, 0.0, 3.0, 4.0, 5.0])
print(tree.plan("map", "base").at(1_500)[:3, 3]) # -> [2. 3. 4.]
That is a real result and not a toy: the query lands halfway between two samples,
so the printed translation is their interpolated midpoint, and plan() is the
object you keep — compiling the route once and evaluating it many times is the
whole shape of the fast path.
The same shape in Rust, which is where the engine actually lives. This block is
compiled by cargo test --doc, so it cannot drift away from the API:
use tf_tree::{Capacity, EdgeCfg, Iso3, Quat, Stamp, TreeBuilder, Vec3};
// Topology is declared up front: `build()` sizes one flat arena from exactly
// these edges, and nothing allocates after it returns.
let tree = TreeBuilder::new()
.static_edge("base_link", "lidar_top", &Iso3::IDENTITY) // (parent, child)
.dynamic_edge("odom", "base_link", EdgeCfg::new(Capacity::history(100.0, 10.0)))
.build()
.expect("layout");
let odom = tree.frame("odom").expect("declared");
let base_link = tree.frame("base_link").expect("declared");
let lidar_top = tree.frame("lidar_top").expect("declared");
// One writer per edge, enforced by the claim table rather than by convention.
// Note the order flips: the builder takes (parent, child), `claim` takes
// (child, parent). Both are annotated here because getting it wrong builds a
// silently inverted tree rather than failing.
let w = tree.claim(base_link, odom).expect("unclaimed"); // (child, parent)
let at_x = |x| Iso3::new(Quat::IDENTITY, Vec3::new(x, 0.0, 0.0));
w.push(1_000_000_000, &at_x(0.0)).expect("monotonic"); // integer nanoseconds
w.push(1_010_000_000, &at_x(1.0)).expect("monotonic");
// Compile the route once, evaluate it many times.
let plan = tree.plan(odom, lidar_top).expect("connected");
let g = tree.guard();
let t: Stamp = Stamp::from_nanos(1_005_000_000);
let pose = plan.at(&g, t).expect("in range");
assert!((pose.t.x - 0.5).abs() < 1e-12);
crates/tf_tree/README.md carries the annotated
version, including what a failed lookup prints. Two things surprise people about
both languages, and both are deliberate:
- Stamps are integer nanoseconds. There is no float-seconds overload. At a
2026 epoch the ULP of
float64seconds is 238 ns, so every interval in a 1 kHz stream is wrong after a round trip. - Nothing returns a view into shared memory. An edge's samples are a ring
another process is overwriting, and correct reads go through a seqlock. "Zero
copy" here means no intermediate allocation — use
Plan.at_intoto supply the destination.
just alone lists everything the repository can do.
Where it fits, and where it does not
The fastest way to evaluate this is to find yourself on one of these two lists. The second one is not a roadmap: most of its rows are decisions, recorded, that will not be reversed.
Reach for tf_tree when:
- You look up transforms inside a loop that has a deadline — a controller, a perception front end — and the per-lookup cost is a thing you have measured.
- Many readers share one host: threads in a process, or several processes. One arena serves all of them, read-only by default, with no middleware between the reader and the bytes.
- You train on recorded data and want transforms in the dataloader without a ROS node in the training loop, or a pickle of precomputed poses that can no longer answer at an arbitrary time.
- Your edges are fast, kilohertz-class, and float-seconds stamps have already cost you resolution.
- You need to debug a transform tree — typed errors that name the edge, the
TFT001–TFT019catalogue,tf_tree top, anddoctoragainst a bag with nothing deployed.
Look elsewhere when:
| You need | Why not this | Where it is written down |
|---|---|---|
A drop-in tf2_ros::Buffer |
Phase 7, gated on operating evidence, not scheduled. What exists is a one-way ingest bridge | PHASE7.md §0.0 |
| Covariance or joint uncertainty | A tree cannot compose a correct one; composing marginals as independent is wrong in the optimistic direction. You need a factor graph | PROJECT.md §1, 0009 |
| Multi-parent frames, loop closure, copy-on-write branches | Multi-parent is the row above. Copy-on-write was cut for reasons of its own: it serves the use case D2 rejects and contradicts fixed capacity, one-writer-per-edge and append-only ids at once | 0009, PROJECT.md §5 D2 |
| Transforms across hosts | Phase 8. Not started | PROJECT.md §4 |
Shared memory or .tft off Linux |
The engine compiles; the mapping code does not exist elsewhere | SUPPORT.md |
| A viewer, or point-cloud deskewing | Deliberately absent, argument recorded. at_adaptive emits knots; the consumer transforms points where they already live |
PHASE5.md §8, PROJECT.md §5 D8 |
| An API that will not move under you | 0.0.x: every release may break every other |
CHANGELOG.md |
Status
On crates.io from 0.0.1; on PyPI from 0.0.2. The five engine crates are
published — cargo add tf_tree. The Python wheel starts at 0.0.2 because the
0.0.1 commit did not compile off Linux, so no wheel for it exists or can; see
CHANGELOG.md.
| Phase | What it is | Status |
|---|---|---|
| 1 | Single-process engine: arena, seqlock buffers, plans, SE(3) math | Implemented |
| 2 | Shared memory: rendezvous, fd passing, claims as leases, reaping | Implemented, with gaps |
| 3 | Python bindings (PyO3, zero intermediate allocation) | Implemented |
| 4 | C ABI, C++ wrapper, ROS 2 ingest bridge, derivatives | Implemented, with gaps |
| 5 | Frozen .tft arena, bag ingestion, diagnostics, tf_tree top |
Mostly implemented |
| 6–8 | Continuous-time interpolation, tf2 shim, multi-host replication |
Not started |
The gaps, named — because "with gaps" on its own is not a status:
- Phase 2 — the daemon and recorder surface (§9–§10) are absent, and
§11.3's fault injection is a separate gap still being worked. §3.5's ownership
migration landed on 2026-08-28: kill the arena's owner and a surviving
read-write participant inherits the role, so new processes can join again. Its
trigger is caller-driven —
Tree::owner_lost()is a non-blocking check a survivor makes in its own loop, and nothing makes it for you, because there is no daemon. - Phase 4 — everything except §5.9's affinity knobs and §6.3's replay rows.
at_with_derivatives, both headers, the header-only C++ wrapper with its CMake package, and both halves of the ingest bridge — therclcpppackage inros/tf_tree_rosincluded. §7's benchmark gate is partial, and §1's exit criterion is operational, not a feature list: it is open, and no amount of code closes it. - Phase 5 —
FORMAT_VERSION = 3, the frozen arena (§2), the offline Python API (§4), the §5 counters andtf_tree top(terminal and--web, §7) have all landed. Ingestion is MCAP only (§3). TheTFT001–TFT019catalogue reports all nineteen ids, of which seventeen can detect (§6). §8 is deliberately not built. §9's benchmark artifact and §10's release readiness are partial. - Phase 7 is gated by D21 and none of its four gates is met.
The per-phase §0.0 tables in docs/ are the source of truth, not
this one — PHASE2.md,
PHASE4.md,
PHASE5.md. If this table and one
of those disagree, the phase document is right and this is stale.
CI runs again as of 2026-08-16, after a gap since 2026-07-23 that ended
when this repository was made public. A green check is evidence once more — of
what the jobs cover. Gate locally with just first; CI is the second opinion.
Is this a tf2 replacement?
It is not tf2, not a fork of it, and not affiliated with ROS. It is an
independent engine that solves the same problem with a different data structure,
and it is deliberately named so that people looking for a tf2 alternative can
find it (0008 records that
decision).
There is no drop-in tf2_ros::Buffer shim, and building one is not scheduled.
That is Phase 7, gated by D21 on operating evidence this project has not yet
produced; docs/PHASE7.md is what such a shim would have to
be — including the places it would deliberately refuse to reproduce tf2's
behaviour — and its §0.0 lists four gates, none of them met. What exists today
instead is the ingest bridge (docs/PHASE4.md §5): a node that subscribes to
/tf and fills an arena, which is a one-way seam and not a compatibility layer.
Two things worth stating precisely, because the loose versions of both are wrong:
- The documented
tf2cost is listener and buffer CPU, per node — not/tfbandwidth. Autoware'sManagedTransformBuffer(inautoware_universe) reports taking a LiDAR sensing pipeline from 13 TF listener nodes to 0 — four per-sensor legs at 3 each plus the concatenation node (autowarefoundation/autoware#5385; the upstream discussion about moving it intotf2isros2/geometry2#758). That is a third-party report about their own stack, not atf_treemeasurement, and it is cited for the shape of the problem this engine's process model addresses. No claim is made about/tfbandwidth: no quantified public source for one exists. - Errors are
Copyidentifiers a program can branch on, with the prose in a separate layer (docs/API.mdR5) — a lookup failure names the offending edge as data, not as a formatted string. That is a durable API-shape difference and deliberately not a claim about any particulartf2defect; the misattributed-extrapolation one people cite (ros2/geometry2#832) was fixed by PR #896, merged 2026-03-18 and backported to Kilted, Jazzy and Humble (#897–#899). Marketing against a bug somebody already fixed is how a README goes stale in public.
Shared memory IPC is not a sandbox
Processes sharing a tf_tree arena are mutually trusting, same-user,
cooperating processes. A read-write participant can corrupt any part of the
arena, and no checksum would change that — it holds a writable mapping of the
same pages (PHASE2.md §3.10). Do not attach a process you
would not run as yourself. SECURITY.md draws the line between
this and an actual vulnerability.
Three things the design does guarantee, and they are the ones that matter on a robot:
- A read-only participant cannot corrupt anything, enforced by the MMU, not by convention. It is the default for consumers (D18), and it converts a class of whole-system failures into a single-process fault.
- A participant that crashes, at any instruction, cannot corrupt the arena or wedge anyone else. A killed writer's edge is reclaimed; a killed interner's entry is recoverable; a killed mutator does not leave a permanently locked topology.
- A participant that hangs cannot be mistaken for a crashed one. Liveness is
the kernel's answer about a file lock, not a heartbeat timeout, so a
SIGSTOPped publisher keeps its claims and a stalled one is never reaped out from under itself.
fork() is the sharp edge worth knowing about up front: the arena is mapped
MADV_DONTFORK, so a child has no mapping and every inherited handle reports
ChildDetached. Python's multiprocessing defaults to fork on Linux — open
inside the worker, or use the spawn start method. A frozen .tft is the
deliberate exception: it is a private read-only mapping, a child inherits it
intact, and poisoning it would break multiprocessing for offline users to
defend against a hazard they do not have (docs/PHASE5.md §4.3).
Workspace
crates/
├── tf_tree_math/ no_std SE(3)/SO(3) + dual quaternions; #![forbid(unsafe_code)]
├── tf_tree_arena/ no_std+alloc pointer-free arena + layout math
├── tf_tree_core/ no_std+alloc engine: interning, topology, seqlock buffers, plans
├── tf_tree/ std facade: builder, plan-cached lookup, Display errors
├── tf_tree_ipc/ zero-config rendezvous: runtime dir, OFD lock file, attach protocol
├── tf_tree_c/ C ABI + header-only C++ wrapper
├── tf_tree_bridge/ the ROS-independent half of the /tf ingest bridge
├── tf_tree_ingest/ MCAP -> arena: the two passes behind `ingest` and `freeze --from-bag`
├── tf_tree_py/ PyO3 bindings — binds the Rust core directly, not the C ABI
├── tf_tree_bench/ criterion benches, tf2 differential harness, the §9 report
├── tf_tree_tf2_sys/ the tf2 side of that differential harness — needs a ROS 2 install
└── tf_tree_cli/ binary `tf_tree` (alias `tft`): tree / echo / doctor / top /
ingest / freeze / topology / participants / bench
ros/ ament_cmake packages: the §5 rclcpp bridge, and the DDS comparison
xtask/ loom, miri, and bench-gate runners
docs/ PROJECT.md, API.md, PHASE1–5.md, RUNBOOK.md, benchmarks/, decisions/
tf_tree_py and tf_tree_tf2_sys are outside the cargo --workspace build on
purpose — they link libpython and a ROS 2 install respectively, neither of which
a clean checkout can assume. ros/ is outside it for the same reason and is not
cargo at all. Each has just recipes of its own (just py-*, just ros-build /
just ros-test, just tf2-check), which is what "gate locally" means for them.
Five crates are intended for crates.io: tf_tree, tf_tree_core,
tf_tree_math, tf_tree_arena, tf_tree_ipc. The rest carry publish = false
with the reason in their manifest.
Commands
just quickstart # clean clone -> a Python REPL with the extension installed
just build # cargo build --workspace --all-targets
just test # nextest + doctests
just lint # fmt --check + clippy -D warnings
just loom # concurrency model checking
just miri # UB checking (arena + core + the facade's one unsafe)
just bench # benchmark suite + go/no-go gate
just bench-report # the PHASE5 §9 artifact -> report/{results.json,index.html}
just bench-check # the same artifact against the committed baseline
just shm-torture # PHASE2 §11.4's multi-process soak (30 min; nightly)
just --list for everything.
Benchmarks, and what they are worth
just bench-report emits report/results.json and report/index.html with a
full provenance header. It is built so it cannot print a number it has no
right to: it probes the host, and a row it cannot measure fairly comes out
UNAVAILABLE with the reason and the command that produces it on a host that
can. On a 4-core development machine that means most rows are gaps — which is
the correct output, not a broken tool. docs/PHASE5.md §9.3 is the rule it
enforces, and it includes a "where tf_tree is worse" section, in the same
table.
just bench-check re-runs it and compares against the committed baseline in
crates/tf_tree_bench/baseline/results.json, failing if a claim was withdrawn,
a row was dropped, the arena layout changed, or a directional number moved past
the slack the baseline itself records. It compares claims, not hosts: CPU
model, core count, kernel, governor, load and every reason string are ignored,
so the gate means the same thing on any machine. just bench-baseline-update
regenerates the baseline; that diff belongs in the commit that causes it.
Standing numbers and their caveats live in
docs/benchmarks/.
Where to go next
Using it.
docs/API.md— the cross-cutting contract: six rules (§1) every binding obeys, and the normative Rust, Python, C and C++ surfaces (§2–§5). This is the reference.docs/RUNBOOK.md— organised by symptom, because that is what you have when a robot's transform tree misbehaves. Every row names an error type and, where one exists, thedoctorcheck that finds it.docs/benchmarks/— the standing measurements, each row naming the command that produced it.
Changing it.
docs/PROJECT.md— overview, architecture, roadmap, and the decision log D1–D22 in §5. Read it before proposing anything: several obvious-looking simplifications are excluded on purpose, and the reasons are there.- The phase spec you are touching:
PHASE1…PHASE5.PHASE2,PHASE4,PHASE5andPHASE7open with a§0.0status table, and it outranks every other document including this one.PHASE1has none because Phase 1 is implemented whole, andPHASE3has none because it records deviations inline, in the section each belongs to. docs/API.mdagain, and before writing any public surface — §1's six rules generate every binding, and §7 is the checklist a new surface passes. It authorizes nothing on its own: its §6 delta table names the phase or decision record each row lands in.docs/decisions/— the records for what the phase specs do not cover, and where a change of that kind starts.CONTRIBUTING.md— the local gates, and the order to run them in.
Contributing and support
CONTRIBUTING.md · SUPPORT.md (response
expectations, platform support, MSRV policy) ·
SECURITY.md · CODE_OF_CONDUCT.md
MSRV is 1.87. just msrv reads the number out of [workspace.package] rust-version, builds --locked on exactly that toolchain, and checks that
every hand-written rust-version — and this line — still agrees with it.
Licence
Dual MIT / Apache-2.0, at your option. See
NOTICE.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distributions
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 transform_tree-0.0.5.tar.gz.
File metadata
- Download URL: transform_tree-0.0.5.tar.gz
- Upload date:
- Size: 1.2 MB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
313078ddd45c804793ab73626089e6e2a9910cde8370c343db7478fb494d2857
|
|
| MD5 |
0129709a46537df280a36955a4c4704d
|
|
| BLAKE2b-256 |
f6a2ef694fa304483bc7c38c5e80a03c71c33a77bbf194bf96195d1e8b33648c
|
Provenance
The following attestation bundles were made for transform_tree-0.0.5.tar.gz:
Publisher:
wheels.yml on NoeFontana/tf_tree
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
transform_tree-0.0.5.tar.gz -
Subject digest:
313078ddd45c804793ab73626089e6e2a9910cde8370c343db7478fb494d2857 - Sigstore transparency entry: 2645160041
- Sigstore integration time:
-
Permalink:
NoeFontana/tf_tree@01a8ba7d754306a7e93d23fa63f38fb33d832db8 -
Branch / Tag:
refs/tags/v0.0.5 - Owner: https://github.com/NoeFontana
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
wheels.yml@01a8ba7d754306a7e93d23fa63f38fb33d832db8 -
Trigger Event:
push
-
Statement type:
File details
Details for the file transform_tree-0.0.5-cp314-cp314t-win_amd64.whl.
File metadata
- Download URL: transform_tree-0.0.5-cp314-cp314t-win_amd64.whl
- Upload date:
- Size: 572.4 kB
- Tags: CPython 3.14t, Windows x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1478b6256be3ae1cf8d6872cab773c39f1fd015db265380dd8980209cc6b59ef
|
|
| MD5 |
59f31f4ba3d54ca7976c431e7d3911d9
|
|
| BLAKE2b-256 |
fb0387e5a7841dec9bc7048ec7230f0da1165f27d818038810b9a1b5a64ab4d5
|
Provenance
The following attestation bundles were made for transform_tree-0.0.5-cp314-cp314t-win_amd64.whl:
Publisher:
wheels.yml on NoeFontana/tf_tree
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
transform_tree-0.0.5-cp314-cp314t-win_amd64.whl -
Subject digest:
1478b6256be3ae1cf8d6872cab773c39f1fd015db265380dd8980209cc6b59ef - Sigstore transparency entry: 2645173842
- Sigstore integration time:
-
Permalink:
NoeFontana/tf_tree@01a8ba7d754306a7e93d23fa63f38fb33d832db8 -
Branch / Tag:
refs/tags/v0.0.5 - Owner: https://github.com/NoeFontana
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
wheels.yml@01a8ba7d754306a7e93d23fa63f38fb33d832db8 -
Trigger Event:
push
-
Statement type:
File details
Details for the file transform_tree-0.0.5-cp314-cp314t-manylinux_2_28_x86_64.whl.
File metadata
- Download URL: transform_tree-0.0.5-cp314-cp314t-manylinux_2_28_x86_64.whl
- Upload date:
- Size: 723.8 kB
- Tags: CPython 3.14t, manylinux: glibc 2.28+ x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
49d2df9337251771d514a7c9247d35ff26af9df68a43b90da7b483748295a05f
|
|
| MD5 |
c84fc6cf0aaaea0bf9506fa80cee2a44
|
|
| BLAKE2b-256 |
b7b29f36ae5199199855d4cbc7b9ad7eb4acb07a20ad1ce8c0d1b5bd1765126f
|
Provenance
The following attestation bundles were made for transform_tree-0.0.5-cp314-cp314t-manylinux_2_28_x86_64.whl:
Publisher:
wheels.yml on NoeFontana/tf_tree
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
transform_tree-0.0.5-cp314-cp314t-manylinux_2_28_x86_64.whl -
Subject digest:
49d2df9337251771d514a7c9247d35ff26af9df68a43b90da7b483748295a05f - Sigstore transparency entry: 2645177429
- Sigstore integration time:
-
Permalink:
NoeFontana/tf_tree@01a8ba7d754306a7e93d23fa63f38fb33d832db8 -
Branch / Tag:
refs/tags/v0.0.5 - Owner: https://github.com/NoeFontana
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
wheels.yml@01a8ba7d754306a7e93d23fa63f38fb33d832db8 -
Trigger Event:
push
-
Statement type:
File details
Details for the file transform_tree-0.0.5-cp314-cp314t-manylinux_2_28_aarch64.whl.
File metadata
- Download URL: transform_tree-0.0.5-cp314-cp314t-manylinux_2_28_aarch64.whl
- Upload date:
- Size: 676.1 kB
- Tags: CPython 3.14t, manylinux: glibc 2.28+ ARM64
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
de048e4c36b325c64233cb32a129e90ab42c1726b19b9266cff8320c23fed161
|
|
| MD5 |
b5f73c8793cf7553e0993ad8aa577e4a
|
|
| BLAKE2b-256 |
8331d78348730a936425a3e0195308c24339556f1b33c1d3bd5d11e51b69f814
|
Provenance
The following attestation bundles were made for transform_tree-0.0.5-cp314-cp314t-manylinux_2_28_aarch64.whl:
Publisher:
wheels.yml on NoeFontana/tf_tree
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
transform_tree-0.0.5-cp314-cp314t-manylinux_2_28_aarch64.whl -
Subject digest:
de048e4c36b325c64233cb32a129e90ab42c1726b19b9266cff8320c23fed161 - Sigstore transparency entry: 2645164607
- Sigstore integration time:
-
Permalink:
NoeFontana/tf_tree@01a8ba7d754306a7e93d23fa63f38fb33d832db8 -
Branch / Tag:
refs/tags/v0.0.5 - Owner: https://github.com/NoeFontana
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
wheels.yml@01a8ba7d754306a7e93d23fa63f38fb33d832db8 -
Trigger Event:
push
-
Statement type:
File details
Details for the file transform_tree-0.0.5-cp314-cp314t-macosx_11_0_arm64.whl.
File metadata
- Download URL: transform_tree-0.0.5-cp314-cp314t-macosx_11_0_arm64.whl
- Upload date:
- Size: 583.3 kB
- Tags: CPython 3.14t, macOS 11.0+ ARM64
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
58112df25393cb084147f30cdfb187063688408c9afe596aa67a7eb767dc703f
|
|
| MD5 |
92108ab49ec7a74556a0ae1a74b4a998
|
|
| BLAKE2b-256 |
2920cc3b1f6a73e2e90ed8545b77c9dcd9111bd109c4c6aae7bc935f3732b5c2
|
Provenance
The following attestation bundles were made for transform_tree-0.0.5-cp314-cp314t-macosx_11_0_arm64.whl:
Publisher:
wheels.yml on NoeFontana/tf_tree
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
transform_tree-0.0.5-cp314-cp314t-macosx_11_0_arm64.whl -
Subject digest:
58112df25393cb084147f30cdfb187063688408c9afe596aa67a7eb767dc703f - Sigstore transparency entry: 2645175066
- Sigstore integration time:
-
Permalink:
NoeFontana/tf_tree@01a8ba7d754306a7e93d23fa63f38fb33d832db8 -
Branch / Tag:
refs/tags/v0.0.5 - Owner: https://github.com/NoeFontana
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
wheels.yml@01a8ba7d754306a7e93d23fa63f38fb33d832db8 -
Trigger Event:
push
-
Statement type:
File details
Details for the file transform_tree-0.0.5-cp39-abi3-win_amd64.whl.
File metadata
- Download URL: transform_tree-0.0.5-cp39-abi3-win_amd64.whl
- Upload date:
- Size: 585.8 kB
- Tags: CPython 3.9+, Windows x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d6b5b30f4d0f51ec6c32867e1f0e4c0db39c823217c73a572c953eebe8958e87
|
|
| MD5 |
eb4f728e00e4c51fcd8d0de42552e068
|
|
| BLAKE2b-256 |
4ba97fec5b3f63b50bd280c503f887721fd47927245b9acc8151139d5d0e6ad1
|
Provenance
The following attestation bundles were made for transform_tree-0.0.5-cp39-abi3-win_amd64.whl:
Publisher:
wheels.yml on NoeFontana/tf_tree
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
transform_tree-0.0.5-cp39-abi3-win_amd64.whl -
Subject digest:
d6b5b30f4d0f51ec6c32867e1f0e4c0db39c823217c73a572c953eebe8958e87 - Sigstore transparency entry: 2645171482
- Sigstore integration time:
-
Permalink:
NoeFontana/tf_tree@01a8ba7d754306a7e93d23fa63f38fb33d832db8 -
Branch / Tag:
refs/tags/v0.0.5 - Owner: https://github.com/NoeFontana
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
wheels.yml@01a8ba7d754306a7e93d23fa63f38fb33d832db8 -
Trigger Event:
push
-
Statement type:
File details
Details for the file transform_tree-0.0.5-cp39-abi3-musllinux_1_2_x86_64.whl.
File metadata
- Download URL: transform_tree-0.0.5-cp39-abi3-musllinux_1_2_x86_64.whl
- Upload date:
- Size: 947.5 kB
- Tags: CPython 3.9+, musllinux: musl 1.2+ x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5ba931b189263df9108a5227be33263eadf6f5415938aaeb220759b56019aae8
|
|
| MD5 |
f0fbe4d9a8f2a91f4102e37009d15827
|
|
| BLAKE2b-256 |
d418b9194e5a76a086b1b5669fd322ef31cd2095c6e96f4c271e4a50a535d4f0
|
Provenance
The following attestation bundles were made for transform_tree-0.0.5-cp39-abi3-musllinux_1_2_x86_64.whl:
Publisher:
wheels.yml on NoeFontana/tf_tree
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
transform_tree-0.0.5-cp39-abi3-musllinux_1_2_x86_64.whl -
Subject digest:
5ba931b189263df9108a5227be33263eadf6f5415938aaeb220759b56019aae8 - Sigstore transparency entry: 2645162532
- Sigstore integration time:
-
Permalink:
NoeFontana/tf_tree@01a8ba7d754306a7e93d23fa63f38fb33d832db8 -
Branch / Tag:
refs/tags/v0.0.5 - Owner: https://github.com/NoeFontana
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
wheels.yml@01a8ba7d754306a7e93d23fa63f38fb33d832db8 -
Trigger Event:
push
-
Statement type:
File details
Details for the file transform_tree-0.0.5-cp39-abi3-musllinux_1_2_aarch64.whl.
File metadata
- Download URL: transform_tree-0.0.5-cp39-abi3-musllinux_1_2_aarch64.whl
- Upload date:
- Size: 863.8 kB
- Tags: CPython 3.9+, musllinux: musl 1.2+ ARM64
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0959ee56394405503fddf021bee12e1ff98c91cc81a7eccc8a61ba58edea6ccc
|
|
| MD5 |
c75af6b73dd0ea1435f1989ba978b346
|
|
| BLAKE2b-256 |
94f976f7c821140244353f4ba02e630542891dd77c504a92772847bbc7b40c77
|
Provenance
The following attestation bundles were made for transform_tree-0.0.5-cp39-abi3-musllinux_1_2_aarch64.whl:
Publisher:
wheels.yml on NoeFontana/tf_tree
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
transform_tree-0.0.5-cp39-abi3-musllinux_1_2_aarch64.whl -
Subject digest:
0959ee56394405503fddf021bee12e1ff98c91cc81a7eccc8a61ba58edea6ccc - Sigstore transparency entry: 2645170142
- Sigstore integration time:
-
Permalink:
NoeFontana/tf_tree@01a8ba7d754306a7e93d23fa63f38fb33d832db8 -
Branch / Tag:
refs/tags/v0.0.5 - Owner: https://github.com/NoeFontana
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
wheels.yml@01a8ba7d754306a7e93d23fa63f38fb33d832db8 -
Trigger Event:
push
-
Statement type:
File details
Details for the file transform_tree-0.0.5-cp39-abi3-manylinux_2_28_x86_64.whl.
File metadata
- Download URL: transform_tree-0.0.5-cp39-abi3-manylinux_2_28_x86_64.whl
- Upload date:
- Size: 734.1 kB
- Tags: CPython 3.9+, manylinux: glibc 2.28+ x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e43a56fd257b1492121e7bd82cdf0fabc8b1c4d3a2cdb36e3519dc7f5f43d7dd
|
|
| MD5 |
65e414ccabaf750e5f1f21b405c11588
|
|
| BLAKE2b-256 |
e382c836f7cdd3004bd775a7bdade10b13ed88982d679e274d4a78e6697f2024
|
Provenance
The following attestation bundles were made for transform_tree-0.0.5-cp39-abi3-manylinux_2_28_x86_64.whl:
Publisher:
wheels.yml on NoeFontana/tf_tree
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
transform_tree-0.0.5-cp39-abi3-manylinux_2_28_x86_64.whl -
Subject digest:
e43a56fd257b1492121e7bd82cdf0fabc8b1c4d3a2cdb36e3519dc7f5f43d7dd - Sigstore transparency entry: 2645166758
- Sigstore integration time:
-
Permalink:
NoeFontana/tf_tree@01a8ba7d754306a7e93d23fa63f38fb33d832db8 -
Branch / Tag:
refs/tags/v0.0.5 - Owner: https://github.com/NoeFontana
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
wheels.yml@01a8ba7d754306a7e93d23fa63f38fb33d832db8 -
Trigger Event:
push
-
Statement type:
File details
Details for the file transform_tree-0.0.5-cp39-abi3-manylinux_2_28_aarch64.whl.
File metadata
- Download URL: transform_tree-0.0.5-cp39-abi3-manylinux_2_28_aarch64.whl
- Upload date:
- Size: 685.7 kB
- Tags: CPython 3.9+, manylinux: glibc 2.28+ ARM64
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ea00663ab87f76ee4e72ccaed468a3f13747cd975dbeb309facd47e188915777
|
|
| MD5 |
103bfbec5c90c062c73628cd18194924
|
|
| BLAKE2b-256 |
845bb898d1866e0e9a057a4d1fbebb0a55eb693a926563e0421d1368491a1098
|
Provenance
The following attestation bundles were made for transform_tree-0.0.5-cp39-abi3-manylinux_2_28_aarch64.whl:
Publisher:
wheels.yml on NoeFontana/tf_tree
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
transform_tree-0.0.5-cp39-abi3-manylinux_2_28_aarch64.whl -
Subject digest:
ea00663ab87f76ee4e72ccaed468a3f13747cd975dbeb309facd47e188915777 - Sigstore transparency entry: 2645172758
- Sigstore integration time:
-
Permalink:
NoeFontana/tf_tree@01a8ba7d754306a7e93d23fa63f38fb33d832db8 -
Branch / Tag:
refs/tags/v0.0.5 - Owner: https://github.com/NoeFontana
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
wheels.yml@01a8ba7d754306a7e93d23fa63f38fb33d832db8 -
Trigger Event:
push
-
Statement type:
File details
Details for the file transform_tree-0.0.5-cp39-abi3-macosx_11_0_arm64.whl.
File metadata
- Download URL: transform_tree-0.0.5-cp39-abi3-macosx_11_0_arm64.whl
- Upload date:
- Size: 594.1 kB
- Tags: CPython 3.9+, macOS 11.0+ ARM64
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b3eec0e8ef6458981530e1562e62b18a1f4a3a095613c1378f0359d506b4d9e4
|
|
| MD5 |
f749a837b28ead6b343b799bc5628d3d
|
|
| BLAKE2b-256 |
c8760eeb18da5cde553de834f2a4c64f31ca7134ededd4945aabef2498cb8614
|
Provenance
The following attestation bundles were made for transform_tree-0.0.5-cp39-abi3-macosx_11_0_arm64.whl:
Publisher:
wheels.yml on NoeFontana/tf_tree
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
transform_tree-0.0.5-cp39-abi3-macosx_11_0_arm64.whl -
Subject digest:
b3eec0e8ef6458981530e1562e62b18a1f4a3a095613c1378f0359d506b4d9e4 - Sigstore transparency entry: 2645168374
- Sigstore integration time:
-
Permalink:
NoeFontana/tf_tree@01a8ba7d754306a7e93d23fa63f38fb33d832db8 -
Branch / Tag:
refs/tags/v0.0.5 - Owner: https://github.com/NoeFontana
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
wheels.yml@01a8ba7d754306a7e93d23fa63f38fb33d832db8 -
Trigger Event:
push
-
Statement type:
File details
Details for the file transform_tree-0.0.5-cp39-abi3-macosx_10_12_x86_64.whl.
File metadata
- Download URL: transform_tree-0.0.5-cp39-abi3-macosx_10_12_x86_64.whl
- Upload date:
- Size: 634.6 kB
- Tags: CPython 3.9+, macOS 10.12+ x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7e18bcd20f945989ba648bb6b8cf5b1ed31714cdaff3dfd09f2f3893b407f765
|
|
| MD5 |
94025e059741219fc1b6f7bbe9426b03
|
|
| BLAKE2b-256 |
17fa2e7718dfb7568330370b13d4e6ad7507aaedb7003b6ace0b8879f7970aab
|
Provenance
The following attestation bundles were made for transform_tree-0.0.5-cp39-abi3-macosx_10_12_x86_64.whl:
Publisher:
wheels.yml on NoeFontana/tf_tree
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
transform_tree-0.0.5-cp39-abi3-macosx_10_12_x86_64.whl -
Subject digest:
7e18bcd20f945989ba648bb6b8cf5b1ed31714cdaff3dfd09f2f3893b407f765 - Sigstore transparency entry: 2645176117
- Sigstore integration time:
-
Permalink:
NoeFontana/tf_tree@01a8ba7d754306a7e93d23fa63f38fb33d832db8 -
Branch / Tag:
refs/tags/v0.0.5 - Owner: https://github.com/NoeFontana
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
wheels.yml@01a8ba7d754306a7e93d23fa63f38fb33d832db8 -
Trigger Event:
push
-
Statement type: