# Users guide
This guide is for contributors and operators who need to understand the
user-visible behaviour of repovec-appliance and its repository automation. It
focuses on what a user can expect to happen, not on the internal crate layout.
## Documentation gate decisions
When a change reaches continuous integration (CI), the workflow decides whether
documentation validation is required and whether Mermaid diagram validation
should also run. The decision is based on the changed-file list, whether any
documentation-tooling configuration changed, and, for Markdown files, whether
the current file contents contain Mermaid diagrams.
Figure 1. Accessible flow diagram showing how the CI policy decides whether the
documentation gate and Mermaid validation are required from the changed-file
list, including the conservative fallback path used when the list is empty or
malformed.
```mermaid
flowchart TD
Start["Start
Receive_changed_file_list"] --> Validate[Validate_input_list]
Validate --> IsEmpty{List_empty_or_malformed?}
IsEmpty -->|Yes| Fallback[Apply_safe_default_policy]
IsEmpty -->|No| Classify[Classify_changes_by_path]
Classify --> DocsOnly{Only_documentation_inputs_changed?}
DocsOnly -->|Yes| RequireDocs[Set docs_gate_required = true]
DocsOnly -->|No| MixedOrCode[Mixed_or_code_only_changes]
MixedOrCode --> HasDocs{Any_documentation_inputs_changed?}
HasDocs -->|Yes| RequireDocs
HasDocs -->|No| SkipDocs[Set docs_gate_required = false]
RequireDocs --> CheckNixie{Mermaid_or_conservative_path?}
CheckNixie -->|Yes| RequireNixie[Set nixie_required = true]
CheckNixie -->|No| SkipNixie[Set nixie_required = false]
Fallback --> Output["Emit_policy_output
with_safe_defaults"]
SkipDocs --> SkipNixie
RequireNixie --> Output
SkipNixie --> Output
Output --> End["End
Workflow_consumes_flags"]
```
In practice, the current policy behaves as follows:
- If the changed-file list is unavailable, CI runs both the documentation gate
and Mermaid validation as a safe default.
- If no documentation inputs changed, the documentation gate is skipped.
- If Markdown files changed, the documentation gate runs.
- If documentation-tooling configuration changed, the documentation gate and
Mermaid validation both run as a conservative default.
- Mermaid validation runs only when one of the changed Markdown files contains
a Mermaid diagram, or when the workflow takes that conservative fallback.
The CI workflow publishes these decisions as stable flags so the `docs-gate`
job can stay required even when it skips documentation-specific work. When the
workflow takes the conservative Mermaid path because a file could not be read,
it also publishes which files triggered that fallback.
## GitHub device-flow authentication
repovec-appliance authenticates to GitHub using OAuth device flow. This lets an
operator authorize the appliance over SSH without opening a browser on the VM.
The appliance requests a device code, shows the verification URL and user code,
then polls GitHub until the user approves the request, denies it, or the code
expires.
During login, the operator-visible values are:
- verification URL: `https://github.com/login/device`
- user code: the short code to enter in GitHub's browser flow
The token polling loop respects GitHub's polling interval and handles the
standard device-flow terminal responses:
- `slow_down`: wait longer before the next poll.
- `access_denied`: stop the login attempt because the user denied access.
- `expired_token`: stop the login attempt because the device code expired.
The access token is encrypted at rest in
`/etc/repovec/github-oauth-token.cred`. Operators should treat that file as
secret material even though it is encrypted. The encrypted credential is bound
to the appliance through `systemd-creds` using the credential name
`repovec-github-oauth-token`.
After a restart, the appliance restores only the bearer secret from this
encrypted credential. Scope-dependent permissions must be revalidated against
GitHub before the control plane relies on them, and operators may need to run a
fresh login if that revalidation fails.
Roadmap item `2.1.1` provides the runtime client, encrypted token-store
adapter, and mock-server test binary. The interactive TUI login screen is a
later roadmap item and will call this authentication surface.
## Qdrant service
repovec-appliance ships Qdrant as an appliance-internal Podman Quadlet.
Operators should treat it as a local dependency of the appliance rather than a
general-purpose network service.
The checked-in Quadlet is installed to
`/etc/containers/systemd/qdrant.container`. It tracks the official Qdrant
`docker.io/qdrant/qdrant:v1` image stream and enables `AutoUpdate=registry` so
the systemd-managed container can participate in Podman's registry-based
auto-update flow within the current major version.
Qdrant's REST and gRPC ports are published only on loopback:
- REST: `127.0.0.1:6333`
- gRPC: `127.0.0.1:6334`
Persistent vector storage lives at `/var/lib/repovec/qdrant-storage` on the
host and is mounted into the container at `/qdrant/storage`. The mount uses an
explicit `:Z` SELinux relabel so the rootful Podman service can write to the
directory on enforcing hosts.
Qdrant requires an API key. On first boot, `repovec-qdrant-api-key.service`
generates a random raw key at `/etc/repovec/qdrant-api-key`, restricts the file
to `repovec:repovec` with mode `0400`, and refreshes the rootful Podman secret
`repovec-qdrant-api-key`. The Qdrant Quadlet injects that Podman secret as
`QDRANT__SERVICE__API_KEY` inside the container.
Operators can inspect service state without printing the key:
```sh
systemctl status repovec-qdrant-api-key.service qdrant.service
journalctl -u repovec-qdrant-api-key.service
stat -c '%U:%G %a %n' /etc/repovec/qdrant-api-key
podman secret inspect repovec-qdrant-api-key
```
Local clients authenticate by reading the key as the `repovec` user and sending
it in Qdrant's `api-key` header:
```sh
sudo -u repovec sh -c \
'api_key="$(cat /etc/repovec/qdrant-api-key)"
curl --config - http://127.0.0.1:6333/collections <`.
## Appliance systemd target
repovec-appliance ships a base systemd target and static daemon service files
under `packaging/systemd/`:
- `repovec.target`
- `repovecd.service`
- `repovec-mcpd.service`
- `repovec-grepai@.service`
Install these files to `/etc/systemd/system/`, then reload systemd:
```sh
sudo install -m 0644 packaging/systemd/repovec.target /etc/systemd/system/repovec.target
sudo install -m 0644 packaging/systemd/repovecd.service /etc/systemd/system/repovecd.service
sudo install -m 0644 packaging/systemd/repovec-mcpd.service /etc/systemd/system/repovec-mcpd.service
sudo install -m 0644 packaging/systemd/repovec-grepai@.service /etc/systemd/system/repovec-grepai@.service
sudo systemctl daemon-reload
```
The target wants `qdrant.service`, `repovecd.service`, `repovec-mcpd.service`,
and `cloudflared.service`. The Qdrant service name is the generated systemd
unit from the installed `/etc/containers/systemd/qdrant.container` Quadlet;
dependent services must use `qdrant.service`.
`repovec-grepai@.service` is the template used for future per-repository
indexer instances. It runs `grepai watch` as the `repovec` user, sets
`HOME=/var/lib/repovec`, works in `/var/lib/repovec/worktrees/%I`, and writes
stdout and stderr to journald. Later reconciliation work creates and manages
concrete instances; operators should not expect installing the template alone
to start indexers.
Enable and start the appliance service group with:
```sh
sudo systemctl enable repovec.target
sudo systemctl start repovec.target
```
> **Note:** `repovecd` and `repovec-mcpd` validate the checked-in systemd unit
> contract and Qdrant liveness at startup before doing any other work. If
> validation fails, the daemon exits immediately with a non-zero exit code.
> Inspect the journal with
> `journalctl -u repovecd.service --no-pager | tail -20` or
> `journalctl -u repovec-mcpd.service --no-pager | tail -20`; the error message
> identifies the violated unit contract or Qdrant liveness condition. This
> validation does not prove that the host has `/usr/bin/grepai`, concrete
> worktrees, or a compatible systemd version.
Starting the target may fail until these prerequisites are present on the host:
- The `repovec` system user exists and matches the checked-in unit contract.
- `/etc/repovec/qdrant-api-key` has been provisioned with the Qdrant API key
and can be read by the daemon services.
- The required daemon binaries, `repovecd` and `repovec-mcpd`, exist at the
paths referenced by the unit files.
- `qdrant.service` can start and accept authenticated gRPC requests before the
bounded daemon readiness wait expires.
If any prerequisite is missing, the target fails closed instead of starting
partially configured services.
### Startup validation logging
Both `repovecd` and `repovec-mcpd` validate their checked-in systemd unit
contracts at startup before starting any async work. The outcome is observable
in the systemd journal:
**Success** — a `TRACE`-level event followed by a `DEBUG`-level confirmation:
```text
TRACE systemd unit contract validated
DEBUG systemd unit contract validated at daemon startup
```
**Failure** — an `ERROR`-level event with structured fields identifying the
violating unit and the nature of the failure, followed by a non-zero process
exit:
```text
ERROR systemd unit contract violation — aborting startup
unit=repovecd.service
error=repovecd.service is missing [Service]
```
The `unit` field contains the logical systemd unit name (e.g.
`repovecd.service`) and the `error` field contains the human-readable
description of the contract violation. The Qdrant Quadlet validator emits
`INFO`, `WARN`, and `ERROR` events; use a target-scoped filter such as
`RUST_LOG=repovec_core::qdrant_quadlet=info` to inspect those validation
events. The systemd unit validator emits `TRACE`, `DEBUG`, and `ERROR` events;
use `RUST_LOG=repovec_core::systemd_units=trace` when startup ordering or
unit-contract diagnostics require the lower-level trace.
Concrete grepai indexer instances also start after and require both Qdrant and
`repovecd`.