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
52 changes: 52 additions & 0 deletions .github/workflows/secret-scan.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# Secret-scan gate for the public docs site.
#
# Blocks sensitive values from reaching docs.jacobpevans.com. Runs gitleaks-action
# with the org-wide private ruleset (the dryvist GITLEAKS_PRIVATE_CONFIG secret,
# injected at run time) and the org GITLEAKS_LICENSE_KEY. The action runs gitleaks
# with --redact, so matched values never appear in logs.
#
# The ruleset lives ONLY in the org secret — never committed — so the list of what
# counts as sensitive stays private. Public-output features (PR comments, SARIF
# artifact, job summary) are disabled: on a public repo they would expose the rule
# taxonomy / file locations on a catch. A finding fails the job; investigate from a
# trusted checkout with the private config.
#
# Make this a required check via branch protection on `main` after merge.
name: Secret Scan

on:
pull_request:
types: [opened, synchronize, reopened, ready_for_review]
push:
branches: [main]

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

permissions:
contents: read
pull-requests: read

jobs:
gitleaks:
name: gitleaks (redacted)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Materialize the org ruleset into a config file
env:
GITLEAKS_PRIVATE_CONFIG: ${{ secrets.GITLEAKS_PRIVATE_CONFIG }}
run: printf '%s' "$GITLEAKS_PRIVATE_CONFIG" > "$RUNNER_TEMP/gitleaks.toml"

- uses: gitleaks/gitleaks-action@v2
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GITLEAKS_LICENSE: ${{ secrets.GITLEAKS_LICENSE_KEY }}
GITLEAKS_CONFIG: ${{ runner.temp }}/gitleaks.toml
GITLEAKS_ENABLE_UPLOAD_ARTIFACT: "false"
GITLEAKS_ENABLE_COMMENTS: "false"
GITLEAKS_ENABLE_SUMMARY: "false"
7 changes: 4 additions & 3 deletions automation/scheduled-routines/claude-code-routines.mdx
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
---
title: "claude-code-routines"
description: "Six cron-scheduled Claude Code routines that watch the whole org without per-repo wiring. Pick up loose ends the event-triggered pipeline misses."
description: "Seven cron-scheduled Claude Code routines that watch the whole org without per-repo wiring. Pick up loose ends the event-triggered pipeline misses."
tier: 2
---

> Cron, not events. Org-wide, not per-repo. One configuration, scans everything.

[`JacobPEvans/claude-code-routines`](https://github.com/JacobPEvans/claude-code-routines) ships six prompt files that run on schedule inside Anthropic's cloud-hosted Claude Code sandbox. They discover repos under `$GH_OWNER` via `gh search`, do their work using `gh` CLI calls, and report results to Slack via the official Slack MCP connector. There is no per-repo wiring — drop a repo into the org and the routines pick it up on the next run.
[`JacobPEvans/claude-code-routines`](https://github.com/JacobPEvans/claude-code-routines) ships seven prompt files that run on schedule inside Anthropic's cloud-hosted Claude Code sandbox. They discover repos under `$GH_OWNER` via `gh search`, do their work using `gh` CLI calls, and report results to Slack via the official Slack MCP connector. There is no per-repo wiring — drop a repo into the org and the routines pick it up on the next run.

## The six routines
## The seven routines

| Routine | Schedule (CT) | What it does |
| --- | --- | --- |
Expand All @@ -17,6 +17,7 @@ tier: 2
| The Custodian | Daily 2:00 AM | Weighted-random maintenance: stale branch cleanup, missing labels, repo health audit |
| Issue Solver | Daily 7:00 AM + 7:00 PM | Picks one open issue, takes it from triage → draft PR in six phases |
| Daily Polish | Daily 11:00 PM | Deep-clean one repo per day: README gaps, missing `.gitignore`, CI config, test coverage |
| Docs Sync | Daily 3:13 AM | Reads 48h of estate change; opens a public `docs` PR (scrubbed) and a private `docs-starlight` PR (DRY links to docs.jacobpevans.com) |
| Weekly Scorecard | Mondays 5:00 AM | Portfolio health scores: repo count, open-issue median, CI pass rate, test coverage % |

## How the Issue Solver picks work
Expand Down
8 changes: 8 additions & 0 deletions security/scrubbed-values.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,14 @@ tier: 2

Never write a real value even in a "wrong" example — the example becomes committed text. Use `${USER}`, `${REPO_ROOT}`, `${MAINTAINER_EMAIL}`, `${NAS_HOST}`, `<redacted>` for shape stand-ins where the placeholder needs to look like a variable.

## Enforcement: the secret-scan gate

The public `docs` repo runs a fail-closed [gitleaks](https://github.com/gitleaks/gitleaks) check (`.github/workflows/secret-scan.yml`) on every PR and push to `main`, via [`gitleaks-action`](https://github.com/gitleaks/gitleaks-action). A finding fails the job, so a sensitive value cannot merge.

So the *list of what counts as sensitive stays private*, the rule set is **never committed**. It lives only as the `dryvist` org Actions secret `GITLEAKS_PRIVATE_CONFIG`, injected into CI at run time. The scan runs with `--redact`, and the public-output features (PR comments, SARIF artifact, job summary) are disabled, so neither the patterns nor any match ever surface publicly. `useDefault` covers generic credentials; org-specific rules cover the values that must never appear here.

The same gate runs on the draft PRs opened by the [Docs Sync routine](/automation/scheduled-routines/claude-code-routines), so machine-authored content is held to the same bar as anything authored by hand.

## Portable path references

**Never commit absolute user paths** (`/Users/{username}/*`, `/home/{username}/*`, `$HOME/*`, `~/*`).
Expand Down
Loading