{"id":51384744,"url":"https://github.com/creatornader/oss-twin","last_synced_at":"2026-07-03T19:09:35.941Z","repository":{"id":358405827,"uuid":"1241246292","full_name":"creatornader/oss-twin","owner":"creatornader","description":"Keep a public OSS repo and its private mirror structurally aligned. Pairs with leakguard.","archived":false,"fork":false,"pushed_at":"2026-05-17T09:14:50.000Z","size":19,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-17T09:32:57.419Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/creatornader.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-05-17T06:20:03.000Z","updated_at":"2026-05-17T09:14:54.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/creatornader/oss-twin","commit_stats":null,"previous_names":["creatornader/oss-twin"],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/creatornader/oss-twin","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/creatornader%2Foss-twin","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/creatornader%2Foss-twin/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/creatornader%2Foss-twin/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/creatornader%2Foss-twin/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/creatornader","download_url":"https://codeload.github.com/creatornader/oss-twin/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/creatornader%2Foss-twin/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":35097966,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-07-03T02:00:05.635Z","response_time":110,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-07-03T19:09:31.378Z","updated_at":"2026-07-03T19:09:35.937Z","avatar_url":"https://github.com/creatornader.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# oss-twin\n\nKeep a public OSS repo and its private mirror structurally aligned.\n\nMany OSS maintainers run a private sister repo alongside their public one (atrib + atrib-internal, foo + foo-internal). The private repo holds operator memory, design specs with rejected alternatives, vendor IDs, deploy notes, and anything else that shouldn't be public but is too valuable to lose to git history archaeology. **oss-twin manages the boundary**: it scaffolds the mirror, gates the public repo against private paths leaking back in, and moves files cleanly across the boundary when you decide something needs to be private.\n\nIt pairs with [`textleaks`](https://github.com/creatornader/textleaks) (catches narrative leaks in file CONTENT) and the usual credential scanners (gitleaks, trufflehog). oss-twin owns the structural side: file LOCATION, not file content.\n\n## Install\n\nInstall directly from GitHub (PyPI publication pending — the name is reserved on PyPI but the package is not yet uploaded):\n\n```sh\npip install \"git+https://github.com/creatornader/oss-twin.git@v0.1.1\"\n```\n\nOr run as a `pre-commit` hook (see below). The pre-commit framework handles the install for you.\n\n## Use\n\n```sh\noss-twin scaffold-config    # write a starter .oss-twin.yaml\noss-twin init               # scaffold the private mirror at the configured path\noss-twin check              # fail if any private_paths exist in the public tree\noss-twin move docs/handoffs/ # move that path from public to the mirror\noss-twin status             # show where each private path currently lives\n```\n\nExit code on `check`: `0` if clean, `1` if any private path leaked into the public tree. Wire into CI.\n\n## Pre-commit hook\n\n```yaml\nrepos:\n  - repo: https://github.com/creatornader/oss-twin\n    rev: v0.1.0\n    hooks:\n      - id: oss-twin-check\n```\n\n## Config (`.oss-twin.yaml`)\n\nLives at the public repo root. Committed.\n\n```yaml\nversion: 1\n\nmirror:\n  path: ../my-repo-internal              # relative to public repo root, or absolute\n  remote: git@github.com:user/my-repo-internal.git  # optional\n  visibility: private\n\nprivate_paths:\n  - docs/handoffs/\n  - docs/internal/\n  - .remember/\n  - thoughts/\n\nmirror_scaffold:\n  - docs/\n  - docs/handoffs/\n```\n\n`private_paths` patterns:\n- Trailing `/` means \"directory and contents.\"\n- Plain names match files exactly.\n- Glob patterns work via fnmatch (`docs/*.internal.md`).\n\n## Typical workflow\n\n```sh\n# Day 0: existing public repo, want to add a mirror\ncd ~/repos/my-public-repo\noss-twin scaffold-config       # writes .oss-twin.yaml\n# edit .oss-twin.yaml to confirm mirror path + private_paths\ngit add .oss-twin.yaml \u0026\u0026 git commit -m \"chore: configure oss-twin\"\n\n# Scaffold the mirror\noss-twin init                  # creates ../my-public-repo-internal/ with CLAUDE.md + .gitignore\n\n# Optionally, create the GitHub side privately\ncd ../my-public-repo-internal\ngh repo create user/my-public-repo-internal --private --source . --push\n\n# Later: realize a file in the public repo should be private\ncd ~/repos/my-public-repo\noss-twin move docs/handoffs/internal-stuff.md\n# review, commit on both sides\n\n# In CI:\noss-twin check                 # exits 1 if any private path leaked back in\n```\n\n## What this is NOT\n\n- **Not a sync tool.** It doesn't bidirectionally keep two repos in lockstep. `move` is one-shot and explicit.\n- **Not a content scanner.** Use [textleaks](https://github.com/creatornader/textleaks) for narrative-leak detection in file content.\n- **Not a credential scanner.** Use gitleaks + trufflehog for that.\n- **Not a history rewriter.** If a private file already landed in public history, use `git-filter-repo` to scrub it (and consider deleting + recreating the GitHub repo to expunge PR refs).\n- **Not a workflow orchestrator.** Each tool above stays focused. If you want a manifest that declares \"for this repo, run textleaks + oss-twin + gitleaks + osv-scanner at these phases,\" that's a separate layer (not built yet).\n\n## Roadmap\n\nThis is v0.1.0, a spike.\n\n- **`oss-twin sync`**: bidirectional structural diff between public and mirror with explicit-confirmation hunks\n- **`oss-twin watch`**: filesystem-watcher that flags new files matching `private_paths` the instant they're created\n- **CI action**: a reusable composite GitHub Action that runs `oss-twin check` cleanly without needing a pip install step\n- **Multi-mirror**: support more than one mirror per public repo (e.g., one for ops, one for strategy)\n\n## Related tools\n\noss-twin is one layer of a three-tool stack for maintaining public OSS repos with private context:\n\n| Tool | Concern | When to install |\n|---|---|---|\n| [**textleaks**](https://github.com/creatornader/textleaks) | Narrative-leak detection in file CONTENT (prose patterns, codenames) | Anywhere you write prose that could leak operator-internal context |\n| **oss-twin** (this tool) | Structural mirror gate that fails if any path declared private exists in the public tree | When you have a `*-internal` mirror repo |\n| [**oss-security-scan**](https://github.com/creatornader/oss-security-scan) | Reusable GitHub Actions workflow (typos + gitleaks + trufflehog + osv-scanner) | Every public OSS repo |\n\nFor the full stack wire-up pattern (one repo, all three tools), see [`oss-security-scan/examples/full-stack-starter/`](https://github.com/creatornader/oss-security-scan/tree/main/examples/full-stack-starter).\n\n## Note for repos with prose linters\n\n`.oss-twin.yaml` lists private paths as YAML VALUES (e.g. `private_paths: [docs/handoffs/, ...]`). The whole job of this config is to enumerate those paths so oss-twin can guard them. If your repo also runs a prose linter (Vale, an LLM-based audit, etc.), exempt `.oss-twin.yaml` from those scanners. Otherwise the linter will flag the path strings as private-material references and propose renames that would break the tool. Same applies to [`textleaks.yaml`](https://github.com/creatornader/textleaks).\n\n## License\n\nApache 2.0. See [LICENSE](LICENSE).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcreatornader%2Foss-twin","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcreatornader%2Foss-twin","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcreatornader%2Foss-twin/lists"}