Nacho
_ _ _ ____ _ _ ___ | \ | | / \ / ___|| | | | / _ \ | \| | / _ \ | | | |_| || | | | | |\ | / ___ \ | |___ | _ || |_| | |_| \_|/_/ \_\ \____||_| |_| \___/
Lightweight, self-hosted dynamic configuration service for Python
Overview
Nacho is a self-hosted configuration service for Python. Run one Nacho server, point your services at it with a few lines of code, and push configuration changes that reach them live — no redeploy, no restart, no polling.
Every write is validated against a JSON Schema before it is stored, every revision is kept in a rollback history, and a built-in single-file web UI manages all of it. When you do not need a server, the same library works standalone against a local file or an in-memory dict.
| Capability | Description |
|---|---|
| Centralized configuration | One server manages every service's configuration through a REST API, a CLI, and a web UI. |
| Live updates | Clients subscribe over WebSocket and receive changes the moment they happen. |
| Schema-first validation | Writes are checked against a JSON Schema and rejected before they reach storage. |
| History and rollback | Every revision is snapshotted; any revision can be inspected and restored. |
| Drop-in Python client | RemoteStorageBackend gives a remote application the same API as a local file. |
| Optimistic concurrency | Revision-checked writes turn lost updates into explicit 409 Conflict responses. |
| Multi-format | JSON, YAML, and TOML are interchangeable across API payloads, stored files, and the UI editor. |
| Standalone mode | Use Nacho as a plain configuration library with no server at all. |
Installation
Nacho keeps its core dependency footprint small through optional extras:
pip install nacho-python[server] # run a configuration server
pip install nacho-python[remote] # connect a service to a server
pip install nacho-python[schema] # JSON Schema validation for standalone use
pip install nacho-python # core: standalone local file management
Requires Python 3.9 or later. Docker images are available for containerized deployments (see Docker).
Quick start
Start a server:
pip install nacho-python[server]
nacho server --config config.yaml --api-key "secure-key"
The server is now live at http://127.0.0.1:8000 with a REST API, WebSocket
push, interactive API docs at /docs, and a management UI at /ui.
Connect a service:
pip install nacho-python[remote]
from nacho import Nacho, RemoteStorageBackend
config = Nacho(
storage=RemoteStorageBackend(
url="http://127.0.0.1:8000",
app_name="my-service",
api_key="secure-key",
watch=True, # receive live updates over WebSocket
),
events=True,
)
# Read configuration exactly like a local dict
port = config.get_int("server.port", default=8000)
# React the instant someone changes it on the server
@config.on_change("features.*")
def on_flag_change(path, new_value, **kwargs):
print(f"{path} is now {new_value}")
Change a value from the UI, the CLI, or the API, and every connected client sees it immediately.
No server needed? Nacho also works as a standalone file-backed library:
config = Nacho("config.yaml"). See
Standalone usage.
Running a server
The CLI is the usual way to run a server:
nacho server \
--config config.yaml \
--schema schema.json \
--port 8000 \
--api-key "secure-key" \
--data-dir ".nacho/apps" \
--history-limit 50
The server binds to 127.0.0.1 by default. Pass --host 0.0.0.0 to accept
connections from other machines; the CLI prints a warning if you expose the
server without --api-key, because an unauthenticated server grants full
write access to anyone who can reach it.
Run exactly one server process per data directory. App state, revision
counters, and WebSocket subscriptions live in process memory, so running the
app behind multiple workers (e.g. uvicorn --workers 2) or pointing two
server processes at the same --data-dir produces inconsistent revisions and
missed live updates.
| Flag | Default | Description |
|---|---|---|
--host |
127.0.0.1 |
Bind address (0.0.0.0 to listen on all interfaces) |
--port |
8000 |
Bind port |
--config, -c |
none | Configuration file to serve as an app |
--schema |
none | JSON Schema enforced for --config |
--app-name |
default |
App name for the --config file |
--data-dir |
none | Directory for API-created app state and history |
--api-key |
none | Enable bearer-token authentication |
--read-only-api-key |
none | Additional key granting read access only |
--history-limit |
50 |
Revision snapshots kept per app (0 disables history) |
--read-only |
off | Reject every write |
--reload |
off | Auto-reload for development |
Embedding in an existing application
NachoOrchestrator wraps one or more Nacho instances in a FastAPI
application, so a server can also be constructed in code or mounted into an
existing app:
from fastapi import FastAPI
from nacho import Nacho, NachoOrchestrator
app = FastAPI(title="My Application")
orchestrator = NachoOrchestrator(
apps={"config": Nacho("config.yaml", events=True)},
api_key="secure-key",
cors_origins=["https://admin.example.com"],
)
app.mount("/config", orchestrator.app) # configuration API under /config
Run it directly with orchestrator.run(host="127.0.0.1", port=8000) when it
is not mounted.
Management UI
The server hosts a single-file web UI at /ui — no separate process, no build
step. It provides:
- App management: list, create, rename, describe, and delete apps.
- Configuration editing: a code editor for JSON, YAML, and TOML with syntax highlighting, format switching, on-demand validation, and revision-aware saves that surface conflicts instead of clobbering newer data.
- Schema editing: view, edit, or clear an app's JSON Schema; the current configuration is re-checked against the new schema before it is accepted.
- History: browse revision snapshots and restore any of them with one click.
- Live updates: changes pushed over WebSocket are reflected in real time, and unsaved local edits are preserved when a remote change arrives.
When the server runs with --api-key, the UI prompts for the key on first
load. The /ui page itself is public so the sign-in screen can load; every
API call behind it requires authentication.
Pre-authenticating an embedded UI
When the orchestrator is mounted inside a host application that already has
its own session auth, users should not face a second login. The UI reads its
credential from two same-origin browser stores, and these two names are a
stable contract — seed them before navigating to /ui and the UI comes up
already signed in:
| Store | Name | Used for |
|---|---|---|
localStorage |
nacho_api_key |
Authorization: Bearer header on REST calls |
| Cookie | NACHO_api_key |
The WebSocket handshake, which cannot send headers |
The cookie value must be URL-encoded, and its path must match the mount
point — the UI derives it as location.pathname with a trailing /ui
removed (so /config/ui yields a cookie path of /config, and a top-level
mount yields /):
// Run on your own login page, same origin as the mounted UI.
function preauthenticateNacho(key, mountPath = "") {
localStorage.setItem("nacho_api_key", key);
const secure = location.protocol === "https:" ? "; Secure" : "";
document.cookie =
"NACHO_api_key=" + encodeURIComponent(key) +
"; path=" + (mountPath || "/") + "; SameSite=Strict" + secure;
}
Seed only a credential the signed-in user is entitled to. A user who may
read configuration but not change it should be given the
--read-only-api-key, not the admin key — the UI surfaces the server's
403 responses on write attempts.
Do not pass the key in the URL (?token=): query strings leak through
browser history, Referer headers, and proxy logs.
Authentication
Passing --api-key (or api_key= to NachoOrchestrator) enables bearer
authentication for the whole API. Clients send the key either as an
Authorization: Bearer <key> header or through the cookie the UI sets for
its own WebSocket handshake. Requests with a missing or wrong key receive
401 Unauthorized. The comparison is timing-safe.
A second key can be added with --read-only-api-key (or
read_only_api_key=). It authenticates GETs and WebSocket subscriptions but
receives 403 Forbidden on any write — hand it to dashboards, pollers, and
services that only consume configuration, so the write credential never
leaves the operators who need it.
/, /health, /ui, /docs, /redoc, and /openapi.json stay public: the
API surface is not a secret, only the data behind it.
Cross-origin browser access is disabled unless you opt in with
cors_origins=[...] — the bundled UI is same-origin and the SDK/CLI are not
browsers, so a drive-by web page cannot reach a default server.
REST API
Interactive documentation is available at /docs (Swagger UI) and /redoc
once the server is running.
Payload format
Config and schema payloads are native JSON objects:
curl -X POST http://127.0.0.1:8000/api/apps \
-H "Authorization: Bearer secure-key" \
-H "Content-Type: application/json" \
-d '{
"name": "my-service",
"data": {"database": {"host": "localhost", "port": 5432}},
"schema": {"type": "object", "properties": {"database": {"type": "object"}}}
}'
Payloads may also be sent as encoded strings in any supported format, which is how the CLI and UI submit YAML and TOML:
{"data": "database:\n host: localhost\n", "format": "yaml"}
Revisions and optimistic concurrency
Every app carries a monotonically increasing revision, bumped on each
successful write. Reads of /config (full or per-path) return the current
revision in the ETag and X-Nacho-Revision response headers. Writes may
include a revision field stating the revision the client last saw; if the
server has moved on, the write fails with 409 Conflict and the stored
configuration is left untouched:
curl -X PUT http://127.0.0.1:8000/api/apps/my-service/config/cache.ttl \
-H "Authorization: Bearer secure-key" \
-H "Content-Type: application/json" \
-d '{"value": 600, "revision": 3}'
Omitting revision performs an unconditional write.
The ETag also enables cheap polling: send it back as If-None-Match and
the server answers 304 Not Modified — no body — until the revision moves.
curl -H 'If-None-Match: "3"' http://127.0.0.1:8000/api/apps/my-service/config
History and rollback
The server snapshots every revision (configuration, schema, and metadata)
into a per-app ring buffer — on disk under data_dir/history/ when a data
directory is configured, in memory otherwise. --history-limit controls
retention.
Rollback is roll-forward: restoring revision 41 does not rewrite history but creates a new revision whose content equals snapshot 41. The revision counter stays monotonic, live clients are notified like on any other write, and a rollback can itself be rolled back. A snapshot restores configuration and schema together, so the result is always self-consistent.
curl http://127.0.0.1:8000/api/apps/my-service/history
curl http://127.0.0.1:8000/api/apps/my-service/history/41
curl -X POST http://127.0.0.1:8000/api/apps/my-service/rollback \
-H "Content-Type: application/json" \
-d '{"revision": 41, "expected_revision": 42}'
Endpoint reference
System:
| Endpoint | Method | Description |
|---|---|---|
/health |
GET | Health check and instance summary |
/ui |
GET | Built-in web management UI |
/docs, /redoc |
GET | Interactive API documentation |
/api/convert |
POST | Convert a payload between JSON, YAML, and TOML |
App management:
| Endpoint | Method | Description |
|---|---|---|
/api/apps |
GET | List all apps |
/api/apps |
POST | Create an app |
/api/apps/{app} |
GET | Get app info |
/api/apps/{app} |
PUT | Replace an app's config, schema, and description |
/api/apps/{app} |
DELETE | Delete an app |
/api/apps/{app}/metadata |
PATCH | Rename an app or change its description |
Configuration and schema:
| Endpoint | Method | Description |
|---|---|---|
/api/apps/{app}/config |
GET | Get the full configuration |
/api/apps/{app}/config |
PUT | Replace the full configuration |
/api/apps/{app}/config/{path} |
GET | Get the value at a dotted path |
/api/apps/{app}/config/{path} |
PUT | Set the value at a dotted path |
/api/apps/{app}/config/{path} |
DELETE | Delete the key at a dotted path |
/api/apps/{app}/schema |
GET | Get the app's JSON Schema |
/api/apps/{app}/schema |
PUT | Replace or clear the app's JSON Schema |
/api/apps/{app}/validate |
POST | Validate a payload against the app's schema |
History:
| Endpoint | Method | Description |
|---|---|---|
/api/apps/{app}/history |
GET | List revision snapshots, newest first |
/api/apps/{app}/history/{revision} |
GET | Get one revision snapshot |
/api/apps/{app}/rollback |
POST | Restore a snapshot as a new revision |
Real-time:
| Endpoint | Protocol | Description |
|---|---|---|
/ws/{app} |
WebSocket | Receive the current config on subscribe, then every change |
The WebSocket is receive-only: the server sends an initial_config message
on subscribe followed by an update message per change, each carrying the
app name, revision, and full configuration. Writes always go through REST, so
there is never ambiguity about who owns the state.
Remote clients
A remote client reads and writes through the REST API and receives pushes
over WebSocket. Once constructed, a remote-backed Nacho instance behaves
exactly like a file-backed one — the same get, set, on_change, and
schema APIs.
Requires pip install nacho-python[remote].
REST reads/writes
+-------------+ GET/PUT/PATCH/DELETE +----------------------+
| Python app | -----------------------> | Nacho server |
| Nacho | | REST API + Web UI |
| Remote | <----------------------- | File/dict storage |
| client | WebSocket pushes | Schema validation |
+-------------+ /ws/{app} +----------------------+
| ^
| on_change handlers |
+---------------------------------------------+
live config updates
from nacho import Nacho, RemoteStorageBackend
storage = RemoteStorageBackend(
url="https://config-server.example.com",
app_name="my-service",
api_key="secure-key",
watch=True, # opt in to WebSocket updates
)
config = Nacho(storage=storage, events=True)
host = config.get("database.host")
@config.on_change("features.*")
def on_feature_change(path, new_value, **kwargs):
print(f"feature flag updated: {path} = {new_value}")
Connecting never mutates the server: a wrong API key fails loudly at
construction, reading a nonexistent app raises a clear error (so a typo
cannot silently return an empty config), and the first save() to a
nonexistent app creates it.
The backend is revision-aware. Every load and push records the server
revision it saw, and save() sends it back — if another client wrote in
between, save() raises ConflictError (with the server's expected/actual
revisions) instead of silently overwriting the other write. Call load(),
reapply your change, and save again:
from nacho import ConflictError
try:
config.save()
except ConflictError:
config.load() # pick up the concurrent change
config.set("my.key", value)
config.save()
If the WebSocket connection drops, the watcher reconnects automatically with
a bounded number of consecutive retries (reconnect=0 retries forever), and
the counter resets on every successful connection. Keepalive pings detect
half-open connections, and permanent failures (bad key, deleted app) stop
the retry loop with a clear log line instead of retrying forever. Stale or
out-of-order pushes are dropped, so the local snapshot never rolls backwards.
The CLI covers the same ground without the SDK — see Command-line interface.
Event system
With events=True, Nacho dispatches change notifications after every
successful write, whether the change was made locally or pushed from a
server. Events carry the changed path, old value, new value, and event type.
from nacho import Nacho, EventType
config = Nacho("config.yaml", events=True)
# Fires for any change to a key under "database"
@config.on_change("database.*")
def on_db_change(path, old_value, new_value, **kwargs):
print(f"database key changed: {path}")
# Fires once per write operation (aggregate event)
@config.on_change("@global")
def on_any_change(**kwargs):
print("config was modified")
# Fires for CREATE or UPDATE events under "cache"
@config.on_event([EventType.CREATE, EventType.UPDATE], path_pattern="cache.*")
def on_cache_change(event_type, path, new_value, **kwargs):
print(f"{event_type.name} {path} = {new_value}")
config.set("database.host", "new-host") # on_db_change, on_any_change
config.set("cache.ttl", 600) # on_cache_change (CREATE)
config.set("cache.ttl", 300) # on_cache_change (UPDATE)
Path pattern reference:
| Pattern | Fires when |
|---|---|
None (default) |
Any change at any path |
"@global" |
Once per write operation (aggregate) |
"*" |
Any per-key event (not the aggregate) |
"database.*" |
Any key nested under database |
Handlers may be sync or async. Async handlers are scheduled on the running
event loop when one exists, and executed with asyncio.run() otherwise.
Registering a handler on an instance created without events=True logs a
warning, because the handler would never fire.
Schema validation
Nacho enforces the schema on every write. An invalid value raises
ValidationError before the change is applied, so the configuration is never
left in an invalid state. The same guarantee applies to local writes and to
writes accepted by the server.
Standalone usage requires pip install nacho-python[schema].
{
"type": "object",
"required": ["database"],
"properties": {
"database": {
"type": "object",
"required": ["host", "port"],
"properties": {
"host": {"type": "string"},
"port": {"type": "integer", "minimum": 1024}
}
}
}
}
from nacho import Nacho, ValidationError
config = Nacho("config.yaml", schema="schema.json")
# An invalid write raises immediately; the config is not modified
try:
config.set("database.port", "not-a-number")
except ValidationError as e:
print(e.errors) # list of violation strings
# Inspect the current config without writing
errors = config.validate()
# Validate an arbitrary dict against the schema
errors = config.check({"database": {"host": "localhost", "port": 80}})
Standalone usage
Nacho does not require a server. Point it at a local file, or hand it a plain dict, and it works as a self-contained configuration library for scripts, tests, and single-process applications. Everything below behaves identically with a file, a dict, or a remote server behind it.
Reading and writing
from nacho import Nacho
config = Nacho({"database": {"host": "127.0.0.1", "port": 5432}}) # in-memory
config = Nacho("config.yaml") # file-backed
# Typed reads with sensible coercion
host = config.get("database.host")
port = config.get_int("database.port")
debug = config.get_bool("app.debug")
tags = config.get_list("app.tags")
options = config.get_dict("app.options")
config.update({"logging": {"level": "DEBUG"}}) # deep-merge
config.replace({"database": {"host": "prod-db", "port": 5432}})
config.delete("legacy.setting")
config.load() # reload from storage
config.save() # persist to storage
print(config.json()) # export as a JSON string
Transactions
Group multiple writes into one atomic operation. The transaction commits when the block exits cleanly and is discarded on any exception. Commit replays the transaction's operations onto the current configuration, so an unrelated write that landed while the transaction was open is preserved, not discarded:
with config.transaction() as txn:
txn.set("database.host", "new-host")
txn.set("database.port", 5433)
# Handlers fire once here with the aggregated changes
config.save()
Environment variable overrides
Pass env_prefix to overlay environment variables on the configuration at
load time. Variable names follow {PREFIX}_{NESTED_KEY}, with nesting levels
separated by the delimiter (default _):
export MYAPP_DATABASE_HOST=prod-db.example.com
export MYAPP_DATABASE_PORT=5433
export MYAPP_FEATURES_ENABLED=true
config = Nacho("config.yaml", env_prefix="MYAPP")
config.get("database.host") # "prod-db.example.com"
config.get_int("database.port") # 5433
config.get_bool("features.enabled") # True
Values are coerced where unambiguous: true/false/yes/no/on/off
become booleans, numeric strings become numbers ("1" is the integer 1, not
a boolean), and JSON-looking strings are parsed as JSON. Floats are only
parsed when the text round-trips, so MYAPP_VERSION=3.10 stays the string
"3.10", and quoting a value (MYAPP_PORT='"8080"') forces it to stay a
string. Everything else stays a string.
Keys with underscores in their names use a doubled delimiter for nesting:
MYAPP_DB__MAX_CONNECTIONS sets db.max_connections. Env overrides are
runtime-only overlays: save() persists the stored configuration, not the
overlaid view.
Command-line interface
nacho --help
nacho --version
Every remote command takes --remote <url>, --app-name <name> (default
default), and --api-key <key>. Errors are written to stderr, and exit
codes distinguish failure classes so scripts can branch without parsing
messages:
| Exit code | Meaning |
|---|---|
| 0 | Success |
| 1 | Generic error (schema violation, transport failure, missing extras) |
| 2 | Usage error (bad flags or arguments) |
| 3 | Not found (missing app, key, path, or history revision) |
| 4 | Revision conflict (a concurrent write won) |
| 5 | Authentication failure (bad or missing API key, read-only server) |
Output is rendered in --format {json,yaml,toml} (default json), so every
command's output is machine-parseable; nacho get missing.key exits 3 with a
stderr message instead of printing None.
Values
# Read a key, or the whole config, locally or remotely
nacho get database.host --config config.yaml
nacho get --format json --remote http://config-server:8000 --app-name my-service
# Include the current revision in the output
nacho get --show-revision --format json --remote http://config-server:8000
# Write and delete, with optional conflict-safe revision checks
nacho set cache.ttl 600 --remote http://config-server:8000 --revision 3
nacho delete legacy.setting --remote http://config-server:8000 --revision 4
# Force a type when auto-detection would guess wrong
nacho set app.version 3.10 --type str --remote http://config-server:8000
nacho set app.flags '{"beta": true}' --type json --remote http://config-server:8000
# Local file equivalents
nacho set database.port 5432 --config config.yaml
nacho delete legacy.setting --config config.yaml
Local writes are read-modify-write on the file with no cross-process lock:
the file itself is written atomically, but two nacho set commands racing on
the same file can lose one of the updates. Point concurrent writers at a
server (--remote) instead — that is what revision-checked writes are for.
Apps
nacho apps list --remote http://config-server:8000
nacho apps create my-service \
--remote http://config-server:8000 \
--description "Core service" \
--config config.yaml \
--schema schema.json
nacho apps show --remote http://config-server:8000 --app-name my-service
nacho apps rename my-service-v2 --remote http://config-server:8000 --app-name my-service
nacho apps describe "Payments service" --remote http://config-server:8000 --app-name my-service
nacho apps delete my-service --remote http://config-server:8000
Schemas and validation
# Print or replace the schema the server enforces
nacho schema get --remote http://config-server:8000 --app-name my-service
nacho schema push schema.json --remote http://config-server:8000 --app-name my-service
# Validate a local file against the server's stored schema
nacho validate --config config.yaml --remote http://config-server:8000 --app-name my-service
# Validate against a local schema file instead
nacho validate --config config.yaml --schema schema.json
History and rollback
nacho history list --remote http://config-server:8000 --app-name my-service
nacho history show 41 --remote http://config-server:8000 --app-name my-service
# What changed between two revisions — or between revision 41 and now
nacho history diff 41 42 --remote http://config-server:8000 --app-name my-service
nacho history diff 41 --remote http://config-server:8000 --app-name my-service
# Restore revision 41 as a new revision; --revision-check makes it conflict-safe
nacho rollback 41 --remote http://config-server:8000 --app-name my-service --revision-check 42
Live updates
# Print the current config, then one JSON line per change (Ctrl+C to stop)
nacho watch --remote http://config-server:8000 --app-name my-service
Scaffolding
# Create a config file from a built-in template
nacho init config.yaml --template default
# Templates: empty, default, web-app, api-service, microservice
Docker
Nacho ships a multi-stage Dockerfile producing a small Alpine-based image
that runs the configuration server as a non-root user, with a container
health check against /health. Published images are available on Docker Hub
and GHCR:
docker pull k3scat/nacho:latest
docker pull ghcr.io/nya-foundation/nacho:latest
# Run the server (UI at http://localhost:8000/ui)
docker run -p 8000:8000 k3scat/nacho:latest
# Run with authentication enabled
docker run -p 8000:8000 k3scat/nacho:latest \
server --host 0.0.0.0 --config config.yaml --api-key "secure-key"
# Mount your own config for the default app
docker run -p 8000:8000 \
-v "$(pwd)/config.yaml:/app/config.yaml" k3scat/nacho:latest
Or with Compose:
docker compose up --build
The image entrypoint is nacho; the default command is
server --host 0.0.0.0 --config config.yaml. Append any nacho server flags
to override the defaults. The container exposes port 8000.
Operational notes
- Dot-notation paths are intentionally simple. Literal dots in key names and numeric string keys are ambiguous; prefer nested object keys.
- The built-in API-key authentication suits local, private, and single-tenant deployments. Shared production deployments should add scoped tokens, audit logging, and rate limiting in front of the service.
- Server state is file-backed and single-process. The revision counter is authoritative in memory, so run one server process per data directory; the storage abstraction is the boundary to implement a stronger backend if you need multi-process or high-availability operation.
- Editing a
--configfile by hand while the server is running is not detected; the server's next write wins. Make changes through the API, CLI, or UI.
Development
git clone https://github.com/nya-foundation/nacho.git
cd nacho
uv sync --all-extras
uv run pytest # fast suites (unit + smoke), 95% coverage gate
uv run pytest -m "integration or e2e" --no-cov # live-server suites
uv run pytest -m docker --no-cov # builds and exercises the Docker image
uv run playwright install chromium # one-time browser download
uv run pytest -m ui --no-cov # browser-driven web UI suite
uvx ruff format . && uvx ruff check . # formatting and lint (enforced in CI)
See CONTRIBUTING.md for the branch model, code style, and release process.
Community
Open an issue on GitHub or join the Nya Foundation Discord.
License
MIT — see LICENSE for details.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file nacho_python-0.1.1.tar.gz.
File metadata
- Download URL: nacho_python-0.1.1.tar.gz
- Upload date:
- Size: 87.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c2664d4db49b6a508374d57b4b684a97ea574149b5b1931e94ef4dbbaac22114
|
|
| MD5 |
f04a099d63d291412d0bf92eba6d861b
|
|
| BLAKE2b-256 |
617b1c67eebb83304fda33884affff4e28f04b40ea9049df217a84cfff5834cd
|
Provenance
The following attestation bundles were made for nacho_python-0.1.1.tar.gz:
Publisher:
publish.yml on Nya-Foundation/nacho
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
nacho_python-0.1.1.tar.gz -
Subject digest:
c2664d4db49b6a508374d57b4b684a97ea574149b5b1931e94ef4dbbaac22114 - Sigstore transparency entry: 2193935460
- Sigstore integration time:
-
Permalink:
Nya-Foundation/nacho@c1f7150a4f76a987563e2ec6ba566269e42bb52b -
Branch / Tag:
refs/heads/main - Owner: https://github.com/Nya-Foundation
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@c1f7150a4f76a987563e2ec6ba566269e42bb52b -
Trigger Event:
push
-
Statement type:
File details
Details for the file nacho_python-0.1.1-py3-none-any.whl.
File metadata
- Download URL: nacho_python-0.1.1-py3-none-any.whl
- Upload date:
- Size: 83.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
bf2fffbc2ae61564ef1828255eb9a834b138597e3cd427543c952039ee2dc1b8
|
|
| MD5 |
80ae412ebd4452b5965a3823af07d7c0
|
|
| BLAKE2b-256 |
fd939a240c13af3e536190a665ca26c841846aae9f0ce2c659d1d64f412c7643
|
Provenance
The following attestation bundles were made for nacho_python-0.1.1-py3-none-any.whl:
Publisher:
publish.yml on Nya-Foundation/nacho
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
nacho_python-0.1.1-py3-none-any.whl -
Subject digest:
bf2fffbc2ae61564ef1828255eb9a834b138597e3cd427543c952039ee2dc1b8 - Sigstore transparency entry: 2193935556
- Sigstore integration time:
-
Permalink:
Nya-Foundation/nacho@c1f7150a4f76a987563e2ec6ba566269e42bb52b -
Branch / Tag:
refs/heads/main - Owner: https://github.com/Nya-Foundation
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@c1f7150a4f76a987563e2ec6ba566269e42bb52b -
Trigger Event:
push
-
Statement type: