Repo layout¶
.
├── server/ # Go module; the backend
│ ├── main.go # entrypoint; flag parsing; mux registration; signal handling
│ ├── handlers.go # POST /api/chat + helpers (statelessChat, statefulChat)
│ ├── *_handlers.go # one file per route group (connections, conversations, ...)
│ ├── store.go # conversation persistence (encrypted)
│ ├── cryptostore.go # the AEAD layer; every persistent write goes through here
│ ├── auth.go # WebAuthn ceremony + session signing
│ ├── tokens.go # bearer-token mint + storage
│ ├── connections.go # ConnectionManager (LLM profiles)
│ ├── image_connections.go # ImageConnectionManager (SD profiles)
│ ├── characters.go # Tavern card store + PNG chunk read/write
│ ├── characters_external.go # read-only cards from card_dirs
│ ├── personas.go # persona store (Tavern v2 with persona extension)
│ ├── builtin_character.go # the embedded Pluma card
│ ├── builtin_workflows.go # ComfyUI workflows that ship with the binary
│ ├── tsnet.go # embedded Tailscale node controller
│ ├── tsnet_handlers.go # POST /api/tailscale/{enable,disable} + status
│ ├── config.go # config.toml read/write + comment-preserving renderer
│ ├── limits.go # body-size caps
│ ├── ssrf.go # restricted HTTP client + dial guard
│ ├── voice_library.go # voice-clone sample storage
│ ├── voice_library_handlers.go
│ ├── voice_library_import.go # yt-dlp + direct-fetch URL import
│ ├── tts.go # OpenAI-compat TTS provider
│ ├── tts_handlers.go # POST /api/tts/{speak,config,voices,models}
│ ├── tts_voice_discovery.go # filesystem + curated catalogue voice fallback
│ ├── cli.go # cobra root command
│ ├── cli_auth.go # pluma auth {login,logout,whoami}
│ ├── embed.go # //go:embed embedded
│ ├── embedded/ # vite build output (gitignored)
│ ├── embedded_chars/ # built-in character PNG (Pluma)
│ └── *_test.go # unit tests
├── web/ # Svelte SPA
│ ├── src/
│ │ ├── App.svelte # root component; layout, modals, routing
│ │ ├── main.ts # mount point; theme registration
│ │ ├── app.css # base styles; theme CSS-variable seeds
│ │ ├── components/ # everything visible
│ │ ├── lib/ # state stores + helpers (api.ts, *.svelte.ts)
│ │ ├── themes/ # pluma-teal, pluma-rose, the Theme abstraction
│ │ └── *.test.ts # vitest unit tests
│ ├── package.json
│ ├── tsconfig.json
│ └── vite.config.ts
├── docs/ # MkDocs Material source for plumachat.app
├── scripts/
│ └── dev-watch.sh # auto-rebuild + bounce loop
├── specs/ # design docs + upstream-issue specs
├── Formula/
│ └── pluma.rb # Homebrew formula template
├── .github/
│ ├── workflows/ # ci, release, docs
│ ├── ISSUE_TEMPLATE/
│ └── PULL_REQUEST_TEMPLATE.md
├── .beads/ # local bd (beads) issue tracker state
├── README.md
├── CHANGELOG.md
├── CONTRIBUTING.md
├── CODE_OF_CONDUCT.md
├── SECURITY.md
├── SECURITY_REVIEW.md # archive of the security-audit pass
├── LICENSE # MIT
├── Makefile
└── mkdocs.yml # docs config
File naming¶
- Go handlers cluster by route group:
<thing>_handlers.gofor the HTTP layer,<thing>.gofor the storage / logic layer. - Tests share the base file name with
_test.go. - Svelte components are PascalCase (
MessageView.svelte); stores are kebab-case-suffixed*.svelte.ts(store.svelte.ts).
Module boundary¶
server/ is the Go module root (go.mod lives there). Imports inside the binary are bare ("pluma" is the module path; everything's package main). External deps are listed in server/go.mod.
The frontend is an entirely separate concern: web/package.json is the npm root. Nothing in server/ reads anything in web/ except the built embedded/ directory at compile time.