Skip to main content

AGMH, a local GitHub backup and repository mirroring CLI.

Project description

AGMH

PyPI Python CI Release License: Unlicense

AGMH means ANTI GITHUB & MICROSOFT HYSTERIA.

AGMH is a Python CLI for local Git repository backups and cross-forge mirroring. It discovers repositories from supported source accounts, organizations, groups, namespaces, or workspaces; downloads them locally as Git mirrors; and can push those mirrors to GitHub, GitLab, Forgejo/Codeberg, Bitbucket, SourceHut, or compatible Git remotes.

Package: agmh on PyPI

Repository: haltman-io/agmh

[!IMPORTANT] The complete technical documentation is in the GitHub Wiki: AGMH Complete Guide.

This README is intentionally short. It is for people who need to install AGMH, configure tokens, and run the most common mirror workflows quickly.

The primary command is:

agmh

The legacy typo aghm was removed. AGMH intentionally uses agmh for commands, default config files, state directories, logs, and generated marker files.

What AGMH Does

  • Backs up repositories locally with git clone --mirror.
  • Discovers repositories from GitHub, GitLab, Forgejo/Gitea, Codeberg, Bitbucket, and SourceHut.
  • Pushes mirrors to GitHub, GitLab, Forgejo/Gitea, Codeberg, Bitbucket, SourceHut, or custom Git remotes.
  • Supports local-only backups, remote-only mirror publishing, full mirror runs, and polling with watching.
  • Uses environment variables for tokens.
  • Keeps resumable state in .agmh/state.json.
  • Writes logs under .agmh/logs/.

What AGMH Does Not Do

AGMH mirrors Git repositories. It does not migrate issues, pull requests, merge requests, reviews, discussions, releases, CI configuration, project boards, branch protection rules, repository permissions, organization members, or forge secrets.

Install

Install from PyPI:

python3 -m pip install -U pip
python3 -m pip install "agmh[tui]"

Check it:

agmh --help
agmh run --help

Ubuntu packages you probably need:

sudo apt update
sudo apt install -y python3 python3-venv python3-pip git ca-certificates openssh-client

Optional:

sudo apt install -y git-lfs curl

Tokens

Use environment variables. Do not paste tokens into config files, shell history, logs, issues, or pull requests.

Common environment names:

export GITHUB_TOKEN="source_github_token_here"
export GITHUB_DEST_TOKEN="destination_github_token_here"
export GITLAB_TOKEN="gitlab_token_here"
export CODEBERG_TOKEN="codeberg_or_forgejo_token_here"
export BITBUCKET_TOKEN="bitbucket_app_password_or_token_here"
export SOURCEHUT_TOKEN="sourcehut_token_here"

Where to create tokens:

Token requirements depend on the provider, but the practical rule is simple:

  • Source token: must be able to read every repository you want to back up.
  • Destination token: must be able to create repositories and push Git refs in the destination account, organization, group, namespace, or workspace.

For private repositories, make sure the token has private repository access. For organizations with SSO or extra approval rules, authorize the token for that organization before running AGMH.

Quick Start

Create a config:

agmh init-config --path agmh.config.toml

Create a source list:

cat > sources.txt <<'EOF'
https://github.com/YOUR_USER_OR_ORG/
EOF

Run a dry-run:

agmh run --config agmh.config.toml --dry-run --verbose

Run it for real:

agmh run --config agmh.config.toml --verbose

Check state:

agmh state --config agmh.config.toml

Common Commands

Back Up My GitHub Locally Only

This downloads mirrors and does not push anywhere.

export GITHUB_TOKEN="..."

agmh local-mirror \
  --source https://github.com/YOUR_USER_OR_ORG/ \
  --github-token env:GITHUB_TOKEN \
  --local-dir backups \
  --verbose

Result:

backups/github/YOUR_USER_OR_ORG/*.git
.agmh/state.json
.agmh/logs/

Mirror My GitHub to GitLab

Use a GitHub token that can read the source and a GitLab token that can create projects and push to the destination namespace.

export GITHUB_TOKEN="..."
export GITLAB_TOKEN="..."

agmh run \
  --source https://github.com/YOUR_USER_OR_ORG/ \
  --github-token env:GITHUB_TOKEN \
  --destination https://gitlab.com/YOUR_GITLAB_NAMESPACE \
  --destination-token gitlab:env:GITLAB_TOKEN \
  --verbose

The destination URL is the namespace/group/user where AGMH should create repositories. It is not an individual repository URL.

Mirror My GitHub to Another GitHub Account or Organization

Use separate tokens if the source and destination accounts are different.

export GITHUB_TOKEN="..."
export GITHUB_DEST_TOKEN="..."

agmh run \
  --source https://github.com/SOURCE_USER_OR_ORG/ \
  --github-token env:GITHUB_TOKEN \
  --destination https://github.com/DESTINATION_USER_OR_ORG \
  --destination-token github:env:GITHUB_DEST_TOKEN \
  --verbose

Mirror GitLab to GitHub

export GITLAB_SOURCE_TOKEN="..."
export GITHUB_DEST_TOKEN="..."

agmh run \
  --source https://gitlab.com/SOURCE_GROUP_OR_USER/ \
  --source-token gitlab:env:GITLAB_SOURCE_TOKEN \
  --destination https://github.com/DESTINATION_USER_OR_ORG \
  --destination-token github:env:GITHUB_DEST_TOKEN \
  --verbose

Mirror Codeberg or Forgejo to GitLab

export CODEBERG_SOURCE_TOKEN="..."
export GITLAB_TOKEN="..."

agmh run \
  --source https://codeberg.org/SOURCE_USER_OR_ORG/ \
  --source-token forgejo:YOUR_CODEBERG_USERNAME:env:CODEBERG_SOURCE_TOKEN \
  --destination https://gitlab.com/DESTINATION_NAMESPACE \
  --destination-token gitlab:env:GITLAB_TOKEN \
  --verbose

Mirror Bitbucket to GitHub

export BITBUCKET_TOKEN="..."
export GITHUB_DEST_TOKEN="..."

agmh run \
  --source https://bitbucket.org/SOURCE_WORKSPACE/ \
  --source-token bitbucket:YOUR_BITBUCKET_USERNAME_OR_EMAIL:env:BITBUCKET_TOKEN \
  --destination https://github.com/DESTINATION_USER_OR_ORG \
  --destination-token github:env:GITHUB_DEST_TOKEN \
  --verbose

Push Existing Local Mirrors Later

First, download locally:

export GITHUB_TOKEN="..."

agmh local-mirror \
  --source https://github.com/YOUR_USER_OR_ORG/ \
  --github-token env:GITHUB_TOKEN \
  --local-dir backups \
  --verbose

Later, push those local mirrors:

export GITLAB_TOKEN="..."

agmh remote-mirror \
  --config agmh.config.toml \
  --destination https://gitlab.com/DESTINATION_NAMESPACE \
  --destination-token gitlab:env:GITLAB_TOKEN \
  --verbose

Force Destination Visibility

Mirror source visibility:

agmh remote-mirror --config agmh.config.toml --destination-visibility mirror

Force everything private:

agmh remote-mirror --config agmh.config.toml --destination-visibility private

Force everything public:

agmh remote-mirror --config agmh.config.toml --destination-visibility public

Use public only after reviewing the repositories. AGMH cannot know your disclosure policy.

Disable the Marker Commit

By default, AGMH can add agmh.txt before remote mirroring. If you want a mirror without that repository content change, set:

[backup]
marker_enabled = false

Watch for Updates

agmh watching \
  --config agmh.config.toml \
  --watch-interval 300 \
  --watch-action full \
  --verbose

Watching mode uses polling. It is not an inbound webhook server.

Quick Config Example

This is enough for a common GitHub to GitLab mirror:

workspace = ".agmh"
mode = "full"
sources_file = "sources.txt"

[github]
tokens = [{ env = "GITHUB_TOKEN", name = "github-source" }]

[backup]
local_dir = "backups"
clone_protocol = "https"
marker_enabled = true
push_mode = "mirror"

[[destinations]]
url = "https://gitlab.com/YOUR_GITLAB_NAMESPACE"
platform = "gitlab"
tokens = [{ env = "GITLAB_TOKEN", name = "gitlab-destination" }]
visibility = "mirror"
push_mode = "mirror"

sources.txt:

https://github.com/YOUR_USER_OR_ORG/

When You Need More Than This README

Read the complete guide:

https://github.com/haltman-io/agmh/wiki/AGMH-COMPLETE-GUIDE

It covers:

  • every supported source and destination provider;
  • full TOML reference;
  • local mirror, remote mirror, and watching mode details;
  • webhook notifications for generic endpoints, Discord, and Telegram;
  • marker commit behavior;
  • visibility rules;
  • push modes;
  • Git LFS;
  • proxy and TLS options;
  • state, logs, resume, and troubleshooting.

Security Notes

  • Prefer environment variables for secrets.
  • Do not commit .agmh/, backups/, agmh.config.toml, sources.txt, destinations.txt, tokens, logs, or private config files.
  • Rotate tokens if they were ever printed or pasted in the wrong place.
  • Use --insecure only for controlled troubleshooting. It disables TLS verification.

Security reports: email root@haltman.io. Do not open a public issue for private vulnerability details.

Project Background

Risk Model

This tool exists because important work should not depend on a single platform remaining available, cooperative, or operational forever.

AGMH was built after past platform access incidents made us reassess the risk of being locked out of a large technology platform without enough time to preserve our work or coordinate with the people closest to a project. The risk is similar to an abrupt offboarding process where access is disabled so quickly that a person cannot even send a final email to close colleagues.

For software projects, that access risk is broader than any single account or provider. A team can lose continuity because of enforcement actions, sanctions, provider policy changes, operational outages, service degradation, acquisition risk, or a platform eventually disappearing. The critical issue is centralization: if repository history, issues, branches, tags, release metadata, and collaboration context all live in one place, a disruption in that place can have a large impact on the surrounding ecosystem.

This is not a personal fight with GitHub. It is a risk-management and business continuity problem. AGMH provides a practical way to keep local mirrors, move repositories between forges, preserve Git history, and maintain high availability of project information when a centralized platform becomes unavailable or unsuitable.

AGMH is produced by Haltman.IO and released freely so others can protect their own work.

This tool has already been used to back up repositories from @extencil and @haltman-io to GitLab, Codeberg, and SourceHut successfully.

Continuity Incident Timeline

AGMH was used to move the work of @extencil and @haltman-io away from a single forge dependency and into independent mirrors:

The account access incident that reinforced this risk model followed this timeline:

Event Time
Account suspended/banned by platform enforcement Monday, 2026-06-08, around 04:00 America/Sao_Paulo (UTC-03:00), approximately 2026-06-08 07:00 UTC
Review ticket opened 2026-06-08 07:59 UTC, 2026-06-08 04:59 America/Sao_Paulo
Priority follow-up sent by our side 2026-06-11 16:47 UTC, 2026-06-11 13:47 America/Sao_Paulo
Case reviewed and reverted by GitHub 2026-06-12 11:19 UTC, 2026-06-12 08:19 America/Sao_Paulo

The incident would have been significantly more damaging without continuity procedures already in place. When Haltman.IO created its GitHub organization, other Haltman.IO members were assigned as organization owners. That avoided a complete lockout scenario.

Someone who is not part of an organization, or who is not an organization owner or repository administrator, cannot reliably operate that organization. They cannot recover organization-level access, manage owners and teams, change organization settings, manage repository permissions, configure secrets, webhooks, deploy keys, branch protection, or security settings, create or transfer repositories, publish releases, or consistently triage and merge work across the organization.

This matters because the affected work is operational, not cosmetic. Haltman.IO voluntarily sustains email-forwarding infrastructure associated with The Hacker's Choice, in collaboration with Phrack, Eurocompton, team-teso, Antisec, pwnbuffer, and other groups connected to cybersecurity research. A complete organization lockout would have affected the ability to manage the many repositories behind that email-forwarding stack.

That impact is not about minor product changes or visual polish. It affects the ability to coordinate proper vulnerability disclosure for people who self-host the email-forwarding stack, publish fixes, document operational changes, and credit researchers correctly when they report vulnerabilities.

It also affects our internal service expectations. There is no legal or commercial SLA: we do not sell this work, and the output is public work for the public. Still, we prefer to respond to issues and pull requests quickly. Acting like a large platform with effectively unbounded response times is neither our role nor consistent with Haltman.IO's operating values.

Haltman.IO

Haltman is a group of Brazilian hackers. Friends for over a decade, building public, privacy-first infrastructure and free software.

We build, break, audit, and publish.

We do not sell platforms. We do not run franchises.

We do not ask permission.

Haltman.IO links:

Operating Values

Doctrine Value
01 Independence We answer to no one. No board. No investors. No sponsors. Our independence guarantees our freedom.
02 Transparency Every tool is open source. Every decision is visible. No back rooms. No hidden agendas.
03 Public Output We publish. We document. We release. Our work speaks for itself. Not our marketing.
04 No Hierarchy Flat structure. No leaders. No bosses. No titles. No org charts. Respect is earned by output.
05 Mutual Aid When one of us needs help, the others show up. No invoices. No politics. Just engineering.
06 No Compromise We do not water down our principles for comfort, profit, or acceptance. Those who trade freedom for security end up with neither.

Project Files

References

License

AGMH is released under the Unlicense.

This means the project is dedicated to the public domain to the fullest extent possible. See:

Author

Author: extencil extencil@segfault.net

Repository: haltman-io/agmh

Produced by Haltman.IO.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

agmh-0.3.0.tar.gz (64.3 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

agmh-0.3.0-py3-none-any.whl (58.4 kB view details)

Uploaded Python 3

File details

Details for the file agmh-0.3.0.tar.gz.

File metadata

  • Download URL: agmh-0.3.0.tar.gz
  • Upload date:
  • Size: 64.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for agmh-0.3.0.tar.gz
Algorithm Hash digest
SHA256 fdc029e8e4953bc9c2572b568fa8b4aad1eb9f64f2ccd7beeff573dfd27cfe5d
MD5 a60a45a84803353b34b2b352586c1a07
BLAKE2b-256 e240417b34fc9fd282c4b3d38a82f86246467c0122b1431f79f20a4f739ee191

See more details on using hashes here.

Provenance

The following attestation bundles were made for agmh-0.3.0.tar.gz:

Publisher: release-please.yml on haltman-io/agmh

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file agmh-0.3.0-py3-none-any.whl.

File metadata

  • Download URL: agmh-0.3.0-py3-none-any.whl
  • Upload date:
  • Size: 58.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for agmh-0.3.0-py3-none-any.whl
Algorithm Hash digest
SHA256 dbcde3a0eac63e5fa60d7d4a95137f1beef6026e30a9dd9d30c84a64de5e8ae8
MD5 5e1cfaf241b6b65b4a72db0533290c80
BLAKE2b-256 1e93a2355498fc81ee58282b6721861ab12e98f67a12ccd10687703d97d60ada

See more details on using hashes here.

Provenance

The following attestation bundles were made for agmh-0.3.0-py3-none-any.whl:

Publisher: release-please.yml on haltman-io/agmh

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page