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
5 changes: 5 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -64,4 +64,9 @@ jobs:
with:
bun-version: 1.3.14
- run: bun install --frozen-lockfile
# Bun doesn't run npm lifecycle scripts by default, so playwright's own
# postinstall (which would normally fetch its browser binaries) never
# runs — apps/server/src/tools-builtin/browser.test.ts needs a real
# Chromium to launch.
- run: bunx playwright install --with-deps chromium
- run: bun run test
29 changes: 22 additions & 7 deletions .github/workflows/secret-scan.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,31 @@ jobs:
gitleaks:
name: Gitleaks
runs-on: ubuntu-latest
# Non-blocking on first rollout — a full-history scan against this repo
# hasn't been triaged yet (see docs/CI_QUALITY_GATES.md and
# docs/SECURITY_AUDIT.md SEC-06 for the nyxel.sqlite* history question
# this is meant to catch going forward). Flip to blocking once the first
# run's findings, if any, have been reviewed.
# Runs the OSS gitleaks CLI binary directly rather than
# gitleaks/gitleaks-action@v2 — that Action added an organization
# license requirement (see https://github.com/gitleaks/gitleaks-action's
# announcement) and has been failing on every run for this org account
# ("missing gitleaks license") without ever completing an actual scan.
# The underlying gitleaks tool itself remains free/OSS; this step just
# downloads and runs it without the Action wrapper.
#
# Kept non-blocking until this fixed version's first real run (the repo
# has never actually been scanned before — every prior run failed on the
# license check above, before gitleaks itself ran) is reviewed. Flip to
# blocking once that run's findings, if any, have been triaged.
continue-on-error: true
steps:
- uses: actions/checkout@v7
with:
fetch-depth: 0
- uses: gitleaks/gitleaks-action@v2
- name: Install gitleaks
run: |
set -euo pipefail
curl -sSL -o gitleaks.tar.gz \
https://github.com/gitleaks/gitleaks/releases/download/v${GITLEAKS_VERSION}/gitleaks_${GITLEAKS_VERSION}_linux_x64.tar.gz
tar -xzf gitleaks.tar.gz gitleaks
chmod +x gitleaks
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GITLEAKS_VERSION: "8.30.1"
- name: Run gitleaks
run: ./gitleaks detect --source . --log-opts="--all" --redact -v
19 changes: 19 additions & 0 deletions .gitleaks.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
title = "NyxelOS gitleaks config"

[extend]
useDefault = true

# These two files are the regression tests for the secret-strength
# validation logic itself (packages/db/src/secret-guard.ts,
# apps/server/src/env.ts) — they necessarily assign secret-shaped strings
# (both weak placeholders like "dev-secret-change-me" and synthetic
# high-entropy values like "kQ7z2mN9pXvB4wR8sT1yU6eL3cJ0hF5g") to
# process.env.BETTER_AUTH_SECRET/NYXEL_ENCRYPTION_KEY to exercise the accept/
# reject paths. None of these are real secrets ever used against a live
# service — see docs/CI_QUALITY_GATES.md for this workflow's first-run triage.
[allowlist]
description = "Synthetic test fixtures for secret-strength validation tests, not real secrets"
paths = [
'''apps/server/src/env\.test\.ts''',
'''packages/db/src/secret-guard\.test\.ts''',
]
2 changes: 2 additions & 0 deletions Caddyfile
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
X-Content-Type-Options nosniff
Referrer-Policy strict-origin-when-cross-origin
X-Frame-Options SAMEORIGIN
Strict-Transport-Security "max-age=31536000; includeSubDomains"
Permissions-Policy "microphone=(self), camera=()"
}

@health path /healthz
Expand Down
15 changes: 11 additions & 4 deletions apps/server/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,23 @@ WORKDIR /repo
LABEL org.opencontainers.image.title="Nyxel server"
LABEL org.opencontainers.image.description="Nyxel API, auth, agents, and scheduler runtime."

COPY package.json bun.lock* turbo.json tsconfig.base.json ./
COPY apps/server ./apps/server
COPY apps/web/package.json apps/web/package.json
COPY packages ./packages
COPY --chown=bun:bun package.json bun.lock* turbo.json tsconfig.base.json ./
COPY --chown=bun:bun apps/server ./apps/server
COPY --chown=bun:bun apps/web/package.json apps/web/package.json
COPY --chown=bun:bun packages ./packages

RUN bun install

# /data is where the SQLite volume (nyxel-data, docker-compose.pc.yml) mounts.
# Docker seeds a fresh named volume from the image's contents at this path on
# first use, so pre-creating it here with bun:bun ownership keeps it writable
# once the process drops root below.
RUN mkdir -p /data && chown bun:bun /data

WORKDIR /repo/apps/server
ENV NODE_ENV=production
ENV PORT=3001
EXPOSE 3001
USER bun
HEALTHCHECK --interval=30s --timeout=5s --start-period=20s --retries=3 CMD bun -e 'const r = await fetch("http://127.0.0.1:3001/"); if (!r.ok) throw new Error("server unhealthy");'
CMD ["bun", "run", "start"]
Loading
Loading