Protect your code and metadata from cloud services using self-hosted Git, Tor routing, and privacy-focused package management.

Sovereign Dev Stack


Gitea as a Tor Hidden Service

Gitea gives you self-hosted Git without relying on GitHub. Run it as a Tor hidden service and no external actor can reach your instance. The service binds to localhost and exposes itself only through the Tor network. This approach eliminates exposure to GitHub’s metadata collection while maintaining full control over your repositories.

The configuration is straightforward. Use the official Gitea image (v1.22.3 at time of writing), mount a data volume, and expose ports 3000 and 22. Set environment variables to disable telemetry and configure the root URL. The platform flag ensures compatibility with ARM64 hardware like Raspberry Pi 5 or AWS Graviton instances.

gitea:
  image: gitea/gitea:v1.22.3
  platform: linux/arm64
  container_name: gitea
  environment:
    - USER_UID=1000
    - USER_GID=1000
    - GITEA__database__DB_TYPE=sqlite3
    - GITEA__server__ROOT_URL=http://localhost:3002
    - GITEA__server__SSH_PORT=2222
    - GITEA__other__ENABLE_SWAGGER=false
    - GITEA__metrics__ENABLED=false
  volumes:
    - /mnt/data/gitea:/data
  ports:
    - "3002:3000"
    - "2222:22"
  restart: always
  labels:
    - "com.centurylinklabs.watchtower.enable=true"

Tor handles the rest. Configure a hidden service in torrc to forward traffic from the onion address to Gitea’s local ports. After restarting Tor, fetch the onion hostname and use it as your remote URL. Note that Tor v0.4.8.10 or later is required for stable hidden service operation.

HiddenServiceDir /var/lib/tor/gitea/
HiddenServicePort 80 127.0.0.1:3002
HiddenServicePort 22 127.0.0.1:2222

Pushes go through Tor automatically when you use torsocks. This hides your IP from GitHub and any other observers watching package registries. Watch out: If you forget to prepend torsocks to git push commands, your real IP will be exposed. Always verify with torsocks --version before proceeding.


Routing Package Managers Through Tor

Package managers leak your IP and the packages you install. PyPI, npmjs, and Docker Hub log both. Routing these tools through Tor replaces your real IP with an exit node’s IP, breaking the fingerprint. This is particularly important for sovereign AI development where model weights and dependencies may reveal sensitive information.

Start with pip. Create a global pip.conf that proxies all requests through Tor’s SOCKS5 port. Trust the PyPI domains to avoid SSL errors. Verify the setup by installing a package and checking your exit IP. Gotcha: Some corporate networks block Tor exit nodes, causing pip install to fail silently. Test with torsocks curl https://api.ipify.org first.

mkdir -p ~/.pip
cat > ~/.pip/pip.conf << 'EOF'
[global]
proxy = socks5h://127.0.0.1:9050
trusted-host = pypi.org
               pypi.python.org
               files.pythonhosted.org
EOF

pip install requests
torsocks curl https://api.ipify.org

npm works similarly. Set global proxy settings to route all requests through Tor. Skip the config file if you prefer torsocks for individual installs. Warning: npm’s proxy configuration can break if you switch between Tor and non-Tor networks. Always run npm config delete proxy and npm config delete https-proxy when not using Tor.

npm config set proxy    socks5://localhost:9050
npm config set https-proxy socks5://localhost:9050
npm config get proxy
torsocks npm install @11ty/eleventy

Docker pulls are slower over Tor, especially for large images like vLLM. Build a local registry to cache images once and avoid repeated Tor-routed downloads. Important: The official Docker registry (registry:2) doesn’t support authentication by default. For private images, use registry:2.8.1 with proper TLS configuration.

docker run -d \
  -p 5000:5000 \
  --name registry \
  --restart always \
  -v /mnt/docker-registry:/var/lib/registry \
  registry:2.8.1

docker pull ghcr.io/remsky/kokoro-fastapi-gpu:latest
docker tag ghcr.io/remsky/kokoro-fastapi-gpu:latest localhost:5000/kokoro-tts:latest
docker push localhost:5000/kokoro-tts:latest

Update your docker-compose.yml to pull from the local registry instead of external sources. Pro tip: Add --insecure-registry localhost:5000 to your Docker daemon configuration (/etc/docker/daemon.json) to avoid TLS errors when using local registries.


Git Pseudonyms and Commit Privacy

Commit metadata reveals more than you think. Timestamps expose your timezone and work habits. Names and email addresses link commits to your identity. Fix both.

Set a global Git identity that doesn’t tie to your real name. Use a pseudonym for user.name and an onion-derived email for public repos. Note: Some Git hosts reject onion email addresses. Use a temporary email service like dev@sovereign.local for private repos.

git config --global user.name  "sovereign-dev"
git config --global user.email "dev@sovereign.local"
git config --global user.email "dev@$(sudo cat /var/lib/tor/gitea/hostname)"

Optional: sign commits with an SSH key to prove authenticity without leaking identity. Watch out: SSH signing requires Git v2.34.0+ and OpenSSH v8.8+. Older versions will fail silently.

ssh-keygen -t ed25519 -C "sovereign-dev" -f ~/.ssh/gitea_signing -N ""
git config --global gpg.format ssh
git config --global user.signingkey ~/.ssh/gitea_signing.pub
git config --global commit.gpgsign true

Freeze timestamps for existing commits or push them on a schedule to break the pattern. Gotcha: Git’s environment variables only affect new commits. Existing commits require git filter-branch as shown below.

GIT_AUTHOR_DATE="2026-01-01T12:00:00+00:00" \
GIT_COMMITTER_DATE="2026-01-01T12:00:00+00:00" \
git commit -m "update"

For daily pushes, use a cron job. Important: Cron jobs run with minimal environment variables. Always use absolute paths in scripts.

cat > /usr/local/bin/scheduled_push.sh << 'EOF'
#!/bin/bash
for repo in /mnt/projects/*/; do
    cd "$repo" && git push origin main 2>/dev/null
done
EOF
chmod +x /usr/local/bin/scheduled_push.sh
crontab -e

Add this line to crontab (runs at 03:00 daily):

0 3 * * * /usr/local/bin/scheduled_push.sh

Rewrite history for existing repos to normalize timestamps. Warning: This is a destructive operation. Backup your repository first with git clone --mirror.

git filter-branch --env-filter '
    export GIT_AUTHOR_DATE="2026-01-01T12:00:00+00:00"
    export GIT_COMMITTER_DATE="2026-01-01T12:00:00+00:00"
' --tag-name-filter cat -- --branches --tags

What I Actually Use

  • Gitea: self-hosted Git with SQLite, running on ARM64 (Raspberry Pi 5)
  • Local Docker registry: caches large images to avoid slow Tor pulls (registry:2.8.1)
  • torsocks: forces pip, npm, and git through Tor without per-command flags (v2.3.0)
  • Commit signing: SSH-based with ed25519 keys for authenticity without identity leakage

Stack

Sovereign Dev Stack

Self-hosted Git & package management via Tor

5
Package Managers pip/npm/Docker via Tor
4
Gitea Self-hosted Git
3
Tor Service Onion routing
2
OS Linux host
1
Hardware Local/ARM server