servatus 0.0.1__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.
@@ -0,0 +1,31 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ pull_request:
6
+
7
+ permissions:
8
+ contents: read
9
+
10
+ jobs:
11
+ verify:
12
+ strategy:
13
+ matrix:
14
+ os: [ubuntu-latest, macos-latest]
15
+ runs-on: ${{ matrix.os }}
16
+ steps:
17
+ - uses: actions/checkout@v4
18
+ - uses: astral-sh/setup-uv@v6
19
+ with:
20
+ enable-cache: true
21
+ - uses: actions/setup-python@v5
22
+ with:
23
+ python-version: "3.11"
24
+ - run: uv sync --locked
25
+ - run: uv run pytest
26
+ - run: uv run ruff check .
27
+ - run: uv run ruff format --check .
28
+ - run: uv run pyright
29
+ - run: uv run vulture
30
+ - run: uv build
31
+ - run: uv run --isolated --no-project --with dist/*.whl python -c "import servatus"
@@ -0,0 +1,22 @@
1
+ name: Publish
2
+
3
+ on:
4
+ workflow_dispatch:
5
+ release:
6
+ types: [published]
7
+
8
+ permissions:
9
+ contents: read
10
+
11
+ jobs:
12
+ pypi:
13
+ runs-on: ubuntu-latest
14
+ environment: pypi
15
+ permissions:
16
+ contents: read
17
+ id-token: write
18
+ steps:
19
+ - uses: actions/checkout@v4
20
+ - uses: astral-sh/setup-uv@v6
21
+ - run: uv build
22
+ - uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,8 @@
1
+ .DS_Store
2
+ .pytest_cache/
3
+ .ruff_cache/
4
+ .venv/
5
+ __pycache__/
6
+ build/
7
+ dist/
8
+ *.egg-info/
@@ -0,0 +1,10 @@
1
+ # Agent guidance
2
+
3
+ Servatus favors small, deep interfaces and ordinary typed Python. Keep application meaning outside
4
+ the package, reject unsafe filesystem behavior, and prefer clean breaks over compatibility layers.
5
+
6
+ Use synthetic temporary directories for tests. Never contact SSH, Slurm, Apptainer, external
7
+ storage, or an application's real outputs from the test suite.
8
+
9
+ Run pytest, Ruff, Pyright, Vulture, package builds, and an installed-artifact smoke before handing
10
+ off a change.
@@ -0,0 +1,19 @@
1
+ # Contributing
2
+
3
+ Servatus keeps its public surface deliberately small. Changes should solve a demonstrated lifecycle
4
+ problem without importing application schemas, workflow topology, or backend plugin machinery.
5
+
6
+ Set up and verify the repository with:
7
+
8
+ ```sh
9
+ uv sync --locked
10
+ uv run pytest
11
+ uv run ruff check .
12
+ uv run ruff format --check .
13
+ uv run pyright
14
+ uv run vulture
15
+ uv build
16
+ ```
17
+
18
+ Tests must use synthetic temporary directories. Pull requests must not depend on a live scheduler,
19
+ cluster, container runtime, or external data store.
servatus-0.0.1/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Edoardo Galli
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,99 @@
1
+ Metadata-Version: 2.4
2
+ Name: servatus
3
+ Version: 0.0.1
4
+ Summary: Run resumable work through Slurm and atomically publish validated outputs.
5
+ Project-URL: Repository, https://github.com/edoski/servatus
6
+ Project-URL: Issues, https://github.com/edoski/servatus/issues
7
+ Author: Edoardo Galli
8
+ License-Expression: MIT
9
+ License-File: LICENSE
10
+ Classifier: Development Status :: 2 - Pre-Alpha
11
+ Classifier: License :: OSI Approved :: MIT License
12
+ Classifier: Operating System :: MacOS
13
+ Classifier: Operating System :: POSIX :: Linux
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3.11
16
+ Classifier: Programming Language :: Python :: 3.12
17
+ Classifier: Programming Language :: Python :: 3.13
18
+ Classifier: Typing :: Typed
19
+ Requires-Python: >=3.11
20
+ Description-Content-Type: text/markdown
21
+
22
+ # Servatus
23
+
24
+ Run resumable work through Slurm and atomically publish validated outputs.
25
+
26
+ Servatus 0.0.1 is a publication-only alpha. It provides a small Python interface for keeping
27
+ private resumable work and exposing an application-built directory atomically. Slurm campaigns
28
+ arrive in a later release.
29
+
30
+ ```sh
31
+ pip install servatus
32
+ ```
33
+
34
+ ## Publication
35
+
36
+ Use `publish` when failed work is disposable:
37
+
38
+ ```python
39
+ from pathlib import Path
40
+
41
+ from servatus import Draft, publish
42
+
43
+
44
+ def build(draft: Draft) -> None:
45
+ (draft.path / "result.json").write_text('{"status":"complete"}\n')
46
+
47
+
48
+ publication = publish(Path("outputs/run-1"), build)
49
+ ```
50
+
51
+ Use `Workspace` when a worker must retain private checkpoints across restarts:
52
+
53
+ ```python
54
+ from servatus import Draft, Workspace
55
+
56
+ destination = Path("outputs/model-1")
57
+ with Workspace(destination, identity=b"model request bytes") as workspace:
58
+ checkpoint = workspace.path / "last.ckpt"
59
+ # The application creates or resumes its own checkpoint here.
60
+
61
+ def assemble(draft: Draft) -> None:
62
+ draft.link(checkpoint, "last.ckpt")
63
+ # Perform application validation before returning.
64
+
65
+ publication = workspace.publish(assemble)
66
+ ```
67
+
68
+ `Workspace` binds its stable hidden state to the SHA-256 digest of the opaque identity and holds a
69
+ nonblocking writer lock. `Draft.link` only hard-links regular files into a safe relative path. The
70
+ application must not mutate a linked source inode after `Draft.link()` returns and before
71
+ publication completes. It owns file contents, validation, schemas, and completion meaning.
72
+
73
+ ## Guarantees
74
+
75
+ - A destination is absent or one complete directory; it is never overwritten.
76
+ - Work, hard-link sources, stages, and destination must share a filesystem.
77
+ - Files and directories are synced before a kernel-exclusive commit; the destination parent is
78
+ synced afterward.
79
+ - Builder failures expose no destination. Resumable work remains available, while disposable
80
+ stages are removed.
81
+ - Successful workspace publication removes private state. A cleanup failure returns
82
+ `Publication(cleanup_pending=True)` without misreporting the committed destination as failed.
83
+ - Symlinks, special files, escaping link paths, path substitution, and unsupported exclusive-rename
84
+ primitives fail closed.
85
+
86
+ Servatus supports POSIX filesystems on Linux and macOS. Hardware durability still depends on the
87
+ filesystem and mount. It is not a security boundary against another process that can arbitrarily
88
+ modify the same parent directory.
89
+
90
+ ## Non-goals in 0.0.1
91
+
92
+ Servatus does not interpret checkpoints or ML artifacts, decide when work is valid, model workflow
93
+ graphs, migrate old outputs, copy across filesystems, overwrite destinations, or provide scheduler
94
+ execution. It has no plugins, callbacks beyond the one build seam, runtime dependencies, daemon,
95
+ database, or global run store.
96
+
97
+ The public API is `Draft`, `Workspace`, `Publication`, `publish`, and the compact errors exported by
98
+ `servatus`. See [the context glossary](docs/CONTEXT.md) and [architecture decisions](docs/adr/README.md)
99
+ for the ownership boundary.
@@ -0,0 +1,78 @@
1
+ # Servatus
2
+
3
+ Run resumable work through Slurm and atomically publish validated outputs.
4
+
5
+ Servatus 0.0.1 is a publication-only alpha. It provides a small Python interface for keeping
6
+ private resumable work and exposing an application-built directory atomically. Slurm campaigns
7
+ arrive in a later release.
8
+
9
+ ```sh
10
+ pip install servatus
11
+ ```
12
+
13
+ ## Publication
14
+
15
+ Use `publish` when failed work is disposable:
16
+
17
+ ```python
18
+ from pathlib import Path
19
+
20
+ from servatus import Draft, publish
21
+
22
+
23
+ def build(draft: Draft) -> None:
24
+ (draft.path / "result.json").write_text('{"status":"complete"}\n')
25
+
26
+
27
+ publication = publish(Path("outputs/run-1"), build)
28
+ ```
29
+
30
+ Use `Workspace` when a worker must retain private checkpoints across restarts:
31
+
32
+ ```python
33
+ from servatus import Draft, Workspace
34
+
35
+ destination = Path("outputs/model-1")
36
+ with Workspace(destination, identity=b"model request bytes") as workspace:
37
+ checkpoint = workspace.path / "last.ckpt"
38
+ # The application creates or resumes its own checkpoint here.
39
+
40
+ def assemble(draft: Draft) -> None:
41
+ draft.link(checkpoint, "last.ckpt")
42
+ # Perform application validation before returning.
43
+
44
+ publication = workspace.publish(assemble)
45
+ ```
46
+
47
+ `Workspace` binds its stable hidden state to the SHA-256 digest of the opaque identity and holds a
48
+ nonblocking writer lock. `Draft.link` only hard-links regular files into a safe relative path. The
49
+ application must not mutate a linked source inode after `Draft.link()` returns and before
50
+ publication completes. It owns file contents, validation, schemas, and completion meaning.
51
+
52
+ ## Guarantees
53
+
54
+ - A destination is absent or one complete directory; it is never overwritten.
55
+ - Work, hard-link sources, stages, and destination must share a filesystem.
56
+ - Files and directories are synced before a kernel-exclusive commit; the destination parent is
57
+ synced afterward.
58
+ - Builder failures expose no destination. Resumable work remains available, while disposable
59
+ stages are removed.
60
+ - Successful workspace publication removes private state. A cleanup failure returns
61
+ `Publication(cleanup_pending=True)` without misreporting the committed destination as failed.
62
+ - Symlinks, special files, escaping link paths, path substitution, and unsupported exclusive-rename
63
+ primitives fail closed.
64
+
65
+ Servatus supports POSIX filesystems on Linux and macOS. Hardware durability still depends on the
66
+ filesystem and mount. It is not a security boundary against another process that can arbitrarily
67
+ modify the same parent directory.
68
+
69
+ ## Non-goals in 0.0.1
70
+
71
+ Servatus does not interpret checkpoints or ML artifacts, decide when work is valid, model workflow
72
+ graphs, migrate old outputs, copy across filesystems, overwrite destinations, or provide scheduler
73
+ execution. It has no plugins, callbacks beyond the one build seam, runtime dependencies, daemon,
74
+ database, or global run store.
75
+
76
+ The public API is `Draft`, `Workspace`, `Publication`, `publish`, and the compact errors exported by
77
+ `servatus`. See [the context glossary](docs/CONTEXT.md) and [architecture decisions](docs/adr/README.md)
78
+ for the ownership boundary.
@@ -0,0 +1,11 @@
1
+ # Security policy
2
+
3
+ Please report vulnerabilities privately through GitHub's security-advisory form for this
4
+ repository. Do not include sensitive paths, credentials, or research data in a public issue.
5
+
6
+ Only the latest released version receives security fixes.
7
+
8
+ Servatus is an unprivileged user library, not an authorization, sandboxing, tenant-isolation, or
9
+ cluster-policy layer. Its publication transaction protects against accidental partial visibility,
10
+ overwrites, and ordinary lifecycle races. Callers remain responsible for directory permissions,
11
+ trusted builders, application validation, filesystem guarantees, and scheduler policy.
@@ -0,0 +1,13 @@
1
+ # Servatus context
2
+
3
+ Servatus uses a small generic vocabulary:
4
+
5
+ - **Destination:** the application-owned canonical path. It is immutable once published.
6
+ - **Workspace:** stable, identity-bound private state retained when resumable work fails.
7
+ - **Identity:** opaque application bytes whose digest binds a workspace to one logical request.
8
+ - **Draft:** one unique, disposable directory assembled before publication.
9
+ - **Publication:** the committed destination plus whether private cleanup remains pending.
10
+ - **Builder:** the application callback that writes and validates a draft before returning.
11
+
12
+ Servatus owns lifecycle mechanics, not application meaning. Checkpoints, manifests, schemas,
13
+ validation rules, task topology, and scientific completion remain with the calling project.
@@ -0,0 +1,10 @@
1
+ # ADR 0001: Keep application meaning opaque
2
+
3
+ Status: accepted
4
+
5
+ Servatus accepts opaque identity bytes and one application-owned builder callback. The callback
6
+ writes the exact destination tree and performs domain validation before returning.
7
+
8
+ Servatus does not receive artifact schemas, validators, checkpoint formats, completion enums, or
9
+ workflow topology. This keeps the lifecycle reusable without becoming an ML framework or moving
10
+ scientific authority away from the calling project.
@@ -0,0 +1,15 @@
1
+ # ADR 0002: Publish through a durable POSIX transaction
2
+
3
+ Status: accepted
4
+
5
+ Servatus builds in a unique destination-adjacent stage, rejects unsafe entries, recursively syncs
6
+ content, and atomically renames the directory into an absent destination with the platform's
7
+ no-replace primitive. It then syncs the destination parent.
8
+
9
+ Linux uses `renameat2(RENAME_NOREPLACE)` and macOS uses descriptor-relative
10
+ `renameatx_np(RENAME_EXCL)`. Unsupported systems and filesystems fail closed. Work, stages, link
11
+ sources, and destinations must share one filesystem; Servatus never weakens the contract with a
12
+ copy or check-then-rename fallback.
13
+
14
+ An identity-bound workspace is retained after build failure and removed only after a committed,
15
+ parent-synced publication. Cleanup failure is reported separately from publication success.
@@ -0,0 +1,4 @@
1
+ # Architecture decisions
2
+
3
+ - [0001: Keep application meaning opaque](0001-opaque-application-seam.md)
4
+ - [0002: Publish through a durable POSIX transaction](0002-posix-workspace-publication.md)
@@ -0,0 +1,5 @@
1
+ # Issue tracker
2
+
3
+ GitHub Issues is the repository's issue tracker. Keep reports scoped to a reproducible Servatus
4
+ contract or implementation problem. Application-specific workflow and artifact questions belong in
5
+ the calling project's tracker.
@@ -0,0 +1,58 @@
1
+ [build-system]
2
+ requires = ["hatchling>=1.27"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "servatus"
7
+ version = "0.0.1"
8
+ description = "Run resumable work through Slurm and atomically publish validated outputs."
9
+ readme = "README.md"
10
+ requires-python = ">=3.11"
11
+ license = "MIT"
12
+ authors = [{ name = "Edoardo Galli" }]
13
+ classifiers = [
14
+ "Development Status :: 2 - Pre-Alpha",
15
+ "License :: OSI Approved :: MIT License",
16
+ "Operating System :: MacOS",
17
+ "Operating System :: POSIX :: Linux",
18
+ "Programming Language :: Python :: 3",
19
+ "Programming Language :: Python :: 3.11",
20
+ "Programming Language :: Python :: 3.12",
21
+ "Programming Language :: Python :: 3.13",
22
+ "Typing :: Typed",
23
+ ]
24
+ dependencies = []
25
+
26
+ [project.urls]
27
+ Repository = "https://github.com/edoski/servatus"
28
+ Issues = "https://github.com/edoski/servatus/issues"
29
+
30
+ [dependency-groups]
31
+ dev = [
32
+ "pyright>=1.1.404",
33
+ "pytest>=8.4",
34
+ "ruff>=0.12",
35
+ "vulture>=2.14",
36
+ ]
37
+
38
+ [tool.hatch.build.targets.wheel]
39
+ packages = ["src/servatus"]
40
+
41
+ [tool.pyright]
42
+ include = ["src"]
43
+ pythonVersion = "3.11"
44
+ typeCheckingMode = "strict"
45
+
46
+ [tool.pytest.ini_options]
47
+ testpaths = ["tests"]
48
+
49
+ [tool.ruff]
50
+ line-length = 100
51
+ target-version = "py311"
52
+
53
+ [tool.ruff.lint]
54
+ select = ["B", "E", "F", "I", "SIM", "UP"]
55
+
56
+ [tool.vulture]
57
+ min_confidence = 90
58
+ paths = ["src"]
@@ -0,0 +1,26 @@
1
+ from ._errors import (
2
+ CrossDevicePublication,
3
+ DestinationExists,
4
+ PublicationError,
5
+ ServatusError,
6
+ UnsafePublication,
7
+ UnsupportedPlatform,
8
+ WorkConflict,
9
+ WorkspaceBusy,
10
+ )
11
+ from ._workspace import Draft, Publication, Workspace, publish
12
+
13
+ __all__ = [
14
+ "CrossDevicePublication",
15
+ "DestinationExists",
16
+ "Draft",
17
+ "Publication",
18
+ "PublicationError",
19
+ "ServatusError",
20
+ "UnsafePublication",
21
+ "UnsupportedPlatform",
22
+ "WorkConflict",
23
+ "Workspace",
24
+ "WorkspaceBusy",
25
+ "publish",
26
+ ]
@@ -0,0 +1,30 @@
1
+ class ServatusError(Exception):
2
+ """Base class for Servatus contract failures."""
3
+
4
+
5
+ class PublicationError(ServatusError):
6
+ """A draft could not be safely published."""
7
+
8
+
9
+ class DestinationExists(PublicationError):
10
+ """The requested destination or draft entry already exists."""
11
+
12
+
13
+ class CrossDevicePublication(PublicationError):
14
+ """Publication would cross a filesystem boundary."""
15
+
16
+
17
+ class UnsafePublication(PublicationError):
18
+ """A path or filesystem entry violates the publication contract."""
19
+
20
+
21
+ class UnsupportedPlatform(PublicationError):
22
+ """The platform cannot provide an atomic no-replace commit."""
23
+
24
+
25
+ class WorkConflict(ServatusError):
26
+ """Existing private work is bound to another identity."""
27
+
28
+
29
+ class WorkspaceBusy(ServatusError):
30
+ """Another writer currently owns the workspace."""