• v0.6.0 3763104765

    v0.6.0
    All checks were successful
    ci / check (push) Successful in 11m47s
    ci / coverage (push) Successful in 2m30s
    ci / live-e2e (push) Successful in 2m40s
    release / prepare (push) Successful in 10s
    release / build (catthehacker/ubuntu:act-22.04, linux-x86_64, docker, x86_64-unknown-linux-gnu) (push) Successful in 2m58s
    release / build (darwin-aarch64, macos-15, aarch64-apple-darwin) (push) Successful in 5m45s
    release / build (darwin-x86_64, macos-15, x86_64-apple-darwin) (push) Successful in 5m54s
    release / publish (push) Successful in 19s
    release / guard (push) Has been skipped
    Stable

    stephen released this 2026-10-11 23:52:08 +00:00 | 5 commits to main since this release

    Signed by stephen
    GPG key ID: 67F885778E01BF16

    Added

    • Notifications can be handled one thread at a time. fj status read <ID>
      marks a single thread read and confirms it against the server.
      fj status unsubscribe <ID> drops your subscription to the issue or pull
      request behind a thread and marks it read; a thread with no work item behind
      it is refused before anything is sent. fj status mute|unmute|muted keep a
      per-account list of repositories to hide from the inbox (stored in
      muted_repos.toml next to hosts.toml), with --include-muted to see them
      anyway. Muted threads still count toward the server's unread count.
    • fj issue reaction, fj issue dependency and fj issue time. Reactions
      (list|add|remove|toggle) work on an issue or pull request body or, with
      --comment, on one comment. Dependencies (list|add|remove) show "Depends
      on" and "Blocks" as two lists and keep the direction through --blocks; a
      self reference, a duplicate and a cycle are refused before any mutation.
      Tracked time (list|add|delete) logs and totals time, reading durations as
      1h30m, 45m, 90s or 1:30. All three accept a pull request number.
    • fj issue list --sort and fj issue create --milestone. --sort takes
      Forgejo's own orders (recentupdate, leastupdate, latest, oldest,
      mostcomment, leastcomment, nearduedate, farduedate). --milestone
      takes a title or an id and errors on an ambiguous title. fj issue view --json and the list JSON gain a milestone field.
    • Inline review comments. `fj pr review N --line-comment ` is repeatable, and `--line-comments-file` takes the same as a JSON array. `LINE` is `N` or `new:N` for the pull request's version of the file and `old:N` for a removed line. Comments given with `--event` go out in that review in one request; without `--event` they post as a plain comment review. Each anchor is checked against the diff first, so a line that cannot be commented on is refused locally with the commentable lines named.
    • fj pr merge explains a blocked merge before it tries. It reads the base
      branch's effective protection, which needs read access only, and stops on a
      certain blocker (a required check failing, pending or not reported, a missing
      required approval, no merge permission) with the reason. Evidence it could
      not read stays "unknown" and never blocks. fj pr checks shows the same
      verdict, gains a REQUIRED column, and lists required checks that have not
      reported; its --json gains required, statuses_complete and
      merge_preflight.
    • Organizations and teams. fj org create creates an organization.
      fj org team create|members|add-member|remove-member|repos|add-repo| remove-repo manage a team; every change is followed by a read, and the
      command errors if that read contradicts it.
    • Repository transfers. fj repo transfer request resolves the new owner
      and refuses an unknown owner, the current owner and an already pending
      transfer before it asks. fj repo transfer list|view|accept|reject review
      incoming transfers. Forgejo has no incoming-transfer endpoint, so list
      scans your repository listings and can miss a transfer addressed to one of
      your organizations; view -R always works.
    • fj repo topics add|remove change single topics and print the list the
      forge confirms.
    • Commits and files at a ref. fj repo commit list|view read history at
      --ref, --branch or --tag and show each commit's signature state.
      fj repo contents [PATH] reads a file or directory at a ref, with a tag
      pinned to its commit. fj browse --tag opens the tag route.
    • Organization labels and exclusive groups. fj label list|create|edit| delete --org ORG work on organization labels, with --exclusive and
      --no-exclusive for group/name labels. Assigning labels on issue and pull
      request create or edit falls back to the organization's labels for names the
      repository does not have.
    • fj admin. fj admin settings and fj admin cron list are read-only
      views of the instance. fj admin user create takes the password from stdin
      or a prompt, never from a flag, and keeps it out of --debug.
      fj admin unadopted list|adopt|delete handle unadopted repositories behind a
      typed confirmation. A 403 reads as "requires an instance administrator" and
      is never folded into "unavailable".
    • fj profile. fj profile view|edit, fj profile email list|add|remove
      and fj profile avatar set|delete. An edit sends only the fields that were
      passed and warns about any the response does not reflect.
    • Account groups. fj auth group list|create|rename|delete|assign|unassign
      keep user-named groups of hosts in hosts.toml. Grouping never changes host
      resolution. fj auth status and fj auth list show the group once one
      exists.
    • The Commons appears in fj instances. A joined Commons is listed beside
      the account's own forges: selectable when the gateway can route to it, and a
      "browser only" row otherwise, which fj instances use commons hands off to
      the browser. The lookup is best-effort, so a slow or failing Commons no
      longer fails the listing.
    • Forge flavor detection and capability gates. fj identifies Forgejo or
      Gitea before applying a version floor, stores the flavor and version at
      login and refresh, and shows them in fj auth status. One table decides
      which routes are offered. fj run rerun uses the Gitea 1.26+ routes and
      fj run cancel the Forgejo 16 route; a withheld action sends nothing and
      names the run URL.
    • Basic-auth reverse proxies. fj auth proxy stores a separate HTTP Basic
      credential for a proxy in front of the forge. A 407 proxy takes it in
      Proxy-Authorization. A 401 Basic proxy takes it in Authorization, and
      the forge token moves to X-Forge-Authorization, which the proxy has to
      forward. --pat-transport query works with an unmodified proxy at the cost
      of the token appearing in access logs. Git over HTTPS behind such a proxy is
      out of scope; use SSH.

    Changed

    • The fj status table has a leading ID column, which shifts the columns
      for anything parsing the table. --limit above 50 now pages instead of
      silently clamping.
    • fj label create|edit --color is now --label-color. The old flag
      shared an id with the global ANSI --color and both commands panicked at
      parse time, so the old spelling never worked.
    • fj variable list output changed. The table is NAME / VALUE / SCOPE
      and the JSON follows the forge's ActionVariable shape (name, data,
      owner_id, repo_id).
    • fj instances list --json gains kind, routing and a commons
      descriptor.
      A browser-only Commons entry has a null id.
    • The token no longer reaches any web route. Job logs are read through the
      Actions API with ranged requests (1 MiB chunks, 64 MiB ceiling). fj run view, fj run watch and artifact downloads use the API where the server
      has it and fall back to an anonymous web request elsewhere, so on older
      servers they work for publicly readable runs only.
    • fj run rerun on Forgejo reports that the forge has no rerun route
      instead of attempting a web POST that could never succeed with a token.
    • A Fjord Account re-login re-validates the remembered instance. It is kept
      when still present and ready; otherwise it is cleared and the ready
      instances are printed with the fj instances use <id> hint.
    • fj pr review --event comment with nothing to say, and --event request-changes with no body, fail locally instead of as a server 422.
    • fj repo topics --set prints the list the forge confirms and validates
      topic names locally.

    Fixed

    • Concurrent commands no longer sign a Fjord Account out. The refresh token
      rotates on every exchange and a replay revokes the whole family, so two
      commands refreshing at once (a prompt hook plus an interactive command is
      enough) killed the session. Refreshes now take a per-platform file lock held
      across the exchange and the persist, and the rotated refresh token is written
      before the new bearer, so an interruption leaves the previous pair intact. A
      spent grant now prints the recovery as the first line of the error.
    • fj auth login --fjord keeps the selected instance. Signing in again to
      a platform you were already signed into cleared the default instance, so the
      command after a successful recovery failed with "no default Fjord instance
      selected".
    • fj pr merge no longer blames a required check on a green pull request.
      Forgejo treats each required status context as a glob, so * means every
      reported context. fj looked the pattern up by name, never found a check
      called *, and reported it missing. Patterns are now matched the way
      Forgejo matches them, and a failing check is reported under its real name.
    • fj repo collaborator list reported every permission as none. The list
      endpoint sends no permissions, so each row is now resolved through the
      per-collaborator endpoint; a row that cannot be read shows "unknown".
    • fj variable set could not create a variable, and a non-empty fj variable list failed to decode. set now creates or replaces and confirms.
      Variable values no longer reach --debug output.
    • fj warned "older than Forgejo 7" on any Gitea 1.x host. The version floor
      now applies to Forgejo only.
    • fj issue time list printed its total on the last row of the table.
    Downloads
  • v0.5.0 4bae59797f

    v0.5.0
    All checks were successful
    release / prepare (push) Successful in 4s
    release / build (catthehacker/ubuntu:act-22.04, linux-x86_64, docker, x86_64-unknown-linux-gnu) (push) Successful in 2m20s
    release / build (darwin-aarch64, macos-15, aarch64-apple-darwin) (push) Successful in 3m55s
    release / build (darwin-x86_64, macos-15, x86_64-apple-darwin) (push) Successful in 3m44s
    release / publish (push) Successful in 20s
    release / guard (push) Has been skipped
    ci / check (push) Successful in 11m6s
    ci / live-e2e (push) Successful in 2m8s
    ci / coverage (push) Successful in 2m30s
    Stable

    stephen released this 2026-09-13 18:32:57 +00:00 | 12 commits to main since this release

    Signed by stephen
    GPG key ID: 67F885778E01BF16

    Added

    • fj stack ship restacks the rest of the stack after a squash land. A
      squash merge replaces the item's commits, so every branch above it was left
      parented on history the trunk no longer had: the PRs above showed the landed
      diff a second time and the stack had to be repaired by hand. Ship now replays
      the remainder onto the new trunk tip with a single
      git rebase --onto <new trunk> <landed tip> <top branch> --update-refs (the
      plumbing stack absorb already used), force-pushes each branch with an
      explicit --force-with-lease, and re-syncs the PR bases. A conflicting
      replay rolls the rebase back and force-pushes nothing, then ship stops with
      the commands to finish by hand. The PR directly above the landed item is
      still re-pointed at the trunk, because the base branch it was targeting has
      merged away and leaving it aimed at a deleted branch would be worse than the
      no-restack end state. Merge-commit lands are unaffected, since the original
      commits stay reachable. --no-restack opts out. The replay needs git 2.38 or
      newer.
    • fj stack ship pre-flights the stack's shape in git. Before merging
      anything it verifies that each unmerged branch descends from the one below
      it, that the lowest one contains the trunk's fetched tip, and that each
      local branch tip matches the head of the PR that would merge. A stale or
      non-linear stack is refused up front with a "restack, then fj stack sync"
      message rather than failing mid-merge with the bottom already landed.
    • fj stack ship re-validates between merges. After an item lands, the
      next one is re-checked (still open, still mergeable, still approved, still at
      the head fj most recently put there) before it is merged. If the ground
      shifted, ship stops cleanly, reports what already landed, and leaves the rest
      for a re-run. Checks that ship itself re-armed by force-pushing a restacked
      branch are not re-gated. A forge still serving the pre-restack head is a
      lagging read rather than a shift, so ship re-reads a few times; if it never
      catches up, that is a stop in its own right, since every field on that
      snapshot describes a shape the restack has already replaced.
    • stack ship --json now reports the branches restacked after each item
      (restacked), and stack review --json carries each PR's head sha
      (head_sha). Both fields are additive.
    • fj run logs prints Actions job logs. The singular fj run log stays as
      an alias, so existing invocations keep working. A personal access token is
      enough for log reads on this Forgejo build; the auth docs no longer claim a
      browser session cookie is required. A session is still needed for the
      cookie-gated web routes, run watch, run rerun, and run cancel.
    • fj instances lists the Fjord Commons. fj read only
      /me/forge-instances, which by design excludes the Commons, so there was no
      instance id to address and no way to reach it. The Commons is now read
      alongside your own instances and routed through the platform gateway, with no
      personal access token anywhere: the gateway mints the forge token server-side
      and nothing minted reaches the client. A Commons you have not joined is
      withheld from instances use but still named in fj instances, on a dimmed
      line, so "not joined" does not read as "no such forge".
    • Host-scoped FJ_TOKEN_<HOST> and FJ_SESSION_<HOST>. The host suffix is
      H followed by the uppercase hex of the host bytes, so rasterhub.com is
      FJ_TOKEN_H7261737465726875622E636F6D. The encoding is injective, so
      foo-bar.com and foo.bar.com cannot collide onto one variable, and CI can
      set a credential for one host without enumerating the rest.
    • fj pr merge --style fast-forward-only. Forgejo's fast-forward-only
      merge style is accepted both as an explicit --style and as a repository's
      default_merge_style, which previously failed as unsupported. A PR that is
      not a fast-forward onto its base is reported as such, with the rebase to do,
      instead of a raw forge error.

    Changed

    • fj pr merge follows the repository's default merge style. An omitted
      --style resolved to merge regardless of how the repository was
      configured, so merging from the CLI could produce a merge commit on a repo
      whose web UI squashes. It now reads the repository's default_merge_style,
      and fails explicitly if that cannot be read or is not a style fj supports
      rather than falling back to a guess. Scripts that relied on the implicit
      merge need --style merge spelled out.
      Merge-style is also reported in
      push-rejection diagnostics, so a signed-commit protection failure is no
      longer misread as something else.
    • A generic FJ_TOKEN is no longer sent to a host it was not meant for.
      It used to be picked up for any host. It is now accepted for the default or
      current host, including when you spell that same host with --host, and
      ignored when --host names a different configured host. Use
      FJ_TOKEN_<HOST> for that case. FJ_SESSION follows the same rule.
    • The default Fjord platform URL is https://fjord.sh, replacing the old
      preview URL. An HTML response on a platform call now names --platform-url
      instead of surfacing an opaque JSON decode error.
    • Global --host reaches the auth subcommands. fj auth status, token,
      refresh, session, logout, and setup-git now honor it, so
      fj auth status --host X reports host X alone.

    Fixed

    • cargo test --all now passes on macOS. Four defects kept it from
      finishing there, and each one was a test reading the developer's real machine
      instead of its own scratch state. ProjectDirs ignores XDG_CONFIG_HOME on
      macOS, so tests that thought they had redirected the config directory were
      loading the real hosts.toml; when that named a host whose token lives in the
      login keychain, the lookup then blocked forever in securityd, because a
      keychain item's ACL names the binaries allowed to read it and a freshly built
      test binary is not one of them. FJ_CONFIG_DIR and FJ_NO_KEYCHAIN give
      those tests, and any scripted run that wants its own state, a way to say so.
      The editor test hardcoded /bin/false, which exists on Linux but not on
      macOS, so it asserted on a spawn failure rather than the nonzero exit it was
      written for. And the loopback sign-in test drove real sockets on a paused
      clock, which cannot work: tokio's auto-advance does not wait for socket
      readiness, so it jumped the clock past the five-minute callback deadline while
      the silent connection was still in the accept queue. That one now runs on the
      real clock with the request-line bound shortened for the test; the shipped
      ten-second bound is unchanged.

    • The pager no longer swallows short, write, and machine output. The
      decision to page was made per top-level command family, so every subcommand
      under repo, issue, pr, release, label, milestone, tag, run,
      search, status, and api was piped into less, including one-line write
      confirmations (pr merge, issue close, repo star, tag create, ...),
      fixed status blocks (fj status, fj pr status), short reads (tag view,
      milestone view, repo topics), and every --json and --web invocation.
      Destructive commands that prompt for confirmation (repo delete,
      release delete, tag delete) were prompting on a stdout already redirected
      into the pager. Paging is now decided per subcommand by
      cli::paging::should_page, which pages only genuinely long read-only views:
      list and view commands, pr diff / commits / files / checks,
      run log, search, and fj api GET dumps. --no-pager, FJ_NO_PAGER,
      FJ_PAGER / PAGER, and the non-TTY check are unchanged.

    • fj pr merge no longer discards the pull request description. With no
      --message, fj sent an empty merge-commit body, and Forgejo does not fall
      back to the description the way its web merge dialog does. Every pull request
      merged through the CLI therefore landed with its body gone, silently: the
      merge succeeded and the commit looked normal. The message now defaults to the
      pull request body. An explicit --message still wins, and --message ""
      still produces a deliberately empty body.

    • Ctrl+C works when fj is blocked in a synchronous call. SIGINT was raced
      against the command future, so it only took effect while that future was
      still pollable. Several paths parked the thread inside a blocking syscall for
      an unbounded time, and because tokio had already replaced SIGINT's default
      disposition the signal did not terminate the process either: Ctrl+C was not
      slow, it was inert. The two confirmed states were an OS keychain read waiting
      on an authorization dialog it cannot present (over ssh, in CI, from a git
      hook, which is what deadlocked the pre-push hook) and the synchronous stdin
      reads behind --body -, --input -, --with-token, and the interactive
      Fjord Account prompts. A backstop now gives the graceful path 250 ms to
      unwind and then exits 130. A child that owns the terminal's foreground
      process group still gets to decide what SIGINT means, so less absorbing it
      is unaffected.

    • Ctrl+C exits during an OIDC sign-in. fj auth login waited for the
      loopback redirect on a blocking task, so an interrupted sign-in printed
      error: interrupted and left the process alive. The wait is now async and
      actually cancellable. A stalled connection no longer wedges the sign-in
      either: a socket that connected and sent nothing (a browser preconnect, a
      port scanner) blocked the accept loop forever and the real redirect behind it
      was never served. Request lines are now bounded at 10s, and the overall 300s
      budget also covers connection handling.

    • FJ_TOKEN no longer blocks fj auth login --fjord. The variable was
      bound to --token by clap, so having one set forced the token flow even when
      --fjord was spelled out. Explicit --fjord now wins. FJ_SESSION was
      bound to fj auth session --cookie the same way, where it conflicted with
      --clear; that binding is gone too.

    • fj pr checks handles a head with no reported checks. Forgejo returns
      statuses: null there, which failed to decode. It now prints
      no checks reported on <sha> and exits 0; --json still emits a normal
      combined-status object with statuses: [].

    • fj run view --log reads logs on Forgejo 16. Logs are resolved through
      the run-jobs and job-log APIs, and displayed run numbers are mapped to API
      run ids first, including for runs past the first page. FJ_TOKEN precedence
      is preserved, so an env-token-only environment does not touch an unreadable
      token store. A Forgejo 15.x host, which does not expose the
      token-authenticated job log API, is now reported as such by version rather
      than as a parse error.

    • fj run view --log no longer downloads every task in the repository on
      Forgejo 16.
      The native job-log path fetched the workflow task list first
      and discarded it, and on 16 that endpoint ignores its run filter: about
      3 MB and 30 seconds on every log read against a busy repository. The task
      list is now loaded only by the Loki fallback, which is the one path that
      needs it, and that fallback also decodes the workflow_runs envelope 16
      returns instead of aborting on an unexpected response shape.

    • fj pr list and fj pr view decode Forgejo's system actors. Forgejo
      uses negative sentinel user ids (-1 for Ghost, -2 for forgejo-actions),
      and the shared User model read id as unsigned, so any repository with a
      system actor as reviewer, assignee, poster, or author failed the whole decode
      with invalid value: integer -2, expected u64. The field is signed now,
      which covers every actor position at once.

    • A failed list decode names where it failed. List responses are decoded
      through serde_path_to_error, so the message carries the endpoint and the
      JSON field path (for example GET /api/v1/repos/o/r/pulls?... and
      field [0].id) instead of only the type error.

    • Tag releases publish their assets, and the generated Homebrew formula
      installs.
      The v0.4.1 release was published with zero assets, so
      brew install fj served 0.3.0 from 22 to 30 July: one build leg failed, and
      re-running could not recover because artifact keys are scoped by run id, so
      the legs that had already uploaded hit "artifact already exists" and the
      publish job never ran. The assets were uploaded on 30 July by the recovery
      dispatch these changes made possible, and the tap has served 0.4.1 since.
      Artifacts are overwritten on a re-run now, and a release can be re-run by
      workflow_dispatch with a tag input, from a branch carrying a fix, without
      moving the tag. The formula could not have installed even if publish had run:
      it did cd fj-<version>-<target>, but Homebrew has already entered the
      tarball's top-level directory and that directory keeps the tag's v prefix,
      which the formula's version drops, so brew install ended in
      Errno::ENOENT: chdir_path. Three failures that used to pass quietly now
      stop the run: a missing matrix leg rendering sha256 "", an asset glob
      matching nothing, and a missing CHANGELOG section for the tag (release
      bodies were always empty, and are now taken from that section). A dispatched
      tag also reaches the shell as data rather than as a substituted expression,
      must exist in the repository's tags, and is checked out as refs/tags/<tag>
      with the resolved sha asserted in every job, so a branch sharing a
      version-shaped name cannot be built and published as if it were the tag.

    Downloads
  • v0.4.1 62d4d7f670

    v0.4.1
    Some checks failed
    ci / check (push) Successful in 10m25s
    ci / coverage (push) Successful in 1m41s
    ci / live-e2e (push) Successful in 1m58s
    release / build (catthehacker/ubuntu:act-22.04, linux-x86_64, docker, x86_64-unknown-linux-gnu) (push) Failing after 1m44s
    release / build (darwin-aarch64, macos-15, aarch64-apple-darwin) (push) Successful in 3m49s
    release / build (darwin-x86_64, macos-15, x86_64-apple-darwin) (push) Failing after 3m40s
    release / publish (push) Has been skipped
    Stable

    stephen released this 2026-07-22 20:19:49 +00:00 | 47 commits to main since this release

    Fixed

    • Silent Fjord Account session refresh across every command. An expired
      Fjord access token is now renewed transparently at the shared HTTP client: on
      a 401 from a host signed with a Fjord bearer, fj refreshes via the stored
      refresh token and retries the request once, so fj api, fj pr, fj issue,
      fj repo, and the Actions log routes no longer fail with "sign in again" once
      the short-lived token lapses. Previously only fj instances and fj auth
      refreshed. PAT hosts (whose tokens are not refreshable) and the case with no
      usable refresh token surface the original 401 unchanged, and the refresh is
      bounded to one attempt per request so a persistently-401 endpoint cannot loop.
    Downloads
  • v0.3.0 e81052ecd1

    v0.3.0
    All checks were successful
    release / build (catthehacker/ubuntu:act-22.04, linux-x86_64, docker, x86_64-unknown-linux-gnu) (push) Successful in 2m35s
    release / build (darwin-aarch64, macos-15, aarch64-apple-darwin) (push) Successful in 3m54s
    release / build (darwin-x86_64, macos-15, x86_64-apple-darwin) (push) Successful in 3m53s
    release / publish (push) Successful in 15s
    ci / check (push) Successful in 10m18s
    ci / coverage (push) Successful in 1m53s
    ci / live-e2e (push) Successful in 1m53s
    Stable

    stephen released this 2026-06-29 22:45:47 +00:00 | 60 commits to main since this release

    Signed by stephen
    SSH key fingerprint: SHA256:eja0m2LEwLSHxRnbQQg/d3CUnVWjxpjuMHSz6S/HHAM
    Downloads
  • v0.2.0 335deac982

    v0.2.0
    All checks were successful
    release / build (catthehacker/ubuntu:act-22.04, linux-x86_64, docker, x86_64-unknown-linux-gnu) (push) Successful in 1m28s
    ci / check (push) Successful in 1m50s
    ci / coverage (push) Successful in 2m0s
    release / build (darwin-aarch64, macos-15, aarch64-apple-darwin) (push) Successful in 3m49s
    release / build (darwin-x86_64, macos-15, x86_64-apple-darwin) (push) Successful in 3m40s
    release / publish (push) Successful in 16s
    Stable

    stephen released this 2026-06-02 22:58:18 +00:00 | 137 commits to main since this release

    Signed by stephen
    GPG key ID: 67F885778E01BF16
    Downloads
  • v0.1.3 0d218188dd

    v0.1.3
    Some checks failed
    release / build (rust:1.95-bookworm, linux-x86_64, docker, x86_64-unknown-linux-gnu) (push) Failing after 1m12s
    release / build (darwin-aarch64, macos, aarch64-apple-darwin) (push) Has been cancelled
    release / build (darwin-x86_64, macos, x86_64-apple-darwin) (push) Has been cancelled
    release / publish (push) Has been cancelled
    Stable

    stephen released this 2026-05-14 21:31:13 +00:00 | 196 commits to main since this release

    Signed by stephen
    GPG key ID: 67F885778E01BF16

    What's new

    • Linux x86_64 binary ships in this release. fj-v0.1.3-linux-x86_64.tar.gz is a statically-linked (against glibc ≥ 3.2) ELF that runs on Debian, Ubuntu, Arch, Fedora, NixOS, and most other modern distros.
    • Notarization scripts land for the next release. scripts/notarize.sh submits a signed darwin tarball to Apple's notary service; scripts/sign.sh --notarize chains signing → notarization in one step. The v0.1.3 macOS binaries are signed but not yet notarized — same signing identity as v0.1.2, so no new keychain prompt.
    • Windows claim removed from FAQ. Replaced with an honest "nobody's tried" + link to file an issue. The code still has #[cfg(windows)] stubs but ships no binary.

    Install

    # macOS via Homebrew:
    brew tap rasterandstate/tap
    brew upgrade fj
    
    # Linux:
    curl -fsSL https://rasterhub.com/rasterstate/fj/releases/download/v0.1.3/fj-v0.1.3-linux-x86_64.tar.gz | tar -xz
    sudo mv fj-v0.1.3-linux-x86_64/fj /usr/local/bin/fj
    fj --version
    

    Or download a tarball below. Verify with shasum -a 256 -c SHA256SUMS.

    Build it yourself

    cargo install --git https://rasterhub.com/rasterstate/fj --tag v0.1.3
    

    Full changelog: CHANGELOG.md.

    Downloads
  • v0.1.2 9bcd54d7a3

    v0.1.2
    Some checks failed
    release / build (rust:1.95-bookworm, linux-x86_64, docker, x86_64-unknown-linux-gnu) (push) Failing after 12m6s
    release / build (darwin-aarch64, macos, aarch64-apple-darwin) (push) Has been cancelled
    release / build (darwin-x86_64, macos, x86_64-apple-darwin) (push) Has been cancelled
    release / publish (push) Has been cancelled
    Stable

    stephen released this 2026-05-14 21:31:13 +00:00 | 197 commits to main since this release

    Signed by stephen
    GPG key ID: 67F885778E01BF16

    What's new

    Signed macOS binaries. Both darwin-aarch64 and darwin-x86_64 tarballs ship binaries signed with the Raster & State Developer ID (62Y3FRM8PD, hardened runtime enabled).

    The visible win: macOS keychain ACLs are scoped to code identity, not binary path. Now that releases share a stable signing identity, you click "Always Allow" on the keychain prompt once and don't get re-prompted on every fj upgrade.

    Notarization is a follow-up. Signing alone solves the prompt-storm; notarization is what makes Gatekeeper not warn on first run for binaries downloaded fresh.

    Also

    • scripts/sign.sh ships in-tree so contributors who build from source can sign their local binary and stop getting prompted. Reads FJ_APPLE_DEVELOPER_ID from .env / .env.local.
    • scripts/preflight.sh audits .env* files for secrets and grep-scans the working tree for PEM markers / Apple-password format / common token shapes. Wired into the pre-push hook.
    • .env.example documents the release/signing variable names. Real values stay out of the repo.
    • support@rasterstate.com is now the canonical contact email; personal-email references removed from tracked files.

    Install

    brew tap rasterandstate/tap
    brew upgrade fj
    

    Or download a tarball below. Verify with shasum -a 256 -c SHA256SUMS.

    Full changelog: CHANGELOG.md.

    Downloads
  • v0.1.1 86e9d8b795

    v0.1.1
    Some checks failed
    ci / check (push) Has been cancelled
    release / build (rust:1.95-bookworm, linux-x86_64, docker, x86_64-unknown-linux-gnu) (push) Failing after 3s
    release / build (darwin-aarch64, macos, aarch64-apple-darwin) (push) Has been cancelled
    release / build (darwin-x86_64, macos, x86_64-apple-darwin) (push) Has been cancelled
    release / publish (push) Has been cancelled
    Stable

    stephen released this 2026-05-14 16:36:39 +00:00 | 200 commits to main since this release

    Signed by stephen
    SSH key fingerprint: SHA256:p38f5YjX8z3zNU1r8/Digt/9pRcXFv2JYwhvVelC/cE

    What's fixed

    • fj auth setup-git installed a broken credential helper. Git invokes the helper via sh -c '<helper> "$@"' sh <verb>, so sed 's/^/password=/' consumed the trailing get / store / erase verb as a filename. Every git push / git pull against the configured host failed with sed: get: No such file or directory. Resolved in b68fb98, tracked in #1.

      The new helper wraps the pipeline in a shell function:

      !f() { fj auth token --host <h> | sed 's/^/password=/'; }; f
      

      Three regression tests cover the structural shape, the verb-positional simulation, and a real sh -c invocation with a stub fj on PATH.

    Recovery

    If you installed v0.1.0 via Homebrew and ran fj auth setup-git, re-run after upgrading:

    brew upgrade fj
    fj auth setup-git --host <hostname>
    

    This overwrites the broken credential.https://<host>.helper git config value with the new function-wrapped form.

    Install

    brew tap rasterandstate/tap
    brew install fj
    

    Or download a tarball below and put fj on your PATH. Verify with shasum -a 256 -c SHA256SUMS.

    Downloads
  • v0.1.0 71e536ffd8

    v0.1.0
    Some checks failed
    ci / check (push) Waiting to run
    release / build (rust:1.95-bookworm, linux-x86_64, docker, x86_64-unknown-linux-gnu) (push) Failing after 1m13s
    release / build (darwin-aarch64, macos, aarch64-apple-darwin) (push) Has been cancelled
    release / build (darwin-x86_64, macos, x86_64-apple-darwin) (push) Has been cancelled
    release / publish (push) Has been cancelled
    Stable

    stephen released this 2026-05-13 22:16:54 +00:00 | 216 commits to main since this release

    First tagged release of fj, a CLI for Forgejo in the spirit of gh.

    See CHANGELOG.md for the full list of features.

    Install

    brew tap rasterandstate/tap
    brew install fj
    

    Or download a tarball below and put fj on your PATH.

    Verify

    shasum -a 256 -c SHA256SUMS
    
    Downloads