הצטרף ל-Nostr
2026-07-29 07:28:37 UTC

mleku on Nostr: this morning i got to thinking a lot about how moxie takes the Go language and ...

this morning i got to thinking a lot about how moxie takes the Go language and creates an interpretation scheme on the syntax that the runtime enforces as constraints - memory usage, which location is used for storage, and what happens when functions/methods return, the distinction between sovereign arenas and function arenas and the use of the built-in codec scheme that defines how the values are encoded position independent to be unpacked into caller scopes - and apply this to capabilities, which are essentially permissions on file handles.

so, here is a full scheme that adds some small extra syntax to moxie that more or less, ultimately, replaces a whole swathe of Go stdlib - fmt for writing to terminals, os.File filehandling, and net library stuff. it essentially creates a whole framework for how to implement a full protocol stack with familiar looking syntax that creates type unions that constrain what types can be used, using the familiar channel read/write restriction syntax creates permissions, and adds another new feature, the "prefix" which defines the required boundaries, eg, URL, filesystem path, that any literal string path that violates this can be caught at compile time, and at runtime, is used to check if the dynamic value is permitted by the owner of the channel.

the implications for the eventual need to refactor the stdlib to follow these conventions are fairly staggering but the other neat thing is that all the existing apis can still be used as is, as they just wrap all this in syntax - the opposite of sugar, uglification. native moxie protocol handling can all be done using familiar channel syntax now, as well. here it is:

## moxie capability scheme

**foundation**

`chan byte` is the sole I/O primitive. all capability derives from it. `pipe[Types]{:buf N :prefix "scope"}` is the constrained, typed, scoped alias. three independent layers compose:

| layer | syntax | enforced | optional |
|---|---|---|---|
| direction | `->chan` / `<-chan` / `chan` | compile-time | no |
| protocol | `[HttpReq \| HttpResponse]` | compile-time via codec | yes (absent = untyped `chan byte`) |
| scope | `{:prefix "/home/app"}` | compile-time for literals, runtime for variables | yes (absent = unrestricted) |

**builtins**

```
len(c) // buffered elements waiting
cap(c) // total buffer capacity
prefix(c) // scope string ("" if un-prefixed)
```

**stdio**

`stdin` (`<-chan byte`), `stdout` (`->chan byte`), `stderr` (`->chan byte`) are per-actor keywords bound at spawn by the runtime. no import. `fmt` writes to the actor's `stdout` automatically. override at spawn:

```
spawn worker with stdout = fileCap
```

**attenuation**

extract a directional channel from a capability struct -- you get a narrower permission:

```
var readOnly <-chan byte = fileCap.read // can read, can't write
var httpOnly ->chan byte = netCap.restrict(80, 443) // only those ports
```

pass the extracted channel to a function; the function can only do what the channel allows. no wrapper types, no annotations.

**filesystem & network**

`DirCap` is a `->chan byte{:prefix "$HOME/.cache/app"}` created by the filesystem actor. `open()` sends the prefix + requested path. the actor resolves the realpath and rejects anything outside the prefix -- including `..` escapes and symlink traversal.

`NetCap` same pattern. `tcp://*:443` matches any host on port 443 only. the network actor resolves DNS before checking containment.

**receive-side type matching**

the variable's declared type selects which message to dequeue. wrong-type messages stay in the buffer:

```
select {
case req := <-pipe[HttpRequest]:
// fires only if the head message is an HttpRequest
case resp := <-pipe[HttpResponse]:
// fires only if the head message is an HttpResponse
}
```

the codec inspects the buffer head. type mismatch skips the case. no manual dispatch, no enum switch, no type tag in the wire format. the pipe carries the protocol; the codec enforces it; the select statement doesn't change.

**what you don't need**

- no union types in the type system -- `[A | B]` is a codec constraint, not a generic parameter
- no `io.Reader` / `io.Writer` interfaces -- channel direction is the permission
- no separate capability wrapper types -- `FileCap` is just a struct with directional channels inside
- no ambient authority -- `main()` receives all capabilities explicitly; functions only access what they're passed
- no runtime security layer separate from the compiler -- the type system and the codec together prove correctness at build time