Skip to content

wasat.certs

Client certificate generation and storage management for Gemini connections.

ClientCertCallback

ClientCertCallback = Callable[
    [AnyURI, ClientCertificateStore],
    Coroutine[
        None,
        None,
        Literal["transient", "persistent", "ignore"],
    ],
]

Async callback function signature for resolving a client certificate requirement.

This callback is invoked when a client certificate is required. It receives the requested URI (GeminiURI or TitanURI) and the ClientCertificateStore instance to query or update.

ClientCertificate

ClientCertificate(
    cert_pem: bytes,
    *,
    key_pem: bytes | None = None,
    cert_path: Path | None = None,
    key_path: Path | None = None,
    scopes: tuple[str, ...] | list[str] = (),
)

Representation of a client TLS X.509 certificate and private key pair.

Parameters:

Name Type Description Default

cert_pem

bytes

The raw PEM-encoded certificate bytes.

required

key_pem

bytes | None

Optional raw PEM-encoded private key bytes.

None

cert_path

Path | None

Optional filesystem path to the certificate PEM file.

None

key_path

Path | None

Optional filesystem path to the private key PEM file.

None

scopes

tuple[str, ...] | list[str]

Tuple or list of Gemini scopes associated with this certificate.

()

cert_path property

cert_path: Path | None

The filesystem path to the certificate PEM file, or None if in-memory.

cert_pem property

cert_pem: bytes

The raw PEM-encoded certificate bytes.

country property

country: str | None

The two-letter country code from the certificate subject, or None.

email property

email: str | None

The email address from the certificate subject, or None if not present.

fingerprint property

fingerprint: str

The hex-encoded SHA-256 fingerprint of the certificate.

is_expired property

is_expired: bool

Whether the certificate is currently expired.

is_self_signed property

is_self_signed: bool

Whether the certificate is self-signed (subject equals issuer).

issuer property

issuer: str

The certificate issuer formatted as an RFC 4514 string.

issuer_common_name property

issuer_common_name: str | None

The Common Name (CN) from the certificate issuer, or None if not present.

key_path property

key_path: Path | None

The filesystem path to the private key PEM file, or None if in-memory.

key_pem property

key_pem: bytes | None

The raw PEM-encoded private key bytes, or None if not available.

key_size property

key_size: int | None

The key size in bits (for RSA) or curve size (for ECDSA), or None.

key_type property

key_type: str

The type of public key in the certificate ('ecdsa', 'rsa', or 'unknown').

not_after property

not_after: datetime

The UTC timestamp at which the certificate expires.

not_before property

not_before: datetime

The UTC timestamp from which the certificate is valid.

organisation property

organisation: str | None

The organisation name from the certificate subject, or None if not present.

raw_pem property

raw_pem: bytes

The raw PEM-encoded certificate bytes.

raw_x509 property

raw_x509: Certificate

The underlying cryptography X.509 Certificate instance.

scopes property

scopes: tuple[str, ...]

The tuple of Gemini scopes associated with this certificate.

serial_number property

serial_number: int

The certificate serial number.

subject property

subject: str

The certificate subject formatted as an RFC 4514 string.

subject_alternative_names property

subject_alternative_names: tuple[str, ...]

Tuple of Subject Alternative Names (DNS names) in the certificate.

subject_common_name property

subject_common_name: str | None

The Common Name (CN) from the certificate subject, or None if not present.

user_id property

user_id: str | None

The user ID from the certificate subject, or None if not present.

__repr__

__repr__() -> str

Representation of the ClientCertificate instance.

export

export(
    target_path: str | Path,
    *,
    key_path: str | Path | None = None,
    combined: bool = False,
) -> tuple[Path, Path | None]

Export certificate and private key to disk with safe file permissions.

When exporting private keys, files are written with restricted permissions (0600).

Parameters:

Name Type Description Default

target_path

str | Path

File or directory destination for the exported certificate.

required

key_path

str | Path | None

Optional specific file destination for the private key.

None

combined

bool

If True, writes certificate and key into a single combined PEM file.

False

Returns:

Type Description
tuple[Path, Path | None]

A tuple of (cert_path, key_path) representing the exported files.

Raises:

Type Description
ValueError

If combined is True and key_path is also specified.

OSError

If creating directories or writing files fails.

from_file classmethod

from_file(
    cert_path: str | Path,
    key_path: str | Path | None = None,
    scopes: Sequence[str | AnyURI] = (),
) -> ClientCertificate

Construct a ClientCertificate instance from PEM files on disk.

If key_path is omitted, cert_path is checked for an embedded private key bundle in addition to the certificate.

Parameters:

Name Type Description Default

cert_path

str | Path

Path to the certificate PEM file (or combined bundle).

required

key_path

str | Path | None

Optional path to the private key PEM file.

None

scopes

Sequence[str | AnyURI]

Sequence of Gemini/Titan scopes or URIs associated with this certificate.

()

Returns:

Type Description
ClientCertificate

A new ClientCertificate instance.

Raises:

Type Description
FileNotFoundError

If cert_path or key_path does not exist.

ValueError

If no valid certificate block can be parsed.

from_pem classmethod

from_pem(
    cert_pem: bytes | str,
    *,
    key_pem: bytes | str | None = None,
    scopes: Sequence[str | AnyURI] = (),
) -> ClientCertificate

Construct a ClientCertificate instance from PEM bytes or text.

Parameters:

Name Type Description Default

cert_pem

bytes | str

PEM-encoded certificate bytes or string (can be a combined certificate and private key bundle).

required

key_pem

bytes | str | None

Optional separate PEM-encoded private key bytes or string.

None

scopes

Sequence[str | AnyURI]

Sequence of Gemini/Titan scopes or URIs associated with this certificate.

()

Returns:

Type Description
ClientCertificate

A new ClientCertificate instance.

Raises:

Type Description
ValueError

If no valid certificate block is present in the input.

to_combined_pem

to_combined_pem() -> bytes

Combine certificate and private key into a single PEM byte sequence.

Returns:

Type Description
bytes

PEM-encoded bytes containing the certificate and, if available,

bytes

the private key.

ClientCertificateStore

Bases: Protocol

Protocol defining the interface for client certificate storage and retrieval.

associate_scope async

associate_scope(
    identifier: str | Path | AnyURI | ClientCertificate,
    scope_or_uri: str | AnyURI,
) -> None

Associate an existing certificate in the store with an additional scope or URI.

Parameters:

Name Type Description Default

identifier

str | Path | AnyURI | ClientCertificate

Target certificate reference (ClientCertificate, URI, fingerprint, or file path/name).

required

scope_or_uri

str | AnyURI

The scope string or URI to associate.

required

Raises:

Type Description
ValueError

If the certificate cannot be identified in the store.

RuntimeError

If updating the store index fails.

close async

close() -> None

Close the store, cleaning up transient resources if necessary.

create_certificate async

create_certificate(
    name: str,
    *,
    scopes: Sequence[str | AnyURI] = (),
    transient: bool = False,
    common_name: str | None = None,
    valid_days: int | None = 365,
    key_type: Literal["ecdsa", "rsa"] = "ecdsa",
    rsa_key_size: int = 2048,
    ecdsa_curve: str = "secp256r1",
    email: str | None = None,
    user_id: str | None = None,
    domain: str | None = None,
    organisation: str | None = None,
    country: str | None = None,
) -> ClientCertificate

Generate and save a new client certificate and private key.

Parameters:

Name Type Description Default

name

str

Base name for the certificate file.

required

scopes

Sequence[str | AnyURI]

Sequence of Gemini/Titan scopes or URIs to associate with the certificate.

()

transient

bool

If True, the certificate is generated in a temporary directory and not registered in the persistent store.

False

common_name

str | None

The Common Name (CN) for the certificate. Defaults to name.

None

valid_days

int | None

Number of days the certificate should be valid. If None, the certificate will expire on 9999-12-31.

365

key_type

Literal['ecdsa', 'rsa']

The key type to generate ('ecdsa' or 'rsa').

'ecdsa'

rsa_key_size

int

RSA key size in bits.

2048

ecdsa_curve

str

ECDSA curve name.

'secp256r1'

email

str | None

Optional email address.

None

user_id

str | None

Optional user identifier.

None

domain

str | None

Optional domain name for Subject Alternative Name.

None

organisation

str | None

Optional organisation name.

None

country

str | None

Optional two-letter country code.

None

Returns:

Type Description
ClientCertificate

The created ClientCertificate instance.

Raises:

Type Description
ValueError

If the key type is unsupported, the RSA key size is not one of 2048, 3072, or 4096, the ECDSA curve is not 'secp256r1' or 'secp384r1', or the country code is not exactly two characters.

OSError

If creating directories or writing the certificate or key file to disk fails.

RuntimeError

If saving the credentials or updating the store index fails.

create_credentials async

create_credentials(
    uri: AnyURI,
    *,
    transient: bool = False,
    common_name: str | None = None,
    valid_days: int | None = 365,
    key_type: Literal["ecdsa", "rsa"] = "ecdsa",
    rsa_key_size: int = 2048,
    ecdsa_curve: str = "secp256r1",
    email: str | None = None,
    user_id: str | None = None,
    domain: str | None = None,
    organisation: str | None = None,
    country: str | None = None,
) -> tuple[Path, Path]

Generate and save a new self-signed client certificate and private key.

Parameters:

Name Type Description Default

uri

AnyURI

The target URI (GeminiURI or TitanURI).

required

transient

bool

If True, the certificate is generated in a temporary directory and not registered in the persistent store.

False

common_name

str | None

The Common Name (CN) for the certificate. Defaults to the host.

None

valid_days

int | None

Number of days the certificate should be valid. If None, the certificate will expire on 9999-12-31.

365

key_type

Literal['ecdsa', 'rsa']

The key type to generate ('ecdsa' or 'rsa').

'ecdsa'

rsa_key_size

int

RSA key size in bits.

2048

ecdsa_curve

str

ECDSA curve name.

'secp256r1'

email

str | None

Optional email address.

None

user_id

str | None

Optional user identifier.

None

domain

str | None

Optional domain name for Subject Alternative Name.

None

organisation

str | None

Optional organisation name.

None

country

str | None

Optional two-letter country code.

None

Returns:

Type Description
tuple[Path, Path]

A tuple of (cert_path, key_path) representing the generated certificate and key.

Raises:

Type Description
ValueError

If the key type is unsupported, the RSA key size is not one of 2048, 3072, or 4096, the ECDSA curve is not 'secp256r1' or 'secp384r1', or the country code is not exactly two characters.

OSError

If creating directories or writing the certificate or key file to disk fails.

RuntimeError

If saving the credentials or updating the store index fails.

delete_certificate async

delete_certificate(
    identifier: str | Path | AnyURI | ClientCertificate,
) -> bool

Delete a client certificate and its private key, removing all associated scopes.

Parameters:

Name Type Description Default

identifier

str | Path | AnyURI | ClientCertificate

Target certificate reference (ClientCertificate, URI, fingerprint, or file path/name).

required

Returns:

Type Description
bool

True if the certificate was found and deleted, False otherwise.

Raises:

Type Description
RuntimeError

If updating the store index fails.

delete_credentials async

delete_credentials(uri: AnyURI) -> bool

Delete the certificate and key associated with the matching scope.

Parameters:

Name Type Description Default

uri

AnyURI

The target URI (GeminiURI or TitanURI).

required

Returns:

Type Description
bool

True if deleted, False if no matching scope was found.

Raises:

Type Description
RuntimeError

If updating the store index fails.

delete_exact_scope async

delete_exact_scope(scope_or_uri: str | AnyURI) -> bool

Delete the exact scope association, removing the certificate files if unused.

Parameters:

Name Type Description Default

scope_or_uri

str | AnyURI

The scope string or URI to delete.

required

Returns:

Type Description
bool

True if the scope was found and deleted, False otherwise.

Raises:

Type Description
RuntimeError

If updating the store index fails.

disassociate_scope async

disassociate_scope(scope_or_uri: str | AnyURI) -> bool

Disassociate a scope or URI without deleting the certificate files.

Parameters:

Name Type Description Default

scope_or_uri

str | AnyURI

The scope string or URI to disassociate.

required

Returns:

Type Description
bool

True if an association was removed, False if no matching scope was registered.

Raises:

Type Description
RuntimeError

If updating the store index fails.

export_certificate async

export_certificate(
    identifier: str | Path | AnyURI | ClientCertificate,
    target_path: str | Path,
    *,
    key_path: str | Path | None = None,
    combined: bool = False,
) -> tuple[Path, Path | None]

Export a stored client certificate and its private key to disk.

Parameters:

Name Type Description Default

identifier

str | Path | AnyURI | ClientCertificate

Target certificate reference (ClientCertificate, URI, fingerprint, or file path/name).

required

target_path

str | Path

File or directory destination for the exported certificate.

required

key_path

str | Path | None

Optional specific file destination for the private key.

None

combined

bool

If True, exports certificate and key into a single combined PEM file.

False

Returns:

Type Description
tuple[Path, Path | None]

A tuple of (cert_path, key_path) representing the exported files.

Raises:

Type Description
ValueError

If the certificate cannot be found in the store, or if combined is True and key_path is specified.

OSError

If creating directories or writing files fails.

get_certificate async

get_certificate(
    identifier: str | Path | AnyURI,
) -> ClientCertificate | None

Retrieve a client certificate by scope, URI, fingerprint, or filename.

Parameters:

Name Type Description Default

identifier

str | Path | AnyURI

A URI (GeminiURI or TitanURI), scope string, SHA-256 fingerprint, or certificate file path/name.

required

Returns:

Type Description
ClientCertificate | None

The matching ClientCertificate instance, or None if not found.

get_credentials async

get_credentials(uri: AnyURI) -> tuple[Path, Path] | None

Retrieve the certificate and private key paths matching the given URI.

This should perform path prefix matching to find the most specific matching certificate for the requested host, port, and path.

Parameters:

Name Type Description Default

uri

AnyURI

The target URI (GeminiURI or TitanURI).

required

Returns:

Type Description
tuple[Path, Path] | None

A tuple of (cert_path, key_path) or None if no certificate is stored

tuple[Path, Path] | None

for this URI's scope.

has_exact_credentials async

has_exact_credentials(uri: AnyURI) -> bool

Check if a certificate exists for the exact scope of the URI.

This checks if a certificate has been registered specifically for the exact host, port, and path of the given URI (without traversing parent scopes).

Parameters:

Name Type Description Default

uri

AnyURI

The target URI (GeminiURI or TitanURI).

required

Returns:

Type Description
bool

True if a certificate is registered for this exact scope, False otherwise.

import_certificate async

import_certificate(
    source: str | Path | bytes | ClientCertificate,
    *,
    key_source: str | Path | bytes | None = None,
    name: str | None = None,
    scopes: Sequence[str | AnyURI] = (),
    transient: bool = False,
) -> ClientCertificate

Import an existing client certificate and key into the store.

Parameters:

Name Type Description Default

source

str | Path | bytes | ClientCertificate

File path, PEM string/bytes, or ClientCertificate instance.

required

key_source

str | Path | bytes | None

Optional separate file path or PEM string/bytes for the private key.

None

name

str | None

Optional base name for the imported certificate files in the store. If omitted, derived from the certificate common name, fingerprint, or source filename.

None

scopes

Sequence[str | AnyURI]

Optional sequence of Gemini/Titan scopes or URIs to associate with the imported certificate.

()

transient

bool

If True, imports the certificate into transient storage.

False

Returns:

Type Description
ClientCertificate

The imported ClientCertificate instance.

Raises:

Type Description
ValueError

If the certificate cannot be parsed.

FileNotFoundError

If a specified file path does not exist.

OSError

If reading or writing files fails.

RuntimeError

If saving the store index fails.

list_certificates async

list_certificates() -> list[ClientCertificate]

List all client certificates stored in this certificate store.

Returns:

Type Description
list[ClientCertificate]

A list of ClientCertificate instances.

register_credentials async

register_credentials(
    uri: AnyURI,
    cert_path: str | Path,
    key_path: str | Path,
    *,
    transient: bool = False,
) -> None

Register existing certificate and private key paths for the URI's scope.

Parameters:

Name Type Description Default

uri

AnyURI

The target URI (GeminiURI or TitanURI).

required

cert_path

str | Path

Path to the existing client certificate.

required

key_path

str | Path

Path to the existing private key.

required

transient

bool

If True, registers as transient.

False

Raises:

Type Description
FileNotFoundError

If the registry is persistent, and the source files do not exist at the specified paths.

OSError

If copying the certificate or private key files fails, or if creating the persistent store directory fails.

RuntimeError

If registering or persisting the credentials in the store index fails.

FileClientCertificateStore

FileClientCertificateStore(store_dir: str | Path)

Bases: ClientCertificateStore

File-based client certificate and key store.

Saves certificate files as PEM pairs and maintains a certs.json registry file mapping Gemini scopes (host[:port]/path) to certificate filenames.

Parameters:

Name Type Description Default

store_dir

str | Path

The directory where certificates, keys, and the index are stored.

required

store_dir instance-attribute

store_dir = Path(store_dir)

The directory path for storing certificates and index.

associate_scope async

associate_scope(
    identifier: str | Path | AnyURI | ClientCertificate,
    scope_or_uri: str | AnyURI,
) -> None

Associate an existing certificate in the store with an additional scope or URI.

Parameters:

Name Type Description Default

identifier

str | Path | AnyURI | ClientCertificate

Target certificate reference (ClientCertificate, URI, fingerprint, or file path/name).

required

scope_or_uri

str | AnyURI

The scope string or URI to associate.

required

Raises:

Type Description
ValueError

If the certificate cannot be identified in the store.

RuntimeError

If updating the store index fails.

close async

close() -> None

Close the store, cleaning up transient directories if any were created.

create_certificate async

create_certificate(
    name: str,
    *,
    scopes: Sequence[str | AnyURI] = (),
    transient: bool = False,
    common_name: str | None = None,
    valid_days: int | None = 365,
    key_type: Literal["ecdsa", "rsa"] = "ecdsa",
    rsa_key_size: int = 2048,
    ecdsa_curve: str = "secp256r1",
    email: str | None = None,
    user_id: str | None = None,
    domain: str | None = None,
    organisation: str | None = None,
    country: str | None = None,
) -> ClientCertificate

Generate and save a new client certificate and private key.

Parameters:

Name Type Description Default

name

str

Base name for the certificate file.

required

scopes

Sequence[str | AnyURI]

Sequence of Gemini scopes or URIs to associate with the certificate.

()

transient

bool

If True, the certificate is generated in a temporary directory and not registered in the persistent store.

False

common_name

str | None

The Common Name (CN) for the certificate. Defaults to name.

None

valid_days

int | None

Number of days the certificate should be valid. If None, the certificate will expire on 9999-12-31.

365

key_type

Literal['ecdsa', 'rsa']

The key type to generate ('ecdsa' or 'rsa').

'ecdsa'

rsa_key_size

int

RSA key size in bits.

2048

ecdsa_curve

str

ECDSA curve name.

'secp256r1'

email

str | None

Optional email address.

None

user_id

str | None

Optional user identifier.

None

domain

str | None

Optional domain name for Subject Alternative Name.

None

organisation

str | None

Optional organisation name.

None

country

str | None

Optional two-letter country code.

None

Returns:

Type Description
ClientCertificate

The created ClientCertificate instance.

Raises:

Type Description
ValueError

If the key type is unsupported, the RSA key size is not one of 2048, 3072, or 4096, the ECDSA curve is not 'secp256r1' or 'secp384r1', or the country code is not exactly two characters.

OSError

If creating directories or writing the certificate or key file to disk fails.

RuntimeError

If saving the credentials or updating the store index fails.

create_credentials async

create_credentials(
    uri: AnyURI,
    *,
    transient: bool = False,
    common_name: str | None = None,
    valid_days: int | None = 365,
    key_type: Literal["ecdsa", "rsa"] = "ecdsa",
    rsa_key_size: int = 2048,
    ecdsa_curve: str = "secp256r1",
    email: str | None = None,
    user_id: str | None = None,
    domain: str | None = None,
    organisation: str | None = None,
    country: str | None = None,
) -> tuple[Path, Path]

Generate and save a new self-signed client certificate and private key.

Parameters:

Name Type Description Default

uri

AnyURI

The target URI (GeminiURI or TitanURI).

required

transient

bool

If True, the certificate is generated in a temporary directory and not registered in the persistent store.

False

common_name

str | None

The Common Name (CN) for the certificate. Defaults to the host.

None

valid_days

int | None

Number of days the certificate should be valid. If None, the certificate will expire on 9999-12-31.

365

key_type

Literal['ecdsa', 'rsa']

The key type to generate ('ecdsa' or 'rsa').

'ecdsa'

rsa_key_size

int

RSA key size in bits.

2048

ecdsa_curve

str

ECDSA curve name.

'secp256r1'

email

str | None

Optional email address.

None

user_id

str | None

Optional user identifier.

None

domain

str | None

Optional domain name for Subject Alternative Name.

None

organisation

str | None

Optional organisation name.

None

country

str | None

Optional two-letter country code.

None

Returns:

Type Description
tuple[Path, Path]

A tuple of (cert_path, key_path) representing the generated certificate and key.

Raises:

Type Description
ValueError

If the key type is unsupported, the RSA key size is not one of 2048, 3072, or 4096, the ECDSA curve is not 'secp256r1' or 'secp384r1', or the country code is not exactly two characters.

OSError

If creating the persistent store directory or the temporary transient directory fails, or if writing the certificate or key file to disk fails.

RuntimeError

If saving the updated index (certs.json) file to disk fails.

delete_certificate async

delete_certificate(
    identifier: str | Path | AnyURI | ClientCertificate,
) -> bool

Delete a client certificate and its private key, removing all associated scopes.

Parameters:

Name Type Description Default

identifier

str | Path | AnyURI | ClientCertificate

Target certificate reference (ClientCertificate, URI, fingerprint, or file path/name).

required

Returns:

Type Description
bool

True if the certificate was found and deleted, False otherwise.

Raises:

Type Description
RuntimeError

If updating the store index fails.

delete_credentials async

delete_credentials(uri: AnyURI) -> bool

Delete the certificate and key associated with the matching scope.

Parameters:

Name Type Description Default

uri

AnyURI

The target URI (GeminiURI or TitanURI).

required

Returns:

Type Description
bool

True if deleted, False if no matching scope was found.

Raises:

Type Description
RuntimeError

If saving the updated index (certs.json) file to disk fails.

delete_exact_scope async

delete_exact_scope(scope_or_uri: str | AnyURI) -> bool

Delete the exact scope association, removing the certificate files if unused.

Parameters:

Name Type Description Default

scope_or_uri

str | AnyURI

The scope string or URI to delete.

required

Returns:

Type Description
bool

True if the scope was found and deleted, False otherwise.

Raises:

Type Description
RuntimeError

If updating the store index fails.

disassociate_scope async

disassociate_scope(scope_or_uri: str | AnyURI) -> bool

Disassociate a scope or URI without deleting the certificate files.

Parameters:

Name Type Description Default

scope_or_uri

str | AnyURI

The scope string or URI to disassociate.

required

Returns:

Type Description
bool

True if an association was removed, False if no matching scope was registered.

Raises:

Type Description
RuntimeError

If updating the store index fails.

export_certificate async

export_certificate(
    identifier: str | Path | AnyURI | ClientCertificate,
    target_path: str | Path,
    *,
    key_path: str | Path | None = None,
    combined: bool = False,
) -> tuple[Path, Path | None]

Export a stored client certificate and its private key to disk.

Parameters:

Name Type Description Default

identifier

str | Path | AnyURI | ClientCertificate

Target certificate reference (ClientCertificate, URI, fingerprint, or file path/name).

required

target_path

str | Path

File or directory destination for the exported certificate.

required

key_path

str | Path | None

Optional specific file destination for the private key.

None

combined

bool

If True, exports certificate and key into a single combined PEM file.

False

Returns:

Type Description
tuple[Path, Path | None]

A tuple of (cert_path, key_path) representing the exported files.

Raises:

Type Description
ValueError

If the certificate cannot be found in the store, or if combined is True and key_path is specified.

OSError

If creating directories or writing files fails.

get_certificate async

get_certificate(
    identifier: str | Path | AnyURI,
) -> ClientCertificate | None

Retrieve a client certificate by scope, URI, fingerprint, or filename.

Parameters:

Name Type Description Default

identifier

str | Path | AnyURI

A URI (GeminiURI or TitanURI), scope string, SHA-256 fingerprint, or certificate file path/name.

required

Returns:

Type Description
ClientCertificate | None

The matching ClientCertificate instance, or None if not found.

get_credentials async

get_credentials(uri: AnyURI) -> tuple[Path, Path] | None

Retrieve the certificate and private key paths matching the given URI.

Parameters:

Name Type Description Default

uri

AnyURI

The target URI (GeminiURI or TitanURI).

required

Returns:

Type Description
tuple[Path, Path] | None

A tuple of (cert_path, key_path) or None if no certificate is stored

tuple[Path, Path] | None

for this URI's scope.

has_exact_credentials async

has_exact_credentials(uri: AnyURI) -> bool

Check if a certificate exists for the exact scope of the URI.

This checks if a certificate has been registered specifically for the exact host, port, and path of the given URI (without traversing parent scopes).

Parameters:

Name Type Description Default

uri

AnyURI

The target URI (GeminiURI or TitanURI).

required

Returns:

Type Description
bool

True if a certificate is registered for this exact scope, False otherwise.

import_certificate async

import_certificate(
    source: str | Path | bytes | ClientCertificate,
    *,
    key_source: str | Path | bytes | None = None,
    name: str | None = None,
    scopes: Sequence[str | AnyURI] = (),
    transient: bool = False,
) -> ClientCertificate

Import an existing client certificate and key into the store.

Parameters:

Name Type Description Default

source

str | Path | bytes | ClientCertificate

File path, PEM string/bytes, or ClientCertificate instance.

required

key_source

str | Path | bytes | None

Optional separate file path or PEM string/bytes for the private key.

None

name

str | None

Optional base name for the imported certificate files in the store. If omitted, derived from the certificate common name, fingerprint, or source filename.

None

scopes

Sequence[str | AnyURI]

Optional sequence of Gemini/Titan scopes or URIs to associate with the imported certificate.

()

transient

bool

If True, imports the certificate into transient storage.

False

Returns:

Type Description
ClientCertificate

The imported ClientCertificate instance.

Raises:

Type Description
ValueError

If the certificate cannot be parsed.

FileNotFoundError

If a specified file path does not exist.

OSError

If reading or writing files fails.

RuntimeError

If saving the store index fails.

list_certificates async

list_certificates() -> list[ClientCertificate]

List all client certificates stored in this certificate store.

Returns:

Type Description
list[ClientCertificate]

A list of ClientCertificate instances.

register_credentials async

register_credentials(
    uri: AnyURI,
    cert_path: str | Path,
    key_path: str | Path,
    *,
    transient: bool = False,
) -> None

Register existing certificate and private key paths for the URI's scope.

Parameters:

Name Type Description Default

uri

AnyURI

The target URI (GeminiURI or TitanURI).

required

cert_path

str | Path

Path to the existing client certificate.

required

key_path

str | Path

Path to the existing private key.

required

transient

bool

If True, registers as transient.

False

Raises:

Type Description
FileNotFoundError

If transient is False, and the source certificate file or private key file does not exist at the specified paths.

OSError

If transient is False, and copying the certificate or private key files fails, or if creating the persistent store directory fails.

RuntimeError

If transient is False, and saving the updated index (certs.json) file to disk fails.

ServerCertificate

ServerCertificate(raw_der: bytes)

Representation of a server TLS X.509 certificate.

Parameters:

Name Type Description Default

raw_der

bytes

The raw DER-encoded certificate bytes.

required

fingerprint property

fingerprint: str

The hex-encoded SHA-256 fingerprint of the certificate.

is_expired property

is_expired: bool

Whether the certificate is currently expired.

is_self_signed property

is_self_signed: bool

Whether the certificate is self-signed (subject equals issuer).

issuer property

issuer: str

The certificate issuer formatted as an RFC 4514 string.

issuer_common_name property

issuer_common_name: str | None

The Common Name (CN) from the certificate issuer, or None if not present.

not_after property

not_after: datetime

The UTC timestamp at which the certificate expires.

not_before property

not_before: datetime

The UTC timestamp from which the certificate is valid.

raw_der property

raw_der: bytes

The raw DER-encoded certificate bytes.

raw_x509 property

raw_x509: Certificate

The underlying cryptography X.509 Certificate instance.

serial_number property

serial_number: int

The certificate serial number.

subject property

subject: str

The certificate subject formatted as an RFC 4514 string.

subject_alternative_names property

subject_alternative_names: tuple[str, ...]

Tuple of Subject Alternative Names (DNS names) in the certificate.

subject_common_name property

subject_common_name: str | None

The Common Name (CN) from the certificate subject, or None if not present.

from_der classmethod

Construct a ServerCertificate instance from raw DER bytes.

Parameters:

Name Type Description Default

cert_der

bytes

The raw DER-encoded certificate bytes.

required

Returns:

Type Description
ServerCertificate

A new ServerCertificate instance.

generate_self_signed_cert

generate_self_signed_cert(
    common_name: str,
    *,
    key_type: Literal["ecdsa", "rsa"] = "ecdsa",
    rsa_key_size: int = 2048,
    ecdsa_curve: str = "secp256r1",
    valid_days: int | None = 365,
    email: str | None = None,
    user_id: str | None = None,
    domain: str | None = None,
    organisation: str | None = None,
    country: str | None = None,
) -> tuple[bytes, bytes]

Generate a self-signed client certificate and private key.

Parameters:

Name Type Description Default

common_name

str

The Common Name (CN) for the certificate.

required

key_type

Literal['ecdsa', 'rsa']

The key type to generate ('ecdsa' or 'rsa').

'ecdsa'

rsa_key_size

int

RSA key size in bits.

2048

ecdsa_curve

str

ECDSA curve name.

'secp256r1'

valid_days

int | None

Certificate validity in days. If None, the certificate will expire on 9999-12-31.

365

email

str | None

Optional email address.

None

user_id

str | None

Optional user identifier.

None

domain

str | None

Optional domain name for Subject Alternative Name.

None

organisation

str | None

Optional organisation name.

None

country

str | None

Optional two-letter country code.

None

Returns:

Type Description
tuple[bytes, bytes]

A tuple containing (cert_pem, key_pem) as bytes.

Raises:

Type Description
ValueError

If the key type is unsupported, the RSA key size is not one of 2048, 3072, or 4096, the ECDSA curve is not 'secp256r1' or 'secp384r1', or the country code is not exactly two characters.

get_candidate_scopes

get_candidate_scopes(uri: AnyURI) -> list[str]

Get candidate certificate scopes for a URI, sorted by specificity.

Parameters:

Name Type Description Default

uri

AnyURI

The URI (GeminiURI or TitanURI) to generate scopes for.

required

Returns:

Type Description
list[str]

A list of scope strings in descending order of specificity.

normalize_scope

normalize_scope(scope_or_uri: str | AnyURI) -> str

Normalise a GeminiURI, TitanURI, or scope string into a canonical scope string.

Parameters:

Name Type Description Default

scope_or_uri

str | AnyURI

A GeminiURI or TitanURI instance, or a scope string (e.g. 'example.com/path' or 'gemini://example.com:1965/path' or 'titan://example.com/path;size=10').

required

Returns:

Type Description
str

The canonical scope string in 'host:port/path' format.