Documentation

Zethal Framework is the original framework MevaSearch was built on. Here is everything you need to run it, make it your own and connect real search and AI providers.

Requirements

  • PHP 8.1 or newer.
  • The curl and mbstring extensions. gd is not needed.
  • Any web server that runs PHP: Apache, LiteSpeed, Nginx with PHP-FPM, or the built-in PHP server for local testing.
  • Outbound HTTPS from the server to wikipedia.org and wikimedia.org, or to your own provider.

No database, no Composer packages, no Node.js and no build step.

Installation

Clone the repository or download the ZIP, then put the files in your web root or in a subfolder.

git clone https://github.com/mevasearch/Zethal
cd Zethal
php -S 127.0.0.1:8080

Open http://127.0.0.1:8080 and search for anything. With no configuration at all, results come from Wikipedia, images and videos from Wikimedia Commons, and summaries are built from the top results without AI.

Files

index.php          Home page with search bar and shortcuts
results.php        Results page with tabs, summary and panel
settings.php       Settings page
api.php            Search endpoint used by results.js
ai.php             Summary and photo search endpoint
bangs.php          List of !bangs and the redirect logic
lang.php           Language loading and small helpers
zethal-icons.php   SVG icon sprite
styles.css         All styles, light and dark
script.js          Shared behaviour, settings, menus, bangs
results.js         Loads and renders results
lang/en/           English texts and info.json
fonts/             Space Grotesk with its OFL license
LICENSE            SA-SDL license text

CSS and JS links get a ?v= parameter from the file's modification time, so visitors always load the latest version after you upload a change.

Branding

Change the product name, tagline and description in lang/en/common.php, and in the same file of every other language you add.

'app_name'        => 'Your Search',
'app_tagline'     => 'Private search, beautifully simple',
'app_description' => 'A private search engine without ads or tracking.',

Colours live as tokens at the top of styles.css, separately for light and dark mode. The icons are in zethal-icons.php: each entry is the inner markup of a 24 by 24 SVG.

Please do not present your project as MevaSearch or use the MevaSearch name or logo. Attribution is not required, but the short "Built with Zethal Framework" note in the footer is appreciated.

Configuration

All settings are environment variables, read on the server with getenv(). Nothing is required. Keys never reach the browser.

VariableWhat it does
ZETHAL_SEARCH_URLYour search endpoint. When set, it replaces Wikipedia and Commons for every tab.
ZETHAL_SEARCH_KEYSent to your endpoint as Authorization: Bearer ....
ZETHAL_AI_PROVIDERanthropic (default) or any other value for an OpenAI-compatible API.
ZETHAL_AI_URLFull chat completions URL for OpenAI-compatible providers, for example https://api.openai.com/v1/chat/completions.
ZETHAL_AI_KEYAPI key of your AI provider.
ZETHAL_AI_MODELModel name. AI is used only when both the key and the model are set.

Apache and LiteSpeed

SetEnv ZETHAL_AI_PROVIDER anthropic
SetEnv ZETHAL_AI_KEY your-key
SetEnv ZETHAL_AI_MODEL your-model

Nginx with PHP-FPM

env[ZETHAL_AI_KEY] = your-key
env[ZETHAL_AI_MODEL] = your-model

A few limits are constants at the top of the PHP files: ZETHAL_RATE_LIMIT and ZETHAL_CACHE_TTL in api.php, ZETHAL_AI_RATE_LIMIT and ZETHAL_AI_MAX_TOKENS in ai.php.

Connecting a search provider

Your endpoint receives a GET request with these query parameters:

ParameterValues
qThe query, up to 400 characters.
typeall, images, videos or news.
page1 to 20.
count10, 20 or 30.
safestrict, moderate or off.

It should answer with status 200 and JSON like this:

{
  "total": 1280,
  "results": [
    {
      "title": "Example page",
      "url": "https://example.com/page",
      "description": "A short snippet of the page.",
      "thumbnail": "https://example.com/thumb.jpg"
    }
  ],
  "panel": {
    "title": "Example",
    "subtitle": "Short description",
    "text": "A few sentences about the topic.",
    "url": "https://example.com/about",
    "source": "Example"
  }
}

Only title and url are required. Results without an http or https URL are dropped, and thumbnails must use https. If you leave out panel, Zethal looks up a Wikipedia summary for the first result. A small adapter script on your side is usually enough to connect any search API.

If you show thumbnails from your own image hosts, add them to the img-src part of the Content Security Policy in results.php.

AI and photo search

Summaries

The results page sends the query and the top five results to ai.php. With a model configured, it asks for a two to four sentence answer based only on those sources. Without one, it picks the most relevant sentences from the results and labels the card as a summary built without AI.

Search with a photo

The camera button opens a dialog where visitors can drop, paste or choose a JPG, PNG or WebP image. The browser resizes it before upload, the server checks that it is a real image and sends it to a vision model that returns a short search query. The image is not saved anywhere.

Without a model, Zethal tries to build a query from a descriptive file name, such as eiffel-tower-night.jpg, and otherwise explains that photo recognition needs an AI model.

Visitors can turn summaries off on the settings page.

Languages

  1. Copy lang/en to a new folder named after the language code, for example lang/de.
  2. Edit info.json:
    {
        "name": "Deutsch",
        "dir": "ltr"
    }
  3. Translate the strings in common.php, index.php, results.php and settings.php.

The language shows up in settings straight away. Missing keys fall back to English, so you can translate step by step. Use "dir": "rtl" for right to left scripts.

The language is chosen from the visitor's setting first, then from the browser's Accept-Language header. Opening lang.php directly returns the list of installed languages as JSON.

Bangs

A bang at the start or the end of a query sends the visitor straight to another site, for example !w black holes or rust traits !gh. Typing ! opens a list of suggestions that can be picked with the arrow keys and Enter or Tab.

To add or remove bangs, edit the array in bangs.php. %s is replaced with the encoded query.

'osm' => ['name' => 'OpenStreetMap', 'url' => 'https://www.openstreetmap.org/search?query=%s'],

The settings page and the suggestion list update automatically.

Privacy and security

  • Settings and shortcuts are stored in the visitor's browser. The only cookie is the chosen language.
  • No analytics, fonts or scripts are loaded from third parties.
  • Tracking parameters such as utm_, fbclid and gclid are removed from result links, and visitors can hide whole domains.
  • Every page sends a strict Content Security Policy, Referrer-Policy: no-referrer and X-Content-Type-Options: nosniff.
  • Rate limits use a daily rotating hash of the IP address with a random salt. The address itself is never written to disk.
  • Search responses are cached for ten minutes in the system temp folder, keyed by a hash of the request.
  • Outgoing requests use HTTPS only, and AI requests never follow redirects.

What leaves your server

Zethal has no telemetry and never contacts MevaSearch. To show results it does send the visitor's query from your server to Wikipedia and Wikimedia Commons, or to your own provider if you set one. The visitor's browser loads image thumbnails directly from upload.wikimedia.org and thumb.wikimedia.org without a referrer. When AI is configured, the query, the top results and uploaded photos go to your AI provider.

Deploying

  • Upload all files, including the lang folder, and serve the site over HTTPS.
  • Set your environment variables in the server configuration, not in files the web server can serve.
  • The results and settings pages send noindex. Leave the home page indexable if you want it to be found.

Set a contact in the User-Agent. Wikimedia asks every client to identify itself with a way to reach its operator. Before you put a public site online, change ZETHAL_USER_AGENT at the top of api.php:

const ZETHAL_USER_AGENT = 'YourSearch/1.0 (https://your-domain.example; you@your-domain.example)';

License and pricing

Zethal Framework is released under the SA-SDL license. The full text is also in the LICENSE file in the repository and it is the only binding description of what you may do with the code. It is source available, which is not the same as open source in the OSI sense.

You may use Zethal in projects that earn money. If you sell Zethal itself as a standalone product, paid service or subscription and it earns more than 1500 USD a month, the license asks you to submit the project for review. For details, read the commercial license and see the plans on the pricing page.

MevaSearch was built on Zethal, but this public edition does not include the MevaSearch index, ranking, data or services.

Questions or ideas? Open an issue on GitHub.