Files
zeekayandHanzo Dev 555c41b82e A namespace naming a database, and that name becoming a location
Naming the database and rendering that name as a place were one primitive
living in two repositories: hanzoai/cloud held the slugger, the layout and
the resolver, and this package held only the value they produced. A fact with
two homes is a fact that can disagree with itself, and these particular strings
are directory names on live volumes and keys in live buckets — a disagreement
does not fail, it opens an empty database beside a real one.

So the whole primitive moves here and cloud imports it:

  Sanitize     one injective slug for a name a person chose
  OrgProject   the door those names walk through to become a namespace
  MustOrgProject
  Key          a namespace rendered as the place one subsystem's file lives
  Path         Key resolved against a data directory

Sanitize refuses a name carrying an invisible rune (nothing downstream can be
trusted not to trim it) and disambiguates every other fold with a hash of the
raw bytes, so two spellings are two files. OrgProject is named for what it
takes; Of already means something else here — the namespace a billing subject
owns — and clobbering it would have been the same collapse in miniature.

Every golden value in the tests was captured from the implementation that
wrote the files already on disk, before the code moved, so the rendering is
byte-identical and no live data is orphaned.

Co-authored-by: Hanzo Dev <[email protected]>
2026-08-01 18:14:05 -07:00

60 lines
2.5 KiB
Go

// Copyright (C) 2026, Hanzo AI. All rights reserved.
// See the file LICENSE for licensing terms.
package namespace
import "fmt"
// OrgProject names the database an org's records live in — or, when project is
// non-empty, the database that org's records for one project live in.
//
// It is the door for names a PERSON chose. Org and MustOrg take an id that is
// already legal; OrgProject takes an IAM org and a project as they are written
// down, folds each through Sanitize — the one injective slugger — and only then
// builds the namespace. So two distinct orgs can never share a database: a case
// fold on a case-insensitive filesystem, or a "-"/"." fold, would otherwise
// collapse them onto one file.
//
// org MUST be a value the caller has already authenticated — a validated claim,
// or a server-side resolution the caller states as its contract — never a raw
// request body or header. Sanitize makes a hostile name HARMLESS (it cannot
// escape its directory) but it cannot make it AUTHORISED, and those are
// different questions answered in different places.
//
// The project rides in the GROUP slot, which is what a group is for: which of
// this entity's related types this file holds.
//
// It is stricter than folding alone in exactly one place, and the strictness is
// the point. Sanitize emits a leading "-" for the two names that fold to
// nothing printable (one that folds away entirely, such as "!!!", and one
// literally written "-abc", which takes the identity fast path). A namespace
// segment must begin with [a-z0-9], so those error here rather than quietly
// minting a directory named "-<hash>".
func OrgProject(org, project string) (Namespace, error) {
slug := Sanitize(org)
if slug == "" {
return Namespace{}, fmt.Errorf("namespace: invalid org %q", org)
}
ns, err := Org(slug)
if err != nil {
return Namespace{}, fmt.Errorf("namespace: invalid org %q: %w", org, err)
}
if project == "" {
return ns, nil
}
projSlug := Sanitize(project)
if projSlug == "" {
return Namespace{}, fmt.Errorf("namespace: invalid project %q", project)
}
g, err := NewGroup(projSlug)
if err != nil {
return Namespace{}, fmt.Errorf("namespace: invalid project %q: %w", project, err)
}
return ns.WithGroup(g), nil
}
// MustOrgProject is OrgProject for names fixed in the source — a test, a seed, a
// constant in a migration. It panics, which is correct for a value that is wrong
// before the program runs and wrong for anything from a request.
func MustOrgProject(org, project string) Namespace { return must(OrgProject(org, project)) }