|
| 1 | +# Dual-Repo Drift Plan |
| 2 | + |
| 3 | +## Goal |
| 4 | + |
| 5 | +Keep one identical codebase in two repositories: |
| 6 | + |
| 7 | +- `owner/repo-public`: public merge surface, no secrets. |
| 8 | +- `owner/repo-private`: private policy engine and secret boundary. |
| 9 | + |
| 10 | +Operationally: |
| 11 | + |
| 12 | +- Drift detection runs in `repo-private` on a schedule. |
| 13 | +- If drift is detected, `repo-private` opens or updates a PR in `repo-public`. |
| 14 | +- After merge in `repo-public`, a sync-back path updates `repo-private` so both repos converge. |
| 15 | + |
| 16 | +This is eventual consistency with PR-driven enforcement, not direct autonomous mutation of both repos. |
| 17 | + |
| 18 | +## Roles and Trust Model |
| 19 | + |
| 20 | +- `repo-private` is the trust boundary (holds `APP_ID`, `APP_PRIVATE_KEY`, drift secrets). |
| 21 | +- `repo-private` is the policy engine (detects/enforces drift contract). |
| 22 | +- `repo-public` is the canonical merge/history surface. |
| 23 | +- Preferred auth model: GitHub App installation token for all cross-repo writes. |
| 24 | +- Rationale: avoids long-lived personal access tokens (PATs) and manual PAT rotation. |
| 25 | +- Cross-repo writes must not rely on `GITHUB_TOKEN` from another repo. |
| 26 | + |
| 27 | +## Repository and Remote Setup |
| 28 | + |
| 29 | +1. Create two repos: |
| 30 | + |
| 31 | +- `owner/repo-public` |
| 32 | +- `owner/repo-private` |
| 33 | + |
| 34 | +2. In one local clone, add both remotes and push the same branches to both. |
| 35 | +3. Keep one shared workflow codebase in git; gate execution by `github.repository` so behavior diverges by target repo, not by file divergence. |
| 36 | + |
| 37 | +## Workflow Gating |
| 38 | + |
| 39 | +Use explicit repo checks in every sensitive job. |
| 40 | + |
| 41 | +Examples: |
| 42 | + |
| 43 | +- Public-safe jobs: `if: github.repository == 'owner/repo-public'` |
| 44 | +- Secret drift jobs: `if: github.repository == 'owner/repo-private'` |
| 45 | + |
| 46 | +## Trigger Model |
| 47 | + |
| 48 | +- Drift/policy workflow in `repo-private`: scheduled (`cron`) trigger. |
| 49 | +- Public-to-private sync workflow: push-based from `repo-public` default branch (or optional manual dispatch if needed). |
| 50 | + |
| 51 | +Current intent: scheduled drift checks, not check-run gating. |
| 52 | + |
| 53 | +## GitHub App Requirements |
| 54 | + |
| 55 | +GitHub App is the default and recommended auth mechanism for this design. |
| 56 | + |
| 57 | +1. Create one GitHub App and install on both repos. |
| 58 | +2. Minimum permissions: |
| 59 | + |
| 60 | +- Contents: read/write (on target repo for branch push) |
| 61 | +- Pull requests: read/write (open/update PRs) |
| 62 | + |
| 63 | +3. Store only in `repo-private`: |
| 64 | + |
| 65 | +- `APP_ID` |
| 66 | +- `APP_PRIVATE_KEY` |
| 67 | +- Drift detection secrets |
| 68 | + |
| 69 | +## Drift Enforcement Flow (Private -> Public) |
| 70 | + |
| 71 | +1. `repo-private` scheduled job runs drift detection. |
| 72 | +2. If no drift: exit cleanly. |
| 73 | +3. If drift exists: |
| 74 | + |
| 75 | +- Mint installation token. |
| 76 | +- Target `repo-public`. |
| 77 | +- Create/update branch (for example `drift-sync/<topic>`). |
| 78 | +- Commit generated changes. |
| 79 | +- Open or update PR in `repo-public`. |
| 80 | + |
| 81 | +## Sync-Back Flow (Public -> Private) |
| 82 | + |
| 83 | +After PR merge in `repo-public`, run sync-back in the opposite direction: |
| 84 | + |
| 85 | +- Push or automation-driven mirror from `repo-public` to `repo-private`. |
| 86 | +- Keep branch tips aligned to maintain eventual consistency. |
| 87 | + |
| 88 | +## Loop and Safety Guards |
| 89 | + |
| 90 | +- Add workflow/job concurrency on drift workflow to avoid competing PRs. |
| 91 | +- Ignore bot branches (for example `drift-sync/*`) where appropriate. |
| 92 | +- Ignore bot-authored commits/events where appropriate. |
| 93 | +- Scope triggers (`branches`, `paths`, event types) to reduce self-trigger loops. |
| 94 | +- Keep public CI limited to non-secret work. |
| 95 | + |
| 96 | +## First Validation Pass |
| 97 | + |
| 98 | +1. Push identical workflow/code branches to both repos. |
| 99 | +2. Verify drift workflow runs only in `repo-private` on schedule. |
| 100 | +3. Verify public-safe jobs run only in `repo-public`. |
| 101 | +4. Force drift and confirm PR opens/updates in `repo-public`. |
| 102 | +5. Merge PR in `repo-public` and confirm sync-back updates `repo-private`. |
| 103 | +6. Negative test: verify public workflows cannot access private-only secrets. |
0 commit comments