曾与蒿藜同雨露,한때 잡초와 쑥과 함께 비와 이슬을 나누던 곳이 이제는 소나무와 삼나무와 함께 서리와 눈을 견뎌내고 있다.终随松柏到冰霜.かつては雑草やヨモギと共に雨や露を分かち合っていたが、今では松やヒノキと共に霜や雪に耐えている。曾与蒿藜同雨露,Once sharing rain and dew with weeds and wormwood, now enduring frost and snow with pines and cypresses.终随松柏到冰霜.曾与蒿藜同雨露한때 잡초와 쑥과 함께 비와 이슬을 나누던 곳이 이제는 소나무와 삼나무와 함께 서리와 눈을 견뎌내고 있다.,终随松柏到冰霜.譖セ荳手珍阯懷酔髮ィ髴イ�檎サ磯囂譚セ譟丞芦蜀ー髴�曾与蒿藜同雨露,鏇句笌钂胯棞鍚岄洦闇诧紝缁堥殢鏉炬煆鍒板啺闇�终随松柏到冰霜.曾与蒿藜同雨露,한때 잡초와 쑥과 함께 비와 이슬을 나누던 곳이 이제는 소나무와 삼나무와 함께 서리와 눈을 견뎌내고 있다.终随松柏到冰霜.曾与蒿藜同雨露,终随松柏到冰霜.
APP_NAME=Laravel
APP_ENV=production
APP_KEY=[[key]]
APP_DEBUG=false
APP_TIMEZONE=UTC
APP_URL="[[softurl]]/public"
REPORT_ERRORS=false
APP_LOCALE=en
APP_FALLBACK_LOCALE=en
APP_FAKER_LOCALE=en_US
APP_MAINTENANCE_DRIVER=file
# APP_MAINTENANCE_STORE=database
BCRYPT_ROUNDS=12
LOG_CHANNEL=stack
LOG_STACK=single
LOG_DEPRECATIONS_CHANNEL=null
# Use "debug" for development; use "warning" or "error" in production
LOG_LEVEL=warning
DB_CONNECTION=mysql
DB_HOST="[[softdbhost]]"
DB_PORT=3306
DB_DATABASE="[[softdb]]"
DB_USERNAME="[[softdbuser]]"
DB_PASSWORD="[[softdbpass]]"
SESSION_DRIVER=file
SESSION_LIFETIME=120
SESSION_ENCRYPT=true
SESSION_PATH=/
# In hosted mode (IS_HOSTED=true) set to your base domain with a leading dot
# (e.g. .yourdomain.com) so sessions span subdomains. If left unset, hosted
# mode defaults it to the APP_URL base domain automatically.
SESSION_DOMAIN=null
# Set to false for local development without HTTPS
SESSION_SECURE_COOKIE=true
BROADCAST_CONNECTION=log
FILESYSTEM_DISK=local
QUEUE_CONNECTION=sync
# Where backup export archives are written. Defaults to the local disk (storage/app), which is
# correct for a single-server install. Set these only if the app runs on more than one container or
# server, where the process that builds an export is not the one that later serves the download.
#
# WARNING: this must NOT be the same bucket you serve images from. An export archive contains every
# sale, attendee email and phone number for the schedules in it, and image buckets are typically
# public and CDN-fronted. Use a separate private bucket with no public policy. BACKUP_SPACES_BUCKET
# has no fallback on purpose: a missing value fails rather than writing backups somewhere public.
# BACKUP_DISK_DRIVER=s3
# BACKUP_SPACES_KEY=
# BACKUP_SPACES_SECRET=
# BACKUP_SPACES_REGION=
# BACKUP_SPACES_ENDPOINT is the REGION endpoint - https://nyc3.digitaloceanspaces.com - not the
# per-bucket origin endpoint your provider's console displays. The bucket is added to the hostname
# for you, so an endpoint that names it too addresses bucket.bucket.region and fails TLS.
# BACKUP_SPACES_ENDPOINT=
# BACKUP_SPACES_BUCKET=
CACHE_STORE=file
CACHE_PREFIX=
MEMCACHED_HOST=127.0.0.1
REDIS_CLIENT=phpredis
REDIS_HOST=127.0.0.1
REDIS_PASSWORD=null
REDIS_PORT=6379
MAIL_MAILER=log
MAIL_HOST=127.0.0.1
MAIL_PORT=2525
MAIL_USERNAME=null
MAIL_PASSWORD=null
MAIL_ENCRYPTION=null
MAIL_FROM_ADDRESS="hello@example.com"
MAIL_FROM_NAME="${APP_NAME}"
AWS_ACCESS_KEY_ID=
AWS_SECRET_ACCESS_KEY=
AWS_DEFAULT_REGION=us-east-1
AWS_BUCKET=
AWS_USE_PATH_STYLE_ENDPOINT=false
VITE_APP_NAME="${APP_NAME}"
GEMINI_API_KEY=
OPENAI_API_KEY=
# AI provider selection (gemini or openai)
AI_TEXT_PROVIDER=gemini
AI_IMAGE_PROVIDER=openai
# AI model selection (optional)
# Translation models are used for batch translation tasks
# Content models are used for interactive features (event parsing, AI details, style generation, blog posts)
GEMINI_TRANSLATION_MODEL=
GEMINI_CONTENT_MODEL=
GEMINI_IMAGE_MODEL=
OPENAI_TRANSLATION_MODEL=
OPENAI_CONTENT_MODEL=
OPENAI_IMAGE_MODEL=
# Stripe Platform
# Used for subscription billing (hosted mode) AND direct ticket payments (self-hosted mode)
# In self-hosted mode, STRIPE_PLATFORM_KEY and STRIPE_PLATFORM_SECRET enable Stripe payments for ticket sales
STRIPE_PLATFORM_KEY=
STRIPE_PLATFORM_SECRET=
STRIPE_PLATFORM_WEBHOOK_SECRET=
STRIPE_PRICE_MONTHLY=
STRIPE_PRICE_YEARLY=
STRIPE_ENTERPRISE_PRICE_MONTHLY=
STRIPE_ENTERPRISE_PRICE_YEARLY=
# Displayed plan prices. These must match the amounts on the Stripe Price objects above:
# nothing reconciles them, so a mismatch means the site advertises one number and charges
# another. Defaults live in config/services.php (5 / 50 / 15 / 150).
#
# A super-admin can override all four at /admin/settings without touching this file or
# re-running config:cache. That override is display only, exactly as these are. Keep these set
# anyway: revenue reporting (ARR, MRR) and renewal emails deliberately read the values here, so
# that a marketing change cannot restate revenue that was already booked.
STRIPE_PRICE_MONTHLY_AMOUNT=
STRIPE_PRICE_YEARLY_AMOUNT=
STRIPE_ENTERPRISE_PRICE_MONTHLY_AMOUNT=
STRIPE_ENTERPRISE_PRICE_YEARLY_AMOUNT=
# The currency those amounts are shown in, everywhere the app quotes its OWN price: the
# marketing pages, the Plan tab, upgrade prompts and the JSON-LD offers. Also the fallback
# currency for a new event whose schedule has no country set.
#
# A label, like the amounts above - what a customer is charged comes from the Stripe Price.
# A super-admin can change it at /admin/settings, which overrides this. Defaults to USD.
# PLATFORM_CURRENCY=USD
# Payfast Platform
# Payfast is the dominant gateway in South Africa, where Stripe is not available. Setting all
# three values below enables Payfast for ticket sales across every schedule on a selfhosted
# install, the same way STRIPE_PLATFORM_KEY and STRIPE_PLATFORM_SECRET do for Stripe.
#
# These are a DEFAULT, never an override: a schedule owner who connects their own Payfast
# account in Settings > Payment Methods keeps using it, and their money keeps reaching them.
# Ignored in hosted mode, where each owner must connect their own account.
#
# The passphrase is optional at Payfast but required here - without one, the payment
# notification signature is a plain MD5 that anyone could reproduce.
PAYFAST_MERCHANT_ID=
PAYFAST_MERCHANT_KEY=
PAYFAST_PASSPHRASE=
# Use Payfast's sandbox instead of live. Test payments look completely normal, so leave this
# false unless you are testing.
PAYFAST_SANDBOX=false
# Restrict the checkout to ONE payment type, e.g. "ef" for Instant EFT. Leave empty to let
# Payfast offer everything. Codes: cc dc ef cp ap sp gp mc mt ss zp mu nd pf rc ab sc mp
PAYFAST_PAYMENT_TYPES=
# PayPal Platform
# Setting the two values below enables PayPal for ticket sales across every schedule on a
# selfhosted install, the same way STRIPE_PLATFORM_KEY and STRIPE_PLATFORM_SECRET do for Stripe.
#
# These are a DEFAULT, never an override: a schedule owner who connects their own PayPal account
# in Settings > Payment Methods keeps using it, and their money keeps reaching them.
# Ignored in hosted mode, where each owner must connect their own account.
#
# Create an app at developer.paypal.com under Apps & Credentials and copy its Client ID and
# Secret. PayPal settles a fixed list of currencies - see config/payments.php, which also records
# why HUF, JPY and TWD are deliberately not on it.
PAYPAL_CLIENT_ID=
PAYPAL_CLIENT_SECRET=
# Use PayPal's sandbox instead of live. Test payments look completely normal, so leave this false
# unless you are testing.
PAYPAL_SANDBOX=false
# Optional, unlike Payfast's passphrase: an ordinary payment is confirmed by the buyer's return, not
# by a notification. Without this you lose only the two cases that finish later - a payment PayPal
# holds for review, and one PayPal took whose reply never reached us. Those sales stay unpaid, and an
# unpaid sale is eventually expired and its seats resold, so set it on a site taking real money.
#
# It is NOT filled in for you. When an owner connects their own PayPal account the app registers a
# listener for them and stores the id against that user - nothing is ever written back to this file.
# For the install-wide account above, add the webhook yourself in PayPal (point it at
# https://your-site/payments/paypal/webhook, subscribe PAYMENT.CAPTURE.COMPLETED and
# PAYMENT.CAPTURE.DENIED) and paste its id here.
PAYPAL_WEBHOOK_ID=
# What a new event's payment method starts on. Defaults to cash. Set this to the gateway your
# install actually uses - e.g. payfast or paypal - so owners are not picking it by hand every time. It
# only applies when the gateway is genuinely usable for that event (connected, and able to
# settle the event's currency); anything else falls back to cash.
DEFAULT_PAYMENT_METHOD=
# White-label settings
IS_HOSTED=false
# Selfhosted installs are single user by default: the first account created is the
# instance admin, and further sign-ups are blocked. Set to true to let anyone register.
# Registered users can create their own schedules, so only enable this on a trusted
# network. For real multi-tenant hosting use IS_HOSTED=true (self-hosted SaaS) instead.
ALLOW_REGISTRATION=false
APP_LOGO_DARK=/images/dark_logo.png
APP_LOGO_LIGHT=/images/light_logo.png
APP_MARKETING_URL=https://eventschedule.com
SUPPORT_EMAIL=contact@eventschedule.com
# The upstream Event Schedule app this install talks to: where shared translation
# improvements are sent, and where federated events are published. Defaults to
# eventschedule.com, so it only needs setting to point at a different network.
#NEXUS_URL=https://eventschedule.com
TRIAL_DAYS=365
# Custom dashboard links (optional) - up to 3 links shown in the admin sidebar
# for all admins. A link only appears when BOTH its title and URL are set.
# Works in both selfhosted and hosted (SaaS) deployments.
CUSTOM_LINK_1_TITLE=
CUSTOM_LINK_1_URL=
CUSTOM_LINK_2_TITLE=
CUSTOM_LINK_2_URL=
CUSTOM_LINK_3_TITLE=
CUSTOM_LINK_3_URL=
# Exclude events/schedules from a country in marketing search (2-letter ISO code, e.g. "us"). Blank = no filter.
SEARCH_EXCLUDE_COUNTRY=
# Secret token for cron job endpoints (/release_tickets, /translate_data)
APP_CRON_SECRET=
# How stale the scheduler heartbeat may get before the admin panel warns that scheduled tasks have
# stopped. Both cron rails stamp it every tick, so this is many missed ticks, not one slow one.
# Keep it above 15: the /translate_data rail holds a 900-second lock, and a request killed by a
# timeout leaves that lock held for its full TTL with no heartbeat stamped behind it.
# SCHEDULER_STALE_MINUTES=20
# Which cron rail this process is, shown on /admin/queue so a dead worker is visible even while
# another rail is still ticking. schedule:run cannot tell a crontab from a worker, so the
# deployment has to say. Set to 'worker' on a dedicated scheduler container.
# SCHEDULER_RAIL=cron
# Which rail MUST be alive for scheduled work to count as happening. Leave unset on a single-cron
# install. Set it to 'worker' once a dedicated scheduler container exists: otherwise another rail
# keeping the shared heartbeat fresh masks a dead worker, and the admin panel reports all clear.
# It must match that container's SCHEDULER_RAIL exactly - any name works, but a mismatch means the
# admin panel is watching a rail nothing writes, and reports a stall that is not real.
# SCHEDULER_EXPECTED_RAIL=
# Trusted proxies - required when running behind a reverse proxy (Nginx/Apache/Cloudflare)
# to avoid infinite HTTPS redirect loops. Set to * to trust all proxies, or a
# comma-separated list of proxy IPs/CIDRs (e.g. 10.0.0.0/8,192.168.1.1).
TRUSTED_PROXIES=
# Twilio SMS (optional phone verification)
TWILIO_SID=
TWILIO_AUTH_TOKEN=
TWILIO_FROM_NUMBER=
# OneSignal push notifications (optional). When set, the app and visitors'
# browsers communicate with OneSignal's servers and load OneSignal's SDK from
# their CDN. Leave blank to disable push entirely (no SDK is loaded and no
# external calls are made). Get these from the OneSignal dashboard.
ONESIGNAL_APP_ID=
ONESIGNAL_REST_API_KEY=
# ONESIGNAL_SAFARI_WEB_ID= # only needed for legacy macOS Safari web push
# Google Wallet passes (optional). When set, an "Add to Google Wallet" button appears on paid
# tickets, on the order page of a multi-event purchase, and in the confirmation email. Leave blank
# to disable it entirely: no button is rendered and nothing is ever sent to Google.
#
# What leaves the install when a buyer taps the button: the attendee name, the event name, the
# venue name and address, the start time, the ticket type, any seat labels, the event's ticket
# notes (truncated), the venue's coordinates, and the ticket URL - which contains that sale's
# secret, because that URL is what the pass's QR code has to carry for the door scanner to accept
# it. Google keeps the pass once a buyer saves it; it can be expired but not deleted.
#
# Setup is documented in docs/GOOGLE_WALLET_SETUP.md. A new issuer account starts in demo mode,
# where only Google accounts you register as test accounts can save a pass.
#
# GOOGLE_WALLET_SERVICE_ACCOUNT takes EITHER an absolute path to the service-account JSON key
# file OR the base64-encoded contents of that file, for hosts with no writable file mount.
# GOOGLE_WALLET_ID_PREFIX namespaces the passes this install creates. Google cannot delete a
# class or an object once made, so give a staging install its own prefix or it will collide with
# production forever.
# GOOGLE_WALLET_ISSUER_ID=
# GOOGLE_WALLET_SERVICE_ACCOUNT=
# GOOGLE_WALLET_ID_PREFIX=es
# Cookie consent banner. Normally shown only when something on the page actually needs
# consent (Google Analytics via ANALYTICS_ID, ADS_ENABLED, STAY22_ENABLED). Set this to true
# to show it anyway, which is what enables the 30-day UTM attribution cookies
# (utm_params, utm_referrer_url, utm_landing_page) used to credit a signup or sale to the
# link that brought the visitor. Those are written only after a visitor clicks Allow.
# Left false, a bare install shows no banner and sets no non-essential cookies: attribution
# still works within a single session, it just does not survive across sessions.
COOKIE_CONSENT_BANNER=false
# Monetization (optional, multi-tenant hosted installs only). Off by default.
# When enabled, free-tier schedules' public pages can carry Google AdSense and/or paid
# promotions bought by other schedules; paid schedules never show either. ADS_ENABLED is a
# deploy-time gate that cannot be overridden from the admin panel - leave it false and the
# feature does not exist. See https://eventschedule.com/docs/saas/monetization for the
# consent and AdSense policy obligations this places on you as the operator.
#
# Turning promotions OFF at /admin/settings is safe at any time: campaigns stop serving and
# the scheduled promo:sync keeps settling and refunding what is already paid for. Setting
# ADS_ENABLED back to false, however, also stops promo:sync, so leave it true until any
# outstanding campaigns have completed and refunded.
ADS_ENABLED=false
# The settings below are editable at /admin/settings once the feature is on; the values here
# only supply the starting defaults.
# ADSENSE_ENABLED=true
# ADSENSE_PUBLISHER_ID=ca-pub-XXXXXXXXXXXXXXXX
# ADSENSE_EVENT_SLOT_ID=XXXXXXXXXX
# ADSENSE_PERSONALIZED=false # true opts into personalized ads; see the consent obligations
# PROMOTIONS_ENGINE_ENABLED=true
# NATIVE_PROMO_PRIORITY_OVER_PROGRAMMATIC=true
# PROMOTIONS_NETWORK_CPM=2.00
# PROMOTIONS_NETWORK_CPC=0.25
# These have no admin-panel equivalent - they can only be set here.
#
# PROMOTIONS_CURRENCY is deliberately NOT tied to the platform currency picked at
# /admin/settings: a promotion purchase is a live Stripe charge, and re-denominating one from a
# dropdown documented as display-only is not something an admin should be able to do by accident.
# PROMOTIONS_CURRENCY=USD
# PROMOTIONS_MIN_BUDGET=5.00
# PROMOTIONS_MAX_BUDGET=1000.00 # a schedule's own trust limit can still be lower
# PROMOTIONS_MAX_CONCURRENT=2 # live promotions per schedule
# PROMOTIONS_FREQUENCY_CAP=3 # times a visitor sees the same promotion per day
# PROMOTIONS_AUTO_APPROVE_AFTER=3 # approved campaigns before an advertiser skips review
# PROMOTIONS_MIN_CTR=0.0002 # CPC campaigns below this are paused
# PROMOTIONS_MIN_CTR_IMPRESSIONS=5000 # ...but only once they have this many impressions
# PROMOTIONS_CACHE_TTL=300 # seconds the servable-campaign list is cached
# PROMOTIONS_STATS_RETENTION_DAYS=400
# Stay22 accommodation map (optional). Independent of ADS_ENABLED: it applies to paid schedules
# too, and each schedule may supply its own affiliate ID and keep the commission itself.
#
# STAY22_ENABLED is a deploy-time gate that cannot be overridden from the admin panel, because the
# Content-Security-Policy is built from it on every request. Leave it false and the feature does not
# exist: no frame host is allow-listed, and the per-schedule toggle is not even rendered. Changing it
# requires regenerating the config cache (php artisan config:cache).
#
# The map never loads until a visitor either has accepted cookies or explicitly clicks to show it.
# See https://eventschedule.com/docs/saas/monetization for the disclosure obligations this places on
# you as the operator.
STAY22_ENABLED=false
# Fallback affiliate ID, used for schedules that enabled the map but did not add their own. Editable
# at /admin/settings once the feature is on; the value here only supplies the starting default.
# Never used on a customer's own custom domain.
# STAY22_AID=
# STAY22_MAX_NIGHTS=30 # upper bound on the derived stay length
# Tickets
# How many options the guest quantity dropdown offers for a ticket with no "Max Per Order"
# of its own. Clamped to 1-100. Not a purchase limit: checkout enforces only the ticket's
# own max per order and its remaining stock.
# MAX_TICKETS_PER_ORDER=20
# DigitalOcean App Platform (optional custom domain provisioning)
DO_API_TOKEN=
DO_APP_ID=
DO_APP_HOSTNAME=
# Cloudflare Turnstile (optional bot protection)
TURNSTILE_SITE_KEY=
TURNSTILE_SECRET_KEY=
# Web accessibility (optional overrides; see config/accessibility.php)
# ACCESSIBILITY_CONTACT_EMAIL=accessibility@example.com
# ACCESSIBILITY_WCAG_TARGET_LABEL="WCAG 2.1 Level AA"
# ACCESSIBILITY_REFERENCE_IS_5568=true
# ACCESSIBILITY_RESPONSE_SLA_BUSINESS_DAYS=10
# ACCESSIBILITY_LAST_REVIEWED=2026-09-04
#
# The measured record published under "Conformance status" on the accessibility page: how
# many public pages were swept, and when. Scope is the marketing and documentation pages
# only, which the declaration says outright. Re-measure and these two move together.
# ACCESSIBILITY_PUBLIC_PAGES_MEASURED=153
# ACCESSIBILITY_PUBLIC_MEASUREMENT_DATE=2026-09-04
# Nexus / marketing-site behaviour. IS_NEXUS marks the one install that serves the marketing
# pages (eventschedule.com). It is not a cosmetic flag: App\Http\Middleware\
# CacheableMarketingResponse and TrackMarketingVisit both stand down entirely when it is false,
# so edge caching of marketing HTML and beacon page-view counting are inert without it.
# IS_NEXUS=false
# The session cookie name. Unset it and config/session.php derives one from APP_NAME
# ("laravel_session" for the shipped APP_NAME). Worth knowing before you change either: a shared
# cache in front of the app has to bypass on this exact cookie name to keep a signed-in visitor
# off a stored anonymous page, and that rule is written in the CDN, not here. See docs/CACHING.md.
# SESSION_COOKIE=
# How long the homepage poster wall query is cached. 0 disables the cache, which is what the test
# suite does - so DO NOT set this to an empty value on a deployed install. An empty value is read
# as 0 and puts five correlated subqueries plus a regex pass on every origin hit of "/".
# MARKETING_WALL_CACHE_SECONDS=600
# Ceilings on one run of each nudge or announcement command, so a backlog drains over several
# runs rather than in one burst of mail. See config/usage.php for what each one bounds.
# ONBOARDING_NUDGE_BATCH=500
# ACTIVATION_NUDGE_BATCH=200
# AUDIENCE_ANNOUNCEMENT_BATCH=100
# AUDIENCE_ANNOUNCEMENT_RECIPIENT_BATCH=2000
# EVENT_INTEREST_RECIPIENT_BATCH=2000
# Hours before an event that the "coming up" reminder goes out to people who asked to hear about
# it, and how long a "tickets are on sale" row stays live. The second one matters because a
# DATELESS event never ages out by date, and a row nothing can ever satisfy must not occupy the
# send window for ever - see SendEventInterestMail::candidates().
# EVENT_INTEREST_REMINDER_HOURS=48
# EVENT_INTEREST_TICKETS_MAX_AGE_DAYS=180
# Minimum hours between automatic new-event announcements to a schedule's subscribers. This is
# the cadence the subscribe confirmation email promises ("at most one every few days"), so
# lowering it breaks a promise already made to people who opted in.
# AUDIENCE_ANNOUNCEMENT_MIN_HOURS=72
# Recipients one newsletter or announcement may reach when the sending schedule has neither its
# own SMTP nor an SMS-verified owner, and so is going out on the platform's shared mailer.
# AUDIENCE_MAIL_UNVERIFIED_MAX_RECIPIENTS=50