tng -- Tangled CLI
Work with Tangled, the decentralized Git collaboration platform, from your terminal.
tng auth login --handle example.bsky.social
tng repo clone example.bsky.social/my-project
cd my-project
tng pr list
tng issue create --title "Bug report"
Installation
From source (Go)
Requires Go 1.26.5 or newer.
go install tangled.org/eric.wien/tng-cli/cmd/tng@latest
Build manually
git clone git@knot.eric.wien:eric.wien/tng-cli
cd tng-cli
make build
./bin/tng version
Makefile targets
| Target | Description |
|---|---|
make build | Build the binary to ./bin/tng |
make test | Run all tests |
make lint | Run golangci-lint |
make install | Install to /usr/local/bin |
make clean | Remove build artifacts |
Authentication
tng authenticates using AT Protocol OAuth 2.0 with PKCE and DPoP. This is the same protocol used by Bluesky. Your handle can be from any AT Protocol provider.
# Opens browser for authorization
tng auth login --handle example.bsky.social
# Paste the localhost callback URL in a headless environment
tng auth login --manual --handle example.bsky.social
# Check current session
tng auth status
# Sign out (revokes tokens)
tng auth logout
The login flow:
- Resolves your handle to a DID and PDS endpoint
- Sends a Pushed Authorization Request (PAR) with PKCE challenge
- Opens your browser, or displays an authorization URL in a remote session
- Handles the OAuth callback on a local HTTP server or from a pasted callback URL
- Exchanges the code for DPoP-bound access tokens
- Stores the session locally at
~/.config/tng/
Over SSH, manual browser authentication is selected automatically. Open the displayed 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 this flow in other headless environments.
Tokens are automatically refreshed. The public client flow gives sessions a lifetime of approximately two weeks.
tng auth
Manage authentication with your Tangled / AT Protocol account.
tng auth login
Authenticate via browser-based OAuth flow.
tng auth login
tng auth login --handle example.bsky.social
tng auth login --manual --handle example.bsky.social
tng auth login --handle did:plc:abc123| Flag | Description |
|---|---|
| --handle | AT Protocol handle or DID. If omitted, prompted interactively. |
| --manual | Complete login by pasting the localhost callback URL. Automatically enabled over SSH. |
tng auth status
Show all authenticated accounts. The active account is marked with *.
tng auth logout
Revoke tokens and remove the session.
| Flag | Description |
|---|---|
| --did | DID of account to log out. Defaults to the active account. |
tng repo
Create, clone, view, and list Tangled repositories. Alias: tng r
tng repo create <name>
Create a repository on a Tangled knot. New repositories publish their knot-issued repository DID, so they are indexed by the Tangled appview. Pass --knot or configure default_knot.
tng repo create my-project --knot knot1.tangled.sh
tng repo create my-project -d "Project description" --knot knot1.tangled.sh
tng repo create my-project --knot https://knot.eric.wien
tng repo create my-project --default-branch main --clone --knot knot1.tangled.sh| Flag | Description |
|---|---|
| -d, --description | Repository description |
| --default-branch | Default branch name (default: main) |
| -k, --knot | Knot URL to create the repo on (e.g., https://knot.eric.wien). Falls back to default_knot in config. |
| -c, --clone | Clone the repository after creation |
tng repo clone <owner/repo> [directory]
Clone a Tangled repository via SSH.
tng repo clone example.bsky.social/my-project
tng repo clone example.bsky.social/my-project ./my-dirtng repo view [owner/repo]
Display repository details: file tree, last commit, and README. Uses the current repo if no argument is given.
| Flag | Description |
|---|---|
| -w, --web | Open in browser instead of printing |
tng repo list [owner]
List repositories for a user. Defaults to the authenticated user. Alias: tng repo ls
| Flag | Description |
|---|---|
| -L, --limit | Maximum number of results (default: 30) |
tng pr
Create, view, merge, and manage pull requests.
Tangled uses a round-based review model. Each push to a PR creates a new immutable round. Reviewers can view interdiffs between rounds.
tng pr create
tng pr create --title "Add feature" --body "Description"
tng pr create -t "Fix bug" --base main --head fix-branch| Flag | Description |
|---|---|
| -t, --title | PR title (required) |
| -B, --body | PR description |
| -b, --base | Target branch (default: main) |
| -H, --head | Source branch (default: current branch) |
tng pr list
tng pr list
tng pr list --repo example.bsky.social/my-project
tng pr list --web| Flag | Description |
|---|---|
| -R, --repo | Filter by repository (owner/name) |
| -w, --web | Print the tangled.sh pull requests URL |
tng pr view <rkey>
Display PR details including title, branches, and body.
| Flag | Description |
|---|---|
| --author | DID of the PR author (defaults to you) |
| -w, --web | Open in browser |
tng pr diff <rkey>
Print the raw diff of a pull request to stdout.
tng pr merge <rkey>
Merge a pull request. The PR must be in open status.
tng pr close <rkey>
Close a pull request without merging.
tng pr comment <rkey>
Add a comment to a pull request.
| Flag | Description |
|---|---|
| -b, --body | Comment text (required) |
tng issue
Create, view, and manage issues. Alias: tng i
tng issue create
tng issue create --title "Bug: login fails" --body "Steps to reproduce..."
tng issue create -t "Feature request"| Flag | Description |
|---|---|
| -t, --title | Issue title (required) |
| -b, --body | Issue body |
| -R, --repo | Repository override |
tng issue list
tng issue list
tng issue list --repo example.bsky.social/my-project
tng issue list --web| Flag | Description |
|---|---|
| -R, --repo | Filter by repository (owner/name) |
| -w, --web | Print the tangled.sh issues URL |
tng issue view <rkey>
| Flag | Description |
|---|---|
| --author | DID of the issue author (defaults to you) |
| -w, --web | Open in browser |
tng issue close <rkey>
Close an issue.
tng issue comment <rkey>
| Flag | Description |
|---|---|
| -b, --body | Comment text (required) |
tng browse
Open the current repository (or a specific file) in your web browser.
tng browse # Open repo root
tng browse src/main.go # Open specific file
tng browse -n # Print URL, don't open
tng browse -R example.bsky.social/proj # Specify repo| Flag | Description |
|---|---|
| -R, --repo | Repository override |
| -n, --no-browser | Print the URL instead of opening it |
Utilities
tng upgrade
Upgrade to the latest released version via the Go module proxy and go install (requires a Go toolchain). Alias: tng update
| Flag | Description |
|---|---|
| --check | Only check for a newer version, don't install |
tng version
Print the version, build date, and commit hash.
tng completion <shell>
Generate shell completion scripts. Supported: bash, zsh, fish, powershell.
Configuration
Config is stored at ~/.config/tng/config.yml (respects XDG_CONFIG_HOME and TNG_CONFIG_DIR).
default_knot: "" # Default knot for repo creation
api_host: "" # Override API host (default: https://tangled.sh)
Environment Variables
| Variable | Description |
|---|---|
TNG_HOST | Override API host (default: https://tangled.sh) |
TNG_CONFIG_DIR | Override config directory |
TNG_REPO | Override repo context (owner/name) |
TNG_FORCE_TTY | Force color output in non-TTY contexts |
NO_COLOR | Disable all color output |
Repository Context
Inside a cloned Tangled repository, tng automatically detects the repo from git remotes. Resolution priority:
TNG_REPOenvironment variablegit config tng.default-repo- Git remotes matching
tangled.sh(priority:upstream>tangled>origin)
Supported remote formats:
git@tangled.sh:owner/repo
https://tangled.sh/owner/repo
Shell Completions
# Bash
tng completion bash > /etc/bash_completion.d/tng
# Zsh (add to fpath)
tng completion zsh > "${fpath[1]}/_tng"
# Fish
tng completion fish > ~/.config/fish/completions/tng.fish
Architecture
tng follows the same patterns as the GitHub CLI:
- Go + cobra for command parsing
- Factory pattern with lazy dependency initialization
- AT Protocol OAuth via indigo (PKCE + DPoP)
- XRPC API client for
sh.tangled.*namespace endpoints - Automatic git remote detection for repository context
Key differences from GitHub CLI
| Aspect | gh (GitHub) | tng (Tangled) |
|---|---|---|
| Auth | GitHub OAuth device flow | AT Protocol OAuth 2.0 with PKCE + DPoP |
| Identity | GitHub username | AT Protocol DID + handle |
| API | REST + GraphQL | XRPC (AT Protocol RPC) |
| PRs | Mutable, linear | Round-based, immutable per round, interdiffs |
| Data | Centralized on GitHub | AT Protocol records on user's PDS |
| Hosting | Monolithic service | Knots (federated git servers) |
Project Structure
cmd/tng/ Entry point
internal/
tngcmd/ Root command assembly, exit codes
authflow/ AT Protocol OAuth flow
config/ Config files and session storage
build/ Version info (set via ldflags)
pkg/
cmd/ Command implementations
auth/ login, logout, status
repo/ create, clone, view, list
pr/ create, list, view, diff, merge, close, comment
issue/ create, list, view, close, comment
browse/ Open in browser
version/ Print version
completion/ Shell completions
cmdutil/ Factory, errors, arg helpers
iostreams/ TTY detection, color, terminal width
api/ XRPC API client and domain methods
context/ Git remote resolution
git/ Git CLI wrapper, remote parsing
tng-cli is open source. Source code at tangled.org/eric.wien/tng-cli.