Go Deep with Claude โ€” or let MoFlo help it learn on its own

A look at three of MoFloโ€˜s โ€œthinkingโ€ skills โ€” /commune/meditate, and divine โ€” that bookend the actual coding: shape a fuzzy idea into a spec before you build, research the world when one search isnโ€™t enough during, and distill durable lessons after. Plus auto-meditate, the always-on version of that last step that captures what you learned without you asking.



Much of the work isnโ€™t writing code

Spend a day pairing with Claude on a real project and youโ€™ll notice the actual typing of code is the easy part. The hard parts sit on either side of it. 
Before the work: figuring out what you actually want, when โ€œI think I need an undo featureโ€ is really โ€œusers keep losing drafts.โ€ 
During the work: needing a fact the codebase canโ€™t tell you โ€” which library won, what the current best practice is, whether that API still works the way the old StackOverflow answer claims. 
After the work: noticing the lesson worth keeping, the gotcha that cost you two hours, the decision youโ€™ll regret re-litigating next month.

Claude is excellent at the middle. The edges are where sessions go sideways โ€” you build the wrong thing because the spec was fuzzy, you ship on a stale assumption because you didnโ€™t dig, or you solve the same problem for the third time because nobody wrote down the answer.

MoFlo now ships three skills aimed squarely at those edges. Theyโ€™re deliberately not code-writing skills. Each one produces thinking โ€” a spec, a cited answer, a durable lesson โ€” and then hands the result somewhere useful.


Three modes of thought

Introducing /commune/meditate and /divine. You commune with the project to surface what it already knows. You meditate on a finished session to keep what mattered. You divine an answer from the wider world.


/commune โ€” shape the idea before you build

Most โ€œbuild me Xโ€ sessions fail at the first sentence, because the first sentence is a solution dressed up as a requirement. /commune is the pre-execution skill: it converges a fuzzy โ€œIโ€™m not sure exactly what I want yetโ€ prompt into a concrete spec you can actually act on โ€” through a short Socratic dialogue, not a form.

/commune a way for users to recover work they lost

Hereโ€™s the flow:

  1. Memory first.ย Before anything, it searches yourย patternsย andย learningsย namespaces for the ideaโ€™s keywords. The worst outcome in a brainstorm is specifying something thatโ€™s already half-built โ€” so if prior art exists, it surfaces that immediately. This might be โ€œextend X,โ€ not โ€œbuild X from scratch.โ€
  2. Reframe as a problem.ย It restates your idea back as aย goal, not a solution: โ€œYou want users to recover a deleted draftโ€ โ€” not โ€œyou want an undo button.โ€ You confirm before any questions start.
  3. Elicit, Socratically.ย It runs a handful of targeted rounds โ€” problem and motivation, users and scenarios, scope and MVP, constraints, success criteria, risks โ€” using interactive multiple-choice questions where thereโ€™s a real branch, and open questions only where the answer is genuinely free-form. The rule it holds itself to:ย every question must change the spec.ย Three sharp questions beat ten that donโ€™t move anything.
  4. Synthesize.ย Out comes a single markdown spec โ€” problem, goal and non-goals, the chosen approach with rejected alternatives, scope, constraints, risks, and observable success criteria. You sign off (or do one round of edits) before it goes anywhere.
  5. Hand off.ย This is the part that matters.ย /communeย doesnโ€™t start a parallel track โ€” it feeds MoFloโ€™s existing surfaces. The specโ€™s success criteria become aย /floย ticketโ€™s acceptance criteria; an automatable pipeline becomes a spell; a decision-to-remember goes to memory; or you just keep the file.

Three speeds: -q for a small, well-bounded idea (1โ€“2 rounds), the default for most ideas (3โ€“5 rounds), and --deep for a risky architectural call, which adds a dedicated round comparing 2โ€“3 candidate approaches side by side.

The point is to stop the most expensive failure mode in AI-assisted development: building the wrong thing, fast, because the prompt was a guess. It is far cheaper to fix the spec than the PR it becomes.


/meditate โ€” keep what the session taught you

You just spent three hours discovering that a daemon port resolves differently in local-dev versus CI, and that the fix has to live in the launcher because a long-lived process clobbers any file-level repair. Thatโ€™s a real lesson. Next week, on a different task, it would save you the three hours again โ€” if itโ€™s written down somewhere Claude will find it. It usually isnโ€™t. The session ends, the insight evaporates, and you rediscover it the hard way.

/meditate is the deliberate retrospective that closes that loop. Run it at the end of a meaningful chunk of work:

/meditate the daemon port work

It reviews the session โ€” what was attempted, what worked, what surprised you, what was decided and why โ€” and then distills lessons against a single durability bar:

Would this help a future session working on a different task?

Keep (durable)Skip (not durable)
A reusable pattern: โ€œfor X, do Y because Zโ€โ€œFixed bug X in file Yโ€ โ€” thatโ€™s git history
A recurring gotcha: โ€œW silently fails when Vโ€โ€œAdded a test for Zโ€ โ€” the test records itself
A decision + the rationale future work must respectSession state (โ€œon branch โ€ฆ, 3 files dirtyโ€)
A cross-platform or blast-radius constraint discoveredRestating a rule thatโ€™s already written down

It aims for a handful of high-signal items, not an exhaustive log โ€” three lessons that change future behavior beat ten that restate the obvious. Crucially, it dedups before every write: it searches whatโ€™s already in your learnings namespace and updates a near-match rather than spawning a duplicate, because a split, contradictory memory is worse than no memory at all. Then it reports a compact ledger of what was stored, updated, or skipped. (--preview runs the whole retrospective but writes nothing, so you can review before committing to memory.)


โ€ฆor just let it learn on its own

Hereโ€™s the part that makes all of this actually stick: you donโ€™t have to remember to run /meditate.

Auto-meditate is the always-on counterpart, and it ships on by default. A hook watches the live session for the moment a durable lesson emerges โ€” a correction you made, an error followed by its fix, a decision with a rationale. It queues those, and at the next session start a brief background pass distills them into the same learnings namespace, applying the same durability bar and the same dedup-then-store discipline as the manual skill.

The two are complementary by design:

  • Auto-meditate is the safety net.ย It runs without being asked and catches the lessons youโ€™d otherwise forget to capture. No slash command, no prompting, no token cost in your active session.
  • /meditateย is the deliberate, curated pass.ย When youย knowย a session was significant and want to shape exactly whatโ€™s kept, you run it yourself.

Both dedup against existing learnings, so neither pollutes the other, and every lesson either of them stores gets embedded and surfaces in future memory searches. Thatโ€™s the whole bet: the project gets smarter about itself over time, whether or not you ever think about it.

Opt out with auto_meditate.enabled: false in moflo.yaml if youโ€™d rather keep capture fully manual. Most people leave it on.


/divine โ€” research the world when one search isnโ€™t enough

Some questions a single WebSearch settles. โ€œWhatโ€™s the latest stable Node version?โ€ โ€” done. But โ€œwhich Rust async runtime should I use in 2026, and why?โ€ isnโ€™t one lookup; itโ€™s a chain โ€” find the contenders, read each oneโ€™s tradeoffs, check whatโ€™s current, weigh the disagreements. Do that ad hoc and you get a confident answer built on the first three blue links. /divine is the disciplined version of that loop.

/divine which embedding model gives the best quality-to-size ratio for local RAG

What makes it different from โ€œjust search a few timesโ€ is a confidence gate. After each hop, the skill scores its own confidence from 0.0 to 1.0 โ€” honestly, weighing source quality, agreement across independent sources, and recency. Thin or single-source evidence scores low even when the snippet sounds definitive.

Confidence after a hopWhat it does
โ‰ฅ 0.8 (target)Stop โ€” the answer is well-supported. Synthesize.
0.6 โ€“ 0.8One more focused hop if budget remains; otherwise synthesize and flag the uncertainty.
< 0.6Keep going โ€” pick an expansion move and hop again.

When it continues, it doesnโ€™t just search the same thing harder โ€” it targets the weakest part of the current answer with a deliberate move: expand a named entity that needs its own lookup, deepen a claim from โ€œwhatโ€ to โ€œhow/why,โ€ check whether a time-sensitive answer has newer state, or follow a causal chain. It stops at the hop cap (default 5, override with --hops N) no matter what, and tells you the confidence it actually reached.

The output is a cited synthesis โ€” the direct answer first, every material claim tied to its source URL, disagreements surfaced rather than smoothed over, and a closing confidence line like Confidence 0.82 after 4 hops; weakest point: pricing may be stale (no 2026 source found). No factual claim without a source; the skillโ€™s own reasoning is marked as such.

And it learns. Every run stores a case to the research memory namespace โ€” which sub-questions and expansion moves paid off, the confidence reached, the best sources. The next time you research a similar question, /divine retrieves that case first and starts from a strategy that already worked instead of from scratch. (--offline skips the web entirely and answers from memory and prior cases, clearly flagged and capped at low confidence.)


How they fit together

These three arenโ€™t a random grab-bag. They map onto the lifecycle of a piece of work:

/commune โ†’ shape a fuzzy idea into a spec (before)
โ†“ hands off to /flo, a spell, or memory
[ build ] โ†’ the actual work
โ†“
/meditate โ†’ distill the durable lessons (after)
โ†‘ auto-meditate does this passively, always-on
/divine โ†’ research the wider world when you're stuck (any time)

/commune opens a unit of work; /meditate closes one. /divine sits orthogonal โ€” reach for it any time the answer lives out on the web rather than in your repo or your head. And the same MoFlo memory substrate underneath all of them โ€” node:sqlite plus HNSW vector search โ€” is what lets /divine reuse winning research strategies and lets both flavors of meditate accumulate lessons that actually compound.

Each one also knows its lane. None of them write code. /commune produces a spec and hands it to /flo or a spell to build/divine produces a cited answer, not an edit. /meditate writes memory, never source. That discipline is deliberate โ€” these are the thinking steps, and keeping them separate from execution is what makes their output trustworthy.


Why this matters

The pitch for MoFlo has always been that an AI assistant which remembers and learns beats one that starts cold every session. These three skills are that pitch applied to the human parts of the loop โ€” the deciding, the researching, the reflecting that normally happens in your head and then vanishes.

/commune makes sure you build the right thing. /divine makes sure you build it on facts, not the first plausible search result. /meditate makes sure you only learn each lesson once. And auto-meditate makes sure that last part happens even on the days youโ€™re moving too fast to stop and reflect.

Go deep when the work calls for it. Let it learn on its own the rest of the time.

Claude can slay dragons, but it needs the flo state

MoFlo โ€” Claude entering the flo state to battle dragons

An update on what’s new in MoFlo โ€” the open-source npm package that upgrades Claude Code with full project knowledge, persistent memory, sandboxed automation, and a swarm of agents that actually stay wired together.



Coding with Claude Code feels a lot like sending adventurers into a dungeon. On a good day, the dragon dies and you walk out with the loot. On a bad day, your heroes forget the map between rooms, pick fights they can’t win, and burn half their potions rediscovering a corridor they cleared yesterday.

MoFlo is the buff stack. It gives Claude:

  • Access to all of your project guidance, patterns, code, and intel, quickly
  • Memory that survives sessions
  • A party of specialist agents that coordinate instead of battling each other
  • Sandboxing so the dragon can’t escape into your filesystem

The last few releases have been a heavy refactor of the engine room. There was significant rework and, yes, some associated churn. Now we’re stronger and lighter than ever, and it’s worth showing what changed.

What you actually do

npm install --save-dev moflo
flo init

Inside a Claude session you’ll mostly notice 3 things: it stops re-exploring files it already knows, it routes work to the right specialist, and when you say /flo 42 it picks up GitHub issue 42 and drives it to a PR โ€” sandboxed, tested, simplified, and tracked in the task list.

One database to rule them all: MoFlo DB

The biggest invisible upgrade is the storage layer. Memory, swarm coordination, hive-mind state, AIDefence threat patterns, learned routing outcomes โ€” all of it now lives in a single sql.js + HNSW store at .moflo/moflo.db.

  • It consumes everything that matters โ€” project guidance, reusable patterns, code maps (exports, classes, functions, types), test-to-source mappings, and a continuously updated stream of learnings from both Claude and the humans on the team. A correction you make once, a convention you write down, a routing decision that worked: all of it lands in the same index and shows up the next time it’s relevant.
  • HNSW vector indexing โ€” semantic search runs 150x to 12,500x faster than brute-force cosine, depending on project size.
  • Neural embeddings by default โ€” fastembed with the all-MiniLM-L6-v2 384-dim model, ONNX runtime, native Rust tokenizer. Hash-based “fake” embeddings are gone. If a search returns a result, it’s because the meaning matched, not because the bytes did.
  • One store, many consumers โ€” the embeddings cache, knowledge graph, RVF/SONA learning store, and the auto-memory bridge all share the same database file. Backups are a single cp.

This matters because the entire MoFlo experience โ€” the memory-first gates, the learned task routing, the “/flo” issue-to-PR workflow โ€” sits on top of this index. Making it fast and unified is what lets the upper layers stop being clever and just be useful.

Swarm and hive-mind, rewritten from the ground up

The multi-agent layer has been rebuilt around a single canonical engine: UnifiedSwarmCoordinator. Every coordination MCP tool now routes through it, with a write-through persistence adapter so state survives crashes, restarts, and process boundaries.

  • agent_spawnagent_listagent_statusagent_terminateagent_poolagent_health โ€” all driven by the coordinator.
  • swarm_initswarm_statusswarm_healthswarm_scale โ€” same engine, same source of truth.
  • A new task_* family (7 MCP tools) handles distribution, execution, cancellation, and status through the coordinator’s task orchestrator.
  • Hive-mind sits on top: hive-mind_* routes through a MessageBus with a write-through adapter, and workers register against the shared coordinator. The Queen delegates, the workers swarm, every message is durable.
  • End-to-end system tests exercise the whole wired path, and flo doctor ships functional tripwires that fail loudly if any of it ever drifts.

For a developer, the headline is simple: when you ask Claude to send a 5-agent party at a feature, the party shows up, divides the work, and reports back through a single coherent state machine โ€” no parallel work stomping on shared files, no agents that quietly forgot what they were doing.

Spells: scripted automation with real sandboxing

MoFlo’s automation primitive is a spell โ€” a YAML or JSON file that composes step commands (bashgithubbrowsermemoryagentconditionloopparallel, IMAP, Slack, MCP) into a runnable workflow. Spells back the /flo issue runner, the epic orchestrator, scheduled jobs, and any custom automation you write.

Because spells can run unattended, they ship with 3 independent layers of defense:

  1. Command denylist โ€” catastrophic patterns (rm -rf /git push --force mainchmod -R 777, fork bombs, curl | sh) are blocked before any step executes, on every platform.
  2. Capability gateway โ€” every step declares the capabilities it needs (fs:readfs:writenetshellmemorycredentialsbrowseragent). The runner blocks any I/O the step didn’t declare. Spell authors can narrow a default but never expand it.
  3. OS-level process isolation โ€” bash steps are wrapped automatically: bwrap on Linux, sandbox-exec on macOS, Docker on Windows. Read-only root, network blocked unless declared, PID isolation, writable mounts only on paths the step asked for.

The contract for an agent running inside a spell is brutally simple: do what the step says, nothing more. A CAPABILITY_DENIED error is intentional, not a problem to engineer around. That’s what makes a scheduled spell safe to deploy at 3 a.m.

AIDefence: a guard at the prompt boundary

AIDefence is now bundled in the same tarball, sharing the same MoFlo DB. It exposes 6 MCP tools (aidefence_scanaidefence_analyzeaidefence_statsaidefence_learnaidefence_is_safeaidefence_has_pii) with sub-millisecond detection on 50+ built-in patterns: prompt injection, jailbreak attempts, role-switching, encoding tricks, PII leaks (emails, SSNs, API keys, credit cards).

It’s self-learning. When you mark a detection accurate or note which mitigation worked, that feedback lands in the same HNSW store and tunes future detections. New threats become old patterns over time.

Lighter, leaner, fewer moving parts

Earlier MoFlo (and its upstream cousins) carried a workspace of @moflo/* packages and optional native dependencies โ€” agentdb, agentic-flow, ruvector, ONNX, sharp โ€” that quietly pulled in roughly a gigabyte of runtime when fully installed. That made sense once. It doesn’t anymore.

  • Workspace collapse foldedย all related packagesย into a singleย mofloย tarball. One package, one version, one install.
  • AgentDB and agentic-flow are out (shipped in 4.8.80, smoke-harness-enforced in CI). Their roles were absorbed by MoFlo DB.
  • Install size dropped from ~1 GB of fully-functional runtime to ~82 MB, with no functional regression. Embeddings are mandatory and always work; there is no peer-optional dance.

The whole stack runs locally โ€” Node.js 22+, WASM and Rust/NAPI bindings, no GPU required, no cloud calls except to Claude itself. It’s a devDependency; you never install it globally.

Why this update matters

The dragon metaphor isn’t just whimsy. The biggest failures of agentic coding aren’t the model being wrong โ€” they’re orchestration failures: forgotten context, parallel agents stepping on each other, automation that escapes its lane, dependencies that bloat the install until nobody trusts the upgrade button. Every change above is aimed at one of those.

MoFlo is, and will stay, free and open source. If your Claude Code sessions feel like they’re starting cold every morning, or your automated workflows feel one bad command away from disaster, this is the buff stack worth trying.

Ready to level up Claude? It’s as simple as installing MoFlo.

Bootsy FTW

Bootsy Collins

Just published a new Typescript functional-light library on NPM. Bootsy has a lot of great ease-of-use features for functional-light JavaScript.

The intended target is node, but it’s small and there are no dependencies, so there’s no reason you couldn’t also use it for browser-based applications.

Bootsy should play just fine with other functional libraries like Lodash or Ramda

Check us out on NPM or GitHub for more information!

Why name it Bootsy?

Bootsy Collins is one of the most successful funk (func?) musicians to grace us with his music. From laying down some of the most classic lines for James Brown to Parliament/Funkadelic and his own solo efforts, Bootsy is known not for extreme complexity, but for slick, fun grooves. We’re drawing inspiration from his music for our functional library.

Introducing Serverless-Multi-Region-Plugin

We use the serverless framework on one of my projects at work. Now, deploying serverless is a piece of cake, but we needed to deploy serverless in an active-active multi-region failover setup to meet the needs of our disaster recovery.ย  The setup we want kinda looks like this:

So as usual, I started by trying to use something that was out there.ย  And I found “Serverless-Multi-Regional-Plugin“.ย  Just one problem…it didn’t really work.

It was a good start, but it left a number of things out. It didn’t set up the API Gateway base path properly, it wasn’t set up to await the outcome of the API being deployed, and it also didn’t set up health checks to allow the fail-over to occur out of the box.ย  The setup also required a lot of properties to be explicitly set that I just wanted to be derived from the host name. In addition, there were no unit tests in the project.ย  That being said, it was a great start to build from.

So I basically added all the things I mentioned.ย  I allow almost everything to be derived from just a couple of settings if you have a domain name set up and a certificate registered. But…I also allow all of the original settings to explicitly override the settings derived by convention.ย  I also automatically added in some default health checks and set up the base path of the CloudFront so that everything just works. And finally, I added a bunch of unit tests to make sure all of the core configuration settings actually did was I expected them to do.

After completing all of this work, of course I put in a PR….but no response. I guess the maintainer of the project has let it go. So I tweaked the name a bit, and now we have:

Serverless-Multi-Region-Plugin!! I know, very original.

Check it out, use it, improve it!

NPM: https://www.npmjs.com/package/serverless-multi-region-plugin

GitHub: https://github.com/unbill/serverless-multi-region-plugin

Introducing Mongo-Up

A month or so ago, I was in need of a Node-based utility to deploy changes or data migrations of my Mongo DB database.ย  I work in an industry that has high compliance needs, so it wasn’t appropriate for us to do manual scripting of changes.ย  And logging onto our production Mongo was out of the question.

I started by using a project called Migrate-Mongo, but because we use AWS and I needed to retrieve configuration dynamically, I had to add some async configuration functionality and a pull request was accepted.

Next, I needed a way to run scripts that I wanted to happen idempotently with every release.ย  I added support for scripts that were run before and after every release (like ensuring indexes).ย  This PR added some significant complexity and was also a significant rewrite, but I added a ton of tests and made sure that coverage was at 100%.

Understandably, the Migrate-Mongo owner didn’t want to incorporate such a large change to the original project. But that’s the great thing about OSS, I just created Mongo-Up to make this great functionality to all of you great peeps!

Check it out, use it, improve it!

NPM: https://www.npmjs.com/package/mongo-up

GitHub: https://github.com/unbill/mongo-up

 

GeoGems Part 1: Getting Set Up

LonghornDam

In this installment, we’ll be getting our Ubuntu environment set up for ASP.Net Core development. I’m using an Ubuntu 14.04 VM on my Mac. If you choose a different Linux installation, hopefully this will help, but mileage may vary.

Ubuntu

This will just be a short article on getting your development environment set up. ย For this article, I’m using Ubuntu 14.04ย desktop edition. ย I’m working on a Mac and running Ubuntu as a VM via VMWare fusion. Here’s some good instructions on getting this set up…not much to it really although their Ubuntu download link has gone bad.

Open a command line and make sure out package manager is up to date:

apt-get update

or if needed, prefix with sudo as shown below.

sudoย apt-get update

In the commands that follow, I’m assuming that if you run into security issues you will use sudo to prefix installation steps. ย The exception is when installing Node/NPM. ย Here we want to avoid using sudo which is why I use the specific technique

Nowย make sure that Unzip andย cURLย are installed (again prefix with sudo if required).

apt-get install unzip curl

Visual Studio Code

Visual Studio Code is just a downloadย and install. ย Just make sure you download the .deb installer if you are using Ubuntu. This will take you to the Ubuntu Software Center and prompt you to install Visual Studio Code. In my case, the installer warned my that it didn’t like the package for Code…I proceeded anyway and it installed just fine. ย Not sure what the deal is there…this is the just-release 1.0 drop of Visual Studio Code…so maybe just a glitch in the installer…but again it appeared to install just fine without a hitch.ย In addition, the installer should also set up launching from the command line via typing “code {your directory here}” to launch in the specifiedย directory.

After starting up Visual Studio Code, assuming you are using the Unity GUI, you will see the Visual Studio Code icon in the Launcher Bar on the left side. ย You may want to right click the Code icon and select Lock to Launcher so the icon stays put when Code is closed.

LockToLauncher

In order to support C# development, we’ll be adding the OmniSharp plugin. Basically you can hit F1ย to bring up the Command Palette and then type/select Install Extension from the command selections. ย Then choose/type C# from the available installations. In a few seconds, OmniSharp will be installed but a restart of Code will be necessary to complete installation. ย Here are the official instructions.

Node

Of course Node is required for build and scaffolding tools etc… ย For this demo I’ve installed Node 4.4.3 but don’t believe there should be any issues using 5.x. ย I used the NVM methodย to install Node. ย This method is being used because it allows node packages to be installed globally without requiring sudo permissions.

The full instructions are here.

The Basics

Open a command line and run the following.

curl https://raw.githubusercontent.com/creationix/nvm/v0.30.2/install.sh | bash

Restart your command line.

To install Node/NPM with NVM:

nvm install 4.4.3


And now we need to install some build tools to help us out via NPM. The executionย of these commands should not require sudo to install if the NVM method worked properly.

npm install -g yo bower grunt-cli gulp

Installing .Net

Now we’re going to install .Net tooling to allow us to control the various .Net runtime versions on our system. ย Note that this tooling will change when RC2 comes out. ย I’ll update this post when V2 officially drops. The information below was taken mostlyย fromย this source.

To install the .Net Version Manager (DNVM):

curl -sSL https://raw.githubusercontent.com/aspnet/Home/dev/dnvminstall.sh | DNX_BRANCH=dev sh && source ~/.dnx/dnvm/dnvm.sh

To install other .Net prerequisites via the Ubuntu package manager:

sudo apt-get install libunwind8 gettext libssl-dev libcurl4-openssl-dev zlib1g libicu-dev uuid-dev

Now we’re going to use the DNVM to install the latest DNX available for the Core CLR. This is a compact, performant, and cross-platform version of the common language runtime (clr) that we will be using.

dnvm upgrade -r coreclr

ASP.Net core applications run on a lightweight and performant web server called Kestrelย thatย uses a cross-platform I/O library called Libuv. ย The following builds and installs Libuv:

sudo apt-get install make automake libtool curl
curl -sSL https://github.com/libuv/libuv/archive/v1.8.0.tar.gz | sudo tar zxfv - -C /usr/local/src
cd /usr/local/src/libuv-1.8.0
sudo sh autogen.sh
sudo ./configure
sudo make
sudo make install
sudo rm -rf /usr/local/src/libuv-1.8.0 && cd ~/
sudo ldconfig

Docker

We’ll be using docker as our method of packaging and deploying our ASP.Net applications. The instructions to install Docker on Ubuntuย can be found here. Make sure you follow all instructionsย for Ubuntu 14.04 (or whichever version you are using).

Yeoman Templates

If you followed the Node instructions above, you’ve already installed Yeoman from NPM. ย All we need to do now is install our ASP.Net yeoman templates to get us started:

npm install -g generator-aspnet

The GitHub repositoryย has muchย more thorough information.

That’s it as far as getting set up for now. ย In our next installment, we’ll build our first ASP.Net Web API on Linux that targets the core framework.

GeoGems: An ASP.Net Core App on Linux

GeoGems

Articles in This Series

GeoGems Part 1: Getting Set Up

The Idea

I’m starting a new “practice project” to suss out creating a complex .Net application on Linux. The application is going to be called GeoGems.io. I’m a runner, but now that I’m older and have a child and slower etc…I don’t really run to be fast anymore. ย I just run because I love to run. But I love to trail run and run on routes I’ve never encountered before. A lot of times I’ll come across awesome urban art or other really cool landmarks. ย I was thinking how cool it would be to have a running app that showed you cool things that others had tagged that you were near…so you could change course and go check things out as you run. And also allow you as a runner to share things you’ve found. So the basic idea is that you can use an app to stop and take pictures and “GeoCode” and tag sites as you run or walk. I’d also like to track some basic running stats like distance and pace. Kind of a combination of a running app and a geocaching app, but based on landmarks as the things to find.

My Technology Stack

The goal is to use free/opensource in all possible cases to keep this zero cost. I want to create this app with C# running ASP.Net Core on Linux. I want to use Docker as my deployment approach. ย I’m thinking about Aurelia for the admin front end and Ionic 2 for the mobile web/web app. If Aurelia releases their Aurelia Interface product any time soon I might swap that for Ionic. I’ll probablyย wantย some kind of event sourcing backend for recording route events so I’m probably going to try Marten, since that sits on Postgres and also has a document store capabilityย (and then the underlying relational DB if I need it).ย Hopefully they release the event storeย support soon.

Code Tools

I’m planning on coding this on Linux (Ubuntu) as well, so I’ll be using Visual Studio Code with OmniSharp. I’ll also use Code as my JavaScript editor for client apps.

Deployment

I want to deploy to the cloud, so I’m thinking DigitalOcean (although AWS does have their free plan). You just get so much bang for your buck with DigitalOcean.

Why This Approach

There are several. ย The first is that I’ve been a Windows/Mac user my entire life, including my entire coding life and always wanted to explore Linux in more detail. And the move for .Net Core to support Linux is compelling for several reasons:

  • Because it’s there…why notย give it a shot?
  • Cost – Price out some Windows VM’s on Azure vs Linux VMs. ย The Linux VMs come in at 60% of the cost. ย That’s huge for a startup or even ambitious hobby projects.
  • Platforms/Hosts – Using Linux opens up several awesome and inexpensive hosts like Digital Ocean. ย It also opens up some great DevOps/Depoyment tools like Docker. I know Windows containers are on the horizon…but they are definitely behind the curve and they still need to run on Windows (see Cost).
  • .Net and C# are awesome – I’ve usedย various Node frameworks and they are impressive…but there’s something to be said about the sweet spot that C# hits as a strongly typed language with so many features, like:
    • Lambdas/LINQ/Delegates
    • Async/Await
    • Great Generics support
    • Dynamics
    • Immutables
    • I could go on for a while here…it’s a fantastic language…and C# 7 looks to be even better.
  • .Net has a solid and growing open source community…and now even .Net Core is open source…so long Ballmer…
  • .Net Core Performance on Kestrel is impressive…even for the RC, so you’re getting a lot of bang for your compute buck. ย This great performance means smaller/fewerย servers in your farm.

So why not just develop on Windows using Visual Studio 2015 (which I have available) and target the core framework so that you can run on Linux? ย There are a couple of reasons. ย The first is that, as mentioned above, I want to improve my comfort with Linux as well as get a better grasp of using a “minimal toolset” approach. ย The other is ease of integration with Docker. Docker can be set up on Windows (currently by using VirtualBox)…but it’s really just a mini Linux VM…why not just get on Linux instead where it’s a first class citizen?

That’s all for now…I’ll use this as a landing page for upcoming posts.

Mapster 2.0 Released!

We’ve released Mapster 2.0, and it’s looking really good! ย This definitely puts Mapster back on top as the best, most complete, fast .Net mapper out there. ย If you need something that won’t get bogged down under heavy load but has very rich features, this one’s for you.

Huge shoutย to Chaowlert, whoย took this project and ran with it, adding all of the new updates and optimizations.

Downloads

GitHub

Nugetย (PM> Install-Package Mapster)

New Features

  • Big speed improvements.
  • Projection is improved to generate nicer sql queries
  • Mapster is now able to map struct
  • Flagged enum is supported
  • Settings are now much more flexible
    • You can now both opt-in and opt-out setting
    • Setting inheritance is able to inherit from interface
    • Setting inheritance is now combined (it does not only pick from the closest parent)
    • New rule based setting, you can defined your setting more granular level
    • Setting is no more static, you can overload your setting to use different setting for your mapping
  • You can ignore properties using attributes
  • Now you can setup your map from different type ie config.Map(dest => dest.AgeString, src => src.AgeInt)
  • Mapster now supportsย circular reference mapping!
  • Supports more frameworks (.NET 4.0, 4.5, .NET Core RC 5.4)

Benchmarks

Engine Structs Simple objects Parent-Child Parent-Children Complex objects Advance mapping
AutoMapper 10871 27075 20895 19199 19333 21496
ExpressMapper 690 1350 1195 1678 3130 3920
OoMapper 2043 1277 1416 2777
ValueInjector 8534 21089 17008 12355 16876 19970
TinyMapper 1282
Mapster 2382 1892 1626 4287 6756
Mapster 2.0 515 1251 950 1037 2455 2342
Native 458 790 870 1253 3037 2754

(NOTE: Benchmark runner is from ExpressMapper. Benchmark was run against largest set of data, times are in milliseconds, lower is better. Blank values mean the library did not supported.)

Give it a shot!

 

Nozus JS 1: Intro to Sails with Passport and JWT (JSON Web Token) Auth

This project extends from some previous posts on creating a SPA style application with Node (Sails) and Aurelia. I’m not going to go into detail about installingย Node, NPM, orย Sails, other than when it’s germane to the subject. ย I’m assuming you are already set up with all of the needed basics for Node/Sails development. If not, visit the Sails site to get started.

We’ll start by creating a new directory in which to create our test projects and bringing up a command line. What we’re creating here is an API; so no need for any front end mixed into the Sails application. ย We can create that later if we like. So the command to create a new app without a front end is:

sails new myApiย --no-frontend

The basics of the app should now be present in the “myApi” directory (or whatever you called it). If you open up the project in your favorite editor, you will see the following structure:

structure

Most of the action will happen in the api folder but we’ll also need to set up some config settings. But first let’s go ahead and install some dependencies. Make sure you are in the root folder of your project and install the following from the command line:

npm install jsonwebtoken --save
npm install bcrypt-nodejs --save
npm install passport --save
npm install passport-jwt --save
npm install passport-local --save

Alternately, you could just add these to the package.json file and run npm install.

Config

Now we’ll add our passport configuration. ย Another nice thing about Sails is that putting a jsย file in the /config folder means that it will be run when you “lift” or start the app. In the config folder, createย passport.js and add the following code:


/**
 * Passport configuration file where you should configure strategies
 */
var passport = require('passport');
var LocalStrategy = require('passport-local').Strategy;
var JwtStrategy = require('passport-jwt').Strategy;

var EXPIRES_IN_MINUTES = 60 * 24;
var SECRET = process.env.tokenSecret || "4ukI0uIVnB3iI1yxj646fVXSE3ZVk4doZgz6fTbNg7jO41EAtl20J5F7Trtwe7OM";
var ALGORITHM = "HS256";
var ISSUER = "nozus.com";
var AUDIENCE = "nozus.com";

/**
 * Configuration object for local strategy
 */
var LOCAL_STRATEGY_CONFIG = {
  usernameField: 'email',
  passwordField: 'password',
  passReqToCallback: false
};

/**
 * Configuration object for JWT strategy
 */
var JWT_STRATEGY_CONFIG = {
  secretOrKey: SECRET,
  issuer : ISSUER,
  audience: AUDIENCE,
  passReqToCallback: false
};

/**
 * Triggers when user authenticates via local strategy
 */
function _onLocalStrategyAuth(email, password, next) {
  User.findOne({email: email})
    .exec(function (error, user) {
      if (error) return next(error, false, {});

      if (!user) return next(null, false, {
        code: 'E_USER_NOT_FOUND',
        message: email + ' is not found'
      });

      // TODO: replace with new cipher service type
      if (!CipherService.comparePassword(password, user))
        return next(null, false, {
          code: 'E_WRONG_PASSWORD',
          message: 'Password is wrong'
        });

      return next(null, user, {});
    });
}

/**
 * Triggers when user authenticates via JWT strategy
 */
function _onJwtStrategyAuth(payload, next) {
  var user = payload.user;
  return next(null, user, {});
}

passport.use(
  new LocalStrategy(LOCAL_STRATEGY_CONFIG, _onLocalStrategyAuth));
passport.use(
  new JwtStrategy(JWT_STRATEGY_CONFIG, _onJwtStrategyAuth));

module.exports.jwtSettings = {
  expiresInMinutes: EXPIRES_IN_MINUTES,
  secret: SECRET,
  algorithm : ALGORITHM,
  issuer : ISSUER,
  audience : AUDIENCE
};

So there’s a few things going on here. ย First, we’re importing both passport and the two strategies we’re going to set up for now (local and JWT). We’ll add social login in the next post. Next, we’re going to add configuration for our two auth strategies. For the local strategy we’ll use email and password in to login. For JWT, we’ll set up the parameters needed to verify the token: secret, issuer and audience. ย It should be noted again that the issuer and audience are optional but provide some additional verification.

Next we’ll add some functions to react to the auth request for each strategy. For local auth, this means making sure that the user matches the user in our database and returning a false response with a message on failure or the user if it succeeded. ย For JWT auth, the strategy is already validating the token internally. If you want additional validation here you can check the user ID against the database to verify that this user actually exists and is active. ย For the sake of simplicity, I’m going to trust the token and retrieve the user information from the token payload and just return it.

Last, we are exporting some settings to be used elsewhere in the API. This is another nice feature of Sails: The ability to export settings in a config file to make them available globally. So in our case, we are exporting jwtSettings from our config. To access these settings from anywhere, we can use the global accessor: ย sails.config.jwtSettings.

Services

Next add a new file under api/services called CipherService. In sails, services and models are named using PascalCase by convention. I need a better name for this one but it will do for the time being. This is where we’ll put all of our code that does hashing and/or token stuff.

var bcrypt = require('bcrypt-nodejs');
var jwt = require('jsonwebtoken');

module.exports = {
  secret: sails.config.jwtSettings.secret,
  issuer: sails.config.jwtSettings.issuer,
  audience: sails.config.jwtSettings.audience,

  /**
   * Hash the password field of the passed user.
   */
  hashPassword: function (user) {
    if (user.password) {
      user.password = bcrypt.hashSync(user.password);
    }
  },

  /**
   * Compare user password hash with unhashed password
   * @returns boolean indicating a match
   */
  comparePassword: function(password, user){
    return bcrypt.compareSync(password, user.password);
  },

  /**
   * Create a token based on the passed user
   * @param user
   */
  createToken: function(user)
  {
    return jwt.sign({
        user: user.toJSON()
      },
      sails.config.jwtSettings.secret,
      {
        algorithm: sails.config.jwtSettings.algorithm,
        expiresInMinutes: sails.config.jwtSettings.expiresInMinutes,
        issuer: sails.config.jwtSettings.issuer,
        audience: sails.config.jwtSettings.audience
      }
    );
  }
};

We’ll use these methods elsewhere. The hash/comparePassword methods are pretty self-explanatory. Bcrypt-nodejs even adds in a salt to the hashed password by default. Pretty nice. The createToken method uses JsonWebToken to create a new token using the sign method. This method accepts a payload (the user in our case), a secret to use for the self-contained JWT hash, and some metadata including the algorithm, expiration, issuer and audience. Issuer and Audience will also be validated when the token is verified coming back in, so it gives a little extra protection. The inclusion of the user in the payload is really just for example. In the real world, you might include the user id and claims etc…

API Generation

Next we’ll create our User API. Return to the root command line of the application and run the following command:

sails generate api User

If you now observe your controllers and models directories, you’ll see the generated controller and model for User. ย The controller can be left as-is for now. Although it looks empty, it’s functional! ย It will use default blueprints included with Sails to provide functionality.ย Now we’ll want to enhance the model. ย Open the api/models/User file and add the following code:

/**
 * User
 * @description :: Model for storing users
 */
module.exports = {
    schema: true,
    attributes: {
        username: {
            type: 'string',
            required: true,
            unique: true,
            alphanumericdashed: true
        },
        password: {
            type: 'string'
        },
        email: {
            type: 'string',
            email: true,
            required: true,
            unique: true
        },
        firstName: {
            type: 'string',
            defaultsTo: ''
        },
        lastName: {
            type: 'string',
            defaultsTo: ''
        },
        photo: {
            type: 'string',
            defaultsTo: '',
            url: true
        },
        socialProfiles: {
            type: 'object',
            defaultsTo: {}
        },

        toJSON: function () {
            var obj = this.toObject();
            delete obj.password;
            delete obj.socialProfiles;
            return obj;
        }
    },
    beforeUpdate: function (values, next) {
        CipherService.hashPassword(values);
        next();
    },
    beforeCreate: function (values, next) {
        CipherService.hashPassword(values);
        next();
    }
};

Here we’re adding a bunch of properties to our User schema, but also a method to remove sensitive data before converting our object to JSON. ย We also have beforeUpdate and beforeCreate delegates defined to hash our password before saving to the data store. ย You can see that the hash will use our CipherService, and one of the nice things about sails is that it makes our services available globally. ย So here we can just use CipherService instead of needing aย require.ย In Sails, you also have models available globally using their name. This is a huge advantage of Sails.

Auth Controller

Now weย have to add an Auth endpoint to perform signup/signin etc… ย We’re only generating a controller here, so you can just add the js file or do it the Sails way:

sails generate controller Auth

Now that we have our AuthController, let’s add the following actions:

/**
 * AuthController
 * @description :: Server-side logic for manage user's authorization
 */
var passport = require('passport');
/**
 * Triggers when user authenticates via passport
 * @param {Object} req Request object
 * @param {Object} res Response object
 * @param {Object} error Error object
 * @param {Object} user User profile
 * @param {Object} info Info if some error occurs
 * @private
 */
function _onPassportAuth(req, res, error, user, info) {
  if (error) return res.serverError(error);
  if (!user) return res.unauthorized(null, info && info.code, info && info.message);

  return res.ok({
    // TODO: replace with new type of cipher service
    token: CipherService.createToken(user),
    user: user
  });
}

module.exports = {
  /**
   * Sign up in system
   * @param {Object} req Request object
   * @param {Object} res Response object
   */
  signup: function (req, res) {
    User
      .create(_.omit(req.allParams(), 'id'))
      .then(function (user) {
        return {
          // TODO: replace with new type of cipher service
          token: CipherService.createToken(user),
          user: user
        };
      })
      .then(res.created)
      .catch(res.serverError);
  },

  /**
   * Sign in by local strategy in passport
   * @param {Object} req Request object
   * @param {Object} res Response object
   */
  signin: function (req, res) {
    passport.authenticate('local', 
      _onPassportAuth.bind(this, req, res))(req, res);
  },
};

So here we’re doing two basic things: Sign up and Sign in. The signup method uses the built-in Create method provided by the user model to create a new user. The signin used the Passport’s local authorization strategy to log in. In both cases, if it succeeds, we’ll generate a token so that they are signed in automatically. All requests that follow should now include the returned token in the header. We’ll demo this shortly, but have one final item to add first: ย The login policy.

Policies

In Sails, the way to add Express middleware is via policies. If you examine the policy structure, that’s really exactly what it is. In our case, we need a policy that will protect our non-authย controllersย from requests that don’t have a token. Look at the api/policies folder.ย There may be a sessionAuth.js file already present. You can delete this file as we won’t use it. Now add a new file to api/policies called isAuthenticated.js with the following contents:

/**
 * isAuthenticated
 * @description :: Policy to inject user in req via JSON Web Token
 */
var passport = require('passport');

module.exports = function (req, res, next) {
    passport.authenticate('jwt', function (error, user, info) {
      if (error) return res.serverError(error);
      if (!user) 
       return res.unauthorized(null, info && info.code, info && info.message);
     req.user = user;

     next();
    })(req, res);
};

The policy is pretty simple: It authenticates the user using the ‘jwt’ strategy that we earlier implemented in the passport configuration. If no token is present, this will return an unauthorized response. Otherwise it will add the user object from the token to the request.

Responses

Let’s briefly discuss Sails responses, another cool Sails convention. If you take a look at api/responses, you’ll see a bunch of responses that have been created for you already. These make it easy to define and reuse typical responses. So to return an OK response, instead of defining our response over and over, we can just do: return res.ok(data). In our case, we need to add two new custom responses: created and unauthorized. ย In the responses folder add created.js with the following content:

/**
 * 201 (Created) Response
 * Successful creation occurred (via either POST or PUT).
 * Set the Location header to contain a link 
 * to the newly-created resource (on POST).
 * Response body content may or may not be present.
 */
module.exports = function (data, code, message, root) {
  var response = _.assign({
    code: code || 'CREATED',
    message: message 
       || 'The request has resulted in a new resource being created',
    data: data || {}
  }, root);

  this.req._sails.log.silly('Sent (201 CREATED)\n', response);

  this.res.status(201);
  this.res.jsonx(response);
};

Now create an unauthorized.js file in the same folder:

/**
 * 401 (Unauthorized) Response
 * Similar to 403 Forbidden.
 * Specifically for authentication failed or not yet provided.
 */
module.exports = function (data, code, message, root) {
  var response = _.assign({
    code: code || 'E_UNAUTHORIZED',
    message: message || 'Missing or invalid authentication token',
    data: data || {}
  }, root);

  this.req._sails.log.silly('Sent (401 UNAUTHORIZED)\n', response);

  this.res.status(401);
  this.res.jsonx(response);
};

I’ve also customized the other responses in accordance with some guidance provided by the Sails API Yeoman generator, but I’m not going to go through all of them. ย If you would like to copy them, feel free to reference the project on GitHub. While you’re there, also checkout the overridden Blueprints in api/blueprints. These let you customize the standard processing of different operations exposed by the api controllers (unless you override them specifically in the controller). I also took these blueprints from the Sails API Yeoman generator.

Now we have our policy…how do we use it? In the /config folder, you should see a policies.js file. Open this file and adjust the code to the following:

module.exports.policies = {

    '*': ['isAuthenticated'],

    AuthController: {
        '*': true
    }
};

This is applying the following rules:

  1. Protect all controllers from unauthenticated users.
  2. Override this for the AuthController and allow anybody to hit that.

Loose Ends

OK, before we test this thing out, just a couple of small cleanups that will make Sails bug you less on “lift”. First open /config/models.js. Lets set up a standard migration policy so it won’t bug us on every project start. ย Uncomment or add the line that reads migrate and change it to ‘drop‘ for now. This will drop the data store each time the application runs. ย You can change to ‘alter‘ย if you don’t want this behavior.

migrate: 'drop'

Now we’ll tell Sails not to expect a Gruntfile. ย When you create Sails withย –no-frontend, it doesn’t scaffold a Gruntfile, but it still warnsย that there isn’t one on every start. Go to /.sailssrc and remove the Grunt hook:

{
  "generators": {
    "modules": {}
  },
  "hooks":{
    "grunt":false
  }
}

Testing it Out!

Wow…that was a lot of stuff to run through but we’re basically done! ย Let’s test it out. If you don’t already have Postman (or you’re preferred test tool) installed, go ahead and install it.

Now lets start up our Api. ย At your project root terminal type:

sails lift

You should see sails start up on port 1337 by default. Now fire up Postman and signup a user by posting the following JSON payload to your local signup endpoint. In my case, this is: http://localhost:1337/auth/signup

{
 "username":"testdude",
 "email":"test1@test.com",
 "password":"testdude"
}

signup

The response should look like:

{
 "code": "CREATED",
 "message": "The request has been fulfilled and resulted in a new resource being created",
 "data": {
 "token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyIjp7InVzZXJuYW1lIjoidGVzdGR1ZGUiLCJlbWFpbCI6InRlc3QxQHRlc3QuY29tIiwiZmlyc3ROYW1lIjoiIiwibGFzdE5hbWUiOiIiLCJwaG90byI6IiIsImNyZWF0ZWRBdCI6IjIwMTUtMDQtMjRUMjE6NTg6MTkuMjcxWiIsInVwZGF0ZWRBdCI6IjIwMTUtMDQtMjRUMjE6NTg6MTkuMjcxWiIsImlkIjoyfSwiaWF0IjoxNDI5OTEyNjk5LCJleHAiOjE0Mjk5OTkwOTksImF1ZCI6Im5venVzLmNvbSIsImlzcyI6Im5venVzLmNvbSJ9.j9mSeoHJiNb_rzxqJ8Cefv5ctcMVzbgvnUlvAWhbXas",
 "user": {
 "username": "testdude",
 "email": "test1@test.com",
 "firstName": "",
 "lastName": "",
 "photo": "",
 "createdAt": "2015-04-24T21:58:19.271Z",
 "updatedAt": "2015-04-24T21:58:19.271Z",
 "id": 2
 }
 }
}

You’ll notice that a token was generated. ย Copy the token that your API generated so that it’s available later.

We’ll now attempt to signin as well. ย Post the following JSON to your local signin endpoint.ย In my case, this is: http://localhost:1337/auth/signin. Earlier in our Passport config we set up local auth to use email and password, but we could change this to use the username if that is preferable.

{
 "email":"test1@test.com",
 "password":"testdude"
}

In this case, the response should be almost identical but the response code will be a 200-OK instead of a 201-Created.

Now lets try to access a resource without our token. Perform a get on your localย user endpoint. In my case:ย http://localhost:1337/user

You should get a response indicating that you are not authorized:

{
 "code": "E_UNAUTHORIZED",
 "message": "No auth token",
 "data": {}
}

Now lets update our request to add an Authorization header. ย For the value, add “JWT“, then a space, then the token you created previously.ย So an example value would be:

JWT eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyIjp7InVzZXJuYW1lIjoidGVzdGR1ZGUiLCJlbWFpbCI6InRlc3QxQHRlc3QuY29tIiwiZmlyc3ROYW1lIjoiIiwibGFzdE5hbWUiOiIiLCJwaG90byI6IiIsImNyZWF0ZWRBdCI6IjIwMTUtMDQtMjRUMjE6NTg6MTkuMjcxWiIsInVwZGF0ZWRBdCI6IjIwMTUtMDQtMjRUMjE6NTg6MTkuMjcxWiIsImlkIjoyfSwiaWF0IjoxNDI5OTEzNTI3LCJleHAiOjE0Mjk5OTk5MjcsImF1ZCI6Im5venVzLmNvbSIsImlzcyI6Im5venVzLmNvbSJ9.9FqGAVnJNM3SWRupOqCoW7tPJqu0ChZt5f2_En6GKqo

getUsers

Now send the Get request and you should get back the user record we created when we signed up!

{
 "code": "OK",
 "message": "Operation is successfully executed",
 "data": [
 {
 "username": "testdude",
 "email": "test1@test.com",
 "firstName": "",
 "lastName": "",
 "photo": "",
 "createdAt": "2015-04-24T21:58:19.271Z",
 "updatedAt": "2015-04-24T21:58:19.271Z",
 "id": 2
 }
 ]
}

Finalย Thoughts

Seems like we did a lot of stuff here, but it was all pretty easy thanks to the conventions and code generation provided by Sails. In upcoming articles, we’ll look at expanding this example to include social auth, a front end via Aurelia and additional storage mechanisms (we’re just using diskย here).

The code for this example is available here.ย The code in my uploaded GitHub example is using Postgresql instead of writing to disk, but that can easily be changed in the config/models.js file. ย Just point to any connection you have set up in the config/connections.js file.

** Warnings **

Always remember that this style of token auth is not secure if the token can be intercepted. In production, always perform communications with the API (at least those that send the token or any sensitive information) over SSL.

Additionally, you don’t want to check any production secrets into public source control. In this case, the secret in the passport config file. In a follow up we’ll talk about how to handle this but you can read about config overrides here.

Nozus JS Preface: Sails with Passport and JWT – Ermahgerd!

The Sails project in this case is only going to serve as an RESTful API. ย We’ll be using Aurelia on the front end, so we don’t need any presentation. ย What we do need is authentication provided by the API.

So my local auth implementation went through a few stages and much hair pulling. I first took a look at sails-generate-auth. This seemed like a great starting place…but I ran into some issues:

  • You can use tokens to protect the API, but the social auth implementation is geared towards server emittedย UI by default. I want something more SPA oriented.
  • It doesn’t use JWT currently although it supports bearer tokens.

So I moved on to Waterlock. Waterlock has a lot of cool features and isย super-easy to set up. In addition, it appears to be more oriented towards a SPA/API implementation.

But I ran into some issues:ย It uses session in situations where tokens are being used. ย This defeats some of the advantages of using tokens and reduces horizontal scalability. ย Some remedies to this are being worked on, but have been stuck in pull request status for several months, although it looks as though they may be resolved soon. ย So I figured I could just reference the github fork instead from npm until this gets resolved…but was still concerned about it using session.

Andย then with local auth in Waterlock:ย There is no register endpoint. ย There is a login endpoint that auto-registers you if it can’t find you. ๐Ÿ˜ฆ ย So if you fat-fingerย your username it just goes ahead and makes a new user. Again there is a pull request that hasn’t yet been merged.

At this point I decided I needed to keep looking. I could try and get those pull requests across the line but I’m already leaning away because of the opacity/complexity of the library as a whole and the session requirement. ย I don’t need something this complex but it’s a great reference implementation.

So next I found a Yeoman generator for Sails that was specialized for API’s and implemented with JWTย based auth. This is a really cool project and major kudo’s to Eugene who was also very responsive to questions. ย But then when I felt so close: ย The Yeoman generator failed because of a Windows file system issue. I think probably a path length issue due to nested npm packages. I’ll see you in hell Windows file system!!! ย Eugene mentioned that he hadn’t tested it on windows…so there it was.

This may have been a good thing because the generator/template also outputs a lot of functionalityย that is both very cool and yet unnecessary for my project. The upside being that you can blow out a complex API in almost no time, but I didn’t want to go through and attempt to clean out everything that I didn’t want/need. ย I also wanted to go through the process myself so I understood it better.

So I used the generator templates as a reference project and borrowedย some great things from it including updated API blueprints and a basic approach for issuing and validating JWT tokens.

So hopefully if you come across this post and are in a similar boat (npi), I can save you some of myย frustration (aka “earned knowledge”). I’ll be adding the implementation information in the next post, but aย reference implementation is already available in GitHub. ย Hope that’s helpful!

Next in series:

Nozus JS 1: Intro to Sails with Passport and JWT (JSON Web Token) Auth