From b7a4374ab265bc6821699d3769430cff837e13e1 Mon Sep 17 00:00:00 2001 From: jatmn Date: Wed, 4 Nov 2026 08:46:13 -0800 Subject: [PATCH 1/7] chore: rebrand user-facing gitlawb references to Twigpine Rename the product name in docs, README, CLI help/output, MCP tool descriptions, macOS app strings, installers, npm metadata and license headers from gitlawb/Gitlawb to Twigpine. Point GitHub links at the renamed Twigpine org (and fix the release workflow's repository guard, which still matched Gitlawb/node), and switch the public node hosts and website/install URLs to twigpine.com. Protocol and operator identifiers are intentionally unchanged (did:gitlawb, gitlawb://, signing domains, GITLAWB_* env vars, crate and binary names, X-Gitlawb-* headers, icaptcha and peer-network URLs); they need a follow-up migration. Co-Authored-By: Claude Opus 5.5 --- .env.example | 2 +- .github/ISSUE_TEMPLATE/bug_report.yml | 2 +- .github/ISSUE_TEMPLATE/config.yml | 4 +- .github/pull_request_template.md | 2 +- .github/workflows/pr-triage.yml | 2 +- .github/workflows/release.yml | 28 +++---- CONTRIBUTING.md | 8 +- Cargo.toml | 4 +- LICENSE-APACHE | 2 +- LICENSE-MIT | 2 +- README.md | 38 ++++----- SECURITY.md | 8 +- bootstrap-peers.json | 10 +-- crates/gitlawb-attest/Cargo.toml | 2 +- crates/gitlawb-attest/src/lib.rs | 2 +- crates/gitlawb-core/Cargo.toml | 2 +- crates/gitlawb-core/src/did.rs | 6 +- crates/gitlawb-core/src/http_sig.rs | 4 +- crates/gitlawb-core/src/identity.rs | 4 +- crates/gitlawb-core/src/redirect.rs | 6 +- crates/gitlawb-core/src/ucan.rs | 4 +- crates/gitlawb-node/Cargo.toml | 2 +- crates/gitlawb-node/src/api/peers.rs | 4 +- crates/gitlawb-node/src/api/repos.rs | 4 +- crates/gitlawb-node/src/api/resolve.rs | 4 +- crates/gitlawb-node/src/auth/mod.rs | 4 +- crates/gitlawb-node/src/bootstrap.rs | 2 +- crates/gitlawb-node/src/config.rs | 2 +- crates/gitlawb-node/src/main.rs | 2 +- crates/gitlawb-node/src/metrics.rs | 2 +- crates/gitlawb-node/src/p2p/mod.rs | 6 +- crates/gl/Cargo.toml | 2 +- crates/gl/src/agent.rs | 12 ++- crates/gl/src/bounty.rs | 48 +++++++++-- crates/gl/src/cert.rs | 12 ++- crates/gl/src/changelog.rs | 8 +- crates/gl/src/clone.rs | 10 ++- crates/gl/src/doctor.rs | 28 +++---- crates/gl/src/http.rs | 2 +- crates/gl/src/identity.rs | 2 +- crates/gl/src/init.rs | 10 ++- crates/gl/src/ipfs_cmd.rs | 14 +++- crates/gl/src/issue.rs | 36 +++++++-- crates/gl/src/main.rs | 14 ++-- crates/gl/src/mcp.rs | 20 +++-- crates/gl/src/mirror.rs | 24 +++--- crates/gl/src/name.rs | 2 +- crates/gl/src/node.rs | 20 ++++- crates/gl/src/node_stake.rs | 2 +- crates/gl/src/peer.rs | 24 +++++- crates/gl/src/pr.rs | 48 +++++++++-- crates/gl/src/profile.rs | 32 ++++++-- crates/gl/src/protect.rs | 18 ++++- crates/gl/src/quickstart.rs | 2 +- crates/gl/src/register.rs | 10 ++- crates/gl/src/repo.rs | 80 +++++++++++++++---- crates/gl/src/star.rs | 18 ++++- crates/gl/src/status.rs | 10 ++- crates/gl/src/sync.rs | 6 +- crates/gl/src/task.rs | 36 +++++++-- crates/gl/src/visibility.rs | 18 ++++- crates/gl/src/webhook.rs | 18 ++++- docker-compose.yml | 2 +- docs/ECONOMICS.md | 2 +- docs/MAINTAINER-ROADMAP.md | 2 +- docs/OSS-READINESS-AUDIT.md | 6 +- docs/RUN-A-NODE.md | 6 +- infra/aws/README.md | 2 +- infra/aws/main.tf | 6 +- infra/aws/variables.tf | 2 +- install.ps1 | 12 +-- install.sh | 16 ++-- macos-app/Sources/GitlawbNode/Config.swift | 2 +- .../Sources/GitlawbNode/DockerCompose.swift | 4 +- macos-app/Sources/GitlawbNode/Info.plist | 4 +- .../GitlawbNode/Resources/docker-compose.yml | 2 +- .../Sources/GitlawbNode/SettingsWindow.swift | 2 +- .../GitlawbNode/StatusBarController.swift | 8 +- npm/README.md | 4 +- npm/packages/gl-darwin-arm64/package.json | 4 +- npm/packages/gl-darwin-x64/package.json | 4 +- npm/packages/gl-linux-arm64/package.json | 4 +- npm/packages/gl-linux-x64/package.json | 4 +- npm/packages/gl/README.md | 12 +-- npm/packages/gl/install.js | 10 +-- npm/packages/gl/package.json | 4 +- scripts/build-macos-app.sh | 8 +- 87 files changed, 604 insertions(+), 288 deletions(-) diff --git a/.env.example b/.env.example index d934c085f..48e69f318 100644 --- a/.env.example +++ b/.env.example @@ -1,4 +1,4 @@ -# gitlawb node — environment variables +# Twigpine node — environment variables # Copy to .env and fill in your values. # All variables are optional unless marked REQUIRED. diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index 20c37b4fa..be7e649ae 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -1,5 +1,5 @@ name: Bug report -description: Report a defect in a Gitlawb crate or node behavior +description: Report a defect in a Twigpine crate or node behavior labels: ["bug"] body: - type: markdown diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index 6cde7be66..1c615be9b 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -1,8 +1,8 @@ blank_issues_enabled: false contact_links: - name: Security vulnerability - url: https://github.com/Gitlawb/node/blob/main/SECURITY.md + url: https://github.com/Twigpine/node/blob/main/SECURITY.md about: Please report security issues privately per our security policy, not as a public issue. - name: Questions & discussion - url: https://github.com/Gitlawb/node/discussions + url: https://github.com/Twigpine/node/discussions about: For usage questions and open-ended discussion. diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index de9bedfbe..f3eebe6b9 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -1,5 +1,5 @@ diff --git a/.github/workflows/pr-triage.yml b/.github/workflows/pr-triage.yml index 1883746fd..c40afb5b6 100644 --- a/.github/workflows/pr-triage.yml +++ b/.github/workflows/pr-triage.yml @@ -124,7 +124,7 @@ jobs: "", lines, "", - "See [CONTRIBUTING.md](https://github.com/Gitlawb/node/blob/main/CONTRIBUTING.md). Update the PR and these notes will clear automatically.", + "See [CONTRIBUTING.md](https://github.com/Twigpine/node/blob/main/CONTRIBUTING.md). Update the PR and these notes will clear automatically.", ].join("\n"); await github.rest.issues.createComment({ owner, repo, issue_number: prNumber, body: comment }); diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 32875dc8c..16136948e 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -31,7 +31,7 @@ jobs: # Skipped on manual dispatch: a backfill must never create or advance a # release PR as a side effect (npm-publish tolerates the skipped need via # !cancelled(), and every other job gates on release_created == 'true'). - if: ${{ github.repository == 'Gitlawb/node' && github.event_name != 'workflow_dispatch' }} + if: ${{ github.repository == 'Twigpine/node' && github.event_name != 'workflow_dispatch' }} name: Release Please runs-on: ubuntu-latest permissions: @@ -64,7 +64,7 @@ jobs: sync-release-lock: name: Sync Cargo.lock on the release PR needs: release-please - if: ${{ !cancelled() && github.repository == 'Gitlawb/node' && github.event_name != 'workflow_dispatch' }} + if: ${{ !cancelled() && github.repository == 'Twigpine/node' && github.event_name != 'workflow_dispatch' }} runs-on: ubuntu-latest permissions: contents: write @@ -669,7 +669,7 @@ jobs: if: ${{ steps.guard.outputs.enabled == 'true' }} uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: - repository: Gitlawb/homebrew-tap + repository: Twigpine/homebrew-tap token: ${{ secrets.HOMEBREW_TAP_PAT }} path: tap persist-credentials: false @@ -695,8 +695,8 @@ jobs: mkdir -p tap/Formula cat > tap/Formula/gl.rb </memlawb (signed) git clone gitlawb:///memlawb # public read, no proof needed gl doctor # preflight: identity, node, version, iCaptcha @@ -430,7 +430,7 @@ Legacy-pin window: releases before the CID-resolver work stored the provider CID ## Optional node staking -Gitlawb Node includes optional Base L2 node-operator hooks. Operators can register a node DID, stake `$GITLAWB`, and post heartbeats. +Twigpine Node includes optional Base L2 node-operator hooks. Operators can register a node DID, stake `$GITLAWB`, and post heartbeats. PoS is disabled unless these are configured: @@ -500,8 +500,8 @@ Build: Output: ```txt -dist/Gitlawb Node.app -dist/Gitlawb Node.dmg +dist/Twigpine Node.app +dist/Twigpine Node.dmg ``` Features: @@ -515,7 +515,7 @@ Features: Unsigned local build: ```bash -xattr -cr "dist/Gitlawb Node.app" +xattr -cr "dist/Twigpine Node.app" ``` --- diff --git a/SECURITY.md b/SECURITY.md index bbe97e7e8..7729484f4 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -2,9 +2,9 @@ ## Reporting a Vulnerability -If you discover a security vulnerability in gitlawb, please **do not open a public issue**. +If you discover a security vulnerability in Twigpine, please **do not open a public issue**. -Report it privately by emailing **security@gitlawb.com** with: +Report it privately by emailing **security@twigpine.com** with: - A description of the vulnerability - Steps to reproduce - Potential impact assessment @@ -118,13 +118,13 @@ These are documented limitations of the current live release. They should be pri ## Threat Model -gitlawb is designed to be secure against: +Twigpine is designed to be secure against: - **Unauthenticated writes** — HTTP Signature auth on protected write endpoints; see Known Limitations for owner and capability authorization gaps - **Tampered git objects** — CIDv1 content addressing detects modification - **Identity spoofing** — DIDs derived from public keys, unforgeable without the private key - **Centralized takedown** — no single point of control; data on IPFS + Arweave -gitlawb is **not yet** designed to defend against: +Twigpine is **not yet** designed to defend against: - A compromised node operator (node operators are trusted for their own node) - Sybil attacks on the DHT (trust score system mitigates, not eliminates) - Timing attacks on signature verification (not constant-time compared in v0.1) diff --git a/bootstrap-peers.json b/bootstrap-peers.json index f2ce80972..8f9c6af63 100644 --- a/bootstrap-peers.json +++ b/bootstrap-peers.json @@ -1,11 +1,11 @@ { - "$comment": "Canonical seed list for the Gitlawb network. Merged with GITLAWB_BOOTSTRAP_PEERS at startup. PRs to add public nodes welcome.", + "$comment": "Canonical seed list for the Twigpine network. Merged with GITLAWB_BOOTSTRAP_PEERS at startup. PRs to add public nodes welcome.", "version": 1, "updated": "2026-06-30", "peers": [ { "name": "gitlawb", - "operator": "Gitlawb", + "operator": "Twigpine", "did": "did:key:z6MkqDnb7Siv3Cwj7pGJq4T5EsUisECqR8KpnDLwcaZq5TPr", "http_url": "https://node.gitlawb.com", "p2p_multiaddr": null, @@ -13,7 +13,7 @@ }, { "name": "gitlawb-node2", - "operator": "Gitlawb", + "operator": "Twigpine", "did": null, "http_url": "https://node2.gitlawb.com", "p2p_multiaddr": null, @@ -21,7 +21,7 @@ }, { "name": "gitlawb-node3", - "operator": "Gitlawb", + "operator": "Twigpine", "did": null, "http_url": "https://node3.gitlawb.com", "p2p_multiaddr": null, @@ -29,7 +29,7 @@ }, { "name": "gitlawb-manila", - "operator": "Gitlawb", + "operator": "Twigpine", "did": null, "http_url": "https://manila.gitlawb.com", "p2p_multiaddr": null, diff --git a/crates/gitlawb-attest/Cargo.toml b/crates/gitlawb-attest/Cargo.toml index e6276b1fd..eed96034f 100644 --- a/crates/gitlawb-attest/Cargo.toml +++ b/crates/gitlawb-attest/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "gitlawb-attest" -description = "External Attestation v1: pluggable provenance attachments for gitlawb ref-update certs" +description = "External Attestation v1: pluggable provenance attachments for Twigpine ref-update certs" version = "0.7.1" # x-release-please-version edition.workspace = true rust-version.workspace = true diff --git a/crates/gitlawb-attest/src/lib.rs b/crates/gitlawb-attest/src/lib.rs index f6e75c3c0..bfb721a18 100644 --- a/crates/gitlawb-attest/src/lib.rs +++ b/crates/gitlawb-attest/src/lib.rs @@ -1,4 +1,4 @@ -//! Pluggable provenance attachments for gitlawb ref-update certs. +//! Pluggable provenance attachments for Twigpine ref-update certs. //! //! A `gitlawb/ref-update/v1` cert proves who pushed and where a ref moved to. //! It does not say how the commit was produced. This crate adds an optional diff --git a/crates/gitlawb-core/Cargo.toml b/crates/gitlawb-core/Cargo.toml index 67e9ed9c1..407ea2ada 100644 --- a/crates/gitlawb-core/Cargo.toml +++ b/crates/gitlawb-core/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "gitlawb-core" -description = "Core cryptographic primitives for the gitlawb network: DIDs, CIDs, UCAN, HTTP Signatures, ref-update certificates" +description = "Core cryptographic primitives for the Twigpine network: DIDs, CIDs, UCAN, HTTP Signatures, ref-update certificates" version = "0.7.1" # x-release-please-version edition.workspace = true rust-version.workspace = true diff --git a/crates/gitlawb-core/src/did.rs b/crates/gitlawb-core/src/did.rs index e3775845b..715bd1f9b 100644 --- a/crates/gitlawb-core/src/did.rs +++ b/crates/gitlawb-core/src/did.rs @@ -1,11 +1,11 @@ -//! Decentralized Identifier (DID) types for the gitlawb network. +//! Decentralized Identifier (DID) types for the Twigpine network. //! //! Supported methods: //! - `did:key` — ephemeral, derived directly from an Ed25519 public key //! - `did:web` — anchored to a domain //! - `did:gitlawb` — native, anchored to the libp2p DHT //! -//! The canonical DID for a gitlawb actor is `did:key` during bootstrap +//! The canonical DID for a Twigpine actor is `did:key` during bootstrap //! and migrates to `did:gitlawb` once DHT anchoring is live. use ed25519_dalek::VerifyingKey; @@ -152,7 +152,7 @@ impl From for String { } } -/// A DID Document for a gitlawb actor. +/// A DID Document for a Twigpine actor. #[derive(Debug, Clone, Serialize, Deserialize)] pub struct DidDocument { #[serde(rename = "@context")] diff --git a/crates/gitlawb-core/src/http_sig.rs b/crates/gitlawb-core/src/http_sig.rs index 1089e34db..1a80b5c1a 100644 --- a/crates/gitlawb-core/src/http_sig.rs +++ b/crates/gitlawb-core/src/http_sig.rs @@ -1,6 +1,6 @@ //! HTTP Signatures (RFC 9421) for gitlawb. //! -//! Every write request to a gitlawb node must be signed by the actor's +//! Every write request to a Twigpine node must be signed by the actor's //! Ed25519 private key. The signature covers: //! - `@method` — HTTP method (uppercase) //! - `@path` — request path and query @@ -20,7 +20,7 @@ use crate::did::Did; use crate::identity::Keypair; use crate::{Error, Result}; -/// The component identifiers covered by every gitlawb signature. +/// The component identifiers covered by every Twigpine signature. pub const COVERED_COMPONENTS: &[&str] = &["@method", "@path", "content-digest"]; /// The three headers produced by RFC 9421 signing. diff --git a/crates/gitlawb-core/src/identity.rs b/crates/gitlawb-core/src/identity.rs index beef4d1bc..1d0b7b318 100644 --- a/crates/gitlawb-core/src/identity.rs +++ b/crates/gitlawb-core/src/identity.rs @@ -1,6 +1,6 @@ //! Keypair generation, storage, and signing. //! -//! A gitlawb identity is an Ed25519 keypair. The public key encodes into a +//! A Twigpine identity is an Ed25519 keypair. The public key encodes into a //! `did:key` DID. The private key is stored as PKCS#8 PEM on disk. use ed25519_dalek::{Signature, Signer, SigningKey, VerifyingKey}; @@ -11,7 +11,7 @@ use zeroize::Zeroizing; use crate::did::Did; use crate::{Error, Result}; -/// An Ed25519 keypair that is the root identity for a gitlawb actor. +/// An Ed25519 keypair that is the root identity for a Twigpine actor. #[derive(Clone)] pub struct Keypair { signing_key: SigningKey, diff --git a/crates/gitlawb-core/src/redirect.rs b/crates/gitlawb-core/src/redirect.rs index 06460c291..30648df70 100644 --- a/crates/gitlawb-core/src/redirect.rs +++ b/crates/gitlawb-core/src/redirect.rs @@ -1,4 +1,4 @@ -//! Redirect policy shared by every gitlawb HTTP client that signs its requests. +//! Redirect policy shared by every Twigpine HTTP client that signs its requests. //! //! Both `gl` (async) and `git-remote-gitlawb` (blocking) attach RFC 9421 //! `Signature` and `Signature-Input` headers, and reqwest strips only @@ -50,7 +50,7 @@ pub const MAX_REDIRECTS: usize = 10; /// signed and unsigned callers alike, for the same reason the predicate is shared: /// two rules would drift. /// -/// The clause pins `@path`, and only `@path`. A gitlawb signature also covers +/// The clause pins `@path`, and only `@path`. A Twigpine signature also covers /// `@method` and `content-digest`, and both of those are still open on a followed /// hop: on a 301, 302 or 303, reqwest 0.12.28 delegates to tower-http's /// `FollowRedirect`, which rewrites a POST to a GET and empties the body @@ -188,7 +188,7 @@ mod tests { "the node's name pushed into the fragment: the host is the attacker's", ), // The request-target clause, both directions. `@path` is the only thing a - // gitlawb signature binds the request to, so a hop that rewrites it hands + // Twigpine signature binds the request to, so a hop that rewrites it hands // the node a signature over a target it never received. ( "https://node.example/a?x=1", diff --git a/crates/gitlawb-core/src/ucan.rs b/crates/gitlawb-core/src/ucan.rs index 86b3dd9f9..91b8f1265 100644 --- a/crates/gitlawb-core/src/ucan.rs +++ b/crates/gitlawb-core/src/ucan.rs @@ -1,7 +1,7 @@ //! UCAN (User Controlled Authorization Networks) — capability token types. //! //! UCANs let a DID delegate specific capabilities to another DID, -//! with optional expiry and revocation. gitlawb uses UCANs for: +//! with optional expiry and revocation. Twigpine uses UCANs for: //! - Delegating push access to a branch to a CI agent //! - Granting a reviewer the ability to approve PRs //! - Bootstrap tokens issued at registration @@ -57,7 +57,7 @@ impl Capability { } } -/// Well-known gitlawb capability strings. +/// Well-known Twigpine capability strings. pub mod caps { pub const GIT_PUSH: &str = "git/push"; pub const GIT_FETCH: &str = "git/fetch"; diff --git a/crates/gitlawb-node/Cargo.toml b/crates/gitlawb-node/Cargo.toml index bc3db2991..ecfbb2824 100644 --- a/crates/gitlawb-node/Cargo.toml +++ b/crates/gitlawb-node/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "gitlawb-node" -description = "The gitlawb node daemon — git hosting over HTTP with DID auth" +description = "The Twigpine node daemon — git hosting over HTTP with DID auth" version = "0.7.1" # x-release-please-version edition.workspace = true rust-version.workspace = true diff --git a/crates/gitlawb-node/src/api/peers.rs b/crates/gitlawb-node/src/api/peers.rs index 3934e7a36..b6e65a5a9 100644 --- a/crates/gitlawb-node/src/api/peers.rs +++ b/crates/gitlawb-node/src/api/peers.rs @@ -520,7 +520,7 @@ mod tests { #[test] fn accepts_public_https_and_http() { - assert!(is_public_http_url("https://node.gitlawb.com")); + assert!(is_public_http_url("https://node.twigpine.com")); assert!(is_public_http_url("https://manila.gitlawb.com/")); assert!(is_public_http_url("http://203.0.113.10:7545")); } @@ -544,7 +544,7 @@ mod tests { "http://[::1]:7545", "http://gitlawb-node.internal:7545", "http://my-node.local/", - "ftp://node.gitlawb.com", + "ftp://node.twigpine.com", "not-a-url", "", // Trailing-dot FQDN of an internal host. diff --git a/crates/gitlawb-node/src/api/repos.rs b/crates/gitlawb-node/src/api/repos.rs index 2abc0dd97..ed0781ec5 100644 --- a/crates/gitlawb-node/src/api/repos.rs +++ b/crates/gitlawb-node/src/api/repos.rs @@ -4249,7 +4249,7 @@ mod tests { "9d92186a", "did:key:z6Mkwbud", "nipmod", - "Decentralized npm for agents on Gitlawb", + "Decentralized npm for agents on Twigpine", "2026-01-15T00:00:00Z", ); @@ -4263,7 +4263,7 @@ mod tests { ); assert_eq!( rec.description.as_deref(), - Some("Decentralized npm for agents on Gitlawb"), + Some("Decentralized npm for agents on Twigpine"), "canonical description and metadata survive, not the mirror placeholder", ); assert_eq!(*stars, 7, "star count follows the canonical row"); diff --git a/crates/gitlawb-node/src/api/resolve.rs b/crates/gitlawb-node/src/api/resolve.rs index 0880042eb..27fb83b84 100644 --- a/crates/gitlawb-node/src/api/resolve.rs +++ b/crates/gitlawb-node/src/api/resolve.rs @@ -1,4 +1,4 @@ -//! DID resolution — look up a node by its gitlawb DID. +//! DID resolution — look up a node by its Twigpine DID. //! //! First checks the local peers table (HTTP-layer peer discovery), //! then queries the Kademlia DHT via the libp2p swarm. @@ -14,7 +14,7 @@ use crate::state::AppState; /// GET /api/v1/resolve/{did} /// -/// Resolves a gitlawb DID to an HTTP URL and p2p multiaddr. +/// Resolves a Twigpine DID to an HTTP URL and p2p multiaddr. /// Checks local peer cache first, then falls back to Kademlia DHT lookup. pub async fn resolve_did( State(state): State, diff --git a/crates/gitlawb-node/src/auth/mod.rs b/crates/gitlawb-node/src/auth/mod.rs index 27b67786e..d6651a013 100644 --- a/crates/gitlawb-node/src/auth/mod.rs +++ b/crates/gitlawb-node/src/auth/mod.rs @@ -120,7 +120,7 @@ pub async fn require_signature(request: Request, next: Next) -> Response { COVERED_COMPONENTS.join(", "), missing.join(", ") ), - "hint": "See https://gitlawb.com/agents#authentication", + "hint": "See https://twigpine.com/agents#authentication", })), ) .into_response(); @@ -380,7 +380,7 @@ fn human_detected(message: &str) -> impl IntoResponse { "error": "not_an_agent", "message": message, "hint": "gl identity new && gl register", - "docs": "https://gitlawb.com/agents", + "docs": "https://twigpine.com/agents", })), ) } diff --git a/crates/gitlawb-node/src/bootstrap.rs b/crates/gitlawb-node/src/bootstrap.rs index 86a6a201e..d9ef732cd 100644 --- a/crates/gitlawb-node/src/bootstrap.rs +++ b/crates/gitlawb-node/src/bootstrap.rs @@ -1,4 +1,4 @@ -//! Embedded seed list of public Gitlawb network nodes. +//! Embedded seed list of public Twigpine network nodes. //! //! This module parses `bootstrap-peers.json` (embedded at compile time) and //! merges its contents into the runtime config so a fresh `docker compose up` diff --git a/crates/gitlawb-node/src/config.rs b/crates/gitlawb-node/src/config.rs index dd12d0144..3cf971191 100644 --- a/crates/gitlawb-node/src/config.rs +++ b/crates/gitlawb-node/src/config.rs @@ -29,7 +29,7 @@ use std::path::PathBuf; pub const GIT_SERVICE_TIMEOUT_SECS_MAX: u64 = 100 * 365 * 24 * 60 * 60; #[derive(Parser, Debug, Clone)] -#[command(name = "gitlawb-node", about = "gitlawb node daemon", version)] +#[command(name = "gitlawb-node", about = "Twigpine node daemon", version)] pub struct Config { /// Directory where bare git repositories are stored #[arg(long, env = "GITLAWB_REPOS_DIR", default_value = "./data/repos")] diff --git a/crates/gitlawb-node/src/main.rs b/crates/gitlawb-node/src/main.rs index d600e5bcd..3fb5658e2 100644 --- a/crates/gitlawb-node/src/main.rs +++ b/crates/gitlawb-node/src/main.rs @@ -129,7 +129,7 @@ async fn main() -> Result<()> { info!("╔══════════════════════════════════════════╗"); info!( - "║ gitlawb node v{} ║", + "║ Twigpine node v{} ║", env!("CARGO_PKG_VERSION") ); info!("╚══════════════════════════════════════════╝"); diff --git a/crates/gitlawb-node/src/metrics.rs b/crates/gitlawb-node/src/metrics.rs index 653f338ca..b331539d3 100644 --- a/crates/gitlawb-node/src/metrics.rs +++ b/crates/gitlawb-node/src/metrics.rs @@ -1,4 +1,4 @@ -//! Prometheus metrics for the gitlawb node. +//! Prometheus metrics for the Twigpine node. //! //! Exposes a small, deliberately conservative set of counters and histograms //! that cover the questions an operator actually asks of a live node: diff --git a/crates/gitlawb-node/src/p2p/mod.rs b/crates/gitlawb-node/src/p2p/mod.rs index 5f9817e9c..2ea8dccce 100644 --- a/crates/gitlawb-node/src/p2p/mod.rs +++ b/crates/gitlawb-node/src/p2p/mod.rs @@ -5,7 +5,7 @@ //! - Real-time ref-update events via Gossipsub //! //! The node's PeerId is derived from its Ed25519 identity keypair, -//! so the gitlawb DID and libp2p PeerId share the same key. +//! so the Twigpine DID and libp2p PeerId share the same key. use std::collections::{hash_map::DefaultHasher, HashMap}; use std::hash::{Hash, Hasher}; @@ -279,7 +279,7 @@ pub struct RefUpdateEvent { /// verification path. #[serde(default, skip_serializing_if = "is_zero")] pub v: u32, - /// gitlawb DID of the node publishing the event + /// Twigpine DID of the node publishing the event pub node_did: String, /// DID of the agent who pushed pub pusher_did: String, @@ -779,7 +779,7 @@ pub(crate) async fn ingest_ref_update( } } -/// A DID record stored in the Kademlia DHT — maps a gitlawb DID to a node. +/// A DID record stored in the Kademlia DHT — maps a Twigpine DID to a node. #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] pub struct DidRecord { pub did: String, diff --git a/crates/gl/Cargo.toml b/crates/gl/Cargo.toml index 2b973a4c6..29ca066ac 100644 --- a/crates/gl/Cargo.toml +++ b/crates/gl/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "gl" -description = "The gitlawb CLI — identity management, node control, MCP server" +description = "The Twigpine CLI — identity management, node control, MCP server" version = "0.7.1" # x-release-please-version edition.workspace = true rust-version.workspace = true diff --git a/crates/gl/src/agent.rs b/crates/gl/src/agent.rs index d72c230a6..c8a6424be 100644 --- a/crates/gl/src/agent.rs +++ b/crates/gl/src/agent.rs @@ -17,7 +17,11 @@ pub struct AgentArgs { pub enum AgentCmd { /// List agents registered on a node List { - #[arg(long, default_value = "https://node.gitlawb.com", env = "GITLAWB_NODE")] + #[arg( + long, + default_value = "https://node.twigpine.com", + env = "GITLAWB_NODE" + )] node: String, /// Filter by capability (e.g. git:push) #[arg(long)] @@ -29,7 +33,11 @@ pub enum AgentCmd { Show { /// Agent DID or short key segment did: String, - #[arg(long, default_value = "https://node.gitlawb.com", env = "GITLAWB_NODE")] + #[arg( + long, + default_value = "https://node.twigpine.com", + env = "GITLAWB_NODE" + )] node: String, }, } diff --git a/crates/gl/src/bounty.rs b/crates/gl/src/bounty.rs index ccc658a15..a3bb7d036 100644 --- a/crates/gl/src/bounty.rs +++ b/crates/gl/src/bounty.rs @@ -39,7 +39,11 @@ pub enum BountyCmd { /// Deadline in seconds (default 7 days) #[arg(long)] deadline: Option, - #[arg(long, default_value = "https://node.gitlawb.com", env = "GITLAWB_NODE")] + #[arg( + long, + default_value = "https://node.twigpine.com", + env = "GITLAWB_NODE" + )] node: String, #[arg(long)] dir: Option, @@ -51,7 +55,11 @@ pub enum BountyCmd { /// Filter by status: open, claimed, submitted, completed #[arg(long)] status: Option, - #[arg(long, default_value = "https://node.gitlawb.com", env = "GITLAWB_NODE")] + #[arg( + long, + default_value = "https://node.twigpine.com", + env = "GITLAWB_NODE" + )] node: String, #[arg(long)] dir: Option, @@ -60,7 +68,11 @@ pub enum BountyCmd { Show { /// Bounty ID id: String, - #[arg(long, default_value = "https://node.gitlawb.com", env = "GITLAWB_NODE")] + #[arg( + long, + default_value = "https://node.twigpine.com", + env = "GITLAWB_NODE" + )] node: String, #[arg(long)] dir: Option, @@ -72,7 +84,11 @@ pub enum BountyCmd { /// Your wallet address for payout #[arg(long)] wallet: Option, - #[arg(long, default_value = "https://node.gitlawb.com", env = "GITLAWB_NODE")] + #[arg( + long, + default_value = "https://node.twigpine.com", + env = "GITLAWB_NODE" + )] node: String, #[arg(long)] dir: Option, @@ -84,7 +100,11 @@ pub enum BountyCmd { /// Pull request ID #[arg(long)] pr: String, - #[arg(long, default_value = "https://node.gitlawb.com", env = "GITLAWB_NODE")] + #[arg( + long, + default_value = "https://node.twigpine.com", + env = "GITLAWB_NODE" + )] node: String, #[arg(long)] dir: Option, @@ -96,7 +116,11 @@ pub enum BountyCmd { /// On-chain payout tx hash #[arg(long)] tx_hash: Option, - #[arg(long, default_value = "https://node.gitlawb.com", env = "GITLAWB_NODE")] + #[arg( + long, + default_value = "https://node.twigpine.com", + env = "GITLAWB_NODE" + )] node: String, #[arg(long)] dir: Option, @@ -105,14 +129,22 @@ pub enum BountyCmd { Cancel { /// Bounty ID id: String, - #[arg(long, default_value = "https://node.gitlawb.com", env = "GITLAWB_NODE")] + #[arg( + long, + default_value = "https://node.twigpine.com", + env = "GITLAWB_NODE" + )] node: String, #[arg(long)] dir: Option, }, /// Show bounty stats and leaderboard Stats { - #[arg(long, default_value = "https://node.gitlawb.com", env = "GITLAWB_NODE")] + #[arg( + long, + default_value = "https://node.twigpine.com", + env = "GITLAWB_NODE" + )] node: String, }, } diff --git a/crates/gl/src/cert.rs b/crates/gl/src/cert.rs index 87ad5aece..fa0c0814c 100644 --- a/crates/gl/src/cert.rs +++ b/crates/gl/src/cert.rs @@ -26,7 +26,11 @@ pub enum CertCmd { List { /// Repository in / or format repo: String, - #[arg(long, default_value = "https://node.gitlawb.com", env = "GITLAWB_NODE")] + #[arg( + long, + default_value = "https://node.twigpine.com", + env = "GITLAWB_NODE" + )] node: String, #[arg(long)] dir: Option, @@ -37,7 +41,11 @@ pub enum CertCmd { repo: String, /// Certificate ID id: String, - #[arg(long, default_value = "https://node.gitlawb.com", env = "GITLAWB_NODE")] + #[arg( + long, + default_value = "https://node.twigpine.com", + env = "GITLAWB_NODE" + )] node: String, #[arg(long)] dir: Option, diff --git a/crates/gl/src/changelog.rs b/crates/gl/src/changelog.rs index 873689445..a74aa060e 100644 --- a/crates/gl/src/changelog.rs +++ b/crates/gl/src/changelog.rs @@ -15,7 +15,11 @@ pub struct ChangelogArgs { /// Maximum number of events to show #[arg(long, default_value = "20")] pub limit: usize, - #[arg(long, default_value = "https://node.gitlawb.com", env = "GITLAWB_NODE")] + #[arg( + long, + default_value = "https://node.twigpine.com", + env = "GITLAWB_NODE" + )] pub node: String, #[arg(long)] pub dir: Option, @@ -31,7 +35,7 @@ pub async fn run(args: ChangelogArgs) -> Result<()> { }; if repo.is_empty() { - anyhow::bail!("no repo specified — pass or run from inside a gitlawb repo"); + anyhow::bail!("no repo specified — pass or run from inside a Twigpine repo"); } let (owner, name) = if let Some((o, n)) = repo.split_once('/') { diff --git a/crates/gl/src/clone.rs b/crates/gl/src/clone.rs index 926a2d57c..35811d98f 100644 --- a/crates/gl/src/clone.rs +++ b/crates/gl/src/clone.rs @@ -1,4 +1,4 @@ -//! `gl clone`: clean partial clone of a gitlawb repo with private subtrees. +//! `gl clone`: clean partial clone of a Twigpine repo with private subtrees. //! //! A repo may withhold blob content under some path globs from the caller //! (Phase 3). The resulting pack is not closed under reachability, so a stock @@ -76,7 +76,11 @@ pub struct CloneArgs { #[arg(long)] pub branch: Option, - #[arg(long, default_value = "https://node.gitlawb.com", env = "GITLAWB_NODE")] + #[arg( + long, + default_value = "https://node.twigpine.com", + env = "GITLAWB_NODE" + )] pub node: String, /// Arweave gateway for B3 manifest discovery/fetch when a node cannot supply @@ -574,7 +578,7 @@ fn merge_manifests( .collect() } -/// Option B3 fallback recovery, with no dependency on a gitlawb node API. Query +/// Option B3 fallback recovery, with no dependency on a Twigpine node API. Query /// the Arweave gateway for this repo's encrypted manifests, merge them, and for /// each blob still missing locally that the caller can decrypt, pull the envelope /// from a public IPFS gateway, decrypt, and install it as a loose object. Returns diff --git a/crates/gl/src/doctor.rs b/crates/gl/src/doctor.rs index 86f503344..50d1221c1 100644 --- a/crates/gl/src/doctor.rs +++ b/crates/gl/src/doctor.rs @@ -1,10 +1,10 @@ -//! `gl doctor` — check your gitlawb installation and connectivity. +//! `gl doctor` — check your Twigpine installation and connectivity. //! //! Checks: //! 1. Identity — ~/.gitlawb/identity.pem exists, DID parseable //! 2. Registration — ~/.gitlawb/ucan.json exists (registered with a node) //! 3. GITLAWB_NODE — env var is set to a non-localhost URL -//! 4. Node — GITLAWB_NODE is reachable (default: https://node.gitlawb.com) +//! 4. Node — GITLAWB_NODE is reachable (default: https://node.twigpine.com) //! 5. git helper — git-remote-gitlawb is in PATH //! 6. git — git is installed //! 7. version — gl is up to date with latest GitHub release @@ -15,7 +15,7 @@ use std::path::PathBuf; use crate::http::NodeClient; -const PUBLIC_NODE: &str = "https://node.gitlawb.com"; +const PUBLIC_NODE: &str = "https://node.twigpine.com"; const GITHUB_API_BASE: &str = "https://api.github.com"; #[derive(Args)] @@ -70,7 +70,7 @@ impl Check { } pub async fn run(args: DoctorArgs) -> Result<()> { - println!("gl doctor — checking your gitlawb setup"); + println!("gl doctor — checking your Twigpine setup"); println!(); let dir = args.dir.unwrap_or_else(|| { @@ -160,7 +160,7 @@ pub async fn run(args: DoctorArgs) -> Result<()> { checks.push(Check::fail( "GITLAWB_NODE", "not set — git-remote-gitlawb will fall back to http://127.0.0.1:7545", - "export GITLAWB_NODE=https://node.gitlawb.com", + "export GITLAWB_NODE=https://node.twigpine.com", )); } } @@ -187,7 +187,7 @@ pub async fn run(args: DoctorArgs) -> Result<()> { "node is v{version} but gl is v{gl_ver} — your CLI may be missing \ features (signing / iCaptcha) this node requires" ), - "upgrade gl: curl -sSf https://gitlawb.com/install.sh | sh", + "upgrade gl: curl -sSf https://twigpine.com/install.sh | sh", )); } } @@ -241,7 +241,7 @@ pub async fn run(args: DoctorArgs) -> Result<()> { checks.push(Check::fail( "git-remote-gitlawb", "not found in PATH — gitlawb:// clone/push will not work", - "curl -sSf https://gitlawb.com/install.sh | sh", + "curl -sSf https://twigpine.com/install.sh | sh", )); } @@ -408,7 +408,7 @@ fn which_in_path(name: &str) -> bool { .unwrap_or(false) } -/// Fetch the latest release tag from Gitlawb/node (the actual release repo — see install.sh's +/// Fetch the latest release tag from Twigpine/node (the actual release repo — see install.sh's /// `GITLAWB_RELEASE_REPO` default) and compare to current version. async fn check_version(current: &'static str, github_api_base: &str) -> Check { let client = match reqwest::Client::builder() @@ -422,7 +422,7 @@ async fn check_version(current: &'static str, github_api_base: &str) -> Check { let resp = match client .get(format!( - "{github_api_base}/repos/Gitlawb/node/releases/latest" + "{github_api_base}/repos/Twigpine/node/releases/latest" )) .send() .await @@ -457,7 +457,7 @@ async fn check_version(current: &'static str, github_api_base: &str) -> Check { Check::warn( "version", format!("v{current} → v{tag} available"), - "curl -sSf https://gitlawb.com/install.sh | sh", + "curl -sSf https://twigpine.com/install.sh | sh", ) } else { Check::pass("version", format!("v{current} (up to date)")) @@ -550,7 +550,7 @@ mod tests { assert!(is_loopback_url("http://localhost:7545")); assert!(is_loopback_url("http://[::1]:7545")); assert!(!is_loopback_url("https://localhost.example")); - assert!(!is_loopback_url("https://node.gitlawb.com/localhost")); + assert!(!is_loopback_url("https://node.twigpine.com/localhost")); assert!(!is_loopback_url("not a url")); } @@ -603,7 +603,7 @@ mod tests { async fn test_check_version_queries_gitlawb_node_releases() { let mut server = mockito::Server::new_async().await; let _m = server - .mock("GET", "/repos/Gitlawb/node/releases/latest") + .mock("GET", "/repos/Twigpine/node/releases/latest") .with_status(200) .with_header("content-type", "application/json") .with_body(r#"{"tag_name":"v9.9.9"}"#) @@ -619,7 +619,7 @@ mod tests { async fn test_check_version_up_to_date() { let mut server = mockito::Server::new_async().await; let _m = server - .mock("GET", "/repos/Gitlawb/node/releases/latest") + .mock("GET", "/repos/Twigpine/node/releases/latest") .with_status(200) .with_header("content-type", "application/json") .with_body(r#"{"tag_name":"v0.1.0"}"#) @@ -635,7 +635,7 @@ mod tests { async fn test_check_version_http_error() { let mut server = mockito::Server::new_async().await; let _m = server - .mock("GET", "/repos/Gitlawb/node/releases/latest") + .mock("GET", "/repos/Twigpine/node/releases/latest") .with_status(403) .create_async() .await; diff --git a/crates/gl/src/http.rs b/crates/gl/src/http.rs index 7a28c6f7b..b5c24f27d 100644 --- a/crates/gl/src/http.rs +++ b/crates/gl/src/http.rs @@ -1,4 +1,4 @@ -//! Signed HTTP client for gitlawb API calls (async). +//! Signed HTTP client for Twigpine API calls (async). //! //! Writes are signed with RFC 9421 HTTP Signatures. When the node gates a write //! behind iCaptcha (HTTP 403 `icaptcha_proof_required`, advertised via the diff --git a/crates/gl/src/identity.rs b/crates/gl/src/identity.rs index bde5c94c6..ae3d475f2 100644 --- a/crates/gl/src/identity.rs +++ b/crates/gl/src/identity.rs @@ -154,7 +154,7 @@ async fn cmd_new_with_reader( println!(" DID: {did}"); println!(" Key: {}", path.display()); println!(); - println!(" Your DID is your identity on the gitlawb network."); + println!(" Your DID is your identity on the Twigpine network."); println!(" Keep your key file safe — it cannot be recovered if lost."); Ok(()) diff --git a/crates/gl/src/init.rs b/crates/gl/src/init.rs index 1bc3c406d..8736e7913 100644 --- a/crates/gl/src/init.rs +++ b/crates/gl/src/init.rs @@ -1,7 +1,7 @@ //! `gl init` — zero-to-push in one command. //! //! Detects or initializes a git repo, ensures an identity exists, -//! registers with the node, creates a remote repo, adds the gitlawb +//! registers with the node, creates a remote repo, adds the Twigpine //! remote, and pushes. use anyhow::{Context, Result}; @@ -19,7 +19,11 @@ pub struct InitArgs { pub name: Option, /// Node URL to register with - #[arg(long, default_value = "https://node.gitlawb.com", env = "GITLAWB_NODE")] + #[arg( + long, + default_value = "https://node.twigpine.com", + env = "GITLAWB_NODE" + )] pub node: String, /// Identity directory (default: ~/.gitlawb) @@ -151,7 +155,7 @@ pub async fn run(args: InitArgs) -> Result<()> { println!(" Repository created."); } - // 5. Add gitlawb remote + // 5. Add Twigpine remote let did_short = did.to_string(); let did_short = did_short.split(':').next_back().unwrap_or(&did_short); let remote_url = format!("gitlawb://{did_short}/{repo_name}"); diff --git a/crates/gl/src/ipfs_cmd.rs b/crates/gl/src/ipfs_cmd.rs index 93ca5511b..678248162 100644 --- a/crates/gl/src/ipfs_cmd.rs +++ b/crates/gl/src/ipfs_cmd.rs @@ -1,6 +1,6 @@ //! `gl ipfs` — IPFS pin management commands. //! -//! Communicates with the gitlawb node to list pinned CIDs and retrieve git +//! Communicates with the Twigpine node to list pinned CIDs and retrieve git //! objects by their content-addressed CID. use std::path::PathBuf; @@ -22,7 +22,11 @@ pub struct IpfsArgs { pub enum IpfsCmd { /// List all CIDs pinned to the node's local IPFS daemon List { - #[arg(long, default_value = "https://node.gitlawb.com", env = "GITLAWB_NODE")] + #[arg( + long, + default_value = "https://node.twigpine.com", + env = "GITLAWB_NODE" + )] node: String, /// Identity directory (default: ~/.gitlawb) #[arg(long)] @@ -69,7 +73,11 @@ pub enum IpfsCmd { Get { /// The CIDv1 string (e.g. bafkrei...) cid: String, - #[arg(long, default_value = "https://node.gitlawb.com", env = "GITLAWB_NODE")] + #[arg( + long, + default_value = "https://node.twigpine.com", + env = "GITLAWB_NODE" + )] node: String, /// Identity directory (default: ~/.gitlawb) #[arg(long)] diff --git a/crates/gl/src/issue.rs b/crates/gl/src/issue.rs index 57bd6944b..951716c2f 100644 --- a/crates/gl/src/issue.rs +++ b/crates/gl/src/issue.rs @@ -32,7 +32,11 @@ pub enum IssueCmd { /// Issue body (optional) #[arg(long, short)] body: Option, - #[arg(long, default_value = "https://node.gitlawb.com", env = "GITLAWB_NODE")] + #[arg( + long, + default_value = "https://node.twigpine.com", + env = "GITLAWB_NODE" + )] node: String, #[arg(long)] dir: Option, @@ -41,7 +45,11 @@ pub enum IssueCmd { List { /// Repository in / or format repo: String, - #[arg(long, default_value = "https://node.gitlawb.com", env = "GITLAWB_NODE")] + #[arg( + long, + default_value = "https://node.twigpine.com", + env = "GITLAWB_NODE" + )] node: String, #[arg(long)] dir: Option, @@ -52,7 +60,11 @@ pub enum IssueCmd { repo: String, /// Issue ID id: String, - #[arg(long, default_value = "https://node.gitlawb.com", env = "GITLAWB_NODE")] + #[arg( + long, + default_value = "https://node.twigpine.com", + env = "GITLAWB_NODE" + )] node: String, #[arg(long)] dir: Option, @@ -63,7 +75,11 @@ pub enum IssueCmd { repo: String, /// Issue ID id: String, - #[arg(long, default_value = "https://node.gitlawb.com", env = "GITLAWB_NODE")] + #[arg( + long, + default_value = "https://node.twigpine.com", + env = "GITLAWB_NODE" + )] node: String, #[arg(long)] dir: Option, @@ -74,7 +90,11 @@ pub enum IssueCmd { id: String, #[arg(long, short)] body: String, - #[arg(long, default_value = "https://node.gitlawb.com", env = "GITLAWB_NODE")] + #[arg( + long, + default_value = "https://node.twigpine.com", + env = "GITLAWB_NODE" + )] node: String, #[arg(long)] dir: Option, @@ -83,7 +103,11 @@ pub enum IssueCmd { Comments { repo: String, id: String, - #[arg(long, default_value = "https://node.gitlawb.com", env = "GITLAWB_NODE")] + #[arg( + long, + default_value = "https://node.twigpine.com", + env = "GITLAWB_NODE" + )] node: String, #[arg(long)] dir: Option, diff --git a/crates/gl/src/main.rs b/crates/gl/src/main.rs index 1c1a50d1d..8744f4e9e 100644 --- a/crates/gl/src/main.rs +++ b/crates/gl/src/main.rs @@ -38,7 +38,7 @@ mod whoami; #[derive(Parser)] #[command( name = "gl", - about = "gitlawb CLI — identity, repos, MCP server", + about = "Twigpine CLI — identity, repos, MCP server", version, propagate_version = true )] @@ -49,16 +49,16 @@ struct Cli { #[derive(Subcommand)] enum Commands { - /// Manage your gitlawb identity (DID + keypair) + /// Manage your Twigpine identity (DID + keypair) Identity { #[command(subcommand)] cmd: identity::IdentityCmd, }, - /// Register this agent with a gitlawb node + /// Register this agent with a Twigpine node Register(register::RegisterArgs), - /// Clone a gitlawb repo, handling private subtrees cleanly + /// Clone a Twigpine repo, handling private subtrees cleanly Clone(clone::CloneArgs), /// Manage repositories @@ -85,10 +85,10 @@ enum Commands { /// Manage webhooks for a repository Webhook(webhook::WebhookArgs), - /// Mirror a public GitHub/GitLab/any-git repo into gitlawb + /// Mirror a public GitHub/GitLab/any-git repo into Twigpine Mirror(mirror::MirrorArgs), - /// MCP server — expose gitlawb tools to LLM agents + /// MCP server — expose Twigpine tools to LLM agents Mcp(mcp::McpArgs), /// Sync repos from peer nodes (HTTP fallback for p2p gossip) @@ -100,7 +100,7 @@ enum Commands { /// Register and resolve names on Base L2 Name(name::NameArgs), - /// Check your gitlawb installation and connectivity + /// Check your Twigpine installation and connectivity Doctor(doctor::DoctorArgs), /// Zero-to-push in one command — init repo, identity, register, remote diff --git a/crates/gl/src/mcp.rs b/crates/gl/src/mcp.rs index ae319c73a..bc180e226 100644 --- a/crates/gl/src/mcp.rs +++ b/crates/gl/src/mcp.rs @@ -2,7 +2,7 @@ //! //! Runs a JSON-RPC 2.0 server over stdin/stdout using LSP-style //! `Content-Length` framing. Exposes 15 tools that give LLM agents -//! structured access to the gitlawb network. +//! structured access to the Twigpine network. //! //! # Tools //! identity_show — current agent DID @@ -56,7 +56,11 @@ pub enum McpCmd { /// Start the MCP server (stdin/stdout JSON-RPC) Serve { /// Node URL to connect to - #[arg(long, default_value = "https://node.gitlawb.com", env = "GITLAWB_NODE")] + #[arg( + long, + default_value = "https://node.twigpine.com", + env = "GITLAWB_NODE" + )] node: String, /// Identity directory #[arg(long)] @@ -181,12 +185,12 @@ fn tool_definitions() -> Value { }, { "name": "node_info", - "description": "Get metadata about the connected gitlawb node: DID, version, network, protocols.", + "description": "Get metadata about the connected Twigpine node: DID, version, network, protocols.", "inputSchema": { "type": "object", "properties": {} } }, { "name": "node_health", - "description": "Check if the gitlawb node is alive and responding.", + "description": "Check if the Twigpine node is alive and responding.", "inputSchema": { "type": "object", "properties": {} } }, { @@ -204,12 +208,12 @@ fn tool_definitions() -> Value { }, { "name": "repo_list", - "description": "List all repositories on the connected gitlawb node.", + "description": "List all repositories on the connected Twigpine node.", "inputSchema": { "type": "object", "properties": {} } }, { "name": "repo_list_federated", - "description": "List repositories across ALL nodes in the gitlawb network (federation). Returns repos from this node and all known peer nodes, each annotated with node_url and node_did.", + "description": "List repositories across ALL nodes in the Twigpine network (federation). Returns repos from this node and all known peer nodes, each annotated with node_url and node_did.", "inputSchema": { "type": "object", "properties": {} } }, { @@ -262,7 +266,7 @@ fn tool_definitions() -> Value { }, { "name": "agent_register", - "description": "Register this agent with the gitlawb node to get a bootstrap UCAN token.", + "description": "Register this agent with the Twigpine node to get a bootstrap UCAN token.", "inputSchema": { "type": "object", "properties": { @@ -278,7 +282,7 @@ fn tool_definitions() -> Value { }, { "name": "agent_capabilities", - "description": "List all available UCAN capability strings in the gitlawb protocol.", + "description": "List all available UCAN capability strings in the Twigpine protocol.", "inputSchema": { "type": "object", "properties": {} } }, { diff --git a/crates/gl/src/mirror.rs b/crates/gl/src/mirror.rs index 400d3d45e..2e532e8b3 100644 --- a/crates/gl/src/mirror.rs +++ b/crates/gl/src/mirror.rs @@ -3,7 +3,7 @@ //! Flow: //! 1. Parse source URL → derive repo name (or use --repo override) //! 2. `git clone --mirror ` into a temp dir -//! 3. Create the repo on the gitlawb node (authenticated) +//! 3. Create the repo on the Twigpine node (authenticated) //! 4. `git push --mirror gitlawb:///` //! 5. Clean up temp dir @@ -22,15 +22,19 @@ pub struct MirrorArgs { /// Source repository URL (https://github.com/owner/repo, GitLab, or any public git URL) pub source: String, - /// Name to use on gitlawb (default: last path segment of source URL, .git suffix stripped) + /// Name to use on Twigpine (default: last path segment of source URL, .git suffix stripped) #[arg(long)] pub repo: Option, - /// Optional description for the gitlawb repo + /// Optional description for the Twigpine repo #[arg(long, short)] pub description: Option, - #[arg(long, default_value = "https://node.gitlawb.com", env = "GITLAWB_NODE")] + #[arg( + long, + default_value = "https://node.twigpine.com", + env = "GITLAWB_NODE" + )] pub node: String, #[arg(long)] @@ -62,7 +66,7 @@ pub async fn run(args: MirrorArgs) -> Result<()> { let owner_did = kp.did().to_string(); println!("Mirroring {source}"); - println!(" → gitlawb repo: {name}"); + println!(" → Twigpine repo: {name}"); println!(); // ── 3. git clone --mirror into a temp dir ───────────────────────────── @@ -89,8 +93,8 @@ pub async fn run(args: MirrorArgs) -> Result<()> { bail!("git clone --mirror failed\nCheck that the source URL is accessible: {source}"); } - // ── 4. Create the repo on gitlawb ───────────────────────────────────── - println!("Creating repo on gitlawb node..."); + // ── 4. Create the repo on Twigpine ───────────────────────────────────── + println!("Creating repo on Twigpine node..."); let client = NodeClient::new(&args.node, Some(kp)); let body = serde_json::to_vec(&json!({ @@ -113,7 +117,7 @@ pub async fn run(args: MirrorArgs) -> Result<()> { bail!("failed to create repo ({status}): {msg}"); } - // ── 5. git push --mirror to gitlawb ─────────────────────────────────── + // ── 5. git push --mirror to Twigpine ─────────────────────────────────── let gitlawb_url = format!("gitlawb://{owner_did}/{name}"); println!("Pushing to {gitlawb_url}..."); @@ -125,7 +129,7 @@ pub async fn run(args: MirrorArgs) -> Result<()> { .context("failed to run git push")?; if !push_status.success() { - bail!("git push --mirror failed\nThe repo was created on gitlawb but the push did not complete.\nYou can retry with:\n cd {path} && git push --mirror {gitlawb_url}", + bail!("git push --mirror failed\nThe repo was created on Twigpine but the push did not complete.\nYou can retry with:\n cd {path} && git push --mirror {gitlawb_url}", path = mirror_path.display()); } @@ -134,7 +138,7 @@ pub async fn run(args: MirrorArgs) -> Result<()> { println!(); println!("✓ Mirror complete: {name}"); println!(" Clone: git clone {gitlawb_url}"); - println!(" View: https://gitlawb.com/{owner_short}/{name}"); + println!(" View: https://twigpine.com/{owner_short}/{name}"); Ok(()) } diff --git a/crates/gl/src/name.rs b/crates/gl/src/name.rs index 9840518b5..57a851c8e 100644 --- a/crates/gl/src/name.rs +++ b/crates/gl/src/name.rs @@ -72,7 +72,7 @@ pub enum NameCmd { /// Name registry contract address #[arg(long, env = "GITLAWB_CONTRACT_NAME_REGISTRY", default_value = DEFAULT_NAME_REGISTRY)] contract: String, - /// gitlawb identity directory (reads your DID) + /// Twigpine identity directory (reads your DID) #[arg(long)] dir: Option, }, diff --git a/crates/gl/src/node.rs b/crates/gl/src/node.rs index 367ba576a..2e670513e 100644 --- a/crates/gl/src/node.rs +++ b/crates/gl/src/node.rs @@ -20,7 +20,11 @@ pub struct NodeArgs { pub enum NodeCmd { /// Show a comprehensive status dashboard for the node Status { - #[arg(long, default_value = "https://node.gitlawb.com", env = "GITLAWB_NODE")] + #[arg( + long, + default_value = "https://node.twigpine.com", + env = "GITLAWB_NODE" + )] node: String, /// Identity directory (default: ~/.gitlawb) #[arg(long)] @@ -29,13 +33,21 @@ pub enum NodeCmd { /// Check trust score for a DID Trust { did: String, - #[arg(long, default_value = "https://node.gitlawb.com", env = "GITLAWB_NODE")] + #[arg( + long, + default_value = "https://node.twigpine.com", + env = "GITLAWB_NODE" + )] node: String, }, /// Resolve a DID to node info Resolve { did: String, - #[arg(long, default_value = "https://node.gitlawb.com", env = "GITLAWB_NODE")] + #[arg( + long, + default_value = "https://node.twigpine.com", + env = "GITLAWB_NODE" + )] node: String, }, @@ -312,7 +324,7 @@ async fn cmd_status(node: String, dir: Option) -> Result<()> { // ── Render dashboard ────────────────────────────────────────────────── println!("╔══════════════════════════════════════════════╗"); - println!("║ gitlawb node status ║"); + println!("║ Twigpine node status ║"); println!("╚══════════════════════════════════════════════╝"); println!(); diff --git a/crates/gl/src/node_stake.rs b/crates/gl/src/node_stake.rs index 869afcf83..48c4c504c 100644 --- a/crates/gl/src/node_stake.rs +++ b/crates/gl/src/node_stake.rs @@ -72,7 +72,7 @@ pub async fn cmd_register( let token_addr: Address = token.parse().context("invalid token address")?; let contract_addr: Address = contract.parse().context("invalid contract address")?; - println!("Registering gitlawb node on Base L2..."); + println!("Registering Twigpine node on Base L2..."); println!(" DID: {did}"); println!(" Stake: {stake} $GITLAWB"); println!(" HTTP URL: {http_url}"); diff --git a/crates/gl/src/peer.rs b/crates/gl/src/peer.rs index aa420f2e9..c83147537 100644 --- a/crates/gl/src/peer.rs +++ b/crates/gl/src/peer.rs @@ -20,14 +20,22 @@ pub struct PeerArgs { pub enum PeerCmd { /// List known peers on the node List { - #[arg(long, default_value = "https://node.gitlawb.com", env = "GITLAWB_NODE")] + #[arg( + long, + default_value = "https://node.twigpine.com", + env = "GITLAWB_NODE" + )] node: String, }, /// Announce yourself to a peer node (adds you to their peer list) Add { /// The URL of the peer node to announce to peer_url: String, - #[arg(long, default_value = "https://node.gitlawb.com", env = "GITLAWB_NODE")] + #[arg( + long, + default_value = "https://node.twigpine.com", + env = "GITLAWB_NODE" + )] node: String, #[arg(long)] dir: Option, @@ -36,14 +44,22 @@ pub enum PeerCmd { Ping { /// The DID of the peer to ping did: String, - #[arg(long, default_value = "https://node.gitlawb.com", env = "GITLAWB_NODE")] + #[arg( + long, + default_value = "https://node.twigpine.com", + env = "GITLAWB_NODE" + )] node: String, }, /// Resolve a DID to its node URL and p2p info (checks local cache then Kademlia DHT) Resolve { /// The DID to resolve (e.g. did:key:z6Mk...) did: String, - #[arg(long, default_value = "https://node.gitlawb.com", env = "GITLAWB_NODE")] + #[arg( + long, + default_value = "https://node.twigpine.com", + env = "GITLAWB_NODE" + )] node: String, }, } diff --git a/crates/gl/src/pr.rs b/crates/gl/src/pr.rs index 8e0c4b72d..f9f6af381 100644 --- a/crates/gl/src/pr.rs +++ b/crates/gl/src/pr.rs @@ -35,7 +35,11 @@ pub enum PrCmd { /// Repo owner DID short key (defaults to your DID — use when opening a PR on another user's repo) #[arg(long)] owner: Option, - #[arg(long, default_value = "https://node.gitlawb.com", env = "GITLAWB_NODE")] + #[arg( + long, + default_value = "https://node.twigpine.com", + env = "GITLAWB_NODE" + )] node: String, #[arg(long)] dir: Option, @@ -44,7 +48,11 @@ pub enum PrCmd { List { /// Repository name repo: String, - #[arg(long, default_value = "https://node.gitlawb.com", env = "GITLAWB_NODE")] + #[arg( + long, + default_value = "https://node.twigpine.com", + env = "GITLAWB_NODE" + )] node: String, #[arg(long)] dir: Option, @@ -55,7 +63,11 @@ pub enum PrCmd { repo: String, /// PR number number: u64, - #[arg(long, default_value = "https://node.gitlawb.com", env = "GITLAWB_NODE")] + #[arg( + long, + default_value = "https://node.twigpine.com", + env = "GITLAWB_NODE" + )] node: String, #[arg(long)] dir: Option, @@ -66,7 +78,11 @@ pub enum PrCmd { repo: String, /// PR number number: u64, - #[arg(long, default_value = "https://node.gitlawb.com", env = "GITLAWB_NODE")] + #[arg( + long, + default_value = "https://node.twigpine.com", + env = "GITLAWB_NODE" + )] node: String, #[arg(long)] dir: Option, @@ -77,7 +93,11 @@ pub enum PrCmd { repo: String, /// PR number number: u64, - #[arg(long, default_value = "https://node.gitlawb.com", env = "GITLAWB_NODE")] + #[arg( + long, + default_value = "https://node.twigpine.com", + env = "GITLAWB_NODE" + )] node: String, #[arg(long)] dir: Option, @@ -94,7 +114,11 @@ pub enum PrCmd { /// Review body #[arg(long, short)] body: Option, - #[arg(long, default_value = "https://node.gitlawb.com", env = "GITLAWB_NODE")] + #[arg( + long, + default_value = "https://node.twigpine.com", + env = "GITLAWB_NODE" + )] node: String, #[arg(long)] dir: Option, @@ -108,7 +132,11 @@ pub enum PrCmd { /// Comment body #[arg(long, short)] body: String, - #[arg(long, default_value = "https://node.gitlawb.com", env = "GITLAWB_NODE")] + #[arg( + long, + default_value = "https://node.twigpine.com", + env = "GITLAWB_NODE" + )] node: String, #[arg(long)] dir: Option, @@ -119,7 +147,11 @@ pub enum PrCmd { repo: String, /// PR number number: u64, - #[arg(long, default_value = "https://node.gitlawb.com", env = "GITLAWB_NODE")] + #[arg( + long, + default_value = "https://node.twigpine.com", + env = "GITLAWB_NODE" + )] node: String, #[arg(long)] dir: Option, diff --git a/crates/gl/src/profile.rs b/crates/gl/src/profile.rs index 7de383bc5..d67b81e8a 100644 --- a/crates/gl/src/profile.rs +++ b/crates/gl/src/profile.rs @@ -1,6 +1,6 @@ //! `gl profile` — manage your agent profile (display name, bio, avatar, socials). //! -//! Profile metadata is stored on the gitlawb node and optionally pinned to IPFS +//! Profile metadata is stored on the Twigpine node and optionally pinned to IPFS //! for decentralized resolution. All writes are signed with your DID key. use anyhow::{Context, Result}; @@ -59,7 +59,11 @@ pub enum ProfileCmd { pin: bool, /// Node URL - #[arg(long, default_value = "https://node.gitlawb.com", env = "GITLAWB_NODE")] + #[arg( + long, + default_value = "https://node.twigpine.com", + env = "GITLAWB_NODE" + )] node: String, /// Identity directory @@ -70,7 +74,11 @@ pub enum ProfileCmd { /// Show your own profile Show { /// Node URL - #[arg(long, default_value = "https://node.gitlawb.com", env = "GITLAWB_NODE")] + #[arg( + long, + default_value = "https://node.twigpine.com", + env = "GITLAWB_NODE" + )] node: String, /// Identity directory @@ -84,7 +92,11 @@ pub enum ProfileCmd { did: String, /// Node URL - #[arg(long, default_value = "https://node.gitlawb.com", env = "GITLAWB_NODE")] + #[arg( + long, + default_value = "https://node.twigpine.com", + env = "GITLAWB_NODE" + )] node: String, }, @@ -98,7 +110,11 @@ pub enum ProfileCmd { pin: bool, /// Node URL - #[arg(long, default_value = "https://node.gitlawb.com", env = "GITLAWB_NODE")] + #[arg( + long, + default_value = "https://node.twigpine.com", + env = "GITLAWB_NODE" + )] node: String, /// Identity directory @@ -109,7 +125,11 @@ pub enum ProfileCmd { /// Export your profile as JSON Export { /// Node URL - #[arg(long, default_value = "https://node.gitlawb.com", env = "GITLAWB_NODE")] + #[arg( + long, + default_value = "https://node.twigpine.com", + env = "GITLAWB_NODE" + )] node: String, /// Identity directory diff --git a/crates/gl/src/protect.rs b/crates/gl/src/protect.rs index a45a5ebf0..4441cf00e 100644 --- a/crates/gl/src/protect.rs +++ b/crates/gl/src/protect.rs @@ -23,7 +23,11 @@ pub enum ProtectCmd { /// Repository name or owner/repo #[arg(long)] repo: String, - #[arg(long, default_value = "https://node.gitlawb.com", env = "GITLAWB_NODE")] + #[arg( + long, + default_value = "https://node.twigpine.com", + env = "GITLAWB_NODE" + )] node: String, #[arg(long)] dir: Option, @@ -34,7 +38,11 @@ pub enum ProtectCmd { branch: String, #[arg(long)] repo: String, - #[arg(long, default_value = "https://node.gitlawb.com", env = "GITLAWB_NODE")] + #[arg( + long, + default_value = "https://node.twigpine.com", + env = "GITLAWB_NODE" + )] node: String, #[arg(long)] dir: Option, @@ -43,7 +51,11 @@ pub enum ProtectCmd { List { #[arg(long)] repo: String, - #[arg(long, default_value = "https://node.gitlawb.com", env = "GITLAWB_NODE")] + #[arg( + long, + default_value = "https://node.twigpine.com", + env = "GITLAWB_NODE" + )] node: String, #[arg(long)] dir: Option, diff --git a/crates/gl/src/quickstart.rs b/crates/gl/src/quickstart.rs index 8b901ad90..7fbf7931b 100644 --- a/crates/gl/src/quickstart.rs +++ b/crates/gl/src/quickstart.rs @@ -15,7 +15,7 @@ use std::path::PathBuf; use crate::http::NodeClient; use crate::identity::load_keypair_from_dir; -const PUBLIC_NODE: &str = "https://node.gitlawb.com"; +const PUBLIC_NODE: &str = "https://node.twigpine.com"; #[derive(Args)] pub struct QuickstartArgs { diff --git a/crates/gl/src/register.rs b/crates/gl/src/register.rs index 8a17a77ef..0ad3701cf 100644 --- a/crates/gl/src/register.rs +++ b/crates/gl/src/register.rs @@ -1,4 +1,4 @@ -//! `gl register` — register this agent identity with a gitlawb node. +//! `gl register` — register this agent identity with a Twigpine node. //! //! Sends a signed POST /api/register request and saves the returned bootstrap //! UCAN token to `~/.gitlawb/ucan.json` for use by other commands. @@ -14,7 +14,11 @@ use crate::identity::load_keypair_from_dir; #[derive(Args)] pub struct RegisterArgs { /// Node URL to register with - #[arg(long, default_value = "https://node.gitlawb.com", env = "GITLAWB_NODE")] + #[arg( + long, + default_value = "https://node.twigpine.com", + env = "GITLAWB_NODE" + )] pub node: String, /// Capabilities to advertise (comma-separated) @@ -100,7 +104,7 @@ pub async fn run(args: RegisterArgs) -> Result<()> { println!(" UCAN expires: {expires}"); println!(); println!(" Bootstrap UCAN saved to ~/.gitlawb/ucan.json"); - println!(" You are now a verified agent on the gitlawb network."); + println!(" You are now a verified agent on the Twigpine network."); Ok(()) } diff --git a/crates/gl/src/repo.rs b/crates/gl/src/repo.rs index c75a7667c..75756d3c4 100644 --- a/crates/gl/src/repo.rs +++ b/crates/gl/src/repo.rs @@ -25,14 +25,22 @@ pub enum RepoCmd { private: bool, #[arg(long, default_value = "main")] branch: String, - #[arg(long, default_value = "https://node.gitlawb.com", env = "GITLAWB_NODE")] + #[arg( + long, + default_value = "https://node.twigpine.com", + env = "GITLAWB_NODE" + )] node: String, #[arg(long)] dir: Option, }, /// List repositories on a node List { - #[arg(long, default_value = "https://node.gitlawb.com", env = "GITLAWB_NODE")] + #[arg( + long, + default_value = "https://node.twigpine.com", + env = "GITLAWB_NODE" + )] node: String, #[arg(long)] dir: Option, @@ -40,7 +48,11 @@ pub enum RepoCmd { /// Print the gitlawb:// clone command for a repo Clone { name: String, - #[arg(long, default_value = "https://node.gitlawb.com", env = "GITLAWB_NODE")] + #[arg( + long, + default_value = "https://node.twigpine.com", + env = "GITLAWB_NODE" + )] node: String, #[arg(long)] dir: Option, @@ -48,7 +60,11 @@ pub enum RepoCmd { /// Show repository metadata Info { repo: String, - #[arg(long, default_value = "https://node.gitlawb.com", env = "GITLAWB_NODE")] + #[arg( + long, + default_value = "https://node.twigpine.com", + env = "GITLAWB_NODE" + )] node: String, #[arg(long)] dir: Option, @@ -63,7 +79,11 @@ pub enum RepoCmd { /// Max number of commits to show #[arg(long, short, default_value = "20")] limit: u32, - #[arg(long, default_value = "https://node.gitlawb.com", env = "GITLAWB_NODE")] + #[arg( + long, + default_value = "https://node.twigpine.com", + env = "GITLAWB_NODE" + )] node: String, #[arg(long)] dir: Option, @@ -75,7 +95,11 @@ pub enum RepoCmd { /// New name for the fork (defaults to source repo name) #[arg(long, short)] name: Option, - #[arg(long, default_value = "https://node.gitlawb.com", env = "GITLAWB_NODE")] + #[arg( + long, + default_value = "https://node.twigpine.com", + env = "GITLAWB_NODE" + )] node: String, #[arg(long)] dir: Option, @@ -84,7 +108,11 @@ pub enum RepoCmd { LabelAdd { repo: String, label: String, - #[arg(long, default_value = "https://node.gitlawb.com", env = "GITLAWB_NODE")] + #[arg( + long, + default_value = "https://node.twigpine.com", + env = "GITLAWB_NODE" + )] node: String, #[arg(long)] dir: Option, @@ -93,7 +121,11 @@ pub enum RepoCmd { LabelRemove { repo: String, label: String, - #[arg(long, default_value = "https://node.gitlawb.com", env = "GITLAWB_NODE")] + #[arg( + long, + default_value = "https://node.twigpine.com", + env = "GITLAWB_NODE" + )] node: String, #[arg(long)] dir: Option, @@ -101,7 +133,11 @@ pub enum RepoCmd { /// List labels on a repository LabelList { repo: String, - #[arg(long, default_value = "https://node.gitlawb.com", env = "GITLAWB_NODE")] + #[arg( + long, + default_value = "https://node.twigpine.com", + env = "GITLAWB_NODE" + )] node: String, #[arg(long)] dir: Option, @@ -110,7 +146,11 @@ pub enum RepoCmd { Owner { /// Repository name or owner/repo repo: String, - #[arg(long, default_value = "https://node.gitlawb.com", env = "GITLAWB_NODE")] + #[arg( + long, + default_value = "https://node.twigpine.com", + env = "GITLAWB_NODE" + )] node: String, #[arg(long)] dir: Option, @@ -126,7 +166,11 @@ pub enum RepoCmd { #[arg(long)] url: String, /// URL of the origin node (where the repo lives) - #[arg(long, default_value = "https://node.gitlawb.com", env = "GITLAWB_NODE")] + #[arg( + long, + default_value = "https://node.twigpine.com", + env = "GITLAWB_NODE" + )] node: String, #[arg(long)] dir: Option, @@ -135,7 +179,11 @@ pub enum RepoCmd { ReplicaUnregister { /// Repository in owner/repo format repo: String, - #[arg(long, default_value = "https://node.gitlawb.com", env = "GITLAWB_NODE")] + #[arg( + long, + default_value = "https://node.twigpine.com", + env = "GITLAWB_NODE" + )] node: String, #[arg(long)] dir: Option, @@ -144,7 +192,11 @@ pub enum RepoCmd { Replicas { /// Repository in owner/repo format repo: String, - #[arg(long, default_value = "https://node.gitlawb.com", env = "GITLAWB_NODE")] + #[arg( + long, + default_value = "https://node.twigpine.com", + env = "GITLAWB_NODE" + )] node: String, #[arg(long)] dir: Option, @@ -265,7 +317,7 @@ async fn cmd_create( println!("✓ Created repository: {name}"); println!(" Clone: git clone {gitlawb_url}"); println!(" HTTP: {clone_url}"); - println!(" View: https://gitlawb.com/{owner_short}/{name}"); + println!(" View: https://twigpine.com/{owner_short}/{name}"); if let Some(desc) = payload["description"].as_str().filter(|s| !s.is_empty()) { println!(" Desc: {desc}"); } diff --git a/crates/gl/src/star.rs b/crates/gl/src/star.rs index 120f824a8..e67d909d3 100644 --- a/crates/gl/src/star.rs +++ b/crates/gl/src/star.rs @@ -20,7 +20,11 @@ pub enum StarCmd { Add { /// Repository name (owner/repo or just name — owner derived from identity) repo: String, - #[arg(long, default_value = "https://node.gitlawb.com", env = "GITLAWB_NODE")] + #[arg( + long, + default_value = "https://node.twigpine.com", + env = "GITLAWB_NODE" + )] node: String, #[arg(long)] dir: Option, @@ -29,7 +33,11 @@ pub enum StarCmd { Remove { /// Repository name (owner/repo or just name — owner derived from identity) repo: String, - #[arg(long, default_value = "https://node.gitlawb.com", env = "GITLAWB_NODE")] + #[arg( + long, + default_value = "https://node.twigpine.com", + env = "GITLAWB_NODE" + )] node: String, #[arg(long)] dir: Option, @@ -38,7 +46,11 @@ pub enum StarCmd { Count { /// Repository in owner/repo format repo: String, - #[arg(long, default_value = "https://node.gitlawb.com", env = "GITLAWB_NODE")] + #[arg( + long, + default_value = "https://node.twigpine.com", + env = "GITLAWB_NODE" + )] node: String, #[arg(long)] dir: Option, diff --git a/crates/gl/src/status.rs b/crates/gl/src/status.rs index cf477cc6b..b7753f491 100644 --- a/crates/gl/src/status.rs +++ b/crates/gl/src/status.rs @@ -10,7 +10,11 @@ use crate::identity::load_keypair_from_dir; #[derive(Args)] pub struct StatusArgs { - #[arg(long, default_value = "https://node.gitlawb.com", env = "GITLAWB_NODE")] + #[arg( + long, + default_value = "https://node.twigpine.com", + env = "GITLAWB_NODE" + )] pub node: String, #[arg(long)] pub dir: Option, @@ -51,7 +55,7 @@ pub async fn run(args: StatusArgs) -> Result<()> { } } - // ── Current git repo + gitlawb remote ───────────────────────────────── + // ── Current git repo + Twigpine remote ───────────────────────────────── let remote = detect_gitlawb_remote(); match &remote { Some((did, repo)) => { @@ -59,7 +63,7 @@ pub async fn run(args: StatusArgs) -> Result<()> { println!(" repo {short_did}/{repo}"); } None => { - println!(" repo (not in a gitlawb repo — no gitlawb:// remote)"); + println!(" repo (not in a Twigpine repo — no gitlawb:// remote)"); } } diff --git a/crates/gl/src/sync.rs b/crates/gl/src/sync.rs index c4e830c3d..61d5fbe53 100644 --- a/crates/gl/src/sync.rs +++ b/crates/gl/src/sync.rs @@ -11,7 +11,11 @@ pub struct SyncArgs { pub cmd: SyncCmd, /// Node URL - #[arg(long, env = "GITLAWB_NODE", default_value = "https://node.gitlawb.com")] + #[arg( + long, + env = "GITLAWB_NODE", + default_value = "https://node.twigpine.com" + )] pub node: String, /// Identity directory for signed sync trigger requests diff --git a/crates/gl/src/task.rs b/crates/gl/src/task.rs index c26cb35f1..53b66b2a4 100644 --- a/crates/gl/src/task.rs +++ b/crates/gl/src/task.rs @@ -38,7 +38,11 @@ pub enum TaskCmd { /// ISO-8601 deadline for the task #[arg(long)] deadline: Option, - #[arg(long, default_value = "https://node.gitlawb.com", env = "GITLAWB_NODE")] + #[arg( + long, + default_value = "https://node.twigpine.com", + env = "GITLAWB_NODE" + )] node: String, #[arg(long)] dir: Option, @@ -51,19 +55,31 @@ pub enum TaskCmd { assignee_did: Option, #[arg(long, default_value = "50")] limit: i64, - #[arg(long, default_value = "https://node.gitlawb.com", env = "GITLAWB_NODE")] + #[arg( + long, + default_value = "https://node.twigpine.com", + env = "GITLAWB_NODE" + )] node: String, }, /// View a specific task View { id: String, - #[arg(long, default_value = "https://node.gitlawb.com", env = "GITLAWB_NODE")] + #[arg( + long, + default_value = "https://node.twigpine.com", + env = "GITLAWB_NODE" + )] node: String, }, /// Claim a pending task Claim { id: String, - #[arg(long, default_value = "https://node.gitlawb.com", env = "GITLAWB_NODE")] + #[arg( + long, + default_value = "https://node.twigpine.com", + env = "GITLAWB_NODE" + )] node: String, #[arg(long)] dir: Option, @@ -73,7 +89,11 @@ pub enum TaskCmd { id: String, #[arg(long)] result: Option, - #[arg(long, default_value = "https://node.gitlawb.com", env = "GITLAWB_NODE")] + #[arg( + long, + default_value = "https://node.twigpine.com", + env = "GITLAWB_NODE" + )] node: String, #[arg(long)] dir: Option, @@ -83,7 +103,11 @@ pub enum TaskCmd { id: String, #[arg(long)] reason: Option, - #[arg(long, default_value = "https://node.gitlawb.com", env = "GITLAWB_NODE")] + #[arg( + long, + default_value = "https://node.twigpine.com", + env = "GITLAWB_NODE" + )] node: String, #[arg(long)] dir: Option, diff --git a/crates/gl/src/visibility.rs b/crates/gl/src/visibility.rs index eef9ecb4b..3987a52c0 100644 --- a/crates/gl/src/visibility.rs +++ b/crates/gl/src/visibility.rs @@ -28,7 +28,11 @@ pub enum VisibilityCmd { /// Replication mode: "a" (hide, whole-repo only) or "b" (lock content) #[arg(long, default_value = "b")] mode: String, - #[arg(long, default_value = "https://node.gitlawb.com", env = "GITLAWB_NODE")] + #[arg( + long, + default_value = "https://node.twigpine.com", + env = "GITLAWB_NODE" + )] node: String, #[arg(long)] dir: Option, @@ -38,7 +42,11 @@ pub enum VisibilityCmd { path_glob: String, #[arg(long)] repo: String, - #[arg(long, default_value = "https://node.gitlawb.com", env = "GITLAWB_NODE")] + #[arg( + long, + default_value = "https://node.twigpine.com", + env = "GITLAWB_NODE" + )] node: String, #[arg(long)] dir: Option, @@ -47,7 +55,11 @@ pub enum VisibilityCmd { List { #[arg(long)] repo: String, - #[arg(long, default_value = "https://node.gitlawb.com", env = "GITLAWB_NODE")] + #[arg( + long, + default_value = "https://node.twigpine.com", + env = "GITLAWB_NODE" + )] node: String, #[arg(long)] dir: Option, diff --git a/crates/gl/src/webhook.rs b/crates/gl/src/webhook.rs index a7311a84b..69148e623 100644 --- a/crates/gl/src/webhook.rs +++ b/crates/gl/src/webhook.rs @@ -31,7 +31,11 @@ pub enum WebhookCmd { /// Optional HMAC secret for payload signing (sets X-Gitlawb-Signature-256) #[arg(long)] secret: Option, - #[arg(long, default_value = "https://node.gitlawb.com", env = "GITLAWB_NODE")] + #[arg( + long, + default_value = "https://node.twigpine.com", + env = "GITLAWB_NODE" + )] node: String, #[arg(long)] dir: Option, @@ -40,7 +44,11 @@ pub enum WebhookCmd { List { /// Repository name repo: String, - #[arg(long, default_value = "https://node.gitlawb.com", env = "GITLAWB_NODE")] + #[arg( + long, + default_value = "https://node.twigpine.com", + env = "GITLAWB_NODE" + )] node: String, #[arg(long)] dir: Option, @@ -51,7 +59,11 @@ pub enum WebhookCmd { repo: String, /// Webhook ID id: String, - #[arg(long, default_value = "https://node.gitlawb.com", env = "GITLAWB_NODE")] + #[arg( + long, + default_value = "https://node.twigpine.com", + env = "GITLAWB_NODE" + )] node: String, #[arg(long)] dir: Option, diff --git a/docker-compose.yml b/docker-compose.yml index b6466afe0..9cd5064be 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,4 +1,4 @@ -# Run a gitlawb node + Postgres locally. For on-chain PoS operation, supply +# Run a Twigpine node + Postgres locally. For on-chain PoS operation, supply # the operator key + contract addresses via a .env file or shell environment. services: diff --git a/docs/ECONOMICS.md b/docs/ECONOMICS.md index cf218f78e..4c7aded91 100644 --- a/docs/ECONOMICS.md +++ b/docs/ECONOMICS.md @@ -1,4 +1,4 @@ -# gitlawb economics +# Twigpine economics How $GITLAWB flows through the protocol and how stakers earn. diff --git a/docs/MAINTAINER-ROADMAP.md b/docs/MAINTAINER-ROADMAP.md index 0fb2eb776..c61ada602 100644 --- a/docs/MAINTAINER-ROADMAP.md +++ b/docs/MAINTAINER-ROADMAP.md @@ -1,6 +1,6 @@ # Maintainer roadmap -Gitlawb node is live software. People are already using the network, so maintainer work should optimize for uptime, compatibility, and predictable rollouts before feature velocity. +Twigpine node is live software. People are already using the network, so maintainer work should optimize for uptime, compatibility, and predictable rollouts before feature velocity. Mission: when code is pushed to a node, the node should not go down, and the network should be able to serve git content like a resilient delivery layer. diff --git a/docs/OSS-READINESS-AUDIT.md b/docs/OSS-READINESS-AUDIT.md index 10ee2c3ec..baa029dbf 100644 --- a/docs/OSS-READINESS-AUDIT.md +++ b/docs/OSS-READINESS-AUDIT.md @@ -53,7 +53,7 @@ Recommended next CI additions: Fixed in this pass: -- `install.sh` now downloads from `Gitlawb/node`, matches release asset names (`gitlawb-node--.tar.gz`), handles `--version vX.Y.Z`, extracts the packaged directory, verifies checksums, and installs `gl`, `git-remote-gitlawb`, and `gitlawb-node` when present. +- `install.sh` now downloads from `Twigpine/node`, matches release asset names (`gitlawb-node--.tar.gz`), handles `--version vX.Y.Z`, extracts the packaged directory, verifies checksums, and installs `gl`, `git-remote-gitlawb`, and `gitlawb-node` when present. - `docs/RUN-A-NODE.md` now matches the README Rust requirement (`1.91+`) and the release workflow's GHCR image path (`ghcr.io/gitlawb/node:latest`). - `docs/ECONOMICS.md` no longer claims this repo ships a `keeper-distribute.yml` workflow that is not present. - `.env.example` now distinguishes HTTP bootstrap peers from libp2p multiaddrs and documents seed-list opt-out. @@ -62,8 +62,8 @@ Fixed in this pass: Remaining doc caveats: -- Public docs URLs (`gitlawb.com/install.sh`, `docs.gitlawb.com`) were not verified in this local pass. -- `git-remote-gitlawb` defaults to `http://127.0.0.1:7545`, while most `gl` commands default to `https://node.gitlawb.com`; the docs should be explicit about setting `GITLAWB_NODE` for public-network cloning/fetching. +- Public docs URLs (`twigpine.com/install.sh`, `docs.gitlawb.com`) were not verified in this local pass. +- `git-remote-gitlawb` defaults to `http://127.0.0.1:7545`, while most `gl` commands default to `https://node.twigpine.com`; the docs should be explicit about setting `GITLAWB_NODE` for public-network cloning/fetching. ## Docker and self-hosting readiness diff --git a/docs/RUN-A-NODE.md b/docs/RUN-A-NODE.md index 7d2e2c83c..cb5555a7a 100644 --- a/docs/RUN-A-NODE.md +++ b/docs/RUN-A-NODE.md @@ -1,4 +1,4 @@ -# Running a gitlawb node +# Running a Twigpine node Step-by-step guide to staking $GITLAWB, registering your node on-chain, and earning protocol fees as a PoS operator. @@ -15,12 +15,12 @@ Step-by-step guide to staking $GITLAWB, registering your node on-chain, and earn ## 1. Install the CLI ```bash -curl -fsSL https://gitlawb.com/install.sh | sh +curl -fsSL https://twigpine.com/install.sh | sh # or build from source: cargo install --path crates/gl ``` -## 2. Create a gitlawb identity +## 2. Create a Twigpine identity Every node is identified by an Ed25519 DID keypair: diff --git a/infra/aws/README.md b/infra/aws/README.md index d8d34844a..34fe38e62 100644 --- a/infra/aws/README.md +++ b/infra/aws/README.md @@ -1,4 +1,4 @@ -# gitlawb node on AWS (Terraform) +# Twigpine node on AWS (Terraform) Single EC2 instance running the published node image + Postgres via Docker compose, with a persistent encrypted EBS volume, Elastic IP, SSM access, and diff --git a/infra/aws/main.tf b/infra/aws/main.tf index 83d602686..0ceab3ede 100644 --- a/infra/aws/main.tf +++ b/infra/aws/main.tf @@ -1,4 +1,4 @@ -# Single-EC2 gitlawb node: Docker compose (node + postgres) on Amazon Linux 2023, +# Single-EC2 Twigpine node: Docker compose (node + postgres) on Amazon Linux 2023, # persistent EBS data volume, Elastic IP, SSM access, daily snapshots. # --------------------------------------------------------------------------- @@ -165,7 +165,7 @@ resource "aws_iam_instance_profile" "node" { resource "aws_security_group" "node" { name = "${var.name_prefix}-sg" - description = "gitlawb node: HTTP API + libp2p UDP" + description = "Twigpine node: HTTP API + libp2p UDP" vpc_id = data.aws_subnet.selected.vpc_id # follows subnet_id overrides into non-default VPCs tags = local.common_tags @@ -399,7 +399,7 @@ resource "aws_iam_role_policy_attachment" "dlm" { } resource "aws_dlm_lifecycle_policy" "data" { - description = "Daily snapshots of the gitlawb data volume" + description = "Daily snapshots of the Twigpine data volume" execution_role_arn = aws_iam_role.dlm.arn state = "ENABLED" tags = local.common_tags diff --git a/infra/aws/variables.tf b/infra/aws/variables.tf index c7f2057f4..c7666f52e 100644 --- a/infra/aws/variables.tf +++ b/infra/aws/variables.tf @@ -86,7 +86,7 @@ variable "image_tag" { # --------------------------------------------------------------------------- variable "icaptcha_mode" { - description = "iCaptcha gate mode: off | shadow | enforce. The live gitlawb network enforces." + description = "iCaptcha gate mode: off | shadow | enforce. The live Twigpine network enforces." type = string default = "enforce" } diff --git a/install.ps1 b/install.ps1 index 740586879..c50aeb155 100644 --- a/install.ps1 +++ b/install.ps1 @@ -1,10 +1,10 @@ <# .SYNOPSIS - gitlawb installer for Windows - downloads pre-built binaries from GitHub Releases. + Twigpine installer for Windows - downloads pre-built binaries from GitHub Releases. .EXAMPLE - irm https://gitlawb.com/install.ps1 | iex + irm https://twigpine.com/install.ps1 | iex .EXAMPLE - & ([scriptblock]::Create((irm https://gitlawb.com/install.ps1))) -Version v0.3.9 + & ([scriptblock]::Create((irm https://twigpine.com/install.ps1))) -Version v0.3.9 #> [CmdletBinding()] param( @@ -16,7 +16,7 @@ $ErrorActionPreference = "Stop" # Windows PowerShell 5.1 may default to TLS 1.0; GitHub requires TLS 1.2+. [Net.ServicePointManager]::SecurityProtocol = [Net.ServicePointManager]::SecurityProtocol -bor [Net.SecurityProtocolType]::Tls12 -$Repo = if ($env:GITLAWB_RELEASE_REPO) { $env:GITLAWB_RELEASE_REPO } else { "Gitlawb/node" } +$Repo = if ($env:GITLAWB_RELEASE_REPO) { $env:GITLAWB_RELEASE_REPO } else { "Twigpine/node" } $InstallDir = if ($env:GITLAWB_INSTALL_DIR) { $env:GITLAWB_INSTALL_DIR } else { "$env:LOCALAPPDATA\Programs\gitlawb" } $arch = if ($env:PROCESSOR_ARCHITEW6432) { @@ -43,7 +43,7 @@ $ver = $tag.TrimStart("v") $archive = "gitlawb-node-$ver-$target.zip" $url = "https://github.com/$Repo/releases/download/$tag/$archive" -Write-Host "Installing gitlawb $tag for windows/x64" +Write-Host "Installing Twigpine $tag for windows/x64" Write-Host " Archive: $archive" Write-Host " Into: $InstallDir" @@ -100,7 +100,7 @@ try { } Write-Host "" - Write-Host "Installed gitlawb $tag" + Write-Host "Installed Twigpine $tag" foreach ($bin in $installed) { Write-Host " $bin -> $InstallDir\$bin" } } finally { diff --git a/install.sh b/install.sh index 416e7c9f3..ae2f0463a 100644 --- a/install.sh +++ b/install.sh @@ -1,18 +1,18 @@ #!/usr/bin/env bash -# gitlawb installer - downloads pre-built binaries from GitHub Releases. +# Twigpine installer - downloads pre-built binaries from GitHub Releases. # # Usage: -# curl -fsSL https://gitlawb.com/install.sh | sh -# curl -fsSL https://gitlawb.com/install.sh | sh -s -- --version v0.3.9 +# curl -fsSL https://twigpine.com/install.sh | sh +# curl -fsSL https://twigpine.com/install.sh | sh -s -- --version v0.3.9 set -euo pipefail -REPO="${GITLAWB_RELEASE_REPO:-Gitlawb/node}" +REPO="${GITLAWB_RELEASE_REPO:-Twigpine/node}" INSTALL_DIR="${GITLAWB_INSTALL_DIR:-$HOME/.local/bin}" VERSION_ARG="latest" usage() { cat < Install a specific release tag, for example v0.3.9. @@ -20,7 +20,7 @@ Options: Environment: GITLAWB_INSTALL_DIR Install directory (default: $HOME/.local/bin) - GITLAWB_RELEASE_REPO GitHub repo to download from (default: Gitlawb/node) + GITLAWB_RELEASE_REPO GitHub repo to download from (default: Twigpine/node) EOF } @@ -97,7 +97,7 @@ ARCHIVE="${PACKAGE_NAME}.tar.gz" DOWNLOAD_URL="https://github.com/${REPO}/releases/download/${TAG}/${ARCHIVE}" CHECKSUM_URL="${DOWNLOAD_URL}.sha256" -echo "Installing gitlawb ${TAG} for ${OS_NAME}/${ARCH_NAME}" +echo "Installing Twigpine ${TAG} for ${OS_NAME}/${ARCH_NAME}" echo " Archive: ${ARCHIVE}" echo " Into: ${INSTALL_DIR}" echo "" @@ -161,7 +161,7 @@ if [ "${#INSTALLED[@]}" -eq 0 ]; then fi echo "" -echo "Installed gitlawb ${TAG}" +echo "Installed Twigpine ${TAG}" for BIN in "${INSTALLED[@]}"; do echo " ${BIN} -> ${INSTALL_DIR}/${BIN}" done diff --git a/macos-app/Sources/GitlawbNode/Config.swift b/macos-app/Sources/GitlawbNode/Config.swift index 2e975e549..406999ba1 100644 --- a/macos-app/Sources/GitlawbNode/Config.swift +++ b/macos-app/Sources/GitlawbNode/Config.swift @@ -122,7 +122,7 @@ class Config { let envPath = dataDirectory.appendingPathComponent(".env") var lines = [String]() - lines.append("# Generated by Gitlawb Node app — do not edit manually") + lines.append("# Generated by Twigpine Node app — do not edit manually") lines.append("POSTGRES_PASSWORD=\(postgresPassword)") lines.append("GITLAWB_PUBLIC_URL=\(publicURL)") lines.append("GITLAWB_PORT=\(httpPort)") diff --git a/macos-app/Sources/GitlawbNode/DockerCompose.swift b/macos-app/Sources/GitlawbNode/DockerCompose.swift index 7a3bdc9d6..067a73355 100644 --- a/macos-app/Sources/GitlawbNode/DockerCompose.swift +++ b/macos-app/Sources/GitlawbNode/DockerCompose.swift @@ -19,7 +19,7 @@ enum NodeStatus: Equatable { } } -/// Manages the Docker Compose lifecycle for the gitlawb node stack. +/// Manages the Docker Compose lifecycle for the Twigpine node stack. class DockerCompose { /// Current status, updated by polling. private(set) var status: NodeStatus = .stopped { @@ -262,7 +262,7 @@ class DockerCompose { private static func generateComposeFile() -> String { return """ -# Generated by Gitlawb Node macOS app +# Generated by Twigpine Node macOS app services: postgres: image: postgres:16-alpine diff --git a/macos-app/Sources/GitlawbNode/Info.plist b/macos-app/Sources/GitlawbNode/Info.plist index a85b66e15..30b2b063f 100644 --- a/macos-app/Sources/GitlawbNode/Info.plist +++ b/macos-app/Sources/GitlawbNode/Info.plist @@ -3,9 +3,9 @@ CFBundleName - Gitlawb Node + Twigpine Node CFBundleDisplayName - Gitlawb Node + Twigpine Node CFBundleIdentifier com.gitlawb.node-app CFBundleVersion diff --git a/macos-app/Sources/GitlawbNode/Resources/docker-compose.yml b/macos-app/Sources/GitlawbNode/Resources/docker-compose.yml index 9cd27babe..5aff116e4 100644 --- a/macos-app/Sources/GitlawbNode/Resources/docker-compose.yml +++ b/macos-app/Sources/GitlawbNode/Resources/docker-compose.yml @@ -1,4 +1,4 @@ -# Run a gitlawb node + Postgres locally. For on-chain PoS operation, supply +# Run a Twigpine node + Postgres locally. For on-chain PoS operation, supply # the operator key + contract addresses via a .env file or shell environment. services: diff --git a/macos-app/Sources/GitlawbNode/SettingsWindow.swift b/macos-app/Sources/GitlawbNode/SettingsWindow.swift index 26ffc365b..140634084 100644 --- a/macos-app/Sources/GitlawbNode/SettingsWindow.swift +++ b/macos-app/Sources/GitlawbNode/SettingsWindow.swift @@ -6,7 +6,7 @@ class SettingsWindowController: NSWindowController { convenience init() { let hostingController = NSHostingController(rootView: SettingsView()) let window = NSWindow(contentViewController: hostingController) - window.title = "Gitlawb Node Settings" + window.title = "Twigpine Node Settings" window.setContentSize(NSSize(width: 480, height: 280)) window.styleMask = [.titled, .closable, .miniaturizable] window.center() diff --git a/macos-app/Sources/GitlawbNode/StatusBarController.swift b/macos-app/Sources/GitlawbNode/StatusBarController.swift index 0bc2351cd..ad97fa214 100644 --- a/macos-app/Sources/GitlawbNode/StatusBarController.swift +++ b/macos-app/Sources/GitlawbNode/StatusBarController.swift @@ -67,7 +67,7 @@ class StatusBarController: NSObject { menu.addItem(.separator()) - let quitItem = NSMenuItem(title: "Quit Gitlawb Node", action: #selector(quit), keyEquivalent: "q") + let quitItem = NSMenuItem(title: "Quit Twigpine Node", action: #selector(quit), keyEquivalent: "q") quitItem.target = self menu.addItem(quitItem) } @@ -102,7 +102,7 @@ class StatusBarController: NSObject { iconImage = img } else { // Last resort fallback to SF Symbol - iconImage = NSImage(systemSymbolName: "network", accessibilityDescription: "Gitlawb Node") ?? NSImage() + iconImage = NSImage(systemSymbolName: "network", accessibilityDescription: "Twigpine Node") ?? NSImage() } } @@ -181,7 +181,7 @@ class StatusBarController: NSObject { backing: .buffered, defer: false ) - panel.title = "Gitlawb Node Logs" + panel.title = "Twigpine Node Logs" panel.isReleasedWhenClosed = false panel.center() self.logsWindow = panel @@ -235,7 +235,7 @@ class StatusBarController: NSObject { let alert = NSAlert() alert.messageText = "Docker Not Found" alert.informativeText = """ - Gitlawb Node requires a Docker runtime to run. + Twigpine Node requires a Docker runtime to run. Install one of the following: • Docker Desktop (docker.com) diff --git a/npm/README.md b/npm/README.md index c8c9c785e..32217aac4 100644 --- a/npm/README.md +++ b/npm/README.md @@ -1,4 +1,4 @@ -# npm distribution for the gitlawb CLI +# npm distribution for the Twigpine CLI This directory packages the `gl` and `git-remote-gitlawb` binaries as the [`@gitlawb/gl`](https://www.npmjs.com/package/@gitlawb/gl) npm package. @@ -23,7 +23,7 @@ packages/ Publishing is **automated** by `.github/workflows/release.yml` (the `npm-publish` job) on every release-please release. That job: -1. Downloads the four unix release tarballs from the `Gitlawb/node` GitHub release. +1. Downloads the four unix release tarballs from the `Twigpine/node` GitHub release. 2. Lays `gl` + `git-remote-gitlawb` into each platform package. 3. Rewrites every `package.json` `version` (and the wrapper's `optionalDependencies` pins) to the release version. diff --git a/npm/packages/gl-darwin-arm64/package.json b/npm/packages/gl-darwin-arm64/package.json index 3138669af..92ee55167 100644 --- a/npm/packages/gl-darwin-arm64/package.json +++ b/npm/packages/gl-darwin-arm64/package.json @@ -1,7 +1,7 @@ { "name": "@gitlawb/gl-darwin-arm64", "version": "0.3.9", - "description": "Gitlawb CLI binary for macOS Apple Silicon (arm64)", + "description": "Twigpine CLI binary for macOS Apple Silicon (arm64)", "license": "MIT", "os": ["darwin"], "cpu": ["arm64"], @@ -11,7 +11,7 @@ }, "repository": { "type": "git", - "url": "https://github.com/Gitlawb/node", + "url": "https://github.com/Twigpine/node", "directory": "npm/packages/gl-darwin-arm64" }, "files": [ diff --git a/npm/packages/gl-darwin-x64/package.json b/npm/packages/gl-darwin-x64/package.json index 39a86c2b9..339b27866 100644 --- a/npm/packages/gl-darwin-x64/package.json +++ b/npm/packages/gl-darwin-x64/package.json @@ -1,7 +1,7 @@ { "name": "@gitlawb/gl-darwin-x64", "version": "0.3.9", - "description": "Gitlawb CLI binary for macOS Intel (x64)", + "description": "Twigpine CLI binary for macOS Intel (x64)", "license": "MIT", "os": ["darwin"], "cpu": ["x64"], @@ -11,7 +11,7 @@ }, "repository": { "type": "git", - "url": "https://github.com/Gitlawb/node", + "url": "https://github.com/Twigpine/node", "directory": "npm/packages/gl-darwin-x64" }, "files": [ diff --git a/npm/packages/gl-linux-arm64/package.json b/npm/packages/gl-linux-arm64/package.json index 8844718d6..9a90568fd 100644 --- a/npm/packages/gl-linux-arm64/package.json +++ b/npm/packages/gl-linux-arm64/package.json @@ -1,7 +1,7 @@ { "name": "@gitlawb/gl-linux-arm64", "version": "0.3.9", - "description": "Gitlawb CLI binary for Linux arm64", + "description": "Twigpine CLI binary for Linux arm64", "license": "MIT", "os": ["linux"], "cpu": ["arm64"], @@ -11,7 +11,7 @@ }, "repository": { "type": "git", - "url": "https://github.com/Gitlawb/node", + "url": "https://github.com/Twigpine/node", "directory": "npm/packages/gl-linux-arm64" }, "files": [ diff --git a/npm/packages/gl-linux-x64/package.json b/npm/packages/gl-linux-x64/package.json index cb565f01a..70ab82dee 100644 --- a/npm/packages/gl-linux-x64/package.json +++ b/npm/packages/gl-linux-x64/package.json @@ -1,7 +1,7 @@ { "name": "@gitlawb/gl-linux-x64", "version": "0.3.9", - "description": "Gitlawb CLI binary for Linux x64", + "description": "Twigpine CLI binary for Linux x64", "license": "MIT", "os": ["linux"], "cpu": ["x64"], @@ -11,7 +11,7 @@ }, "repository": { "type": "git", - "url": "https://github.com/Gitlawb/node", + "url": "https://github.com/Twigpine/node", "directory": "npm/packages/gl-linux-x64" }, "files": [ diff --git a/npm/packages/gl/README.md b/npm/packages/gl/README.md index 6d9a3f807..e834b8b4b 100644 --- a/npm/packages/gl/README.md +++ b/npm/packages/gl/README.md @@ -1,6 +1,6 @@ # @gitlawb/gl -The [gitlawb](https://gitlawb.com) CLI — decentralized git for AI agents and developers. +The [Twigpine](https://twigpine.com) CLI — decentralized git for AI agents and developers. ## Install @@ -27,10 +27,10 @@ Native Windows is not supported via npm — use the PowerShell installer below. brew install gitlawb/tap/gl # curl (macOS / Linux) -curl -sSf https://gitlawb.com/install.sh | sh +curl -sSf https://twigpine.com/install.sh | sh # PowerShell (Windows) -irm https://gitlawb.com/install.ps1 | iex +irm https://twigpine.com/install.ps1 | iex ``` ## Quick start @@ -39,7 +39,7 @@ irm https://gitlawb.com/install.ps1 | iex gl identity new gl register gl doctor -gl repo create my-project --description "My first gitlawb repo" +gl repo create my-project --description "My first Twigpine repo" git remote add gitlawb gitlawb://my-project git push gitlawb main ``` @@ -63,5 +63,5 @@ Windows is supported via the PowerShell installer or WSL. ## Links - Docs: https://docs.gitlawb.com -- Site: https://gitlawb.com -- Source: https://github.com/Gitlawb/node +- Site: https://twigpine.com +- Source: https://github.com/Twigpine/node diff --git a/npm/packages/gl/install.js b/npm/packages/gl/install.js index 2e2904dae..5c5429328 100644 --- a/npm/packages/gl/install.js +++ b/npm/packages/gl/install.js @@ -18,14 +18,14 @@ function getPlatformKey() { if (arch !== "arm64" && arch !== "x64") { throw new Error( - `Unsupported architecture: ${arch}. Gitlawb CLI supports x64 and arm64.` + `Unsupported architecture: ${arch}. Twigpine CLI supports x64 and arm64.` ); } if (platform !== "darwin" && platform !== "linux") { throw new Error( - `Unsupported platform: ${platform}. Gitlawb CLI supports macOS and Linux.\n` + - `For Windows, use WSL or the installer: https://gitlawb.com/install.ps1` + `Unsupported platform: ${platform}. Twigpine CLI supports macOS and Linux.\n` + + `For Windows, use WSL or the installer: https://twigpine.com/install.ps1` ); } @@ -58,7 +58,7 @@ function install() { console.error( `@gitlawb/gl: Platform package ${packageName} not found.\n` + `This can happen if optional dependencies were skipped.\n` + - `Install manually: curl -sSf https://gitlawb.com/install.sh | sh` + `Install manually: curl -sSf https://twigpine.com/install.sh | sh` ); process.exit(1); } @@ -78,7 +78,7 @@ function install() { chmodSync(dest, 0o755); } - console.log(`@gitlawb/gl: Installed gitlawb CLI for ${platformKey}`); + console.log(`@gitlawb/gl: Installed Twigpine CLI for ${platformKey}`); } try { diff --git a/npm/packages/gl/package.json b/npm/packages/gl/package.json index a5f7d372d..1b02a8271 100644 --- a/npm/packages/gl/package.json +++ b/npm/packages/gl/package.json @@ -1,7 +1,7 @@ { "name": "@gitlawb/gl", "version": "0.3.9", - "description": "Gitlawb CLI — decentralized git for AI agents and developers", + "description": "Twigpine CLI — decentralized git for AI agents and developers", "license": "MIT", "bin": { "gl": "bin/gl", @@ -16,7 +16,7 @@ }, "repository": { "type": "git", - "url": "https://github.com/Gitlawb/node", + "url": "https://github.com/Twigpine/node", "directory": "npm/packages/gl" }, "keywords": [ diff --git a/scripts/build-macos-app.sh b/scripts/build-macos-app.sh index 9ca343d37..afa0ec92f 100755 --- a/scripts/build-macos-app.sh +++ b/scripts/build-macos-app.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash set -euo pipefail -# Build the Gitlawb Node macOS menu bar app. +# Build the Twigpine Node macOS menu bar app. # Usage: ./scripts/build-macos-app.sh [--sign IDENTITY] SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" @@ -9,7 +9,7 @@ ROOT_DIR="$(dirname "$SCRIPT_DIR")" APP_DIR="$ROOT_DIR/macos-app" BUILD_DIR="$APP_DIR/.build/release" OUTPUT_DIR="$ROOT_DIR/dist" -APP_NAME="Gitlawb Node" +APP_NAME="Twigpine Node" APP_BUNDLE="$OUTPUT_DIR/${APP_NAME}.app" SIGN_IDENTITY="" @@ -27,7 +27,7 @@ while [[ $# -gt 0 ]]; do esac done -echo "==> Building Gitlawb Node macOS app..." +echo "==> Building Twigpine Node macOS app..." # Ensure docker-compose.yml resource is up to date mkdir -p "$APP_DIR/Sources/GitlawbNode/Resources" @@ -101,7 +101,7 @@ mkdir -p "$DMG_STAGING" cp -R "$APP_BUNDLE" "$DMG_STAGING/" ln -s /Applications "$DMG_STAGING/Applications" -hdiutil create -volname "Gitlawb Node" \ +hdiutil create -volname "Twigpine Node" \ -srcfolder "$DMG_STAGING" \ -ov -format UDZO \ "$DMG_PATH" From 421ae904be8790c049d4b737143252c0215a429b Mon Sep 17 00:00:00 2001 From: jatmn Date: Wed, 4 Nov 2026 09:19:07 -0800 Subject: [PATCH 2/7] docs: point image pulls at ghcr.io/twigpine/node and fix macOS DMG name The release workflow publishes the container image to ghcr.io/${GITHUB_REPOSITORY,,}, which is ghcr.io/twigpine/node since the org rename; the old ghcr.io/gitlawb/node path no longer resolves for anonymous pulls. Point the RUN-A-NODE docker run example, the OSS readiness audit's claim about it, and the macOS app's generated compose file at the published path. The README listed the macOS DMG as "Twigpine Node.dmg", but build-macos-app.sh writes GitlawbNode-macOS.dmg (kept on purpose), so document the real file name. Co-Authored-By: Claude Opus 5.5 --- README.md | 2 +- docs/OSS-READINESS-AUDIT.md | 2 +- docs/RUN-A-NODE.md | 2 +- macos-app/Sources/GitlawbNode/DockerCompose.swift | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index f0ba3b3fb..e638819b8 100644 --- a/README.md +++ b/README.md @@ -524,7 +524,7 @@ Output: ```txt dist/Twigpine Node.app -dist/Twigpine Node.dmg +dist/GitlawbNode-macOS.dmg ``` Features: diff --git a/docs/OSS-READINESS-AUDIT.md b/docs/OSS-READINESS-AUDIT.md index baa029dbf..044a440f2 100644 --- a/docs/OSS-READINESS-AUDIT.md +++ b/docs/OSS-READINESS-AUDIT.md @@ -54,7 +54,7 @@ Recommended next CI additions: Fixed in this pass: - `install.sh` now downloads from `Twigpine/node`, matches release asset names (`gitlawb-node--.tar.gz`), handles `--version vX.Y.Z`, extracts the packaged directory, verifies checksums, and installs `gl`, `git-remote-gitlawb`, and `gitlawb-node` when present. -- `docs/RUN-A-NODE.md` now matches the README Rust requirement (`1.91+`) and the release workflow's GHCR image path (`ghcr.io/gitlawb/node:latest`). +- `docs/RUN-A-NODE.md` now matches the README Rust requirement (`1.91+`) and the release workflow's GHCR image path (`ghcr.io/twigpine/node:latest`). - `docs/ECONOMICS.md` no longer claims this repo ships a `keeper-distribute.yml` workflow that is not present. - `.env.example` now distinguishes HTTP bootstrap peers from libp2p multiaddrs and documents seed-list opt-out. - `scripts/build-bins.sh` now writes to `dist/bin` instead of a missing `web/public/bin` path. diff --git a/docs/RUN-A-NODE.md b/docs/RUN-A-NODE.md index cb5555a7a..596c940c0 100644 --- a/docs/RUN-A-NODE.md +++ b/docs/RUN-A-NODE.md @@ -69,7 +69,7 @@ docker run -d \ -e GITLAWB_CONTRACT_NODE_STAKING=$GITLAWB_CONTRACT_NODE_STAKING \ -e GITLAWB_CHAIN_RPC_URL=$GITLAWB_CHAIN_RPC_URL \ -e GITLAWB_OPERATOR_STRICT_MODE=true \ - ghcr.io/gitlawb/node:latest + ghcr.io/twigpine/node:latest ``` ### Option B — docker-compose diff --git a/macos-app/Sources/GitlawbNode/DockerCompose.swift b/macos-app/Sources/GitlawbNode/DockerCompose.swift index 067a73355..1d005c5e4 100644 --- a/macos-app/Sources/GitlawbNode/DockerCompose.swift +++ b/macos-app/Sources/GitlawbNode/DockerCompose.swift @@ -280,7 +280,7 @@ services: restart: unless-stopped node: - image: ghcr.io/gitlawb/node:latest + image: ghcr.io/twigpine/node:latest depends_on: postgres: condition: service_healthy From f837d63153481ff2e2c78e8ecedda6052a28448c Mon Sep 17 00:00:00 2001 From: jatmn Date: Wed, 4 Nov 2026 10:09:10 -0800 Subject: [PATCH 3/7] fix(infra): keep the AWS security group description to avoid replacement `description` on aws_security_group is ForceNew, so renaming it forced a replace of the in-use group on the next apply of an existing deployment. Restore the original text and note why it must stay until a separate infra migration. Co-Authored-By: Claude Opus 5.5 --- infra/aws/main.tf | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/infra/aws/main.tf b/infra/aws/main.tf index 0ceab3ede..1dcd4c5e7 100644 --- a/infra/aws/main.tf +++ b/infra/aws/main.tf @@ -164,8 +164,9 @@ resource "aws_iam_instance_profile" "node" { # --------------------------------------------------------------------------- resource "aws_security_group" "node" { + # description is ForceNew: changing it replaces the in-use group, so keep the original text. name = "${var.name_prefix}-sg" - description = "Twigpine node: HTTP API + libp2p UDP" + description = "gitlawb node: HTTP API + libp2p UDP" vpc_id = data.aws_subnet.selected.vpc_id # follows subnet_id overrides into non-default VPCs tags = local.common_tags From e04efe4ec3c397603ec43ce245d675ccc8e549fc Mon Sep 17 00:00:00 2001 From: jatmn Date: Wed, 4 Nov 2026 10:15:03 -0800 Subject: [PATCH 4/7] fix(macos): regenerate the app's compose file instead of treating it as a repo's Config.repoPath fell back to the data directory when no repo clone was found, and DockerCompose then picked up the compose file it had generated there as if it belonged to a repo. The generator therefore only ran on first launch, so upgraded installs kept their original compose, including the old ghcr.io/gitlawb/node image, which no longer serves anonymous pulls. repoPath now returns nil when there is no clone, and the app regenerates its compose file whenever no repo compose is in use. Project directory and named volumes are unchanged. Co-Authored-By: Claude Opus 5.5 --- macos-app/Sources/GitlawbNode/Config.swift | 11 +++++++--- .../Sources/GitlawbNode/DockerCompose.swift | 21 +++++++++++-------- 2 files changed, 20 insertions(+), 12 deletions(-) diff --git a/macos-app/Sources/GitlawbNode/Config.swift b/macos-app/Sources/GitlawbNode/Config.swift index 406999ba1..eef54ed74 100644 --- a/macos-app/Sources/GitlawbNode/Config.swift +++ b/macos-app/Sources/GitlawbNode/Config.swift @@ -28,8 +28,13 @@ class Config { // MARK: - Paths - /// Path to the gitlawb-node repo clone (for local builds). - var repoPath: URL { + /// Path to the gitlawb-node repo clone (for local builds), or nil when there is none. + /// + /// Must not fall back to `dataDirectory`: the app writes its generated compose + /// file there, and treating that file as a repo's own would stop it from ever + /// being regenerated, pinning upgraded installs to the compose (and image) + /// they first started with. + var repoPath: URL? { if !repoPathString.isEmpty { return URL(fileURLWithPath: repoPathString) } @@ -48,7 +53,7 @@ class Config { FileManager.default.fileExists(atPath: dockerfile.path) { return repoRoot } - return dataDirectory + return nil } var dataDirectory: URL { diff --git a/macos-app/Sources/GitlawbNode/DockerCompose.swift b/macos-app/Sources/GitlawbNode/DockerCompose.swift index 1d005c5e4..2750f016d 100644 --- a/macos-app/Sources/GitlawbNode/DockerCompose.swift +++ b/macos-app/Sources/GitlawbNode/DockerCompose.swift @@ -39,11 +39,11 @@ class DockerCompose { /// Path to the docker-compose.yml — uses the repo's file if available, otherwise generates one. var composeFilePath: String { // Prefer the repo's own docker-compose.yml (supports `build: .`) - let repoCompose = Config.shared.repoPath.appendingPathComponent("docker-compose.yml") - if FileManager.default.fileExists(atPath: repoCompose.path) { - return repoCompose.path + if let repo = repoWithCompose { + return repo.appendingPathComponent("docker-compose.yml").path } - // Fallback: generate a compose file with pre-built image + // Fallback: (re)generate the compose file with the pre-built image each time, + // so an app upgrade also upgrades the stack it runs. let userPath = Config.shared.dataDirectory.appendingPathComponent("docker-compose.yml").path try? FileManager.default.createDirectory(at: Config.shared.dataDirectory, withIntermediateDirectories: true) let content = Self.generateComposeFile() @@ -53,11 +53,14 @@ class DockerCompose { /// Project directory for docker compose (needed for `build: .` context). var projectDirectory: String { - let repoCompose = Config.shared.repoPath.appendingPathComponent("docker-compose.yml") - if FileManager.default.fileExists(atPath: repoCompose.path) { - return Config.shared.repoPath.path - } - return Config.shared.dataDirectory.path + (repoWithCompose ?? Config.shared.dataDirectory).path + } + + /// The repo clone, if there is one and it has its own docker-compose.yml. + private var repoWithCompose: URL? { + guard let repo = Config.shared.repoPath else { return nil } + let compose = repo.appendingPathComponent("docker-compose.yml") + return FileManager.default.fileExists(atPath: compose.path) ? repo : nil } /// Path to the .env file with user configuration. From c9a86c19221acafa18921c5a408ef57da86b700e Mon Sep 17 00:00:00 2001 From: jatmn Date: Wed, 4 Nov 2026 10:15:03 -0800 Subject: [PATCH 5/7] fix(infra): default the AWS node image to ghcr.io/twigpine/node release.yml publishes to ghcr.io/${GITHUB_REPOSITORY,,}, which is now ghcr.io/twigpine/node; ghcr.io/gitlawb/node denies anonymous pulls, so the default made new deployments fail to start and the upgrade command fail on existing ones. On an existing deployment `terraform apply` only updates the upgrade SSM document in place (user_data drift is ignored); the new image is pulled on the next upgrade_command. Co-Authored-By: Claude Opus 5.5 --- infra/README.md | 2 +- infra/aws/README.md | 2 +- infra/aws/terraform.tfvars.example | 2 +- infra/aws/variables.tf | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/infra/README.md b/infra/README.md index 876a1f426..b5e90ed97 100644 --- a/infra/README.md +++ b/infra/README.md @@ -24,7 +24,7 @@ file, so it points to `../../Dockerfile`. ## Deploying to AWS See [`aws/README.md`](aws/README.md) — Terraform for a single EC2 instance -running the published `ghcr.io/gitlawb/node` image with Docker compose. +running the published `ghcr.io/twigpine/node` image with Docker compose. ## What intentionally stays at the repo root diff --git a/infra/aws/README.md b/infra/aws/README.md index 34fe38e62..d410a4b74 100644 --- a/infra/aws/README.md +++ b/infra/aws/README.md @@ -7,7 +7,7 @@ daily snapshots. ```text Elastic IP ──► EC2 t4g.small (Amazon Linux 2023, arm64) 7545/tcp docker compose: - 7546/udp ├─ node (ghcr.io/gitlawb/node, pulled — not built) + 7546/udp ├─ node (ghcr.io/twigpine/node, pulled — not built) └─ postgres:16-alpine EBS gp3 volume mounted at /mnt/data ├─ node/ → container /data (repos + identity key) diff --git a/infra/aws/terraform.tfvars.example b/infra/aws/terraform.tfvars.example index 421f48ba7..f4ad16799 100644 --- a/infra/aws/terraform.tfvars.example +++ b/infra/aws/terraform.tfvars.example @@ -5,7 +5,7 @@ name_prefix = "gitlawb-node" instance_type = "t4g.small" # ARM/Graviton; node image is multi-arch # --- Image ----------------------------------------------------------------- -# image_repo = "ghcr.io/gitlawb/node" +# image_repo = "ghcr.io/twigpine/node" # image_tag = "latest" # pin a version for production, e.g. "0.9.2" # Or host the image in a private ECR repo in this account (image_repo is then # ignored; build & push per README "Building and pushing the image"): diff --git a/infra/aws/variables.tf b/infra/aws/variables.tf index c7666f52e..2fab31cf3 100644 --- a/infra/aws/variables.tf +++ b/infra/aws/variables.tf @@ -66,7 +66,7 @@ variable "snapshot_retain_count" { variable "image_repo" { description = "Container image repository for the node. Ignored when create_ecr_repo is true (the module-managed ECR repo is used instead)." type = string - default = "ghcr.io/gitlawb/node" + default = "ghcr.io/twigpine/node" } variable "create_ecr_repo" { From 09242262ce6aca20a9f3c4ff15077dd254237473 Mon Sep 17 00:00:00 2001 From: jatmn Date: Wed, 4 Nov 2026 10:23:43 -0800 Subject: [PATCH 6/7] fix(macos): keep operator edits to the generated compose, migrate only the image e04efe4 regenerated the app's compose file on every access, overwriting edits operators make to pass settings the generated environment block omits (e.g. GITLAWB_OPERATOR_STRICT_MODE). Generate the file only when it is missing; when it exists, keep it and rewrite only references to the retired ghcr.io/gitlawb/node image, keeping tags and digests. Co-Authored-By: Claude Opus 5.5 --- macos-app/Sources/GitlawbNode/Config.swift | 7 ++--- .../Sources/GitlawbNode/DockerCompose.swift | 30 +++++++++++++++---- 2 files changed, 28 insertions(+), 9 deletions(-) diff --git a/macos-app/Sources/GitlawbNode/Config.swift b/macos-app/Sources/GitlawbNode/Config.swift index eef54ed74..8ced72551 100644 --- a/macos-app/Sources/GitlawbNode/Config.swift +++ b/macos-app/Sources/GitlawbNode/Config.swift @@ -30,10 +30,9 @@ class Config { /// Path to the gitlawb-node repo clone (for local builds), or nil when there is none. /// - /// Must not fall back to `dataDirectory`: the app writes its generated compose - /// file there, and treating that file as a repo's own would stop it from ever - /// being regenerated, pinning upgraded installs to the compose (and image) - /// they first started with. + /// Must not fall back to `dataDirectory`: the app's generated compose file lives + /// there, and treating it as a repo's own would skip `DockerCompose`'s handling + /// of that file (first-use generation and image migration). var repoPath: URL? { if !repoPathString.isEmpty { return URL(fileURLWithPath: repoPathString) diff --git a/macos-app/Sources/GitlawbNode/DockerCompose.swift b/macos-app/Sources/GitlawbNode/DockerCompose.swift index 2750f016d..2babb8aa3 100644 --- a/macos-app/Sources/GitlawbNode/DockerCompose.swift +++ b/macos-app/Sources/GitlawbNode/DockerCompose.swift @@ -42,15 +42,35 @@ class DockerCompose { if let repo = repoWithCompose { return repo.appendingPathComponent("docker-compose.yml").path } - // Fallback: (re)generate the compose file with the pre-built image each time, - // so an app upgrade also upgrades the stack it runs. + // Fallback: the app's own compose file with the pre-built image. Generate it + // on first use. Afterwards keep it: it is the only place an operator can pass + // settings the generated environment block omits (e.g. + // GITLAWB_OPERATOR_STRICT_MODE), so only the retired image path is migrated. let userPath = Config.shared.dataDirectory.appendingPathComponent("docker-compose.yml").path - try? FileManager.default.createDirectory(at: Config.shared.dataDirectory, withIntermediateDirectories: true) - let content = Self.generateComposeFile() - try? content.write(toFile: userPath, atomically: true, encoding: .utf8) + if FileManager.default.fileExists(atPath: userPath) { + Self.migrateRetiredImage(atPath: userPath) + } else { + try? FileManager.default.createDirectory(at: Config.shared.dataDirectory, withIntermediateDirectories: true) + let content = Self.generateComposeFile() + try? content.write(toFile: userPath, atomically: true, encoding: .utf8) + } return userPath } + /// Rewrites references to the pre-rename image, which no longer serves anonymous + /// pulls, to the image `release.yml` publishes. Tags and digests are kept. + private static func migrateRetiredImage(atPath path: String) { + guard let existing = try? String(contentsOfFile: path, encoding: .utf8) else { return } + let migrated = existing.replacingOccurrences( + of: #"ghcr\.io/gitlawb/node(?![A-Za-z0-9_./-])"#, + with: "ghcr.io/twigpine/node", + options: .regularExpression + ) + if migrated != existing { + try? migrated.write(toFile: path, atomically: true, encoding: .utf8) + } + } + /// Project directory for docker compose (needed for `build: .` context). var projectDirectory: String { (repoWithCompose ?? Config.shared.dataDirectory).path From b2d519ea54df1527c5694abe0a597dd824724126 Mon Sep 17 00:00:00 2001 From: jatmn Date: Wed, 4 Nov 2026 10:30:44 -0800 Subject: [PATCH 7/7] fix(macos): only migrate complete ghcr.io/gitlawb/node references Without a left boundary the migration also rewrote the retired path when it appeared inside a longer reference, such as a registry mirror (registry.example.com/ghcr.io/gitlawb/node), pointing it at a path the mirror may not have. Co-Authored-By: Claude Opus 5.5 --- macos-app/Sources/GitlawbNode/DockerCompose.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/macos-app/Sources/GitlawbNode/DockerCompose.swift b/macos-app/Sources/GitlawbNode/DockerCompose.swift index 2babb8aa3..048c66ff2 100644 --- a/macos-app/Sources/GitlawbNode/DockerCompose.swift +++ b/macos-app/Sources/GitlawbNode/DockerCompose.swift @@ -62,7 +62,7 @@ class DockerCompose { private static func migrateRetiredImage(atPath path: String) { guard let existing = try? String(contentsOfFile: path, encoding: .utf8) else { return } let migrated = existing.replacingOccurrences( - of: #"ghcr\.io/gitlawb/node(?![A-Za-z0-9_./-])"#, + of: #"(?