Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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