diff --git a/.dockerignore b/.dockerignore
new file mode 100644
index 00000000..3ef84740
--- /dev/null
+++ b/.dockerignore
@@ -0,0 +1,15 @@
+node_modules
+#.git
+.env
+.gitignore
+
+.DS_Store
+.env
+*.code-workspace
+_site
+.turbo/
+.vscode/
+coverage/
+html/
+NOTES.md
+tsconfig.tsbuildinfo
diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml
new file mode 100644
index 00000000..f198a79d
--- /dev/null
+++ b/.github/workflows/deploy.yml
@@ -0,0 +1,75 @@
+name: 'Run tests'
+
+on:
+ push:
+ branches:
+ - main
+ workflow_dispatch:
+
+env:
+ TAG_NAME: ghcr.io/gsa-tts/tts-10x-atj-dev/doj-demo:latest
+
+jobs:
+ init-deploy:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v3
+
+ - name: Read node version from `.nvmrc` file
+ id: nvmrc
+ shell: bash
+ run: echo ::set-output name=NODE_VERSION::$(cat .nvmrc)
+
+ - name: Install required node.js version
+ uses: actions/setup-node@v3
+ with:
+ node-version: ${{ steps.nvmrc.outputs.NODE_VERSION }}
+
+ - name: Install pnpm
+ uses: pnpm/action-setup@v2
+ id: pnpm-install
+ with:
+ version: 8
+ run_install: false
+ - name: Get pnpm store directory
+ id: pnpm-cache
+ shell: bash
+ run: |
+ echo "STORE_PATH=$(pnpm store path)" >> $GITHUB_OUTPUT
+
+ - uses: actions/cache@v3
+ name: Setup pnpm cache
+ with:
+ path: ${{ steps.pnpm-cache.outputs.STORE_PATH }}
+ key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
+ restore-keys: |
+ ${{ runner.os }}-pnpm-store-
+
+ - name: Install dependencies
+ run: pnpm install
+
+ build-image:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v3
+
+ - name: Build container image
+ run: docker build . --platform linux/amd64 --target doj-demo --tag ${TAG_NAME}
+
+ - name: Publish to container registry
+ run: docker push ${TAG_NAME}
+
+ deploy:
+ runs-on: ubuntu-latest
+ needs: [build-image, init-deploy]
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v3
+
+ - name: Initialize Terraform CDK configuration
+ run: pnpm build
+
+ - name: Apply Terraform CDK configuration
+ run: pnpm cdktf deploy
diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 00000000..ed66c6c5
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,33 @@
+FROM node:20-bookworm AS base
+
+ENV PNPM_HOME="/pnpm"
+ENV PATH="$PNPM_HOME:$PATH"
+RUN corepack enable
+
+FROM base AS build
+
+RUN apt update && \
+ apt install -y git
+COPY . /usr/src/app
+WORKDIR /usr/src/app
+RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm install --frozen-lockfile
+RUN pnpm build
+RUN pnpm deploy --filter=doj-demo --prod /app/doj-demo
+#RUN pnpm deploy --filter=spotlight --prod /app/spotlight
+
+FROM base AS doj-demo
+
+LABEL org.opencontainers.image.description 10x-atj DOJ demo
+
+COPY --from=build /app/doj-demo /app/doj-demo
+COPY --from=build /usr/src/app/apps/doj-demo/dist /app/doj-demo/dist
+WORKDIR /app/doj-demo
+
+ENV HOST=0.0.0.0
+ENV PORT=4321
+EXPOSE 4321
+
+CMD [ "node", "./dist/server/entry.mjs" ]
+
+#HEALTHCHECK --interval=5m --timeout=3s \
+# CMD curl -f http://localhost:4321/ || exit 1
diff --git a/apps/doj-demo/.gitignore b/apps/doj-demo/.gitignore
new file mode 100644
index 00000000..6240da8b
--- /dev/null
+++ b/apps/doj-demo/.gitignore
@@ -0,0 +1,21 @@
+# build output
+dist/
+# generated types
+.astro/
+
+# dependencies
+node_modules/
+
+# logs
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+pnpm-debug.log*
+
+
+# environment variables
+.env
+.env.production
+
+# macOS-specific files
+.DS_Store
diff --git a/apps/doj-demo/.nvmrc b/apps/doj-demo/.nvmrc
new file mode 120000
index 00000000..15bb0fb5
--- /dev/null
+++ b/apps/doj-demo/.nvmrc
@@ -0,0 +1 @@
+../../.nvmrc
\ No newline at end of file
diff --git a/apps/doj-demo/README.md b/apps/doj-demo/README.md
new file mode 100644
index 00000000..e34a99b4
--- /dev/null
+++ b/apps/doj-demo/README.md
@@ -0,0 +1,47 @@
+# Astro Starter Kit: Minimal
+
+```sh
+npm create astro@latest -- --template minimal
+```
+
+[![Open in StackBlitz](https://developer.stackblitz.com/img/open_in_stackblitz.svg)](https://stackblitz.com/github/withastro/astro/tree/latest/examples/minimal)
+[![Open with CodeSandbox](https://assets.codesandbox.io/github/button-edit-lime.svg)](https://codesandbox.io/p/sandbox/github/withastro/astro/tree/latest/examples/minimal)
+[![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://codespaces.new/withastro/astro?devcontainer_path=.devcontainer/minimal/devcontainer.json)
+
+> π§βπ **Seasoned astronaut?** Delete this file. Have fun!
+
+## π Project Structure
+
+Inside of your Astro project, you'll see the following folders and files:
+
+```text
+/
+βββ public/
+βββ src/
+β βββ pages/
+β βββ index.astro
+βββ package.json
+```
+
+Astro looks for `.astro` or `.md` files in the `src/pages/` directory. Each page is exposed as a route based on its file name.
+
+There's nothing special about `src/components/`, but that's where we like to put any Astro/React/Vue/Svelte/Preact components.
+
+Any static assets, like images, can be placed in the `public/` directory.
+
+## π§ Commands
+
+All commands are run from the root of the project, from a terminal:
+
+| Command | Action |
+| :------------------------ | :----------------------------------------------- |
+| `npm install` | Installs dependencies |
+| `npm run dev` | Starts local dev server at `localhost:4321` |
+| `npm run build` | Build your production site to `./dist/` |
+| `npm run preview` | Preview your build locally, before deploying |
+| `npm run astro ...` | Run CLI commands like `astro add`, `astro check` |
+| `npm run astro -- --help` | Get help using the Astro CLI |
+
+## π Want to learn more?
+
+Feel free to check [our documentation](https://docs.astro.build) or jump into our [Discord server](https://astro.build/chat).
diff --git a/apps/doj-demo/astro.config.mjs b/apps/doj-demo/astro.config.mjs
new file mode 100644
index 00000000..7230dea6
--- /dev/null
+++ b/apps/doj-demo/astro.config.mjs
@@ -0,0 +1,29 @@
+import { defineConfig } from 'astro/config';
+import node from '@astrojs/node';
+import react from '@astrojs/react';
+
+// https://astro.build/config
+export default defineConfig({
+ output: 'server',
+ trailingSlash: 'never',
+ base: addTrailingSlash(process.env.BASEURL || ''),
+ adapter: node({
+ mode: 'standalone',
+ }),
+ integrations: [
+ react({
+ include: ['src/components/react/**'],
+ }),
+ ],
+ server: {
+ port: 80,
+ },
+});
+
+function addTrailingSlash(path) {
+ var lastChar = path.substr(-1);
+ if (lastChar === '/') {
+ return path;
+ }
+ return path + '/';
+}
diff --git a/apps/doj-demo/package.json b/apps/doj-demo/package.json
new file mode 100644
index 00000000..91d9be94
--- /dev/null
+++ b/apps/doj-demo/package.json
@@ -0,0 +1,29 @@
+{
+ "name": "doj-demo",
+ "type": "module",
+ "version": "0.0.1",
+ "scripts": {
+ "dev": "astro dev",
+ "start": "astro dev",
+ "build": "astro check && astro build",
+ "preview": "astro preview",
+ "astro": "astro"
+ },
+ "dependencies": {
+ "@astrojs/check": "^0.5.6",
+ "@astrojs/node": "^8.2.3",
+ "@astrojs/react": "^3.0.9",
+ "@atj/design": "workspace:*",
+ "@atj/documents": "workspace:*",
+ "@atj/form-service": "workspace:*",
+ "@atj/forms": "workspace:*",
+ "astro": "^4.4.11",
+ "react": "^18.2.0",
+ "react-dom": "^18.2.0",
+ "react-error-boundary": "^4.0.12",
+ "typescript": "^5.3.3"
+ },
+ "devDependencies": {
+ "@types/react": "^18.2.62"
+ }
+}
diff --git a/apps/doj-demo/public/favicon.ico b/apps/doj-demo/public/favicon.ico
new file mode 100644
index 00000000..0c0872e1
Binary files /dev/null and b/apps/doj-demo/public/favicon.ico differ
diff --git a/apps/doj-demo/public/uswds b/apps/doj-demo/public/uswds
new file mode 120000
index 00000000..bb61b72f
--- /dev/null
+++ b/apps/doj-demo/public/uswds
@@ -0,0 +1 @@
+../../../packages/design/static/uswds
\ No newline at end of file
diff --git a/apps/doj-demo/src/components/AppAvailableFormList.tsx b/apps/doj-demo/src/components/AppAvailableFormList.tsx
new file mode 100644
index 00000000..57216134
--- /dev/null
+++ b/apps/doj-demo/src/components/AppAvailableFormList.tsx
@@ -0,0 +1,26 @@
+import React from 'react';
+import { ErrorBoundary } from 'react-error-boundary';
+
+import { AvailableFormList } from '@atj/design';
+import { getAppContext } from '../context';
+import { getFormUrl } from '../routes';
+import DebugTools from './DebugTools';
+
+export default () => {
+ const ctx = getAppContext();
+ return (
+