Sunday 20 October 2024
Google Home Minis (1st Gen) bricked
As of this month, I have two out of my fleet of three *Google Home Mini (Gen 1)*s out of commission. Neither will boot; one slows a lonely green LED when the reset button is hit, the other nothing at all (and pulls massive power from USB while it boots).
There is a [truly MASSIVE thread](https://www.googlenestcommunity.com/t5/Speakers-and-Displays/HOME-mini-UNRESPONSIVE-thread-quot-Google-home-mini-4-dots-stuck-problem/m-p/558790/page/209876543210) about this over on the Google Nest forum. The *TL;DR* is this: if you contact Google about this issue, they will stall and give you the runaround. Eventually if you persist, they will get you to confirm the purchase date of the device(s), at which point they will either send you a new device or close the conversation with __"[it is|they are] out of warranty"__.
The speculation on the forums is that Google are remote-bricking these devices as they reach their warranty period. This truly saddens me but given the well-documented Google anti-enthusiasm for long-term product support, it makes perfect sense. These devices acheived their aim of massive market penetration and have become almost-indispensable around my house, with the excellent Spotify and Chromecast integrations being used multiple times per day. The very low purchase price (indeed *$free* in one case) made it a no-brainer to dot (hah!) them around the place.
The only working *Home Mini* left is a little newer. How much longer will it survive? I can't imagine there is any way to block it from receiving that remote kill-code from the mothership without completely nerfing its internet access, so it's just a ticking time bomb now.
There's also a highly visible lack of supply for the obvious replacement, the *Nest Mini (Gen 2)*. I suspect Google is letting these evaporate without replacement, so they can introduce a *Gen 3* model, considerably more expensive, for all the people who they've locked into the Google Nest/Home ecosystem and who now, funnily enough, need to replace a fleet of devices. Damn.
Draw your own conclusions about how Google's tracking with _"Don't Be Evil"_ at this point, as tonnes of still-useful electronics make their way into landfill.
Sunday 8 September 2024
UnSAFe at any speed
I first encountered the [Scaled Agile Framework for enterprise](https://scaledagileframework.com/), (from here-on referred to as SAFe) in 2014, when the large enterprise I was working for decided it was what was needed in order to ship solutions faster. (*Spoiler:* it wasn't, it didn't help in the slightest, it made us considerably slower, at considerable expense). I'll let you peruse their website at your leisure, but before you go, remember the tenets of the [Agile Manifesto](https://agilemanifesto.org/) (emphasis mine):
- Individuals and interactions over *processes* and tools
- Working software over comprehensive *documentation*
- Customer collaboration over contract negotiation
- Responding to change over *following a plan*
Now look at this, the ***SAFe 6.0*** I-don't-even-know-what-to-call-it diagram:
All I see is prescriptive *processes*, *documentation* and *plans*. You don't "do" agile development by signing up for expensive certifications that basically allow you to continue to ship 4-times-a-year but call yourself an agile workplace when you're recruiting. You also won't fool *anyone* which half a clue during the recruitment process.
Just one more example. This is grabbed from a PDF from [Accenture](https://sai2.wpengine.com/wp-content/uploads/delightful-downloads/2020/01/Key_Accenture_Learning_on_Scaled_and_Distributed_Agile_August-18-for-SAFe.pdf), who are [fully on-board with SAFe](https://scaledagile.com/case_study/accenture/), I suspect because:
- Acronym
- Includes the word Agile
- You can get certified for it (at great expense)
- It seems to make sense if you don't look at it too closely
- It actually makes you go slower than before (more billable hour$)
Ready? Here we go. This is how easy it is to "integrate" agile and waterfall:
So simple! It's super-cool that they like, don't have any interdependencies whatsoever! So clean!
Saturday 31 August 2024
Tesla Model 3 problems that the BYD Seal fixes
As stated at the time, after [having a Tesla Model 3 for 4 months](https://blog.themillhousegroup.com/2024/03/tesla-model-3-2022-standard-range-rwd.html), I won't be buying one. But what I may well get (or at least, lease), is the [*BYD Seal*](https://www.byd.com/eu/car/seal) instead - here's why:
| Criteria | Tesla Model 3 2024 RWD Standard Range | BYD Seal Premium 2024 |
| --------- | ----------- | -------- |
| Door handles | Aero, but annoying and fiddly | Aero, but pop out when needed |
| Instrument binnacle | None | Yes, 10.25" panel |
| HUD | No | Yes |
| Indicator stalks | None | Yes, normal |
| Rear entertainment screen | Yes | No |
| Roof | Glass | Glass, silver plated, shade insert |
| Supercharger access | Yes | Yes |
| Manufactured in | China | China |
| Apple CarPlay | No | Yes, wireless |
| Qi charging pads | 1 | 2 |
| 0-100km/h | 6.1 sec | 5.9 sec |
| V2L | No | Yes |
Yep, it fixes just about everything I disliked about the (pre-Highland) Model 3, plus what they "improved" in the Highland refresh, and it costs less to boot. Nice.
Sunday 28 July 2024
Next.js Server Actions - how do I send complex data
[Next.js](https://nextjs.org/docs/app/building-your-application/data-fetching/server-actions-and-mutations#forms) has some extraordinary integration between front- and back-ends. It takes some thinking about, but the thing that stumped me the longest was getting relatively complex objects (i.e. not just simple strings) across the "Server Actions" boundary.
## The Rules
There are lots of [rules](https://react.dev/reference/rsc/use-server#serializable-parameters-and-return-values) about __what__ you can send. Basically, it has to be serializable, which makes sense. But *how* do you send these things?
Unfortunately, most of the documentation limits itself to [trivially-simple](https://react.dev/reference/rsc/use-server#server-actions-in-forms) examples; like [a single simple string](https://react.dev/reference/react-dom/components/form#handle-form-submission-with-a-server-action)
My specific use case was trying to get an object that contained an array of very simple objects across the boundary:
```
type DTO = {
id: number;
foo: string;
people: Person[];
}
type Person = {
name: string;
age: number;
}
const dataToSend: DTO = {
id: 123,
foo: "bar",
people: [
{
name: "Tom",
age: 12,
},
{
name: "Dick",
age: 45,
},
{
name: "Harry",
age: 78,
},
]
];
```
While the order of the objects within the array was unimportant, it was crucial that the elements of the object could be rehydrated (for want of a better expression) correctly on the server side - i.e. with the `Harry`-is-`78` connection intact, guaranteed.
## On the client (version 1)
This should not be too hard, I thought to myself. I mean, sending form contents was pretty-much the original "interactive internet" mechanism; `/cgi-bin` and all that old stuff. I found a useful page that explained a [decent mechanism](https://mattstauffer.com/blog/a-little-trick-for-grouping-fields-in-an-html-form/) to serialize arrays, which I implemented like this in my React form:
```
{dto.people.map((p, i) => (
<>
>
))}
```
I went down that rabbit hole for far too long, before realising ... I *can* just send a string - a **JSON** string!
## Client, version 2
This new version feels much more React-idiomatic, and does a lot less grubbing around in `form` minutae. It uses the `useFormState` hook as well as a `useState` to allow the client to snappily update while the user is doing data-entry. Then when it's submission-time, everything gets shipped off in the everything-old-is-new-again Server Actions kinda way (various validation and button-enablement niceties removed for simplicity):
```
"use client"
const initialState = {
message: ''
}
export const SelectionForm = ({ dto: DTO}) => {
const [state, formAction] = useFormState(createOrUpdateServerAction, initialState);
const [inFlightPeople, setInFlightPeople] = useState(dto.people);
const onPeopleChanged = (newPeople: Person[]) => {
setInFlightPeople(newPeople);
}
return (
)
}
```
## Server action
In case you're wondering what the server-side of this looks like:
```
"use server"
export const createOrUpdateServerAction = async (prevState: any, formData: FormData) => {
const id = parseInt(formData.get("id"), 10);
const foo = formData.get("foo");
const peopleString = formData.get("people");
const people = JSON.parse(peopleString) as Person[];
// etc
```
.. Yes, you could just `JSON.stringify()` your whole object and send that instead of using `hidden` form fields. But I kinda like seeing them make their way over the network old-skool. Maybe that's just me :-)
Saturday 15 June 2024
Home-Cooked and Delicious
I've just read a wonderful article by [Maggie Appleton](https://maggieappleton.com/about) calling out what she terms ["home-cooked" software](https://maggieappleton.com/home-cooked-software) and the "barefoot" developers who work on such things.
It really made me want to crack back into publishing *scratch-my-own-itch* software; I've done a [little](https://github.com/themillhousegroup/mondrian) [of](https://github.com/themillhousegroup/scoup) [it](https://github.com/themillhousegroup/react-chromakeyed-image) in the past but a lot of it remains unfinished, either due to being "good enough" or, in probably my most public flame-out, the [Broadlink binding](https://github.com/themillhousegroup/broadlink-openhab-binding) for [openHAB](https://github.com/themillhousegroup/openhab2-addons), being ground down by a PR process (to get the binding integrated into the main codebase) that took so long that it became too onerous for me to continue with it.
I've got a few things brewing in my Github that aren't quite ready for public consumption yet, but in the meantime, almost as a counterpoint to my [post at the start of this year](https://blog.themillhousegroup.com/2024/01/my-apps-2024.html) where I shared my totally-*un*-niche list of uncontroversial, mainstream tools I use regularly, here are a couple of **super-barefoot**, totally home-cooked bits of software that excellent people have crafted, that happen to intersect with my niches of interest.
I should preface this short list with some background. As keen longtime readers might have [deduced](https://blog.themillhousegroup.com/2022/04/automating-heating-vents-with-openhab.html), I am a lover of Danish plastic. Yep, I'm an AFOL, an Adult Fan Of LEGO. I'm actually such a fan that I have *two* distinct collections; my personal stash of umpteen pieces, which is gradually being formed into a rendition of a railway running through a mythical French town in the 1980s:
As a side-effect of picking the "good bits" out of the many LEGO sets I've been gifted or bought, I've ended up with quite a number (over 11,000 at last count) less desirable (to me) LEGO pieces, and so those are in my "for sale" collection, available for perusal at both the major online LEGO marketplaces, [BrickLink](https://store.bricklink.com/trainfan#/shop) and [BrickOwl](https://trainfan.brickowl.com/). Which brings me to the first bit of home-cooked software.
### BrickSync
[BrickSync](http://www.bricksync.net/) does just what you'd expect inventory-management software to do in this instance; it keeps my inventory list in sync between the two marketplaces. Given that there are currently around 18,000 stores in BrickLink and 2,500 in BrickOwl (it's much younger, you can clearly see it in the UI), you would think that there would be, **at most** a worldwide market of 2,500 customers for this application. And yet here, completely free (although a donation is of course welcomed) is an open-source product that works extremely well, **just for us**.
It's a little rough around the edges, works in a weird *kinda-CLI, kinda-DOS-app* way and unfortunately *requires* to be run on an x86 platform (so no Raspberry Pi, but luckily I have an Intel NUC that fits the bill) but it deserves a shout-out for getting the job done. Something I've just added to my todo list is to download the [source](https://github.com/ZZJHONS/Bricksync) and make it much more like a traditional Unix CLI program, so that I can schedule synchronization runs with `cron` instead of having to leave a terminal window open while SSHed into my NUC.
### Brickognize
Closely-related to the first item. Historically, one of the most painful tasks when weeding out parts from my personal collection into my online for-sale inventory, has been **classification**. BrickLink has been around for a *very* long time, and being the only LEGO marketplace has led to certain naming conventions chosen by the original developer having "stuck". For example, *Light Bluish Gray* is *the* term used in the community, despite being completely different to the official LEGO colour name: *Medium Stone Gray*. Similarly, Bricklink groups similar parts into "categories" in ways that are sometimes difficult to follow. This made finding the right colour, category and part number for the given lump of ABS sitting in front of you quite the challenge. Until now.
[Brickognize](https://brickognize.com/) lets you take a picture (e.g. with your phone) of a LEGO part and will give you the most likely parts (including their BrickLink part number) that it corresponds to. There's obviously a very well-trained AI/LLM under there because in my experience (and I've used it probably a hundred times by now) it gets the part exactly right 98% of the time, and if it's not right, it's *very very* close.
And again, it's free. It even generously shares an [API](https://api.brickognize.com/docs) so you could build it into a workflow to streamline inventory additions yet further. Awesome.
Barefoot developers, you inspire and delight me. Thankyou.
Labels:
ai,
dev,
lego,
markdown-enabled,
nuc,
raspberrypi,
software
Tuesday 21 May 2024
Facepalm: Vanity email, insanity-email
Wow. Long time no [facepalm](https://blog.themillhousegroup.com/search?q=facepalm). Guess I must be in the right job!
This was a good one though.
So at work when a new customer signs up, one of the *many* things we do is create an [Auth0](https://auth0.com/) account for them. It's really just a "shell", with nothing of any value in it, but it gives them a stable identity to build other stuff off.
To create such a shell account we just need their email address, and we conjure up a [random UUID](https://developer.mozilla.org/en-US/docs/Web/API/Crypto/randomUUID) to use as their password. This has worked flawlessly for *tens of thousands of customers*.
Then, today, it didn't.
Auth0 gave us:
```PasswordNoUserInfoError: Password contains user information```
I'm sorry, what?
A certain amount of back-and-forth ensued with the devs who feed-and-water Auth0. It turns out there's a rule in Auth0 that is trying to avoid users including part of their username in their password. You know, how Granny likes her credentials to be `grangran@hotmail.com / grangran`.
So this *particular* customer had a custom "vanity" domain (which I will change for the sake of privacy) and was using a single letter as their email address; e.g.:
```d@dangermouse.com``` *(not their real address)*
And the Auth0 check was thus exploding if it found ***any instance of `d` in the random UUID password***. A [quick check](https://stackblitz.com/edit/node-yab2dv?file=index.js) shows that *~85% of UUIDs* generated by [Node's `crypto/randomUUID`](https://developer.mozilla.org/en-US/docs/Web/API/Crypto/randomUUID) will contain a `d`.
**Facepalm.**
Tuesday 9 April 2024
My Clojure Brings All The Recruiters To The Yard
So my LinkedIn profile has included the word **Clojure** for the last few years, despite my only having used it for ~2 years about a decade ago. I enjoyed it back then, and even [blogged about how nice an experience it was](https://blog.themillhousegroup.com/2014/09/why-clojure-is-fascinating-me.html). But I can't say I've kept current with it since. It's pretty niche, and I suspect only getting more so ...
Which is I guess why my inbox **exploded** yesterday with recruiters _all clamouring to tell me about the same Clojure position_:
I guess **QANTAS** aren't bothering with any exclusivity agreements on that one 😂
Saturday 30 March 2024
Tesla Model 3 (2022) standard range RWD review
I've been lucky enough to have this car as a "technology evaluator"/guinea-pig for some work experiments (I work at an energy company and we're exploring the possibilities of smart-charging EVs based on solar panel output, and/or time-of-day/off-peak rates). Having lived the Tesla ownership experience, but without the usual accompanying financial commitment, for two months now, I think puts me in an interesting and somewhat unusual position.
Sure, a car reviewer would get the car for free but would be unlikely to hold onto it for the twelve weeks I'm going to have it for before I have to give it back. A purchaser has a significant vested interest in seeing past the flaws - it's just human nature.
## Background
Unlike many, historically I've considered myself neutral towards Tesla the company. I am able to separate the company from their CEO, and recognise that _not every_ design decision is always Musk's personal directive. High-profile tech leaders like Gates, Jobs and Musk have all at times had their names unfairly cursed, probably by someone who's never seen just how many layers of (mis)management separates a modern CEO from people actually creating stuff.
What cannot be denied is the extraordinary acceleration of EV adoption that can be directly attributed to Tesla. The **Roadster**, **Model S** and **Supercharger network** were truly groundbreaking pieces of technology that traditional automakers would probably never have come up with in this half of the 21st century.
## First impressions - Exterior
This car is made in China, unlike earlier Teslas which all came out of the USA. Build quality feels good and the panel gaps look consistent, aside from the left C-pillar where the decklid panel sits proud after you close the boot/trunk. Then again, this car isn't new (it's a lease model and has had at least one owner before me) so it might have just been abused since coming out of the factory.
The car wears black aero wheelcovers which preclude using my bike pump to inflate the tyres. Annoying, because the car warns constantly about having low tyre pressure (sub 42psi). I've since purchased a $3 adaptor to allow correct inflation. I could have gone to a service station to inflate the tyres but I'd feel guilty about using their facilities when I haven't bought fuel from them.
The glass roof instantly strikes me as inappropriate for Australian conditions. Even on partly-cloudy days I can feel huge amounts of heat radiating off the inside of the ceiling. Cooling the cabin down hence uses far more electrical energy than it should, affecting range. I think Tesla missed a trick not reverting to a solid metal roof in the 2024 **"Highland"** Model 3 refresh. Make the glass roof an option if you want, but I've perceived very few benefits after the initial "oooh" factor.
## First impressions - Interior
The central screen is huge, but then again it has to be as there is nothing else. A strange plank of driftwood-coloured plastic stretches the width of the car, disguising the slot from which cool air and audio emanate. I like that I can get a direct breeze in my face, and the audio sounds good too, but I really object to the single screen and the UI it presents.
The touchscreen looks and feels exactly like a giant iPad. This is a double-edged sword. It's snappy, polished and pretty well laid-out. However many, many things are either hidden behind at least two-too-many screen taps, or insufficiently large target areas. This is simply dangerous, as it requires taking eyes off the road to focus on a nearfield object and a hand off the wheel to then try and hit a small target.
The screen defaults to a very large map view, with a smaller camera view and even-smaller "info tile" for currently-playing media or trip info. A lot of the time, the thing you need to see or do is on that smallest of UI elements, and requires a swipe and/or a tap on what feels like a 30px square target. This would be fine on an iPad - it's not fine in a moving vehicle with other tasks to be maintained.
## Driving experience
I've owned cars that are as fast as this one (0-100km/h in 6.1 seconds) and as "luxurious" (leather, electronic gadgetry), but never at the same time. The silence combined with the exhilarating acceleration is a potent combination. It's a pretty nice thing to tootle around the 'burbs in, enjoying the ability to punch off the line to be first in an upcoming lane-merging situation with minimal outward effort. The steering is nice and tight (no matter what mode you've put it in) and the suspension is on the firm side, but this is exactly how I like it. I specifically chose the Model 3 rather than the Model Y because I loathe SUVs and their bloated body-roll. It belies its hefty 1800kg kerb weight.
It doesn't take long to notice how quickly the battery percentage/range (you can't show both at the same time - come on, guys!) gets used up while tootling around though. I knew EV range-overestimation was a thing, but really, this car should get a real-world around-town figure of 350km (not 513km). And that's with me driving in Chill mode, with a _very_ gentle right foot and going for maximal regeneration.
I've taken it for a 250km round-trip (no charging required) and it was a comfortable highway cruiser. This particular car has the full "Self-Driving Capability" option box ticked but I don't think there is anything to show for it - the adaptive cruise control was enough for me, and it works well.
## Charging experience
Since the entire reason for having the car is based around home-charging, I've never taken the car to a Supercharger. It's always just been charged at home, using the standard Tesla charger that comes in the frunk, plugged into a regular 10A socket. Charging this way is the exact opposite of supercharging. It is _painfully sloooow_. How slow? It adds 15km of range _per hour_. This was hugely disappointing. Luckily we still have our existing ICE car, because you're looking at the car being off the road for most of a day to get the thing back up to 90% full.
If you were to buy this car, you'd _definitely_ want to budget on the **Tesla Wall Connector** (and possibly upgraded house wiring to back it up) to alleviate this unexpected source of Charge Anxiety.
## Conclusion
### Positives
I can't comment on its extended road-trip-ability or Supercharger network, but in my view, this is a **great car for city usage**.
The slow home-charging can be mitigated (at a cost) and the range (despite being massively less than advertised) is more than enough to only need topping up once or twice a week (for our needs).
It's comfortable, nippy, and handles nicely given its weight.
### Negatives
The **glass roof** is a misfeature, and a major one. Having to order (at extra cost, and loss of headroom) the sunshade just to mitigate it would annoy me no end.
The central **screen UI** has major safety ramifications and needs an overhaul to make common functions much more accessible.
I would save a substantial amount off the purchase price and _not_ option the **Enhanced Autopilot** (~AUD5k) or **Full Self Driving Capability** (~AUD10k) features - I just think they are overpriced vapourware items.
### Differences
Yes, I've added an extra section here that you wouldn't normally find in a conclusion. This is to sum up the things you'll find in a Model 3 that are maybe-good, maybe-not, but they are **different** to a "normal" car.
It pairs well enough with my iPhone that the **lack of CarPlay** doesn't feel like a big thing, and the **Tesla app** is a simple and effective replacement for a conventional key, but it takes getting used to.
The **push-buttons on the door interior** to open them are guaranteed to confuse first-time passengers, and the double-ended **exterior door handles** may be flush and aero but are incredibly fiddly to use and kids struggle with them. The **lack of a speedometer** in the dead-ahead position (even just a HUD would have sufficed) takes some adjustment. These feel like "just being different" features. The dispensing of indicator stalks in the "Highland" refresh again seems like something that saves Tesla $3 in parts, and they lean into their "minimal" mantra and get their fans to justify it as revolutionary.
## Final thoughts
Long term, I'm worried that Tesla are implementing "features" like the minimal driftwood dashboard and the "Gigacasting" of body parts that make things cheaper and easier for them, while not actually benefitting the customer in any real way. Tesla fanboys might be excited about it but they'd change their tune when they get rear-ended and the car gets written off because the entire back of the car cannot be repaired without being entirely replaced...
The whole car is, even without those expensive option boxes ticked, **about AUD$15k too much**. I understand there's a whole lotta lithium-ion under that floor and investment costs to recoup, but it just doesn't hit the mark for a AUD$60k+ car. Perhaps the _"Highland"_ refresh addresses some of those things, with the removal of that "plank" and addition of a small screen in the back for rear-seat passengers, and perhaps, well no, _definitely_, the electric vehicle rebates in my state (Victoria, Australia) are pathetic, but it's just too much of an ask.
I'm excited for the future of BEVs and PHEVs, but I don't think the Model 3 will be in the future for me.
Saturday 24 February 2024
My Perfect AWS Console
Yeah that's literally it.
I love AWS and use a decent portion of their offerings, but could really honestly get by with 2 of the OG AWS features, and one relative newcomer.
## AWS S3
The performance these days is absolutely top-notch (without even going down the [Directory Buckets](https://docs.aws.amazon.com/AmazonS3/latest/userguide/directory-buckets-overview.html) route). It's cheap enough that with a well-designed path structure, you can put just-about any workflow that can be represented with JSON into it. As in, you probably don't need [Step Functions](https://aws.amazon.com/step-functions/).
## AWS Lambda
I can't remember the last time I've needed a server that hangs around all the time, whether for work or side-gigs. Lambdas just fit _so well_ with modern request-response patterns that it's difficult to justify anything else. Add some [Provisioned Concurrency](https://docs.aws.amazon.com/lambda/latest/operatorguide/provisioned-scaling.html) if you really need nice warm caches and connections, but you still get the super-fast deployment and observability of functions-in-the-cloud. And you're not limited to 30-second execution time any more either (it's currently [up to 15 minutes](https://blog.awsfundamentals.com/lambda-limitations)), so you can wait for those slow 3rd-party APIs.
*Protip:* The Lambda Test Console allows you to store (and share!) test JSON payloads for each lambda. This can be a superb way to perform ad-hoc jobs, or re-process things that didn't quite work right the first time. Add a `dryRun?: boolean` option to the input shape and pass it though your lambda code to check things before opening the taps.
## AWS AppSync
Sure the web console is a little clunky and bug-ridden (it won't reauthenticate its own IAM session so your queries will eventually just ... die) but if you've got a GraphQL interface deep inside some WAF-protected VPN, this is a great way to give it a poke.
Sunday 28 January 2024
My Apps, 2024
Following a bit of a blogger-trend, here's the stuff I use on the daily.
I've omitted things I simply don't use, like custom launchers, podcast listeners, RSS, tracking and/or Mastodon clients;
- Mail service: __GMail__
- Tasks: __Drafts in GMail__
- Cloud storage: __Dropbox__
- Web browser: __Chrome__
- Calendar: __Google Calendar__
- Weather: __BOM__ (Melbourne, Australia) app
- Video: __Netflix, Disney Plus, Amazon Prime Video__
- Music (Listening): __Spotify__, Spotify via Google Home and/or Chromecast
- Music (Creation): __GarageBand__
- Passwords: __1Password__
- Notes: __Drafts in GMail__
- Code: __Visual Studio Code__
- Terminal: __Terminal.app__
- Search: __Google__
This list has shown me how much I depend on Google
a) not being evil; and
b) not just giving up on a product because they're bored of it
... which concerns me a little.
While it still exists though 😉, I *do* highly recommend the use of __Drafts in GMail__ as your general-purpose, cross-platform notes/todo app. You can attach files of arbitrary size, they sync to everything/everywhere *fast* (faster than Dropbox) and it's free (free-r than Dropbox... hmmm 🤔)
Labels:
apple,
google,
mac,
macos,
markdown-enabled,
music,
productivity,
programming
Subscribe to:
Posts (Atom)