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 synfs builds a synthetic, read-only ninep file server whose contents are computed by a function at read time.

Readme View source

gno.land/p/moul/x/plan9/synfs/v0

A synthetic, read-only file server whose contents are computed at read time. This is the package that makes the rest of the suite adoptable.

1import synfs "gno.land/p/moul/x/plan9/synfs/v0"
2
3t := synfs.New("dev", "sys", func() int64 { return runtime.ChainHeight() })
4t.Root().
5	Add("sysname", func() string { return runtime.ChainID() }).
6	Add("height", func() string { return strconv.FormatInt(runtime.ChainHeight(), 10) })

In Plan 9 a device is not storage, it is code behind a name: reading /dev/time runs a function. A realm that wants to publish its state as a browsable tree does the same thing here, in a few lines, and gets ls, cat, stat and mountability for free.

Read-only by construction. The tree implements ninep.File and not ninep.Mutable, so every method is free of side effects and the tree is safe to hand to a namespace owned by somebody else. That is the property the whole cross-realm mount story rests on.

Two details worth knowing:

  • A synthetic file pins Qid.Version at 0 forever. Its contents can change on every block, so a version would be a lie; a client that needs change detection should read the file.
  • AddRange serves the 9P read window itself, for a file with no natural end. /dev/zero uses it: an unbounded read returns nothing rather than an endless value, which is what stops cat /dev/zero from being a denial of service.

Live demo: r/moul/x/plan9/dev publishes the chain itself as a device tree. Design and analysis: moul/gno-contracts#136.


Not affiliated with Plan 9. Plan 9 from Bell Labs is the work of the Computing Science Research Center at Bell Labs; the name and the marks are theirs, and the copyright is held by the Plan 9 Foundation. This package borrows the vocabulary and none of the code: it is an independent homage, asking what that ecosystem's spirit looks like on a chain. Full attribution: NOTICE.


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/x/plan9/synfs/v0 dependency graph

🧪 Highly experimental — potentially vibe-coded. Not audited; may break, change, or be removed at any time. Do not use with anything of value. Full disclaimer: DISCLAIMER.

Overview

Package synfs builds a synthetic, read-only ninep file server whose contents are computed by a function at read time.

This is the package that makes the rest of the suite adoptable. Plan 9's device drivers are not storage, they are code behind a name: reading /dev/time runs a function. A realm that wants to publish its state as a browsable tree does the same thing here, in a few lines, and gets ls, cat, stat and mountability for free:

Example
1t := synfs.New("dev", "g1...", func() int64 { return runtime.ChainHeight() })
2t.Root().
3	Add("sysname", func() string { return runtime.ChainID() }).
4	Add("height", func() string { return strconv.FormatInt(runtime.ChainHeight(), 10) })

The tree is READ-ONLY by construction, which is what makes it safe to hand to another realm's namespace: every method is free of side effects, so it can be called from a frame that does not own these objects.

A synthetic file reports Qid.Version 0 forever. Its contents can change on every block, so a version would be a lie; clients that need change detection should read the file.

NOTICE. Plan 9 from Bell Labs is the work of the Computing Science Research Center at Bell Labs; the name and the marks are theirs, and the copyright is held by the Plan 9 Foundation (https://p9f.org). This package is not affiliated with, endorsed by, or sponsored by them, and contains no Plan 9 code: it borrows the vocabulary so that the design reads without a glossary, and it is an homage, asking what that ecosystem's spirit looks like on a chain. Full attribution: NOTICE.md at the root of moul/gno-contracts.

Functions 1

func New

1func New(name, uid string, clock func() int64) *Tree
source

New returns a tree whose root directory is named name, owned by uid, using clock (normally the block height) as every node's mtime. clock may be nil, in which case the mtime is zero.

Types 4

type Dir

struct
1type Dir struct {
2	tree     *Tree
3	name     string
4	perm     ninep.Perm
5	qid      ninep.Qid
6	children *avl.Tree // name -> ninep.File
7}
source

Dir is a synthetic directory. Its children are held in an avl tree, so a listing is ordered by name and identical on every node.

Methods on Dir

func Add

method on Dir
1func (d *Dir) Add(name string, fn ReadFn) *Dir
source

Add attaches a read-only file computed by fn, mode 0444. It returns the directory, so calls chain.

func AddDir

method on Dir
1func (d *Dir) AddDir(sub *Dir) *Dir
source

AddDir attaches a subdirectory built with Tree.NewDir.

func AddFile

method on Dir
1func (d *Dir) AddFile(name string, f ninep.File) *Dir
source

AddFile attaches any ninep.File under the given name, which is how a synthetic tree splices in a tree served by something else.

func AddPerm

method on Dir
1func (d *Dir) AddPerm(name string, perm ninep.Perm, fn ReadFn) *Dir
source

AddPerm is Add with an explicit mode.

func AddRange

method on Dir
1func (d *Dir) AddRange(name string, perm ninep.Perm, fn RangeFn) *Dir
source

AddRange attaches a file that serves a read window itself. Use it for a file with no natural end, where materialising the whole thing would be wrong.

func Names

method on Dir
1func (d *Dir) Names() []string
source

Names returns the directory's entries, in order. Useful for a file that wants to describe its own directory, as /dev/drivers does.

func Read

method on Dir
1func (d *Dir) Read(off, count int64) (string, error)
source

Read implements ninep.File.

func ReadDir

method on Dir
1func (d *Dir) ReadDir() ([]ninep.Stat, error)
source

ReadDir implements ninep.File.

func Stat

method on Dir
1func (d *Dir) Stat() ninep.Stat
source

Stat implements ninep.File.

func Walk

method on Dir
1func (d *Dir) Walk(name string) (ninep.File, error)
source

Walk implements ninep.File.

type RangeFn

func
1type RangeFn func(off, count int64) (string, error)
source

RangeFn serves a 9P read window directly, for files that are cheaper (or only possible) to generate a slice at a time, such as an endless one.

type ReadFn

func
1type ReadFn func() string
source

ReadFn computes a whole file.

type Tree

struct
1type Tree struct {
2	root  *Dir
3	next  uint64
4	clock func() int64
5	uid   string
6}
source

Tree owns the qid allocator, the clock and the owner name shared by every node in one synthetic server.

Methods on Tree

func NewDir

method on Tree
1func (t *Tree) NewDir(name string) *Dir
source

NewDir returns a detached directory belonging to this tree. Attach it with AddDir.

func Root

method on Tree
1func (t *Tree) Root() *Dir
source

Root returns the tree's root directory.

func Uid

method on Tree
1func (t *Tree) Uid() string
source

Uid returns the owner stamped on every node.

Imports 2

Source Files 4