# gitlawb node — environment variables # Copy to .env and fill in your values. # All variables are optional unless marked REQUIRED. # ── Node identity ───────────────────────────────────────────────────────── # Path to the node's Ed25519 keypair PEM file. # Generate with: gl identity new GITLAWB_KEY=/data/keys/identity.pem # Publicly reachable URL of this node (used in peer announcements) GITLAWB_PUBLIC_URL=https://your-node.example.com # ── Server ──────────────────────────────────────────────────────────────── GITLAWB_HOST=0.0.0.0 GITLAWB_PORT=7545 # ── Storage ─────────────────────────────────────────────────────────────── GITLAWB_REPOS_DIR=/data/repos # PostgreSQL connection URL. Required. # When using the bundled docker-compose, this is wired automatically. DATABASE_URL=postgresql://gitlawb:changeme@localhost:5432/gitlawb # ── Database pool & startup resilience ──────────────────────────────────── # Maximum connections in the PostgreSQL pool. A cap, not a floor — # connections open lazily. Size against the DB server's max_connections, # remembering admin tooling opens its own pool. GITLAWB_DB_MAX_CONNECTIONS=20 # Seconds a request waits for a pool connection before failing with 503. GITLAWB_DB_ACQUIRE_TIMEOUT_SECS=5 # Upper bound on each startup connect+migrate attempt, in seconds. Keep it # generous enough for a peer instance to finish migrating (the migration # advisory lock is held across a deploy); on expiry the attempt is retried. GITLAWB_DB_CONNECT_TIMEOUT_SECS=60 # Startup retry backoff while the database is unavailable: first delay and # ceiling, in seconds. The node stays up serving 503s (with a truthful # /ready) instead of crash-looping. GITLAWB_DB_RETRY_INITIAL_SECS=5 GITLAWB_DB_RETRY_MAX_SECS=60 # ── IPFS pinning (Pinata) ───────────────────────────────────────────────── # Get a JWT at https://app.pinata.cloud/developers/api-keys GITLAWB_PINATA_JWT= GITLAWB_PINATA_UPLOAD_URL=https://uploads.pinata.cloud/v3/files # ── Arweave permanent anchoring (Irys devnet) ───────────────────────────── # Leave empty to disable Arweave anchoring. GITLAWB_IRYS_URL=https://devnet.irys.xyz # ── Base L2 smart contracts ─────────────────────────────────────────────── GITLAWB_CHAIN_RPC_URL=https://sepolia.base.org GITLAWB_CONTRACT_DID_REGISTRY=0x8046284116C5ac6724adbBf860feBeA85692d574 GITLAWB_CONTRACT_NAME_REGISTRY=0x73094B9DAb2421878A20Abed1497001fbD51302c # $GITLAWB ERC20 token (Base mainnet). GITLAWB_TOKEN=0x5F980Dcfc4c0fa3911554cf5ab288ed0eb13DBa3 # ── On-chain PoS (operators — required to earn rewards) ─────────────────── # Set both of the next two to enable Proof-of-Stake mode. With both set, the # node verifies its registration on startup and posts heartbeats automatically. # Leave empty to run without on-chain PoS (local dev / pre-launch). # Deployed GitlawbNodeStaking contract address. GITLAWB_CONTRACT_NODE_STAKING= # Operator wallet private key (0x-prefixed hex). Used to sign heartbeats. # Use a dedicated wallet with only enough ETH for gas — not your treasury. GITLAWB_OPERATOR_PRIVATE_KEY= # If true, the node refuses to start unless registered and currently active. # Leave false while bootstrapping; flip to true once your stake is confirmed. GITLAWB_OPERATOR_STRICT_MODE=false # Heartbeat cadence in hours. Must be < 24 with headroom. Default: 20. GITLAWB_HEARTBEAT_INTERVAL_HOURS=20 # ── docker-compose Postgres password ────────────────────────────────────── # Only used when running via `docker compose up` with the bundled Postgres. POSTGRES_PASSWORD=changeme # ── P2P networking ──────────────────────────────────────────────────────── GITLAWB_P2P_PORT=7546 # Comma-separated HTTP peer URLs to announce to on startup. # The binary also ships with bootstrap-peers.json; set # GITLAWB_BOOTSTRAP_DISABLE_SEEDS=true for isolated dev/test networks. GITLAWB_BOOTSTRAP_PEERS= GITLAWB_BOOTSTRAP_DISABLE_SEEDS=false # Require signatures on peer announce/sync write routes. Keep false during # rolling upgrades so existing live nodes can still communicate. GITLAWB_REQUIRE_SIGNED_PEER_WRITES=false # Require the authenticated pusher to be the repo owner on git-receive-pack. # A valid did:key signature is authentication, not authorization: anyone can # sign as their own DID. When true, pushes from a non-owner DID are rejected. # Keep false until the repo owner is ready for owner-only writes. GITLAWB_ENFORCE_OWNER_PUSH=false # Comma-separated libp2p multiaddrs. # Example: /ip4/1.2.3.4/udp/7546/quic-v1/p2p/12D3KooW... GITLAWB_P2P_BOOTSTRAP= # ── Access control ──────────────────────────────────────────────────────── # Reserved for private-read mode. Public/private repo read enforcement is not # wired in the current live release; do not rely on this for private repositories. GITLAWB_PUBLIC_READ=true # Maximum git smart-HTTP pack request size, in bytes. GITLAWB_MAX_PACK_BYTES=2147483648 # Max seconds a served git upload-pack / receive-pack (clone / push) may run # before it is aborted with a 504. Bounds a hung git that would otherwise pin a # worker and, on push, the repo write lock. Does NOT cover the info/refs # advertisement or the withheld-blob path, which remain unbounded. Default 600. GITLAWB_GIT_SERVICE_TIMEOUT_SECS=600 # ── Push rate limiting (git-receive-pack flood brake) ───────────────────── # Max receive-pack requests (info/refs advertisement + push POST) per client # IP per hour. 0 disables. Default 600. GITLAWB_PUSH_RATE_LIMIT=600 # ── Creation rate limiting (repo/agent/issue/PR flood brake) ────────────── # Max creation requests (POST /api/v1/repos, /api/register, fork, issues, # pulls) per client IP per hour, in addition to the per-DID limit. The per-DID # limit alone is bypassed by a did:key farm (one throwaway identity per repo), # which is how spam-repo floods slip past it and the iCaptcha gate; the per-IP # brake caps a single-source flood. Uses GITLAWB_TRUSTED_PROXY below to resolve # the client IP. 0 disables. Default 120. GITLAWB_CREATE_RATE_LIMIT=120 # ── Peer-sync rate limiting (per client IP, uses GITLAWB_TRUSTED_PROXY below) ─ # /api/v1/peers/announce and /api/v1/sync/notify accept unsigned requests from # known peers and run at higher frequency, so a generous bucket. Separate from # the trigger bucket so an unsigned notify flood can't drain trigger's quota. # 0 disables. Default 600. GITLAWB_PEER_WRITE_RATE_LIMIT=600 # /api/v1/sync/trigger requires a signature and fans out to every peer per call, # so it gets a tight bucket. 0 disables. Default 60. GITLAWB_SYNC_TRIGGER_RATE_LIMIT=60 # Which forwarded header the edge is trusted to set, used to resolve the real # client IP for the push limiter. One of: # (unset) — no trusted proxy: key on the socket peer address, ignore headers. # fly — behind Fly's edge (trust Fly-Client-IP). # x-forwarded-for — behind a single reverse proxy like Caddy/NGINX (trust the # rightmost X-Forwarded-For hop). # Only set this when a proxy you control actually fronts the node; trusting a # forwarded header on a directly-exposed node lets clients spoof the key. GITLAWB_TRUSTED_PROXY= # ── Sync ───────────────────────────────────────────────────────────────── # Enable automatic background sync from known peers GITLAWB_AUTO_SYNC=false # ── iCaptcha proof-of-intelligence gate ─────────────────────────────────── # Optional gate on create_repo + register: require callers to present an # iCaptcha proof (X-ICaptcha-Proof header) earned at icaptcha.gitlawb.com. # Default off = fully inert (no behavior change). # off - gate disabled (default) # shadow - verify + log would-be rejections, but always allow # enforce - reject requests without a valid, strong-enough proof ICAPTCHA_MODE=off # Optional base64url Ed25519 public key override; if unset it is fetched from # ICAPTCHA_URL/v1/pubkey at startup. Recommended in production: pinning the key # removes the startup dependency on the iCaptcha service being reachable and # avoids silently running inert if that one-time fetch fails. ICAPTCHA_PUBKEY= # Minimum proof difficulty level required to pass. ICAPTCHA_REQUIRED_LEVEL=3 # Base URL of the iCaptcha service. ICAPTCHA_URL=https://icaptcha.gitlawb.com