- Three inbound routes from docs/hub-api.md: POST /ask, POST /approve, GET /health. - Five-step pipeline in src/hub, with packages hear, understand, check, route, answer. - Routing through OmniRoute with the capability list and routing.md. The never-route control check runs before any LLM call. - One real destination: Jervis house TTS, started out of band. - Fake /ask project (src/tests/fakeproject) for the round-trip test. - Append-only, size-capped request log. Bearer auth, source match, rate limit. - Tests: all packages pass, including the fake /ask round trip. Race clean. - Dockerfile and compose updated to mount routing.md and secrets, log on tmpfs. Co-Authored-By: Paperclip <noreply@paperclip.ing>
88 lines
2.0 KiB
Go
88 lines
2.0 KiB
Go
// Package ids makes the request and approval identifiers from docs/hub-api.md.
|
|
// The shape is a ULID: 26 Crockford base32 characters, time-ordered. It needs
|
|
// no dependency, so the Hub stays a small static binary.
|
|
package ids
|
|
|
|
import (
|
|
"crypto/rand"
|
|
"sync"
|
|
"time"
|
|
)
|
|
|
|
// Crockford base32 alphabet. ULID uses these 32 symbols.
|
|
const alphabet = "0123456789ABCDEFGHJKMNPQRSTVWXYZ"
|
|
|
|
var (
|
|
mu sync.Mutex
|
|
lastMS uint64
|
|
lastRnd [10]byte
|
|
)
|
|
|
|
// New returns a 26-character ULID string. The first 10 characters hold the
|
|
// time. The last 16 hold randomness. Two calls in the same millisecond do not
|
|
// repeat.
|
|
func New() string {
|
|
mu.Lock()
|
|
defer mu.Unlock()
|
|
|
|
ms := uint64(time.Now().UnixMilli())
|
|
if ms == lastMS {
|
|
// Same millisecond: increment the random part.
|
|
for i := len(lastRnd) - 1; i >= 0; i-- {
|
|
lastRnd[i]++
|
|
if lastRnd[i] != 0 {
|
|
break
|
|
}
|
|
}
|
|
} else {
|
|
lastMS = ms
|
|
if _, err := rand.Read(lastRnd[:]); err != nil {
|
|
// crypto/rand does not fail in practice. Fall back to the clock.
|
|
for i := range lastRnd {
|
|
lastRnd[i] = byte(ms >> (8 * (i % 8)))
|
|
}
|
|
}
|
|
}
|
|
|
|
return encodeTime(ms) + encodeRandom(lastRnd)
|
|
}
|
|
|
|
// Approval returns an approval id, for example "ap_01J8Z...".
|
|
func Approval() string {
|
|
return "ap_" + New()
|
|
}
|
|
|
|
// encodeTime writes 10 base32 characters from the low 50 bits of ms.
|
|
func encodeTime(ms uint64) string {
|
|
var out [10]byte
|
|
for i := 9; i >= 0; i-- {
|
|
out[i] = alphabet[ms&0x1f]
|
|
ms >>= 5
|
|
}
|
|
return string(out[:])
|
|
}
|
|
|
|
// encodeRandom writes 16 base32 characters from 10 random bytes (80 bits).
|
|
func encodeRandom(rnd [10]byte) string {
|
|
var out [16]byte
|
|
var hi, lo uint64
|
|
hi = uint64(rnd[0])<<8 | uint64(rnd[1])
|
|
hi = hi<<8 | uint64(rnd[2])
|
|
hi = hi<<8 | uint64(rnd[3])
|
|
hi = hi<<8 | uint64(rnd[4])
|
|
lo = uint64(rnd[5])<<8 | uint64(rnd[6])
|
|
lo = lo<<8 | uint64(rnd[7])
|
|
lo = lo<<8 | uint64(rnd[8])
|
|
lo = lo<<8 | uint64(rnd[9])
|
|
|
|
for i := 15; i >= 8; i-- {
|
|
out[i] = alphabet[lo&0x1f]
|
|
lo >>= 5
|
|
}
|
|
for i := 7; i >= 0; i-- {
|
|
out[i] = alphabet[hi&0x1f]
|
|
hi >>= 5
|
|
}
|
|
return string(out[:])
|
|
}
|