Observation
Round-8 webhooks-9421 DX agent (PR #205) reported:
"`adcp-keygen` shipped to `.venv/bin/` isn't on PATH when invoking `.venv/bin/python` directly (typical CI / subprocess context). First `subprocess.run(["adcp-keygen", ...])` raised `FileNotFoundError`. Worked around with `Path(sys.executable).parent / "adcp-keygen"`. Suggest either documenting this idiom in the keygen module or shipping `generate_signing_keypair()` as a programmatic API returning `(pem_bytes, public_jwk)` so callers don't need to shell out."
Proposal
Add a programmatic entry point to src/adcp/signing/keygen.py:
def generate_signing_keypair(
*,
alg: Literal["ed25519", "es256"] = "ed25519",
kid: str | None = None,
purpose: Literal["request-signing", "webhook-signing"] = "request-signing",
passphrase: bytes | None = None,
) -> tuple[bytes, dict[str, Any]]:
"""Generate a signing keypair. Returns (pem_bytes, public_jwk).
Programmatic companion to the ``adcp-keygen`` CLI — call this from
tests, provisioning scripts, or any non-shell context where spawning
a subprocess is wrong.
Returns:
(pem_bytes, public_jwk) tuple. ``pem_bytes`` is the PKCS#8
private key (optionally encrypted if ``passphrase`` is set).
``public_jwk`` is the public half, ready to publish at your
agent's ``jwks_uri``.
"""
Re-export from adcp.signing + top-level adcp. Rewrite the CLI's main() to call this helper + handle file-writing + stdout printing separately. Zero duplication between CLI and programmatic paths.
Acceptance
- Unit test:
generate_signing_keypair() returns a PEM that loads via load_pem_private_key and a JWK that WebhookSender.from_jwk accepts.
- Unit test:
purpose="webhook-signing" produces a JWK with adcp_use: "webhook-signing".
- CLI tests still pass — CLI
main() now just wraps the helper.
- Doc example in
src/adcp/signing/__init__.py showing programmatic + CLI equivalence.
Priority
4.1 DX polish. Current CLI works; this eliminates subprocess-path ergonomic tax for test harnesses and provisioning code.
Related: #205 (round-8 validation)
Observation
Round-8 webhooks-9421 DX agent (PR #205) reported:
Proposal
Add a programmatic entry point to
src/adcp/signing/keygen.py:Re-export from
adcp.signing+ top-leveladcp. Rewrite the CLI'smain()to call this helper + handle file-writing + stdout printing separately. Zero duplication between CLI and programmatic paths.Acceptance
generate_signing_keypair()returns a PEM that loads viaload_pem_private_keyand a JWK thatWebhookSender.from_jwkaccepts.purpose="webhook-signing"produces a JWK withadcp_use: "webhook-signing".main()now just wraps the helper.src/adcp/signing/__init__.pyshowing programmatic + CLI equivalence.Priority
4.1 DX polish. Current CLI works; this eliminates subprocess-path ergonomic tax for test harnesses and provisioning code.
Related: #205 (round-8 validation)