Spaces:
Running
chore: production-readiness pass — add missing release files, clean up code
Browse filesNew files:
- .env.example: full configuration reference with inline docs
- .gitattributes: normalize line endings (LF), mark binaries
- CHANGELOG.md: v1.0.0 release history + unreleased roadmap
- CODE_OF_CONDUCT.md: community standards
- SECURITY.md: vulnerability reporting, secrets mgmt, known limitations
- docker-compose.yml: local development with hot-reload volume mounts
Code fixes:
- health-server.js: remove dead .replace() call on template literal output
- Dockerfile: remove dashboard.html COPY (nginx no longer serves it)
- dashboard.html: delete orphaned file
Docs:
- CONTRIBUTING.md: full rewrite with file overview table, docker-compose workflow, hot-reload note
- README.md: fix stale worker count caveat (1 worker, not 2)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- .env.example +105 -0
- .gitattributes +43 -0
- CHANGELOG.md +42 -0
- CODE_OF_CONDUCT.md +36 -0
- CONTRIBUTING.md +59 -22
- Dockerfile +0 -1
- LICENSE +1 -1
- README.md +2 -0
- SECURITY.md +73 -0
- dashboard.html +0 -308
- docker-compose.yml +71 -0
- health-server.js +4 -4
|
@@ -0,0 +1,105 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# ============================================================================
|
| 2 |
+
# HuggingFlow — DeerFlow Research Agent on Hugging Face Spaces
|
| 3 |
+
# Configuration Reference
|
| 4 |
+
# ============================================================================
|
| 5 |
+
# Copy this file to .env for local Docker development.
|
| 6 |
+
# On HF Spaces, set these as Secrets in Settings → Variables and Secrets.
|
| 7 |
+
# NEVER commit your .env file — it contains real credentials.
|
| 8 |
+
# ============================================================================
|
| 9 |
+
|
| 10 |
+
# ============================================================================
|
| 11 |
+
# LLM Provider (REQUIRED)
|
| 12 |
+
# ============================================================================
|
| 13 |
+
|
| 14 |
+
# Model in "provider/model-name" format.
|
| 15 |
+
# See README for full provider list.
|
| 16 |
+
LLM_MODEL=openai/gpt-4o
|
| 17 |
+
|
| 18 |
+
# API key for the chosen provider.
|
| 19 |
+
LLM_API_KEY=sk-xxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
| 20 |
+
|
| 21 |
+
# ============================================================================
|
| 22 |
+
# Search Tools (Optional — highly recommended)
|
| 23 |
+
# ============================================================================
|
| 24 |
+
|
| 25 |
+
# Serper — real Google Search results. 2,500 free queries/month.
|
| 26 |
+
# https://serper.dev
|
| 27 |
+
# SERPER_API_KEY=xxxxxxxxxxxxxxxxxxxx
|
| 28 |
+
|
| 29 |
+
# Tavily — alternative web search with free tier.
|
| 30 |
+
# https://tavily.com
|
| 31 |
+
# TAVILY_API_KEY=tvly-xxxxxxxxxxxxxxxxxxxx
|
| 32 |
+
|
| 33 |
+
# Jina AI — better web page fetching and reading.
|
| 34 |
+
# https://jina.ai
|
| 35 |
+
# JINA_API_KEY=jina_xxxxxxxxxxxxxxxxxxxx
|
| 36 |
+
|
| 37 |
+
# ============================================================================
|
| 38 |
+
# Authentication & Security
|
| 39 |
+
# ============================================================================
|
| 40 |
+
|
| 41 |
+
# JWT signing secret for DeerFlow auth. Sessions survive restarts if this is set.
|
| 42 |
+
# Generate: openssl rand -base64 32
|
| 43 |
+
# AUTH_JWT_SECRET=your-random-secret-here-minimum-32-characters
|
| 44 |
+
|
| 45 |
+
# ============================================================================
|
| 46 |
+
# Data Persistence (Optional — required for threads to survive restarts)
|
| 47 |
+
# ============================================================================
|
| 48 |
+
|
| 49 |
+
# Your Hugging Face API token (needs Write access).
|
| 50 |
+
# https://huggingface.co/settings/tokens
|
| 51 |
+
# HF_TOKEN=hf_xxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
| 52 |
+
|
| 53 |
+
# HF Dataset repo name for backups (auto-created as private).
|
| 54 |
+
# Will be: {your-hf-username}/{BACKUP_DATASET_NAME}
|
| 55 |
+
BACKUP_DATASET_NAME=huggingflow-backup
|
| 56 |
+
|
| 57 |
+
# How often to sync to HF Dataset (seconds). Default: 10 minutes.
|
| 58 |
+
SYNC_INTERVAL=600
|
| 59 |
+
|
| 60 |
+
# ============================================================================
|
| 61 |
+
# Custom / Self-hosted LLM (Optional)
|
| 62 |
+
# ============================================================================
|
| 63 |
+
|
| 64 |
+
# OpenAI-compatible base URL for any custom or self-hosted endpoint.
|
| 65 |
+
# When set, uses LLM_API_KEY as the API key and LLM_MODEL as the model name.
|
| 66 |
+
# CUSTOM_BASE_URL=https://your-llm-endpoint.example.com/v1
|
| 67 |
+
|
| 68 |
+
# ============================================================================
|
| 69 |
+
# Cloudflare Integration (Optional)
|
| 70 |
+
# ============================================================================
|
| 71 |
+
|
| 72 |
+
# Cloudflare API token with Workers Edit permission.
|
| 73 |
+
# Enables: (1) outbound proxy Worker — bypasses HF Spaces IP blocks on some APIs.
|
| 74 |
+
# (2) keep-awake cron Worker — pings /health every 10 min to prevent sleep.
|
| 75 |
+
# https://dash.cloudflare.com/profile/api-tokens
|
| 76 |
+
# CLOUDFLARE_WORKERS_TOKEN=your_cloudflare_api_token
|
| 77 |
+
|
| 78 |
+
# Skip auto-setup and use an existing Cloudflare Worker URL directly.
|
| 79 |
+
# CLOUDFLARE_PROXY_URL=https://your-worker.your-subdomain.workers.dev
|
| 80 |
+
|
| 81 |
+
# ============================================================================
|
| 82 |
+
# Startup Timeouts (Optional)
|
| 83 |
+
# ============================================================================
|
| 84 |
+
|
| 85 |
+
# Max seconds to wait for the DeerFlow backend (FastAPI) to start.
|
| 86 |
+
BACKEND_READY_TIMEOUT=120
|
| 87 |
+
|
| 88 |
+
# Max seconds to wait for the DeerFlow frontend (Next.js) to start.
|
| 89 |
+
FRONTEND_READY_TIMEOUT=120
|
| 90 |
+
|
| 91 |
+
# ============================================================================
|
| 92 |
+
# HuggingFace Spaces — Automatically injected by the platform
|
| 93 |
+
# ============================================================================
|
| 94 |
+
|
| 95 |
+
# These are set automatically by HF Spaces at runtime. Do not set manually.
|
| 96 |
+
# SPACE_ID=your-username/your-space-name
|
| 97 |
+
# SPACE_HOST=your-username-your-space-name.hf.space
|
| 98 |
+
# SPACE_AUTHOR_NAME=your-hf-username
|
| 99 |
+
|
| 100 |
+
# ============================================================================
|
| 101 |
+
# Privacy / Telemetry
|
| 102 |
+
# ============================================================================
|
| 103 |
+
|
| 104 |
+
# DeerFlow is self-hosted — no external telemetry. These are informational only.
|
| 105 |
+
DO_NOT_TRACK=1
|
|
@@ -0,0 +1,43 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Normalize line endings to LF everywhere
|
| 2 |
+
* text=auto eol=lf
|
| 3 |
+
|
| 4 |
+
# Force LF for shell scripts (Windows editors sometimes write CRLF)
|
| 5 |
+
*.sh text eol=lf
|
| 6 |
+
|
| 7 |
+
# Force LF for Python
|
| 8 |
+
*.py text eol=lf
|
| 9 |
+
|
| 10 |
+
# Force LF for JS/TS
|
| 11 |
+
*.js text eol=lf
|
| 12 |
+
*.ts text eol=lf
|
| 13 |
+
*.tsx text eol=lf
|
| 14 |
+
*.jsx text eol=lf
|
| 15 |
+
*.json text eol=lf
|
| 16 |
+
|
| 17 |
+
# Force LF for config / markup
|
| 18 |
+
*.yaml text eol=lf
|
| 19 |
+
*.yml text eol=lf
|
| 20 |
+
*.md text eol=lf
|
| 21 |
+
*.conf text eol=lf
|
| 22 |
+
*.txt text eol=lf
|
| 23 |
+
*.html text eol=lf
|
| 24 |
+
*.css text eol=lf
|
| 25 |
+
|
| 26 |
+
# Dockerfiles
|
| 27 |
+
Dockerfile text eol=lf
|
| 28 |
+
*.dockerfile text eol=lf
|
| 29 |
+
|
| 30 |
+
# Binary — no diff, no merge
|
| 31 |
+
*.png binary
|
| 32 |
+
*.jpg binary
|
| 33 |
+
*.jpeg binary
|
| 34 |
+
*.gif binary
|
| 35 |
+
*.ico binary
|
| 36 |
+
*.woff binary
|
| 37 |
+
*.woff2 binary
|
| 38 |
+
*.ttf binary
|
| 39 |
+
*.otf binary
|
| 40 |
+
*.eot binary
|
| 41 |
+
*.gz binary
|
| 42 |
+
*.zip binary
|
| 43 |
+
*.tar binary
|
|
@@ -0,0 +1,42 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Changelog
|
| 2 |
+
|
| 3 |
+
All notable changes to HuggingFlow are documented here.
|
| 4 |
+
|
| 5 |
+
## [1.0.0] - 2026-05-08
|
| 6 |
+
|
| 7 |
+
### Added
|
| 8 |
+
|
| 9 |
+
- Initial public release of HuggingFlow
|
| 10 |
+
- DeerFlow v2 (ByteDance) deployed as a single-container Hugging Face Space
|
| 11 |
+
- Pre-built GHCR image strategy — pulls `ghcr.io/bytedance/deer-flow-backend:latest` and `ghcr.io/bytedance/deer-flow-frontend:latest` instead of compiling from source; build time ~5 min (was 30+ min)
|
| 12 |
+
- Multi-provider LLM support: OpenAI, Anthropic, Google Gemini, DeepSeek, Groq, Mistral, xAI/Grok, OpenRouter, Qwen/Alibaba, Moonshot/Kimi, any OpenAI-compatible endpoint
|
| 13 |
+
- Pluggable search: Serper (Google), Tavily, DuckDuckGo fallback
|
| 14 |
+
- Live status dashboard at `/` — tile grid showing DeerFlow App, Model, Runtime, Search, Backup, Keep Awake; auto-refreshes every 30 s
|
| 15 |
+
- HF Dataset backup/restore via `deerflow-sync.py` — syncs SQLite DB + workspace files on interval and graceful shutdown
|
| 16 |
+
- Cloudflare outbound proxy — routes backend traffic through a Cloudflare Worker to bypass HF Spaces IP blocks
|
| 17 |
+
- Cloudflare keep-awake cron — Worker pings `/health` every 10 min to prevent free-tier sleep
|
| 18 |
+
- JWT auth via DeerFlow v2 — admin account created at `/setup` on first boot
|
| 19 |
+
- nginx reverse proxy (port 7861) — routes `/api/*` to FastAPI backend, `/*` to Next.js frontend, LangGraph SDK alias at `/api/langgraph/*`
|
| 20 |
+
- health-server.js (port 7860, public) — status dashboard + transparent reverse proxy to nginx + WebSocket upgrade passthrough
|
| 21 |
+
- Graceful shutdown with final HF Dataset sync
|
| 22 |
+
- `docker-compose.yml` for local development
|
| 23 |
+
- `.env.example` configuration reference
|
| 24 |
+
- MIT License
|
| 25 |
+
|
| 26 |
+
### Fixed
|
| 27 |
+
|
| 28 |
+
- `DEER_FLOW_TRUSTED_ORIGINS` must be explicitly set for pre-built frontend image (sha `af6e48cc`); without it, zod schema validation fails silently → "Application error" on every Next.js page
|
| 29 |
+
- `--workers 1` on uvicorn prevents SQLite `CREATE TABLE` race condition with multiple workers
|
| 30 |
+
- Next.js `.bin/next` is a shell shim — must not be called with `node` prefix
|
| 31 |
+
- nginx `alias` on a file path appends `index.html` — replaced with `root` + `try_files`
|
| 32 |
+
|
| 33 |
+
## [Unreleased]
|
| 34 |
+
|
| 35 |
+
### Planned
|
| 36 |
+
|
| 37 |
+
- Multi-user admin management UI
|
| 38 |
+
- Backup versioning and restore from specific snapshots
|
| 39 |
+
- Prometheus / Grafana monitoring integration
|
| 40 |
+
- External PostgreSQL support option
|
| 41 |
+
- GitHub Actions CI for image freshness checks
|
| 42 |
+
- Kubernetes deployment manifests
|
|
@@ -0,0 +1,36 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Code of Conduct
|
| 2 |
+
|
| 3 |
+
## Our Pledge
|
| 4 |
+
|
| 5 |
+
We pledge to make participation in HuggingFlow a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity, level of experience, nationality, personal appearance, race, religion, or sexual identity.
|
| 6 |
+
|
| 7 |
+
## Our Standards
|
| 8 |
+
|
| 9 |
+
**Positive behavior includes:**
|
| 10 |
+
- Using welcoming and inclusive language
|
| 11 |
+
- Being respectful of differing viewpoints and experience
|
| 12 |
+
- Gracefully accepting constructive criticism
|
| 13 |
+
- Focusing on what is best for the community
|
| 14 |
+
- Showing empathy toward other contributors
|
| 15 |
+
|
| 16 |
+
**Unacceptable behavior includes:**
|
| 17 |
+
- Trolling, insulting, or derogatory comments
|
| 18 |
+
- Harassment in any form, public or private
|
| 19 |
+
- Publishing others' private information without explicit permission
|
| 20 |
+
- Other conduct that could reasonably be considered inappropriate
|
| 21 |
+
|
| 22 |
+
## Enforcement
|
| 23 |
+
|
| 24 |
+
Project maintainers may remove, edit, or reject comments, commits, code, issues, and other contributions that violate this Code of Conduct. Repeated violations may result in a temporary or permanent ban.
|
| 25 |
+
|
| 26 |
+
## Scope
|
| 27 |
+
|
| 28 |
+
This Code of Conduct applies in all project spaces (GitHub issues, pull requests, discussions) and in public spaces when an individual represents the project.
|
| 29 |
+
|
| 30 |
+
## Reporting
|
| 31 |
+
|
| 32 |
+
Report violations by opening a private GitHub issue or contacting the maintainers directly. All reports will be handled confidentially.
|
| 33 |
+
|
| 34 |
+
## Attribution
|
| 35 |
+
|
| 36 |
+
Adapted from the [Contributor Covenant](https://www.contributor-covenant.org/), version 2.1.
|
|
@@ -1,38 +1,75 @@
|
|
| 1 |
# Contributing to HuggingFlow
|
| 2 |
|
| 3 |
-
|
| 4 |
|
| 5 |
-
##
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 6 |
|
| 7 |
1. Fork the repository
|
| 8 |
2. Create a feature branch: `git checkout -b feat/your-feature`
|
| 9 |
-
3. Make
|
| 10 |
-
4. Commit
|
| 11 |
-
5.
|
| 12 |
|
| 13 |
-
|
| 14 |
|
| 15 |
-
|
| 16 |
|
| 17 |
```bash
|
| 18 |
-
|
| 19 |
-
|
| 20 |
-
|
| 21 |
-
|
| 22 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 23 |
```
|
| 24 |
|
| 25 |
-
|
| 26 |
-
or `http://localhost:7860/dashboard` for the status dashboard.
|
| 27 |
|
| 28 |
-
##
|
| 29 |
|
| 30 |
-
|
| 31 |
-
-
|
| 32 |
-
|
| 33 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 34 |
|
| 35 |
-
##
|
| 36 |
|
| 37 |
-
|
| 38 |
-
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
# Contributing to HuggingFlow
|
| 2 |
|
| 3 |
+
Thank you for your interest in contributing!
|
| 4 |
|
| 5 |
+
## Bug Reports
|
| 6 |
+
|
| 7 |
+
1. Check existing [issues](https://github.com/somratpro/HuggingFlow/issues) first
|
| 8 |
+
2. Open a new issue with:
|
| 9 |
+
- Clear title and description
|
| 10 |
+
- Steps to reproduce
|
| 11 |
+
- Expected vs actual behavior
|
| 12 |
+
- Your `LLM_MODEL` provider (no keys please)
|
| 13 |
+
- HF Space tier or Docker version if relevant
|
| 14 |
+
|
| 15 |
+
## Pull Requests
|
| 16 |
|
| 17 |
1. Fork the repository
|
| 18 |
2. Create a feature branch: `git checkout -b feat/your-feature`
|
| 19 |
+
3. Make changes and test locally (see below)
|
| 20 |
+
4. Commit using [Conventional Commits](https://www.conventionalcommits.org/): `feat:`, `fix:`, `docs:`, `refactor:`, `chore:`
|
| 21 |
+
5. Push and open a Pull Request describing what changed and why
|
| 22 |
|
| 23 |
+
Open an issue first for large or architectural changes.
|
| 24 |
|
| 25 |
+
## Local Development
|
| 26 |
|
| 27 |
```bash
|
| 28 |
+
# Clone
|
| 29 |
+
git clone https://github.com/somratpro/HuggingFlow
|
| 30 |
+
cd HuggingFlow
|
| 31 |
+
|
| 32 |
+
# Configure
|
| 33 |
+
cp .env.example .env
|
| 34 |
+
# Edit .env — set LLM_MODEL, LLM_API_KEY, optionally SERPER_API_KEY
|
| 35 |
+
|
| 36 |
+
# Build and run
|
| 37 |
+
docker-compose up --build
|
| 38 |
+
|
| 39 |
+
# Dashboard
|
| 40 |
+
open http://localhost:7860/
|
| 41 |
+
|
| 42 |
+
# Create admin account
|
| 43 |
+
open http://localhost:7860/setup
|
| 44 |
+
|
| 45 |
+
# Research workspace
|
| 46 |
+
open http://localhost:7860/workspace
|
| 47 |
+
|
| 48 |
+
# Health check
|
| 49 |
+
curl http://localhost:7860/health
|
| 50 |
```
|
| 51 |
|
| 52 |
+
Hot-reload for orchestration scripts: `docker-compose.yml` volume-mounts `start.sh`, `health-server.js`, `deerflow-sync.py`, and `nginx.conf` — edit and restart the container without a full rebuild.
|
|
|
|
| 53 |
|
| 54 |
+
## File Overview
|
| 55 |
|
| 56 |
+
| File | Purpose |
|
| 57 |
+
|------|---------|
|
| 58 |
+
| `Dockerfile` | Container build — pulls pre-built DeerFlow GHCR images |
|
| 59 |
+
| `start.sh` | Startup orchestration: config generation, service sequencing, graceful shutdown |
|
| 60 |
+
| `health-server.js` | Port 7860 public gateway — status dashboard + transparent reverse proxy to nginx |
|
| 61 |
+
| `nginx.conf` | nginx reverse proxy (port 7861) — routes API, frontend, LangGraph alias |
|
| 62 |
+
| `deerflow-sync.py` | HF Dataset backup/restore for SQLite DB + workspace files |
|
| 63 |
+
| `cloudflare-proxy.js` | Node.js `http`/`https` agent that routes traffic through a Cloudflare Worker |
|
| 64 |
+
| `cloudflare-proxy-setup.py` | Auto-provisions the Cloudflare outbound proxy Worker |
|
| 65 |
+
| `cloudflare-keepalive-setup.py` | Auto-provisions the Cloudflare keep-awake cron Worker |
|
| 66 |
+
| `docker-compose.yml` | Local development setup |
|
| 67 |
+
| `.env.example` | Full configuration reference with inline documentation |
|
| 68 |
|
| 69 |
+
## Guidelines
|
| 70 |
|
| 71 |
+
- Keep changes minimal and focused — avoid touching unrelated files
|
| 72 |
+
- Test with at least one LLM provider before submitting
|
| 73 |
+
- Update `README.md` and `.env.example` if you add new environment variables
|
| 74 |
+
- Update `CHANGELOG.md` with a summary under `[Unreleased]`
|
| 75 |
+
- Security issues: see [SECURITY.md](SECURITY.md) — do not open public issues for vulnerabilities
|
|
@@ -96,7 +96,6 @@ COPY --from=source --chown=1000:1000 /src/config.example.yaml /app/config.exampl
|
|
| 96 |
|
| 97 |
# ── Copy HuggingFlow runtime scripts ─────────────────────────────
|
| 98 |
COPY --chown=1000:1000 nginx.conf /etc/nginx/nginx.conf
|
| 99 |
-
COPY --chown=1000:1000 dashboard.html /app/dashboard.html
|
| 100 |
COPY --chown=1000:1000 start.sh /app/start.sh
|
| 101 |
COPY --chown=1000:1000 deerflow-sync.py /app/deerflow-sync.py
|
| 102 |
COPY --chown=1000:1000 health-server.js /app/health-server.js
|
|
|
|
| 96 |
|
| 97 |
# ── Copy HuggingFlow runtime scripts ─────────────────────────────
|
| 98 |
COPY --chown=1000:1000 nginx.conf /etc/nginx/nginx.conf
|
|
|
|
| 99 |
COPY --chown=1000:1000 start.sh /app/start.sh
|
| 100 |
COPY --chown=1000:1000 deerflow-sync.py /app/deerflow-sync.py
|
| 101 |
COPY --chown=1000:1000 health-server.js /app/health-server.js
|
|
@@ -1,6 +1,6 @@
|
|
| 1 |
MIT License
|
| 2 |
|
| 3 |
-
Copyright (c)
|
| 4 |
|
| 5 |
Permission is hereby granted, free of charge, to any person obtaining a copy
|
| 6 |
of this software and associated documentation files (the "Software"), to deal
|
|
|
|
| 1 |
MIT License
|
| 2 |
|
| 3 |
+
Copyright (c) 2026 somratpro
|
| 4 |
|
| 5 |
Permission is hereby granted, free of charge, to any person obtaining a copy
|
| 6 |
of this software and associated documentation files (the "Software"), to deal
|
|
@@ -167,6 +167,7 @@ Serper is strongly recommended for research quality. Sign up at [serper.dev](htt
|
|
| 167 |
HF Spaces shares IPs that some APIs block. The Cloudflare outbound proxy routes backend HTTP requests through a Cloudflare Worker, giving you a clean egress IP.
|
| 168 |
|
| 169 |
**Setup:**
|
|
|
|
| 170 |
1. Get a Cloudflare API token with **Workers Edit** permission
|
| 171 |
2. Set `CLOUDFLARE_WORKERS_TOKEN` in your Space secrets
|
| 172 |
3. On next start, `cloudflare-proxy-setup.py` auto-creates the Worker and sets `CLOUDFLARE_PROXY_URL`
|
|
@@ -236,6 +237,7 @@ nginx
|
|
| 236 |
| 3000 | Next.js frontend | internal only |
|
| 237 |
|
| 238 |
**Images used:**
|
|
|
|
| 239 |
- `ghcr.io/bytedance/deer-flow-backend:latest` — pre-built Python backend + `.venv`
|
| 240 |
- `ghcr.io/bytedance/deer-flow-frontend:latest` — pre-built Next.js + `node_modules`
|
| 241 |
- No source compilation — build time ~5 min instead of 30+ min
|
|
|
|
| 167 |
HF Spaces shares IPs that some APIs block. The Cloudflare outbound proxy routes backend HTTP requests through a Cloudflare Worker, giving you a clean egress IP.
|
| 168 |
|
| 169 |
**Setup:**
|
| 170 |
+
|
| 171 |
1. Get a Cloudflare API token with **Workers Edit** permission
|
| 172 |
2. Set `CLOUDFLARE_WORKERS_TOKEN` in your Space secrets
|
| 173 |
3. On next start, `cloudflare-proxy-setup.py` auto-creates the Worker and sets `CLOUDFLARE_PROXY_URL`
|
|
|
|
| 237 |
| 3000 | Next.js frontend | internal only |
|
| 238 |
|
| 239 |
**Images used:**
|
| 240 |
+
|
| 241 |
- `ghcr.io/bytedance/deer-flow-backend:latest` — pre-built Python backend + `.venv`
|
| 242 |
- `ghcr.io/bytedance/deer-flow-frontend:latest` — pre-built Next.js + `node_modules`
|
| 243 |
- No source compilation — build time ~5 min instead of 30+ min
|
|
@@ -0,0 +1,73 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Security Policy
|
| 2 |
+
|
| 3 |
+
## Supported Versions
|
| 4 |
+
|
| 5 |
+
| Version | Supported |
|
| 6 |
+
|---------|-----------|
|
| 7 |
+
| 1.x | ✅ Yes |
|
| 8 |
+
|
| 9 |
+
## Reporting a Vulnerability
|
| 10 |
+
|
| 11 |
+
**Do NOT open a public GitHub issue for security vulnerabilities.**
|
| 12 |
+
|
| 13 |
+
Instead, report privately:
|
| 14 |
+
|
| 15 |
+
- Open a [GitHub Security Advisory](https://github.com/somratpro/HuggingFlow/security/advisories/new) (preferred)
|
| 16 |
+
- Or email the maintainer directly (see GitHub profile)
|
| 17 |
+
|
| 18 |
+
Include:
|
| 19 |
+
- Description of the vulnerability
|
| 20 |
+
- Steps to reproduce
|
| 21 |
+
- Potential impact
|
| 22 |
+
- Suggested fix (if any)
|
| 23 |
+
|
| 24 |
+
We will respond within 48 hours and aim to patch critical issues within 7 days.
|
| 25 |
+
|
| 26 |
+
## Security Best Practices
|
| 27 |
+
|
| 28 |
+
### Secrets Management
|
| 29 |
+
|
| 30 |
+
- **Never commit secrets to git** — use HF Space secrets or environment variables only
|
| 31 |
+
- `LLM_API_KEY`: Store as HF Space secret — never in code or Dockerfile `ENV`
|
| 32 |
+
- `HF_TOKEN`: Same — HF Space secret only
|
| 33 |
+
- `AUTH_JWT_SECRET`: Generate a strong random value (`openssl rand -base64 32`); without it, a new secret is generated on every restart (sessions lost)
|
| 34 |
+
- `CLOUDFLARE_WORKERS_TOKEN`: HF Space secret only
|
| 35 |
+
- Rotate all tokens immediately if accidentally exposed
|
| 36 |
+
|
| 37 |
+
### Network Security
|
| 38 |
+
|
| 39 |
+
- `umask 0077` enforced at startup — all files created owner-only by default
|
| 40 |
+
- nginx binds on `127.0.0.1:7861` (internal only) — not exposed externally
|
| 41 |
+
- FastAPI backend binds on `127.0.0.1:8001` (internal only)
|
| 42 |
+
- Next.js frontend binds on `127.0.0.1:3000` (internal only)
|
| 43 |
+
- Only `health-server.js` on port `7860` is publicly accessible
|
| 44 |
+
|
| 45 |
+
### Container Security
|
| 46 |
+
|
| 47 |
+
- Non-root user `user` (UID 1000) — required by HF Spaces and a security best practice
|
| 48 |
+
- Based on `python:3.12-slim-bookworm` — minimal attack surface
|
| 49 |
+
- No secrets baked into the image — all configuration via environment variables
|
| 50 |
+
- Cloudflare proxy uses an auto-generated shared secret for Worker authentication
|
| 51 |
+
|
| 52 |
+
### DeerFlow Auth
|
| 53 |
+
|
| 54 |
+
- DeerFlow v2 uses JWT auth; all `/api/*` routes require authentication
|
| 55 |
+
- Create your admin account at `/setup` immediately after first deploy — it is only accessible until an admin exists
|
| 56 |
+
- Set `AUTH_JWT_SECRET` to a strong random value or sessions reset on every restart
|
| 57 |
+
|
| 58 |
+
### HF Dataset Backup
|
| 59 |
+
|
| 60 |
+
- Backup dataset is created as **private** automatically
|
| 61 |
+
- The archive contains your full SQLite database (threads, messages, API key hashes) — protect your `HF_TOKEN` and dataset access
|
| 62 |
+
- Do not share the backup dataset URL publicly
|
| 63 |
+
|
| 64 |
+
### Cloudflare Worker Proxy
|
| 65 |
+
|
| 66 |
+
- The Cloudflare Worker proxy can observe proxied HTTP traffic — review the `cloudflare-proxy.js` source before enabling
|
| 67 |
+
- The Worker is scoped to specific domains; set `CLOUDFLARE_PROXY_DOMAINS` to restrict further
|
| 68 |
+
|
| 69 |
+
## Known Limitations
|
| 70 |
+
|
| 71 |
+
- **HF Spaces free tier is public** — anyone can reach your Space URL. DeerFlow's auth (`/setup` → JWT) protects the API and UI, but the dashboard at `/` and `/health` are intentionally unauthenticated
|
| 72 |
+
- **Ephemeral storage without backup** — if `HF_TOKEN` is not set, all threads are lost on restart
|
| 73 |
+
- **Single-worker backend** — `uvicorn --workers 1` prevents SQLite race conditions; for high-concurrency workloads, consider a dedicated server with PostgreSQL
|
|
@@ -1,308 +0,0 @@
|
|
| 1 |
-
<!DOCTYPE html>
|
| 2 |
-
<html lang="en">
|
| 3 |
-
<head>
|
| 4 |
-
<meta charset="UTF-8" />
|
| 5 |
-
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
| 6 |
-
<title>HuggingFlow — DeerFlow on Hugging Face</title>
|
| 7 |
-
<style>
|
| 8 |
-
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
| 9 |
-
|
| 10 |
-
:root {
|
| 11 |
-
--bg: #0f1117;
|
| 12 |
-
--surface: #1a1d27;
|
| 13 |
-
--border: #2a2d3e;
|
| 14 |
-
--accent: #ff9d00;
|
| 15 |
-
--accent2: #7c3aed;
|
| 16 |
-
--text: #e2e8f0;
|
| 17 |
-
--muted: #64748b;
|
| 18 |
-
--green: #22c55e;
|
| 19 |
-
--card: #161927;
|
| 20 |
-
}
|
| 21 |
-
|
| 22 |
-
body {
|
| 23 |
-
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
|
| 24 |
-
background: var(--bg);
|
| 25 |
-
color: var(--text);
|
| 26 |
-
min-height: 100vh;
|
| 27 |
-
display: flex;
|
| 28 |
-
flex-direction: column;
|
| 29 |
-
align-items: center;
|
| 30 |
-
justify-content: center;
|
| 31 |
-
padding: 2rem 1rem;
|
| 32 |
-
}
|
| 33 |
-
|
| 34 |
-
.container { max-width: 720px; width: 100%; }
|
| 35 |
-
|
| 36 |
-
/* ── Header ─────────────────────────────────────────── */
|
| 37 |
-
.header {
|
| 38 |
-
text-align: center;
|
| 39 |
-
margin-bottom: 3rem;
|
| 40 |
-
}
|
| 41 |
-
|
| 42 |
-
.logo {
|
| 43 |
-
font-size: 3.5rem;
|
| 44 |
-
line-height: 1;
|
| 45 |
-
margin-bottom: 0.75rem;
|
| 46 |
-
filter: drop-shadow(0 0 24px rgba(255,157,0,0.4));
|
| 47 |
-
}
|
| 48 |
-
|
| 49 |
-
.title {
|
| 50 |
-
font-size: 2rem;
|
| 51 |
-
font-weight: 700;
|
| 52 |
-
letter-spacing: -0.02em;
|
| 53 |
-
background: linear-gradient(135deg, #ff9d00 0%, #ff6b35 50%, #7c3aed 100%);
|
| 54 |
-
-webkit-background-clip: text;
|
| 55 |
-
-webkit-text-fill-color: transparent;
|
| 56 |
-
background-clip: text;
|
| 57 |
-
margin-bottom: 0.5rem;
|
| 58 |
-
}
|
| 59 |
-
|
| 60 |
-
.subtitle {
|
| 61 |
-
color: var(--muted);
|
| 62 |
-
font-size: 0.95rem;
|
| 63 |
-
}
|
| 64 |
-
|
| 65 |
-
.subtitle a {
|
| 66 |
-
color: var(--accent);
|
| 67 |
-
text-decoration: none;
|
| 68 |
-
}
|
| 69 |
-
|
| 70 |
-
.subtitle a:hover { text-decoration: underline; }
|
| 71 |
-
|
| 72 |
-
/* ── Status pill ────────────────────────────────────── */
|
| 73 |
-
.status-pill {
|
| 74 |
-
display: inline-flex;
|
| 75 |
-
align-items: center;
|
| 76 |
-
gap: 0.5rem;
|
| 77 |
-
background: rgba(34,197,94,0.1);
|
| 78 |
-
border: 1px solid rgba(34,197,94,0.3);
|
| 79 |
-
color: var(--green);
|
| 80 |
-
font-size: 0.8rem;
|
| 81 |
-
font-weight: 600;
|
| 82 |
-
padding: 0.3rem 0.85rem;
|
| 83 |
-
border-radius: 999px;
|
| 84 |
-
margin-bottom: 2rem;
|
| 85 |
-
letter-spacing: 0.04em;
|
| 86 |
-
text-transform: uppercase;
|
| 87 |
-
}
|
| 88 |
-
|
| 89 |
-
.dot {
|
| 90 |
-
width: 7px; height: 7px;
|
| 91 |
-
background: var(--green);
|
| 92 |
-
border-radius: 50%;
|
| 93 |
-
animation: pulse 2s ease-in-out infinite;
|
| 94 |
-
}
|
| 95 |
-
|
| 96 |
-
@keyframes pulse {
|
| 97 |
-
0%,100% { opacity: 1; transform: scale(1); }
|
| 98 |
-
50% { opacity: 0.5; transform: scale(0.8); }
|
| 99 |
-
}
|
| 100 |
-
|
| 101 |
-
/* ── Primary CTA ────────────────────────────────────── */
|
| 102 |
-
.cta-row {
|
| 103 |
-
display: flex;
|
| 104 |
-
gap: 0.75rem;
|
| 105 |
-
justify-content: center;
|
| 106 |
-
margin-bottom: 2.5rem;
|
| 107 |
-
flex-wrap: wrap;
|
| 108 |
-
}
|
| 109 |
-
|
| 110 |
-
.btn {
|
| 111 |
-
display: inline-flex;
|
| 112 |
-
align-items: center;
|
| 113 |
-
gap: 0.45rem;
|
| 114 |
-
font-size: 0.9rem;
|
| 115 |
-
font-weight: 600;
|
| 116 |
-
padding: 0.65rem 1.4rem;
|
| 117 |
-
border-radius: 8px;
|
| 118 |
-
text-decoration: none;
|
| 119 |
-
transition: all 0.15s ease;
|
| 120 |
-
border: 1px solid transparent;
|
| 121 |
-
}
|
| 122 |
-
|
| 123 |
-
.btn-primary {
|
| 124 |
-
background: linear-gradient(135deg, #ff9d00, #ff6b35);
|
| 125 |
-
color: #fff;
|
| 126 |
-
box-shadow: 0 4px 16px rgba(255,157,0,0.3);
|
| 127 |
-
}
|
| 128 |
-
|
| 129 |
-
.btn-primary:hover {
|
| 130 |
-
transform: translateY(-1px);
|
| 131 |
-
box-shadow: 0 6px 24px rgba(255,157,0,0.45);
|
| 132 |
-
}
|
| 133 |
-
|
| 134 |
-
.btn-ghost {
|
| 135 |
-
background: var(--surface);
|
| 136 |
-
color: var(--text);
|
| 137 |
-
border-color: var(--border);
|
| 138 |
-
}
|
| 139 |
-
|
| 140 |
-
.btn-ghost:hover {
|
| 141 |
-
background: var(--border);
|
| 142 |
-
transform: translateY(-1px);
|
| 143 |
-
}
|
| 144 |
-
|
| 145 |
-
/* ── Info grid ──────────────────────────────────────── */
|
| 146 |
-
.grid {
|
| 147 |
-
display: grid;
|
| 148 |
-
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
| 149 |
-
gap: 1rem;
|
| 150 |
-
margin-bottom: 2rem;
|
| 151 |
-
}
|
| 152 |
-
|
| 153 |
-
.card {
|
| 154 |
-
background: var(--card);
|
| 155 |
-
border: 1px solid var(--border);
|
| 156 |
-
border-radius: 12px;
|
| 157 |
-
padding: 1.25rem 1.5rem;
|
| 158 |
-
}
|
| 159 |
-
|
| 160 |
-
.card-label {
|
| 161 |
-
font-size: 0.7rem;
|
| 162 |
-
font-weight: 600;
|
| 163 |
-
text-transform: uppercase;
|
| 164 |
-
letter-spacing: 0.08em;
|
| 165 |
-
color: var(--muted);
|
| 166 |
-
margin-bottom: 0.5rem;
|
| 167 |
-
}
|
| 168 |
-
|
| 169 |
-
.card-value {
|
| 170 |
-
font-size: 1rem;
|
| 171 |
-
font-weight: 600;
|
| 172 |
-
color: var(--text);
|
| 173 |
-
}
|
| 174 |
-
|
| 175 |
-
.card-value.mono { font-family: "SF Mono", "Fira Code", monospace; font-size: 0.85rem; }
|
| 176 |
-
|
| 177 |
-
/* ── Routes table ───────────────────────────────────── */
|
| 178 |
-
.routes {
|
| 179 |
-
background: var(--card);
|
| 180 |
-
border: 1px solid var(--border);
|
| 181 |
-
border-radius: 12px;
|
| 182 |
-
overflow: hidden;
|
| 183 |
-
margin-bottom: 2rem;
|
| 184 |
-
}
|
| 185 |
-
|
| 186 |
-
.routes-header {
|
| 187 |
-
padding: 0.85rem 1.5rem;
|
| 188 |
-
font-size: 0.75rem;
|
| 189 |
-
font-weight: 600;
|
| 190 |
-
text-transform: uppercase;
|
| 191 |
-
letter-spacing: 0.08em;
|
| 192 |
-
color: var(--muted);
|
| 193 |
-
border-bottom: 1px solid var(--border);
|
| 194 |
-
}
|
| 195 |
-
|
| 196 |
-
.route-row {
|
| 197 |
-
display: flex;
|
| 198 |
-
align-items: center;
|
| 199 |
-
justify-content: space-between;
|
| 200 |
-
padding: 0.85rem 1.5rem;
|
| 201 |
-
border-bottom: 1px solid var(--border);
|
| 202 |
-
text-decoration: none;
|
| 203 |
-
color: var(--text);
|
| 204 |
-
transition: background 0.1s;
|
| 205 |
-
}
|
| 206 |
-
|
| 207 |
-
.route-row:last-child { border-bottom: none; }
|
| 208 |
-
|
| 209 |
-
.route-row:hover { background: var(--border); }
|
| 210 |
-
|
| 211 |
-
.route-path {
|
| 212 |
-
font-family: "SF Mono", "Fira Code", monospace;
|
| 213 |
-
font-size: 0.85rem;
|
| 214 |
-
color: var(--accent);
|
| 215 |
-
}
|
| 216 |
-
|
| 217 |
-
.route-desc { font-size: 0.85rem; color: var(--muted); }
|
| 218 |
-
|
| 219 |
-
.route-arrow { color: var(--muted); font-size: 0.8rem; }
|
| 220 |
-
|
| 221 |
-
/* ── Footer ─────────────────────────────────────────── */
|
| 222 |
-
.footer {
|
| 223 |
-
text-align: center;
|
| 224 |
-
font-size: 0.78rem;
|
| 225 |
-
color: var(--muted);
|
| 226 |
-
}
|
| 227 |
-
|
| 228 |
-
.footer a { color: var(--muted); }
|
| 229 |
-
.footer a:hover { color: var(--text); }
|
| 230 |
-
</style>
|
| 231 |
-
</head>
|
| 232 |
-
<body>
|
| 233 |
-
<div class="container">
|
| 234 |
-
<div class="header">
|
| 235 |
-
<div class="logo">🦌</div>
|
| 236 |
-
<div class="title">HuggingFlow</div>
|
| 237 |
-
<p class="subtitle">
|
| 238 |
-
<a href="https://github.com/bytedance/deer-flow" target="_blank" rel="noopener">DeerFlow</a>
|
| 239 |
-
research agent · hosted on Hugging Face Spaces
|
| 240 |
-
</p>
|
| 241 |
-
</div>
|
| 242 |
-
|
| 243 |
-
<div style="text-align:center">
|
| 244 |
-
<span class="status-pill"><span class="dot"></span>Running</span>
|
| 245 |
-
</div>
|
| 246 |
-
|
| 247 |
-
<div class="cta-row">
|
| 248 |
-
<a href="/workspace" class="btn btn-primary">⚡ Open Workspace</a>
|
| 249 |
-
<a href="/setup" class="btn btn-ghost">🔧 Admin Setup</a>
|
| 250 |
-
<a href="/docs" class="btn btn-ghost">📖 API Docs</a>
|
| 251 |
-
</div>
|
| 252 |
-
|
| 253 |
-
<div class="grid">
|
| 254 |
-
<div class="card">
|
| 255 |
-
<div class="card-label">Stack</div>
|
| 256 |
-
<div class="card-value">DeerFlow v2</div>
|
| 257 |
-
</div>
|
| 258 |
-
<div class="card">
|
| 259 |
-
<div class="card-label">Backend</div>
|
| 260 |
-
<div class="card-value mono">FastAPI · port 8001</div>
|
| 261 |
-
</div>
|
| 262 |
-
<div class="card">
|
| 263 |
-
<div class="card-label">Frontend</div>
|
| 264 |
-
<div class="card-value mono">Next.js · port 3000</div>
|
| 265 |
-
</div>
|
| 266 |
-
<div class="card">
|
| 267 |
-
<div class="card-label">Proxy</div>
|
| 268 |
-
<div class="card-value mono">nginx · port 7861</div>
|
| 269 |
-
</div>
|
| 270 |
-
</div>
|
| 271 |
-
|
| 272 |
-
<div class="routes">
|
| 273 |
-
<div class="routes-header">Available Routes</div>
|
| 274 |
-
<a href="/" class="route-row">
|
| 275 |
-
<span class="route-path">/</span>
|
| 276 |
-
<span class="route-desc">This dashboard</span>
|
| 277 |
-
<span class="route-arrow">→</span>
|
| 278 |
-
</a>
|
| 279 |
-
<a href="/workspace" class="route-row">
|
| 280 |
-
<span class="route-path">/workspace</span>
|
| 281 |
-
<span class="route-desc">DeerFlow research workspace</span>
|
| 282 |
-
<span class="route-arrow">→</span>
|
| 283 |
-
</a>
|
| 284 |
-
<a href="/setup" class="route-row">
|
| 285 |
-
<span class="route-path">/setup</span>
|
| 286 |
-
<span class="route-desc">Admin account creation (first boot)</span>
|
| 287 |
-
<span class="route-arrow">→</span>
|
| 288 |
-
</a>
|
| 289 |
-
<a href="/api/health" class="route-row">
|
| 290 |
-
<span class="route-path">/api/health</span>
|
| 291 |
-
<span class="route-desc">Backend health check (JSON)</span>
|
| 292 |
-
<span class="route-arrow">→</span>
|
| 293 |
-
</a>
|
| 294 |
-
<a href="/docs" class="route-row">
|
| 295 |
-
<span class="route-path">/docs</span>
|
| 296 |
-
<span class="route-desc">Swagger API reference</span>
|
| 297 |
-
<span class="route-arrow">→</span>
|
| 298 |
-
</a>
|
| 299 |
-
</div>
|
| 300 |
-
|
| 301 |
-
<p class="footer">
|
| 302 |
-
<a href="https://github.com/somratpro/HuggingFlow" target="_blank" rel="noopener">HuggingFlow</a>
|
| 303 |
-
· MIT License ·
|
| 304 |
-
<a href="https://github.com/bytedance/deer-flow" target="_blank" rel="noopener">DeerFlow by ByteDance</a>
|
| 305 |
-
</p>
|
| 306 |
-
</div>
|
| 307 |
-
</body>
|
| 308 |
-
</html>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@@ -0,0 +1,71 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version: '3.8'
|
| 2 |
+
|
| 3 |
+
# HuggingFlow — local development
|
| 4 |
+
# Usage:
|
| 5 |
+
# cp .env.example .env # fill in LLM_MODEL, LLM_API_KEY, etc.
|
| 6 |
+
# docker-compose up --build
|
| 7 |
+
|
| 8 |
+
services:
|
| 9 |
+
huggingflow:
|
| 10 |
+
build:
|
| 11 |
+
context: .
|
| 12 |
+
dockerfile: Dockerfile
|
| 13 |
+
container_name: huggingflow
|
| 14 |
+
environment:
|
| 15 |
+
# ── Required ────────────────────────────────────────────────
|
| 16 |
+
LLM_MODEL: ${LLM_MODEL:-openai/gpt-4o}
|
| 17 |
+
LLM_API_KEY: ${LLM_API_KEY:-}
|
| 18 |
+
|
| 19 |
+
# ── Search (optional) ────────────────────────────────────────
|
| 20 |
+
SERPER_API_KEY: ${SERPER_API_KEY:-}
|
| 21 |
+
TAVILY_API_KEY: ${TAVILY_API_KEY:-}
|
| 22 |
+
JINA_API_KEY: ${JINA_API_KEY:-}
|
| 23 |
+
|
| 24 |
+
# ── Auth ─────────────────────────────────────────────────────
|
| 25 |
+
AUTH_JWT_SECRET: ${AUTH_JWT_SECRET:-dev-secret-change-in-production}
|
| 26 |
+
|
| 27 |
+
# ── HF Dataset backup (optional) ─────────────────────────────
|
| 28 |
+
HF_TOKEN: ${HF_TOKEN:-}
|
| 29 |
+
HF_USERNAME: ${HF_USERNAME:-}
|
| 30 |
+
BACKUP_DATASET_NAME: ${BACKUP_DATASET_NAME:-huggingflow-backup}
|
| 31 |
+
SYNC_INTERVAL: ${SYNC_INTERVAL:-600}
|
| 32 |
+
|
| 33 |
+
# ── Custom LLM endpoint (optional) ───────────────────────────
|
| 34 |
+
CUSTOM_BASE_URL: ${CUSTOM_BASE_URL:-}
|
| 35 |
+
|
| 36 |
+
# ── Cloudflare (optional) ─────────────────────────────────────
|
| 37 |
+
CLOUDFLARE_WORKERS_TOKEN: ${CLOUDFLARE_WORKERS_TOKEN:-}
|
| 38 |
+
CLOUDFLARE_PROXY_URL: ${CLOUDFLARE_PROXY_URL:-}
|
| 39 |
+
|
| 40 |
+
# ── Timeouts ─────────────────────────────────────────────────
|
| 41 |
+
BACKEND_READY_TIMEOUT: ${BACKEND_READY_TIMEOUT:-120}
|
| 42 |
+
FRONTEND_READY_TIMEOUT: ${FRONTEND_READY_TIMEOUT:-120}
|
| 43 |
+
|
| 44 |
+
# ── Privacy ──────────────────────────────────────────────────
|
| 45 |
+
DO_NOT_TRACK: "1"
|
| 46 |
+
|
| 47 |
+
ports:
|
| 48 |
+
- "7860:7860" # Public: status dashboard + reverse proxy
|
| 49 |
+
|
| 50 |
+
volumes:
|
| 51 |
+
# Persist data between docker-compose up/down cycles
|
| 52 |
+
- huggingflow_data:/app/data
|
| 53 |
+
|
| 54 |
+
# Hot-reload orchestration scripts (no rebuild needed)
|
| 55 |
+
- ./start.sh:/app/start.sh
|
| 56 |
+
- ./health-server.js:/app/health-server.js
|
| 57 |
+
- ./deerflow-sync.py:/app/deerflow-sync.py
|
| 58 |
+
- ./nginx.conf:/etc/nginx/nginx.conf
|
| 59 |
+
|
| 60 |
+
healthcheck:
|
| 61 |
+
test: ["CMD", "curl", "-fsS", "http://localhost:7860/health"]
|
| 62 |
+
interval: 30s
|
| 63 |
+
timeout: 10s
|
| 64 |
+
retries: 3
|
| 65 |
+
start_period: 90s
|
| 66 |
+
|
| 67 |
+
restart: unless-stopped
|
| 68 |
+
|
| 69 |
+
volumes:
|
| 70 |
+
huggingflow_data:
|
| 71 |
+
driver: local
|
|
@@ -136,9 +136,9 @@ function renderDashboard({ backendUp, frontendUp, uptimeHuman, sync, keepalive }
|
|
| 136 |
return `<!doctype html>
|
| 137 |
<html lang="en">
|
| 138 |
<head>
|
| 139 |
-
<meta charset="utf-8"/>
|
| 140 |
-
<meta name="viewport" content="width=device-width,initial-scale=1"/>
|
| 141 |
-
<meta http-equiv="refresh" content="30"/>
|
| 142 |
<title>HuggingFlow Dashboard</title>
|
| 143 |
<style>
|
| 144 |
:root{color-scheme:dark;--bg:#080c10;--panel:#0e1218;--line:#1e2530;--text:#eef2f7;--muted:#6b7a8d;--soft:#a8b5c4;--good:#22c55e;--warn:#f59e0b;--bad:#f43f5e;--accent:#3b82f6}
|
|
@@ -199,7 +199,7 @@ function renderDashboard({ backendUp, frontendUp, uptimeHuman, sync, keepalive }
|
|
| 199 |
});
|
| 200 |
</script>
|
| 201 |
</body>
|
| 202 |
-
</html>`
|
| 203 |
}
|
| 204 |
|
| 205 |
// ── Request handler ────────────────────────────────────────────────────────
|
|
|
|
| 136 |
return `<!doctype html>
|
| 137 |
<html lang="en">
|
| 138 |
<head>
|
| 139 |
+
<meta charset="utf-8" />
|
| 140 |
+
<meta name="viewport" content="width=device-width,initial-scale=1" />
|
| 141 |
+
<meta http-equiv="refresh" content="30" />
|
| 142 |
<title>HuggingFlow Dashboard</title>
|
| 143 |
<style>
|
| 144 |
:root{color-scheme:dark;--bg:#080c10;--panel:#0e1218;--line:#1e2530;--text:#eef2f7;--muted:#6b7a8d;--soft:#a8b5c4;--good:#22c55e;--warn:#f59e0b;--bad:#f43f5e;--accent:#3b82f6}
|
|
|
|
| 199 |
});
|
| 200 |
</script>
|
| 201 |
</body>
|
| 202 |
+
</html>`;
|
| 203 |
}
|
| 204 |
|
| 205 |
// ── Request handler ────────────────────────────────────────────────────────
|