gestate 1.0.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.
@@ -0,0 +1,39 @@
1
+ name: Docs
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ workflow_dispatch:
7
+
8
+ permissions:
9
+ contents: read
10
+ pages: write
11
+ id-token: write
12
+
13
+ concurrency:
14
+ group: pages
15
+ cancel-in-progress: false
16
+
17
+ jobs:
18
+ build:
19
+ runs-on: ubuntu-latest
20
+ steps:
21
+ - uses: actions/checkout@v4
22
+ - uses: astral-sh/setup-uv@v6
23
+ with:
24
+ enable-cache: true
25
+ - run: uv sync --group docs --frozen
26
+ - run: uv run zensical build
27
+ - uses: actions/upload-pages-artifact@v3
28
+ with:
29
+ path: site
30
+
31
+ deploy:
32
+ needs: build
33
+ runs-on: ubuntu-latest
34
+ environment:
35
+ name: github-pages
36
+ url: ${{ steps.deployment.outputs.page_url }}
37
+ steps:
38
+ - uses: actions/deploy-pages@v4
39
+ id: deployment
@@ -0,0 +1,29 @@
1
+ name: Release
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ - "v*"
7
+
8
+ jobs:
9
+ test:
10
+ runs-on: ubuntu-latest
11
+ steps:
12
+ - uses: actions/checkout@v4
13
+ - uses: astral-sh/setup-uv@v6
14
+ with:
15
+ enable-cache: true
16
+ - run: uv sync --frozen
17
+ - run: uv run pytest
18
+
19
+ publish:
20
+ needs: test
21
+ runs-on: ubuntu-latest
22
+ environment: pypi
23
+ permissions:
24
+ id-token: write
25
+ steps:
26
+ - uses: actions/checkout@v4
27
+ - uses: astral-sh/setup-uv@v6
28
+ - run: uv build
29
+ - uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,18 @@
1
+ __pycache__/
2
+ *.py[cod]
3
+ *$py.class
4
+
5
+ .venv/
6
+ .pytest_cache/
7
+ .ruff_cache/
8
+ .mypy_cache/
9
+
10
+ build/
11
+ dist/
12
+ site/
13
+ *.egg-info/
14
+
15
+ .coverage
16
+ htmlcov/
17
+
18
+ .DS_Store
gestate-1.0.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Lincoln Loop
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.
gestate-1.0.0/PKG-INFO ADDED
@@ -0,0 +1,89 @@
1
+ Metadata-Version: 2.4
2
+ Name: gestate
3
+ Version: 1.0.0
4
+ Summary: Set a minimum release age on local package managers to defend against zero-day supply chain attacks.
5
+ Author-email: Lincoln Loop <chris@lincolnloop.com>
6
+ License-Expression: MIT
7
+ License-File: LICENSE
8
+ Keywords: bun,cooldown,deno,npm,pip,pnpm,security,supply-chain,uv,yarn
9
+ Classifier: Development Status :: 4 - Beta
10
+ Classifier: Environment :: Console
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: Intended Audience :: System Administrators
13
+ Classifier: Operating System :: MacOS
14
+ Classifier: Operating System :: POSIX :: Linux
15
+ Classifier: Topic :: Security
16
+ Classifier: Topic :: Software Development :: Build Tools
17
+ Classifier: Topic :: System :: Installation/Setup
18
+ Requires-Python: >=3.11
19
+ Requires-Dist: rich>=13
20
+ Requires-Dist: tomli-w>=1.0
21
+ Description-Content-Type: text/markdown
22
+
23
+ # gestate
24
+
25
+ Set a minimum release age on local package managers so installs ignore versions younger than `N` days. Most malicious packages are caught and yanked within days of publishing; refusing fresh versions blocks the bulk of supply-chain attacks.
26
+
27
+ ## Run
28
+
29
+ ```
30
+ uvx gestate # interactive
31
+ uvx gestate set 3 # 3-day minimum, installed tools only
32
+ uvx gestate set 3 --all # also pre-configure file-based tools (bun, deno, uv)
33
+ uvx gestate revert # remove gestate's settings
34
+ uvx gestate explain bun # show how one tool's setting is stored
35
+ ```
36
+
37
+ Interactive mode always shows a plan and asks for explicit confirmation before touching anything. The subcommands skip the confirm — meant for scripts, not your daily shell. Running with no subcommand outside a TTY exits with an error.
38
+
39
+ Plain text output is used when stdout isn't a terminal (no Rich tables/colors).
40
+
41
+ ## What it sets
42
+
43
+ | Tool | Where | Key (unit) |
44
+ |---|---|---|
45
+ | npm | `~/.npmrc` | `min-release-age` (days) |
46
+ | pnpm | global pnpm config | `minimumReleaseAge` (minutes) |
47
+ | yarn | `~/.yarnrc.yml` (4.10+) | `npmMinimalAgeGate` (minutes) |
48
+ | bun | `~/.bunfig.toml` | `[install] minimumReleaseAge` (seconds) |
49
+ | deno | shell profile | `alias deno='command deno --minimum-dependency-age=P<N>D'` |
50
+ | pip | user pip config | `global.uploaded-prior-to` (`P<N>D`) |
51
+ | uv | `~/.config/uv/uv.toml` | `exclude-newer` (`"N days"`) |
52
+
53
+ `gestate explain <tool>` prints that tool's current value and the exact mechanism `set` / `revert` use.
54
+
55
+ Scope:
56
+ - default — only configure installed tools
57
+ - `--all` — also pre-write config files for `bun`, `deno`, `uv` even if they aren't installed yet
58
+
59
+ ## Revert
60
+
61
+ `uvx gestate revert` removes everything gestate set:
62
+
63
+ - CLI tools — `config delete` / `config unset`
64
+ - `bun` / `uv` — remove the key; delete the file if it was the only key
65
+ - `deno` — remove our alias line; leave foreign `alias deno=` lines alone
66
+
67
+ Backups (`.bak`) are written next to any edited shell-profile or TOML file.
68
+
69
+ ## Allowlists
70
+
71
+ If you publish packages of your own and want them exempt from the delay, see [docs/allowlists.md](docs/allowlists.md). Most managers support an exclude list; npm and pip don't yet.
72
+
73
+ ## Caveats
74
+
75
+ - **yarn**: 4.10+ only. Older yarn is detected and skipped.
76
+ - **deno**: no global config exists; the shell alias only covers interactive shells. For CI, pass `--minimum-dependency-age=P<N>D` to `deno install`/`deno update`, or commit a project `deno.json` with `"minimumDependencyAge": "P<N>D"`.
77
+ - **npm exclude**: tracked in [npm/cli#8994](https://github.com/npm/cli/issues/8994).
78
+ - **pip exclude**: none — `global.uploaded-prior-to` is global only.
79
+
80
+ For PR-creation guards (Dependabot, Renovate) and adjacent layers, see [docs/complementary.md](docs/complementary.md).
81
+
82
+ ## Development
83
+
84
+ ```
85
+ uv sync
86
+ uv run pytest
87
+ ```
88
+
89
+ Requires Python 3.11+, macOS or Linux.
@@ -0,0 +1,67 @@
1
+ # gestate
2
+
3
+ Set a minimum release age on local package managers so installs ignore versions younger than `N` days. Most malicious packages are caught and yanked within days of publishing; refusing fresh versions blocks the bulk of supply-chain attacks.
4
+
5
+ ## Run
6
+
7
+ ```
8
+ uvx gestate # interactive
9
+ uvx gestate set 3 # 3-day minimum, installed tools only
10
+ uvx gestate set 3 --all # also pre-configure file-based tools (bun, deno, uv)
11
+ uvx gestate revert # remove gestate's settings
12
+ uvx gestate explain bun # show how one tool's setting is stored
13
+ ```
14
+
15
+ Interactive mode always shows a plan and asks for explicit confirmation before touching anything. The subcommands skip the confirm — meant for scripts, not your daily shell. Running with no subcommand outside a TTY exits with an error.
16
+
17
+ Plain text output is used when stdout isn't a terminal (no Rich tables/colors).
18
+
19
+ ## What it sets
20
+
21
+ | Tool | Where | Key (unit) |
22
+ |---|---|---|
23
+ | npm | `~/.npmrc` | `min-release-age` (days) |
24
+ | pnpm | global pnpm config | `minimumReleaseAge` (minutes) |
25
+ | yarn | `~/.yarnrc.yml` (4.10+) | `npmMinimalAgeGate` (minutes) |
26
+ | bun | `~/.bunfig.toml` | `[install] minimumReleaseAge` (seconds) |
27
+ | deno | shell profile | `alias deno='command deno --minimum-dependency-age=P<N>D'` |
28
+ | pip | user pip config | `global.uploaded-prior-to` (`P<N>D`) |
29
+ | uv | `~/.config/uv/uv.toml` | `exclude-newer` (`"N days"`) |
30
+
31
+ `gestate explain <tool>` prints that tool's current value and the exact mechanism `set` / `revert` use.
32
+
33
+ Scope:
34
+ - default — only configure installed tools
35
+ - `--all` — also pre-write config files for `bun`, `deno`, `uv` even if they aren't installed yet
36
+
37
+ ## Revert
38
+
39
+ `uvx gestate revert` removes everything gestate set:
40
+
41
+ - CLI tools — `config delete` / `config unset`
42
+ - `bun` / `uv` — remove the key; delete the file if it was the only key
43
+ - `deno` — remove our alias line; leave foreign `alias deno=` lines alone
44
+
45
+ Backups (`.bak`) are written next to any edited shell-profile or TOML file.
46
+
47
+ ## Allowlists
48
+
49
+ If you publish packages of your own and want them exempt from the delay, see [docs/allowlists.md](docs/allowlists.md). Most managers support an exclude list; npm and pip don't yet.
50
+
51
+ ## Caveats
52
+
53
+ - **yarn**: 4.10+ only. Older yarn is detected and skipped.
54
+ - **deno**: no global config exists; the shell alias only covers interactive shells. For CI, pass `--minimum-dependency-age=P<N>D` to `deno install`/`deno update`, or commit a project `deno.json` with `"minimumDependencyAge": "P<N>D"`.
55
+ - **npm exclude**: tracked in [npm/cli#8994](https://github.com/npm/cli/issues/8994).
56
+ - **pip exclude**: none — `global.uploaded-prior-to` is global only.
57
+
58
+ For PR-creation guards (Dependabot, Renovate) and adjacent layers, see [docs/complementary.md](docs/complementary.md).
59
+
60
+ ## Development
61
+
62
+ ```
63
+ uv sync
64
+ uv run pytest
65
+ ```
66
+
67
+ Requires Python 3.11+, macOS or Linux.
@@ -0,0 +1,38 @@
1
+ # Releasing
2
+
3
+ ## Publishing to PyPI
4
+
5
+ Requires PyPI credentials in your keyring (see below).
6
+
7
+ Tag the current release and push it:
8
+
9
+ ```bash
10
+ git pull
11
+ git tag --list "v*"
12
+ git tag -a vX.Y.Z -m "Release vX.Y.Z"
13
+ git push --tags
14
+ ```
15
+
16
+ Build and publish:
17
+
18
+ ```bash
19
+ rm -rf dist
20
+ uv build
21
+ uv publish
22
+ ```
23
+
24
+ ## PyPI Credentials Setup
25
+
26
+ Install keyring (used by uv for publishing) and set your token:
27
+
28
+ ```bash
29
+ uv tool install keyring
30
+ keyring set 'https://upload.pypi.org/legacy/' __token__
31
+ ```
32
+
33
+ Add these environment variables to your shell:
34
+
35
+ ```bash
36
+ export UV_KEYRING_PROVIDER=subprocess
37
+ export UV_PUBLISH_USERNAME=__token__
38
+ ```
@@ -0,0 +1,65 @@
1
+ # Allowlists
2
+
3
+ Each manager (where supported) lets you exempt specific packages from the release-age delay. Useful for your own org's packages and trusted hotfixes.
4
+
5
+ ## bun — `minimumReleaseAgeExcludes`
6
+
7
+ `~/.bunfig.toml`:
8
+
9
+ ```toml
10
+ [install]
11
+ minimumReleaseAge = 604800
12
+ minimumReleaseAgeExcludes = ["@myorg/*", "typescript"]
13
+ ```
14
+
15
+ Globs allowed. Matches every version of the package.
16
+
17
+ ## yarn — `npmPreapprovedPackages`
18
+
19
+ `~/.yarnrc.yml`:
20
+
21
+ ```yaml
22
+ npmMinimalAgeGate: 10080
23
+ npmPreapprovedPackages:
24
+ - "@myorg/*"
25
+ - "@aws-sdk/types@3.877.0"
26
+ ```
27
+
28
+ Globs or exact `name@version` locators.
29
+
30
+ ## pnpm — `minimumReleaseAgeExclude`
31
+
32
+ `pnpm-workspace.yaml` (or `.npmrc` with kebab-case keys):
33
+
34
+ ```yaml
35
+ minimumReleaseAge: 10080
36
+ minimumReleaseAgeExclude:
37
+ - '@myorg/*'
38
+ - nx@21.6.5
39
+ - 'webpack@4.47.0 || 5.102.1'
40
+ ```
41
+
42
+ Package name, glob, or `name@version-range`.
43
+
44
+ ## uv — `exclude-newer-package`
45
+
46
+ `pyproject.toml`:
47
+
48
+ ```toml
49
+ [tool.uv]
50
+ exclude-newer-package = { requests = "ALLOW_ANY", setuptools = "30 days" }
51
+ ```
52
+
53
+ `ALLOW_ANY` bypasses the cooldown for that package; a duration string applies a different window.
54
+
55
+ ## npm — not shipped
56
+
57
+ Tracked in [npm/cli#8994](https://github.com/npm/cli/issues/8994). Until then, `min-release-age` is global only.
58
+
59
+ ## pip — no equivalent
60
+
61
+ `global.uploaded-prior-to` is global. Pinning a specific newer version in `requirements.txt` is the workaround.
62
+
63
+ ## deno — no equivalent
64
+
65
+ `--minimum-dependency-age` is global per invocation.
@@ -0,0 +1,64 @@
1
+ # Complementary Controls
2
+
3
+ `gestate` guards the **install** step. Pair it with controls at other layers for defense in depth.
4
+
5
+ ## PR-creation layer
6
+
7
+ Stop bump PRs from landing fresh versions in the first place.
8
+
9
+ ### Dependabot cooldown (GitHub-native)
10
+
11
+ Added July 2025. Configured per ecosystem in `.github/dependabot.yml`:
12
+
13
+ ```yaml
14
+ version: 2
15
+ updates:
16
+ - package-ecosystem: npm
17
+ directory: /
18
+ schedule:
19
+ interval: daily
20
+ cooldown:
21
+ default-days: 7
22
+ semver-major-days: 14
23
+ ```
24
+
25
+ Supported on all Dependabot ecosystems. Different cooldowns per semver bump type are allowed.
26
+
27
+ See: <https://github.blog/changelog/2025-07-01-dependabot-supports-configuration-of-a-minimum-package-age/>
28
+
29
+ ### Renovate `minimumReleaseAge`
30
+
31
+ Renovate has shipped this for years. Set in `renovate.json`:
32
+
33
+ ```json
34
+ {
35
+ "minimumReleaseAge": "7 days"
36
+ }
37
+ ```
38
+
39
+ Applies to all managers Renovate supports (npm, pip, cargo, gomod, etc.).
40
+
41
+ ## Install-script layer
42
+
43
+ Most supply-chain payloads execute via `postinstall` / `preinstall` hooks. Disable them unless explicitly needed.
44
+
45
+ - **npm**: `npm config set ignore-scripts true` (global). Allow per-package via dedicated trusted lists.
46
+ - **pnpm**: `onlyBuiltDependencies` in `package.json` — explicit allowlist for which packages may run install scripts.
47
+ - **bun**: `trustedDependencies` in `package.json`.
48
+
49
+ ## Lockfile + provenance layer
50
+
51
+ - **Lockfiles committed**: `package-lock.json`, `pnpm-lock.yaml`, `bun.lock`, `uv.lock` — pin exact resolved versions so a malicious republish under the same range can't slip in silently.
52
+ - **npm provenance**: `npm publish --provenance` ties published artifacts to a verifiable CI build. Consumers can check `npm view <pkg> --json` for the `dist.attestations` field.
53
+ - **Sigstore / SLSA**: same idea, broader ecosystem coverage.
54
+
55
+ ## Layer summary
56
+
57
+ | Layer | Tool | Catches |
58
+ |-------|------|---------|
59
+ | PR creation | Dependabot cooldown, Renovate `minimumReleaseAge` | Bump PRs for fresh versions |
60
+ | Install resolution | `gestate` (this repo) | Direct + transitive + ad-hoc installs, CI without bots |
61
+ | Install scripts | `ignore-scripts`, `onlyBuiltDependencies`, `trustedDependencies` | Lifecycle-hook payloads |
62
+ | Identity | Lockfiles, provenance, Sigstore | Republish / typosquat / impersonation |
63
+
64
+ No single layer is sufficient. The install-time guard in this repo is the broadest net; the others narrow specific attack paths.
@@ -0,0 +1,37 @@
1
+ ---
2
+ icon: lucide/shield-check
3
+ ---
4
+
5
+ # gestate
6
+
7
+ Set a minimum release age on local package managers so installs ignore versions younger than `N` days. Most malicious packages are caught and yanked within days of publishing; refusing fresh versions blocks the bulk of supply-chain attacks.
8
+
9
+ ## Run
10
+
11
+ ```sh
12
+ uvx gestate # interactive
13
+ uvx gestate set 3 # 3-day minimum, installed tools only
14
+ uvx gestate set 3 --all # also pre-configure file-based tools (bun, deno, uv)
15
+ uvx gestate revert # remove gestate's settings
16
+ uvx gestate explain bun # show how one tool's setting is stored
17
+ ```
18
+
19
+ Interactive mode always shows a plan and asks for confirmation before touching anything. The subcommands skip the confirm — meant for scripts, not your daily shell.
20
+
21
+ ## What it configures
22
+
23
+ | Tool | Where | Key (unit) |
24
+ |---|---|---|
25
+ | npm | `~/.npmrc` | `min-release-age` (days) |
26
+ | pnpm | global pnpm config | `minimumReleaseAge` (minutes) |
27
+ | yarn | `~/.yarnrc.yml` (4.10+) | `npmMinimalAgeGate` (minutes) |
28
+ | bun | `~/.bunfig.toml` | `[install] minimumReleaseAge` (seconds) |
29
+ | deno | shell profile | `alias deno='command deno --minimum-dependency-age=P<N>D'` |
30
+ | pip | user pip config | `global.uploaded-prior-to` (`P<N>D`) |
31
+ | uv | `~/.config/uv/uv.toml` | `exclude-newer` (`"N days"`) |
32
+
33
+ ## More
34
+
35
+ - [Allowlists](allowlists.md) — exempt your own packages from the delay
36
+ - [Complementary controls](complementary.md) — pair gestate with PR-creation guards and other defense layers
37
+ - [Source on GitHub](https://github.com/lincolnloop/gestate)
@@ -0,0 +1,43 @@
1
+ [project]
2
+ name = "gestate"
3
+ version = "1.0.0"
4
+ description = "Set a minimum release age on local package managers to defend against zero-day supply chain attacks."
5
+ readme = "README.md"
6
+ requires-python = ">=3.11"
7
+ license = "MIT"
8
+ license-files = ["LICENSE"]
9
+ authors = [{ name = "Lincoln Loop", email = "chris@lincolnloop.com" }]
10
+ keywords = ["supply-chain", "security", "cooldown", "npm", "pip", "uv", "pnpm", "bun", "yarn", "deno"]
11
+ classifiers = [
12
+ "Development Status :: 4 - Beta",
13
+ "Environment :: Console",
14
+ "Intended Audience :: Developers",
15
+ "Intended Audience :: System Administrators",
16
+ "Topic :: Security",
17
+ "Topic :: Software Development :: Build Tools",
18
+ "Topic :: System :: Installation/Setup",
19
+ "Operating System :: MacOS",
20
+ "Operating System :: POSIX :: Linux",
21
+ ]
22
+ dependencies = [
23
+ "rich>=13",
24
+ "tomli-w>=1.0",
25
+ ]
26
+
27
+ [project.scripts]
28
+ gestate = "gestate.cli:main"
29
+
30
+ [dependency-groups]
31
+ dev = ["pytest>=8"]
32
+ docs = ["zensical>=0.0.43"]
33
+
34
+ [build-system]
35
+ requires = ["hatchling"]
36
+ build-backend = "hatchling.build"
37
+
38
+ [tool.hatch.build.targets.wheel]
39
+ packages = ["src/gestate"]
40
+
41
+ [tool.pytest.ini_options]
42
+ testpaths = ["tests"]
43
+ addopts = "-q"
@@ -0,0 +1,3 @@
1
+ """Configure package managers with a minimum release age."""
2
+
3
+ __version__ = "1.0.0"
@@ -0,0 +1,3 @@
1
+ from .cli import main
2
+
3
+ raise SystemExit(main())