Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 14 additions & 1 deletion .github/workflows/cut-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@
# 4. Commit CHANGELOG on main, create annotated tag, push both
# 5. Publish via GoReleaser (GitHub release + Homebrew) and npm
#
# The job runs on a macOS arm64 runner so GoReleaser can produce cgo builds
# of `xurl chat` for every platform chat-xdk supports: darwin targets build
# natively with Apple clang, and linux/amd64 cross-compiles with `zig cc`
# against chat-xdk's musl static library (see .goreleaser.yaml).
#
# No pull request is opened. GITHUB_TOKEN cannot create PRs unless the repo
# setting "Allow GitHub Actions to create and approve pull requests" is on,
# and the PR path is unnecessary for a release cut.
Expand Down Expand Up @@ -70,7 +75,9 @@ permissions:
jobs:
cut:
name: Cut ${{ inputs.version || inputs.bump }}
runs-on: ubuntu-latest
# macOS arm64: darwin cgo builds need Apple clang, and zig cross-compiles
# the linux/amd64 cgo build. Stub platforms cross-compile from anywhere.
runs-on: macos-latest
steps:
- name: Checkout main
uses: actions/checkout@v4
Expand All @@ -86,6 +93,12 @@ jobs:
with:
go-version: "1.24"

# zig is the C cross-compiler for the linux/amd64 cgo build.
- name: Set up Zig
uses: mlugg/setup-zig@v2
with:
version: "0.16.0"

- name: Resolve version and promote changelog
id: meta
run: |
Expand Down
43 changes: 42 additions & 1 deletion .github/workflows/go.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,5 +24,46 @@ jobs:
- name: Run tests
run: go test -v ./...

# The chat stub (cli/chat_unsupported.go) must keep compiling for the
# platforms that ship without the chat client.
- name: Build chat-stub targets
run: |
GOOS=windows GOARCH=amd64 CGO_ENABLED=0 go build ./...
GOOS=linux GOARCH=arm64 CGO_ENABLED=0 go build ./...

- name: Run lint
run: gofmt -l .
run: gofmt -l .

# Compile every release target exactly as the Cut Release workflow does
# (macOS runner, cgo darwin builds, zig-cross-compiled static linux/amd64,
# CGO_ENABLED=0 stubs) so a broken release build is caught before a cut.
release-build:
runs-on: macos-latest
steps:
- uses: actions/checkout@v4

- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: '1.24'

- name: Set up Zig
uses: mlugg/setup-zig@v2
with:
version: "0.16.0"

- name: GoReleaser build (snapshot)
uses: goreleaser/goreleaser-action@v6
with:
version: "~> v2"
args: build --snapshot --clean

- name: Smoke-test the darwin binary
run: |
BIN=$(ls dist/darwin-cgo_darwin_arm64*/xurl)
"$BIN" version
# The real chat client's help must load, not the stub message.
if "$BIN" chat 2>&1 | grep -q "not available in this build"; then
echo "::error::darwin/arm64 release binary shipped the chat stub"
exit 1
fi
78 changes: 61 additions & 17 deletions .goreleaser.yaml
Original file line number Diff line number Diff line change
@@ -1,32 +1,76 @@
# This is an example .goreleaser.yml file with some sensible defaults.
# Make sure to check the documentation at https://goreleaser.com

# The lines below are called `modelines`. See `:help modeline`
# Feel free to remove those if you don't want/need to use them.
# GoReleaser config. Docs: https://goreleaser.com
# yaml-language-server: $schema=https://goreleaser.com/static/schema.json
# vim: set ts=2 sw=2 tw=0 fo=cnqoj

version: 2

before:
hooks:
# You may remove this if you don't use go modules.
- go mod tidy
# you may remove this if you don't need go generate
- go generate ./...

# The release runs on a macOS arm64 runner (see cut-release.yml) so the
# `xurl chat` XChat client — which requires cgo to link the chat-xdk static
# libraries — can be compiled into every platform chat-xdk supports:
#
# darwin/arm64 native cgo build
# darwin/amd64 cgo cross-build with Apple clang (-arch x86_64)
# linux/amd64 cgo cross-build with `zig cc` against chat-xdk's musl
# static lib, fully statically linked so the one binary
# runs on both glibc and musl distros
#
# Platforms chat-xdk has no library for (windows/*, linux/arm64, linux/386)
# are built with CGO_ENABLED=0 and ship the graceful `xurl chat` stub.
builds:
# Release binaries are built with CGO disabled so a single Linux runner can
# cross-compile every platform. The `xurl chat` XChat client requires cgo
# (the chat-xdk crypto binding links prebuilt static libraries), so release
# binaries ship a stub for it; chat needs a source build with CGO_ENABLED=1
# on macOS (amd64/arm64) or Linux (amd64). See README "Encrypted chat".
- env:
- id: darwin-cgo
targets:
- darwin_arm64
- darwin_amd64
env:
- CGO_ENABLED=1
ldflags:
- -s -w
- -X github.com/xdevplatform/xurl/version.Version={{.Version}}
- -X github.com/xdevplatform/xurl/version.Commit={{.Commit}}
- -X github.com/xdevplatform/xurl/version.BuildDate={{.Date}}
hooks:
post:
# Ad-hoc sign so both darwin binaries carry a valid signature. The
# native arm64 build is already linker-signed, but the cross-built
# amd64 binary comes out unsigned. No Apple credentials involved;
# Gatekeeper quarantine is handled per install channel (the Homebrew
# cask strips it, and npm's programmatic download never sets it).
- cmd: codesign -s - --force {{ .Path }}

- id: linux-cgo
targets:
- linux_amd64
env:
- CGO_ENABLED=1
- CC=zig cc -target x86_64-linux-musl
# The musl tag selects chat-xdk's linux_amd64_musl static library.
flags:
- -tags=musl
ldflags:
- -s -w
- -X github.com/xdevplatform/xurl/version.Version={{.Version}}
- -X github.com/xdevplatform/xurl/version.Commit={{.Commit}}
- -X github.com/xdevplatform/xurl/version.BuildDate={{.Date}}
- -linkmode external
# Static link against musl so the binary has no runtime libc
# dependency; -lunwind provides the unwinder symbols the Rust-built
# chat-xdk library needs (zig bundles LLVM's libunwind).
- -extldflags "-static -lunwind"

- id: stub
targets:
- linux_arm64
- linux_386
- windows_amd64
- windows_arm64
- windows_386
env:
- CGO_ENABLED=0
goos:
- linux
- windows
- darwin
ldflags:
- -s -w
- -X github.com/xdevplatform/xurl/version.Version={{.Version}}
Expand Down
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ All user-visible bugs and enhancements should be recorded here.

## Unreleased

### Changed

- Release binaries now include the full `xurl chat` XChat client on macOS (arm64/amd64) and Linux (amd64) — installing via Homebrew, npm, or a GitHub release tarball no longer prints the "not available in this build" stub on those platforms. The Linux amd64 binary is statically linked against musl, so it runs on both glibc and musl (e.g. Alpine) distros. Platforms without a chat-xdk library (Windows, Linux arm64/i386) still ship the graceful stub.

## v1.3.0 - 2026-07-21

### Added
Expand Down
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -425,8 +425,10 @@ Notes:
writes to Juicebox or the key-registration endpoint.
- Private keys live in `~/.xurl/keys.yml` (mode 600). Losing it is safe as long as the
Juicebox backup (made by the original client) still exists.
- `chat` requires a cgo build on macOS (Intel/Apple Silicon) or Linux (amd64). Prebuilt
release binaries ship a stub; build from source to enable it:
- `chat` is supported on macOS (Intel/Apple Silicon) and Linux (amd64), and the release
binaries (Homebrew, npm, GitHub releases) include it on those platforms. On other
platforms (Windows, Linux arm64/i386) `xurl chat` prints a stub explaining it is
unavailable. Source builds on supported platforms need cgo:
`CGO_ENABLED=1 go install github.com/xdevplatform/xurl@latest`.

## Token Storage
Expand Down
2 changes: 1 addition & 1 deletion SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -254,7 +254,7 @@ xurl dms -n 25

### Encrypted Chat (XChat)

`xurl chat` is an end-to-end encrypted XChat client: encryption and decryption happen locally via the chat-xdk crypto library, so the server only sees ciphertext. Requires OAuth2 user auth with `dm.read` + `dm.write` scopes, and is available on macOS (Intel/Apple Silicon) and Linux amd64 when built with cgo (prebuilt release binaries ship a stub that says so).
`xurl chat` is an end-to-end encrypted XChat client: encryption and decryption happen locally via the chat-xdk crypto library, so the server only sees ciphertext. Requires OAuth2 user auth with `dm.read` + `dm.write` scopes, and is available on macOS (Intel/Apple Silicon) and Linux amd64 — release binaries include it there; elsewhere (Windows, Linux arm64/i386) `xurl chat` prints a stub that says so.

**Keys come from another XChat client** — xurl never generates or registers encryption keys. The account must already have keys (e.g. from the X app); bring them to this machine once with `restore` (Juicebox PIN recovery) or `import` (an exported key blob). Private keys are stored in `~/.xurl/keys.yml` (mode 600) — never read that file into LLM context.

Expand Down
4 changes: 2 additions & 2 deletions cli/chat_unsupported.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ func CreateChatCommand(a *auth.Auth) *cobra.Command {
Use: "chat",
Short: "Send and read end-to-end encrypted XChat messages (unavailable in this build)",
Run: func(cmd *cobra.Command, args []string) {
fmt.Fprintln(os.Stderr, "xurl chat is not available in this build: the XChat crypto library requires cgo and supports macOS (amd64/arm64) and Linux (amd64).")
fmt.Fprintln(os.Stderr, "On a supported platform, build from source with CGO enabled: CGO_ENABLED=1 go install github.com/xdevplatform/xurl@latest")
fmt.Fprintln(os.Stderr, "xurl chat is not available in this build: the XChat crypto library supports macOS (amd64/arm64) and Linux (amd64) only.")
fmt.Fprintln(os.Stderr, "Release binaries for those platforms include chat; on them a source build also needs cgo: CGO_ENABLED=1 go install github.com/xdevplatform/xurl@latest")
os.Exit(1)
},
}
Expand Down
Loading