Skip to content
This repository was archived by the owner on Oct 19, 2026. It is now read-only.

Commit 083ed9f

Browse files
Merge pull request #21 from superdurable/codex/hosted-release-artifacts
chore: add hosted release artifact contract
2 parents 89c97be + 8b429e4 commit 083ed9f

11 files changed

Lines changed: 672 additions & 17 deletions

File tree

‎.superverse/template.json‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,16 @@
11
{
22
"schemaVersion": 1,
33
"buildProfile": "go-react-v1",
4-
"templateVersion": "1.7.3",
4+
"templateVersion": "1.8.0",
55
"minimumSandboxImageContractRevision": 3,
66
"openapiSpec": "openapi/openapi.yaml",
7+
"applicationManifest": "dex-app.yaml",
78
"agentInstructions": "AGENTS.md",
89
"commands": {
910
"bootstrap": "make bootstrap",
1011
"generate": "make generate",
1112
"checkFdgV2": "make check-fdg-v2",
13+
"releaseArtifacts": "make superverse-release-artifacts",
1214
"testUnit": "make test-unit",
1315
"testIntegration": "make test-integration",
1416
"testE2E": "make test-e2e",

‎AGENTS.md‎

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,14 @@ If `make check` fails or cannot run, report `blocked=true`. Do not weaken, skip,
4949
or delete a failing check.
5050

5151
Stable commands are `make bootstrap`, `make generate`, `make check-fdg-v2`,
52-
`make test-unit`, `make test-integration`, `make test-e2e`, `make build`,
53-
`make dev`, and `make check`.
52+
`make superverse-release-artifacts`, `make test-unit`,
53+
`make test-integration`, `make test-e2e`, `make build`, `make dev`, and
54+
`make check`.
55+
56+
`dex-app.yaml` declares every application Flow source and exact connector
57+
connection. Update it whenever either set changes. Never put endpoint values,
58+
tokens, keys, refresh tokens, webhook secrets, or other configuration material
59+
in this manifest or the generated Release artifacts.
5460

5561
`make bootstrap`, `npm ci`, and `go mod download` may restore dependencies
5662
already declared by the committed manifests and lockfiles. Before adding or

‎Makefile‎

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
SHELL := /usr/bin/env bash
22

3-
.PHONY: bootstrap generate check-fdg-v2 test-unit test-integration test-e2e build dev check
3+
.PHONY: bootstrap generate check-fdg-v2 superverse-release-artifacts test-unit test-integration test-e2e build dev check
44
bootstrap:
55
go mod download
66
go -C tools/openapi mod download
@@ -9,9 +9,12 @@ generate:
99
./scripts/generate-openapi.sh
1010
check-fdg-v2:
1111
./scripts/check-fdg-v2.sh
12+
superverse-release-artifacts:
13+
@test -n "$(SUPERVERSE_RELEASE_ARTIFACT_DIR)" || { echo "SUPERVERSE_RELEASE_ARTIFACT_DIR is required" >&2; exit 1; }
14+
python3 ./scripts/generate-release-artifacts.py --output-directory "$(SUPERVERSE_RELEASE_ARTIFACT_DIR)"
1215
test-unit: generate
1316
go test ./...
14-
python3 -m unittest scripts/test_check_template_version.py scripts/test_generate_openapi.py
17+
python3 -m unittest scripts/test_check_template_version.py scripts/test_generate_openapi.py scripts/test_generate_release_artifacts.py
1518
npm --prefix web test
1619
test-integration: generate
1720
./scripts/with-dex.sh go test -tags=integration ./...
@@ -27,7 +30,7 @@ check: bootstrap generate check-fdg-v2
2730
go mod tidy -diff
2831
go vet ./...
2932
go test ./...
30-
python3 -m unittest scripts/test_check_template_version.py scripts/test_generate_openapi.py
33+
python3 -m unittest scripts/test_check_template_version.py scripts/test_generate_openapi.py scripts/test_generate_release_artifacts.py
3134
npm --prefix web test
3235
./scripts/with-dex.sh go test -tags=integration ./...
3336
./scripts/with-dex.sh ./scripts/run-e2e.sh

‎README.md‎

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,36 @@ Run `make bootstrap` once after a fresh checkout so the locked generators are
6767
available. Build, test, development, and full-check targets regenerate the
6868
clients before compiling. Never edit or commit generated files.
6969

70+
## Hosted release artifacts
71+
72+
`dex-app.yaml` is the checked-in application manifest. It lists every Flow
73+
Definition source and every exact connector connection used by the whole app;
74+
it never contains configuration values or credentials. The file uses
75+
JSON-compatible YAML so release generation has no undeclared parser dependency.
76+
77+
The Superverse build profile runs:
78+
79+
```bash
80+
make superverse-release-artifacts SUPERVERSE_RELEASE_ARTIFACT_DIR=/tmp/release
81+
```
82+
83+
The target requires the platform-provided Release, project, source commit, and
84+
build-profile identities. It renders every declared Flow with FDG 2.0 and emits
85+
`flow-definitions.json`, `connector-contract.json`,
86+
`environment-contract.json`, and the exact `dex-app.yaml`. Invalid, duplicate,
87+
or diagnostic-bearing Flow Definitions fail the Release build. Superverse pins
88+
the resulting object versions and digests; application secrets are never part
89+
of these artifacts.
90+
91+
At startup, local development may supply `DEX_CONNECTOR_CONFIG_FILE`. A hosted
92+
deployment supplies the read-only `SUPERVERSE_CONNECTOR_CONFIG_FILE`, its
93+
`SUPERVERSE_CONNECTOR_CONFIG_DIGEST`, `PUBLIC_BASE_URL`, and—when the app has
94+
connector connections—the internal `SUPERVERSE_CONNECTOR_BROKER_URL` plus a
95+
`SUPERVERSE_CONNECTOR_WORKLOAD_CREDENTIAL_FILE`. Startup fails before the Dex
96+
Worker begins when the snapshot is missing or its digest differs. The app only
97+
retains the credential file path; refresh tokens and provider secrets remain in
98+
the platform broker.
99+
70100
## Verification
71101

72102
```bash

‎dex-app.yaml‎

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
{
2+
"schemaVersion": "superverse.dev/dex-app/v1",
3+
"application": {
4+
"port": 8080,
5+
"healthPath": "/healthz"
6+
},
7+
"flowDefinitions": [
8+
{
9+
"sourcePath": "internal/process/flow.go"
10+
}
11+
],
12+
"connectors": []
13+
}
Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
// Copyright (c) 2026 Super Durable
2+
// SPDX-License-Identifier: MIT
3+
4+
// Package connectorconfiguration validates the platform-owned connector snapshot boundary.
5+
package connectorconfiguration
6+
7+
import (
8+
"crypto/sha256"
9+
"encoding/hex"
10+
"fmt"
11+
"io"
12+
"net/url"
13+
"os"
14+
"path/filepath"
15+
"regexp"
16+
"strings"
17+
)
18+
19+
const (
20+
maximumConfigurationBytes = 32 << 20
21+
maximumCredentialBytes = 16 << 10
22+
)
23+
24+
// Mode identifies the active connector configuration authority.
25+
type Mode string
26+
27+
const (
28+
// ModeLocal reads the developer-owned Dex connector configuration file.
29+
ModeLocal Mode = "local"
30+
// ModeHosted reads a deployment-pinned snapshot and resolves credentials through the broker.
31+
ModeHosted Mode = "hosted"
32+
)
33+
34+
// Configuration describes safe connector inputs without loading credential material.
35+
type Configuration struct {
36+
Mode Mode
37+
ConfigurationFile string
38+
CredentialBrokerURL string
39+
WorkloadCredentialFile string
40+
PublicBaseURL string
41+
}
42+
43+
// Load validates local or hosted connector configuration before the Worker starts.
44+
func Load(requiresConnections bool) (Configuration, error) {
45+
hostedFile := strings.TrimSpace(os.Getenv("SUPERVERSE_CONNECTOR_CONFIG_FILE"))
46+
localFile := strings.TrimSpace(os.Getenv("DEX_CONNECTOR_CONFIG_FILE"))
47+
if hostedFile != "" || hasHostedEnvironment() {
48+
if localFile != "" {
49+
return Configuration{}, fmt.Errorf("local and hosted connector configuration cannot be combined")
50+
}
51+
return loadHosted(hostedFile, requiresConnections)
52+
}
53+
if localFile == "" {
54+
if requiresConnections {
55+
return Configuration{}, fmt.Errorf("DEX_CONNECTOR_CONFIG_FILE is required")
56+
}
57+
return Configuration{Mode: ModeLocal}, nil
58+
}
59+
if _, err := readBoundedRegularFile(localFile, maximumConfigurationBytes, false); err != nil {
60+
return Configuration{}, fmt.Errorf("read local connector configuration: %w", err)
61+
}
62+
return Configuration{Mode: ModeLocal, ConfigurationFile: localFile}, nil
63+
}
64+
65+
func loadHosted(configurationFile string, requiresConnections bool) (Configuration, error) {
66+
if configurationFile == "" || !filepath.IsAbs(configurationFile) {
67+
return Configuration{}, fmt.Errorf("SUPERVERSE_CONNECTOR_CONFIG_FILE must be an absolute path")
68+
}
69+
contents, err := readBoundedRegularFile(configurationFile, maximumConfigurationBytes, false)
70+
if err != nil {
71+
return Configuration{}, fmt.Errorf("read hosted connector configuration: %w", err)
72+
}
73+
expectedDigest := strings.TrimSpace(os.Getenv("SUPERVERSE_CONNECTOR_CONFIG_DIGEST"))
74+
if !regexp.MustCompile(`^[0-9a-f]{64}$`).MatchString(expectedDigest) {
75+
return Configuration{}, fmt.Errorf("SUPERVERSE_CONNECTOR_CONFIG_DIGEST must be lowercase hexadecimal")
76+
}
77+
actualDigest := sha256.Sum256(contents)
78+
if expectedDigest != hex.EncodeToString(actualDigest[:]) {
79+
return Configuration{}, fmt.Errorf("hosted connector configuration digest does not match")
80+
}
81+
publicBaseURL, err := validatedHTTPURL("PUBLIC_BASE_URL", true)
82+
if err != nil {
83+
return Configuration{}, err
84+
}
85+
brokerURL := strings.TrimSpace(os.Getenv("SUPERVERSE_CONNECTOR_BROKER_URL"))
86+
credentialFile := strings.TrimSpace(os.Getenv("SUPERVERSE_CONNECTOR_WORKLOAD_CREDENTIAL_FILE"))
87+
if brokerURL == "" && requiresConnections {
88+
return Configuration{}, fmt.Errorf("SUPERVERSE_CONNECTOR_BROKER_URL is required")
89+
}
90+
if brokerURL != "" {
91+
if _, err := validatedHTTPURL("SUPERVERSE_CONNECTOR_BROKER_URL", false); err != nil {
92+
return Configuration{}, err
93+
}
94+
}
95+
if credentialFile == "" && requiresConnections {
96+
return Configuration{}, fmt.Errorf("SUPERVERSE_CONNECTOR_WORKLOAD_CREDENTIAL_FILE is required")
97+
}
98+
if credentialFile != "" {
99+
if !filepath.IsAbs(credentialFile) {
100+
return Configuration{}, fmt.Errorf("SUPERVERSE_CONNECTOR_WORKLOAD_CREDENTIAL_FILE must be an absolute path")
101+
}
102+
if _, err := readBoundedRegularFile(credentialFile, maximumCredentialBytes, true); err != nil {
103+
return Configuration{}, fmt.Errorf("read workload credential: %w", err)
104+
}
105+
}
106+
return Configuration{
107+
Mode: ModeHosted,
108+
ConfigurationFile: configurationFile,
109+
CredentialBrokerURL: brokerURL,
110+
WorkloadCredentialFile: credentialFile,
111+
PublicBaseURL: publicBaseURL,
112+
}, nil
113+
}
114+
115+
func hasHostedEnvironment() bool {
116+
for _, name := range []string{
117+
"SUPERVERSE_CONNECTOR_CONFIG_DIGEST",
118+
"SUPERVERSE_CONNECTOR_BROKER_URL",
119+
"SUPERVERSE_CONNECTOR_WORKLOAD_CREDENTIAL_FILE",
120+
} {
121+
if strings.TrimSpace(os.Getenv(name)) != "" {
122+
return true
123+
}
124+
}
125+
return false
126+
}
127+
128+
func validatedHTTPURL(name string, allowHTTP bool) (string, error) {
129+
value := strings.TrimSpace(os.Getenv(name))
130+
parsed, err := url.Parse(value)
131+
if err != nil || parsed.Host == "" || parsed.User != nil || parsed.RawQuery != "" || parsed.Fragment != "" {
132+
return "", fmt.Errorf("%s must be an absolute URL without credentials, query, or fragment", name)
133+
}
134+
if parsed.Scheme != "https" && (!allowHTTP || parsed.Scheme != "http") {
135+
return "", fmt.Errorf("%s must use HTTPS", name)
136+
}
137+
return strings.TrimRight(value, "/"), nil
138+
}
139+
140+
func readBoundedRegularFile(path string, maximumBytes int64, requirePrivate bool) ([]byte, error) {
141+
file, err := os.Open(path)
142+
if err != nil {
143+
return nil, err
144+
}
145+
defer file.Close()
146+
info, err := file.Stat()
147+
if err != nil {
148+
return nil, err
149+
}
150+
if !info.Mode().IsRegular() || info.Size() < 1 || info.Size() > maximumBytes {
151+
return nil, fmt.Errorf("file must be regular and contain between 1 and %d bytes", maximumBytes)
152+
}
153+
if requirePrivate && info.Mode().Perm()&0o077 != 0 {
154+
return nil, fmt.Errorf("file permissions must not grant group or other access")
155+
}
156+
contents, err := io.ReadAll(io.LimitReader(file, maximumBytes+1))
157+
if err != nil {
158+
return nil, err
159+
}
160+
if int64(len(contents)) != info.Size() {
161+
return nil, fmt.Errorf("file changed while it was being read")
162+
}
163+
return contents, nil
164+
}
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
// Copyright (c) 2026 Super Durable
2+
// SPDX-License-Identifier: MIT
3+
4+
package connectorconfiguration
5+
6+
import (
7+
"crypto/sha256"
8+
"encoding/hex"
9+
"os"
10+
"path/filepath"
11+
"testing"
12+
)
13+
14+
func TestLoadAllowsAnEmptyLocalConnectorSet(t *testing.T) {
15+
clearConfigurationEnvironment(t)
16+
17+
configuration, err := Load(false)
18+
if err != nil {
19+
t.Fatalf("load empty local connector configuration: %v", err)
20+
}
21+
if configuration.Mode != ModeLocal || configuration.ConfigurationFile != "" {
22+
t.Fatalf("unexpected local configuration: %+v", configuration)
23+
}
24+
}
25+
26+
func TestLoadValidatesAHostedSnapshotAndCredentialBoundary(t *testing.T) {
27+
clearConfigurationEnvironment(t)
28+
directory := t.TempDir()
29+
configurationFile := filepath.Join(directory, "connections.json")
30+
credentialFile := filepath.Join(directory, "workload-token")
31+
contents := []byte(`{"connections":[]}`)
32+
if err := os.WriteFile(configurationFile, contents, 0o600); err != nil {
33+
t.Fatalf("write configuration: %v", err)
34+
}
35+
if err := os.WriteFile(credentialFile, []byte("opaque-token"), 0o600); err != nil {
36+
t.Fatalf("write workload credential: %v", err)
37+
}
38+
digest := sha256.Sum256(contents)
39+
t.Setenv("SUPERVERSE_CONNECTOR_CONFIG_FILE", configurationFile)
40+
t.Setenv("SUPERVERSE_CONNECTOR_CONFIG_DIGEST", hex.EncodeToString(digest[:]))
41+
t.Setenv("SUPERVERSE_CONNECTOR_BROKER_URL", "https://connector-broker.internal")
42+
t.Setenv("SUPERVERSE_CONNECTOR_WORKLOAD_CREDENTIAL_FILE", credentialFile)
43+
t.Setenv("PUBLIC_BASE_URL", "https://application.example.test/")
44+
45+
configuration, err := Load(true)
46+
if err != nil {
47+
t.Fatalf("load hosted connector configuration: %v", err)
48+
}
49+
if configuration.Mode != ModeHosted || configuration.ConfigurationFile != configurationFile {
50+
t.Fatalf("unexpected hosted configuration: %+v", configuration)
51+
}
52+
if configuration.PublicBaseURL != "https://application.example.test" {
53+
t.Fatalf("unexpected public base URL: %q", configuration.PublicBaseURL)
54+
}
55+
}
56+
57+
func TestLoadFailsClosedWhenTheHostedDigestDoesNotMatch(t *testing.T) {
58+
clearConfigurationEnvironment(t)
59+
configurationFile := filepath.Join(t.TempDir(), "connections.json")
60+
if err := os.WriteFile(configurationFile, []byte(`{"connections":[]}`), 0o600); err != nil {
61+
t.Fatalf("write configuration: %v", err)
62+
}
63+
t.Setenv("SUPERVERSE_CONNECTOR_CONFIG_FILE", configurationFile)
64+
t.Setenv("SUPERVERSE_CONNECTOR_CONFIG_DIGEST", "0"+"123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef")
65+
t.Setenv("PUBLIC_BASE_URL", "https://application.example.test")
66+
67+
if _, err := Load(false); err == nil || err.Error() != "hosted connector configuration digest does not match" {
68+
t.Fatalf("expected digest mismatch, got %v", err)
69+
}
70+
}
71+
72+
func TestLoadRejectsAWorldReadableWorkloadCredential(t *testing.T) {
73+
clearConfigurationEnvironment(t)
74+
directory := t.TempDir()
75+
configurationFile := filepath.Join(directory, "connections.json")
76+
credentialFile := filepath.Join(directory, "workload-token")
77+
contents := []byte(`{"connections":[]}`)
78+
if err := os.WriteFile(configurationFile, contents, 0o600); err != nil {
79+
t.Fatalf("write configuration: %v", err)
80+
}
81+
if err := os.WriteFile(credentialFile, []byte("opaque-token"), 0o644); err != nil {
82+
t.Fatalf("write workload credential: %v", err)
83+
}
84+
digest := sha256.Sum256(contents)
85+
t.Setenv("SUPERVERSE_CONNECTOR_CONFIG_FILE", configurationFile)
86+
t.Setenv("SUPERVERSE_CONNECTOR_CONFIG_DIGEST", hex.EncodeToString(digest[:]))
87+
t.Setenv("SUPERVERSE_CONNECTOR_BROKER_URL", "https://connector-broker.internal")
88+
t.Setenv("SUPERVERSE_CONNECTOR_WORKLOAD_CREDENTIAL_FILE", credentialFile)
89+
t.Setenv("PUBLIC_BASE_URL", "https://application.example.test")
90+
91+
if _, err := Load(true); err == nil {
92+
t.Fatal("expected insecure workload credential permissions to fail")
93+
}
94+
}
95+
96+
func clearConfigurationEnvironment(t *testing.T) {
97+
t.Helper()
98+
for _, name := range []string{
99+
"DEX_CONNECTOR_CONFIG_FILE",
100+
"SUPERVERSE_CONNECTOR_CONFIG_FILE",
101+
"SUPERVERSE_CONNECTOR_CONFIG_DIGEST",
102+
"SUPERVERSE_CONNECTOR_BROKER_URL",
103+
"SUPERVERSE_CONNECTOR_WORKLOAD_CREDENTIAL_FILE",
104+
"PUBLIC_BASE_URL",
105+
} {
106+
t.Setenv(name, "")
107+
}
108+
}

0 commit comments

Comments
 (0)