diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..098a399
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,6 @@
+node_modules/
+dist/
+.next/
+target/
+.env
+*.log
diff --git a/CLAUDE.md b/CLAUDE.md
new file mode 100644
index 0000000..b0c8b02
--- /dev/null
+++ b/CLAUDE.md
@@ -0,0 +1,23 @@
+# Reckue Dev
+
+## Project Structure
+- `apps/api` -- NestJS backend (REST + WebSocket)
+- `apps/web` -- Next.js frontend
+- `apps/agent` -- Rust agent for Windows machines
+- `packages/shared` -- Shared TypeScript types
+- `tools/` -- MCP servers and utilities (git submodules)
+- `deploy/` -- Docker Compose, Nginx configs, Dockerfiles
+
+## Development
+- API: `npm run dev:api` (port 3001)
+- Web: `npm run dev:web` (port 3000)
+- Agent: `cd apps/agent && cargo run`
+
+## Servers
+- Git: git.reckue.com (176.53.162.120)
+- Deploy: app.reckue.com (72.56.119.246)
+- Database: 72.56.119.162 (PostgreSQL)
+
+## Redmine
+- Project: reckue-dev
+- URL: https://redmine.reckue.com/projects/reckue-dev
diff --git a/apps/agent/Cargo.toml b/apps/agent/Cargo.toml
new file mode 100644
index 0000000..284cb9a
--- /dev/null
+++ b/apps/agent/Cargo.toml
@@ -0,0 +1,14 @@
+[package]
+name = "reckue-agent"
+version = "0.1.0"
+edition = "2021"
+
+[dependencies]
+tokio = { version = "1", features = ["full"] }
+tokio-tungstenite = { version = "0.24", features = ["native-tls"] }
+serde = { version = "1", features = ["derive"] }
+serde_json = "1"
+uuid = { version = "1", features = ["v4"] }
+tracing = "0.1"
+tracing-subscriber = "0.3"
+portable-pty = "0.8"
diff --git a/apps/agent/src/main.rs b/apps/agent/src/main.rs
new file mode 100644
index 0000000..4509082
--- /dev/null
+++ b/apps/agent/src/main.rs
@@ -0,0 +1,11 @@
+use tracing::info;
+
+#[tokio::main]
+async fn main() {
+ tracing_subscriber::init();
+ info!("Reckue Agent starting...");
+ // TODO: WebSocket connection to Control Plane
+ // TODO: Machine registration + heartbeats
+ // TODO: PTY session manager
+ info!("Agent ready");
+}
diff --git a/apps/api/nest-cli.json b/apps/api/nest-cli.json
new file mode 100644
index 0000000..2566481
--- /dev/null
+++ b/apps/api/nest-cli.json
@@ -0,0 +1,5 @@
+{
+ "$schema": "https://json.schemastore.org/nest-cli",
+ "collection": "@nestjs/schematics",
+ "sourceRoot": "src"
+}
diff --git a/apps/api/package.json b/apps/api/package.json
new file mode 100644
index 0000000..c4af49e
--- /dev/null
+++ b/apps/api/package.json
@@ -0,0 +1,36 @@
+{
+ "name": "@reckue-dev/api",
+ "version": "0.0.1",
+ "private": true,
+ "scripts": {
+ "build": "nest build",
+ "dev": "nest start --watch",
+ "start": "node dist/main",
+ "start:prod": "node dist/main"
+ },
+ "dependencies": {
+ "@nestjs/common": "^11.0.0",
+ "@nestjs/core": "^11.0.0",
+ "@nestjs/platform-express": "^11.0.0",
+ "@nestjs/websockets": "^11.0.0",
+ "@nestjs/platform-socket.io": "^11.0.0",
+ "@nestjs/jwt": "^11.0.0",
+ "@nestjs/passport": "^11.0.0",
+ "@nestjs/typeorm": "^0.3.0",
+ "typeorm": "^0.3.0",
+ "pg": "^8.13.0",
+ "passport": "^0.7.0",
+ "passport-jwt": "^4.0.0",
+ "bcrypt": "^5.1.0",
+ "class-validator": "^0.14.0",
+ "class-transformer": "^0.5.0",
+ "reflect-metadata": "^0.2.0",
+ "rxjs": "^7.8.0"
+ },
+ "devDependencies": {
+ "@nestjs/cli": "^11.0.0",
+ "@nestjs/schematics": "^11.0.0",
+ "@types/node": "^22.0.0",
+ "typescript": "^5.7.0"
+ }
+}
diff --git a/apps/api/src/app.module.ts b/apps/api/src/app.module.ts
new file mode 100644
index 0000000..ee5f2c9
--- /dev/null
+++ b/apps/api/src/app.module.ts
@@ -0,0 +1,8 @@
+import { Module } from '@nestjs/common';
+
+@Module({
+ imports: [],
+ controllers: [],
+ providers: [],
+})
+export class AppModule {}
diff --git a/apps/api/src/main.ts b/apps/api/src/main.ts
new file mode 100644
index 0000000..a302bcc
--- /dev/null
+++ b/apps/api/src/main.ts
@@ -0,0 +1,13 @@
+import { NestFactory } from '@nestjs/core';
+import { ValidationPipe } from '@nestjs/common';
+import { AppModule } from './app.module';
+
+async function bootstrap() {
+ const app = await NestFactory.create(AppModule);
+ app.enableCors();
+ app.useGlobalPipes(new ValidationPipe({ whitelist: true, transform: true }));
+ app.setGlobalPrefix('api');
+ await app.listen(process.env.PORT ?? 3001);
+ console.log(`API running on port ${process.env.PORT ?? 3001}`);
+}
+bootstrap();
diff --git a/apps/api/tsconfig.json b/apps/api/tsconfig.json
new file mode 100644
index 0000000..a1c778d
--- /dev/null
+++ b/apps/api/tsconfig.json
@@ -0,0 +1,21 @@
+{
+ "compilerOptions": {
+ "module": "commonjs",
+ "declaration": true,
+ "removeComments": true,
+ "emitDecoratorMetadata": true,
+ "experimentalDecorators": true,
+ "allowSyntheticDefaultImports": true,
+ "target": "ES2021",
+ "sourceMap": true,
+ "outDir": "./dist",
+ "baseUrl": "./",
+ "incremental": true,
+ "skipLibCheck": true,
+ "strictNullChecks": true,
+ "noImplicitAny": true,
+ "strictBindCallApply": true,
+ "forceConsistentCasingInFileNames": true,
+ "noFallthroughCasesInSwitch": true
+ }
+}
diff --git a/apps/web/next.config.ts b/apps/web/next.config.ts
new file mode 100644
index 0000000..94647ad
--- /dev/null
+++ b/apps/web/next.config.ts
@@ -0,0 +1,7 @@
+import type { NextConfig } from 'next';
+
+const nextConfig: NextConfig = {
+ output: 'standalone',
+};
+
+export default nextConfig;
diff --git a/apps/web/package.json b/apps/web/package.json
new file mode 100644
index 0000000..579dcf5
--- /dev/null
+++ b/apps/web/package.json
@@ -0,0 +1,21 @@
+{
+ "name": "@reckue-dev/web",
+ "version": "0.0.1",
+ "private": true,
+ "scripts": {
+ "dev": "next dev --port 3000",
+ "build": "next build",
+ "start": "next start"
+ },
+ "dependencies": {
+ "next": "^15.0.0",
+ "react": "^19.0.0",
+ "react-dom": "^19.0.0"
+ },
+ "devDependencies": {
+ "@types/node": "^22.0.0",
+ "@types/react": "^19.0.0",
+ "@types/react-dom": "^19.0.0",
+ "typescript": "^5.7.0"
+ }
+}
diff --git a/apps/web/src/app/layout.tsx b/apps/web/src/app/layout.tsx
new file mode 100644
index 0000000..10b333e
--- /dev/null
+++ b/apps/web/src/app/layout.tsx
@@ -0,0 +1,12 @@
+export const metadata = {
+ title: 'Reckue Dev',
+ description: 'Project management & Claude Code sessions',
+};
+
+export default function RootLayout({ children }: { children: React.ReactNode }) {
+ return (
+
+
{children}
+
+ );
+}
diff --git a/apps/web/src/app/page.tsx b/apps/web/src/app/page.tsx
new file mode 100644
index 0000000..302a652
--- /dev/null
+++ b/apps/web/src/app/page.tsx
@@ -0,0 +1,8 @@
+export default function Home() {
+ return (
+
+ Reckue Dev
+ Platform is starting up...
+
+ );
+}
diff --git a/apps/web/tsconfig.json b/apps/web/tsconfig.json
new file mode 100644
index 0000000..54446d5
--- /dev/null
+++ b/apps/web/tsconfig.json
@@ -0,0 +1,21 @@
+{
+ "compilerOptions": {
+ "target": "ES2017",
+ "lib": ["dom", "dom.iterable", "esnext"],
+ "allowJs": true,
+ "skipLibCheck": true,
+ "strict": true,
+ "noEmit": true,
+ "esModuleInterop": true,
+ "module": "esnext",
+ "moduleResolution": "bundler",
+ "resolveJsonModule": true,
+ "isolatedModules": true,
+ "jsx": "preserve",
+ "incremental": true,
+ "plugins": [{ "name": "next" }],
+ "paths": { "@/*": ["./src/*"] }
+ },
+ "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
+ "exclude": ["node_modules"]
+}
diff --git a/deploy/.env.example b/deploy/.env.example
new file mode 100644
index 0000000..d5500ca
--- /dev/null
+++ b/deploy/.env.example
@@ -0,0 +1,2 @@
+DATABASE_PASSWORD=
+JWT_SECRET=
diff --git a/deploy/Dockerfile.api b/deploy/Dockerfile.api
new file mode 100644
index 0000000..39bf25b
--- /dev/null
+++ b/deploy/Dockerfile.api
@@ -0,0 +1,17 @@
+FROM node:22-alpine AS builder
+WORKDIR /app
+COPY package.json ./
+COPY apps/api/package.json apps/api/
+COPY packages/shared/package.json packages/shared/
+RUN npm install --workspace=apps/api --workspace=packages/shared
+COPY apps/api apps/api
+COPY packages/shared packages/shared
+RUN npm run build --workspace=packages/shared
+RUN npm run build --workspace=apps/api
+
+FROM node:22-alpine
+WORKDIR /app
+COPY --from=builder /app/apps/api/dist ./dist
+COPY --from=builder /app/node_modules ./node_modules
+EXPOSE 3001
+CMD ["node", "dist/main"]
diff --git a/deploy/Dockerfile.web b/deploy/Dockerfile.web
new file mode 100644
index 0000000..e262b30
--- /dev/null
+++ b/deploy/Dockerfile.web
@@ -0,0 +1,15 @@
+FROM node:22-alpine AS builder
+WORKDIR /app
+COPY package.json ./
+COPY apps/web/package.json apps/web/
+RUN npm install --workspace=apps/web
+COPY apps/web apps/web
+RUN npm run build --workspace=apps/web
+
+FROM node:22-alpine
+WORKDIR /app
+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
+EXPOSE 3000
+CMD ["node", "apps/web/server.js"]
diff --git a/deploy/docker-compose.yml b/deploy/docker-compose.yml
new file mode 100644
index 0000000..751d72b
--- /dev/null
+++ b/deploy/docker-compose.yml
@@ -0,0 +1,32 @@
+version: '3.8'
+
+services:
+ api:
+ build:
+ context: ..
+ dockerfile: deploy/Dockerfile.api
+ ports:
+ - "3001:3001"
+ environment:
+ - NODE_ENV=production
+ - PORT=3001
+ - DATABASE_HOST=72.56.119.162
+ - DATABASE_PORT=5432
+ - DATABASE_NAME=reckue_dev
+ - DATABASE_USER=reckue
+ - DATABASE_PASSWORD=${DATABASE_PASSWORD}
+ - JWT_SECRET=${JWT_SECRET}
+ restart: unless-stopped
+
+ web:
+ build:
+ context: ..
+ dockerfile: deploy/Dockerfile.web
+ ports:
+ - "3000:3000"
+ environment:
+ - NODE_ENV=production
+ - NEXT_PUBLIC_API_URL=https://app.reckue.com/api
+ restart: unless-stopped
+ depends_on:
+ - api
diff --git a/deploy/nginx/reckue-dev.conf b/deploy/nginx/reckue-dev.conf
new file mode 100644
index 0000000..bb66875
--- /dev/null
+++ b/deploy/nginx/reckue-dev.conf
@@ -0,0 +1,33 @@
+server {
+ listen 80;
+ server_name app.reckue.com;
+ return 301 https://$host$request_uri;
+}
+
+server {
+ listen 443 ssl http2;
+ server_name app.reckue.com;
+
+ ssl_certificate /etc/letsencrypt/live/app.reckue.com/fullchain.pem;
+ ssl_certificate_key /etc/letsencrypt/live/app.reckue.com/privkey.pem;
+
+ location /api {
+ proxy_pass http://localhost:3001;
+ proxy_http_version 1.1;
+ proxy_set_header Upgrade $http_upgrade;
+ proxy_set_header Connection "upgrade";
+ proxy_set_header Host $host;
+ proxy_set_header X-Real-IP $remote_addr;
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+ proxy_set_header X-Forwarded-Proto $scheme;
+ }
+
+ location / {
+ proxy_pass http://localhost:3000;
+ proxy_http_version 1.1;
+ proxy_set_header Upgrade $http_upgrade;
+ proxy_set_header Connection "upgrade";
+ proxy_set_header Host $host;
+ proxy_set_header X-Real-IP $remote_addr;
+ }
+}
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..ba3d808
--- /dev/null
+++ b/package.json
@@ -0,0 +1,11 @@
+{
+ "name": "reckue-dev",
+ "private": true,
+ "workspaces": ["apps/*", "packages/*"],
+ "scripts": {
+ "dev:api": "npm run dev --workspace=apps/api",
+ "dev:web": "npm run dev --workspace=apps/web",
+ "build:api": "npm run build --workspace=apps/api",
+ "build:web": "npm run build --workspace=apps/web"
+ }
+}
diff --git a/packages/shared/package.json b/packages/shared/package.json
new file mode 100644
index 0000000..b94c296
--- /dev/null
+++ b/packages/shared/package.json
@@ -0,0 +1,13 @@
+{
+ "name": "@reckue-dev/shared",
+ "version": "0.0.1",
+ "private": true,
+ "main": "dist/index.js",
+ "types": "dist/index.d.ts",
+ "scripts": {
+ "build": "tsc"
+ },
+ "devDependencies": {
+ "typescript": "^5.7.0"
+ }
+}
diff --git a/packages/shared/src/index.ts b/packages/shared/src/index.ts
new file mode 100644
index 0000000..bd8ec84
--- /dev/null
+++ b/packages/shared/src/index.ts
@@ -0,0 +1,52 @@
+export interface User {
+ id: string;
+ email: string;
+ name: string;
+ role: 'owner' | 'admin' | 'manager' | 'developer';
+ createdAt: Date;
+}
+
+export interface Machine {
+ id: string;
+ name: string;
+ hostname: string;
+ status: 'online' | 'offline';
+ lastHeartbeat: Date;
+}
+
+export interface Project {
+ id: string;
+ name: string;
+ description: string;
+ ownerId: string;
+ createdAt: Date;
+}
+
+export interface Workspace {
+ id: string;
+ projectId: string;
+ machineId: string;
+ path: string;
+ gitUrl: string;
+ branch: string;
+}
+
+export interface Session {
+ id: string;
+ workspaceId: string;
+ status: 'running' | 'idle' | 'stopped' | 'error';
+ startedAt: Date;
+ stoppedAt?: Date;
+}
+
+export type AgentMessage =
+ | { type: 'register'; machineId: string; hostname: string; os: string }
+ | { type: 'heartbeat'; machineId: string; timestamp: string }
+ | { type: 'session_output'; sessionId: string; data: string }
+ | { type: 'session_status'; sessionId: string; status: Session['status'] };
+
+export type ServerMessage =
+ | { type: 'session_start'; sessionId: string; workspaceId: string; command: string }
+ | { type: 'session_stop'; sessionId: string }
+ | { type: 'session_input'; sessionId: string; data: string }
+ | { type: 'workspace_init'; workspaceId: string; gitUrl: string; branch: string };
diff --git a/packages/shared/tsconfig.json b/packages/shared/tsconfig.json
new file mode 100644
index 0000000..039d254
--- /dev/null
+++ b/packages/shared/tsconfig.json
@@ -0,0 +1,11 @@
+{
+ "compilerOptions": {
+ "target": "ES2021",
+ "module": "commonjs",
+ "declaration": true,
+ "outDir": "./dist",
+ "strict": true,
+ "skipLibCheck": true
+ },
+ "include": ["src/**/*"]
+}