keyfleet 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- keyfleet-0.1.0/.gitattributes +4 -0
- keyfleet-0.1.0/.github/workflows/ci.yml +28 -0
- keyfleet-0.1.0/.github/workflows/release.yml +38 -0
- keyfleet-0.1.0/.gitignore +16 -0
- keyfleet-0.1.0/.pre-commit-config.yaml +14 -0
- keyfleet-0.1.0/.python-version +1 -0
- keyfleet-0.1.0/AGENTS.md +100 -0
- keyfleet-0.1.0/CHANGELOG.md +61 -0
- keyfleet-0.1.0/CLAUDE.md +34 -0
- keyfleet-0.1.0/CONTRIBUTING.md +75 -0
- keyfleet-0.1.0/LICENSE +201 -0
- keyfleet-0.1.0/PKG-INFO +181 -0
- keyfleet-0.1.0/PLAN.md +58 -0
- keyfleet-0.1.0/README.md +155 -0
- keyfleet-0.1.0/SECURITY.md +29 -0
- keyfleet-0.1.0/docs/ASSUMPTIONS.md +24 -0
- keyfleet-0.1.0/docs/DECISIONS.md +94 -0
- keyfleet-0.1.0/docs/SERVICES.md +45 -0
- keyfleet-0.1.0/docs/demo.gif +0 -0
- keyfleet-0.1.0/keyfleet-BRIEF.md +197 -0
- keyfleet-0.1.0/keyfleet.example.yaml +99 -0
- keyfleet-0.1.0/pyproject.toml +61 -0
- keyfleet-0.1.0/schema/keyfleet.schema.json +535 -0
- keyfleet-0.1.0/scripts/demo_ledger.yaml +79 -0
- keyfleet-0.1.0/scripts/gen_demo_gif.py +171 -0
- keyfleet-0.1.0/scripts/gen_schema.py +26 -0
- keyfleet-0.1.0/scripts/gen_services_md.py +24 -0
- keyfleet-0.1.0/src/keyfleet/__init__.py +5 -0
- keyfleet-0.1.0/src/keyfleet/bundled.py +243 -0
- keyfleet-0.1.0/src/keyfleet/checks.py +263 -0
- keyfleet-0.1.0/src/keyfleet/cli.py +196 -0
- keyfleet-0.1.0/src/keyfleet/crypto.py +67 -0
- keyfleet-0.1.0/src/keyfleet/data/advisories.yaml +32 -0
- keyfleet-0.1.0/src/keyfleet/data/example.yaml +99 -0
- keyfleet-0.1.0/src/keyfleet/data/models.yaml +52 -0
- keyfleet-0.1.0/src/keyfleet/data/services.yaml +271 -0
- keyfleet-0.1.0/src/keyfleet/impact.py +86 -0
- keyfleet-0.1.0/src/keyfleet/model.py +418 -0
- keyfleet-0.1.0/src/keyfleet/report.py +430 -0
- keyfleet-0.1.0/tests/conftest.py +70 -0
- keyfleet-0.1.0/tests/fixtures/bad_ref.yaml +10 -0
- keyfleet-0.1.0/tests/fixtures/dup_ids.yaml +4 -0
- keyfleet-0.1.0/tests/fixtures/min_keys_gap.yaml +38 -0
- keyfleet-0.1.0/tests/fixtures/not_yaml.yaml +2 -0
- keyfleet-0.1.0/tests/fixtures/secret_codes.yaml +15 -0
- keyfleet-0.1.0/tests/fixtures/secret_seed.yaml +12 -0
- keyfleet-0.1.0/tests/fixtures/valid.yaml +71 -0
- keyfleet-0.1.0/tests/golden/check_min_keys_gap.json +50 -0
- keyfleet-0.1.0/tests/golden/check_min_keys_gap.txt +9 -0
- keyfleet-0.1.0/tests/golden/lost_k_main.md +8 -0
- keyfleet-0.1.0/tests/golden/report_valid.md +27 -0
- keyfleet-0.1.0/tests/test_advisories.py +84 -0
- keyfleet-0.1.0/tests/test_bundled.py +113 -0
- keyfleet-0.1.0/tests/test_check_capacity.py +109 -0
- keyfleet-0.1.0/tests/test_check_lost_retired.py +60 -0
- keyfleet-0.1.0/tests/test_check_min_keys.py +70 -0
- keyfleet-0.1.0/tests/test_check_recovery_codes.py +58 -0
- keyfleet-0.1.0/tests/test_check_spare_unregistered.py +30 -0
- keyfleet-0.1.0/tests/test_check_unknown_service.py +54 -0
- keyfleet-0.1.0/tests/test_check_weak_factors.py +44 -0
- keyfleet-0.1.0/tests/test_checks.py +31 -0
- keyfleet-0.1.0/tests/test_cli.py +253 -0
- keyfleet-0.1.0/tests/test_crypto.py +102 -0
- keyfleet-0.1.0/tests/test_data_files.py +89 -0
- keyfleet-0.1.0/tests/test_golden.py +42 -0
- keyfleet-0.1.0/tests/test_impact.py +107 -0
- keyfleet-0.1.0/tests/test_model.py +177 -0
- keyfleet-0.1.0/tests/test_no_network.py +22 -0
- keyfleet-0.1.0/tests/test_schema_sync.py +19 -0
- keyfleet-0.1.0/uv.lock +452 -0
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
name: ci
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
test:
|
|
10
|
+
name: test (${{ matrix.os }})
|
|
11
|
+
strategy:
|
|
12
|
+
fail-fast: false
|
|
13
|
+
matrix:
|
|
14
|
+
os: [ubuntu-latest, windows-latest, macos-latest]
|
|
15
|
+
runs-on: ${{ matrix.os }}
|
|
16
|
+
steps:
|
|
17
|
+
- uses: actions/checkout@v7
|
|
18
|
+
- uses: astral-sh/setup-uv@v10.0.1
|
|
19
|
+
with:
|
|
20
|
+
python-version: "3.12"
|
|
21
|
+
# Exercise the age round-trip tests on at least one OS (they skip
|
|
22
|
+
# gracefully where the binary is absent).
|
|
23
|
+
- if: runner.os == 'Linux'
|
|
24
|
+
run: sudo apt-get update && sudo apt-get install -y age
|
|
25
|
+
- run: uv sync --all-extras --dev
|
|
26
|
+
- run: uv run ruff check .
|
|
27
|
+
- run: uv run ruff format --check .
|
|
28
|
+
- run: uv run pytest -q
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
name: release
|
|
2
|
+
|
|
3
|
+
# Publishes to PyPI via trusted publishing (OIDC, no stored token) when a
|
|
4
|
+
# v* tag is pushed. PyPI-side publisher config: owner ThePrimeLayer,
|
|
5
|
+
# repo keyfleet, workflow release.yml, environment pypi.
|
|
6
|
+
|
|
7
|
+
on:
|
|
8
|
+
push:
|
|
9
|
+
tags: ["v*"]
|
|
10
|
+
|
|
11
|
+
jobs:
|
|
12
|
+
build:
|
|
13
|
+
runs-on: ubuntu-latest
|
|
14
|
+
steps:
|
|
15
|
+
- uses: actions/checkout@v7
|
|
16
|
+
- uses: astral-sh/setup-uv@v10.0.1
|
|
17
|
+
with:
|
|
18
|
+
python-version: "3.12"
|
|
19
|
+
- run: uv build
|
|
20
|
+
- uses: actions/upload-artifact@v7
|
|
21
|
+
with:
|
|
22
|
+
name: dist
|
|
23
|
+
path: dist/
|
|
24
|
+
|
|
25
|
+
publish:
|
|
26
|
+
needs: build
|
|
27
|
+
runs-on: ubuntu-latest
|
|
28
|
+
environment:
|
|
29
|
+
name: pypi
|
|
30
|
+
url: https://pypi.org/project/keyfleet/
|
|
31
|
+
permissions:
|
|
32
|
+
id-token: write
|
|
33
|
+
steps:
|
|
34
|
+
- uses: actions/download-artifact@v8
|
|
35
|
+
with:
|
|
36
|
+
name: dist
|
|
37
|
+
path: dist/
|
|
38
|
+
- uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# keyfleet — never commit a real ledger (AGENTS.md §8.3).
|
|
2
|
+
# Only the fictional keyfleet.example.yaml is committed.
|
|
3
|
+
keyfleet.yaml
|
|
4
|
+
keyfleet.yml
|
|
5
|
+
|
|
6
|
+
# Python
|
|
7
|
+
__pycache__/
|
|
8
|
+
*.py[cod]
|
|
9
|
+
*.egg-info/
|
|
10
|
+
.venv/
|
|
11
|
+
build/
|
|
12
|
+
dist/
|
|
13
|
+
.pytest_cache/
|
|
14
|
+
.ruff_cache/
|
|
15
|
+
.coverage
|
|
16
|
+
htmlcov/
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# ruff and gitleaks are pinned to exact versions on purpose (AGENTS.md §8.5).
|
|
2
|
+
# The ruff rev must match the ruff pin in pyproject.toml [dependency-groups].
|
|
3
|
+
# Bump either only in a dedicated `chore(deps):` commit.
|
|
4
|
+
repos:
|
|
5
|
+
- repo: https://github.com/astral-sh/ruff-pre-commit
|
|
6
|
+
rev: v0.16.5
|
|
7
|
+
hooks:
|
|
8
|
+
- id: ruff-check
|
|
9
|
+
args: [--fix]
|
|
10
|
+
- id: ruff-format
|
|
11
|
+
- repo: https://github.com/gitleaks/gitleaks
|
|
12
|
+
rev: v8.30.1
|
|
13
|
+
hooks:
|
|
14
|
+
- id: gitleaks
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3.12
|
keyfleet-0.1.0/AGENTS.md
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
# AGENTS.md — keyfleet
|
|
2
|
+
|
|
3
|
+
Instructions for any coding agent working in this repository (Codex, Cursor, Copilot, Cline/Kilo, Claude Code via `CLAUDE.md`). Explicit instructions from the user override this file. This file is the single source of truth for **how to work here**; `keyfleet-BRIEF.md` is the source of truth for **what to build**. If they conflict on scope, the brief wins; on process, this file wins.
|
|
4
|
+
|
|
5
|
+
## 1. What this project is
|
|
6
|
+
|
|
7
|
+
`keyfleet` is a local-first Python CLI that keeps a YAML ledger of hardware security keys ↔ accounts ↔ credential types and reports coverage gaps, lost-key impact, capacity, and advisories. It stores **no secrets** and makes **no network calls**. Full specification: `keyfleet-BRIEF.md` — read it completely before your first change, then consult it by section (§7 data model, §8 CLI, §9 logic, §15 open decisions).
|
|
8
|
+
|
|
9
|
+
## 2. Session start (every session, every harness)
|
|
10
|
+
|
|
11
|
+
1. If `PLAN.md` exists, read it first: milestones, open questions, and the session log tell you where the last agent stopped. If it does not exist, read the brief and create `PLAN.md` per brief §16.
|
|
12
|
+
2. Run `git status` and `git log --oneline -15` to orient.
|
|
13
|
+
3. Work in small steps. Before ending, append a session-log line to `PLAN.md` (format in §9).
|
|
14
|
+
|
|
15
|
+
## 3. Commands
|
|
16
|
+
|
|
17
|
+
| Task | Command |
|
|
18
|
+
|---|---|
|
|
19
|
+
| Set up environment | `uv sync --all-extras --dev` |
|
|
20
|
+
| Run the CLI | `uv run keyfleet --help` |
|
|
21
|
+
| Tests (all / one file) | `uv run pytest -q` / `uv run pytest tests/test_checks.py -q` |
|
|
22
|
+
| Lint + format | `uv run ruff check . && uv run ruff format .` |
|
|
23
|
+
| All hooks | `uv run pre-commit run --all-files` |
|
|
24
|
+
| Build wheel | `uv build` |
|
|
25
|
+
|
|
26
|
+
Always prefix with `uv run`. Never `pip install` into a global/system Python. Commands are identical in PowerShell on Windows; write cross-platform code (`pathlib`, no shell one-liners in library code).
|
|
27
|
+
|
|
28
|
+
## 4. Repository map
|
|
29
|
+
|
|
30
|
+
```
|
|
31
|
+
src/keyfleet/ cli.py (typer) · model.py (pydantic) · checks.py, impact.py (pure functions) · report.py (rich/markdown/json) · crypto.py (optional age decryption) · data/{models,services,advisories}.yaml
|
|
32
|
+
schema/ keyfleet.schema.json (generated from the pydantic models; regenerate when model.py changes)
|
|
33
|
+
tests/ fixtures/*.yaml · one test module per check · data-integrity tests
|
|
34
|
+
docs/ ASSUMPTIONS.md (unverified facts) · DECISIONS.md (choices + rationale) · SERVICES.md (generated)
|
|
35
|
+
keyfleet.example.yaml fictional example ledger; the only ledger ever committed
|
|
36
|
+
PLAN.md milestones, open questions, session log
|
|
37
|
+
CHANGELOG.md Keep a Changelog format; add to "Unreleased" as you go
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## 5. Code conventions
|
|
41
|
+
|
|
42
|
+
- Python 3.12+, full type hints, `pydantic` v2 models in `model.py`, `typer` CLI in `cli.py`, `rich` for terminal output, `yaml.safe_load` only.
|
|
43
|
+
- `checks.py` and `impact.py` are **pure**: they take the loaded ledger model and return findings; no file or terminal I/O inside them.
|
|
44
|
+
- Exit codes: `0` clean, `1` findings at FAIL level, `2` tool/usage error. Every finding carries an actionable message and, where possible, a link from `services.yaml`.
|
|
45
|
+
- Errors for bad ledgers must say *which file, which key/account id, which field* and how to fix it.
|
|
46
|
+
- Use `logging`, not `print`, outside the report layer. No global mutable state.
|
|
47
|
+
- Keep modules under ~400 lines; split by responsibility, not by size.
|
|
48
|
+
- Do not add a dependency without a line in `docs/DECISIONS.md` (what, why, alternative considered).
|
|
49
|
+
|
|
50
|
+
## 6. Data files (`src/keyfleet/data/*.yaml`)
|
|
51
|
+
|
|
52
|
+
- Every entry has `source_url` and `verified: YYYY-MM-DD`. If a fact cannot be verified on the vendor's or service's own page, write `null` — never guess.
|
|
53
|
+
- `models.yaml`: capabilities, interfaces, discoverable-credential capacity per model/firmware.
|
|
54
|
+
- `services.yaml`: security-settings URL, max keys allowed, discoverable-credential support. Keep entries alphabetical by id.
|
|
55
|
+
- `advisories.yaml`: vendor advisories with `affects` rules (firmware ranges). Summaries in your own words; link the advisory.
|
|
56
|
+
- Data-integrity tests must pass after any edit (unique ids, valid URL syntax, ints or null for capacities). Regenerate `docs/SERVICES.md` when `services.yaml` changes.
|
|
57
|
+
|
|
58
|
+
## 7. Testing
|
|
59
|
+
|
|
60
|
+
- `pytest`; fixtures under `tests/fixtures/`; one module per check with positive and negative cases; golden files for `check`/`report` output.
|
|
61
|
+
- Tests never touch the network and never depend on the `age` binary (skip that test when it is absent).
|
|
62
|
+
- Must pass on Linux, macOS, and Windows (CI runs all three): use `tmp_path`, `pathlib`, UTF-8 explicitly.
|
|
63
|
+
- Add or update tests in the same commit as the behavior change. Target ≥85% coverage on `checks.py` and `impact.py`.
|
|
64
|
+
|
|
65
|
+
## 8. Security and privacy invariants (non-negotiable)
|
|
66
|
+
|
|
67
|
+
1. **No network calls in runtime code.** A test greps `src/` for `httpx`, `requests`, `urllib`, `socket`, `aiohttp` and fails on any import.
|
|
68
|
+
2. **No secrets in the ledger.** Validation rejects fields or values that look like recovery codes, TOTP seeds, PINs, or OTP secrets, with a clear message. Tests cover this.
|
|
69
|
+
3. **Never commit a real ledger.** `keyfleet.yaml` and `*.age` decrypted output are gitignored; only `keyfleet.example.yaml` (fictional) is committed.
|
|
70
|
+
4. **No telemetry, ever.**
|
|
71
|
+
5. **Supply chain:** `.pre-commit-config.yaml` pins `ruff` and `gitleaks` to exact versions; do not change pins except in a dedicated `chore(deps):` commit.
|
|
72
|
+
6. If a request would violate any of the above, stop and say so instead of complying.
|
|
73
|
+
|
|
74
|
+
## 9. Workflow and git
|
|
75
|
+
|
|
76
|
+
- Conventional commits: `feat:`, `fix:`, `test:`, `docs:`, `chore:`, `refactor:`. One logical change per commit; run `ruff` and `pytest` before committing.
|
|
77
|
+
- Small changes go directly on `main`. Anything that changes the ledger schema, a data-file format, or the CLI surface goes on a `feat/<topic>` branch with a short PR description (even if the owner merges it alone).
|
|
78
|
+
- Never force-push `main` or rewrite published history.
|
|
79
|
+
- `CHANGELOG.md`: add a bullet under **Unreleased** in the same commit as a user-visible change.
|
|
80
|
+
- `docs/ASSUMPTIONS.md`: record every external fact you relied on but could not verify (vendor capacities, service limits, API details) with a date and a link.
|
|
81
|
+
- `docs/DECISIONS.md`: one short entry per non-obvious choice (dependency, schema shape, threshold): context → decision → alternatives.
|
|
82
|
+
- **No churn.** Do not reformat, rename, or reorganize files that the task does not require. Do not rewrite `PLAN.md` milestones or the brief; propose changes in chat and edit only after agreement.
|
|
83
|
+
- Session log (append to the end of `PLAN.md`):
|
|
84
|
+
`- 2026-08-29 · <harness> · <what changed, 1 line> · next: <1 line> · open: <question or "none">`
|
|
85
|
+
|
|
86
|
+
## 10. Decisions that require the owner
|
|
87
|
+
|
|
88
|
+
From brief §15: license (Apache-2.0 recommended), default policy numbers (`T0: 3, T1: 2, T2: 1`), and the package/repo name if `keyfleet` is taken on PyPI/GitHub. Ask these **once, in a single batched message**, propose a default for each, and continue with the recommended default on anything non-blocking. Record the answers in `docs/DECISIONS.md`. Do not ask questions the brief or this file already answers.
|
|
89
|
+
|
|
90
|
+
## 11. Definition of done for v0.1.0
|
|
91
|
+
|
|
92
|
+
Everything in brief §4 shipped; CI green on all three OSes; the README quick start executed literally from a clean clone; `CHANGELOG.md` entry; git tag `v0.1.0`; `uv build` succeeds. When a milestone or the release is complete, report: what shipped, what is assumed (link `docs/ASSUMPTIONS.md`), what is next.
|
|
93
|
+
|
|
94
|
+
## 12. Harness notes
|
|
95
|
+
|
|
96
|
+
- **Claude Code** reads `CLAUDE.md`, which imports this file. Put shared rules here, not there.
|
|
97
|
+
- **Codex** reads this file directly (keep it under the 32 KiB cap; no `AGENTS.override.md` is used in this repo).
|
|
98
|
+
- **Cursor** reads `AGENTS.md` natively; add `.cursor/rules/*.mdc` only for glob-scoped extras, never for rules that belong here.
|
|
99
|
+
- **GitHub Copilot** reads `AGENTS.md`; no separate `copilot-instructions.md` is maintained.
|
|
100
|
+
- **Other harnesses** that do not discover `AGENTS.md`: point their rules file at this one (a one-line "Read and follow AGENTS.md" is enough). Do not fork the content.
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable, user-visible changes to keyfleet are documented in this file.
|
|
4
|
+
|
|
5
|
+
Format: [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) ·
|
|
6
|
+
versioning: [SemVer](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [Unreleased]
|
|
9
|
+
|
|
10
|
+
## [0.1.0] - 2026-08-30
|
|
11
|
+
|
|
12
|
+
First release: the ledger, the checker, the incident tooling, and the
|
|
13
|
+
source-cited services dataset.
|
|
14
|
+
|
|
15
|
+
### Added
|
|
16
|
+
|
|
17
|
+
- YAML ledger schema (keys ↔ accounts ↔ registrations, policy, advisories)
|
|
18
|
+
with strict validation, referential-integrity checks, and errors that name
|
|
19
|
+
the file, the offending key/account id, and the field.
|
|
20
|
+
- Secret rejection: a ledger containing anything that looks like a recovery
|
|
21
|
+
code, TOTP seed, PIN, or OTP secret (by field name or value shape) is
|
|
22
|
+
refused with an explanation — keyfleet stores pointers, never secrets.
|
|
23
|
+
- `keyfleet validate [LEDGER]` — exit 0 valid, 1 invalid, 2 missing file.
|
|
24
|
+
- Published JSON Schema at `schema/keyfleet.schema.json` for editor
|
|
25
|
+
completion, generated from the models.
|
|
26
|
+
- Fictional example ledger `keyfleet.example.yaml`.
|
|
27
|
+
- `keyfleet check [LEDGER] [--json]` with the min-keys coverage rule: FAIL for
|
|
28
|
+
every account holding fewer active/spare keys than `policy.min_keys[tier]`
|
|
29
|
+
(defaults T0:3, T1:2, T2:1); lost/retired keys never count. Exit 1 when any
|
|
30
|
+
FAIL finding exists.
|
|
31
|
+
- Lost/retired hygiene check: FAIL for every lost or retired key that is still
|
|
32
|
+
registered on any account, pointing at `keyfleet lost KEY` for the
|
|
33
|
+
de-registration checklist.
|
|
34
|
+
- Weak-factor check: WARN for every factor a tier's policy warns against
|
|
35
|
+
(default: sms and email on T0 accounts).
|
|
36
|
+
- Unregistered-spare check: WARN for spare keys registered on no account.
|
|
37
|
+
- Recovery-code pointer check: INFO for accounts in tiers listed in
|
|
38
|
+
`policy.require_recovery_codes_for` (default T0+T1) that have no stored
|
|
39
|
+
recovery-code pointer.
|
|
40
|
+
- Encrypted ledgers: every command transparently reads `keyfleet.yaml.age`
|
|
41
|
+
(or any `*.age` path) via the `age` CLI, decrypting to memory only; set
|
|
42
|
+
`KEYFLEET_AGE_IDENTITY` to an identity file for non-interactive use.
|
|
43
|
+
- `keyfleet init` — writes the fictional example ledger into the current
|
|
44
|
+
directory and makes sure `.gitignore` covers `keyfleet.yaml`.
|
|
45
|
+
- `keyfleet advisories` — matches every key against the bundled advisory list
|
|
46
|
+
(plus any ledger-local advisories) by vendor and firmware range; keys
|
|
47
|
+
without `firmware:` are prompted to set it.
|
|
48
|
+
- `keyfleet services [--search NAME]` — prints the bundled service table.
|
|
49
|
+
- `keyfleet report [--md|--json]` — coverage matrix (accounts x keys with
|
|
50
|
+
registration types), per-tier summary, and key utilization including
|
|
51
|
+
discoverable-credential usage against known capacities.
|
|
52
|
+
- `keyfleet lost KEY_ID [--md]` — lost-key impact analysis and an ordered
|
|
53
|
+
de-registration checklist (tier first, becomes-inaccessible first) with the
|
|
54
|
+
registration nicknames to delete and each service's security-settings URL.
|
|
55
|
+
- Bundled reference data, every fact verified on the vendor's own page (or
|
|
56
|
+
`null`, never guessed): `services.yaml` with 32 services (settings URLs,
|
|
57
|
+
documented key limits, passkey support), `models.yaml` with
|
|
58
|
+
discoverable-credential capacities per firmware (YubiKey 5 / Security Key
|
|
59
|
+
series 25→100 at 5.7, Nitrokey 3 family), and `advisories.yaml` seeded with
|
|
60
|
+
YSA-2024-02, YSA-2024-03, and YSA-2025-02. `docs/SERVICES.md` is generated
|
|
61
|
+
from the services table.
|
keyfleet-0.1.0/CLAUDE.md
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# keyfleet — Claude Code
|
|
2
|
+
|
|
3
|
+
@AGENTS.md
|
|
4
|
+
|
|
5
|
+
Shared rules live in `AGENTS.md` (imported above). This file adds only what is specific to working with Claude Code. Keep it under 60 lines; if a rule applies to every harness, move it to `AGENTS.md`.
|
|
6
|
+
|
|
7
|
+
## Orientation
|
|
8
|
+
|
|
9
|
+
- Start from `PLAN.md`. If it is missing, read `keyfleet-BRIEF.md` in full and create `PLAN.md` per the brief's §16 before touching code.
|
|
10
|
+
- The brief is deliberately **not** imported here: imports load on every launch, and the brief is only needed when a task touches something it specifies (schema, CLI surface, a check's logic, data files). Open the relevant section at that point.
|
|
11
|
+
|
|
12
|
+
## Working style
|
|
13
|
+
|
|
14
|
+
- When a decision needs the owner, present it as a short numbered fork with a recommended default, batch all such questions into one message, and keep working on unblocked tasks. No "shall I continue?" check-ins; end the turn when the requested slice is done or you are blocked.
|
|
15
|
+
- Push back plainly when the brief, `PLAN.md`, or a request is wrong or would breach an invariant in `AGENTS.md` §8 — before doing it, not after.
|
|
16
|
+
- Use plan mode (or a ≤10-line plan in chat) for anything that changes the ledger schema, a data-file format, or the CLI surface. Skip the plan for single-check or single-test changes.
|
|
17
|
+
- Do not rewrite `PLAN.md` milestones or the brief on your own; propose in chat, edit after agreement.
|
|
18
|
+
|
|
19
|
+
## Tooling
|
|
20
|
+
|
|
21
|
+
- `uv run …` for every Python invocation; never call `pip`, `python -m pip`, or a global interpreter.
|
|
22
|
+
- The owner works on both Windows (PowerShell) and macOS: prefer commands that are identical on both; use `pathlib` in code and in test helpers.
|
|
23
|
+
- When you need a fact about a key vendor, a service's security-key settings, or an advisory, read the vendor's/service's own page and record the URL and date in the data file (`source_url`, `verified`) or in `docs/ASSUMPTIONS.md`. Never fill a data entry from memory.
|
|
24
|
+
|
|
25
|
+
## Finishing a task or milestone
|
|
26
|
+
|
|
27
|
+
1. `uv run ruff check . && uv run ruff format . && uv run pytest -q`
|
|
28
|
+
2. Update `CHANGELOG.md` (Unreleased) for user-visible changes and append the session-log line to `PLAN.md`.
|
|
29
|
+
3. Summarize: shipped / assumed / next.
|
|
30
|
+
|
|
31
|
+
## Don'ts
|
|
32
|
+
|
|
33
|
+
- Don't add dependencies silently, touch the `gitleaks` or `ruff` pins outside a `chore(deps):` commit, add network calls, or commit `keyfleet.yaml`.
|
|
34
|
+
- Don't reformat or reorganize files the task doesn't require.
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# Contributing to keyfleet
|
|
2
|
+
|
|
3
|
+
Thanks for helping! The most valuable contribution by far is a **service
|
|
4
|
+
entry** — it takes ten minutes and makes `keyfleet lost` and `keyfleet check`
|
|
5
|
+
smarter for everyone.
|
|
6
|
+
|
|
7
|
+
## Add a service (the ten-minute PR)
|
|
8
|
+
|
|
9
|
+
1. Open the service's **own** documentation/help page about security keys or
|
|
10
|
+
passkeys. Facts must come from a page on the vendor's domain — that page
|
|
11
|
+
becomes `source_url`. No blog posts, no memory, no guessing.
|
|
12
|
+
2. Add an entry to [`src/keyfleet/data/services.yaml`](src/keyfleet/data/services.yaml),
|
|
13
|
+
**alphabetical by id**:
|
|
14
|
+
|
|
15
|
+
```yaml
|
|
16
|
+
example:
|
|
17
|
+
name: "Example"
|
|
18
|
+
security_settings_url: https://example.com/settings/security # only if documented/linked; else null
|
|
19
|
+
max_keys: null # integer only if the page states a limit
|
|
20
|
+
fido2_discoverable: true # passkeys supported? true / false / null (page doesn't say)
|
|
21
|
+
notes: "One short sentence from the page, in your own words."
|
|
22
|
+
source_url: https://help.example.com/security-keys
|
|
23
|
+
verified: 2026-08-30 # the day YOU read the page
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
`null` never means "probably not" — it means *the page does not say*.
|
|
27
|
+
A service with **no** security-key support at all is still a great entry:
|
|
28
|
+
set `fido2_discoverable: false` and say so in `notes` (see `steam`).
|
|
29
|
+
3. Regenerate the table and run the data tests:
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
uv run python scripts/gen_services_md.py
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
uv run pytest tests/test_data_files.py -q
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
4. Commit as `data(services): add example`, one service per PR, and paste the
|
|
40
|
+
sentence from the vendor page that supports each non-null fact into the PR
|
|
41
|
+
description.
|
|
42
|
+
|
|
43
|
+
Corrections use the same rules — update `verified` to the day you re-checked.
|
|
44
|
+
`models.yaml` (key capabilities/capacities) and `advisories.yaml` (vendor
|
|
45
|
+
advisories with firmware ranges) follow the same source-cited pattern; see the
|
|
46
|
+
header comments in each file.
|
|
47
|
+
|
|
48
|
+
## Code contributions
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
uv sync --all-extras --dev
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
uv run pytest -q
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
uv run pre-commit run --all-files
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
- Conventional commits (`feat:`, `fix:`, `test:`, `docs:`, `data:`, `chore:`);
|
|
63
|
+
one logical change per commit; tests land in the same commit as the change.
|
|
64
|
+
- `checks.py` and `impact.py` stay pure (no I/O); rendering lives in
|
|
65
|
+
`report.py`; every finding must tell the user what to *do*.
|
|
66
|
+
- Two hard rules, enforced by tests, not negotiable: **no network calls in
|
|
67
|
+
runtime code** and **no storing secrets** (the validator must keep refusing
|
|
68
|
+
anything that looks like one).
|
|
69
|
+
- After changing `model.py`, regenerate the JSON schema:
|
|
70
|
+
`uv run python scripts/gen_schema.py`. After changing CLI output on
|
|
71
|
+
purpose, regenerate goldens: `KEYFLEET_UPDATE_GOLDENS=1 uv run pytest -q`
|
|
72
|
+
and review the diff.
|
|
73
|
+
|
|
74
|
+
Agent-assisted contributions are welcome; the repo's ground rules for that
|
|
75
|
+
live in [AGENTS.md](AGENTS.md).
|
keyfleet-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
Apache License
|
|
2
|
+
Version 2.0, January 2004
|
|
3
|
+
http://www.apache.org/licenses/
|
|
4
|
+
|
|
5
|
+
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
|
6
|
+
|
|
7
|
+
1. Definitions.
|
|
8
|
+
|
|
9
|
+
"License" shall mean the terms and conditions for use, reproduction,
|
|
10
|
+
and distribution as defined by Sections 1 through 9 of this document.
|
|
11
|
+
|
|
12
|
+
"Licensor" shall mean the copyright owner or entity authorized by
|
|
13
|
+
the copyright owner that is granting the License.
|
|
14
|
+
|
|
15
|
+
"Legal Entity" shall mean the union of the acting entity and all
|
|
16
|
+
other entities that control, are controlled by, or are under common
|
|
17
|
+
control with that entity. For the purposes of this definition,
|
|
18
|
+
"control" means (i) the power, direct or indirect, to cause the
|
|
19
|
+
direction or management of such entity, whether by contract or
|
|
20
|
+
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
|
21
|
+
outstanding shares, or (iii) beneficial ownership of such entity.
|
|
22
|
+
|
|
23
|
+
"You" (or "Your") shall mean an individual or Legal Entity
|
|
24
|
+
exercising permissions granted by this License.
|
|
25
|
+
|
|
26
|
+
"Source" form shall mean the preferred form for making modifications,
|
|
27
|
+
including but not limited to software source code, documentation
|
|
28
|
+
source, and configuration files.
|
|
29
|
+
|
|
30
|
+
"Object" form shall mean any form resulting from mechanical
|
|
31
|
+
transformation or translation of a Source form, including but
|
|
32
|
+
not limited to compiled object code, generated documentation,
|
|
33
|
+
and conversions to other media types.
|
|
34
|
+
|
|
35
|
+
"Work" shall mean the work of authorship, whether in Source or
|
|
36
|
+
Object form, made available under the License, as indicated by a
|
|
37
|
+
copyright notice that is included in or attached to the work
|
|
38
|
+
(an example is provided in the Appendix below).
|
|
39
|
+
|
|
40
|
+
"Derivative Works" shall mean any work, whether in Source or Object
|
|
41
|
+
form, that is based on (or derived from) the Work and for which the
|
|
42
|
+
editorial revisions, annotations, elaborations, or other modifications
|
|
43
|
+
represent, as a whole, an original work of authorship. For the purposes
|
|
44
|
+
of this License, Derivative Works shall not include works that remain
|
|
45
|
+
separable from, or merely link (or bind by name) to the interfaces of,
|
|
46
|
+
the Work and Derivative Works thereof.
|
|
47
|
+
|
|
48
|
+
"Contribution" shall mean any work of authorship, including
|
|
49
|
+
the original version of the Work and any modifications or additions
|
|
50
|
+
to that Work or Derivative Works thereof, that is intentionally
|
|
51
|
+
submitted to Licensor for inclusion in the Work by the copyright owner
|
|
52
|
+
or by an individual or Legal Entity authorized to submit on behalf of
|
|
53
|
+
the copyright owner. For the purposes of this definition, "submitted"
|
|
54
|
+
means any form of electronic, verbal, or written communication sent
|
|
55
|
+
to the Licensor or its representatives, including but not limited to
|
|
56
|
+
communication on electronic mailing lists, source code control systems,
|
|
57
|
+
and issue tracking systems that are managed by, or on behalf of, the
|
|
58
|
+
Licensor for the purpose of discussing and improving the Work, but
|
|
59
|
+
excluding communication that is conspicuously marked or otherwise
|
|
60
|
+
designated in writing by the copyright owner as "Not a Contribution."
|
|
61
|
+
|
|
62
|
+
"Contributor" shall mean Licensor and any individual or Legal Entity
|
|
63
|
+
on behalf of whom a Contribution has been received by Licensor and
|
|
64
|
+
subsequently incorporated within the Work.
|
|
65
|
+
|
|
66
|
+
2. Grant of Copyright License. Subject to the terms and conditions of
|
|
67
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
68
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
69
|
+
copyright license to reproduce, prepare Derivative Works of,
|
|
70
|
+
publicly display, publicly perform, sublicense, and distribute the
|
|
71
|
+
Work and such Derivative Works in Source or Object form.
|
|
72
|
+
|
|
73
|
+
3. Grant of Patent License. Subject to the terms and conditions of
|
|
74
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
75
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
76
|
+
(except as stated in this section) patent license to make, have made,
|
|
77
|
+
use, offer to sell, sell, import, and otherwise transfer the Work,
|
|
78
|
+
where such license applies only to those patent claims licensable
|
|
79
|
+
by such Contributor that are necessarily infringed by their
|
|
80
|
+
Contribution(s) alone or by combination of their Contribution(s)
|
|
81
|
+
with the Work to which such Contribution(s) was submitted. If You
|
|
82
|
+
institute patent litigation against any entity (including a
|
|
83
|
+
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
|
84
|
+
or a Contribution incorporated within the Work constitutes direct
|
|
85
|
+
or contributory patent infringement, then any patent licenses
|
|
86
|
+
granted to You under this License for that Work shall terminate
|
|
87
|
+
as of the date such litigation is filed.
|
|
88
|
+
|
|
89
|
+
4. Redistribution. You may reproduce and distribute copies of the
|
|
90
|
+
Work or Derivative Works thereof in any medium, with or without
|
|
91
|
+
modifications, and in Source or Object form, provided that You
|
|
92
|
+
meet the following conditions:
|
|
93
|
+
|
|
94
|
+
(a) You must give any other recipients of the Work or
|
|
95
|
+
Derivative Works a copy of this License; and
|
|
96
|
+
|
|
97
|
+
(b) You must cause any modified files to carry prominent notices
|
|
98
|
+
stating that You changed the files; and
|
|
99
|
+
|
|
100
|
+
(c) You must retain, in the Source form of any Derivative Works
|
|
101
|
+
that You distribute, all copyright, patent, trademark, and
|
|
102
|
+
attribution notices from the Source form of the Work,
|
|
103
|
+
excluding those notices that do not pertain to any part of
|
|
104
|
+
the Derivative Works; and
|
|
105
|
+
|
|
106
|
+
(d) If the Work includes a "NOTICE" text file as part of its
|
|
107
|
+
distribution, then any Derivative Works that You distribute must
|
|
108
|
+
include a readable copy of the attribution notices contained
|
|
109
|
+
within such NOTICE file, excluding those notices that do not
|
|
110
|
+
pertain to any part of the Derivative Works, in at least one
|
|
111
|
+
of the following places: within a NOTICE text file distributed
|
|
112
|
+
as part of the Derivative Works; within the Source form or
|
|
113
|
+
documentation, if provided along with the Derivative Works; or,
|
|
114
|
+
within a display generated by the Derivative Works, if and
|
|
115
|
+
wherever such third-party notices normally appear. The contents
|
|
116
|
+
of the NOTICE file are for informational purposes only and
|
|
117
|
+
do not modify the License. You may add Your own attribution
|
|
118
|
+
notices within Derivative Works that You distribute, alongside
|
|
119
|
+
or as an addendum to the NOTICE text from the Work, provided
|
|
120
|
+
that such additional attribution notices cannot be construed
|
|
121
|
+
as modifying the License.
|
|
122
|
+
|
|
123
|
+
You may add Your own copyright statement to Your modifications and
|
|
124
|
+
may provide additional or different license terms and conditions
|
|
125
|
+
for use, reproduction, or distribution of Your modifications, or
|
|
126
|
+
for any such Derivative Works as a whole, provided Your use,
|
|
127
|
+
reproduction, and distribution of the Work otherwise complies with
|
|
128
|
+
the conditions stated in this License.
|
|
129
|
+
|
|
130
|
+
5. Submission of Contributions. Unless You explicitly state otherwise,
|
|
131
|
+
any Contribution intentionally submitted for inclusion in the Work
|
|
132
|
+
by You to the Licensor shall be under the terms and conditions of
|
|
133
|
+
this License, without any additional terms or conditions.
|
|
134
|
+
Notwithstanding the above, nothing herein shall supersede or modify
|
|
135
|
+
the terms of any separate license agreement you may have executed
|
|
136
|
+
with Licensor regarding such Contributions.
|
|
137
|
+
|
|
138
|
+
6. Trademarks. This License does not grant permission to use the trade
|
|
139
|
+
names, trademarks, service marks, or product names of the Licensor,
|
|
140
|
+
except as required for reasonable and customary use in describing the
|
|
141
|
+
origin of the Work and reproducing the content of the NOTICE file.
|
|
142
|
+
|
|
143
|
+
7. Disclaimer of Warranty. Unless required by applicable law or
|
|
144
|
+
agreed to in writing, Licensor provides the Work (and each
|
|
145
|
+
Contributor provides its Contributions) on an "AS IS" BASIS,
|
|
146
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
147
|
+
implied, including, without limitation, any warranties or conditions
|
|
148
|
+
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
|
149
|
+
PARTICULAR PURPOSE. You are solely responsible for determining the
|
|
150
|
+
appropriateness of using or redistributing the Work and assume any
|
|
151
|
+
risks associated with Your exercise of permissions under this License.
|
|
152
|
+
|
|
153
|
+
8. Limitation of Liability. In no event and under no legal theory,
|
|
154
|
+
whether in tort (including negligence), contract, or otherwise,
|
|
155
|
+
unless required by applicable law (such as deliberate and grossly
|
|
156
|
+
negligent acts) or agreed to in writing, shall any Contributor be
|
|
157
|
+
liable to You for damages, including any direct, indirect, special,
|
|
158
|
+
incidental, or consequential damages of any character arising as a
|
|
159
|
+
result of this License or out of the use or inability to use the
|
|
160
|
+
Work (including but not limited to damages for loss of goodwill,
|
|
161
|
+
work stoppage, computer failure or malfunction, or any and all
|
|
162
|
+
other commercial damages or losses), even if such Contributor
|
|
163
|
+
has been advised of the possibility of such damages.
|
|
164
|
+
|
|
165
|
+
9. Accepting Warranty or Additional Liability. While redistributing
|
|
166
|
+
the Work or Derivative Works thereof, You may choose to offer,
|
|
167
|
+
and charge a fee for, acceptance of support, warranty, indemnity,
|
|
168
|
+
or other liability obligations and/or rights consistent with this
|
|
169
|
+
License. However, in accepting such obligations, You may act only
|
|
170
|
+
on Your own behalf and on Your sole responsibility, not on behalf
|
|
171
|
+
of any other Contributor, and only if You agree to indemnify,
|
|
172
|
+
defend, and hold each Contributor harmless for any liability
|
|
173
|
+
incurred by, or claims asserted against, such Contributor by reason
|
|
174
|
+
of your accepting any such warranty or additional liability.
|
|
175
|
+
|
|
176
|
+
END OF TERMS AND CONDITIONS
|
|
177
|
+
|
|
178
|
+
APPENDIX: How to apply the Apache License to your work.
|
|
179
|
+
|
|
180
|
+
To apply the Apache License to your work, attach the following
|
|
181
|
+
boilerplate notice, with the fields enclosed by brackets "[]"
|
|
182
|
+
replaced with your own identifying information. (Don't include
|
|
183
|
+
the brackets!) The text should be enclosed in the appropriate
|
|
184
|
+
comment syntax for the file format. We also recommend that a
|
|
185
|
+
file or class name and description of purpose be included on the
|
|
186
|
+
same "printed page" as the copyright notice for easier
|
|
187
|
+
identification within third-party archives.
|
|
188
|
+
|
|
189
|
+
Copyright [yyyy] [name of copyright owner]
|
|
190
|
+
|
|
191
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
192
|
+
you may not use this file except in compliance with the License.
|
|
193
|
+
You may obtain a copy of the License at
|
|
194
|
+
|
|
195
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
196
|
+
|
|
197
|
+
Unless required by applicable law or agreed to in writing, software
|
|
198
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
199
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
200
|
+
See the License for the specific language governing permissions and
|
|
201
|
+
limitations under the License.
|