I actually had written more about the exploit & vulnerability in my original drafts but I cut it out because it was a bit boring to read.
You are correct that with domain control I am able to serve content to any sign but the content will only be loaded once at boot time. Any future updates would have needed to come from their defunct AWS IoT connection (ignoring full restarts).
Using the exploit I remove the connection to AWS IoT and update some of the code to better connect it to the recreated API so users can update their signs in mostly real time.
The key point here would be "did things correctly" :)
The sign did use AWS IoT for real time configuration updates however initial configuration was pulled from their HTTP server. Using the vulnerability I describe in the article I just remove the connection to AWS IoT.
Hello, author here. Happy to answer any questions!
My apologies for the downtime, I wasn't expecting much traffic today since I submitted the post to HN yesterday but I've started scaling my server now!
Love this story. I did something similar with a website. Previous owner took it down, bought the domain from him and it organically grew to over 1m users per month with the same exact functionality.
I wouldn't discount your ability to bring this back to market. Would consult a lawyer to see what implications taking over the branding and APIs have. But clearly this has a big market.
I would really like to read more of this story. E.g. what did you do differently, did the original owner ever get in touch with you after it grew, stuff like that. Only if you can, ofc!
> Good question! No signs connected to the server until I reached out to some other sign owners to try out my instructions.
I do not know how many signs are out there. I imagine most people would have just unplugged their sign after the company's API vanished since any data would be stale and useless.
Love the story! At one point, you are asking about the purpose of the giant hole in the backside of the display: The most plausible explanation is that, it is to allow air circulation within the device to prevent overheating.
Another explanation that I saw [0] was that it was for people to pop the back panel out. I think this is the most likely explanation but it didn't occur to me while working with the sign. I feel like a little notch would have been more appropriate for an actual product.
That particle board material would crumble too much if they used a notch for removing the back. I've seen other items in similar cases with the exact same drilled hole in the back cover. Many used staples to hold the back cover on as wood screws tend to damage the (other) particle board used to make the sides. The "designers" probably used it because they had some other item at home (stereo, TV, loudspeakers or clock-radio).
Giving it little thought, I can say that NYC would have at best 10k sales of this item. In total... including any knockoffs.
The product may have scaled to be able to sell to London, Paris, Tokyo, Soul, Beijing, Shanghai, Hong Kong and Singapore. But this is the total addressable market for this item.
Let's say you have 2 engineers in US working on this full time - that's 500k in salaries(that includes all associated staff).
In NYC you would be able to charge $300, but no other major metro area has that kind of disposable income. In London the max price would be $170, Paris and others - $150... And boom, you can't even make a product to cover development costs.(not to mention any operating costs)
Cool art project and commercial viability clash perfectly here. This item is at best - an art piece, that should cost as such($3000+).
I've had a few projects like this where I list out the BOM then go for 'reasonable business success profit' and scratch my head at exactly this line:
> At even just 60k per founder, with the profit per sign sold being maybe $400, you would probably need to sell ~600 signs at full price per year to create enough revenue to run payroll.
Hardware is an exponent to the value of the internal reward, but the root of all business profit. We need a better funding mechanism for this style of development - more like the hardware lifecycle - wasn't Bolt doing something like that?
I had found some tweets by the company where they talked about using an Adafruit panel that was $40. The price on Amazon was about $30 so I figured I would go with the lower price. They may have switched to a lower cost panel but my guess is that didn't happen.
The generic name for these panels are "HUB75", based on the interface they use for driving them. I can confirm, these are $10-15/ea in small quantities on sites like Aliexpress. But you're likely right, they were probably buying retail from Adafruit.
"HUB75" refers to the 16 pin connector and specific structure of the square wave signals it's expecting (row order, pixel order, etc). This particular sign appears to use a 4mm pitch (space between each R/G/B led), indoor-level brightness SMD leds, and modules that are 32x64 pixels, like this one on AliExpress: https://www.aliexpress.us/item/2251832064290423.html?spm=a2g... ( ~$8 + ~$9 shipping to the US for single qty).
There are various "HUB75" panels of different pitch, brightness, LED type, matrix size, etc. Also, there's variations like "HUB75E" with 5 address lines instead of 4. Address lines are typically A/B/C, A/B/C/D, or A/B/C/D/E. And there's also "HUB08", "HUB12" and other standards.
I suspect the intention was to get an MVP out the door and get some initial "growth & engagement" so they can attract VCs - cost-effectiveness wasn't a priority at this stage.
It's a shame because there is a small but sustainable business here.
Yes I reached out to another owner who was able to connect the sign to the API. I've reached out to more people but haven't gotten too many responses. It's been 5 years after all. If you know anyone with one of these signs send them this post!
As noted by another HNer, we are using one cluster and up to 10 nodes as the top end, with only one running 90% of the time and only up to 3 covering the other 9%. We set 10 to the upper limit to ensure we're not going to have borked runs in case some crazy random ML model isn't going to take everything out and force killed jobs. The vast majority of workloads running on the cluster are HIGHLY variable with most running on Monday morning/afternoon and Month or Quarter starts + 3 days.
We are running many hundreds of jobs on those peak days with only a handful running on any other day. While many bring up examples where 24/7 infrastructure from a single box is more than plenty, we find that we can run micro VMs in this configuration and not have to worry about resource contention as our jobs run.
Pre-GKE, we were managing the timing manually, which was fine until we started to scale, but we found this to be a far better situation. Particularly because we simply don't have to think about it.
I've eaten their strawberries and they're pretty good! The berries are held in a plastic tray with the berries individually floating on plastic wrap so they don't get squished from their own weight.
While I don't think the berries are worth the price tag, I'd definitely be interested in trying some of the new products they mention in the article (tomatoes, melons).
The Gitlab integration for Pages is also pretty immature. It doesn't appear to currently work with Groups/Subgroups. I've been back and forth with people on their Discord for maybe a week now trying to just get it setup for a test.
The sqlite file format spec (https://www.sqlite.org/fileformat.html) is fairly short. I looked at hexdumps of where the db header should be, and a few page headers, and it seemed to line up, but not everything was filled in correctly. Trying to patch the header fields to match the sqlite spec and running it through sqlite3 didn't work, so I figured there was some more structural differences.
Since the spec is pretty short, I wrote a small parser for the sqlite files, printing out hexdumps of various sections as I went to check against the spec. The cell pointers in the btree leaf pages were missing, so I just tried reading them out in order from the cell data section and that worked (the pointers are really just an optimization to skip over cells when scanning). The table schemas are stored differently, but its a text format so its pretty easy to read (comma-separated columnName:type).
The record format was the most different part. Instead of type-tagged values like in sqlite, these records just had the column count and offsets of values. The offsets can be 1 or 2 bytes depending on the length of the record, which tripped me up for a bit. This was mostly figured out by looking at hexdumps of records. The values have to be interpreted based on the schema of the table the record is in.
The file format used little-endian for the record values, despite everything else in the file being big-endian. The schema in these files allowed array-of-vals and array-of-structs column types that I haven't decoded yet, but they don't seem to be on any of the important tables, mostly file metadata tables. There are some untyped binary columns containing an array of 64-bit floats, which was found by comparing against the csv of the same data.
Not counting some of the binary columns which I haven't decoded yet, the alternate version of the various sections were still pretty simple, so I was able to figure them out by eyeballing it. There weren't any numeric enums (outside of the ones in the sqlite spec) or flag masks that would have taken a while to decipher.
I had been working on this myself! I haven’t dug into SQLite to this level before, but found the header easy to correct. I got stumped when eyeballing the additional pages of information.
I really enjoyed learning more about b-trees and binary encoding formats taking this on. My primary exposure to those concepts has come from reading Designing Data-intensive Applications — so having something concrete to work on to test that knowledge was fun.