Skip to main content

dfukit

Firmware updates over any link. dfukit is a small update protocol with a server for the device and a client for the host, both in C, a Python package and CLI that drive the C client, and a client in JavaScript for web pages.

  • One protocol for every link: UART, UDP, TCP, BLE, USB, or a link of your own. dfukit sends frames; the link only has to carry them, and may lose some.
  • A pipeline, not a request per frame: the host sends what the device's window allows, the device acknowledges in bulk, and a lost frame is sent again from where it went missing (go-back-N).
  • Images raw or deflated, decoded on the device as they come in (miniz's inflater, built in).
  • A 32-bit board ID per image target: it selects the backend that writes the image, and a device refuses IDs it does not know before it touches anything.
  • Backends for what is behind a board ID: an ESP-IDF OTA slot, MCUboot's slot and RAM come with dfukit; write your own for another slot, or carry the session to a device further down a chain (pass-through).
  • Resume after a lost link, with the device's state kept for a while.
  • No threads, no heap and no platform code in the core: the application feeds it frames and time, and gets frames back through a callback.

The protocol and the internals are described in DESIGN.md, and what changed in CHANGELOG.md.

How an update goes

The host sends START with the image's board ID, size and CRC-32. The device prepares, then tells the host how much data it may send ahead of the acknowledgements: the window. The host streams DATA up to that limit and the device acknowledges as it goes. When a frame is lost, the host goes back and sends again from the missing point (go-back-N); it does the same after a retransmission timeout (RTO) without progress. FINISH checks the CRC and runs the backend's own checks, and ACTIVATE switches to the new image.

Adding it to a project

With CMake, from a copy or a submodule of this repository (dfukit_ram only if you use the RAM backend):

add_subdirectory(dfukit)
target_link_libraries(app PRIVATE dfukit dfukit_ram)

In an ESP-IDF project the repository is a component: add it to EXTRA_COMPONENT_DIRS (or components/) and REQUIRES it. In a Zephyr build it is a module (zephyr/module.yml): add it to ZEPHYR_EXTRA_MODULES, or to one image's <image>_EXTRA_ZEPHYR_MODULES with sysbuild, and turn it on with CONFIG_DFUKIT.

Building and testing

pip install pytest "nanobind>=2.5"
cmake -S . -B build && cmake --build build && ctest --test-dir build --no-tests=error

For the C libraries alone, add -DDFUKIT_PYTHON=OFF to the first cmake. That build has no tests, so leave out ctest.

The tests run the server and the client against each other through a simulated link that drops, duplicates and delays frames, and the Python package and CLI against a device in the test process. They are in Python, on the C code through its bindings; tests/README.md has how to measure their coverage.

A device: the server

The server is a struct the application owns. Frames go in with dfukit_server_feed(), answers come out through the send callback, and dfukit_server_poll() runs its timers. One task calls into a server at a time.

#include <string.h>

#include "dfukit_server.h"

static int send_frame(void *ctx, const dfukit_peer_t *to, const uint8_t *frame, size_t len)
{
	// To the peer `to` on your link (its address, as you gave it to dfukit_server_feed())
	return my_link_send(to, frame, len);
}

static dfukit_server_t server;
static dfukit_backend_t backend;

void update_init(void)
{
	const dfukit_server_config_t config = {
		.send = send_frame,
		.name = "my-device",
		.max_frame = 512, // the largest frame the link carries
	};

	dfukit_server_init(&server, &config);
	my_backend_init(&backend, 0x0000a001); // see "Backends"
	dfukit_server_register(&server, &backend);
}

// For each frame from the link, and every few milliseconds
void on_frame(const uint8_t *addr, size_t addr_len, const uint8_t *frame, size_t len)
{
	dfukit_peer_t from = {.len = (uint8_t)addr_len};

	if (addr_len > DFUKIT_PEER_MAX) {
		return; // longer than a peer: map it to 16 bytes of your own (an index, a hash)
	}
	memcpy(from.bytes, addr, addr_len);
	dfukit_server_feed(&server, &from, frame, len, now_ms());
}

void every_10_ms(void)
{
	dfukit_server_poll(&server, now_ms());
}

A peer is an opaque address of up to 16 bytes (DFUKIT_PEER_MAX): a socket address, a BLE connection handle, a bus address; the server drops frames from a longer one. An IPv6 address with its port is 18 bytes: give the server something shorter that names it (a slot in a table of your own). Two peers are the same when their bytes are. The peer that starts a session owns it; another one gets BUSY until it ends, unless it resumes the same image (see "Security"). INFO and STATUS are answered to anyone.

To take deflated images, give the server an inflater and a dictionary as large as the window the backend announces (32 KiB takes any deflate stream):

#include "dfukit_inflate.h"

static dfukit_inflate_t inflate;
static dfukit_inflater_t inflater;
static uint8_t dict[1u << 15];

dfukit_inflate_init(&inflate, &inflater, dict, sizeof(dict));
// and in the server's config: .inflater = &inflater

on_status in the config reports the session's state and progress, on every change and every acknowledgement: for a light or a display. A pass-through session has no progress to report.

INFO lists as many boards as fit in one frame, after a name cut to fit. The C client does not send or parse it: a C application that wants it sends the frame itself and reads the answer (DESIGN.md, Messages). In Python it is dfukit.info(). It can warn early, but START is the real check.

Backends

A backend puts an image in place for one board ID (dfukit_backend_t, dfukit_server.h). The server calls it in order:

Callback Required When What it does
begin yes START gets ready for an image of start->image_size bytes (may erase)
write yes data comes in writes the decoded image at offset, in order
window yes every acknowledgement how many more stream bytes it can take now (0: wait)
finish yes FINISH its own checks of the whole image; DFUKIT_PENDING while still writing
activate yes ACTIVATE makes the image the one that runs (restarts, or at the next boot)
abort no the session is broken off undoes what it can

The server does not check for missing callbacks: a required one left NULL crashes when it is called. src/backends/ram/dfukit_ram.c is a complete backend to start from. Setting one up looks like this:

void my_backend_init(dfukit_backend_t *backend, uint32_t board_id)
{
	memset(backend, 0, sizeof(*backend));
	backend->board_id = board_id;
	backend->kind = DFUKIT_KIND_IMAGE;
	backend->max_size = MY_SLOT_SIZE;
	backend->encodings = DFUKIT_ENCODING_BIT(DFUKIT_ENCODING_RAW); // and _DEFLATE, if it fits
	backend->begin = my_begin;
	backend->write = my_write;
	backend->window = my_window;
	backend->finish = my_finish;
	backend->activate = my_activate;
	backend->abort = my_abort;
	backend->ctx = &my_slot;
}

The application owns the backend, and it must outlive the server.

The server has checked the image's CRC-32 before it calls finish. The CRC catches accidents, not attackers: checking signatures is up to the backend, which hands the image to whatever checks them on its platform.

The CRC is a small module of its own (lib/crc32). Its dfukit_crc32() is weak, so an application replaces it by defining one (a hardware engine, say), in an object file that gets linked. MSVC has no weak symbols: there the static library gives way to the application's definition.

The ones that come with dfukit:

Backend Platform Writes
ram any a buffer; for tests and for devices that handle the image themselves
esp-idf-ota ESP-IDF the next OTA slot; optionally erased ahead of time (dfukit_esp_ota_preerase())
mcuboot-recovery Zephyr, inside MCUboot MCUboot's slot 0, from a recovery entered at reset

A pass-through backend (kind = DFUKIT_KIND_PASSTHROUGH) does not write anything: forward gets the session's frames as they are, for a device further down a chain, and that device's answers go back with dfukit_server_downstream(). The server does not read the session: it ends with the owner's ABORT, after the idle time, or when the backend calls dfukit_server_passthrough_end(). abort is called however it ends. A pass-through backend needs forward; its abort is optional, and the image callbacks are not used.

A backend usually ends the session once the device behind it has activated. Hand the answer on first, because ending the session first drops it without an error:

void on_frame_from_the_device_behind(const uint8_t *frame, size_t len)
{
	dfukit_server_downstream(&server, frame, len, now_ms());
	if (dfukit_frame_activated(frame, len)) {
		dfukit_server_passthrough_end(&server);
	}
}

esp-idf-ota

Writes the next OTA slot, so the partition table needs two OTA slots. FINISH runs ESP-IDF's check of the image, and ACTIVATE makes it the boot image and restarts.

#include "dfukit_esp_ota.h"

static dfukit_esp_ota_t ota;

// window 0: 16 KiB. window_log2 12: takes deflate, with an inflater of that size in the server's
// config; 0 takes raw only
ESP_ERROR_CHECK(dfukit_esp_ota_init(&ota, &backend, 0x0000a001, 0, 12));
dfukit_server_register(&server, &backend);

Rollback is ESP-IDF's, and off unless CONFIG_BOOTLOADER_APP_ROLLBACK_ENABLE is on: without it a bad image that boots stays. With it, the new image must call esp_ota_mark_app_valid_cancel_rollback() once it is up, or the next reset goes back to the old one. Until it does, updates are refused with BACKEND, and the device's log says why.

After that confirmation, dfukit_esp_ota_preerase() erases the other slot in the background, so the next update writes without waiting for erases. Asked for while the slot holds an update's image, it waits for the next update.

mcuboot-recovery

An update inside MCUboot itself, for devices with a single application slot. MCUboot's own serial recovery is off; dfukit's Zephyr module uses MCUboot's action hooks to enter its recovery when the mcuboot-button0 pin is active at reset or when there is no bootable image. It listens on the UART chosen as dfukit,uart, receives by DMA, and takes its frames encoded with burst-link. FINISH runs MCUboot's own check of the image, so the host hears of an image that will not boot; MCUboot checks it again at the next boot.

The image needs burst-link as a module next to dfukit's: both in mcuboot_EXTRA_ZEPHYR_MODULES with sysbuild.

# MCUboot's configuration (sysbuild: sysbuild/mcuboot.conf)
CONFIG_DFUKIT=y
CONFIG_DFUKIT_MCUBOOT_RECOVERY=y
CONFIG_DFUKIT_MCUBOOT_BOARD_ID=0x0000b001
CONFIG_DMA=y
/* MCUboot's devicetree overlay */
/ {
	chosen {
		dfukit,uart = &lpuart1;
	};
};

&lpuart1 {
	current-speed = <3000000>;
	dmas = <&edma0 2 9>, <&edma0 3 8>;
	dma-names = "rx", "tx";
};

The receive buffers (CONFIG_DFUKIT_MCUBOOT_RX_BUFFER) must each take the window (CONFIG_DFUKIT_MCUBOOT_WINDOW): the flash's erases stop the interrupts, and everything a host may have in flight then lands in one buffer.

Notes for integrators

  • The window is a promise the backend makes to the host, not a limit the server enforces: a host that sends past it still gets its frames to write. A backend that buffers takes more, or fails.
  • One inflater serves every backend: its dictionary must take the largest window_log2 a backend announces, else a stream compressed to that window fails at its CRC.
  • begin runs inside the START it answers: it must be done within the client's tries times its RTO (4 s by default), or open with a window of 0 and do the rest in the background.
  • activate only schedules the switch (a restart after delay_ms, from a timer or a task): the server answers ACTIVATE with what it returns, so it must return before restarting.
  • finish may return DFUKIT_PENDING while it still writes what it buffered; the host asks again.
  • mcuboot-recovery writes the flash in whole write units (flash_area_align()); the last one is padded with the erased value.

Security

The protocol has no authentication. dfukit leaves this to the link and the backend:

  • Anyone who can reach the device can send START, and begin may erase flash before any signature has been checked. Restrict who can reach it, or authenticate the link.
  • A peer that knows an image's CRC-32 and sizes can take over a session on that image by resuming it. There is no other credential.
  • The CRC-32 only catches accidents. To refuse an image that is not yours, the backend or the bootloader checks a signature.

A host: the client

The C client is driven like the server: dfukit_client_feed() with the device's frames, dfukit_client_poll() every few milliseconds, frames out through send, and the image read through read at any offset (a resend reads again), so it need not be in RAM.

#include "dfukit_client.h"
#include "dfukit_crc32.h"

dfukit_client_t client;
const dfukit_client_config_t config = {
	.send = link_send,
	.read = image_read,
	.max_frame = 512,
};
dfukit_start_t start = {
	.board_id = 0x0000a001,
	.encoding = DFUKIT_ENCODING_RAW,
	.image_size = image_len,
	.stream_size = image_len,
};

start.crc32 = dfukit_crc32(0, image, image_len);
dfukit_client_init(&client, &config);
// true: ACTIVATE after FINISH. 0: activate now (DFUKIT_ACTIVATE_AT_NEXT_BOOT: at the next boot).
// 0: no delay before the switch, in milliseconds
dfukit_client_begin(&client, &start, true, 0, 0, now_ms());
while (dfukit_client_state(&client) < DFUKIT_CLIENT_DONE) {
	// feed what came in, then:
	dfukit_client_poll(&client, now_ms());
}
// DFUKIT_CLIENT_DONE, or DFUKIT_CLIENT_FAILED and dfukit_client_status() says why

After a lost link (or a DFUKIT_TIMEOUT), dfukit_client_resume() on the new one goes on where the device stopped, or starts over when the device has no state left.

A host: Python

pip install dfukit
pip install 'dfukit[serial]'   # with the serial transport (pyserial, burst-link)
import dfukit

transport = dfukit.open_transport("udp:192.168.1.20:5000")
board = dfukit.info(transport).board(0x0000A001)  # None when it does not fit INFO's frame

# Compressed for the window the device has: a larger one fails at the end, with a CRC mismatch
update = dfukit.Update(
    transport, image, 0x0000A001, encoding=dfukit.DEFLATE, window_log2=board.window_log2
)
update.run(progress=lambda done, total: print(done, total))

Update.run() takes a reconnect callback that returns a new transport after a lost link; the update then resumes on it, and gives up after 70 s without progress. Once ACTIVATE is sent it does not reconnect, because a device that restarted would be flashed again: a lost answer raises dfukit.ActivationNotConfirmed, and the device may well be running the new image.

The CLI:

dfukit info --via udp:192.168.1.20:5000
dfukit update --via serial:/dev/ttyUSB0@3000000 --board 0x0000B001 firmware.bin

--encoding auto (the default) deflates when the board takes it, with the board's window.

The transports that come with the package are udp (one frame per datagram) and serial (burst-link frames). Others come as plugins: a package registers a dfukit.Transport subclass under the entry point dfukit.transports, and the CLI finds it by its name.

class MyLinkTransport(dfukit.Transport):
    max_frame = 509

    def open(self, address: str) -> None: ...
    def send(self, frame: bytes) -> None: ...  # hand each received frame to self.received()
    def close(self) -> None: ...
[project.entry-points."dfukit.transports"]
mylink = "my_link.transport:MyLinkTransport"

A transport raises dfukit.LinkError when its link is gone.

The client and server directly

For a link and a clock of your own, without a transport: the same client and server as in C, driven the same way.

The caller provides the link and the clock, in milliseconds that fit 32 bits. Unlike the C client, begin() takes the whole image as bytes.

import time

import dfukit


def now_ms() -> int:
    return int(time.monotonic() * 1000) & 0xFFFFFFFF


client = dfukit.Client(link_send, lambda offset, length: image[offset : offset + length], max_frame=512)
client.begin(0x0000A001, image, now_ms())
while client.state not in (dfukit.ClientState.DONE, dfukit.ClientState.FAILED):
    for frame in frames_from_the_link():
        client.feed(frame, now_ms())
    client.poll(now_ms())
if client.state == dfukit.ClientState.FAILED:
    raise RuntimeError(dfukit.status_name(client.status))

dfukit.Server is the device's end, with add() for a backend (RamBackend, PassthroughBackend), feed(peer, frame, now) and poll(now); its send callback gets (peer, frame).

A deflated image goes the same way: the client streams the compressed bytes, and begin() still takes the image, for its size and CRC. The stream is raw deflate, with a window the device announces:

compressor = zlib.compressobj(9, zlib.DEFLATED, -15)
stream = compressor.compress(image) + compressor.flush()
client = dfukit.Client(link_send, lambda offset, length: stream[offset : offset + length], max_frame=512)
client.begin(0x0000A001, image, now_ms(), stream_size=len(stream), encoding=1)

On the server, server.set_inflater(dfukit.Inflate(15)) turns deflate on.

A host: JavaScript

src/js/dfukit.js is the client for a web page, on a link the page has (a WebSocket, say). It is a plain script without dependencies, and defines dfukit:

const update = new dfukit.Update({
	send: frame => socket.send(frame), // throws when the link is gone: the resends cover it
	image,                             // a Uint8Array
	boardId: 0x0000a001,
	deflated: await dfukit.deflate(image), // optional
	maxFrame: 509,
	onProgress: (done, total) => show(done, total),
});

socket.onmessage = event => update.received(new Uint8Array(event.data));
await update.run(); // throws dfukit.UpdateError when it fails; update.resume() on a new link

dfukit.deflate() needs CompressionStream, which every current browser has. It compresses with a 32 KiB window and can not use a smaller one, so a board that announces less takes the image raw.

Like Python's Update, it does not resume once ACTIVATE is sent, because a device that restarted would be flashed again: a lost answer is an UpdateError, and the device may well be running the new image.

License

Apache-2.0 (LICENSE). miniz's inflater in lib/miniz is MIT (its LICENSE).

Metadata

Release files for dfukit 2.2.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for dfukit 2.2.0
File Size Uploaded Provenance Actions
dfukit-2.2.0.tar.gz 180.6 kB With provenance Details for dfukit-2.2.0.tar.gz

Built distributions (wheels)

Table of built distributions (wheels) for dfukit 2.2.0
File Actions
dfukit-2.2.0-cp312-abi3-win_amd64.whl CPython 3.12 abi3 Windows x86-64 Details for dfukit-2.2.0-cp312-abi3-win_amd64.whl
dfukit-2.2.0-cp312-abi3-win32.whl CPython 3.12 abi3 Windows x86-32 Details for dfukit-2.2.0-cp312-abi3-win32.whl
dfukit-2.2.0-cp312-abi3-musllinux_1_2_x86_64.whl CPython 3.12 abi3 Linux musl 1.2+ x86-64 Details for dfukit-2.2.0-cp312-abi3-musllinux_1_2_x86_64.whl
dfukit-2.2.0-cp312-abi3-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl CPython 3.12 abi3 Linux glibc 2.28+ x86-64, Linux glibc 2.27+ x86-64 Details for dfukit-2.2.0-cp312-abi3-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl
dfukit-2.2.0-cp312-abi3-macosx_11_0_arm64.whl CPython 3.12 abi3 macOS 11.0+ ARM64 Details for dfukit-2.2.0-cp312-abi3-macosx_11_0_arm64.whl
dfukit-2.2.0-cp312-abi3-macosx_10_14_x86_64.whl CPython 3.12 abi3 macOS 10.14+ x86-64 Details for dfukit-2.2.0-cp312-abi3-macosx_10_14_x86_64.whl
dfukit-2.2.0-cp311-cp311-win_amd64.whl CPython 3.11 CPython 3.11 Windows x86-64 Details for dfukit-2.2.0-cp311-cp311-win_amd64.whl
dfukit-2.2.0-cp311-cp311-win32.whl CPython 3.11 CPython 3.11 Windows x86-32 Details for dfukit-2.2.0-cp311-cp311-win32.whl
dfukit-2.2.0-cp311-cp311-musllinux_1_2_x86_64.whl CPython 3.11 CPython 3.11 Linux musl 1.2+ x86-64 Details for dfukit-2.2.0-cp311-cp311-musllinux_1_2_x86_64.whl
dfukit-2.2.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl CPython 3.11 CPython 3.11 Linux glibc 2.28+ x86-64, Linux glibc 2.27+ x86-64 Details for dfukit-2.2.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl
dfukit-2.2.0-cp311-cp311-macosx_11_0_arm64.whl CPython 3.11 CPython 3.11 macOS 11.0+ ARM64 Details for dfukit-2.2.0-cp311-cp311-macosx_11_0_arm64.whl
dfukit-2.2.0-cp311-cp311-macosx_10_14_x86_64.whl CPython 3.11 CPython 3.11 macOS 10.14+ x86-64 Details for dfukit-2.2.0-cp311-cp311-macosx_10_14_x86_64.whl
dfukit-2.2.0-cp310-cp310-win_amd64.whl CPython 3.10 CPython 3.10 Windows x86-64 Details for dfukit-2.2.0-cp310-cp310-win_amd64.whl
dfukit-2.2.0-cp310-cp310-win32.whl CPython 3.10 CPython 3.10 Windows x86-32 Details for dfukit-2.2.0-cp310-cp310-win32.whl
dfukit-2.2.0-cp310-cp310-musllinux_1_2_x86_64.whl CPython 3.10 CPython 3.10 Linux musl 1.2+ x86-64 Details for dfukit-2.2.0-cp310-cp310-musllinux_1_2_x86_64.whl
dfukit-2.2.0-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl CPython 3.10 CPython 3.10 Linux glibc 2.28+ x86-64, Linux glibc 2.27+ x86-64 Details for dfukit-2.2.0-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl
dfukit-2.2.0-cp310-cp310-macosx_11_0_arm64.whl CPython 3.10 CPython 3.10 macOS 11.0+ ARM64 Details for dfukit-2.2.0-cp310-cp310-macosx_11_0_arm64.whl
dfukit-2.2.0-cp310-cp310-macosx_10_14_x86_64.whl CPython 3.10 CPython 3.10 macOS 10.14+ x86-64 Details for dfukit-2.2.0-cp310-cp310-macosx_10_14_x86_64.whl

Total release size: 4.7 MB

Release files / dfukit-2.2.0.tar.gz

Download URL dfukit-2.2.0.tar.gz
Size 180.6 kB
Tags Source
SHA-256 checksum
How to use checksums
30ff79821666d8b6c16c616be834e927356a2d0c5d6760445864052f41b1b3c5
BLAKE2b-256 checksum
How to use checksums
79275d4eee5207c14b0e6720d961c2cde64e30bfdca4d543a21853c716baa1d7
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 5, 2026.

Transparency log

Release files / dfukit-2.2.0-cp312-abi3-win_amd64.whl

Download URL dfukit-2.2.0-cp312-abi3-win_amd64.whl
Size 302.7 kB
Tags CPython 3.12 Windows x86-64 abi3
SHA-256 checksum
How to use checksums
12d9ce5953ad0a85f7fa5c37d23a2456dfa6d1a9d4d8de91c002dac0659656ee
BLAKE2b-256 checksum
How to use checksums
e0c2a55dcbda4b359f6df4ea2207567000c323f0c00256f5f7c13acce3e900d8
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 5, 2026.

Transparency log

Release files / dfukit-2.2.0-cp312-abi3-win32.whl

Download URL dfukit-2.2.0-cp312-abi3-win32.whl
Size 296.6 kB
Tags CPython 3.12 Windows x86-32 abi3
SHA-256 checksum
How to use checksums
c67313a30d14e67dd9d7267d9ed9e41680bd4019b3213cb410478c64eedb241f
BLAKE2b-256 checksum
How to use checksums
274c9b09f97747ede38d3779ed63bee906048b960385704f228ac57a7abf085f
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 5, 2026.

Transparency log

Release files / dfukit-2.2.0-cp312-abi3-musllinux_1_2_x86_64.whl

Download URL dfukit-2.2.0-cp312-abi3-musllinux_1_2_x86_64.whl
Size 578.0 kB
Tags CPython 3.12 Linux musl 1.2+ x86-64 abi3
SHA-256 checksum
How to use checksums
e837719d3475a954e0b07b52845e5ab5244110b8ce9b0fdefd14ed2ef1209ebe
BLAKE2b-256 checksum
How to use checksums
33bbd666aa66377c5a33d08b0f63fac69d14ef2c715f5ea4077ff9f77e5b9ee5
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 5, 2026.

Transparency log

Release files / dfukit-2.2.0-cp312-abi3-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl

Download URL dfukit-2.2.0-cp312-abi3-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl
Size 120.7 kB
Tags CPython 3.12 Linux glibc 2.27+ x86-64 Linux glibc 2.28+ x86-64 abi3
SHA-256 checksum
How to use checksums
2efd8fa44a446ac7d71ae2bf5cabda7933230fe50203ec6bfe9b18b38d0ef06c
BLAKE2b-256 checksum
How to use checksums
9481dd4c9aec2fde42489c9e898236acad4ae967b54643dafffc33abbcc6ee6d
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 5, 2026.

Transparency log

Release files / dfukit-2.2.0-cp312-abi3-macosx_11_0_arm64.whl

Download URL dfukit-2.2.0-cp312-abi3-macosx_11_0_arm64.whl
Size 95.9 kB
Tags CPython 3.12 abi3 macOS 11.0+ ARM64
SHA-256 checksum
How to use checksums
1c95690de352a3ae37447a9f78b04d53c72ce6a011f45ca481ddcd3113a13d85
BLAKE2b-256 checksum
How to use checksums
3434c28c8261609159b666da032bbbe1083ff001a4c6c66063a12cdb240b56e5
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 5, 2026.

Transparency log

Release files / dfukit-2.2.0-cp312-abi3-macosx_10_14_x86_64.whl

Download URL dfukit-2.2.0-cp312-abi3-macosx_10_14_x86_64.whl
Size 100.2 kB
Tags CPython 3.12 abi3 macOS 10.14+ x86-64
SHA-256 checksum
How to use checksums
bb265bdaff53c8f1e6fe0c0ea3fe9a9a00408d53b3626445c8f942b434422c1d
BLAKE2b-256 checksum
How to use checksums
4bfa61b6f9deb12c835f0963aa0e5f72a18f6ad77903afdbd5ac020316b81df5
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 5, 2026.

Transparency log

Release files / dfukit-2.2.0-cp311-cp311-win_amd64.whl

Download URL dfukit-2.2.0-cp311-cp311-win_amd64.whl
Size 303.3 kB
Tags CPython 3.11 Windows x86-64
SHA-256 checksum
How to use checksums
6ed77d301c92c6b4646f2bdfec4d3a282793e8083f8a086f8feeb2366abc8758
BLAKE2b-256 checksum
How to use checksums
5da0acb1402b5f450b8e297c05fddd75235b7bdc459f07f55838ffc03ef1cf7d
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 5, 2026.

Transparency log

Release files / dfukit-2.2.0-cp311-cp311-win32.whl

Download URL dfukit-2.2.0-cp311-cp311-win32.whl
Size 297.5 kB
Tags CPython 3.11 Windows x86-32
SHA-256 checksum
How to use checksums
d560fd7c47b3d3faaaf0f03af6549c6d558fbace029810da400ab2018204373a
BLAKE2b-256 checksum
How to use checksums
2bc481d4e65782ce01164203fd5fd5054ac3a4860e75911fa10f4157c9fa486d
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 5, 2026.

Transparency log

Release files / dfukit-2.2.0-cp311-cp311-musllinux_1_2_x86_64.whl

Download URL dfukit-2.2.0-cp311-cp311-musllinux_1_2_x86_64.whl
Size 583.0 kB
Tags CPython 3.11 Linux musl 1.2+ x86-64
SHA-256 checksum
How to use checksums
26fb938e3c0d4c333277ca9c49315d0c1e7bdf4097677488605522b42c9f54b1
BLAKE2b-256 checksum
How to use checksums
ee7415959e9ffce2110bac9d7760e9524ac8b6ee10f520f663525cbdeee47b45
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 5, 2026.

Transparency log

Release files / dfukit-2.2.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl

Download URL dfukit-2.2.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl
Size 125.9 kB
Tags CPython 3.11 Linux glibc 2.27+ x86-64 Linux glibc 2.28+ x86-64
SHA-256 checksum
How to use checksums
a0336a64d21e52ad68989699d92fab1c1d39490e1987648b7a54ebd590db49cc
BLAKE2b-256 checksum
How to use checksums
79c74bf2024d02c247bae19bf4cc707e9aba5327e2f387a04974da2b46623d73
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 5, 2026.

Transparency log

Release files / dfukit-2.2.0-cp311-cp311-macosx_11_0_arm64.whl

Download URL dfukit-2.2.0-cp311-cp311-macosx_11_0_arm64.whl
Size 98.0 kB
Tags CPython 3.11 macOS 11.0+ ARM64
SHA-256 checksum
How to use checksums
81d6fdefe24439fe13e4283c9128aa9ec76a6f305f1a9b2351a8b0096edb0eb3
BLAKE2b-256 checksum
How to use checksums
df423916d6af4c96952147174a5404aa6257bb68168cc2a46ee106e6441ec218
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 5, 2026.

Transparency log

Release files / dfukit-2.2.0-cp311-cp311-macosx_10_14_x86_64.whl

Download URL dfukit-2.2.0-cp311-cp311-macosx_10_14_x86_64.whl
Size 102.2 kB
Tags CPython 3.11 macOS 10.14+ x86-64
SHA-256 checksum
How to use checksums
5ee050a4d7d9bc280ef42ba35b469cc97c97d674f7f803dcb76a1936ccb14cc6
BLAKE2b-256 checksum
How to use checksums
59381bc1b6e8f33b9b15c5c3507539a86882b99a04d267609405ed99e71bc642
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 5, 2026.

Transparency log

Release files / dfukit-2.2.0-cp310-cp310-win_amd64.whl

Download URL dfukit-2.2.0-cp310-cp310-win_amd64.whl
Size 303.0 kB
Tags CPython 3.10 Windows x86-64
SHA-256 checksum
How to use checksums
bb973d9b6aab3b6b2d0a7079b11b36a76dda1113f4f3d2525c7bb9a135caca8f
BLAKE2b-256 checksum
How to use checksums
3da30049b539ecae2b6a0a642d84a3849701ec53f3b65628ff8c9955be59af88
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 5, 2026.

Transparency log

Release files / dfukit-2.2.0-cp310-cp310-win32.whl

Download URL dfukit-2.2.0-cp310-cp310-win32.whl
Size 297.3 kB
Tags CPython 3.10 Windows x86-32
SHA-256 checksum
How to use checksums
2b873b13f7dd3ee6d25f78237d83b8ac8c4445555156b4e9bd65a35c2978e954
BLAKE2b-256 checksum
How to use checksums
c5e43ae77215b3965edc2a246395393a04632a8fd0c96c4875618409956afe33
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 5, 2026.

Transparency log

Release files / dfukit-2.2.0-cp310-cp310-musllinux_1_2_x86_64.whl

Download URL dfukit-2.2.0-cp310-cp310-musllinux_1_2_x86_64.whl
Size 582.7 kB
Tags CPython 3.10 Linux musl 1.2+ x86-64
SHA-256 checksum
How to use checksums
c15a67751d25449cc4f03264ca4cf099a0bcee50230a0f7b27a3ad06496c6c81
BLAKE2b-256 checksum
How to use checksums
7dd6f39cd354fa6c16de4b39480508c775e6c6a3c99506fce9a61968c87bcfcd
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 5, 2026.

Transparency log

Release files / dfukit-2.2.0-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl

Download URL dfukit-2.2.0-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl
Size 125.4 kB
Tags CPython 3.10 Linux glibc 2.27+ x86-64 Linux glibc 2.28+ x86-64
SHA-256 checksum
How to use checksums
392706a2727821389415ca78f26a88e0c9ebbafa3522f1a848df7db5f7dc37fd
BLAKE2b-256 checksum
How to use checksums
c961c4c2d54afa9b46b52813d3d328dd5b4108ff0dddcb6012bd567e1fe2461d
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 5, 2026.

Transparency log

Release files / dfukit-2.2.0-cp310-cp310-macosx_11_0_arm64.whl

Download URL dfukit-2.2.0-cp310-cp310-macosx_11_0_arm64.whl
Size 97.4 kB
Tags CPython 3.10 macOS 11.0+ ARM64
SHA-256 checksum
How to use checksums
0e326b49391775ca32ba0b6629fa48eaa64ab2ad52cd53edca1e6ac578ba82da
BLAKE2b-256 checksum
How to use checksums
e73dea3287b9ec022cb4f58d9e4a50bcf4fa2af5d5dc42cbc74e3cceef826bb5
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 5, 2026.

Transparency log

Release files / dfukit-2.2.0-cp310-cp310-macosx_10_14_x86_64.whl

Download URL dfukit-2.2.0-cp310-cp310-macosx_10_14_x86_64.whl
Size 101.5 kB
Tags CPython 3.10 macOS 10.14+ x86-64
SHA-256 checksum
How to use checksums
20b9735c4c1ccaa7e222a16b82dfc93bb0730a2350964c281263d3384091eed8
BLAKE2b-256 checksum
How to use checksums
d24d632d4db1c7883709512291dd052f6997ac4829f171350ef63cc3506301e3
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 5, 2026.

Transparency log

Security for dfukit 2.2.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 19 files in this release have been uploaded with provenance metadata.

Select from 19 files

Attestation for dfukit-2.2.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 5, 2026.

Transparency log

A 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.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page