Building Ruby 3.2 on FreeBSD

An update from the one I wrote years ago. That one overrides entire CFLAGS and thus missing -O3 and all those good optimization stuff.

In short, do this first:

export cflags="-I/usr/local/include"
export LDFLAGS="-L/usr/local/lib"

And then build as usual: ./configure --prefix=... && make ....

I usually put that block of config in a script called _build:

#!/bin/sh

<the export lines above>

exec "$@"

To use it just prefix the build commands with it: _build ./configure ... && _build ....

span, p, and html

<!doctype html>
<html lang="en">
<head>
  <title>Wat</title>
</head>
<p>
  <div></div>
</p>
<span>
  <div></div>
</span>
</html>

Given the html page above, the first <div> “inside” <p> is actually valid because </p> is optional. The end result is <p></p><div></div></p> with the last </p> being invalid because there’s no matching <p>.

On the other hand, <span> requires matching </span> so even though <div> can’t be put inside <span>, browsers still consider it to be inside the <span>, making it seem correct when viewed from developer tools.

Getting first address of specified IP and prefixlen in PHP

Dumping this here so I can refer to this when needed and in case anyone is looking for this.

function inet_prefixlen_start(string $inet, int $version, int $prefixlen): ?string
{
    switch ($version) {
        case 4:
            if (filter_var($inet, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) !== false) {
                $pack = 'c*';
                $size = 8;
            }
            break;
        case 6:
            if (filter_var($inet, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) !== false) {
                $pack = 'n*';
                $size = 16;
            }
            break;
    }

    if (!isset($pack)) {
        return null;
    }

    $ipArray = unpack($pack, inet_pton($inet));
    $groupMask = (1 << $size) - 1;
    $fullSize = $size * count($ipArray);
    $groupCount = count($ipArray);
    for ($i = 0; $i < $groupCount; $i++) {
        $mask = $groupMask;
        $indexStart = $i * $size;
        for ($j = 0; $j < $size; $j++) {
            $fullIndex = $indexStart + $j;
            if ($fullIndex < $prefixlen) {
                continue;
            }
            // shifting goes from the end, one less of size:
            // (size 8)
            // i = 0 -> shift = 7 (1000 0000)
            // i = 7 -> shift = 0 (0000 0001)
            $index = $size - $j - 1;
            $mask ^= 1 << $index;
        }
        // ipArray is 1-indexed because that's how unpack works
        $group = $i + 1;
        $ipArray[$group] = $ipArray[$group] & $mask;
    }

    return inet_ntop(pack($pack, ...$ipArray));
}

/**
some sample data
['1.2.3.4', 4, 24, '1.2.3.0'],
['1.2.3.5', 4, 31, '1.2.3.4'],
['::10.0.0.3', 4, 32, null],
['1:2:3:4::', 4, 24, null],
['invalid', 4, 32, null],
['1:2:3:4::1', 6, 64, '1:2:3:4::'],
['1:2:3::1', 6, 64, '1:2:3::'],
['1:2:3:4:5:6:7:8', 6, 64, '1:2:3:4::'],
['1::4:5:6:7:8', 6, 64, '1:0:0:4::'],
['1:2:3:4::10.0.0.1', 6, 64, '1:2:3:4::'],
['1:2:3:ffff::1', 6, 56, '1:2:3:ff00::'],
['::3', 6, 127, '::2'],
['1.2.3.4', 6, 128, null],
['invalid', 6, 128, null],
*/

Or just use one of those IP handling libraries.

Windows 8 Login Profile Image

For some reason my new Windows install isn’t displaying my profile image on login screen.

After many hours of digging it turned out the settings app which handles changing profile image doesn’t update the registry correctly. Namely it’s missing Image200. There are entries for other size but for some reason this install doesn’t set that specific one.

I’m not sure what caused it but for now adding the entry manually fixes it.

The entry is located at:

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\AccountPicture\Users\{USER-SID}

I also had to takeover ownership of the key.

Edit: WAIT NEVERMIND. My other install (which I used as reference) turned to also show exactly same problem. And so does my laptop with latest updates. There’s only one system which doesn’t have the problem but I haven’t updated it recently so it’s most likely broken by one of the recent updates.

Using ownCloud

Thanks to the fact NextCloud dropped Windows 8 support recently, I switched to ownCloud.

Installing the web app is pretty simple. Slightly more annoying in term of nginx rewrite compared to NextCloud but it’s fixed by just disabling “pretty” url. Their “pretty” url still involves paths ending with .php anyway. So much for being “pretty”.

There’s no problem with the desktop app either. It’s very similar to NextCloud app.

Then there’s the mobile app and here thing went bad. It occasionally crashes. By occasionally I mean it crashes at least twice when launching the app first time after not using it for a while. Opening Photos directory also used to cause crashes but apparently they fixed it recently.

That was with iOS version. Unfortunately the Android version also occasionally crashes. Not as bad as iOS though.

I’m staying with this for now until I fed up with the crashes and maybe just go back to NextCloud and use their older client version hoping it’ll stay working. Or if they finally decide to support earlier versions of Windows Server as it should also mean it’ll work with Windows 8 again.

Edit: browsing the NextCloud issues it looks like they removed hard requirement on the Windows 10 1709+ only API and Windows 8.1 will be supported again.

Serving Mercurial on OpenBSD with Gunicorn

One and other thing lead me to hosting my mercurial repositories on an OpenBSD VPS.

Here’s a bit of memo on how I did it.

A dedicated user needs to be created. I call it hg which is as generic as it can be. Then I create home directory of /home/hg, and set its $HOME to /home/hg/repos. Wait.

It’s so I can just push to ssh://hg@hg.myconan.net/reponame and not having to specify additional namespace. The /home/hg itself needs to contain some other files so that’s just how it ended up. I can probably put the extra files somewhere else but it seems simpler to have them all in single directory tree. Now I write it maybe I should’ve made it at /var/hg/root or something like that.

Well, it’s done deal.

I also made ~hg/.ssh/authorized_keys file and fill it with my key. Again, so I can push to it.

With that done, next is installing the required packages:

  • py3-gunicorn
  • supervisor
  • mercurial
  • nginx
  • certbot

Refer to this post on configuring the certbot. It worked so well and requires barely any maintenance so far.

As for gunicorn, I made /home/hg/hgweb directory which contains following files:

  • gunicorn.conf.py
  • hgweb.config
  • hgweb.py

Gunicorn config is pretty simple:

bind = 'unix:/home/hg/gunicorn.sock'
workers = 4
accesslog = '-'
errorlog = '-'
timeout = 30

Nothing fancy, and there’s no worker_class because none of the supported workers (apart of sync) seem to be supported under OpenBSD. Should be fine as it’s just for my personal use.

As for hgweb.py, it’s copied from /usr/local/share/mercurial/hgweb.cgi with config path adjusted to local hgweb.config and removed references to wsgicgi (import and .launch) as I’m using Gunicorn, not CGI.

hgweb.config itself on the other hand, it’s also pretty basic:

[paths]
/ = /home/hg/repos/*

[web]
baseurl = https://hg.myconan.net/
contact = nanaya
staticurl = /static

All those done, last part to start serving with Gunicorn is updating /etc/supervisord.conf. There’s an example in their official docs and I made some adjustments:

[program:hg]
command=/usr/local/bin/gunicorn --config=/home/hg/hgweb/gunicorn.conf.py hgweb:application
user=hg
directory=/home/hg/hgweb
stopsignal=INT
environment=PATH="/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin"
stdout_logfile=/var/log/supervisord/%(program_name)s-%(process_num)s.stdout.log
stderr_logfile=/var/log/supervisord/%(program_name)s-%(process_num)s.stderr.log

Mainly for having non-random log file path.

Create log directory with mkdir -p /var/log/supervisord, enable the service with rcctl enable supervisord, and hope it works.

Oh and chown hg:www /home/hg && chmod 710 /home/hg for basic file permissions. Oh and hg:hg owner and 700 permission for repos directory itself.

And lastly nginx:

server {
    listen 443;
    listen [::]:443;
    server_name hg.myconan.net;

    access_log /var/log/nginx/hg.myconan.net-access.log;
    error_log /var/log/nginx/hg.myconan.net-error.log;

    ssl_certificate certs/hg.myconan.net/fullchain.pem;
    ssl_certificate_key certs/hg.myconan.net/privkey.pem;
    ssl_trusted_certificate certs/hg.myconan.net/chain.pem;

    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;

    root /nonexistent;

    location = /favicon.ico {
        return 204;
    }

    location = /robots.txt {
        return 204;
    }

    location / {
        proxy_pass http://unix:/home/hg/tmp/gunicorn.sock;
        proxy_set_header Client-Ip $remote_addr;
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-For $remote_addr;
        proxy_set_header X-Forwarded-Port $remote_port;
        proxy_set_header X-Forwarded-Proto $scheme;

        limit_except GET HEAD {
            deny all;
        }
    }

    location /static/ {
        root /usr/local/lib/python3.8/site-packages/mercurial/templates;
    }
}

Nothing fancy either, just a basic https proxying setup with no write permission as I don’t want to setup http auth and only push using ssh.

/static/ directory is served directly to the installation’s templates directory. Subdirectory name already matches so no alias or symlink is needed.

rcctl enable nginx and don’t forget to rotate the log files by adding the two specified files to /etc/newsyslog.conf.

…that’s kinda long.

IPoE, but static IPv4

Continuing from previous post, at the end I mentioned about using Vultr to avoid paying extra for static IPv4 address through my ISP.

Well, there has been a different problem with IPv4 connection crapping out every now and then so I ended up getting that ISP static IP option hoping it will lessen the problem. No comment on that yet because it’s only been less than 12 hours since I got it set up.

So, the setup itself, because I’m not using one of the supported routers, I had to figure it out myself.

The ISP provides a few needed information for the setup:

  • Static IP Tunnel Endpoint: an IPv6 address to connect for IPv4 connectivity
  • Interface ID: IPv6 address suffix (last 4 group)
    • FreeBSD doesn’t support it (it’s ip-token in Linux) but it really is just for address suffix. Mine’s ::feed so my expected address is 2409:11:1c0:2300::feed. I have it set as external IP address
  • Static IPv4 Address: this is to be set at tunnel interface as source address.
    • There’s no IPv4 target address provided which is required for FreeBSD’s gif interface but apparently any address works. I put in 10.0.0.0
    • This blog says to use source as target as well but apparently it results in packet being forwarded back and forth indicated by 14ms ping to the source IP
  • “Update Server Details”: I have no clue what this actually does
    • It’s a set of URL, username, and password where you’re supposed to make a request to to update… something. The form is simple, just $URL?username=$USERNAME&password=$PASSWORD. The URL uses internal domain so the DNS server from IPv6 autoconfiguration is required to resolve it
    • I just hit it with curl and the move on
    • I suspect it’s to tell the tunnel provider the expected source IPv6 address?

Geared with information above, there are a few changes needed since last post for setup on FreeBSD:

  • IP address on internet port should be suffixed with provided interface ID
  • Tunnel source and target address need to be adjusted
  • Tunnel interface need IPv4 address
  • Default routing for IPv4 is no longer on interface level (-iface gif) but instead the random IPv4 address used as tunnel target address (10.0.0.0 in my example above)
  • NAT is not automatically available anymore so PF is required
  • Also on NAT, MSS will need to be fixed as well
    • I still don’t really understand how this works

Most of the changes should be obvious. And here’s the config for PF:

# This is pf.conf for FreeBSD and won't work on OpenBSD

# variable to not hardcode interface names and stuff
ext_if = gif0
net_local = "192.168.0.0/24"

# I still don't know if this is needed. Or even what the correct value is.
scrub on $ext_if max-mss 1420

# basic nat
nat on $ext_if from $net_local -> ($ext_if)

DKIM signing with OpenSMTPD and dkimproxy

After a long wait of OpenSMTPD 6.6 and its accompanying rspamd filter, I finally sent my mails DKIM-signed. Along the way I also discovered rspamd does a bit too much for my need and learned that it’s way simpler to use dkimproxy instead.

Outline of the steps required as follow:

  • Install OpenSMTPD
  • Install dkimproxy
  • Create signing keys
  • Decide “selector” name
  • Add relevant DKIM entry to all relevant domains
  • Setup dkimproxy to sign stuff
  • Setup OpenSMTPD to relay to dkimproxy before finally sending the message
  • Test
  • Done

Signing keys are created by:

openssl genrsa -out /etc/mail/dkim/selector1.key 1024

Followed by creating the public key for DNS entry:

openssl rsa -in /etc/mail/dkim/selecto1.key -pubout -out /etc/mail/dkim/selector1.pub

Don’t forget to fix private key permission to 400 owned by whatever user running dkimproxy.

The dkimproxy setting is pretty simple:

listen 127.0.0.1:10027
relay 127.0.0.1:10028
domain domain1.com,domain2.net
signature dkim(c=relaxed)
signature domainkeys(c=nofws)
keyfile /etc/mail/dkim/selector1.key
selector selector1

It’s pretty straightforward.

And equally straightforward the settings for OpenSMTPD:

table aliases file:/etc/mail/aliases

listen on lo0
listen on lo0 port 10028 tag DKIM

action "local" mbox alias <aliases>
action "relay_dkim" relay host smtp://127.0.0.1:10027
action "outbound" relay

match tag DKIM for any action "outbound"
match for local action "local"
match for any action "relay_dkim"

First line sets the aliases.

Followed by a listener on localhost because this is just an example for sending-only server.

The listener on port 10028 is to accept the signed mail by DKIM to be finally sent.

local action sending mails to mbox for local user.

relay_dkim action will send mails to dkimproxy which will sign the email…

And relayed to 10028, accepted by smtpd, tagged DKIM and thus will be finally sent to where it should be. I learned the hard way it needs to come first because mails will be acted on first match.

Local for local. Don’t bother doing anything.

And finally the rest will be relayed to dkimproxy. As mentioned above, this must come after the outbound action for DKIM tagged mails.

If you want to run proper mail server with rspamd and stuff, read this instead. That article was also the reason I started looking into all this DKIM stuff.

Letsencrypt, cavemen edition

Just had to do some letsencrypt setup in some servers so I figured I should write down what I did so I can just check this page again instead of digging how I did it previously.

Requirements:

  • nginx
  • certbot

This assumes the server only serves https and redirects all http traffic. Adjust as needed otherwise.

Full nginx SSL/TLS config not included.

First add this config to nginx to handle verification:

# part of default port 80 config block
location /.well-known/acme-challenge/ {
    root /var/www/certbot;
}

And then create the directory (I’m not actually sure if needed):

# mkdir -p /var/www/certbot

Make the first cert because I’m too lazy to ensure the config directory is setup correctly:

# certbot certonly --webroot -w /var/www/certbot -d DOMAIN_NAME_GOES_HERE --keep --agree-tos --email SOME_KIND_OF@EMAIL_ADDRESS --no-eff-email

At this step, the certificate and all should have been properly generated.

Then use it in nginx configuration, the relevant server block:

ssl_certificate /etc/letsencrypt/live/DOMAIN_NAME_GOES_HERE/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/DOMAIN_NAME_GOES_HERE/privkey.pem;
ssl_trusted_certificate /etc/letsencrypt/live/DOMAIN_NAME_GOES_HERE/chain.pem;

If the full path seems too long, symlink it to nginx config base directory or something.

Update certbot CLI configuration located at /etc/letsencrypt/cli.ini:

rsa-key-size = 4096
text = True
authenticator = webroot
webroot-path = /var/www/certbot

To add more certificates:

# certbot certonly -d ANOTHER_DOMAIN

Don’t forget to update nginx configuration as before.

Since the certificate needs renewal periodically, create this simple script:

#!/bin/sh
# I personally put this in /root/bin/refresh-ssl-certbot

/usr/bin/certbot renew
/path/to/sbin/nginx -s reload

Make executable, etc. Try it to make sure it runs properly.

Then add it crontab. I usually do it weekly.

And done.

There might be smarter way using certbot’s nginx plugin or something but I haven’t bothered reading its documentation and initially this was just a stopgap switching from acme-client which is way simpler but stopped working for me few months ago.

IP address checker

Random idea I came up when reading nginx mailing list. A very simple way to set up external IP address checker using nginx on a remote server.

location = /ip {
    default_type text/plain;
    types { }

    return 200 $remote_addr\n;
}

Accessing /ip will then return the current external IP address. A more fancy output like JSON is possible as well.

VirtualBox again

Due to reasons I swapped my web server (was in VM) and VM box role. Or more like web server is now bare metal and functions as VM box. It’s downgraded from real virtualization (Hyper-V) to VirtualBox though.

That said, FreeBSD doesn’t seem to be quite fully functional on Hyper-V thanks to performance hit and slow disk detection on boot causing failure.

Yeah, I’ve had enough of it so I decided to swap the role and run the web server directly on the server instead.

On the bright side, I can use zfs for everything and no more stupid SSD setup (it was unbalanced 500 and 250 GB disks).

VirtualBox seems to be flaky as always though apparently once it’s setup correctly it runs without much trouble. Also thankfully VirtualBox on FreeBSD has complete scripts for running it headless and autostart on boot.

This was also my main setup for quite a long time years ago and I sure hope it’s at least as stable as it was back then! That said, the initial setup seems to be flakier. Oh well.

Memo and stuff.

Rails and Bootstrap

This hit me when I was rewriting front-end part of Zeropaste. Of course later I found out that there’s gem for it.

But anyway, if you don’t want to add another gem and feel like writing crapload of divs, it’s not that difficult. It breaks Rails standard form error handling though.

First, change the default error field handling to do nothing instead of wrapping it in a div:

config.action_view.field_error_proc = proc { |html| html }

(put in application.rb)

And then create this helper:

def error_class(object, attribute)
  "has-error" if object.errors.include? attribute
end

Finally, here’s how to use it (for attribute key of a model):

<div class="form-group <%= error_class f.object, :key %>">
  <%= f.label :key %>
  <%= f.text_field :key, :class => "form-control" %>
</div>

…and done.

Network performance checklist: TSO/LSO

Took me good few hours to find out why specific combination of task performs incredibly slowly on my server.

For reference, in my case it’s port-forwarded ssh/https connection over openvpn.

[ Client ] --(Internet)--> [ Gateway ] --(OpenVPN)--> [ Server (SSH) ]

Doing anything which takes up bandwidth (displaying log files, etc) will shoot the cpu load (at interrupt) up by a lot. By a lot I mean over 50% of one core.

This guy have the reasoning why it’s happening but I don’t know how much of it is correct. VMware support page also suggesting disabling it if network performance is slow.

In FreeBSD it’s:

echo 'net.inet.tcp.tso=0' >> /etc/sysctl.conf

In Windows it’s this .reg file:

Windows Registry Editor Version 5.00

[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters]
"DisableTaskOffload"=dword:00000001

It seems to involve a shell script in Linux so I won’t bother writing it off here since it differs by system.

As usual, YMMV.

…and there goes my time 🙁

dirlist-php

A few years ago I wrote a php script to provide better autoindex within nginx. I used it for quite a long period until I rewrote it in Ruby/Sinatra. But then I figured the setup for it is overly complicated just for simple task. And takes additional memory. I always have php-fpm running anyway so it’s free.

And so I decided to take up the old php script and fix it up. Unfortunately there isn’t other language as easy to setup for web as php which is why I fixed it instead of rewriting in some other languages (or keeping it ruby). The “fixed” version is still pretty much imperative-style but hey, it works.

Only tested with nginx.

Wasting time / 2048

2014-05-24 03.43.52

I’ve been wasting time on this thing called 2048 lately. And finally obtained my then-current goal of 8192. And then ended at 112k score since I wasn’t careful enough.

Next goal is 16384 but I’ll need to be able to reach 8192 without too much effort first…

PSA: Never trust external X-Forwarded-For

For god knows how long, proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; is one of the line usually included in nginx config snippet for proxying to a unicorn (Rails) backend.

…which is something you should never do unless you have another load balancer in front of the nginx being configured.

That line basically tells nginx to append $remote_addr to whatever X-Forwarded-For value currently set. It is only useful when your nginx is behind other load balancer which set up its own (hopefully correctly) X-Forwarded-For. It should be set explicitly to $remote_addr for any external-facing proxy. Otherwise fun things will happen.

FreeBSD pkg (manual) upgrade thingy

For working with locked packages (the ones which must be upgraded through compilation because of using custom options)

#!/bin/sh
# filename: pkg-lock-outdated

pkg query -e '%k = 1' %o | while read pkgorig; do
  pkg version -ovL => -O "$pkgorig"
done

The script above is to list locked packages which need upgrading. And to upgrade everything at once (and sit in front of PC waiting for whole process)

#!/bin/sh

listfile="/tmp/pkgforupgrade.$(date '+%Y%m%d%H%M%S')"
pkg-lock-outdated | cut -f 1 -d '<' > "$listfile"

while read <&3 outdated; do
  pkg unlock "$outdated"
  portmaster "$outdated"
  pkg lock "$outdated"
done 3< "$listfile"

rm -f "$listfile"

There’s another alternative of unlocking all packages at once, run batched portmaster, and lock them all again.

WordPress 3.5 Initial Impressions

This blog has been updated to WordPress 3.5. Initial impressions:

  • everything is less rounded now
  • Twenty Twelve (the new theme) sucks for following reasons:
    • uses custom font: causes this blog unreadable until fonts are loaded (at least in chrome)
    • image header: positioned below navigation menu (and the header text is above the menu)
    • no more search bar in navigation menu
  • the new media uploader looks useful

Overall there isn’t much change (what should I expect here) but the new theme sucks. At least to be used on this blog.

Zeropaste update

I decided to mess around with Zeropaste and added some “features”:

  • Show paste in plaintext (fixed width) and Markdown
  • Allows displaying paste in fixed width instead of full/auto
  • Setting mode and width based on url
  • Automatically updating url based on selected mode and width

0paste.com has been updated accordingly, including Rubinius 2.0.0rc1.

Now you can read glopping Asuna in convenient fixed width markdown (NSFW).

Yet another pastebin: 0paste.com

I decided to get a domain for it because of :reasons:. Anyway, the old ones from p.myconan.net is still accessible (it’ll redirect to new url at 0paste.com). Donations welcome.

Also because the world needs yet another pastebin. This one is running on Rubinius using Puma, by the way. Four threads with awesomest possible caching for showing pastes.

[ 0paste.com | Source ]

Completely Disable UAC in Windows 8

Windows 8, just like Windows 7, has Control Panel interface to disable UAC. There’s difference though: disabling UAC via Control Panel in Windows 8 doesn’t fully disable UAC. You can check it by launching Command Prompt: in Windows 7, you’ll get administrator command prompt (the signs are “Administrator: Command Prompt” window title and default directory at %WINDIR%System32) while in Windows 8, you’ll get normal command prompt.

Also reported here (complete with “fix”).

Fix by editing registry:

  • Key/Path: HKEY_LOCAL_MACHINESOFTWAREMicrosoftWindowsCurrentVersionPoliciesSystem
  • Name: EnableLUA
  • Value: 0
Note that there isn’t really much reason anymore to completely disable UAC. Well, in my case, this ancient (but useful) tool isn’t quite compatible with UAC.

Windows 8 Initial Impressions

6 hours with Windows 8.

  • Metro Modern UI is shit (the applications)
  • The Start Menu (or whatever it’s called now) is ok-ish though
  • The lock screen is awesome with its customizable background image
  • The global one is difficult to change though
  • File association was updated and doesn’t work properly in at least IrfanView and 7-Zip
  • Compressed folder still exists and can be disabled the same way as Windows 7
  • So is Aero Shake. Really though, there is no more Aero but there’s still Aero Shake? Is it some kind of joke?
  • The window border is thick-ish but I guess OK for now (quick google also showed some border customizer)
  • Hyper-V is in it as promised. Works as expected. Most OS need Legacy Network Adapter (except Ubuntu and SLES) or installation of the integration driver for some OSes. The biggest plus is it works at system level – VMs can be automagically started as Windows starts
  • All applications I usually use works without problem
  • It’s not move to (top right) corner but move to corner and move down a bit
  • Updates (from Microsoft Update) which requires license agreement doesn’t work (at least on my PC)
  • There’s no discernible performance difference
  • Yes, Everything also works
  • Also, flat
  • Windows Explorer is a bit better: no more intrusive action buttons above file list (moved to the top)
  • Remote desktop work OK. There’s no more classic style but I think the new one (Modern UI) doesn’t use too much bandwidth (being flat and all)
  • WRT RDP, the 2X Client I usually use in Android doesn’t work with Windows 8. The good old Remote RDP Lite works though (and with CyanogenMod’s plain keyboard, the physical keyboard input works again)

I think that’s it for now.

Low End Box and Web Applications

…one does not simply run both PHP and Rails applications (and MySQL) in single 128 MB box.

This is what happens if you try doing it. Swapping all the time. Hopefully Debian will fare better but it means I’ll need to reinstall the box again. I’ll do a test run in my local machine first though as not to waste my time doing backup/restore again like today.

(Yeah, Zeropaste is up at p.myconan.net)