id-configurator
id-configurator is a CLI tool by InterDiode that configures a device (typically a
Docker image) in an air-gapped environment: no network access is required or ever used.
It reads a single configuration (JSON or YAML), and applies it to a filesystem tree, writing only the files that need to change: root certificates, system package manager sources (APT, YUM/DNF, APK) and language-specific package manager settings (pip/uv/poetry, Maven/Gradle/Ivy/SBT/Leiningen, Composer, npm, cargo, Go modules, RubyGems, NuGet). The result can then be used to build a Docker image or otherwise deployed to the target device.
How it works
--dstpoints to the destination directory that receives every modified or created file.id-configuratornever writes anywhere else.--src(optional) points to an existing filesystem, used to detect the current configuration (existing APT sources, existing Java keystores, etc.) so it can be extended or disabled instead of blindly overwritten.- The configuration is loaded from a file (
--config), from a base64-encoded string, typically an environment variable (--config-env/$ID_CONFIGURATOR_CONFIG), or downloaded over HTTP(S) from a URL (--config-url).--config-url-cacert-contentoptionally gives the PEM content of the CA certificate to trust for that one download, for an internal endpoint signed by a private CA the running system doesn't otherwise trust yet (e.g. before thecertificatesmodule has ever run) - it defaults to the system's own trust store otherwise.--config-url-insecuredisables certificate (and hostname) verification entirely instead, overriding--config-url-cacert-content- insecure, only meant for a first bootstrap against a server whose certificate isn't trustable, or trusted, yet by any other means. --user(repeatable) names a user to also prepare per-user configuration for, on top of the usual system-wide files, for the modules that have one:go,nuget,php(Composer),python(poetry) andrust. The user's home directory is taken from--src's own/etc/passwdentry when there is one, otherwise assumed to be/home/<user>(/rootforroot).--auto-usersdoes the same for every regular (non-system) user found in--src's/etc/passwd: one with a login shell (notnologin,syncorfalse) and a UID at or aboveUID_MIN(read from--src's/etc/login.defs, 1000 by default) - exceptroot, always included regardless of its UID. It combines freely with one or more--user.- On
apply, every module inspects the configuration and the source filesystem and decides for itself whether it has something to do; only relevant modules touch the destination directory. For example, the configured Alpine repositories are only written if--srccan be identified as an Alpine filesystem (i.e. it has an/etc/apkdirectory); and a source cloning Debian's official repository is only configured if that official repository is actually present in the filesystem given via--src(viauri-to-replace, see "Replacing an existing repository" below).
Installation
As a CLI tool
# with pip
pip install id-configurator
# or with uv tool, in its own isolated environment
uv tool install id-configurator
Once installed this way, run it directly as id-configurator (rather than uv run id-configurator, which is
only for the "from source" setup below).
As a Docker image
id-configurator is also published as interdiode/id-configurator
on Docker Hub - handy since its output is typically meant to become another Docker image in the first place.
One-off use:
docker run --rm -v "$(pwd)/config.yaml:/config.yaml:ro" -v "$(pwd)/output:/dst" -v "/:/src" \
interdiode/id-configurator:latest \
id-configurator apply --config /config.yaml --dst /dst --src /src
More typically, as a builder stage of the target image's own Dockerfile - <src> here is the target image
itself, taken as of its target stage, before id-configurator's own changes are copied back on top of it:
FROM debian:trixie-slim AS target
FROM interdiode/id-configurator:latest AS configuration
COPY --from=target / /src/
COPY config.yaml /config.yaml
RUN id-configurator apply --config /config.yaml --src /src --dst /dst
FROM target
COPY --from=configuration /dst/ /
From source (development)
uv sync
Usage
# Apply a configuration file to a destination directory
id-configurator apply --config config.yaml --dst ./output
# Take an existing filesystem into account (e.g. to extend its APT sources)
id-configurator apply --config config.yaml --src /path/to/rootfs --dst ./output
# Use a base64-encoded configuration from the environment instead of a file
export ID_CONFIGURATOR_CONFIG=$(base64 -w0 config.yaml)
id-configurator apply --dst ./output
# Download the configuration from an internal HTTPS server instead of a local file
id-configurator apply \
--config-url https://config.internal.example/device.yaml \
--config-url-cacert-content "$(cat internal-ca.pem)" \
--dst ./output
# Also prepare per-user configuration (go, nuget, php, python, rust) for specific users
id-configurator apply --config config.yaml --dst ./output --user alice --user bob
# Same, but auto-detect every regular (non-system) user from --src's /etc/passwd
id-configurator apply --config config.yaml --src /path/to/rootfs --dst ./output --auto-users
# Validate a configuration without applying it
id-configurator validate --config config.yaml
# List the available modules
id-configurator list-modules
# Same thing, via the Docker image instead of installing anything locally
docker run --rm \
-v "$(pwd)/config.yaml:/config.yaml:ro" \
-v "$(pwd)/input:/src" \
-v "$(pwd)/output:/dst" \
interdiode/id-configurator:latest \
id-configurator apply --config /config.yaml --dst /dst --src /src
The tool can also be invoked as python -m id_configurator.
Configuration format
The simplest way to generate a new configuration is InterDiode itself, through its simple interface - it also handles duplicating application mirrors onto disconnected networks.
The configuration is a JSON or YAML object. uuid, organisation and description are all optional
(identifying metadata only - nothing in the tool itself relies on any of them), as is every configurations.*
block - a module that finds nothing to do for it simply does not run. version is the configuration
format's own schema version (currently "1.0", and the default if omitted) - independent of the
id-configurator package version - so a future, incompatible format change can be told apart from this one:
version: "1.0"
uuid: <UUID, optional>
organisation: <string, optional>
description: <string, optional>
configurations:
certificates:
- name: <string>
content: <PEM>
apt:
sources:
- name: <string>
uris: [<string>]
suites: [<string>]
components: [<string>]
signed-by: <ASCII-armored GPG public key> # or signed-by-file: <path already on the target>
uri-to-replace: <fnmatch pattern, optional> # see "Replacing an existing repository" below
apk:
repositories:
- name: <string>
url: <string>
key: <RSA public key, optional>
key_filename: <string, optional> # name of the key file under /etc/apk/keys/, if not the derived default
url_to_replace: <fnmatch pattern, optional>
yum:
repositories:
- name: <string>
index-url: <string>
gpg-url: <string, optional>
index-url-to-replace: <fnmatch pattern, optional>
keys:
- name: <string>
content: <GPG key>
python:
repositories:
- name: <string>
index-url: <string>
java:
repositories:
- name: <string>
url: <string>
php:
repositories:
- name: <string>
url: <string>
npm:
repositories:
- name: <string>
url: <string>
rust:
repositories:
- name: <string>
url: <string>
is_sparse: <bool, optional, default false> # declares the registry's index as sparse+<url>
go:
repositories:
- name: <string>
url: <string>
ruby:
repositories:
- name: <string>
url: <string>
nuget:
repositories:
- name: <string>
url: <string>
Replacing an existing repository
apt.sources[].uri-to-replace, apk.repositories[].url_to_replace and yum.repositories[].index-url-to-replace
are optional fnmatch patterns. When set, the configured
repository is only written if the pattern matches at least one URI/URL already present in --src's existing
configuration - and only the matching existing entries are disabled, the rest are left untouched. This lets a
configuration safely target "whatever machine this is applied to": e.g. uri-to-replace: "http://deb.debian.org/*"
replaces Debian's default mirror if (and only if) it's actually there, without disturbing any other source a
given machine might already have. Leaving the field unset never disables anything - the configured repository
is simply added alongside whatever is already present.
Non-HTTPS repositories
A repository URL doesn't have to be HTTPS - several modules automatically adjust the tool's configuration so the plain-HTTP repository is actually usable, instead of silently producing a config the package manager would refuse at run time:
python: pip gets the repository's host added totrusted-host.php: Composer getssecure-httpdisabled, andpackagist.orgis disabled alongside it (an HTTP source implies a private, non-internet setup where the public registry wouldn't be reachable anyway).nuget: the source getsallowInsecureConnections="true".java: SBT's resolver getsallowInsecureProtocolappended. Leiningen is the one exception: its standalone launcher refuses any non-HTTPS repository unconditionally, with no configuration able to change that - a repository configured here simply won't be usable from Leiningen specifically.go,rust,apt,apk,yum: no adjustment is needed - these already work the same regardless of scheme.
Modules
| Module | Applies to |
|---|---|
certificates |
Installs root certificates into the system's CA store, rebuilding the trust bundle in pure Python (Debian/Ubuntu and RHEL/Fedora trust stores). |
apt |
Adds the configured APT sources (DEB822 format); disables only the existing entries matching uri-to-replace. |
apk |
Adds the configured Alpine /etc/apk/repositories entries and their keys; disables only the existing lines matching url_to_replace. |
yum |
Adds the configured YUM/DNF .repo files and imports their GPG keys; disables only the existing sections matching index-url-to-replace. Covers DNF too - same config files. |
python |
Points pip, uv, poetry and easy_install to the configured Python package indexes. |
java |
Adds root certificates to every detected JKS/PKCS12 keystore, and configures Maven, Gradle, Ivy, SBT and Leiningen repositories. |
php |
Registers the configured repositories with Composer; sets openssl.cafile whenever any certificate is configured, so PHP's own OpenSSL calls trust it too. |
npm |
Points npm at the configured registries and its own cafile; sets NODE_EXTRA_CA_CERTS system-wide whenever any certificate is configured, since Node ignores the system CA store. |
rust |
Points cargo at the configured registries, replacing crates.io. |
go |
Points the Go toolchain at the configured module proxies (GOPROXY), replacing proxy.golang.org, and disables GOSUMDB. |
ruby |
Points RubyGems at the configured sources, replacing rubygems.org. |
nuget |
Points NuGet at the configured package sources, replacing nuget.org. |
go, nuget, php, python and rust additionally honor --user/--auto-users (see "How it works" above),
writing the same per-user configuration under each such user's home directory.
This tool's own modular structure is meant to be extended: see CLAUDE.md for the module interface and how to add one.
Development
uv sync # install dependencies
uv run pytest # run the test suite
uv run ruff check --fix . # lint
uv run ruff format . # format
uv run pre-commit run --all-files # run all pre-commit hooks
License
CeCILL v2.1 (GPL-compatible free software license). See LICENSE. Copyright InterDiode SA.
Metadata
Release files for id-configurator 2026.10.24
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 | |
|---|---|---|---|---|
| id_configurator-2026.10.24.tar.gz | 54.3 kB | Without provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| id_configurator-2026.10.24-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 115.3 kB
Release files / id_configurator-2026.10.24.tar.gz
| Download URL | id_configurator-2026.10.24.tar.gz |
|---|---|
| Size | 54.3 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
e25f25b6ffa1f175fba42d3d6798e5ce8a5f4dcc762415682fae9febd18a6a1c
|
|
BLAKE2b-256 checksum How to use checksums |
5b06403fe276edf402822363c9d32f1f20865dc15b7198e43121b11ed2e7801a
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.13.5 {"installer":{"name":"uv","version":"0.13.5","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"26.04","id":"resolute","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|
Release files / id_configurator-2026.10.24-py3-none-any.whl
| Download URL | id_configurator-2026.10.24-py3-none-any.whl |
|---|---|
| Size | 61.0 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
463b0efc0454471b61a649752768a84eb28ab215b21ad488b888e7c292013293
|
|
BLAKE2b-256 checksum How to use checksums |
7fc4a16cd8f52f33ea642f1d3a51771c8f04b0abcefb94ed5a5ce82cf3b90aaf
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.13.5 {"installer":{"name":"uv","version":"0.13.5","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"26.04","id":"resolute","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|
Security for id-configurator 2026.10.24
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.
No provenance found
None of the files in this release were uploaded with provenance metadata.
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.