localapp

package
v0.96.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 7, 2026 License: MIT Imports: 17 Imported by: 0

Documentation

Overview

Package localapp is the local state of an app that runs for one OS user on one device, with no server of its own to sign people in: a desktop window (battery/desktop), a phone app serving itself in process (gofastr-mobile), a signed-in service on the user's machine. Open gives such an app its data directory's contents: the SQLite database, the session secret that keeps sessions valid across restarts, and the installation's identity.

The identity exists because owner-scoped CRUD still needs an owner id. The host puts a stable per-installation User into the request context, and the owner extractor that Install sets up reads it back.

The identity is a FALLBACK for an app with no auth battery, never an override of one. battery/auth installs the framework/owner extractor from its package init, so a foreign extractor already in place is a reliable "auth owns identity" signal, and Install reports it. A host that sees it installs no identity at all: auth's "this request is anonymous" is a decision, and handler.GetUser cannot tell a stored nil from no decision (the 2026-09-04 desktop finding pinned in battery/desktop/localuser_security_test.go).

Index

Constants

View Source
const (
	DBFile     = "app.db"
	SecretFile = "secret"
)

The files Open keeps in the data directory.

View Source
const IdentityFile = "identity"

IdentityFile is the identity file inside the app's data directory.

Variables

This section is empty.

Functions

func Extractor

func Extractor(ctx context.Context) (any, bool)

Extractor reads the owner id off the context user. It is a named top-level function so IsExtractor can recognize it.

func Install

func Install() (authOwnsIdentity bool)

Install sets Extractor as the process-wide owner extractor when no extractor is set, and reports whether a foreign one (battery/auth's, from its package init) owns identity. Extractor itself, installed earlier by another battery or another App in the same process, is not foreign: every Go test suite builds several apps per process.

func IsExtractor

func IsExtractor(fn owner.Extractor) bool

IsExtractor reports whether fn is Extractor. Func values are not comparable; their code pointers are, and a top-level function has exactly one.

func LoadOrMintSecret

func LoadOrMintSecret(dir string) (string, error)

LoadOrMintSecret returns the secret stored in dir/secret, minting 32 bytes of crypto/rand (base64url) and persisting them 0600 on first run. Two processes racing the first boot converge on one secret: the file is created exclusively, and the loser reads the winner's.

func OpenDB

func OpenDB(dir string) (*sql.DB, error)

OpenDB opens (creating if needed) the SQLite database dir/app.db. The file is created owner-only before SQLite opens it, so it never exists with the process umask's mode.

func Valid

func Valid(id string) bool

Valid reports whether id matches the grammar LoadOrMint mints with. A value read back off disk and used as a security principal is re-validated: it is what every owner-scoped query compares against, so a trailing newline or a truncated file would silently become a different owner and hide every row the app had written.

Types

type Data

type Data struct {
	// Dir is the absolute data directory.
	Dir string
	// DB is app.db, opened through sqlite/stdlib. Pass it to
	// framework.WithDB. The caller closes it, or calls Close.
	DB *sql.DB
	// Secret keeps sessions valid across restarts. Pass it to
	// framework.WithSecret.
	Secret string
	// User is the installation's identity, the owner of every row the
	// app writes for its one person.
	User *User
}

Data is what a local app's data directory holds, opened.

func Open

func Open(dir string) (*Data, error)

Open opens the data directory dir, creating it owner-only (0700) when it is missing, and returns its database, secret and identity, minting the secret and the identity on first run. dir must be absolute: it is the directory the platform gives the app (Application Support, an Android app's files dir), never one resolved from the working directory. An existing directory keeps its mode: Open never tightens a directory it did not create.

func (*Data) Close

func (d *Data) Close() error

Close closes the database.

type User

type User struct {
	// contains filtered or unexported fields
}

User is the context user value. Its GetID/GetEmail/GetRoles method set satisfies battery/auth's User interface structurally, without importing battery/auth.

func ForID

func ForID(id string) *User

ForID returns the User for id, for tests and for a battery that already holds a validated id.

func LoadOrMint

func LoadOrMint(dir string) (*User, error)

LoadOrMint returns the identity stored under dir, minting one (16 bytes of crypto/rand, hex) and persisting it 0600 on first run.

func (*User) GetEmail

func (u *User) GetEmail() string

func (*User) GetID

func (u *User) GetID() string

func (*User) GetRoles

func (u *User) GetRoles() []string

GetRoles is ["admin"]: the one person who can reach a single-user app is its owner. Install bounds WHEN this identity is handed out.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL