← Projects
DeckIn developmentLive demo ↗
deck / project specs

Development and releases

How to run Deck from source, test it, add an action to the panel and publish a signed release.

Requirements

  • Go (the go.mod version) for the CLI and the panel server.
  • Node 22 and npm for the screens (build only: the final binary does not depend on Node).
  • An authenticated gh, for the GitHub integrations and deck update.

From source to binary

Terminal window
git clone git@github.com:thecodecodex/deck.git && cd deck
npm install
npm run build # static export of the screens, copied to internal/hubserver/web/
make build # compiles ./deck embedding the latest screens build
./deck ui

Order matters: go build embeds whatever is in internal/hubserver/web/ at that moment. Without npm run build, the binary still compiles, but deck ui warns that it has no panel and only the API responds.

Everyday commands

CommandPurpose
npm run devScreens with hot reload: next dev on 4005 behind deck ui --dev on 4004 (open 4004)
go run ./cmd/deck <command>Run the CLI without compiling
make testgo vet + go test
npx tsc --noEmitTypecheck the screens
npm run build:demoBuild the public demo (fixtures instead of the API)

Repository layout

FolderContents
cmd/deckCLI entry point
cmd/assinar-releaseSigns checksums.txt in CI and generates the key pair
internal/commandsCommand registry (registry.go) and the deck ui command
internal/hubserverPanel server: middleware, actions, /api/* routes, embedded pages
internal/configdeck.yaml
internal/selfupdatedeck update, channels, rollback and signature
internal/<module>gitworkflow, azure, database, mocks, setup, testscenarios, devlauncher…
src/app, src/componentsScreens (static export)
src/demoPublic demo fixtures
public/_headersDemo headers, with the same CSP as deck ui

Project rules

  • Nothing project-specific in the code. Repository, organization, URL, e-mail, tenant or board column come from deck.yaml. New field: internal/config, deck.example.yaml and, if a screen needs it, the public configuration (internal/hubserver/config.go and src/lib/configTipos.ts).
  • Language: code, comments and messages in Brazilian Portuguese; README in English.
  • Interface: only components and tokens from @thecodecodex/shell; a new generic component is born there.
  • Screens only talk to same-origin /api/*, and the server never receives flags from the client.

Adding an action to the panel

A new action (for example, a button that runs a CLI command) always follows the same path:

  1. The command exists in the CLI (internal/commands/registry.go), with --json/--yes when it makes sense.
  2. The action is declared in the route handler, with typed parameters and the function that builds the argv:
"comment-card": {
Timeout: 2 * time.Minute,
Params: map[string]Param{
"id": obrigatorio(Numerico("ID de card")),
"text": obrigatorio(Texto("Texto do comentário", 4000)),
},
Monta: func(p map[string]string) []string {
return []string{"boards-comment-card", "--id", p["id"], "--text", p["text"]}
},
},
  1. Two tests in internal/hubserver: one that checks the exact argv built and one that checks that invalid inputs (a flag instead of a value, a value outside the list, a line break) are rejected without running anything.
  2. The screen calls the route with POST and Content-Type: application/json.
  3. If the action reads or writes a file, the path goes through a containment check.

Tests

SuiteWhat it covers
internal/hubserverMiddleware (host, origin, cookie, method), argv of each action, malicious inputs rejected, file containment, E2E lock and abort, CSP identical to the demo’s
internal/hubserver (Unix)Real execution: no shell, empty stdin, timeout killing the process tree
internal/commonsInput validators
internal/selfupdateChannels, checksum, valid, tampered, wrong-key and missing signatures
Other packagesRules of each module (git, boards, database, mocks, setup…)

CI runs go vet, go test -race, a cross-build for Windows, typecheck, the screens build and the demo build.

Releases

Every push to main publishes a v<version>-dev release; a manual dispatch with the “stable” channel promotes the commit to v<version>. Versioning follows CalSemVer.

push to main

npm run build (screens)

go test

go build: macOS, Linux, Windows

checksums.txt

assinar-release: checksums.txt.sig

GitHub Release

deck update: signature → checksum → --version → swap

push to main

npm run build (screens)

go test

go build: macOS, Linux, Windows

checksums.txt

assinar-release: checksums.txt.sig

GitHub Release

deck update: signature → checksum → --version → swap

Signing key
  • The public key lives in internal/selfupdate/assinatura.go and is embedded in every binary.
  • The private key lives only in the repository secret DECK_RELEASE_CHAVE_PRIVADA. Without it, the signing step fails and the release is not published (an unsigned release would be rejected by every binary).
  • Generating a new pair (for example, if the key leaks): go run ./cmd/assinar-release -gerar <file> writes the private key with 0600 permissions and prints the public one. Rotating the key requires publishing a version with the new public key; anyone on an older version needs to update manually once.

Public demo

npm run build:demo generates the same export with NEXT_PUBLIC_DECK_DEMO=1: calls to /api/* are answered by fixtures in the browser (src/demo/fixtures.ts). It is deployed to Cloudflare (wrangler.jsonc) at deck.thecodecodex.com, with the headers from public/_headers.