2025-12-14

The "Smart" appliance policy at Casa NoSwampCoolers

By way of introduction to some follow up posts appliance that require a external computer to make them go are a kind of cyber vulnerability. The utility of the device is dependent on the vailability of a working interface. If they drop support; if they decide to lock some features behind a paywall;1 if they don't support the app on the platform you you your device is worthless.

While someone could, in principle, program a new interface there are a number of problems:

  • Access to information about the device side of the interface. This stuff is trade secrets, and the holders rarely see it as profitable to share it, even if they are no longer supporting the affected devices.
  • Good engineering practce may suggest using a a common code base, but manufacturers are also willing to switch hardaware platfomrs to minimize costs. The result is a lot of diversity even within signale product lines.
  • Finally there are legal barriers to the "just hack it scheme". In the US, for isntance, the DMCA, means that the most trival effort by the manufacture to "protect" their kit makes the hacking job a felenoy. Until the right to repair is legally recognized,2 this multiples the difficulties improsed by the others.

So here in Casa NoSwampCoolers we have a three part policy:

  • No device which requies an account registered with the manufacturer.
  • No device which requies an app provided by the manufacture to access it's core function.
  • Any device which requires an app to access special features or functions must be explicitly discussed by the grownup prior to purchase.

The second part of this series will address the engineering reasons kin favor of "use you device to control it", and the third will look at a solution that lets' us have the upsides without the downsides.


1 No names, BMW.

2 I consider that a cause worth my political dollars. Would you care to join me?

The anagram problem (part 1/N)

Programmatically finding the anagrams of a given input is an easy problem if you have a list of valid words. Easy enough that it is often used as an example problem or assignment in introductory programming classes. The key1 observation is that the order of the letters doesn't matter so you can safely place them in a deterministic order for comparison. You decide if two words are anagrams of one another by sorting the letter in each and comparing the resulting strings. In psudocode it looks roughly like skey := sorted(input) for (word in words) if (sorted(word) = skey) println(word) Easy peasy.

Except that I'm writing in C++, so some things are wordier than in other langauges and there some boilerplate to wrap around it all. But, hey, at least it isn't Java. Oh, and I usually work on more substantial projects and in a pinch you code like you practice, so perhaps I should employ some techniques that I don't actually need for a toy example like this. Yeah, that's the ticket. Let's make the key a strong type.

The point is that the sorted nature of the contents of the key are the very core of it's nature. There is no point in comparing a key against an unsorted word: it won't tell us what we need to know. So make ourselves a type that wraps up a string with the content being sorted as a class invariant and provide comparison support between keys but not between keys and plain strings. That way the compiler will tell us if we make a mistake like that. Not really needed in a toy project like this—debugging those kinds of issues isn't too painful in a tiny code base like this—but good practice.

class key
{
public:
  template <typename Iter>
  explicit key(Iter first, Iter last)
      : val(first, last)
  {
    make_invariant(val);
  }
  explicit key(const std::string &input)
    : key(input.begin(), input.end()) {}
  // Move and copy c'tors and assignments operator

  store_type::size_type length() const;
  store_type::size_type size() const;

  // Provide iterators for use in the algorithms header

  auto operator<=>(const key &rhs) const = default;

private:
  void make_invariant(store_type &);

  std::vector<std::string::value_type> val;
  };

So, put into almost up-to-date C++ idiom (i.e. C++20, as I'm not yet ready to deal with the ranges and views library), the core of the anagram program looks like:

  std::ifstream in(argv[2]);
  if (!in) {
    std::cerr << "Failed to open '" << argv[2] << "' for reading."
              << std::endl;
    return EXIT_FAILURE;
  }
  std::vector<std::string> dictionary = anagram::get_dict(in);
  const anagram::key search_key = anagram::key(std::string(argv[1]));

  std::vector<std::string> alternatives;
  std::copy_if(dictionary.begin(), dictionary.end(), std::back_inserter(alternatives),
	       [&search_key](const std::string &word){
		 return anagram::key(word) == search_key;
	       });

  for (const auto &word : alternatives)
      std::cout << word << std::endl;

Digression

Perhaps you're wondering why I would care about an introductory exercise? Afterall, the problem describe above had no surprises. That is a fair question, and the answer is simple: I've recently started doing more puzzles involving letter and words. Cryptics, wordle and things like that. I've been a fan of plain crosswords for a long time, but new things are good for the brain at my age.

Anyway. Anagraming is a common sub-puzzle in cryptics, and I'm only so good at it. So I wrote a tool to use when I'm struggling. But it turns out the puzzles present cases that extend the basic anagram problem...

Back to the main story arc

Sometimes you know how long the desired word is but only some of the input letter. Say in cryptics you have an anagram idicator and you're confident about what fodder goes with it, but you have more spaces than characters in the existing source. For that we need a little more input (the range of lengths to consider, and to change the condition from equality of keys, from strict equality to the search key being a strict subset of the word key. We'll need to user to indicate the total size as well as the known source characters. As a minor generalization we can allow a range of sizes. Then we replace the main loop above with

  std::vector<std::string> anagram::find_single(const anagram::key &search_key, size_t minl,
					      size_t maxl,
					      const std::vector<std::string> &words)
{
  std::vector<std::string> alternatives;
  std::copy_if(std::execution::par_unseq,
	       words.begin(), words.end(), std::back_inserter(alternatives),
	       [&search_key, minl, maxl](const std::string &word){
		 const key word_key(word);
		 const auto word_len = word_key.length();
		 return (word_len > 0 && word_len >= minl && word_len <= maxl &&
			 is_superset_of(word_key, search_key));
	       });
    return alternatives;
      }
which calls
bool anagram::is_superset_of(const key ∓candidate, const key ∓search_key)
{
  std::string unmatched;
  std::set_difference(search_key.begin(), search_key.end(), candidate.begin(),
		      candidate.end(), std::back_inserter(unmatched));
  return unmatched.size() == 0;
}
I call that "partialgram".

Multi-word results remain outstanding

One more problem. Maybe you have a set of source character and want to create more then one word from them That will be the subject of the next part.


1 Pun intended.

2025-11-29

Has "AI" just made that experience worse?

Courtesy of my father's lengthy service in the Navy, I've been able to use USAA1 for insurance for my whole adult life. A small but comfortable privilege. Now, I bring this up because of USAA has had a ready-for-prime-time, flexible, responsive, automated-voice-interface answering their phones since (at least) the mid nineties (it informed my comments on an earlier post about this category of convenience). It's been my preferred interface for paying bills since I got my first policy with them.

Anyway. I paid a bill recently, and the voice interaction on the phone felt laggy—compared to a system that's been working since before the turn of the millennium—and I formed a suspicion that someone has replaced the gut of the existing system with a big machine learning model.

Now, there is a sense in which that is a very good idea. The existing system presumably had trouble with some accents and a large model can learn to overcome that. But it still felt like a worse experience.


1 A mutual insurance corporation in the United States. Originally for US Army officers, they've expanded their membership criteria over the years but the idea appears to be to select a highly responsible membership base.

2025-10-17

Just where are you from, anyway, my good mammal?

I've been packing for a short trip, and I found some paperwork from our last short trip. The one to Carlsbad Cavarerns. We were prevailed upon in the gift shop and "adopted" a bat (i.e. made a donation, but the kiddo liked the idea).

Only I see in the paperwork that it is a Brazilian Free-Tailed bat (one of the many speicies found in the park). Brazilian. In America.

No one tell Trump.

2025-09-26

I'll just add that to my list of "Question you don't want to hear"

My wife has been browsing online home listings from time to time lately. To be aware of the market because, while we're not looking now, we expect to be looking in the near to medium future. And, just maybe, also because she finds it fun.

Yesterday she looked up from her laptop and said "How much remodeling do you want to do?".

2025-09-20

I don't want a framework, but I do want the convenience

Programming frameworks can be a huge boon in terms of simplifying the provision on desirable features and behavior that aren't the core of your project.1 But they evolve toward someone's ideal of an all-singing, all-dancing, universal tool and that makes them huge and complicated. For a lot of the things I write at home, I don't need most of those facilities and I don't want huge or complicated. But I also don't want to be writing thousands of lines of code just to make my tool ergonomic.

Thankfully the open source community provides hundreds of high quality utility libraries covering all kinds of tasks you might want to do. Usually a handful or more for every feature you might want. Which means you have to choose.

This week, I decided that I wanted a command-line argument processor for a tool I"m dinking on. I've used gengetopt in the past when simple ad hoceries were insufficient, but it needs a two stage build and has a clunky, pure-C interface. Surely I can do better than that!

How to decide?

Well, I have a partial answer: The build system I use for home projects is Meson, which uses a facility it calls "wraps" to support fetching and building projects you depend on.2 Moreover, the tool has a facility to fetching wrap files from a centralized database which makes adding a provisioning library to your project nearly transparent.

So I'm going to restrict my consideration to tools that appear on Awesome C++ and are available from Meson's Wrap DB. There might be other good options out there, but these have had some level of peer review and (critically) are easy for me to get. That gets me down to:

After a cursory reading of the github READMEs it's clear that they all provide the features I need and have a version of the same kind of interface that QT uses.3

But that doesn't mean they're equivalent. One (argparse) uses a Method Chaining (AKA Fluent) interface for the "tell the parse what arguments to expect" step,4 and another (cxxopts) uses method chaining with statically polymorphic calls to operator() which makes a slightly weird syntax that I suspect clang-format is going to balk on. Neither of those is a deal breaker, but they are a little odd. Alas, the third option (CLI11) is a little clunky: requiring you to create two objects and relying on a try-catch for detecting errors (though it provides a c-preprocessor macro to hide the exception handling if you prefer).

Anyone have any relevant experience here?


1They also provide consistency across a set of tools for both users and developers, and you should not neglect that value in that. At work we use Qt, which has all the faults I list above and a swath of legacy issues as well. But the bit where my manager can say "Hey, we like that command line feature you added on your project, put it in Jane's too." And even though I've never worked in Jane's project before I can still dive right in because I know how command-line interactions work in QT. Programming at scale is a team sport and that changes the trade-offs.

2 I already use the doctest wrap in several projects, so I know how this goes.

3 You create a parser object, tell it what arguments you expect, pass it the command line, and then ask it what it found. With convenience facilities for detecting violated expectations, issuing help messages, and so on.

4 I've tried method chaining a few times in my own coding, and found that I only like it in cases where most of the uses of a class are going to be transient. Thankfully, if you declare a named object you don't have to use the fluent syntax if you don't want it.

2025-09-07

Wait, how many nodes?!?

My employer has been working through a IT security upgrade process, and recently performed a dry-run of an upcoming external audit. They report that they didn't find anything surprising or particularly worrying, but they took the opportunity to roll out new and updated employee agreements for just about everything computing related. Use of company network resources and email addresses, protection of company hardware, and so on. Including a new agreement for work-from-home.

That WFH agreement is the one that caught my eye. Buried in a lengthy bulleted list was a requirement to have anti-virus on "every other system on the network". We have—currently—one windows box, one antiquated mac, a networked Epson printer, a few linux systems, three phones, an e-book reader and a bushel (or at least a peck) of tablets. How does that mess fit in?

I run Clam AV on all the Linux devices (I have reason to think that will be considered sufficient). My wife's windows system has Windows defender, and I'm told that's okay. But what about the antique Mac (even if I only boot it occasionally), and those tablets, phones, and readers? And what about guests: we have a bunch of people who come to visit bringing phones, computers and tablets with them, and common hospitality requires that we offer them connectivity.

Then, as I started to think about it, I realized that this list was incomplete. Like, seriously, incomplete. I mean, the PlayStation 4 is just the beginning. Despite an disinterest in "Smart Home" stuff, we have a number of things that could reasonably be characterized as IoT devices.

So I launched into the hardware identification stage of a network security audit this morning.

We have two smart TVs; two other WiFI equipped appliances (though we have not configured their WiFi connections), and at least ten other WiFi connected nodes providing minor but useful services. That's circa thirty devices sharing a single network segment. And it got that way one, little choice at a time, without us really noticing.

My current thinking is that I need to segment the network to isolate guest and IoT devices from our primary computing resources and run at least some basic intrusion detection. Fun, eh?