Director Service Quick Start Guide

Created: Tue 29 Jul 2025 07:50:45 CEST
Document Version: 1.0 - MVP implementation guide
Security Classification: Internal Technical Documentation
Target Audience: Developers implementing the Director service
Author: Paul Wisén

🚀 From Zero to Working Edge Function

This guide provides step-by-step instructions to get s.plings.io operational as quickly as possible.

Step 1: Create GitHub Repository

# Create new repository (do this on GitHub.com)
Repository name: Plings-Director
Description: Edge server for QR/NFC scanning and routing
Private/Public: Your choice
Initialize with: README.md, .gitignore (Node), No license yet

# Clone locally
git clone https://github.com/[your-org]/Plings-Director.git
cd Plings-Director

Step 2: Initialize Vercel Edge Functions Project

# Initialize npm project
npm init -y

# Install dependencies
npm install --save-dev @vercel/node typescript @types/node
npm install --save-dev vitest @vitest/ui

# Create TypeScript config
cat > tsconfig.json << 'EOF'
{
  "compilerOptions": {
    "target": "ES2020",
    "module": "commonjs",
    "lib": ["ES2020"],
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true,
    "resolveJsonModule": true,
    "moduleResolution": "node",
    "outDir": "./dist",
    "rootDir": "./",
    "declaration": true,
    "declarationMap": true
  },
  "include": ["api/**/*", "lib/**/*", "types/**/*"],
  "exclude": ["node_modules", "dist"]
}
EOF

Step 3: Create Basic Project Structure

# Create directories
mkdir -p api lib types tests

# Create .gitignore
cat > .gitignore << 'EOF'
node_modules/
.env
.env.local
.vercel
dist/
*.log
.DS_Store
EOF

# Create minimal edge function
cat > api/index.ts << 'EOF'
export const config = {
  runtime: 'edge',
};

export default async function handler(request: Request): Promise<Response> {
  const url = new URL(request.url);
  
  // Extract parameters
  const params = {
    t: url.searchParams.get('t'),  // Tag type (q/n/r)
    i: url.searchParams.get('i'),  // Instance key
    p: url.searchParams.get('p'),  // Path
    cp: url.searchParams.get('cp'), // Class pointer (optional)
  };
  
  // Basic validation
  if (!params.t || !params.i || !params.p) {
    return Response.redirect('https://plings.io/error/invalid-scan', 302);
  }
  
  // For now, redirect all scans to main app
  const targetUrl = new URL('https://plings.io/welcome');
  targetUrl.searchParams.set('ikey', params.i);
  targetUrl.searchParams.set('path', params.p);
  targetUrl.searchParams.set('src', 'scan');
  
  return Response.redirect(targetUrl.toString(), 302);
}
EOF

Step 4: Create Vercel Configuration

# Create vercel.json
cat > vercel.json << 'EOF'
{
  "name": "plings-director",
  "functions": {
    "api/index.ts": {
      "runtime": "edge",
      "maxDuration": 5
    }
  },
  "rewrites": [
    {
      "source": "/(.*)",
      "destination": "/api/index"
    }
  ]
}
EOF

Step 5: Deploy to Vercel

# Install Vercel CLI globally if not already installed
npm install -g vercel

# Login to Vercel
vercel login

# Deploy (first time - follow prompts)
vercel

# When prompted:
# - Set up and deploy: Y
# - Which scope: [Select your team/account]
# - Link to existing project: N
# - Project name: plings-director
# - Directory: ./ 
# - Override settings: N
# Add domain to project
vercel domains add s.plings.io

# Or do this in Vercel Dashboard:
# 1. Go to project settings
# 2. Navigate to Domains
# 3. Add s.plings.io
# 4. Follow DNS configuration instructions

Step 7: Test Basic Functionality

# Test locally first
vercel dev

# Open browser to: http://localhost:3000?t=q&i=TEST123&p=1.1.1

# Test production after DNS propagates
curl -I "https://s.plings.io?t=q&i=TEST123&p=1.1.1"
# Should return 302 redirect to https://plings.io/welcome

Step 8: Add MVP Routing Logic

Update api/index.ts with basic routing:

import { validateParams } from '../lib/validation';
import { determineRoute } from '../lib/routing';

export const config = {
  runtime: 'edge',
};

export default async function handler(request: Request): Promise<Response> {
  try {
    const url = new URL(request.url);
    
    // Extract parameters
    const params = {
      t: url.searchParams.get('t'),
      i: url.searchParams.get('i'),
      p: url.searchParams.get('p'),
      cp: url.searchParams.get('cp'),
    };
    
    // Validate
    if (!validateParams(params)) {
      return Response.redirect('https://plings.io/error/invalid-scan', 302);
    }
    
    // Determine route (MVP: all go to welcome for now)
    const targetUrl = await determineRoute(params);
    
    return Response.redirect(targetUrl, 302);
  } catch (error) {
    console.error('Director error:', error);
    return Response.redirect('https://plings.io/error/system', 302);
  }
}

Create lib/validation.ts:

export function validateParams(params: any): boolean {
  // Required parameters
  if (!params.t || !params.i || !params.p) return false;
  
  // Type validation
  if (!['q', 'n', 'r'].includes(params.t)) return false;
  
  // Basic format validation (can enhance later)
  if (params.i.length < 20 || params.i.length > 48) return false;
  
  return true;
}

Create lib/routing.ts:

interface RouteParams {
  t: string;
  i: string;
  p: string;
  cp?: string;
}

export async function determineRoute(params: RouteParams): Promise<string> {
  // MVP: Simple routing logic
  const baseUrl = 'https://plings.io';
  
  // Build target URL with parameters
  const url = new URL(`${baseUrl}/welcome`);
  url.searchParams.set('ikey', params.i);
  url.searchParams.set('path', params.p);
  url.searchParams.set('src', 'scan');
  
  if (params.cp) {
    url.searchParams.set('cptr', params.cp);
  }
  
  return url.toString();
}

Step 9: Deploy MVP

# Deploy updated version
vercel --prod

# Your edge function is now live at s.plings.io!

Next Steps (Priority Order)

  1. Add Plings public key verification (See director-system-overview.md)
  2. Implement resolveIdentifier API call to check if objects exist
  3. Add proper routing logic for different object states
  4. Set up monitoring (Vercel Analytics or custom)
  5. Add rate limiting with Upstash Redis
  6. Implement scan event logging

Environment Variables (Add via Vercel Dashboard)

# Minimal MVP variables
PLINGS_API_ENDPOINT=https://api.plings.io/graphql
PLINGS_PUBLIC_KEY=[Base58 encoded public key]

# Add more as you implement features

Testing QR Codes

Generate test QR codes at: https://qr-code-generator.com/

Example URL to encode:

https://s.plings.io?t=q&i=4kyQCd5tMDjJVWJH5h95gUcjq3qTX2cj5nwjVyqBRwLo&p=2.1.1.1.1

🎉 Congratulations! You now have a working edge function at s.plings.io that can handle QR scans and redirect users. This MVP can be enhanced incrementally following the todo list.