pixie-lab-dibs 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.
- pixie_lab_dibs-0.1.0/.github/workflows/format.yml +46 -0
- pixie_lab_dibs-0.1.0/.github/workflows/publish.yml +62 -0
- pixie_lab_dibs-0.1.0/.github/workflows/test.yml +42 -0
- pixie_lab_dibs-0.1.0/.gitignore +13 -0
- pixie_lab_dibs-0.1.0/.pre-commit-config.yaml +53 -0
- pixie_lab_dibs-0.1.0/CHANGELOG.md +23 -0
- pixie_lab_dibs-0.1.0/LICENSE +28 -0
- pixie_lab_dibs-0.1.0/LICENSES/BSD-3-Clause.txt +11 -0
- pixie_lab_dibs-0.1.0/Makefile +37 -0
- pixie_lab_dibs-0.1.0/PKG-INFO +253 -0
- pixie_lab_dibs-0.1.0/README.md +231 -0
- pixie_lab_dibs-0.1.0/REUSE.toml +19 -0
- pixie_lab_dibs-0.1.0/pyproject.toml +67 -0
- pixie_lab_dibs-0.1.0/src/dibs/__init__.py +15 -0
- pixie_lab_dibs-0.1.0/src/dibs/_sources.py +153 -0
- pixie_lab_dibs-0.1.0/src/dibs/_sshsig.py +87 -0
- pixie_lab_dibs-0.1.0/src/dibs/_store.py +327 -0
- pixie_lab_dibs-0.1.0/src/dibs/_users.py +251 -0
- pixie_lab_dibs-0.1.0/src/dibs/app.py +344 -0
- pixie_lab_dibs-0.1.0/src/dibs/cli.py +727 -0
- pixie_lab_dibs-0.1.0/tests/__init__.py +3 -0
- pixie_lab_dibs-0.1.0/tests/conftest.py +136 -0
- pixie_lab_dibs-0.1.0/tests/test_app.py +155 -0
- pixie_lab_dibs-0.1.0/tests/test_cli.py +237 -0
- pixie_lab_dibs-0.1.0/tests/test_sources.py +74 -0
- pixie_lab_dibs-0.1.0/tests/test_store.py +115 -0
- pixie_lab_dibs-0.1.0/tests/test_users.py +153 -0
- pixie_lab_dibs-0.1.0/uv.lock +1151 -0
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# SPDX-FileCopyrightText: Simon A. F. Lund <os@safl.dk>
|
|
2
|
+
#
|
|
3
|
+
# SPDX-License-Identifier: BSD-3-Clause
|
|
4
|
+
|
|
5
|
+
name: format
|
|
6
|
+
|
|
7
|
+
# Only the default branch on push. Every other branch is covered by the pull
|
|
8
|
+
# request event, and running both would check each commit twice.
|
|
9
|
+
on:
|
|
10
|
+
push:
|
|
11
|
+
branches: [main]
|
|
12
|
+
pull_request:
|
|
13
|
+
|
|
14
|
+
permissions:
|
|
15
|
+
contents: read
|
|
16
|
+
|
|
17
|
+
jobs:
|
|
18
|
+
format:
|
|
19
|
+
runs-on: ubuntu-latest
|
|
20
|
+
steps:
|
|
21
|
+
- uses: actions/checkout@v7
|
|
22
|
+
|
|
23
|
+
- uses: actions/setup-python@v7
|
|
24
|
+
with:
|
|
25
|
+
python-version: "3.x"
|
|
26
|
+
|
|
27
|
+
- name: Cache pre-commit environments
|
|
28
|
+
uses: actions/cache@v6
|
|
29
|
+
with:
|
|
30
|
+
path: ~/.cache/pre-commit
|
|
31
|
+
key: pre-commit-${{ hashFiles('.pre-commit-config.yaml') }}
|
|
32
|
+
|
|
33
|
+
- name: Install pre-commit
|
|
34
|
+
run: pip install pre-commit
|
|
35
|
+
|
|
36
|
+
- name: make format
|
|
37
|
+
run: make format
|
|
38
|
+
|
|
39
|
+
# Most hooks fail when they rewrite a file, so the step above catches
|
|
40
|
+
# them. This catches the ones that would fix something and exit zero.
|
|
41
|
+
- name: Verify nothing changed
|
|
42
|
+
run: git diff --exit-code
|
|
43
|
+
|
|
44
|
+
- name: Show what changed
|
|
45
|
+
if: failure()
|
|
46
|
+
run: git --no-pager diff
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# SPDX-FileCopyrightText: Simon A. F. Lund <os@safl.dk>
|
|
2
|
+
#
|
|
3
|
+
# SPDX-License-Identifier: BSD-3-Clause
|
|
4
|
+
|
|
5
|
+
name: publish
|
|
6
|
+
|
|
7
|
+
# A version tag publishes that version to PyPI, as pixie-lab-dibs. The upload uses
|
|
8
|
+
# PyPI's trusted publishing, so no token is stored here: PyPI accepts it
|
|
9
|
+
# because it comes from this workflow in this repository.
|
|
10
|
+
on:
|
|
11
|
+
push:
|
|
12
|
+
tags: ["v*"]
|
|
13
|
+
|
|
14
|
+
permissions:
|
|
15
|
+
contents: read
|
|
16
|
+
|
|
17
|
+
jobs:
|
|
18
|
+
test:
|
|
19
|
+
strategy:
|
|
20
|
+
fail-fast: false
|
|
21
|
+
matrix:
|
|
22
|
+
python: ["3.11", "3.13"]
|
|
23
|
+
runs-on: ubuntu-latest
|
|
24
|
+
steps:
|
|
25
|
+
- uses: actions/checkout@v7
|
|
26
|
+
|
|
27
|
+
- uses: astral-sh/setup-uv@v7.2.1
|
|
28
|
+
with:
|
|
29
|
+
python-version: ${{ matrix.python }}
|
|
30
|
+
|
|
31
|
+
- name: make format
|
|
32
|
+
run: make format PRECOMMIT="uvx pre-commit"
|
|
33
|
+
|
|
34
|
+
- name: make check
|
|
35
|
+
run: make check
|
|
36
|
+
|
|
37
|
+
publish:
|
|
38
|
+
needs: test
|
|
39
|
+
runs-on: ubuntu-latest
|
|
40
|
+
permissions:
|
|
41
|
+
id-token: write
|
|
42
|
+
steps:
|
|
43
|
+
- uses: actions/checkout@v7
|
|
44
|
+
|
|
45
|
+
- uses: astral-sh/setup-uv@v7.2.1
|
|
46
|
+
with:
|
|
47
|
+
python-version: "3.13"
|
|
48
|
+
|
|
49
|
+
# Without this, a tag that does not match __version__ would publish
|
|
50
|
+
# the version in the code under a release named after the tag.
|
|
51
|
+
- name: Check the tag matches the version
|
|
52
|
+
run: |
|
|
53
|
+
version=$(sed -n 's/^__version__ = "\(.*\)"/\1/p' src/dibs/__init__.py)
|
|
54
|
+
if [ "v$version" != "$GITHUB_REF_NAME" ]; then
|
|
55
|
+
echo "tag $GITHUB_REF_NAME does not match __version__ $version" >&2
|
|
56
|
+
exit 1
|
|
57
|
+
fi
|
|
58
|
+
|
|
59
|
+
- name: make build
|
|
60
|
+
run: make build
|
|
61
|
+
|
|
62
|
+
- uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# SPDX-FileCopyrightText: Simon A. F. Lund <os@safl.dk>
|
|
2
|
+
#
|
|
3
|
+
# SPDX-License-Identifier: BSD-3-Clause
|
|
4
|
+
|
|
5
|
+
name: test
|
|
6
|
+
|
|
7
|
+
# Only the default branch on push. Every other branch is covered by the pull
|
|
8
|
+
# request event, and running both would check each commit twice.
|
|
9
|
+
on:
|
|
10
|
+
push:
|
|
11
|
+
branches: [main]
|
|
12
|
+
pull_request:
|
|
13
|
+
|
|
14
|
+
permissions:
|
|
15
|
+
contents: read
|
|
16
|
+
|
|
17
|
+
jobs:
|
|
18
|
+
test:
|
|
19
|
+
strategy:
|
|
20
|
+
fail-fast: false
|
|
21
|
+
matrix:
|
|
22
|
+
# The oldest and newest Python dibs supports.
|
|
23
|
+
python: ["3.11", "3.13"]
|
|
24
|
+
runs-on: ubuntu-latest
|
|
25
|
+
steps:
|
|
26
|
+
- uses: actions/checkout@v7
|
|
27
|
+
|
|
28
|
+
- uses: astral-sh/setup-uv@v7.2.1
|
|
29
|
+
with:
|
|
30
|
+
python-version: ${{ matrix.python }}
|
|
31
|
+
|
|
32
|
+
# The login tests sign and verify with ssh-keygen, which the runner
|
|
33
|
+
# image ships; they skip rather than pass if it is ever missing, so
|
|
34
|
+
# fail here instead.
|
|
35
|
+
- name: Check ssh-keygen is present
|
|
36
|
+
run: ssh-keygen -Y 2>&1 | grep -q sign
|
|
37
|
+
|
|
38
|
+
- name: make check
|
|
39
|
+
run: make check
|
|
40
|
+
|
|
41
|
+
- name: make build
|
|
42
|
+
run: make build
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# SPDX-FileCopyrightText: Simon A. F. Lund <os@safl.dk>
|
|
2
|
+
#
|
|
3
|
+
# SPDX-License-Identifier: BSD-3-Clause
|
|
4
|
+
|
|
5
|
+
# The license texts under LICENSES/ are kept away from the hooks that rewrite
|
|
6
|
+
# files. Editing a license to suit a formatter is the one thing that must never
|
|
7
|
+
# happen to one, whatever the formatter thinks of its whitespace.
|
|
8
|
+
exclude: ^LICENSES/
|
|
9
|
+
|
|
10
|
+
repos:
|
|
11
|
+
- repo: https://github.com/pre-commit/pre-commit-hooks
|
|
12
|
+
rev: v6.0.0
|
|
13
|
+
hooks:
|
|
14
|
+
- id: trailing-whitespace
|
|
15
|
+
- id: end-of-file-fixer
|
|
16
|
+
- id: mixed-line-ending
|
|
17
|
+
- id: check-merge-conflict
|
|
18
|
+
- id: check-yaml
|
|
19
|
+
- id: check-added-large-files
|
|
20
|
+
|
|
21
|
+
- repo: https://github.com/astral-sh/ruff-pre-commit
|
|
22
|
+
rev: v0.16.2
|
|
23
|
+
hooks:
|
|
24
|
+
- id: ruff-check
|
|
25
|
+
args: [--fix]
|
|
26
|
+
- id: ruff-format
|
|
27
|
+
|
|
28
|
+
- repo: https://github.com/hukkin/mdformat
|
|
29
|
+
rev: 1.0.0
|
|
30
|
+
hooks:
|
|
31
|
+
- id: mdformat
|
|
32
|
+
# Pinned because `pre-commit autoupdate` does not touch these, so an
|
|
33
|
+
# unpinned release would change formatting on an unrelated commit and
|
|
34
|
+
# fail CI there.
|
|
35
|
+
additional_dependencies:
|
|
36
|
+
- mdformat-gfm==1.0.0
|
|
37
|
+
|
|
38
|
+
- repo: https://github.com/fsfe/reuse-tool
|
|
39
|
+
rev: v6.2.0
|
|
40
|
+
hooks:
|
|
41
|
+
- id: reuse
|
|
42
|
+
|
|
43
|
+
- repo: local
|
|
44
|
+
hooks:
|
|
45
|
+
# Alternation, not a character class: pygrep matches on bytes, so a class
|
|
46
|
+
# of multi-byte characters becomes a class of their individual bytes and
|
|
47
|
+
# then flags unrelated letters. "æ" shares a byte with "…".
|
|
48
|
+
- id: ascii-punctuation
|
|
49
|
+
name: ASCII punctuation only
|
|
50
|
+
language: pygrep
|
|
51
|
+
entry: '(—|–|‘|’|“|”|…|→|←|•)'
|
|
52
|
+
types: [text]
|
|
53
|
+
exclude: ^\.pre-commit-config\.yaml$
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
SPDX-FileCopyrightText: Simon A. F. Lund <os@safl.dk>
|
|
3
|
+
|
|
4
|
+
SPDX-License-Identifier: BSD-3-Clause
|
|
5
|
+
-->
|
|
6
|
+
|
|
7
|
+
# Changelog
|
|
8
|
+
|
|
9
|
+
Follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
|
10
|
+
|
|
11
|
+
## [Unreleased]
|
|
12
|
+
|
|
13
|
+
## [0.1.0] - 2026-10-27
|
|
14
|
+
|
|
15
|
+
### Added
|
|
16
|
+
|
|
17
|
+
- The first version: things to claim, fed from a config file and from
|
|
18
|
+
pixie servers; claims that are all or nothing, never expire unless
|
|
19
|
+
asked to, and say when an agent made them; users who register an SSH
|
|
20
|
+
public key, are approved by the admin, and log in by signing a
|
|
21
|
+
one-time challenge; a login hook that shows a machine's claim; and
|
|
22
|
+
one `dibs` command for the server, its systemd service, the admin, and
|
|
23
|
+
everyone claiming.
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
BSD 3-Clause License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026, Simon A. F. Lund
|
|
4
|
+
|
|
5
|
+
Redistribution and use in source and binary forms, with or without
|
|
6
|
+
modification, are permitted provided that the following conditions are met:
|
|
7
|
+
|
|
8
|
+
1. Redistributions of source code must retain the above copyright notice, this
|
|
9
|
+
list of conditions and the following disclaimer.
|
|
10
|
+
|
|
11
|
+
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
12
|
+
this list of conditions and the following disclaimer in the documentation
|
|
13
|
+
and/or other materials provided with the distribution.
|
|
14
|
+
|
|
15
|
+
3. Neither the name of the copyright holder nor the names of its
|
|
16
|
+
contributors may be used to endorse or promote products derived from
|
|
17
|
+
this software without specific prior written permission.
|
|
18
|
+
|
|
19
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
20
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
21
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
22
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
23
|
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
24
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
25
|
+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
26
|
+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
27
|
+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
28
|
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
Copyright (c) Simon A. F. Lund <os@safl.dk>
|
|
2
|
+
|
|
3
|
+
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
|
4
|
+
|
|
5
|
+
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
|
6
|
+
|
|
7
|
+
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
|
|
8
|
+
|
|
9
|
+
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
|
|
10
|
+
|
|
11
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# SPDX-FileCopyrightText: Simon A. F. Lund <os@safl.dk>
|
|
2
|
+
#
|
|
3
|
+
# SPDX-License-Identifier: BSD-3-Clause
|
|
4
|
+
#
|
|
5
|
+
# Common tasks, and the home of the CI logic: the workflows call these
|
|
6
|
+
# targets, so everything CI does can be run locally. Run "make" for the list.
|
|
7
|
+
|
|
8
|
+
UV ?= uv
|
|
9
|
+
PRECOMMIT ?= pre-commit
|
|
10
|
+
|
|
11
|
+
.DEFAULT_GOAL := help
|
|
12
|
+
.PHONY: help format lint typecheck test build check
|
|
13
|
+
|
|
14
|
+
help: ## Show this help
|
|
15
|
+
@grep -hE '^[a-z-]+:.*?## ' $(MAKEFILE_LIST) \
|
|
16
|
+
| awk 'BEGIN{FS=":.*?## "}{printf " %-10s %s\n", $$1, $$2}'
|
|
17
|
+
|
|
18
|
+
format: ## Run every pre-commit hook: ruff, mdformat, reuse, ASCII punctuation
|
|
19
|
+
$(PRECOMMIT) run --all-files
|
|
20
|
+
|
|
21
|
+
lint: ## ruff check
|
|
22
|
+
$(UV) run ruff check .
|
|
23
|
+
|
|
24
|
+
typecheck: ## mypy --strict over src
|
|
25
|
+
$(UV) run mypy
|
|
26
|
+
|
|
27
|
+
test: ## pytest
|
|
28
|
+
$(UV) run pytest -q
|
|
29
|
+
|
|
30
|
+
build: ## Build the sdist and wheel, and check every command the wheel installs runs
|
|
31
|
+
rm -rf dist
|
|
32
|
+
$(UV) build
|
|
33
|
+
for cmd in $$(unzip -p dist/*.whl '*/entry_points.txt' | sed -n 's/ *=.*//p' | grep -v '^\['); do \
|
|
34
|
+
$(UV) tool run --from "$$(ls dist/*.whl)" "$$cmd" --help >/dev/null || exit 1; \
|
|
35
|
+
done
|
|
36
|
+
|
|
37
|
+
check: lint typecheck test ## Everything but format
|
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: pixie-lab-dibs
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Call dibs on shared lab machines: see who is using what, claim it, release it
|
|
5
|
+
Project-URL: Homepage, https://github.com/safl/pixie-lab-dibs
|
|
6
|
+
Author-email: "Simon A. F. Lund" <os@safl.dk>
|
|
7
|
+
License: BSD-3-Clause
|
|
8
|
+
License-File: LICENSE
|
|
9
|
+
Keywords: claims,coordination,lab,pixie,reservation
|
|
10
|
+
Classifier: Development Status :: 3 - Alpha
|
|
11
|
+
Classifier: Environment :: Console
|
|
12
|
+
Classifier: Intended Audience :: System Administrators
|
|
13
|
+
Classifier: License :: OSI Approved :: BSD License
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Requires-Python: >=3.11
|
|
16
|
+
Requires-Dist: fastapi>=0.115
|
|
17
|
+
Requires-Dist: httpx>=0.27
|
|
18
|
+
Requires-Dist: pyyaml>=6
|
|
19
|
+
Requires-Dist: tomlkit>=0.12
|
|
20
|
+
Requires-Dist: uvicorn[standard]>=0.30
|
|
21
|
+
Description-Content-Type: text/markdown
|
|
22
|
+
|
|
23
|
+
<!--
|
|
24
|
+
SPDX-FileCopyrightText: Simon A. F. Lund <os@safl.dk>
|
|
25
|
+
|
|
26
|
+
SPDX-License-Identifier: BSD-3-Clause
|
|
27
|
+
-->
|
|
28
|
+
|
|
29
|
+
# dibs
|
|
30
|
+
|
|
31
|
+
[](https://github.com/safl/pixie-lab-dibs/actions/workflows/test.yml)
|
|
32
|
+
[](https://github.com/safl/pixie-lab-dibs/actions/workflows/format.yml)
|
|
33
|
+
[](https://pypi.org/project/pixie-lab-dibs/)
|
|
34
|
+
[](https://pypi.org/project/pixie-lab-dibs/)
|
|
35
|
+
[](LICENSE)
|
|
36
|
+
[](https://api.reuse.software/info/github.com/safl/pixie-lab-dibs)
|
|
37
|
+
|
|
38
|
+
Call dibs on shared lab machines.
|
|
39
|
+
|
|
40
|
+
A lab machine is shared with people, ai-agents and automation-runners.
|
|
41
|
+
dibs lets each of them see who is on a machine before touching it, by
|
|
42
|
+
claiming it. A claim is a note, not a lock: SSH and the console stay
|
|
43
|
+
open to everyone. Everywhere a claim shows up, it reads as one sentence:
|
|
44
|
+
|
|
45
|
+
```
|
|
46
|
+
'alice' has claimed 'node-01' for 'xnvme' expires in: 'never'
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
dibs keeps a list of **things** to claim, mostly machines. It fills the
|
|
50
|
+
list from **sources**: a config file, and any number of
|
|
51
|
+
[pixie](https://github.com/safl/pixie) servers, whose machines show up
|
|
52
|
+
on their own, with their hardware inventory.
|
|
53
|
+
|
|
54
|
+
## Install
|
|
55
|
+
|
|
56
|
+
```
|
|
57
|
+
uv tool install pixie-lab-dibs
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
That gives you the `dibs` command, for both the server and the client.
|
|
61
|
+
Signing in uses `ssh-keygen` from OpenSSH 8.1 or newer.
|
|
62
|
+
|
|
63
|
+
## Run the server
|
|
64
|
+
|
|
65
|
+
On the host that will run it, list the things in `/etc/dibs/dibs.toml`:
|
|
66
|
+
|
|
67
|
+
```toml
|
|
68
|
+
[[pixie]]
|
|
69
|
+
url = "http://pixie:8080" # every machine pixie knows becomes a thing
|
|
70
|
+
|
|
71
|
+
[[thing]]
|
|
72
|
+
name = "ryzen-01" # a machine pixie does not boot
|
|
73
|
+
mac = "aa:bb:cc:dd:ee:ff" # any other key is kept as an attribute
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
Then make it a service and start it:
|
|
77
|
+
|
|
78
|
+
```
|
|
79
|
+
sudo dibs server install
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
The command writes a systemd unit for the `dibs` you ran, enables it
|
|
83
|
+
and starts it. `dibs server start`, `stop`, `restart`, `status` and
|
|
84
|
+
`logs` manage it from then on. The service runs as a throwaway system
|
|
85
|
+
user that owns only `/var/lib/dibs`. Without root, `dibs server install --user` installs a per-user service instead. For a quick try,
|
|
86
|
+
`dibs serve` runs the server in the foreground.
|
|
87
|
+
|
|
88
|
+
The system service can only run a `dibs` installed outside your home
|
|
89
|
+
directory, for example with
|
|
90
|
+
`sudo UV_TOOL_DIR=/opt/uv UV_TOOL_BIN_DIR=/usr/local/bin uv tool install pixie-lab-dibs`.
|
|
91
|
+
|
|
92
|
+
## Sign in
|
|
93
|
+
|
|
94
|
+
Claiming needs a user. Register a name with your SSH public key:
|
|
95
|
+
|
|
96
|
+
```
|
|
97
|
+
export DIBS_URL=http://dibs:8090
|
|
98
|
+
dibs register alice
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
The admin approves it on the server host:
|
|
102
|
+
|
|
103
|
+
```
|
|
104
|
+
sudo dibs admin approve alice
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
Then you log in once. `dibs` signs a one-time challenge with your key,
|
|
108
|
+
through your ssh-agent if the key lives there, and keeps the token it
|
|
109
|
+
gets back:
|
|
110
|
+
|
|
111
|
+
```
|
|
112
|
+
dibs login
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
The token does not expire; `dibs logout` drops it, and
|
|
116
|
+
`dibs admin revoke alice` ends every login alice has. `-i` picks a key
|
|
117
|
+
other than `~/.ssh/id_ed25519.pub`.
|
|
118
|
+
|
|
119
|
+
Over plain HTTP, a token can be read off the network. dibs gives claims
|
|
120
|
+
an identity among people who share a lab in good faith; it is not
|
|
121
|
+
protection against someone on that network. Put HTTPS in front of it if
|
|
122
|
+
the network is not trusted.
|
|
123
|
+
|
|
124
|
+
## Claim
|
|
125
|
+
|
|
126
|
+
```
|
|
127
|
+
dibs list [--mine]
|
|
128
|
+
dibs info <thing>
|
|
129
|
+
dibs claim <thing>... --for <purpose> [--expires 2h] [--force]
|
|
130
|
+
dibs release <thing>... | --mine
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
A claim never expires unless given `--expires`, and an expired claim
|
|
134
|
+
counts as free. Claiming several things is all or nothing: if any of
|
|
135
|
+
them is claimed by someone else, you get none, and the refusal says who
|
|
136
|
+
holds it. `--force` takes or releases someone else's claim, and the
|
|
137
|
+
server's log records that it did.
|
|
138
|
+
|
|
139
|
+
`list` and `info` print for people. Add `--json`, `--yaml` or `--toml`
|
|
140
|
+
to get the answer as data instead; an agent or script should use one of
|
|
141
|
+
those rather than parse the text.
|
|
142
|
+
|
|
143
|
+
The exit status is 0 when done, 1 when refused because someone else
|
|
144
|
+
holds a thing, and 2 for anything else.
|
|
145
|
+
|
|
146
|
+
### AI-agents
|
|
147
|
+
|
|
148
|
+
Point your agent at this section. An agent claims as its human, and
|
|
149
|
+
names itself the way an `Assisted-by:` trailer does:
|
|
150
|
+
|
|
151
|
+
```
|
|
152
|
+
export DIBS_ASSISTED_BY="Claude Code:claude-opus-5-5"
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
The claim then reads `'alice' assisted by 'Claude Code:claude-opus-5-5' has claimed ...`.
|
|
156
|
+
|
|
157
|
+
If you are an agent reading this: before you run anything on a lab
|
|
158
|
+
machine, run `dibs list` and claim the machine with
|
|
159
|
+
`dibs claim <machine> --for <purpose>`. Never use a machine someone else
|
|
160
|
+
has claimed, and never pass `--force`; ask your human instead. Release
|
|
161
|
+
what you claimed with `dibs release --mine` when the work is done, and
|
|
162
|
+
also when it fails or is abandoned. That releases only what you claimed,
|
|
163
|
+
not your human's own claims.
|
|
164
|
+
|
|
165
|
+
### Automation-runners
|
|
166
|
+
|
|
167
|
+
Give the runner a user of its own: register it with a key made for it,
|
|
168
|
+
approve it, log in once, and store the token as the runner's
|
|
169
|
+
`DIBS_TOKEN` secret. A refused claim exits 1, which a runner can wait
|
|
170
|
+
out; anything else exits 2 and should fail the job. In GitHub Actions:
|
|
171
|
+
|
|
172
|
+
```yaml
|
|
173
|
+
env:
|
|
174
|
+
DIBS_URL: http://dibs:8090
|
|
175
|
+
DIBS_TOKEN: ${{ secrets.DIBS_TOKEN }}
|
|
176
|
+
steps:
|
|
177
|
+
- name: Claim the machine
|
|
178
|
+
run: |
|
|
179
|
+
until dibs claim node-01 --for "${{ github.workflow }}" --expires 3h; do
|
|
180
|
+
[ $? -eq 1 ] || exit 1
|
|
181
|
+
sleep 60
|
|
182
|
+
done
|
|
183
|
+
- name: Test
|
|
184
|
+
run: ...
|
|
185
|
+
- name: Release the machine
|
|
186
|
+
if: always()
|
|
187
|
+
run: dibs release --mine
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
The `--expires` is a safety net for a runner that dies before its
|
|
191
|
+
release step.
|
|
192
|
+
|
|
193
|
+
## See the claim when you log in
|
|
194
|
+
|
|
195
|
+
Install the login hook on each machine. At every interactive login it
|
|
196
|
+
asks dibs for the machine's claim and prints it. It stays silent when
|
|
197
|
+
dibs is unreachable, and does nothing for non-interactive SSH such as
|
|
198
|
+
`scp`. It finds the machine by its MAC addresses, or else its hostname.
|
|
199
|
+
|
|
200
|
+
```
|
|
201
|
+
curl -fsS http://dibs:8090/login.sh | sudo tee /etc/profile.d/dibs.sh
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
## Shell completion
|
|
205
|
+
|
|
206
|
+
```
|
|
207
|
+
eval "$(dibs completion bash)"
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
Add that line to `~/.bashrc`, or write the script to
|
|
211
|
+
`/etc/bash_completion.d/dibs`. It completes commands and options, and
|
|
212
|
+
thing names from the server.
|
|
213
|
+
|
|
214
|
+
## HTTP
|
|
215
|
+
|
|
216
|
+
Anything that can send JSON can use dibs without the client. Reads need
|
|
217
|
+
no login; claim and release take `Authorization: Bearer <token>`.
|
|
218
|
+
|
|
219
|
+
| Route | |
|
|
220
|
+
| -------------------------- | -------------------------------------------------------------------------------------------- |
|
|
221
|
+
| `GET /things` | every thing, with its `sentence` and, while claimed, its `claim` |
|
|
222
|
+
| `GET /things/<name>` | one thing, plus `pixie` when a pixie server knows it |
|
|
223
|
+
| `GET /things/<name>/claim` | the sentence as one line of text |
|
|
224
|
+
| `POST /claims` | `{"things": [...], "purpose": "...", "assisted_by": "", "expires_in": null, "force": false}` |
|
|
225
|
+
| `POST /claims/release` | `{"things": [...], "force": false}`, or `{"mine": true, "assisted_by": ""}` |
|
|
226
|
+
| `POST /auth/register` | `{"user": "...", "pubkey": "ssh-ed25519 ..."}` |
|
|
227
|
+
| `POST /auth/challenge` | `{"user": "..."}` gives a nonce and the message to sign |
|
|
228
|
+
| `POST /auth/login` | `{"user", "nonce", "signature"}` gives a token |
|
|
229
|
+
| `GET /login.sh` | the login hook |
|
|
230
|
+
|
|
231
|
+
A claim or release blocked by someone else's claim answers 409 with
|
|
232
|
+
the blocking claims' sentences in `detail.claimed`.
|
|
233
|
+
|
|
234
|
+
## Releasing
|
|
235
|
+
|
|
236
|
+
Set the version in `src/dibs/__init__.py`, move the changelog's
|
|
237
|
+
`Unreleased` entries under that version, commit, and push a tag named
|
|
238
|
+
after it:
|
|
239
|
+
|
|
240
|
+
```
|
|
241
|
+
git tag v0.1.0 && git push origin v0.1.0
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
The tag runs the publish workflow, which checks the tag matches the
|
|
245
|
+
version, runs the checks, and uploads `pixie-lab-dibs` to PyPI through trusted
|
|
246
|
+
publishing.
|
|
247
|
+
|
|
248
|
+
## License
|
|
249
|
+
|
|
250
|
+
BSD-3-Clause. The repository follows [REUSE](https://reuse.software):
|
|
251
|
+
every file states its copyright and license, `LICENSES/` holds the
|
|
252
|
+
license text, and `REUSE.toml` covers the files that cannot carry a
|
|
253
|
+
header.
|