Danvas
Danvas
DashboardSupportWelcome

👤 USER DOCS

User Guides

Getting Started

Getting StartedDashboard & OnboardingApp Settings

Tutorials

Tutorial: Setting Up Shift Tasks & ClosersTutorial: Managing Incidents in the InboxTutorial: Tracking Compliance & Sync StatusTutorial: Operational Workflows with the AI AssistantTutorial: Building & Deploying Custom Checklists

Daily Operations (Staff)

Shift Workspace & TasksService Day SetupDaily Line-UpStaff Service Day ReportsForms

Communication & Chat

Messages & AnnouncementsUnified Operations InboxAI Assistant

Manager & Admin Guides

Daily Line-Up SetupStaff SchedulingManaging LocationsNPS and Guest FeedbackCouponsContacts and Guest HistoryManager CloseoutsDaily Line-Up & ComplianceAnalyticsIncident ReportingWhistleblower Concerns & FeedbackAdmin Tools

⚙️ DEVELOPER DOCS

Getting Started

Getting StartedDevelopmentDeployment Guide

Architecture

Architecture OverviewExplanation: AI Integration & Tenant SecurityExplanation: Dynamic Forms Engine DesignExplanation: Compliance Ledger DesignExplanation: Live Sync & Data FreshnessData FlowArchitecture Decision Records

Core Domain

Core DomainDatabase ReferenceLocations DomainAuth & RBACScheduling DomainReports DomainIncidents DomainUnified Operations InboxLive Sync Data FreshnessToast Sync PipelineNotifications DomainCoupons and Guest NPSAudit Log & Compliance ArchitectureDesign Audit FindingsAI Chat IntegrationAnalytics & Tips Integration

Frontend

Frontend ArchitectureFormsLoading SkeletonsComponentsPWA & Offline ShellScreenshots

API Reference

API Reference

Endpoints

POS Sales APIOptimization Data APISchedule Shifts APIEmployee Export APIReports APIIncidents APIAI Chat APIPush Notifications APIWebhooks APICron API

Contributing

ContributingCode Examples

Security

Security & Compliance

Release Notes

What's New

Security & Compliance

Technical design of authentication, authorization, and data protection

Overview

Danvas employs a multi-layered security strategy to protect sensitive operational data. This includes robust identity management via Clerk, strict multi-tenant isolation, automated rate limiting, and comprehensive audit trails.

Authentication & Identity

Clerk Integration

We leverage Clerk for all authentication and session management.

  • Teams: A Canvas Forge concept, not a Clerk one. users.teamId is a local tenant identifier configured through CANVAS_TEAM_ID. Clerk Organizations are not used and are never read to establish team membership.
  • Roles: Enforced from the local users.role column only. Clerk metadata and session claims are not written or read as authorization inputs.
  • Provisioning: Invitation-only. provisionUser() claims an outstanding invitation on first sign-in or grants nothing, and Clerk webhooks sync approved profile fields only. See ADR-0018.

Auth Guards

All data access is guarded by server-side checks. Prefer getActionContext() — it bundles auth, role, and location scope in one call and is the source of truth for the navigation. The raw requireUserAuth() / requireAdmin() shims from @repo/auth/get-user-auth are still exported but are being phased out.

// apps/app/src/lib/action-context.ts
import { getActionContext } from "@/lib/action-context";

export async function secureAction() {
  const { auth } = await getActionContext("admin");
  // auth.userId, auth.teamId, auth.role, auth.locationIds
  // redirects to /403 for non-admins; switch to "manager" /
  // "adminOrManager" in API routes to throw instead of redirect.
}

Data Isolation

Danvas is a multi-tenant platform. Data isolation is enforced at the database query level using the local teamId.

  • Multi-tenancy: Every row in the database (except global configuration) contains a teamId.
  • Query Scoping: All Drizzle queries MUST include a filter for the active team.
  • Location Isolation: Operational data (reports, shifts, incidents) is further isolated by locationId.

API & Network Security

Rate Limiting

We use Upstash Redis to implement sliding-window rate limiting on critical endpoints.

  • AI Chat: 20 requests per minute per user.
  • Public APIs: 10 requests per minute per IP.
  • Fallback: System falls back to in-memory limiting if Redis is unreachable.

Security Headers

All HTTP responses include security-hardening headers via Nosecone:

  • CSP: Restricts content sources to trusted domains.
  • HSTS: Enforces secure connections over HTTPS.
  • X-Frame-Options: Prevents clickjacking attacks.

Data Protection

Encryption at Rest

Sensitive information, such as Slack bot secrets, is encrypted using AES-256-GCM before being stored in the database.

PII Redaction at the Edge

Customer contact details (emails, phone numbers) must never reach model context, error reports, or product analytics. A single redaction primitive lives in @repo/security/pii and is wired into every output surface:

import { redactPII, scrubPII } from "@repo/security/pii";

redactPII("Email me at jane@example.com or 415-555-1234");
// → "Email me at [EMAIL_REDACTED] or [PHONE_REDACTED]"

scrubPII({ name: "Jane", contact: "jane@example.com", nested: { phone: "415-555-1234" } });
// → { name: "Jane", contact: "[EMAIL_REDACTED]", nested: { phone: "[PHONE_REDACTED]" } }

Consumers:

  • Sentry — scrubSentryEvent runs in beforeSend for the client, server, and edge runtimes. It scrubs message, exception.values, breadcrumbs, and extra before the event leaves the process.
  • PostHog — Use captureSecureEvent from @repo/telemetry/server instead of calling analytics.capture directly. Event properties are scrubbed with scrubPII before transmission. Scrubbing is depth-bounded (six levels) to guard against pathological inputs. Add new patterns to packages/security/pii.ts rather than re-implementing locally — there should be one source of truth.

Audit Trail

The audit_log table captures all administrative and sensitive actions, including:

  • User role changes.
  • Incident escalations.
  • Form definition updates.
  • Security configuration changes.

logAudit() runs inside the same database transaction as the mutation it describes, so a crash between the write and the audit call cannot leave an action without a trail.

Coupon capabilities and Contact evidence

Coupon assignment links, private issuance URLs, resume cookies, and legacy staff proof have different authority. Private entry tokens are exchanged for path-scoped HttpOnly sessions; recovery IDs identify an original attempt and are not permission to redeem or reveal a token. A private issuance token is revealed once and is never returned by the history list or replay.

Ordinary Coupon reports mask Contact values for every role. Association repair, Contact-profile reveal, and raw Coupon export have their own server authorization, operational purpose, and audit; one does not grant the others. Merges preserve append-only consent and effective revocations. Anonymous NPS stores no Contact or contact/marketing consent.

Keep private links, cookies, proof fragments, and guest personal data out of logs, analytics, support tickets, screenshots, video, and traces. Legacy proof navigation uses a fragment consumed before telemetry and held only in document memory; it must be rescanned after refresh/full authentication navigation. Use the feature's capability-aware telemetry controls and the separate real-device/privacy acceptance checks; successful synthetic browser tests do not certify those operational checks.

See Coupons and Guest NPS, Contacts, and the rollout runbook.

Related

Auth & RBAC Domain

API Security

Database Schema

Code Examples

Code patterns used in Danvas development

What's New

Recent changes, hardening rounds, and the new capabilities they unlock

On this page

OverviewAuthentication & IdentityClerk IntegrationAuth GuardsData IsolationAPI & Network SecurityRate LimitingSecurity HeadersData ProtectionEncryption at RestPII Redaction at the EdgeAudit TrailCoupon capabilities and Contact evidenceRelated