Skip to content

Commit dbd113b

Browse files
fix(env): the output environment carries a configuration document carrier (#1260)
* fix(env): the output environment carries a configuration document carrier A native run refused to start saas/auth-gateway with "output environment record 51: its name is not uppercase ASCII, digits and underscores". The record is CODEFLY__CONFIGURATION_DOCUMENT_V1__<sha256>, the workspace's fixture-composition document: core names a structured configuration document's carrier with a lowercase hex digest (resources.ConfigurationDocumentKey), and validOutputKey mirrors a reader grammar that admitted only [A-Z0-9_]. Admit exactly the document carrier core mints (a document prefix and 64 lowercase hex digits), as the reader now does (codefly-dev/sdk-go#95). The name is kept, not upper-cased in core, because every consumer finds the document by calling that same function and looking the result up exactly. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * refactor(env): take the document digest length from core Mirror codefly-dev/sdk-go#95: read the digest length from core's own ConfigurationDocumentKey instead of spelling a SHA-256 length here, so the writer and the reader derive the shape the same way. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> --------- Co-authored-by: Antoine Toussaint <312536060+antoinetoussaint-byte@users.noreply.github.com> Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
1 parent 50ae44d commit dbd113b

3 files changed

Lines changed: 94 additions & 30 deletions

File tree

‎docs/commands.md‎

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -225,12 +225,14 @@ configuration.
225225
Two consequences worth stating plainly. An override whose key is not a
226226
`CODEFLY__` carrier (`--set FOO=bar`) now refuses the export rather than
227227
writing a file the reader would reject wholesale. And a **structured
228-
configuration document** cannot be exported this way at all: its carrier is a
229-
prefix plus a lowercase hex SHA256, which that grammar does not admit, so the
230-
reader has never been able to load such a file. The CLI now says so before
231-
writing instead of reporting success. Making it exportable needs an uppercase
232-
document carrier in core, or a reader in the SDK that accepts this one; neither
233-
is the CLI's to decide.
228+
configuration document** is the one carrier outside `[A-Z0-9_]` the format
229+
admits: its name is a document prefix plus a lowercase hex SHA-256, exactly as
230+
core mints it (`resources.ConfigurationDocumentKey`), and the reader (sdk-go
231+
`LoadRuntimeEnvironmentFile`) admits that shape and no other lowercase name.
232+
The name is kept rather than upper-cased because every consumer finds the
233+
document by calling that same core function and looking the result up
234+
exactly; a different spelling would be missed by every consumer built on a
235+
core that mints this one.
234236

235237
**The delivery host's federation surface.** In a host environment the native
236238
run hands the service `host.delivery` names, when it declares the `federation`

‎pkg/orchestration/output_records.go‎

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,9 @@ type outputRecord struct {
2323
//
2424
// The reader (sdk-go LoadRuntimeEnvironmentFile) scans lines, strips one
2525
// trailing CR, cuts at the FIRST `=`, and refuses the whole file if any name
26-
// is not `CODEFLY__` plus at least one character of [A-Z0-9_]. So this is
26+
// is neither `CODEFLY__` plus at least one character of [A-Z0-9_] nor a
27+
// structured configuration document's carrier exactly as core mints it
28+
// (validOutputKey). So this is
2729
// transport grammar, not a second semantic carrier matcher, and it applies to
2830
// EVERY record in the batch — bound artifact, ordinary value, override,
2931
// identity, fixture, endpoint alike. A value carrying LF is the reason: the
@@ -61,6 +63,9 @@ func validOutputKey(key string) error {
6163
if !strings.HasPrefix(key, prefix) || len(key) == len(prefix) {
6264
return fmt.Errorf("its name is not a CODEFLY__ carrier, so the supported reader refuses the whole artifact")
6365
}
66+
if isConfigurationDocumentCarrier(key) {
67+
return nil
68+
}
6469
for index := 0; index < len(key); index++ {
6570
character := key[index]
6671
if (character < 'A' || character > 'Z') && (character < '0' || character > '9') && character != '_' {
@@ -70,6 +75,41 @@ func validOutputKey(key string) error {
7075
return nil
7176
}
7277

78+
// configurationDocumentDigestLength is the length of the digest core appends
79+
// to a document prefix, read from core's own key rather than recomputed.
80+
var configurationDocumentDigestLength = len(resources.ConfigurationDocumentKey("", "", "", false)) - len(resources.ConfigurationDocumentPrefix)
81+
82+
// isConfigurationDocumentCarrier reports a structured configuration
83+
// document's carrier exactly as core mints it (resources.ConfigurationDocumentKey):
84+
// one of the two document prefixes and a lowercase hex SHA-256, nothing else.
85+
//
86+
// It is the one name outside [A-Z0-9_] the format carries. The digest is
87+
// lowercase because core spells it so, and every consumer finds the document by
88+
// calling that same function and looking the result up exactly; an uppercase
89+
// carrier would be missed by every consumer built on a core that mints the
90+
// lowercase one. So the grammar admits the name core mints rather than the
91+
// name being changed under its readers, and it admits only that shape: a
92+
// lowercase letter anywhere else is still refused.
93+
func isConfigurationDocumentCarrier(key string) bool {
94+
for _, prefix := range []string{resources.ConfigurationDocumentPrefix, resources.SecretConfigurationDocumentPrefix} {
95+
digest, found := strings.CutPrefix(key, prefix)
96+
if !found {
97+
continue
98+
}
99+
if len(digest) != configurationDocumentDigestLength {
100+
return false
101+
}
102+
for index := 0; index < len(digest); index++ {
103+
character := digest[index]
104+
if (character < '0' || character > '9') && (character < 'a' || character > 'f') {
105+
return false
106+
}
107+
}
108+
return true
109+
}
110+
return false
111+
}
112+
73113
// validOutputValue refuses the delimiters this format cannot represent. An
74114
// empty value and a value holding one or many `=` are representable: the
75115
// reader cuts at the first separator and keeps the rest verbatim.

‎pkg/orchestration/output_records_test.go‎

Lines changed: 45 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -111,15 +111,22 @@ func TestRefusalDiagnosticsCarryNoValuesOrMalformedKeys(t *testing.T) {
111111
require.NotContains(t, err.Error(), "secret-value")
112112
}
113113

114-
// readerGrammar is sdk-go v0.4.0's validRuntimeEnvironmentName, transcribed
115-
// for this test ONLY.
114+
// readerGrammar is sdk-go's validRuntimeEnvironmentName as of
115+
// codefly-dev/sdk-go#95, transcribed for this test ONLY: CODEFLY__ plus
116+
// [A-Z0-9_], or a configuration document's carrier — a document prefix and
117+
// exactly 64 lowercase hex digits.
116118
//
117119
// The CLI does not depend on root sdk-go and this must not make it: the
118120
// production check lives in validOutputKey, and the agreement between the two
119121
// is itself asserted below, so a divergence shows up here rather than in a
120122
// run. A roundtrip through the real reader is a separate, out-of-process
121123
// check against a built binary.
122124
func readerGrammar(name string) bool {
125+
for _, prefix := range []string{"CODEFLY__CONFIGURATION_DOCUMENT_V1__", "CODEFLY__SECRET_CONFIGURATION_DOCUMENT_V1__"} {
126+
if digest, found := strings.CutPrefix(name, prefix); found && len(digest) == 64 && strings.Trim(digest, "0123456789abcdef") == "" {
127+
return true
128+
}
129+
}
123130
if !strings.HasPrefix(name, "CODEFLY__") || len(name) == len("CODEFLY__") {
124131
return false
125132
}
@@ -144,6 +151,16 @@ func TestTheWriterAgreesWithTheReadersGrammar(t *testing.T) {
144151
"CODEFLY__", "CODEFLY", "codefly__A", "CODEFLY__a", "CODEFLY__A-B", "CODEFLY__A B",
145152
"CODEFLY__A=B", "CODEFLY__A\nB", "CODEFLY__Ä", "", "NOTE",
146153
resources.ConfigurationDocumentKey(resources.ConfigurationWorkspace, "fixture-composition", "local", false),
154+
resources.ConfigurationDocumentKey("saas/accounts", "fixture-composition", "local", true),
155+
// The document shape admits its own digest and nothing near it.
156+
resources.ConfigurationDocumentPrefix + strings.Repeat("a", 63),
157+
resources.ConfigurationDocumentPrefix + strings.Repeat("a", 65),
158+
resources.ConfigurationDocumentPrefix + strings.Repeat("g", 64),
159+
resources.ConfigurationDocumentPrefix + strings.Repeat("a", 63) + "-",
160+
resources.ConfigurationDocumentPrefix + strings.Repeat("A", 64),
161+
resources.ConfigurationDocumentPrefix,
162+
"CODEFLY__CONFIGURATION_DOCUMENT_V2__" + strings.Repeat("a", 64),
163+
"CODEFLY__WORKSPACE_CONFIGURATION__" + strings.Repeat("a", 64),
147164
} {
148165
require.Equal(t, readerGrammar(name), validOutputKey(name) == nil,
149166
"the writer and the supported reader disagree about a carrier name")
@@ -201,20 +218,15 @@ func TestTheWriterRefusesMalformedRetainedRecords(t *testing.T) {
201218
}
202219
}
203220

204-
// TestAStructuredConfigurationDocumentCannotBeExportedRaw records a defect
205-
// this format rule surfaced rather than created, and pins the CLI's honest
206-
// half of it.
207-
//
208-
// resources.ConfigurationDocumentKey is a prefix plus a LOWERCASE hex SHA256,
209-
// and sdk-go's validRuntimeEnvironmentName accepts only A–Z, 0–9 and `_`. So
210-
// the reader refuses the WHOLE artifact whenever a run exports a structured
211-
// configuration document — platform-core's merged fixture-composition is
212-
// exactly that shape. Before this check the CLI wrote such a file and
213-
// reported success; nothing could ever load it. Refusing before the first
214-
// write is the only answer available here: making it exportable needs either
215-
// an uppercase document carrier in core or a reader in sdk-go that accepts
216-
// this one, and neither belongs to the CLI.
217-
func TestAStructuredConfigurationDocumentCannotBeExportedRaw(t *testing.T) {
221+
// TestAStructuredConfigurationDocumentIsExported is the defect cli#1213's
222+
// check surfaced, closed: resources.ConfigurationDocumentKey is a prefix plus
223+
// a LOWERCASE hex SHA-256, and a run that delivers a structured configuration
224+
// document to the --output-env service — platform-core's merged
225+
// fixture-composition, which saas/auth-gateway receives — refused the whole
226+
// artifact ("its name is not uppercase ASCII…"). The carrier is now written
227+
// under the name core mints, and a consumer that looks it up the way the SDK
228+
// does (that same function, exactly) recovers the document.
229+
func TestAStructuredConfigurationDocumentIsExported(t *testing.T) {
218230
ctx := context.Background()
219231
path := filepath.Join(t.TempDir(), "runtime.env")
220232
native := resources.NewRuntimeContextNative()
@@ -223,12 +235,22 @@ func TestAStructuredConfigurationDocumentCannotBeExportedRaw(t *testing.T) {
223235
Data: &basev0.ConfigurationData{Kind: "yaml", Content: []byte("schema: codefly.dev/authority/fixture-composition/v1\nroles: []\n")},
224236
}}}
225237
key := resources.ConfigurationDocumentKey(resources.ConfigurationWorkspace, "fixture-composition", "local", false)
226-
require.True(t, strings.HasPrefix(key, "CODEFLY__"), "the prefix is fine; the digest is not")
227-
require.Error(t, validOutputKey(key), "the reader's own grammar refuses a lowercase hex digest")
238+
require.NoError(t, validOutputKey(key))
228239

229-
err := AppendServiceProcessConfigurationsToFile(ctx, path, localRunEnvironment(), native, nil,
230-
[]*basev0.Configuration{document}, nil, nil)
231-
require.Error(t, err)
232-
_, statErr := os.Stat(path)
233-
require.True(t, os.IsNotExist(statErr), "nothing was written")
240+
require.NoError(t, AppendServiceProcessConfigurationsToFile(ctx, path, localRunEnvironment(), native, nil,
241+
[]*basev0.Configuration{document}, nil, nil))
242+
body, err := os.ReadFile(path)
243+
require.NoError(t, err)
244+
values := map[string]string{}
245+
for _, line := range strings.Split(strings.TrimSuffix(string(body), "\n"), "\n") {
246+
name, value, found := strings.Cut(line, "=")
247+
require.True(t, found)
248+
require.True(t, readerGrammar(name), "the supported reader admits every record written")
249+
values[name] = value
250+
}
251+
encoded, found := values[key]
252+
require.True(t, found, "the document rides the carrier core names")
253+
content, err := resources.DecodeConfigurationDocument(encoded, resources.ConfigurationWorkspace, "fixture-composition", "local", false)
254+
require.NoError(t, err)
255+
require.JSONEq(t, `{"schema":"codefly.dev/authority/fixture-composition/v1","roles":[]}`, string(content))
234256
}

0 commit comments

Comments
 (0)