PathrockNetwork Gno Explorer
HomeBlocksTransactionsTokensRealmsPackagesValidatorsAnalytics

PathrockNetwork Gno Explorer — an independent explorer for Gno.land Mainnet (gnoland-1), operated by PathrockNetwork. Not an official Gno.land service.

gnowebarchive RPC

gno.land/r/moul/forge/v0

Realm
Open in gnoweb ↗

Overview

Kind
Realm (renderable)
Name
v0
Namespace
moul / forge
Files
7 (README)(gnomod.toml)
Exported functions
27
Module
gno.land/r/moul/forge/v0
gno
0.9

Files (7)

  • README.mdmarkdown
  • gnomod.tomltoml
  • forge.gnogno
  • render.gnogno
  • forge_test.gnogno
  • helpers_test.gnogno
  • render_test.gnogno
README.mdPreviewRaw
# forge

An on-chain software forge, browsable in gnoweb and writable only through signed
transactions. The domain model lives in
[`gno.land/p/moul/forge/v0`](../../../../p/moul/forge/v0); this realm is the
wiring, the routes and the events.

The code itself is not here. Git objects stay in git, behind whatever mirror a
repo declares. What the chain keeps is the part a forge is trusted for: which
object a branch points at, in what order, on whose authority, and what was
reviewed before it moved.

## Routes

| path | page |
| --- | --- |
| `/` | every repo, with counts |
| `/<ns>/<name>` | repo overview: refs, recent log, open changes and issues |
| `/<ns>/<name>/log` | the full reference log, paginated with `?page=N` |
| `/<ns>/<name>/issues` | issues, `?page=N` |
| `/<ns>/<name>/issues/<id>` | one issue and its replies |
| `/<ns>/<name>/changes` | change requests, `?page=N` |
| `/<ns>/<name>/changes/<id>` | one change, its reviews and its replies |
| `/help` | what the realm is and how to call it |

Every action link on those pages is a `txlink` into the matching function, so
the whole forge is usable from gnoweb without a client.

## Writing

```sh
gnokey maketx call -pkgpath gno.land/r/moul/forge/v0 \
  -func CreateRepo -args "moul/forge" -args "an on-chain forge" -args "" \
  -gas-fee 1000000ugnot -gas-wanted 3000000 \
  -broadcast -chainid <chain> -remote <rpc> <key>
```

```sh
# move a branch: the fourth argument is the tip you expect to replace
gnokey maketx call -pkgpath gno.land/r/moul/forge/v0 \
  -func SetRef -args "moul/forge" -args "refs/heads/main" \
  -args "<expected-oid>" -args "<new-oid>" -args "ship it" \
  -gas-fee 1000000ugnot -gas-wanted 3000000 \
  -broadcast -chainid <chain> -remote <rpc> <key>
```

A stale expectation aborts the transaction instead of overwriting the branch. A
move with no expectation is `ForceSetRef`, needs `maintainer`, and is recorded
as a force forever.

Arguments that would be slices are comma-separated strings (`SetMirrors`,
`OpenIssue` labels): a transaction can only carry strings, so a `[]string`
parameter would not be callable from gnokey.

## Reading

Read paths are free. Browse the routes above, or query:

```sh
gnokey query vm/qeval -data 'gno.land/r/moul/forge/v0.RefOID("moul/forge","refs/heads/main")' -remote <rpc>
gnokey query vm/qeval -data 'gno.land/r/moul/forge/v0.LogHead("moul/forge")' -remote <rpc>
```

`LogHead` is the digest of the last log entry, committing to every entry before
it. Pin it in a release note or a package manifest and the repo's whole history
becomes falsifiable by anyone who can read the chain.

## Namespaces

A repo id is `<namespace>/<name>`. The namespace is either a name the caller
holds in `r/sys/users` or the caller's own bech32 address, checked on every
`CreateRepo` and `Fork`:

- `g1.../forge` works for any account with nothing to register and nothing to
  lose, including a realm, whose address is its namespace. That is what makes a
  DAO-owned repo work.
- `moul/forge` requires the `moul` name to resolve to the caller, renames
  included, since the registry resolves aliases to the same record.

Nobody can claim a namespace they do not own, so there is no squatting to
arbitrate and no reservation list to maintain.

## Roles

`reader < writer < maintainer < admin < owner`. Writers move refs, maintainers
force and merge, admins manage members and the merge policy, and the last owner
cannot be demoted. Opening an issue or a change request needs no role at all:
the author pays gas and locks the storage deposit for their own bytes, which is
the spam gate.

A role can be held by **another realm**, because the realm resolves its caller
without requiring an end user. A repo whose owner is a DAO realm is a repo whose
merge button is a governance vote, which is the thing a centralized forge
structurally cannot offer.

## Events

Every mutation emits one: `RepoCreated`, `RepoForked`, `RepoUpdated`,
`MemberSet`, `RefLog`, `IssueOpened`, `IssueComment`, `IssueClosed`,
`IssueReopened`, `IssueLabeled`, `ChangeOpened`, `ChangeUpdated`,
`ChangeReviewed`, `ChangeComment`, `ChangeClosed`, `ChangeMerged`. `RefLog`
carries the sequence number and the chain digest, so an indexer can mirror the
log without replaying state.

## Costs

A realm write locks 100ugnot per byte, refundable when the data is deleted. A
log entry is roughly 200 bytes including the digest, so a push costs about
0.02 GNOT of deposit plus gas: the reason this design anchors refs instead of
storing objects. An issue with a 500-byte body is about 0.05 GNOT.

<!-- BEGIN GNOCONTRACTS FOOTER (generated by `make readmes`; do not edit below) -->

---

Part of **[moul/gno-contracts](https://github.com/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/r/moul/forge/v0 dependency graph](https://raw.githubusercontent.com/moul/gno-contracts/main/_assets/gno.land/r/moul/forge/v0/deps.png)

> ⚠️ **Disclaimer:** provided as-is, without warranty; not security-audited. Full disclaimer: [DISCLAIMER](https://github.com/moul/gno-contracts/blob/main/DISCLAIMER.md).

<!-- END GNOCONTRACTS FOOTER -->

Functions

  • CloseChange(cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, repoID string, changeID int64)

  • CloseIssue(cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, repoID string, issueID int64)

  • CommentChange(cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, repoID string, changeID int64, body string)

  • CommentIssue(cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, repoID string, issueID int64, body string)

  • CreateRepo(cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, id string, description string, defaultRef string)

  • DeleteRef(cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, repoID string, name string, expectedOID string, note string)

  • ForceSetRef(cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, repoID string, name string, newOID string, note string)

  • Fork(cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, srcID string, newID string)

  • HasRepo(repoID string) bool

  • LogHead(repoID string) string

  • MergeChange(cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, repoID string, changeID int64, expectedTargetOID string, mergedOID string, note string)

  • OpenChange(cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, repoID string, title string, body string, sourceRepo string, sourceRef string, headOID string, targetRef string) int64

  • OpenIssue(cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, repoID string, title string, body string, labels string) int64

  • RefOID(repoID string, name string) string

  • Render(path string) string

  • ReopenIssue(cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, repoID string, issueID int64)

  • RepoCount() int

  • ReviewChange(cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, repoID string, changeID int64, verdict string, body string)

  • SetArchived(cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, repoID string, archived bool)

  • SetDefaultRef(cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, repoID string, name string)

  • SetDescription(cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, repoID string, description string)

  • SetIssueLabels(cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, repoID string, issueID int64, labels string)

  • SetMember(cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, repoID string, member string, role string)

  • SetMirrors(cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, repoID string, mirrors string)

  • SetPolicy(cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, repoID string, requiredApprovals int, allowSelfApproval bool)

  • SetRef(cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, repoID string, name string, expectedOID string, newOID string, note string)

  • UpdateChangeHead(cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, repoID string, changeID int64, headOID string)

Signatures reconstructed verbatim from vm/qfuncs — interface params keep their inline definitions.

Rendered

RenderedRawgnoweb ↗

vm/qrender output, sanitized (docs/render-security.md) and displayed in an empty-sandbox iframe — scripts, forms and popups cannot run. Links stay inert in-preview; right-click to open.