Files
bitvid/config/instance-config.js
T
thePR0M3TH3ANandClaude Opus 4.8 c5e8c7f9f1 feat(storage): Blossom Phase 1b — keyless multi-server config UI [LOCAL, flag ON] (#30)
Makes Blossom configurable + uploadable end-to-end (local testing).

- Storage pane: a "Blossom (Nostr-native)" provider option (revealed only when the
  flag is on) that swaps the S3 field group (#storageS3Fields) for a keyless
  #storageBlossomSection — a multi-server textarea (one URL per line). No
  access-key/secret/bucket/CORS.
- ProfileStorageController: parseBlossomServers(); save branch persists the server
  list in the PLAINTEXT connection meta ({servers}, no encrypted secret) via the
  per-provider slot; provider-change loads servers from meta WITHOUT an unlock;
  Test does a best-effort reachability ping; visibility branch hides S3 fields +
  CORS helper for Blossom.
- mediaUploader.resolveActiveConnection: Blossom connections resolve from plaintext
  meta with no unlock (uploads authorize via the Nostr signer, not a stored key),
  so credentials = { provider, servers, meta } flow straight into the Phase-1a
  engine.
- instance-config: FEATURE_BLOSSOM_STORAGE = true for LOCAL testing (reset to false
  before pushing).

Tests: blossom-service 7/7 (isAvailable now flag-relative). Lint + build clean.
Blossom UI present in dist.

VERIFY locally: Storage → Provider → Blossom → add a server → Save → upload a
video → it plays from <server>/<sha256>. Watch the UploadModal's own storage
gating (may need a tweak for the no-secret/no-PIN Blossom case).

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-07-09 16:31:34 -04:00

552 lines
23 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// config/instance-config.js
// -----------------------------------------------------------------------------
// bitvid instance configuration
// -----------------------------------------------------------------------------
//
// This file consolidates the values that operators usually customize when they
// deploy their own bitvid instance. Update the exports below to match your
// environment, commit the changes, and redeploy. Leaving this file in the repo
// (instead of hidden in environment variables) makes it easy for future
// maintainers to understand how your instance is configured.
//
// Each setting includes guidance about how bitvid uses the value and what
// adjustments are safe. When in doubt, mirror the structure shown here so other
// contributors can follow along.
/**
* Whether the current deployment should enable developer-centric behavior.
*
* When `true`, bitvid surfaces extra logging, debug helpers, and experimental
* UI affordances intended for development environments. Leave the value
* `false` for production so end users receive the stable experience; use the
* runtime overrides documented in `js/config.js` when you need to toggle dev
* mode without editing this file. Downstream modules read this flag via
* `js/config.js`.
*/
export const IS_DEV_MODE = false;
/**
* Whether development builds should surface extra verbose diagnostics.
*
* Set this to `true` to keep high-volume development warnings (like COUNT
* request failures) visible in the console. Flip it to `false` when you want
* a quieter dev mode without disabling other development helpers entirely.
* Use the runtime override documented in `js/config.js` if you need to toggle
* verbosity without editing this file.
*
* Security: This must be false in production.
*/
export const IS_VERBOSE_DEV_MODE = false;
/**
* Whether the current deployment should operate in lockdown mode.
*
* When `true`, bitvid restricts access to operator-approved functionality and
* enables defensive UI copy intended for incidents or maintenance windows.
* Leave this `false` for normal operation so downstream modules (via
* `js/config.js`) continue exposing the full experience.
*/
export const IS_LOCKDOWN_MODE = false;
/**
* The title displayed in the header when the site is in lockdown mode.
*
* Customize this to match the tone of your maintenance window or incident
* response. Default is "Maintenance Mode".
*/
export const LOCKDOWN_TITLE = "Maintenance Mode";
/**
* The primary message displayed to visitors when the site is in lockdown mode.
*
* This text should explain why the site is unavailable and when users might
* expect it to return. Default is generic maintenance copy.
*/
export const LOCKDOWN_MESSAGE =
"We are currently undergoing maintenance. Please come back later.";
/**
* The primary administrator for this bitvid instance.
*
* bitvid treats this Nostr public key (npub) as the "Super Admin". This user
* cannot be removed from moderator lists, and only they can promote new
* moderators or toggle whitelist-only mode. Replace the string below with the
* npub of the account you want to act as the ultimate authority for your
* deployment.
*/
export const ADMIN_SUPER_NPUB =
"npub15jnttpymeytm80hatjqcvhhqhzrhx6gxp8pq0wn93rhnu8s9h9dsha32lx";
/**
* Canonical URL for the public bitvid site.
*
* Surfaces in admin outreach copy and moderation DMs so that recipients can
* quickly jump back to the primary destination. Update this if your
* deployment relies on a different canonical hostname.
*/
export const BITVID_WEBSITE_URL = "https://bitvid.network/";
/**
* Optional external destination for the blog menu link.
*
* Sidebar navigation routes visitors to this URL when they select "Blog".
* Update the value to point at the long-form publishing platform you
* maintain for release notes or community updates. Leave empty (null or an
* empty string) to hide the Blog link entirely.
*/
export const BLOG_URL =
"https://habla.news/p/npub13yarr7j6vjqjjkahd63dmr27curypehx45ucue286ac7sft27y0srnpmpe";
/**
* Optional external destination for the community menu link.
*
* Configure this with a URL to your preferred community hub (e.g., Discord,
* Telegram, Flotilla). When unset (null or an empty string) the Community link
* is omitted from the sidebar dropup menu.
*/
export const COMMUNITY_URL = "https://groups.nip29.com/?relay=wss://relay.groups.nip29.com&groupId=d92ef5";
/**
* Optional external destination for the Nostr menu link.
*
* Point this at the profile or relay hub you want visitors to land on when
* they tap "Nostr" in the sidebar dropup. Leave empty (null or an empty
* string) to hide the link entirely.
*/
export const NOSTR_URL =
"https://nostrudel.ninja/u/npub13yarr7j6vjqjjkahd63dmr27curypehx45ucue286ac7sft27y0srnpmpe";
/**
* Optional external destination for the GitHub menu link.
*
* Set this to the repository you want surfaced to operators and contributors.
* Leave empty (null or an empty string) to remove the GitHub entry from the
* sidebar dropup menu.
*/
export const GITHUB_URL = "https://github.com/PR0M3TH3AN/bitvid";
/**
* Optional external destination for the Beta menu link.
*
* Deployments that maintain a staging or beta environment can direct users to
* that surface here. Leave empty (null or an empty string) if there is no
* public beta to advertise.
*/
export const BETA_URL = "https://beta.bitvid.network/";
/**
* Optional external destination for the DNS menu link.
*
* Operators can expose their canonical DNS landing page here. Leave empty
* (null or an empty string) to hide the DNS entry from the sidebar.
*/
export const DNS_URL = "https://bitvid.network/";
/**
* Optional external destination for the Tip Jar menu link.
*
* Configure this with the Lightning tipping experience you want to surface in
* the sidebar dropup. When left empty (null or an empty string) the Tip Jar
* entry is removed entirely so operators that do not collect donations are not
* forced to show the control.
*/
export const TIP_JAR_URL =
"https://tipjar.bitvid.network/?n=npub13yarr7j6vjqjjkahd63dmr27curypehx45ucue286ac7sft27y0srnpmpe";
/**
* Default image included in automated moderation DMs.
*
* bitvid embeds this media asset at the top of notification messages so the
* payload renders with a recognizable preview. Provide a fully qualified URL
* that points to a hosted image accessible by the intended recipients.
*/
export const ADMIN_DM_IMAGE_URL =
"https://beta.bitvid.network/assets/jpg/video-thumbnail-fallback.jpg";
/**
* Maximum satoshi value allowed when storing the default zap amount.
*
* Wallet settings clamp user input to this value before persistence to guard
* against accidental overpayment. Tune the ceiling to match your instance's
* risk tolerance.
*/
export const MAX_WALLET_DEFAULT_ZAP = 100000000;
/**
* Percentage of every Lightning payment the platform retains as a fee.
*
* Accepts numbers between 0 and 100 (inclusive). Decimals are supported when
* you want to keep a fractional cut—e.g., `2.5` represents a 2.5% fee. The
* default of `0` disables the fee so creators receive the full payment.
*/
export const PLATFORM_FEE_PERCENT = 30;
/**
* Lightning address to fall back to when authors omit their own `lud16`.
*
* Supply a string like `"[email protected]"` to force a deployment-wide
* fallback Lightning address. When `PLATFORM_FEE_PERCENT` is greater than 0,
* this override also acts as the Lightning target for the platform’s split.
* Leave the value as `null` to respect the creator’s metadata and rely on the
* Super Admin profile publishing a `lud16` so bitvid still knows where to route
* fees when they are enabled.
*/
// coinos: Nostr-native, publishes NIP-57 zap receipts, reliable routing,
// 1-sat minimum. Replaced [email protected], whose LNURL was flaky and never
// published receipts, causing platform-share pay timeouts + missing zap tallies.
export const PLATFORM_LUD16_OVERRIDE = "[email protected]";
/**
* Optional list of relays to seed new sessions with instead of the defaults.
*
* Provide WSS URLs (e.g., `"wss://relay.example.com"`). Leave the array empty
* to keep bitvid’s bundled defaults. Operators that need a custom bootstrap set
* should list the relays in priority order; entries later in the list are used
* as fallbacks when earlier relays fail.
*/
export const DEFAULT_RELAY_URLS_OVERRIDE = Object.freeze([
// "wss://relay.example.com",
]);
/**
* Storage key used to persist whitelist-only mode in the browser.
*
* You usually do not need to change this, but the export lives here so that all
* whitelist-related knobs are grouped together. If you do change the key, make
* sure to migrate any previously stored values in localStorage.
*/
export const ADMIN_WHITELIST_MODE_STORAGE_KEY = "bitvid_admin_whitelist_mode";
/**
* Whether whitelist-only mode should be enabled the first time an operator
* loads the admin dashboard.
*
* Set this to `true` if you want bitvid to start with whitelist-only access and
* require an explicit opt-out. Set to `false` to allow all creators by default.
* Operators can still toggle the mode at runtime; this value only controls the
* default state when no preference has been stored in localStorage yet.
*/
export const DEFAULT_WHITELIST_MODE_ENABLED = true;
/**
* Whether the public client should display content flagged as NSFW.
*
* When set to `false`, bitvid filters out any video notes marked with the
* `isNsfw` flag so they never appear in feeds or video lists. Toggle to `true`
* only if your deployment allows NSFW media to surface publicly.
*/
export const ALLOW_NSFW_CONTENT = false;
/**
* Whether the client ingests NIP-71 video events published by *other* Nostr
* apps (kinds 21/22 and addressable 34235/34236) and surfaces them in bitvid's
* feeds.
*
* Defaults to `true` so bitvid discovers videos from the wider Nostr ecosystem
* (Nostube, Flotilla, etc.). bitvid's own outbound mirrors are skipped to avoid
* duplicate listings, and ingested NSFW content still respects
* `ALLOW_NSFW_CONTENT` plus the standard trust/blacklist/mute gating.
*
* Set to `false` to disable all inbound NIP-71 ingest with a single switch if it
* ever causes problems (spam, low-quality content, performance).
*/
export const FEATURE_NIP71_INGEST = true;
/**
* Whether this deployment enables the Audio / Music / Podcast experience
* (dedicated Audio player, sidebar tab, and the channel / "My Content" Audio
* tabs — see docs/audio-integration-plan.md and TODO #60).
*
* Defaults to `false` — "off = no trace": with it off, nothing audio renders and
* audio-only notes stay filtered out of the video feeds (current behavior). Flip
* to `true` to light up the Audio surfaces. Watch/ingest-first; a separate
* publish flag will gate uploading audio later.
*/
export const FEATURE_AUDIO_INGEST = false;
/**
* Whether this deployment shows the "Connect wallet" button that uses Alby's
* Bitcoin Connect for a smooth NWC connect flow (docs/bitcoin-connect-plan.md,
* TODO #61). When on, the Wallet Connect pane offers Bitcoin Connect alongside
* the manual URI field (which stays as the backup). It only lazy-loads the
* vendored Bitcoin Connect bundle when the user actually clicks Connect.
*
* SHIPPING DEFAULT should be `false` ("off = no trace"). Currently `true` for
* local testing only — reset to `false` before pushing/shipping until verified.
*/
export const FEATURE_BITCOIN_CONNECT = true;
/**
* Whether this deployment offers Blossom (nostr-native blob storage) as a
* first-class upload provider alongside Cloudflare R2 / generic S3 / Backblaze B2
* (docs/blossom-plan.md, TODO #30). Blossom uploads authorize with the user's
* existing nostr signer (kind-24242 auth events) instead of S3 access keys, and
* mirror to multiple servers for resilience.
*
* Defaults to `false` ("off = no trace"): with it off the Storage pane shows only
* R2 / S3 / B2 as today and the vendored Blossom SDK is never imported. Flip to
* `true` to expose the Blossom provider.
*
* Currently `true` for LOCAL testing of Phase 1b — reset to `false` (shipping
* default) before pushing/promoting until the upload flow is verified.
*/
export const FEATURE_BLOSSOM_STORAGE = true;
/**
* Card liveness visibility policy — what a non-owner video card does while its
* CDN/WebTorrent liveness probes are still running:
* - "show-pending" : show the card immediately, hide only if every source is
* confirmed dead (current behavior; can briefly flash a
* dead foreign card before the probe hides it).
* - "hide-foreign" : foreign/ingested cards stay hidden until a source is
* confirmed playable; bitvid-native/own cards show pending.
* - "hide-all" : every non-owner card stays hidden until confirmed playable.
* Owner's own uploads are always visible regardless of policy.
*
* Can be flipped live for A/B feel-testing without a rebuild by setting
* `window.__BITVID_CARD_LIVENESS_POLICY__ = "hide-foreign"` in the console (then
* refresh / scroll the grid). Invalid values fall back to "show-pending".
*/
export const CARD_LIVENESS_POLICY = "hide-foreign";
/**
* IntersectionObserver `rootMargin` for the card liveness probes (URL + WebTorrent).
* "0px" probes a card only once it touches the viewport; a positive margin (e.g.
* "600px") probes cards that distance *below* the fold so they're verified before
* the user scrolls to them — makes hide-until-verified feel instant. Probes stay
* viewport-gated + concurrency-capped, so a larger margin only reorders work.
* Override live with `window.__BITVID_LIVENESS_PREFETCH_MARGIN__`.
*/
export const LIVENESS_PROBE_PREFETCH_MARGIN = "600px";
/**
* Emergency fallback accounts for seeding the moderation graph.
*
* Runtime moderation now derives its trust seeds from the Super Admin and the
* active moderator roster, ensuring their reports shape anonymous/default
* visitor filters automatically. The array below is kept only as a last-resort
* fallback when the live moderator lists cannot be loaded (for example,
* bootstrapping a brand-new deployment or recovering from relay outages).
* Populate it with the operator keys you want to lean on during that rare
* failure mode; under normal conditions these values are ignored. bitvid will
* freeze and sanitize the npubs at runtime when reading from `js/config.js`.
*/
export const DEFAULT_TRUST_SEED_NPUBS = [
"npub15jnttpymeytm80hatjqcvhhqhzrhx6gxp8pq0wn93rhnu8s9h9dsha32lx",
"npub13yarr7j6vjqjjkahd63dmr27curypehx45ucue286ac7sft27y0srnpmpe",
];
/**
* Baseline trusted-report count that triggers thumbnail blurring.
*
* When a video receives at least this many trusted reports for the active
* category (for example, `nudity`), thumbnails render with the blurred preview
* by default. Align this value with your moderation policy — the upstream
* deployment targets 1 trusted report before blurring.
*/
export const DEFAULT_BLUR_THRESHOLD = 1;
/**
* Baseline trusted-report count that blocks autoplay during browsing.
*
* Videos that meet or exceed this count will not autoplay in feeds unless the
* viewer explicitly opts in. Increase the number for a more permissive stance
* or decrease it if you want autoplay to stop sooner. The upstream deployment
* targets 1 trusted report before blocking autoplay.
*/
export const DEFAULT_AUTOPLAY_BLOCK_THRESHOLD = 1;
/**
* Trusted mute threshold that hides creators globally.
*
* Once this many trusted accounts mute a creator, their videos disappear from
* feeds until an operator intervenes. Keep the value low (1–2) to rapidly
* enforce community standards or raise it to tolerate more reports before
* hiding.
*/
export const DEFAULT_TRUSTED_MUTE_HIDE_THRESHOLD = 20;
/**
* Trusted spam/report threshold that hides videos globally.
*
* When a video's trusted report count reaches this number, bitvid treats it as
* spam or policy violating content and removes it from default feeds. Align
* the value with your moderation policy — 1 trusted report is the upstream
* baseline.
*/
export const DEFAULT_TRUSTED_SPAM_HIDE_THRESHOLD = 1;
/**
* Optional theme accent overrides for light and dark mode.
*
* Operators can provide hex color strings (e.g., `"#2563eb"`) to customize the
* accent tokens without editing the generated CSS bundle. Leave values as
* `null` to keep the upstream defaults defined in `css/tokens.css`. Each theme
* accepts overrides for the base accent color as well as the stronger and
* pressed states used for hover/active presentations.
*/
export const THEME_ACCENT_OVERRIDES = Object.freeze({
light: Object.freeze({
accent: "#540011",
accentStrong: "#fe0032",
accentPressed: "#a90021",
}),
dark: Object.freeze({
accent: "#540011",
accentStrong: "#fe0032",
accentPressed: "#a90021",
}),
});
/**
* Nostr kind used when persisting watch history events.
*
* bitvid standardizes on kind 30079 so that watch events, view logs, and media
* metadata stay in the same family of documents. Operators that want to
* experiment with a separate list kind (for example, a NIP-51 collection) can
* flip this number so long as their relays accept the chosen kind.
*/
export const WATCH_HISTORY_KIND = 30079;
/**
* Identifier applied to the watch-history list when storing it on relays.
*
* This becomes the value of the `d` tag so clients can find the correct list
* without guessing. Customize the slug if you need isolation between multiple
* bitvid deployments that share relays, but keep it stable once clients begin
* syncing history.
*/
export const WATCH_HISTORY_LIST_IDENTIFIER = "watch-history";
/**
* Maximum number of watch-history entries to retain per user.
*
* The roadmap targets a rolling window of 1,500 items so the UI can highlight
* recently played videos without ballooning relay storage. Adjust this cap up
* or down depending on your retention and privacy policies; smaller values
* reduce storage pressure while larger ones surface a deeper backlog.
*/
export const WATCH_HISTORY_MAX_ITEMS = 1500;
/**
* Whether clients should resolve watch-history entries in batches.
*
* When enabled, bitvid fetches video metadata in grouped queries instead of
* issuing one request per entry. Operators with relays that struggle under
* bursty loads can disable batching at the cost of additional round trips.
*/
export const WATCH_HISTORY_BATCH_RESOLVE = true;
/**
* Maximum number of canonical items each batched watch-history response should
* include.
*
* Leave this as `null` (the default) to return the full
* `WATCH_HISTORY_MAX_ITEMS` window, even when batching is enabled. Deployments
* that page through histories in smaller slices can set a positive integer to
* cap the resolver output so pagination and API responses stay aligned.
*/
export const WATCH_HISTORY_BATCH_PAGE_SIZE = 10;
/**
* Maximum size of the JSON payload for each watch-history chunk, measured
* before encryption.
*
* bitvid splits large histories across multiple events to stay within relay
* limits. This cap keeps individual chunks under roughly 60 KB so that
* encrypted payloads remain relay-friendly even after base64 expansion.
* Lower the number if your relays enforce tighter limits.
*/
export const WATCH_HISTORY_PAYLOAD_MAX_BYTES = 60000;
/**
* Number of watch-history events to request when syncing from relays.
*
* Chunked histories can span several events. This fetch limit should exceed
* the maximum number of chunks you expect a client to publish in one snapshot
* so the UI can stitch the full list back together.
*/
export const WATCH_HISTORY_FETCH_EVENT_LIMIT = 64;
/**
* How long clients should cache watch-history snapshots in localStorage.
*
* A 24-hour window keeps recently played videos available across reloads
* without requiring a fresh relay sync on every visit. Increase the value for
* longer-lived caches or decrease it if your deployment needs tighter
* retention guarantees.
*/
export const WATCH_HISTORY_CACHE_TTL_MS = 24 * 60 * 60 * 1000;
/**
* How long the view counter should treat repeat plays as duplicates.
*
* bitvid de-duplicates view events that occur within this rolling window so
* quick refreshes or stalled replays do not inflate the totals. The default of
* 24 hours mirrors common analytics tooling, but you can tighten or relax the
* window depending on how aggressively you want to filter repeat traffic.
*/
export const VIEW_COUNT_DEDUPE_WINDOW_SECONDS = 86_400;
/**
* How far back the view counter should hydrate historical events during
* backfills.
*
* When a new analytics worker starts up, it can walk older Nostr events to
* reconstruct totals. Limiting the backfill horizon keeps catch-up jobs
* bounded—90 days covers recent trends without hammering relays for year-old
* history. Increase the number if you need deeper analytics or shrink it for
* lighter start-up workloads.
*/
export const VIEW_COUNT_BACKFILL_MAX_DAYS = 90;
/**
* How long clients can trust cached view totals before re-fetching.
*
* Cached results smooth out traffic spikes and reduce relay load. Five minutes
* strikes a balance between responsiveness and efficiency; lower the TTL for
* fresher dashboards or raise it if your analytics traffic is heavy.
*/
export const VIEW_COUNT_CACHE_TTL_MS = 5 * 60 * 1000;
/**
* Cooldown window (in seconds) between manual "Rebroadcast" attempts.
*
* Rebroadcasting is an escape hatch for creators to nudge stale relays. A
* short cooldown keeps this safeguard from turning into an accidental DDoS.
* Five minutes provides enough breathing room for relays to catch up while
* still letting operators retry if a publish genuinely failed.
*/
export const ENSURE_PRESENCE_REBROADCAST_COOLDOWN_SECONDS = 5 * 60;
/**
* Preferred playback source strategy.
*
* Options:
* - "url": Try CDN/URL first, fall back to WebTorrent.
* - "torrent": Try WebTorrent first, fall back to CDN/URL.
*/
export const DEFAULT_PLAYBACK_SOURCE = "torrent";
/**
* Timeout (in milliseconds) for the initial playback attempt.
*
* If the preferred source (URL or Torrent) does not reach the "playing" state
* within this window, the player will automatically switch to the fallback
* source. This prevents users from staring at a loading spinner indefinitely
* if the primary source is slow or stalled.
*/
export const DEFAULT_PLAYBACK_START_TIMEOUT = 3000;
/**
* Whether to display privacy warnings when NIP-17 DM relay lists are missing.
*
* When set to `true`, the UI alerts users if their recipient has not published
* a NIP-17 relay list (falling back to default relays), or if DM delivery falls
* back to NIP-04/default relays due to missing configuration.
*
* Set to `false` to suppress these warnings for a cleaner UI, assuming users
* accept the privacy trade-offs of legacy routing.
*/
export const ENABLE_NIP17_RELAY_WARNING = false;