Skip to content
Open
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
84 changes: 84 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -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 ./...
51 changes: 51 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -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 }}
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
/trace
/bin/
/dist/
/data/
*.test
50 changes: 50 additions & 0 deletions .goreleaser.yml
Original file line number Diff line number Diff line change
@@ -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`.
74 changes: 74 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -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 (<https://github.com/GrayCodeAI>). 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/.<name>.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.
4 changes: 2 additions & 2 deletions ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand All @@ -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/<key>/...`. 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

Expand Down
39 changes: 39 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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 <https://github.com/GrayCodeAI/trace>. 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.
71 changes: 71 additions & 0 deletions CODE_OF_CONDUCT.md
Original file line number Diff line number Diff line change
@@ -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
<https://www.contributor-covenant.org/version/2/1/code_of_conduct.html>.
Loading
Loading