Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions .github/workflows/checks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,28 @@ jobs:
uv run --offline watchpost compare examples/baseline.xml examples/after.xml \
--confirm-same-context --output "$output_dir/report.md"
diff -u examples/report.md "$output_dir/report.md"
- name: Build and smoke-test the installed wheel
run: |
uv build --out-dir "$RUNNER_TEMP/watchpost-dist"
uv venv "$RUNNER_TEMP/watchpost-wheel" --python "${{ matrix.python-version }}"
uv pip install --python "$RUNNER_TEMP/watchpost-wheel/bin/python" \
"$RUNNER_TEMP/watchpost-dist/"*.whl
cd "$RUNNER_TEMP"
"$RUNNER_TEMP/watchpost-wheel/bin/python" -I - <<'PY'
import os
import tomllib
from importlib.metadata import metadata
from pathlib import Path

expected = tomllib.loads(
(Path(os.environ["GITHUB_WORKSPACE"]) / "pyproject.toml").read_text()
)["project"]
installed = metadata("watchpost")
assert installed["Version"] == expected["version"]
assert installed["License-Expression"] == expected["license"]
PY
"$RUNNER_TEMP/watchpost-wheel/bin/watchpost" inspect \
"$GITHUB_WORKSPACE/examples/baseline.xml"
"$RUNNER_TEMP/watchpost-wheel/bin/watchpost" compare \
"$GITHUB_WORKSPACE/examples/baseline.xml" "$GITHUB_WORKSPACE/examples/after.xml" \
--confirm-same-context --output "$RUNNER_TEMP/wheel-report.md"
33 changes: 33 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# Changelog

## 0.1.0 — prepared, not yet released

Initial release candidate for local-first review of saved Nmap XML.
This entry describes the proposed v0.1.0 release, not an existing tag or registry publication.

### Included

- Inspect explicit IPv4/TCP observations in one saved scan.
- Compare completed TCP connect or SYN scans with matching Nmap versions and declared port coverage.
- Require human confirmation of scan location, intended targets, and remaining settings.
- Preserve missing observations as `not observed`, never assumed closed.
- Generate deterministic Markdown reports with evidence, limitations, suggested checks, and blank review fields.
- Protect existing report files and notes with non-replacing output.
- Reject unsafe XML, unsupported inputs, incompatible scans, and reported host timeouts.
- Provide three synthetic walkthroughs and a [user wiki](https://github.com/ejames-dev/watchpost/wiki).
- Distribute Watchpost under the MIT license.

### Boundaries

Python 3.11+ on Linux or Ubuntu WSL. CI covers Python 3.11, 3.12, and 3.13.
Inputs are limited to 10 MiB per snapshot. Report output requires a trusted directory with hard-link support.
Watchpost does not scan, upload data, or make runtime network calls.
It does not infer device/application identity or declare a network secure or compromised.

No live scanning, scheduling, dashboard, packet capture, UDP/IPv6 support, AI verdicts, or automatic remediation.

### Verification

See [the release-readiness checklist](docs/release-v0.1.0.md).
The package version alone does not prove publication.
Published releases, when available, appear on [GitHub Releases](https://github.com/ejames-dev/watchpost/releases).
21 changes: 21 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (c) 2026 Evan Newman

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
4 changes: 4 additions & 0 deletions MANIFEST.in
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
include LICENSE
include CHANGELOG.md
include uv.lock
include examples/baseline.xml
include examples/after.xml
include examples/incomplete.xml
Expand All @@ -6,3 +9,4 @@ include docs/brief-v0.1.md
include docs/compare-milestone.md
include docs/reporting-milestone.md
include docs/scenarios.md
include docs/release-v0.1.0.md
11 changes: 10 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,11 @@ Local-first, evidence-backed reviews of changes between saved Nmap scans.
**Status: inspection, comparison, Markdown reporting, and synthetic walkthroughs implemented.**
Two saved XML files can produce a change-review report. The [three walkthroughs](docs/scenarios.md)
cover expected deployment, unintended backend exposure, and an incomplete scan.
The package remains a development build (`0.1.0.dev0`), not a tagged v0.1 release.
**v0.1.0 is prepared for release, not yet tagged or published.**
See the [release notes](CHANGELOG.md) and [release-readiness checklist](docs/release-v0.1.0.md).

**[User wiki](https://github.com/ejames-dev/watchpost/wiki)** — setup, report interpretation,
scenarios, troubleshooting, and safety.

## Scope

Expand Down Expand Up @@ -176,6 +180,11 @@ The tests use the standard library's `unittest`. Pytest is an optional developme
- IP addresses are not device identities. Port numbers are not application identities.
- An observed change does not prove vulnerability or compromise.

## License

Watchpost is licensed under the [MIT License](LICENSE).
Third-party dependencies retain their own licenses.

## Roadmap

- [x] Safely inspect one saved IPv4/TCP scan.
Expand Down
121 changes: 121 additions & 0 deletions docs/release-v0.1.0.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
# v0.1.0 release readiness

Status: local release-readiness checks passed on 2026-10-03. PR review and CI remain required.
No release tag or registry publication is authorized by this checklist.
The package metadata targets `0.1.0`. That value alone does not mean a release exists.

## Scope and evidence

The [approved brief](brief-v0.1.md) defines the release scope.
The [changelog](../CHANGELOG.md) describes the proposed release.

| Requirement | Evidence in the repository |
|---|---|
| Offline report from saved XML | README report command, `examples/report.md`, CLI report tests |
| No-change result without a safety verdict | Identical/unchanged comparison and report tests |
| Explicit changes and newly observed ports | Comparison and report tests for both categories |
| Missing evidence never becomes closure | Missing/grouped observation tests |
| Invalid/incompatible inputs fail clearly | XML, metadata, scan-window, completion, and timeout tests |
| Three documented scenarios | `docs/scenarios.md`, `examples/incomplete.xml` |
| Repeatable findings | Deterministic output and sample-report equality tests |
| Existing reports and notes stay intact | Existing destination, symlink, race, and I/O-failure tests |

## Preparation changes

- Set package version to `0.1.0` and update the lockfile.
- Add the user-selected MIT license and package license metadata.
- Require Setuptools 77.0.3 or newer for modern license metadata.
- Include the license, changelog, lockfile, and release checklist in the source archive.
- Add repository, documentation, and issue links to package metadata.
- Add an installed-wheel build/smoke check to the existing Python CI matrix.
- Link the public GitHub wiki from the README.

No runtime behavior or runtime dependencies change in this preparation step.
The build-tool minimum changes, but dependency installation remains separate from offline application use.

## Verification commands

From the repository root:

```bash
uv sync --locked --dev --python 3.11
uv run --offline python -m unittest discover -s tests -v
uv run --offline pytest
uv run --offline ruff format --check .
uv run --offline ruff check .
```

Repeat the unittest suite on Python 3.12 and 3.13:

```bash
uv run --offline --isolated --python 3.12 python -m unittest discover -s tests -v
uv run --offline --isolated --python 3.13 python -m unittest discover -s tests -v
```

Build fresh artifacts in a new temporary directory:

```bash
build_dir="$(mktemp -d)"
uv build --out-dir "$build_dir"
```

Before release, inspect both archives for version/license metadata and unexpected files.
The source archive must contain the synthetic examples, tests, docs, license, changelog, and lockfile.
It must not contain `scans/`, `reports/`, credentials, or local environment files.
Install the wheel in a fresh environment outside the checkout.
Check help, inspection, comparison, report creation, rejected inputs, and preservation of existing notes.
Run the tests from an extracted source archive as well.

The CI build step checks installed version/license metadata and runs the wheel outside the source checkout.
Build and dependency installation can use the network. Watchpost must not use it at runtime.

## Audit status

Local results on 2026-10-03:

| Check | Result |
|---|---|
| Unittest suite | 50 tests passed on Python 3.11, 3.12, and 3.13 |
| Pytest | 50 tests and 125 subtests passed |
| Ruff format and lint | Passed |
| Locked dependency installation | Passed in the checkout and extracted source archive |
| Source archive and wheel | Built successfully, version `0.1.0`, MIT license present |
| Archive contents | Required docs/examples/tests/lock present, no private data directories |
| Extracted source archive | All 50 tests passed |
| Installed wheel outside checkout | Help, invalid arguments, inspect, compare, and report checks passed |
| Report safety | Sample bytes matched, existing notes preserved, timeout created no report |
| Offline behavior | Installed-code comparison passed with network entry points disabled |
| New CI shell step | Executed locally without modification except the Python matrix value |
| Package consistency | `uv pip check` passed for the installed wheel environment |
| Dependency advisory check | No known vulnerabilities reported for seven locked Linux runtime/dev dependencies |
| Self-review | No release-blocking correctness, security, performance, or clarity findings identified |

The advisory check used `pip-audit` with the PyPI service and made no dependency changes.
It sent package names and versions, not scan data. It does not cover every build tool or platform-specific dependency.
To repeat that separate, network-enabled maintenance check:

```bash
audit_dir="$(mktemp -d)"
uv export --locked --offline --no-emit-project --format requirements-txt \
--output-file "$audit_dir/requirements.txt"
uvx pip-audit --disable-pip --require-hashes --progress-spinner off \
--requirement "$audit_dir/requirements.txt"
```

The self-review covered XML parsing, evidence interpretation, report-path escaping, non-replacing output, and package boundaries.
The runtime modules are unchanged by release preparation.
Independent reviewer tools were unavailable. This is not a comprehensive independent security audit or a guarantee of safety.

## Before tagging or publishing

1. Review and merge the release-preparation PR through the normal repository workflow.
2. Wait for successful post-merge CI and sync a clean local `main`.
3. Obtain explicit approval for the release tag and publication destination.
4. Update release-status text with the actual release date and state.
5. Rebuild and verify artifacts from the exact approved release commit.
6. Create and verify the signed `v0.1.0` tag only after approval.
7. Publish the approved release notes and artifacts, then confirm the public result.
8. Update the wiki's status to match the published release.

Do not reuse artifacts built before the final commit.
No PyPI upload is part of this preparation. A registry release needs separate authorization and name/ownership checks.
11 changes: 9 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,15 +1,22 @@
[build-system]
requires = ["setuptools>=68"]
requires = ["setuptools>=77.0.3"]
build-backend = "setuptools.build_meta"

[project]
name = "watchpost"
version = "0.1.0.dev0"
version = "0.1.0"
description = "Local-first, evidence-backed reviews of changes between saved Nmap scans."
readme = "README.md"
license = "MIT"
license-files = ["LICENSE"]
requires-python = ">=3.11"
dependencies = ["defusedxml>=0.7.1,<0.8"]

[project.urls]
Repository = "https://github.com/ejames-dev/watchpost"
Documentation = "https://github.com/ejames-dev/watchpost/wiki"
Issues = "https://github.com/ejames-dev/watchpost/issues"

[project.scripts]
watchpost = "watchpost:main"

Expand Down
2 changes: 1 addition & 1 deletion uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading