Files
iceBear67andClaude Opus 5 051efe8fec grok-glance: web control plane for grok's /rc remote control
A single Go binary that grok dials out to over a WebSocket, and a HeroUI
web UI for driving the session it is attached to.

The roles are inverted relative to the terminal: over the /rc link grok
is the ACP Agent and glance is the Client. That makes glance a stock ACP
client and the web Stop button a real session/cancel rather than a
bespoke control message.

Both notification rails are mirrored. The stable session/update rail
carries correctness; x.ai/session_notification is presentation only and
degrades rather than erroring, because its ~60 variants are grok
internal and drift with every upstream sync. _meta is forwarded
byte for byte so viewers can dedup and order.

Permissions race: the terminal and any browser may answer, first
responder wins, and the loser's UI retracts by itself. All three
interaction methods go through that path, not just permissions.

Auth is TOTP only, with no accounts to have. A bootstrap token printed
at first start gates /setup, which is a 404 without it; state lives in
one 0600 JSON file and history in an in-memory ring, so there is no
database and no recovery story beyond deleting the file.

ARCHITECTURE.md covers the topology and the limits of that auth model;
CLAUDE.md covers building, the fakeagent loop, and the end-to-end
checklist that unit tests cannot replace.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-15 10:07:33 +00:00

268 lines
6.9 KiB
Go

package state
import (
"encoding/json"
"os"
"path/filepath"
"strings"
"testing"
)
func open(t *testing.T) *Store {
t.Helper()
store, err := Open(t.TempDir())
if err != nil {
t.Fatalf("Open: %v", err)
}
return store
}
func TestOpenCreatesPrivateStateAndSurvivesReopen(t *testing.T) {
dir := t.TempDir()
store, err := Open(dir)
if err != nil {
t.Fatalf("Open: %v", err)
}
info, err := os.Stat(store.Path())
if err != nil {
t.Fatalf("state file not written: %v", err)
}
// The file holds the TOTP secret and the cookie-signing key. Group- or
// world-readable would make every other precaution in this package pointless.
if perm := info.Mode().Perm(); perm != 0o600 {
t.Fatalf("state file mode = %o, want 600", perm)
}
key := append([]byte(nil), store.SessionKey()...)
reopened, err := Open(dir)
if err != nil {
t.Fatalf("reopen: %v", err)
}
if string(reopened.SessionKey()) != string(key) {
t.Fatal("session key changed across reopen; every cookie would be invalidated")
}
}
func TestOpenRefusesAFutureVersion(t *testing.T) {
dir := t.TempDir()
path := filepath.Join(dir, "state.json")
blob, _ := json.Marshal(map[string]any{"version": Version + 1})
if err := os.WriteFile(path, blob, 0o600); err != nil {
t.Fatal(err)
}
// Silently "upgrading" a file we do not understand would drop fields a newer
// build wrote. Refusing keeps a downgrade from being destructive.
if _, err := Open(dir); err == nil {
t.Fatal("expected a refusal for a newer state file")
}
}
func TestEnrollmentBurnsTheBootstrapToken(t *testing.T) {
store := open(t)
if store.Enrolled() {
t.Fatal("a fresh store should not be enrolled")
}
token, err := store.NewBootstrapToken()
if err != nil {
t.Fatalf("NewBootstrapToken: %v", err)
}
if !store.BootstrapValid(token) {
t.Fatal("a freshly minted token should be valid")
}
if store.BootstrapValid(token + "x") {
t.Fatal("a wrong token was accepted")
}
if err := store.EnrollTOTP("SECRET", "grok-glance", "operator"); err != nil {
t.Fatalf("EnrollTOTP: %v", err)
}
if !store.Enrolled() {
t.Fatal("Enrolled should report true after enrollment")
}
// This is the whole point of the gate: once setup succeeds, the token that
// opened it must never open it again.
if store.BootstrapValid(token) {
t.Fatal("the bootstrap token still works after enrollment")
}
if store.BootstrapPending() {
t.Fatal("BootstrapPending should be false after enrollment")
}
}
func TestBootstrapStateOutlivesTheProcess(t *testing.T) {
dir := t.TempDir()
store, err := Open(dir)
if err != nil {
t.Fatal(err)
}
token, err := store.NewBootstrapToken()
if err != nil {
t.Fatal(err)
}
if err := store.EnrollTOTP("SECRET", "grok-glance", "operator"); err != nil {
t.Fatal(err)
}
// A restart must not reopen the enrollment window.
reopened, err := Open(dir)
if err != nil {
t.Fatal(err)
}
if reopened.BootstrapValid(token) {
t.Fatal("a spent bootstrap token came back after a restart")
}
if !reopened.Enrolled() {
t.Fatal("enrollment did not persist")
}
}
func TestAPIKeyLifecycle(t *testing.T) {
store := open(t)
plaintext, key, err := store.AddAPIKey("laptop")
if err != nil {
t.Fatalf("AddAPIKey: %v", err)
}
if !strings.HasPrefix(plaintext, "glance_sk_") {
t.Fatalf("key %q lacks the glance_sk_ prefix that makes it greppable in a leak", plaintext)
}
// Only the hash is kept, so a stolen state.json does not yield usable keys.
blob, err := os.ReadFile(store.Path())
if err != nil {
t.Fatal(err)
}
if strings.Contains(string(blob), plaintext) {
t.Fatal("the plaintext API key was written to disk")
}
found := store.LookupAPIKey(plaintext)
if found == nil || found.ID != key.ID {
t.Fatalf("LookupAPIKey did not find the key it just minted")
}
if store.LookupAPIKey("glance_sk_nonsense") != nil {
t.Fatal("an unknown key was accepted")
}
if store.LookupAPIKey("") != nil {
t.Fatal("an empty key was accepted")
}
store.TouchAPIKey(key.ID)
keys := store.ListAPIKeys()
if len(keys) != 1 {
t.Fatalf("ListAPIKeys returned %d keys, want 1", len(keys))
}
if keys[0].LastSeen == nil {
t.Fatal("TouchAPIKey did not record a last-seen time")
}
if keys[0].Hash != "" {
t.Fatal("ListAPIKeys leaked the stored hash to its caller")
}
removed, err := store.RemoveAPIKey("laptop")
if err != nil || !removed {
t.Fatalf("RemoveAPIKey by name: removed=%v err=%v", removed, err)
}
if store.LookupAPIKey(plaintext) != nil {
t.Fatal("a revoked key still authenticates")
}
removed, err = store.RemoveAPIKey("laptop")
if err != nil || removed {
t.Fatalf("removing a missing key should be a no-op: removed=%v err=%v", removed, err)
}
}
func TestAPIKeysAreDistinct(t *testing.T) {
store := open(t)
seen := make(map[string]bool)
for i := 0; i < 16; i++ {
plaintext, _, err := store.AddAPIKey("k")
if err != nil {
t.Fatal(err)
}
if seen[plaintext] {
t.Fatal("AddAPIKey repeated a key")
}
seen[plaintext] = true
}
}
// `glance apikey add` runs in a second process against the state directory of a
// server that is already up. The server has to see that key without a restart,
// and must not persist its own older snapshot over it afterwards -- so this
// exercises both directions with two Stores on one file.
func TestASecondProcessCanAddKeysToARunningServer(t *testing.T) {
dir := t.TempDir()
server, err := Open(dir)
if err != nil {
t.Fatalf("Open server: %v", err)
}
existing, _, err := server.AddAPIKey("first")
if err != nil {
t.Fatal(err)
}
cli, err := Open(dir)
if err != nil {
t.Fatalf("Open cli: %v", err)
}
added, _, err := cli.AddAPIKey("added-while-running")
if err != nil {
t.Fatal(err)
}
if server.LookupAPIKey(added) == nil {
t.Fatal("the running server does not see a key added by the CLI")
}
// The server writing afterwards must not resurrect its startup snapshot.
server.TouchAPIKey(server.LookupAPIKey(added).ID)
reopened, err := Open(dir)
if err != nil {
t.Fatalf("reopen: %v", err)
}
if reopened.LookupAPIKey(added) == nil {
t.Fatal("a later server write dropped the CLI's key")
}
if reopened.LookupAPIKey(existing) == nil {
t.Fatal("the CLI's write dropped the server's earlier key")
}
if got := len(reopened.ListAPIKeys()); got != 2 {
t.Fatalf("keys = %d, want 2", got)
}
}
// The same hazard for `glance bootstrap`: a token minted while the server is up
// has to be accepted by that server.
func TestASecondProcessCanMintABootstrapToken(t *testing.T) {
dir := t.TempDir()
server, err := Open(dir)
if err != nil {
t.Fatal(err)
}
if server.BootstrapPending() {
t.Fatal("a fresh store should have no pending token")
}
cli, err := Open(dir)
if err != nil {
t.Fatal(err)
}
token, err := cli.NewBootstrapToken()
if err != nil {
t.Fatal(err)
}
if !server.BootstrapPending() {
t.Fatal("the running server does not see the new token as pending")
}
if !server.BootstrapValid(token) {
t.Fatal("the running server rejects a token the CLI just minted")
}
}