Files
pad/internal/server/apps_available.go
T
xarmian 37066f97be feat(apps): app attachment routes: item-scoped upload, metadata, and a streamed download re-admitted before its first byte (TASK-3401, SPEC-6 U6c) (#1776)
* feat(apps): app attachment routes: item-scoped upload, metadata and a streamed download re-admitted before its first byte (TASK-3401, SPEC-6 U6c)

* test(server): an app attachment read refuses a subject whose membership no longer reaches the item (TASK-3401, U6c)

* test(server): U6c tests reach the item re-check, the upload's edit right and the download's type decision (TASK-3401, U6c)

* test(server): a deletion before the first byte reaches the attachment re-check (TASK-3401, U6c)

* fix(apps): U6c codex r1: authorize before storage, a transport write deadline, the credential last in re-admission, and a live companion check in the fence (TASK-3401, U6c)

* fix(apps): U6c codex r2: a truncated upload is 400, a re-admission fault is 500 not 401, HEAD on the download, and unknown variants refused (TASK-3401, U6c)

* fix(apps): U6c codex r3: the upload re-admits the subject before its row (StageUpload/Insert, no store callback), and variant is null on originals (TASK-3401, U6c)

* fix(apps): U6c codex r4: a token-state or settings read failure during admission is a fault (500), not a denial (TASK-3401, U6c)

* fix(apps): U6c codex r5: a credential that dies mid-upload answers 401 invalid_token, a permission change 403 (TASK-3401, U6c)

* fix(apps): U6c codex r6: the served variant is re-checked at the gate, and a settings read fault at the first gate is a 500 (TASK-3401, U6c)

* test(server): a viewer's upload is refused before its body is read (TASK-3401, U6c)

* test(server): account for the app upload's raw body reader in the body-reader census (TASK-3401, U6c)
2026-10-04 15:14:27 -04:00

43 lines
1.5 KiB
Go

package server
import "log/slog"
// Whether installed apps are available on this server (SPEC-6, DOC-3371 §9;
// Dave's day-85 answer Q1). Shared by U8 (the install routes) and U5 (the
// token and introspection endpoints for install clients), which must agree
// exactly, so it lives alone in this file.
// settingAppsEnabled is the platform_settings key an instance admin turns on.
// Self-host default is off: an unset key reads as off.
const settingAppsEnabled = "apps_enabled"
// appsAvailable: Pad Cloud always; self-host only when an admin has enabled
// apps AND the server has an OAuth server with an https issuer, since every
// app credential is an OAuth token. It does not depend on the MCP setting.
// A settings read error counts as off: the capability fails closed.
func (s *Server) appsAvailable() bool {
on, err := s.appsAvailableChecked()
if err != nil {
slog.Warn("apps: reading the apps_enabled setting failed; treating apps as off", "error", err)
return false
}
return on
}
// appsAvailableChecked is appsAvailable with a failure to read the setting
// reported rather than folded into "off", for re-admission, where a fault
// must not read as a revocation (TASK-3401 U6c codex r4).
func (s *Server) appsAvailableChecked() (bool, error) {
if s.cloudMode {
return s.oauthServer != nil, nil
}
if s.oauthServer == nil || !s.mcpEndpoints.HTTPS() {
return false, nil
}
v, err := s.store.GetPlatformSetting(settingAppsEnabled)
if err != nil {
return false, err
}
return v == "true", nil
}