HIVE

Local Setup

Edge Functions

Edge Functions

Edge Functions are serverless functions that run on Supabase's global edge network. HIVE Protocol uses them for AI integrations, webhook processing, and secure operations.

Overview

┌─────────────────────────────────────────────────────────────────┐
│                    Edge Function Architecture                    │
├─────────────────────────────────────────────────────────────────┤
│                                                                  │
│  Client Request                                                  │
│       │                                                          │
│       ▼                                                          │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │              Supabase Edge Runtime (Deno)               │    │
│  │  ┌─────────┐  ┌─────────┐  ┌─────────┐  ┌─────────┐    │    │
│  │  │ agent-  │  │ execute │  │  test-  │  │ webhook │    │    │
│  │  │ respond │  │  -tool  │  │ webhook │  │dispatch │    │    │
│  │  └─────────┘  └─────────┘  └─────────┘  └─────────┘    │    │
│  └─────────────────────────────────────────────────────────┘    │
│       │              │              │              │              │
│       ▼              ▼              ▼              ▼              │
│  ┌─────────┐   ┌─────────┐   ┌─────────┐   ┌─────────┐          │
│  │ OpenAI  │   │External │   │ User    │   │External │          │
│  │Anthropic│   │  APIs   │   │Endpoint │   │Services │          │
│  │ Google  │   │         │   │         │   │         │          │
│  └─────────┘   └─────────┘   └─────────┘   └─────────┘          │
│                                                                  │
└─────────────────────────────────────────────────────────────────┘

HIVE Protocol Edge Functions

FunctionPurposeAuth Required
agent-respondGenerate AI agent responsesYes
execute-toolRun tool integrationsYes
generate-toolAI-generate tool schemasYes
test-webhookSend test webhook eventsYes
webhook-dispatcherProcess webhook delivery queueYes
check-rate-limitValidate rate limitsYes
two-factor-authHandle 2FA operationsYes
delete-accountSecurely delete user dataYes
admin-statsAdmin dashboard statisticsYes (Admin)
admin-usersAdmin user managementYes (Admin)

Function Structure

Each edge function follows this structure:

supabase/functions/
├── _shared/                    # Shared code
│   └── sanitize.ts             # Input sanitization
├── agent-respond/
│   └── index.ts                # Main function file
├── execute-tool/
│   └── index.ts
├── test-webhook/
│   └── index.ts
└── ... (other functions)

Writing Edge Functions

Basic Template

// supabase/functions/my-function/index.ts
import "jsr:@supabase/functions-js/edge-runtime.d.ts";

const corsHeaders = {
  "Access-Control-Allow-Origin": "*",
  "Access-Control-Allow-Methods": "GET, POST, PUT, DELETE, OPTIONS",
  "Access-Control-Allow-Headers": "Content-Type, Authorization, X-Client-Info, Apikey",
};

Deno.serve(async (req: Request) => {
  // Handle CORS preflight
  if (req.method === "OPTIONS") {
    return new Response(null, { status: 200, headers: corsHeaders });
  }

  try {
    // Verify authentication
    const authHeader = req.headers.get("Authorization");
    if (!authHeader) {
      return new Response(
        JSON.stringify({ error: "Missing authorization" }),
        { status: 401, headers: { ...corsHeaders, "Content-Type": "application/json" } }
      );
    }

    // Parse request body
    const body = await req.json();

    // Your function logic here
    const result = { success: true, data: body };

    return new Response(
      JSON.stringify(result),
      {
        status: 200,
        headers: { ...corsHeaders, "Content-Type": "application/json" },
      }
    );
  } catch (error) {
    console.error("Function error:", error);
    return new Response(
      JSON.stringify({ error: error.message }),
      {
        status: 500,
        headers: { ...corsHeaders, "Content-Type": "application/json" },
      }
    );
  }
});

With Supabase Client

import "jsr:@supabase/functions-js/edge-runtime.d.ts";
import { createClient } from "npm:@supabase/supabase-js@2";

const corsHeaders = {
  "Access-Control-Allow-Origin": "*",
  "Access-Control-Allow-Methods": "GET, POST, PUT, DELETE, OPTIONS",
  "Access-Control-Allow-Headers": "Content-Type, Authorization, X-Client-Info, Apikey",
};

Deno.serve(async (req: Request) => {
  if (req.method === "OPTIONS") {
    return new Response(null, { status: 200, headers: corsHeaders });
  }

  try {
    // Create Supabase client with user's auth
    const supabaseClient = createClient(
      Deno.env.get("SUPABASE_URL") ?? "",
      Deno.env.get("SUPABASE_ANON_KEY") ?? "",
      {
        global: {
          headers: { Authorization: req.headers.get("Authorization")! },
        },
      }
    );

    // Get authenticated user
    const { data: { user }, error: authError } = await supabaseClient.auth.getUser();
    if (authError || !user) {
      return new Response(
        JSON.stringify({ error: "Unauthorized" }),
        { status: 401, headers: { ...corsHeaders, "Content-Type": "application/json" } }
      );
    }

    // Query database as user
    const { data: agents, error } = await supabaseClient
      .from("agents")
      .select("*")
      .eq("user_id", user.id);

    return new Response(
      JSON.stringify({ agents }),
      { headers: { ...corsHeaders, "Content-Type": "application/json" } }
    );
  } catch (error) {
    return new Response(
      JSON.stringify({ error: error.message }),
      { status: 500, headers: { ...corsHeaders, "Content-Type": "application/json" } }
    );
  }
});

With External AI APIs

import "jsr:@supabase/functions-js/edge-runtime.d.ts";

const corsHeaders = {
  "Access-Control-Allow-Origin": "*",
  "Access-Control-Allow-Methods": "GET, POST, PUT, DELETE, OPTIONS",
  "Access-Control-Allow-Headers": "Content-Type, Authorization, X-Client-Info, Apikey",
};

Deno.serve(async (req: Request) => {
  if (req.method === "OPTIONS") {
    return new Response(null, { status: 200, headers: corsHeaders });
  }

  try {
    const { message, model = "gpt-4o" } = await req.json();

    // Call OpenAI API
    const openaiResponse = await fetch("https://api.openai.com/v1/chat/completions", {
      method: "POST",
      headers: {
        "Authorization": `Bearer ${Deno.env.get("OPENAI_API_KEY")}`,
        "Content-Type": "application/json",
      },
      body: JSON.stringify({
        model,
        messages: [{ role: "user", content: message }],
        max_tokens: 4096,
      }),
    });

    const data = await openaiResponse.json();

    return new Response(
      JSON.stringify({ response: data.choices[0].message.content }),
      { headers: { ...corsHeaders, "Content-Type": "application/json" } }
    );
  } catch (error) {
    return new Response(
      JSON.stringify({ error: error.message }),
      { status: 500, headers: { ...corsHeaders, "Content-Type": "application/json" } }
    );
  }
});

Deploying Functions

Using Supabase CLI

# Deploy a single function
supabase functions deploy agent-respond

# Deploy all functions
supabase functions deploy

# Deploy with specific project
supabase functions deploy --project-ref your-project-ref

Using Dashboard

  1. Navigate to Edge Functions in Supabase dashboard
  2. Click "Deploy a new function"
  3. Upload your function code
  4. Configure settings

Environment Variables

Edge functions have access to these built-in variables:

Deno.env.get("SUPABASE_URL")         // Your project URL
Deno.env.get("SUPABASE_ANON_KEY")    // Public anon key
Deno.env.get("SUPABASE_SERVICE_ROLE_KEY") // Service role key
Deno.env.get("SUPABASE_DB_URL")      // Direct database URL

Setting Custom Secrets

# Set a secret
supabase secrets set OPENAI_API_KEY=sk-...

# Set multiple secrets
supabase secrets set \
  OPENAI_API_KEY=sk-... \
  ANTHROPIC_API_KEY=sk-ant-...

# List secrets
supabase secrets list

# Unset a secret
supabase secrets unset OPENAI_API_KEY

Access in function:

const openaiKey = Deno.env.get("OPENAI_API_KEY");

Calling Edge Functions

From Client (Browser)

const response = await fetch(
  `${process.env.NEXT_PUBLIC_SUPABASE_URL}/functions/v1/agent-respond`,
  {
    method: "POST",
    headers: {
      "Authorization": `Bearer ${session.access_token}`,
      "Content-Type": "application/json",
      "apikey": process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
    },
    body: JSON.stringify({
      swarm_id: "uuid",
      message: "Hello!",
    }),
  }
);

const data = await response.json();

Using Supabase Client

const { data, error } = await supabase.functions.invoke("agent-respond", {
  body: {
    swarm_id: "uuid",
    message: "Hello!",
  },
});

With Streaming

const response = await fetch(url, {
  method: "POST",
  headers: { ... },
  body: JSON.stringify({ stream: true, ... }),
});

const reader = response.body?.getReader();
const decoder = new TextDecoder();

while (reader) {
  const { done, value } = await reader.read();
  if (done) break;

  const chunk = decoder.decode(value);
  // Process streaming data
  console.log(chunk);
}

Function Patterns

Rate Limiting

const rateLimitKey = `rate_limit:${user.id}`;
const { data: count } = await supabaseAdmin
  .from("rate_limits")
  .select("count")
  .eq("key", rateLimitKey)
  .single();

if (count && count.count >= 100) {
  return new Response(
    JSON.stringify({ error: "Rate limit exceeded" }),
    { status: 429, headers: corsHeaders }
  );
}

Error Handling

try {
  const result = await riskyOperation();
  return new Response(JSON.stringify(result), {
    headers: { ...corsHeaders, "Content-Type": "application/json" },
  });
} catch (error) {
  // Log error for debugging
  console.error("Function error:", {
    message: error.message,
    stack: error.stack,
    timestamp: new Date().toISOString(),
  });

  // Return user-friendly error
  const status = error.status || 500;
  const message = status === 500 ? "Internal server error" : error.message;

  return new Response(
    JSON.stringify({ error: message, code: error.code }),
    { status, headers: { ...corsHeaders, "Content-Type": "application/json" } }
  );
}

Input Validation

interface RequestBody {
  swarm_id: string;
  message: string;
  agent_id?: string;
}

function validateRequest(body: unknown): body is RequestBody {
  if (!body || typeof body !== "object") return false;
  const b = body as Record<string, unknown>;
  return (
    typeof b.swarm_id === "string" &&
    typeof b.message === "string" &&
    (b.agent_id === undefined || typeof b.agent_id === "string")
  );
}

Deno.serve(async (req) => {
  const body = await req.json();

  if (!validateRequest(body)) {
    return new Response(
      JSON.stringify({ error: "Invalid request body" }),
      { status: 400, headers: corsHeaders }
    );
  }

  // body is now typed as RequestBody
  const { swarm_id, message } = body;
});

Monitoring & Logs

View Function Logs

# Stream logs
supabase functions logs agent-respond --follow

# View recent logs
supabase functions logs agent-respond --limit 100

In Dashboard

  1. Navigate to Edge Functions
  2. Select a function
  3. Click "Logs" tab
  4. Filter by time range or search

Adding Custom Logs

console.log("Info:", { action: "process_message", swarm_id });
console.warn("Warning:", { rate_limit_approaching: true });
console.error("Error:", { message: error.message, stack: error.stack });

Testing Functions

Local Testing

# Start local Supabase
supabase start

# Serve functions locally
supabase functions serve

# In another terminal, test
curl -X POST http://localhost:54321/functions/v1/agent-respond \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"swarm_id": "test", "message": "Hello"}'

Unit Testing

// agent-respond.test.ts
import { assertEquals } from "https://deno.land/std/testing/asserts.ts";

Deno.test("validates input", async () => {
  const response = await fetch("http://localhost:54321/functions/v1/agent-respond", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({}),
  });

  assertEquals(response.status, 400);
});

Troubleshooting

Function Not Found

# Verify function is deployed
supabase functions list

# Redeploy
supabase functions deploy agent-respond

CORS Errors

Ensure CORS headers are set for all responses including errors:

// Always include corsHeaders in responses
return new Response(data, { headers: { ...corsHeaders, ... } });

Timeout Issues

// Functions have 150s timeout by default
// For long operations, use streaming or background tasks
const controller = new AbortController();
const timeout = setTimeout(() => controller.abort(), 30000);

try {
  const response = await fetch(url, { signal: controller.signal });
} finally {
  clearTimeout(timeout);
}

Next Steps

  • [Troubleshooting](/docs/local-setup/troubleshooting): Common issues
  • [Webhooks API](/docs/api/webhooks-api): Webhook functions
  • [Tools API](/docs/api/tools-api): Tool execution

Cookie Preferences

We use cookies to enhance your experience, analyze site traffic, and for marketing purposes. By clicking "Accept All", you consent to our use of cookies. Read our Privacy Policy for more information.