Writing Go Plugins
Overview
Section titled “Overview”This guide walks you through creating a native Go plugin for DeepIntShield using our hello-world example as a reference. You’ll learn how to structure your plugin, implement required functions, build the shared object, and integrate it with DeepIntShield.
Prerequisites
Section titled “Prerequisites”Before you start, ensure you have:
- Go 1.26.1 installed (must match DeepIntShield’s Go version)
- Linux or macOS (Go plugins are not supported on Windows)
- DeepIntShield installed and configured
- Basic understanding of Go programming
Project Structure
Section titled “Project Structure”A minimal plugin project should have the following structure:
hello-world/├── main.go # Plugin implementation├── go.mod # Go module definition├── go.sum # Dependency checksums├── Makefile # Build automation└── .gitignore # Git ignore patternsStep 1: Initialize Your Plugin Project
Section titled “Step 1: Initialize Your Plugin Project”Create a new directory and initialize a Go module:
mkdir my-plugincd my-plugingo mod init github.com/yourusername/my-pluginAdd DeepIntShield as a dependency:
go get github.com/maximhq/deepintshield/core@latestYour go.mod should look like this:
module github.com/yourusername/my-plugin
go 1.26.1
require github.com/maximhq/deepintshield/core v1.2.38Step 2: Implement the Plugin Interface
Section titled “Step 2: Implement the Plugin Interface”Create main.go with the required plugin functions. Here’s the complete hello-world example:
package main
import ( "fmt"
"github.com/maximhq/deepintshield/core/schemas")
// Init is called when the plugin is loaded// config contains the plugin configuration from config.jsonfunc Init(config any) error { fmt.Println("Init called") // Initialize your plugin here (database connections, API clients, etc.) return nil}
// GetName returns the plugin's unique identifierfunc GetName() string { return "Hello World Plugin"}
// HTTPTransportPreHook intercepts requests BEFORE they enter DeepIntShield core// Modify req in-place. Return (*HTTPResponse, nil) to short-circuit.// Only called when using HTTP transport (deepintshield-http)func HTTPTransportPreHook(ctx *schemas.DeepIntShieldContext, req *schemas.HTTPRequest) (*schemas.HTTPResponse, error) { fmt.Println("HTTPTransportPreHook called")
// Read headers using case-insensitive helper (recommended) contentType := req.CaseInsensitiveHeaderLookup("Content-Type") fmt.Printf("Content-Type: %s\n", contentType)
// Modify request in-place req.Headers["x-custom-header"] = "custom-value"
// Store values in context for use in other hooks ctx.SetValue(schemas.DeepIntShieldContextKey("my-plugin-key"), "pre-hook-value")
// Return nil to continue, or return &schemas.HTTPResponse{} to short-circuit return nil, nil}
// HTTPTransportPostHook intercepts responses AFTER they exit DeepIntShield core// Modify resp in-place. Called in reverse order of pre-hooks.// Only called for NON-STREAMING responses when using HTTP transport (deepintshield-http)func HTTPTransportPostHook(ctx *schemas.DeepIntShieldContext, req *schemas.HTTPRequest, resp *schemas.HTTPResponse) error { fmt.Println("HTTPTransportPostHook called")
// Modify response headers resp.Headers["x-processed-by"] = "my-plugin"
// Read context values set in pre-hook if val := ctx.Value(schemas.DeepIntShieldContextKey("my-plugin-key")); val != nil { fmt.Printf("Context value: %v\n", val) }
// Return nil to continue, or return error to short-circuit return nil}
// HTTPTransportStreamChunkHook intercepts streaming chunks BEFORE they're sent to the client// Modify chunk or return nil to skip. Called in reverse order of pre-hooks.// Only called for STREAMING responses when using HTTP transport (deepintshield-http)func HTTPTransportStreamChunkHook(ctx *schemas.DeepIntShieldContext, req *schemas.HTTPRequest, chunk *schemas.DeepIntShieldStreamChunk) (*schemas.DeepIntShieldStreamChunk, error) { fmt.Println("HTTPTransportStreamChunkHook called")
// chunk is a typed struct containing one of: // - DeepIntShieldTextCompletionResponse (text completion streaming) // - DeepIntShieldChatResponse (chat completion streaming) // - DeepIntShieldResponsesStreamResponse (responses API streaming) // - DeepIntShieldSpeechStreamResponse (speech synthesis streaming) // - DeepIntShieldTranscriptionStreamResponse (transcription streaming) // - DeepIntShieldImageGenerationStreamResponse (image generation streaming) // - DeepIntShieldError (error during streaming)
// Return chunk unchanged to pass through return chunk, nil
// Or return nil to skip/filter this chunk: // return nil, nil
// Or return modified chunk: // modifiedChunk := &schemas.DeepIntShieldStreamChunk{DeepIntShieldChatResponse: ...} // return modifiedChunk, nil}
// PreLLMHook is called before the request is sent to the provider// This is where you can modify requests or short-circuit the flowfunc PreLLMHook(ctx *schemas.DeepIntShieldContext, req *schemas.DeepIntShieldRequest) (*schemas.DeepIntShieldRequest, *schemas.LLMPluginShortCircuit, error) { fmt.Println("PreLLMHook called") // Modify the request or return a short-circuit to skip provider call return req, nil, nil}
// PostLLMHook is called after receiving a response from the provider// This is where you can modify responses or handle errorsfunc PostLLMHook(ctx *schemas.DeepIntShieldContext, resp *schemas.DeepIntShieldResponse, bifrostErr *schemas.DeepIntShieldError) (*schemas.DeepIntShieldResponse, *schemas.DeepIntShieldError, error) { fmt.Println("PostLLMHook called") // Modify the response or error before returning to caller return resp, bifrostErr, nil}
// Cleanup is called when DeepIntShield shuts downfunc Cleanup() error { fmt.Println("Cleanup called") // Clean up resources (close connections, flush buffers, etc.) return nil}package main
import ( "fmt"
"github.com/maximhq/deepintshield/core/schemas")
// Init is called when the plugin is loaded// config contains the plugin configuration from config.jsonfunc Init(config any) error { fmt.Println("Init called") // Initialize your plugin here (database connections, API clients, etc.) return nil}
// GetName returns the plugin's unique identifierfunc GetName() string { return "Hello World Plugin"}
// TransportInterceptor modifies raw HTTP headers and body// Only called when using HTTP transport (deepintshield-http)func TransportInterceptor(ctx *schemas.DeepIntShieldContext, url string, headers map[string]string, body map[string]any) (map[string]string, map[string]any, error) { fmt.Println("TransportInterceptor called") // Modify headers or body before they enter DeepIntShield core return headers, body, nil}
// PreHook is called before the request is sent to the provider// This is where you can modify requests or short-circuit the flowfunc PreHook(ctx *schemas.DeepIntShieldContext, req *schemas.DeepIntShieldRequest) (*schemas.DeepIntShieldRequest, *schemas.PluginShortCircuit, error) { fmt.Println("PreHook called") // Modify the request or return a short-circuit to skip provider call return req, nil, nil}
// PostHook is called after receiving a response from the provider// This is where you can modify responses or handle errorsfunc PostHook(ctx *schemas.DeepIntShieldContext, resp *schemas.DeepIntShieldResponse, bifrostErr *schemas.DeepIntShieldError) (*schemas.DeepIntShieldResponse, *schemas.DeepIntShieldError, error) { fmt.Println("PostHook called") // Modify the response or error before returning to caller return resp, bifrostErr, nil}
// Cleanup is called when DeepIntShield shuts downfunc Cleanup() error { fmt.Println("Cleanup called") // Clean up resources (close connections, flush buffers, etc.) return nil}Understanding Each Function
Section titled “Understanding Each Function”Init(config any) error
Section titled “Init(config any) error”Called once when the plugin is loaded. Use this to:
- Parse plugin configuration
- Initialize database connections
- Set up API clients
- Validate required environment variables
func Init(config any) error { // Parse configuration cfg, ok := config.(map[string]interface{}) if !ok { return fmt.Errorf("invalid config format") }
apiKey := cfg["api_key"].(string) // Initialize your resources return nil}GetName() string
Section titled “GetName() string”Returns a unique identifier for your plugin. This name appears in logs and status reports.
HTTPTransportPreHook(ctx, req)
Section titled “HTTPTransportPreHook(ctx, req)”HTTP transport only. Intercepts requests BEFORE they enter DeepIntShield core. Use this to:
- Modify request headers, body, or query params in-place
- Short-circuit with a custom response
- Store values in
DeepIntShieldContextfor use in other hooks - Works with both native .so and WASM plugins
Key points:
- Receives serializable
*HTTPRequest(not raw fasthttp) - Modify
req.Headers,req.Body,req.Querydirectly - Return
(nil, nil)to continue to next plugin/handler - Return
(*HTTPResponse, nil)to short-circuit with response - Return
(nil, error)to short-circuit with error
HTTPTransportPostHook(ctx, req, resp)
Section titled “HTTPTransportPostHook(ctx, req, resp)”HTTP transport only. Intercepts responses AFTER they exit DeepIntShield core. Use this to:
- Modify response headers or body in-place
- Log or monitor response data
- Access context values set in pre-hook
- Called in reverse order of pre-hooks
Key points:
- Receives both
*HTTPRequestand*HTTPResponse - Modify
resp.Headers,resp.Body,resp.StatusCodedirectly - Return
nilto continue to next plugin/handler - Return
errorto short-circuit with error and skip remaining post-hooks - NOT called for streaming responses - use
HTTPTransportStreamChunkHookinstead
HTTPTransportStreamChunkHook(ctx, req, chunk)
Section titled “HTTPTransportStreamChunkHook(ctx, req, chunk)”HTTP transport only. Intercepts streaming response chunks BEFORE they’re written to the client. Use this to:
- Modify streaming chunks in real-time
- Filter/skip specific chunks
- Log or monitor streaming data
- Called in reverse order of pre-hooks
Key points:
- Receives a
*schemas.DeepIntShieldStreamChunktyped struct (not raw bytes) - The struct contains one non-nil field based on the response type (chat, text completion, responses, speech, transcription, image generation, or error)
- Return
(chunk, nil)to pass through unchanged - Return
(nil, nil)to skip/filter the chunk entirely - Return
(modifiedChunk, nil)to return a modified chunk - Return
(nil, error)to send error to client and stop streaming
TransportInterceptor(...)
Section titled “TransportInterceptor(...)”HTTP transport only. Called before requests enter DeepIntShield core. Use this to:
- Add or modify HTTP headers
- Transform request body
- Implement authentication at the transport layer
PreLLMHook(...)
Section titled “PreLLMHook(...)”Called before each provider request. Use this to:
- Modify request parameters
- Add logging or monitoring
- Implement caching (check cache, return cached response)
- Apply governance rules (rate limiting, budget checks)
- Short-circuit to skip provider calls
Short-Circuiting Example:
func PreLLMHook(ctx *schemas.DeepIntShieldContext, req *schemas.DeepIntShieldRequest) (*schemas.DeepIntShieldRequest, *schemas.LLMPluginShortCircuit, error) { // Return cached response without calling provider if cachedResponse := checkCache(req) { return req, &schemas.LLMPluginShortCircuit{ Response: cachedResponse, }, nil } return req, nil, nil}PostLLMHook(...)
Section titled “PostLLMHook(...)”Called after provider responses (or short-circuits). Use this to:
- Transform responses
- Log response data
- Store responses in cache
- Handle errors or implement fallback logic
- Add custom metadata
Response Transformation Example:
func PostLLMHook(ctx *schemas.DeepIntShieldContext, resp *schemas.DeepIntShieldResponse, bifrostErr *schemas.DeepIntShieldError) (*schemas.DeepIntShieldResponse, *schemas.DeepIntShieldError, error) { if resp != nil && resp.ChatResponse != nil { // Add custom metadata resp.ChatResponse.ExtraFields.RawResponse = map[string]interface{}{ "plugin_processed": true, "timestamp": time.Now().Unix(), } } return resp, bifrostErr, nil}Cleanup() error
Section titled “Cleanup() error”Called on DeepIntShield shutdown. Use this to:
- Close database connections
- Flush buffers
- Save state
- Release resources
Step 3: Create a Makefile
Section titled “Step 3: Create a Makefile”Create a Makefile to automate building your plugin:
.PHONY: all build clean install help
PLUGIN_NAME = my-pluginOUTPUT_DIR = build
# Platform detectionUNAME_S := $(shell uname -s)ifeq ($(UNAME_S),Linux) PLUGIN_EXT = .so PLATFORM = linuxendififeq ($(UNAME_S),Darwin) PLUGIN_EXT = .so PLATFORM = darwinendif
# Architecture detectionUNAME_M := $(shell uname -m)ifeq ($(UNAME_M),x86_64) ARCH = amd64endififeq ($(UNAME_M),arm64) ARCH = arm64endif
OUTPUT = $(OUTPUT_DIR)/$(PLUGIN_NAME)$(PLUGIN_EXT)
build: ## Build the plugin for current platform @echo "Building plugin for $(PLATFORM)/$(ARCH)..." @mkdir -p $(OUTPUT_DIR) go build -buildmode=plugin -o $(OUTPUT) main.go @echo "Plugin built successfully: $(OUTPUT)"
clean: ## Remove build artifacts @rm -rf $(OUTPUT_DIR)
install: build ## Build and install to DeepIntShield plugins directory @mkdir -p ~/.deepintshield/plugins @cp $(OUTPUT) ~/.deepintshield/plugins/ @echo "Plugin installed to ~/.deepintshield/plugins/"Step 4: Build Your Plugin
Section titled “Step 4: Build Your Plugin”Build the plugin using the Makefile:
make buildThis creates build/my-plugin.so in your project directory.
For production, you may need to build for specific platforms:
# Build for Linux AMD64GOOS=linux GOARCH=amd64 go build -buildmode=plugin -o my-plugin-linux-amd64.so main.go
# Build for Linux ARM64GOOS=linux GOARCH=arm64 go build -buildmode=plugin -o my-plugin-linux-arm64.so main.go
# Build for macOS ARM64 (M1/M2)GOOS=darwin GOARCH=arm64 go build -buildmode=plugin -o my-plugin-darwin-arm64.so main.goStep 5: Configure DeepIntShield to Load Your Plugin
Section titled “Step 5: Configure DeepIntShield to Load Your Plugin”Add your plugin to DeepIntShield’s config.json:
{ "plugins": [ { "enabled": true, "name": "my-plugin", "path": "/path/to/my-plugin.so", "version": 1, "config": { "api_key": "your-api-key", "custom_setting": "value" } } ]}Plugin Configuration Options
Section titled “Plugin Configuration Options”enabled- Set totrueto load the pluginname- Plugin identifier (used in logs)path- Absolute or relative path to the.sofileconfig- Plugin-specific configuration passed toInit()version- (Optional) Plugin version number (default: 1). Increment this value to force a reload of the plugin and database update when DeepIntShield restarts. Useful when you want to ensure config changes take effect without manually clearing plugin state.
Step 6: Test Your Plugin
Section titled “Step 6: Test Your Plugin”Start DeepIntShield and verify your plugin loads:
./deepintshield-httpYou should see output like:
Init called[INFO] Plugin loaded: Hello World PluginMake a test request:
curl -X POST http://localhost:8080/v1/chat/completions \ -H "Content-Type: application/json" \ -d '{ "model": "openai/gpt-4o-mini", "messages": [{"role": "user", "content": "Hello!"}] }'Check the logs for plugin hook calls:
HTTPTransportPreHook calledPreLLMHook calledPostLLMHook calledHTTPTransportPostHook calledHTTPTransportPreHook calledPreLLMHook calledPostLLMHook calledHTTPTransportStreamChunkHook called (per chunk)TransportInterceptor calledPreHook calledPostHook calledAdvanced Plugin Patterns
Section titled “Advanced Plugin Patterns”Stateful Plugins
Section titled “Stateful Plugins”For plugins that need to maintain state across requests:
package main
import ( "sync" "github.com/maximhq/deepintshield/core/schemas")
var ( requestCount int64 mu sync.Mutex)
func PreLLMHook(ctx *schemas.DeepIntShieldContext, req *schemas.DeepIntShieldRequest) (*schemas.DeepIntShieldRequest, *schemas.LLMPluginShortCircuit, error) { mu.Lock() requestCount++ count := requestCount mu.Unlock()
// Use count for rate limiting, metrics, etc. return req, nil, nil}Error Handling with Fallbacks
Section titled “Error Handling with Fallbacks”Control whether DeepIntShield should try fallback providers:
func PostLLMHook(ctx *schemas.DeepIntShieldContext, resp *schemas.DeepIntShieldResponse, bifrostErr *schemas.DeepIntShieldError) (*schemas.DeepIntShieldResponse, *schemas.DeepIntShieldError, error) { if bifrostErr != nil { // Allow fallbacks for rate limit errors if bifrostErr.Error.Type != nil && *bifrostErr.Error.Type == "rate_limit" { allowFallbacks := true bifrostErr.AllowFallbacks = &allowFallbacks } else { // Don't try fallbacks for auth errors allowFallbacks := false bifrostErr.AllowFallbacks = &allowFallbacks } } return resp, bifrostErr, nil}Caching Plugin Example
Section titled “Caching Plugin Example”var cache sync.Map
func PreLLMHook(ctx *schemas.DeepIntShieldContext, req *schemas.DeepIntShieldRequest) (*schemas.DeepIntShieldRequest, *schemas.LLMPluginShortCircuit, error) { // Generate cache key from request key := generateCacheKey(req)
// Check cache if cached, ok := cache.Load(key); ok { return req, &schemas.LLMPluginShortCircuit{ Response: cached.(*schemas.DeepIntShieldResponse), }, nil }
return req, nil, nil}
func PostLLMHook(ctx *schemas.DeepIntShieldContext, resp *schemas.DeepIntShieldResponse, bifrostErr *schemas.DeepIntShieldError) (*schemas.DeepIntShieldResponse, *schemas.DeepIntShieldError, error) { if resp != nil && bifrostErr == nil { // Store in cache key := generateCacheKeyFromResponse(resp) cache.Store(key, resp) } return resp, bifrostErr, nil}Troubleshooting
Section titled “Troubleshooting”Plugin Fails to Load
Section titled “Plugin Fails to Load”Error: plugin: not a plugin file
Solution: Ensure you built with -buildmode=plugin:
go build -buildmode=plugin -o plugin.so main.goVersion Mismatch Errors
Section titled “Version Mismatch Errors”Error: plugin was built with a different version of package
Why this happens: Go’s plugin system requires exact version matching for:
- The Go compiler version
- All shared packages (especially
github.com/maximhq/deepintshield/core) - Transitive dependencies (packages that your dependencies depend on)
This is more strict than typical Go builds. Even if only one transitive dependency differs by a patch version, the plugin will fail to load.
Solution: Ensure your plugin is built with the exact same versions as DeepIntShield.
Step 1: Diagnose the mismatch
Use go version -m to inspect the build info of both your plugin and the DeepIntShield binary:
# Check what versions your plugin was built with:$ go version -m my-plugin.somy-plugin.so: go1.26.1 dep github.com/maximhq/deepintshield/core v1.3.50 dep github.com/valyala/fasthttp v1.51.0
# Check what versions DeepIntShield was built with:$ go version -m deepintshield-httpdeepintshield-http: go1.26.1 dep github.com/maximhq/deepintshield/core v1.3.54 # <-- MISMATCH! dep github.com/valyala/fasthttp v1.55.0 # <-- MISMATCH!Notice that even though the Go version matches (go1.26.1), the package versions are different — this causes the error.
Step 2: Update your plugin dependencies
# Update to match DeepIntShield's core versiongo get github.com/maximhq/deepintshield/core@v1.3.54go mod tidy
# Rebuild the plugingo build -buildmode=plugin -o my-plugin.so main.goStep 3: Verify the fix
# Confirm versions now match$ go version -m my-plugin.so | grep deepintshield dep github.com/maximhq/deepintshield/core v1.3.54 # Now matches!Platform/Architecture Mismatch
Section titled “Platform/Architecture Mismatch”Error: cannot load plugin built for GOOS=linux on darwin
Solution: Build on the target platform or use the correct GOOS/GOARCH for your system.
Function Not Found
Section titled “Function Not Found”Error: plugin: symbol Init not found
Solution: Ensure all required functions are exported (start with capital letter) and have the correct signature.
Source Code Reference
Section titled “Source Code Reference”The complete hello-world example is available in the DeepIntShield repository:
- Full Example: examples/plugins/hello-world
- main.go: Plugin implementation
- Makefile: Build configuration
- go.mod: Dependencies
Real-World Plugin Examples
Section titled “Real-World Plugin Examples”Explore production-ready plugins in the DeepIntShield repository:
- Mocker Plugin - Mock responses for testing
- Logging Plugin - Advanced request/response logging
- Semantic Cache Plugin - Cache based on semantic similarity
- Governance Plugin - Rate limiting and budget controls
- JSON Parser Plugin - Parse and validate JSON responses
Frequently Asked Questions
Section titled “Frequently Asked Questions”Do I need to rebuild my plugin when upgrading DeepIntShield?
Section titled “Do I need to rebuild my plugin when upgrading DeepIntShield?”Yes, absolutely. Plugins must be compiled against the exact same version of github.com/maximhq/deepintshield/core that DeepIntShield is using. This is a fundamental requirement of Go’s plugin system.
When you upgrade DeepIntShield, you must:
- Update your plugin’s
go.modto use the matching core version - Rebuild the plugin with the same Go version
- Redeploy the plugin alongside the new DeepIntShield version
Example:
If upgrading from DeepIntShield v1.2.17 to v1.3.0:
# Update your plugin dependencygo get github.com/maximhq/deepintshield/core@v1.3.0go mod tidy
# Rebuild the plugingo build -buildmode=plugin -o my-plugin.so main.goShould plugin builds be part of my deployment pipeline?
Section titled “Should plugin builds be part of my deployment pipeline?”Yes, strongly recommended. Your plugin build and deployment should be tightly coupled with your DeepIntShield deployment.
Recommended CI/CD Workflow:
# Example GitHub Actions workflowname: Deploy DeepIntShield with Plugins
on: push: branches: [main]
jobs: deploy: runs-on: ubuntu-latest steps: # 1. Checkout code - uses: actions/checkout@v3
# 2. Setup Go - uses: actions/setup-go@v4 with: go-version: '1.26.1'
# 3. Build DeepIntShield - name: Build DeepIntShield run: | cd transports/deepintshield-http go build -o deepintshield-http
# 4. Build ALL plugins with matching version - name: Build Plugins run: | cd plugins/my-plugin # Ensure plugin uses same core version as DeepIntShield go get github.com/maximhq/deepintshield/core@${{ env.DEEPINTSHIELD_VERSION }} go mod tidy go build -buildmode=plugin -o my-plugin.so main.go
# 5. Bundle everything together - name: Create deployment bundle run: | mkdir -p deploy/plugins cp transports/deepintshield-http/deepintshield-http deploy/ cp plugins/my-plugin/my-plugin.so deploy/plugins/ cp config.json deploy/
# 6. Deploy bundle to your infrastructure - name: Deploy to Production run: | # Upload to S3, copy to servers, deploy to K8s, etc. ./deploy.shKey Principles:
- Version Lock - Pin your plugin dependencies to specific DeepIntShield versions
- Atomic Deployment - Deploy DeepIntShield and plugins together as a single unit
- Build Verification - Test plugin loading as part of CI
- Rollback Strategy - Keep previous plugin versions for rollbacks
How do I handle plugin versioning in production?
Section titled “How do I handle plugin versioning in production?”Organize your plugin deployments by version:
/opt/deepintshield/├── v1.3.0/│ ├── deepintshield-http│ └── plugins/│ ├── my-plugin.so│ └── cache-plugin.so├── v1.2.17/│ ├── deepintshield-http│ └── plugins/│ ├── my-plugin.so│ └── cache-plugin.so└── current -> v1.3.0/ # Symlink to active versionThis allows easy rollbacks:
# Rollback to previous versionln -sfn /opt/deepintshield/v1.2.17 /opt/deepintshield/currentsystemctl restart deepintshieldCan I use different plugin versions for different DeepIntShield instances?
Section titled “Can I use different plugin versions for different DeepIntShield instances?”No. Each plugin must match the exact core version of the DeepIntShield instance loading it. If you’re running multiple DeepIntShield versions (e.g., staging vs production), you need separate plugin builds for each version.
staging/ deepintshield-http (v1.3.0) plugins/ my-plugin-v1.3.0.so
production/ deepintshield-http (v1.2.17) plugins/ my-plugin-v1.2.17.soWhat happens if I forget to rebuild a plugin?
Section titled “What happens if I forget to rebuild a plugin?”You’ll see errors like:
plugin: symbol Init not found in plugin github.com/you/pluginplugin was built with a different version of package github.com/maximhq/deepintshield/coreSolution: Rebuild the plugin with the correct core version. See the Version Mismatch Errors troubleshooting section for detailed diagnosis steps using go version -m.
How do I test plugins before production deployment?
Section titled “How do I test plugins before production deployment?”Multi-stage testing approach:
-
Unit Tests - Test plugin logic in isolation
func TestPreHook(t *testing.T) {req := &schemas.DeepIntShieldRequest{...}modifiedReq, shortCircuit, err := PreLLMHook(&ctx, req)assert.NoError(t, err)assert.Nil(t, shortCircuit)} -
Integration Tests - Load plugin in test DeepIntShield instance
Terminal window # Start test DeepIntShield with plugin./deepintshield-http --config test-config.json# Run test requestscurl -X POST http://localhost:8080/v1/chat/completions ... -
Staging Environment - Deploy to staging with production-like load
-
Canary Deployment - Gradually roll out to production
Can I hot-reload plugins without restarting DeepIntShield?
Section titled “Can I hot-reload plugins without restarting DeepIntShield?”Yes! DeepIntShield supports hot-reloading plugins at runtime. You can update plugin configurations or reload plugin code without restarting the entire DeepIntShield instance.
How do I debug plugin loading issues?
Section titled “How do I debug plugin loading issues?”Enable verbose logging:
{ "log_level": "debug", "plugins": [ { "enabled": true, "name": "my-plugin", "path": "./plugins/my-plugin.so", "config": {} } ]}Check plugin symbols:
# List symbols exported by plugingo tool nm my-plugin.so | grep -E 'Init|GetName|PreLLMHook'Verify Go version:
# Check Go version used to build plugingo version -m my-plugin.soCommon debugging steps:
- Verify file exists and has correct permissions
- Check Go version matches DeepIntShield
- Confirm core package version matches
- Ensure all required symbols are exported
- Review DeepIntShield logs for detailed error messages
Need Help?
Section titled “Need Help?”- Discord Community: Join our Discord
- GitHub Issues: Report bugs or request features
- Documentation: Browse all docs