tng #
A command-line interface for Tangled, the decentralized Git collaboration platform built on the AT Protocol.
tng brings the full Tangled workflow to your terminal: authenticate with your AT Protocol identity, manage repositories, create and review pull requests, file and track issues -- all without leaving the command line.
Known Issues #
- PR/issue list scope:
tng pr listandtng issue listshow only records authored by the authenticated user. The appview aggregates records from all authors but does not expose a public JSON API, so cross-user listing is not yet possible from the CLI. - OAuth session expiry: The public client OAuth flow produces tokens that cannot always be refreshed silently. If you encounter
invalid_granterrors, runtng auth loginto re-authenticate. - Repo view on empty repos:
tng repo viewreturns a 404 for repos with no commits, since the knot has no ref to resolve. Push at least one commit first.
Repositories created by current versions of tng include the knot-minted repository DID
required by Tangled's appview ingester. The target knot must support repository DIDs
(Tangled core v1.13 or newer).
Installation #
From source #
go install tangled.org/eric.wien/tng-cli/cmd/tng@latest
Build from source #
git clone git@tangled.sh:eric.wien/tng-cli
cd tng-cli
make build
# Binary is at ./bin/tng
Updating #
tng upgrade # Check the Go module proxy and install the latest release via go install
tng upgrade --check # Only check whether a newer release exists
tng upgrade is equivalent to go install tangled.org/eric.wien/tng-cli/cmd/tng@latest
and requires a Go toolchain. For source builds, use git pull && make install instead.
Makefile targets #
make build # Build the binary
make test # Run tests
make lint # Run golangci-lint
make install # Install to /usr/local/bin
make clean # Remove build artifacts
Quick Start #
# 1. Authenticate with your AT Protocol handle
tng auth login --handle example.bsky.social
# 2. Create a repository on a knot
tng repo create my-project --knot knot.example.com
# 3. Clone it
tng repo clone example.bsky.social/my-project
# 4. Work on code, then create a pull request
cd my-project
git checkout -b feature-branch
# ... make changes, commit ...
tng pr create --title "Add new feature" -b main
# 5. List and view your pull requests
tng pr list
tng pr view <rkey>
# 6. Open in browser
tng browse
Authentication #
tng uses AT Protocol OAuth 2.0 with PKCE and DPoP. Your handle can be from any AT Protocol provider -- Bluesky (example.bsky.social), Tangled (example.tngl.sh), or any self-hosted PDS.
# Interactive login (opens browser)
tng auth login
# Login with a specific handle
tng auth login --handle example.bsky.social
# Login from an SSH or other headless session
tng auth login --manual --handle example.bsky.social
# Check who you're logged in as
tng auth status
# Log out
tng auth logout
Sessions are stored in ~/.config/tng/. Tokens are persisted locally and refreshed automatically. The public client flow is used, so tokens expire after approximately two weeks.
When run over SSH, tng auth login automatically uses manual browser
authentication. Open the displayed authorization URL on your local machine,
authorize the CLI, then copy the full localhost callback URL from the browser's
address bar back into the terminal. Use --manual to select the same flow in
other headless environments.
Environment variables #
| Variable | Description |
|---|---|
TNG_HOST |
Override API host (default: https://tangled.sh) |
TNG_CONFIG_DIR |
Override config directory |
TNG_REPO |
Override repository context (owner/name) |
TNG_FORCE_TTY |
Force TTY output in non-TTY contexts |
NO_COLOR |
Disable color output |
Commands #
tng auth -- Authentication #
tng auth login [--handle <handle>] [--manual] # Log in via browser OAuth
tng auth logout [--did <did>] # Log out and revoke tokens
tng auth status # Show authenticated accounts
tng repo -- Repositories #
tng repo create <name> [flags] # Create a new repository
tng repo clone <owner/repo> [dir] # Clone via SSH
tng repo delete <owner/repo> # Delete a repository
tng repo view [owner/repo] # View repository details
tng repo list [owner] # List repositories
Flags for repo create:
-d, --description-- Repository description--default-branch-- Default branch name (default:main)-k, --knot-- Knot URL to create the repo on (required unlessdefault_knotis set in config)-c, --clone-- Clone after creation
Flags for repo clone:
-k, --knot-- Knot hostname to clone from (skips AT Protocol record lookup)
Flags for repo delete:
-y, --yes-- Skip confirmation prompt
Flags for repo view:
-w, --web-- Open in browser instead
Flags for repo list:
-L, --limit-- Maximum number of results (default: 30)
tng pr -- Pull Requests #
Pull requests are AT Protocol records identified by a TID rkey (e.g. 3jxn7q7bklr2r), not sequential numbers. The rkey is shown in tng pr list output. Patches are stored as gzip-compressed blobs on the user's PDS.
tng pr create [flags] # Create a pull request
tng pr list [flags] # List your pull requests
tng pr view <rkey> [flags] # View PR details
tng pr diff <rkey> # View PR patch
tng pr merge <rkey> # Merge a pull request
tng pr close <rkey> # Close a pull request
tng pr comment <rkey> --body "..." # Comment on a PR
Flags for pr create:
-t, --title-- PR title (required)-B, --body-- PR description-b, --base-- Target branch (default: the repo's default branch, falling back tomain)-H, --head-- Source branch (default: current branch)
Flags for pr list:
-R, --repo-- Filter by repository (owner/name)-w, --web-- Open pull requests page on tangled.sh
Flags for pr view, pr diff, pr merge, pr close, pr comment:
--author-- DID of the PR author (defaults to the authenticated user)
tng issue -- Issues #
Issues are AT Protocol records identified by a TID rkey, similar to pull requests.
tng issue create [flags] # Create an issue
tng issue list [flags] # List your issues
tng issue view <rkey> [flags] # View issue details
tng issue close <rkey> # Close an issue
tng issue comment <rkey> --body "..." # Comment on an issue
Flags for issue create:
-t, --title-- Issue title (required)-b, --body-- Issue body-R, --repo-- Repository override (owner/name)
Flags for issue list:
-R, --repo-- Filter by repository (owner/name)-w, --web-- Open issues page on tangled.sh
Flags for issue view, issue close, issue comment:
--author-- DID of the issue author (defaults to the authenticated user)
tng browse -- Open in Browser #
tng browse # Open current repo
tng browse src/main.go # Open specific file
tng browse -n # Print URL without opening
tng browse -R owner/repo # Open specific repo
Utilities #
tng upgrade [--check] # Upgrade to the latest release (alias: update)
tng version # Print version
tng completion <bash|zsh|fish> # Generate shell completions
Repository Context #
When you run commands inside a cloned Tangled repository, tng automatically detects the repository from git remotes. Both tangled.sh and knot hostnames (e.g. knot.example.com) are recognized. Remote priority:
upstreamremotetangledremoteoriginremote
You can override this with:
- The
-R/--repoflag on any command - The
TNG_REPOenvironment variable git config tng.default-repo owner/name
Shell Completions #
# Bash
tng completion bash > /etc/bash_completion.d/tng
# Zsh
tng completion zsh > "${fpath[1]}/_tng"
# Fish
tng completion fish > ~/.config/fish/completions/tng.fish
Configuration #
Config file is at ~/.config/tng/config.yml:
default_knot: "" # Default knot for repo creation
api_host: "" # Override API host
Architecture #
tng follows the same architectural patterns as the GitHub CLI:
- Written in Go with cobra for command parsing
- Factory pattern for dependency injection with lazy initialization
- AT Protocol OAuth via indigo (PKCE + DPoP)
- XRPC API client for Tangled's AT Protocol-based endpoints
- Automatic git remote detection for Tangled repositories
Key differences from gh #
- Authentication: AT Protocol OAuth 2.0 (not GitHub's device flow). Requires identity resolution through the AT Protocol stack (handle -> DID -> PDS -> auth server).
- Data model: Issues, PRs, and other collaboration data are AT Protocol records stored on the user's PDS, not on a central server. They are identified by TID record keys, not sequential numbers.
- Pull requests: Patches are generated via
git format-patch, gzip-compressed, and uploaded as blobs to the PDS. Merging sends the patch to the knot for server-side application. - Infrastructure: Repositories live on knots (lightweight git servers), not a single monolithic service. The CLI resolves which knot hosts a repo via AT Protocol records.
Project Structure #
cmd/tng/ Entry point
internal/
tngcmd/ Root command assembly, exit codes
authflow/ AT Protocol OAuth (PKCE, DPoP, callback server)
config/ Config files, session storage
build/ Version info (ldflags, Go build metadata fallback)
pkg/
cmd/ Command implementations
auth/ login, logout, status
repo/ create, clone, delete, view, list
pr/ create, list, view, diff, merge, close, comment
issue/ create, list, view, close, comment
browse/ Open in browser
upgrade/ Upgrade via the Go module proxy + go install
cmdutil/ Factory, errors, arg validators
iostreams/ TTY detection, color, terminal width
api/ XRPC API client and domain methods
context/ Git remote -> Tangled repo resolution
git/ Git CLI wrapper, remote URL parsing
License #
MIT