Metadata-Version: 2.4
Name: coditation-sentinel
Version: 0.1.4
Summary: Local-first git commit guardrails: block secrets, forbidden files, and disallowed git identities before they are committed.
Author: Coditation
License: Proprietary
Project-URL: Homepage, https://github.com/rajeshd-coditation/coditation-sentinel
Project-URL: Repository, https://github.com/rajeshd-coditation/coditation-sentinel
Keywords: git,pre-commit,secrets,gitleaks,security,guardrails,compliance
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Operating System :: OS Independent
Classifier: Environment :: Console
Classifier: Topic :: Software Development :: Version Control :: Git
Classifier: Topic :: Security
Classifier: License :: Other/Proprietary License
Requires-Python: >=3.9
Description-Content-Type: text/markdown
Requires-Dist: PyYAML>=6.0
Provides-Extra: dev
Requires-Dist: pytest>=7.0; extra == "dev"

# Coditation Sentinel

Local-first git commit guardrails. Sentinel installs a global git hook that runs on
**every `git commit`, in every repository on the machine**, and blocks the commit
before anything leaves the laptop when it finds:

- 🔑 **Secrets** — API keys, tokens, private keys (via [gitleaks](https://github.com/gitleaks/gitleaks))
- 📄 **Forbidden files** — `.env`, `*.pem`, `*.key`, keystores, dumps, …
- 🧑‍💻 **Disallowed git identity** — commits authored with a personal email

It is built for a services company: developers commit to **customer repositories**
whose remotes and CI we may not control, so enforcement lives on the developer's
machine and never writes anything into the customer's repo. It is **host-agnostic** —
GitHub, GitLab, Bitbucket, Azure DevOps, or no remote at all are all protected the same,
because the check runs at `git commit`, before anything is pushed anywhere.

Secret scanning is **fail-closed**: if gitleaks can't run, the commit is **blocked**
rather than silently allowed, so no change is ever committed unscanned (override with
`secrets.require_gitleaks: false`).

## How it works

```
git commit
   │  git runs the global hook → sentinel scan --staged
   ▼  (scans only the staged diff, so commits stay fast)
 secrets? · forbidden file? · git identity?
   │
 clean → commit proceeds      problem → friendly message + fix, commit aborted
```

The hook is wired via `git config --global core.hooksPath`, so a single install
covers every repo automatically — nothing per-project, nothing added to customer code.

## Git identity — three tiers

| Identity | Example | Behavior |
|---|---|---|
| Company | `you@coditation.com` | ✅ silent pass |
| Customer / corporate | `you@acme-client.com` | ⚠️ allowed — "assuming valid identity" notice on every commit, recorded locally |
| Personal | `@gmail.com`, `@outlook.com`, … | ❌ always blocked |

We don't allow-list customer domains (impossible to maintain). Instead `policy.yaml`
keeps a **deny-list of personal providers**; the company domain passes, personal is
blocked, and everything else is treated as a legitimate customer identity.

Because tier 2 is "everything not on either list", an unrecognised address like
`abc@email.com` is **allowed** — Sentinel can't tell a real client domain from a typo,
so it says so explicitly: on **every commit** it prints a notice that it is *assuming* a
legitimate customer identity and shows how to correct it if that's wrong. (Set
`reporting.fyi_once_per_repo: true` to show it only once per repo instead.) To make a
domain a hard block, add it to `personal_providers`; to make it a silent pass, add it
to `company_domains`.

## Install

### Quick install (recommended) — one-shot scripts

Hand a developer the script for their OS from [`runner/`](runner/). It ensures
**Python 3.11**, installs Sentinel into an isolated virtualenv, installs gitleaks,
and wires the global git hook — no manual steps.

| OS | Run |
|----|-----|
| macOS | `bash runner/run-macos.sh` |
| Ubuntu / Debian | `bash runner/run-ubuntu.sh` |
| Windows | `powershell -ExecutionPolicy Bypass -File runner/run-windows.ps1` |

Open a new terminal afterwards so the PATH change applies. Override the install
source or versions via `SENTINEL_PACKAGE`, `PYTHON_VERSION`, `GITLEAKS_VERSION`
(see [`runner/README.md`](runner/README.md)). Prerequisite: **git**, plus a package
manager (Homebrew / apt / winget) so Python 3.11 can be installed if missing.

### Manual install

`coditation-sentinel` is published on **public PyPI**, so this needs no credentials.
Since it's a command-line tool that wires a **global** git hook, install it with
[`pipx`](https://pipx.pypa.io/) — it puts the `sentinel` command on your PATH in an
isolated environment:

```bash
brew install pipx                    # macOS (Linux: python3 -m pip install --user pipx)
pipx ensurepath                      # adds pipx's bin dir to PATH — open a new terminal after
pipx install coditation-sentinel     # installs the `sentinel` command globally
sentinel install                     # sets global core.hooksPath + writes default policy
brew install gitleaks                # required for secret scanning (macOS; see gitleaks docs for Linux/Windows)
sentinel status                      # verify enforcement + gitleaks
```

> **macOS / Homebrew Python users:** a plain `pip install coditation-sentinel` fails
> with `error: externally-managed-environment` (PEP 668). Use `pipx` as above, or
> `pip install --user coditation-sentinel`. After a `pipx`/`--user` install, open a
> **new terminal** so the updated PATH takes effect — otherwise `sentinel` reports
> "command not found".

## Commands

| Command | Purpose |
|---|---|
| `sentinel install [--force]` | Install the global hook and default policy |
| `sentinel scan --staged` | Scan staged changes (this is what the hook calls) |
| `sentinel status` | Show enforcement status, policy source, resolved gitleaks path + whether secret scanning is active |
| `sentinel selftest` | Verify the hook blocks a bad commit (throwaway repo, auto-deleted) |
| `sentinel uninstall` | Remove the hook and revert `core.hooksPath` |

## Configuration

Policy lives at `~/.coditation-sentinel/policy.yaml` (a copy of the packaged default).
Edit it to adjust company domains, the personal-provider deny-list, and forbidden-file
patterns. gitleaks rules live alongside it in `gitleaks.toml`.

**Policy auto-upgrades.** The packaged policy carries a `version:`. When you upgrade
Sentinel and run `sentinel install` (which re-running a `runner/` script does), an
older installed `policy.yaml` is automatically upgraded to the new version — your old
file is copied to `policy.yaml.bak-<timestamp>` first, so re-apply any customisations
from it. A policy already at the current version is left untouched; `--force` always
resets to the packaged default (also keeping a backup).

Key secret-scanning knob: `secrets.require_gitleaks` (default `true`) makes scanning
**fail-closed** — if gitleaks is missing or errors, the commit is blocked. Set it to
`false` only to knowingly accept committing without secret scanning. Sentinel finds
gitleaks on `PATH` and in the usual install dirs (`~/.local/bin`, `/usr/local/bin`,
`/opt/homebrew/bin`, `%LOCALAPPDATA%\CoditationSentinel\bin`), so an installed binary
still works even when the git hook runs with a reduced `PATH`.

A metadata-only log of non-Coditation identities used is kept at
`~/.coditation-sentinel/identity-usage.log` (timestamp, repo, email — never code).

## Scope (Phase 1)

In: secrets, forbidden files, three-tier identity, global hook, CLI.
Out (later): dependency/IaC scanning, PII detection, MDM/device enforcement, central
dashboards. The local hook is the guarantee; server-side CI is intentionally not relied on.

## Development

```bash
python3 -m venv .venv && . .venv/bin/activate
pip install -e ".[dev]"
pytest
```

## Publishing to PyPI (maintainers)

### Automated (recommended) — push a version tag

`.github/workflows/publish.yml` runs tests, builds, and publishes whenever a
`v*` tag is pushed (the tag must match `version` in `pyproject.toml`):

```bash
# bump version in pyproject.toml, commit, then:
git tag v0.1.1 && git push origin v0.1.1
```

This uses **PyPI Trusted Publishing** (OIDC) — no token stored in GitHub. One-time
setup on PyPI: project → *Settings → Publishing → Add a trusted publisher*:
owner `rajeshd-coditation`, repo `coditation-sentinel`, workflow `publish.yml`,
environment `pypi`. (Prefer a token? See the commented alternative in the workflow.)

### Manual

```bash
. .venv/bin/activate && pip install build twine
# bump `version` in pyproject.toml first (PyPI rejects re-uploading an existing version)
rm -rf dist build *.egg-info && python -m build && twine check dist/*
twine upload dist/*    # auth: __token__ + a PyPI API token
```

After it lands, `pipx install coditation-sentinel` and the `runner/` scripts work
with no credentials. The import package is `coditation_sentinel`; the CLI is `sentinel`.
