aitoolsblocklist

package module
v1.0.3 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 25, 2026 License: MIT Imports: 8 Imported by: 0

README

aitoolsblocklist-go

Go module for recognising AI tools by domain. Network software written in Go (proxies, DNS forwarders, egress gateways, log processors) can call Check and learn whether a hostname belongs to an AI product, what category it falls in, and what the vendor says about training on customer input. The same data powers DNS filtering with AI threat protection, refreshed daily.

go get github.com/explainableaixai/aitoolsblocklist-go

The module uses only the standard library.

Minimal program

package main

import (
	"context"
	"fmt"
	"log"
	"os"

	atb "github.com/explainableaixai/aitoolsblocklist-go"
)

func main() {
	c := atb.New(os.Getenv("AQ_API_KEY"))
	r, err := c.Check(context.Background(), "chat.openai.com")
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(r["blocked"], r["primary_category"], r["trains_on_data"])
}

API surface

The package is deliberately small:

  • New(key string) *Client builds a client with the hosted base URL and an http.Client that times out after 30 seconds.
  • (*Client).Check(ctx, domain) (Result, error) performs one lookup.
  • Result is a map[string]any holding the decoded JSON.
  • *APIError carries the HTTP Status and raw Body for any response of 400 or above.

Client has three exported fields, APIKey, BaseURL and HTTPClient, which you can change after New. Replace HTTPClient to add tracing, a custom transport or a different timeout. Point BaseURL at a stub in tests.

Fields in a Result

Key Type after JSON decoding Notes
domain string What you asked about
blocked bool True for a known AI tool
matched_domain string Only when a parent domain matched
primary_category string Main function of the tool
ai_type string Whether AI is the product or a feature of it
categories []any of map[string]any category and optional subcategory
trains_on_data string yes, no, opt_out_default or unstated
opt_out_available, enterprise_no_training, api_no_training string Same value set
terms_checked string Date of the terms review

Numbers in map[string]any decode as float64, and booleans as bool. Type-assert with the two-value form, v, ok := r["blocked"].(bool), so a missing field never panics.

Error handling with errors.As

r, err := c.Check(ctx, host)
var apiErr *atb.APIError
switch {
case errors.As(err, &apiErr) && apiErr.Status == 429:
	// back off
case errors.As(err, &apiErr) && (apiErr.Status == 401 || apiErr.Status == 403):
	// key problem or quota exhausted: alert a human
case err != nil:
	// network error, context deadline, or invalid JSON
}

Empty key or empty domain returns an ordinary error before any request is made. Response bodies are read up to 2 MiB, which is far more than a lookup ever returns.

Putting it in the request path

A lookup is a network round trip, so put a cache in front of it. For a proxy that sees the same hosts over and over, a map guarded by a mutex with a fixed lifetime is enough. golang.org/x/sync/singleflight prevents a burst of identical lookups when a popular host first appears:

type cachedChecker struct {
	c   *atb.Client
	g   singleflight.Group
	mu  sync.RWMutex
	hit map[string]entry
}

type entry struct {
	r   atb.Result
	exp time.Time
}

func (cc *cachedChecker) Check(ctx context.Context, host string) (atb.Result, error) {
	cc.mu.RLock()
	e, ok := cc.hit[host]
	cc.mu.RUnlock()
	if ok && time.Now().Before(e.exp) {
		return e.r, nil
	}
	v, err, _ := cc.g.Do(host, func() (any, error) {
		return cc.c.Check(ctx, host)
	})
	if err != nil {
		return nil, err
	}
	r := v.(atb.Result)
	cc.mu.Lock()
	cc.hit[host] = entry{r, time.Now().Add(12 * time.Hour)}
	cc.mu.Unlock()
	return r, nil
}

Twelve hours keeps answers fresh without flooding the API. Unknown domains are worth caching too. They are the majority of traffic.

From answer to action

The lookup describes a tool. Your code decides. Keep that decision in one function so it can be reviewed:

func verdict(r atb.Result) string {
	if b, _ := r["blocked"].(bool); !b {
		return "allow"
	}
	switch r["trains_on_data"] {
	case "no":
		return "log"
	case "yes", "unstated":
		return "deny"
	default:
		return "warn"
	}
}

Most deployments add an approved-tools list that overrides this, keyed on matched_domain when present.

High-volume networks

A resolver answering thousands of queries a second should not depend on an outside call per query, however well cached. Plans that include the full downloadable list let you load every classified domain into memory at startup and refresh it nightly. Use this module for the gaps: hosts that are new since the last download, or ad-hoc checks from tooling and scripts.

Context and cancellation

Every call takes a context.Context. In a server, pass the request context so an abandoned client connection cancels the lookup. For background jobs, set a deadline:

ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
r, err := c.Check(ctx, "example.com")

The shorter of the context deadline and HTTPClient.Timeout wins.

Testing

net/http/httptest makes the client easy to test:

srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
	fmt.Fprint(w, `{"domain":"x.ai","blocked":true,"trains_on_data":"unstated"}`)
}))
defer srv.Close()
c := atb.New("test")
c.BaseURL = srv.URL

The module's own test does the same, and checks that the Accept: application/json header is sent.

Deploying behind a corporate proxy

If your service reaches the internet through an outbound proxy, set it on the transport rather than in code that builds requests:

c := atb.New(key)
c.HTTPClient = &http.Client{
	Timeout:   10 * time.Second,
	Transport: &http.Transport{Proxy: http.ProxyFromEnvironment},
}

ProxyFromEnvironment honours HTTPS_PROXY and NO_PROXY, the same variables your other tools already use.

The register is also reachable from Rust through the aitoolsblocklist crate, from notebooks through the PyPI release, from Node through the npm module, and from Flutter through the pub.dev package.

License

MIT

Documentation

Overview

Package aitoolsblocklist provides a client for AI Tools Blocklist.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type APIError

type APIError struct {
	Status int
	Body   string
}

func (*APIError) Error

func (e *APIError) Error() string

type Client

type Client struct {
	APIKey, BaseURL string
	HTTPClient      *http.Client
}

func New

func New(key string) *Client

func (*Client) Check

func (c *Client) Check(ctx context.Context, value string) (Result, error)

type Result

type Result map[string]any

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL