RevoGram documentation
Install, configure and run your social network. A practical guide to your community, its services and the tools behind it.
Download the complete docs ZIP ↓
Offline copy · Guides, search, fonts and design are loaded from this folder.
How RevoGram fits together
A self-hosted Laravel and React social network, with optional services you can enable as your community grows.
| Layer | Purpose |
|---|---|
| Web interface | React and Inertia provide the member website, messaging workspace, creator tools and administration. |
| Application | Laravel manages users, permissions, publishing, wallets, providers and background tasks. |
| Database and storage | Your database stores application records. Local or configured cloud storage holds media and derivatives. |
| Optional services | Redis and Reverb deliver real-time functionality; LiveKit powers configured calls and broadcasts; external providers supply payments, AI and media discovery. |
Start with the server requirements and installation steps. Configure the scheduler, storage permissions and HTTPS before inviting members. Enable optional integrations after their service accounts, workers and connection settings are ready.
Server requirements
Prepare a PHP application server, a database, and compiled frontend assets. Add Redis and LiveKit only when enabling the features that need them.
| Component | Requirement / purpose |
|---|---|
| PHP | Use PHP 8.4 or later for the documented setup. Use the same supported PHP version for PHP-FPM, Composer, scheduled tasks, and workers. Run Composer’s platform check for the complete installed dependency requirements. |
| Composer | Composer 2 to install the locked PHP dependencies. |
| Node.js and npm | A supported current Node.js runtime for frontend builds; Node 24 is recorded in the deployment documentation. The Node runtime is a build requirement unless you separately enable server-side rendering. |
| Database | The example configuration selects MySQL. Provision an empty database and an application-specific database user. The Laravel database configuration also defines MariaDB, SQLite, PostgreSQL, and SQL Server connections; validate the full application on your chosen engine before deployment. |
| Web server | Nginx with PHP-FPM is the documented delivery path. Local protected media uses Nginx internal redirects, and legacy uploads use auth_request. Enable HTTPS for public use. |
| Filesystem | The PHP-FPM/worker account must be able to write storage/ and bootstrap/cache/. Preserve the configured upload root and private media storage. |
| Media processing | GD with WebP support or Imagick enables image derivatives. FFmpeg and FFprobe, executable through PHP’s process support, enable video optimization, thumbnails, and duration inspection. Derivatives are best effort; the original remains usable when a codec is unavailable. |
| Optional Redis | Used by dedicated WebSocket/presence queues, remote link-preview enrichment, and optional performance caching. Performance-cache failures fall back to the existing SQL paths. |
| Optional LiveKit | Required for configured calls and live broadcasts. Ingress, Egress, and recording are additional services for their corresponding live-streaming features. |
cd /path/to/project
php --version
composer check-platform-reqs --no-dev
node --version
npm --version
Project structure
The project separates the PHP application, React theme sources, compiled public files, translations, extensions, and persistent storage.
| Path | What belongs here |
|---|---|
| app/ | Application services, models, policies, controllers, jobs, and console commands. |
| bootstrap/ and config/ | Application bootstrap and configuration. bootstrap/cache/ must be writable by the runtime account. |
| database/migrations/ | Versioned schema changes. DatabaseSeeder intentionally creates no synthetic production content. |
| public/ | The web document root, index.php, static assets, and compiled frontend files. |
| resources/themes/default/ | The default React/TypeScript frontend and its CSS, pages, components, hooks, and manifest. |
| public/build/themes/<theme>/ | Production frontend output. Build every installed theme with npm run build. |
| bootstrap/ssr/themes/<theme>/ | Optional server-rendering bundles, produced by the SSR build. |
| lang/ | Shared application translations. |
| routes/ | Web, API, and console/scheduler route definitions. |
| storage/uploads/ | Default local public-upload destination. The configured local root may override this path. |
| storage/app/private/media/ | Private local originals and derivatives; never expose this directory directly. |
| extensions/ | Extension packages, including the provided example. Keep production extension paths and backups outside files replaced by a core update. |
| deploy/ | Deployment templates for real-time workers, LiveKit, reverse proxies, and extension scheduling. |
| docs/ | Maintained technical notes for modules and integrations. |
| .env.example | A configuration template. Copy it to the private .env file and supply your own values. |
Fresh installation
Install the supplied project into an empty deployment directory and database. Configure the environment before applying migrations.
- Upload or extract the RevoGram project into /path/to/project, keeping public/ as the web document root.
- Create an empty database and an application-specific database user. Keep its credentials private.
- Copy .env.example to .env, then edit APP_URL and the DB_* settings for your installation. Do this before running migrations.
- Install the locked PHP dependencies, generate a unique application key, verify the PHP platform, and apply database migrations.
- Install the locked frontend dependencies and build the installed theme assets. Configure the web server and writable paths, then open the site.
- Register your owner account through /register, complete any configured registration confirmation and onboarding, and promote that specific account from the server console as shown below.
- Log in again, visit /admin, configure the site identity, email delivery, enabled modules, storage, and any optional services. Configure the scheduler before launch.
cd /path/to/project
cp .env.example .env
# Edit .env with your own domain and database credentials before continuing.
APP_NAME="RevoGram"
APP_ENV=production
APP_DEBUG=false
APP_URL=https://YOUR-DOMAIN
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=YOUR_DATABASE
DB_USERNAME=YOUR_DATABASE_USER
DB_PASSWORD=YOUR_DATABASE_PASSWORD
SESSION_DRIVER=database
SESSION_SECURE_COOKIE=true
CACHE_STORE=file
QUEUE_CONNECTION=sync
STORAGE_DRIVER=local
STORAGE_LOCAL_PATH=storage/uploads
# Keep optional services disabled until configured.
REALTIME_ENABLED=false
LIVEKIT_ENABLED=false
PERFORMANCE_CACHE_ENABLED=false
cd /path/to/project
composer install --no-dev --prefer-dist --optimize-autoloader
php artisan key:generate
composer check-platform-reqs --no-dev
php artisan migrate --force
npm ci
npm run build
php artisan optimize:clear
The repository also includes composer setup, which runs installation and the build in sequence. The explicit sequence above lets you enter production database settings before migration. Create the first administrator by promoting your registered owner account as shown below.
cd /path/to/project
php artisan tinker
$user = App\Models\User::where('email', 'admin@YOUR-DOMAIN')->firstOrFail();
App\Models\User::changeRole($user->id, App\Models\User::ROLE_ADMIN);
exit;
Web server & protected media
Serve the public directory and let Laravel authorize protected media before Nginx transfers the file.
The supplied nginx.conf and deploy/realtime/nginx.conf are deployment templates with installation-specific paths and ports. Adapt them to your own server. The normalized example below assumes root /path/to/project/public and a PHP-FPM socket chosen for your installed PHP version.
root /path/to/project/public;
index index.php;
client_max_body_size 512M;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location = /index.php {
include fastcgi_params;
fastcgi_pass unix:/run/php/YOUR-PHP-FPM.sock;
fastcgi_param SCRIPT_FILENAME $document_root/index.php;
}
# Laravel uses X-Accel-Redirect after access checks.
location ^~ /_private_media/ {
internal;
alias /path/to/project/storage/app/private/media/;
add_header Cache-Control "private, no-store" always;
add_header X-Content-Type-Options "nosniff" always;
}
location ^~ /_legacy_media/ {
internal;
alias /path/to/project/storage/uploads/;
add_header Cache-Control "private, no-store" always;
add_header X-Content-Type-Options "nosniff" always;
}
# Authorization subrequest for historical /uploads URLs.
location = /_media_legacy_auth {
internal;
include fastcgi_params;
fastcgi_pass unix:/run/php/YOUR-PHP-FPM.sock;
fastcgi_pass_request_body off;
fastcgi_param CONTENT_LENGTH "";
fastcgi_param SCRIPT_FILENAME $document_root/index.php;
fastcgi_param SCRIPT_NAME /index.php;
fastcgi_param REQUEST_URI /api/v1/media/legacy-authorize;
fastcgi_param REQUEST_METHOD GET;
fastcgi_param QUERY_STRING "";
fastcgi_param HTTP_X_ORIGINAL_URI $request_uri;
}
# Branding is public; other legacy uploads retain authorization.
location ^~ /uploads/branding/ {
alias /path/to/project/storage/uploads/branding/;
add_header X-Content-Type-Options "nosniff" always;
}
location ^~ /uploads/ {
auth_request /_media_legacy_auth;
alias /path/to/project/storage/uploads/;
add_header Cache-Control "private, no-store" always;
add_header X-Content-Type-Options "nosniff" always;
}
location ~ /\.(?!well-known(?:/|$)) { return 404; }
location ~ \.php$ { return 404; }
- Enable the Nginx auth_request module used by /uploads/ authorization. Configure the correct PHP-FPM socket and ensure the PHP-FPM user can read authorized media files.
- Grant the runtime account write access to storage/ and bootstrap/cache/ without granting broad write access to the whole application.
- Align Nginx client_max_body_size, PHP upload_max_filesize and post_max_size, and the application upload limits for your chosen upload policy.
- After configuration, validate Nginx before reloading it. Check the landing page, login, public assets, an authorized private image/video, and rejection of a direct internal-media URL.
- The PHP development server does not implement Nginx X-Accel-Redirect or auth_request. Use the configured Nginx path to validate protected media delivery.
nginx -t
# Reload Nginx using your server’s service manager after validation succeeds.
Scheduler & queue workers
Run scheduled maintenance once per minute and supervise the dedicated workers for the optional services you enable.
The application registers due work in routes/console.php. A cron entry invoking schedule:run every minute lets Laravel run the registered frequencies, including the LiveKit reconciliation task scheduled every thirty seconds. Run it as the same account that owns the application’s runtime files.
* * * * * cd /path/to/project && /path/to/php artisan schedule:run >> /path/to/project/storage/logs/scheduler.log 2>&1
| Scheduled command | Frequency / purpose |
|---|---|
| stories:expire | Every minute; marks expired stories. |
| posts:publish-birthdays | Every fifteen minutes; birthday publications. |
| blogs:publish-scheduled | Every minute; releases scheduled blog posts. |
| subscriptions:renew | Hourly; renews due creator subscriptions or expires access. |
| accounts:finalize-deletions | Hourly; permanently processes due account-deletion requests after their grace period. |
| livekit:reconcile | Every thirty seconds; recovers session/recording state when delivery or connections are interrupted. |
| profile-moderation:deliver-notices | Every minute; retries pending required profile moderation notices. |
| feeds:prune | Daily; removes expired personalized-feed history. |
| music:cleanup-uploads | Daily; cleans abandoned temporary music uploads. |
| Process | When needed |
|---|---|
| Default queue worker | The example uses QUEUE_CONNECTION=sync, so default jobs execute inline. If you select an asynchronous default queue, supervise a worker for that configured connection. |
| WebSocket server | For REALTIME_ENABLED deployments, supervise php artisan reverb:start --host=127.0.0.1 --port=8087 behind the documented reverse proxy. |
| Broadcast worker | Consumes the explicit realtime connection / realtime queue. It is separate from the default queue and media processing. |
| Link-preview worker | Consumes the explicit realtime connection / link-previews queue. Remote message/comment preview enrichment waits if this worker is stopped; the message itself can remain delivered. |
| Presence sweep | The supplied revogram-presence-prune.timer runs realtime:prune-presence once per minute. It is separate from the main scheduler. |
| Extension tasks | Run extension:schedule-run for enabled extensions as documented by the extension system. deploy/extensions/schedule.sh provides a locked runner; adapt its PHP path. |
php artisan queue:work realtime --queue=realtime --sleep=1 --tries=3 --backoff=1,5,15 --timeout=60 --memory=192 --max-time=3600 --no-interaction
php artisan queue:work realtime --queue=link-previews --sleep=1 --tries=2 --backoff=5 --timeout=45 --memory=192 --max-time=3600 --no-interaction
cd /path/to/project
php artisan schedule:list
php artisan queue:failed
Local and cloud storage
Choose local storage, Amazon S3, Backblaze B2, Google Cloud Storage or Azure Blob Storage for new uploads.
Open Admin → Settings → Uploads at /admin/settings?section=uploads and find Storage provider. The separate Storage Runtime view reports the active driver and CDN state; provider credentials are configured under Uploads.
| Provider | Configuration |
|---|---|
| Local | A writable local upload root owned by the application worker. |
| Amazon S3 | Bucket, region, optional custom endpoint, access key, secret and optional path-style addressing; supports compatible services. |
| Backblaze B2 | Bucket, S3-compatible region/endpoint, key ID and application key. |
| Google Cloud Storage | Bucket, interoperability endpoint and Google HMAC access key/secret; this adapter uses HMAC interoperability. |
| Azure Blob Storage | Account, account key, container and optional endpoint. |
- Create the bucket/container in the provider account and configure access for the application’s actual media visibility requirements.
- Enter the selected provider’s fields in Uploads. Use the Storage account reference when distinct accounts share the same compatible endpoint and bucket name.
- Save the provider settings and confirm the intended active upload destination. Configure the CDN only when its origin and privacy policy match the media being delivered.
- Upload a small image and video, verify their derivatives, and check playback after a page refresh. Test private content with an unauthorized account.
- Keep older storage accounts and local roots available while existing media still references them.
- Do not assume a failed cloud upload silently succeeds on local storage. Managed music uploads deliberately retain the captured destination and report failures.
- When rotating credentials for an older managed destination, select that exact destination/account reference, save replacement credentials and restore your preferred upload default.
- A CDN-only historical bucket still needs an accessible delivery route after switching the current provider.
Media delivery and location tracking
Preserve correct delivery and cleanup when your installation contains local files and several cloud destinations.
Every original, optimized file, thumbnail and preview can have its own registered storage location. The active upload provider is never guessed as the source of an old file. Read paths batch location lookups instead of probing cloud objects during feed rendering.
| Media kind | Delivery and location behavior |
|---|---|
| Public uploads | The destination is captured before processing; new derivatives use the captured destination. |
| Ordinary private uploads | New files and new private derivatives use local private storage, independent of the public upload default. |
| Existing remote private files | Delivered through authorized, short-lived signed URLs. |
| Managed story music | Each original/artwork file retains its exact immutable configuration; private cloud media requires a private origin and appropriate CDN exclusion. |
| Live recordings, exports and gifts | Retain their dedicated recorded storage locations rather than following a later public-default change. |
php artisan media:backfill-locations --json
The audit is read-only. Review unresolved, conflicting and missing entries before choosing to run the same command with --execute. Execution registers proven metadata, upload journals and verified local files; it does not move or delete files or rewrite content references.
- An original’s explicit provider takes priority. A derivative uses its own registered location, then the original’s provider when no per-file evidence exists.
- A legacy path without metadata can resolve locally only when that local file exists. Unknown cloud provenance requires investigation.
- Cleanup compares provider and object key together, preserves shared referenced objects and keeps location evidence when a provider deletion fails.
- Managed private music requires a CDN rule excluding protected/music/. The upload pipeline rejects anonymously exposed private destinations.
- Global upload limits and actual media inspection continue to apply regardless of storage provider. A cloud account does not remove application limits.
WebSockets and real-time updates
Run Laravel Reverb and its dedicated broadcast worker to deliver messages, presence, notifications and community updates over one authenticated WebSocket connection.
The browser uses a public secure WebSocket endpoint. Laravel authorizes private channels with the member’s current session, and the backend publishes signed events through the private Reverb API. The public application key identifies the installation; it is not a channel access credential.
| Component | Purpose |
|---|---|
| Reverb | Maintains browser WebSocket connections. |
| Broadcast worker | Consumes the dedicated realtime queue separately from slow media jobs. |
| Redis | Stores presence leases and the dedicated real-time queue. |
| Presence cleanup | Expires abandoned connection leases after crashes or abrupt disconnects. |
- Generate distinct REVERB_APP_ID, REVERB_APP_KEY and REVERB_APP_SECRET values and keep the secret in the server environment.
- Configure the Reverb listener and server publishing addresses. Configure REALTIME_HOST, REALTIME_PORT, REALTIME_SCHEME and REALTIME_PATH for the public browser endpoint.
- Set the allowed origin to your actual site hostname. Route the public WebSocket endpoint through your TLS reverse proxy, including Upgrade and Connection headers; keep the publishing API private.
- Run Reverb, the dedicated realtime queue worker and presence cleanup under the application worker identity. Adapt the supplied deploy/realtime service templates to your installation paths and runtime.
- Verify an authenticated subscription, denial for an unauthorized member, rejection of an invalid origin, two-member delivery and reconnect recovery.
REALTIME_ENABLED=true
REALTIME_HOST=community.example.com
REALTIME_PORT=443
REALTIME_SCHEME=https
REALTIME_PATH=/realtime
REVERB_ALLOWED_ORIGINS=community.example.com
REDIS_REALTIME_DB=2
The dedicated worker has a 60-second job timeout; its queue retry_after must be greater than that timeout. Restart long-running Reverb and broadcast workers after deploying changed event or job code. Keep the presence-pruning schedule active.
- A connected socket alone does not prove authorized delivery. Test with two real authenticated accounts.
- If notifications work but media processing is delayed, inspect the media worker independently from the dedicated realtime worker.
- If reconnects fail, check the browser endpoint, TLS proxy path, allowed origins and channel authorization response before changing Redis settings.
Redis performance cache
Use the optional dedicated cache for feed ranking inputs and media metadata while keeping authorization and fresh content checks in the database.
- Open Admin → Settings → Redis cache at /admin/settings?section=redis.
- Choose inherited or custom server settings and configure host, port, ACL username/password, TLS and a dedicated cache database as needed.
- Use the connection test before saving. It tests unsaved values with a short-lived namespaced key and does not change the active connection.
- Set bounded TTLs, enable caching and save. Use a distinct installation prefix and keep this cache separate from realtime queues/presence.
- Verify feed behavior with a cold cache, a warm cache and a temporary Redis outage.
| Cached input | Documented default lifetime |
|---|---|
| Raw post-media metadata | 300 seconds |
| Per-member affinity and preferred formats | 60 seconds |
| Reaction, comment and repost ranking statistics | 20 seconds |
Post eligibility, blocks, group/page access, subscriptions, premium unlocks, protected-media authorization, new-post selection and impression writes remain current SQL operations. Full personalized feed responses are not cached. Video and image bytes are not copied into Redis.
- The dedicated cache can use inherited Redis settings or REDIS_PERFORMANCE_* overrides. REDIS_PERFORMANCE_DB selects its database even when the inherited Redis URL contains another database number.
- PERFORMANCE_CACHE_ENABLED=false bypasses these caches. PERFORMANCE_CACHE_STORE can select another Laravel cache store.
- Saving configuration rotates a private namespace revision so older cached metadata cannot reappear after server/TTL changes or a disabled period.
- Connection/read timeouts are bounded. The first Redis error disables the optional cache for the remainder of that request and uses the normal SQL path.
- After a cache outage, missed invalidations may leave metadata until its short TTL. Access control is still checked live.
Member quick start
A practical tour for the people joining your RevoGram community.
- Create an account when registration is open, complete the configured confirmation, and follow the onboarding flow. Add a recognizable profile image and basic details; onboarding can be skipped when that option is available.
- Open your profile and /settings/personal-details to refine identity and biography. Review /settings/privacy and /settings/audience before posting publicly.
- Use the feed at / to publish an update, explore content at /explore, and discover people at /people-you-may-know. Visibility depends on the site’s enabled modules and account permissions.
- Visit /groups, /communities, /pages, or /threads to find spaces that fit your interests. Join or accept invitations where required.
- Open /messages for conversations and /notifications for activity alerts. Calls require the administrator’s call service and browser permissions.
- Use /bookmarks for saved content, /history for activity history, and /memories for historical reminders where those modules are enabled.
| Need | Go to |
|---|---|
| Change language, theme or accessibility | Settings preferences and accessibility sections |
| Control messages and online visibility | /settings/messaging |
| Review notification channels and quiet hours | /settings/notifications |
| Block or mute an account | /settings/blocking |
| View wallet or plans | /wallet or /plans |
| Review event tickets | /event-tickets |
| Download account information | /settings/data |
| Get help | /settings/help |
Some pages allow guest browsing, while creation, messaging, private rooms, purchases, and account settings require an authenticated active account. A hidden navigation item may mean the administrator disabled a module; it does not necessarily indicate a broken installation.
Posts, media, blogs & articles
Publish short updates or long-form writing while keeping audience, authoring, and media controls consistent.
The feed is the main publishing surface. Posts and comments use the shared rich-text editor, including formatting and selected member mentions. The application stores readable plain text alongside a validated structured document; ordinary text clients remain supported. Mentions resolve to account identities, so changing a display name does not turn the reference into a different account.
- Create a post from the feed composer. Add text and supported attachments, review the audience and any contextual group or community destination, then submit.
- Use the post’s menu to edit or remove content you own. A moderator’s case actions are separate from the author’s edit controls.
- Open Photos, Videos, or Clips from the main navigation to browse the enabled media collections. Media availability follows the owning record and audience.
- For longer writing, open /blogs and use /blogs/create. Choose Blog or Article, enter the title and body, set category and privacy, and use the available draft, scheduling, or publishing controls. Edit owned publications through /blogs/manage/{id}/edit.
- Admin → Settings → Post controls posting behavior; Uploads controls the global upload switch and limits.
- Admin → Settings → Blogs & Articles can independently allow new blogs and new articles. Disabling one type preserves existing publications and allows editing them without switching to that disabled type.
- Admin → Photo Viewer configures the shared viewer. Opening a photo does not bypass the media owner’s visibility rules.
- Draft and scheduled publications should be checked with a different member account before being treated as publicly discoverable.
Groups, Pages, threads & Shoutbox
Choose a community space for persistent publishing, organized discussion, or room-based conversation.
| Space | Member route | Purpose |
|---|---|---|
| Groups | /groups | Group profiles, memberships, posts and rules |
| Communities | /communities | Community profiles, memberships and posts |
| Pages | /pages | Public publishing identities, followers and invitations |
| Threads | /threads | Categorized discussion topics, replies, likes and following |
| Shoutbox | /shoutbox | Room directory and real-time conversation |
- Enable the relevant family in Admin → Settings → Modules. For Threads or Shoutbox, also open its dedicated settings section.
- For Threads, decide whether members can create topics, set topic/reply length limits, and configure up to 12 categories. Members can search and sort discussions, reply, like, or follow topics.
- For Shoutbox, choose member posting, message length, slow-mode delay, history window, and the desired contextual placements.
- Create a public or private room from Shoutbox. Public rooms require joining before reading or posting; private invitations must be accepted before conversation history is available.
- For a group or community, choose All members or Invited members only for its chat. Parent privacy and approved membership continue to determine eligibility.
Shoutbox supports text, replies, edits, deletion, pinned messages, image/video attachments, configured GIF search, and browser-recorded voice. A send can contain visual attachments, one voice recording, or one GIF. Microphone recording requires HTTPS and a browser with MediaRecorder support. Photos and GIFs open in a standalone gallery without post likes or comments.
- The history-days setting limits visible history; it does not permanently erase older messages.
- Context rooms can appear alongside groups, communities, and marketplace item details. Item-specific marketplace rooms are isolated from one another.
- Removing a room participant and banning them are different actions. A ban prevents room history, attachments, messages, and channel access until lifted.
- Following a discussion collects it in the Following feed; it does not subscribe the member to email or notification fanout.
Marketplace & job workflows
Connect community members with listings and opportunities, with owner tools and private application records.
Marketplace is available at /market when its module is enabled. Members can browse listings, save items, and use seller tools. Individual item pages use /market/{id}. Where contextual Shoutbox is enabled, an eligible signed-in viewer is admitted to that item’s own conversation room; separate listings never share their message history.
- Enable Marketplace and Jobs in Admin → Settings → Modules. Review the global upload configuration before allowing listing images or application documents.
- Open /jobs to discover opportunities. Members can open /jobs/create to publish as themselves or an eligible Page they own.
- Choose job details, location/workplace information, employment type, salary information, application mode, and CV requirement. Use /jobs/{id}/edit for owner changes.
- Review job policies and applications in Admin → Jobs at /admin/jobs. Administrators can require approval and set active listing and CV-size limits.
- Applicants use the job’s internal application form or its configured external/email destination. Owners and administrators review internal applications; applicants can withdraw where the current state permits it.
Only active, approved, unexpired listings from available authors and valid Page identities are publicly available. Owners and administrators may see management states that other members cannot. Applicants cannot apply to their own listing, and bilateral profile blocks prevent normal application interactions.
Internal applications preserve a job snapshot and status history. Applicant contact details, cover letters, profile resumes, and uploaded CVs are private. Authorized owners and administrators can manage applications; a withdrawn application no longer exposes its resume to the employer. Application history can remain accessible when a job is closed or archived.
User plans & memberships
Offer fixed-term or lifetime membership benefits while preserving the terms a member purchased.
Admin → User Plans at /admin/user-plans manages plan offerings. Members browse active offers at /plans. A plan defines its name, presentation, price and currency, term, included benefits, highlights, and Page/group creation allowances.
| Benefit | Behavior |
|---|---|
| Supporter badge | A membership badge separate from identity verification |
| Profile visitor history | Shows visits only from people who choose to share them |
| Featured membership | Adds a featured label and modest people-discovery boost while respecting privacy |
| Page and group allowances | An active membership can increase the free creation allowance; zero means unlimited |
- Review the site currency and free Page/group allowances. Configure the wallet before selling paid plans.
- Create a plan, choose its term and benefits, and make it active when the offer is ready. Draft or archived offers are not offered as new purchases.
- Review the member-facing /plans screen with a regular account. The member confirms the current offer and pays from the wallet for a priced plan.
- Use the administrator membership actions to grant or revoke access when appropriate, recording the reason. Review the membership’s recorded history after changes.
User Plans do not automatically renew. Access begins for the term shown; members choose when to purchase again. A member with current access cannot buy another plan until it ends. The plan and wallet must use the current site currency, and paid purchases require the wallet module. If an offer changes before confirmation, the member must refresh and review it.
Event tickets and organizer settlement
Offer built-in wallet tickets or an external HTTPS ticket link for group and community events while retaining the normal RSVP flow.
- Open Admin → Settings → Events & Ticketing at /admin/settings?section=event-ticketing. Enable the events module, built-in wallet tickets and/or external ticket links.
- Set the maximum tickets per order and maximum ticket price in major wallet currency units. Paid bookings also require the wallet module.
- As an authorized event organizer, open the event’s Tickets panel. Create up to 20 types with prices, capacities, per-person limits, availability and optional sales windows.
- Verify the event’s audience and organizer membership. Buy a test ticket, inspect the independent receipt and check in its entry code against the correct event.
- Review orders and collect eligible proceeds only after the event ends, or 24 hours after its start when it has no end time.
- Buying a ticket does not create an RSVP or silently join the buyer to the host group.
- Each order retains its original title, price, currency and quantity even if the ticket type changes later.
- Inventory and ledger changes are transactional and idempotent. An altered quote, exhausted capacity, limit violation or insufficient balance cannot issue tickets.
- Paid proceeds are held on the confirmed order until settlement so unsettled orders remain fully refundable.
- Before the event starts, a buyer may cancel if no ticket in the order has checked in. The organizer may refund an unsettled order; cancellation refunds all unsettled orders.
- Refunds invalidate entry codes and restore inventory. Settlement and refunds are mutually exclusive; paid settled events cannot then be cancelled through the event cancellation API.
The independent /event-tickets page retains buyer receipts and organizer financial access if new sales are disabled. Financial receipts/refunds can survive a later audience change. Setup and check-in still require active manager membership. External links must use HTTPS and are not fetched by the backend.
Audio and video calls
Enable browser calls with LiveKit media transport, private conversation authorization and member-controlled microphone, camera and screen sharing.
Calls share the configured LiveKit service with live broadcasting. Reverb delivers invitations and call-state updates; LiveKit carries audio, video and screen-share tracks. A call URL contains an application UUID rather than a media access token, and copying the URL does not grant membership.
- Configure LIVEKIT_ENABLED, LIVEKIT_URL, LIVEKIT_API_URL, LIVEKIT_API_KEY and LIVEKIT_API_SECRET on the server. The browser URL must use secure WebSocket signaling.
- Deploy a reachable LiveKit service, reverse proxy and matching signed webhook configuration. Use the supplied deploy/livekit templates only on a new installation without conflicting media services.
- Open Admin → Settings → Audio & Video Calls at /admin/settings?section=calls. Enable calls, choose the participant limit and set whether screen sharing is permitted.
- Check HTTPS camera/microphone permission, popup behavior and media connectivity from at least two networks. Test an audio-only answer as well as a video answer.
| Setting | Behavior |
|---|---|
| calls_enabled | Offers calls when the integration is configured and the member is eligible. |
| calls_max_participants | Total room capacity, including the host; the saved limit applies to new calls. The default is 12. |
| calls_screen_share_enabled | Allows participants to choose a screen, window or browser tab. |
- An incoming invitation requires an explicit Answer click before a call window opens or capture begins. Mobile browsers may open a tab instead of a window.
- Microphone and camera remain each participant’s choice. Another participant cannot remotely enable capture.
- Conversation membership, messaging privacy, call permissions, blocking and account restrictions are enforced by the server.
- Missed-call alerts use notification preferences, quiet hours and the Calls category. Unanswered invitations create missed-call records; answered or declined invitations do not.
Call reconciliation runs through livekit:reconcile on the application schedule. Run only one reconciliation mechanism. The current call feature does not record calls; livestream recording controls do not enable call recording. WebRTC protects media in transit and should not be presented as end-to-end encryption against the media server.
Live broadcasting
Publish camera, microphone or screen-share tracks to LiveKit rooms, with audience-aware viewing, live discussion and optional replays.
The Live Studio sends media directly through LiveKit over WebRTC. Viewers subscribe to the active room. Live broadcasting does not depend on recording and uploading browser chunks; turning recording off keeps the broadcast live.
- Configure the LiveKit server credentials, secure browser signaling URL and server API endpoint.
- Apply the application migrations, run the normal queue worker and configure signed LiveKit callbacks at /api/livekit/webhook on your public HTTPS application origin.
- Ensure ICE or TURN media connectivity is reachable in addition to HTTPS signaling. Keep the LiveKit administration API and Redis private.
- Run the existing Laravel scheduler so livekit:reconcile can repair stale state after missed callbacks. Do not add a second reconciliation cron if the scheduler already performs it.
- Open Live Studio, create a preview, publish intentionally, and verify a second authorized viewer receives video and audio. End the broadcast and verify that viewer media stops.
- A private preview is not a published stream. Verify both the media track and the application’s published state.
- The owner’s studio route /live/studio/{session_uuid} restores that specific session and remains restricted to its owner.
- Viewer counts represent concurrently connected viewing sessions. Hosts, ingress publishers, recorders and joining/disconnected participants are excluded; unavailable counts are not reported as a false zero.
- Live comments and reactions use existing post permissions, blocking, account restrictions and moderation. Media-room access does not bypass those checks.
- Finished streams with a ready recording play in feed/detail cards through the normal replay player. A replay does not join a live room.
Before service changes, check for active calls and broadcasts. Restarting the media server disconnects participants. Run application commands as the same account used by PHP and queue workers to preserve writable cache locks.
Recording and RTMP encoders
Add optional recording, external encoders and outgoing restream destinations through separate LiveKit Egress and Ingress services.
| Capability | Required service and configuration |
|---|---|
| External encoder input | LiveKit Ingress; LIVEKIT_INGRESS_ENABLED and, when needed, LIVEKIT_INGRESS_URL. |
| Replay recording | LiveKit Egress; LIVEKIT_RECORDING_ENABLED plus private recording storage. |
| Outgoing RTMP | LiveKit Egress; LIVEKIT_EGRESS_ENABLED. Each destination uses an independent job. |
- Deploy Ingress and Egress separately from the LiveKit server, sharing the server’s Redis settings and matching API credentials. Size media processing independently from the PHP application.
- For local recordings, mount the same private output directory into Egress and configure the host path and container path with LIVEKIT_RECORDING_LOCAL_PATH and LIVEKIT_RECORDING_EGRESS_PATH.
- Configure efficient authorized local playback with LIVEKIT_RECORDING_INTERNAL_REDIRECT_PREFIX, or configure the recording-specific S3-compatible bucket, region, endpoint and credentials.
- Enable the desired capabilities. Create encoder credentials from the owner’s studio and enter the returned RTMP/RTMPS address and unique stream key in the encoder.
- Test recording start, stop, restart and finalization while the broadcast continues. Verify a completed replay and an outgoing destination independently.
- Stopping recording ends only its Egress job. Starting it again creates a new replay segment.
- Outgoing destinations are independent of recording. The application allows up to three outgoing destinations per session.
- Ingress stream keys and outgoing destination keys are encrypted and omitted from viewer responses. Treat a stream key as a password.
- LIVEKIT_RTMP_ALLOWED_HOSTS can restrict outgoing destination hosts. Validation also rejects unsafe schemes and private/reserved destination addresses.
- Egress room composites use a browser and require the documented container sandbox capability. Budget CPU and memory for each concurrent composite job.
If a replay remains in preparation, inspect Egress health, shared output permissions, expected file paths, signed webhook delivery and the reconciliation schedule. Do not resolve the problem by making the recording directory public.
Wallet payments and settlement
Connect hosted payment gateways for wallet top-ups, then use wallet credit across eligible purchases and donations.
- Open Admin → Settings → Payments at /admin/settings?section=payments. Enable global payments and select a gateway.
- Complete merchant onboarding with the provider. Enter the exact test/sandbox or live credentials for that environment, supported currency and exchange rate.
- Copy the gateway’s displayed webhook URL into the matching merchant dashboard. Use your public HTTPS application URL.
- Save the settings and enable the gateway only after configuration is complete. Blank credential replacement fields preserve existing encrypted values.
- Complete an end-to-end sandbox checkout, confirm authenticated server settlement and verify the wallet ledger before accepting real customer payments.
https://community.example.com/api/v1/payments/webhooks/PROVIDER_ID
The exchange rate is gateway currency units per one unit of the website wallet currency. Matching currencies always use a rate of one; a different currency requires a positive configured rate. Amounts and rates are saved with the payment so later settings changes do not rewrite an in-flight transaction.
- Only an authenticated provider notification or authoritative server-side API verification can settle a payment. A browser success page is not a receipt.
- The application checks the saved reference, amount, currency and provider transaction before crediting. Duplicate requests and webhook retries cannot credit the wallet twice.
- Some hosted returns remain pending until a webhook arrives. Reopen the wallet and reconcile pending/failed payments with the merchant dashboard.
- A gateway disabled after checkout can still complete an existing in-flight payment.
- Refund, dispute and chargeback notifications do not generate a new positive wallet credit.
Provider countries, supported currencies, acquiring capability, taxes, fees and live account permissions depend on the merchant agreement. Keep the sandbox and live webhook registrations, secrets and dashboard environments consistent.
Payment gateway reference
RevoGram includes 12 wallet top-up integrations. Configure each gateway’s own credentials and notification rules rather than reusing another gateway’s keys.
| Gateway / ID | Credentials and setup |
|---|---|
| Stripe / stripe | Secret key and webhook signing secret; test/live mode. Uses hosted Checkout. Register the matching Checkout completion and asynchronous payment notifications. |
| PayPal / paypal | Client ID, client secret and webhook ID from the matching sandbox/live app. Uses Orders capture; payment completion must be verified. |
| Wave / wave | API key, webhook secret and optional request-signing secret. Configure its gateway-to-wallet exchange rate and signed checkout notifications. |
| PayFast / payfast | Merchant ID, merchant key and passphrase; sandbox/live mode. ZAR payment amounts require the configured conversion rate and validated ITN notifications. |
| NOWPayments / nowpayments | API key plus a separate IPN secret; sandbox/live and invoice fiat currency. Only signed finished notifications settle; partial/confirming payments remain pending. |
| Cashfree / cashfree | App ID and secret; sandbox/live, INR and conversion rate. Enable Payment Link notifications. The customer needs a valid phone number in their profile. |
| Razorpay / razorpay | Key ID, key secret and a separate webhook secret; test/live. Register paid, partially_paid, cancelled and expired Payment Link events. Only a fully captured payment settles. |
| Paystack / paystack | Secret key and matching test/live environment. Register charge.success; a valid customer email is required and the server verifies the transaction. |
| Paysera / paysera | Project ID and signing password; test/live and currency/rate. Checkout Classic callbacks settle only received-payment status 1 and return the required OK acknowledgment. |
| 2Checkout / twocheckout | Merchant code, IPN secret key and buy-link secret word. Configure ConvertPlus signed Buy Links and SHA2-256 or SHA3-256 IPN; only COMPLETE settles. |
| Authorize.net / authorize_net | API login ID, transaction key and 128-character hexadecimal signature key. Uses Accept Hosted for a USD merchant account; register capture/fraud/void notifications and disable live-account test mode. |
| CoinPayments / coinpayments | Current REST v2 integration client ID/secret, API instance and numeric fiat currency ID. Legacy merchant/public/private/IPN credentials are not accepted. Only a completed invoice settles. |
The displayed admin form is the configuration contract for your installed version. Supported currency choices vary by gateway and still depend on merchant capabilities. CoinPayments signs the exact public callback URL, including its path; keep application URL and provider registration aligned.
AI providers and member tools
Configure the post writer, image generator and site assistant independently, using hosted APIs or your own supported inference service.
- Open Admin → AI Configuration at /admin/ai-configuration and enable the master AI switch.
- Select the text provider, endpoint, model and credentials. Writer and assistant share the configured text model; load its catalog or enter an exact valid model ID.
- For images, configure the independent Image generation provider, endpoint and image-capable model. Load models when supported or enter its exact ID.
- Enable only the intended member tools, set instructions/output limits/timeouts and choose the shared daily request allowance. Save.
- Verify each enabled tool as a normal member. Generate a draft or preview, then explicitly apply or attach it; generation alone must not publish a post.
| Tool | Behavior |
|---|---|
| Post writer | Generates or edits a draft with tone/length controls; applying does not overwrite a caption changed since generation. |
| Image generation | Returns a validated image preview for explicit attachment, then uses normal upload/processing and audience rules. |
| Site assistant | Answers questions and explains supported navigation; has no action tools, private account data or search. Its conversation remains in component memory. |
The installed text catalog supports direct providers including OpenAI, Anthropic, Gemini, xAI, Kimi, DeepSeek, Mistral and Cohere; gateways including Groq, OpenRouter, Together AI, Fireworks AI, Perplexity, Cerebras, NVIDIA NIM and Hugging Face; Azure OpenAI; and Ollama, LM Studio or a compatible API. Model IDs and availability must be confirmed with the selected service.
Image providers include LocalAI, Hugging Face Inference and compatible /images/generations APIs. Compatible endpoints must return b64_json; URL-only responses are not downloaded. Requested shapes are square, landscape or portrait, and the model must support the requested dimensions within the configured timeout.
- Only fully configured enabled tools appear to members. Image generation also requires uploads to be enabled and an eligible composer format.
- The shared daily per-member allowance defaults to 30 requests and can be set from 1 to 500. It resets at UTC midnight.
- Provider attempts consume usage even if the provider fails or the browser cancels. Text is additionally limited to 15 requests/minute and images to 5/minute.
- Generated images are checked as real raster data, with an 8 MiB cap or the smaller global upload limit, plus bounded dimensions and provider response size.
- Prompts and the supplied conversation context are sent to the configured provider. Provider keys and operator instructions remain server-side.
GIFs and stickers
Configure GIPHY for post/message GIF search and KLIPY independently for comment and reply stickers.
- Open Admin → Settings → Stickers & GIFs at /admin/settings?section=stickers-gifs.
- For GIFs, obtain a GIPHY browser API key, enter it, choose the content rating and two-letter country code, enable search and save.
- Use Preview saved GIF search to test trending results and keyword searches with the persisted configuration.
- For stickers, create a KLIPY browser key, select its content filter and country code, enable the integration and save.
- Use Preview saved sticker search to check trending, categories and searches. Arrange provider production access and quotas before a public rollout.
| Integration | Where and how it is used |
|---|---|
| GIPHY | Searchable GIF picker in Create post, Post Studio, Messages, floating conversations and eligible community messaging. The browser requests GIPHY directly. |
| KLIPY | New sticker selections in post/article comments and replies. Transparent WebP is preferred with GIF/PNG fallbacks. |
- These are intentional browser integration keys: authenticated users receive them when opening the picker. Use credentials intended for this application, not a server-only secret.
- Keys are encrypted and masked in settings. Blank replacement fields preserve them; explicit removal clears the saved key and disables that integration.
- Provider media loads directly from the provider. The application does not mirror, proxy or download the GIF/sticker catalog.
- Disabling search blocks new selections while existing posts/messages/stickers remain readable.
- Uploaded attachments and searched GIFs cannot be combined in one post or message; polls cannot include GIF attachments.
- The picker retains provider attribution, validates selected metadata and handles empty results, provider failures and rate-limit retries.
Story music and the shared library
Let members choose an excerpt from uploaded music or eligible Openverse discoveries, with independent music and original-video volume controls.
- Open Admin → Music at /admin/music. Set upload availability and the music size/duration limits.
- Install a usable ffprobe binary. Set MEDIA_FFPROBE_BINARY if it is not discoverable beside MEDIA_FFMPEG_BINARY or in standard locations.
- Enable Openverse discovery when desired. Optional OAuth credentials can raise authenticated service capability; anonymous discovery does not require a key.
- Run the normal migrations, queue worker and Laravel scheduler. Restart long-running workers after code/configuration changes.
- Test an uploaded track through processing, preview, excerpt selection and story publication. Check public discovery and private story-audience access separately.
The story Music picker has All music, Uploaded, Openverse and My uploads views. Only ready, visible and permitted tracks appear in discovery. My uploads includes the owner’s processing/failed items and recovery information. Uploaded tracks default to private and require explicit rights confirmation; public visibility permits discovery and reuse.
- Openverse discovery is restricted to eligible CC0 music and loops. It is not a catalog of commercial songs; broad terms such as music or piano are more useful.
- Selected Openverse media remains external. RevoGram stores its source/license metadata and streams from the original host rather than bulk-importing or mirroring the catalog.
- Uploads capture the global destination before queuing. Original audio and artwork retain separate exact storage configurations; cloud failures do not fall back to local files.
- Private tracks stay absent from other members’ libraries. Selecting a private track for a story authorizes only that story’s audience while the story remains visible.
- Removing a story does not remove a shared track. Disabling/removing a track creates an unavailable-audio state in stories that used it.
- Source/license metadata supports review; it is not independent proof of rights. Music reports use the existing moderation workflow.
php artisan music:backfill --json
Review the dry-run report before using --execute. Backfill adopts only proven locations and usable durations into owner-private tracks; it does not move files or guess today’s cloud configuration for historical audio. The daily music:cleanup-uploads task removes abandoned temporary work and retries unreferenced-object cleanup without removing ready referenced tracks.
Weather, news and external data
Configure shared external-data cards while retaining source attribution, bounded requests and useful unavailable states.
| Source | Purpose and configuration |
|---|---|
| Open-Meteo | Weather forecasts, city search and air quality through the configured services.open_meteo endpoints. Forecast data is cached for 10 minutes. |
| Nominatim / OpenStreetMap | Optional device-coordinate city labels, with attribution and a configurable WEATHER_REVERSE_GEOCODING_URL. |
| Local GeoIP database | Approximate public-IP location when device coordinates are unavailable. |
| GDELT | News discovery; no API key required. Configure query, language, timespan, article count, refresh and transport. |
| football-data.org | Optional football scores; configure its encrypted server-side key, competitions and refresh limits. |
| CoinMarketCap | Optional crypto card; configure its encrypted server-side key, symbols, display currency and refresh limits. |
- Open Admin → Settings → News at /admin/settings?section=news.
- Enable and configure GDELT news. Choose the source transport supported by your deployment and use a focused query and appropriate shared refresh interval.
- For football or crypto, add the appropriate provider API key before enabling the card. Choose supported competitions or symbols and save.
- Verify ready, empty and upstream-unavailable states. Shared cached data reduces duplicate provider requests across visitors.
- On the Weather page, test permitted device location, denied permission/IP fallback and manual city search. Check source attribution and your chosen units.
Automatic weather prefers permitted device coordinates. A denied permission, missing location service, device error or unanswered prompt falls back to the request’s public IP. IP location may describe a VPN or network exit. If neither source resolves, the interface offers city search rather than an invented default location.
- If reverse geocoding fails, forecasts can still use coordinates with a coordinate label. Names are applied after forecast-cache lookup to prevent reuse of the wrong place name.
- News and market cards share provider caches. Cached headlines can remain available during an upstream outage.
- Football and crypto keys stay server-side and are masked in responses. Blank replacement fields preserve a saved credential; explicit removal disables its card.
- Provider plans and rate limits still apply even when the application card is enabled. External availability and current source data are outside the application’s control.
Administration & feature modules
Configure the network from the administrator workspace and expose the modules your community needs.
Sign in with an active administrator account and open /admin. Use Dashboard for an overview, Users for account administration, and System Status at /admin/system for deployment information. The admin navigation and search provide direct links to individual settings; administrative routes enforce the role on the server.
| Workspace | Location | Purpose |
|---|---|---|
| Settings | /admin/settings | General settings and service configuration |
| Users | /admin/users | Account management and staff roles |
| Modules | /admin/settings?section=modules | Enable or pause feature families |
| System Status | /admin/system | Inspect the system configuration |
| Moderation cabinet | /moderation | Moderator casework outside global administration |
- Open Admin → Settings → General and review the site name, tagline, description, and site currency.
- Open Settings → Modules and choose which feature families are available. Save the configuration, then check the member navigation.
- Use the dedicated Threads, Shoutbox, Blogs & Articles, Events & Ticketing, and Audio & Video Calls sections to configure each module after enabling it.
- Test the resulting access with a separate ordinary member account; an administrator view can expose additional controls.
- Available module families include groups, communities, Pages, events, people discovery, threads, Shoutbox, blogs/articles, clips, videos, photos, live, memories, marketplace, jobs, fundraisers, wallet, referrals, weather, bookmarks, Avatar Studio, and optional 3D posts.
- Calls have a separate settings section. A module switch does not provision its external service: calls/live and payment integrations still require their own configuration.
- Disabling new event activity preserves access to existing ticket obligations. Do not treat module switches as a data removal tool.
Branding, appearance & SEO
Set the visible identity of your network and control how public content is presented to search engines.
The configured site name is shared with headings, page titles, mail, and exported content. Set it in Admin → Settings → General. Admin → Settings → Appearance contains the logo, favicon, brand display mode, sizing, colors, first-visit theme, and active frontend theme.
- Open /admin/settings?section=appearance. Upload a logo and check the preview. Logos accept SVG, PNG, JPG, or WebP up to 2 MB.
- Upload a square PNG or WebP favicon between 32 and 512 pixels, up to 512 KB.
- Choose Logo and brand name, Brand name only, or Logo only. Adjust logo dimensions and brand text sizing; zero values use automatic sizing.
- Set the accent, success, and danger colors, choose the initial light/dark/system appearance, and save. Check both guest and member views at desktop and phone widths.
| Configuration | Admin location | Review |
|---|---|---|
| SEO | /admin/settings?section=seo | Title suffix, description, keywords, canonical URL, indexing, link following, social image and card type |
| Sitemaps | /admin/settings?section=sitemap | Enable XML sitemaps, select public content sources, and set URLs per file |
| Footer pages | /admin/footer-pages | Manage informational pages and their public content |
The sitemap index is /sitemap.xml, with numbered source files under /sitemaps/. The robots.txt response advertises it when indexing and sitemaps are enabled. Sources include static pages, eligible profiles, posts, blogs, groups, communities, Pages, jobs, and marketplace listings. Disabled modules are excluded.
Registration, login & OAuth
Choose how people join, recover access, and sign in using passwords, passkeys, or configured identity providers.
| Settings section | Location | Controls |
|---|---|---|
| Access | /admin/settings?section=access | Guest access and read-only controls |
| Registration | /admin/settings?section=registration | Registration, confirmation, phone/username collection and referral requirement |
| Login Security | /admin/settings?section=login-security | Password rules, login attempts, inactivity, two-factor authentication and passkeys |
| Security | /admin/settings?section=security | CAPTCHA provider and protected public forms |
| OAuth Sign-In | /admin/settings?section=oauth | Apple, Google, X, and VK credentials and callback URLs |
- Configure email delivery and, when required, SMS/OTP before requiring registration confirmation or recovery by those channels.
- Choose the registration and login policies, save them, and exercise registration, login, logout, and recovery in a separate browser session.
- For OAuth, create a web application with the selected provider and copy the Authorized callback URL displayed for that provider in Admin. Enter the matching client ID and secret, then enable that provider.
- For CAPTCHA, choose Simple PHP CAPTCHA, Google reCAPTCHA v2, or Cloudflare Turnstile. Configure the matching public/secret key pair for external providers and select which login, registration, and recovery forms require verification.
OAuth buttons are available only when their provider is enabled and both client ID and secret are configured. Copy the generated callback URL rather than guessing its path. X requires an OAuth 2.0 application with user email access; the Google option uses a Google OAuth 2.0 web application.
Passkey enrollment and login are controlled in Login Security. The screen shows the website origin and relying-party configuration. Keep these stable so existing credentials continue to work. Members enroll passkeys from Security & Login; managing credentials requires a recent password confirmation. Turning passkeys off preserves enrolled credentials while pausing their use.
Account security, privacy & data
Give members clear control over their identity, visibility, interactions, and account information.
| Member area | Location | Use |
|---|---|---|
| Personal details | /settings/personal-details | Name, username, biography, profile and cover image |
| Account | /settings/account | Contact details and recovery information |
| Privacy | /settings/privacy | Profile, post, story and search visibility |
| Audience | /settings/audience | Followers, comments, mentions, sharing and recommendations |
| Timeline and tagging | /settings/timeline | Who can add or tag content and review controls |
| Security | /settings/security | Sessions, login activity and security review |
| Password & two-factor | /settings/password-2fa | Password and authenticator setup |
| Blocking | /settings/blocking | Blocked, muted and snoozed accounts |
| Data | /settings/data | Exports, deactivation and deletion requests |
- Review Privacy and Audience before publishing. Choose who may discover the account and interact with future content.
- Use Messaging settings at /settings/messaging to select message permissions, read receipts, typing, last-seen and online-status audiences.
- Review active sessions in Security. Revoke devices that should no longer have access, then enroll two-factor authentication when enabled by the administrator.
- Use Data to request a protected account export or manage account ownership requests. Sensitive actions require confirmation.
A full profile block removes friendship/follow relationships and pending invitations, and enforces bilateral visibility and interaction restrictions. A separate Block messages and calls control preserves the profile connection while preventing direct contact. Members can remove only their own block; the other person’s block can continue to apply. Unblocking does not restore removed friendships or follows.
Full blocks do not replace group or room moderation. Existing financial records, paid periods, refunds, tickets, and moderation cases retain their own access rules. Public content may still be available without authentication under the site’s public-access policy.
Moderation, sanctions & appeals
Review reports through assigned cases, maintain an audit trail, and apply account restrictions with clear staff boundaries.
Administrators review the workspace at /admin/moderation. Moderators use /moderation, with case queues, sanctions they issued, and their own activity. Assign the Moderator role through Admin → Users. Report actions are available for eligible visible resources; members cannot report their own content.
| Role | Allowed work |
|---|---|
| Member | Submit reports, view personal sanctions, and appeal a sanction once |
| Moderator | View unassigned or personally assigned summaries, self-claim cases, review assigned evidence, dismiss/resolve cases, hide reported posts and issue limited non-staff sanctions |
| Administrator | Assign/review all cases, manage reasons, issue/revoke all sanctions, and review eligible appeals |
- Configure active report reasons and resource scopes in the administrator moderation workspace.
- Filter the queue and assign or claim an open case before reviewing its private evidence.
- Review reports, evidence and history, then record an internal note and decision. Case states include open, reviewing, actioned, dismissed, and resolved.
- Use warnings or posting/commenting/messaging restrictions when proportionate. Moderator-issued restrictions are limited to 30 days and non-staff case subjects.
- Review appeals as an eligible administrator. The person who issued a sanction cannot uphold or reverse its appeal.
Sanctions enforce actual server-side access: restrictions block the corresponding writes, temporary suspensions deny normal authenticated use, and bans require administrator revocation. Sanctioned users may receive access only to their own appeal path and logout. A sanction’s expiry is checked at request time.
- Private-message evidence contains a snapshot of the reported message, not arbitrary surrounding conversations.
- Assigned-case access does not grant staff general access to private drafts or conversations.
- Case actions are audited. Internal appeal notes stay in staff APIs; reporter notifications do not reveal other reporters.
- The case workspace hides reported posts; it does not promise reversible removal for every resource type. Other resource cases can be resolved or addressed with account sanctions.
Support tickets & troubleshooting reports
Provide a real support queue and collect the information needed to reproduce an issue safely.
Members open Help & Support at /settings/help. The page provides searchable help topics and a support form with category, subject, and message. Submitted requests are stored in the application’s support-ticket system and show a reference, status, update time, and administrator response.
- Choose an appropriate category, enter a subject of 5–160 characters, and describe the issue in 20–5,000 characters. Submit the request and retain its reference.
- Administrators open /admin/tickets, filter by search, status or category, then select a ticket to read the request.
- Write a response and move the ticket through Open, In progress, Resolved, or Closed. Review the saved response and status.
- Where response email is requested, inspect the delivery result. A failed or disabled email does not mean the saved ticket response was lost.
- Include the affected page URL, approximate time and timezone, browser/device, and the member role used.
- List exact reproduction steps, expected behavior, actual behavior, and whether it affects one account or multiple accounts.
- For deployment issues, include the installed release, recent changes, relevant System Status information, and a small sanitized error excerpt.
- For uploads, include file type, approximate size, storage provider, and whether the problem occurs during upload or playback.
- For realtime/calls, include whether messages, notifications, or calls are affected and whether reconnecting changes the result.
Use a content report for abusive or unsafe material rather than a general support ticket when a report action is available. Use /appeals for an eligible sanction appeal. The ticket system is a product support queue; it does not replace moderation assignment and evidence controls.
Languages, translations & regional formats
Enable the languages you want to offer and review missing translations without confusing coverage with language quality.
Admin → Languages at /admin/languages requires the administrator role. The installed catalog provides locale codes, display names and text direction. Coverage varies by language; a listed locale is not a promise that every interface phrase is fully translated or professionally reviewed.
- Choose the website default from enabled, installed languages. Existing explicit member choices remain intact.
- Enable or disable locales to control public language selectors. Change the default before disabling it; the last enabled language cannot be disabled.
- Select a locale in the translation editor. Search source or target text, filter missing or translated entries, and edit the selected phrase.
- Preserve placeholders such as {siteName}, {name}, :attribute, and %s. Review long labels, plural forms, errors, dates, and right-to-left layouts before enabling a language widely.
| Editor action | Effect |
|---|---|
| Clear translation | Marks the selected locale value missing and uses the source fallback |
| Reset to installed translation | Removes the override and restores the bundled value |
| Rename source key | Carries stored translations but also requires code callers to be changed |
| Delete key | Removes the active key and overrides globally; source callers can still show their fallback |
Missing native phrases fall back through the English administrator override, English bundle, and source key. The supplied-key percentage measures coverage, not translation quality. Member text, account names, configured branding, editor content, filenames, and internal identifiers should remain opaque to UI translation. Date/number formatting also respects each member’s region, timezone, date order, clock and separator preferences.
npm run i18n:check -- --report /tmp/localization-source-audit.json
npm run i18n:audit -- --all --output=/tmp/localization-all-packs-audit.json
npm run i18n:test
npm run i18n:version
Frontend themes & source customization
Customize a complete frontend while keeping PHP, media, translations and application services shared.
Default is the only installed theme in the supplied source. Each directory below resources/themes is an independently compiled React frontend with its own pages, layouts, components, hooks, types and CSS. Backend PHP, API routes, translations, uploaded media and package dependencies remain shared.
- Copy resources/themes/default to resources/themes/your-theme.
- Change the id, name, and version in the copied manifest.json. Keep the theme ID consistent with its directory.
- Customize the copied js and css source directories. Retain application permissions, localization boundaries, and the existing API contracts.
- Build the theme, inspect guest/member/admin views in both appearances and at narrow widths, then select it in Admin → Settings → Appearance.
npm run build
# Or compile one installed theme:
npm run build:theme -- your-theme
| Output | Location |
|---|---|
| Browser assets | public/build/themes/<theme> |
| Server-rendered bundle | bootstrap/ssr/themes/<theme> |
| Authored source | resources/themes/<theme>/js and css |
REVOGRAM_THEME=your-theme php artisan inertia:start-ssr
Installing & developing extensions
Add scoped services, routes and interface elements through the extension system without patching the core.
Use Admin → Extensions at /admin/extensions or the extension:manage Artisan command. An extension package is a ZIP with extension.json at its root, optionally inside one containing folder. The manifest identifies the extension, semantic version, PHP/core compatibility, provider, declared permissions, assets, and optional migrations. A reference package is available at extensions/.example/hello-extension/.
- Review the author, source code, compatibility requirements, and declared permissions before installing a package. Extensions run trusted PHP with application privileges.
- Install the ZIP. New installations and updates remain disabled until explicitly enabled.
- Enable it to apply its unrecorded migrations and expose its declared routes, navigation, and assets. Check its user and administrator pages.
- Disable an extension before updating, rolling back, or uninstalling. Restart persistent PHP workers after PHP code changes.
php artisan extension:manage list
php artisan extension:manage install /path/to/hello-extension.zip
php artisan extension:manage enable hello-extension
php artisan extension:manage disable hello-extension
php artisan extension:manage install /path/to/hello-extension-1.1.0.zip --update
php artisan extension:manage rollback hello-extension
Public extension routes are scoped below /extensions/{id}; API routes below /api/v1/extensions/{id}; administrator routes below /admin/extensions/{id}. Core helpers attach authentication, active-account, enabled-extension, and administrator checks as appropriate. Frontend mounts can use site.main.before/after, admin.main.before/after, and page:{id}:{page}, with cleanup functions for listeners and timers.
Disabled extensions preserve data and stop serving their assets/navigation. Uninstall also preserves data unless the package declares complete cleanup. A cleanup declaration may permanently delete extension-owned tables and storage; the administrator confirmation discloses this. Failed or interrupted removal remains disabled and retryable.
Updates & backups
Back up persistent data, stage the new release, apply its schema changes, and restart long-running processes so every service runs the same code.
- Back up the database, the private .env configuration and APP_KEY, local uploads, private media, exports or recordings stored locally, and extension packages/configuration/backups.
- For cloud media, keep object storage retention/versioning or a separate provider-aware backup. A database-only backup does not contain uploaded files.
- Retain each historically used storage provider’s bucket/container, endpoint, credentials, and files while those files are still referenced. A change of the upload default does not migrate old files.
- Keep a copy of the last working application release and compiled assets. Record any custom theme and extension changes before replacing core files.
- Restore a test copy periodically so database references, media files, application keys, and provider settings are verified together.
- Read the release notes and stage the new files outside the active document root. Test them against a separate test database and copied storage configuration.
- Back up the active database, persistent files, and configuration. Enter maintenance mode for an in-place update when needed.
- Install dependencies from the release lockfiles and build its frontend. Preserve .env, APP_KEY, persistent storage, and external extension directories.
- Apply the release’s migrations before activating code that reads newly introduced columns. Deploy new hashed frontend files before switching the manifest or activating the release.
- Clear stale application caches, restart long-running queue/WebSocket/SSR processes, and resume the site.
- Confirm login, feeds, upload and authorized media delivery, payments in test mode, scheduler status, and the optional integrations you have configured.
composer install --no-dev --prefer-dist --optimize-autoloader
composer check-platform-reqs --no-dev
npm ci
npm run build
php artisan migrate --force
php artisan optimize:clear
php artisan queue:restart
php artisan down
# Apply the prepared update and complete its checks.
php artisan up
Troubleshooting
Start with the failing layer: application, assets, database, media delivery, scheduled jobs, or an optional external service.
| Symptom | What to check |
|---|---|
| 500 error / blank page | Check your private application and PHP-FPM logs, the supported PHP version/extensions, the database connection, APP_KEY, and runtime write permissions. Keep APP_DEBUG=false on a public site. |
| Frontend assets missing | Run npm ci and npm run build for the release. Confirm the selected theme has compiled output under public/build/themes/<theme> and that the web root is public/. Deploy the hashed files with their matching manifest. |
| Login/session or CSRF failures | Confirm APP_URL matches the public HTTPS origin, cookies are configured for the deployed domain, SESSION_SECURE_COOKIE matches HTTPS, the session database table exists, and the application key was preserved. |
| Protected local media is blank or inaccessible | Validate the /_private_media/ and /_legacy_media/ internal locations, their aliases and filesystem access. The application sends X-Accel-Redirect; a plain PHP development server will not transfer the file. |
| Legacy /uploads URLs return 403 | Verify auth_request is enabled and /_media_legacy_auth passes X-Original-URI to /api/v1/media/legacy-authorize. Confirm the viewer is actually allowed to access that item. Do not bypass the authorization rule. |
| Video thumbnails/optimization absent | Check executable FFmpeg/FFprobe paths and PHP process support. Originals remain a fallback when derivative generation is unavailable. Use the media inspection/repair commands only after reviewing their help and dry-run output. |
| Stories or scheduled posts do not update | Check that the runtime user’s minute cron invokes schedule:run with the correct PHP binary and project directory. Inspect php artisan schedule:list. |
| WebSocket status does not connect | Check the optional service is enabled and configured, the TLS reverse proxy forwards /realtime/app/, the exact allowed origin matches, and the supervised Reverb/broadcast worker and scoped Redis connection are healthy. |
| Remote link previews remain pending | Check the dedicated link-previews worker and its realtime Redis queue. The default sync queue does not consume this explicit queue. A blocked remote site can still remain an ordinary link. |
| Old cloud media breaks after provider change | Restore the old provider’s settings and delivery route. Each object keeps its provider identity; changing the upload default does not move historical objects. |
| Changes appear stale after update | Clear stale caches, ensure frontend files match the active manifest/theme, and restart long-running workers, the WebSocket server, and any optional SSR process. |
cd /path/to/project
composer check-platform-reqs --no-dev
php artisan migrate:status
php artisan schedule:list
php artisan queue:failed
php artisan optimize:clear
Maintenance commands
Use the application’s own audit and repair commands to inspect migrations, media, feeds, and optional services. Read each command’s help before running a write operation.
php artisan list
php artisan help media:backfill-locations
php artisan help media:migrate-protected
php artisan help media:repair-video-thumbnails
php artisan help extension:manage
| Command | Behavior |
|---|---|
| media:audit-legacy | Read-only inventory of registered, unregistered, and missing local media; --examples controls sample paths. |
| media:backfill-locations --json | Read-only provider-location audit. Add --execute only after reviewing results; registers evidence-backed locations without moving or deleting files. |
| media:migrate-protected --batch=100 | Dry run for known restricted legacy media. --execute copies verified files into private storage and switches references; originals are retained. |
| media:optimize-existing | Generates missing derivatives and protected-content previews. --premium-only and --subscriber-only narrow the work. This writes derivative files and metadata. |
| media:backfill-metadata | Writes missing dimensions, aspect ratio, and applicable video duration for existing media. |
| media:repair-video-thumbnails | Inspects or repairs missing, unreadable, or black video/short covers while preserving custom thumbnails; inspect help for the available execution options. |
| livekit:repair-replay-thumbnails | Inspects or repairs covers for completed live replays; review options before executing repairs. |
| music:backfill --json | Audits eligible uploaded audio. --execute saves evidence-backed private owner tracks without guessing historical cloud destinations. |
| feeds:sync-publications --backfill | Links native publications to feed announcements while preserving original dates and suppressing publication hints. |
| moderation:link-existing-reports | Previews existing report-to-case links. --execute creates links without changing report rows. |
| posts:publish-birthdays --dry-run | Counts candidates without publishing. The ordinary scheduled command publishes due birthday posts. |
| profile-visits:prune | Removes profile visits beyond the 90-day retention period. |
| user-plans:seed-drafts | Creates editable membership examples as drafts; does not publish offers or change existing plans. |
| livekit:reconcile | Reconciles live sessions/recordings after missed delivery or interruptions. |
| realtime:prune-presence | Cleans expired real-time connection leases. |
| realtime:schedule-notifications | Schedules future existing notifications for real-time delivery; also useful when restoring future-delivery work after dedicated queue data loss. |
| extension:manage list | Lists installed extensions. Other supported actions include install, enable, disable, rollback, and uninstall. |
| extension:schedule-run | Runs due tasks belonging to enabled extensions. |
Use these docs offline
The complete documentation is a standalone folder. You can read it without a server, Node.js or an internet connection.
- Download the documentation ZIP and extract it while preserving the docs folder structure.
- Open docs/index.html in your browser. The stylesheet, fonts, branding, scripts, search index and reference pages are bundled inside that same folder.
- Use the sidebar to browse guides, the feature reference and the changelog. Search and theme switching run locally; links marked as website or provider destinations require a connection.
- Move or rename the extracted folder if you like. Keep its internal files together, and open index.html from the new location.
| Included path | Purpose |
|---|---|
| index.html | Installation, configuration, operations and product guides. |
| features.html | The complete local feature reference. |
| changelog.html | Individual recorded changes and version history. |
| assets/ | Compiled Tailwind CSS, the documentation stylesheet, fonts, favicon, scripts and local search data. |
| content/ | Editable guide and reference data. |
| build.py | Optional dependency-free Python builder for regenerating the HTML and local search index. |
cd /path/to/docs
python3 build.py
All rendered assets are included. Regenerating HTML does not require a CSS build; styles remain editable in assets/docs.css. The bundled assets/tailwind.css is ready to use.