files

package
v0.1.2 Latest Latest
Warning

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

Go to latest
Published: Oct 1, 2026 License: MIT Imports: 26 Imported by: 0

Documentation

Overview

Package files implements member file storage for AgentBBS, reachable over SFTP with the member's existing SSH login key (docs/files.md). It is wired as an "sftp" subsystem on the shared wish server (port 22), so a member runs

sftp files@bbs.profullstack.com

and lands in a virtual filesystem with two areas:

/me      — their private, per-user workspace (quota-limited)
/public  — a single shared file area (old-school BBS file area); world-read,
           members-only write by default, operator-moderated.

Identity is the SSH public key (one key = one account, like the rest of the BBS); the SFTP username is conventional ("files") and ignored. The server is a fully virtual Go SFTP server (github.com/pkg/sftp) — there are no OS users.

Security: every path the client supplies is resolved through resolve() in fs.go, which confines it to its area root (no traversal, no symlink escape); see the path-traversal tests. Per §9.2 the operator can inspect and act on hosted files (the management TUI), and per §9.3 (amended) the only sharing surface is the single public area — there is no workspace-to-workspace transfer.

Index

Constants

View Source
const DefaultQuota int64 = 1 << 30 // 1 GiB

DefaultQuota is the per-user workspace quota when none is configured.

View Source
const OpenAccessPath = "/.well-known/openaccess.json"

OpenAccessPath is the well-known location of the OpenAccess descriptor (https://logicsrc.com/openaccess). Hubs such as openaccess.logicsrc.com fetch it to list the BBS and to link accounts with OAuth 2.1 + PKCE.

Variables

This section is empty.

Functions

func NewAdminModel

func NewAdminModel(svc *Service) tea.Model

NewAdminModel returns the operator management TUI for the SFTP server. Gate the route that launches it on the operator allowlist (auth.IsAdmin).

func NewPlugin

func NewPlugin(svc *Service) plugin.Plugin

NewPlugin returns the in-BBS file browser plugin bound to svc.

func OpenAccessHandler

func OpenAccessHandler() http.Handler

OpenAccessHandler serves the embedded OpenAccess descriptor as JSON with a short public cache. It needs no session: hubs fetch it anonymously.

Types

type Config

type Config struct {
	// Root is the storage root, e.g. <dataDir>/files. The service owns
	// <Root>/users/<name> (private) and <Root>/public (shared).
	Root string
	// DefaultQuota is the per-user workspace quota in bytes (0 → DefaultQuota).
	DefaultQuota int64
}

Config configures the Files service.

type Conn

type Conn struct {
	ID      int64
	User    string
	Key     string // SSH key fingerprint
	Remote  string
	Started time.Time
	RX      int64 // bytes received from the client (uploads)
	TX      int64 // bytes sent to the client (downloads)
}

Conn is a snapshot of a live SFTP connection, as shown by the management TUI.

type Entry

type Entry struct {
	Name    string
	IsDir   bool
	Size    int64
	ModTime time.Time
}

Entry is a directory entry for the in-BBS browser.

type FilesStore

type FilesStore interface {
	UserByFingerprint(fp string) (store.User, bool, error)
	UserByName(name string) (store.User, bool, error)
	ListUsers(limit int) ([]store.User, error)
	FilesAccess(userID int64) (store.FilesAccess, error)
	SetFilesQuota(userID, bytes int64) error
	SetFilesRevoked(userID int64, revoked bool) error
	FilesSetting(key string) (string, bool, error)
	SetFilesSetting(key, value string) error
}

FilesStore is the slice of the store the Files service needs.

type Member

type Member struct {
	Name        string
	PublicBytes int64
}

Member is a member listed in the anonymous ~user directory at the root of the web file host. PublicBytes is the size of their ~/public folder.

type Service

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

Service is the shared Files engine: one instance backs the SFTP subsystem, the in-BBS browser, and the operator management TUI.

func New

func New(st FilesStore, cfg Config) (*Service, error)

New builds a Files service and ensures the storage layout exists.

func (*Service) Access

func (s *Service) Access(userID int64) (store.FilesAccess, error)

Access returns a user's SFTP access record (quota override + revoked flag).

func (*Service) AnonRoot

func (s *Service) AnonRoot(name string) (root string, ok bool, err error)

AnonRoot resolves the on-disk root for an anonymous, read-only browse target: a member's own public area /public (name = the ~handle), served at ~<name>/public. ok is false when the named member does not exist or is banned. The returned root is a confinement boundary — callers must safeJoin onto it, and it never exposes the member's private /me.

func (*Service) Kick

func (s *Service) Kick(id int64) bool

Kick force-disconnects a live SFTP connection by id. Returns false if unknown.

func (*Service) Members

func (s *Service) Members() ([]Member, error)

Members lists every (non-banned) account, sorted by name — the source for the anonymous ~user directory at the root of the web file host. Every member is listed (it is a real directory), each with a link to their site and to their public files; PublicBytes shows how much they have published.

func (*Service) OpenFor

func (s *Service) OpenFor(name string) (*session, store.User, error)

OpenFor builds a member session by account name, for the in-BBS browser. It returns the resolved store user too.

func (*Service) PublicList

func (s *Service) PublicList() ([]Entry, error)

PublicList lists the top level of the shared public area for moderation.

func (*Service) PublicRemove

func (s *Service) PublicRemove(name string) error

PublicRemove deletes a top-level entry from the public area (moderation). name is treated as a single segment; traversal is rejected.

func (*Service) PublicWritable

func (s *Service) PublicWritable() bool

PublicWritable reports whether members may currently write to the public area.

func (*Service) SafeJoin

func (s *Service) SafeJoin(root, rel string) (string, error)

SafeJoin exposes the area-confinement join (lexical + symlink-escape guard) for the web host's anonymous read-only surface.

func (*Service) Sessions

func (s *Service) Sessions() []Conn

Sessions returns a snapshot of live SFTP connections, newest first.

func (*Service) SetPublicWrite

func (s *Service) SetPublicWrite(on bool) error

SetPublicWrite toggles members' write access to the shared public area.

func (*Service) SetQuota

func (s *Service) SetQuota(userID, bytes int64) error

SetQuota sets a per-user quota override (bytes; 0 = server default).

func (*Service) SetRevoked

func (s *Service) SetRevoked(userID int64, revoked bool) error

SetRevoked revokes/restores a user's SFTP access (BBS login is unaffected).

func (*Service) Subsystem

func (s *Service) Subsystem() ssh.SubsystemHandler

Subsystem returns the wish/ssh "sftp" subsystem handler. Wire it with

wish.WithSubsystem("sftp", svc.Subsystem())

so members reach their files over the existing :22 listener. Identity is the connecting SSH key (the username is ignored); access is refused for non-members, banned accounts, and members whose SFTP access an operator has revoked.

func (*Service) Usage

func (s *Service) Usage(u store.User) (Usage, error)

Usage computes a member's owned-storage usage (private /me + public /public) against their quota.

func (*Service) Users

func (s *Service) Users() ([]store.User, error)

Users lists accounts for the management TUI (newest first).

func (*Service) WebHandler

func (s *Service) WebHandler(cfg WebConfig) http.Handler

WebHandler returns the HTTP handler for the web file browser. Members log in with their webmail username + password and browse the same /me and /public areas as SFTP — no SSH key required, and no home directory is ever exposed.

type Usage

type Usage struct {
	Bytes int64
	Quota int64
}

Usage is a member's workspace usage snapshot.

func (Usage) Free

func (u Usage) Free() int64

Free reports remaining bytes (never negative).

type WebConfig

type WebConfig struct {
	// Authenticate returns the member and true when user+pass are valid. user
	// may be a bare handle ("alice") or a full address ("alice@host").
	Authenticate func(user, pass string) (store.User, bool, error)
	// Title is shown in the page header (e.g. "files.profullstack.com").
	Title string
	// SiteBase is the base URL of the member homepages ("sites"), e.g.
	// "https://bbs.profullstack.com". The ~user directory links each member to
	// SiteBase + "/~" + name. Empty disables the site link.
	SiteBase string
	// SessionTTL defaults to 12h.
	SessionTTL time.Duration
}

WebConfig configures the browser-facing file manager served at files.<host>. Authenticate validates a member's webmail credentials.

Jump to

Keyboard shortcuts

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