diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..81d57cc --- /dev/null +++ b/.editorconfig @@ -0,0 +1,90 @@ +# EditorConfig — canonical configuration for all quarkloop repositories. +# +# Copy this file to the repo root as `.editorconfig` (no changes needed — +# it is language-aware and works for all repos). +# +# @see https://github.com/quarkloop/guidelines/blob/main/github/SPEC.md +# @see https://editorconfig.org + +root = true + +[*] +charset = utf-8 +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true + +# Go — gofmt requires tabs +[*.go] +indent_style = tab +indent_size = 4 + +# Rust — rustfmt default is tabs +[*.rs] +indent_style = tab +indent_size = 4 + +# Java — Maven/Java convention is 4 spaces +[*.java] +indent_style = space +indent_size = 4 + +# TypeScript, JavaScript, JSX, TSX — Prettier convention is 2 spaces +[*.{ts,tsx,js,jsx,mjs,cjs}] +indent_style = space +indent_size = 2 + +# CSS, SCSS, Less — 2 spaces +[*.{css,scss,less,sass}] +indent_style = space +indent_size = 2 + +# HTML — 2 spaces +[*.html] +indent_style = space +indent_size = 2 + +# YAML, JSON — 2 spaces +[*.{yml,yaml,json,jsonc,json5}] +indent_style = space +indent_size = 2 + +# TOML — 2 spaces +[*.toml] +indent_style = space +indent_size = 2 + +# Markdown, MDX — 2 spaces (but don't trim trailing whitespace — +# markdown uses two trailing spaces for line breaks) +[*.{md,mdx}] +indent_style = space +indent_size = 2 +trim_trailing_whitespace = false + +# Shell scripts — 2 spaces +[*.sh] +indent_style = space +indent_size = 2 + +# Makefile — MUST use tabs (make requires it) +[Makefile] +indent_style = tab + +# Proto — 2 spaces +[*.proto] +indent_style = space +indent_size = 2 + +# Properties files — 2 spaces +[*.properties] +indent_style = space +indent_size = 2 + +# Env files — 2 spaces +[.env] +indent_style = space +indent_size = 2 + +# Git submodules config — tabs (git convention) +[.gitmodules] +indent_style = tab diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml new file mode 100644 index 0000000..612d9d5 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -0,0 +1,71 @@ +name: Bug Report +description: Something isn't working as expected +labels: [bug] +body: + - type: markdown + attributes: + value: | + Please fill out the sections below to help us reproduce and fix the issue. + The more detail you provide, the faster we can triage. + + - type: textarea + id: description + attributes: + label: Description + description: A clear and concise description of the bug. + validations: + required: true + + - type: textarea + id: steps + attributes: + label: Steps to reproduce + description: Provide a minimal reproducer. If possible, include the exact commands you ran. + placeholder: | + 1. Run `...` + 2. ... + 3. See error + validations: + required: true + + - type: textarea + id: expected + attributes: + label: Expected behaviour + description: What did you expect to happen? + validations: + required: true + + - type: textarea + id: actual + attributes: + label: Actual behaviour + description: What actually happened? Include error messages, logs, or screenshots if relevant. + validations: + required: true + + - type: input + id: version + attributes: + label: Project version + description: The version of the quarkloop project you're using. + placeholder: e.g. output of `quark version`, `npm ls @quarkloop/quark-js`, or commit SHA + validations: + required: true + + - type: input + id: runtime-version + attributes: + label: Runtime version + description: The language runtime you're using. + placeholder: e.g. Go 1.26.2, Node 22.4.0, Bun 1.1.0, JDK 21.0.5 + validations: + required: true + + - type: input + id: os + attributes: + label: Operating system + placeholder: e.g. macOS 14, Ubuntu 24.04, Windows 11 + validations: + required: true diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml new file mode 100644 index 0000000..b4478b4 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -0,0 +1,32 @@ +name: Feature Request +description: Propose a new feature or improvement +labels: [enhancement] +body: + - type: markdown + attributes: + value: | + Thanks for taking the time to propose a feature! Please describe the + problem you're trying to solve — this helps us understand the + motivation before evaluating the solution. + + - type: textarea + id: problem + attributes: + label: Problem + description: What problem does this feature solve? What are you trying to do that currently isn't possible or is painful? + validations: + required: true + + - type: textarea + id: solution + attributes: + label: Proposed solution + description: What would you like to happen? Be as specific as you can — API sketches, CLI examples, or pseudocode are helpful. + validations: + required: true + + - type: textarea + id: alternatives + attributes: + label: Alternatives considered + description: Have you tried any workarounds or alternative approaches? Why didn't they work? diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000..9ce5e74 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,22 @@ +## Description + + + +## Type of change + +- [ ] Bug fix +- [ ] New feature +- [ ] Refactor +- [ ] Documentation +- [ ] Chore / dependency update + +## Checklist + +- [ ] `make build` passes (Go binaries + Java modules) +- [ ] `make test` passes (Go + Java unit tests) +- [ ] `make arch-check` passes when architecture or package ownership changes +- [ ] Services do not call each other directly (communication via NATS only) +- [ ] No TypeScript parsing added to the control plane (control plane treats `.quark.ts` as opaque) +- [ ] GraalJS changes are scoped to the data plane only +- [ ] Relevant documentation updated (README, AGENTS.md, `docs/*.mdx`) +- [ ] Changes are scoped — no unrelated files in this PR diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..8805546 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,42 @@ +version: 2 + +updates: + # Go modules (control plane, CLI, catalog) + - package-ecosystem: gomod + directory: / + schedule: + interval: weekly + day: monday + open-pull-requests-limit: 10 + groups: + minor-and-patch: + update-types: + - minor + - patch + labels: + - dependencies + + # Maven (Java runtime modules) + - package-ecosystem: maven + directory: / + schedule: + interval: weekly + day: monday + open-pull-requests-limit: 10 + groups: + minor-and-patch: + update-types: + - minor + - patch + labels: + - dependencies + + # GitHub Actions + - package-ecosystem: github-actions + directory: / + schedule: + interval: weekly + day: monday + open-pull-requests-limit: 10 + labels: + - dependencies diff --git a/.gitignore b/.gitignore index 8b0d8c6..4acc6b1 100755 --- a/.gitignore +++ b/.gitignore @@ -2,9 +2,11 @@ target/ !.mvn/wrapper/maven-wrapper.jar -# Go -cli/quarkctl -cli/dist/ +# Go build artifacts +quark-cli/quarkctl +quark-cli/dist/ +quark-server/quark-server +quark-catalog/quark-catalog dist/ # IDE @@ -37,5 +39,3 @@ tool-results/ worklog.md *.zip .env -quark-catalog/quark-catalog -cli/cli diff --git a/.markdownlint.json b/.markdownlint.json new file mode 100644 index 0000000..66fd0c0 --- /dev/null +++ b/.markdownlint.json @@ -0,0 +1,11 @@ +{ + "default": true, + + "MD013": false, + "MD033": false, + "MD041": false, + "MD024": { "siblings_only": true }, + + "MD007": { "indent": 2 }, + "MD030": { "ul_single": 1, "ul_multi": 1, "ol_single": 1, "ol_multi": 1 } +} diff --git a/.markdownlintignore b/.markdownlintignore new file mode 100644 index 0000000..51c54bf --- /dev/null +++ b/.markdownlintignore @@ -0,0 +1,8 @@ +node_modules/ +.next/ +.vercel/ +target/ +bin/ +dist/ +docs/content/ +go.sum diff --git a/AGENTS.md b/AGENTS.md index 4a7973a..88f51a9 100755 --- a/AGENTS.md +++ b/AGENTS.md @@ -2,6 +2,14 @@ > **If you read nothing else, read this:** Quark is a **three-service platform** with a strict service-based directory layout. The `.quark.ts` file IS the program — users write only TypeScript and never touch Java. The flow: CLI sends the TypeScript to the **control plane** (server, Go + Fiber), which persists it verbatim to the **Catalog** (Go + SQLite, via NATS) and forwards deploy commands to a **data plane** (runtime, Java/GraalJS) process. The data plane parses the source with `GraalJsSystemParser` (full ESM evaluation) + `SimpleSystemParser` (structural extraction), pulls every node package from the Catalog via `registry.node.pull`, and executes TypeScript node logic over an external NATS server. +## Repository + +- **Name**: Quark Platform +- **Language**: Java 21+ (Quarkus/GraalVM), Go 1.24+ (Fiber/nats.go), TypeScript (node definitions) +- **License**: Apache 2.0 +- **Repo**: [github.com/quarkloop/quark](https://github.com/quarkloop/quark) +- **Guidelines**: [quarkloop/guidelines](https://github.com/quarkloop/guidelines) + --- ## Quick orientation @@ -11,7 +19,7 @@ quark-platform/ ├── AGENTS.md ← this file — READ FIRST ├── README.md ← human-facing project overview ├── Makefile ← all build/test/run commands -├── pom.xml ← Maven parent POM (runtime/* modules only) +├── pom.xml ← Maven parent POM (quark-runtime/* modules only) ├── mvnw, mvnw.cmd, .mvn/ ← Maven wrapper (DO NOT delete .mvn/wrapper/) │ ├── docs/ ← specifications @@ -22,7 +30,7 @@ quark-platform/ │ ├── CLI.md ← CLI / server conceptual alignment │ └── USER-STORY.md ← how a typical user interacts with the system │ -├── server/ ← CONTROL PLANE — Go + Fiber (single binary) +├── quark-server/ ← CONTROL PLANE — Go + Fiber (single binary) │ ├── go.mod, go.sum ← module github.com/quarkloop/quark/server │ ├── cmd/server/main.go ← entry point: env config + graceful shutdown │ └── internal/ @@ -45,7 +53,7 @@ quark-platform/ │ └── http/ ← Fiber app + handlers + middleware + DTOs │ (NO TypeScript parsing, NO GraalJS, NO in-memory node registry) │ -├── runtime/ ← DATA PLANE — Java + GraalJS/Truffle +├── quark-runtime/ ← DATA PLANE — Java + GraalJS/Truffle │ ├── quark-core/ ← Consolidated module: domain records, engine │ │ ← (NATS, lifecycle, dataplane, metrics, polyglot │ │ ← lookup, store SPIs), event bus, registry SPI, @@ -61,7 +69,7 @@ quark-platform/ │ ← native-image config w/ --macro:truffle-svm) │ (NO providers/ subdir — runtime pulls every node from the Catalog at deploy time) │ -├── nodes/ ← STANDARD LIBRARY (canonical node source) +├── quark-nodes/ ← STANDARD LIBRARY (canonical node source) │ ├── README.md ← node layout + the 18 domains │ ├── CHECKLIST.md ← 9-phase node implementation checklist │ └── quark/ ← namespace @@ -87,7 +95,7 @@ quark-platform/ │ ├── store/ ← SQLite persistence (modernc.org/sqlite, pure Go) │ └── server/ ← NATS handlers (catalog.* + registry.*) │ -├── cli/ ← Go CLI (quarkctl) +├── quark-cli/ ← Go CLI (quarkctl) │ ├── main.go │ ├── cmd/ ← Cobra commands │ └── internal/ ← HTTP client + model + output printers @@ -111,9 +119,9 @@ Quark runs as **three cooperating services** plus an external NATS broker: | Service | Language | Binary | Includes GraalJS? | Role | |---------|----------|--------|-------------------|------| -| **Control plane** (server) | Go | `server/quark-server` (~13 MB Go binary, <5s build) | ❌ No | REST API, deploy/undeploy orchestration, spawns data-plane processes | +| **Control plane** (quark-server) | Go | `quark-server/quark-server` (~13 MB Go binary, <5s build) | ❌ No | REST API, deploy/undeploy orchestration, spawns data-plane processes | | **Catalog** | Go | `quark-catalog/quark-catalog` (15 MB) | ❌ No | SQLite-backed metadata store (systems, nodes, events, sources, registry) | -| **Data plane** (runtime) | Java/Native | `runtime/quark-runtime/target/quark-runtime-runner-runner` (194 MB native, 9 min build) | ✅ Yes (via `--macro:truffle-svm`) | Executes nodes, hosts GraalJS, runs providers | +| **Data plane** (quark-runtime) | Java/Native | `quark-runtime/quark-runtime/target/quark-runtime-runner-runner` (194 MB native, 9 min build) | ✅ Yes (via `--macro:truffle-svm`) | Executes nodes, hosts GraalJS, runs providers | | **NATS broker** | Go | `nats-server` (external) | n/a | Message bus for all inter-service communication | The data plane is **spawned on demand** by the control plane's `ProcessManager`. There is one shared runtime process (`runtimeId=shared`) for non-isolated namespaces; isolated namespaces get their own process (`runtimeId=ns-`). @@ -124,12 +132,12 @@ The platform is split into four top-level trees, each with strict dependency rul | Tree | Language | May depend on | May NOT depend on | |------|----------|---------------|-------------------| -| **server/** | Go (Fiber + nats.go + zap) | stdlib + Fiber + nats.go + zap + envconfig | Java, GraalJS, runtime/, core/ (doesn't exist) | -| **runtime/** | Java (Quarkus + GraalJS) | quark-core (internal), Quarkus, NATS, GraalJS/Truffle | server/, nodes/ (runtime pulls nodes from the Catalog, never compiles them) | -| **quark-catalog/** | Go (SQLite + nats.go) | stdlib + modernc.org/sqlite + nats.go | Java, GraalJS, server/, runtime/ | -| **cli/** | Go (Cobra) | stdlib + cobra | Java, GraalJS, server/, runtime/ (talks to the server via HTTP only) | +| **quark-server/** | Go (Fiber + nats.go + zap) | stdlib + Fiber + nats.go + zap + envconfig | Java, GraalJS, quark-runtime/, core/ (doesn't exist) | +| **quark-runtime/** | Java (Quarkus + GraalJS) | quark-core (internal), Quarkus, NATS, GraalJS/Truffle | server/, quark-nodes/ (runtime pulls nodes from the Catalog, never compiles them) | +| **quark-catalog/** | Go (SQLite + nats.go) | stdlib + modernc.org/sqlite + nats.go | Java, GraalJS, quark-server/, quark-runtime/ | +| **quark-cli/** | Go (Cobra) | stdlib + cobra | Java, GraalJS, quark-server/, quark-runtime/ (talks to the server via HTTP only) | -**Key invariant:** `runtime/quark-core/.../script/SimpleSystemParser` is a comment-aware regex parser (no GraalJS). The GraalJS-based `GraalJsSystemParser` lives in `runtime/quark-script/`. The Go control plane has **no parser at all** — it treats `.quark.ts` as an opaque string and forwards it verbatim to the runtime via NATS. A minimal regex "sniffer" in `server/internal/deploy/service.go` extracts just the system name and runtime mode (shared/isolated) for NATS routing; full parsing happens in the runtime. +**Key invariant:** `quark-runtime/quark-core/.../script/SimpleSystemParser` is a comment-aware regex parser (no GraalJS). The GraalJS-based `GraalJsSystemParser` lives in `quark-runtime/quark-script/`. The Go control plane has **no parser at all** — it treats `.quark.ts` as an opaque string and forwards it verbatim to the runtime via NATS. A minimal regex "sniffer" in `quark-server/internal/deploy/service.go` extracts just the system name and runtime mode (shared/isolated) for NATS routing; full parsing happens in the runtime. **TypeScript handling:** GraalJS Community Edition does NOT natively parse TypeScript (see [graaljs#784](https://github.com/oracle/graaljs/issues/784)). The platform's `.ts` files are valid ECMAScript modules using `export default { ... }` without actual type annotations, so the runtime evaluates them directly via GraalJS's native ESM module support (`Source.mimeType("application/javascript+module")` + `js.esm-eval-returns-exports=true`). If real TS type annotations need to be supported in the future, integrate `tsc`/`esbuild`/`swc` at catalog push time. @@ -170,7 +178,7 @@ it only requires pushing the new node package to the Catalog. ``` ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ - │ nodes/ │ │ Catalog │ │ Catalog │ │ Runtime │ + │ quark-nodes/ │ │ Catalog │ │ Catalog │ │ Runtime │ │ (source) │ │ (registry) │ │ (store) │ │ (exec) │ └──────┬──────┘ └──────┬──────┘ └──────┬──────┘ └──────┬──────┘ │ │ │ │ @@ -207,7 +215,7 @@ it only requires pushing the new node package to the Catalog. quarkctl node build quark/time/schedule/timer:v1 ``` -- Resolves `nodes/quark/time/schedule/timer/v1/` from the URI. +- Resolves `quark-nodes/quark/time/schedule/timer/v1/` from the URI. - Reads `manifest.json` to determine the language (`java` | `typescript`). - For **Java**: resolves the classpath from `manifest.json`'s `dependencies.java` list (Maven coordinates → `~/.m2/repository/`), @@ -255,7 +263,7 @@ When `quarkctl apply -f system.quark.ts -n alice` triggers a deploy: `Class.forName(name, false, loader)`, and check whether it implements `NodeImplementationFactory`. - Instantiate the first match via `getDeclaredConstructor()` + - `setAccessible(true)` (nodes/ convention uses package-private + `setAccessible(true)` (quark-nodes/ convention uses package-private classes so the file can be named `node.java`). 4. For **TypeScript** (`contentType=typescript`): - Pass the source to `TypeScriptNodeFactory`, which evaluates it @@ -282,7 +290,7 @@ Once the factory is loaded, the engine: ### What this means in practice -- **Adding a node**: write `nodes//////`, +- **Adding a node**: write `quark-nodes//////`, run `quarkctl node build ` + `quarkctl node push `. No runtime rebuild. - **Updating a node**: edit `src/`, re-run `build` + `push`. The @@ -301,7 +309,7 @@ Once the factory is loaded, the engine: 1. **Never add cross-namespace methods.** All lookups require a `Namespace` parameter. -2. **Never put provider code in the runtime.** Node implementations live exclusively in `nodes/quark/////src/`. The runtime NEVER compiles them — it pulls every node from the Catalog at deploy time via `registry.node.pull` over NATS and loads it dynamically (TypeScript via GraalJS ESM, Java shared-libraries via URLClassLoader). This is the docker-image model: `nodes/` is the source, the Catalog is the registry, the runtime is the container runtime. Adding a node requires `quarkctl node build ` + `quarkctl node push ` — no runtime rebuild. +2. **Never put provider code in the runtime.** Node implementations live exclusively in `quark-nodes/quark/////src/`. The runtime NEVER compiles them — it pulls every node from the Catalog at deploy time via `registry.node.pull` over NATS and loads it dynamically (TypeScript via GraalJS ESM, Java shared-libraries via URLClassLoader). This is the docker-image model: `quark-nodes/` is the source, the Catalog is the registry, the runtime is the container runtime. Adding a node requires `quarkctl node build ` + `quarkctl node push ` — no runtime rebuild. 3. **Never bypass NATS.** All node-to-node communication flows through NATS subjects. The control plane talks to the data plane via NATS, not via in-process method calls. @@ -381,3 +389,12 @@ make clean-native # Remove only native binaries ``` The `RUN_MODE=jvm|native` env var (replaces the old `BUILD_MODE`) controls which binary `make run-*` targets use. Native builds are independent of `RUN_MODE` — `make build-native-*` always builds native regardless. + +## When you're stuck + +- Read `docs/architecture.mdx` for the three-service model, process types, and runtime isolation. +- Read `docs/protocol.mdx` for NATS subjects and wire protocol shapes. +- Read `docs/build.mdx` for JVM vs native mode, Makefile targets, and Docker verification. +- Read the [AGENTS.md spec](https://github.com/quarkloop/guidelines/blob/main/agents/SPEC.md) for org-wide conventions. +- Search existing issues and PRs before asking. +- If unsure about a service boundary change, open an issue and ask before implementing. diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..49849e1 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,42 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [Unreleased] + +### Added + +- Apache License 2.0 — the project is now formally licensed under Apache 2.0 (see [LICENSE](./LICENSE)). +- [CONTRIBUTING.md](./CONTRIBUTING.md) — development setup, PR workflow, code style rules per language, and test expectations. +- [SECURITY.md](./SECURITY.md) — vulnerability reporting process, disclosure timeline, scope, and production hardening recommendations. +- [CODE_OF_CONDUCT.md](./CODE_OF_CONDUCT.md) — Contributor Covenant v2.0. + +### Changed + +- Top-level directory layout renamed for clarity. The four component directories are now prefixed with `quark-`: + - `cli/` → `quark-cli/` + - `runtime/` → `quark-runtime/` + - `nodes/` → `quark-nodes/` + - `server/` → `quark-server/` + - `quark-catalog/` was already prefixed and is unchanged. +- All path references in `Makefile`, `pom.xml`, `Dockerfile`, `scripts/*.sh`, `README.md`, and `AGENTS.md` updated to use the new directory names. +- `Dockerfile` rewritten to match the current v6 architecture. The previous version referenced module paths (`core/`, `server/quark-app/`, `runtime/providers/`) that haven't existed since the v6 refactor — it would have failed at `COPY` time. + +## [0.1.0] — Pre-release + +The platform is in pre-release development. The first tagged release will be `0.1.0` once the E2E example (`make run-example`) is verified to pass on a clean container build via `make docker-verify`. + +### Architecture summary + +The platform is a three-service architecture for executing programmable nodes: + +- **Control plane** (`quark-server/`) — Go + Fiber. REST API, deploy orchestration, process management. Single binary, ~13 MB, <50 ms startup. No TypeScript parsing. +- **Catalog service** (`quark-catalog/`) — Go + SQLite. Stores systems, nodes, events, source files, and node packages. Pure Go (no CGO), 15 MB binary. +- **Data plane** (`quark-runtime/`) — Java + GraalJS/Truffle. Executes node systems. Parses `.quark.ts` source via GraalJS ESM evaluation. Native binary: 194 MB, 38 ms startup, includes GraalJS via `--macro:truffle-svm`. +- **CLI** (`quark-cli/`) — Go + Cobra. Operator tool (`quarkctl`) for deploy, query, watch, and node management. +- **Standard node library** (`quark-nodes/`) — Reference node implementations across 10 domains (time, system, io, stream, log, codec, data, route, net, ...). + +All services communicate via an external NATS broker. Multi-tenancy is enforced by NATS subject encoding — two tenants can deploy same-named systems simultaneously with zero data leakage. diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..bb85ff7 --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,128 @@ +# Contributor Covenant Code of Conduct + +## Our pledge + +We as members, contributors, and leaders pledge to make participation in our +community a harassment-free experience for everyone, regardless of age, body +size, visible or invisible disability, ethnicity, sex characteristics, gender +identity and expression, level of experience, education, socio-economic status, +nationality, personal appearance, race, religion, or sexual identity +and orientation. + +We pledge to act and interact in ways that contribute to an open, welcoming, +diverse, inclusive, and healthy community. + +## Our standards + +Examples of behavior that contributes to a positive environment for our +community include: + +* Demonstrating empathy and kindness toward other people +* Being respectful of differing opinions, viewpoints, and experiences +* Giving and gracefully accepting constructive feedback +* Accepting responsibility and apologizing to those affected by our mistakes, + and learning from the experience +* Focusing on what is best not just for us as individuals, but for the + overall community + +Examples of unacceptable behavior include: + +* The use of sexualized language or imagery, and sexual attention or + advances of any kind +* Trolling, insulting or derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or email + address, without their explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Enforcement responsibilities + +Community leaders are responsible for clarifying and enforcing our standards of +acceptable behavior and will take appropriate and fair corrective action in +response to any behavior that they deem inappropriate, threatening, offensive, +or harmful. + +Community leaders have the right and responsibility to remove, edit, or reject +comments, commits, code, wiki edits, issues, and other contributions that are +not aligned to this Code of Conduct, and will communicate reasons for moderation +decisions when appropriate. + +## Scope + +This Code of Conduct applies within all community spaces, and also applies when +an individual is officially representing the community in public spaces. +Examples of representing our community include using an official e-mail address, +posting via an official social media account, or acting as an appointed +representative at an online or offline event. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported to the community leaders responsible for enforcement at +**reza.ebrahimi.dev@gmail.com**. +All complaints will be reviewed and investigated promptly and fairly. + +All community leaders are obligated to respect the privacy and security of the +reporter of any incident. + +## Enforcement guidelines + +Community leaders will follow these Community Impact Guidelines in determining +the consequences for any action they deem in violation of this Code of Conduct: + +### 1. Correction + +**Community impact**: Use of inappropriate language or other behavior deemed +unprofessional or unwelcome in the community. + +**Consequence**: A private, written warning from community leaders, providing +clarity around the nature of the violation and an explanation of why the +behavior was inappropriate. A public apology may be requested. + +### 2. Warning + +**Community impact**: A violation through a single incident or series +of actions. + +**Consequence**: A warning with consequences for continued behavior. No +interaction with the people involved, including unsolicited interaction with +those enforcing the Code of Conduct, for a specified period of time. This +includes avoiding interactions in community spaces as well as external channels +like social media. Violating these terms may lead to a temporary or +permanent ban. + +### 3. Temporary ban + +**Community impact**: A serious violation of community standards, including +sustained inappropriate behavior. + +**Consequence**: A temporary ban from any sort of interaction or public +communication with the community for a specified period of time. No public or +private interaction with the people involved, including unsolicited interaction +with those enforcing the Code of Conduct, is allowed during this period. +Violating these terms may lead to a permanent ban. + +### 4. Permanent ban + +**Community impact**: Demonstrating a pattern of violation of community +standards, including sustained inappropriate behavior, harassment of an +individual, or aggression toward or disparagement of classes of individuals. + +**Consequence**: A permanent ban from any sort of public interaction within +the community. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], +version 2.0, available at +https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. + +[homepage]: https://www.contributor-covenant.org + +Community Impact Guidelines were inspired by [Mozilla's code of conduct +enforcement ladder](https://github.com/mozilla/diversity). + +For answers to common questions about this code of conduct, see the FAQ at +https://www.contributor-covenant.org/faq. Translations are available at +https://www.contributor-covenant.org/translations. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..464846b --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,141 @@ +# Contributing to Quark + +Thanks for your interest in contributing! This document describes how to set up a development environment and submit changes. + +## Development setup + +### Prerequisites + +The Quark platform has three components with different toolchain requirements: + +| Component | Language | Toolchain | +|---|---|---| +| Control plane (`quark-server/`) | Go | Go ≥ 1.24 | +| Catalog service (`quark-catalog/`) | Go | Go ≥ 1.24 | +| CLI (`quark-cli/`) | Go | Go ≥ 1.24 | +| Data plane (`quark-runtime/`) | Java | JDK 21 + Maven 3.9+ | +| Native data plane (optional) | Java/GraalVM | GraalVM 21+ with `native-image` | + +External runtime dependencies: + +- A running [NATS server](https://nats.io/download-nats-io/) for inter-service communication. + +### Install dependencies + +```bash +git clone https://github.com/quarkloop/quark.git +cd quark + +# Go dependencies are fetched automatically on first build. +# Java dependencies are fetched by Maven on first build. +``` + +### Build + +```bash +# Build everything (Go control plane + CLI + Catalog + Java runtime): +make build + +# Build only the Go components: +make go + +# Build only the Java runtime: +make runtime + +# Native-image build of the runtime (requires GraalVM): +make native +``` + +### Test + +```bash +# Go unit tests for all three Go modules: +make test-go + +# Java unit tests: +make test-java + +# Full E2E example (starts NATS, builds everything, runs simple-streaming): +make run-example +``` + +### Verify in a clean container + +```bash +make docker-verify +``` + +This builds the entire project inside Docker containers with no host toolchain — useful for confirming the build does not depend on any local installations. + +## Submitting changes + +### Pull requests + +1. Fork the repository and create a feature branch from `main`: + ```bash + git checkout -b feat/my-feature + ``` +2. Make your changes. Keep commits focused — one logical change per commit. +3. Ensure `make test-go` and `make test-java` both pass. +4. Write a clear PR description explaining what changed and why. +5. Reference any related issues (e.g. `Closes #42`). + +### Commit message conventions + +We follow [Conventional Commits](https://www.conventionalcommits.org/): + +| Prefix | Use | +|---|---| +| `feat:` | New user-facing feature | +| `fix:` | Bug fix | +| `docs:` | Documentation only | +| `chore:` | Tooling, dependencies, configs | +| `refactor:` | Code restructuring with no behavior change | +| `test:` | Test additions or fixes | +| `perf:` | Performance improvement | +| `build:` | Build system, Makefile, Dockerfile, POM changes | + +Scope suffixes are encouraged where helpful, e.g. `feat(server):`, `fix(runtime):`, `docs(agents):`. + +### Code style + +**Go:** +- Run `gofmt -s` and `go vet` before committing. `make test-go` runs both. +- Follow [Effective Go](https://go.dev/doc/effective_go) and the [Go Code Review Comments](https://github.com/golang/go/wiki/CodeReviewComments). +- One responsibility per package. The current packages (`config`, `domain`, `nats`, `store`, `dataplane`, `deploy`, `event`, `metrics`, `query`, `health`, `http`) each have a single concern — don't conflate them. + +**Java:** +- Follow the existing package layout under `com.quarkloop.quark.runtime.*`. +- Quarkus best practices apply (use CDI, avoid static state, prefer constructor injection). +- Run `mvn -B clean install` before committing to ensure the build passes. + +**General:** +- Comments explain **why**, not **what**. The code already says what; comments should explain the reasoning behind non-obvious decisions. +- Don't add new top-level directories without discussing in an issue first. + +### Tests + +- Go: every package should have `_test.go` files alongside the source. Integration tests use `// +build integration` and require a running NATS. +- Java: JUnit 5 tests live in `src/test/java/...`. Native-image compatibility tests live in `src/test/java/.../nativeimage/`. + +If you add a feature, add tests. Bug-fix PRs should include a regression test. + +## Reporting bugs + +File issues at [github.com/quarkloop/quark/issues](https://github.com/quarkloop/quark/issues). Include: + +- Component (`quark-server`, `quark-runtime`, `quark-cli`, `quark-catalog`, or `quark-nodes`) +- Version (run `quarkctl version` or check the POM version) +- Go / Java version +- NATS server version +- Minimal reproducer +- Expected vs actual behavior +- Relevant logs (Go components use zap structured logging; Java components use Quarkus logging) + +## Reporting security vulnerabilities + +See [SECURITY.md](./SECURITY.md). **Do not file public issues for security vulnerabilities.** + +## Code of conduct + +By participating in this project you agree to abide by the [Code of Conduct](./CODE_OF_CONDUCT.md). diff --git a/Dockerfile b/Dockerfile index 3683b66..2e9417f 100755 --- a/Dockerfile +++ b/Dockerfile @@ -1,82 +1,72 @@ # ============================================================================= -# Dockerfile — Quark Platform build verification +# Dockerfile — Quark build verification image # ============================================================================= -# Builds the entire project (Java + Go) in a clean container with no host -# dependencies. Used by `make docker-verify` to confirm the build doesn't -# depend on any local tool installations or paths. +# Multi-stage build that compiles every component of the platform in a clean +# container with no host toolchain dependencies. Used by `make docker-verify` +# to confirm the build does not depend on any local installations. # -# This is a BUILD verification image, not a runtime image. The runtime image -# would be smaller (only JRE + the quarkus-app). +# This is a BUILD verification image, not a production runtime image. A +# production image would split components into separate images and ship +# only the JRE / Go binary each one needs. # ============================================================================= -# ----- Stage 1: Build Java modules ----- +# ----- Stage 1: Build Java runtime modules ----- +# The data plane is a Quarkus application built with Maven. GraalVM is +# required for native-image builds; for JVM-mode verification a plain JDK 21 +# is enough. FROM maven:3.9-eclipse-temurin-21 AS java-builder WORKDIR /build -# Copy parent POM and Maven wrapper first (cached layer for deps) -COPY pom.xml . -COPY mvnw mvnw.cmd .mvn ./ - -# Copy all module POMs (the reactor layout is: core/, server/, runtime/) -COPY core/quark-domain/pom.xml core/quark-domain/ -COPY core/quark-event/pom.xml core/quark-event/ -COPY core/quark-registry/pom.xml core/quark-registry/ -COPY core/quark-script/pom.xml core/quark-script/ -COPY core/quark-engine/pom.xml core/quark-engine/ -COPY server/quark-app/pom.xml server/quark-app/ -COPY server/quark-api/pom.xml server/quark-api/ -COPY server/quark-observability/pom.xml server/quark-observability/ -COPY server/quark-server/pom.xml server/quark-server/ -COPY runtime/quark-script/pom.xml runtime/quark-script/ -COPY runtime/quark-polyglot/pom.xml runtime/quark-polyglot/ -COPY runtime/quark-app/pom.xml runtime/quark-app/ -COPY runtime/quark-runtime/pom.xml runtime/quark-runtime/ -COPY runtime/providers/pom.xml runtime/providers/ -COPY runtime/providers/provider-timer/pom.xml runtime/providers/provider-timer/ -COPY runtime/providers/provider-cpu-profiler/pom.xml runtime/providers/provider-cpu-profiler/ -COPY runtime/providers/provider-memory-profiler/pom.xml runtime/providers/provider-memory-profiler/ -COPY runtime/providers/provider-json-writer/pom.xml runtime/providers/provider-json-writer/ -COPY runtime/providers/provider-streaming-endpoint/pom.xml runtime/providers/provider-streaming-endpoint/ - -# Pre-fetch dependencies (cached layer). Tolerate failures here because -# the parent POM references some artifacts that aren't needed in this build. +# Copy the parent POM and Maven wrapper first so dependency resolution +# is cached across source-only changes. +COPY pom.xml mvnw mvnw.cmd .mvn ./ + +# Copy every Maven module POM. The reactor layout is: +# quark-runtime/quark-core +# quark-runtime/quark-script +# quark-runtime/quark-polyglot +# quark-runtime/quark-app +# quark-runtime/quark-runtime +COPY quark-runtime/quark-core/pom.xml quark-runtime/quark-core/ +COPY quark-runtime/quark-script/pom.xml quark-runtime/quark-script/ +COPY quark-runtime/quark-polyglot/pom.xml quark-runtime/quark-polyglot/ +COPY quark-runtime/quark-app/pom.xml quark-runtime/quark-app/ +COPY quark-runtime/quark-runtime/pom.xml quark-runtime/quark-runtime/ + +# Pre-fetch dependencies. Tolerate failures because the parent POM +# references some artifacts (GraalVM polyglot) that may not resolve +# cleanly in every environment. RUN mvn -B dependency:go-offline -DskipTests || true # Copy sources and build -COPY core/quark-domain/src core/quark-domain/src -COPY core/quark-event/src core/quark-event/src -COPY core/quark-registry/src core/quark-registry/src -COPY core/quark-script/src core/quark-script/src -COPY core/quark-engine/src core/quark-engine/src -COPY server/quark-app/src server/quark-app/src -COPY server/quark-api/src server/quark-api/src -COPY server/quark-observability/src server/quark-observability/src -COPY server/quark-server/src server/quark-server/src -COPY runtime/quark-script/src runtime/quark-script/src -COPY runtime/quark-polyglot/src runtime/quark-polyglot/src -COPY runtime/quark-app/src runtime/quark-app/src -COPY runtime/quark-runtime/src runtime/quark-runtime/src -COPY runtime/providers/provider-timer/src runtime/providers/provider-timer/src -COPY runtime/providers/provider-cpu-profiler/src runtime/providers/provider-cpu-profiler/src -COPY runtime/providers/provider-memory-profiler/src runtime/providers/provider-memory-profiler/src -COPY runtime/providers/provider-json-writer/src runtime/providers/provider-json-writer/src -COPY runtime/providers/provider-streaming-endpoint/src runtime/providers/provider-streaming-endpoint/src +COPY quark-runtime/quark-core/src quark-runtime/quark-core/src +COPY quark-runtime/quark-script/src quark-runtime/quark-script/src +COPY quark-runtime/quark-polyglot/src quark-runtime/quark-polyglot/src +COPY quark-runtime/quark-app/src quark-runtime/quark-app/src +COPY quark-runtime/quark-runtime/src quark-runtime/quark-runtime/src RUN mvn -B clean install -DskipTests -# ----- Stage 2: Build Go CLI + Catalog ----- +# ----- Stage 2: Build Go control plane, CLI, and catalog ----- FROM golang:1.24 AS go-builder -# Build CLI -WORKDIR /build/cli -COPY cli/go.mod cli/go.sum ./ +# Build the control plane (quark-server) +WORKDIR /build/quark-server +COPY quark-server/go.mod quark-server/go.sum ./ RUN go mod download -COPY cli/ . +COPY quark-server/ . +RUN go vet ./... && go test ./... && go build -trimpath -buildvcs=false -o /quark-server ./cmd/server + +# Build the CLI (quarkctl) +WORKDIR /build/quark-cli +COPY quark-cli/go.mod quark-cli/go.sum ./ +RUN go mod download +COPY quark-cli/ . RUN go vet ./... && go test ./... && go build -trimpath -buildvcs=false -o /quarkctl . -# Build Catalog -WORKDIR /build/catalog +# Build the Catalog service +WORKDIR /build/quark-catalog COPY quark-catalog/go.mod quark-catalog/go.sum ./ RUN go mod download COPY quark-catalog/ . @@ -85,24 +75,24 @@ RUN go vet ./... && go test ./... && go build -trimpath -buildvcs=false -o /quar # ----- Stage 3: Runtime image (JVM mode) ----- FROM eclipse-temurin:21-jre AS runtime-jvm -# Copy control plane (server) jar + lib dir -COPY --from=java-builder /build/server/quark-server/target/quark-server-0.1.0-SNAPSHOT-runner.jar /app/quark-server.jar -COPY --from=java-builder /build/server/quark-server/target/lib /app/lib/server -# Copy data plane (runtime) jar + lib dir -COPY --from=java-builder /build/runtime/quark-runtime/target/quark-runtime-runner-runner.jar /app/quark-runtime.jar -COPY --from=java-builder /build/runtime/quark-runtime/target/lib /app/lib/runtime -# Copy Go binaries -COPY --from=go-builder /quarkctl /app/quarkctl +# Copy the data-plane (Java runtime) jar + lib dir +COPY --from=java-builder /build/quark-runtime/quark-runtime/target/quark-runtime-runner-runner.jar /app/quark-runtime.jar +COPY --from=java-builder /build/quark-runtime/quark-runtime/target/lib /app/lib/runtime + +# Copy Go binaries (control plane, CLI, catalog) +COPY --from=go-builder /quark-server /app/quark-server +COPY --from=go-builder /quarkctl /app/quarkctl COPY --from=go-builder /quark-catalog /app/quark-catalog -# Smoke tests +# Smoke tests — confirm every binary is at least runnable. RUN /app/quarkctl --help | head -1 RUN /app/quark-catalog -h 2>&1 | head -1 || true +RUN /app/quark-server -h 2>&1 | head -1 || true RUN java -version 2>&1 | head -1 WORKDIR /app EXPOSE 8080 8081 4222 -# Default: run the JVM server. For full platform bring-up, also start -# nats-server, the Catalog, and (if needed) the data-plane jar. -CMD ["java", "-jar", "quark-server.jar"] +# Default: run the Go control plane. The data plane is spawned as a +# child process by the control plane's ProcessManager. +CMD ["/app/quark-server"] diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..5864f80 --- /dev/null +++ b/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for describing the origin of the Work and + reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2026 Quarkloop Contributors + + 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. diff --git a/Makefile b/Makefile index 8061871..a17e02a 100755 --- a/Makefile +++ b/Makefile @@ -2,10 +2,10 @@ # Quark Platform — Makefile # ============================================================================= # Build system for the v6 Quark Platform: -# server/ — control plane (Go + Fiber, single binary) -# runtime/ — data plane (Java/GraalJS, with GraalJS/Truffle) +# quark-server/ — control plane (Go + Fiber, single binary) +# quark-runtime/ — data plane (Java/GraalJS, with GraalJS/Truffle) # quark-catalog/ — Catalog service (Go + SQLite) -# cli/ — quarkctl (Go + Cobra) +# quark-cli/ — quarkctl (Go + Cobra) # # The control plane is now a single Go binary. It spawns the data plane # (Java runtime with GraalJS) as a child process via ProcessManager. @@ -39,9 +39,9 @@ endif GOFLAGS ?= -trimpath -buildvcs=false # ----- Project paths ----- -CLI_DIR := cli +CLI_DIR := quark-cli CLI_BIN := $(CLI_DIR)/quarkctl -SERVER_DIR := server +SERVER_DIR := quark-server SERVER_BIN := $(SERVER_DIR)/quark-server CATALOG_DIR := quark-catalog CATALOG_BIN := $(CATALOG_DIR)/quark-catalog @@ -59,8 +59,8 @@ SERVER_GO_MAIN := $(SERVER_DIR)/cmd/server # -runner.jar = quark-runtime-runner-runner.jar. # The thin jar .jar = quark-runtime-runner.jar has no main manifest # and is NOT runnable — do not use it for `java -jar`. -RUNTIME_JAR := runtime/quark-runtime/target/quark-runtime-runner-runner.jar -RUNTIME_NATIVE := runtime/quark-runtime/target/quark-runtime-runner-runner +RUNTIME_JAR := quark-runtime/quark-runtime/target/quark-runtime-runner-runner.jar +RUNTIME_NATIVE := quark-runtime/quark-runtime/target/quark-runtime-runner-runner # Default run mode = JVM (use RUN_MODE=native for native runtime) # Note: RUN_MODE only affects the runtime now — the server is always Go. @@ -185,7 +185,7 @@ build-native: build-native-runtime ## Build the native runtime binary (server is build-native-runtime: ## Build the data plane native binary with GraalJS (~9 min, 6.5 GB RAM, 194 MB output) @printf "$(C_BLUE)[native] > Building data plane (runtime) native image with GraalJS/Truffle...$(C_RESET)\n" $(check_native_image) - @$(MAVEN) $(MAVEN_OPTS) -pl runtime/quark-runtime -am -Pnative install -DskipTests + @$(MAVEN) $(MAVEN_OPTS) -pl quark-runtime/quark-runtime -am -Pnative install -DskipTests @printf "$(C_GREEN)✓ Runtime native build complete$(C_RESET)\n" @ls -lh $(RUNTIME_NATIVE) @@ -302,7 +302,7 @@ docker-build-java: ## Build Java project in a clean Docker container docker-build-native: ## Build the native runtime in Docker (Mandrel builder image) @printf "$(C_BLUE)> Building native runtime in Docker (quay.io/quarkus/ubi-quarkus-mandrel-builder-image)...$(C_RESET)\n" docker run --rm -v "$$PWD":/app -w /app maven:3.9-eclipse-temurin-21 \ - mvn -B -pl runtime/quark-runtime -am -Pnative clean install -DskipTests + mvn -B -pl quark-runtime/quark-runtime -am -Pnative clean install -DskipTests @printf "$(C_GREEN)✓ Native Docker build complete (runtime binary)$(C_RESET)\n" docker-build-go: ## Build Go CLI in a clean Docker container diff --git a/README.md b/README.md index d549d33..ad110dc 100755 --- a/README.md +++ b/README.md @@ -1,341 +1,81 @@ -# Quark Platform +# Quark -A universal runtime for programmable nodes, built on a **three-service architecture**: a Go control plane (Fiber + nats.go), a Go + SQLite Catalog service, and a Java/Native data plane (with GraalJS for TypeScript execution). All services communicate via an external NATS broker. +A universal runtime for programmable nodes, built on a three-service architecture: a Go control plane, a Go + SQLite Catalog service, and a Java/Native data plane with GraalJS for TypeScript execution. All services communicate via an external NATS broker. -Everything in Quark — timers, profilers, parsers, writers, endpoints, policies — is a **Node** identified by a Docker-style URI (`///:`). Users declare nodes and their communication patterns in `.quark.ts` files. The control plane persists these declarations verbatim and forwards them to the data plane, where GraalJS's native ESM module support evaluates TypeScript node logic over NATS. +## Overview -**Multi-tenant by construction**: NATS subjects encode the namespace. Two tenants can deploy same-named systems simultaneously with zero data leakage. - ---- - -## Architecture - -### Three-Service Architecture - -``` -┌──────────────────┐ NATS ┌──────────────────┐ NATS ┌────────────────┐ -│ Control Plane │◄─────────►│ Catalog Service │◄─────────►│ Data Plane(s) │ -│ (Go + Fiber) │ │ (Go + SQLite) │ │ (Java/Native) │ -│ │ │ │ │ │ -│ - REST API │ catalog.* │ - System Store │ quark. │ - Node Exec │ -│ - ProcessMgr │ subjects │ - Node Store │ control.* │ - GraalJS │ -│ - Deploy Orch │ │ - Event Store │ quark.data.*│ - Event Fwd │ -│ - Query→NATS │ │ - Node Registry │ │ - Metrics Fwd │ -│ - No TS parsing │ │ - QNP Storage │ │ - TS Parser │ -└──────────────────┘ └──────────────────┘ └────────────────┘ - ▲ ▲ - │ │ - └────────────────── NATS broker (external, nats://localhost:4222) ──────────────┘ -``` - -- **Control Plane** (`server/`): REST API, process management, deploy orchestration. Written in Go (Fiber + nats.go + zap). Does NOT parse TypeScript — treats `.quark.ts` as opaque, forwards it verbatim to the data plane via NATS. A minimal regex "sniffer" extracts just the system name + runtime mode for routing. Spawns data-plane processes on demand. -- **Catalog Service** (`quark-catalog/`): Standalone Go process with SQLite storage. Pure Go (`modernc.org/sqlite`, no CGO), no JNI, no GraalVM issues. Stores systems, nodes, events, source, and node packages (`.ts`/`.so` files). Performs JSONL migration on first startup. -- **Data Plane** (`runtime/`): Executes node systems. Spawned by the control plane. Includes GraalJS/Truffle for TypeScript node execution. Parses the `.quark.ts` source via `GraalJsSystemParser` (full ESM evaluation) + `SimpleSystemParser` (structural extraction). Forwards events and metrics back via NATS. - -### Native Binary Characteristics - -| Binary | Size | Build time | Peak RAM | Startup | Includes GraalJS | -|--------|------|------------|----------|---------|------------------| -| Control plane (`server/quark-server`) | ~13 MB | <5s | <100 MB | <50 ms | ❌ No (Go binary) | -| Data plane (`runtime/quark-runtime-runner-runner`) | 194 MB | ~9 min | 6.5 GB | 38 ms | ✅ Yes (`--macro:truffle-svm`) | -| Catalog (`quark-catalog`) | 15 MB | <5s | <50 MB | <100 ms | n/a (Go) | - -### IPC Protocol (NATS) - -All control-plane ↔ data-plane communication flows through NATS: - -| Direction | Subject | Purpose | -|---|---|---| -| Control → Data | `quark.control..deploy` | Deploy command (carries .quark.ts source) | -| Control → Data | `quark.control..undeploy` | Undeploy command | -| Data → Control | `quark.data..status` | Deploy/undeploy result (includes node info) | -| Data → Control | `quark.data.event.>` | Forwarded lifecycle events (NODE_CREATED, etc.) — wildcard sub | -| Data → Control | `quark.data.heartbeat.>` | Per-namespace metrics (CPU%, throughput, errors) — wildcard sub | - -- `runtimeId` = `"shared"` for non-isolated namespaces, `"ns-"` for isolated -- Serialization: JSON via Jackson -- Deploy/undeploy use NATS request-reply (synchronous, 3s timeout, 5 retries) -- Events/metrics use NATS pub/sub (asynchronous, fire-and-forget) -- Note: NATS **Core** is used (not JetStream) — no message persistence, no automatic retries, no fallback routing. The `onFailure` field is parsed but not enforced at runtime. - -### Process Types - -| # | Process | How spawned | Port | Purpose | -|---|---|---|---|---| -| 1 | **Control plane** | Operator (`make run-server`) | 8080 | REST API, ProcessManager, event/metrics receivers | -| 2 | **Shared data plane** | Auto-spawned by ProcessManager | 9100+ | Executes all non-isolated namespace systems | -| 3 | **Isolated data plane** | Auto-spawned when `.quark.ts` has `runtime: "isolated"` | 9101+ | Dedicated process per isolated namespace | -| 4 | **NATS server** | Operator (`nats-server`) | 4222 | Message bus for all IPC | -| 5 | **Catalog service** | Operator (`./quark-catalog/quark-catalog`) | — | Metadata store (SQLite) + node package registry | -| 6 | **Go CLI** (`quarkctl`) | Operator (`./cli/quarkctl`) | — | Talks to control plane REST API | - -### Runtime Isolation - -The `runtime` field in `.quark.ts` controls process isolation: - -```typescript -export default { - name: "monitor", - namespace: "alice", - runtime: "isolated", // or "shared" (default) - nodes: { ... } -}; -``` - -- **`shared`** (default): The system runs in the shared data-plane process alongside other non-isolated namespaces. -- **`isolated`**: The system runs in a dedicated data-plane process (`runtimeId=ns-`). The process is stopped when the namespace's last system is undeployed. - ---- - -## Build Modes - -The platform supports **two run modes**: JVM (default) and Native Image. The `RUN_MODE` env var selects which binary `make run-*` targets use. - -### JVM Mode (default) - -Standard `java -jar` execution. Full GraalJS support for `.quark.ts` evaluation. - -```bash -make build # Build JVM jars + Go CLI + Catalog -make run-example # Run example with JVM server -make run-server # Start server (Ctrl+C to stop) -``` - -### Native Image Mode - -GraalVM native executable. Starts in milliseconds, uses less memory. **Two separate native binaries** — one for the control plane (no GraalJS, 76 MB), one for the data plane (with GraalJS, 194 MB). - -```bash -make build-native # Build BOTH native binaries + Go CLI -make build-native-server # Control plane only (~4 min, 3 GB RAM, 76 MB) -make build-native-runtime # Data plane with GraalJS (~9 min, 6.5 GB RAM, 194 MB) -RUN_MODE=native make run-example # Run example with native binaries -``` - -**Prerequisites for native mode:** -- Oracle GraalVM 21+ with `native-image` on `$PATH` -- Set `JAVA_HOME` to the GraalVM installation -- Mandrel is sufficient for the **control plane** (no GraalJS), but the **data plane** requires Oracle GraalVM (Truffle support) - -**Native mode notes:** -- **GraalJS in data plane only**: The data plane native binary includes GraalJS via `--macro:truffle-svm`. The control plane native binary excludes GraalJS entirely (uses `SimpleSystemParser`). This is what keeps the server image small. GraalJS evaluates `.ts` files via native ESM module support (`js.esm-eval-returns-exports=true`) — the platform's `.ts` files are valid ECMAScript modules with no actual TypeScript type annotations. -- **Virtual threads**: Truffle JIT compilation doesn't support virtual threads. Providers automatically use platform threads in native mode (detected via `quark.native` system property). -- **Catalog persistence**: Works in both JVM and native modes (Go + SQLite, no JNI). - -### Run Mode Selection - -The `RUN_MODE=jvm|native` env var (replaces the old `BUILD_MODE`) controls which binary `make run-*` targets use: +Everything in Quark — timers, profilers, parsers, writers, endpoints, policies — is a **Node** identified by a Docker-style URI (`///:`). Users declare nodes and their communication patterns in `.quark.ts` files. The control plane persists these declarations verbatim and forwards them to the data plane, where GraalJS's native ESM module support evaluates TypeScript node logic. -```bash -make run-example # JVM mode (default) -make run-example RUN_MODE=native # Native mode -``` - -The `ProcessManager` automatically detects which binary is available (native or JAR) and spawns data-plane processes accordingly. Native binaries are preferred when both exist. - ---- - -## Repository Layout - -``` -quark-platform/ -├── AGENTS.md ← Guide for AI agents (READ FIRST if you're an AI) -├── README.md ← This file -├── Makefile ← All build/test/run commands (run `make help`) -├── pom.xml ← Parent POM (runtime/* modules only — server/ is Go) -├── mvnw / mvnw.cmd / .mvn/ ← Maven wrapper (DO NOT delete .mvn/wrapper/) -├── Dockerfile ← Clean-container build verification -├── docs/ ← Specification documents -│ -├── server/ ← CONTROL PLANE — Go + Fiber (single binary) -│ ├── go.mod / go.sum ← module github.com/quarkloop/quark/server -│ ├── cmd/server/main.go ← entry point: env config + graceful shutdown -│ └── internal/ -│ ├── config/ ← env-var config (QUARK_HTTP_PORT, etc.) -│ ├── domain/ ← Go structs mirroring Java records -│ ├── nats/ ← NATS connection wrapper -│ ├── store/ ← repository interfaces + NatsCatalogClient -│ ├── dataplane/ ← ProcessManager + DataPlaneProcess + ipc -│ ├── deploy/ ← DeployService (persist + forward; NO TS parsing) -│ ├── event/ ← event receiver (quark.data.event.> sub) -│ ├── metrics/ ← heartbeat collector + rate computer -│ ├── query/ ← read-side services (System/Node/.../Event/Source) -│ ├── health/ ← /health/live + /health/ready -│ └── http/ ← Fiber app + handlers + middleware + DTOs -│ -├── runtime/ ← DATA PLANE — Java + GraalJS/Truffle -│ ├── quark-core/ ← Consolidated module: domain records, engine -│ │ ← (NATS, lifecycle, dataplane, metrics, polyglot -│ │ ← lookup, store SPIs), event bus, registry SPI, -│ │ ← and SimpleSystemParser (moved from core/) -│ ├── quark-script/ ← GraalJsSystemParser (GraalJS ESM-based parser) -│ ├── quark-polyglot/ ← TypeScriptNodeFactory + PolyglotNodeRegistry (catalog pull) + JsConsole/JsConfig/JsMessage/JsPublisher bridges -│ ├── quark-app/ ← RuntimeDeployService, DataPlaneCommandHandler -│ └── quark-runtime/ ← Quarkus runner (QuarkRuntime.java, --macro:truffle-svm) -│ (NO providers/ subdir — runtime pulls every node from the Catalog at deploy time) -│ -├── nodes/ ← STANDARD LIBRARY (canonical node source — see nodes/README.md) -│ └── quark/ ← 10 nodes: 5 Java (timer, cpu, memory, writer, stream) + 5 TypeScript (stdout, json-parse, map, conditional, fetch) -│ Each node dir: manifest.json + src/node.{java,ts} + build.toml + README.md -│ Build + push: quarkctl node build → quarkctl node push -│ -├── quark-catalog/ ← CATALOG service (Go + SQLite) -│ ├── cmd/quark-catalog/main.go ← Entry point -│ └── internal/ -│ ├── config/ ← Config from flags -│ ├── natsx/ ← NATS connection -│ ├── api/ ← JSON request/response types -│ ├── store/ ← SQLite persistence (pure Go) -│ └── server/ ← NATS handlers -│ -├── example/ ← Runnable examples -│ ├── simple-streaming/ ← Multi-tenant streaming monitor -│ ├── json-pipeline/ ← Timer → JSON parse → map → stdout -│ └── conditional-routing/ ← Conditional router with two stdout destinations -│ -└── cli/ ← Go-based CLI (quarkctl, with --json flag) -``` - ---- - -## Node Lifecycle: Build → Push → Pull → Run - -The platform uses a **docker-image model** for nodes. The runtime -binary NEVER contains node implementations — every node is fetched -from the Catalog on first use and cached for the rest of the process -lifetime. - -``` -nodes/ ──build──▶ .jar/.ts ──push──▶ Catalog ──pull──▶ Runtime ──run──▶ execute -(source) (artifact) (registry) (exec) -``` - -| Phase | Command | What happens | -|-------|---------|--------------| -| **Build** | `quarkctl node build ` | Java: `javac` compiles `src/*.java` → `target/.jar` (classpath resolved from `manifest.json`'s `dependencies.java`). TypeScript: no-op. | -| **Push** | `quarkctl node push ` | Packages `manifest.json` + build output into a zip, sends to Catalog via `registry.node.push` NATS subject. Catalog stores in `node_packages` SQLite table. | -| **Pull** | (automatic, on deploy) | When `quarkctl apply` triggers a deploy, the data plane's `PolyglotNodeRegistry` calls `registry.node.pull` for each node URI. Catalog returns the zip blob; runtime unzips + loads (Java: `URLClassLoader`; TypeScript: GraalJS ESM). | -| **Run** | (automatic) | Engine calls `factory.create(config)` → `provider.init(config)` → `provider.start()` or `provider.onMessage()`. On undeploy: `provider.close()`. | - -The data plane logs every pull at INFO level: -`Loaded node from catalog (type=, bytes)`. - -**Adding a node** requires only `quarkctl node build ` + `quarkctl -node push ` — no runtime rebuild. See `AGENTS.md` § "Node -Lifecycle" for the full flow diagram and implementation details. - ---- - -## Per-Namespace CPU Attribution - -For shared namespaces running in the same data-plane JVM, CPU time is attributed per-namespace by measuring `ThreadMXBean.getCurrentThreadCpuTime()` inside the message handler path. The data plane forwards metrics snapshots to the control plane every 2 seconds via NATS heartbeat. - -For isolated namespaces, all metrics are exact at the process level because the entire JVM serves a single namespace. - ---- +**Multi-tenant by construction**: NATS subjects encode the namespace. Two tenants can deploy same-named systems simultaneously with zero data leakage. -## How to Build & Run +## Features -### Prerequisites +- **Three-service architecture** — Go control plane (Fiber + nats.go), Go Catalog (SQLite, pure Go no CGO), Java data plane (Quarkus + GraalJS/Truffle) +- **Multi-tenant by construction** — NATS subjects encode the namespace; zero data leakage between tenants +- **TypeScript-native node execution** — GraalJS evaluates `.quark.ts` files via native ESM module support; no separate transpile step +- **Declarative system definitions** — users write `.quark.ts` files, the platform handles deploy/undeploy lifecycle +- **Node registry / docker-image model** — nodes are built, pushed to the Catalog, and pulled on demand by the runtime; no runtime rebuild to add a node +- **Runtime isolation modes** — `shared` (default, multi-tenant in one process) or `isolated` (dedicated process per namespace) +- **Native image support** — GraalVM native executables for both control plane (76 MB, no GraalJS) and data plane (194 MB, with GraalJS via `--macro:truffle-svm`) +- **REST API + CLI** — `quarkctl` for operators, REST for programmatic integrations +- **Per-namespace CPU attribution** — `ThreadMXBean.getCurrentThreadCpuTime()` measured per message handler for shared namespaces -- **Java 21+** (JDK — must include `javac`). For native mode: Oracle GraalVM 21+ -- **Go 1.24+** (for the CLI and Catalog) -- The repo includes `mvnw` — no need to pre-install Maven -- **NATS server** on `nats://localhost:4222` (external) +## Installation -### Build everything (JVM mode) +This is a multi-language platform — there is no single install command. Clone and build: ```bash +git clone https://github.com/quarkloop/quark.git +cd quark make build # builds Java modules + Go CLI + Catalog service ``` -### Build native executables - -```bash -make build-native # builds both native binaries + Go CLI + Catalog -make build-native-server # control plane only (~4 min) -make build-native-runtime # data plane with GraalJS (~9 min) -``` +See [Build & development](./docs/build.mdx) for full prerequisites (JDK 21+, Go 1.24+, NATS server, optional GraalVM for native mode) and per-component build instructions. -### Run the tests +## Quick start ```bash -make test # runs Java + Go tests (JVM mode) -``` +# 1. Start NATS (external): +nats-server & -### Run the example +# 2. Build everything (JVM mode): +make build -```bash -make run-example # JVM mode, 15-second run (default) -make run-example EXAMPLE_DURATION=30 # JVM mode, 30-second run -make run-example RUN_MODE=native # Native mode +# 3. Run the multi-tenant streaming example (15 seconds): +make run-example ``` -### Run the server +This deploys `example/simple-streaming/system.quark.ts` under namespace `alice`, observes the streaming output for 15 seconds, then undeploys and shuts down cleanly. -```bash -make run-server # JVM mode (port 8080) -make run-server-native # Native mode (port 8080) -make server-dev # Quarkus dev mode (hot reload) -``` +For native mode: `make build-native && make run-example RUN_MODE=native`. ---- +## Documentation -## CLI +- [Architecture](./docs/architecture.mdx) — three-service model, runtime isolation, process types, node lifecycle +- [API reference](./docs/api.mdx) — REST endpoints and CLI commands +- [Wire protocol](./docs/protocol.mdx) — NATS subjects for control-plane ↔ data-plane communication +- [Build & development](./docs/build.mdx) — prerequisites, JVM vs native mode, Makefile targets, Docker verification +- [Changelog](./CHANGELOG.md) — release history +- [Contributing](./CONTRIBUTING.md) — development setup, PR workflow, code style per language +- [AI agent guide](./AGENTS.md) — read this first if you're an AI working on the codebase -```bash -# Deploy a .quark.ts file -quarkctl apply -f monitor.quark.ts -n alice - -# List systems / nodes / namespaces -quarkctl get systems -n alice -quarkctl get nodes -n alice -s monitor -quarkctl get namespaces - -# Get system / node details -quarkctl get system monitor -n alice -quarkctl get node cpu -n alice -s monitor - -# Query events -quarkctl get events -n alice -quarkctl watch events -n alice - -# Delete a system -quarkctl delete system monitor -n alice +The documentation website (Next.js site in [`docs/`](./docs/)) hosts the human-facing tutorials and conceptual deep-dives. -# Node package registry -quarkctl node list -quarkctl node info quark/time/schedule/timer:v1 -quarkctl node search timer -quarkctl node push -f my-node.ts --uri acme/data/payments/risk-score:v1 -quarkctl node pull acme/data/payments/risk-score:v1 +## Compatibility -# Get JSON output (for AI agents) -quarkctl get system monitor -n alice --json -``` - ---- - -## REST API +| Component | Language | Version | +|---|---|---| +| Control plane (`quark-server/`) | Go | 1.24+ | +| Catalog service (`quark-catalog/`) | Go | 1.24+ | +| CLI (`quark-cli/`) | Go | 1.24+ | +| Data plane (`quark-runtime/`) | Java | JDK 21+ (GraalVM 21+ for native mode) | +| NATS server | — | 2.10+ | +| Build system | — | Maven 3.9+ (wrapper included) | -| Method | Path | Description | -|--------|------|-------------| -| GET | `/api/v1/namespaces` | List all active namespaces | -| GET | `/api/v1/namespaces/{ns}` | Get namespace details + metrics | -| GET | `/api/v1/namespaces/{ns}/systems` | List systems in a namespace | -| GET | `/api/v1/namespaces/{ns}/systems/{name}` | Get system details | -| PUT | `/api/v1/namespaces/{ns}/systems/{name}` | Apply (declarative reconcile) | -| DELETE | `/api/v1/namespaces/{ns}/systems/{name}` | Undeploy a system | -| GET | `/api/v1/namespaces/{ns}/systems/{name}/source` | Get the original .quark.ts source | -| GET | `/api/v1/namespaces/{ns}/systems/{name}/nodes` | List nodes in a system | -| GET | `/api/v1/namespaces/{ns}/systems/{name}/nodes/{node}` | Get node details | -| GET | `/api/v1/namespaces/{ns}/events` | Query events | -| GET | `/api/v1/registry` | List registered node implementations | -| GET | `/q/health/live` | Liveness check (SmallRye Health default path) | -| GET | `/q/health/ready` | Readiness check (NATS, Catalog, registry) | +## Contributing ---- +Pull requests are welcome. See [CONTRIBUTING.md](./CONTRIBUTING.md) for development setup, commit message conventions, and per-language code style rules. By participating you agree to abide by the [Code of Conduct](./CODE_OF_CONDUCT.md). -## AI agent guide +## License -If you're an AI agent working on this codebase, read [`AGENTS.md`](AGENTS.md) first. +This project is licensed under the Apache License, Version 2.0 — see the [LICENSE](./LICENSE) file for details. diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000..9c0cb5f --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,92 @@ +# Security Policy + +## Supported versions + +This project is in early development. Only the latest `main` branch receives security fixes. Once a 1.0 release is cut, we'll publish a support table here. + +| Version | Supported | +|---------|--------------------| +| main | :white_check_mark: | +| tagged releases | :white_check_mark: (latest two minor versions) | +| older | :x: | + +## Reporting a vulnerability + +**Do NOT file a public GitHub issue for security vulnerabilities.** + +Instead, email **reza.ebrahimi.dev@gmail.com** with: + +1. A description of the vulnerability and its impact. +2. Steps to reproduce (a minimal reproducer is ideal). +3. Affected component (`quark-server`, `quark-runtime`, `quark-cli`, `quark-catalog`, or `quark-nodes`) and version. +4. Any suggested fixes or mitigations. + +You should receive an acknowledgment within 48 hours. If you don't, please follow up to confirm we received the original report — email can get filtered. + +We will coordinate disclosure with you and credit your report in the release notes unless you prefer to remain anonymous. + +## Disclosure timeline + +1. **Day 0**: We receive the report and confirm receipt within 48 hours. +2. **Day 0–7**: We reproduce the issue and assess severity. +3. **Day 7–30**: A fix is developed on a private branch. +4. **Day 30**: The fix is released and the vulnerability is disclosed publicly with credit to the reporter (unless anonymity is requested). + +Critical vulnerabilities may be fixed and disclosed faster. Lower-severity issues may sit longer if a fix would require a breaking change. + +## Scope + +This security policy applies to all five components of the Quark platform: `quark-server`, `quark-runtime`, `quark-cli`, `quark-catalog`, and `quark-nodes`. + +### In scope + +- Authentication or authorization bypass in the control plane REST API. +- Authentication or authorization bypass in NATS subject routing (cross-tenant data leakage). +- Remote code execution via the data plane (e.g. via GraalJS sandbox escape, native node execution, or malicious `.quark.ts` source). +- Deserialization vulnerabilities in any component. +- Memory safety issues in the Java runtime or Go binaries. +- SQL injection in the Catalog's SQLite layer. +- Supply-chain risks (compromised dependencies, malicious publish artifacts). + +### Out of scope + +- Vulnerabilities in NATS server itself — report to [nats-io/nats-server](https://github.com/nats-io/nats-server). +- Vulnerabilities in GraalVM, Quarkus, or other upstream Java libraries — report upstream. +- Vulnerabilities in the Go standard library or Fiber / nats.go / zap — report upstream. +- Social engineering attacks against maintainers or users. +- Theoretical timing attacks without a demonstrated exploit. +- Denial of service via resource exhaustion on the NATS broker (mitigate at the broker level). + +## Hardening recommendations + +When deploying Quark in production: + +1. **Secure the NATS broker.** Use TLS for NATS connections (`tls://` or `wss://`). Configure NATS account credentials so each tenant has isolated subject namespaces. The platform's multi-tenant model relies on NATS subject isolation; do not run with anonymous NATS access in production. +2. **Restrict the control plane REST API.** Bind `quark-server` to a private network or put it behind an authenticated reverse proxy. The REST API has no built-in auth — it assumes a trusted network. +3. **Validate `.quark.ts` source before deploy.** The data plane evaluates TypeScript via GraalJS with ESM. While GraalJS is sandboxed by default, do not deploy untrusted `.quark.ts` files without code review. +4. **Restrict Catalog SQLite access.** The Catalog uses `modernc.org/sqlite` (pure Go, no CGO). The database file should be on a filesystem with appropriate permissions; the Catalog process should run as a non-root user. +5. **Pin container images by digest.** When deploying via Docker, pin to a specific image digest, not just a tag. +6. **Monitor NATS subjects.** Set up NATS streaming / JetStream observability so you can detect unusual subject activity (e.g. a tenant trying to subscribe to another tenant's subjects). + +## Trust boundaries + +``` + ┌─────────────────────────────────────────────┐ + │ Trusted zone (your deployment) │ + Operator ────┤ │ + │ quark-server (no built-in auth) │ + │ quark-runtime (GraalJS sandbox) │ + │ quark-catalog (SQLite, no remote access) │ + │ NATS broker (must be TLS + auth) │ + └─────────────────────────────────────────────┘ + │ + ▼ + ┌─────────────────────────────────────────────┐ + │ Untrusted zone │ + │ .quark.ts source files (review before │ + │ deploy — GraalJS sandbox contains them but │ + │ defense in depth is still recommended) │ + │ Untrusted tenant workloads (rely on NATS │ + │ subject isolation) │ + └─────────────────────────────────────────────┘ +``` diff --git a/docs/.gitignore b/docs/.gitignore deleted file mode 100644 index c8502b6..0000000 --- a/docs/.gitignore +++ /dev/null @@ -1,34 +0,0 @@ -# dependencies -/node_modules -/.pnp -.pnp.* - -# next.js -/.next/ -/out/ - -# production -/build - -# misc -.DS_Store -*.pem - -# debug -npm-debug.log* -yarn-debug.log* -yarn-error.log* -.pnpm-debug.log* - -# env files -.env* - -# typescript -*.tsbuildinfo -next-env.d.ts - -# pagefind (generated search index) -/public/pagefind - -# fumadocs generated source -/.source diff --git a/docs/content/docs/abstraction.mdx b/docs/abstraction.mdx similarity index 100% rename from docs/content/docs/abstraction.mdx rename to docs/abstraction.mdx diff --git a/docs/api.mdx b/docs/api.mdx new file mode 100644 index 0000000..f92baeb --- /dev/null +++ b/docs/api.mdx @@ -0,0 +1,154 @@ +--- +title: "API reference" +description: "" +--- + +# API reference + +Complete reference for the Quark platform's external APIs: the REST API exposed by the control plane, and the `quarkctl` CLI commands. + +For architecture, see [ARCHITECTURE.md](./ARCHITECTURE.md). For NATS wire protocol, see [PROTOCOL.md](./PROTOCOL.md). + +## Table of contents + +- [REST API](#rest-api) + - [Namespaces](#namespaces) + - [Systems](#systems) + - [Nodes](#nodes) + - [Events](#events) + - [Registry](#registry) + - [Health](#health) +- [CLI (`quarkctl`)](#cli-quarkctl) + - [Apply (declarative)](#apply-declarative) + - [Get (queries)](#get-queries) + - [Watch (streams)](#watch-streams) + - [Delete](#delete) + - [Node registry](#node-registry) + +--- + +## REST API + +The control plane (`quark-server`) exposes a REST API on port 8080 (override with `QUARK_HTTP_PORT`). All responses are JSON. + +### Namespaces + +| Method | Path | Description | +|--------|------|-------------| +| GET | `/api/v1/namespaces` | List all active namespaces | +| GET | `/api/v1/namespaces/{ns}` | Get namespace details + metrics | + +### Systems + +| Method | Path | Description | +|--------|------|-------------| +| GET | `/api/v1/namespaces/{ns}/systems` | List systems in a namespace | +| GET | `/api/v1/namespaces/{ns}/systems/{name}` | Get system details | +| PUT | `/api/v1/namespaces/{ns}/systems/{name}` | Apply (declarative reconcile) | +| DELETE | `/api/v1/namespaces/{ns}/systems/{name}` | Undeploy a system | +| GET | `/api/v1/namespaces/{ns}/systems/{name}/source` | Get the original `.quark.ts` source | + +### Nodes + +| Method | Path | Description | +|--------|------|-------------| +| GET | `/api/v1/namespaces/{ns}/systems/{name}/nodes` | List nodes in a system | +| GET | `/api/v1/namespaces/{ns}/systems/{name}/nodes/{node}` | Get node details | + +### Events + +| Method | Path | Description | +|--------|------|-------------| +| GET | `/api/v1/namespaces/{ns}/events` | Query events | + +### Registry + +| Method | Path | Description | +|--------|------|-------------| +| GET | `/api/v1/registry` | List registered node implementations | + +### Health + +| Method | Path | Description | +|--------|------|-------------| +| GET | `/q/health/live` | Liveness check (SmallRye Health default path) | +| GET | `/q/health/ready` | Readiness check (NATS, Catalog, registry) | + +--- + +## CLI (`quarkctl`) + +The `quarkctl` binary in `quark-cli/` is the operator's primary interface. It talks to the control plane's REST API over HTTP. + +### Apply (declarative) + +Deploy a `.quark.ts` file under a namespace: + +```bash +quarkctl apply -f monitor.quark.ts -n alice +``` + +This is the equivalent of `PUT /api/v1/namespaces/alice/systems/monitor` with the file contents as the body. The control plane persists the source, forwards it to the data plane via NATS, and the data plane evaluates it with GraalJS. + +### Get (queries) + +```bash +# List systems / nodes / namespaces +quarkctl get systems -n alice +quarkctl get nodes -n alice -s monitor +quarkctl get namespaces + +# Get system / node details +quarkctl get system monitor -n alice +quarkctl get node cpu -n alice -s monitor + +# Query events +quarkctl get events -n alice +``` + +### Watch (streams) + +```bash +# Stream events as they happen +quarkctl watch events -n alice +``` + +### Delete + +```bash +# Delete a system (undeploy) +quarkctl delete system monitor -n alice +``` + +### Node registry + +```bash +# List / search / inspect registered nodes +quarkctl node list +quarkctl node info quark/time/schedule/timer:v1 +quarkctl node search timer + +# Push a node package to the Catalog +quarkctl node push -f my-node.ts --uri acme/data/payments/risk-score:v1 + +# Pull a node package from the Catalog (debugging) +quarkctl node pull acme/data/payments/risk-score:v1 +``` + +### JSON output (for AI agents and scripting) + +Most `get` commands support a `--json` flag that emits structured JSON instead of human-readable tables: + +```bash +quarkctl get system monitor -n alice --json +quarkctl get nodes -n alice -s monitor --json +quarkctl get namespaces --json +``` + +This is the recommended mode for scripting, CI/CD, and AI-agent integrations. + +--- + +## SDK clients + +For programmatic access from TypeScript applications, use the [`@quarkloop/quark-js`](https://github.com/quarkloop/quark-js) SDK. It provides a typed client over the same REST API plus direct NATS access for execute / batch / pipeline operations. diff --git a/docs/app/docs/[...slug]/page.tsx b/docs/app/docs/[...slug]/page.tsx deleted file mode 100644 index 8d3d2de..0000000 --- a/docs/app/docs/[...slug]/page.tsx +++ /dev/null @@ -1,67 +0,0 @@ -import { source } from "@/lib/source"; -import { - DocsPage, - DocsBody, - DocsDescription, - DocsTitle, -} from "fumadocs-ui/page"; -import { notFound } from "next/navigation"; -import defaultMdxComponents from "@/mdx-components"; -import type { ComponentType } from "react"; - -export default async function Page({ - params, -}: { - params: Promise<{ slug?: string[] }>; -}) { - const { slug } = await params; - const page = source.getPage(slug); - if (!page) notFound(); - - // The body is the compiled MDX component — cast for type safety. - const data = page.data as { - body: ComponentType<{ components?: Record }>; - toc?: unknown; - full?: boolean; - }; - const MDX = data.body; - const toc = Array.isArray(data.toc) ? data.toc : undefined; - const full = data.full; - - return ( - - - {page.data.title} - - - {page.data.description} - - - - - - ); -} - -export async function generateStaticParams() { - return source.generateParams(); -} - -export async function generateMetadata({ - params, -}: { - params: Promise<{ slug?: string[] }>; -}) { - const { slug } = await params; - const page = source.getPage(slug); - if (!page) return {}; - return { - title: page.data.title, - description: page.data.description, - }; -} diff --git a/docs/app/docs/layout.tsx b/docs/app/docs/layout.tsx deleted file mode 100644 index 0bf0a5f..0000000 --- a/docs/app/docs/layout.tsx +++ /dev/null @@ -1,24 +0,0 @@ -import { DocsLayout } from "fumadocs-ui/layouts/docs"; -import type { ReactNode } from "react"; -import { baseOptions } from "@/app/layout.config"; -import { source } from "@/lib/source"; - -export default function Layout({ children }: { children: ReactNode }) { - return ( - - - v0.1.0-SNAPSHOT - - ), - }} - > - {children} - - ); -} diff --git a/docs/app/docs/page.tsx b/docs/app/docs/page.tsx deleted file mode 100644 index 0d2772a..0000000 --- a/docs/app/docs/page.tsx +++ /dev/null @@ -1,58 +0,0 @@ -import Link from "next/link"; -import { source } from "@/lib/source"; -import { - Cpu, - Database, - GitBranch, - Layers, - MessageSquare, - Terminal, -} from "lucide-react"; -import { DocCard } from "@/components/doc-card"; - -const categoryIcons: Record = { - abstraction: Layers, - cli: Terminal, - declaration: GitBranch, - design: Layers, - "environment-bootstrap": Cpu, - node: Database, - "user-story": MessageSquare, -}; - -export default function DocsIndexPage() { - const docs = source.getPages(); - return ( -
-
-
- - Documentation -
-

- Browse the docs -

-

- Everything from the bootstrap environment through the node - specification and the user story. Pick a topic. -

-
- -
- {docs.map((doc) => { - const slug = doc.slugs[0] ?? ""; - const Icon = categoryIcons[slug] ?? Cpu; - return ( - - ); - })} -
-
- ); -} diff --git a/docs/app/global.css b/docs/app/global.css deleted file mode 100644 index ec77754..0000000 --- a/docs/app/global.css +++ /dev/null @@ -1,150 +0,0 @@ -@import "fumadocs-ui/style.css"; - -@tailwind base; -@tailwind components; -@tailwind utilities; - -@layer base { - :root { - /* - * Light theme — warm cream like aged paper. - * Background is a visible warm cream (#f5ede0), NOT white. - * Cards are warm ivory (#fffaf5), clearly distinct from the bg. - * Text is warm dark brown (#3d2f24), NOT cold black. - */ - --background: 33 40% 93%; - --foreground: 25 30% 19%; - --muted: 33 30% 90%; - --muted-foreground: 30 18% 42%; - --card: 36 55% 99%; - --card-foreground: 25 30% 19%; - --border: 33 35% 84%; - --accent: 22 88% 49%; - --accent-foreground: 0 0% 100%; - --ring: 22 88% 49%; - - --fd-radius: 0.5rem; - } - - .dark { - /* - * Dark theme — warm dark brown like roasted coffee beans. - * Background is #1c1610 (warm near-black), NOT cold zinc. - * Cards are #2d2218 (warm dark brown), clearly distinct. - * Text is #f0e4d0 (warm beige), NOT cold gray. - */ - --background: 30 26% 9%; - --foreground: 36 40% 85%; - --muted: 30 20% 14%; - --muted-foreground: 33 20% 60%; - --card: 30 24% 13%; - --card-foreground: 36 40% 85%; - --border: 30 18% 20%; - --accent: 22 88% 54%; - --accent-foreground: 30 26% 9%; - --ring: 22 88% 54%; - } - - * { - border-color: hsl(var(--border)); - } - - html { - scroll-behavior: smooth; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; - text-rendering: optimizeLegibility; - } - - body { - background-color: hsl(var(--background)); - color: hsl(var(--foreground)); - font-feature-settings: "ss01", "cv11", "cv02"; - font-variation-settings: "opsz" 32; - } - - /* Warm scrollbar — solid color, no gradient */ - ::-webkit-scrollbar { - width: 10px; - height: 10px; - } - ::-webkit-scrollbar-track { - background: hsl(var(--muted)); - } - ::-webkit-scrollbar-thumb { - background: hsl(var(--muted-foreground) / 0.4); - border-radius: 8px; - border: 2px solid transparent; - background-clip: padding-box; - } - ::-webkit-scrollbar-thumb:hover { - background: hsl(var(--accent) / 0.6); - background-clip: padding-box; - border: 2px solid transparent; - } - - ::selection { - background: hsl(var(--accent) / 0.25); - color: hsl(var(--foreground)); - } -} - -@layer components { - /* - * Solid card panel — warm background, clear border, real shadow. - * This is the primary surface for cards and content blocks. - */ - .card-warm { - background: hsl(var(--card)); - border: 1px solid hsl(var(--border)); - box-shadow: 0 2px 4px 0 rgba(74,58,38,0.08), 0 4px 12px -2px rgba(74,58,38,0.10); - } - - .card-warm:hover { - border-color: hsl(var(--accent) / 0.4); - box-shadow: 0 4px 8px -2px rgba(74,58,38,0.10), 0 12px 32px -4px rgba(74,58,38,0.14); - } - - /* Nav bar — solid warm background, not translucent */ - .nav-warm { - background: hsl(var(--card) / 0.85); - backdrop-filter: blur(16px) saturate(150%); - -webkit-backdrop-filter: blur(16px) saturate(150%); - border-bottom: 1px solid hsl(var(--border)); - } - - /* Premium focus ring — solid accent color */ - .focus-ring { - outline: none; - } - .focus-ring:focus-visible { - box-shadow: - 0 0 0 2px hsl(var(--background)), - 0 0 0 4px hsl(var(--accent) / 0.6); - } -} - -@layer utilities { - .text-balance { - text-wrap: balance; - } - .text-pretty { - text-wrap: pretty; - } -} - -/* - * Fumadocs CSS variable overrides — map fd-* variables to the warm palette. - */ -:root { - --fd-background: hsl(var(--background)); - --fd-foreground: hsl(var(--foreground)); - --fd-muted: hsl(var(--muted)); - --fd-muted-foreground: hsl(var(--muted-foreground)); - --fd-card: hsl(var(--card)); - --fd-card-foreground: hsl(var(--card-foreground)); - --fd-border: hsl(var(--border)); - --fd-primary: hsl(var(--accent)); - --fd-primary-foreground: hsl(var(--accent-foreground)); - --fd-ring: hsl(var(--ring)); -} diff --git a/docs/app/icon.tsx b/docs/app/icon.tsx deleted file mode 100644 index 129e155..0000000 --- a/docs/app/icon.tsx +++ /dev/null @@ -1,31 +0,0 @@ -import { ImageResponse } from "next/og"; - -export const runtime = "edge"; - -export const size = { width: 32, height: 32 }; -export const contentType = "image/png"; - -export default function Icon() { - return new ImageResponse( - ( -
- Q -
- ), - { ...size } - ); -} diff --git a/docs/app/layout.config.tsx b/docs/app/layout.config.tsx deleted file mode 100644 index bea598c..0000000 --- a/docs/app/layout.config.tsx +++ /dev/null @@ -1,29 +0,0 @@ -import type { BaseLayoutProps } from "fumadocs-ui/layouts/shared"; -import { Logo } from "@/components/logo"; - -/** - * Shared layout options used by every Fumadocs layout (docs, home, search). - * Keeps nav consistent across routes. - */ -export const baseOptions: BaseLayoutProps = { - nav: { - title: , - }, - links: [ - { - text: "Docs", - url: "/docs", - active: "nested-url", - }, - { - text: "Bootstrap", - url: "/docs/environment-bootstrap", - active: "nested-url", - }, - { - text: "Spec", - url: "/docs/declaration", - active: "nested-url", - }, - ], -}; diff --git a/docs/app/layout.tsx b/docs/app/layout.tsx deleted file mode 100644 index 90a2854..0000000 --- a/docs/app/layout.tsx +++ /dev/null @@ -1,57 +0,0 @@ -import type { ReactNode } from "react"; -import { Inter, JetBrains_Mono } from "next/font/google"; -import { RootProvider } from "fumadocs-ui/provider"; -import "./global.css"; - -const inter = Inter({ - subsets: ["latin"], - variable: "--font-inter", - display: "swap", -}); - -const jetbrains = JetBrains_Mono({ - subsets: ["latin"], - variable: "--font-jetbrains", - display: "swap", -}); - -export const metadata = { - title: { - default: "Quark Platform — Documentation", - template: "%s · Quark Platform", - }, - description: - "A universal runtime for programmable nodes. Three-service architecture with a Java/Native control plane, Go + SQLite Catalog, and a GraalJS-powered data plane.", -}; - -export default function RootLayout({ children }: { children: ReactNode }) { - return ( - - - {/* Theme bootstrap — avoid FOUC by setting the class before hydration */} -