webfilteringdatabase

package module
v1.0.0 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

webfilteringdatabase-go

Classification client for Go gateways, resolvers and proxies. Give Classify a hostname or URL and it returns the filtering category the service assigns, drawn from 59 filtering categories built for enforcement. Use it next to the downloadable database: the file answers for domains you already have, and this client answers for the ones you do not.

go get github.com/explainableaixai/webfilteringdatabase-go

Call it

wf := webfilteringdatabase.New(os.Getenv("AQ_API_KEY"))
res, err := wf.Classify(ctx, "newly-registered-site.example")

res is a webfilteringdatabase.Result, a map[string]any decoded from the JSON reply. The central value is the filtering category, with a confidence score. The API reference on the product site lists every field. The client passes the JSON through untouched.

Fitting it into a filter

A filter built in Go usually has a hot path that must never block and a slow path that may. Classification belongs on the slow path. A workable shape:

type Filter struct {
	local   map[string]string // loaded from the licensed file
	learned sync.Map          // host -> category, filled in the background
	pending sync.Map          // host -> struct{}, avoids duplicate work
	wf      *webfilteringdatabase.Client
}

func (f *Filter) Category(host string) string {
	if c, ok := f.local[host]; ok {
		return c
	}
	if c, ok := f.learned.Load(host); ok {
		return c.(string)
	}
	if _, busy := f.pending.LoadOrStore(host, struct{}{}); !busy {
		go f.learn(host)
	}
	return "unclassified"
}

func (f *Filter) learn(host string) {
	defer f.pending.Delete(host)
	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
	defer cancel()
	r, err := f.wf.Classify(ctx, host)
	if err != nil {
		return
	}
	if c, ok := r["web_filtering_category"].(string); ok && c != "" {
		f.learned.Store(host, c)
	}
}

The first request for an unknown host gets the policy for unclassified. Every request after that gets the real category. Nothing waits on the network.

Policy for the unclassified window

What unclassified means is a policy decision, not a technical one:

Environment Typical choice
School or library Block until classified
Corporate office Allow and log
Guest Wi-Fi Allow, but block known high-risk categories
Kiosk or locked-down device Block

Newly registered domains are over-represented in phishing and scams, so strict networks lose little by holding them for the minute a classification takes.

Mapping categories to actions

Keep categories and actions apart. A small table per tenant, loaded from configuration, is easier to audit than logic spread through code. The names below are placeholders, so use the exact category names from the API reference:

var policy = map[string]string{
	"Adult":      "block",
	"Gambling":   "block",
	"Proxy":      "block",
	"Social":     "allow",
	"Streaming":  "throttle",
}

Look up the category, fall back to a default action, and log both values. When someone asks why a site was blocked, the log answers in one line.

Errors and backpressure

  • *webfilteringdatabase.APIError for any status of 400 or above. Inspect Status: 429 means ease off, while 401 and 403 mean check the key or quota.
  • Context and transport errors for timeouts and network faults.
  • A plain error, before sending, when the key or value is empty.

In a background learner like the one above, drop the item on error and let the next request for that host try again. For 429, pause all learners briefly, because every goroutine will hit the same limit.

Configuration

New sets BaseURL to the hosted API and HTTPClient to a client with a 30 second timeout. Both are exported fields. Swap HTTPClient for one with your own transport if the gateway must egress through a proxy, and set BaseURL to a stub for integration tests. The key travels in the POST body over HTTPS, as the endpoint requires.

What gets sent

Only the string you pass to Classify, plus the key. The client adds no client IPs, user identities or headers from the filtered request. If you classify full URLs, strip query strings first, since they sometimes carry tokens or email addresses. The host is enough for a category.

MSPs and multi-tenant filters

When one gateway serves many customers, share the learned categories across all tenants and keep policy tables per tenant. A domain's category is a fact about the domain, while blocking it is a customer's choice. New tenants then benefit from everything the gateway has already learned.

AI and shadow IT

Generative AI rarely fits a traditional filtering scheme. To give it proper treatment, load a dedicated AI category for DNS and proxy filters next to the general database. To find out how much AI is already in use, the resolver logs you keep anyway reveal unapproved AI use hidden in DNS logs.

If you need URL category data you host yourself for analytics rather than enforcement, or want to classify newly seen domains on demand with the IAB taxonomy, those services use the same account model.

Compliance notes

US schools that receive E-rate discounts must filter under the Children's Internet Protection Act. The categories map directly onto the content types CIPA names. Enterprises often use the same categories to enforce acceptable-use policies and to cut exposure to malware and phishing sites.

Testing

srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
	fmt.Fprint(w, `{"web_filtering_category":"News","confidence":0.93}`)
}))
defer srv.Close()
wf := webfilteringdatabase.New("test")
wf.BaseURL = srv.URL

Observability

Export three counters from the learner: lookups started, lookups failed by status, and time to classify. A rising share of 429s means the learner pool is too eager. A growing backlog of pending hosts means traffic has shifted to many new domains, which is worth a look on its own, since sudden bursts of fresh domains often come from malware or a phishing campaign.

Same client, other stacks

There is a Dart and Flutter build, a Rust crate for the same endpoint, a PHP library on Packagist and a JavaScript package.

License

MIT

Documentation

Overview

Package webfilteringdatabase provides a client for Web Filtering Database.

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) Classify

func (c *Client) Classify(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