af-credmon v0.1.1
An HTCondor credential monitor (credmon) that places a user's AF MCP broker-held credentials -- x509/VOMS proxy, CERN Kerberos ticket, ServiceX access token -- on worker nodes, without the job submitter (human or LLM agent) ever handling them.
It is the access-point half of the
af-mcp-platform HTCondor credmon
integration; see that repository's docs/credmon.md for the broker side. In
short:
- The broker stores a per-user, per-kind top token in credd as
<creddir>/<user>/af_<kind>.top. - af-credmon presents it to the broker's
POST /v1/credentials/<kind>/redeemand writes the credential to<creddir>/<user>/af_<kind>.use, renewing it before it expires. - A job with
use_oauth_services = af_krb5, af_x509gets$_CONDOR_CREDS/af_krb5.use(a krb5 ccache) and$_CONDOR_CREDS/af_x509.use(a VOMS proxy PEM) on the worker node, refreshed by HTCondor everySEC_CREDENTIAL_REFRESH(default 300s).
Installing on the access point
af-credmon runs as root on the access point (the schedd host), next to credd. Install it as a pixi environment:
git clone https://github.com/maniaclab/af-credmon /opt/af-credmon
cd /opt/af-credmon && git checkout v0.1.1 && pixi install
# -> /opt/af-credmon/.pixi/envs/default/bin/af-credmon
or into any Python 3.11+ environment that has the HTCondor Python bindings:
pip install af-credmon
HTCondor configuration
The access point needs credd, an OAuth credential directory, and the identity the broker stores with listed as a credential super-user:
DAEMON_LIST = $(DAEMON_LIST) CREDD CREDMON_OAUTH
SEC_CREDENTIAL_DIRECTORY_OAUTH = /var/lib/condor/oauth_credentials
CRED_SUPER_USERS = <identity of the broker's htcondor-api token>
CREDMON_OAUTH = /opt/af-credmon/.pixi/envs/default/bin/af-credmon
CREDMON_OAUTH_ARGS = --broker-url https://mcp.example.org --log-file $(LOG)/AfCredmonLog
This is primary mode (the default): af-credmon is the credmon credd talks
to. It owns <creddir>/pid (credd sends SIGHUP after every store, which
triggers an immediate rescan), touches CREDMON_COMPLETE after each scan, and
sends condor_master its ready message after the first one.
Next to an existing credmon
If the access point already runs condor_credmon_oauth (for SciTokens, an
OAuth2 provider, ...), it keeps owning CREDMON_OAUTH, the pid file and
CREDMON_COMPLETE. Run af-credmon as a separate daemon in alongside mode,
which only polls:
DAEMON_LIST = $(DAEMON_LIST) AF_CREDMON
AF_CREDMON = /opt/af-credmon/.pixi/envs/default/bin/af-credmon
AF_CREDMON_ARGS = --mode alongside --broker-url https://mcp.example.org --log-file $(LOG)/AfCredmonLog
The existing credmons never touch af_* files, with one exception: if the Vault
credmon is enabled with its default VAULT_CREDMON_PROVIDER_NAMES = *, it
claims every unclaimed .top file. Set an explicit list instead.
Options
| Option | Default | Meaning |
|---|---|---|
--broker-url |
(required) | AF MCP broker base URL |
--cred-dir |
HTCondor's SEC_CREDENTIAL_DIRECTORY_OAUTH |
credd's OAuth credential directory |
--prefix |
af_ |
credd service-name prefix; must match the broker's CREDMON_SERVICE_PREFIX |
--mode |
primary |
primary or alongside (see above) |
--interval |
60 |
seconds between scans |
--min-remaining |
600 |
refuse a credential expiring sooner than this (twice the default SEC_CREDENTIAL_REFRESH) |
--log-file |
stderr | log destination; condor_master does not capture a daemon's stderr |
--log-level |
INFO |
Python logging level |
What a scan does
For every <creddir>/<user>/<prefix><kind>.top (kinds x509, krb5,
servicex):
- redeem the top token when the
.usefile is missing, not yet issued since startup, or in the last third of its lifetime, and write the.useatomically (temp file, mode 0400, rename); - remove the
.usewhen the broker answers 404 (the identity is no longer linked), and anyaf_*.usewhose.topcredd has deleted; - keep the existing
.useon any other failure (broker outage, expired top token) and retry on the next scan, so running jobs never lose a still-valid credential.
Files without the prefix belong to other credmons and are never touched. Nothing
besides .use files (and, in primary mode, pid and CREDMON_COMPLETE) is
written to the credential directory.
Keeping credentials out of job output
HTCondor does not redact job stdout/stderr, and tools that read job output back (including LLM agents through condor-mcp) see it verbatim. Job scripts should reference the credentials by path only:
export KRB5CCNAME="FILE:${_CONDOR_CREDS}/af_krb5.use"
export X509_USER_PROXY="${_CONDOR_CREDS}/af_x509.use"
and never cat, base64 or echo them. Keep SEC_DEBUG_PRINT_KEYS off on the
access point; it writes credentials into the ShadowLog.
Development
pixi run -e dev pytest # unit + integration tests
pixi run -e dev typecheck # mypy
pixi run lint # pre-commit hooks
To release, bump the version, commit and tag in one step, then push:
pixi run -e dev tbump 0.1.1
Metadata
Release files for af-credmon 0.1.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| af_credmon-0.1.1.tar.gz | 94.1 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| af_credmon-0.1.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 111.6 kB
Release files / af_credmon-0.1.1.tar.gz
| Download URL | af_credmon-0.1.1.tar.gz |
|---|---|
| Size | 94.1 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
bc0fee4bc274e010becce14387aee2c64becd88b0b0a66d50a9a6e4ddb9a0ef8
|
|
BLAKE2b-256 checksum How to use checksums |
660ccb9003ce8a8957d02243055dedec0b3494f890acb2fbf95e6390b041dd6c
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
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 Oct 7, 2026.
Transparency logRelease files / af_credmon-0.1.1-py3-none-any.whl
| Download URL | af_credmon-0.1.1-py3-none-any.whl |
|---|---|
| Size | 17.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
00fe304aec0f97c349b540b7b5b6b4abf5258e752e2156ca6479d3e59c0f29ef
|
|
BLAKE2b-256 checksum How to use checksums |
a7cde6f0621c5f7c9a287c4db235b5b7bec3a4cc7d9f17fcb4bd63949e1219a5
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
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 Oct 7, 2026.
Transparency log