Skip to content

System & Local Storage

Vulpis provides a suite of native C++ bindings designed to handle the complexities of cross-platform file systems, encrypted data persistence, and OS-level integration like the clipboard.

These APIs are essential for building production-ready desktop applications, allowing your Lua code to interact securely and reliably with the host operating system.

Secure Storage

Standard file I/O (like Lua's io.open) is sufficient for saving basic app settings. However, it is a major security vulnerability to store sensitive information—such as OAuth tokens, API keys, or user passwords—in plain text.

The Vulpis Secure Storage API solves this by utilizing platform-native encryption. This ensures that the data can only be decrypted and read by your application while running under the current user's operating system account.

Platform Implementation

  • Windows: Utilizes the Data Protection API (DPAPI) via CryptProtectData to encrypt strings using the logged-in user's credentials.
  • Linux/macOS: Writes files using strictly enforced POSIX permissions (0600), ensuring that only the owner of the process can read or write the data.

Methods

FunctionArgumentsDescription
vulpis.saveSecure(filename, data)Encrypts and saves a string to the Vulpis cache directory. Returns a boolean indicating success.
vulpis.loadSecure(filename)Decrypts and returns the stored string. Returns nil if the file is missing or decryption fails.
vulpis.deleteSecure(filename)Permanently removes the encrypted file. Returns a boolean indicating success.

Example: A Secure Authentication Panel

This example demonstrates how to build a login panel that securely saves a session token, allowing the user to remain logged in between app restarts.

lua
local el = require("utils.core.elements")

function App()
    -- Attempt to load the secure token on startup. If it fails, default to "None"
    local saved_token = useState("auth_token", vulpis.loadSecure("session.dat") or "None")

    return el.VBox({
        style = { gap = 10, padding = 20, BGColor = "#222222", borderRadius = 8 },
        children = {
            el.Text("Current Token: " .. saved_token),
            
            el.HBox({
                style = { gap = 10 },
                children = {
                    el.Button({
                        text = "Login (Save Token)",
                        onClick = function()
                            -- Simulate getting a token from a web request
                            local newToken = "usr_9988776655"
                            
                            -- Encrypt and save to disk
                            if vulpis.saveSecure("session.dat", newToken) then
                                setState("auth_token", newToken)
                                print("Token secured.")
                            else
                                print("Critical error: Failed to encrypt token.")
                            end
                        end
                    }),
                    el.Button({
                        text = "Logout (Delete Token)",
                        style = { BGColor = "#EF4444" },
                        onClick = function()
                            -- Remove the encrypted file from the disk
                            if vulpis.deleteSecure("session.dat") then
                                setState("auth_token", "None")
                                print("User logged out.")
                            end
                        end
                    })
                }
            })
        }
    })
end

Path Utilities

Handling file paths manually is notoriously difficult due to the differences between Windows (\) and Unix-like (/) file systems. The Path Utilities module provides a consistent, OS-agnostic way to locate assets and internal directories.

Core Directories Explained

  • Project Root: The absolute path to the directory containing your Vulpis application. The engine intelligently strips out temporary build folders (like /build or /debug) to find your true project source.
  • Assets Directory: Automatically resolves to /assets/.
  • Cache Directory: The OS-specific location for persistent application data.
    • Windows: %LOCALAPPDATA%/Vulpis/Cache.
    • macOS: ~/Library/Caches/Vulpis.
    • Linux: Respects $XDG_CACHE_HOME or defaults to ~/.cache/Vulpis.

Functions

FunctionDescription
vulpis.getProjectRoot()Returns the absolute path to the application root folder.
vulpis.getAssetPath(rel)Appends a relative string (e.g., "data/level.json") to the absolute asset path.
vulpis.getCacheDir()Returns the OS-specific directory for persistent cache files.

Example: Reliable File Parsing

While Vulpis automatically resolves paths for internal UI components like el.Image (meaning you can just pass "images/icon.png" directly to src), you must use vulpis.getAssetPath when using standard Lua file I/O.

This guarantees that io.open will not crash if the application is launched from a different working directory (e.g., via a desktop shortcut).

lua
function LoadLevelData()
    -- Get the bulletproof absolute path regardless of OS
    local abs_path = vulpis.getAssetPath("data/level_1.json")
    
    local file = io.open(abs_path, "r")
    if file then
        local content = file:read("*all")
        file:close()
        
        -- Parse your JSON string here
        print("Level Loaded Successfully!")
        return content
    else
        print("Error: Could not locate " .. abs_path)
        return nil
    end
end

Clipboard Access

Vulpis bridges the gap between your UI and the host operating system's clipboard, allowing users to copy and paste data seamlessly between Vulpis and other desktop applications.

(Note: The core TextInput utility already implements these bindings automatically for standard system shortcuts like Ctrl+C / Ctrl+V.)

Functions

FunctionDescription
vulpis.setClipboardText(text)Places the provided string into the system clipboard.
vulpis.getClipboardText()Returns the current string stored in the system clipboard.

Example: Copy and Paste Tools

lua
local el = require("utils.core.elements")

function App()
    local pasted_text = useState("pasted_content", "Nothing pasted yet.")

    return el.VBox({
        style = { gap = 15, padding = 20 },
        children = {
            el.Button({
                text = "Copy Invite Link",
                onClick = function()
                    vulpis.setClipboardText("https://myapp.com/invite/vulpis123")
                    print("Copied to clipboard!")
                end
            }),
            
            el.Button({
                text = "Paste from OS",
                onClick = function()
                    local txt = vulpis.getClipboardText()
                    if txt and txt ~= "" then
                        setState("pasted_content", txt)
                    end
                end
            }),
            
            el.Text("Clipboard contains: " .. pasted_text)
        }
    })
end

Cache Management

To maintain high rendering performance, Vulpis aggressively caches baked textures (.vtex) and downloaded web images on both the physical disk and in GPU memory.

The clearCache function is provided as a utility to reset these systems if you need to force-reload assets or free up disk space for the user.

vulpis.clearCache()

When called, this function performs two actions simultaneously:

  1. Disk: Safely deletes and recreates the entire Vulpis cache directory for the current OS.
  2. Memory: Wipes the OpenGL texture registry and deletes texture IDs to prevent stale pointers and memory leaks.

Returns true if the wipe was successful, false otherwise.

Performance Impact

Calling clearCache() will cause a noticeable, temporary performance drop. The engine will need to re-bake local textures (like fonts and images) and re-download any active web images during the next render frame. Do not call this function during normal gameplay or continuous loops.

Example: A "Clear Storage" Settings Button

lua
local el = require("utils.core.elements")

function App()
    local status = useState("cache_status", "Tracking...")

    return el.VBox({
        style = { gap = 10, padding = 20, BGColor = "#333333" },
        children = {
            el.Text("System Maintenance"),
            el.Text(status),
            el.Button({
                text = "Clear All Cached Data",
                style = { BGColor = "#EF4444" },
                onClick = function()
                    if vulpis.clearCache() then
                        setState("cache_status", "Cache successfully cleared!")
                    else
                        setState("cache_status", "Error: Failed to clear cache.")
                    end
                end
            })
        }
    })
end