個人 GitHub 帳號下公開儲存庫的擁有者可以登入並驗證所有權,自行管理介紹,無需管理員核准。
登入並產生驗證檔案,在 30 分鐘內將其提交至此儲存庫的預設分支。其他分支或分叉儲存庫中的檔案無效。然後在下方檢查檔案,即可無需管理員核准直接管理專案。請保留檔案以維持管理權限。
如需登入,將前往登入頁面。登入後請回到此處,再次點擊所選的驗證按鈕。
在專案資料夾中執行 /nomorevibe verify,即可驗證所有權並自行管理介紹。
一分鐘完成驗證Share your grow in real time with a self-hosted dashboard for a camera live stream, and some details about your grow!.
翻譯準備中
取自儲存庫 README
正在載入完整 README。
檢視完整 README ↗GrowCast is a Next.js web app for a live garden: climate, energy, gallery, an OBS overlay, and Twitch broadcast, with a protected admin panel.
GrowCast lets you share your grow in real time. Visitors see the live stream, climate, energy, and media. Admin users update the grow from the settings pages.
/overlay and the Broadcast page (/admin/stream) for music, alerts, Twitch, and camera lookTo see a live demo, visit my instance. The official project site is growcast.0xmarcel.com.
npm run dev or npm run setup:adminA camera, MediaMTX, and Cloudflare are for publishing a stream. They are not required to open the site.
Clone the repository and start the stack:
docker compose up --build -d
That builds and starts the website, the climate sidecar, the Twitch restream image, and the timelapse worker. The first build downloads Chromium for the restream image and can take a while.
Open http://localhost:3000. Until an admin account exists, the site opens the setup wizard. The container log prints a setup code (docker compose logs growcast). Enter that code on the first step, then create the admin login. The wizard also asks which sidecars to configure:
The mesh token is generated on first boot and shared with the sidecars. You do not paste it.
npm run setup:admin still writes .env.local for an admin account created outside the wizard. Passwords must be at least 12 characters. For local/dev only:
npm run setup:admin:insecure
# or: npm run setup:admin -- --allow-insecure
(Do not use npm run setup:admin --allow-insecure — npm treats that as its own config, not a script argument.)
An account already present in .env.local skips the wizard.
.env.local is optional. Compose loads it when the file exists (env_file required: false).
ADMIN_USERNAME=your_admin_username
ADMIN_PASSWORD_HASH=scrypt$...$...
ADMIN_SESSION_SECRET=at_least_32_chars_random_secret
GROWCAST_MESH_TOKEN=optional_existing_token
Notes:
ADMIN_PASSWORD_HASH must use the scrypt$... format.ADMIN_SESSION_SECRET must be at least 32 characters.GROWCAST_MESH_TOKEN is set, that value is copied to data/mesh.token and the sidecars use it. When it is unset, the container creates one token on first boot.npm run setup:admin and the wizard enforce this). For local/dev only, use npm run setup:admin:insecure (or npm run setup:admin -- --allow-insecure).docker compose up --build recreates the website container and signs you out. Sign in again..env.local, not the restream sidecar): TWITCH_CLIENT_ID and TWITCH_CLIENT_SECRET. EventSub webhooks also need GROWCAST_PUBLIC_URL (the public HTTPS origin, e.g. the Cloudflare Tunnel hostname).GrowCast writes human-readable logs to stdout so docker compose logs shows the setup code, auth events, and request lines without a JSON parser. Set LOG_FORMAT=json for one JSON object per line (log shipping). Correlation IDs are set in the Next.js proxy (X-Request-ID on responses). Optional env vars: LOG_LEVEL, LOG_FORMAT, GROWCAST_ENV.
Full schema, event catalog, redaction rules, Docker log shipping, retention guidance, and alert examples: docs/logging.md.
docker compose up --build -d is the supported start command. It runs four services from docker-compose.yml: growcast, ggs, restream, and timelapse.
For production, put the origin behind a Cloudflare Tunnel (HTTPS public hostname → http://127.0.0.1:3000). Compose publishes on ${GROWCAST_BIND:-127.0.0.1}:${GROWCAST_PORT:-3000} (loopback unless you set GROWCAST_BIND=0.0.0.0 for a LAN) and sets GROWCAST_TRUST_PROXY=1. Login rate-limits use CF-Connecting-IP, then X-Real-IP, then the first X-Forwarded-For hop, which Caddy sends by default. Those headers are ignored unless the flag is set. Admin cookies are Secure when X-Forwarded-Proto: https or CF-Connecting-IP is present (or COOKIE_SECURE=1). Direct HTTP to a public :3000 is not the supported admin path.
Local-only UI: http://localhost:3000 (session cookie is not Secure).
Useful commands:
docker compose up --build -d
docker compose logs -f growcast
docker compose down
docker compose up --build -d recreates the website container and signs you out. Sessions stay in memory for 24 hours or until logout. Sign in again.
What gets persisted on the host:
./data -> /app/data (grow data, mesh token, sidecar env files, restream state)./extensions/GrowCast-Timelapse -> /app/extensions/GrowCast-Timelapse (snapshots and timelapse video)./public/setup -> /app/public/setup./public/yourPictures -> /app/public/yourPicturesThe website writes data/ggs.env for the climate sidecar and data/timelapse.env for the camera URL. Those files live on the ./data directory mount, so Docker does not create a directory in place of a missing env file. The climate and timelapse containers read those files and reload their processes when the files change.
Sidecars without credentials wait, then start when the wizard (or admin settings) writes the config. Twitch restream stays idle until you save a stream key and press Start on Broadcast (/admin/stream). GrowCast writes data/restream/capture.token on its own; GROWCAST_RESTREAM_TOKEN in .env.local is an optional override.
Broadcast (/admin/stream) previews the 1920×1080 program with background music (uploaded playlist or a stream URL; URL wins while set) and on-stream alerts (manual Send alert, plus Twitch follow/sub/raid/bits after Connect Twitch). Public /overlay and the homepage stay silent.
The website container runs as uid 1001 (growcast). The entrypoint chowns ./data, the upload folders, and the timelapse snapshots and timelapse directories so the process can write them. It does not change ownership of the plugin source, so a later git pull still works. After the first run those data folders are owned by 1001:1001 on the host. The climate and timelapse sidecars use the same uid so they can read ./data.
Optional address:
${GROWCAST_BIND:-127.0.0.1}:${GROWCAST_PORT:-3000}:3000.GROWCAST_PORT for a different port, or GROWCAST_BIND=0.0.0.0 to reach the site from other machines on the LAN.MediaMTX stays outside this compose file. .env.local, media folders, and data/ are provided at runtime and are not baked into the image.
npm run dev
Open http://localhost:3000.
npm run build
npm run start
This starts the standard Next.js production server. The Docker image builds a standalone bundle automatically during docker compose build.
app/
(site)/page.tsx # Public dashboard
(site)/gallery/page.tsx # Gallery
(site)/energy/page.tsx # Energy
overlay/page.tsx # OBS overlay
setup/page.tsx # First-run wizard
admin/page.tsx # Admin login + grow settings
admin/stream/page.tsx # Broadcast
admin/ggs/page.tsx # Climate and energy settings
admin/timelapse/page.tsx # Timelapse settings
extensions/
GrowCast-GGS/ # Climate sidecar
GrowCast-Restream/ # Twitch restream sidecar
GrowCast-Timelapse/ # Timelapse sidecar and media
data/
mesh.token # Shared sidecar token, created on first boot
ggs.env # Climate credentials written by the wizard
timelapse.env # Camera URL written by the wizard
The homepage player uses a browser HLS URL. MediaMTX converts the camera RTSP stream. These settings avoided stutter on iOS and some Windows players:
hlsAlwaysRemux: true
hlsVariant: fmp4
hlsSegmentCount: 7
hlsSegmentDuration: 1s
hlsPartDuration: 200ms
hlsSegmentMaxSize: 50M
hlsDirectory: ''
hlsMuxerCloseAfter: 60s
hlsAllowOrigin: '*'
paths:
growcam:
source: rtsp://USER:[email protected]/stream1
sourceProtocol: tcp
sourceOnDemand: no
If playback still stutters, lower the camera frame rate. 15 fps is a reasonable start.
A public stream needs a second Cloudflare hostname for MediaMTX HLS, separate from the GrowCast hostname. Point Broadcast's stream URL at that public HLS path, for example https://stream.example.com/growcam/.
The timelapse sidecar uses the camera's RTSP URL directly. That URL is collected in the setup wizard and stored in data/timelapse.env.
GrowCast expects a browser-playable stream URL in the admin dashboard. Since some cameras expose RTSP, use MediaMTX to convert RTSP to HLS:
rtsp://<camera-ip>:554/<path>)./admin/stream), for example:
http://<mediamtx-host>:8888/<path>/This app uses Next.js route handlers and local filesystem storage.
data/current-grow.jsonlib/db.tsGET /api/data/current-grow
Cache-Control: no-store, must-revalidateGET /api/snapshots/[filename]
extensions/GrowCast-Timelapse/snapshotsGET /api/timelapse
extensions/GrowCast-Timelapse/timelapse/latest_timelapse.mp4GET /api/data/live-climate
Cache-Control: no-storeGET /api/data/broadcast
{ "live": true, "login" } while the homepage Twitch toast may show; otherwise { "live": false }Cache-Control: no-store, must-revalidateGET /api/data/live-climate/stream
nginx: location /api/data/live-climate/stream { proxy_buffering off; proxy_http_version 1.1; }, Caddy: flush_interval -1)GET /api/mesh/[pluginId]
/api/mesh/growcast.timelapsedata/mesh.token, and a matching Authorization: Bearer <token> (fail-closed when both are unset)POST /api/mesh/growcast.ggs/state
GROWCAST_MESH_TOKEN.env.local or from the setup wizard (data/setup/admin.json)setup:admin and the wizard require a 12-character password; --allow-insecure is local/dev onlydata/setup/totp.json, turned on from Admin → Security. Sign-in stays password-only until that file is confirmed.data/setup/totp.json. The next sign-in is password-only. Rotating ADMIN_SESSION_SECRET also makes that file unreadable; delete it and turn the authenticator on again.docker compose up --build signs you out.To make the HLS source and app publicly accessible without exposing your home network, publish both services through Cloudflare Tunnel:
http://localhost:3000).http://localhost:8888).cloudflared:
growcast.example.com -> http://localhost:3000)stream.example.com -> http://localhost:8888)Stream URL to your public MediaMTX HLS URL:
https://stream.example.com/<path>/Important:
ADMIN_* env vars).Cause:
ADMIN_* env variables.Fix:
npm run setup:admin and restart the app.Cause:
extensions/GrowCast-Timelapse folder missing or no media generated.Fix:
Cause:
Fix: