Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions cmd/engine/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,16 @@ func main() {

func run() error {
configPath := flag.String("config", "", "path to config.yaml (optional; env vars take precedence)")
localMode := flag.Bool("local", false, "zero-config local mode: localhost Postgres, local storage, listen on :7654, no setup (sets VLE_LOCAL_MODE)")
flag.Parse()

// --local is sugar for VLE_LOCAL_MODE=true so the CLI flag and the env
// var (used by the all-in-one Docker image) flow through one path in
// config.Load. Set it before Load reads the environment.
if *localMode {
_ = os.Setenv("VLE_LOCAL_MODE", "true")
}

cfg, err := config.Load(*configPath)
if err != nil {
return fmt.Errorf("load config: %w", err)
Expand All @@ -56,6 +64,7 @@ func run() error {
logger := newLogger(cfg.Log)
logger.Info("starting vectorless-engine",
"version", version,
"local_mode", config.LocalModeEnabled(),
"storage_driver", cfg.Storage.Driver,
"queue_driver", cfg.Queue.Driver,
"llm_driver", cfg.LLM.Driver,
Expand Down
13 changes: 13 additions & 0 deletions config.example.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,19 @@
#
# `vectorless-engine config print` prints the effective config with secrets
# redacted; `vectorless-engine config check` validates it and exits 0/1.
#
# ZERO-CONFIG LOCAL MODE
# ----------------------
# Run `engine --local` (or set VLE_LOCAL_MODE=true) to boot with no config
# at all: it listens on :7654, points at a localhost Postgres
# (postgres://vectorless:vectorless@localhost:5432/vectorless), uses local
# file storage and the Postgres-backed river queue, and requires no API key
Comment on lines +20 to +22

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Align the documented local Postgres URL with the actual default.

The comment block shows postgres://vectorless:vectorless@localhost:5432/vectorless, but local mode actually defaults to .../vectorless?sslmode=disable in pkg/config/config.go. Please update this string to match the real injected default to avoid copy/paste confusion.

Proposed doc fix
-# (postgres://vectorless:vectorless@localhost:5432/vectorless), uses local
+# (postgres://vectorless:vectorless@localhost:5432/vectorless?sslmode=disable), uses local
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
# at all: it listens on :7654, points at a localhost Postgres
# (postgres://vectorless:vectorless@localhost:5432/vectorless), uses local
# file storage and the Postgres-backed river queue, and requires no API key
# at all: it listens on :7654, points at a localhost Postgres
# (postgres://vectorless:vectorless@localhost:5432/vectorless?sslmode=disable), uses local
# file storage and the Postgres-backed river queue, and requires no API key
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@config.example.yaml` around lines 20 - 22, The documented Postgres URL in the
comment block differs from the actual default configuration. Update the example
Postgres URL in the comment from
`postgres://vectorless:vectorless@localhost:5432/vectorless` to
`postgres://vectorless:vectorless@localhost:5432/vectorless?sslmode=disable` to
match the real default used in pkg/config/config.go, ensuring the documentation
accurately reflects the actual configuration that gets injected in local mode.

# to call the engine. This matches the all-in-one Docker image, where
# Postgres is bundled in the same container. You still supply an LLM
# provider key (e.g. VLE_LLM_ANTHROPIC_API_KEY) for ingestion + retrieval.
# Override any local default with the usual env/flags, e.g.
# VLE_SERVER_ADDR=:9000 or VLE_STORAGE_LOCAL_ROOT=/data/documents.
# Local mode is for dev/local use — do NOT expose it to the public internet.

server:
addr: ":8080"
Expand Down
57 changes: 57 additions & 0 deletions pkg/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -790,10 +790,64 @@ func Default() Config {
}
}

// localDefaultAddr is the canonical local-mode listen address. The whole
// product (engine API + bundled dashboard) is reachable at
// localhost:7654 so every doc, link, and quickstart can say one number.
const localDefaultAddr = ":7654"

// defaultLocalDatabaseURL is the Postgres DSN local mode assumes when no
// URL is configured. It matches the bundled Postgres in the all-in-one
// Docker image and the dev docker-compose (user/pass/db all "vectorless"
// on localhost:5432), so a bare `engine --local` next to those services
// just connects.
const defaultLocalDatabaseURL = "postgres://vectorless:vectorless@localhost:5432/vectorless?sslmode=disable"

// LocalModeEnabled reports whether zero-config local mode was requested
// via the VLE_LOCAL_MODE env var (truthy: 1/true/yes/on). The engine's
// --local flag sets this var before Load runs, so the CLI flag and the
// env var (used by the Docker image) share one code path.
func LocalModeEnabled() bool {
switch strings.ToLower(strings.TrimSpace(os.Getenv("VLE_LOCAL_MODE"))) {
case "1", "true", "yes", "on":
return true
}
return false
}

// applyLocalDefaults rewrites the base config for zero-config local
// running: the canonical :7654 port, a localhost Postgres URL matching
// the bundled/dev database, local file storage, and the Postgres-backed
// river queue (no Redis required). It runs on the Default() base BEFORE
// the YAML file and env overrides are applied, so any value the operator
// sets explicitly still wins — local mode only moves the starting point
// so the engine boots with no required configuration.
//
// Auth: the standalone engine (cmd/engine) is already unauthenticated —
// it serves a single logical tenant with no API key — so "local-mode
// auth" needs no extra wiring here. This is dev/local only and must not
// be exposed to the public internet.
func applyLocalDefaults(c *Config) {
c.Server.Addr = localDefaultAddr
c.Database.URL = defaultLocalDatabaseURL
c.Storage.Driver = "local"
if c.Storage.Local.Root == "" {
c.Storage.Local.Root = "./data/documents"
}
c.Queue.Driver = "river"
}

// Load reads configuration from a YAML file (optional) and applies
// environment overrides on top. Pass an empty path to skip the file.
//
// When VLE_LOCAL_MODE is truthy (or the engine is run with --local, which
// sets it), zero-config local defaults are applied to the base before the
// file/env layers, so the engine boots on :7654 against a localhost
// Postgres with no required configuration. File and env still override.
func Load(path string) (Config, error) {
cfg := Default()
if LocalModeEnabled() {
applyLocalDefaults(&cfg)
}
if path != "" {
data, err := os.ReadFile(path)
if err != nil {
Expand Down Expand Up @@ -869,6 +923,9 @@ func applyEnvOverrides(c *Config) {
if v := os.Getenv("VLE_STORAGE_DRIVER"); v != "" {
c.Storage.Driver = v
}
if v := os.Getenv("VLE_STORAGE_LOCAL_ROOT"); v != "" {
c.Storage.Local.Root = v
}
if v := os.Getenv("VLE_QUEUE_DRIVER"); v != "" {
c.Queue.Driver = v
}
Expand Down
99 changes: 99 additions & 0 deletions pkg/config/config_local_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
package config

import "testing"

// TestLocalModeDefaults: with VLE_LOCAL_MODE set, Load with no file and no
// other env boots a complete, valid config — :7654, a localhost Postgres
// URL, local storage, river queue — with nothing else required.
func TestLocalModeDefaults(t *testing.T) {
t.Setenv("VLE_LOCAL_MODE", "true")

cfg, err := Load("")
if err != nil {
t.Fatalf("local-mode Load() with no other config should succeed, got: %v", err)
}
if cfg.Server.Addr != ":7654" {
t.Errorf("local mode server.addr = %q, want :7654", cfg.Server.Addr)
}
if cfg.Database.URL != defaultLocalDatabaseURL {
t.Errorf("local mode database.url = %q, want %q", cfg.Database.URL, defaultLocalDatabaseURL)
}
if cfg.Storage.Driver != "local" {
t.Errorf("local mode storage.driver = %q, want local", cfg.Storage.Driver)
}
if cfg.Queue.Driver != "river" {
t.Errorf("local mode queue.driver = %q, want river", cfg.Queue.Driver)
}
if cfg.Storage.Local.Root == "" {
t.Error("local mode storage.local.root must be set")
}
}

// TestLocalModeTruthyForms: the env flag accepts the usual truthy spellings
// and ignores everything else.
func TestLocalModeTruthyForms(t *testing.T) {
for _, v := range []string{"1", "true", "TRUE", "yes", "on"} {
t.Setenv("VLE_LOCAL_MODE", v)
if !LocalModeEnabled() {
t.Errorf("VLE_LOCAL_MODE=%q should enable local mode", v)
}
}
for _, v := range []string{"", "0", "false", "no", "off", "nope"} {
t.Setenv("VLE_LOCAL_MODE", v)
if LocalModeEnabled() {
t.Errorf("VLE_LOCAL_MODE=%q should NOT enable local mode", v)
}
}
}

// TestLocalModeEnvOverridesWin: local mode only moves the starting point —
// explicit env values still override the local defaults.
func TestLocalModeEnvOverridesWin(t *testing.T) {
t.Setenv("VLE_LOCAL_MODE", "true")
t.Setenv("VLE_SERVER_ADDR", ":9999")
t.Setenv("VLE_DATABASE_URL", "postgres://custom:custom@db:5432/custom?sslmode=disable")
t.Setenv("VLE_STORAGE_LOCAL_ROOT", "/srv/docs")

cfg, err := Load("")
if err != nil {
t.Fatalf("Load() failed: %v", err)
}
if cfg.Server.Addr != ":9999" {
t.Errorf("env should override local addr: got %q, want :9999", cfg.Server.Addr)
}
if cfg.Database.URL != "postgres://custom:custom@db:5432/custom?sslmode=disable" {
t.Errorf("env should override local db url, got %q", cfg.Database.URL)
}
if cfg.Storage.Local.Root != "/srv/docs" {
t.Errorf("VLE_STORAGE_LOCAL_ROOT should set storage root, got %q", cfg.Storage.Local.Root)
}
}

// TestNonLocalModeUnchanged: without the flag the historical defaults hold
// (:8080), and the engine still requires a database URL for the river
// queue — i.e. local mode is the ONLY thing that injects one.
func TestNonLocalModeUnchanged(t *testing.T) {
t.Setenv("VLE_LOCAL_MODE", "")
// Provide a DB URL so validation passes for the river default.
t.Setenv("VLE_DATABASE_URL", "postgres://x:x@localhost:5432/x?sslmode=disable")

cfg, err := Load("")
if err != nil {
t.Fatalf("Load() failed: %v", err)
}
if cfg.Server.Addr != ":8080" {
t.Errorf("non-local addr = %q, want :8080", cfg.Server.Addr)
}
}

// TestNonLocalModeMissingDBURLFails proves the local-mode injection is what
// removes the "no required config" gap: without it and without a DB URL,
// the river queue fails validation.
func TestNonLocalModeMissingDBURLFails(t *testing.T) {
t.Setenv("VLE_LOCAL_MODE", "")
t.Setenv("VLE_DATABASE_URL", "")

if _, err := Load(""); err == nil {
t.Fatal("expected validation error for river queue with no database.url, got nil")
}
}
Loading