Give claude-code's runtime its forge credential file, and say a credential it cannot place as a condition (issue 774)
The file gitea-read.secret was declared with no owner, so it was root's at 0600; claude-code's tools run in the
node's runtime as the operator's account and could not read it ("permission denied", ~870 times a machine), said
only in the journal. A file a bundle reads is the runtime account's only when a word of the bundle names it
(catalogue givenTo); this one was found through MESH_CLAUDE_CODE_STATE alone.
Quick fix: name the file with the bundle word MESH_CLAUDE_CODE_FORGE, which the reader now uses. Thorough fix: the
same, plus a failure to place the credential raises a warning condition of the module on the operator-channel seat,
once per reason, cleared when it places again. Chose thorough: the fault ran unseen for days. A change of the
credential now restarts the node's runtime, as for every file a bundle word names.
claude-code's mesh-sdk pin moves to 2da0de75 (main's), the first with asks.Condition.
This commit is contained in:
@@ -2,6 +2,7 @@ package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"net/http"
|
||||
"net/http/cgi"
|
||||
"net/http/httptest"
|
||||
@@ -10,6 +11,8 @@ import (
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"git.novox.be/novox/mesh-sdk/go/asks"
|
||||
)
|
||||
|
||||
const agentToken = "0123456789abcdef0123456789abcdef01234567"
|
||||
@@ -260,3 +263,72 @@ func TestForgeCredentialLinesAreReadStrictly(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The forge credential's file is named by a word of the bundle, so the runtime's account owns it and reads it (the
|
||||
// sweep of 2026-10-30: root's 0600 file, permission denied ~870 times a machine), and a failure to place it is said to
|
||||
// the operator as a condition, raised once per reason, cleared when it places again, and cleared at a first good look.
|
||||
func TestTheForgeCredentialIsTheRuntimesAndItsFailureIsAConditionOfTheModule(t *testing.T) {
|
||||
raw, err := os.ReadFile("../../module.json")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
var m struct {
|
||||
Secrets map[string]string `json:"secrets"`
|
||||
Build struct {
|
||||
Artifacts []struct {
|
||||
Kind string `json:"kind"`
|
||||
Env map[string]string `json:"env"`
|
||||
} `json:"artifacts"`
|
||||
} `json:"build"`
|
||||
}
|
||||
if err := json.Unmarshal(raw, &m); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
named := false
|
||||
for _, a := range m.Build.Artifacts {
|
||||
if a.Kind == "bundle" && a.Env["MESH_CLAUDE_CODE_FORGE"] == m.Secrets[ForgeProvision] && m.Secrets[ForgeProvision] != "" {
|
||||
named = true
|
||||
}
|
||||
}
|
||||
if !named {
|
||||
t.Errorf("no word of the bundle names the forge credential's file %q: the runtime cannot read it", m.Secrets[ForgeProvision])
|
||||
}
|
||||
p, _ := PathsFrom(envMap{"MESH_CLAUDE_CODE_STATE": "/s", "MESH_CLAUDE_CODE_FORGE": "/s/forge"}.get)
|
||||
if p.forgeSecret() != "/s/forge" {
|
||||
t.Errorf("the word is not read: %s", p.forgeSecret())
|
||||
}
|
||||
|
||||
f := &ForgeHealth{Node: "anchor"}
|
||||
denied := errors.New("the forge's read credential cannot be read: open /s/gitea-read.secret: permission denied")
|
||||
for i, row := range []struct {
|
||||
err error
|
||||
want string // "" nothing said, else the state said
|
||||
}{
|
||||
{nil, "cleared"}, // a first good look clears what a run before raised
|
||||
{nil, ""},
|
||||
{denied, "raised"},
|
||||
{denied, ""},
|
||||
{errors.New("the forge credential could not be written: disk full"), "raised"},
|
||||
{nil, "cleared"},
|
||||
{nil, ""},
|
||||
} {
|
||||
c := f.Look(row.err)
|
||||
got := ""
|
||||
if c != nil {
|
||||
got = c.State
|
||||
if _, err := asks.EncodeCondition(*c); err != nil {
|
||||
t.Errorf("look %d: the condition is refused: %v", i, err)
|
||||
}
|
||||
if c.Key != "claude-code.machine.anchor.forge-credential" {
|
||||
t.Errorf("look %d: key %s", i, c.Key)
|
||||
}
|
||||
}
|
||||
if got != row.want {
|
||||
t.Errorf("look %d: said %q, want %q", i, got, row.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type envMap map[string]string
|
||||
|
||||
func (e envMap) get(k string) string { return e[k] }
|
||||
|
||||
Reference in New Issue
Block a user