Metadata-Version: 2.5
Name: sayfirstd
Version: 0.3.0
Summary: The operator surface of the open control plane's daemon
Project-URL: Repository, https://github.com/fredaime/sayfirst-control-plane
Project-URL: Documentation, https://github.com/fredaime/sayfirst-control-plane/blob/main/packages/cli/README.md
Project-URL: Changelog, https://github.com/fredaime/sayfirst-control-plane/blob/main/CHANGELOG.md
Author-email: fredaime <frederic.aime@gmail.com>
License-Expression: Apache-2.0
License-File: LICENSE
License-File: NOTICE
Classifier: Development Status :: 4 - Beta
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Requires-Python: <3.15,>=3.12
Requires-Dist: sayfirst-contract==0.3.0
Description-Content-Type: text/markdown

<!-- SPDX-License-Identifier: Apache-2.0 -->
# `sayfirstd` — the operator surface

This distribution is the command the operator of a daemon of this repository
types. It inspects; it starts nothing.

```console
sayfirstd plugins list --config /etc/sayfirst/plugins.toml
sayfirstd whoami --socket /run/sayfirst/daemon.sock --mode system --daemon-user sayfirst
sayfirstd status --socket /run/sayfirst/daemon.sock --mode system --daemon-user sayfirst
sayfirstd conformance replay --socket allow=/run/conformance/allow.sock
```

`whoami` and `status` take `--socket` as an option: given none, a per-user
profile is looked for at the per-user default address — the one a per-user
daemon given no address serves at, `sayfirst-daemon up --quickstart` included —
so `sayfirstd status` alone asks that daemon. A system profile always names its
socket and the account the daemon runs as, as the two lines above do for a
system daemon running as `sayfirst`. Without `--mode system --daemon-user`, a
profile is per-user: it expects the daemon to run as the account asking, and a
system daemon at that address is refused as `server_not_the_daemon_principal`
before a byte is sent (exit `4`).

## The three names

The distribution is `sayfirstd`, the import package is `sayfirstd`, and the
console script is `sayfirstd`. One name in all three forms, held by
`tests/test_client_distribution_names.py` against the `[project]` tables and
against the source tree.

The distribution `sayfirst-cli`, the import package `sayfirst_cli` and the
console script `sayfirst` were claimed here until 2026-09-05. Two repositories
claimed them, and the operator settled it: in all three forms they belong to the
product command-line interface, and are not published from this repository.
Nothing had been published under either claim (article 0), so the collision was
a fact about two source trees and is now closed in both.

`sayfirst` remains the project's public name wherever it names the *product*
— article 0, `TRADEMARKS.md`, the contract's attribute namespace, the sibling
distributions `sayfirst-contract`, `sayfirst-control-plane`,
`sayfirst-conformance` and `sayfirst-testing`. Only the three claims moved.

## Why `sayfirstd`

The conventional Unix shape: the daemon and the commands that inspect it share
one binary. Article 6's `whoami`, article 7's `status` and article 8's
`plugins list` all ask the daemon about itself, so they belong with it.

## What starts the daemon

Not this. `sayfirst-daemon serve`, from the `sayfirst-control-plane`
distribution, starts it. The two are not yet one binary because folding `serve`
in would make this distribution depend on the server it inspects, and article
14 points the dependency the other way: this package depends on
`sayfirst-contract` and on nothing else of this repository, which
`packages/cli/tests/test_plugins_list.py` holds. A remote operator forwards a
socket over SSH and inspects a daemon on a host where this wheel is the only
one installed (`docs/deployment.md`); a fold would put the whole server in that
install.

## What it answers, and what it forwards

`plugins list` is answered here: it reports configured selections against
discovered package metadata and against a recorded composition, and it claims no
provider is active that bootstrap would refuse. `whoami`, `status` and
`conformance replay` are handed whole to `sayfirst-contract`, which implements
them, so their parsers and exit codes stay in one place.

`status` reads the daemon's own account of itself over the same verified
connection `whoami` uses: the integrity grade of article 7 with its basis and
the interval it is re-evaluated on, the active privacy provider of article 11,
and whether evidence is being delivered. Each of those has a value for "the
daemon did not report it", and the command renders that value rather than a
plausible one (article 2).
