Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions docs/agenstra/deployment/local-development.md
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,17 @@ docker ps
# Linux: sudo systemctl start docker
```

## Demo Data

To fill a local stack with demo workspaces, environments, tickets, automation runs, knowledge and statistics, start both stacks with their `start-containers` targets, wait for the APIs to finish their migrations, and run:

```bash
nx run demo-data:seed:agenstra # replaces previously seeded demo data
nx run demo-data:reset:agenstra # removes all demo data again
```

The seeder also connects the controller containers to the agent-manager network so seeded workspaces reach the local manager. Accounts and details are listed in [`tools/demo-data/README.md`](../../../tools/demo-data/README.md).

## Troubleshooting

### Database Connection Issues
Expand Down
11 changes: 11 additions & 0 deletions docs/decabill/deployment/local-development.md
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,17 @@ When `QUEUE_ROLE=all` or `api` with `QUEUE_BULL_BOARD_ENABLED=true`:

See **[Background Jobs](./background-jobs.md)**.

## Demo Data

To fill a local stack with plausible demo data (every tenant, all account, subscription, invoice and offer states, dummy provisioned servers), start the containers with `nx run decabill-backend-billing-manager:start-containers`, wait for the API to finish its migrations, and run:

```bash
nx run demo-data:seed:decabill # replaces previously seeded demo data
nx run demo-data:reset:decabill # removes all demo data again
```

Only rows created by the tool are touched. Accounts, the shared demo password and details are listed in [`tools/demo-data/README.md`](../../../tools/demo-data/README.md).

## Troubleshooting

### Database Connection Issues
Expand Down
96 changes: 95 additions & 1 deletion graph/graph.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"version": 1,
"generatedAt": "2026-10-05T19:13:17.421Z",
"generatedAt": "2026-10-06T20:03:43.002Z",
"nodes": [
{
"id": "project:@forepath/test/mounted-plugin-fixture",
Expand Down Expand Up @@ -1830,6 +1830,27 @@
"featureGroup": "app"
}
},
{
"id": "project:demo-data",
"type": "tool",
"attrs": {
"name": "demo-data",
"root": "tools/demo-data",
"tags": [
"npm:private",
"type:tool",
"scope:repo"
],
"type": "tool",
"targets": [
"lint",
"build",
"test",
"seed",
"reset"
]
}
},
{
"id": "project:graph",
"type": "tool",
Expand Down Expand Up @@ -13167,6 +13188,24 @@
"projectName": "release-integrity"
}
},
{
"id": "file:tools/demo-data/README.md",
"type": "readme",
"attrs": {
"path": "tools/demo-data/README.md",
"languageOrKind": "md",
"projectName": "demo-data"
}
},
{
"id": "file:tools/demo-data/src/lib/agenstra/agenstra-controller.builder.ts",
"type": "controller",
"attrs": {
"path": "tools/demo-data/src/lib/agenstra/agenstra-controller.builder.ts",
"languageOrKind": "ts",
"projectName": "demo-data"
}
},
{
"id": "file:tools/graph/README.md",
"type": "readme",
Expand Down Expand Up @@ -24829,6 +24868,16 @@
"domain": "agenstra"
}
},
{
"id": "concept:agenstra-demo-data",
"type": "concept",
"attrs": {
"title": "Demo Data",
"docPath": "docs/agenstra/deployment/local-development.md",
"sectionAnchor": "demo-data",
"domain": "agenstra"
}
},
{
"id": "concept:agenstra-operator-runbook",
"type": "concept",
Expand Down Expand Up @@ -28739,6 +28788,16 @@
"domain": "decabill"
}
},
{
"id": "concept:decabill-demo-data",
"type": "concept",
"attrs": {
"title": "Demo Data",
"docPath": "docs/decabill/deployment/local-development.md",
"sectionAnchor": "demo-data",
"domain": "decabill"
}
},
{
"id": "concept:decabill-troubleshooting",
"type": "concept",
Expand Down Expand Up @@ -41651,6 +41710,16 @@
"to": "file:tools/release-integrity/README.md",
"type": "contains"
},
{
"from": "project:demo-data",
"to": "file:tools/demo-data/README.md",
"type": "contains"
},
{
"from": "project:demo-data",
"to": "file:tools/demo-data/src/lib/agenstra/agenstra-controller.builder.ts",
"type": "contains"
},
{
"from": "project:graph",
"to": "file:tools/graph/README.md",
Expand Down Expand Up @@ -47266,6 +47335,11 @@
"to": "concept:agenstra-linux-sudo-systemctl-start-docker",
"type": "contains"
},
{
"from": "file:docs/agenstra/deployment/local-development.md",
"to": "concept:agenstra-demo-data",
"type": "contains"
},
{
"from": "file:docs/agenstra/deployment/local-development.md",
"to": "concept:agenstra-troubleshooting",
Expand Down Expand Up @@ -49956,6 +50030,11 @@
"to": "concept:decabill-bull-board-local",
"type": "contains"
},
{
"from": "file:docs/decabill/deployment/local-development.md",
"to": "concept:decabill-demo-data",
"type": "contains"
},
{
"from": "file:docs/decabill/deployment/local-development.md",
"to": "concept:decabill-troubleshooting",
Expand Down Expand Up @@ -73106,6 +73185,11 @@
"to": "project:decabill-frontend-docs",
"type": "documents"
},
{
"from": "concept:decabill-demo-data",
"to": "project:decabill-backend-billing-manager",
"type": "documents"
},
{
"from": "concept:decabill-install-verification-checklist",
"to": "api:HTTP:GET:/health",
Expand Down Expand Up @@ -79516,6 +79600,11 @@
"to": "domain:agenstra",
"type": "belongs_to"
},
{
"from": "concept:agenstra-demo-data",
"to": "domain:agenstra",
"type": "belongs_to"
},
{
"from": "concept:agenstra-operator-runbook",
"to": "domain:agenstra",
Expand Down Expand Up @@ -81471,6 +81560,11 @@
"to": "domain:decabill",
"type": "belongs_to"
},
{
"from": "concept:decabill-demo-data",
"to": "domain:decabill",
"type": "belongs_to"
},
{
"from": "concept:decabill-troubleshooting",
"to": "domain:decabill",
Expand Down
39 changes: 39 additions & 0 deletions tools/demo-data/.eslintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
{
"extends": ["../../.eslintrc.json", "../../.eslintrc.overrides.json"],
"ignorePatterns": ["!**/*", "dist/**/*"],
"overrides": [
{
"files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
"rules": {}
},
{
"files": ["*.ts", "*.tsx"],
"rules": {}
},
{
"files": ["*.js", "*.jsx"],
"rules": {}
},
{
"files": ["*.json"],
"parser": "jsonc-eslint-parser",
"rules": {
"@nx/dependency-checks": [
"error",
{
"ignoredFiles": [
"{projectRoot}/eslint.config.{js,cjs,mjs,ts,cts,mts}"
]
}
]
}
},
{
"files": ["./package.json", "./generators.json"],
"parser": "jsonc-eslint-parser",
"rules": {
"@nx/nx-plugin-checks": "error"
}
}
]
}
139 changes: 139 additions & 0 deletions tools/demo-data/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
# @forepath/demo-data

Nx project **`demo-data`**: seeds plausible random demo data into the **Decabill** and **Agenstra** databases of a local container stack, and removes it again.

The seeders assume the stacks were started with each app's `start-containers` target, so the containers use the values in `.start-containers.env`:

```bash
nx run decabill-backend-billing-manager:start-containers
nx run agenstra-backend-agent-manager:start-containers
nx run agenstra-backend-agent-controller:start-containers
```

Wait until the API containers are healthy before seeding. They run the migrations on start, and the seeder refuses to run against an incomplete schema.

## Usage

```bash
nx run demo-data:seed # Decabill + Agenstra
nx run demo-data:seed:decabill
nx run demo-data:seed:agenstra

nx run demo-data:reset # remove all demo rows again
nx run demo-data:reset:decabill
nx run demo-data:reset:agenstra
```

CLI (after `nx run demo-data:build`, from the repository root):

```bash
node tools/demo-data/dist/src/cli.js seed decabill --seed 42
node tools/demo-data/dist/src/cli.js seed all --dry-run --sql-out tmp/demo-sql
node tools/demo-data/dist/src/cli.js reset agenstra
```

| Option | Description |
| ----------------- | ----------------------------------------------------------------- |
| `--seed <number>` | Random seed; the same seed produces the same data set |
| `--dry-run` | Build SQL only; no container is contacted |
| `--sql-out <dir>` | Write the generated SQL scripts (useful for review and debugging) |

- **`seed`** first removes previously seeded rows and then inserts a fresh data set, so it can be repeated.
- **`reset`** only removes rows created by this tool. Every seeded row has an id starting with **`5eedda7a-`**. Real data in the same database is never touched.
- Each database is written in a single transaction (`psql --single-transaction`). If any statement fails, nothing is applied.

## How it connects

The compose files do not publish the Postgres ports. The tool runs SQL through `docker exec <container> psql`. It reads database names and `ENCRYPTION_KEY` from each app's `.start-containers.env`, and falls back to the compose defaults for empty values. Encrypted columns use the same AES-256-GCM format as `@forepath/shared/backend/util-crypto`.

| Variable | Default |
| -------------------------------------------------- | --------------------------------- |
| `DEMO_DATA_DECABILL_POSTGRES_CONTAINER` | `billing-manager-postgres` |
| `DEMO_DATA_AGENSTRA_CONTROLLER_POSTGRES_CONTAINER` | `agent-controller-postgres` |
| `DEMO_DATA_AGENSTRA_MANAGER_POSTGRES_CONTAINER` | `agent-manager-postgres` |
| `DEMO_DATA_AGENSTRA_MANAGER_ENDPOINT` | `http://agent-manager-api:<PORT>` |
| `DEMO_DATA_AGENSTRA_CONNECT_NETWORK` | `true` |

## Accounts

All demo accounts use the password **`Demo-Passw0rd!`**. Accounts with TOTP use the base32 secret **`KRUGKIDROVUWG2ZAMJZG653OEBTG66BAJJ2W24DT`**. Pending email confirmations and password resets use the code **`DEMO42`**. These are public demo values. Never use them outside local environments.

One account exists per state (local part = state key):

| Account | State |
| ----------------------------- | --------------------------------------- |
| `admin` | Administrator |
| `admin-totp` | Administrator with authenticator app |
| `user` | Active user |
| `user-email-2fa` | Email 2FA opt-in |
| `user-totp` | Authenticator app |
| `user-unconfirmed` | Registered, email not confirmed |
| `user-locked` | Locked |
| `user-password-reset` | Password reset pending |
| `user-password-reset-expired` | Password reset expired |
| `user-sso` | Keycloak-linked (no local password) |
| `user-sessions-revoked` | Sessions revoked (bumped token version) |
| `user-billing-day` | Fixed billing day of month |
| `controller` (Agenstra only) | Service account role |

- **Decabill:** `<state>@<tenant>.decabill.example` for **every** tenant (`default` plus `TENANTS`). Send the matching `X-Tenant` header, or use the tenant's console URL.
- **Agenstra:** `<state>@agenstra.example`.

With `DISABLE_FORCE_LOGIN_2FA=false` (the default), every password login also asks for an emailed code. Read it in MailHog (Decabill: http://localhost:8026, Agenstra: http://localhost:8025).

## What gets seeded

### Decabill (per tenant)

- **Customers:** customer profiles with complete, incomplete and missing data; VAT ids in all validation states; trust levels; auto billing on and off. Customers come from DE, AT, NL, FR, PL, CH and US, so domestic VAT, reverse charge, OSS and third-country tax modes all appear.
- **Catalog:**
- Service types and plans: hourly, daily, monthly, quarterly and yearly; billing-only; reduced VAT; inactive plans.
- Meters, add-ons and cloud-init configs.
- Promotions: running, expired, upcoming, paused and capped.
- **Subscriptions:** every status. Server items are provisioned with hostname, IP and server snapshot, plus some failed and pending items. Also add-ons in every status, config changes, usage records, promotion redemptions, backorders and open positions.
- **Invoices:** every status, with line items, payment attempts, refunds and promotion applications.
- **Offers:** every status.
- **Projects:** milestones, tickets in every status and priority (with sub-tickets), comments, activity, and billed and unbilled time entries.
- **Back office:** suppliers, contracts and supplier invoices; DATEV debtor/creditor accounts and exports; OSS ledger; audit logs; webhooks with deliveries; email delivery log; personal access tokens.

**Dummy provisioning:** server products use the provider id `demo`. It is not a registered provisioning module, so no remote resources are ever created. The provisioning job just marks such items active. Live server lookups fail and fall back to the seeded snapshot. Start, stop and restart fail, because there is no server.

### Agenstra

- **Agent manager:** six environments (agents) of every container type. Each has chat sessions and history, environment variables, deployment configurations and runs, and synced filter rules. Agents have no container, so the history is readable but new chat messages cannot run.
- **Controller:**
- **Workspaces:** three point to the local agent-manager; one remote Keycloak workspace is offline.
- Members with workspace roles, and agent credentials.
- **Tickets:** every status and priority, sub-tickets, comments, activity, AI body generation sessions.
- **Automation:** settings, runs in every final state with steps and leases.
- **Knowledge:** folders, pages and relations.
- **Statistics:** about 45 days of chat usage, filter drops and flags, entity events.
- **Configuration and integrations:** console filter rules with sync states, OpenCode workspace configs with MCP allow/deny lists, Atlassian imports, webhooks, the email log and personal access tokens.

The local controller and manager run in separate compose networks. After seeding, the tool runs `docker network connect agent-manager-network` for the controller API, worker and scheduler, so workspaces can reach `http://agent-manager-api:3000`. `start-containers` recreates the containers, so seed again (or connect again) after restarting them. Set `DEMO_DATA_AGENSTRA_CONNECT_NETWORK=false` to skip this step.

## Background jobs

The seeded data is shaped so that the schedulers leave it mostly alone:

- Next billing dates are at period end.
- Open backorders have a future retry date.
- Accepted offers are already fulfilled.
- Automation runs only on tickets that are not open.
- Atlassian imports are disabled.

A few transitional states are processed by the running jobs within minutes, as they would be in production:

- Decabill: pending instant cancellation, pending config changes, pending add-ons, and hourly/daily subscriptions.
- Agenstra: the automation scheduler only picks up approved open tickets, and the seed leaves none of those.

Search results come from OpenSearch, which is filled on the next reindex run (`SEARCH_REINDEX_INTERVAL`). List pages read Postgres and show the data immediately.

## Development

```bash
nx run demo-data:build
nx run demo-data:test
```

Seeders live in `src/lib/decabill` and `src/lib/agenstra`. Shared helpers live in `src/lib/core`: SQL building, encryption, account states, random data and `docker exec` access. When a migration changes a seeded table, update the matching builder and the table order in `*.tables.ts`.
7 changes: 7 additions & 0 deletions tools/demo-data/jest.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export default {
displayName: 'demo-data',
preset: '../../jest.preset.cjs',
testEnvironment: 'node',
coverageDirectory: '../../coverage/tools/demo-data',
testMatch: ['**/src/**/*.spec.ts'],
};
Loading
Loading