Skip to content

feat(workspace): use filesystem storage without S3 - #13012

Merged
mudler merged 7 commits into
masterfrom
feat/shared-workspace-files
Nov 1, 2026
Merged

mudler merged 7 commits into
masterfrom
feat/shared-workspace-files

Conversation

@mudler-agent

@mudler-agent mudler-agent commented Nov 1, 2026 •

Copy link
Copy Markdown
Collaborator

Description

Allow distributed workspace Files to use the existing filesystem backend when S3 is not configured. No new flag or workspace path is needed.

Workspace blob selection is automatic for frontends, the add-on owner and optional chat workers:

  • Configured S3: use S3, including on an owner without distributed services.
  • No S3: use the existing DataPath/chats/blobs directory through the direct workspace filesystem adapter.

Distributed deployments without S3 must mount the same backing data directory on every Files participant and use shared PostgreSQL metadata. Equal path strings do not prove shared storage; this is an operator responsibility. No authoritative filesystem blobs are placed in temporary scratch.

Global model/input staging and the generic distributed ObjectStore remain unchanged. Standalone JSON/SQLite workspace behavior remains supported. The owner does not need a different distributed role.

Notes for Reviewers

This revision removes the custom path and opt-in configuration from the earlier version of this PR. The safety fixes remain:

  • Complete temporary-file publication, atomic rename, and file/directory synchronization.
  • Copied materialization instead of chmod-mutable hard links into authoritative blobs.
  • In-tree symlink rejection.
  • Automatic PostgreSQL/direct-filesystem coordination covering ingestion through metadata publication and purge reference checks through deletion. Tests reproduce the prior dangling-blob race across independent connections.
  • Bounded purge waits and cleanup of failed workspace startup, including configured-S3 SQLite/no-auth owners.

PostgreSQL filesystem uploads hold the workspace revision lock through ingestion, serializing metadata writers during slow uploads. Input readers must honor cancellation while blocked. SQLite/JSON behavior is preserved; S3 does not use filesystem coordination.

Documentation covers shared metadata versus shared blob bytes, same-node PVC placement, coherent RWX requirements and compatible service permissions. Existing filesystem blob paths stay unchanged. This PR does not copy or migrate existing data. A backend change on populated metadata still requires a separate copy/verification procedure and consistent selection across participants.

Verification

Commands run with GOPROXY=off GOTOOLCHAIN=local:

go test -race -count=1 -timeout 120s ./core/services/studio/workspace/blob
go test -tags auth -count=1 -timeout 120s ./core/services/studio/workspace/gormstore -ginkgo.skip PostgreSQL
go test -count=1 -timeout 120s ./core/config -ginkgo.focus 'automatic workspace'
go test -tags auth -count=1 -timeout 120s ./core/cli -ginkgo.focus 'automatic workspace'
go test -tags auth -race -count=1 -timeout 120s ./core/application -ginkgo.focus 'automatic workspace|studio files at startup' -ginkgo.skip PostgreSQL
go test -count=1 -timeout 120s ./core/application -ginkgo.focus 'automatic workspace|studio files at startup' -ginkgo.skip PostgreSQL
go test -tags auth -run '^$' ./core/application ./core/cli

With LOCALAI_TEST_POSTGRES_URL set to a new disposable PostgreSQL 16 fixture:

go test -tags auth -race -count=1 -timeout 180s -v ./core/services/studio/workspace/gormstore -ginkgo.focus 'PostgreSQL shared filesystem'
go test -tags auth -race -count=1 -timeout 180s -v ./core/services/studio/workspace/gormstore -ginkgo.focus PostgreSQL
go test -tags auth -race -count=1 -timeout 120s -v ./core/application -ginkgo.focus 'PostgreSQL shared filesystem'

Root verification passes: blob 30 specs, non-PostgreSQL workspace 137 specs, config/CLI and application regressions, PostgreSQL application 3 selected specs, PostgreSQL shared-filesystem 14 selected specs, and all PostgreSQL workspace 142 selected specs. No selected specs skipped at runtime; other specs were excluded by focus. Disposable containers were removed. Independent final review passes and independently reran application/config/CLI/blob unit tests and diff checks.

Genuine regression failures were observed before implementation, including automatic selection, owner S3 validation and standalone S3 startup cleanup.

Limits: no full build/lint, real RWX storage, live S3, UI or full-server startup validation. S3 tests use spies plus constructor initialization against a fake endpoint. Existing local protobuf generators and a prebuilt real UI bundle satisfy ignored compile prerequisites; no generated artifacts enter this PR. Real volume identity and power-loss behavior remain deployment checks. No infrastructure changes or deployment included.

Signed commits

  • Yes, I signed off my commits.
  • Documentation updated (docs/content/features/chats.md and distributed-mode.md).

Sync filesystem publication and isolate materialized tool files from
immutable blob inodes. Reject symlink paths within the blob tree.

Opt shared PostgreSQL participants into revision-lock coordination for
ingest, reference publication, and purge checks and deletion. Preserve
legacy ingest order when the explicit option is disabled.

Verify process publication and interruption, injected failures, and
shared-schema PostgreSQL races across independent connection pools.

Assisted-by: nib:gpt-6.1-sol
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>
Honor immediate caller cancellation and give delayed PostgreSQL cleanup an independent 30-second budget for revision-lock acquisition and reference checks. Preserve blobs when cleanup cannot acquire the lock.

Cover cancellation, delayed cleanup, and pool release. Convert publication tests to Ginkgo/Gomega while retaining independent-process child bootstrapping.

Assisted-by: nib:gpt-6.1-sol
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>
Allow Files participants to share immutable blobs without S3. Require
PostgreSQL metadata and reject invalid explicit roots at startup.
Keep default storage and model staging unchanged. Document the volume,
permissions and coordinated backend-switching contract.

Assisted-by: nib:gpt-6.1-sol
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>
Cancel the owned startup context and close the auth pool when explicit
shared Files startup fails. Preserve the caller context and legacy
startup behavior.

Cover early validation and late Files failure with PostgreSQL-backed
cleanup assertions.

Assisted-by: nib:gpt-6.1-sol
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>
Use configured S3 for every workspace participant, including owners
without distributed services. Otherwise retain DataPath/chats/blobs
through the direct filesystem adapter, without another path setting.

Derive PostgreSQL publication and purge coordination from the blob
backend so non-distributed owners also protect shared filesystem bytes.
Keep shared mount and metadata identity an explicit operator contract.
Remove the opt-in probe and document storage selection without migration.

Verify automatic selection with red/green regressions, S3 constructor
spies, focused startup tests and blob race tests. Exercise shared files
and PostgreSQL coordination against an owned PostgreSQL 16 fixture.

Assisted-by: nib:gpt-6.1-sol
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>
Own the startup child context for configured S3 as well as PostgreSQL metadata. Cancel background work, shut down opened resources and close the auth pool on fatal initialization errors without canceling the caller context or changing legacy JSON/SQLite without S3.

Regress missing-bucket failures using temporary SQLite and no-auth owners through the production cleanup helper. Verify pool closure, owned-context cancellation and resource shutdown.

Assisted-by: nib:gpt-6.1-sol
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>
@mudler-agent mudler-agent changed the title feat(workspace): support shared filesystem storage feat(workspace): use filesystem storage without S3 Nov 1, 2026
if err := s.stage("directory sync"); err != nil {
return err
}
f, err := os.Open(path)
if err := s.safe(p); err != nil {
return nil, err
}
f, err := os.Open(p)
if err != nil {
return "", 0, err
}
defer func() { f.Close(); os.Remove(f.Name()) }()
if err != nil {
return "", 0, err
}
defer func() { f.Close(); os.Remove(f.Name()) }()
Mark best-effort cleanup errors explicitly and inspect the config type
without copying its atomic fields. Document why the validated blob
paths do not permit file inclusion.

Keep publication, synchronization and PostgreSQL coordination unchanged.

Assisted-by: nib:gpt-6.1-sol
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>
@mudler
mudler merged commit e03f427 into master Nov 1, 2026
1 of 30 checks passed
@mudler
mudler deleted the feat/shared-workspace-files branch November 1, 2026 17:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants