# Build, Deploy & Extension Points

> How to build (docker build -f docker/Dockerfile) and run (docker run -p 8085:8085) the image with default and overridden env vars. The CI/CD pipeline: dkcicd build-and-push-image-v1 on push to main, publishing to AWS ECR account 053131491888 / us-east-1 / dk-flags-party-server:0.0.1. Production endpoint https://flags.vtex.com/. Backstage catalog registration (te-0034 / OG35ZXTZ). Downstream OpenFeature SDK consumers. Known risks: floating base image tag, mutable 0.0.1 ECR tag, build-time-only admin baking. Outstanding follow-ups from the spec: reconcile package.json#main, make portfolio/org/app names env-overridable, pin the base image.

- 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

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

---

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

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

# Build, Deploy & Extension Points

This page covers how the `dk-flags-party-server` Docker image is built, published, and consumed. It explains the build-time initialization pipeline that pre-seeds a FeatureHub instance, the CI/CD configuration that publishes the image to AWS ECR on every push to `main`, the production endpoint this image backs, and the catalog metadata that registers the component in the VTEX service catalog. It also documents the extension points available to operators (environment variable overrides, seed flag additions) and the known risks and outstanding follow-up items that consumers should be aware of.

The core design choice—running FeatureHub initialization **at image build time** rather than at container start—means a `docker run` yields a fully seeded, immediately usable flag server with no post-start configuration steps. The trade-off is that admin credentials and flag seeds are immutable for a given image tag; changing them requires a rebuild.

---

## Building the Image

### Command

```bash
docker build -f docker/Dockerfile -t dk-flags-party-server .
```

The Dockerfile is at `docker/Dockerfile`. The build context must be the repository root (`.`) because the `COPY` instructions reference paths under `docker/`.

Sources: [docker/Dockerfile:1-54]()

### What the Build Does

The Dockerfile extends `featurehub/party-server:latest`, installs Node.js and supporting tools via Alpine's `apk`, copies the modular setup scripts, then runs `build-setup.sh` as a `RUN` layer to initialize the embedded FeatureHub server before the image is finalized:

```dockerfile
FROM featurehub/party-server:latest

RUN set -eux; \
    apk add --no-cache nodejs npm curl procps;

WORKDIR /app
COPY docker/package.json ./
RUN npm install

COPY docker/setup.js ./
COPY docker/config.js ./
COPY docker/api.js ./
COPY docker/auth.js ./
COPY docker/portfolio.js ./
COPY docker/features.js ./
COPY docker/flags.js ./
COPY docker/exampleFlags.json ./
COPY docker/entrypoint.sh ./
COPY docker/build-setup.sh ./

RUN chmod +x entrypoint.sh build-setup.sh

ENV APP_URL=http://localhost:8085
ENV ADMIN_USER=admin
ENV ADMIN_PASSWORD=admin
ENV ADMIN_EMAIL=admin@example.com

RUN /app/build-setup.sh

EXPOSE 8085
ENTRYPOINT ["/app/entrypoint.sh"]
CMD []
```

Sources: [docker/Dockerfile:1-54]()

### Build-Time Initialization Pipeline

`build-setup.sh` orchestrates a three-phase sequence inside the `RUN` layer:

1. **Start the embedded server.** The script probes for the party-server JAR in `/app/classpath/` and launches it via `bathe.BatheBooter` with the classpath from `/app/classpath/*` and `/app/libs/*`. Several fallback launch strategies are tried in order (jar in `/app`, `featurehub-party-server` command, common paths) to survive base-image layout changes.
2. **Wait for readiness.** Polls `GET ${APP_URL}` every 2 seconds for up to 120 seconds. Exits with an error and dumps the last 50 lines of `/tmp/server.log` if the server does not become reachable.
3. **Run `node /app/setup.js`.** The orchestrator script calls the FeatureHub Management REST API to create admin + portfolio + application + environment + flags.
4. **Stop the server.** Sends `kill`, waits 2 seconds, then `kill -9` if the process is still alive.

```
build-setup.sh
  ├── start_server()             # bathe.BatheBooter → /tmp/server.log
  ├── poll $APP_URL (max 120 s)
  ├── node /app/setup.js         # seed admin + flags via REST
  └── kill $SERVER_PID           # graceful → force if needed
```

Sources: [docker/build-setup.sh:1-128]()

### `setup.js` REST API Flow

`setup.js` is the orchestrator. It calls the FeatureHub Management API through six modular helper modules:

| Module | Responsibility |
|---|---|
| `config.js` | Exports env values (`APP_URL`, `ADMIN_*`, `MAX_WAIT_TIME`) and hard-coded names (`PORTFOLIO_NAME`, `ORGANIZATION_NAME`, `APPLICATION_NAME`) |
| `api.js` | `makeRequest()` (HTTP + auth header), `waitForApp()` (60 s readiness probe) |
| `auth.js` | `initializeAdmin()` via `POST /mr-api/initialize`; falls back to `POST /mr-api/login` if already initialized |
| `portfolio.js` | Discovers portfolio by `GET /mr-api/portfolio`; creates application; resolves default environment |
| `features.js` | Creates feature definitions, unlocks `BOOLEAN` flags, and sets typed values per flag |
| `flags.js` | Loads `exampleFlags.json` from one of four candidate paths |

```mermaid
sequenceDiagram
    participant B as build-setup.sh
    participant S as party-server (build time)
    participant SU as setup.js
    participant API as FeatureHub Mgmt API

    B->>S: start via bathe.BatheBooter
    B->>S: poll APP_URL (max 120 s)
    B->>SU: node /app/setup.js
    SU->>API: POST /mr-api/initialize (or fallback /mr-api/login)
    API-->>SU: token
    SU->>API: GET /mr-api/portfolio
    SU->>API: POST /mr-api/portfolio/{id}/application
    SU->>API: GET /mr-api/application/{id}/environment
    loop each flag in exampleFlags.json
        SU->>API: POST /mr-api/application/{id}/features
        alt type == BOOLEAN
            SU->>API: PUT /mr-api/application/{id}/feature-environments/{key} (unlock)
        end
        SU->>API: PUT /mr-api/features/{envId}/feature/{key} (set typed value)
    end
    SU-->>B: exit 0
    B->>S: kill (graceful → force after ~2 s)
```

Sources: [specs/dk-flags-party-server-image.md:299-317](), [docker/config.js:1-10]()

---

## Environment Variables

All four variables have Dockerfile `ENV` defaults. They can be overridden at **build time** (affecting the admin baked into the image) or at **run time** (affecting the server's behavior, but **not** rewriting the already-embedded admin data).

| Variable | Default | Build-time effect | Run-time effect |
|---|---|---|---|
| `APP_URL` | `http://localhost:8085` | URL that `build-setup.sh` polls and `setup.js` targets | URL passed to the server process |
| `ADMIN_USER` | `admin` | Admin display name passed to `/mr-api/initialize` | No effect on embedded admin |
| `ADMIN_PASSWORD` | `admin` | Admin password baked into the image | No effect on embedded admin |
| `ADMIN_EMAIL` | `admin@example.com` | Admin login email baked into the image | No effect on embedded admin |

> **Security note**: The defaults are intentionally insecure and intended for local development and integration testing only. Production builds must override all `ADMIN_*` variables at build time. See [Known Risks](#known-risks) below.

Sources: [docker/Dockerfile:39-43](), [docker/config.js:1-10](), [specs/dk-flags-party-server-image.md:280-284]()

---

## Running the Container

### Default run

```bash
docker run -p 8085:8085 dk-flags-party-server
```

FeatureHub is available at `http://localhost:8085`. The pre-seeded admin is `admin@example.com` / `admin`.

### Custom credentials at run time

```bash
docker run -p 8085:8085 \
  -e ADMIN_USER=myadmin \
  -e ADMIN_PASSWORD=mypassword \
  -e ADMIN_EMAIL=admin@myorg.com \
  dk-flags-party-server
```

These env vars are visible to the server process but **do not** re-run setup. The admin embedded at build time remains unchanged.

### What `entrypoint.sh` does

The entrypoint launches the party-server in the foreground using `exec` (replacing the shell process) via the same `bathe.BatheBooter` strategy as `build-setup.sh`, with no setup re-run:

```sh
exec java -cp "/app/classpath/*:/app/libs/*" \
  bathe.BatheBooter \
  -Rio.featurehub.party.Application \
  -P/etc/common-config/common.properties \
  -P/etc/app-config/application.properties \
  -P/etc/app-config/secrets.properties
```

Sources: [docker/entrypoint.sh:1-44]()

---

## CI/CD Pipeline

Every push to the `main` branch triggers the `dkcicd` pipeline `build-and-push-image-v1`, which builds the image on `amd64` and pushes it to AWS ECR:

```yaml
build:
  provider: dkcicd
  pipelines:
  - name: build-and-push-image-v1
    parameters:
      awsAccountId: "053131491888"
      awsRegion: us-east-1
      imageRepo: dk-flags-party-server
      imageTag: 0.0.1
      dockerfilePath: "./docker/Dockerfile"
    runtime:
      architecture: amd64
    when:
    - event: push
      source: branch
      regex: ^(main)$
```

Sources: [.vtex/deployment.yaml:1-21]()

| Property | Value |
|---|---|
| CI provider | `dkcicd` |
| Pipeline | `build-and-push-image-v1` |
| Trigger | push to `main` |
| AWS account | `053131491888` |
| AWS region | `us-east-1` |
| ECR repository | `dk-flags-party-server` |
| Image tag | `0.0.1` |
| Build architecture | `amd64` |

---

## Production Endpoint

The published image runs behind **https://flags.vtex.com/**, which is the canonical FeatureHub Management UI and API endpoint for DK services. The deployment topology — load balancer, TLS, persistent storage, secret injection, scaling, backups, and DNS — is owned outside this repository. This repo contracts only the local image artifact.

Sources: [specs/dk-flags-party-server-image.md:11-12](), [specs/dk-flags-party-server-image.md:323-326]()

---

## Backstage Catalog Registration

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

| Field | Value |
|---|---|
| Component name | `dk-flags` |
| Application ID | `OG35ZXTZ` |
| Owner team | `te-0034` |
| Lifecycle | `stable` |
| Type | `fullstack-app` |
| System | `dk-flags` |
| GitHub slug | `vtex/dk-flags` |
| TechDocs ref | `dir:../` |

Sources: [.vtex/catalog-info.yaml:1-22]()

---

## Downstream OpenFeature SDK Consumers

The image exposes the standard FeatureHub Management API. DK services connect using OpenFeature provider SDKs maintained in separate repositories:

| SDK | Target | Repository |
|---|---|---|
| JS client-side provider | Frontend | `vtex/featurehub-openfeature-client-provider-js` |
| JS server-side provider | Backend (Node) | `vtex/featurehub-openfeature-provider-js` |
| Go provider | Backend (Go) | `vtex/featurehub-openfeature-provider-go` |
| .NET provider | Backend (.NET) | `vtex/featurehub-openfeature-provider-dotnet` |

These SDKs connect to the running FeatureHub instance at the `APP_URL` (or `https://flags.vtex.com/` in production) to read feature flag state. The image does not bundle or distribute these SDKs; they are separate artifacts.

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

---

## Extension Points

### Adding or changing seed flags

Edit `docker/exampleFlags.json` and rebuild:

```bash
docker build -f docker/Dockerfile -t dk-flags-party-server .
```

Each entry in the file follows this schema:

```json
{
  "key": "my-feature-flag",
  "type": "BOOLEAN | STRING | NUMBER | JSON",
  "defaultValue": true,
  "name": "Optional display name",
  "description": "Optional description"
}
```

A `409` response from feature creation is treated as "already exists" and counted as `skipped` (not an error), making the setup idempotent across rebuilds.

Sources: [specs/dk-flags-party-server-image.md:241-253](), [specs/dk-flags-party-server-image.md:206-209]()

### Overriding admin credentials

Pass `ADMIN_USER`, `ADMIN_PASSWORD`, `ADMIN_EMAIL`, and optionally `APP_URL` as `--build-arg` or `-e` flags at `docker build` time. These are consumed by `build-setup.sh` and `setup.js` before the image layer is committed. The resulting image embeds those values immutably.

---

## Known Risks

| Risk | Impact | Notes |
|---|---|---|
| Floating base image tag (`featurehub/party-server:latest`) | High | A breaking API change in the base image will silently break the build-time setup. Pin to a specific FeatureHub version for stability. |
| Mutable ECR tag `0.0.1` | Medium | Every push to `main` overwrites the same tag. Consumers who reference `0.0.1` will silently pick up a new image on next pull. Pin by digest for stability-sensitive environments. |
| Admin credentials baked at build time | High (security) | Default `admin`/`admin` is intentional for dev/test. Production builds must override all `ADMIN_*` env vars at build time. Once baked, credentials cannot be rotated without a rebuild. |
| Run-time env var override confusion | Medium | `ADMIN_*` passed only at `docker run` will not rewrite the embedded admin. The README documents this, but the mismatch is a common operator confusion point. |
| Hard-coded `PORTFOLIO_NAME`, `ORGANIZATION_NAME`, `APPLICATION_NAME` | Medium | All three are constants in `config.js`, not env-overridable. Images for non-`integrationTest` topologies require a code change. |

Sources: [specs/dk-flags-party-server-image.md:168-177](), [docker/config.js:7-9]()

---

## Outstanding Follow-Ups

The spec identifies four open items that are not yet addressed in the current implementation:

1. **Reconcile `package.json#main`**: The `main` field points to `setup-api.js` and the `scripts.setup` field runs `node setup-api.js`, but the image build runs `setup.js` (the modular rewrite). These should be aligned.
2. **Make `PORTFOLIO_NAME`, `ORGANIZATION_NAME`, `APPLICATION_NAME` env-overridable**: Currently hard-coded in `config.js`; consumers with a different topology cannot customize them without a code change.
3. **Pin the base image tag**: Replace `featurehub/party-server:latest` with a versioned tag once a target FeatureHub version is selected.
4. **Evaluate `docker/run-setup.sh`**: This script provides a manual re-run path for setup but is not invoked by the image build. Its role should be clarified or the file removed.

Sources: [specs/dk-flags-party-server-image.md:226-234](), [docker/package.json:4-7]()

---

The `dk-flags-party-server` image encapsulates a fully-initialized FeatureHub instance in a single artifact, publishing to ECR on every merge to `main` and backing the production endpoint at `https://flags.vtex.com/`. The primary operational levers are the four `ADMIN_*` / `APP_URL` env vars at build time and the `docker/exampleFlags.json` flag seed file; everything else is hardwired and requires a code change or rebuild to modify.
