Public Mirror

July 2, 2026 ยท View on GitHub

Utilities in this folder publish a filtered snapshot of the ALIS repository to a separate public mirror repository.

Files:

  • mirror_to_github.sh: main mirror script. Battle-tested flow copied from the async mirror pattern and adapted for ALIS validation.
  • mirror_to_github.ps1: Windows wrapper that runs the Bash script through WSL.
  • mirror_to_github.bat: simple batch entrypoint for Windows shells.
  • mirror.exclude: blacklist of files and folders removed from the public mirror snapshot.
  • forbidden_text_patterns.regex: hard-fail content validation for text files that must never survive filtering.

Why This Exists

Goal:

  • publish source code and public docs to GitHub;
  • avoid UE assets, generated artifacts, local workspace files, and internal-only material;
  • keep the working repository remotes untouched.

The script does not change local origin and does not push from your working repository.

Public-First Policy

ALIS mirror policy is public-first and denylist-first: a tracked file is published unless it (or a parent dir) matches a rule in mirror.exclude. Folder name alone does not make content private.

  • code is public by default;
  • architecture and build docs are public by default;
  • build, packaging, and verification scripts are public by default;
  • plugin Data/ JSON schemas and data-driven generator inputs (loot, dialogue, UI layouts, vitals) are public references; source in a folder named Data is code and stays public too.

What stays out of the mirror:

  • secrets and credentials;
  • private keys and key material (*.pem, *.key, *.p12, *.pfx, id_rsa*);
  • machine-local config;
  • generated outputs;
  • licensed or non-redistributable asset payloads;
  • UE asset/binary/media file TYPES anywhere - including inside published Data/ dirs, where only text/JSON survives.

This matches the long-term direction:

  • open-source development;
  • public review;
  • decentralized distribution and verification;
  • no dependence on hidden architecture for security.

If a workflow requires secrets, keep them outside the repository and inject them separately at runtime or in CI.

Safety Model

The flow is intentionally isolated:

  1. Reads tracked file paths from HEAD.
  2. Applies mirror.exclude before export, then checks out only surviving files from a temporary HEAD index.
  3. Preserves the canonical Alis.uproject in the filtered snapshot so the public mirror shows the real plugin graph.
  4. Runs hard validation against forbidden paths, binary file types, and forbidden text patterns.
  5. Creates a temporary git repository.
  6. Commits filtered snapshot in the temp repo.
  7. Pushes to --remote-url only if --push is provided.
  8. Deletes temp folder on exit.

This prevents accidental mutation of your working tree and avoids remote misconfiguration in local .git/config.

Baseline Model

The normal mirror flow compares against the mirror remote branch, not against a persistent local clone.

Persistent state:

  • the separate mirror repository and its target branch.

Disposable state:

  • the temporary mirror repo created for each run.

Normal behavior:

  1. fetch mirror branch tip into the temp repo;
  2. copy filtered snapshot over it;
  3. use git add -A + git diff --cached --quiet to decide whether anything changed.

Result:

  • no SHA manifest is used for the normal git mirror flow;
  • no persistent local mirror checkout is required;
  • the remote mirror branch is the baseline.

--ephemeral-preview is the only mode that runs without a remote baseline. Use it only for one-off local inspection.

Dirty Working Tree Policy

Default behavior is strict:

  • fails if staged files exist;
  • fails if unstaged tracked changes exist;
  • fails if untracked files exist.

Use --force to bypass this check intentionally. --allow-dirty is a backward-compatible alias.

Usage

Dry run against real mirror baseline:

bash ./scripts/git/mirror/mirror_to_github.sh --remote-url git@github.com:org/repo.git --dry-run

One-off local preview without remote baseline:

bash ./scripts/git/mirror/mirror_to_github.sh --dry-run --ephemeral-preview

Real push:

bash ./scripts/git/mirror/mirror_to_github.sh --remote-url git@github.com:org/repo.git --push

Windows wrapper:

.\scripts\git\mirror\mirror_to_github.ps1 --remote-url git@github.com:org/repo.git --dry-run
.\scripts\git\mirror\mirror_to_github.ps1 --dry-run --ephemeral-preview
.\scripts\git\mirror\mirror_to_github.ps1 --remote-url git@github.com:org/repo.git --push

Batch wrapper:

scripts\git\mirror\mirror_to_github.bat --remote-url git@github.com:org/repo.git --dry-run
scripts\git\mirror\mirror_to_github.bat --dry-run --ephemeral-preview
scripts\git\mirror\mirror_to_github.bat --remote-url git@github.com:org/repo.git --push

Make Wrapper

The root Makefile exposes a mirror wrapper:

Push (default):

make mirror

Default mirror remote:

git@github.com:fallintodusk/alis.git

Dry run:

make mirror MIRROR_DRY_RUN=1

Ephemeral local preview:

make mirror MIRROR_DRY_RUN=1 MIRROR_EPHEMERAL_PREVIEW=1

Allow dirty tree:

make mirror MIRROR_DRY_RUN=1 MIRROR_FORCE=1

Override branch or exclude file:

make mirror MIRROR_REMOTE_URL=git@github.com:org/repo.git MIRROR_BRANCH=main
make mirror MIRROR_DRY_RUN=1 MIRROR_EXCLUDE_FILE=scripts/git/mirror/mirror.exclude

Pass raw extra args:

make mirror MIRROR_ARGS='--help'

Validation Notes

Blacklist filtering is the first line of defense.

ALIS adds a second line of defense:

  • hard-fail if global UE asset extensions survive filtering (uasset, umap, ubulk, uexp, uptnl, ushaderbytecode, utoc, ucas, pak);
  • hard-fail if forbidden binary/media/key file types survive (executables, png/jpg/exr/blend/fbx/wav/mp4..., pem/key/p12/pfx);
  • hard-fail if forbidden paths survive;
  • hard-fail if text files still contain patterns from forbidden_text_patterns.regex;
  • hard-fail if a Git LFS pointer file survives;
  • hard-fail on any non-empty binary-content file (NUL byte) regardless of extension - this is what makes "text data only" true, not just the extension denylist, so an unknown-extension binary cannot sneak through.

Because the model is denylist-first, plugin Data/ dirs are validated by file TYPE, not by path: JSON/text survives, any binary/asset inside is rejected. This keeps the low-maintenance blacklist model while still stopping unsafe publication.

In other words:

  • ALIS docs/scripts are not excluded just because they describe architecture;
  • exclusions target concrete risk categories such as secrets, licensed assets, generated binaries, and local machine state.

Secret hardening rules:

  • docs and examples must use placeholders such as <api-key>, <password>, <user>, and <host>;
  • do not publish credential-shaped env assignments such as API_KEY=<api-key>;
  • do not publish URLs with embedded credentials such as postgres://<user>:<password>@<host>/db;
  • keep real secrets only in local env files, CI secret stores, or runtime-injected configuration.

Anonymity Model

The mirror normalizes obvious personal metadata before commit:

  • mirror commits always use mirror-bot <mirror-bot@localhost>;
  • .uplugin creator/support metadata is rewritten to project-safe values;
  • maintainer-local repo roots, UE/tool install paths, and Windows/WSL home paths are rewritten to neutral placeholders;
  • sibling local repos referenced from docs are rewritten to neutral placeholders as well;
  • explicit local usernames are rewritten when they appear in public docs/examples.

This is intended to remove maintainer identity leakage and machine-local traces.

What it does not do:

  • it does not rewrite project-domain identifiers such as public map, region, or lore names;
  • it does not hide the ALIS project brand.

If full world-setting anonymity is required, that is a separate content policy decision and cannot be solved safely by generic mirror sanitization alone.

Phase 1:

  • run manually from a trusted maintainer machine;
  • validate dry-run output against the real mirror remote;
  • push to a separate GitHub mirror repository.

Phase 2:

  • move the same script into Azure pipeline once mirror credentials and ownership are settled.