# Configuration Management Practices for ai-job-search: Defensive Layered Security

> Learn configuration management practices for ai-job-search. Discover single source of truth, permission whitelisting, and CI validation for secure deployments.

- Repository: [Mads Lorentzen/ai-job-search](https://github.com/MadsLorentzen/ai-job-search)
- Tags: best-practices
- Published: 2026-07-25

---

**The ai-job-search repository implements a defensive, layered configuration management strategy centered on a single source of truth in the `.claude/` directory, strict permission whitelisting, and automated CI validation to prevent configuration drift and accidental data leakage.**

This open-source project by MadsLorentzen/ai-job-search manages AI agent configurations through a disciplined approach that separates template specifications from personal customizations. Configuration management practices for ai-job-search prioritize security and reproducibility by centralizing runtime settings under version control while blocking unsafe command execution through explicit allow-lists.

## Centralized Configuration Architecture

All runtime settings, skill specifications, and command definitions live in a hidden `.claude/` directory at the repository root. This thin-pointer design prevents duplication across multiple agent runtimes including Claude Code, Antigravity, and Codex.

The canonical configuration source resides in [`.claude/settings.json`](https://github.com/MadsLorentzen/ai-job-search/blob/main/.claude/settings.json), which acts as the central permission allow-list. Skill and command definitions are stored as markdown files within subdirectories, such as [`.claude/skills/job-application-assistant/01-candidate-profile.md`](https://github.com/MadsLorentzen/ai-job-search/blob/main/.claude/skills/job-application-assistant/01-candidate-profile.md). This structure ensures that every AI agent instance references identical configuration parameters without maintaining separate copies.

## Permission-First Security Model

The repository ships with a minimal permission whitelist that explicitly limits which commands the Claude runtime may invoke. This protects users from accidental execution of unsafe code by enumerating only approved operations rather than relying on broad exclusions.

The `"permissions": { "allow": [...] }` block in [`.claude/settings.json`](https://github.com/MadsLorentzen/ai-job-search/blob/main/.claude/settings.json) restricts execution to specific, vetted scripts:

- `Skill(job-application-assistant)`
- `Bash(bun run:*)`
- `Bash(python salary_lookup.py:*)`
- `Bash(python3 salary_lookup.py:*)`
- `Bash(pdftotext:*)`

Any attempt to broaden these permissions beyond the predefined set triggers a CI failure before merge.

## Automated CI Validation Pipeline

Every push or pull request triggers a multi-job GitHub Actions workflow defined in [`.github/workflows/ci.yml`](https://github.com/MadsLorentzen/ai-job-search/blob/main/.github/workflows/ci.yml) that enforces configuration integrity through four specialized validation stages.

### Lint and Schema Verification

A dedicated Python linter scans the `.claude/` tree for malformed skill definitions and missing required fields. The **lint** job executes [`tools/lint_skills.py`](https://github.com/MadsLorentzen/ai-job-search/blob/main/tools/lint_skills.py) to verify that all markdown skill files conform to the expected schema:

```yaml
jobs:
  lint:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-python@v5
        with:
          python-version: "3.12"
      - run: pip install pyyaml
      - run: python tools/lint_skills.py

```

### Security Guards and Permission Enforcement

The **security-guards** job runs [`tools/security_guards.py`](https://github.com/MadsLorentzen/ai-job-search/blob/main/tools/security_guards.py) to verify three critical invariants: the [`settings.json`](https://github.com/MadsLorentzen/ai-job-search/blob/main/settings.json) permission block has not been broadened, the repository's `.gitignore` protects personal data, and placeholder tokens remain in template files. The script enforces permission limits through explicit set comparison:

```python
import json, sys, pathlib

settings = json.load(open('.claude/settings.json'))
allowed = set(settings['permissions']['allow'])
if not allowed.issubset({
    "Skill(job-application-assistant)",
    "Bash(bun run:*)",
    "Bash(python salary_lookup.py:*)",
    "Bash(python3 salary_lookup.py:*)",
    "Bash(pdftotext:*)"}):
    sys.exit("❌ Unauthorized permission added!")

```

### Dependency and Placeholder Integrity

For upstream pull requests only, the **dependency-review** job invokes GitHub's official Dependency Review action to flag high-severity vulnerabilities. A separate **placeholder-integrity** job asserts that template files still contain placeholder markers such as `[YOUR_NAME]`, preventing accidental commits of personal data to the canonical repository.

## Upstream Update Management

Users can safely merge upstream changes without overwriting personalized files. The [`tools/check_upstream_updates.py`](https://github.com/MadsLorentzen/ai-job-search/blob/main/tools/check_upstream_updates.py) helper, documented in [`SETUP.md`](https://github.com/MadsLorentzen/ai-job-search/blob/main/SETUP.md), diffs the upstream template against the local fork and lists only files that would be affected:

```bash
python3 tools/check_upstream_updates.py

```

Additionally, [`tools/check_framework_version.py`](https://github.com/MadsLorentzen/ai-job-search/blob/main/tools/check_framework_version.py) runs exclusively when the repository is the official upstream, ensuring that forks do not unintentionally diverge from the canonical framework version.

## Summary

- **Centralized specifications** in [`.claude/settings.json`](https://github.com/MadsLorentzen/ai-job-search/blob/main/.claude/settings.json) eliminate configuration drift across AI agent runtimes.
- **Explicit permission whitelisting** restricts command execution to vetted scripts only, preventing arbitrary code execution.
- **Automated CI gates** in [`.github/workflows/ci.yml`](https://github.com/MadsLorentzen/ai-job-search/blob/main/.github/workflows/ci.yml) catch malformed skills, unauthorized permission changes, and missing placeholders before merge.
- **Security hardening** via [`tools/security_guards.py`](https://github.com/MadsLorentzen/ai-job-search/blob/main/tools/security_guards.py) validates `.gitignore` protections and placeholder integrity in every pull request.
- **Safe upstream synchronization** through [`tools/check_upstream_updates.py`](https://github.com/MadsLorentzen/ai-job-search/blob/main/tools/check_upstream_updates.py) preserves personal customizations when pulling template updates.

## Frequently Asked Questions

### How does ai-job-search prevent accidental execution of dangerous commands?

The repository uses an explicit allow-list model rather than block-listing. The [`.claude/settings.json`](https://github.com/MadsLorentzen/ai-job-search/blob/main/.claude/settings.json) file contains a strict `permissions.allow` array that enumerates only five approved command patterns. The CI pipeline enforces this list immutably through [`tools/security_guards.py`](https://github.com/MadsLorentzen/ai-job-search/blob/main/tools/security_guards.py), which exits with an error if any unauthorized permission is added.

### Where should I add new AI skills or commands?

All skill definitions belong in the `.claude/` directory, specifically under `.claude/skills/`. Each skill is a markdown file with a numbered prefix (e.g., [`01-candidate-profile.md`](https://github.com/MadsLorentzen/ai-job-search/blob/main/01-candidate-profile.md)). The [`tools/lint_skills.py`](https://github.com/MadsLorentzen/ai-job-search/blob/main/tools/lint_skills.py) linter validates these files during CI to ensure they contain required frontmatter fields and valid command references.

### How do I update my fork without losing personal data?

Run `python3 tools/check_upstream_updates.py` before pulling upstream changes. This script compares your local files against the upstream template and identifies which files would be modified, allowing you to preserve personalizations such as filled-in candidate profiles while incorporating template improvements.

### What prevents personal information from leaking into the public template?

The **placeholder-integrity** CI job continuously scans template files for placeholder tokens like `[YOUR_NAME]`. If these markers are removed (indicating real data has been inserted), the job fails and blocks the merge. Additionally, [`tools/security_guards.py`](https://github.com/MadsLorentzen/ai-job-search/blob/main/tools/security_guards.py) verifies that `.gitignore` properly excludes personal data directories.