Realm on pearl-1
permission_registry
gno.land/r/g1ut6uspuh73e02yauxpmyt8g3wwddaq8utagvm3/permission_registry
realmpipeline-applicationaccess-control
Global named-permission registry with admin-scoped resources. GitHub-pipeline application #4.
Identity
| Import path | gno.land/r/g1ut6uspuh73e02yauxpmyt8g3wwddaq8utagvm3/permission_registry |
|---|---|
| Kind | realm (/r/) |
| Chain | pearl-1 |
| Namespace | g1ut6uspuh73e02yauxpmyt8g3wwddaq8utagvm3 |
| Realm address | g1x9tatmqcdlas2w6k724mrs2zcgh3awf3s5wkhk derived, never confirmed against the realm |
Provenance
chain-attested| Deployed at height | 604,615 |
|---|---|
| Deploy transaction | a30c09427173a45553a9befcac38d410aa0e416eb785145942ee5378d6a8c54f look it up on the RPC |
| Deployer | g1ut6uspuh73e02yauxpmyt8g3wwddaq8utagvm3 |
| Gas used | 29,251,583 |
| Storage | 24,721 bytes, deposit 2472100ugnot |
| Files on chain | gnomod.toml permission_registry.gno |
| Deployed bytes | permission_registry.gno — 18,070 bytes |
| sha256 | 603fec571a55d051353e101c64c0490a5268124abda8d88ac703b9bbf26fb763 |
Do not take the hash above on trust. $download returns the bytes pearl-1 is actually running; this command fetches them and prints their digest, which should equal the one in the table:
curl -sS 'https://pearl.testnets.gno.land/r/g1ut6uspuh73e02yauxpmyt8g3wwddaq8utagvm3/permission_registry$download&file=permission_registry.gno' | shasum -a 256Expected: 603fec571a55d051353e101c64c0490a5268124abda8d88ac703b9bbf26fb763 — 18,070 bytes. This was checked for all 21 packages while building this site's architecture record; every one matched. Use curl: pearl's edge answers Python's default user-agent with HTTP 403.
API
chain-derived 13 exported functions, 1 type.
Every function below deep-links to gnoweb's call builder, which generates a ready-to-run gnokey maketx call for it:
AcceptAdminCancelAdminTransferCreateResourceDeleteResourceGetAdminGetPendingAdminGetPermissionsGrantHasListResourcesRenderRevokeTransferAdmin
Overview
This package carries no package doc comment on chain, so there is nothing for vm/qdoc to return and gnoweb's $help Overview is empty. Deployed bytes are immutable, so this cannot be repaired in place — see catalog/DISCOVERY_APIDOCS.md §2.4.
Imports
chain/runtime/unsafesortstrconvstringstime
Constants and variables
const (
// MaxResources bounds total registry state. Raised from the upstream
// 200 as part of the R1 remediation: with a per-admin quota now
// carrying the anti-monopoly duty, the global cap is a pure state
// bound rather than the sole defense against namespace exhaustion.
MaxResources = 1000
// MaxResourcesPerAdmin bounds how many resources one address may hold
// at once. R1 (audit 2026-09-21): the upstream design had only a
// global cap on a permissionless shared registry, so one unprivileged
// key could occupy every slot for ~200 cheap transactions and
// permanently deny the registry to every other tenant. DeleteResource
// is admin-only, so the squat was irreversible.
MaxResourcesPerAdmin = 20
MaxPermissionsPerResource = 50
MaxHoldersPerPermission = 200
MaxNameLen = 64
// ReservationPeriod is how long a deleted resource name stays
// reserved. Finite (re-audit 2026-09-02): eternal tombstones let an
// attacker cycle create/delete to lock the namespace forever.
ReservationPeriod = int64(90 * 24 * 3600) // 90 days
// Render bounds (Y3, audit 2026-09-21). Render walks the whole
// registry and is reachable by any viewer through gnoweb and
// vm/qrender, so its cost is borne by third parties rather than by
// whoever grew the state. Uncapped, the declared limits allowed
// 1000*50*200 rendered holder entries. The full data stays available
// through ListResources / GetPermissions / Has, which are bounded per
// call by construction.
MaxRenderResources = 20
MaxRenderPermissions = 8
MaxRenderHolders = 10
)
Types
type Reservation
type Reservation struct {
Admin address
Creator address
Expires time.Time
}
Reservation holds a deleted resource name for its former admin AND its original creator, and expires.
Functions
AcceptAdmin
func AcceptAdmin(cur realm, resourceName string)
Crossing function. Callable from a transaction via
MsgCall, and from another realm asAcceptAdmin(cross(cur), ...).
AcceptAdmin completes a pending handoff; only the nominee may call it. The nominee's quota is checked HERE — at consent time — so a nomination can never push an account past MaxResourcesPerAdmin without that account agreeing to carry the resource.
CancelAdminTransfer
func CancelAdminTransfer(cur realm, resourceName string)
Crossing function. Callable from a transaction via
MsgCall, and from another realm asCancelAdminTransfer(cross(cur), ...).
CancelAdminTransfer withdraws a pending nomination. Only the current admin can call this.
CreateResource
func CreateResource(cur realm, resourceName string)
Crossing function. Callable from a transaction via
MsgCall, and from another realm asCreateResource(cross(cur), ...).
CreateResource registers a new named resource. The caller becomes its admin and is the only address that can grant or revoke permissions on it. A deleted resource name stays reserved for its former admin and its original creator until the reservation expires.
Each address may administer at most MaxResourcesPerAdmin resources at once, and the registry holds at most MaxResources in total.
DeleteResource
func DeleteResource(cur realm, resourceName string)
Crossing function. Callable from a transaction via
MsgCall, and from another realm asDeleteResource(cross(cur), ...).
DeleteResource removes a resource and every permission under it. Only the resource admin can call this. The name stays reserved for the caller and for the original creator: nobody else can re-create it and inherit its consumers until the reservation expires.
GetAdmin
func GetAdmin(resourceName string) string
GetAdmin returns the admin address of a resource.
GetPendingAdmin
func GetPendingAdmin(resourceName string) string
GetPendingAdmin returns the nominated admin awaiting acceptance for a resource, or "none".
GetPermissions
func GetPermissions(resourceName string, addr address) string
GetPermissions returns all permission names granted to addr on a resource, as a comma-separated string. Returns "none" if the address has no permissions.
Grant
func Grant(cur realm, resourceName, permission string, addr address)
Crossing function. Callable from a transaction via
MsgCall, and from another realm asGrant(cross(cur), ...).
Grant gives an address a named permission on a resource. Only the resource admin can call this. Panics if the permission is already granted to avoid silent no-ops.
Has
func Has(resourceName, permission string, addr address) bool
Has returns true if addr holds the named permission on the resource. Returns false (never panics) for unknown resources or permissions.
INTEGRATOR CONTRACT (Y7): Has takes the subject address explicitly and performs NO caller authentication — it answers "does this address hold this permission", not "may my caller do this". A consuming realm must derive addr from its own crossing entrypoint's cur.Previous().Address() and pass it in. Deriving it inside a non-crossing helper via unsafe.PreviousRealm() resolves the consumer's own caller's caller and is a Class-2 designation-forgery bug in the consumer.
ListResources
func ListResources() string
ListResources returns all registered resource names as a comma-separated string in registration order. Returns "none" if no resources exist.
Render
func Render(path string) string
Render returns a markdown overview. Never panics. Output is bounded by MaxRenderResources / MaxRenderPermissions / MaxRenderHolders (Y3); truncated sections name the query to use for complete data.
Revoke
func Revoke(cur realm, resourceName, permission string, addr address)
Crossing function. Callable from a transaction via
MsgCall, and from another realm asRevoke(cross(cur), ...).
Revoke removes a permission from an address. Only the resource admin can call this. Panics if the permission was not granted. A permission left with no holders is pruned from the resource's permission list.
TransferAdmin
func TransferAdmin(cur realm, resourceName string, newAdmin address)
Crossing function. Callable from a transaction via
MsgCall, and from another realm asTransferAdmin(cross(cur), ...).
TransferAdmin nominates a new admin for a resource. Only the current admin can call this, and the handoff does NOT take effect until the nominee calls AcceptAdmin.
Y4 (audit 2026-09-21): the upstream one-step transfer made a well-formed-but-unowned destination permanently fatal. address.IsValid only checks bech32 form, so a mistyped address passed the check and left the resource with an admin nobody controls — it could never again be granted on, revoked from, transferred or deleted, and its slot was lost from both the global cap and the former admin's quota forever. Nomination is reversible; only the nominee's consent is final.
Doc text is reproduced as vm/qdoc returns it. The node markdown-escapes doc comments, so a bracket or angle bracket may carry a backslash the committed source does not have. The source itself is at source and in this repository.
Dependencies
chain-attested| Imports | chain/runtime/unsafe, sort, strconv, strings, time |
|---|---|
| First-party dependencies | none |
| Used by | none |
Known limitations
Recorded by the people who built and deployed it. This list is deliberately not empty where honesty costs something.
curated- Caps are global, shared and unraisable on an immutable realm (MaxResources 1000, MaxResourcesPerAdmin 20, MaxHoldersPerPermission 200); p/permbook exists because of this.
Source and records
| Source file | pearl/r/permission_registry/permission_registry.gno at commit 6a510c665a53 in the project repository (not public — the digest command above is the check that needs no repository) |
|---|---|
| Matches the deployed bytes | yes — byte-identical |
| Upstream repository | https://github.com/SillyZir/permission_registry |
| Deployed from commit | e3d6aeaf3571ec35c2af68c5fbb756480b76ed03 |
| Records | catalog/applications.md#permission_registrypearl/DEPLOYMENT.md |