SearXNG and The Wall of Shame

Once again, I have looked at my lonely little webserver, just to see it mostly without use.

And so i said to myself: "Why do I even have a server?"

And then I realised: "Oh yea, because I got it for free!"

But I should still use it for something tho. And so I thought again and remembered that SearXNG exists. And so I decided to install it.

And no, I still haven't got the promised 4-CPU 24G-RAM Arm server, why do you ask.

The SearXNG

Do I even gain something?

Well yes! I have something to flex with on my server, what else do you want from me?

Some could say tha SearXNG is supposed to be decentralised, as it gains most of it's anonymity benefits by lots of people using one instance. Yes, that is true, but I'm more interested in the meta-search-engine aspect.

You see, in the realm of search engines, there are two main groups. The cool ones that maintain their own search index like Google, Brave or Mojeek and the normies that just use search index of someone else like DuckDuckGo or Startpage. Sure, they can claim that they are private and that they don't collect your data, but I don't trust anything that isn't selfhosted by some eastern European from his basement for about five users. I mean sure, they don't throw my data back at me via ads, so that's a plus, but I would still think trice before searching for anything sketchy with anything that ain't over Tor.

And then there are meta searchengines. These engines don't have their own index, but they search through multiple indexes at once and then compose all the results into a single list, making them less vulnerable to biases of separate search indexes.

And yes, when you use SearXNG, you can specify which indexes you want to source, but I want to add one extra that is not part of standard SearXNG, so I even have an actual reason to selfhost.

Install

First step is to clone the SearXNG repo somewhere on your server. Then you run the install script like so:

sudo -H ./utils/searxng.sh install all

Then you are faced with a lot of pressing return over and over again. So called "chicken installer". SearXNG seems to have something to do with Redis and it tries to compile some part of it. I got a bunch of errors and didn't notice any instance of Redis running afterwards, but everything works, so who cares? Right?

The installer even detected Nginx and tried to add itself, but failed miserably. Doesn't matter tho, as the docs provide a solution. Just put this into your site config:

location /s {
	uwsgi_pass unix:///usr/local/searxng/run/socket;

	include uwsgi_params;

	uwsgi_param    HTTP_HOST             $host;
	uwsgi_param    HTTP_CONNECTION       $http_connection;

	# see flaskfix.py
	uwsgi_param    HTTP_X_SCHEME         $scheme;
	uwsgi_param    HTTP_X_SCRIPT_NAME    /s;

	# see limiter.py
	uwsgi_param    HTTP_X_REAL_IP        $remote_addr;
	uwsgi_param    HTTP_X_FORWARDED_FOR  $proxy_add_x_forwarded_for;
	}

SearXNG can also serve over http, but my servers over a unix socket. I have decided to put SearXNG into /s/, because I was to lazy to get a certificate for another domain. I think it can stay there.

Now it's time to config!!

You config SearXNG in '/etc/searxng/settings.yml'. My config looks like this:

# SearXNG settings

use_default_settings: true

general:
  debug: false
  instance_name: "unit37-searxng"
  enable_metrics: true
  open_metrics: 'pass'

search:
  safe_search: 0
  autocomplete: 'brave'
  default_lang: 'en-US'
  formats:
    - html

server:
  # Is overwritten by ${SEARXNG_SECRET}
  secret_key: [REDACTED BY THE SECRET POLICE]
  limiter: true
  image_proxy: true
  # public URL of the instance, to ensure correct inbound links. Is overwritten
  # by ${SEARXNG_BASE_URL}.
  # base_url: http://example.com/location

redis:
  # URL to connect redis database. Is overwritten by ${SEARXNG_REDIS_URL}.
  url: unix:///usr/local/searxng-redis/run/redis.sock?db=0

ui:
  static_use_hash: true
  infinite_scroll: true

engines:
  - name: wikidata
    engine: wikidata
    disabled: false

  - name: wikipedia
    engine: wikipedia
    disabled: true

  - name: startpage
    engine: startpage
    disabled: true

  - name: duckduckgo
    engine: duckduckgo
    disabled: true

  # instead of duckduckgo (same index)
  # nevermind, it just started to throw a bunch of chinese at me
  - name: bing
    engine: bing
    disabled: true

  - name: mojeek
    engine: mojeek
    disabled: false

  - name: marginalia
    engine: json_engine
    paging: true
    # using shared key for now
    search_url: https://api.marginalia.nu/public/search/{query}
    results_query: results
    url_query: url
    title_query: title
    content_query: description
    categories: general
    shortcut: ma
    disabled: false

plugins:
  searx.plugins.tor_check.SXNGPlugin:
    active: true

To restart SearXNG, you run:

sudo -H service uwsgi restart searxng

The configuration seems quite well documented, nothing to complain about here. The 'use_default_settings: true' line includes the default config, so I'm just overwriting it instead of writing the entire config from scratch.

One thing to note is the addition of 'marginalia'. Marginalia is a search engine for... lets say obscure webpages. I think that it adds a nice touch to the SEO based outcomes of the other engines.

I have set the default language to English instead of auto, as it thought I was Hungarian for some reason. I don't even use a VPN, it just felt like it. I prefer not to be identifed a Hungarian, so I changed it.

Skining

So now I have a working SearXNG instance. Good. But I would also like it to feel a bit more personal. I would like to achieve this by changing the logo from SearXNG to something my.

So how do you do that you ask? Simple! You just have to fork the repo and change it there. Yes, this is the official way to customise your instance. But wait, it gets worse...

The logo is a png that is displayed via CSS 'background-image' property. The CSS is not written in any normal way, but as less. What is less you ask? IDK; some heresy probably.

Either way, you have to use the

./manage themes.all

script. NICE.

By which I mean like, before commit. You first write css, then compile it, then commit it, so that it is already compiled in the repo. I don't even know what this project is anymore. (it's written in python BTW)

Also, do you remember how I told you that the logo is a png? Well, turns out, it gets compiled from svg together with the css. This also removes everything from the image directory, so I had to put my jpg somewhere to be copied somewhere else next to some pngs that used to be svgs...

Why would you even want to use a png instead of a svg?

So yea, while I was at it, I also played a bit with spacing and made the values normally used for tablets default, as they work better with my larger image.

Anyways...

The point is that i have a SearXNG instance running now and I'm using it as my main search engine from now on.

And yes, it works surprisingly well with so little resources.

The wall of shame

You forgot bout this one, EY?

There's more new stuff. I feel productive today; a feeling I haven't felt in a long time.

So basically, I've found this fun api that writes funny insults and I thought that it would be fun to write a webpage that would just continuously write a bunch of them on a wall while someone reads them.

And so I wrote that.

the page

the source

Some fun stuff

Modern web browsers have a built-in TTS. It's not great, some would call it not good at all, but it's in there. And I'm sure you can make it a bit better if you try. Except for Brave. Chromium has it. It's just Brave. At this point I don't even want to ask anymore.

You cannot just start TTS on your own, it must be in reaction to the user doing something. (I HATE THE WEB I HATE THE WEB I HATE THE WEB I HATE THE WEB I HATE THE WEB)

When I asked Claude to add tags that would solve a CORS error, it just told me to wrap the request in a proxy. I'm not even surprised and it works, so I'm leaving it in.

async function getInsult() {
    var insult = 'ERROR WHILE FETCHING INSULT';
    const res = await fetch('https://api.codetabs.com/v1/proxy?quest=' + encodeURIComponent('https://evilinsult.com/generate_insult.php?lang=en&type=text'))

    if (res.ok) {
        insult = await res.text();
    }
    return insult;
}

You are not allowed to call async functions from outside of functions. To do that, you have to wrap your code in lambda like so:

(async () => {
   // do stuff here
})();

This is one of the expressions that require semicolon before them, so some people would write it like so:

;(async () => {
   // do stuff here
})();

And yes, the drawing code was written by Claude. I have learned JS canvas once and I'm not planing on doing it again. You might wish not to use the program for religious reasons or something.