Elsewhen 1.5: Time Zones, Translated

DevelopmentApps

We’ve recently released Elsewhen 1.5, a time zone app that I develop with a group of friends. Jason Snell featured it over at Six Colors:

One app that can help is Elsewhen from The Lovely Developers, a fun group that sprung out of the Relay FM Discord. Elsewhen lets you quickly set a date and time and then translate it — either into a bunch of human-readable time zones, or into Discord’s time-code format.

The latest version not only showcases a brand new UI and the ability to create and save groups of time zones, but also brings it to the Mac for the first time. If you ever have the need to share a given time in a multitude of time zones around the world, or are part of an iternational Discord community where their time codes feature comes in handy, give it a whirl. It’s been a blast working on it.

My D&D Adventures

D&D

When I first got into D&D a year or so ago, I created my first character for a play-by-post (PbP) game called The Lost Jungles of Rabbad. Since then, I’ve joined other PbP games, played a few live one-shots (via video call), starting running my own PbP game with a pre-rolled adventure from D&D Beyond, fought other players in two player-vs-player (PvP) arenas, and play-tested a couple of new campaign ideas from our DM.

I hope to write more about my characters in the future, but here’s a summary of the games so far.

We’re also playtesting a new campaign idea from Rob, which I won’t say much about other than it’s not D&D-based this time, but uses fate dice. In the first playthrough I played as Kalamae, Goddess of Life and Destiny, Mother of Creation, Watcher of the Sands, and in the second (currently in-progress) as Sa’khuna, God of the Night and the Underworld, Lord of Darkness, Keeper of the Keys. It’s a lot of fun!

One of the most fun aspects of character creation is their visual design, and Hero Forge is a fantastic place to get inspiration and build colourful 3D models. You can even buy them as STL files or 3D printed figures for your in-person games, though I haven’t done that yet. Here’s what all my characters look like:

Hero Forge designs of my characters

I never realised that D&D could be as fun as all this. I have found a fantastic group of people to play with and seeing everybody’s creativity is truly amazing.

LEGO Review: 71403 Adventures with Princess Peach

LEGO Reviews

LEGO’s expanding their Super Mario range this summer, and I’ve written a review of the latest character to the lineup, Princess Peach, over on Brickset:

This year, another well-known figure from the Mushroom Kingdom joins the lineup, with 71403 Adventures with Peach! The previous two starter courses were designed to provide everything you need to get going with the range, even if you don’t have the previous sets, and Peach is no different, and is in fact the largest of the three.

The Lost Jungles of Rabbad

D&DThe Lost Jungles of Rabbad

It is high summer in Illwind, the cool air off the sea balanced well against the heat from the desert to the east. The port town is busier than ever today, with the Feast Of Forgotten Gods in full swing. All throughout the streets, children run with green and yellow streamers, horns and whistles blaring. In the town square, the inns have thrown open their doors, each serving feasts for the revellers, here you see a whole buffalo being roasted, there the largest fish pie you’ve ever seen. The smells and sounds are a heady mix, not to mention the free flowing beer, wine, ale and mead.

Most of the festival goers are gathering around here, to enjoy the food and watch the Momerie taking place there. This year telling the tales of a lost goddess of the plains, who fell in love with a mortal man, and forsook her divine power to live and age and die at his side.


Welcome to Illwind, a small city-state bordering the jungle, the sea, and the desert. This is a town of Rob’s creation, one of many settings in the large world that he’s created as a Dungeons and Dragons (D&D) game master, where he runs many campaigns for experienced and new players alike.

About a year ago, I joined my first Dungeons and Dragons group. It’s something I’d been vaguely interested in, but had never had the opportunity to get involved with. These games were set up by Rob as a way to introduce new people to the hobby, and play in a no-pressure, no-rush environment as text chat in a private Discord server. Play-by-post, as it’s called, means it doesn’t matter that none of our time zones line up, or if we’re busy with “life” - we can play at our own pace, and somebody’s absence only holds up the storyline during the more rigid turn-based combat parts. I eagerly joined, and was grouped together with four others into a campaign called The Lost Jungles of Rabbad, the initial hook being described as follows:

The Jungles of Rabbad are a wild and unexplored wilderness, replete with tales of lost cities, roaming undead and wild forgotten beasts. Two weeks ago, a party from the Temple of Oghma left on a expedition to find a hidden temple. Three days ago, contact was lost. Someone needs to go in and get them....

We then created our characters, starting at Level 2, and put together this band of misfits:

It was a good mix - a squishy spellcaster, a sneaky rogue, a couple of melee fighters, and a healer. It was time to jump in, and start playing the game.


The campaign has now been running for almost a year, and the characters have levelled up from two to six. A lot has happened, some of it following the initial path of the DM’s campaign, and much of it not! I thought I would write up the campaign from the start to where we are now, summarising each event, as a log for future reference.


Alb’s Assignment

The characters, never having met each other before, all (except Sumunar) come together in an inn in Illwind, The Half-Turned World, presided over by the barkeep, Undril. New to the others, Tylress is a regular, a place he meets with his friend Sylvie (an elf with severe PTSD). Together, the crew head on down to the docks in search of Alb, a man known in the town for offering coin in exchange for jobs, not all of which are legal.

Joined by Sumunar on their way onto Alb’s boat, The Orphans Lament, they convince Alb of their competence by way of a fight against four animated and armed mannequins. He promises them 30 gold now, and 50 gold on return, for delivering a chest to Fulsi at the Farr Inn.

After deceiving Alb’s minions and getting 45 gold out of them instead, they head off to the south towards the inn, a midpoint on the road between Illwind and Barrow’s Edge, a journey of a few days.

Nib and Jul

During the second day on the road, the group meet a pair of elves, destined for Barrow’s Edge, and they fall into travel together. These are the siblings Nib, a white-haired bard, and Jul, a mute monk with some unknown dark magic about her. The group learn that the pair have been hired by The General (the leader of the barracks town Barrow’s Edge), on a “dangerous and urgent” mission, so of course Deimos offers their services.

Tylress is less than convinced about Jul, warning the rest of the group against her, which almost results in a fight on the road.

The Farr Inn

At the inn, Elmer greets Fulsi (to whom he apparently owes coin), and they take the chest round to the barn at the back. Fulsi runs an illegal fighting ring from here, and Sumunar taps in to take her turn in a bout, losing to a tall goliath, Xeri. Exhausted, the party all retire to bed.

Tylress is woken in the night by Jul, gesturing to him to head outside in the rain. She challenges him to a fight, to put their previous quarrel behind them. It’s tough, but Tylress is victorious, knocking Jul unconscious.

In the morning, the party awake. Deimos finds a small bronze coin with a compass engraved on it tucked in his pack - the assumption is it came from Nib, but Nib denies all knowledge of it.

Bandits on the road!

On the road from the Farr Inn down to Barrow’s Edge, the party is stopped by a group of figures in green uniforms, two mounted on horses. They demand a toll for usung the road, “in the Queen’s Name”. The group doesn’t fall for this, and a fight breaks out. The bandits are dispatched, along with one of the horses (unfortunately), and Elmer takes over looking after the other horse, whom he names Rafael.

The bandits all have matching tattoos on their wrists of golden stars, that Kosta recognises as the mark of an undead cult that’s been operating out of the jungle. Rumours say they’re trying to build an army!

Kosta calls upon Turn Undead, to make sure there are no undead around. Nothing happens, other than Jul winces as the shockwave from the spell passes over her.

As night falls, the party share some of the elves’ wine. Sumunar is plagued by dreams of burning cities, of skeletons crawling out of shallow graves, of shambling undead clawing at her face, and Nib sleeps badly, but nobody else is affected.

Barrow’s Edge

Reaching the city after lunch, Nib leads the group to meet with The General, a white dragonborn. She presents them with a rough, old map of the jungle, and tasks them with the following:

“The other planes are slowly encroaching on us, and this is where we believe the key, and the lock, are. I need you to go here, find the key, find the lock and seal the gates again.”

They’re looking for the source of the weak points in the lines between the planes of existence, and they sent in two sets troops to deal with it. They were all lost, only one person returned.

The group are given three old texts relating to the jungle:

Kosta heads to the infirmary to go and talk with the only surviving member of the scouting group, a green tiefling called Hoc. She’s missing a hand and an eye, and constantly speaking wordlessly, but uncommunicative.

Kosta contacts some celestial power, a being that appears to speak through him, who pours energy into Hoc to help her speak:

…from beneath one by two by one beneath the trees and the screams and the dark deep deeper deep…

The group learn they’d been in the jungle for a week or so, lost their healer to a group of undead, who they’d then followed south to the river, where there was a long abandonded camp across the water. They found an altar, headed down some dark, wet steps beneath it, that just ended abruptly.

Kosta’s new celestial patron then helps heal the tiefling, replacing her missing eye with a jet black orb, before the Barrow’s Edge healers step in.

The Celestial

Kosta reveals that the past few days he’s been … communing with a higer power. The Celestial, some kind of god/demon, is able to take over Kosta’s body, and speak through his voice. This appears to be the result of some kind of deal, details are scarce. It was the source of the power that helped heal Hoc, and it is quite happy to help in destroying any undead.

It also does not seem to like Jul. It has told Kosta she is “touched by darkness”, and that the group needs to “deal with her”. Elmer’s unable to get anything out of Nib, other than vague talk of dangerous missions “up north” to look for a lost tribe of elves; and likewise Deimos out of Jul.

That night, Deimos tails Jul to a building in Barrow’s Edge, which appears to be a tailors. She returns emptyhanded. Breaking in, Deimos finds nothing untoward inside.

Into the Jungle

The next morning, Nib’s been looking through the book written in celestial, and reveals that there is talk of binding spells that “lock” gates between the planes, and how to maintain them. This has previously been done at “the hollow of the world”.

Jul disappears, and returns with a black bag slung over her shoulder. She doesn’t mention it, other than to say it contains “supplies”.

The General presents the group with a set of cloaks that offer some camoflage in the jungle, and after obtaining a slightly better-drawn map from the local cartographer’s, they set off, heading into the jungle in the general direction of a wizard’s tower marked on it.

There’s no path through the jungle, the group having to pick their way over the tree roots etc. Passing a tree with an ancient small golden star carved into it, similar in design to the bandits’ tattoos, they stop to let Elmer cast a ritual to detect magic. The star glows of abjuration and enchantment, and there’s a similar one in trees to the north and the south, forming a sizeable magic circle, amplfying some arcana within, serving as a ward of the area.

Tylress destroys the magic in the star with his greatsword, with seemingly no effect to the rest of the sigils, and the team pass through further into the jungle.

The Clearing

The group comes to a clearing, which contains a large stone ring with four pillars surrounding it. Three of the pillars have a glowing red stone atop them, slowly pulsing. As Deimos circles the clearing to get a better look, the party are attacked by the vines, and have to fight hard to free themselves and clear the creatures. Deimos spends much of it poisoned and entrapped, with Kosta dolling out regular healing.

Kosta’s patron seems to believe it’s being caused by “the darkness in Jul”.

Eventually the creatures are cleared, the last one running into the foliage and disappearing. Poking about, a fourth red crystal is found on the ground, while Elmer scales a pillar and investigates the stone altar. It turns out to be some kind of teleportation circle. The group decide to rest for the night before activating the circle with the fourth crystal, and the conversation turns to their life stories.

Tylress’ Story

Tylress sighs “Fine, if nobody will tell my story then I will tell it myself. I’m sure many of you have met a variety of Dragonborn, but us reds are a particularly warfaring lot. Like my brethren I rose through the ranks, conquest after conquest. Everyone loses a battle, but I never lost a war. It’s how I earned my title of “The Victorious”. But I have honor, and I will not commit atrocities for the sake of it. Unfortunately others do not share this mindset. They see the blood and death as sport. And since I hadn’t yet reached the highest rank, there were plenty of bastards above me who would rather have their fun than honor. One day, I was instructed to lead my men into a village of elves and to slaughter them all. Men, women, and children. I refused. My commander seemed to be waiting for this moment. I was set up. Apparently, he felt threatened by a younger and more skillful warrior. He had me arrested and charged with insubordination. I was stripped of my rank and banished.”

Deimos’ Story

Deimos hesitates for a moment and takes a long drink before sighing. “I suppose I did start this. Well I was born a poor devil child and have no idea where I’m actually from or any of those little details. I was a foundling near a small provincial farming village by a wonderful human family. They overlooked…well all of this…” he indicates his wings and rather demonic appearance. “They raised me alongside their real daughter as a member of the family and taught me some of the finer arts.” He nods to to cook pot. “They made the decision to hide my appearance from the locals, which seems insightful considering what comes next. I attended the small human school and survived as the sickly child in robes. On the farm at night I was encouraged to fly and stretch my wings. All was good until…that day. I was in school as usual as for some reason a fire broke out at the school. As the students were running out, I heard a scream from within. As became apparent in that last fight, I enjoy a certain resistance to fire so I ran back to help. Well I may be resistant but not so much the robes.” Deimos is clearly uncomfortable at this point. “I was able to pull the boy out, but…didn’t make it in time. He didn’t survive, and neither did my disguise. I probably don’t need to detail what happened when the not-particularly open minded townsfolk saw…well me…carrying a dead child from a fire that isn’t having any effect on him.” A bit of sadness combined with anger is apparent now. “Well there’s a reason I’m not a huge fan of pitchforks. The mob realized who I was and proceeded to the farm. I was…too late to save my parents, but managed to pull my sister [Marlena] out before they could get her. I flew us as far and fast as I could. In the chaos of the next few days we were separated. I’ve been on my own, surviving and trying to find out what happened to her since then.”

“I’ve gotten pretty good at the survival part, but haven’t really made any progress on the other, and generally don’t stay in one place long enough to…enjoy…a similar reception.” Deimos is very clearly out of quips at this point and sits in silence, taking another long drink.

The Blackened Figure

During Deimos’ shift, a figure steps out into the clearing, a figure only he can see. A man, about Deimos’ age, half his face burnt away. He tells Deimos that they met, many years ago, and it was Deimos who did that to him, and that he will always be there waiting for him. None of the others in the group are able to see or hear the figure.

Deimos tells the others, believing it to be the boy he tried to save.

During Kosta’s shift that night, he casts Detect Magic, and sees that Jul glows brightly with necromancy, and the bag she is carrying with abjuration magic. He fights with his patron, The Celestial, who wants him to fight Jul and take her out. An internal argument between two higher powers happens within him.

In the morning, he confronts Nib about it, who is defensive and protective of his sister. Nib reveals that Jul has done a deal with something, something Kosta or The Celestial find distasteful, but that it is not their business. She is not evil or corrupt.

Teleportation

Deimos and Elmer use mage hands to place the crystal atop the final tower, and a dark space appears in the middle of the ring. Obviously, the group all jump through!

They find themselves in the basement of a half ruined church or temple, still in the middle of the jungle. The alter glows with a magic of an unknown school. It’s engraved with an incantation of an offering to The Dark One, possibly relating to Kiaransalee, a now-dead Drow god of revenge and undeath.

Crocodiles

The group leave the temple, and head towards the sound of running water. They soon come to a river, which they attempt to swim across but are beset by giant crocodiles. It’s a tough crossing, but they make it to the other side.

Katrina

The land this side is less jungle, more swamp. There is a large clearing to the south, with many gravestones made of reclaimed wood dotted across it, arranged in a wide spiral, culminating at a handful of buildings in the middle. Somebody watches them from a shaded porch, and Deimos approaches, hailing them. Their name is Katrina, they are a survivor from The Eli Flyer, a ship that ran aground. The rest of the crew are marked by the gravestones, and Katrina says she remains here to keep the woods nearby free of bodies, lest the Golden Hands raise them as undead - the cult with the star tattoos.

She seemingly doesn’t realise that the graves are in a spiral, and says that she cannot find her way “out”. Deimos offers to help, once the group’s current mission is completed. Elmer determines that the place is infused with enchatment magic, and Katrina herself with transmutation magic. He confronts her about this, and she says she will release Deimos from his enchantment when they have destroyed a red crystal atop a nearby tower, which will remove the binds keeping her there.

Having little choice, they head towards the tower she indicated.

Elmer’s Invisible Man

That night, during Elmer’s shift, a figure steps into the clearing, invisible aside from an outline against the trees. As Elmer approaches, it says nothing but:

HELP I. YOU HELP I. I YOU. I ARE YOU. HELP…

It disappates at Elmer’s touch.

The Red Hammers’ Tower

The tower sits within a square clearing, about five stories tall, with a red glow at the top. The top stories are half-exploded, with the chunks of masonry blown out but hovering in mid-air, as if the explosion had been paused. Deimos attempts to talk his way into the tower, but (of course) it turns into a fight, with the guards calling Katrina “the defiler”, and it is their duty to keep her bound.

During the fight with the guards, an enormous bird (a roc) joins the fight, causing trouble for Deimos’ usual flying tactics. The group dispatch the guards, but one flees. Nib is pushed from the top of the tower, and hits the ground, dead. Deimos blames himself, for his enchantment causing them to head to the tower. Kosta blames himself for abandoning his cleric path making a deal with another patron.

Deimos destroys the crystal, and with it, the entire tower collapses. Tylress grabs Nib, picks him up, and runs off into the trees. Jul takes Rafael and follows. The others rest, badly hurt.

Tylress takes Nib back to the clearing with the graves, to Katrina. All the graves are on fire. In return for a future favour, she is able to bring Nib back, though he is not quite the elf he was before. He is cold, his eyes misty, his skin grey.

Deimos’ Dream

The others make it back to the clearing, finding Jul lost in the woods. Resting for the night, Deimos finds himself on an endless plain of black sand, where he meets Katrina, an old man, and a devil around a campfire. They call themselves allies against oblivion, saying their name is Gargauth. They thank him.

In the morning, Kosta reveals he is familiar with the name Gargauth, as the Tenth Lord, the banished Lord of Hell.

Zombie T-rex

Sounds of movement lead the party to a clearing, where a load of zombie workers are clearing out a mine entrance, guarded by a group of living overseers, including a tabaxi that they had tracked riding a zombie t-rex. Deimos flies over, invisible, and sees they are pouring over a map. The group attempt to forge the map and swap it, but the swap goes badly and they end up fighting a load of zombies and the zombie t-rex ridden by the tabaxi, Night Bane. The rex also keeps vomiting up more zombies. It is a tough fight, with Elmer being seriously injured by the dinosaur, but they are victorious with the help of a jar of eyes that Sumunar had, turning into a pack of wolves when smashed.

The tabaxi is tied up, unconscious. When he wakes, Deimos enchants him to believe the group are his friends, and he tells them that he “serves at the pleasure of Vecna”. The zombies were digging to search for the key, otherwise known as “the Bone”, which fits “everything, and opens the whole of creation to The Ascended One”. He reveals the location of their current base. The team untie him, but as he is walking away, Kosta swings his warhammer (newly-acquired from the tabaxi himself) and hits the cat, Sumunar following up with a killing blow.

The Map

After studying the map the party retrieved, Nib translates the text on it. There are four circles surrounded by text in Celestial, with the smaller circles also written in Abyssal:

Kosta’s Freedom

Ashamed by his behaviour regarding the tabaxi, Kosta pleads with his patron Pelor to be free from his deal with The Celestial. He’s struck by a blinding white light, and wakes to find he feels free of the influence of the second god.

The Mine

Deimos is told by the old man from his dreams that going down the mine is going to be bad. “Take light. It’s less… underneath the world, more like between.”

The party take a long rest, but during the first watch Tylress feels the mine calling to him, and heads down into it, first on steps, then climbing down ropes. Deimos and Sumunar also feel the pull, but Nib and Elmer slap them out of it. They head down after Tylress, who has found his way to a rubble-filled tunnel, which he starts clearing. Deimos heads down after him, and they have an argument, but eventually come back out, and they all complete their rest.

All down in the mine the next day, crossing some black water, Tylress, Kosta, and Sumunar are dragged below into the inky blackness, and disappear. At this, the party is split.

Deimos, Elmer, Nib, and Jul

Continuing on, the party are attacked by a pair of basilisks, which turn Deimos and Nib to stone. After defeating them, Jul and Elmer explore the rest of the mine, finding more red crystals, which they destroy. At each impact, their reality shifts briefly into an alternative space, and then snaps back. At one point, Jul also vanishes and Elmer is alone. He eventually finds her and helps save her from a gelantinous cube.

Tylress, Kosta, and Sumunar

The three fall through into a small circular room, with a red crystal high above. Exploring through the door, the find other rooms, and some statues of a tiefling and an elf - Deimos and Nib, presumably. They also smash red crystals they find, after one of which Kosta disappears in a flash of bright light. Eventually, Sumunar opens a door and find themselves face to face with Elmer and Jul…

The Staff and Dawn Song

Suddenly, a bright blinding light fills the area and Deimos and Nib return from stone, but trapped in a cage. Sumunar is back with Elmer and Jul, Kosta and Tylress have disappeared…

They find their way to the room which Deimos and Nib are caged in, and fight a swirl of ink that’s protecting a staff in the middle. They escape with the staff, out the mine, only to be met by Dawn Song, who fights them for the staff. Deimos is enchanted, and attacks Nib, and the party give up the staff to save themselves. Dawn Song disappears into the jungle.

Fulgrin’s Lament

During a rest, Deimos discovers a couple of passages from the Fulgrin’s Lament book:

“…and the cowards bound him there, by staff and by blade, by hammer and by helm, rather than face their own failures…”

“…the ashes, cross-bound about his gate, the wood and glass and steel and gold, held tight by the elemental keys, his door bound, waiting to rise again…”

The Camp

They find Dawn Song’s camp - another open mine, mostly undead; giants, dogs, and dinos. Dawn Song has the staff and the helm. During the night, Elmer creeps over to watch and sees them sacrificing some figures in a number of fires, while the rest chant.

Meanwhile, Deimos dreams of the endless plain again, and is told:

These items are parts of the key that will unlock the door. Taking them from where they were held has started to unlock that door. If they get all three, and reach the door, they will open it fully. They will need collecting, and taking to that door to relock the door too. The Crystal Blade is not part of the key, it is part of the lock. As you might expect, it’s location is hidden from the likes of us. Last we heard it was in the mountains. The stolen dead will know, seek their camp, it has what you need.

The Mountains

In the mountains, the group find The Dark Sun, a glowing black orb of nothingness inside a volcano crater. While there, they are ambushed by Dawn Song and her crew of undead and skeletons.

In the fight, Deimos touches the black orb, and disappears. Elmer, Sumunar, and Nib die. Deimos is transported to an endless desert of sand, and finds man sitting beneath a tent at a table. They play a game of cards, Deimos is given a steel warhammer, and is returned to the volcano holding a card. Using the card, Deimos brings Sumunar, Elmer, and Nib back from death. They fight again, and Dawn Song leaves with the staff, promising to come back for the hammer.

To the Red Hammers

The party decide to go to the Red Hammer’s base at Fort Hope and ask for help. On the way, they pass the body of the Red Hammer that fled the tower earlier, killed by arrows, half his face dissolved. The ground gets swampy and they are set upon by a group of frog creatures. They fight them off, and later come across an enormous snail being ridden by mice. This is Meile, a travelling tradesperson. The group pay for passage on the snail, Bessy, to Fort Hope.

Qhell

At the fort, the group are given bed and board by Jadel, the leader, a friend of Sumunar’s. They also offer their help, and that of their clerics, in the fight against Dawn Song.

Also at the fort the group meet Qhell, an Aarakocra Druid. He is there as a guest of the clerics, and joins the party in their campaign.

Fighting Dawn Song. Again

Finally, Dawn Song attacks the fort to retrieve the hammer. She brings many zombies, including zombie aarakocra, velociraptors, giants, and an ankylosaurus. The fight is tough, and seemingly not going their way, when…

Kosta Returns!

A break in the clouds forms, and from it, you see a pillar of light streak down and strike the earth next to Jul. Shielding your eyes, you can see something zip down, crashing to the ground with a thunderous force. The light fades, and you see a dwarf adorned in shining armor. His hair is a brilliant white, and he’s missing an eye, but as he turns to you and smiles, you recognize him instantly.

Kosta returns from the heavens! His Turn Undead proves incredibly effective, and eventually the tide of the battle turns and the group defeat the zombie hordes.

The Necrodragon

Just as Dawn Song is killed:

As last of her life force drains away, you hear her whisper, “I am ascended....”

As her body slumps to the floor, the blood and viscera that lines it begins to slowly boil. You all see around you, all the bodies and blood, mortal and undead jerk and wither before you, their blood being drawn deeper in to her. And you see her body start to twist, and bulge, a pair of black skeletal wings burst from her back, her claws distend into long talons, with a sickening crack her back breaks and grows, and in her place stands a haggard and undead dragon, it’s skin white as her fur and her eyes a deep shimmering blue. She looks around at you all, and with a deep roar akin to the creaking of a crypt, she lifts into the air, and flies off, limping as she does.

In her wake, she leaves behind the helm, the staff, and various other belongings.


The Story Continues…

And that is where the group is currently up to! We have many questions to answer as we continue: is Dawn Song actually dead? Will the Necrodragon return? Where is the Crystal Blade and can the party find the door and lock it once more?

The story continues to play out in it’s typical PbP fashion, with ebbs and flows. Maybe in another year (or sooner!) I’ll write up another update here, but for now… you’ll just have to wait and see, won’t you?

I'm into pens

Pens & Ink

I’m into pens. There, I said it.

I’ve always had a vague, background interest in writing and writing implements. As a child, I had a couple of “teach yourself calligraphy” books, a few calligraphy pens, and I enjoyed finding excuses to use them: writing up the menu for tonight’s dinner, a sign for my bedroom door, etc. But then computers happened and I started writing by hand a lot less, and when I did, it was with whatever cheap biro happened to be close by.

Fast forward a few years (too many than I’d like to count), and in 2020 I joined the Relay FM members Discord. This was a place where people who enjoyed the variety of podcasts that Relay FM offered could socialise and discuss the topics that interested them; I joined mainly because they talked about it so much on Upgrade and Connected, and it seemed like a good place to find new people to chat with.

Turns out, I’ve made some of my best friends there. People I talk to every day, who know me better than I know some “in person” friends that I’ve known for years. But these friends have been a bad influence (on my wallet, at least).

There’s a channel in the Discord for each rough topic that the Relay FM shows cover. For example, there’s #tech, #apple, #creativity, #systems-and-themes etc. It makes sense, then, that there’s a #pens, given that there’s an entire show on Relay FM dedicated to them—The Pen Addict. I’ve listened to the show a couple of times, but it’s not one I’m subscribed to, and it never totally grabbed me. However, people sharing photos of their fountain pens, beautiful handwriting in stunning inks, letters they were writing and sending around the world—I wanted in.

I resisted for a while, I didn’t need another hobby to suck up what disposable income I had. So I started small—I asked for suggestions on a daily pen that could replace my cheap biros, nice to write with but nothing fancy. Madi suggested the Zebra Sarasa, and a three-pack dropped through the letterbox shortly afterwards. I was blown away! These were so much nicer to write with than what I’d been using before, and I was back to finding excuses to write by hand. I took to carrying one around in my pocket (a habit I’d never had before), along with a Field Notes pocket notebook that Madi had kindly sent across the ocean to me, in a package that also contained a whole host of different coloured refills. I was in.

One of the other members of the Discord, Ellen, had started up a small pen pals group, an offshoot from the official Relay Discord. I joined the group and wrote a couple of letters with the Sarasas, but really I wanted a fountain pen and everything that came with it - the little bottles of ink, the choice of nib size, the process of flushing, cleaning, and refilling, etc. So I asked for advice, took the plunge, and bought a TWSBI Eco Clear, and a bottle of TWSBI 1791 Orange ink. I mean, how else was I going to get to write in orange?!

It arrived (along with a packet of Love Hearts and the invoice held together with a little colourful smiley face paper clip, nice cute touches from Cult Pens) and I quickly filled it with the ink and started writing. Needless to say, I love it. I had chosen a medium nib, because I don’t like too skinny or too fat writing, and the only thing letting my letters down now is my handwriting!

Fast forward a few months, and I’m itching for a new colour. Don’t get me wrong, I love the orange, but I also fancied a nice vibrant pink. So what was I to do? How could I get the pink, without giving up having the orange easily to hand?

Clearly, the answer was to get a second fountain pen. (I see how this snowballs, now…) Ellen recommended another starter pen, the Lamy Safari, which I ordered in pink, with a broad nib this time. (It does need a converter to use bottled inks, as it usually takes a cartridge.) I also intended to add a bottle of Lamy Crystal ink in Rhodonite, which is a vibrant pink, but when the order arrived it turned out I’d actually somehow clicked on Agate, which is a rather flat grey. I was somewhat disappointed!

Contacting Cult Pens, they were happy for me to return the Agate and they would send out a replacement Rhodonite, which was very kind of them. I packaged the ink back up and took it off to the post office.

Ellen and another friend from the Discord, Alex, had been listening to me whine about not getting the pink, and a surprise package dropped through my letter box a day or so later. Opening it up, it contained a bottle of Robert Oster Signature ink in Hot Pink! Unbeknownst to me, they’d conspired to order me a replacement themselves, and chose Robert Oster in part because it’s good ink, but I suspect mostly because it’s Australian, and so is Ellen. They have a truly enormous range of colours, and the Hot Pink is fantastic. Exactly what I was after! It’s also in a 50ml bottle, which will last me literally forever.

TWSBI Eco with Robert Oster Signature Hot Pink ink

However, I was still going to be getting a replacement of the Lamy Crystal Rhodonite, so I contacted Cult Pens again and asked if they could instead send out a different colour (despite Ellen’s protestations that two pinks is not too many pinks, I want a nice range of colours before I start doubling up and comparing!)

They agreed, and so we come to today: I have a TWSBI Eco (M) inked in Robert Oster Signature Hot Pink, a Lamy Safari (B) with a tiny amount of Lamy Crystal Agate, and a bottle of Lamy Crystal Amazonite (a really lovely turquoise blue) on its way. I can’t wait to try that out.

The three inks and two pens described in this post

The only question is: now I have three ink bottles, and just the two pens. There’s only one solution, surely…


Python and the Magical OAuth Token

DevelopmentPython

A recent post by Colin demonstrated a utility class they’d written to handle fetching and refreshing OAuth bearer tokens for APIs and services that are secured that way, to stop you having to worry about it in the majority of your code. For years, I’ve used a similar class at work that I wrote to simplify this, and Colin’s post prompted me to share it here too.

The main difference from Colin’s solution is that the class I use is a subclass of the Requests Session object, rather than a stand-alone object. It handles not only fetching and refreshing the bearer token where necessary, but also inserting the correct headers to the requests you make. Here’s the entire class (and a couple of helper exception classes):

import requests
import datetime

class OAuthResponseError(Exception):
    pass

class OAuthInvalidGrant(OAuthResponseError):
    pass

class OAuthInvalidScope(OAuthResponseError):
    pass


class TokenRefreshSession(requests.Session):

    ERROR_MAPPING = {
        "invalid_grant": OAuthInvalidGrant,
        "invalid_scope": OAuthInvalidScope,
    }

    def __init__(self, client_id, client_secret, scope, token_url, *args, **kwargs):
        self.oauth_client_id = client_id
        self.oauth_client_secret = client_secret
        self.oauth_scope = scope
        self.oauth_token_url = token_url
        super().__init__(*args, **kwargs)

    def prepare_request(self, request):
        '''Add the access token to the headers'''
        self.headers["Authorization"] = f"Bearer {self.oauth_access_token}"
        return super().prepare_request(request)

    @property
    def oauth_access_token(self):
        '''Return a valid Access Token. Renews the token if it has expired'''
        if not self.oauth_access_token_valid():

            data = {
                "client_id": self.oauth_client_id,
                "client_secret": self.oauth_client_secret,
                "grant_type": "client_credentials",
                "scope": self.oauth_scope,
            }
            headers = {
                "Accept": "application/json",
                "Content-Type": "application/x-www-form-urlencoded",
            }

            response = requests.post(
                self.oauth_token_url,
                data = data,
                headers = headers,
            )

            response.raise_for_status()
            response_data = response.json()

            if "error" in response_data:
                raise self.ERROR_MAPPING.get(
                    response_data["error"],
                    OAuthResponseError)(response_data["error_description"]
                )

            self._oauth_access_token = response_data["access_token"]
            self._oauth_access_token_expiry = datetime.datetime.now() + datetime.timedelta(seconds=response_data["expires_in"])

        return self._oauth_access_token

    def oauth_access_token_valid(self):
        '''Check the validity of the current access token'''

        now = datetime.datetime.now()
        fudged_time = now + datetime.timedelta(minutes=5)

        access_token = getattr(self, "_oauth_access_token", None)
        access_token_expiry = getattr(self, "_oauth_access_token_expiry", now)

        if not access_token or access_token_expiry <= fudged_time:
            return False

        return True

The code is relatively simple. As a subclass of requests.Session, it accepts all the usual arguments, but also requires the OAuth client ID, client secret, scope, and token endpoint. This could be hardcoded, or a default provided in the class definition as we do internally, to avoid having to specify it each time.

The prepare_request method is part of the requests.Session mechanism for generating the HTTP call when the library is used. The overriden method in the TokenRefreshSession object inserts the correct Authorization: Bearer header and token into the request on behalf of the user.

As with Colin’s solution, the oauth_access_token property is computed when accessed, and performs the necessary update or refresh should the currently stored token have expired, by posting the client credentials data to the token endpoint provided.

Here’s an example of the class in action:

session = TokenRefreshSession(
    client_id="123456",
    client_secret="shh",
    scope=["read", "write", "etc"],
    token_url="https://api.my-great-app.com/v1.0/auth",
)

user = session.get("https://api.my-great-app.com/v1.0/users/23")

The first API call will fetch the token and store it, and subsequent calls (using the same session object) will either use the stored token or fetch a new one as necessary. You never need worry about including the Bearer header yourself. And, to quote Colin’s post:

Your token will magically manage itself, leaving you free to write good code against your favorite API.

LEGO 71374 Nintendo Entertainment System

LEGO

Beyond my budget (for now!), but the new NES LEGO set looks fantastic:

71374 Nintendo Entertainment System achieves extraordinary realism, potentially surpassing any previous LEGO model in that regard. The external detail is absolutely spectacular and the designers have made splendid use of existing elements to recreate distinctive shapes from the NES. I am particularly delighted with the controller and the casing around the connection ports on the console, both of which could be mistaken for their real equivalents.

Hopefully it will be available in the LEGO employee store when I visit Billund for the 2021 Inside Tour next year!

Junos commit history: who did what?

NetworkingJunos

Junos has always had one fantastic advantage over Cisco’s IOS: the ability to stage a pending commit, and see a history of changes, rather than each command immediately being executed on the running config a soon as you hit Enter.

The show | compare rollback X command has always been able to show you the difference between your current config and what the config was like X commits ago. However, I’ve always found it difficult to figure out what exactly was done in a single commit (or batch of commits) if they weren’t the most recent. That is, until I learnt of this command: show system rollback compare X Y! This lets you see the changes that were introduced just between the X and Y commits.

Here’s a real life example. Below is the commit history for a device at work. You can see there were a number of batches of commits going back in time where different individuals have done sole changes on the device:

ben@switch> show system commit
0   2019-11-13 15:06:51 GMT by katerina via cli
1   2019-11-13 14:49:19 GMT by katerina via cli
2   2019-11-13 14:43:01 GMT by katerina via cli
3   2019-11-13 14:42:11 GMT by katerina via cli
4   2019-11-13 14:41:01 GMT by katerina via cli
5   2019-11-13 14:03:53 GMT by katerina via cli
6   2019-11-13 12:28:33 GMT by katerina via cli
7   2019-11-13 12:25:44 GMT by katerina via cli
8   2019-11-13 12:24:53 GMT by katerina via cli
9   2019-11-13 12:23:28 GMT by katerina via cli
… output truncated  
20  2019-09-13 14:16:43 BST by ben via cli
21  2019-09-13 14:15:27 BST by ben via cli
22  2019-09-13 14:13:48 BST by ben via cli
23  2019-09-13 14:13:06 BST by ben via cli
24  2019-09-13 14:08:43 BST by ben via cli
25  2019-09-13 14:08:13 BST by ben via cli
26  2019-09-13 14:06:50 BST by ben via cli
27  2019-09-13 14:06:12 BST by ben via cli
28  2019-08-07 14:21:33 BST by stuart via cli
29  2019-08-07 14:21:08 BST by stuart via cli
30  2019-08-07 14:18:43 BST by stuart via cli
31  2019-08-05 09:06:28 BST by stuart via cli
32  2019-06-19 13:04:54 BST by lewis via cli
33  2019-06-19 13:02:34 BST by lewis via cli
34  2019-06-19 13:01:31 BST by lewis via cli commit confirmed, rollback in 3mins
35  2019-06-19 12:59:30 BST by lewis via cli
36  2019-06-19 12:58:39 BST by lewis via cli
37  2019-06-19 12:58:03 BST by lewis via cli commit confirmed, rollback in 3mins
38  2019-06-07 13:58:39 BST by bart via cli
39  2019-06-07 13:07:17 BST by bart via cli commit confirmed, rollback in 1mins
40  2019-06-06 12:27:51 BST by bart via cli
41  2019-05-15 13:07:07 BST by chris via cli
42  2019-05-09 14:14:43 BST by chris via cli

I needed to find what Stuart and Lewis had each introduced in their batches of changes back in August and June, respectively. This new command makes that trivially easy.

Stuart’s batches of changes were in commits 28–31. Running the following command, I was able to easily extract just what those commits introduced:

ben@switch> show system rollback compare 32 28
[edit interfaces interface-range ir_stp_edge]
     member ge-0/0/10 { ... }
+    member ge-0/0/6;
+    member ge-0/0/7;
+    member ge-0/0/8; more changes 

Note that you need to put the higher (older) commit number first, followed by the lower (more recent). Additionally, you need to increase the higher commits number by one, as you want to know what was introduced between those commits, including the oldest one.

Likewise, to see what Lewis introduced in his commits, we can run the following:

ben@switch> show system rollback compare 38 32 some changes ...

This will definitely save me some time in future!

Tiny Git Tip

DevelopmentTipGit

Brandur, on Twitter:

Git tip I wish I’d discovered ten years ago: if you git config --global diff.noprefix true it removes the silly a/ and b/ prefixes so that when you double-click select one to copy, you get a usable filename instead of a mangled path.

This is something I’ve always been annoyed by—I’m so glad there’s a fix!

NASA Administrator Remembers Mission Control Pioneer Chris Kraft

Space

Chris Kraft, NASA’s first Flight Director, died yesterday, two days after the 50th anniversary of the first man on the moon.

Once comparing his complex work as a flight director to a conductor’s, Kraft said, ‘The conductor can’t play all the instruments–he may not even be able to play any one of them. But, he knows when the first violin should be playing, and he knows when the trumpets should be loud or soft, and when the drummer should be drumming. He mixes all this up and out comes music. That’s what we do here.’

It’s almost as if he waited for the anniversary. The world remembers the names of the men who set foot on the moon, but Chris Kraft was another hugely important person managing the hundreds of highly skilled engineers and scientists without whom the moon landing would not have been possible.

We’re fast approaching a time where there will be nobody left who was involved in project Apollo. It will never be forgotten, but it will be sad to no longer be remembered first hand.