Documentation
¶
Overview ¶
Package paths provides centralized path resolution for all SageOx directories.
Architecture Overview ¶
This package implements an XDG-inspired directory structure within ~/.sageox/ for discoverability, while supporting full XDG compliance via OX_XDG_ENABLE=1 for users who prefer standard XDG locations.
Directory Structure ¶
Default layout (~/.sageox/):
~/.sageox/ ├── config/ # User configuration (XDG_CONFIG_HOME equivalent) ├── data/ # Persistent data like team contexts (XDG_DATA_HOME equivalent) ├── cache/ # Disposable cached data (XDG_CACHE_HOME equivalent) └── state/ # Runtime state like daemon sockets (XDG_STATE_HOME equivalent)
Why ~/.sageox/ Instead of XDG by Default ¶
- Single discoverable location - users can easily find all SageOx data
- Easier to inspect, backup, and troubleshoot
- Follows precedent of ~/.docker, ~/.cargo, ~/.npm
- XDG purists can opt-in via OX_XDG_ENABLE=1
XDG Compatibility Mode ¶
Set OX_XDG_ENABLE=1 to use standard XDG locations:
config/ → $XDG_CONFIG_HOME/sageox/ (default: ~/.config/sageox/) data/ → $XDG_DATA_HOME/sageox/ (default: ~/.local/share/sageox/) cache/ → $XDG_CACHE_HOME/sageox/ (default: ~/.cache/sageox/) state/ → $XDG_STATE_HOME/sageox/ (default: ~/.local/state/sageox/)
Note: When OX_XDG_ENABLE=1, daemon state uses $XDG_RUNTIME_DIR/sageox/ if available, falling back to /tmp/sageox/ for ephemeral runtime files (sockets, locks).
Usage ¶
All path access should go through this package. Never hardcode paths elsewhere:
// Good configPath := paths.UserConfigFile() teamsDir := paths.TeamsDataDir() // Bad - don't do this configPath := filepath.Join(os.UserHomeDir(), ".sageox", "config", "config.yaml")
Thread Safety ¶
All functions in this package are safe for concurrent use. Path resolution is deterministic based on environment variables read at call time.
Package paths provides canonical path resolution for all SageOx data locations.
XDG BASE DIRECTORY SPECIFICATION COMPLIANCE ===========================================
The ox CLI MUST follow the XDG Base Directory Specification (v0.8+): https://specifications.freedesktop.org/basedir-spec/latest/
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC 2119.
Requirements:
- Config data MUST be stored in $XDG_CONFIG_HOME/sageox (default: ~/.config/sageox/)
- Persistent data MUST be stored in $XDG_DATA_HOME/sageox (default: ~/.local/share/sageox/)
- Cache data MUST be stored in $XDG_CACHE_HOME/sageox (default: ~/.cache/sageox/)
- State data SHOULD be stored in $XDG_STATE_HOME/sageox (default: ~/.local/state/sageox/)
- Runtime data MAY be stored in $XDG_RUNTIME_DIR/sageox if available
Legacy mode (OX_XDG_DISABLE=1) is provided for backwards compatibility only and SHOULD NOT be used in new installations.
Path Construction:
- All path functions in this package are CANONICAL
- Code MUST NOT construct SageOx paths manually with filepath.Join()
- Changes to path locations REQUIRE Ryan's review
Index ¶
- func AuthFile() string
- func CacheDir() string
- func CodeDBDataDir(projectRoot string) string
- func CodeDBSharedDir(repoID, endpointURL string) string
- func ConfigDir() string
- func CreateTeamSymlinks(ledgerPath string, teamIDs []string) error
- func DaemonCacheDir() string
- func DaemonLogFile(repoID, workspaceID string) string
- func DaemonPidFile(workspaceID string) string
- func DaemonRegistryFile() string
- func DaemonSocketFile(workspaceID string) string
- func DaemonStateDir() string
- func DataDir() string
- func DetectLegacyLedgerPath(projectRoot string) string
- func EndpointSlug(path string) string
- func EnsureDir(path string) (string, error)
- func EnsureDirForFile(filePath string) (string, error)
- func EnsureMigrated() error
- func GitCredentialsFile() string
- func GuidanceCacheDir() string
- func HeartbeatCacheDir(ep string) string
- func LedgerNeedsMigration(projectRoot, ep string) bool
- func LedgersDataDir(repoID, ep string) string
- func LegacySessionCacheDirs(repoID string) []string
- func MachineIDFile() string
- func MigrateLedgerToNewStructure(projectRoot, ep string) error
- func MigrateTeamContext(teamID, legacyPath string) error
- func NewLedgerPath(projectRoot, ep string) string
- func SageoxDir() string
- func SessionCacheDir(repoID string) string
- func StateDir() string
- func TeamContextDir(teamID, ep string) string
- func TeamContextMigrationNeeded(projectRoot, teamID, legacyPath string) bool
- func TeamsDataDir(ep string) string
- func TempDir() string
- func UserConfigFile() string
- func VerificationCacheFile() string
- type LedgerMigrationStatus
- type LegacyPaths
- type MigrationResult
- type MigrationStatus
- type TeamContextLegacyPath
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func AuthFile ¶
func AuthFile() string
AuthFile returns the path to the authentication tokens file. Contains per-endpoint auth tokens. Should have 0600 permissions.
func CacheDir ¶
func CacheDir() string
CacheDir returns the cache directory per XDG Base Directory Specification. https://specifications.freedesktop.org/basedir-spec/latest/
Contains: guidance/, sessions/, daemon/ (logs)
XDG mode (default): $XDG_CACHE_HOME/sageox (default: ~/.cache/sageox) Legacy mode (OX_XDG_DISABLE=1): ~/.sageox/cache
func CodeDBDataDir ¶ added in v0.4.0
CodeDBDataDir returns the legacy per-worktree CodeDB directory. Deprecated: Use CodeDBSharedDir for new code. This is kept for migration detection.
func CodeDBSharedDir ¶ added in v0.5.0
CodeDBSharedDir returns the directory for the shared CodeDB index (committed content). Stored inside the ledger's local cache, shared across all worktrees for the same repo. Format: ~/.local/share/sageox/<endpoint>/ledgers/<repoID>/.sageox/cache/codedb/
func ConfigDir ¶
func ConfigDir() string
ConfigDir returns the configuration directory per XDG Base Directory Specification. https://specifications.freedesktop.org/basedir-spec/latest/
Contains: config.yaml, auth.json, git-credentials.json, verification-cache.json, machine-id
XDG mode (default): $XDG_CONFIG_HOME/sageox (default: ~/.config/sageox) Legacy mode (OX_XDG_DISABLE=1): ~/.sageox/config
func CreateTeamSymlinks ¶
CreateTeamSymlinks creates symlinks from team context directories to the ledger. This is a placeholder for future implementation when team context integration with the new ledger structure is defined.
The intent is to create bidirectional references:
- From ledger to team contexts it uses
- From team contexts to ledgers that reference them
func DaemonCacheDir ¶
func DaemonCacheDir() string
DaemonCacheDir returns the base directory for daemon cache files. Used for heartbeats and other daemon-managed cache data.
~/.cache/sageox/ (or XDG equivalent)
func DaemonLogFile ¶
DaemonLogFile returns the path to a specific daemon's log file. Ensures the log directory exists with proper permissions.
Returns /tmp/<username>/sageox/logs/daemon_<repo_id>_<workspace_id>.log
Uses composite identifier (repo_id + workspace_id) for consistency with heartbeats:
- repo_id makes logs debuggable (can see which repo)
- workspace_id ensures uniqueness (supports multiple worktrees)
Creates entire directory tree with 0700 permissions (owner-only access):
/tmp/<username>/sageox/ (0700 - owner-only) /tmp/<username>/sageox/logs/ (0700 - owner-only)
CRITICAL: All directories are 0700 to prevent other users from accessing daemon logs, which may contain sensitive information.
If directory creation fails, still returns the path (caller will get error when opening file).
func DaemonPidFile ¶
DaemonPidFile returns the path to a daemon's PID file.
func DaemonRegistryFile ¶
func DaemonRegistryFile() string
DaemonRegistryFile returns the path to the daemon registry. Contains JSON registry of all active daemons.
func DaemonSocketFile ¶
DaemonSocketFile returns the path to a daemon's Unix socket.
func DaemonStateDir ¶
func DaemonStateDir() string
DaemonStateDir returns the directory for daemon runtime state. Contains sockets, PIDs, and the daemon registry.
func DataDir ¶
func DataDir() string
DataDir returns the persistent data directory per XDG Base Directory Specification. https://specifications.freedesktop.org/basedir-spec/latest/
Contains: teams/ (team context repositories)
XDG mode (default): $XDG_DATA_HOME/sageox (default: ~/.local/share/sageox) Legacy mode (OX_XDG_DISABLE=1): ~/.sageox/data
func DetectLegacyLedgerPath ¶
DetectLegacyLedgerPath checks for the old repo_sageox_ledger/ sibling directory. Returns the path if it exists, empty string if not.
The legacy format is: <project_parent>/<repo_name>_sageox_ledger/ For example: /Users/dev/Code/myrepo_sageox_ledger/
func EndpointSlug ¶
EndpointSlug extracts the endpoint slug from a SageOx data path. This is used to verify endpoint consistency between project config and local paths.
The function looks for paths in the data directory structure:
~/.local/share/sageox/<endpoint>/teams/... ~/.local/share/sageox/<endpoint>/ledgers/...
Returns the endpoint slug (e.g., "sageox.ai", "localhost") if found, or empty string if the path is not a SageOx data path or the endpoint cannot be determined.
Examples:
~/.local/share/sageox/sageox.ai/teams/team_abc -> "sageox.ai" ~/.local/share/sageox/localhost/ledgers/xyz123 -> "localhost" /some/other/path -> ""
func EnsureDir ¶
EnsureDir creates a directory and all parent directories if they don't exist. Returns the path for convenience in chained calls. SECURITY: Uses 0700 (owner-only) to prevent other users from listing contents.
func EnsureDirForFile ¶
EnsureDirForFile creates the parent directory for a file path. Returns the original path for convenience. SECURITY: Uses 0700 (owner-only) to prevent other users from listing contents.
func EnsureMigrated ¶
func EnsureMigrated() error
EnsureMigrated runs migration once if needed. This is intended to be called early in application startup. Returns any error from the migration attempt.
func GitCredentialsFile ¶
func GitCredentialsFile() string
GitCredentialsFile returns the path to git server credentials. Fallback storage when keychain is unavailable. Should have 0600 permissions.
func GuidanceCacheDir ¶
func GuidanceCacheDir() string
GuidanceCacheDir returns the directory for cached guidance content. Contains obfuscated guidance cache files.
func HeartbeatCacheDir ¶
HeartbeatCacheDir returns the directory for daemon heartbeat files. Heartbeats are organized by endpoint to support multi-environment usage.
~/.cache/sageox/<endpoint>/heartbeats/
The endpoint is normalized via NormalizeSlug() which strips common prefixes (api., www., app.) and removes port numbers.
IMPORTANT: ep is REQUIRED. Use endpoint.GetForProject(projectRoot) to get the correct endpoint for a project context.
func LedgerNeedsMigration ¶
LedgerNeedsMigration checks if ledger migration from the old structure to the new endpoint-namespaced structure is needed.
Returns true if: - Old structure exists (repo_sageox_ledger/) - New structure doesn't exist (repo_sageox/<endpoint>/ledger/)
The endpoint parameter determines the target namespace. If empty, uses current endpoint.
func LedgersDataDir ¶
LedgersDataDir returns the CANONICAL directory for ledger git checkouts.
Format:
~/.local/share/sageox/<endpoint>/ledgers/<repo_id>/
This centralized location allows all worktrees to share a single ledger and enables daemons to discover ledgers at a known location.
The endpoint is normalized via NormalizeSlug() which strips common prefixes (api., www., app.) and removes port numbers.
IMPORTANT: ep is REQUIRED. Use endpoint.GetForProject(projectRoot) to get the correct endpoint for a project context. Only use endpoint.Get() during login or when no project context exists.
If repoID is empty, returns the base ledgers directory for that endpoint.
func LegacySessionCacheDirs ¶ added in v0.5.0
LegacySessionCacheDirs returns additional session cache directories from older ox versions that used different cache paths. On macOS, older versions used ~/Library/Caches/sageox/sessions/ (native cache) before the switch to XDG (~/.cache/sageox/sessions/). Returns only directories that exist on disk. The repoID parameter scopes to a specific repo; empty returns all legacy bases.
func MachineIDFile ¶
func MachineIDFile() string
MachineIDFile returns the path to the machine identifier file. Contains a unique machine ID used for HMAC operations.
func MigrateLedgerToNewStructure ¶
MigrateLedgerToNewStructure migrates from the old repo_sageox_ledger/ sibling directory to the new repo_sageox/<endpoint>/ledger/ structure.
Steps:
- Verify old path exists
- Create new directory structure (repo_sageox/<endpoint>/)
- Move ledger contents (not rename, to preserve git)
- Create team symlinks (placeholder for future implementation)
- Remove old directory
Returns error if migration fails, nil on success or if no migration needed.
The migration is designed to be safe and reversible:
- Files are copied before the original is removed
- If copy fails partway through, original remains intact
- The old directory is only removed after successful copy
func MigrateTeamContext ¶
MigrateTeamContext moves a team context from sibling directory to ~/.sageox/data/teams/ Note: Legacy paths are always migrated to production endpoint location.
func NewLedgerPath ¶
NewLedgerPath returns the new endpoint-namespaced ledger path.
Format: <project_parent>/<repo_name>_sageox/<endpoint>/ledger/ For production endpoints: <project_parent>/<repo_name>_sageox/ledger/
Examples:
- Production: /Users/dev/Code/myrepo_sageox/ledger/
- Staging: /Users/dev/Code/myrepo_sageox/staging.sageox.ai/ledger/
- Localhost: /Users/dev/Code/myrepo_sageox/localhost/ledger/
IMPORTANT: ep is REQUIRED. Use endpoint.GetForProject(projectRoot) to get the correct endpoint for a project context.
func SageoxDir ¶
func SageoxDir() string
SageoxDir returns the legacy base SageOx directory.
XDG mode (default): Returns empty string (use specific *Dir functions instead) Legacy mode (OX_XDG_DISABLE=1): ~/.sageox
func SessionCacheDir ¶
SessionCacheDir returns the directory for session cache. If repoID is empty, returns the base sessions directory. Otherwise returns the repo-specific session directory.
func StateDir ¶
func StateDir() string
StateDir returns the runtime state directory per XDG Base Directory Specification. https://specifications.freedesktop.org/basedir-spec/latest/
Contains: daemon/ (sockets, PIDs, registry)
XDG mode (default): $XDG_RUNTIME_DIR/sageox (ephemeral, doesn't persist across reboots) Legacy mode (OX_XDG_DISABLE=1): ~/.sageox/state
func TeamContextDir ¶
TeamContextDir returns the CANONICAL directory for a specific team context repository.
~/.sageox/data/<endpoint>/teams/<team_id>/
IMPORTANT: This is the ONLY function that should determine team context paths. NEVER construct team context paths manually (e.g., filepath.Join(projectRoot, ".team-contexts", ...)). Team contexts belong in the user's home directory, NOT in the project working tree.
If ep is empty, uses the current endpoint from environment.
func TeamContextMigrationNeeded ¶
TeamContextMigrationNeeded checks if there are team contexts in sibling directories that should be migrated to ~/.sageox/data/teams/ Note: Legacy paths are always migrated to production endpoint location.
func TeamsDataDir ¶
TeamsDataDir returns the directory containing all team context repositories.
All endpoints use a consistent namespaced structure:
~/.sageox/data/<endpoint>/teams/ e.g. ~/.sageox/data/sageox.ai/teams/ e.g. ~/.sageox/data/staging.sageox.ai/teams/ e.g. ~/.sageox/data/localhost/teams/
The endpoint is normalized via NormalizeSlug() which strips common prefixes (api., www., app.) and removes port numbers.
IMPORTANT: ep is REQUIRED. Use endpoint.GetForProject(projectRoot) to get the correct endpoint for a project context. Only use endpoint.Get() during login or when no project context exists.
func TempDir ¶
func TempDir() string
TempDir returns the base temporary directory for SageOx ephemeral files.
CRITICAL: Uses /tmp/<username>/sageox/ to avoid multi-user permission conflicts.
Why /tmp/<username>/sageox/ (not /tmp/sageox/<username>/)?
- If user A creates /tmp/sageox/ first, user B cannot write into it (owned by A)
- /tmp/<username>/ is always owned by that user, so each user can create sageox/ inside
- Daemon logs are ephemeral - only useful while daemon is running
- OS automatically cleans /tmp (on reboot or via tmpwatch/systemd-tmpfiles)
- Standard pattern (many apps use /tmp/<username>/)
Structure:
/tmp/<username>/sageox/logs/daemon-<workspace_id>.log
Example:
ryan: /tmp/ryan/sageox/logs/daemon-abc123.log ajit: /tmp/ajit/sageox/logs/daemon-def456.log
Note: Heartbeats use cache (bounded size), logs use /tmp (OS cleanup).
func UserConfigFile ¶
func UserConfigFile() string
UserConfigFile returns the path to the user configuration file. Contains user preferences like tips_enabled, telemetry settings, etc.
func VerificationCacheFile ¶
func VerificationCacheFile() string
VerificationCacheFile returns the path to the signature verification cache. Contains HMAC-protected cache entries for guidance signature verification.
Types ¶
type LedgerMigrationStatus ¶
type LedgerMigrationStatus struct {
// NeedsMigration is true if old structure exists and new doesn't
NeedsMigration bool
// LegacyPath is the old ledger path (repo_sageox_ledger/)
LegacyPath string
// NewPath is the new ledger path (repo_sageox/<endpoint>/ledger/)
NewPath string
// LegacyExists is true if the legacy path exists
LegacyExists bool
// NewExists is true if the new path already exists
NewExists bool
}
LedgerMigrationStatus represents the result of checking ledger migration state.
func CheckLedgerMigrationStatus ¶
func CheckLedgerMigrationStatus(projectRoot, ep string) LedgerMigrationStatus
CheckLedgerMigrationStatus returns detailed status about ledger migration. This is useful for displaying migration status to users.
type LegacyPaths ¶
type LegacyPaths struct {
// ConfigDir is the old XDG config location (~/.config/sageox)
ConfigDir string
// GuidanceCache is the old hardcoded guidance cache (~/.sageox/guidance/cache)
GuidanceCache string
// SessionCache is the old XDG cache location (~/.cache/sageox)
SessionCache string
}
LegacyPaths contains the old path locations for migration detection.
func GetLegacyPaths ¶
func GetLegacyPaths() LegacyPaths
GetLegacyPaths returns the legacy path locations for migration checks.
type MigrationResult ¶
type MigrationResult struct {
// ConfigMigrated is true if config was migrated
ConfigMigrated bool
// GuidanceCacheMigrated is true if guidance cache was migrated
GuidanceCacheMigrated bool
// SessionCacheMigrated is true if session cache was migrated
SessionCacheMigrated bool
// Errors contains any errors encountered (migration continues on error)
Errors []error
}
MigrationResult contains the outcome of a migration attempt.
func Migrate ¶
func Migrate() MigrationResult
Migrate performs migration from legacy paths to the new consolidated structure. This is safe to call multiple times - it will only migrate once. Migration is skipped if OX_XDG_ENABLE is set.
Migration moves:
- ~/.config/sageox/* → ~/.sageox/config/
- ~/.sageox/guidance/cache/* → ~/.sageox/cache/guidance/
- ~/.cache/sageox/* → ~/.sageox/cache/
Note: Team contexts in sibling directories are NOT automatically migrated. Use TeamContextMigrationNeeded() and MigrateTeamContext() for those.
type MigrationStatus ¶
type MigrationStatus struct {
// Needed is true if migration should be performed
Needed bool
// LegacyConfigExists is true if ~/.config/sageox exists
LegacyConfigExists bool
// LegacyGuidanceCacheExists is true if ~/.sageox/guidance/cache exists
LegacyGuidanceCacheExists bool
// LegacySessionCacheExists is true if ~/.cache/sageox exists
LegacySessionCacheExists bool
// NewStructureExists is true if ~/.sageox/config exists (new structure)
NewStructureExists bool
}
MigrationStatus represents the result of checking migration state.
func CheckMigrationStatus ¶
func CheckMigrationStatus() MigrationStatus
CheckMigrationStatus checks whether migration from legacy paths is needed. This does NOT perform migration, only checks the current state.
type TeamContextLegacyPath ¶
type TeamContextLegacyPath struct {
TeamID string
LegacyPath string // e.g., /Users/foo/Code/sageox_team_abc123_context
ProjectPath string // the project that referenced it
}
TeamContextLegacyPath represents a team context in the old sibling directory format.