Skip to content

Conversation

@SK8-infi
Copy link

@SK8-infi SK8-infi commented Oct 2, 2025

Summary

This PR adds a minimal, single-file Go script that demonstrates basic integration with the APort /verify endpoint, helping Go developers get started with APort in seconds.

Please review the code and suggest any changes if required. Also please remember to add label Hacktoberfest-accepted
Closes #27

Changes Made

New Files Added:

  • examples/hello-world/go/main.go - Single-file Go script using only standard library
  • examples/hello-world/go/README.md - Comprehensive documentation with examples
  • examples/hello-world/go/go.mod - Go module definition
  • examples/hello-world/go/env.example - Environment configuration template

Success Criteria Met

  • Single main.go file created in new /examples/hello-world/go directory
  • Uses only standard library (net/http, encoding/json, fmt, log, os, io, bytes)
  • Prints allow status and reasons from JSON response with user-friendly formatting
  • Simple README.md with clear instructions on how to run the script

Technology Stack

  • Go 1.16+ with standard library only
  • net/http for HTTP requests
  • APort API integration at /api/verify/policy/{policy} endpoint

Features

  • Zero dependencies - uses only Go standard library
  • Environment configuration - supports APORT_BASE_URL, APORT_AGENT_ID, APORT_POLICY
  • Sample data included - works out of the box with sample agents
  • Comprehensive error handling - network, HTTP, and JSON parsing errors
  • User-friendly output - formatted results with clear status messages
  • Professional appearance - clean output without emojis

Usage Examples

# Run with defaults
go run main.go

# Build and run
go build -o aport-hello main.go
./aport-hello

# Custom configuration
APORT_AGENT_ID=agt_tmpl_mg8jr8l1_geckvz APORT_POLICY=data.export.v1 go run main.go

Testing

  • Code compiles without errors
  • Successfully connects to APort API
  • Properly handles API responses
  • Works with different agent/policy combinations
  • Builds standalone executable
  • Follows Go best practices and conventions

Documentation

Includes comprehensive README with:

  • Quick start guide
  • Configuration options
  • Sample agents and policies
  • Code structure explanation
  • Customization examples
  • Error handling details
  • Next steps for developers

Related

Part of the APort integrations ecosystem to help developers quickly get started with APort verification in their Go applications.

Copy link
Contributor

@uchibeke uchibeke left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good start

  • Please make this work for /verify and /verify/policy/[pack_id]

Review https://github.com/aporthq/aport-sdks/blob/main/middleware/express/src/index.ts

Also see http://localhost:8787/api/swagger-ui#/Verification/verifyPolicyDecision

And https://github.com/aporthq/aport-policies/tree/main/payments.charge.v1 for fields required for policies. Its different depending on the policy

@SK8-infi
Copy link
Author

SK8-infi commented Oct 2, 2025

okay will work on it

@uchibeke
Copy link
Contributor

uchibeke commented Oct 3, 2025

Overall Assessment

Good effort creating a Go hello world example! However, there are critical misunderstandings about what the APort API does and which endpoint should be used for a "hello world" example.


🚨 Critical Issue: Wrong API Endpoint

Problem: Using Policy Verification Instead of Agent Verification

The issue #27 asks for "a basic call to the APort /verify endpoint" and links to https://aport.io/api/verify/ap_128094d3 as a sample.

Current Implementation: Uses /api/verify/policy/{policy} (policy verification endpoint)

url := fmt.Sprintf("%s/api/verify/policy/%s", baseURL, policy)

Expected Implementation: Should use /api/verify/{agent_id} (agent passport view endpoint)

url := fmt.Sprintf("%s/api/verify/%s", baseURL, agentID)

What's the Difference?

1. /api/verify/{agent_id} (GET) - Agent Passport View

  • Purpose: View an agent's public passport/profile
  • Method: GET (no request body needed)
  • Use Case: "Hello World" - check if an agent exists and view its details
  • Response: Agent profile with capabilities, limits, verification status
  • Example: https://aport.io/api/verify/ap_128094d3

2. /api/verify/policy/{policy_id} (POST) - Policy Decision

  • Purpose: Check if an agent is authorized to perform a specific action
  • Method: POST with context
  • Use Case: Production authorization - "Can this agent process a $100 refund?"
  • Response: Decision (allow/deny) with reasons
  • Requires: API key, agent_id, context

Why This Matters

For a "Hello World" example:

  • Should be simple: No auth required, no complex request body
  • Should demonstrate basics: "Does this agent exist? What can it do?"
  • Quick start: Developers can run it immediately with a sample agent ID

The current implementation is actually a production verification example, not a hello world.


🔧 Required Changes

1. Update main.go to Use Agent Verification Endpoint

package main

import (
	"encoding/json"
	"fmt"
	"io"
	"log"
	"net/http"
	"os"
)

// AgentPassport represents the APort agent passport response
type AgentPassport struct {
	AgentID      string       `json:"agent_id"`
	Name         string       `json:"name"`
	Role         string       `json:"role"`
	Description  string       `json:"description"`
	Status       string       `json:"status"`
	Capabilities []Capability `json:"capabilities"`
	Limits       Limits       `json:"limits"`
}

// Capability represents an agent capability
type Capability struct {
	ID     string                 `json:"id"`
	Params map[string]interface{} `json:"params,omitempty"`
}

// Limits represents agent operational limits
type Limits struct {
	MaxActionsPerMin int  `json:"max_actions_per_min"`
	MaxExportRows    int  `json:"max_export_rows"`
	AllowPII         bool `json:"allow_pii"`
}

const (
	// Default APort API base URL
	defaultBaseURL = "https://aport.io"

	// Sample agent IDs for testing
	sampleRefundBot    = "ap_128094d3"
	sampleDataExporter = "agt_tmpl_mg8jr8l1_geckvz"
	samplePRMerger     = "agt_tmpl_mg8jtzzk_rl0diw"
)

func main() {
	fmt.Println("APort Go Hello World Example")
	fmt.Println("==============================")
	fmt.Println()

	// Get configuration from environment or use defaults
	baseURL := getEnvOrDefault("APORT_BASE_URL", defaultBaseURL)
	agentID := getEnvOrDefault("APORT_AGENT_ID", sampleRefundBot)

	fmt.Printf("Fetching agent passport for: %s\n", agentID)
	fmt.Printf("From: %s\n", baseURL)
	fmt.Println()

	// Fetch the agent passport
	passport, err := getAgentPassport(baseURL, agentID)
	if err != nil {
		log.Fatalf("Failed to fetch agent passport: %v", err)
	}

	// Print the agent details
	printAgentPassport(passport)
}

// getAgentPassport fetches an agent's public passport from APort
func getAgentPassport(baseURL, agentID string) (*AgentPassport, error) {
	// Construct the URL - simple GET request, no auth required
	url := fmt.Sprintf("%s/api/verify/%s", baseURL, agentID)

	// Make the GET request
	resp, err := http.Get(url)
	if err != nil {
		return nil, fmt.Errorf("failed to make request: %w", err)
	}
	defer resp.Body.Close()

	// Read response body
	body, err := io.ReadAll(resp.Body)
	if err != nil {
		return nil, fmt.Errorf("failed to read response: %w", err)
	}

	// Check for HTTP errors
	if resp.StatusCode != http.StatusOK {
		return nil, fmt.Errorf("API returned status %d: %s", resp.StatusCode, string(body))
	}

	// Parse JSON response
	var passport AgentPassport
	if err := json.Unmarshal(body, &passport); err != nil {
		return nil, fmt.Errorf("failed to parse response: %w", err)
	}

	return &passport, nil
}

// printAgentPassport prints agent details in a user-friendly format
func printAgentPassport(passport *AgentPassport) {
	fmt.Println("Agent Passport:")
	fmt.Println("===============")
	fmt.Printf("Agent ID:    %s\n", passport.AgentID)
	fmt.Printf("Name:        %s\n", passport.Name)
	fmt.Printf("Role:        %s\n", passport.Role)
	fmt.Printf("Status:      %s\n", passport.Status)
	fmt.Println()

	fmt.Println("Description:")
	fmt.Printf("  %s\n", passport.Description)
	fmt.Println()

	// Print capabilities
	if len(passport.Capabilities) > 0 {
		fmt.Println("Capabilities:")
		for _, cap := range passport.Capabilities {
			fmt.Printf("  - %s", cap.ID)
			if len(cap.Params) > 0 {
				fmt.Printf(" (")
				first := true
				for k, v := range cap.Params {
					if !first {
						fmt.Printf(", ")
					}
					fmt.Printf("%s: %v", k, v)
					first = false
				}
				fmt.Printf(")")
			}
			fmt.Println()
		}
		fmt.Println()
	}

	// Print limits
	fmt.Println("Limits:")
	fmt.Printf("  Max actions/min: %d\n", passport.Limits.MaxActionsPerMin)
	fmt.Printf("  Max export rows: %d\n", passport.Limits.MaxExportRows)
	fmt.Printf("  Allow PII:       %v\n", passport.Limits.AllowPII)
	fmt.Println()

	fmt.Println("Success! Agent passport retrieved.")
}

// getEnvOrDefault returns the value of an environment variable or a default value
func getEnvOrDefault(key, defaultValue string) string {
	if value := os.Getenv(key); value != "" {
		return value
	}
	return defaultValue
}

2. Update README.md

Current: References policy verification with complex context
Should be: Simple GET request explanation

# APort Go Hello World

A minimal, single-file Go script that demonstrates fetching an agent's public passport from the APort `/verify` endpoint.

## Quick Start

```bash
# Run with default settings (no API key required!)
go run main.go

# Try a different agent
APORT_AGENT_ID=agt_tmpl_mg8jr8l1_geckvz go run main.go

Expected Output

APort Go Hello World Example
==============================

Fetching agent passport for: ap_128094d3
From: https://aport.io

Agent Passport:
===============
Agent ID:    ap_128094d3
Name:        HappyRefundBot Pro
Role:        Snr. Refund Specialist
Status:      active

Description:
  AI-powered refund processing agent with advanced fraud detection and
  compliance monitoring.

Capabilities:
  - payments.refund (max_amount: 10000, currency: USD)
  - inventory.adjust
  - messaging.send (max_recipients: 10)

Limits:
  Max actions/min: 60
  Max export rows: 1000
  Allow PII:       false

Success! Agent passport retrieved.

What This Does

This example demonstrates the simplest possible APort integration:

  1. Makes a GET request to /api/verify/{agent_id}
  2. No authentication required (public endpoint)
  3. No request body needed
  4. Returns the agent's public passport/profile

This is different from policy verification which requires:

  • API key authentication
  • POST request with context
  • Policy evaluation logic

Sample Agents

Try these agent IDs:

  • ap_128094d3 - HappyRefundBot Pro (refund specialist)
  • agt_tmpl_mg8jr8l1_geckvz - Data Export Agent
  • agt_tmpl_mg8jtzzk_rl0diw - PR Merger Agent

Next Steps

Once you understand agent passports, learn about:

  1. Policy Verification: Check if an agent can perform specific actions
    • See: /examples/policy-verification/go/
  2. APort SDK: Production-ready Go SDK with middleware
  3. Integration Examples: Real-world use cases

Why This Matters

Agent passports are like digital IDs for AI agents. They contain:

  • ✅ What the agent can do (capabilities)
  • ✅ What limits apply (rate limits, quotas)
  • ✅ Verification status (who vouches for this agent)
  • ✅ Operational metadata (regions, frameworks)

This "hello world" shows you how to view an agent's passport.
For verifying actions, you'll use the policy verification endpoint.


### 3. Update `env.example`

```bash
# APort Configuration
# Copy this file to .env if needed

# APort API base URL (optional)
# APORT_BASE_URL=https://aport.io

# Agent ID to view (optional - uses sample agent if not set)
# APORT_AGENT_ID=ap_128094d3

# Sample Agent IDs for testing:
# Refund Bot: ap_128094d3
# Data Exporter: agt_tmpl_mg8jr8l1_geckvz
# PR Merger: agt_tmpl_mg8jtzzk_rl0diw

# Note: This endpoint is PUBLIC and doesn't require an API key
# For policy verification (authorization), see /examples/policy-verification/

4. Remove Policy-Related Code

The current code includes:

  • VerifyRequest struct (not needed for GET)
  • VerifyContext struct (not needed for GET)
  • ❌ Policy ID configuration (not needed for agent view)
  • ❌ POST request logic (should be GET)
  • ❌ Request body marshaling (GET has no body)

📋 Success Criteria Review

Let's check if the PR meets the original requirements:

Criteria Status Notes
Single main.go file ✅ Met File exists
In /examples/hello-world/go ✅ Met Correct location
Uses standard library ✅ Met Only net/http, encoding/json
Prints response data ⚠️ Partial Prints data but from wrong endpoint
Simple README.md ⚠️ Needs work Too complex for hello world
Working example ❌ Not met Wrong endpoint (policy vs agent)
Proper documentation ❌ Not met Explains wrong concept
Go best practices ✅ Met Code is well-structured

🎯 Conceptual Issues to Address

1. Misunderstanding of "Hello World"

A hello world example should be:

  • Minimal: Fewest concepts possible
  • No auth: Should work immediately without setup
  • Read-only: View data, don't make decisions
  • Educational: Introduce one concept at a time

Current implementation is actually an intermediate example that shows policy authorization.

2. Endpoint Confusion

There are several APort endpoints:

Endpoint Method Auth Purpose Use in
/api/verify/{agent_id} GET No View agent passport Hello World
/api/verify/policy/{policy} POST Yes Check authorization Production ❌
/api/passports/{agent_id} GET Yes Full passport details Advanced

The issue links to /api/verify/ap_128094d3 which is the first endpoint (agent view).

3. Sample Request Confusion

The example in the PR shows:

{
  "context": {
    "agent_id": "ap_128094d3",
    "policy_id": "payments.refund.v1",
    "context": {
      "amount": 50,
      "currency": "USD"
    }
  }
}

This is for policy verification, not for viewing an agent passport.

For the agent view endpoint, there's no request body - it's a simple GET request!


✅ Action Items

  • Change endpoint from /api/verify/policy/{policy} to /api/verify/{agent_id}
  • Change HTTP method from POST to GET
  • Remove request body logic (no JSON marshaling needed)
  • Update structs to match agent passport response format
  • Simplify README to explain agent passports (not policy verification)
  • Remove policy-related configuration and examples
  • Update output formatting to show agent details (not decision data)
  • Clarify this is viewing agent data, not authorizing actions
  • Add note about policy verification being a separate example

💡 Suggested Example Output

APort Go Hello World Example
==============================

Fetching agent passport for: ap_128094d3
From: https://aport.io

Agent Passport:
===============
Agent ID:    ap_128094d3
Name:        HappyRefundBot Pro
Role:        Snr. Refund Specialist
Status:      active

Description:
  AI-powered refund processing agent with advanced fraud detection

Capabilities:
  - payments.refund (max_amount: 10000, currency: USD)
  - inventory.adjust
  - messaging.send (max_recipients: 10)

Limits:
  Max actions/min: 60
  Max export rows: 1000

Success! Agent passport retrieved.

📚 References

Agent View Endpoint: https://aport.io/api/verify/ap_128094d3
Sample Response: Returns full agent passport JSON

NOT the Policy Verification Endpoint: /api/verify/policy/{policy} (that's for authorization checks)


Summary

The code quality is good and follows Go best practices, but it's implementing the wrong feature. The issue asks for a simple "hello world" that views an agent's passport (GET request, no auth), but the PR implements policy verification (POST request, requires auth, complex request body).

To fix: Use the simpler /api/verify/{agent_id} GET endpoint instead of /api/verify/policy/{policy} POST endpoint.

This is actually a great policy verification example - it just belongs in a different location (like /examples/policy-verification/go/ or /examples/authorization/go/) rather than /examples/hello-world/go/.

@SK8-infi
Copy link
Author

SK8-infi commented Oct 3, 2025

Okay. I apologise for the misunderstanding. Will resolve the issues and get back soon.
Thanks

@SK8-infi
Copy link
Author

@uchibeke Made the required changes. Please review
Got a bit late due to mid term exams

@SK8-infi SK8-infi requested a review from uchibeke October 19, 2025 18:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Hacktoberfest] Create a "Hello, APort!" Example in Go

2 participants