Spaces:
Sleeping
π Implement User Privacy Protection for HF Spaces
Browse filesβ
Problem solved:
β’ Fixed multi-user data sharing issue in HF Spaces environment
β’ All users were seeing each other's uploaded traces and data
β’ SQLite database was globally shared across all sessions
π― Root cause analysis:
β’ HF Spaces containers share the same persistent database file
β’ All users access the same datasets/db/agent_monitoring.db instance
β’ No user session isolation or data privacy protection
β‘ Privacy solutions implemented:
β’ HF Spaces detection: Check SPACE_ID environment variable
β’ In-memory database: Use sqlite:///:memory: for HF Spaces
β’ Session isolation: Each container restart gets fresh database
β’ Local development: Still uses persistent file database
π User experience improvements:
β’ Added privacy notice banner for HF Spaces users
β’ Clear messaging about data temporariness
β’ Backend logging shows database type on startup
β’ Frontend detects HF environment automatically
π Security impact:
β’ Eliminates cross-user data visibility
β’ Prevents sensitive trace information leakage
β’ Ensures session-based data isolation
β’ Maintains full functionality while protecting privacy
- backend/app.py +8 -0
- backend/database/__init__.py +13 -2
- frontend/src/components/layout/MainWorkspace.tsx +13 -0
- frontend/src/lib/config.ts +11 -0
|
@@ -77,6 +77,14 @@ async def startup_event():
|
|
| 77 |
try:
|
| 78 |
from backend.database.init_db import init_database
|
| 79 |
init_database(reset=False, force=False)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 80 |
logger.info("ποΈ Database initialized successfully")
|
| 81 |
except Exception as e:
|
| 82 |
logger.error(f"β Database initialization failed: {e}")
|
|
|
|
| 77 |
try:
|
| 78 |
from backend.database.init_db import init_database
|
| 79 |
init_database(reset=False, force=False)
|
| 80 |
+
|
| 81 |
+
# Show database type info
|
| 82 |
+
if os.getenv("SPACE_ID"):
|
| 83 |
+
logger.info("π HF Spaces: Using in-memory database for user privacy")
|
| 84 |
+
logger.info("π Note: Data will be cleared when container restarts")
|
| 85 |
+
else:
|
| 86 |
+
logger.info("πΎ Local development: Using persistent database")
|
| 87 |
+
|
| 88 |
logger.info("ποΈ Database initialized successfully")
|
| 89 |
except Exception as e:
|
| 90 |
logger.error(f"β Database initialization failed: {e}")
|
|
@@ -11,8 +11,19 @@ from sqlalchemy.orm import sessionmaker, scoped_session
|
|
| 11 |
# Get the absolute path to the project root directory
|
| 12 |
ROOT_DIR = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
| 13 |
|
| 14 |
-
# Database URL
|
| 15 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 16 |
|
| 17 |
# Create engine
|
| 18 |
engine = create_engine(DATABASE_URL, connect_args={"check_same_thread": False})
|
|
|
|
| 11 |
# Get the absolute path to the project root directory
|
| 12 |
ROOT_DIR = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
| 13 |
|
| 14 |
+
# Database URL - Use in-memory SQLite for HF Spaces to avoid data sharing between users
|
| 15 |
+
# This ensures each container restart gets a fresh database
|
| 16 |
+
import uuid
|
| 17 |
+
session_id = str(uuid.uuid4())[:8]
|
| 18 |
+
|
| 19 |
+
# For HF Spaces: Use in-memory database to prevent user data sharing
|
| 20 |
+
# For local development: Use file database
|
| 21 |
+
if os.getenv("SPACE_ID"): # HF Spaces environment
|
| 22 |
+
DATABASE_URL = "sqlite:///:memory:"
|
| 23 |
+
print(f"π HF Spaces: Using in-memory database (session: {session_id})")
|
| 24 |
+
else:
|
| 25 |
+
DATABASE_URL = f"sqlite:///{os.path.join(ROOT_DIR, 'datasets/db/agent_monitoring.db')}"
|
| 26 |
+
print(f"πΎ Local: Using persistent database")
|
| 27 |
|
| 28 |
# Create engine
|
| 29 |
engine = create_engine(DATABASE_URL, connect_args={"check_same_thread": False})
|
|
@@ -16,6 +16,7 @@ import { ConnectionsView } from "../features/connections/ConnectionsView";
|
|
| 16 |
import { UploadView } from "../features/upload/UploadView";
|
| 17 |
import { Button } from "@/components/ui/button";
|
| 18 |
import { ArrowLeft } from "lucide-react";
|
|
|
|
| 19 |
|
| 20 |
interface MainWorkspaceProps {
|
| 21 |
isSidebarCollapsed: boolean;
|
|
@@ -391,6 +392,18 @@ export function MainWorkspace({
|
|
| 391 |
</div>
|
| 392 |
)}
|
| 393 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 394 |
{/* Main Content */}
|
| 395 |
<div className="flex-1 flex flex-col min-h-0">{renderView()}</div>
|
| 396 |
</div>
|
|
|
|
| 16 |
import { UploadView } from "../features/upload/UploadView";
|
| 17 |
import { Button } from "@/components/ui/button";
|
| 18 |
import { ArrowLeft } from "lucide-react";
|
| 19 |
+
import { IS_HF_SPACES } from "@/lib/config";
|
| 20 |
|
| 21 |
interface MainWorkspaceProps {
|
| 22 |
isSidebarCollapsed: boolean;
|
|
|
|
| 392 |
</div>
|
| 393 |
)}
|
| 394 |
|
| 395 |
+
{/* HF Spaces Privacy Notice */}
|
| 396 |
+
{IS_HF_SPACES && (
|
| 397 |
+
<div className="bg-amber-50 border-b border-amber-200 px-6 py-3 text-sm text-amber-800 flex items-center gap-2">
|
| 398 |
+
<span className="text-amber-600">π</span>
|
| 399 |
+
<span>
|
| 400 |
+
<strong>Privacy Notice:</strong> You're using AgentGraph on Hugging Face Spaces.
|
| 401 |
+
Your data is stored in a temporary session and will be cleared when the container restarts.
|
| 402 |
+
Data is not shared between users or sessions.
|
| 403 |
+
</span>
|
| 404 |
+
</div>
|
| 405 |
+
)}
|
| 406 |
+
|
| 407 |
{/* Main Content */}
|
| 408 |
<div className="flex-1 flex flex-col min-h-0">{renderView()}</div>
|
| 409 |
</div>
|
|
@@ -24,3 +24,14 @@ export const getApiBase = () => {
|
|
| 24 |
};
|
| 25 |
|
| 26 |
export const API_BASE = getApiBase();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 24 |
};
|
| 25 |
|
| 26 |
export const API_BASE = getApiBase();
|
| 27 |
+
|
| 28 |
+
// Check if running on Hugging Face Spaces
|
| 29 |
+
export const isHuggingFaceSpaces = () => {
|
| 30 |
+
if (typeof window === "undefined") return false;
|
| 31 |
+
|
| 32 |
+
return window.location.hostname.includes("huggingface.co") ||
|
| 33 |
+
window.location.hostname.includes("hf.space") ||
|
| 34 |
+
window.location.hostname.endsWith(".hf.space");
|
| 35 |
+
};
|
| 36 |
+
|
| 37 |
+
export const IS_HF_SPACES = isHuggingFaceSpaces();
|