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
- func NewAdminModel(svc *Service) tea.Model
- func NewPlugin(svc *Service) plugin.Plugin
- func OpenAccessHandler() http.Handler
- type Config
- type Conn
- type Entry
- type FilesStore
- type Member
- type Service
- func (s *Service) Access(userID int64) (store.FilesAccess, error)
- func (s *Service) AnonRoot(name string) (root string, ok bool, err error)
- func (s *Service) Kick(id int64) bool
- func (s *Service) Members() ([]Member, error)
- func (s *Service) OpenFor(name string) (*session, store.User, error)
- func (s *Service) PublicList() ([]Entry, error)
- func (s *Service) PublicRemove(name string) error
- func (s *Service) PublicWritable() bool
- func (s *Service) SafeJoin(root, rel string) (string, error)
- func (s *Service) Sessions() []Conn
- func (s *Service) SetPublicWrite(on bool) error
- func (s *Service) SetQuota(userID, bytes int64) error
- func (s *Service) SetRevoked(userID int64, revoked bool) error
- func (s *Service) Subsystem() ssh.SubsystemHandler
- func (s *Service) Usage(u store.User) (Usage, error)
- func (s *Service) Users() ([]store.User, error)
- func (s *Service) WebHandler(cfg WebConfig) http.Handler
- type Usage
- type WebConfig
Constants ¶
const DefaultQuota int64 = 1 << 30 // 1 GiB
DefaultQuota is the per-user workspace quota when none is configured.
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 ¶
NewAdminModel returns the operator management TUI for the SFTP server. Gate the route that launches it on the operator allowlist (auth.IsAdmin).
func OpenAccessHandler ¶
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 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 ¶
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 ¶
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 ¶
Kick force-disconnects a live SFTP connection by id. Returns false if unknown.
func (*Service) Members ¶
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 ¶
OpenFor builds a member session by account name, for the in-BBS browser. It returns the resolved store user too.
func (*Service) PublicList ¶
PublicList lists the top level of the shared public area for moderation.
func (*Service) PublicRemove ¶
PublicRemove deletes a top-level entry from the public area (moderation). name is treated as a single segment; traversal is rejected.
func (*Service) PublicWritable ¶
PublicWritable reports whether members may currently write to the public area.
func (*Service) SafeJoin ¶
SafeJoin exposes the area-confinement join (lexical + symlink-escape guard) for the web host's anonymous read-only surface.
func (*Service) SetPublicWrite ¶
SetPublicWrite toggles members' write access to the shared public area.
func (*Service) SetRevoked ¶
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 ¶
Usage computes a member's owned-storage usage (private /me + public /public) against their quota.
func (*Service) WebHandler ¶
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 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.