Help us shape the future of Go! Take the 2026 Go Developer Survey

manifest

package
v0.10.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 31, 2026 License: Apache-2.0 Imports: 21 Imported by: 0

Documentation

Overview

Package manifest reads and checks aicoded.yaml, the app's permission list.

Runners, the delivery pipeline and the aicoded CLI import it. App code may not: E-LINT-001 refuses the import.

Index

Examples

Constants

View Source
const FileName = "aicoded.yaml"

FileName is the name of the permission list in an app's directory.

Variables

View Source
var Classes = []string{"public", "internal", "confidential", "personal"}

Classes are the classes of data a source may hold, from the least sensitive to the most.

View Source
var ReservedClasses = []string{"health", "hr", "card"}

ReservedClasses are the classes of data no app may hold: health, HR and card data. A permission list that names one is not allowed (E-MAN-024).

Functions

func Canonical added in v0.7.0

func Canonical(m Manifest) []byte

Canonical returns the form of m that Hash hashes: its JSON encoding, keyed by the names of aicoded.yaml, with every list sorted and each item once, and with what is empty or false left out. An egress entry is its host alone when it binds no key and an object otherwise, the entries sorted by host. Two permission lists that differ only in the order of keys or of list items, or in comments, have one canonical form.

func ClassRank added in v0.7.0

func ClassRank(class string) (int, bool)

ClassRank returns the place of class in Classes, and false for any other name, a reserved class included.

func Hash added in v0.7.0

func Hash(m Manifest) string

Hash returns "sha256:" followed by the lowercase hex SHA-256 of Canonical(m).

func PlatformHost added in v0.10.0

func PlatformHost(host string) bool

PlatformHost reports whether host, in any case and with or without a dot at the end, is a host of the platform: one of its domains, such as aicoded.cloud, or a subdomain of one.

func ValidApp

func ValidApp(name string) bool

ValidApp reports whether name is a valid app name: 2-63 lowercase letters, digits and dashes, starting with a letter and ending with a letter or digit.

func ValidHost added in v0.7.0

func ValidHost(host string) bool

ValidHost reports whether host is a host name an app may reach: lower-case, with no scheme, port, path, wildcard or IP address, and not a local name, one whose last label is localhost, local, internal or arpa.

Types

type Access

type Access struct {
	Require []string `yaml:"require" json:"require,omitzero"`
	Guard   bool     `yaml:"guard" json:"guard,omitzero"`
	Shared  bool     `yaml:"shared" json:"shared,omitzero"`
	Calls   []string `yaml:"calls" json:"calls,omitzero"`
}

Access is one entry of the access section aicoded generate writes: the role rules a viewer must meet, whether a Guard on the page's path decides who sees each record, whether everyone the rules admit may see every record, and the page calls. Guard and Shared are never both set.

type Audience

type Audience struct {
	Internal []string `yaml:"internal" json:"internal,omitzero"`
	External []string `yaml:"external" json:"external,omitzero"`
}

Audience is who may use the app: people of the company, as group:<name> or everyone, and outside people, as idp:<name> or magic-link.

func (Audience) Admits added in v0.7.0

func (a Audience) Admits(groups []string) bool

Admits reports whether the audience's internal entries admit a member of the groups, given by name without "group:": everyone does, and so does group:<n> for any n in groups. External entries admit no one here.

type Change

type Change struct{ Field, Key, Before, After string }

Change is one difference between two permission lists. Key names the entry of a keyed field and is empty otherwise; Before is empty for what was added, After for what was removed.

func Diff

func Diff(before, after Manifest) []Change

Diff returns the differences between before and after, nil when there are none.

The changes come in the order of the fields, then by key. A list, such as egress, changes by each item removed or added, removals first, each sorted; a new order is no change, and a field left out equals an empty one. A keyed entry changes as a whole, written as text: a data source's classes, sorted; an access path's "require <rules>", then "; guard" or "; shared", then "; calls <names>", when they apply, its lists in written order; a served function's "callers <apps>", then "; require <rules>" and "; apps" when they apply, its lists sorted; a job's cron. Egress changes by each host removed or added, then by each key binding, keyed by its host: "secret <name> in <header>", then ` after "<prefix>"` with a prefix, and empty for a host without a key. The functions called from another app change one by one, keyed by that app.

Example

Diff lists what changed between two permission lists, field by field, as the platform's change records show it. A new order of a list is no change.

package main

import (
	"fmt"

	"aicoded.dev/framework/manifest"
)

func main() {
	before := manifest.Manifest{
		App:      "rooms",
		Access:   map[string]manifest.Access{"/rooms/{id}": {Require: []string{"staff"}}},
		Settings: []string{"greeting", "report_day"},
	}
	after := manifest.Manifest{
		App:      "rooms",
		Access:   map[string]manifest.Access{"/rooms/{id}": {Require: []string{"staff"}, Guard: true}},
		Egress:   []manifest.Egress{{Host: "api.partner.example"}},
		Settings: []string{"report_day", "greeting"},
	}
	for _, c := range manifest.Diff(before, after) {
		fmt.Printf("%+v\n", c)
	}
}
Output:
{Field:access Key:/rooms/{id} Before:require staff After:require staff; guard}
{Field:egress Key: Before: After:api.partner.example}

type Data

type Data struct {
	Source  string   `yaml:"source" json:"source,omitzero"`
	Classes []string `yaml:"classes" json:"classes,omitzero"`
}

Data is one source of data the app reaches: sqldb, filestore:<name> or connector:<name>, with the classes of data it holds.

type Egress added in v0.10.0

type Egress struct {
	Host   string `yaml:"host" json:"host"`
	Secret string `yaml:"secret" json:"secret,omitzero"`
	Header string `yaml:"header" json:"header,omitzero"`
	Prefix string `yaml:"prefix" json:"prefix,omitzero"`
}

Egress is one entry of egress: the host of an outside service the app calls, and, when the entry binds a key, the secret the runner adds to every request to the host, in the header Header after Prefix. In aicoded.yaml, and in JSON, a plain entry is the host alone and a binding is a mapping of host, secret, header and prefix.

func (Egress) Bound added in v0.10.0

func (e Egress) Bound() bool

Bound reports whether the entry binds a key to its host.

func (Egress) MarshalJSON added in v0.10.0

func (e Egress) MarshalJSON() ([]byte, error)

MarshalJSON writes a plain entry as its host and a binding as an object.

func (*Egress) UnmarshalJSON added in v0.10.0

func (e *Egress) UnmarshalJSON(data []byte) error

UnmarshalJSON reads what MarshalJSON writes.

func (*Egress) UnmarshalYAML added in v0.10.0

func (e *Egress) UnmarshalYAML(n *yaml.Node) error

UnmarshalYAML reads a scalar as the host alone and a mapping as the entry's fields.

type Email

type Email struct {
	From      string   `yaml:"from" json:"from,omitzero"`
	ToDomains []string `yaml:"to_domains" json:"to_domains,omitzero"`
}

Email is the address the app sends mail from and the domains it may send to.

type Job

type Job struct {
	Cron string `yaml:"cron" json:"cron,omitzero"`
	Name string `yaml:"job" json:"job,omitzero"`
}

Job is one scheduled job: its name and the cron expression, in UTC, of when it runs.

type Manifest

type Manifest struct {
	App       string            `yaml:"app" json:"app,omitzero"`
	Class     string            `yaml:"class" json:"class,omitzero"`
	Owner     string            `yaml:"owner" json:"owner,omitzero"`
	Audience  Audience          `yaml:"audience" json:"audience,omitzero"`
	Data      []Data            `yaml:"data" json:"data,omitzero"`
	Access    map[string]Access `yaml:"access" json:"access,omitzero"`
	Services  Services          `yaml:"services" json:"services,omitzero"`
	Egress    []Egress          `yaml:"egress" json:"egress,omitzero"`
	Email     *Email            `yaml:"email" json:"email,omitzero"`
	Schedule  []Job             `yaml:"schedule" json:"schedule,omitzero"`
	Settings  []string          `yaml:"settings" json:"settings,omitzero"`
	Secrets   []string          `yaml:"secrets" json:"secrets,omitzero"`
	Modules   []string          `yaml:"modules" json:"modules,omitzero"`
	Size      string            `yaml:"size" json:"size,omitzero"`
	Resources string            `yaml:"resources" json:"resources,omitzero"`
	TTL       string            `yaml:"ttl" json:"ttl,omitzero"`
}

Manifest is the permission list, aicoded.yaml. A field left out or empty is not stated.

func Load

func Load(path string) (Manifest, error)

Load reads and checks the manifest at path, as Parse does.

func Parse

func Parse(path string, data []byte) (Manifest, error)

Parse checks the manifest data and returns it. Path is used only in the positions of problems. It reports every problem it finds as one coded error each: its error unwraps, with Unwrap() []error, to them, sorted by line. Data that is not one YAML document of the expected shape, or that uses an anchor, an alias or a merge key, is not checked any further.

Example

Parse checks a permission list and returns its fields. A list with problems returns every problem, each with a code and its position.

package main

import (
	"fmt"

	"aicoded.dev/framework/manifest"
)

func main() {
	m, err := manifest.Parse(manifest.FileName, []byte(`app: rooms
audience: {internal: [everyone]}
egress:
  - hooks.partner.example
  - {host: api.partner.example, secret: partner_api_key, header: Authorization, prefix: "Bearer "}
secrets: [partner_api_key]
`))
	if err != nil {
		fmt.Println(err)
		return
	}
	fmt.Println(m.EgressHosts())
	b, _ := m.Binding("api.partner.example")
	fmt.Printf("%s in %s after %q\n", b.Secret, b.Header, b.Prefix)
}
Output:
[api.partner.example hooks.partner.example]
partner_api_key in Authorization after "Bearer "

func (Manifest) Binding added in v0.10.0

func (m Manifest) Binding(host string) (Egress, bool)

Binding returns the entry of egress that binds a key to host, and false when host is not listed or is listed without a key.

func (Manifest) BoundSecret added in v0.10.0

func (m Manifest) BoundSecret(name string) (Egress, bool)

BoundSecret returns the entry of egress that binds the secret name, the first by host when several do, and false when none does.

func (Manifest) Connectors

func (m Manifest) Connectors() []string

Connectors returns the names of the connectors the app declares, sorted.

func (Manifest) EgressHosts added in v0.10.0

func (m Manifest) EgressHosts() []string

EgressHosts returns the hosts of egress, sorted, each once.

func (Manifest) Roles

func (m Manifest) Roles() []string

Roles returns every role the access and services sections name, sorted, without "*".

func (Manifest) SQLDB

func (m Manifest) SQLDB() bool

SQLDB reports whether the app declares its SQL database.

func (Manifest) Stores

func (m Manifest) Stores() []string

Stores returns the names of the file stores the app declares, sorted.

type Serve

type Serve struct {
	Callers []string `yaml:"callers" json:"callers,omitzero"`
	Require []string `yaml:"require,omitempty" json:"require,omitzero"`
	Apps    bool     `yaml:"apps,omitempty" json:"apps,omitzero"`
}

Serve says who may call one function the app serves: the calling apps, the role rules a viewer must meet, written as in the access section, and whether a calling app may call it with no viewer. A function without role rules takes no call on behalf of a viewer.

type Services

type Services struct {
	Calls  map[string][]string `yaml:"calls" json:"calls,omitzero"`
	Serves map[string]Serve    `yaml:"serves" json:"serves,omitzero"`
}

Services is the services section aicoded generate writes: the functions of other apps this app calls, by app, and the functions it serves to other apps, by name.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL