Skip to content

About

AppThrust first-party marketplace app: builder-console

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

25 Commits

Folders and files

Repository files navigation

Builder Console

The Project-scoped AppThrust console for building, releasing, sharing, and opening business applications. The Go server serves the React SPA, the console API, and a same-origin Platform API proxy. Directory owns people, groups, and memberships; Builder Console keeps app metadata, access requests, comments, and sharing policy.

Repository

  • cmd/server/: Go server and tests, using github.com/appthrust/appserver-go v0.2.0.
  • web/: Vite/React SPA, unit tests, in-memory development APIs, and Playwright stories.
  • catalog/entry.yaml: ApplicationStackDefinition, including the Directory dependency, PostgreSQL connection, identity access settings, probes, and existing version floors.
  • hack/publish-entry.ts: immutable version rendering and signed catalog publication.
  • .github/workflows/ci.yml: Go, web, and publisher checks.
  • .github/workflows/publish.yml: tag-only multi-architecture image and catalog publisher.

Imported from appthrust/platform revision 5536b5065bcd571f59078eae71a0269a65294e6f, paths cmd/builder-console-server and dashboard/packages/builder-console. The app no longer needs a Platform source checkout. See web/README.md for UI architecture, development modes, and product contracts.

Development and checks

Use Go 1.26 or newer, Bun 1.4.1 or newer, and a GitHub token with package read access. web/.npmrc routes the @appthrust scope to GitHub Packages; tokens are supplied only through NODE_AUTH_TOKEN. All three published packages (api-schema, app-launcher, platform-dashboard-i18n) use ^0.1.1. The console has its own Japanese-default language store; the shared i18n package is used by the app launcher. API schema imports use the published /platform-api and /platform-api-router exports.

go build ./...
go test ./...
go vet ./...
bun test hack/

cd web
NODE_AUTH_TOKEN="$(gh auth token)" bun install --frozen-lockfile
bun run build:image
bun run typecheck
bun run test

PostgreSQL integration tests are opt-in: set BUILTIN_APPSERVER_TEST_DATABASE_URL (or BUILDER_CONSOLE_TEST_DATABASE_URL) to a disposable test database. The Go SDK creates and cleans an isolated schema per test; without a DSN those tests skip. DOM unit tests use the repository-local web/test/dom.ts preload from web/bunfig.toml, with no sibling Dashboard package dependency.

From web/, run bun run mock and bun run dev in separate terminals. The default ports are 54385 for the mock API and 5199 for Vite; set CONSOLE_MOCK_PORT and CONSOLE_PORT to isolate concurrent work. Neither command requires a live Platform.

The retained browser suite runs against its own local mock API and Vite server:

cd web
bun run typecheck:e2e
bun x playwright install chromium
bun run test:e2e:mock

test:e2e runs the complete story suite. Configure CONSOLE_E2E_PORT and CONSOLE_E2E_MOCK_PORT to isolate browser runs. No live platform credentials are needed. CI runs every story on each pull request and main push (console-mock-stories, aggregated with the other jobs into the required console-verification check); see Browser stories for the automatic checks and how to declare an expected failure. Stories may import only types from @appthrust/api-schema: its published ESM files have extensionless relative imports that Playwright's Node loader cannot resolve, and one value import fails discovery of the whole suite. Fixing the package's Node-ESM exports is owned by the SDK repository, not by an app-local source alias or loader shim.

Console response contracts (APT-832 / W1)

web/src/api/contracts/console/schema/ is the canonical Zod source for Console success wires and the HTTP error envelope. operations.ts in that directory registers all 33 SPA operations, including /config, screenshot multipart responses, and the three 204 deletes. Adapters in the parent directory reuse those schemas and expose the domain types in model/contracts/console.ts. Feature migrations remain separate; this slice does not replace the unverified facade's callers.

Operation family Count Responses
Bootstrap 2 me, config
Directory proxy 6 groups, people, person profile, create person, members, realm users
Apps and resolution 11 list/get/by-path/create/update/bind/delete, directory list/entry, tools, resolve
Comments 3 list, post, delete
Screenshots 3 multipart upload, reorder, delete
Access requests 6 mine, create, pending, app requests, approve, decline
Administration 2 platform settings, Directory migration

Collections carry presence: total; optional observations carry presence: optionalObservation. There are no blanket null/absence defaults in the Console readers. Missing permissions fail bootstrap. Unknown share scopes, roles, and request states remain explicitly unknown; unknown tagged results are unsupported. A removed person is { unknown: true, id } in the domain, never a fabricated contact. The older public SDK's broad Person shape is not permission to invent missing email, subject, or ownership evidence.

Generate and check the internal artifact from web/:

bun run generate:console-schema
bun run check:console-schema

cmd/server/internal/api/console-spa.openapi.json is committed, generated, and not served. Go tests validate real response writers and handler output against it. testdata/console-wire.json contains Go-marshaled fixtures, including empty collections, omitted observations, pending people, and removed people; SPA tests replay those original wires, not parsed/defaulted values. To deliberately update fixtures after reviewing a writer change:

UPDATE_CONSOLE_WIRE_FIXTURES=1 go test ./cmd/server/internal/api -run '^TestConsoleWriterContract$'

The existing served openapi.json remains byte-identical to the public appserver-go v0.5.0 Console contract. Compatibility tests check its listApps / getApp fields and fixtures against the internal schema. Replacing that public document needs separate review. This intentionally deviates from the design's single generated public document and separate B/api-schema package: the schema currently has one TypeScript consumer, so it shares the SPA's Zod installation. A package boundary is deferred until a second consumer needs it, avoiding file-dependency symlink resolution and build changes.

check:console-schema regenerates into a temporary directory, detects drift, and cleans up. The existing web CI job runs it. The boundary checker also rejects feature/model imports of the canonical schema directory, including type-only imports, barrels, and assertions. PostgreSQL handler contract tests use the same opt-in DSN and isolated-schema cleanup as the other server integration tests.

NEW-APP conversation contracts (APT-753 / S4-0)

The same canonical schema directory now owns build-sessions.ts and build-session-operations.ts. api/contracts/console/build-sessions.ts exposes response boundaries and inferred input/domain types; model/contracts/console.ts exports the domain types. Features must not import raw schemas. These are declarations only: no v1 handlers, client, database, prompts or UI are registered.

The existing generator also emits cmd/server/internal/domain/build-reply.schema.json (JSON Schema 2020-12, urn:appthrust:builder-console:reply:1, at most 8 KiB). Go embeds it through domain.BuildReplySchema() for the later AgentTurn input. Both artifacts are covered by check:console-schema. The served openapi.json is unchanged. This deliberately replaces the design's proposed independent contracts/conversation package and public-document edits with the established SPA canonical-source/internal-document convention.

Go HTTP DTOs remain in api/build_session_dto.go; reusable Reply types and the raw validator input live in domain/build_reply.go, following the existing API-to-domain response ownership. A conversation service can therefore consume them without depending on its HTTP handlers or creating an import cycle.

All paths below have prefix /api/console/v1. All responses, including errors, require Cache-Control: no-store. K means required Idempotency-Key (1–128 printable ASCII characters); V means required strong If-Match: "bs-<version>". Replay lookup precedes version checks; missing preconditions are 428 precondition_required, mismatches 412 version_conflict with details.currentVersion, and key/body conflicts 409 idempotency_conflict.

Method/path Body/query Success resource Request headers
POST /build-sessions kind: new-app, groupId, intent, locale: ja/en 202 Session K
GET /build-sessions cursor?, limit? (1–50; default 20) 200 {items,nextCursor?} —
GET /build-sessions/{id} — 200 Session —
GET /build-sessions/resolve required draftId 200 console-v1/sessionId or platform-v1/draftId —
POST /build-sessions/{id}/advance {} 202 Session K, V
POST /build-sessions/{id}/turns conversation or request_plan union 202 Turn K, V
GET /build-sessions/{id}/turns cursor?, limit? (1–50; default 20), ordinal order 200 {items,nextCursor?} —
GET /build-sessions/{id}/turns/{turnId} — 200 Turn —
POST /build-sessions/{id}/turns/{turnId}/resolution decision: accept_spec/keep_spec 200 Turn K, V
GET /build-sessions/{id}/spec-revisions/{n} nonnegative revision 200 SpecRevision —
PATCH /build-sessions/{id}/publish-preferences name, shareScope: private/group/company 202 PublishResult K, V
POST /build-sessions/{id}/archive {} 202 Session K, V

Session/Turn resource responses and PublishResult return the session ETag; Turn creation also returns Location. Turns always include sessionVersion, so resolution returns the updated version without introducing a wrapper. PublishResult is {desired:{name,shareScope},sessionVersion,sync:{state,failure?}}. There is no cancel operation and no public share scope in this flow.

Contract choices where the design left a shape or bound implicit:

  • Session requires groupId, locale, createdAt, updatedAt in addition to its illustrated fields; archivedAt, draftId, activeTurnId, failure, and pollAfterMs are optional observations. Dates are RFC3339 UTC (Z). Generated session/turn IDs are UUIDs; Directory and Platform IDs remain opaque.
  • Versions/ordinals are positive and revisions nonnegative safe JSON integers (maximum 9007199254740991), including ETags. Revision zero has explicit sourceTurnId: null; all subsequent revisions require a UUID source.
  • nextAction is none/observe/advance/confirm/resume. Projection state is not_requested/pending/syncing/applied/conflict/failed; publish sync is pending/syncing/applied/failed. appliedRevision is optional, not fabricated. Unknown observed enums become explicit unknown domain values. Writer enums and command unions remain closed; unknown Turn kinds are rejected.
  • The publish name is nonblank, at most 40 Unicode scalars and one line (CR/LF/NEL/Unicode line separators are rejected); it is never inferred. Human text bounds use Unicode scalars, not UTF-16 units. Initial intent is also limited to 4000 UTF-8 bytes and conversation messages to 16 KiB.
  • Reply has exactly the fields of design §6. Text totals include answer, summary, changes, option labels/descriptions/messages, open topics/wishes, and nextQuestion, excluding IDs. Both option arrays require unique IDs and labels. Empty summary is allowed only when specChanged=false; required human text is nonblank. A changed spec is a proposal, not a committed revision.
  • Error envelopes stay {error,message,details?}. Typed details may contain field (JSON pointer), reason, policy, currentVersion, resource (session/operation/workspace/artifact), retryable, storage limit (files/bytes/entries/depth), and observedAt. Upstream reason strings such as AgentTurnActive, WorkspaceNotRunning, PolicyPending, DraftVersionChanged and DraftSpecChanged stay reasons, not error codes. Failure codes cover every row in the design's error table plus its precondition/session/input/status errors. Optional short text caps are 128 for reason/policy/prompt version, 256 for field, 512 for opaque IDs, 2048 for cursors, and 200 for each of at most three safe progress activities.

cmd/server/internal/api/testdata/build-conversation.json is the single shared corpus. Bun and Go enumerate every case (no per-language fixture allowlist), require every registered Build schema to be represented, and check identical accept/reject and JSON-pointer paths. It includes APT-752/792 answers/candidates, mixed edits, undecided names, explicit decisions, repair, open reader enums, duplicate JSON keys, byte/scalar limits and ETags. Go also decodes accepted wires into the actual DTOs, marshals them back losslessly, and validates the writer against the generated schema; adding a schema without a DTO/corpus case fails. jsonschema/v6 remains test-only.

Standard JSON Schema cannot express UTF-8 byte totals, keyed uniqueness, display-text sums or comparison with request/spec bytes. The canonical schema records those as x-* assertions; Zod applies them and the Go parity test interprets them. BuildReplyValidationInput retains the original reply/spec bytes and typed expected context for S4-2. The strict JSON contract parser rejects duplicate keys, invalid UTF-8, trailing JSON and depth above eight. The S4-2 production validator must still enforce YAML/BuildSpec validity, artifact provenance/digests and semantic evaluation; this slice does not claim that schema validation proves natural-language correctness. S4-1/3 must enforce lifecycle transitions, one active Turn, authorization, replay precedence, immutable attempts and transactional confirmation.

Run the web checks listed in web/package.json, then the server tests/vet, gofmt on the three new Go files, and go mod tidy -diff. The known APT-845 DSN-only startup tests (TestPublicURLEnvStartup and TestConsoleMountPrecedesProxyAndMissingDatabase) are unrelated to these contracts; excluding them does not prove database integration.

Container and runtime

NODE_AUTH_TOKEN="$(gh auth token)" docker build --platform linux/amd64 \
  --secret id=NODE_AUTH_TOKEN,env=NODE_AUTH_TOKEN -t builder-console-local .

The build uses a BuildKit secret for npm authentication and plain go mod download. The distroless image contains /builder-console-server and the SPA at /srv/console, runs as UID 65532, and listens on port 8080. No JavaScript runtime or database is required during the image build.

Platform supplies PLATFORM_API_URL, PLATFORM_PROJECT_ID, and OIDC identity configuration through the Go SDK. Runtime-specific settings include:

Setting Purpose
BUILDER_CONSOLE_DATABASE_URL / DATABASE_URL PostgreSQL connection (the prefixed value takes precedence).
BUILDER_CONSOLE_PUBLIC_URL HTTPS console origin for access-request redirects.
BUILDER_CONSOLE_DIRECTORY_URL Internal Directory dependency URL.
BUILDER_CONSOLE_DIRECTORY_PUBLIC_URL Public Directory URL for user-facing links.
BUILDER_CONSOLE_STATIC_DIR SPA directory; defaults to /srv/console; --static-dir overrides it.
BUILDER_CONSOLE_DEV_SEED=1 Explicit development-only app fixture seeding.
BUILDER_CONSOLE_RELEASE_VERSION / BUILDER_CONSOLE_ARTIFACT_DIGEST Identity reported by /releasez.

--listen defaults to :8080. /healthz reports liveness; /readyz gates database readiness. /config, /launcher, /launcher/entries, /api/console/*, /authz, and the /api/* Platform proxy retain their existing contracts.

Publication

Only a v<semver> tag starts publication. The initial standalone release is v0.3.0, stream 0.3, at ghcr.io/appthrust/builder-console. Do not reuse 0.1.0 or 0.2.0: both served Platform-image floors remain verbatim in the committed catalog entry. The publisher retains their images, merges persisted release history, keeps earlier versions served, and makes the new version the only default.

The workflow builds linux/amd64 and linux/arm64, signs the image index with GitHub OIDC/cosign, then submits the catalog entry to Cloud. A queued publication is observed directly; a snapshot-only response triggers Cloud publication with the repository's GitHub App credentials. Success requires the exact version/image digest in the signed catalog artifact, not merely a successful dispatch.

Required repository secrets: CLOUD_CATALOG_ADMIN_TOKEN, APPTHRUST_GITHUB_ACTIONS_APP_ID, and APPTHRUST_GITHUB_ACTIONS_APP_PRIVATE_KEY. Optional variable: CLOUD_API_URL (defaults to https://api.appthrust.com). GITHUB_TOKEN needs package read/write access to install the published npm SDKs and push the app image. No secrets belong in this repository.

Existing installations using the Platform image path require one explicit update to 0.3.0 with replaceRepository: true; future updates can follow stream 0.3. An initial source push does not create a release, publish a catalog, or modify an installation.

About

AppThrust first-party marketplace app: builder-console

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages