diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..cd2ff5c --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,84 @@ +name: CI + +on: + push: + branches: [main] + pull_request: + branches: [main] + workflow_dispatch: + +permissions: + contents: read + +concurrency: + group: ci-${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +env: + # Pinned toolchain, matching the release workflow and sibling repositories. + GO_VERSION: "1.26.6" + # Keep this repository buildable regardless of any go.work file in a + # parent directory of a local multi-repository checkout. + GOWORK: "off" + +jobs: + quality: + name: tidy + fmt + build + vet + runs-on: ubuntu-latest + timeout-minutes: 15 + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 + with: + go-version: ${{ env.GO_VERSION }} + cache: true + - name: Module tidy is clean + run: | + go mod tidy + git diff --exit-code -- go.mod go.sum + - name: gofmt + run: test -z "$(gofmt -l .)" + - run: go build ./... + - run: go vet ./... + + vulncheck: + name: govulncheck + runs-on: ubuntu-latest + timeout-minutes: 15 + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 + with: + go-version: ${{ env.GO_VERSION }} + cache: true + - run: go run golang.org/x/vuln/cmd/govulncheck@v1.1.4 ./... + + test: + name: test (${{ matrix.os }}) + strategy: + fail-fast: false + matrix: + # macOS exercises the sandbox-exec runner; Linux the Docker path. + os: [ubuntu-latest, macos-latest] + runs-on: ${{ matrix.os }} + timeout-minutes: 30 + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 + with: + go-version: ${{ env.GO_VERSION }} + cache: true + # The suite starts real listeners and shells out to git and ssh. + - run: go test -count=1 -timeout=20m ./... + + race: + name: race + runs-on: ubuntu-latest + timeout-minutes: 40 + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 + with: + go-version: ${{ env.GO_VERSION }} + cache: true + - run: go test -race -count=1 -timeout=30m ./... diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..0c7a7ae --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,51 @@ +name: release + +# Builds Linux and macOS binaries for a pushed v* tag and publishes them with +# a SHA-256 checksums.txt on the GitHub release. Artifacts are not signed. + +on: + push: + tags: ["v*"] + +permissions: + contents: write + +concurrency: + group: release-${{ github.ref }} + cancel-in-progress: false + +env: + GO_VERSION: "1.26.6" + GOWORK: "off" + +jobs: + release: + runs-on: ubuntu-latest + timeout-minutes: 45 + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + fetch-depth: 0 + - uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 + with: + go-version: ${{ env.GO_VERSION }} + cache: true + - name: Tag matches VERSION + run: | + want="v$(head -n1 VERSION | tr -d '[:space:]')" + if [ "${GITHUB_REF_NAME}" != "${want}" ]; then + echo "tag ${GITHUB_REF_NAME} does not match VERSION (${want})" >&2 + exit 1 + fi + - name: Vet and test + run: | + go vet ./... + go test -count=1 -timeout=20m ./... + - name: Build and publish with GoReleaser + uses: goreleaser/goreleaser-action@1a80836c5c9d9e5755a25cb59ec6f45a3b5f41a8 # v7.2.1 + with: + distribution: goreleaser + version: "v2.17.0" + args: release --clean + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.gitignore b/.gitignore index 33fd474..c64113b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ /trace +/bin/ +/dist/ /data/ *.test diff --git a/.goreleaser.yml b/.goreleaser.yml new file mode 100644 index 0000000..049d342 --- /dev/null +++ b/.goreleaser.yml @@ -0,0 +1,50 @@ +# Release builds for Trace. Run by .github/workflows/release.yml on a v* tag. +# Trace uses POSIX file locking, so only Linux and macOS are built. +version: 2 +project_name: trace + +before: + hooks: + - go mod verify + +builds: + - id: trace + main: ./cmd/trace + binary: trace + env: + - CGO_ENABLED=0 + - GOWORK=off + goos: [linux, darwin] + goarch: [amd64, arm64] + flags: [-trimpath] + ldflags: + - -s -w -X main.Version={{.Version}} -X main.Commit={{.ShortCommit}} -X main.BuildDate={{.Date}} + mod_timestamp: "{{ .CommitTimestamp }}" + +archives: + - id: default + formats: [tar.gz] + name_template: "{{ .ProjectName }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}" + files: + - README.md + - LICENSE + - CHANGELOG.md + - SECURITY.md + +checksum: + name_template: checksums.txt + algorithm: sha256 + +changelog: + disable: true + +release: + github: + owner: GrayCodeAI + name: trace + prerelease: auto + footer: | + Trace is pre-1.0 alpha software. See CHANGELOG.md for what changed. + + These binaries are not signed. Verify a download against `checksums.txt` + (SHA-256), for example `sha256sum --ignore-missing -c checksums.txt`. diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..7b67cb0 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,74 @@ +# AGENTS.md + +Guidance for anyone, human or coding agent, changing this repository. +This file is the only agent-instruction file here: do not add `CLAUDE.md`, +`GEMINI.md`, `.cursorrules`, or similar files, and delete them if a tool +generates one. Put shared guidance in this file instead. + +## What Trace is + +Trace is a self-hosted Git forge for small teams with signed agent history, +part of the GrayCode tools (). It is pre-1.0 +alpha software. One Go binary (`cmd/trace`, package `main`) serves the web +UI, the JSON API, Git smart HTTP and SSH, and runs the local CI runner. + +## Layout + +- `cmd/trace/*.go`: all code, grouped by feature (`actions.go`, `oidc.go`, + `ssh.go`, and so on), with tests beside it in `*_test.go`. +- `cmd/trace/*.html`, `cmd/trace/assets/`: embedded templates and assets. +- Runtime state lives in a data directory (default `./data`, ignored by + Git): bare repositories in `repos/OWNER/NAME.git`, JSON stores such as + `users.json` and `issues.json`, the append-only `audit.jsonl`, and + per-repository directories such as `lfs/OWNER/NAME`. +- README.md (operator guide), ARCHITECTURE.md (design and security + boundary), FEATURES.md (capability matrix): keep them true. + +## Commands + +Run Go with `GOWORK=off` (the Makefile sets it). + +```sh +make build # bin/trace +make check # gofmt check, go vet, build, race tests (the local gate) +make test # tests without the race detector +make vulncheck # govulncheck (needs network) +``` + +The tests start local listeners and run real `git` and `ssh`; they need no +network and write only to `t.TempDir()`. + +## Code conventions + +- Standard library first. The only direct dependency is + `golang.org/x/crypto`; discuss any new dependency in an issue first. +- Format with gofmt. `go vet` must stay clean. +- JSON stores follow one pattern: take the store's `flock` on + `data/..lock`, load, change, write to a temporary file, fsync, + rename. Keep writes atomic and never rewrite `audit.jsonl`. +- Per-repository files live under `data/KIND/OWNER/NAME` so a transfer can + move them and a delete can remove them. +- Run external programs with `exec.Command` and separate arguments; never + build a shell command line from user or repository input. Anything + written into a generated script (such as the pre-receive hook) must be + validated and quoted. +- Treat repository content (workflow files, HTML, pointers) as untrusted. + Authorization checks use `canRead`/`canWrite`/`canMaintain`, not raw + role strings. +- Never read, print, log, or commit secrets: the data directory's + `admin-token`, token hashes, `secrets.json`, `oidc.json`, or SSH and node + keys. Tests create their own throwaway data directories. + +## Changes and evidence + +- One concern per commit, [Conventional Commits](https://www.conventionalcommits.org/) + messages, branch from `main`, never force-push a shared branch. +- Every behaviour change needs a test; a bug fix needs a test that fails + without the fix. Do not weaken, skip, or delete tests to get a green run. +- Update the docs in the same change when behaviour changes. Describe only + what the code does and the tests verify; mark missing work as missing. +- Report verification exactly: the commands you ran and their real + results. Do not claim a check passed if you did not run it, and say so + when something could not be run (for example, a macOS-only path on + Linux). When citing facts, separate what you observed in code or output + from what a document states. diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index 49ed63b..6e91eef 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -6,7 +6,7 @@ Make a code forge that one developer can run on a VPS, while allowing repositori ## Current milestone -The Go process serves a public landing page and a private team and code browser. People sign in through a web form that sets a short-lived, signed session cookie. Git clients use HTTP Basic authentication with the same personal token, or optional key-authenticated SSH Git transport and basic Git LFS. The server checks per-repository read/write grants, creates bare repositories, and invokes `git http-backend` for Git smart HTTP. Admins can update `main` and tags; writers can push other branches. A managed `pre-receive` hook enforces this on the Git server. The web page shows branches, diffs, pull requests, issues, tag-backed releases, and bounded live code search; pull requests can be approved and fast-forward merged by an admin when their immutable refs are unchanged. Release archives are generated directly from the verified Git tag. A writer can manually trigger a local workflow stored in `.trace/workflow.json`; Trace persists run state, bounded logs, and artifacts. +The Go process serves a public landing page and a private team and code browser. People sign in through a web form that sets a short-lived, signed session cookie. Git clients use HTTP Basic authentication with the same personal token, or optional key-authenticated SSH Git transport and basic Git LFS. The server checks per-repository read/write grants, creates bare repositories, and invokes `git http-backend` for Git smart HTTP. Admins can update protected branches (`main` by default) and tags; writers can push other branches. A managed `pre-receive` hook enforces this on the Git server. The web page shows branches, diffs, pull requests, issues, tag-backed releases, and bounded live code search; pull requests can be approved and merged (fast-forward, squash, or an explicit merge commit) by an admin or maintainer when their immutable refs are unchanged and the repository's approval, check, and CODEOWNERS policy is met. Release archives are generated directly from the verified Git tag. A writer can manually trigger a local workflow stored in `.trace/workflow.json`; Trace persists run state, bounded logs, and artifacts. User records are a small JSON file with hashed random tokens. The server reloads it on each request so rotation and revocation take effect immediately. Browser mutations authenticate through the signed session cookie and per-user CSRF token; script clients can use the JSON API with HTTP Basic personal tokens. Mutations append an owner-only audit event and can dispatch a signed webhook; webhook delivery is retried three times and recorded locally. A second node can fetch branches and tags into a read-only mirror. The system has no central database. Each repository can be copied with standard Git tools. @@ -19,7 +19,7 @@ This provides independent copies of code, but one writable node is still authori 3. **Recovery and multiple writers.** Keep each writer's refs under a separate namespace, such as `refs/trace/writers//...`. A repository owner can promote a writer's branch after reviewing it. This avoids silently overwriting divergent branches. 4. **Portable collaboration records.** Store issues, patches, reviews, and comments as signed, versioned objects that peers can replicate. Keep the format documented and exportable without the web application. 5. **Portable agent history.** Signed, versioned JSON bundles move structured checkpoints between nodes with an explicit expected source ID and target commit checks. An administrator can explicitly publish the same signed snapshot to a protected Git ref in a private repository. Automatic per-commit records, full run capture, and cross-node session sync remain future work; publication stays opt-in so summaries are not exposed with public code. -6. **Operations.** Add isolated runners and queues, TLS deployment examples, backup and restore verification, metrics, Git LFS support, and release builds. +6. **Operations.** Backup verification, basic Git LFS, and checksummed release builds exist. Still missing: hardened isolated runners and distributed queues, TLS deployment examples, metrics, and LFS locking and garbage collection. ## Trust rules diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..9b15b59 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,39 @@ +# Changelog + +Notable changes to Trace are recorded here. The format follows +[Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and versions +follow [Semantic Versioning](https://semver.org/) (before 1.0, a minor +version may contain breaking changes). + +## [Unreleased] + +## [0.0.1] - not yet released + +First public version of Trace at . The +project was previously developed privately under the names MeshGit and +Refweave. + +### Added + +- Module path `github.com/GrayCodeAI/trace`, a `VERSION` file, and a + `trace version` command. +- Makefile targets for building, testing, vetting, formatting checks, and + vulnerability scanning. +- CI on Linux and macOS with a pinned Go toolchain, race tests, job + timeouts, and `govulncheck`. +- A tag-triggered release workflow that publishes Linux and macOS + binaries (amd64, arm64) with a SHA-256 `checksums.txt`. Binaries are not + signed. +- SECURITY.md, CONTRIBUTING.md, CODE_OF_CONDUCT.md, and AGENTS.md. + +### Security + +- Upgraded `golang.org/x/crypto` to v0.56.0 (GO-2026-6354, GO-2026-6355: + SSH channel denial of service). + +### Documentation + +- README, ARCHITECTURE, and FEATURES now match the code: pull-request + merges by admins and maintainers in the browser, the basic npm and PyPI + endpoints, the `maintain` grant in `trace user grant`, and a refreshed, + dated competitor comparison. diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..659f0af --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,71 @@ +# Code of conduct + +## Our pledge + +We, the maintainers and contributors of Trace, pledge to make participation +in this project a harassment-free experience for everyone, regardless of +age, body size, visible or invisible disability, ethnicity, sex +characteristics, gender identity and expression, level of experience, +education, socio-economic status, nationality, personal appearance, race, +religion, or sexual identity and orientation. + +We pledge to act and interact in ways that contribute to an open, +welcoming, diverse, inclusive, and healthy community. + +## Our standards + +Examples of behaviour that contributes to a positive environment: + +- showing empathy and kindness toward other people; +- being respectful of differing opinions, viewpoints, and experiences; +- giving and gracefully accepting constructive feedback; +- accepting responsibility, apologising to those affected by our mistakes, + and learning from the experience; +- focusing on what is best for the whole community. + +Examples of unacceptable behaviour: + +- sexualised language or imagery, and sexual attention or advances of any + kind; +- trolling, insulting or derogatory comments, and personal or political + attacks; +- public or private harassment; +- publishing others' private information, such as a physical or email + address, without their explicit permission; +- other conduct that could reasonably be considered inappropriate in a + professional setting. + +## Enforcement responsibilities + +Maintainers are responsible for clarifying and enforcing these standards +and will take appropriate and fair corrective action in response to any +behaviour they deem inappropriate, threatening, offensive, or harmful. They +may remove, edit, or reject comments, commits, code, issues, and other +contributions that do not align with this code of conduct. + +## Scope + +This code of conduct applies in all project spaces (issues, pull requests, +discussions, and other channels) and when someone officially represents +the project in public spaces. + +## Reporting + +Report unacceptable behaviour to the maintainers at +`hello@graycodeai.com`. All complaints will be reviewed and investigated +promptly and fairly, and the privacy and security of the reporter will be +respected. + +## Enforcement guidelines + +Maintainers follow these steps, depending on the impact of the behaviour: +a private written warning; a warning with consequences for continued +behaviour; a temporary ban from interaction with the project; or, for a +pattern of violations or serious harm, a permanent ban. + +## Attribution + +This code of conduct is adapted from the +[Contributor Covenant](https://www.contributor-covenant.org), version 2.1, +available at +. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..b5934ba --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,50 @@ +# Contributing to Trace + +Thanks for helping. Trace is a small, self-hosted Git forge for teams, part +of the [GrayCode](https://graycodeai.com) tools. It is pre-1.0 alpha +software, so behaviour and data formats can still change. + +## Before you start + +- For a security problem, follow [SECURITY.md](SECURITY.md) instead of + opening an issue. +- For anything larger than a small fix, open an issue first so we can agree + on the approach. +- Read [AGENTS.md](AGENTS.md): it describes the conventions for both people + and coding agents working in this repository. + +## Development setup + +You need Go (the version in `go.mod` or newer; CI uses the version pinned +in `.github/workflows/ci.yml`), Git, and an `ssh` client for the transport +tests. Trace builds for Linux and macOS. + +```sh +git clone https://github.com/GrayCodeAI/trace.git +cd trace +make build # bin/trace +make check # gofmt check, go vet, build, race tests +``` + +`make help` lists all targets. The test suite starts local listeners and +runs real `git` and `ssh` commands, so it takes a few minutes. + +## Pull requests + +- Branch from `main` and keep each pull request to one topic. +- Use [Conventional Commits](https://www.conventionalcommits.org/) for + commit messages and pull request titles (`fix(ssh): ...`, + `feat(actions): ...`, `docs: ...`). Mark breaking changes with `!` and a + `BREAKING CHANGE:` footer. +- Add or update tests for every behaviour change; a bug fix should come + with a test that fails without it. +- Update README.md, FEATURES.md, or ARCHITECTURE.md in the same pull + request when behaviour changes. Do not claim a capability that is not + implemented and tested. +- Run `make check` before pushing. CI runs the same checks plus tests on + macOS and `govulncheck`. + +## License + +By contributing, you agree that your contributions are licensed under the +[MIT License](LICENSE). diff --git a/FEATURES.md b/FEATURES.md index b123da1..0cb76d4 100644 --- a/FEATURES.md +++ b/FEATURES.md @@ -1,6 +1,6 @@ # Trace capability audit -This is the honest baseline for the current monorepo. “All features from the top 20 competitors” is not a finite implementation task: GitHub, GitLab, Forgejo, SourceHut, agent platforms, and CI vendors overlap only partially, and several features require separate infrastructure (runners, package registries, email, object storage, search, and an agent sandbox). Trace must ship a coherent product before it attempts parity. +This is the honest baseline for this repository. “All features from the top 20 competitors” is not a finite implementation task: GitHub, GitLab, Forgejo, SourceHut, agent platforms, and CI vendors overlap only partially, and several features require separate infrastructure (runners, package registries, email, object storage, search, and an agent sandbox). Trace must ship a coherent product before it attempts parity. ## Current evidence @@ -42,7 +42,7 @@ This is the honest baseline for the current monorepo. “All features from the t ## Competitor-derived scope -The comparison set is GitHub, GitLab, Bitbucket, Gitea, Forgejo, Codeberg, SourceHut, OneDev, Gogs, Gitness, RhodeCode, Phorge, Gerrit, Azure DevOps, AWS CodeCommit, Entire, Graphite, GitLab Duo, GitHub Copilot coding agent, and Sourcegraph Cody. These products do not expose one common feature set. Trace will measure itself against the following product slices: +The comparison set is GitHub, GitLab, Bitbucket, Gitea, Forgejo, Codeberg, SourceHut, OneDev, Gogs, Harness Open Source (formerly Gitness), RhodeCode, Phorge, Gerrit, Azure DevOps, AWS CodeCommit, Entire, Graphite, the GitLab Duo Agent Platform, GitHub's Copilot coding agent and agent control plane, and Sourcegraph (Cody, now supported on Sourcegraph Enterprise only, and its Amp agent). These products do not expose one common feature set. Trace will measure itself against the following product slices: 1. **Forge basics:** Git over HTTP and SSH, repository visibility, protected branches, pull requests, review comments, approvals, bounded CODEOWNERS rules, issues, labels, milestones, releases, webhooks, and an API. 2. **Team administration:** roles, teams, token lifecycle, 2FA, audit events, rate limits, backups, and repository transfer/deletion. @@ -51,28 +51,44 @@ The comparison set is GitHub, GitLab, Bitbucket, Gitea, Forgejo, Codeberg, Sourc 5. **Local-first and distributed operation:** portable on-disk data, signed manifests, peer sync, conflict-safe writer refs, restore verification, and explicit failover. 6. **Agent workflows:** opt-in session/checkpoint records linked to commits, agent identity, prompt/tool redaction, searchable context, and review gates. +### Agent features in other forges (sources checked 2026-09-26) + +| Product | Change | What Trace lacks | +| --- | --- | --- | +| GitHub | Agents tab in repositories ([2026-01-26](https://github.blog/changelog/2026-01-26-introducing-the-agents-tab-in-your-repository/)); enterprise agent control plane with an `actor_is_agent` audit field and a custom-agent definition API ([GA 2026-02-26](https://github.blog/changelog/2026-02-26-enterprise-ai-controls-agent-control-plane-now-generally-available/)) | Trace's audit log does not record whether an actor is an agent | +| GitLab | Duo Agent Platform GA with custom agents and external agents such as Claude Code and Codex CLI ([2026-01-15](https://about.gitlab.com/press/releases/2026-01-15-gitlab-announces-duo-agent-platform-general-availability/)) | No review gates specific to agent-authored changes | +| Forgejo | Project decision to prohibit AI-generated contributions ([March 2026 report](https://forgejo.org/2026-03-monthly-report/)); v15.0 LTS added ephemeral runners and repository-scoped tokens ([2026-04-16](https://forgejo.org/2026-04-release-v15-0/)) | No per-repository policy for AI-generated contributions | +| Gitea | 1.26.0 added Actions concurrency and a Terraform state registry, with no agent features ([2026-04-18](https://blog.gitea.com/release-of-1.26.0/)) | — | +| Graphite | Cursor Cloud Agents inside pull requests ([2026-03-02](https://graphite.com/blog)) | No pull-request handoff to a cloud agent | +| Sourcegraph | Cody is supported on Sourcegraph Enterprise only ([docs](https://sourcegraph.com/docs/cody)); the Amp agent is free with bring-your-own-key since 2026-09-13 ([news](https://ampcode.com/news)) | — | +| Harness | Gitness continues as Harness Open Source ([repository](https://github.com/harness/harness)) | — | + +Self-hosted forges (Forgejo, Gitea) had no agent features at those dates, so Trace's structured agent sessions remain a differentiator; the hosted forges' agent attribution and policy controls are the gaps above. + ## Remaining implementation order 1. Richer status reporting, CODEOWNERS pattern semantics, merge queues, and code-review ownership UX. 2. Distributed rate limiting, stronger account security, SSH host-key rotation/distribution, and isolated CI execution. 3. Hosted or sandboxed CI runners, language-complete package registries, and distributed artifact storage. 4. Multi-writer federation, automatic peer discovery, richer conflict reconciliation, and portable collaboration records. -5. Automatic Git-native agent checkpoints and sync, more agent adapters, richer redaction/search controls, and provider-complete SSO. +5. Automatic Git-native agent checkpoints and sync, more agent adapters, agent attribution in the audit log and a per-repository AI-contribution policy, richer redaction/search controls, and provider-complete SSO. Trace is **not feature-complete** against this matrix today. The implemented baseline is intentionally smaller and testable; each missing row needs its own design, tests, and operational story before it can be called done. -## Entire comparison (reviewed 2026-09-20) +## Entire comparison (reviewed 2026-09-20; adapters, storage, and mirrors updated from sources checked 2026-09-26) This is a capability comparison with [Entire's product page](https://entire.io/), not a claim of performance parity. Trace's landing page uses an original visual and its own product copy. | Entire product area | Trace today | Gap | | --- | --- | --- | -| Git hosting and regional mirrors | Standard Git HTTP and SSH on one writable node; signed, read-only peer mirrors | No measured speed claim, managed regional network, or automatic failover | +| Git hosting and regional mirrors | Standard Git HTTP and SSH on one writable node; signed, read-only peer mirrors | No measured speed claim, managed regional network (Entire previewed US, EU, and AU mirrors on 2026-07-08, [announcement](https://entire.io/news/entire-launches-distributed-git-network-for-the-agent-era)), or automatic failover | | CLI and browser UI | Both exist for core workflows | CLI parity across every web action and a consistently polished application UI are unfinished | | Agent sessions and checkpoints | Structured records linked to commits, portable opt-in signed JSON bundles, private Git refs with signed snapshots, and opt-in Codex/Claude Code/Gemini CLI/Cursor completion capture | No full conversation capture, verified agent authorship, automatic per-commit checkpoints, or automatic cross-node session sync | | Local privacy controls | Selected secret fields are redacted from stored session data | No general transcript scanner or configurable redaction policy | | Semantic graph | Not implemented | No graph construction, semantic retrieval, or verified token-saving measurement | | Agentic search | One literal query across code, selected-branch commits, and structured session summaries | No semantic ranking, raw-transcript search, or graph-based context retrieval | | Agent integrations | Codex, Claude Code, Gemini CLI, and Cursor completion notifications can queue observed HEAD metadata with idempotent replay | No full run capture, verified authorship, or other agent hooks; Cursor's adapter has been tested against its documented schema but not through a live Cursor session | +| Agent adapter coverage | 4 completion-hook adapters: Codex, Claude Code, Gemini CLI, Cursor | Entire's CLI lists 8 agents: Claude Code, Codex, Cursor, Copilot CLI, Antigravity, Pi (preview), OpenCode, and Factory AI Droid ([entireio/cli](https://github.com/entireio/cli)). Trace has no adapter for Copilot CLI, Antigravity, Pi, OpenCode, or Factory AI Droid | +| Checkpoint storage | Signed, per-node snapshots published to private refs under `refs/trace/agent-bundles/` | Entire stores checkpoints in Git refs under `refs/entire/checkpoints//` ([ref-based storage](https://entire.io/blog/introducing-ref-based-checkpoint-storage)); Trace cannot import Entire checkpoints | Do not market Trace as an Entire equivalent until these gaps have implementations and independent verification. The near-term product goal is a reliable self-hosted Git workspace for a small team, with explicit agent context and safe read-only replication. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..8aeb9d0 --- /dev/null +++ b/Makefile @@ -0,0 +1,53 @@ +# Trace — self-hosted Git forge for small teams (github.com/GrayCodeAI/trace). +# Run `make help` for the targets. Every target runs with GOWORK=off so a +# go.work file in a parent directory cannot change what is built. + +NAME := trace +MAIN_PKG := ./cmd/trace +export GOWORK := off + +VERSION ?= $(shell v=$$(head -n1 VERSION 2>/dev/null | tr -d '[:space:]'); if [ -n "$$v" ]; then echo "$$v"; else echo dev; fi) +COMMIT := $(shell git rev-parse --short HEAD 2>/dev/null || echo none) +DATE := $(shell date -u '+%Y-%m-%dT%H:%M:%SZ') +LDFLAGS := -s -w -X main.Version=$(VERSION) -X main.Commit=$(COMMIT) -X main.BuildDate=$(DATE) + +.PHONY: help build install test race vet fmt fmt-check tidy-check vulncheck check clean version + +help: ## List targets. + @grep -E '^[a-zA-Z_-]+:.*?## ' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf " %-12s %s\n", $$1, $$2}' + +build: ## Build bin/trace with version information. + CGO_ENABLED=0 go build -trimpath -ldflags="$(LDFLAGS)" -o bin/$(NAME) $(MAIN_PKG) + +install: ## Install trace into $(go env GOPATH)/bin. + CGO_ENABLED=0 go install -trimpath -ldflags="$(LDFLAGS)" $(MAIN_PKG) + +test: ## Run the test suite (starts local listeners, shells out to git and ssh). + go test -count=1 -timeout=20m ./... + +race: ## Run the test suite with the race detector. + go test -race -count=1 -timeout=30m ./... + +vet: ## Run go vet. + go vet ./... + +fmt: ## Format the source with gofmt. + gofmt -w . + +fmt-check: ## Fail if any file is not gofmt-formatted. + @out=$$(gofmt -l .); if [ -n "$$out" ]; then echo "gofmt needed:"; echo "$$out"; exit 1; fi + +tidy-check: ## Fail if go.mod or go.sum are not tidy. + go mod tidy + git diff --exit-code -- go.mod go.sum + +vulncheck: ## Scan reachable code for known vulnerabilities (needs network). + go run golang.org/x/vuln/cmd/govulncheck@v1.1.4 ./... + +check: fmt-check vet build race ## The local gate: formatting, vet, build, race tests. + +version: ## Print the version from VERSION. + @echo $(VERSION) + +clean: ## Remove build output. + rm -rf bin dist diff --git a/README.md b/README.md index 57ae092..cf72982 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,8 @@ # Trace -Trace is a self-hosted Git service for a small team. It keeps code in ordinary bare Git repositories, supports standard Git over HTTP(S), and can copy repositories to a read-only node on another VPS. The current design has one writable node per repository and manual failover. +Trace is a self-hosted Git forge for small teams with signed agent history, part of the [GrayCode](https://graycodeai.com) tools. It keeps code in ordinary bare Git repositories, supports standard Git over HTTP(S) and SSH, and can copy repositories to a read-only node on another VPS. The current design has one writable node per repository and manual failover. + +> **Status: pre-1.0 alpha.** Version 0.0.1 has not been released yet, and behaviour and data formats can still change. Build Trace from source as shown below. Read [SECURITY.md](SECURITY.md) before exposing a node, and report vulnerabilities privately as described there. ## Team features @@ -8,8 +10,8 @@ Trace is a self-hosted Git service for a small team. It keeps code in ordinary b - Admins can create repositories and manage users and access from the web page or CLI. - Repository grants are `read`, `write`, `maintain`, or `none`. Maintainers can merge reviewed pull requests and process the merge queue without becoming global administrators. Admins can access every repository. - Repositories can be public or private. Public repositories allow anonymous web browsing and Git clone/fetch; pushes and all write operations still require authentication. -- Writers can push branches. Only admins can update `main` or tags. A server-side Git hook enforces this rule. -- The web page lists branches, recent commits, files, file contents, and a branch's changes relative to `main`. Admins review there and merge with standard Git commands. +- Writers can push branches. Only admins can update protected branches (`main` by default; configurable per repository) or tags. A server-side Git hook enforces this rule. +- The web page lists branches, recent commits, files, file contents, and a branch's changes relative to `main`. Admins and maintainers review pull requests in the browser and merge them there (fast-forward, squash, or merge commit) or with standard Git commands. - The public home page introduces Trace; the private dashboard has its own sign-in form and a session cookie. Git clients continue to use HTTP Basic authentication. - Mirrors can serve clones but cannot accept pushes. Git history remains portable. @@ -39,14 +41,17 @@ For an entirely local operation, use the same board model without HTTP: ## Requirements -- Go 1.26 or newer to build. +- Go 1.26 or newer to build (CI and releases use Go 1.26.6). +- Linux or macOS. Trace relies on POSIX file locking and does not build for Windows. - Git installed on each node. - A TLS reverse proxy for use outside the machine. Trace listens on `127.0.0.1:8787` by default. Tokens must not cross the public internet without HTTPS. ## Start the main node ```sh -go build -o trace ./cmd/trace +git clone https://github.com/GrayCodeAI/trace.git && cd trace +go build -o trace ./cmd/trace # or: make build (writes bin/trace with version information) +./trace version ./trace init -data ./data ./trace repo create -data ./data team/project # or create a public repository @@ -340,7 +345,7 @@ Trace exposes the basic authenticated Git LFS batch protocol under `/lfs/OWNER/N ## Package artifacts -Trace also provides a generic immutable artifact registry. It is intentionally protocol-neutral; it does not claim npm, PyPI, or Maven compatibility yet: +Trace also provides a generic immutable artifact registry, plus the basic npm and PyPI endpoints described below. Neither endpoint is a complete npm or PyPI implementation, and Maven is not supported: ```sh ./trace package publish -data ./data team/project lib 1.0.0 lib-1.0.0.tgz @@ -555,10 +560,13 @@ The mirror is read-only, synchronization is scheduled externally, and failover i ## Develop ```sh -go test ./... -go vet ./... +make check # gofmt check, go vet, build, race tests +make test # tests without the race detector +make help # all targets ``` +See [CONTRIBUTING.md](CONTRIBUTING.md) for the workflow and [AGENTS.md](AGENTS.md) for repository conventions. Releases are built from `v*` tags by `.github/workflows/release.yml` for Linux and macOS (amd64 and arm64) with a SHA-256 `checksums.txt`; the binaries are not signed. No release has been published yet. + The integration tests verify authenticated Git push and clone, repository permissions, protected `main`, branch review, token rotation, mirror behavior, pull-request and issue lifecycles, webhook signing, release archives, and SSH key-authenticated Git transport. The web UI bundles the Inter typeface under the [SIL Open Font License](cmd/trace/assets/OFL-Inter.txt); it does not request fonts from a third-party server. diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000..d93d13e --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,51 @@ +# Security policy + +Trace is a self-hosted Git forge. It stores source code, access grants, +personal-token hashes, CI secrets, and an administrator token, so we treat +vulnerability reports as a priority. + +## Supported versions + +Trace is pre-1.0 alpha software. Only the latest release (and `main`) +receives security fixes. The current version is in [`VERSION`](VERSION). + +## Reporting a vulnerability + +Please do not open a public issue, pull request, or discussion for a +security problem. Report it privately through either channel: + +- GitHub private vulnerability reporting: + +- Email: `security@graycodeai.com` + +Include what you can of: + +- the affected component or route and the Trace version or commit; +- steps to reproduce, ideally a minimal proof of concept; +- the impact you expect (for example, which role is needed and what it + gains); +- any suggested fix. + +We aim to acknowledge a report within 3 business days and to agree on a +disclosure timeline with you once we have assessed it. Reporters are +credited in the advisory unless they prefer otherwise. Please give us a +reasonable chance to release a fix before disclosing publicly; we will not +pursue legal action against good-faith research that follows this policy. + +## Scope + +In scope: the `trace` binary and everything in this repository, including +the web UI, the JSON API, Git smart HTTP and SSH transports, the CI runner, +OIDC, SCIM, webhooks, federation, and the release workflow. + +Out of scope: vulnerabilities in Git, Docker, the operating system, or a +reverse proxy in front of Trace; findings that require an administrator to +attack their own node; and denial of service that needs more traffic than +the documented rate limits allow. + +## Operating Trace safely + +The README describes the security model. In short: keep the data directory +private (it holds `data/admin-token`, token hashes, and CI secrets), put a +TLS reverse proxy in front of Trace, and run untrusted repositories' CI only +with sandboxing. diff --git a/VERSION b/VERSION new file mode 100644 index 0000000..8acdd82 --- /dev/null +++ b/VERSION @@ -0,0 +1 @@ +0.0.1 diff --git a/cmd/trace/main.go b/cmd/trace/main.go index 925520c..0cbc532 100644 --- a/cmd/trace/main.go +++ b/cmd/trace/main.go @@ -39,6 +39,9 @@ func run(args []string) error { return errors.New("usage: trace (try -help after a command)") } switch args[0] { + case "version", "-version", "--version": + fmt.Println(versionString()) + return nil case "init": fs := flag.NewFlagSet("init", flag.ContinueOnError) data := fs.String("data", "./data", "data directory") diff --git a/cmd/trace/users.go b/cmd/trace/users.go index 1ae21c3..2f71556 100644 --- a/cmd/trace/users.go +++ b/cmd/trace/users.go @@ -346,7 +346,7 @@ func userCommand(args []string) error { fmt.Printf("created %s\nToken: %s\nShow this token to the user once over a private channel.\n", fs.Arg(0), token) case "grant": if fs.NArg() != 3 { - return errors.New("usage: trace user grant [-data DIR] USER OWNER/REPO ") + return errors.New("usage: trace user grant [-data DIR] USER OWNER/REPO ") } if err := s.grantUser(fs.Arg(0), fs.Arg(1), fs.Arg(2)); err != nil { return err diff --git a/cmd/trace/version.go b/cmd/trace/version.go new file mode 100644 index 0000000..b4fed5b --- /dev/null +++ b/cmd/trace/version.go @@ -0,0 +1,16 @@ +package main + +import "fmt" + +// Version, Commit, and BuildDate identify a build. Release builds set them +// with -ldflags "-X main.Version=... -X main.Commit=... -X main.BuildDate=..."; +// the values below are what a plain `go build` reports. +var ( + Version = "dev" + Commit = "none" + BuildDate = "unknown" +) + +func versionString() string { + return fmt.Sprintf("trace %s (commit %s, built %s)", Version, Commit, BuildDate) +} diff --git a/cmd/trace/version_test.go b/cmd/trace/version_test.go new file mode 100644 index 0000000..cc8b3de --- /dev/null +++ b/cmd/trace/version_test.go @@ -0,0 +1,24 @@ +package main + +import ( + "io" + "os" + "strings" + "testing" +) + +func TestVersionCommand(t *testing.T) { + reader, writer, err := os.Pipe() + if err != nil { + t.Fatal(err) + } + stdout := os.Stdout + os.Stdout = writer + runErr := run([]string{"version"}) + os.Stdout = stdout + writer.Close() + out, _ := io.ReadAll(reader) + if runErr != nil || !strings.HasPrefix(string(out), "trace dev (commit none") { + t.Fatalf("trace version printed %q (err %v)", out, runErr) + } +} diff --git a/go.mod b/go.mod index 58e3e4b..4e02c39 100644 --- a/go.mod +++ b/go.mod @@ -1,7 +1,7 @@ -module trace +module github.com/GrayCodeAI/trace -go 1.26 +go 1.26.0 -require golang.org/x/crypto v0.55.0 +require golang.org/x/crypto v0.56.0 require golang.org/x/sys v0.47.0 // indirect diff --git a/go.sum b/go.sum index d29e079..9952466 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,5 @@ -golang.org/x/crypto v0.55.0 h1:+KWHjbgOaAQ66dh/YlkZKHlz9ZUlq61AFirAR9ntP8M= -golang.org/x/crypto v0.55.0/go.mod h1:uq0V9dE/fzQuJtbnL+2EhWOE63vo164FY8xqEnV9xis= +golang.org/x/crypto v0.56.0 h1:GUh5Ii4J5jtcseSMiRqr1jXCNHoxjeV9Fmekc2oLy6Y= +golang.org/x/crypto v0.56.0/go.mod h1:OMW5y6CY9l38uPLmxU6l6pwcXp1obtLo3e6gT7gQR2I= golang.org/x/sys v0.47.0 h1:o7XGOvZQCADBQQ4Y7VNq2dRWQR7JmOUW8Kxx4ZsNgWs= golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= golang.org/x/term v0.45.0 h1:NwWyBmoJCbfTHpxrWoZ9C6/VxOf7ic219I8xZZFdrf0=