Search Apps Documentation Source Content File Folder Download Copy Actions Download State String Boolean Number Struct Map Slice Pointer Function Closure Reference Nil Package Type Interface Unknown

v0 source pure

Package forge is the domain engine of an on-chain software forge: repos, roles, an append-only reference log, issues ...

Readme View source

forge

The domain engine of an on-chain software forge: repos, roles, an append-only reference log, issues, change requests and reviews. Pure gno, no chain imports, no realm globals. The realm that wires it to gno.land is gno.land/r/moul/forge/v0.

What it stores, and what it refuses to store

It does not store code. Git objects stay in git, behind whatever mirror a repo declares (an https remote, an IPFS CID, a peer). On gno.land a realm write locks a storage deposit of 100ugnot per byte, so a 1 MB repository would cost about 100 GNOT to park on chain and more on every push. Anchoring is the only shape that survives contact with real repositories.

What it does store is the part a forge is actually trusted for, and that git alone does not authenticate:

  1. Which object a ref points at, in what order it got there, and who said so: an append-only, hash-chained reference log.
  2. Who may move which ref, and under what review policy.
  3. The social layer: issues, change requests and reviews bound to addresses rather than to platform accounts.
  4. The merge decision, recorded as one more entry in the same log.

The reference log

Every ref move appends a LogEntry: ref name, old object, new object, actor, block height, kind (create, update, force, delete, merge), an optional note, and a Digest committing to the previous entry's digest. Publish LogHead() anywhere off chain and the entire history of every ref becomes falsifiable. VerifyLog() recomputes the chain; a client should run the same computation over the values it read back, since a transparency log nobody verifies is just a log.

Moves are compare-and-swap:

1r.SetRef(actor, height, "refs/heads/main", expectedOID, newOID, "ship it")

expectedOID is the tip the caller last saw, empty to create the ref. A stale expectation returns ErrStaleRef instead of overwriting. That is git's --force-with-lease, except the lease is held by consensus rather than by the server you are pushing to. ForceSetRef skips the expectation, needs RoleMaintainer, and is permanently recorded as KindForce: a force-push is not forbidden here, it is made impossible to hide.

The chain has no objects, so it cannot check that a new tip descends from the old one, and this package does not pretend otherwise. Ordering, attribution and policy are on chain; ancestry is verified by a client that holds the repo. This is the same split as gittuf's reference state log, with the log moved out of the repository and into a place no maintainer can rewrite.

Roles

RoleNone < RoleReader < RoleWriter < RoleMaintainer < RoleAdmin < RoleOwner, totally ordered so every check is one comparison. Writers move refs, maintainers force and merge, admins manage members and policy. The last owner cannot be demoted. Anyone can open an issue or a change request without a role: the spam gate is that the author pays gas and locks the deposit for their own bytes.

Reviews that cannot go stale unnoticed

A Review names the object id it reviewed, not the change. Push a new head and every earlier approval stops counting, because it approved something that is no longer what would be merged. Nothing has to remember to dismiss it, and no setting can turn the behaviour off. Only a writer's approval counts toward RequiredApprovals; anyone else's review is signal, not authority. A request-changes verdict from a writer blocks the merge while it stands.

MergeChange is a compare-and-swap on the target ref plus a policy check, and it writes a KindMerge entry naming the change it came from.

API shape

Errors, never panics: this package is pure, so a realm turns an error into an abort (the only way to revert state in gno) and a test asserts on the value. Every collection is an avl.Tree, so every listing is ordered and paginatable, and no iteration walks unbounded state. The caller supplies the actor address and the block height, which is what makes the whole engine unit-testable with no chain at all.

1f := forge.New()
2r, _ := f.CreateRepo(alice, height, "moul/forge", "an on-chain forge", "")
3r.SetMember(alice, bob, forge.RoleMaintainer)
4r.SetRef(alice, height, "refs/heads/main", "", oid, "initial import")
5c, _ := r.OpenChange(carol, height, "title", "body", "", "refs/heads/feat", head, "refs/heads/main")
6r.ReviewChange(bob, height, c.ID, forge.VerdictApprove, "lgtm")
7r.MergeChange(bob, height, c.ID, oid, merged, "merge change 0")

Limits

Every stored string is bounded (see the Max* constants) because an unbounded field is an unbounded deposit. Ref names are a refs/-rooted subset of git-check-ref-format; object ids are 40 or 64 lowercase hex characters; repo ids are <namespace>/<name>, where the name is a lowercase slug and the namespace is either a slug (a claimed user name) or a bech32 address. The two shapes cannot collide: an address is 40 characters and a slug caps at 39.

This package validates the shape of a namespace and nothing else. Whether a caller may claim one is an ownership question that needs a chain, so it lives in the realm: a name must be held in r/sys/users, an address must be the caller's own.

One economic rule shows up in the API: deleting is privileged. On gno.land the storage-deposit refund goes to whoever frees the bytes, not to whoever paid for them, so an open delete path pays for vandalism. DeleteRef needs RoleMaintainer, and issues, comments and reviews have no delete at all.


Part of moul/gno-contracts — moul's versioned gno.land contracts. See the repository for the full catalog, build/test tooling, and usage.

Dependency graph:

gno.land/p/moul/forge/v0 dependency graph

⚠️ Disclaimer: provided as-is, without warranty; not security-audited. Full disclaimer: DISCLAIMER.

Overview

Package forge is the domain engine of an on-chain software forge: repos, roles, an append-only reference log, issues and change requests (pull requests), with no chain imports of its own.

What it does NOT do, on purpose: store blobs, trees or packfiles. Git objects stay wherever git already puts them (a mirror, an IPFS CID, a peer) and this package records what a forge is actually trusted for and what git alone does not authenticate:

  1. which object id a ref points at, in what order it got there, and who said so: an append-only, hash-chained reference log (the same shape as gittuf's reference state log, with consensus playing the notary);
  2. who is allowed to move which ref, and under what review policy;
  3. the social layer: issues, change requests, reviews: bound to addresses rather than to platform accounts;
  4. the merge decision itself, recorded as one more entry in the same log.

The chain cannot see the object graph, so it cannot verify that a new tip descends from the old one. It does not pretend to: every ref move is a compare-and-swap against the tip the caller expected (git's --force-with-lease, moved somewhere the forge operator cannot rewrite), any move that abandons that discipline is recorded as a force, and ancestry is checked by a client that has the objects. Ordering, attribution and policy are on chain; proof is local.

All state lives in avl trees so every listing is ordered and paginatable, and every mutation takes the actor and the block height from the caller: the package is pure, deterministic and unit-testable without a chain.

Live demo: gno.land/r/moul/forge/v0.

Constants 5

const KindCreate, KindUpdate, KindForce, KindDelete, KindMerge

1const (
2	KindCreate = "create" // a ref that did not exist now points somewhere
3	KindUpdate = "update" // compare-and-swap succeeded
4	KindForce  = "force"  // the tip was replaced without a matching expectation
5	KindDelete = "delete" // the ref is gone (the log is not)
6	KindMerge  = "merge"  // an update performed by merging a change request
7)
source

Log entry kinds.

const MaxRepoPartLen, MaxRefNameLen, MaxTitleLen, MaxBodyLen, MaxCommentLen, MaxDescLen, MaxNoteLen, MaxMirrorLen, MaxMirrors, MaxLabels, MaxLabelLen

 1const (
 2	MaxRepoPartLen = 39   // per side of "<namespace>/<name>"
 3	MaxRefNameLen  = 255  // git's own limit for a single ref name
 4	MaxTitleLen    = 200  // issue / change title
 5	MaxBodyLen     = 8192 // issue / change body
 6	MaxCommentLen  = 4096
 7	MaxDescLen     = 512
 8	MaxNoteLen     = 140 // single-line note attached to a log entry
 9	MaxMirrorLen   = 512
10	MaxMirrors     = 8
11	MaxLabels      = 10
12	MaxLabelLen    = 32
13)
source

Size caps. Every string the chain stores is bounded: an unbounded field is an unbounded storage deposit, and on gno.land the deposit is paid per byte by whoever writes it (100ugnot/byte at the time of writing).

const RoleNone, RoleReader, RoleWriter, RoleMaintainer, RoleAdmin, RoleOwner

1const (
2	RoleNone       Role = iota // not a member
3	RoleReader                 // explicit read (all repos are public in v0)
4	RoleWriter                 // move non-protected refs, update own changes
5	RoleMaintainer             // force-move refs, merge changes, triage issues
6	RoleAdmin                  // manage members and repo settings
7	RoleOwner                  // admin + transfer; at least one always exists
8)
source

Variables 1

var ErrInvalidRepoID, ErrInvalidRefName, ErrInvalidOID, ErrInvalidText, ErrInvalidRole, ErrInvalidVerdict, ErrInvalidMirror, ErrTooLong, ErrTooMany, ErrRepoExists, ErrRepoNotFound, ErrRepoArchived, ErrRefNotFound, ErrRefExists, ErrStaleRef, ErrUnauthorized, ErrIssueNotFound, ErrIssueClosed, ErrChangeNotFound, ErrChangeNotOpen, ErrSelfApproval, ErrNotEnoughApproval, ErrChangesRequested, ErrSameOID, ErrLastOwner

 1var (
 2	ErrInvalidRepoID     = errors.New("forge: invalid repo id")
 3	ErrInvalidRefName    = errors.New("forge: invalid ref name")
 4	ErrInvalidOID        = errors.New("forge: invalid object id")
 5	ErrInvalidText       = errors.New("forge: invalid text")
 6	ErrInvalidRole       = errors.New("forge: invalid role")
 7	ErrInvalidVerdict    = errors.New("forge: invalid review verdict")
 8	ErrInvalidMirror     = errors.New("forge: invalid mirror locator")
 9	ErrTooLong           = errors.New("forge: value too long")
10	ErrTooMany           = errors.New("forge: too many entries")
11	ErrRepoExists        = errors.New("forge: repo already exists")
12	ErrRepoNotFound      = errors.New("forge: repo not found")
13	ErrRepoArchived      = errors.New("forge: repo is archived")
14	ErrRefNotFound       = errors.New("forge: ref not found")
15	ErrRefExists         = errors.New("forge: ref already exists")
16	ErrStaleRef          = errors.New("forge: stale ref (compare-and-swap failed)")
17	ErrUnauthorized      = errors.New("forge: unauthorized")
18	ErrIssueNotFound     = errors.New("forge: issue not found")
19	ErrIssueClosed       = errors.New("forge: issue is closed")
20	ErrChangeNotFound    = errors.New("forge: change not found")
21	ErrChangeNotOpen     = errors.New("forge: change is not open")
22	ErrSelfApproval      = errors.New("forge: self-approval is not allowed")
23	ErrNotEnoughApproval = errors.New("forge: not enough approvals")
24	ErrChangesRequested  = errors.New("forge: changes requested by a reviewer")
25	ErrSameOID           = errors.New("forge: ref already points at that object")
26	ErrLastOwner         = errors.New("forge: cannot demote the last owner")
27)
source

Stable, machine-readable error values. Callers (realms, clients, indexers) should switch on these rather than on message text: a realm turns them into panics, and the panic string is the only thing a user sees.

Functions 12

func AddressNamespace

1func AddressNamespace(ns string) bool
source

AddressNamespace reports whether ns is shaped like a gno bech32 address, the namespace every account owns without registering anything. The realm still checks that it is the CALLER's address; this only says which of the two ownership rules applies.

func EntryDigest

1func EntryDigest(prev string, e *LogEntry) string
source

EntryDigest computes the chain digest of e given the previous entry's digest. It is exported so an off-chain verifier can recompute the chain byte for byte from the values it read back; the field order below is the wire format and must not change within a version.

func SplitRepoID

1func SplitRepoID(s string) (ns, name string, ok bool)
source

SplitRepoID splits "<namespace>/<name>" into its two halves. It does not validate either half; ok is false only when the id is not two slash-separated non-empty parts.

func ValidLabel

1func ValidLabel(s string) bool
source

ValidLabel reports whether s is an issue label.

func ValidLine

1func ValidLine(s string, max int) bool
source

ValidLine reports whether s is single-line text within max bytes. Used for titles and for log-entry notes, which are fields of the digest chain: a newline there would let one note impersonate two.

func ValidMirror

1func ValidMirror(s string) bool
source

ValidMirror reports whether s looks like a fetch locator. The chain does not resolve it: it only records where the maintainers say the objects are: so the check is a shape check, not a promise that anything is reachable.

func ValidOID

1func ValidOID(s string) bool
source

ValidOID reports whether s is a git object id: 40 (SHA-1) or 64 (SHA-256) lowercase hex characters. Case is fixed so the same object always has the same on-chain key and the same digest-chain input.

func ValidRefName

1func ValidRefName(s string) bool
source

ValidRefName reports whether s is a fully-qualified ref name this forge accepts: a "refs/"-rooted subset of git-check-ref-format(1).

Deliberately stricter than git: the name must be fully qualified, so there is never an ambiguity between "main" the branch and "main" the tag, and a client can map an on-chain name onto a local ref without a lookup table.

func ValidRepoID

1func ValidRepoID(s string) bool
source

ValidRepoID reports whether s is "<namespace>/<name>". The name is always a lowercase slug; the namespace is either a slug (a claimed user name) or a bech32 address (the caller's own). The two shapes cannot collide: an address is 40 characters and a slug caps at MaxRepoPartLen, which is 39.

This layer validates the SHAPE only. Whether the caller may claim a given namespace is an ownership question that needs a chain, so it belongs to the realm (see the realm's README).

func ValidText

1func ValidText(s string, max int) bool
source

ValidText reports whether s fits in max bytes and carries no control characters other than newline and tab. Render output is markdown served by gnoweb, so a stray control byte is a rendering bug for every reader forever.

func New

1func New() *Forge
source

New returns an empty forge.

func ParseRole

1func ParseRole(s string) (Role, error)
source

ParseRole is the inverse of Role.String.

Types 9

type Change

struct
 1type Change struct {
 2	ID         int64
 3	Title      string
 4	Body       string
 5	Author     address
 6	SourceRepo string // forge repo id, or a mirror locator; "" means this repo
 7	SourceRef  string
 8	HeadOID    string
 9	TargetRef  string
10	State      string
11	CreatedAt  int64
12	UpdatedAt  int64
13
14	MergedOID string // the object TargetRef moved to
15	MergedBy  address
16	MergedAt  int64
17
18	reviews     *avl.Tree // address string -> *Review (latest per reviewer)
19	comments    *avl.Tree // padded id -> *Comment
20	nextComment int64
21}
source

Change is a change request (a pull request): a claim that TargetRef should be moved to include HeadOID, plus the reviews of that claim.

Reviews are bound to the object id they reviewed, not to the change. Push a new head and every earlier approval stops counting: not by a policy toggle a maintainer can switch off, but because the approval names an object that is no longer what is being merged.

Methods on Change

func CommentCount

method on Change
1func (c *Change) CommentCount() int
source

CommentCount is the number of replies on the change.

func IterateComments

method on Change
1func (c *Change) IterateComments(offset, count int, cb func(*Comment) bool)
source

IterateComments walks replies oldest-first.

func IterateReviews

method on Change
1func (c *Change) IterateReviews(cb func(*Review) bool)
source

IterateReviews walks reviews in reviewer-address order.

func Review

method on Change
1func (c *Change) Review(a address) *Review
source

Review returns a reviewer's latest verdict, or nil.

func ReviewCount

method on Change
1func (c *Change) ReviewCount() int
source

ReviewCount is the number of reviewers who have weighed in (latest verdict per reviewer, on any head).

func Stale

method on Change
1func (c *Change) Stale(rv *Review) bool
source

Stale reports whether a review no longer applies to the change's head.

type Comment

struct
1type Comment struct {
2	ID        int64
3	Author    address
4	Body      string
5	CreatedAt int64
6}
source

Comment is one reply, on an issue or on a change request.

type Forge

struct
1type Forge struct {
2	repos *avl.Tree // "<namespace>/<name>" -> *Repo
3}
source

Forge is the top-level registry: repo id -> repo.

Methods on Forge

func CreateRepo

method on Forge
1func (f *Forge) CreateRepo(actor address, height int64, id, description, defaultRef string) (*Repo, error)
source

CreateRepo registers a repo owned by actor.

func Fork

method on Forge
1func (f *Forge) Fork(actor address, height int64, srcID, newID string) (*Repo, error)
source

Fork registers newID as a fork of srcID and copies the parent's current refs into the child's log, so the fork records exactly what it forked from. The objects are not copied: they never were on chain: so the child inherits the parent's mirrors as its initial fetch locators.

func HasRepo

method on Forge
1func (f *Forge) HasRepo(id string) bool
source

HasRepo reports whether the id is taken.

func IterateNamespace

method on Forge
1func (f *Forge) IterateNamespace(ns string, cb func(*Repo) bool)
source

IterateNamespace walks the repos of one namespace in id order.

func IterateRepos

method on Forge
1func (f *Forge) IterateRepos(offset, count int, cb func(*Repo) bool)
source

IterateRepos walks repos in id order, newest-last, and stops when cb returns true. offset/count page the walk; count <= 0 means "to the end".

func Repo

method on Forge
1func (f *Forge) Repo(id string) *Repo
source

Repo returns the repo, or nil.

func Size

method on Forge
1func (f *Forge) Size() int
source

Size is the number of repos.

type Issue

struct
 1type Issue struct {
 2	ID        int64
 3	Title     string
 4	Body      string
 5	Author    address
 6	Open      bool
 7	Labels    []string
 8	CreatedAt int64
 9	UpdatedAt int64
10
11	comments    *avl.Tree // padded id -> *Comment
12	nextComment int64
13}
source

Issue is a discussion thread bound to a repo. Anyone with an address may open one: the spam gate is not a moderator, it is that the author pays gas and locks the storage deposit for every byte they write.

Methods on Issue

func CommentCount

method on Issue
1func (i *Issue) CommentCount() int
source

CommentCount is the number of replies on the issue.

func IterateComments

method on Issue
1func (i *Issue) IterateComments(offset, count int, cb func(*Comment) bool)
source

IterateComments walks replies oldest-first.

type LogEntry

struct
 1type LogEntry struct {
 2	Seq      int64
 3	Ref      string
 4	OldOID   string // "" when the ref did not exist
 5	NewOID   string // "" on delete
 6	Actor    address
 7	Height   int64
 8	Kind     string
 9	ChangeID int64 // the merged change, 0 otherwise
10	Note     string
11	Digest   string // hex sha256 over the previous digest and this entry
12}
source

LogEntry is one link of the repo's reference log. The log is append-only and hash-chained: Digest commits to every earlier entry, so publishing a single digest (in a release note, a package manifest, a tweet) pins the entire history of every ref up to that point.

type Ref

struct
1type Ref struct {
2	Name      string
3	OID       string
4	UpdatedAt int64
5	UpdatedBy address
6}
source

Ref is the current state of one reference. The history of how it got here is in the repo log, which nothing can rewrite.

type Repo

struct
 1type Repo struct {
 2	ID          string // "<namespace>/<name>", immutable
 3	Description string
 4	DefaultRef  string // fully-qualified, e.g. "refs/heads/main"
 5	Mirrors     []string
 6	ParentID    string // fork lineage, "" for a root repo
 7	CreatedAt   int64  // block height
 8	Archived    bool
 9
10	// Merge policy.
11	RequiredApprovals int  // approvals needed to merge a change
12	AllowSelfApproval bool // may the change author's own approval count
13
14	members *avl.Tree // address string -> Role
15	refs    *avl.Tree // ref name -> *Ref
16	log     *avl.Tree // padded seq -> *LogEntry (append-only)
17	issues  *avl.Tree // padded id -> *Issue
18	changes *avl.Tree // padded id -> *Change
19
20	head       string // digest of the last log entry ("" when the log is empty)
21	nextSeq    int64
22	nextIssue  int64
23	nextChange int64
24}
source

Repo is one repository. Nothing here is the code: Mirrors says where the objects can be fetched, Refs says what the objects are supposed to be.

Methods on Repo

func Can

method on Repo
1func (r *Repo) Can(a address, min Role) bool
source

Can reports whether a holds at least the given role.

func Change

method on Repo
1func (r *Repo) Change(id int64) *Change
source

Change returns a change by id, or nil.

func CloseChange

method on Repo
1func (r *Repo) CloseChange(actor address, height, id int64) error
source

CloseChange withdraws or rejects a change. Author or maintainer.

func CommentChange

method on Repo
1func (r *Repo) CommentChange(actor address, height, id int64, body string) (*Comment, error)
source

CommentChange appends a reply to a change request.

func CommentIssue

method on Repo
1func (r *Repo) CommentIssue(actor address, height, id int64, body string) (*Comment, error)
source

CommentIssue appends a reply. Closed issues still take comments (closing is a triage state, not a gag); an archived repo takes none.

func CountApprovals

method on Repo
1func (r *Repo) CountApprovals(c *Change) int
source

CountApprovals counts approvals that still apply: cast by a writer or above, against the change's current head, and (unless the repo allows it) not the author's own.

func CountBlocking

method on Repo
1func (r *Repo) CountBlocking(c *Change) int
source

CountBlocking counts writers who requested changes on the current head.

func DeleteRef

method on Repo
1func (r *Repo) DeleteRef(actor address, height int64, name, expectedOID, note string) (*LogEntry, error)
source

DeleteRef removes a ref by compare-and-swap. The ref disappears from the current state; the log keeps every object it ever pointed at.

Maintainer-only for an economic reason as much as a safety one: on gno.land the storage-deposit refund goes to whoever frees the bytes, not to whoever paid for them (`receiver := caller` in the vm keeper's deposit path, gno master 2026-09-19), so an open delete path pays for vandalism.

func ForceSetRef

method on Repo
1func (r *Repo) ForceSetRef(actor address, height int64, name, newOID, note string) (*LogEntry, error)
source

ForceSetRef moves a ref without an expectation. It needs RoleMaintainer and is permanently recorded as KindForce: the point is not to forbid a force-push (sometimes it is the right call) but to make one impossible to hide.

func Issue

method on Repo
1func (r *Repo) Issue(id int64) *Issue
source

Issue returns an issue by id, or nil.

func IterateChanges

method on Repo
1func (r *Repo) IterateChanges(offset, count int, cb func(*Change) bool)
source

IterateChanges walks change requests newest-first.

func IterateIssues

method on Repo
1func (r *Repo) IterateIssues(offset, count int, cb func(*Issue) bool)
source

IterateIssues walks issues newest-first.

func IterateLog

method on Repo
1func (r *Repo) IterateLog(offset, count int, cb func(*LogEntry) bool)
source

IterateLog walks the log oldest-first.

func IterateLogReverse

method on Repo
1func (r *Repo) IterateLogReverse(offset, count int, cb func(*LogEntry) bool)
source

IterateLogReverse walks the log newest-first, which is what a UI wants.

func IterateMembers

method on Repo
1func (r *Repo) IterateMembers(cb func(addr string, role Role) bool)
source

IterateMembers walks members in address order.

func IterateRefs

method on Repo
1func (r *Repo) IterateRefs(cb func(*Ref) bool)
source

IterateRefs walks refs in name order.

func LogEntryAt

method on Repo
1func (r *Repo) LogEntryAt(seq int64) *LogEntry
source

LogEntryAt returns one entry by sequence number, or nil.

func LogHead

method on Repo
1func (r *Repo) LogHead() string
source

LogHead is the digest of the last entry, "" for an empty log. Pin this value anywhere off chain and the whole history becomes falsifiable.

func LogSize

method on Repo
1func (r *Repo) LogSize() int
source

LogSize is the number of entries ever appended.

func MemberCount

method on Repo
1func (r *Repo) MemberCount() int
source

MemberCount is the number of members with an explicit role.

func MergeChange

method on Repo
1func (r *Repo) MergeChange(actor address, height, id int64, expectedTargetOID, mergedOID, note string) (*LogEntry, error)
source

MergeChange moves TargetRef to mergedOID and records the move as one more entry in the reference log, tagged with the change it came from.

expectedTargetOID is a compare-and-swap on the target ref ("" when the ref does not exist yet): a change approved against one base cannot be merged onto a base that moved underneath it. mergedOID is computed off chain by whoever performs the merge: the chain records the claim, signed, ordered and attributed, and a client with the objects verifies that the result actually contains HeadOID.

func OpenChange

method on Repo
1func (r *Repo) OpenChange(actor address, height int64, title, body, sourceRepo, sourceRef, headOID, targetRef string) (*Change, error)
source

OpenChange files a change request. Permissionless, like an issue: the proposal costs its author gas and deposit, and costs a maintainer nothing until they choose to look.

func OpenChangeCount

method on Repo
1func (r *Repo) OpenChangeCount() int
source

OpenChangeCount counts change requests still open.

func OpenIssue

method on Repo
1func (r *Repo) OpenIssue(actor address, height int64, title, body string, labels []string) (*Issue, error)
source

OpenIssue files an issue. Permissionless by design.

func OpenIssueCount

method on Repo
1func (r *Repo) OpenIssueCount() int
source

OpenIssueCount counts issues still open.

func Ref

method on Repo
1func (r *Repo) Ref(name string) *Ref
source

Ref returns the current state of a ref, or nil.

func RefCount

method on Repo
1func (r *Repo) RefCount() int
source

Counts for rendering.

func ReviewChange

method on Repo
1func (r *Repo) ReviewChange(actor address, height, id int64, verdict, body string) error
source

ReviewChange records a verdict against the change's current head. Anyone may review; only a writer's approval counts toward the merge policy (see CountApprovals): an unprivileged review is signal, not authority.

func RoleOf

method on Repo
1func (r *Repo) RoleOf(a address) Role
source

RoleOf returns the member's role, RoleNone when not a member.

func SetArchived

method on Repo
1func (r *Repo) SetArchived(actor address, archived bool) error
source

SetArchived freezes (or unfreezes) the repo. An archived repo takes no mutation except unarchiving: the log stays readable forever.

func SetDefaultRef

method on Repo
1func (r *Repo) SetDefaultRef(actor address, name string) error
source

SetDefaultRef points the repo at another default branch.

func SetDescription

method on Repo
1func (r *Repo) SetDescription(actor address, description string) error
source

SetDescription updates the one-line description.

func SetIssueLabels

method on Repo
1func (r *Repo) SetIssueLabels(actor address, height, id int64, labels []string) error
source

SetIssueLabels replaces an issue's labels. Triage is a maintainer action.

func SetIssueOpen

method on Repo
1func (r *Repo) SetIssueOpen(actor address, height, id int64, open bool) error
source

SetIssueOpen closes or reopens an issue. The author can always close their own; maintainers can close anyone's.

func SetMember

method on Repo
1func (r *Repo) SetMember(actor address, target address, role Role) error
source

SetMember grants or revokes a role. Admins manage members; only an owner may mint another owner, and the last owner cannot be demoted: a repo with no owner is a repo nobody can ever unarchive.

func SetMirrors

method on Repo
1func (r *Repo) SetMirrors(actor address, mirrors []string) error
source

SetMirrors replaces the fetch locators. The first one is the canonical remote; the rest are fallbacks. The chain records them, it never fetches.

func SetPolicy

method on Repo
1func (r *Repo) SetPolicy(actor address, requiredApprovals int, allowSelfApproval bool) error
source

SetPolicy sets the merge policy: how many approvals a change needs, and whether the author's own approval counts.

func SetRef

method on Repo
1func (r *Repo) SetRef(actor address, height int64, name, expectedOID, newOID, note string) (*LogEntry, error)
source

SetRef moves a ref by compare-and-swap: expectedOID must be the tip the caller last saw ("" to create a ref that does not exist yet). This is git's --force-with-lease, except the lease is held by consensus rather than by the server you are pushing to, so a concurrent push cannot be silently lost and a rewritten history cannot be presented as if it had always been that way.

The chain cannot check that newOID descends from expectedOID: it has no objects. That check belongs to a client holding the repo, which is exactly why every move is recorded rather than merely applied.

func UpdateChangeHead

method on Repo
1func (r *Repo) UpdateChangeHead(actor address, height, id int64, headOID string) error
source

UpdateChangeHead repoints an open change at a new object. The author may always update their own; a writer may update anyone's (the "maintainer pushed a fixup" case).

func VerifyLog

method on Repo
1func (r *Repo) VerifyLog() (bool, int64)
source

VerifyLog recomputes the whole digest chain and reports the first entry whose digest does not follow from its predecessor. It should be impossible on a live chain: it is here because a transparency log nobody can verify is just a log, and a client should be running this against the values it read back.

type Review

struct
1type Review struct {
2	Reviewer address
3	Verdict  string
4	OID      string // the head the reviewer actually looked at
5	Body     string
6	Height   int64
7}
source

Review is one reviewer's verdict on one object id.

type Role

ident
1type Role int
source

Role is a repo-scoped capability level. Roles are totally ordered: every check is "at least this role", so there is one comparison to audit.

Methods on Role

func String

method on Role
1func (r Role) String() string
source

String renders the role as the lowercase token used by the realm API.

Imports 6

Source Files 14