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
8 changes: 8 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -48,3 +48,11 @@ skills-lock.json
# Claude Code worktrees and local state
.claude/worktrees/
.claude/ralph-loop.local.md

# Dashboard (Next.js)
dashboard/node_modules/
dashboard/.next/
dashboard/apps/*/node_modules/
dashboard/apps/*/.next/
dashboard/packages/*/node_modules/
dashboard/.env.local
13 changes: 12 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
.PHONY: smoke check test build clean clippy fmt publish publish-dry-run install changelog release harness-audit entropy-check control-refresh control-validate conversations eval-run eval-check eval-rollback
.PHONY: smoke check test build clean clippy fmt publish publish-dry-run install changelog release harness-audit entropy-check control-refresh control-validate conversations eval-run eval-check eval-rollback dashboard-install dashboard-dev dashboard-build

# === GATES ===

Expand Down Expand Up @@ -94,6 +94,17 @@ release: smoke
@echo "Release v$(VERSION) ready. Push with:"
@echo " git push origin master v$(VERSION)"

# === DASHBOARD ===

dashboard-install:
cd dashboard && bun install

dashboard-dev: dashboard-install
cd dashboard && bun run dev

dashboard-build: dashboard-install
cd dashboard && bun run build

# === CONTROL AUDIT ===

control-audit: smoke fmt-check
Expand Down
41 changes: 41 additions & 0 deletions crates/symphony-observability/src/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ use axum::{Json, Router, routing::get};
use serde::Serialize;
use symphony_core::OrchestratorState;
use tokio::sync::Mutex;
use tower_http::cors::{AllowOrigin, Any, CorsLayer};

/// Shared state for the HTTP server.
#[derive(Clone)]
Expand Down Expand Up @@ -92,6 +93,29 @@ pub struct ErrorDetail {
pub message: String,
}

/// Build CORS layer from environment.
///
/// - `SYMPHONY_CORS_ORIGINS` — comma-separated allowed origins (e.g. `http://localhost:3000,https://app.example.com`)
/// - If unset, defaults to permissive `Any` for development convenience.
fn build_cors_layer() -> CorsLayer {
match std::env::var("SYMPHONY_CORS_ORIGINS") {
Ok(origins) if !origins.is_empty() => {
let parsed: Vec<_> = origins
.split(',')
.filter_map(|o| o.trim().parse().ok())
.collect();
CorsLayer::new()
.allow_origin(AllowOrigin::list(parsed))
.allow_methods(Any)
.allow_headers(Any)
}
_ => CorsLayer::new()
.allow_origin(Any)
.allow_methods(Any)
.allow_headers(Any),
}
}

/// Build the HTTP router (S13.7).
pub fn build_router(state: AppState) -> Router {
// API routes — protected by optional bearer token auth
Expand Down Expand Up @@ -120,6 +144,7 @@ pub fn build_router(state: AppState) -> Router {
.route("/readyz", get(readyz))
.route("/metrics", get(get_prometheus_metrics))
.merge(api_routes)
.layer(build_cors_layer())
.with_state(state)
}

Expand Down Expand Up @@ -978,6 +1003,22 @@ mod tests {
assert!(text.contains("symphony_issues_completed"));
}

#[tokio::test]
async fn cors_preflight_returns_headers() {
let state = make_app_state();
let app = build_router(state);
let req = Request::builder()
.method("OPTIONS")
.uri("/api/v1/state")
.header("origin", "http://localhost:3000")
.header("access-control-request-method", "GET")
.body(Body::empty())
.unwrap();
let resp = app.oneshot(req).await.unwrap();
assert!(resp.headers().contains_key("access-control-allow-origin"));
assert!(resp.headers().contains_key("access-control-allow-methods"));
}

#[tokio::test]
async fn prometheus_metrics_bypasses_auth() {
let state = AppState {
Expand Down
5 changes: 5 additions & 0 deletions dashboard/.dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
node_modules
.next
.env.local
*.log
.git
73 changes: 73 additions & 0 deletions dashboard/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.

# dependencies
node_modules
.pnp
.pnp.js

# testing
coverage

# next.js
.next/
apps/*/.next/
out/
build
next-env.d.ts

# misc
.DS_Store
*.pem

# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
.pnpm-debug.log*

# local env files
.env.local
.neon-branch
.env.development.local
.env.test.local
.env.production.local

# turbo
.turbo

.env
.vercel
.env*.local

# Playwright
/test-results/
/playwright-report/
/blob-report/
/playwright/*
apps/*/test-results/
apps/*/playwright-report/
apps/*/blob-report/
apps/*/playwright/*

tsconfig.tsbuildinfo

packages/*/dist
packages/cli/templates
templates

# Evalite DB
evals/db/
apps/*/evals/db/

# Neon
.neon

/.skillz
/AGENTS.md
/AGENTS.md.bak
/CLAUDE.md
/CLAUDE.md.bak
# END Skiller Generated Files
.devtools

.cursor/hooks/state
54 changes: 54 additions & 0 deletions dashboard/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# Symphony Dashboard — multi-stage Docker build
# For Railway / self-hosted deployment

FROM oven/bun:1.3 AS base
WORKDIR /app

# Install dependencies
FROM base AS deps
COPY package.json bun.lock ./
COPY apps/web/package.json ./apps/web/
COPY packages/symphony-client/package.json ./packages/symphony-client/
RUN bun install --frozen-lockfile

# Build the application
FROM base AS builder
COPY --from=deps /app/node_modules ./node_modules
COPY --from=deps /app/apps/web/node_modules ./apps/web/node_modules
COPY . .

# Build args become env vars for Next.js build
ARG DATABASE_URL
ARG AUTH_SECRET
ARG SYMPHONY_API_URL=http://localhost:8080
ARG SYMPHONY_API_TOKEN
ARG APP_URL

ENV DATABASE_URL=${DATABASE_URL}
ENV AUTH_SECRET=${AUTH_SECRET}
ENV SYMPHONY_API_URL=${SYMPHONY_API_URL}
ENV SYMPHONY_API_TOKEN=${SYMPHONY_API_TOKEN}
ENV APP_URL=${APP_URL}

RUN bun run build

# Production image
FROM base AS runner
WORKDIR /app

ENV NODE_ENV=production

RUN addgroup --system --gid 1001 nodejs && \
adduser --system --uid 1001 nextjs

COPY --from=builder /app/apps/web/.next/standalone ./
COPY --from=builder /app/apps/web/.next/static ./apps/web/.next/static
COPY --from=builder /app/apps/web/public ./apps/web/public

USER nextjs

EXPOSE 3000
ENV PORT=3000
ENV HOSTNAME="0.0.0.0"

CMD ["node", "apps/web/server.js"]
13 changes: 13 additions & 0 deletions dashboard/LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
Copyright 2025 Francisco Moretti

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
92 changes: 92 additions & 0 deletions dashboard/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
<div align="center">

<img src="apps/chat/app/icon.svg" alt="ChatJS" width="64" height="64">

# ChatJS

Stop rebuilding the same AI chat infrastructure. ChatJS gives you a production-ready foundation with authentication, 120+ models, streaming, and tools so you can focus on what makes your app unique.

[**Documentation**](https://chatjs.dev/docs) · [**Live Demo**](https://chatjs.dev)

![DemosOnly](https://github.com/user-attachments/assets/f12e89dd-c10c-4e06-9b1a-a9fbd809d234)

</div>

<br />

## CLI

Create a new ChatJS app:

```bash
npx @chat-js/cli@latest create my-app
```

The CLI walks you through gateway, features, and auth choices, generates `chat.config.ts`, and lists the env vars required by your selections.

## Features

- **120+ Models**: Claude, GPT, Gemini, Grok via one API
- **Auth**: GitHub, Google, anonymous. Ready to go.
- **Attachments**: Images, PDFs, docs. Drag and drop.
- **Resumable Streams**: Continue generation after page refresh
- **Branching**: Fork conversations, explore alternatives
- **Sharing**: Share conversations with public links
- **Web Search**: Real-time web search integration
- **Image Generation**: AI-powered image creation
- **Code Execution**: Run code snippets in sandbox
- **MCP**: Model Context Protocol support

## Stack

- [Next.js](https://nextjs.org) - App Router, React Server Components
- [TypeScript](https://www.typescriptlang.org) - Full type safety
- [AI SDK](https://ai-sdk.dev/) - The AI Toolkit for TypeScript
- [AI Gateway](https://vercel.com/ai-gateway) - Unified access to 120+ AI models
- [Better Auth](https://www.better-auth.com) - Authentication & authorization
- [Drizzle ORM](https://orm.drizzle.team) - Type-safe database queries
- [PostgreSQL](https://www.postgresql.org) - Primary database
- [Redis](https://redis.io) - Caching & resumable streams
- [Vercel Blob](https://vercel.com/storage/blob) - Blob storage
- [Shadcn/UI](https://ui.shadcn.com) - Beautiful, accessible components
- [Tailwind CSS](https://tailwindcss.com) - Styling
- [tRPC](https://trpc.io) - End-to-end type-safe APIs
- [Zod](https://zod.dev) - Schema validation
- [Zustand](https://docs.pmnd.rs/zustand) - State management
- [Motion](https://motion.dev) - Animations
- [t3-env](https://env.t3.gg) - Environment variables
- [Pino](https://getpino.io) - Structured Logging
- [Langfuse](https://langfuse.com) - LLM observability & analytics
- [Vercel Analytics](https://vercel.com/analytics) - Web analytics
- [Biome](https://biomejs.dev) - Code linting and formatting
- [Ultracite](https://ultracite.ai) - Biome preset for humans and AI
- [Streamdown](https://streamdown.ai/) - Markdown for AI streaming
- [AI Elements](https://ai-sdk.dev/elements/overview) - AI-native Components
- [AI SDK Tools](https://ai-sdk-tools.dev/) - Developer tools for AI SDK

## Monorepo Layout

- `apps/chat`: Next.js chat app
- `apps/docs`: Mintlify docs
- `packages/cli`: interactive scaffold CLI

## Development

- `bun dev:chat`: run chat app
- `bun dev:docs`: run docs
- `bun lint`: run workspace lint
- `bun test:types`: run chat app typecheck

## Documentation

Visit [chatjs.dev/docs](https://chatjs.dev/docs) to view docs.

## License

Apache-2.0

<br />
<a href="https://vercel.com/oss">
<img alt="Vercel OSS Program" src="https://vercel.com/oss/program-badge.svg" />
</a>
<br />
12 changes: 12 additions & 0 deletions dashboard/apps/web/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Database
DATABASE_URL=postgresql://localhost:5432/symphony_dashboard

# Auth
AUTH_SECRET=change-me-to-a-random-string

# Symphony daemon
SYMPHONY_API_URL=http://localhost:8080
SYMPHONY_API_TOKEN=

# App URL (for non-localhost deployments)
APP_URL=http://localhost:3000
Loading
Loading