Resync hq ADR references 0044-0054 -> 0039-0049 after the hq record reconciliation
This commit is contained in:
@@ -67,7 +67,7 @@ const ExchangeName = "mesh"
|
||||
// constant that differed would be caught by the test that asserts they agree.
|
||||
const BuildQueueName = "builds"
|
||||
|
||||
// The events bus (novox/hq ADR 0047): one topic exchange every event rides, a second for tool
|
||||
// The events bus (novox/hq ADR 0042): one topic exchange every event rides, a second for tool
|
||||
// RPC kept apart, and a dead-letter home for a poison event. The substrate owns these — a module's
|
||||
// account cannot declare them, only bind its own queue to the events one.
|
||||
const (
|
||||
@@ -77,15 +77,15 @@ const (
|
||||
)
|
||||
|
||||
// ModuleQueueFor is the durable queue a module consumes its events from — one per module per node
|
||||
// (novox/hq ADR 0047), named so the account that may read it is exactly this module's.
|
||||
// (novox/hq ADR 0042), named so the account that may read it is exactly this module's.
|
||||
func ModuleQueueFor(node, module string) string { return node + "." + module + ".events" }
|
||||
|
||||
// modulePermissions is a module's authority on the bus, derived from its manifest (novox/hq
|
||||
// ADR 0048): what it consumes and what it emits, and nothing else. Pure, so the scope is tested as
|
||||
// ADR 0043): what it consumes and what it emits, and nothing else. Pure, so the scope is tested as
|
||||
// patterns without a broker — the way a builder's is.
|
||||
//
|
||||
// A note on the limit: the broker's write permission is per exchange, not per routing key (LavinMQ
|
||||
// has no topic permissions), so an emitting module is granted the events exchange whole. ADR 0047's
|
||||
// has no topic permissions), so an emitting module is granted the events exchange whole. ADR 0042's
|
||||
// origin reservation — a module publishes only under `module.<self>.*` — is stamped by the sdk, not
|
||||
// enforced here; that gap is the broker's, and is recorded rather than hidden. A pure consumer like
|
||||
// the audit logger is unaffected: it is granted no write to the exchange at all.
|
||||
@@ -94,7 +94,7 @@ func modulePermissions(node, module string, emits, consumes []string) (configure
|
||||
events := regexp.QuoteMeta(EventsExchangeName)
|
||||
rpc := regexp.QuoteMeta(RPCExchangeName)
|
||||
// A module serves each of its tools on its own queue, namespaced by the module (novox/hq
|
||||
// ADR 0052) — serve.<module>.<tool> — so the account may declare, bind and read exactly its own,
|
||||
// ADR 0047) — serve.<module>.<tool> — so the account may declare, bind and read exactly its own,
|
||||
// and no other module's.
|
||||
serve := "serve\\." + regexp.QuoteMeta(module) + "\\..*"
|
||||
|
||||
@@ -102,7 +102,7 @@ func modulePermissions(node, module string, emits, consumes []string) (configure
|
||||
configure = "^(" + queue + "|" + serve + ")$"
|
||||
|
||||
// Write to bind its queue and serve queues (binding is a write on the queue), and to the RPC
|
||||
// exchange to publish replies (ADR 0052: replies ride mesh.rpc, never the default exchange, which
|
||||
// exchange to publish replies (ADR 0047: replies ride mesh.rpc, never the default exchange, which
|
||||
// would let it publish into any queue). To the events exchange only if it emits.
|
||||
writes := []string{queue, serve, rpc}
|
||||
if len(emits) > 0 {
|
||||
@@ -121,7 +121,7 @@ func modulePermissions(node, module string, emits, consumes []string) (configure
|
||||
}
|
||||
|
||||
// CreateModuleAccount gives an assigned module its own broker account, scoped by what it emits and
|
||||
// consumes (novox/hq ADR 0048). The account name carries the node so the same module on two machines
|
||||
// consumes (novox/hq ADR 0043). The account name carries the node so the same module on two machines
|
||||
// holds two accounts, each sealed to its own; the permissions carry the module so one module cannot
|
||||
// read another's queue. Generic — the builder is one instance of this rule, not a separate kind.
|
||||
func (m *Management) CreateModuleAccount(ctx context.Context, node, module, password string, emits, consumes []string) (string, error) {
|
||||
@@ -152,7 +152,7 @@ func (m *Management) CreateModuleAccount(ctx context.Context, node, module, pass
|
||||
|
||||
// EnsureModuleQueue declares a consuming module's queue with its dead-letter exchange, idempotently.
|
||||
// The substrate declares it because a scoped module account may not: the broker refuses a queue with
|
||||
// a dead-letter exchange to a non-administrator (novox/hq ADR 0048), so a consumer passively checks
|
||||
// a dead-letter exchange to a non-administrator (novox/hq ADR 0043), so a consumer passively checks
|
||||
// the queue the mesh made rather than declaring its own.
|
||||
func (m *Management) EnsureModuleQueue(ctx context.Context, node, module string) error {
|
||||
queue := ModuleQueueFor(node, module)
|
||||
|
||||
@@ -14,7 +14,7 @@ import (
|
||||
|
||||
// The audit logger consumes everything and emits nothing. Its account must let it declare and read
|
||||
// its own queue and read the events exchange to bind onto — and must not reach another module's
|
||||
// queue, nor grant any write to the events exchange (novox/hq ADR 0048).
|
||||
// queue, nor grant any write to the events exchange (novox/hq ADR 0043).
|
||||
func TestAConsumerReadsItsOwnQueueAndTheEventsExchangeAndNoOthers(t *testing.T) {
|
||||
// Rebuilt through CreateModuleAccount's own path by asking for the same scope it would apply.
|
||||
// The queue this module reads:
|
||||
|
||||
@@ -61,7 +61,7 @@ type Grant struct {
|
||||
Values map[string]any
|
||||
// Slug is the consumer module's identity slug, if it declared one — carried on the grant so the
|
||||
// provider side derives the same login the consumer does, even across nodes where the consumer's
|
||||
// manifest is not in view (novox/hq ADR 0054). Empty means "use the module name".
|
||||
// manifest is not in view (novox/hq ADR 0049). Empty means "use the module name".
|
||||
Slug string
|
||||
// At is where the consuming machine is on the private network, empty if it is not on one.
|
||||
//
|
||||
@@ -143,7 +143,7 @@ func (r Resolution) Declaration(with Rendering) ([]map[string]any, error) {
|
||||
|
||||
// Once, from every module's listens -- not per module. A module receiving only its own ports
|
||||
// would write a rule set that closed every other module on the machine. Each module's per-node
|
||||
// exposure settings override its listens' source first (novox/hq ADR 0051).
|
||||
// exposure settings override its listens' source first (novox/hq ADR 0046).
|
||||
exposure := map[string]map[int]string{}
|
||||
for _, m := range r.Modules {
|
||||
e, err := Exposure(m, with.Settings[m.Module])
|
||||
|
||||
@@ -73,7 +73,7 @@ func (r Resolution) Filtering(computed map[string]Generator, ports map[string]ma
|
||||
}
|
||||
// The source, with any per-node exposure setting applied. The override names the port
|
||||
// the module declares, so it travels with that port to wherever the machine publishes
|
||||
// it (novox/hq ADR 0051): the same module is internal on one node and public on another.
|
||||
// it (novox/hq ADR 0046): the same module is internal on one node and public on another.
|
||||
from := l.From
|
||||
if override, set := exposure[m.Module][l.Port]; set {
|
||||
from = override
|
||||
@@ -108,7 +108,7 @@ func (r Resolution) Filtering(computed map[string]Generator, ports map[string]ma
|
||||
return widest(out), nil
|
||||
}
|
||||
|
||||
// ExposeSetting is the settings key that overrides a listen's source per node (novox/hq ADR 0051):
|
||||
// ExposeSetting is the settings key that overrides a listen's source per node (novox/hq ADR 0046):
|
||||
//
|
||||
// {"expose": {"5432": "anywhere"}}
|
||||
//
|
||||
@@ -122,7 +122,7 @@ const ExposeSetting = "expose"
|
||||
// It refuses an override for a port the module does not listen on, or to a source that is not a
|
||||
// real one — an exposure setting that reaches no port, or names a source nothing enforces, is the
|
||||
// "reads as a restriction and is none" fault this whole mechanism exists to prevent (novox/hq
|
||||
// ADR 0048/0050). A module with no `expose` setting yields nothing and keeps its manifest defaults.
|
||||
// ADR 0043/0045). A module with no `expose` setting yields nothing and keeps its manifest defaults.
|
||||
func Exposure(m Manifest, layers []Layer) (map[int]string, error) {
|
||||
listened := make(map[int]bool, len(m.Listens))
|
||||
for _, l := range m.Listens {
|
||||
|
||||
@@ -580,7 +580,7 @@ func named(r Resolution) []string {
|
||||
}
|
||||
|
||||
// A port's source is a per-node setting, not a manifest constant: the same module is internal on
|
||||
// one machine and public on another (novox/hq ADR 0051). postgres listens from the mesh by default;
|
||||
// one machine and public on another (novox/hq ADR 0046). postgres listens from the mesh by default;
|
||||
// a setting on one node exposes it to anywhere, and the rule set follows.
|
||||
func TestExposureSettingOverridesAListensSource(t *testing.T) {
|
||||
postgres := Manifest{Module: "postgres", Version: "1",
|
||||
@@ -605,7 +605,7 @@ func TestExposureSettingOverridesAListensSource(t *testing.T) {
|
||||
}
|
||||
|
||||
// Exposure refuses a setting that names a port the module does not listen on, or a source that is
|
||||
// not a real one — a setting reaching nothing is worse than none (novox/hq ADR 0048/0051).
|
||||
// not a real one — a setting reaching nothing is worse than none (novox/hq ADR 0043/0046).
|
||||
func TestExposureRefusesAPortNotListenedOnAndABadSource(t *testing.T) {
|
||||
postgres := Manifest{Module: "postgres", Listens: []Listening{{Port: 5432, From: FromMesh}}}
|
||||
layer := func(port, source string) []Layer {
|
||||
|
||||
@@ -35,7 +35,7 @@ var identityUnusable = regexp.MustCompile(`[^a-z0-9_]+`)
|
||||
const IdentityPrefix = "mesh_"
|
||||
|
||||
// IdentitySource is the name the mesh derives a consumer's identity from: the module's slug when it
|
||||
// has declared one, otherwise its name (novox/hq ADR 0054). A module with a name short enough to fit
|
||||
// has declared one, otherwise its name (novox/hq ADR 0049). A module with a name short enough to fit
|
||||
// the tightest backend needs no slug; one whose name would overflow declares a short legible one.
|
||||
func IdentitySource(slug, name string) string {
|
||||
if slug != "" {
|
||||
@@ -59,7 +59,7 @@ func ConsumerIdentity(node, module string) string {
|
||||
// identityLimit is the shortest identifier limit among the systems these names reach: an S3 access
|
||||
// key's 20 (novox/hq 04-ISSUES/010). PostgreSQL keeps 63 and MinIO 20, so 20 is the one that binds —
|
||||
// the comment used to name PostgreSQL and was wrong. A name over it is refused, with the remedy a
|
||||
// short slug (ADR 0054), not silently cut to fit.
|
||||
// short slug (ADR 0049), not silently cut to fit.
|
||||
const identityLimit = 20
|
||||
|
||||
// CheckIdentity refuses an identity that would not fit the tightest backend a consumer reaches.
|
||||
@@ -68,7 +68,7 @@ const identityLimit = 20
|
||||
// the statement succeeds, so two consumers agreeing for the first N bytes would become one login
|
||||
// (04-ISSUES/022) — and S3 refuses outright. Refused here, at the mesh, because the mesh chose the
|
||||
// name and is the only thing that can choose another. The remedy is a first-class one: give the
|
||||
// module a short `slug` (ADR 0054), or shorten the machine's name.
|
||||
// module a short `slug` (ADR 0049), or shorten the machine's name.
|
||||
func CheckIdentity(node, module string) error {
|
||||
got := ConsumerIdentity(node, module)
|
||||
if len(got) <= identityLimit {
|
||||
|
||||
@@ -6,7 +6,7 @@ import "testing"
|
||||
|
||||
func TestASlugIsPreferredOverTheModuleName(t *testing.T) {
|
||||
// A module that declared a slug is identified by it, so a long name can be made to fit the
|
||||
// tightest backend without a hash (novox/hq ADR 0054).
|
||||
// tightest backend without a hash (novox/hq ADR 0049).
|
||||
if got := IdentitySource("kc", "keycloak"); got != "kc" {
|
||||
t.Errorf("the slug was not preferred: %q", got)
|
||||
}
|
||||
|
||||
@@ -120,7 +120,7 @@ type Manifest struct {
|
||||
Version string `json:"version,omitempty"`
|
||||
|
||||
// Slug is a short identifier the mesh uses in place of the module name when it derives a
|
||||
// consumer's login (novox/hq ADR 0054). Optional: a module with a short name needs none. It
|
||||
// consumer's login (novox/hq ADR 0049). Optional: a module with a short name needs none. It
|
||||
// exists because `mesh_<node>_<module>` must fit the tightest backend a consumer reaches — an S3
|
||||
// access key is 20 characters — and a long module name would overflow it. A person choosing
|
||||
// `kc` for keycloak keeps the identity legible where a hash would not.
|
||||
@@ -135,7 +135,7 @@ type Manifest struct {
|
||||
|
||||
// Emits are the event types this module publishes onto the broker — dotted topic keys, e.g.
|
||||
// "module.umami.site.created". Declared so the mesh knows the event graph; events are
|
||||
// provisioning's lighter sibling — 1:many and broadcast, no credential (novox/hq ADR 0046).
|
||||
// provisioning's lighter sibling — 1:many and broadcast, no credential (novox/hq ADR 0041).
|
||||
Emits []string `json:"emits,omitempty"`
|
||||
|
||||
// Consumes are the event patterns this module subscribes to — topic patterns over module,
|
||||
@@ -474,7 +474,7 @@ func ParseManifest(raw []byte) (Manifest, error) {
|
||||
problems = append(problems, fmt.Sprintf(
|
||||
"%q is not a usable module name: lower-case letters, digits, dashes and dots", m.Module))
|
||||
}
|
||||
// A slug is a short identifier the mesh derives a login from (novox/hq ADR 0054). The same
|
||||
// A slug is a short identifier the mesh derives a login from (novox/hq ADR 0049). The same
|
||||
// charset as a name; its length is checked against a backend's limit at assignment, where the
|
||||
// node it joins is known — a slug that is fine on one machine's short name can overflow another's.
|
||||
if m.Slug != "" && !name.MatchString(m.Slug) {
|
||||
|
||||
@@ -172,7 +172,7 @@ func UnusedSettings(m Manifest, layers []Layer) []string {
|
||||
for _, layer := range layers {
|
||||
for key := range layer.Values {
|
||||
// `expose` is a real destination for a module that listens: it overrides a port's
|
||||
// source (novox/hq ADR 0051), validated in Exposure, so it is not stray here.
|
||||
// source (novox/hq ADR 0046), validated in Exposure, so it is not stray here.
|
||||
if key == ExposeSetting && len(m.Listens) > 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -56,7 +56,7 @@ func Make(consumerKey, providerKey string) (Sealed, error) {
|
||||
|
||||
// 30 bytes, not 32: base64url of 30 is exactly 40 characters, and 40 is the longest secret an
|
||||
// S3 access key accepts (8–40), the tightest of the backends a minted password reaches — the same
|
||||
// "fit the tightest backend" rule ADR 0054 sets for the login, on the secret. 240 bits is ample.
|
||||
// "fit the tightest backend" rule ADR 0049 sets for the login, on the secret. 240 bits is ample.
|
||||
value := make([]byte, 30)
|
||||
if _, err := rand.Read(value); err != nil {
|
||||
return Sealed{}, err
|
||||
|
||||
Reference in New Issue
Block a user