> For the complete documentation index, see [llms.txt](https://doc.peeringhub.io/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://doc.peeringhub.io/guides/generating-certificate/python-libraries.md).

# Python Libraries

Peeringhub provides two Python packages for service providers who want to generate and operate STIR/SHAKEN certificates from a Python-based workflow:

* [stir-shaken-toolkit](https://pypi.org/project/stir-shaken-toolkit/) is the reusable toolkit. Use it when you want a command-line utility or Python library for ACME, TNAuthList, STI-PA SPC tokens, CSRs, certificate inspection, and Peeringhub certificate issuance.
* [shaken-cert-manager](https://pypi.org/project/shaken-cert-manager/) is the lifecycle manager. Use it when you want a Certbot-style operations tool that keeps the active certificate current, manages archive/live state, runs deployment hooks, reports status, renews certificates, and cleans up old material.

The video walkthrough [SHAKEN Cert Manager intro](https://www.youtube.com/watch?v=A4O7BrlWtq4) explains the intended operating model: use the toolkit for the low-level STIR/SHAKEN and ACME work, then use the manager when you need repeatable certificate operations on a production signing server.

#### Which Python project should I use?

Use `stir-shaken-toolkit` when you are learning the flow, testing Peeringhub issuance, building your own automation, or integrating certificate issuance into an existing Python service. It exposes provider-neutral RFC 8555 ACME primitives, STIR/SHAKEN-specific helpers, and Peeringhub defaults.

Use `shaken-cert-manager` when the certificate must be maintained over time. It is the operational layer on top of `stir-shaken-toolkit`. It tracks which certificate generation is active, keeps old generations in an archive, publishes stable `live/current` links, decides when renewal is needed, and provides `status`, `renew`, `force-renew`, and `cleanup` commands.

Neither package signs calls by itself. Your STI-AS, SIP proxy, or signing service still signs outbound calls and inserts the SIP `Identity` header. The Python tools only help you obtain, publish, rotate, inspect, and monitor the STIR/SHAKEN certificate material used by that signing service.

#### Prerequisites

Before using either Python package, prepare these values:

* Python 3.10 or later.
* An active Peeringhub STI-CA account.
* The Peeringhub ACME key identifier, also called `ACME_KID`.
* STI-PA credentials: `STIPA_USER_ID`, `STIPA_PASSWORD`, and `STIPA_SP_ID`.
* Your service provider code, usually the same value as the OCN/SPC, for example `818H`.
* Certificate subject values, including country, state, locality, and organization.
* A secure location for the ACME account key, for example `/var/lib/shaken/account/account.key`.
* A public HTTPS URL where your signing service can publish the active `certificate-chain.pem`. This URL is the certificate URL used by PASSporT `x5u`.

Install the packages in a virtual environment:

```bash
python3 -m venv .venv
. .venv/bin/activate
pip install --upgrade pip
pip install stir-shaken-toolkit shaken-cert-manager
```

#### Option 1: Issue a certificate directly with stir-shaken-toolkit

`stir-shaken-toolkit` is the best starting point because it shows every important step in the Peeringhub issuance flow. The package is split into three layers:

* `acme_core`: provider-neutral ACME request signing, nonces, accounts, orders, challenges, finalization, and download logic.
* `stir_shaken_acme`: STIR/SHAKEN-specific helpers for TNAuthList, STI-PA SPC tokens, fingerprints, CSRs, certificate issuance, inspection, and validation.
* `stir_shaken_toolkit.providers.peeringhub`: Peeringhub defaults and convenience APIs so operators do not need to build the ACME profile by hand.

Start by exporting the required values. Replace the example values with your own production values:

```bash
export STIPA_USER_ID="sti-pa-user"
export STIPA_PASSWORD="sti-pa-password"
export STIPA_SP_ID="818H"
export STIPA_SPC="818H"
export ACME_KID="peeringhub-kid"
export SHAKEN_SUBJECT_COUNTRY="US"
export SHAKEN_SUBJECT_STATE="TX"
export SHAKEN_SUBJECT_LOCALITY="Irving"
export SHAKEN_SUBJECT_ORGANIZATION="Example Telecom"
```

Create or verify the Peeringhub ACME account directory:

```bash
stir-shaken-toolkit peeringhub-account-setup --account-dir /var/lib/shaken/account
```

This creates or verifies two important files:

* `account.key`: the durable EC P-256 private key used for Peeringhub ACME account authentication.
* `account.json`: a recoverable cache of the Peeringhub ACME account URL.

For Peeringhub issuance, the same `account.key` is used for ACME request signing, the STI-PA SPC token fingerprint, the CSR public key, and the final certificate/private-key pair. Protect it like any other production private key.

Issue the certificate:

```bash
stir-shaken-toolkit peeringhub-issue --account-dir /var/lib/shaken/account
```

During issuance, the toolkit performs this flow:

1. Prepares or verifies the local Peeringhub ACME account.
2. Builds the TNAuthList value from your SPC.
3. Generates the fingerprint required for the STI-PA SPC token.
4. Requests and validates the STI-PA SPC token.
5. Creates a Peeringhub ACME order.
6. Submits the `tkauth-01` challenge.
7. Builds a CSR using the local ACME account key.
8. Finalizes the ACME order.
9. Downloads and validates the issued STIR/SHAKEN certificate chain.

By default, the command writes artifacts to a timestamped directory such as:

```
./shaken-cert-20260508T162900Z
```

The output directory contains:

* `csr.pem`: the CSR submitted to Peeringhub.
* `csr.der`: the DER form of the same CSR.
* `leaf.pem`: the issued subscriber certificate.
* `certificate-chain.pem`: the subscriber certificate followed by the Peeringhub intermediate certificate.
* `issuance.json`: issuance metadata, order URLs, certificate URLs, validation details, subject details, and the account key path used for the CSR.

The output directory does not contain a private key. The matching private key is the ACME account key. In most deployments, publish `certificate-chain.pem` at your public STIR/SHAKEN certificate URL and configure your signing system to use `account.key` as the private key. Do not publish `account.key`.

You can inspect the certificate and verify that the key matches:

```bash
stir-shaken-toolkit inspect --certificate ./shaken-cert-20260508T162900Z/leaf.pem
stir-shaken-toolkit inspect --certificate ./shaken-cert-20260508T162900Z/certificate-chain.pem --json
stir-shaken-toolkit validate-key-pair \
  --key /var/lib/shaken/account/account.key \
  --certificate ./shaken-cert-20260508T162900Z/leaf.pem
```

#### Option 2: Operate certificates with shaken-cert-manager

`shaken-cert-manager` is for servers that need ongoing certificate management. The manager owns a state directory, usually `/var/lib/shaken`, and keeps a safe operational layout:

```
/var/lib/shaken/
  account/
    account.key
    account.json
  archive/
    <generation_id>/
      csr.pem
      csr.der
      leaf.pem
      certificate-chain.pem
      manifest.json
  live/
    <generation_id>/
      leaf.pem -> ../../archive/<generation_id>/leaf.pem
      certificate-chain.pem -> ../../archive/<generation_id>/certificate-chain.pem
    current -> <generation_id>
  failed/
    <generation_id>/
  active.json
  last-attempt.json
```

The `archive` directory is the durable certificate history. The `live` directory exposes stable paths to currently usable generations. `live/current` points to the active generation, which makes it easy for a web server or deploy hook to publish the current `certificate-chain.pem` without changing application configuration every time a certificate is renewed.

Create a private manager config file:

```bash
cp shaken-cert-manager.example.yaml /etc/shaken-cert-manager.yaml
chmod 600 /etc/shaken-cert-manager.yaml
```

A minimal production config looks like this:

```yaml
enabled: true
peeringhub_environment: production
server_id: voice1

stipa_spc: 818H
stipa_sp_id: 818H
stipa_user_id: sti-pa-user
stipa_password: sti-pa-password
acme_kid: peeringhub-kid

shaken_subject_country: US
shaken_subject_state: TX
shaken_subject_locality: Irving
shaken_subject_organization: Example Telecom

state_dir: /var/lib/shaken
renew_before_days: 45
warning_days: 30
minimum_certificate_lifetime_days: 14

pre_activate_hook: /usr/local/sbin/shaken-pre-activate
deploy_hook: /usr/local/sbin/shaken-deploy
```

Create or verify the ACME account key first:

```bash
stir-shaken-toolkit peeringhub-account-setup --account-dir /var/lib/shaken/account
```

Issue the first certificate:

```bash
shaken-cert-manager --config /etc/shaken-cert-manager.yaml issue-initial
```

`issue-initial` only issues a certificate when no active usable certificate exists. If an active certificate is already present, it exits successfully without replacing it.

Check status:

```bash
shaken-cert-manager --config /etc/shaken-cert-manager.yaml status
shaken-cert-manager --config /etc/shaken-cert-manager.yaml status --json
shaken-cert-manager --config /etc/shaken-cert-manager.yaml status --nagios
```

Use `status --json` for local automation and `status --nagios` for monitoring systems. The Nagios output includes a status code and certificate days remaining, so your monitoring system can alert before the certificate becomes unsafe to use.

Run renewal from your scheduler:

```bash
shaken-cert-manager --config /etc/shaken-cert-manager.yaml renew
```

`renew` checks the active certificate first. It only issues a replacement when the certificate is inside the configured renewal window, when active state is critical, or when the active certificate needs replacement. For deliberate manual replacement, use:

```bash
shaken-cert-manager --config /etc/shaken-cert-manager.yaml force-renew
```

For non-interactive operations, add `--skip-confirm`:

```bash
shaken-cert-manager --config /etc/shaken-cert-manager.yaml force-renew --skip-confirm
```

Run cleanup periodically:

```bash
shaken-cert-manager --config /etc/shaken-cert-manager.yaml cleanup
```

Cleanup removes expired inactive archives, stale live links, and old failed transaction directories. It does not remove the active certificate generation.

#### Automating renewal

A simple cron job can run renewal twice per day:

```cron
0 0,12 * * * root /usr/local/bin/shaken-cert-manager --config /etc/shaken-cert-manager.yaml renew
```

For systemd hosts, use a timer instead. A systemd timer can add randomized delay, persistent catch-up after missed runs, and structured logs. Keep lifecycle hooks in the YAML config; the scheduler should only run `renew`.

#### How this connects to call signing

After issuance, the signing system needs two things:

* The private key, stored locally and readable only by the signing service. With Peeringhub issuance this is usually `/var/lib/shaken/account/account.key`.
* A public HTTPS URL that serves the active `certificate-chain.pem`, usually from `/var/lib/shaken/live/current/certificate-chain.pem` or a web-server path updated by the deploy hook.

The signing service uses the private key to sign PASSporT tokens and places the public certificate URL in the SIP `Identity` header as `x5u`. Downstream verifiers download the certificate chain from that URL and validate the signature. If the private key and certificate do not match, verification will fail.

#### Using the toolkit from Python code

Use the Python API when you need the issuance flow inside your own application instead of calling the CLI. This example shows the shape of a Peeringhub issuer:

```python
from pathlib import Path

from stir_shaken_acme import (
    ShakenCertificatePolicy,
    ShakenSubject,
    StipaSettings,
    TnAuthList,
)
from stir_shaken_toolkit.providers.peeringhub import (
    PeeringhubIssuer,
    PeeringhubProfile,
)

profile = PeeringhubProfile.for_environment("production")
tn_auth_list = TnAuthList("818H")

policy = ShakenCertificatePolicy(
    subject=ShakenSubject(
        country="US",
        state="TX",
        locality="Irving",
        organization="Example Telecom",
        common_name="SHAKEN 818H generation-1",
    ),
    tn_auth_list_der=tn_auth_list.der(),
    expected_crl_url=profile.stipa_crl_url,
    minimum_certificate_lifetime_days=21,
)

issuer = PeeringhubIssuer.build(
    profile=profile,
    account_key_path=Path("/var/lib/shaken/account/account.key"),
    account_state_path=Path("/var/lib/shaken/account/account.json"),
    acme_kid="peeringhub-kid",
    stipa_settings=StipaSettings(
        base_url=profile.stipa_base_url,
        user_id="sti-pa-user",
        password="sti-pa-password",
        sp_id="818H",
        expected_crl_url=profile.stipa_crl_url,
    ),
    certificate_policy=policy,
)

# Live issuance should only be run from an operator-controlled environment.
# Use the CLI or manager unless your application needs direct control.
```

{% hint style="info" %}
For one-time testing, start with `stir-shaken-toolkit`. For production operations, use `shaken-cert-manager` and schedule `renew` with monitoring on `status --nagios` or `status --json`.
{% endhint %}

>


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://doc.peeringhub.io/guides/generating-certificate/python-libraries.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
