Skip to content

API reference

The public surface of apwlib: everything importable from the package root, plus the two blessed modules apwlib.pinwindow and apwlib.diagnostics. Generated from the source.

The facade

ApplePasswords

ApplePasswords(
    socket_path: str | Path | None = None,
    auto_start: bool = True,
    pin_provider: Callable[[], str] | None = None,
)

Read and write Apple Passwords (iCloud Keychain) via a managed daemon.

The daemon (a headless browser hosting the iCloud Passwords extension) is auto-started on first use as a detached singleton and reused thereafter; use Daemon for explicit lifecycle control. Pairing needs a one-time PIN: if pin_provider is given, an unpaired request transparently pairs (pops the macOS dialog, calls pin_provider(), retries); otherwise it raises NotPairedError.

Pass auto_start=False to require an already-running daemon.

get_password

get_password(
    url: str, username: str | None = None
) -> list[PasswordEntry]

Password entries for a site, optionally restricted to username.

save_password

save_password(
    url: str, username: str, password: str
) -> None

Create or update a credential.

get_otp

get_otp(url: str) -> list[OTPEntry]

The current one-time code(s) for a site.

list_otp

list_otp(url: str) -> list[OTPEntry]

The accounts that have one-time codes for a site (no codes).

Daemon control

Daemon

Daemon(
    socket_path: str | Path | None = None,
    auto_start: bool = True,
)

The connection to the background daemon: transport, lifecycle, and pairing.

ApplePasswords manages one internally (auto-start, auto-pair); construct your own only for explicit lifecycle control — it backs apwcli daemon start/stop/status/pair.

log_path property

log_path: Path

Where the detached daemon writes its log.

start

start(browser: str | None = None) -> None

Ensure a daemon is running with its bridge connected.

A no-op when one is already reachable — except with an explicit browser, which needs a fresh daemon and raises instead (use restart). Otherwise spawns one — after waiting out a stopping daemon's singleton lock, so stop immediately followed by start works. browser applies to that daemon only (it is not persisted). Raises DaemonStartError if the bridge does not come up, and ApwError when no supported browser is installed.

stop

stop() -> bool

Ask a running daemon to shut down. Returns False if none was running.

restart

restart(browser: str | None = None) -> None

Stop any running daemon and start a fresh one (raises like start).

status

status() -> DaemonStatus

Report daemon reachability, bridge connectivity, and pairing (does not auto-start).

When running, also reports browser (the managed browser's name) and browser_pid (its process id); both are None otherwise.

request_challenge

request_challenge() -> None

Ask the extension to display the macOS pairing PIN.

Returns once the handshake is ready for the PIN, so verify_challenge can be called immediately. Raises if the daemon/extension can't pair at all.

verify_challenge

verify_challenge(pin: str) -> bool

Submit the PIN and block until pairing settles; True once paired.

False means the helper rejected the PIN (reported the moment the handshake collapses) or the attempt timed out daemon-side.

DaemonStatus

Bases: TypedDict

The shape of Daemon.status; keeps its two return paths in sync.

Entries

PasswordEntry dataclass

PasswordEntry(
    username: str,
    domain: str,
    password: str | None = None,
    title: str | None = None,
    sites: list[str] = list(),
    high_level_domain: str | None = None,
)

OTPEntry dataclass

OTPEntry(
    username: str,
    domain: str,
    code: str | None = None,
    source: str | None = None,
)

PIN window

request_pin

request_pin(
    timeout: float = _TIMEOUT, css: str | None = None
) -> str

Pop the PIN window and return the six digits the user typed.

css replaces the window's stylesheet (falling back to ~/.apwlib/pinwindow.css, then the bundled default). Raises NotPairedError when no supported browser is installed, the window is closed without a code, or nobody answers within timeout.

Diagnostics

run_checks

run_checks(daemon: Daemon | None = None) -> list[Check]

Diagnose the setup chain; does not auto-start the daemon.

Check dataclass

Check(
    key: str,
    ok: bool,
    required: bool,
    detail: str,
    hint: str = "",
)

One diagnostic result. hint says how to fix it (empty when ok).

Errors

ApwError

ApwError(status: Status | int, message: str | None = None)

Bases: Exception

Base error carrying a protocol Status.

SessionError

SessionError(message: str | None = None)

Bases: ApwError

The daemon is not running or the session is not paired.

DaemonNotRunningError

DaemonNotRunningError(message: str | None = None)

Bases: SessionError

No daemon is reachable on the socket (recoverable by starting one).

NotPairedError

NotPairedError(message: str | None = None)

Bases: SessionError

The daemon and extension are up, but no PIN pairing has been completed.

DaemonStartError

DaemonStartError(message: str | None = None)

Bases: ApwError

A daemon was spawned but did not become ready (browser/extension trouble).

ServerError

ServerError(message: str | None = None)

Bases: ApwError

The extension returned an unexpected response.

Status

Bases: IntEnum