internals
A look under the hood
internals
Curious how gaur works? Here’s the technical breakdown.
architecture
gaur follows the Elm Architecture via Bubble Tea. The codebase is modular and readable.
core components
| File | Purpose |
|---|---|
model.go | Central state: config, packages, selections, UI state |
update.go | Event handling: keyboard, mouse, messages |
view.go | Main rendering logic and layout |
styles.go | Themes, colors, and Lip Gloss styles |
commands.go | System interaction: pacman, AUR helpers, fzf |
config.go | Configuration loading and validation |
settings.go | In-app settings overlay |
feature_dashboard.go | Dashboard stats and widgets |
feature_install.go | Install mode logic |
feature_remove.go | Remove mode logic |
feature_update.go | Update mode logic |
cache_views.go | Cache management UI |
utils.go | Helpers: fuzzy matching, highlighting, validation |
types.go | Type definitions and constants |
aur helper integration
gaur doesn’t replace your package manager. It wraps it.
search pipeline
- Local query: Searches the pacman sync database for official packages
- AUR query: Queries AUR through your configured helper (debounced to avoid hammering)
- Fuzzy filter: Results pipe through
fzf --filterfor relevance-ranked matching - Merge: Official and AUR results are combined and deduplicated
interactive handoff
Operations requiring user input use tea.ExecProcess to hand control to the terminal:
- Password prompts (sudo)
- Conflict resolution
- License acceptance
- Build confirmations (AUR)
This keeps the terminal fully interactive. gaur resumes after the operation completes.
real-time state sync
After every install, remove, or update:
- gaur runs a full system scan
- Dashboard statistics update
- Package lists refresh
- Cache sizes recalculate
The UI always reflects reality. No stale data.
security measures
gaur is careful about what it executes:
package name validation
Every package name is validated before being passed to commands:
// Only allows: a-z, A-Z, 0-9, @, ., _, +, -
func isValidPackageName(name string) bool
This prevents shell injection through malicious package names.
config validation
Configuration values are validated and sanitized:
- AUR helper: Only
paruoryayallowed - Cache tool: Only
paccacheallowed - Cache directory: Must be an absolute path, no
..traversal - File permissions: Config files created with
0600(owner-only)
command building
Commands are built using structured argument arrays, never string concatenation:
cmd := exec.Command(helper, "-S", "--noconfirm", pkg1, pkg2)
No shell interpretation, no injection vectors.
file locations
| Path | Purpose |
|---|---|
~/.config/gaur/config.toml | User configuration |
~/.config/gaur/gaur.log | Error logging |
/var/cache/pacman/pkg | Pacman package cache |
~/.cache/paru/clone | Paru build directory |
~/.cache/yay | Yay build directory |
gaur respects $XDG_CONFIG_HOME and $XDG_CACHE_HOME if set.
dependencies
gaur shells out to these tools:
| Tool | Package | Purpose |
|---|---|---|
pacman | pacman | Package database queries |
paru or yay | AUR helper | Install, remove, update, AUR search |
fzf | fzf | Fuzzy filtering |
paccache | pacman-contrib | Cache management |
performance notes
- Debouncing: Package detail fetches are debounced (default 150ms) to reduce system calls
- Caching: Package details are cached in memory to avoid repeat lookups
- Lazy loading: AUR search only triggers after the local search completes
- Batch operations: Multiple packages are passed to a single command, not one-by-one