# Technical Orientation

> What dk-flags is, its core purpose, the build-time initialization strategy, the module layout, and how the rest of this reference is organized. Entry point for any engineer onboarding to the repo.

- Repository: vtex/dk-flags
- GitHub: https://github.com/vtex/dk-flags
- Human wiki: https://grok-wiki.com/public/wiki/vtex-dk-flags-0a8c140c3cfa
- Complete Markdown: https://grok-wiki.com/public/wiki/vtex-dk-flags-0a8c140c3cfa/llms-full.txt

## Source Files

- `README.md`
- `docker/Dockerfile`
- `docker/setup.js`
- `docker/config.js`
- `docker/exampleFlags.json`
- `.vtex/catalog-info.yaml`
- `specs/dk-flags-party-server-image.md`

---

<details>
<summary>Relevant source files</summary>
The following files were used as context for generating this wiki page:

- [README.md](README.md)
- [docker/Dockerfile](docker/Dockerfile)
- [docker/build-setup.sh](docker/build-setup.sh)
- [docker/entrypoint.sh](docker/entrypoint.sh)
- [docker/setup.js](docker/setup.js)
- [docker/config.js](docker/config.js)
- [docker/api.js](docker/api.js)
- [docker/auth.js](docker/auth.js)
- [docker/portfolio.js](docker/portfolio.js)
- [docker/features.js](docker/features.js)
- [docker/flags.js](docker/flags.js)
- [docker/exampleFlags.json](docker/exampleFlags.json)
- [.vtex/catalog-info.yaml](.vtex/catalog-info.yaml)
- [specs/dk-flags-party-server-image.md](specs/dk-flags-party-server-image.md)
</details>

# Technical Orientation

`dk-flags` is the VTEX Dark Kitchen repository that ships a self-contained Docker image (`dk-flags-party-server`) wrapping [FeatureHub](https://docs.featurehub.io/featurehub/latest/index.html)'s `party-server`. Its defining design decision is that the entire initialization pipeline — admin user bootstrap, portfolio/application/environment creation, and feature flag seeding — runs **at image build time**, not at container start. The result is a container that is ready to serve immediately upon `docker run` with no extra sidecars, init containers, or post-start scripts.

This page explains what the repository contains, why the build-time approach was chosen, how the Node.js setup modules are organized, and how the image is registered and published. It is the recommended starting point for any engineer integrating with or maintaining the DK flags infrastructure.

---

## What dk-flags Is

DK engineers need a reproducible, pre-seeded FeatureHub instance for local development and integration testing. The production FeatureHub deployment is hosted at **https://flags.vtex.com/**, but every local machine and CI job previously required a multi-step manual bootstrap. `dk-flags` solves this by versioning all initialization logic in one repository and baking the resulting state into a Docker image.

The primary artifact is the Docker image. Everything in the repository supports building, populating, and publishing that image. The repository does **not** own the OpenFeature SDK clients (those live in separate repositories listed in the README), nor the production deployment topology behind `flags.vtex.com`.

Sources: [README.md:1-13](), [specs/dk-flags-party-server-image.md:9-19]()

---

## Build-Time Initialization Strategy

The central architectural decision is: **initialize once at `docker build` time, ship the state as an image layer, and let `docker run` find everything already in place**.

```text
┌─────────────────────────────────────────────────────────────┐
│  docker build                                               │
│                                                             │
│  1. FROM featurehub/party-server:latest                     │
│  2. apk add nodejs npm curl procps                          │
│  3. COPY docker/*.js + exampleFlags.json → /app/            │
│  4. RUN /app/build-setup.sh                                 │
│       ├── start party-server (bathe.BatheBooter) → bg      │
│       ├── poll APP_URL up to 120 s for HTTP 200             │
│       ├── node /app/setup.js  (full API bootstrap)          │
│       └── kill server (graceful → SIGKILL after ~2 s)       │
│  5. EXPOSE 8085                                             │
│  6. ENTRYPOINT ["/app/entrypoint.sh"]                       │
└─────────────────────────────────────────────────────────────┘
         │
         ▼ image layer contains post-init FeatureHub state
┌─────────────────────────────────────────────────────────────┐
│  docker run                                                 │
│                                                             │
│  entrypoint.sh → exec party-server (foreground, no setup)   │
│  Container ready immediately on :8085                       │
└─────────────────────────────────────────────────────────────┘
```

The build-time server is launched using `bathe.BatheBooter` (the launcher embedded in the base image's classpath) and logs to `/tmp/server.log`. `build-setup.sh` polls `$APP_URL` with `curl` every 2 seconds for up to 120 seconds, then hands off to `node /app/setup.js`. The server is stopped — gracefully first, then `kill -9` after ~2 seconds — before the image layer is committed.

At run time, `entrypoint.sh` simply `exec`s the same server command; no initialization re-runs. Environment variables such as `ADMIN_USER` or `ADMIN_EMAIL` override admin credentials only when passed at **build time**. Passing them at `docker run` time has no effect on the already-embedded credentials.

Sources: [docker/Dockerfile:5-54](), [docker/build-setup.sh:6-128](), [docker/entrypoint.sh:1-44](), [specs/dk-flags-party-server-image.md:181-191]()

### Why Not Initialize at Container Start?

| Alternative | Rejection reason |
|---|---|
| Init at container start (entrypoint runs setup) | Every start takes seconds; race-prone on cold start |
| Sidecar / one-shot Kubernetes Job | Consumers must coordinate the job; couples seed lifecycle to deploys |
| FeatureHub CLI / setup.yaml | CLI adds image weight; REST API gives full typed-value control with plain `node` + `fetch` |
| Headless browser scripting the Admin UI | Heavy (Chromium); brittle to UI changes |
| External volume seeded at runtime | Loses self-contained image guarantee |

Sources: [specs/dk-flags-party-server-image.md:158-165]()

---

## Module Layout

All initialization code lives under `docker/`. The Node.js pipeline is split into seven focused modules, plus three shell scripts and one data file.

### Node.js Modules

```text
docker/
├── config.js       ← environment / constant values
├── api.js          ← HTTP client + readiness probe
├── auth.js         ← admin bootstrap + login
├── portfolio.js    ← portfolio / app / environment resolution
├── features.js     ← create, unlock, and value-set per flag
├── flags.js        ← load exampleFlags.json from disk
└── setup.js        ← orchestrator (entry point)
```

#### `config.js`

Exports all runtime constants from environment variables with hard-coded defaults:

```js
// docker/config.js
module.exports = {
    APP_URL: process.env.APP_URL || 'http://localhost:8085',
    ADMIN_USER: process.env.ADMIN_USER || 'admin',
    ADMIN_PASSWORD: process.env.ADMIN_PASSWORD || 'admin',
    ADMIN_EMAIL: process.env.ADMIN_EMAIL || 'admin@example.com',
    MAX_WAIT_TIME: 60000,
    PORTFOLIO_NAME: 'integrationTest',
    ORGANIZATION_NAME: 'vtex',
    APPLICATION_NAME: 'integrationTest'
};
```

`PORTFOLIO_NAME`, `ORGANIZATION_NAME`, and `APPLICATION_NAME` are hard-coded (not env-overridable) in the current version. Sources: [docker/config.js:1-10]()

#### `api.js`

Provides two exports: `makeRequest(method, path, data?, token?)` for all FeatureHub Management REST calls (using Node 18+ built-in `fetch`), and `waitForApp()` which polls `APP_URL` until the root returns `ok` or `MAX_WAIT_TIME` is exceeded.

#### `auth.js`

`authenticate()` first attempts `POST /mr-api/initialize` (first-time site admin creation). If that fails (the system is already initialized), it falls back to `POST /mr-api/login` with the configured credentials. Returns a bearer token or `null`. Sources: [docker/auth.js:4-84]()

#### `portfolio.js`

Resolves the FeatureHub object graph:
- `getPortfolio(token)` → `GET /mr-api/portfolio`; returns the ID of the first portfolio found.
- `createApplication(portfolioId, token)` → `POST /mr-api/portfolio/{id}/application`.
- `getEnvironment(applicationId, token)` → `GET /mr-api/application/{id}/environment`; returns the first environment ID.

#### `features.js`

Implements three low-level operations and one high-level orchestrator:

| Function | REST call | Notes |
|---|---|---|
| `createFeature(appId, flag, token)` | `POST /mr-api/application/{id}/features` | Returns `{ success, skipped }` (HTTP 409 → `skipped: true`) |
| `unlockBooleanFlag(appId, key, envId, value, version, token)` | `PUT /mr-api/application/{id}/feature-environments/{key}` | Only called for `BOOLEAN` flags; sets `locked: false` + `valueBoolean` |
| `setFeatureValue(envId, flag, version, token)` | `PUT /mr-api/features/{envId}/feature/{key}` | Sets the single typed field (`valueBoolean`, `valueString`, `valueNumber`, or `valueJson`) |
| `createAllFeatures(appId, envId, flags, token)` | — | Iterates flags, calls the above three in order, tracks created/skipped/error counters |

The `BOOLEAN` flag requires an explicit unlock call **before** the value-set call because FeatureHub locks new flags by default; non-boolean types set `locked: false` directly in the value-set body. Sources: [docker/features.js:39-64](), [specs/dk-flags-party-server-image.md:213-218]()

#### `flags.js`

`loadFlags()` searches four candidate file paths in priority order (`__dirname`, `cwd`, `/app/`, `./`) for `exampleFlags.json`. Returns the parsed array or `null` (with a warning) if not found. Sources: [docker/flags.js:4-29]()

#### `setup.js`

The orchestrator. Calls the five steps in order and exits 0 on success or 1 on a thrown error. Mid-pipeline failures (e.g., `portfolioId` is `null`) emit a warning and return without throwing, so the build still succeeds but with partial state. Sources: [docker/setup.js:8-82]()

### Shell Scripts

| Script | Invoked by | Purpose |
|---|---|---|
| `docker/build-setup.sh` | `Dockerfile` (`RUN`) | Start server → wait → run `setup.js` → stop server |
| `docker/entrypoint.sh` | `ENTRYPOINT` | Start server in foreground at container run time (no setup) |
| `docker/run-setup.sh` | Manual only | Poll readiness, then run `setup.js` against a live server; not used by the image build |

### Seed Data

`docker/exampleFlags.json` defines the seven flags baked into every image build:

| Key | Type | Default Value |
|---|---|---|
| `booleanConf` | `BOOLEAN` | `true` |
| `stringConf` | `STRING` | `"myStringConf"` |
| `intConf` | `NUMBER` | `10` |
| `floatConf` | `NUMBER` | `1.5` |
| `jsonConf` | `JSON` | `{ "data": "test", "evaluation": true }` |
| `objectConf` | `JSON` | nested object with string/int/bool/array attributes |
| `stringArrayConf` | `JSON` | `["stringValue1", "stringValue2", "stringValue3"]` |

All flags are created under the portfolio `integrationTest`, application `integrationTest`, and the first discovered environment. Sources: [docker/exampleFlags.json:1-55](), [docker/config.js:8-9]()

---

## API Endpoints Consumed at Build Time

`setup.js` interacts exclusively with the FeatureHub Management REST API (prefix: `$APP_URL/mr-api`):

| Method | Path | Purpose |
|---|---|---|
| `GET` | `/` | Readiness probe |
| `POST` | `/mr-api/initialize` | Create site admin (first-time) |
| `POST` | `/mr-api/login` | Auth fallback if already initialized |
| `GET` | `/mr-api/portfolio` | Discover portfolio |
| `POST` | `/mr-api/portfolio/{portfolioId}/application` | Create application |
| `GET` | `/mr-api/application/{applicationId}/environment` | Resolve environment |
| `POST` | `/mr-api/application/{applicationId}/features` | Create feature definition |
| `PUT` | `/mr-api/application/{applicationId}/feature-environments/{key}` | Unlock BOOLEAN flag |
| `PUT` | `/mr-api/features/{environmentId}/feature/{key}` | Set typed value |

Sources: [specs/dk-flags-party-server-image.md:288-298]()

---

## Environment Variables

| Variable | Default | Scope | Effect |
|---|---|---|---|
| `APP_URL` | `http://localhost:8085` | Build + Run | URL polled by `build-setup.sh` and `config.js` |
| `ADMIN_USER` | `admin` | **Build only** | Admin display name baked into the image |
| `ADMIN_PASSWORD` | `admin` | **Build only** | Admin password baked into the image |
| `ADMIN_EMAIL` | `admin@example.com` | **Build only** | Admin login email baked into the image |

The defaults are intentionally insecure and documented as dev/test-only. Production builds must override all `ADMIN_*` variables at **build time** so that real credentials are never stored in the default image. Sources: [docker/Dockerfile:39-43](), [specs/dk-flags-party-server-image.md:170-171]()

---

## Catalog Registration and CI/CD

The component is registered in the VTEX Backstage catalog under `.vtex/catalog-info.yaml`:

- **Name**: `dk-flags`
- **Owner**: `te-0034`
- **Application ID**: `OG35ZXTZ`
- **Lifecycle**: `stable`
- **Type**: `fullstack-app`

On every push to the `main` branch, the `dkcicd` pipeline (`build-and-push-image-v1`) builds the image on `amd64` and pushes it to AWS ECR (account `053131491888`, region `us-east-1`, repo `dk-flags-party-server`, tag `0.0.1`). The `0.0.1` tag is mutable; consumers who need pinned versions should reference the image by digest.

Sources: [.vtex/catalog-info.yaml:1-22](), [specs/dk-flags-party-server-image.md:219-224]()

---

## How the Reference Is Organized

| Page area | What it covers |
|---|---|
| **Technical Orientation** (this page) | Repository purpose, build-time init strategy, module layout, CI/CD |
| **Docker Setup** | Detailed build and run instructions, env var reference (see README) |
| **Flag Schema & Seeding** | `exampleFlags.json` format, supported types, default values, how to add flags |
| **FeatureHub Management API** | Endpoint contracts consumed by `setup.js` |
| **OpenFeature SDK Clients** | Frontend and backend SDKs (separate repositories; linked from README) |

---

The repository is intentionally narrow in scope: it owns the image build pipeline and the seed flag definitions. Persistence across restarts, production deployment topology (load balancer, TLS, secrets rotation, scaling), and OpenFeature SDK distribution are all handled outside this repository. Any engineer extending the image should start by editing `docker/exampleFlags.json` and rebuilding — the modular setup pipeline will handle the rest automatically at build time.

Sources: [specs/dk-flags-party-server-image.md:89-98]()
