TECHNICAL REFERENCE v0.1.3 RUNBOOK

Architecture & Agent Guide

Tidyorg is a configuration-driven control plane for GitHub organizations. It replaces fragile manual settings UI clicks with declarative YAML manifests, audited Pull Requests, and idempotent Terraform reconciliation.

Core Principles

  • Rules are bound to roles, not people: Team members receive access via role mappings. When a developer changes teams, only their single role assignment changes, not the repository rules.
  • Strict Separation of Proposer & Reconciler: The web dashboard only opens Pull Requests against the configuration repository. It does not call Terraform or talk to the Terraform daemon directly.
  • One-Way Reconciliation: Anything manually changed in the GitHub UI is reverted on the next apply. The YAML configuration is the sole authority.
  • Escalation Immunity: The dashboard UI and proposal service never generate edits for privileged.yml. Privilege escalation remains review-gated by CODEOWNERS and branch protection.

YAML Schema Specifications

The configuration directory resides at config/ and contains four distinct components:

1. organization.yml

Defines organization-wide role mappings, permission semantics, repository defaults, and the public organization profile.

config/organization.yml
version: 1
organization: your-org # Must match TF_VAR_github_org_name

roles:
  head-of-engineering:
    scope: organization
    repo_permission: admin

  mentor:
    scope: repository
    repo_permission: admin

  developer:
    scope: repository
    repo_permission: push

  viewer:
    scope: repository
    repo_permission: pull

# Default settings inherited by every repository unless overridden
defaults:
  visibility: private
  has_issues: true
  has_projects: false
  has_wiki: false
  delete_branch_on_merge: true
  allow_squash_merge: true
  allow_merge_commit: false
  allow_rebase_merge: false

  branch_protection:
    required_reviews: 2
    require_code_owner_review: true
    require_status_checks:
      - ci/test

profile:
  name: "Your Organization"
  description: "Declarative Infrastructure"
  blog: "https://example.com"

2. repositories/<repo-name>.yml

One file per repository. The filename must match the repository name on GitHub. Only fields that differ from organization.yml defaults need to be declared.

config/repositories/example-service.yml
description: "Payment gateway integration service"
language: go
visibility: private
default_branch: main

# Mentors get 'admin' permission on the repository (at least 1 required)
mentors:
  - tony

# Developers receive 'push' permission
developers:
  - bob
  - carol

# Viewers receive read-only pull permission
viewers:
  - george

# Branch protection overrides for this repository
protected_branches:
  main:
    required_reviews: 3 # Overrides default 2
  develop:
    required_reviews: 0 # Experimental branch

# Path-based review routing
code_owners:
  "/proto/": [tony]
  "/billing/": [bob]

3. people.yml

Contains the flat list of all GitHub usernames that belong to the organization. Members inherit the base permission defined in organization defaults, and receive repository access through team assignments.

config/people.yml
version: 1

members:
  - tony
  - bob
  - carol
  - george

4. privileged.yml

HUMANS ONLY: Defines organization owners and org-scoped roles. The dashboard does not generate edits for this file; GitHub CODEOWNERS and branch protection require administrator review before changes can be merged.

config/privileged.yml
version: 1

org_owners:
  - tony
  - frank

roles:
  head-of-engineering:
    - tony

GitHub Apps Configuration

Tidyorg uses up to two dedicated GitHub Apps. No personal access tokens (PATs) are used:

1. Engine Bot (Required)

Installed across the whole organization. Used by the Terraform runner to reconcile GitHub state.

Permission ScopeRequired AccessPurpose
Organization → AdministrationRead & WriteManage org settings, profile, and base permissions
Organization → MembersRead & WriteReconcile team memberships and org members
Repository → AdministrationRead & WriteCreate repositories, branch protection, and settings
Repository → ContentsRead & WriteManage CODEOWNERS and distributed files
Repository → IssuesRead & WriteManage default issue labels
Repository → WorkflowsRead & WriteDistribute CI/CD workflows
Repository → MetadataRead-onlyQuery repo information

2. Dashboard App (Optional)

Used only if running the web dashboard. Installed only on the configuration repository with minimal scopes.

Permission ScopeRequired AccessPurpose
Repository → ContentsRead & WriteCreate proposal branch & commits
Repository → Pull requestsRead & WriteOpen batched proposal PRs
Repository → ActionsRead-onlyDisplay live sync status badge
Repository → MetadataRead-onlyRead repository info

Docker & Compose Deployment

The standard docker-compose.ghcr.yml allows quick multi-container setup:

docker-compose.ghcr.yml
services:
  engine:
    image: \${TIDYORG_REGISTRY:-ghcr.io/uslanozan}/tidyorg:\${TIDYORG_VERSION:-0.1.3}
    pull_policy: always
    volumes:
      - ./config:/config
      - ./state:/state
      - ./app.pem:/secrets/app.pem:ro
    environment:
      TF_VAR_github_org_name: your-org
      TF_VAR_github_app_id: "123456"
      TF_VAR_github_app_installation_id: "12345678"
    command: ["plan"]

  dashboard:
    image: \${TIDYORG_REGISTRY:-ghcr.io/uslanozan}/tidyorg-dashboard:\${TIDYORG_VERSION:-0.1.3}
    pull_policy: always
    ports:
      - "8080:8080"
    environment:
      GITHUB_CLIENT_ID: your_client_id
      CONFIG_OWNER: your-org
      CONFIG_REPO: your-config-repo
      CONFIG_BRANCH: main

To use the Docker Hub automated mirror instead of GHCR, set TIDYORG_REGISTRY=uslanozan.

Terraform State Backends

The TF_STATE environment variable specifies where Terraform persists state:

TF_STATE ValueLocationRequired Environment Variables
local (Default) Mounted /state volume None (zero external dependencies)
hcp HCP Terraform (Terraform Cloud) TF_CLOUD_ORGANIZATION, TF_WORKSPACE, TF_TOKEN_app_terraform_io
custom AWS S3, Google Cloud Storage, Azure Blob Mount your backend HCL file at /engine/backend.tf

Review Gates & Access Controls

In a production Tidyorg deployment, changes travel through two distinct review gates:

  1. Branch Protection on the Config Repo: The main branch of the configuration repository requires passing status checks (terraform plan) and at least 1 or 2 code review approvals.
  2. CODEOWNERS on privileged.yml: Only members of the org_admin_team (or designated organization owners) can approve modifications to config/privileged.yml.

Repository Deletion Safety Locks

Tidyorg is specifically architected to prevent catastrophic accidents:

  • No Hard Deletes in Dashboard: The dashboard UI does not feature a hard delete button. The most a mentor can propose is archiving a repository (archived: true), keeping the repository read-only while preserving all commit history, issues, PR discussions, and tags.
  • prevent_destroy Lifecycle Lock: The Terraform module has a prevent_destroy block on repository resources. If someone accidentally deletes a repository's YAML file, terraform apply will refuse to execute with a safety error.
  • Deliberate Destruction Runbook: To permanently delete a repository, a platform administrator must deliberately remove the YAML file via reviewed PR, manually run terraform state rm 'module.repositories["<repo>"]', and run the dedicated repository deletion script in the engine repository.

Unauthorized Dashboard Access Behavior

When users log into the web dashboard via GitHub Device Flow:

  • Non-Organization Members: Receive an immediate Access Denied screen (AccessDenied). No repositories, members, or settings are exposed.
  • Non-Mentors: Organization members who are not mentors on a repository see that repository in Read-Only mode. Buttons to add members, edit repository settings, or archive are hidden/disabled.
  • API Push Protection: Write access to proposal branches is granted exclusively to the generated tidyorg-dashboard-writers team. Any direct unauthorized git push is rejected by GitHub.

Handling Configuration Drift

If someone makes manual modifications in the GitHub web interface (for example, inviting a member directly, or disabling branch rules), those changes are treated as drift.

When terraform plan runs, it flags the delta. When terraform apply runs, the unauthorized changes are undone and reverted to match the YAML files. The only way to make permanent modifications is through a merged Pull Request against the configuration repository.