Skip to content
Merged
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
Binary file modified data/disciplr.db
Binary file not shown.
10 changes: 10 additions & 0 deletions docs/auth.md
Original file line number Diff line number Diff line change
Expand Up @@ -369,3 +369,13 @@ The test suite includes property-based tests with minimum 100 iterations per pro

## Middleware Consolidation
`auth.middleware.ts` and `userAuth.ts` have been consolidated into `auth.ts`. Please import `authenticate` and `authorize` strictly from `src/middleware/auth.js`. `requireUserAuth` is deprecated and will be removed in #454.

## Abuse Detection and Anomaly Categories

Failed authentication attempts are tracked by the `security/abuse-monitor.ts` middleware and emitted as structured log events with an `AbuseCategory` discriminated union. See `src/types/security.ts` for the full type definition.

Auth-related categories:

- **`brute-force`**: Triggered when a source IP exceeds `SECURITY_FAILED_LOGIN_BURST_THRESHOLD` failed logins within `SECURITY_FAILED_LOGIN_WINDOW_MS`. Carries `failedLoginCount` and `windowMs`.

Aggregate counts are available at `GET /api/admin/abuse/category-counts` for admin users.
43 changes: 34 additions & 9 deletions docs/operations.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,19 +49,44 @@ All environment variables are validated at startup using `src/config/env.ts`. If
| `JOB_WORKER_CONCURRENCY` | 2 | Number of concurrent job workers. |
| `JOB_QUEUE_POLL_INTERVAL_MS` | 250 | How often the job queue checks for new work. |
| `JOB_HISTORY_LIMIT` | 50 | Number of completed/failed jobs to keep in memory metrics. |
| `DATABASE_URL` | - | PostgreSQL connection URL. |
| `JWT_SECRET` | - | Secret for signing JWTs. |

## Docker images & healthchecks
## Structured Abuse Category Taxonomy (#467)

- Dockerfile: A multi-stage, Node 20 (alpine) image is provided at the repository root. It sets `WORKDIR /app` and runs the container as the non-root `node` user for improved security.
- Healthcheck: The `docker-compose.yml` now declares a `backend` service with a `healthcheck` that calls `/api/health`. The Postgres `db` service also has a readiness check. Compose `depends_on` is configured so `backend` will wait for `db` to be healthy.
The abuse monitor now emits structured `security.abuse_detected` events instead of free-form strings, enabling downstream aggregation by anomaly class.

Validation (recommended in CI):
### Categories

```bash
docker compose build && docker compose up --wait
| Category | Trigger | Key fields |
|---|---|---|
| `brute-force` | `failed_login_burst` pattern | `failedLoginCount`, `windowMs` |
| `enumeration` | `endpoint_scan` pattern | `notFoundCount`, `distinctPathCount`, `windowMs` |
| `payload-anomaly` | `repeated_bad_requests` pattern | `badRequestCount`, `windowMs` |
| `rate-limit-trip` | `high_volume` pattern | `requestCount`, `windowMs` |

### Admin endpoint

`GET /api/admin/abuse/category-counts` (admin token required) returns a snapshot of per-category counts:

```json
{
"data": {
"brute-force": 3,
"enumeration": 1,
"payload-anomaly": 0,
"rate-limit-trip": 2
}
}
```

Notes:
- The runtime image includes `curl` so the healthcheck can probe the HTTP endpoint.
- CI should run the `docker compose up --wait` step to ensure service health ordering behaves as expected.
### Log format

```json
{
"event": "security.suspicious_pattern",
"ip": "1.2.3.4",
"category": { "type": "brute-force", "failedLoginCount": 6, "windowMs": 900000 },
"alertCooldownMs": 300000
}
```
69 changes: 69 additions & 0 deletions docs/vaults-api.md
Original file line number Diff line number Diff line change
Expand Up @@ -259,3 +259,72 @@ The validation logic is covered by comprehensive tests including:
// ... other fields
}
```

## Soroban Transaction Polling and Timeout

When `onChain.mode` is `"submit"`, the backend sends the transaction to the Soroban RPC and then polls `getTransaction` until the tx reaches a terminal state.

### Polling behaviour

- After `sendTransaction` returns `PENDING` or `TRY_AGAIN_LATER`, the backend enters a bounded poll loop.
- Each poll calls `getTransaction(hash)`.
- `NOT_FOUND` → sleep `SOROBAN_SUBMIT_POLL_INTERVAL_MS` ms and retry (up to `SOROBAN_SUBMIT_POLL_MAX_ATTEMPTS` attempts).
- `SUCCESS` → resolve with `{ txHash }`.
- `FAILED` → throw `Error("Soroban transaction did not succeed: FAILED")`.
- The entire poll window is bounded by `SOROBAN_SUBMIT_TIMEOUT_MS`. If the deadline elapses before a terminal status is reached, a `SorobanTimeoutError` is thrown.

### Environment variables

| Variable | Default | Description |
|---|---|---|
| `SOROBAN_SUBMIT_TIMEOUT_MS` | `60000` | Hard deadline (ms) for the entire poll window. |
| `SOROBAN_SUBMIT_POLL_INTERVAL_MS` | `1000` | Delay between individual `getTransaction` polls. |
| `SOROBAN_SUBMIT_POLL_MAX_ATTEMPTS` | `30` | Maximum number of poll attempts before giving up. |

### SorobanTimeoutError

`SorobanTimeoutError` is thrown (and surfaced in the submission response as `status: "error"`) when the deadline is exceeded. It carries:

- `txHash` — the transaction hash that was being polled.
- `elapsedMs` — the configured deadline that was exceeded (`SOROBAN_SUBMIT_TIMEOUT_MS`).
- `code` — `"SOROBAN_TIMEOUT"`.
- `status` — `504`.

Example submission response when a timeout occurs:

```json
{
"mode": "submit",
"payload": { "..." },
"submission": {
"attempted": true,
"status": "error",
"error": "Soroban transaction tx-abc123 did not finalise within 60000ms"
}
}
```

## Soroban Transaction Polling and Timeout

When `onChain.mode` is `"submit"`, the backend polls `getTransaction` after sending the transaction until a terminal state is reached.

### Polling behaviour

- After `sendTransaction` returns `PENDING`, the backend enters a bounded poll loop using `retryWithBackoff`.
- Each poll calls `getTransaction(hash)`:
- `NOT_FOUND` → sleep `SOROBAN_SUBMIT_POLL_INTERVAL_MS` ms and retry.
- `SUCCESS` → resolves with `{ txHash }`.
- `FAILED` → throws an error immediately.
- The entire poll window is bounded by `SOROBAN_SUBMIT_TIMEOUT_MS`. If the deadline elapses, a `SorobanTimeoutError` is thrown.

### Env vars

| Variable | Default | Description |
|---|---|---|
| `SOROBAN_SUBMIT_TIMEOUT_MS` | `60000` | Hard deadline (ms) for the whole poll window. |
| `SOROBAN_SUBMIT_POLL_INTERVAL_MS` | `1000` | Delay between `getTransaction` polls. |
| `SOROBAN_SUBMIT_POLL_MAX_ATTEMPTS` | `30` | Max poll attempts before giving up. |

### SorobanTimeoutError

Thrown when the deadline is exceeded. Carries `txHash`, `elapsedMs`, `code: "SOROBAN_TIMEOUT"`, `status: 504`. Surfaced in the submission response as `status: "error"`.
Loading
Loading