LDEDQ57E4TRB3XN43A2FAOSWYKDTDF6J6ECBDTDERZ7GZJ2EATBQC
`fly`-ing `huginn` close to the ground
===
konami are remaking MGS3. some people are trying to sell [electric
ekranoplans](https://www.regentcraft.com/seagliders/viceroy#specifications). and
here i am being a freeloader nerd for the 2nd decade. ingredients:
- [huginn/huginn image](https://ghcr.io/huginn/huginn) (sponsor [co-author & maintainer knu](https://github.com/sponsors/knu))
- [sentry DSN](https://sentry.io/pricing/) and/or [GlitchTip](https://glitchtip.com/pricing) ([donation page](https://liberapay.com/GlitchTip/donate))
- [fly.io dashboard](https://fly.io/docs/about/pricing/)
- [inoreader](https://jp.inoreader.com/pricing) and/or [feedly](https://feedly.com/i/pro) and/or [feedbin](https://feedbin.com/)
i use its agent to crawl comic/blogs' syndication feeds, then reformat & augment them to read easily in [feedme](https://github.com/seazon/feedme). The comics update at most daily ([SMBC](https://www.smbc-comics.com/comic/archive) has been running since 2002), or thrice-weekly (Ryan North's [dinosaurs](https://qwantz.com/index.php?comic=1) have been talkin' since 2003 (lovingly crafted in [XP's MS Paint](https://www.qwantz.com/index.php?comic=4005)). and the blogs update less frequently than that. there's no need to keep huginn web-part running 24/7, and we can also reduce the scheduler wake-up frequency.
Getting `flyctl` on MediaTek ramips mt7621
---
it's a softfloat Little-Endian MIPS cpu, we'll build `flyctl` like this:
```shell
scalar clone http://github.com/superfly/flyctl
cd flyctl/src
git sparse-checkout disable
env GOARCH=mipsle GOMIPS=softfloat make build
gzip bin/flyctl
# deliver flyctl.gz to the mips
```
Making sentry crons and/or glitchtip uptime monitor
---
on sentry, SMBC will have a daily check-in schedule. Dinosaur Comics posts on Mon-Wed-Fri, accounting for TZ & scheduling delay, we'll have a Tue-Thu-Sat check-in regimen. both will allow some further ~2h slack time. the monitoring slugs are readable text now, not UUIDv4 anymore. but that's guessable. along with the project's `SENTRY_DSN` and quickstart for rails, we're good to go.
on glitchtip, set up a 86399-second interval uptime heartbeat (they don't accept >=86400s). we'll use the URL later.
Launch pad
---
we start simple with the `supervisord`-based image. that'd keep a 1GB fly machine running all the time.
Fly people use docker images as a packaging format, they define the
environment around that. here, we bake in postgresql and the sentry
SDK gems. remember to [load `stackprof`
first](https://docs.sentry.io/platforms/ruby/profiling/#enable-profiling):
```bash
mkdir hug
cd hug
echo -e '.dockerignore\nDockerfile\nfly.toml\nhug-db.toml\nsentry.rb' | tee .dockerignore
tee Dockerfile <<-EOD
FROM ghcr.io/huginn/huginn:cbeb5c293ec3394dcd202b132b80e14398da8a11
ENV ADDITIONAL_GEMS=stackprof,sentry-ruby,sentry-rails
RUN APP_SECRET_TOKEN=nbd DATABASE_ADAPTER=postgresql bundle install -j 4
# we tune this often
COPY sentry.rb /app/config/initializers/sentry.rb
EOD
tee sentry.rb <<-EOR
Sentry.init do |config|
config.breadcrumbs_logger = [:active_support_logger, :http_logger]
config.traces_sample_rate = 0.5
config.profiles_sample_rate = 1.0
end
EOR
```
generate random for `APP_SECRET_TOKEN`:
```sh
podman run --rm -it --entrypoint sh ghcr.io/huginn/huginn:cbeb5c293ec3394dcd202b132b80e14398da8a11 rake secret
```
Build initial fly config, along with a managed postgres:
```bash
flyctl launch \
--org personal \
--name hug \
--region fra \
--push \
--vm-cpukind shared \
--vm-cpus 1 \
--vm-memory 1024
```
for the postgres, you can use development, 256MB, auto scale to zero (default after 1h, but [you can be more aggressive/conservative](https://fly.io/docs/postgres/managing/scale-to-zero/#turn-off-the-scale-to-zero-feature)). let's write down its config file here too:
```bash
flyctl config save \
--app hug-db \
--config hug-db.toml
```
and it's a good idea to save the running postgres image for later use:
```bash
flyctl image show -a hug-db
```
load some more secrets into the app:
```bash
flyctl secrets import <<-EOS
SENTRY_DSN=your_dsn
APP_SECRET_TOKEN=your_random
INVITATION_CODE=your_knock_password
EOS
```
and put more app config
```bash
tee -a fly.toml <<-EOT
[[statics]]
guest_path = "/app/public"
url_prefix = "/public"
[env]
RAILS_SERVE_STATIC_FILES="false"
TIMEZONE="Hanoi"
TZ="Asia/Phnom_Penh"
DOMAIN="hug.fly.dev"
FORCE_SSL="true"
DATABASE_ADAPTER="postgresql"
RACK_ENV="production"
RAILS_LOG_TO_STDOUT="enabled"
DO_NOT_SEED="true"
IMPORT_DEFAULT_SCENARIO_FOR_ALL_USERS="true"
AGENT_LOG_LENGTH="10" # used to fit heroku 10_000 rows limit
SCHEDULER_FREQUENCY="5"
DELAYED_JOB_SLEEP_DELAY="900" # s
USE_GRAPHVIZ_DOT="dot"
DIAGRAM_DEFAULT_LAYOUT="neato"
EOT
```
now you try to `flyctl launch` it, see the resulting 2.0GB image starts on fly regino `fra`
if you get context deadline exceeded for build, try wesockets mode
```bash
flyctl wg websockets enable
```
to attach in for debugging
```bash
flyctl ssh console -s
```
in there, multi-process img has `pstree` to view supervisor's tree
and tail the logs
```bash
flyctl logs
```
here, you can open the public URL, and create an account with the invitation code. there's no `admin` account because we didn't run SEED job. huginn will have created sample agents for you, explore them for idea
Mythical 0-link readable file
---
{
// "layer": "top", // Waybar at top layer
// "position": "bottom", // Waybar position (top|bottom|left|right)
// "height": 30, // Waybar height (to be removed for auto height)
// "width": 1280, // Waybar width
"spacing": 0, // Gaps between modules (4px)
// Choose the order of the modules
"modules-left": ["sway/workspaces", "sway/mode", "sway/scratchpad", "custom/media"],
"modules-center": ["sway/window"],
"modules-right": ["idle_inhibitor", "pulseaudio", "network", "cpu", "memory", "temperature", "battery", "battery#bat2", "tray","clock"],
// Modules configuration
// "sway/workspaces": {
// "disable-scroll": true,
// "all-outputs": true,
// "format": "{name}: {icon}",
// "format-icons": {
// "1": "",
// "2": "",
// "3": "",
// "4": "",
// "5": "",
// "urgent": "",
// "focused": "",
// "default": ""
// }
// },
"keyboard-state": {
"numlock": true,
"capslock": true,
"format": "{name} {icon}",
"format-icons": {
"locked": "",
"unlocked": ""
}
},
"sway/mode": {
"format": "<span style=\"italic\">{}</span>"
},
"sway/scratchpad": {
"format": "{icon} {count}",
"show-empty": false,
"format-icons": ["", ""],
"tooltip": true,
"tooltip-format": "{app}: {title}"
},
"mpd": {
"format": "{stateIcon} {consumeIcon}{randomIcon}{repeatIcon}{singleIcon}{artist} - {album} - {title} ({elapsedTime:%M:%S}/{totalTime:%M:%S}) ⸨{songPosition}|{queueLength}⸩ {volume}% ",
"format-disconnected": "Disconnected ",
"format-stopped": "{consumeIcon}{randomIcon}{repeatIcon}{singleIcon}Stopped ",
"unknown-tag": "N/A",
"interval": 2,
"consume-icons": {
"on": " "
},
"random-icons": {
"off": "<span color=\"#f53c3c\"></span> ",
"on": " "
},
"repeat-icons": {
"on": " "
},
"single-icons": {
"on": "1 "
},
"state-icons": {
"paused": "",
"playing": ""
},
"tooltip-format": "MPD (connected)",
"tooltip-format-disconnected": "MPD (disconnected)"
},
"idle_inhibitor": {
"format": "{icon}",
"format-icons": {
"activated": "",
"deactivated": ""
}
},
"tray": {
// "icon-size": 21,
"spacing": 0
},
"clock": {
// "timezone": "America/New_York",
"tooltip-format": "<big>{:%Y %B}</big>\n<tt><small>{calendar}</small></tt>",
"format-alt": "{:%Y-%m-%d}"
},
"cpu": {
"format": "{usage}% ",
"tooltip": false
},
"memory": {
"format": "{}% "
},
"temperature": {
// "thermal-zone": 2,
// "hwmon-path": "/sys/class/hwmon/hwmon2/temp1_input",
"critical-threshold": 80,
// "format-critical": "{temperatureC}°C {icon}",
"format": "{temperatureC}°C {icon}",
"format-icons": ["", "", ""]
},
"backlight": {
// "device": "acpi_video1",
"format": "{percent}% {icon}",
"format-icons": ["", "", "", "", "", "", "", "", ""]
},
"battery": {
"states": {
// "good": 95,
"warning": 30,
"critical": 15
},
"format": "{capacity}% {icon}",
"format-charging": "{capacity}% ",
"format-plugged": "{capacity}% ",
"format-alt": "{time} {icon}",
// "format-good": "", // An empty format will hide the module
// "format-full": "",
"format-icons": ["", "", "", "", ""]
},
"battery#bat2": {
"bat": "BAT2"
},
"network": {
"interface": "wl*", // (Optional) To force the use of this interface
"format-wifi": "{essid} ({signalStrength}%) ",
"format-ethernet": "{ipaddr}/{cidr} ",
"tooltip-format": "{ifname} via {gwaddr} ",
"format-linked": "{ifname} (No IP) ",
"format-disconnected": "Disconnected ⚠",
"format-alt": "{ifname}: {ipaddr}/{cidr}"
},
"pulseaudio": {
// "scroll-step": 1, // %, can be a float
"format": "{volume}% {icon} {format_source}",
"format-bluetooth": "{volume}% {icon} {format_source}",
"format-bluetooth-muted": " {icon} {format_source}",
"format-muted": " {format_source}",
"format-source": "{volume}% ",
"format-source-muted": "",
"format-icons": {
"headphone": "",
"hands-free": "",
"headset": "",
"phone": "",
"portable": "",
"car": "",
"default": ["", "", ""]
},
"on-click": "pavucontrol"
},
"custom/media": {
"format": "{icon} {}",
"return-type": "json",
"max-length": 40,
"format-icons": {
"spotify": "",
"default": "🎜"
},
"escape": true,
"exec": "$HOME/.config/waybar/mediaplayer.py 2> /dev/null" // Script in resources folder
// "exec": "$HOME/.config/waybar/mediaplayer.py --player spotify 2> /dev/null" // Filter player based on name
}
}
output 'Dell Inc. DELL P2422H BHFBMQ3' transform 270
input "type:keyboard" {
xkb_layout us
xkb_variant dvorak
xkb_options ctrl:nocaps
}
set $mod Mod4
# Font for window titles. Will also be used by the bar unless a different font
# is used in the bar {} block below.
font pango:monospace 8
exec --no-startup-id mako
exec --no-startup-id ibus-daemon -drx
exec --no-startup-id firefox
exec --no-startup-id emacs
exec --no-startup-id flatpak run org.telegram.desktop
exec --no-startup-id flatpak run com.skype.Client
exec --no-startup-id $TERMINAL
exec --no-startup-id org.keepassxc.KeePassXC
# Use Mouse+$mod to drag floating windows to their wanted position
floating_modifier $mod
# start a terminal
bindsym $mod+Return exec $TERMINAL
# kill focused window
bindsym $mod+Shift+apostrophe kill
# start dmenu (a program launcher)
#bindsym $mod+d exec --no-startup-id dmenu_run -fn sans
default_border pixel 1
hide_edge_borders smart
set $main LVDS-1
# Virtual1
set $side HDMI-A-1
# Virtual2
workspace 1 output $side $main
workspace 2 output $main
workspace 8 output $main
workspace 4 output $main
# assign [class="(?i)navigator"] 4
assign [class="(?i)firefox"] 4
assign [app_id="(?i)firefox"] 4
workspace 3 output $main
assign [class="(?i)emacs"] 3
assign [app_id="(?i)emacs"] 3
workspace 6 output $side $main
assign [class="(?i)skype"] 7
for_window [class="(?i)skype"] floating disable
workspace 7 output $main
assign [class="(?i)telegram"] 7
assign [app_id="(?i)org.telegram.desktop"] 7
workspace 9 output $side $main
assign [class="(?i)keepassxc"] 9
assign [app_id="(?i)org.keepassxc.KeePassXC"] 9
# change focus
bindsym $mod+h focus left
bindsym $mod+t focus down
bindsym $mod+c focus up
bindsym $mod+n focus right
# move focused window
# bindsym $mod+Shift+h move left
# bindsym $mod+Shift+t move down
# bindsym $mod+Shift+c move up
# bindsym $mod+Shift+n move right
# enter fullscreen mode for the focused container
bindsym $mod+u fullscreen toggle
# change container layout (stacked, tabbed, toggle split)
bindsym $mod+o layout stacking
bindsym $mod+comma layout tabbed
bindsym $mod+period layout toggle split
# toggle tiling / floating
# bindsym $mod+Shift+space floating toggle
# change focus between tiling / floating windows
# bindsym $mod+space focus mode_toggle
# focus the parent container
#bindsym $mod+a focus parent
# focus the child container
#bindsym $mod+d focus child
# Define names for default workspaces for which we configure key bindings later on.
# We use variables to avoid repeating the names in multiple places.
set $ws1 "1"
set $ws2 "2"
set $ws3 "3"
set $ws4 "4"
set $ws5 "5"
set $ws6 "6"
set $ws7 "7"
set $ws8 "8"
set $ws9 "9"
set $ws10 "10"
# switch to workspace
bindsym $mod+1 workspace number $ws1
bindsym $mod+2 workspace number $ws2
bindsym $mod+3 workspace number $ws3
bindsym $mod+4 workspace number $ws4
bindsym $mod+5 workspace number $ws5
bindsym $mod+6 workspace number $ws6
bindsym $mod+7 workspace number $ws7
bindsym $mod+8 workspace number $ws8
bindsym $mod+9 workspace number $ws9
bindsym $mod+0 workspace number $ws10
# move focused container to workspace
bindsym $mod+Shift+1 move container to workspace number $ws1
bindsym $mod+Shift+2 move container to workspace number $ws2
bindsym $mod+Shift+3 move container to workspace number $ws3
bindsym $mod+Shift+4 move container to workspace number $ws4
bindsym $mod+Shift+5 move container to workspace number $ws5
bindsym $mod+Shift+6 move container to workspace number $ws6
bindsym $mod+Shift+7 move container to workspace number $ws7
bindsym $mod+Shift+8 move container to workspace number $ws8
bindsym $mod+Shift+9 move container to workspace number $ws9
bindsym $mod+Shift+0 move container to workspace number $ws10
# reload the configuration file
bindsym $mod+Shift+j reload
bindsym $mod+Shift+p restart
# bindsym $mod+Shift+period exec "i3-nagbar -t warning -m 'You pressed the exit shortcut. Do you really want to exit i3? This will end your X session.' -B 'Yes, exit i3' 'i3-msg exit'"
bar {
# status_command i3status-rs
}
SSH_AUTH_SOCK=/run/user/1000/keyring/ssh
EDITOR=emacsclient
ALTERNATE_EDITOR=""
BAT_THEME=ansi
KUBECTL_EXTERNAL_DIFF=difft
XDG_DATA_DIRS=/home/hieuhg/.local/share/flatpak/exports/share:/var/lib/flatpak/exports/share:/usr/local/share:/usr/share
PATH=/sbin/:/bin/:/home/hieuhg/run/:/home/hieuhg/.cargo/bin/:/home/hieuhg/.local/share/flatpak/exports/bin/
BROWSER=firefox
TERMINAL=wezterm
#TERMINAL=org.wezfurlong.wezterm
#TERMINAL=terminator
GTK_IM_MODULE=ibus
QT_IM_MODULE=ibus
XMODIFIERS=@im=ibus
PKG_CONFIG_PATH=/home/hieuhg/.local/lib/pkgconfig
LD_LIBRARY_PATH=/home/hieuhg/.local/lib/