BytesafeDependency Firewall

PyPI

Configure pip, uv, and Poetry to install through your firewall. Index setup, authentication, CI, publishing with twine, and troubleshooting.

A PyPI firewall implements the simple repository API (PEP 503 HTML and PEP 691 JSON). Anything that speaks it can install through the firewall: pip, uv, and Poetry.

The examples use <namespace-id> and <firewall-id> placeholders. Replace them with IDs, not with namespace or firewall names. The firewall's Setup tab shows a ready-to-copy pip snippet with both values already filled in. This page also covers uv and Poetry, whose configuration is not generated in the dashboard.

Index endpoint

https://eu-sov-1.bytesafecloud.eu/v1/<namespace-id>/pypi/<firewall-id>/simple/

The namespace ID and firewall ID are part of the URL. Use HTTP basic authentication. Set the username to the literal value __token__ and the password to your Bytesafe access token.

Configure the project

Commit a pip.conf (pip.ini on Windows) or set the index per project:

pip.conf
[global]
index-url = https://__token__:${BYTESAFE_TOKEN}@eu-sov-1.bytesafecloud.eu/v1/<namespace-id>/pypi/<firewall-id>/simple/

Or without a config file:

pip install --index-url "https://__token__:${BYTESAFE_TOKEN}@eu-sov-1.bytesafecloud.eu/v1/<namespace-id>/pypi/<firewall-id>/simple/" requests

Use index-url, not extra-index-url. An extra index keeps pypi.org as a parallel source, and pip is free to install from it, bypassing the firewall entirely.

uv reads the index from pyproject.toml:

pyproject.toml
[[tool.uv.index]]
name = "firewall"
url = "https://eu-sov-1.bytesafecloud.eu/v1/<namespace-id>/pypi/<firewall-id>/simple/"
default = true

With credentials in the environment:

export UV_INDEX_FIREWALL_USERNAME=__token__
export UV_INDEX_FIREWALL_PASSWORD=$BYTESAFE_TOKEN

The pip-compatible interface also accepts the flag directly:

uv pip install --index-url "https://__token__:${BYTESAFE_TOKEN}@eu-sov-1.bytesafecloud.eu/v1/<namespace-id>/pypi/<firewall-id>/simple/" requests

Point Poetry's source at the firewall and store the credential outside the repository:

pyproject.toml
[[tool.poetry.source]]
name = "firewall"
url = "https://eu-sov-1.bytesafecloud.eu/v1/<namespace-id>/pypi/<firewall-id>/simple/"
priority = "primary"
poetry config http-basic.firewall __token__ $BYTESAFE_TOKEN

Each developer exports a personal access token:

export BYTESAFE_TOKEN=<your token>

Verify

pip install requests
uv add requests
poetry add requests

A successful install confirms the firewall is serving the package, and every request through that index is evaluated against your rules.

Do not expect a log entry from this. A request is logged only when it matches a rule with log: true or a logging exception, so an install that breaks no rule leaves no trace. An empty log is not evidence that the client bypassed the firewall. To see the requests while verifying setup, add a log-only rule that matches all packages. See What is logged when.

CI

Use a Service Access Token (SAT) instead of a PAT. Set it as a secret in your CI system and export it as BYTESAFE_TOKEN before install steps.

To correlate all requests from one pipeline run, append an interaction ID to the token:

export BYTESAFE_TOKEN="${BYTESAFE_TOKEN}::${CI_PIPELINE_ID}"

The ID shows up on each log entry generated by a matching logging rule or exception from that run.

Publishing

A firewall can also receive uploads. First set a publish target: the upstream that should receive published packages. Without one the firewall is install-only and rejects uploads. Then upload with twine against the firewall's legacy endpoint:

twine upload \
  --repository-url https://eu-sov-1.bytesafecloud.eu/v1/<namespace-id>/pypi/<firewall-id>/legacy/ \
  -u __token__ -p $BYTESAFE_TOKEN \
  dist/*

Upload rules run against the publish before it is forwarded. A blocked upload fails with:

requests@2.32.0 upload blocked. See firewall log for request <request-id>

What a blocked install looks like

Rules on the versions phase filter releases out of the simple index. The effect depends on how the dependency is declared:

  • A range like requests>=2.31 resolves to the newest version that passed the rules. The install succeeds, possibly with an older version than the public latest.
  • An exact pin to a filtered version fails with pip's Could not find a version that satisfies the requirement error, because that release no longer exists as far as the client can see.

Rules on the download phase block the file fetch. The firewall returns a 404 with:

requests@2.32.0 is blocked. Check firewall log entries for request <request-id> for details

Take the request ID to the logs to see which rule fired. See Investigate a blocked install.

Yanked releases

Releases yanked by their publisher (PEP 592) match the deprecated selector function, so one rule keeps yanked releases out of your builds.

Install scripts in sdists

The install-scripts selector function matches source distributions that run package code at build time: a root setup.py, or an in-tree PEP 517 build backend (backend-path in pyproject.toml). Wheels install without executing package code, so they never match. This asymmetry is visible during rollout: for a release shipping both formats, pip normally picks the wheel and installs fine, while a client forced to build from source (--no-binary, or a platform with no matching wheel) hits the sdist and the rule. See Block install scripts.

Troubleshooting

BYTESAFE_TOKEN is unset, expired, or revoked, or the URL lost its credential part. Confirm the variable is set in the shell that runs the install, that the URL still carries __token__:<token>@ before the host, and that the token is still listed and unexpired in the dashboard. The token travels in the password field; the username is not checked, so __token__ is convention, not a requirement.

Most likely a versions rule filtered it. Check the firewall logs before assuming an upstream problem.

If a rule expected to log an install but no matching entry appears, check for extra-index-url entries and overriding config with pip config list, and check the PIP_INDEX_URL environment variable. Any extra index pointing at pypi.org is an open bypass.

Credentials embedded in the index URL are parsed as a URL, so characters like @ or / in a password would need percent-encoding. Bytesafe tokens (pat-/sat- plus a UUID) contain none of these, but an appended interaction ID should stick to URL-safe characters.

On this page