This document covers security practices, authentication, audit logging, and secrets management for the Regarde platform.
Authentication uses a 7-step stateless verification process:
- Token generated using 16-character secure random string
- Created client-side to avoid sending credentials to server
- Token stored in
RegardeTokenAuthCoMap with 24-hour expiration
RegardeTokenAuth.create({
token: "16-char-random-string",
expiresAt: Date.now() + 24 * 60 * 60 * 1000
})- Stored in Jazz CoMap (not localStorage or sessionStorage)
- User has full ownership and control
- Automatic expiration tracking
headers: {
"X-Regarde-Token": token,
"X-Regarde-Auth-ID": regardeAuthId
}- Token transmitted in headers only
- Never in URL parameters or query strings
- Auth ID identifies the RegardeTokenAuth CoMap
const tokenAuth = await RegardeTokenAuth.load(regardeAuthId, {
loadAs: workerAccount
});- Worker loads the RegardeTokenAuth CoMap
- Jazz permissions ensure only owner can create/access
if (tokenAuth._owner !== account.id) {
throw new Error("Token ownership mismatch");
}- Verifies requesting user owns the token
- Prevents token replay attacks
if (Date.now() > tokenAuth.expiresAt) {
throw new Error("Token expired");
}- 24-hour maximum lifetime
- Automatic invalidation after expiry
- User must refresh token before expiration
- No session state maintained server-side
- Token validated from CoMap on each request
- Scalable and stateless architecture
- Own Data: Full read/write access to personal CoMaps
- Registry Data: Read-only access to public registry entries
- Payment Events: Read-only access to own payment history
- User Payment Events: Write access to append payment events
- Registry Access: Full read access to all registries
- No User Data: Cannot read user-owned CoMaps beyond what's necessary
- Direct Write: Bypasses token authentication
- Registry Modifications: Full write access to all registries
- Audit Log: Read-only access for security reviews
| Resource | User | Worker | Admin |
|---|---|---|---|
| RegardeTokenAuth | Owner | None | None |
| Payment Events | Read | Append | Read |
| Registry | Read | Read/Write | Read/Write |
| Audit Log | None | Append | Read |
Every registry modification is recorded:
- App registration
- Registry updates
- Payment event ingestion
- Administrative changes
{
timestamp: number, // Unix timestamp
operation: string, // Operation type
nickname: string, // Target identifier
accountId: string, // Acting account
source: "user" | "worker" | "admin" // Initiator type
}- Stored in
RegistryAuditLogCoMap - Append-only structure prevents tampering
- Accessible via Admin CLI for review
# View recent audit entries
regarde-admin audit list --limit 50
# Filter by operation type
regarde-admin audit list --operation REGISTER_APP
# Export for compliance review
regarde-admin audit export --output audit-report.json- Tokens automatically expire after 24 hours
- Forces regular re-authentication
- Limits exposure window if token compromised
const token = generateSecureRandom(16);- Cryptographically secure random generation
- 128-bit entropy
- URL-safe character set
- Stored in Jazz CoMap, not browser storage
- Protected by Jazz encryption and sync
- Owner controls access permissions
// Correct - headers only
headers: {
"X-Regarde-Token": token
}
// Wrong - never in URL
/api/endpoint?token=xxxEvery webhook endpoint requires a secret:
- Generated per-endpoint
- Stored encrypted in registry
- Used for signature verification
const signature = crypto
.createHmac('sha256', webhookSecret)
.update(payload)
.digest('hex');
if (!crypto.timingSafeEqual(
Buffer.from(receivedSignature),
Buffer.from(signature)
)) {
throw new Error("Invalid signature");
}- LemonSqueezy: Signature-based verification
- Stripe: Signed webhook secrets
- Polar: HMAC-SHA256 signatures
const WebhookPayloadSchema = z.object({
event: z.string(),
data: z.object({
id: z.string(),
amount: z.number()
})
});
const payload = WebhookPayloadSchema.parse(rawPayload);# Worker authentication
WORKER_ACCOUNT_SECRET=<64-char-secret>
# Webhook secrets (provider-specific)
LEMONSQUEEZY_WEBHOOK_SECRET=<secret>
STRIPE_WEBHOOK_SECRET=whsec_<secret>
POLAR_WEBHOOK_SECRET=<secret>
# API keys
REGARDE_API_KEY=<api-key># Development settings
REGARDE_ENV=development
LOG_LEVEL=debug
# Performance
CACHE_TTL=3600
RATE_LIMIT_WINDOW=60000# .gitignore
.env
.env.local
.env.*.local
*.secret- Create
.env.localfor local secrets - Never commit to version control
- Copy from
.env.exampletemplate
WARNING: Admin CLI has direct write access to registries.
This bypasses normal authentication and authorization checks.
Use with extreme caution.
# Always backup before destructive changes
regarde-admin registry backup --output backup-$(date +%Y%m%d).json$ regarde-admin registry clear
This will DELETE ALL registry entries. Are you sure? [y/N]# Run security audit
regarde-admin audit security
# Check for anomalies
regarde-admin audit anomalies --since 7d- All external inputs validated with Zod
- No secrets in code or config files
- Audit logging enabled and tested
- Token expiry configured (24 hours)
- Webhook secrets rotated
- Environment variables documented
- Admin CLI access restricted
- Backup procedures tested
- Review audit logs weekly
- Rotate webhook secrets quarterly
- Update dependencies monthly
- Security scan on deployment
- Access review for admin users
- Immediately rotate compromised secrets
- Review audit logs for unauthorized access
- Notify affected users
- Document incident and remediation
- Update security procedures as needed