Krrish Ghimire c362219a40 prune old sites
2026-07-11 01:28:09 +05:45
2026-07-11 01:28:09 +05:45
2026-07-11 01:28:09 +05:45
2026-07-02 01:15:16 +05:45
2026-07-06 14:49:44 +05:45
2026-07-06 01:39:18 +05:45
2026-07-06 23:04:16 +05:45
2026-07-08 23:25:29 +05:45

Indie — Independent Website Platform

A platform that lets anyone create, own, and publish their own static website through AI prompts. Describe what you want, and the AI generates it. Every site is backed by a git repository.


Quick Start

Prerequisites

  • Java 17+
  • Node.js 18+ and npm
  • Docker & Docker Compose (for PostgreSQL, MongoDB)
  • npm packages: @angular/cli@17 (npm install -g @angular/cli@17)

Setup

  1. Start infrastructure:

    docker compose up -d
    

    This starts PostgreSQL (port 5432) and MongoDB (port 27017).

  2. Start the backend:

    cd backend
    ./gradlew bootRun
    

    The API will be available at http://localhost:8080. Swagger UI at http://localhost:8080/swagger-ui.html.

  3. Start the frontend (separate terminal):

    cd frontend
    npm install
    ng serve
    

    The app will be available at http://localhost:4200. API calls are proxied to http://localhost:8080.


Infrastructure (Docker Compose)

Service Image Ports Credentials Volume
PostgreSQL postgres:16-alpine host:5432 indie / indie_dev pgdata
MongoDB mongo:7 host:27017 (no auth) mongodata

Environment Variables

Variable Description Default
Database
SPRING_DATASOURCE_URL PostgreSQL JDBC URL jdbc:postgresql://localhost:5432/indie
SPRING_DATASOURCE_USERNAME DB username indie
SPRING_DATASOURCE_PASSWORD DB password indie_dev
MONGODB_URI MongoDB connection URI mongodb://localhost:27017/indie
JWT
INDIE_JWT_ACCESS_EXPIRATION Access token TTL (ms) 900000 (15 min)
INDIE_JWT_REFRESH_EXPIRATION Refresh token TTL (ms) 604800000 (7 days)
INDIE_JWT_PRIVATE_KEY_PATH RS256 private key path config/jwt-private.key
INDIE_JWT_PUBLIC_KEY_PATH RS256 public key path config/jwt-public.key
Git
INDIE_GIT_REPOS_PATH Git repositories root /var/git/sites/
INDIE_GIT_AUTHOR_NAME Default git author name Indie Platform
INDIE_GIT_AUTHOR_EMAIL Default git author email git@indie.local
Encryption
INDIE_ENCRYPTION_KEY_PATH AES-256-GCM master key path config/encryption.key
App
INDIE_APP_BASE_URL Frontend base URL (for verification emails) http://localhost:4200
INDIE_VERIFICATION_TOKEN_EXPIRATION Email verification token TTL (minutes) 1440 (24h)
INDIE_CORS_ORIGINS Allowed CORS origins (comma-separated) http://localhost:4200,http://localhost:3000
LLM
GEMINI_API_KEY Google Gemini API key
GEMINI_MODEL Gemini model name gemini-2.0-flash
OPENAI_API_KEY OpenAI API key
OPENAI_MODEL OpenAI model name gpt-4o-mini
INDIE_LLM_RATE_LIMIT Max LLM requests per hour per user 10
Email (MailHog compatible)
EMAIL_HOST SMTP host localhost
EMAIL_PORT SMTP port 1025
EMAIL_USERNAME SMTP username (empty)
EMAIL_PASSWORD SMTP password (empty)
EMAIL_SMTP_AUTH Enable SMTP auth true
EMAIL_SMTP_STARTTLS Enable STARTTLS true

Project Structure

backend/
├── build.gradle                          # Dependencies and build config
├── settings.gradle                       # Project name
├── gradlew / gradlew.bat                 # Gradle wrapper
├── gradle/wrapper/                       # Wrapper JAR + properties
└── src/
    ├── main/java/com/krrishg/
    │   ├── IndieApplication.java         # Spring Boot main class (@EnableScheduling)
    │   ├── config/
    │   │   ├── SecurityConfig.java       # HTTP security, JWT filter, CORS
    │   │   ├── JwtConfig.java            # RS256 token generation and validation
    │   │   ├── TokenService.java         # Interface for token generation/validation
    │   │   ├── JwtAuthenticationToken.java # Custom AbstractAuthenticationToken
    │   │   ├── UserPrincipal.java        # UserDetails record
    │   │   ├── GitConfig.java            # Git repository settings

    │   │   ├── EncryptionService.java    # AES-256/GCM for LLM API key encryption
    │   │   ├── RateLimitConfig.java      # Bucket4j token-bucket rate limiter
    │   │   ├── SchemaMigration.java      # Startup DDL migrations
    │   │   ├── MapToJsonConverter.java   # JPA converter for JSON text columns
    │   │   └── GlobalExceptionHandler.java # @ControllerAdvice error handling
    │   ├── model/
    │   │   ├── User.java                 # User entity (email, password, emailVerified)
    │   │   ├── VerificationToken.java    # Email verification / set-password tokens
    │   │   ├── Site.java                 # Site entity per user
    │   │   ├── SiteStatus.java           # Enum: DRAFT, PUBLISHED
    │   │   ├── AuthProvider.java         # Enum: LOCAL only (OAuth planned)
    │   │   ├── GitRemote.java            # Connected git remote (GitHub/GitLab)
    │   │   ├── SelfHostedConfig.java     # SSH/deploy configuration per site
    │   │   └── GenerationVersion.java    # MongoDB: LLM generation history per site
    │   ├── repository/
    │   │   ├── UserRepository.java
    │   │   ├── SiteRepository.java
    │   │   ├── GitRemoteRepository.java
    │   │   ├── SelfHostedConfigRepository.java
    │   │   └── VerificationTokenRepository.java
    │   ├── dto/
    │   │   ├── RegisterRequest.java
    │   │   ├── RegistrationResponse.java
    │   │   ├── LoginRequest.java
    │   │   ├── RefreshTokenRequest.java
    │   │   ├── AuthResponse.java         # JWT tokens + user info
    │   │   ├── VerifyEmailRequest.java
    │   │   ├── VerifyEmailResponse.java
    │   │   ├── ResendVerificationRequest.java
    │   │   ├── SetPasswordRequest.java
    │   │   ├── SiteRequest.java          # Includes deploymentTarget field
    │   │   ├── SiteResponse.java
    │   │   ├── SiteStructure.java        # Top-level DTO: pages + theme
    │   │   ├── Reference.java            # URL/IMAGE reference for LLM context
    │   │   ├── LLMOptions.java           # LLM generation options
    │   │   ├── LLMResult.java            # LLM generation result
    │   │   ├── LLMConfigRequest.java     # User LLM provider config
    │   │   ├── SelfHostedConfigRequest.java
    │   │   └── SelfHostedConfigResponse.java
    │   ├── service/
    │   │   ├── AuthService.java          # Register, login, token refresh, verify email
    │   │   ├── EmailService.java         # Verification email sender
    │   │   ├── CleanupService.java       # Scheduled cleanup of stale unverified accounts
    │   │   ├── LLMService.java           # Orchestrates LLM calls, stores/restores versions
    │   │   ├── LLMResponseParser.java    # Parses LLM JSON into SiteStructure
    │   │   ├── ReferenceService.java     # Fetches web page content for LLM context
    │   │   ├── RenderService.java        # Renders SiteStructure into HTML + CSS + JS
    │   │   ├── DeploymentService.java    # Orchestrates publish/unpublish
    │   │   ├── Deployer.java             # Interface for self-hosted deployment
    │   │   ├── SshRsyncDeployer.java     # SSH + rsync + nginx + Let's Encrypt
    │   │   ├── PagesProvider.java        # Interface for git provider Pages
    │   │   ├── GitHubPagesProvider.java  # GitHub Pages implementation
    │   │   ├── GitLabPagesProvider.java  # GitLab Pages implementation
    │   │   ├── GitService.java           # JGit: init, commit, push, log, rollback
    │   │   ├── KeyManager.java           # SSH key-pair generation per site

    │   │   ├── ExportService.java        # Exports site structure to ZIP
    │   │   └── DomainService.java        # Custom domain resolution skeleton
    │   ├── service/llm/
    │   │   ├── LLMProvider.java          # Interface for LLM providers
    │   │   ├── GeminiProvider.java       # Google Gemini implementation
    │   │   ├── OpenAIProvider.java       # OpenAI implementation
    │   │   └── ProviderFactory.java      # Factory creating providers by name + key
    │   ├── engine/
    │   │   ├── JsBundle.java             # Generates static JS (nav, smooth scroll, lightbox, contact form)
    │   │   └── StyleCompiler.java        # Generates base CSS with custom property fallbacks
    │   └── controller/
    │       ├── AuthController.java       # POST /api/auth/*
    │       ├── SiteController.java       # CRUD /api/sites
    │       ├── LLMController.java        # POST /api/llm/generate, /refine, /versions/*
    │       ├── GitController.java        # POST /api/sites/{id}/git/*
    │       ├── DeploymentController.java # POST /api/sites/{id}/publish, /unpublish
    │       ├── ExportController.java     # GET /api/sites/{id}/export
    │       ├── UserSettingsController.java # GET/PUT/DELETE /api/user/llm-config
    │       └── SelfHostedController.java # CRUD /api/sites/{id}/self-hosted
    ├── main/resources/
    │   ├── application.yml               # All configuration
    │   └── prompts/
    │       ├── site-generator.txt        # LLM system prompt for site generation
    │       └── site-refiner.txt          # LLM system prompt for refinement
    └── test/java/com/krrishg/
        ├── config/                       # 7 config tests
        ├── controller/                   # 9 controller tests
        ├── dto/                          # 7 DTO tests
        ├── engine/                       # 2 engine tests
        ├── model/                        # 7 model tests
        ├── repository/                   # 4 repository tests
        ├── service/                      # 16 service tests + fakes
        ├── service/llm/                  # 3 LLM provider tests
        └── support/                      # 13 test fakes, stubs, and configs

frontend/
├── Dockerfile                            # Multi-stage production build
├── nginx.conf                            # Nginx config (SPA fallback + /api/ proxy)
├── proxy.conf.json                       # Dev proxy to backend
├── angular.json                          # Angular CLI configuration
├── tailwind.config.js                    # Tailwind CSS configuration
├── postcss.config.js                     # PostCSS (tailwindcss + autoprefixer)
├── package.json
├── tsconfig.json / tsconfig.app.json / tsconfig.spec.json
├── src/
│   ├── index.html
│   ├── main.ts                           # Angular bootstrap
│   ├── styles.css                        # Global styles + Tailwind imports
│   ├── favicon.ico
│   ├── assets/
│   └── app/
│       ├── app.component.ts / .html / .css
│       ├── app.config.ts                 # App providers
│       ├── app.routes.ts                 # Lazy-loaded routes
│       ├── core/
│       │   ├── models/
│       │   │   ├── auth-response.ts      # AuthResponse & UserInfo
│       │   │   ├── site.ts               # SiteResponse
│       │   │   ├── page.ts               # PageResponse
│       │   │   ├── llm.ts                # LLM request/response types, GenerationVersion
│       │   │   └── deployment.ts         # Git/self-hosted config types
│       │   ├── services/
│       │   │   ├── auth.service.ts       # Auth HTTP + token storage
│       │   │   ├── site.service.ts       # Site CRUD HTTP
│       │   │   ├── page.service.ts       # Page CRUD HTTP
│       │   │   ├── deployment.service.ts # Export, git, publish HTTP
│       │   │   ├── llm-api.service.ts    # LLM generate/refine/version HTTP
│       │   │   └── llm-config.service.ts # User LLM key storage HTTP
│       │   ├── guards/
│       │   │   ├── auth.guard.ts         # Redirects unauthenticated to /login
│       │   │   └── login.guard.ts        # Redirects authenticated to /dashboard
│       │   └── interceptors/
│       │       └── jwt.interceptor.ts    # Attaches Bearer token
│       ├── auth/
│       │   ├── login/
│       │   │   └── login.component.ts
│       │   ├── register/
│       │   │   └── register.component.ts
│       │   └── verify-email/
│       │       └── verify-email.component.ts  # Email verification + set-password
│       ├── dashboard/
│       │   ├── dashboard.component.ts         # Site cards grid + toolbar
│       │   ├── create-site-dialog/
│       │   │   └── create-site-dialog.ts
│       │   └── delete-site-dialog/
│       │       └── delete-site-dialog.ts
│       └── llm-workspace/
│           ├── llm-workspace.component.ts     # Main workspace (prompt, preview, timeline)
│           ├── llm-session.service.ts         # LLM generation session state
│           ├── settings/
│           │   └── settings.component.ts      # LLM provider key/dialog settings
│           ├── git-settings/
│           │   └── git-settings.component.ts  # Git remote connection settings
│           ├── preview-dialog/
│           │   └── preview-dialog.component.ts # Preview generated site
│           └── delete-version-dialog/
│               └── delete-version-dialog.ts   # Confirm delete version

config/                                     # Auto-generated on first boot
├── jwt-private.key                         # RS256 private key
├── jwt-public.key                          # RS256 public key
└── encryption.key                          # AES-256-GCM master key (base64)

API Overview

Authentication (/api/auth)

Method Path Description
POST /api/auth/register Register with name + email + password (returns verification pending)
POST /api/auth/login Login with email + password
POST /api/auth/refresh Refresh access token
POST /api/auth/logout Logout
POST /api/auth/verify-email Verify email via token, returns setup token
POST /api/auth/resend-verification Resend verification email
POST /api/auth/set-password Set password after email verification

Sites (/api/sites)

Method Path Description
GET /api/sites List user's sites
POST /api/sites Create a new site (with deploymentTarget: GIT_PAGES, SELF_HOSTED, or NONE)
GET /api/sites/{id} Get site details
PUT /api/sites/{id} Update site
DELETE /api/sites/{id} Delete site

LLM Generation (/api/llm)

Method Path Description
POST /api/llm/generate Generate site from prompt (rate-limited to 10/hour/user)
POST /api/llm/refine Refine existing site structure with follow-up prompt
GET /api/llm/versions/{siteId} List generation versions for a site
POST /api/llm/versions/{id}/restore Restore a specific generation version
DELETE /api/llm/versions/{id} Delete a generation version

Git Remotes (/api/sites/{id}/git)

Method Path Description
POST /api/sites/{id}/git/connect Connect git remote (GitHub/GitLab)
POST /api/sites/{id}/git/disconnect Disconnect git remote
GET /api/sites/{id}/git/status Get connection status, SSH keys, commit log

Deployment (/api/sites/{id})

Method Path Description
POST /api/sites/{id}/publish Publish site (git push)
POST /api/sites/{id}/unpublish Unpublish site
GET /api/sites/{id}/export Download site as .zip archive

Self-Hosted Deployment (/api/sites/{id}/self-hosted)

Method Path Description
GET /api/sites/{id}/self-hosted Get SSH/deploy configuration
PUT /api/sites/{id}/self-hosted Save SSH/deploy configuration
DELETE /api/sites/{id}/self-hosted Remove configuration

User Settings (/api/user)

Method Path Description
GET /api/user/llm-config Get saved LLM provider configs (keys redacted)
PUT /api/user/llm-config Save API key / base URL / model for a provider
DELETE /api/user/llm-config/{provider} Delete saved config for a provider

Frontend Routes

Path Component Auth
/login LoginComponent Redirects to /dashboard if already logged in
/register RegisterComponent Redirects to /dashboard if already logged in
/verify-email VerifyEmailComponent Public (email verification flow)
/dashboard DashboardComponent Requires auth
/llm/:siteId LlmWorkspaceComponent Requires auth
/llm/:siteId/settings SettingsComponent Requires auth
/llm/:siteId/git GitSettingsComponent Requires auth

Tech Stack

Layer Technology
Backend Java 17, Spring Boot 3.2, Gradle (Groovy)
Frontend Angular 17, Angular Material, Tailwind CSS, Leaflet
Relational DB PostgreSQL 16
Document DB MongoDB 7 (generation versions, LLM sessions)

| Auth | Spring Security + JWT (RS256), email/password | | LLM | Gemini (primary), OpenAI (fallback), provider-abstraction | | Git | JGit — local repo per site, GitHub/GitLab remote push | | Email | Spring Boot Mail (MailHog compatible) | | Rate Limiting | Bucket4j | | API Docs | Springdoc OpenAPI (Swagger UI) |


Architecture

                    ┌──────────────────────┐
                    │   Spring Boot API    │
                    │   (VPS / Docker)     │
                    └────────┬─────────────┘
                             │
              ┌──────────────┼──────────────┐
              │              │              │
     ┌────────▼───┐  ┌──────▼──────┐  ┌───▼────────┐
     │ PostgreSQL  │  │   MongoDB   │  │  Git Repos  │
     │ (users,     │  │ (versions,  │  │ /var/git/   │
     │  sites)     │  │  sessions)  │  └────────────┘
     └─────────────┘  └─────────────┘

Development

# Backend
cd backend
./gradlew build         # Build
./gradlew test          # Run tests (unit + slice + integration)
./gradlew bootRun       # Run locally (requires docker compose up -d first)

# Frontend
cd frontend
npm install             # Install dependencies
ng serve                # Dev server at localhost:4200
ng build                # Production build (outputs to dist/)
ng test                 # Run unit tests (Jasmine + Karma)

Production Build (Docker)

docker build -t indie-frontend frontend/
docker run -p 80:80 indie-frontend

The frontend Docker image serves the app via nginx (port 80) and proxies /api/ requests to http://backend:8080.


Roadmap

v0.1 — Auth & Foundation

  • User registration (email/password + email verification)
  • User login (email/password + JWT)
  • Token refresh with 7-day refresh token
  • Site CRUD (create, list, get, update, delete)
  • Frontend: Login, Register, Verify Email, Dashboard
  • Frontend: JWT interceptor, auth guard, login guard, lazy routes
  • Scheduled cleanup of unverified accounts

v0.2 — LLM Prompt Workspace

  • Prompt-to-site generation via Gemini/OpenAI
  • Version timeline with MongoDB storage
  • Live preview (sandboxed iframe)
  • Content editor (grouped by page)
  • Refine via follow-up prompts
  • Rate limiting (10 generations/hour/user)
  • Reference URL support for context

v0.3 — Export, Git & Hosting

  • Render engine (HTML + CSS + JS generation)
  • Git init, commit, push via JGit
  • SSH key-pair management per site
  • GitHub Pages / GitLab Pages providers
  • Export as .zip archive
  • Publish/unpublish flow
  • Custom domain skeleton
  • Self-hosted deployment (SSH + rsync + nginx)

LLM Provider Abstraction

public interface LLMProvider {
    LLMResult generate(String systemPrompt, String userPrompt,
                       List<Reference> references, LLMOptions options);
    String getName();
}

Adding a new provider = implement LLMProvider interface + add config block in application.yml.

Provider API keys can be configured globally via env vars (GEMINI_API_KEY, OPENAI_API_KEY) or per-user through the /api/user/llm-config endpoint (encrypted at rest via AES-256-GCM).


Key Design Decisions

  1. Pure prompt-based — No drag-and-drop builder. Every site starts from a prompt. Iteration happens through follow-up prompts and inline text edits.

  2. Static output only — Generated sites are pure HTML/CSS/JS. No runtime framework dependency. Users own the output forever.

  3. Git as source of truth — Every site is a git repository. Publish = commit. Rollback = checkout. Export includes full .git/.

  4. Version timeline — Every generation creates a version in MongoDB. Users can browse, preview, and restore any previous version.

  5. No billing in MVP — Monetization is post-MVP.


config/ Directory

The following files live in config/ and are auto-generated on first boot if they don't exist. In production, generate and mount them manually:

# RS256 key pair for JWT
openssl genpkey -algorithm RSA -out config/jwt-private.key -pkeyopt rsa_keygen_bits:2048
openssl rsa -pubout -in config/jwt-private.key -out config/jwt-public.key

# AES-256-GCM master key for LLM API key encryption
openssl rand -base64 32 > config/encryption.key

Set the INDIE_JWT_PRIVATE_KEY_PATH, INDIE_JWT_PUBLIC_KEY_PATH, and INDIE_ENCRYPTION_KEY_PATH env vars to point to these files.

Description
No description provided
Readme 651 KiB
Languages
Java 78.8%
TypeScript 20.9%