package suite import ( "fmt" "strings" ) // SubjectKind says how to read an asker subject's value. Doctrine §3: the // meaning is shape-dependent and nothing else on the wire says which shape // the pair is in, so a receiver that does not key on the kind it is sent // answers UNKNOWN — loudly — rather than failing to match and answering // NOT_VISIBLE, which callers are entitled to act on silently. type SubjectKind string const ( // SubjectPersonRef is the assertion's opaque `sub` (home §5.3). Suite // shape, where the home is the identity authority. SubjectPersonRef SubjectKind = "person_ref" // SubjectAddress is a lowercased address. Lone shape, where the instance // keys its own members on the address it signed them in with. SubjectAddress SubjectKind = "address" ) // Subject is the named asker an S2 or S6 call carries beside its consumer // token. S3 carries none, by design (§6). type Subject struct { Kind SubjectKind `json:"kind"` Value string `json:"value"` } // NewSubject builds a subject, refusing anything a receiver could not key on // and normalising what it can. func NewSubject(kind SubjectKind, value string) (Subject, error) { switch kind { case SubjectPersonRef: // Opaque. Case may be significant, so it is passed through untouched. case SubjectAddress: // Lowercased, because that is what a lone instance stores. Sending // mixed case to a receiver holding the lowercased form misses, and a // miss answers NOT_VISIBLE — the silent value — for what is really a // normalisation bug. value = strings.ToLower(value) default: return Subject{}, fmt.Errorf("suite: unknown asker subject kind %q", kind) } if value == "" { return Subject{}, fmt.Errorf("suite: empty asker subject value for kind %q", kind) } return Subject{Kind: kind, Value: value}, nil } // MaxS2Pairs caps one S2 question. 256 is memoria §6.1's number, carried here // with the contract: it bounds the work a sibling can be made to do in one // call, and it bounds the size of a response the total decoder has to keep in // memory before it can answer. const MaxS2Pairs = 256 // CheckS2Batch refuses an over-cap question rather than truncating it. A // silently shortened question produces a confident answer about the items // that survived; the ones dropped decode as UNKNOWN and nobody learns why. func CheckS2Batch(pairs []ItemRevision) error { if len(pairs) > MaxS2Pairs { return fmt.Errorf("suite: %d pairs exceeds the S2 cap of %d", len(pairs), MaxS2Pairs) } return nil }