<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>where does this go on davepedu.com</title><link>https://davepedu.com/</link><description>Recent content in where does this go on davepedu.com</description><generator>Hugo -- gohugo.io</generator><language>en-us</language><managingEditor>dave@davepedu.com (dave)</managingEditor><webMaster>dave@davepedu.com (dave)</webMaster><lastBuildDate>Wed, 11 Mar 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://davepedu.com/index.xml" rel="self" type="application/rss+xml"/><item><title>John Henry in the age of AI</title><link>https://davepedu.com/blog/2026/08/06/john-henry-in-the-age-of-ai/</link><pubDate>Thu, 06 Aug 2026 00:00:00 +0000</pubDate><author>dave@davepedu.com (dave)</author><guid>https://davepedu.com/blog/2026/08/06/john-henry-in-the-age-of-ai/</guid><description>&lt;p&gt;There is an American folk tale about a man named John Henry. It dates to the mid 19th century, a time during which
America was rapidly industrializing and the use of steam power was picking up&amp;hellip; Well, steam.&lt;/p&gt;
&lt;p&gt;Being a folklore tale, there are many different variations of it, though, most examples describe him as a man employed
on the construction of railroads. His specific role is often described as a &amp;ldquo;steel-driving&amp;rdquo; man but this takes
various specific forms such as hammering steel drills into rock to dig railroad tunnels, or hammering the spikes that
hold the rails to the &lt;a href="https://en.wikipedia.org/wiki/Railway_sleeper"&gt;wooden sleepers&lt;/a&gt;. Generally speaking, John Henry
is depicted as an extremely skilled and well respected worker.&lt;/p&gt;</description><content>&lt;p&gt;There is an American folk tale about a man named John Henry. It dates to the mid 19th century, a time during which
America was rapidly industrializing and the use of steam power was picking up&amp;hellip; Well, steam.&lt;/p&gt;
&lt;p&gt;Being a folklore tale, there are many different variations of it, though, most examples describe him as a man employed
on the construction of railroads. His specific role is often described as a &amp;ldquo;steel-driving&amp;rdquo; man but this takes
various specific forms such as hammering steel drills into rock to dig railroad tunnels, or hammering the spikes that
hold the rails to the &lt;a href="https://en.wikipedia.org/wiki/Railway_sleeper"&gt;wooden sleepers&lt;/a&gt;. Generally speaking, John Henry
is depicted as an extremely skilled and well respected worker.&lt;/p&gt;
&lt;p&gt;There are many full-text versions available online, but I&amp;rsquo;ll specifically reference
&lt;a href="https://www.poetryfoundation.org/poems/42897/john-henry"&gt;this one&lt;/a&gt;. The synopsis of the tale of John Henry is
as follows:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;John Henry is a railway construction worker and sees a steam-powered machine performing his job at an incredible pace.&lt;/li&gt;
&lt;li&gt;He decides to measure his prowess as a railway construction worker by having a duel against the steam-powered machine.
The duel is usually something along the lines of racing to mine a railway tunnel or set and spike down a length of
track, with the winner being the fastest to complete.&lt;/li&gt;
&lt;li&gt;John Henry ultimately beats the machine, but the exertion he put his body through to do so causes it to give out, and
he passes away.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It is &lt;em&gt;the&lt;/em&gt; classical man-versus-machine tale. And I think the parallels that can be drawn between it and the emergence
of AI in recent years are clear, and I will discuss them later on.&lt;/p&gt;
&lt;p&gt;I heard the story of John Henry when I was a child, but I forgot all about it until just recently. I subscribe to
several different interpretations of the folklore tale, but, in my opinion, it ultimately boils down to a statement on
how ever-advancing technology disrupts and conflicts with human dignity, perseverance, ability, and purpose. It is a
reasonable conclusion that John Henry felt threatened by the emergence of technology that could replace him. And his
response of setting up a duel against said technology suggests that he wanted to prove that he, a human, can equal or
defeat the machine. And the outcome of the duel asserts that man, the human spirit, ultimately triumphs over machine,
but at a cost. This time, the cost was John Henry&amp;rsquo;s life.&lt;/p&gt;
&lt;p&gt;Looking back on this story almost two centuries later, with a modern perspective over the technology we have now, coaxes
out some obviosities. The use of machinery in tunneling and railway construction are the norm. Construction without
machinery and with only human labor is practically non-existent anywhere machinery is available. There are still
&amp;ldquo;steel-driving&amp;rdquo; men, but their tools are no longer hand tools like John Henry&amp;rsquo;s, but machines.&lt;/p&gt;
&lt;p&gt;And the benefits of this shift to mechanization are obvious: such projects are completed faster, consuming fewer
resources, and - most importantly - more safely and with a lower human cost. Other external factors like safety
standards evolving over time too absolutely come into play here. However, having fewer humans on the job site, and
more machines, ultimately expose less humans to harm for simple mathematical reasons.&lt;/p&gt;
&lt;p&gt;I assert that the meaning behind the tale of John Henry is directly applicable to what is happening right now. It can
easily be interpreted as an allegory describing the emergence of AI and the impact that AI&amp;rsquo;s emergence has on how labor
is executed and what the value of a human being is with respect to work.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s interpret the tale of John Henry in the context of the last couple of years of technological advancement and the
emergence and adoption of - and opposition to - AI. I&amp;rsquo;m a software engineer, and will largely speak from the perspective
of my career.&lt;/p&gt;
&lt;p&gt;Software engineering is a field some speculate will be replaced by AI. Boris Cherny is the creator and head of Claude
Code at Anthropic and, Anthropic is currently one of the top companies that actually creates AI, rather than consuming
it. Cherny famously stated that &amp;ldquo;coding is largely solved&amp;rdquo; - to the chagrin of many software engineers everywhere.&lt;/p&gt;
&lt;p&gt;That&amp;rsquo;s our first parallel to John Henry. A machine has been created that, in theory, can take the place of a human and
do a task a human was previously required for. This corresponds to the steam-powered machine John Henry dueled against.&lt;/p&gt;
&lt;p&gt;Sidebar: I completely understand that you can&amp;rsquo;t remove a human from their desk and plop an AI there and get 1:1 results.
I am not making the argument that AI is sophisticated and accurate enough that this is doable. I write code for a
living. I have seen the code that AI generates. They are not on the same level yet. Yet. I am also not commenting on
whether I think AI will ever reach that point. It is inarguable that technology advances and gets better, but it is not a
given that &lt;em&gt;all&lt;/em&gt; technologies advance and get better. Plenty hit their limit and do not evolve further.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s get back to our steam and silicon powered machines.&lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt; Watching the emergence of AI, I have absolutely felt the
threat of it, personally. Watching a machine in seconds churn out a Python script that would have taken me at least an
hour to write is frightening. Why pay me to spend an hour doing that when a machine can do it instantly? It felt like
looking at my replacement. And I can imagine that John Henry, nearly two centuries ago, felt the same dread looking at
the steam-powered machine doing his job.&lt;/p&gt;
&lt;p&gt;How would my story look if I decided to duel the machine replacing me, like John Henry dueled his?&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ve been programming since the mid 2000s, and professionally since 2010. I have always followed emerging technology,
including AI, and looked for ways that I could leverage them to be a better developer. By now, I have a decent amount
of experience using AI tools to execute my craft.&lt;/p&gt;
&lt;p&gt;That being said, I look at the output of AI tools regularly and I have observed that it generally does not compare to
code written by skilled humans. But that is not too surprising. Communicating the requirements of a software project
often trips up humans too. There is an entire field of work dedicated to communicating and tracking requirements -
project management. So it comes as no surprise that a bot lacking context outputs code that misses the mark. On top of
that, AI is technologically limited by constraints like context windows that can easily lead to similar breakdowns.&lt;/p&gt;
&lt;p&gt;But I also recognize that the code LLMs output is often &amp;ldquo;good enough&amp;rdquo;. It completes the task, but is written with
patterns I don&amp;rsquo;t agree with or know better approaches to. And sometimes, that&amp;rsquo;s okay. The output can serve as a general
skeleton, ready for improvements by humans. It can make assumptions that even a human missed, and now needs to consider.
Or if the task is trivial enough, say one-off tasks, &amp;ldquo;good enough&amp;rdquo; might just be good enough.&lt;/p&gt;
&lt;p&gt;I also need to mention that sometimes AI writes code exactly as I would have written it, which feels amazing
and magical.&lt;/p&gt;
&lt;p&gt;Quality does matter to me. I cannot stress enough how much it pains me to ship ugly, but working, code. Even outside the
context of AI. I fully recognize Steve Jobs to be a deeply flawed man, but I adore
&lt;a href="https://www.brianhertzog.com/blog/cabinets"&gt;the story&lt;/a&gt; of his interaction with his father in which his father explains
the value of a job done right and with care.&lt;/p&gt;
&lt;p&gt;The story of John Henry doesn&amp;rsquo;t touch on the quality of the work or whether it mattered to him. But, his position on
the matter can be inferred. Versions of the tale include:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;He picked up a hammer and a little piece of steel&lt;/p&gt;
&lt;p&gt;Saying, &amp;ldquo;Hammer&amp;rsquo;s going to be the death of me, Lord, Lord,&lt;/p&gt;
&lt;p&gt;    Hammer&amp;rsquo;s going to be the death of me.&amp;rdquo;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;John Henry dedicated his life to his craft.&lt;/p&gt;
&lt;p&gt;And that brings us to the crux of what I&amp;rsquo;m trying to convey:&lt;/p&gt;
&lt;p&gt;He cared about the craft and pride of the work itself, not just the outcome. And the same is true when it comes to AI:
If you care about making and doing, and the process is more important than the result, AI isn&amp;rsquo;t for you.&lt;/p&gt;
&lt;p&gt;If you&amp;rsquo;re trying to get a job done, AI can be a force multiplier. The code might not be great, but it just might work.
Human code needs review and tweaking, and that doesn&amp;rsquo;t go away with AI. Human-written code can still be slop and that
slop can get sent to production if it goes unreviewed.&lt;/p&gt;
&lt;p&gt;These are the two extremes. The far, far ends of the spectrum of AI use. What defines the reasonable middle ground?&lt;/p&gt;
&lt;p&gt;Quality defines the middle ground. A skilled developer can use AI as a force multiplier to write more code at the same
quality he would have otherwise, but faster. And that&amp;rsquo;s what I think a major part of the debate around AI is. What I&amp;rsquo;ve
revealed here is that it all boils down to what one&amp;rsquo;s viewpoint of the purpose of a human being is.&lt;/p&gt;
&lt;p&gt;Everybody has to work to make the world and society go around. A lot of people think this sucks, and working your entire
life is a tragedy. And I&amp;rsquo;m inclined to agree.&lt;/p&gt;
&lt;p&gt;But I also see technology as an escape from this existence. In John Henry&amp;rsquo;s time, digging a tunnel required the toil of
thousands of men and women. Or, similarly, farming required hundreds of men or women working the fields, becoming
sunburnt and ruining their backs bending down to reach the plants on the ground. These occupations were dangerous and
took an extreme toll on the body.&lt;/p&gt;
&lt;p&gt;But those jobs are done differently now. Farming no longer requires thousands tending the fields year-round, almost all
of the work can be done by machine, with one machine replacing many, many workers. Ditto for tunneling.&lt;/p&gt;
&lt;p&gt;The men and women that would have been dying or destroying their bodies doing such labor are now doing something else.
That something else might not take a similar toll on their body. And that&amp;rsquo;s a good thing. I&amp;rsquo;m describing a net reduction
human suffering. And I&amp;rsquo;m describing manpower that has been freed up that can now be put towards something else that
improves humanity, like figuring out how to replace jobs that are necessary but incur human suffering with solutions and
machines that don&amp;rsquo;t suffer.&lt;/p&gt;
&lt;p&gt;However, it&amp;rsquo;s worth mentioning that shift doesn&amp;rsquo;t happen overnight. People out of work because they were replaced by
automation can have a hard time finding a new job. Starting over and shifting to a new field of work is often necessary
due to the very automation making the job market in a specific field disappear. Some people become poorer. There are
immediate and real world consequences worth considering. This is a legitimate grievance.&lt;/p&gt;
&lt;p&gt;But, machines replacing humans isn&amp;rsquo;t inherently a bad thing. It can be beneficial as it frees up labor resources - those
humans - and potentially allocates them to fields that advances the knowledge and capabilities and comfort of the
human race.&lt;/p&gt;
&lt;p&gt;And it&amp;rsquo;s a snowball effect. The observation that technology is advancing at a faster and faster pace is a common one,
and accurate in my opinion.&lt;/p&gt;
&lt;p&gt;I dream that, one day, machines are intelligent and accurate enough that mankind is freed from labor entirely. That one
day we&amp;rsquo;ll hit a point of technological advancement that allows most, if not every, necessary function in society and the world
to be handled by machines that don&amp;rsquo;t need to be paid and don&amp;rsquo;t suffer. And, unlike the above descriptions where this
now-freed up manpower can be redirected into some other form of labor, it can now be directed into leisure. I feel that
I need to mention that I believe some roles will probably still need a human touch forever. Social issues, perhaps.&lt;/p&gt;
&lt;p&gt;The non-financial cost of AI is a hot topic and worth connecting to John Henry as well.&lt;/p&gt;
&lt;p&gt;It has been said that the resources consumed by AI damage the planet. AI requires, among other things, electricity, and
it is inarguable that many forms of electrical generation harm the planet. Burning coal, for example. And that&amp;rsquo;s exactly
what powered the machine in the tale of John Henry.&lt;/p&gt;
&lt;p&gt;We have the benefit of hindsight here. We can observe that over the past centuries that the burning of coal - releasing
greenhouse gasses - has done measurable damage to the planet. Damage aside, descriptions of the absolute filth and
constant depressingly grey skies of 19th century cities, such as London, boggle the modern mind.&lt;/p&gt;
&lt;p&gt;But we can also see that old, harmful technologies such as coal are being replaced by better alternatives. Steam engines
gave way to diesel, which perform significantly more work and output less pollution. Even diesel gave way to electric
engines, which are even cleaner.&lt;/p&gt;
&lt;p&gt;Technology advances. Improvements to the things we want to do - like writing code or laying railways - always come at
a cost. There is no free lunch. But those costs decrease over time. We get the same amount of work done given less
input. Just look at the advancement of the power efficiency of CPUs or density of hard drives, or the improvement
in miles per gallon in cars or even the shift to electric vehicles.&lt;/p&gt;
&lt;p&gt;AI has real use, and it&amp;rsquo;s not going away. It has a cost, and while the level of impact of that cost is hotly debated,
it is inarguable that this cost is going to go down over time.&lt;/p&gt;
&lt;p&gt;AI feels like a step towards the utopia I described - where all work is taken care of by machines, and humans are free
to live as they please and pursue what makes them happy.&lt;/p&gt;
&lt;p&gt;This seems like a time to touch on the subject of creativity. A common criticism of AI is that it does not create novel
solutions, but is simply an information-laundering operation that - using software engineering for example - steals code
from someone else.&lt;/p&gt;
&lt;p&gt;And there&amp;rsquo;s a point to be made there. AI models are in fact created by feeding things created by humans into them.
Many opinions, some moral, some legal, exist on the matter. I expect a moment of reckoning will come, like the way it
did for Napster.&lt;/p&gt;
&lt;p&gt;One version of the tale of John Henry concludes as follows:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Well, they carried John Henry down the tunnel&lt;/p&gt;
&lt;p&gt;And they laid his body in the sand.&lt;/p&gt;
&lt;p&gt;Now every woman riding on a C and O train&lt;/p&gt;
&lt;p&gt;Says, &amp;ldquo;There lies my steel-driving man, Lord, Lord,&lt;/p&gt;
&lt;p&gt;     There lies my steel-driving man.&amp;rdquo;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;In the context of the story, the memory of John Henry isn&amp;rsquo;t preserved in a monument or a plaque. It&amp;rsquo;s preserved in
folklore. The memory of his deed is remembered by &amp;ldquo;every woman riding on a C and O train&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;The story&amp;rsquo;s own image of his legacy is happening on board the very kind of machine he dueled. The railroad won in the
sense that it kept running, kept carrying passengers, kept being the dominant mode of transportation.&lt;/p&gt;
&lt;p&gt;The craft John Henry dedicated his life to became a footnote of the past, but he wasn&amp;rsquo;t forgotten by the world. He was
preserved, but as folklore riding inside the machine&amp;rsquo;s success. And it is worth pointing out that the very occupation
John Henry was laboring in was itself a form of the advancement of technology: the proliferation of
rail-based transportation.&lt;/p&gt;
&lt;p&gt;John Henry won his duel, and it didn&amp;rsquo;t matter. The railroad kept running exactly as it was always going to. This part of
the story gets lost when we tell it as a tale of triumph of man over machine. Individual victory and industry-level
outcome were never the same contest, and John Henry seems to have believed that winning one settled something about
the other.&lt;/p&gt;
&lt;p&gt;There&amp;rsquo;s a version of that same mistake available to us right now. The engineer who refuses to touch AI on principle, be
it for quality or moral or resource-related reasons, who wants to out-code the machine to prove that human craft still
matters most&amp;hellip; I understand that instinct, I&amp;rsquo;ve felt it myself, and there&amp;rsquo;s real dignity in it. But it&amp;rsquo;s a duel, and
duels don&amp;rsquo;t move industries. Winning it, the way John Henry won his, doesn&amp;rsquo;t change anything.&lt;/p&gt;
&lt;p&gt;That&amp;rsquo;s different from the person who lost their job to automation and is still finding their footing, or the person
objecting because their work trained the very model now competing with them. Those aren&amp;rsquo;t duels for pride, those are
real costs and real grievances, and I don&amp;rsquo;t think they get resolved just because I believe the long arc bends toward
less work for everyone. They deserve consideration.&lt;/p&gt;
&lt;p&gt;John Henry&amp;rsquo;s duel itself was a fight to prove something rather than a fight for something. And I think it only delays
the utopic fantasy I described earlier. The fantasy, which is the same thing every generation before this one has fought
for without knowing it: fewer hours breaking your back, more of them living. Every labor dispute in the story of
mechanization was, underneath the immediate fight, an argument about who gets to keep the gains from the machine. My
hope is that the endpoint of that argument, run out far enough, is a world with no labor left to dispute over. And so,
for the first time, nothing left to exploit either.&lt;/p&gt;
&lt;p&gt;None of that arrives on its own, though. The eight-hour work day didn&amp;rsquo;t show up because factories got generous. People,
real human beings, organized and fought for a share of what the machine made possible.&lt;/p&gt;
&lt;p&gt;If the same pattern holds here, the utopia I&amp;rsquo;m describing isn&amp;rsquo;t something AI hands us automatically just by getting more
capable. It&amp;rsquo;s something we&amp;rsquo;ll still have to fight for, the same way every generation before us did.&lt;/p&gt;
&lt;p&gt;I just don&amp;rsquo;t think the fight looks like a duel.&lt;/p&gt;
&lt;div class="footnotes" role="doc-endnotes"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;Writing this sentence made me chuckle, as most electricity generation involves creating steam and using it to spin
a turbine, which makes even modern AI arguable steam-powered.&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</content></item><item><title>modernizing this blog</title><link>https://davepedu.com/blog/2026/07/10/modernizing-this-blog/</link><pubDate>Fri, 10 Jul 2026 00:00:00 +0000</pubDate><author>dave@davepedu.com (dave)</author><guid>https://davepedu.com/blog/2026/07/10/modernizing-this-blog/</guid><description>&lt;p&gt;My personal website has existed in some form since the late 2000s, and like many hobby projects, it has been rebuilt far
more times than strictly necessary. My taste in tech stacks has changed over time, and each version reflects whatever I
was experimenting with at the time.&lt;/p&gt;
&lt;p&gt;These are some of thee iterations it has gone through over the years, and what is to come:&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;2011 - &lt;a href="https://web.archive.org/web/20110227204954/davepedu.com/blog/"&gt;WordPress&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Early 2012 - &lt;a href="https://web.archive.org/web/20120202030252/http://davepedu.com/"&gt;A single word to mark my existence on the Internet&lt;/a&gt;&lt;/p&gt;</description><content>&lt;p&gt;My personal website has existed in some form since the late 2000s, and like many hobby projects, it has been rebuilt far
more times than strictly necessary. My taste in tech stacks has changed over time, and each version reflects whatever I
was experimenting with at the time.&lt;/p&gt;
&lt;p&gt;These are some of thee iterations it has gone through over the years, and what is to come:&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;2011 - &lt;a href="https://web.archive.org/web/20110227204954/davepedu.com/blog/"&gt;WordPress&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Early 2012 - &lt;a href="https://web.archive.org/web/20120202030252/http://davepedu.com/"&gt;A single word to mark my existence on the Internet&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Late 2012 - &lt;a href="https://web.archive.org/web/20120919081823/http://davepedu.com/"&gt;Cheesy wood paneling and filler text&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Early 2013 - &lt;a href="https://web.archive.org/web/20130402141622/http://davepedu.com/"&gt;A fully custom Python blog engine&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Late 2013 - &lt;a href="https://web.archive.org/web/20130402141622/http://davepedu.com/"&gt;Drupal 7 on a new .io domain&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;2014 - &lt;a href="https://web.archive.org/web/20141217064451/http://dpedu.io/"&gt;Design changes&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;2016 - &lt;a href="https://web.archive.org/web/20160305084026/http://dpedu.io/"&gt;More design changes&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;p&gt;Aside - this list was actually a lot of fun to put together. I had forgotten entirely about some of these iterations.&lt;/p&gt;
&lt;p&gt;Out of all of these, the Drupal 7 iteration lasted, by far, the longest. Which, itself, is amusing. Drupal is
implemented in PHP, and there is a lot of writing on the internet about whether PHP is the greatest language or a
&lt;a href="https://eev.ee/blog/2012/04/09/php-a-fractal-of-bad-design/"&gt;terrible&lt;/a&gt; language. But either way, it is undeniable that
PHP is like a cockroach that will not die.&lt;/p&gt;
&lt;p&gt;But why Drupal, anyways? It bills itself as being for Enterprise, which usually means large and hulking codebases. And
it is. When I set up this site using Drupal, I was working for the &lt;a href="https://rit.edu"&gt;Rochester Institute of Technology&lt;/a&gt;,
building &lt;a href="https://davepedu.com/projects/2013/12/01/rit-kgcoe-website/"&gt;the school&amp;rsquo;s own websites&lt;/a&gt; in Drupal. It was
already familiar to me from this, so it was easy enough to use Drupal for my own purposes.&lt;/p&gt;
&lt;p&gt;But, I eventually stopped working with Drupal and web development in general as I moved into other roles -
&lt;a href="https://davepedu.com/blog/2017/07/12/on-moving-to-the-bay/"&gt;which I wrote about here&lt;/a&gt; - and I didn&amp;rsquo;t keep up with
Drupal or PHP&amp;rsquo;s evolution. Which, for Drupal, actually would have required some serious re-writes as the theme rendering
layer was changed pretty drastically from version 7 to 8. But that&amp;rsquo;s probably more detail about Drupal than
anyone needs.&lt;/p&gt;
&lt;p&gt;PHP too, changed. My Drupal website was written to work with PHP 5, which hasn&amp;rsquo;t been supported for years. Server
operating systems have long dropped support for it. Between it and Drupal&amp;rsquo;s age, it&amp;rsquo;s honestly a miracle the site never
got hacked.&lt;/p&gt;
&lt;p&gt;PHP evolved, Drupal evolved, became more complicated, and I did not keep up. So now it is time to build something new,
and to keep it simple this time around.&lt;/p&gt;
&lt;p&gt;This time, I&amp;rsquo;ve decided to use &lt;a href="https://github.com/gohugoio/hugo"&gt;Hugo&lt;/a&gt;, which is a static website framework. In
summary, you give it your content formatted in Markdown which it will organize and feed into a template, which then in
turn outputs HTML.&lt;/p&gt;
&lt;p&gt;This post, too, is a Markdown file that contains the text you see and some metadata at the top:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-shell"&gt;+++
title = "modernizing this blog"
date = "2026-03-14"
author = "Dave"
cover = "img/IMG_1053_cropped.jpg"
hideCoverInSingle = true
description = "This blog has been migrated to a static website, using Hugo!"
tags = ["Programming", "Meta"]
+++&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;I&amp;rsquo;ll lose a few things by switching to a simple static site. I could update the Drupal website from anywhere - just log
in and write a new post, or save it as a draft. And the comment section - sorry, you&amp;rsquo;ll have to write to me
&lt;a href="https://davepedu.com/contact/"&gt;directly&lt;/a&gt; now.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;m also switching to my original &lt;code&gt;davepedu.com&lt;/code&gt; domain, instead of &lt;code&gt;dpedu.io&lt;/code&gt;. The .io domain was trendy at some point
in the past, and I&amp;rsquo;ve come to think that a classic .com is better anyway. On top of that, it allows me to keep the old
Drupal site online at &lt;code&gt;dpedu.io&lt;/code&gt; while I manually extract content from Drupal and re-write it for Hugo. After a time,
that domain will redirect here as well. The old site didn&amp;rsquo;t have terribly much content, but I&amp;rsquo;m also going through
unpublished drafts and making them into real posts.&lt;/p&gt;
&lt;p&gt;The new hosting stack will be similar, too. Before, I had a bare-metal dedicated server rented from
&lt;a href="https://ovhcloud.com"&gt;OVH&lt;/a&gt;. I went with dedicated servers since at the time I was working on computer infrastructure.
Another big complex thing I don&amp;rsquo;t want to take care of anymore, hah. I&amp;rsquo;ll be sticking with OVH, but I&amp;rsquo;ll be using a VPS
instead. A VPS is a good middle ground - over something completely hands-off like static hosting on Cloudflare - because
it still gives me a place to play sysadmin, which I enjoy. And everything configured on it will be installed with a
simple form of configuration management - but more on that in a future post.&lt;/p&gt;
&lt;p&gt;Of course, I couldn&amp;rsquo;t resist adding a bit of engineering fun.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ve written a Go-based application that acts like a toy version of AWS&amp;rsquo;s
&lt;a href="https://docs.aws.amazon.com/AmazonS3/latest/userguide/WebsiteHosting.html"&gt;S3 static website hosting&lt;/a&gt;. It&amp;rsquo;s called
&lt;a href="https://git.davepedu.com/dave/s3static"&gt;s3static&lt;/a&gt;, and is meant to be used with S3-compatible storage systems such as
&lt;a href="https://github.com/minio/minio"&gt;Minio&lt;/a&gt; or &lt;a href="https://github.com/rustfs/rustfs"&gt;RustFS&lt;/a&gt;. But more about that project in
its own dedicated post.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/traefik/traefik"&gt;Traefik&lt;/a&gt; will also be involved. It&amp;rsquo;s meant for Kubernetes but works great with a
text config file too. It also automatically handles TLS certificates via &lt;a href="https://letsencrypt.org/"&gt;Let&amp;rsquo;s Encrypt&lt;/a&gt;, for
which I used an unusual and convoluted process, which is a whole other story.&lt;/p&gt;
&lt;p&gt;Hopefully, keeping things simple will help this version stick around. Either way, rebuilding the site has been a fun
excuse to revisit old memories, clean things up, and experiment with a few new tools.&lt;/p&gt;
&lt;figure class="left" &gt;
&lt;img src="https://davepedu.com/blog/2026/07/10/modernizing-this-blog/img/IMG_1053_cropped_hu_4d96ba1b6988630d.jpg" alt="davepedu.com chief technology officer" /&gt;
&lt;figcaption class="center" &gt;davepedu.com chief technology officer&lt;/figcaption&gt;
&lt;/figure&gt;</content></item><item><title>highly available redis with redis-sentinel-proxy</title><link>https://davepedu.com/projects/2020/03/05/redis-sentinel-proxy/</link><pubDate>Thu, 05 Mar 2020 00:00:00 +0000</pubDate><author>dave@davepedu.com (dave)</author><guid>https://davepedu.com/projects/2020/03/05/redis-sentinel-proxy/</guid><description>&lt;p&gt;Redis is a widely used and extremely fast cache server. Redis supports high availability out-of-the-box, but requires a
cluster-aware client library to accomplish it. In this project, I explore making cluster-unaware clients compatible with
highly available Redis.&lt;/p&gt;
&lt;h1 id="high-availability"&gt;High availability&lt;/h1&gt;
&lt;p&gt;What is high availability? First, let&amp;rsquo;s cover a few basic concepts.&lt;/p&gt;
&lt;p&gt;Computer hardware and software are not perfect. Software can crash due to bugs or other problems. Hardware can fail due
to physical problems that prevent it from operating, like a short circuit.&lt;/p&gt;</description><content>&lt;p&gt;Redis is a widely used and extremely fast cache server. Redis supports high availability out-of-the-box, but requires a
cluster-aware client library to accomplish it. In this project, I explore making cluster-unaware clients compatible with
highly available Redis.&lt;/p&gt;
&lt;h1 id="high-availability"&gt;High availability&lt;/h1&gt;
&lt;p&gt;What is high availability? First, let&amp;rsquo;s cover a few basic concepts.&lt;/p&gt;
&lt;p&gt;Computer hardware and software are not perfect. Software can crash due to bugs or other problems. Hardware can fail due
to physical problems that prevent it from operating, like a short circuit.&lt;/p&gt;
&lt;p&gt;But in today&amp;rsquo;s world, software services that break sometimes just aren&amp;rsquo;t good enough. We could talk about the flight
computers on an aircraft - these absolutely cannot fail, or the consequences for those on board could be deadly. Your
car has a computer-controlled airbag that will save your life in the event of a crash.&lt;/p&gt;
&lt;p&gt;Less serious scenarios exist too. Retail giants like Amazon.com are almost always online.&lt;/p&gt;
&lt;p&gt;First, let&amp;rsquo;s define availability. Availability describes how reachable and usable a piece of software or service is, in
that all incoming requests receive a response. If you&amp;rsquo;re able to open Amazon.com in your web browser and buy something,
then Amazon.com is said to be &amp;ldquo;available&amp;rdquo;: it is online and able to fulfill the purpose for which it was created.&lt;/p&gt;
&lt;p&gt;High availability, as the name suggests, describes a higher level of availability. That is, a system that is designed to
continue operating despite certain supporting component failures. Generally speaking, this means running the software -
and supporting components - across many separate computer servers and often in different geographical locations.&lt;/p&gt;
&lt;p&gt;High availability represents a challenge in the software industry. Creating a program that runs across many computers
has challenges and caveats that are completely different from those of programs that run in one place.&lt;/p&gt;
&lt;p&gt;The &lt;a href="https://en.wikipedia.org/wiki/CAP_theorem"&gt;CAP theorem&lt;/a&gt; is a brief description of the type of challenge faced. It
was written to describe distributed data stores, but the idea applies to highly available systems in general:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The CAP theorem &amp;hellip; states that any distributed data store can provide at most two of the following three guarantees:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Consistency&lt;/strong&gt; - every read receives the most recent write or an error. Consistency means that all clients see the
same data at the same time, no matter which node they connect to.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Availability&lt;/strong&gt; - every request received by a non-failing node in the system must result in a response, without the
guarantee that it contains the most recent version of the data.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Partition tolerance&lt;/strong&gt; - the system continues to operate despite an arbitrary number of messages being dropped
(or delayed) by the network between nodes.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;- &lt;a href="https://en.wikipedia.org/wiki/CAP_theorem"&gt;Wikipedia&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;In practice, network partitions are unavoidable, and systems must choose between consistency and availability.&lt;/p&gt;
&lt;p&gt;But why does this matter? I&amp;rsquo;ll illustrate using an example:&lt;/p&gt;
&lt;p&gt;Credit cards are a staple of modern society. In America, almost a third of all transactions are made using a credit
card, according to the Federal Reserve. Thus, any disruption to the ability of consumers to use a credit card creates
massive disruptions in everyday life, perhaps even to the point of being life-threatening. When issues like this happen,
it makes headline news.&lt;/p&gt;
&lt;p&gt;That makes it clear why &lt;strong&gt;availability&lt;/strong&gt; is important: people have things to do, and they need your systems to do it.&lt;/p&gt;
&lt;p&gt;But what about consistency?&lt;/p&gt;
&lt;p&gt;Credit cards have a limit. When you make a transaction, the amount transacted counts towards your limit and the credit
card provider may refuse the transaction if it would put you over the limit. This exists to protect you from
accidentally spending too much money, and protects the credit card companies from lending you more money than they want
to.&lt;/p&gt;
&lt;p&gt;But what if you make two credit card transactions at the exact same time? Your credit card provider needs to be able to
handle this situation. One transaction needs to be processed before the next, and the subsequent transaction needs to be
processed using up-to-date data. For example, if your credit limit is $100 and you make two $75 transactions at once,
one of them must not go through as it would leave you over your limit.&lt;/p&gt;
&lt;p&gt;That all sounds rather obvious. But what about partition tolerance?&lt;/p&gt;
&lt;p&gt;As mentioned above, highly available software is often run in different geographic locations. This is to protect
against environmental threats to availability, such as natural disasters knocking down power lines or communications
equipment. This stuff happens, and even if some servers are knocked offline, others exist elsewhere to handle
the workload.&lt;/p&gt;
&lt;p&gt;But, machines being located in separate locations create a risk for partitions: that is, when the two sets of machines
cannot communicate with each other, but can still reach the outside world. To use the credit card transaction example
again - for this example to work correctly, the entire fleet of transaction-handling servers needs a consistent view on
how much money is in your account. This is impossible if they cannot communicate.&lt;/p&gt;
&lt;h1 id="redis"&gt;Redis&lt;/h1&gt;
&lt;p&gt;While high availability can be used to describe entire services, it can also describe subcomponents of a system.&lt;/p&gt;
&lt;p&gt;Redis was described as a cache server above, but technically, it is a database server. And, as software systems are
often backed by a database, it is often very important within a system. A system that needs Redis to function can only
be as reliable as the Redis instances it communicates with.&lt;/p&gt;
&lt;p&gt;As such, Redis has built-in support for high availability. The exact details can be read in
&lt;a href="https://redis.io/docs/latest/operate/oss_and_stack/management/sentinel/"&gt;Redis&amp;rsquo;s documentation&lt;/a&gt;, but the gist is as
follows: a program called Sentinel monitors multiple distinct Redis instances, and orchestrates the instances so that if
one fails, another instantly takes over.&lt;/p&gt;
&lt;p&gt;This works great in practice, but using such a cluster requires some additional work. Specifically, Redis provides
mechanisms and client libraries that are cluster-aware that integrates with Sentinel, so that from your application&amp;rsquo;s
perspective, any turmoil in the Redis cluster is hidden away.&lt;/p&gt;
&lt;p&gt;But not all software requiring Redis is written using this special library.&lt;/p&gt;
&lt;p&gt;And that&amp;rsquo;s where redis-sentinel-proxy comes in.&lt;/p&gt;
&lt;h1 id="redis-sentinel-proxy"&gt;Redis-sentinel-proxy&lt;/h1&gt;
&lt;p&gt;Redis-sentinel-proxy is a reverse proxy that exposes a standard - that is, non-cluster/Sentinel-aware - Redis
connection, that standard Redis clients can use. My project is a fork of
&lt;a href="https://github.com/enriclluelles/redis-sentinel-proxy"&gt;@enriclluelles&amp;rsquo;s original&lt;/a&gt;, and makes some
reliability and usability improvements.&lt;/p&gt;
&lt;p&gt;It works as follows:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Continuously query Sentinel for the Redis cluster&amp;rsquo;s status.&lt;/li&gt;
&lt;li&gt;When a Redis client connects, connect the client to the active Redis instance.&lt;/li&gt;
&lt;li&gt;When the Redis instance fails, close connections to clients and allow them to connect anew.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;That&amp;rsquo;s it! Now, cluster-unaware Redis clients can take advantage of highly available Redis.&lt;/p&gt;
&lt;p&gt;This was a fun project. If you find some use in it, &lt;a href="https://davepedu.com/contact/"&gt;let me know&lt;/a&gt;!&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Source code: &lt;a href="https://git.davepedu.com/dave/redisproxy"&gt;https://git.davepedu.com/dave/redisproxy&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;</content></item><item><title>ubuntu kickstart / preseed builder</title><link>https://davepedu.com/projects/2019/04/07/ubuntu-kickstart-builder/</link><pubDate>Sun, 07 Apr 2019 00:00:00 +0000</pubDate><author>dave@davepedu.com (dave)</author><guid>https://davepedu.com/projects/2019/04/07/ubuntu-kickstart-builder/</guid><description>&lt;p&gt;Kickstart and Preseed are tools for automatically installing Linux distributions such as Ubuntu. Installing new copies
of Linux can be time-consuming, especially if you must do so from a ISO image. These tools automate the install process.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://help.ubuntu.com/community/KickstartCompatibility"&gt;From the Ubuntu documentation:&lt;/a&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Many system administrators would prefer to use an automated installation method to install Ubuntu on their machines.
The preseeding feature of debian-installer answers this need.&lt;/p&gt;
&lt;p&gt;Using Kickstart, a system administrator can create a single file containing the answers to all the questions that
would normally be asked during a typical Ubuntu installation.&lt;/p&gt;</description><content>&lt;p&gt;Kickstart and Preseed are tools for automatically installing Linux distributions such as Ubuntu. Installing new copies
of Linux can be time-consuming, especially if you must do so from a ISO image. These tools automate the install process.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://help.ubuntu.com/community/KickstartCompatibility"&gt;From the Ubuntu documentation:&lt;/a&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Many system administrators would prefer to use an automated installation method to install Ubuntu on their machines.
The preseeding feature of debian-installer answers this need.&lt;/p&gt;
&lt;p&gt;Using Kickstart, a system administrator can create a single file containing the answers to all the questions that
would normally be asked during a typical Ubuntu installation.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;After creating a few ISO images with embedded Kickstart/Preseed configurations, I decided I needed faster iteration, and
so I created a tool for generating Ubuntu Linux .iso files with embedded automated installers: &lt;strong&gt;Ubuntu Kickstart
Builder.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="https://davepedu.com/projects/2019/04/07/ubuntu-kickstart-builder/img/kickstart-full_hu_db886f949d2e1b95.png" alt="Kickstart Builder main UI screenshot"/&gt;&lt;/p&gt;
&lt;p&gt;The tool works in a fairly straightforward way:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Unpack Ubuntu ISO images in the tool&amp;rsquo;s data directory.&lt;/li&gt;
&lt;li&gt;Open the web UI and edit the Kickstart and Preseed configuration files.&lt;/li&gt;
&lt;li&gt;Optionally, upload additional files to be included in the ISO image.&lt;/li&gt;
&lt;li&gt;Click Build, and your customized CD image will be downloaded.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The ISO image can then be written to physical media or booted in a VM.&lt;/p&gt;
&lt;p&gt;While Kickstart and Preseed configurations can also be served over the network, embedding them directly in an
installation image has some advantages. A self-contained ISO is easy to distribute, works in environments without
network infrastructure, and guarantees that the installer always uses the intended configuration. This can be especially
convenient for testing in virtual machines, provisioning isolated lab machines, or sharing reproducible install media.&lt;/p&gt;</content></item><item><title>pyods: open directory scraper</title><link>https://davepedu.com/projects/2019/02/28/pyods-open-directory-scraper/</link><pubDate>Thu, 28 Feb 2019 00:00:00 +0000</pubDate><author>dave@davepedu.com (dave)</author><guid>https://davepedu.com/projects/2019/02/28/pyods-open-directory-scraper/</guid><description>&lt;p&gt;I&amp;rsquo;m a long-time reader and participant of the Reddit.com Subreddits
&lt;a href="https://old.reddit.com/r/DataHoarder/"&gt;/r/Datahoarder&lt;/a&gt; and
&lt;a href="https://old.reddit.com/r/opendirectories/"&gt;/r/OpenDirectories&lt;/a&gt;. As both of these deal with retrieval and archival of
data and information, wget is a wildly popular tool within these communities.&lt;/p&gt;
&lt;p&gt;For the unfamiliar, an &amp;ldquo;open directory&amp;rdquo; is a directory on a website where there is no index file. Since there&amp;rsquo;s no
content to serve, most web servers will generate a page showing a listing of files on the server. Wget is a command-line
tool for making various types of HTTP and FTP requests. Specifically, wget is specialized for content mirroring and it
works nicely with open directories. That is, following links at the target URL to discover additional links to download
with the end goal of downloading an identical mirror of the remote site.&lt;/p&gt;</description><content>&lt;p&gt;I&amp;rsquo;m a long-time reader and participant of the Reddit.com Subreddits
&lt;a href="https://old.reddit.com/r/DataHoarder/"&gt;/r/Datahoarder&lt;/a&gt; and
&lt;a href="https://old.reddit.com/r/opendirectories/"&gt;/r/OpenDirectories&lt;/a&gt;. As both of these deal with retrieval and archival of
data and information, wget is a wildly popular tool within these communities.&lt;/p&gt;
&lt;p&gt;For the unfamiliar, an &amp;ldquo;open directory&amp;rdquo; is a directory on a website where there is no index file. Since there&amp;rsquo;s no
content to serve, most web servers will generate a page showing a listing of files on the server. Wget is a command-line
tool for making various types of HTTP and FTP requests. Specifically, wget is specialized for content mirroring and it
works nicely with open directories. That is, following links at the target URL to discover additional links to download
with the end goal of downloading an identical mirror of the remote site.&lt;/p&gt;
&lt;p&gt;For the most part, wget excels at doing this. It&amp;rsquo;s been around for decades and is supported on nearly every operating
system, is easy to find information about online, and is generally bug free.&lt;/p&gt;
&lt;p&gt;However, wget suffers from a design flaw - its single threaded operation.&lt;/p&gt;
&lt;p&gt;Meet Pyods - the modern, Python, Open Directory, Scraper:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-"&gt;$ pyods --help
usage: pyods [-h] [-u URL] [-o OUTPUT_DIR] [-p PARALLEL] [-c] [-d DELAY]
[-e EXCLUDE [EXCLUDE ...]] [-f EXCLUDE_FROM] [-v]
Open directory scraper
optional arguments:
-h, --help show this help message and exit
-u URL, --url URL url to scrape
-o OUTPUT_DIR, --output-dir OUTPUT_DIR
destination for downloaded files
-p PARALLEL, --parallel PARALLEL
number of downloads to execute in parallel
-c, --clobber clobber existing files instead of resuming
-d DELAY, --delay DELAY
delay between requests
-e EXCLUDE [EXCLUDE ...], --exclude EXCLUDE [EXCLUDE ...]
exclude patterns
-f EXCLUDE_FROM, --exclude-from EXCLUDE_FROM
exclude patterns from file
-v, --verbose enable info logging&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Pyods was written, in Python 3, from the ground up with speed and mirroring in mind. In contrast with wget, Pyods
downloads many URLs in parallel. The options in the help text above show off Pyods&amp;rsquo; other capabilities such as excluding
URLs, adding delay between requests, etc.&lt;/p&gt;
&lt;p&gt;An example invocation might look like:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-"&gt;pyods -u http://example.com/some_open_directory/ -o ./output -p 10 --delay 5&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This example would use 10 parallel downloads with a delay of 5 seconds between each HTTP requests.&lt;/p&gt;
&lt;p&gt;I intend to release this on Pypi eventually. But for now, Pyods is available on my git instance. Happy scraping!&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Source code: &lt;a href="https://git.davepedu.com/dave/pyods"&gt;https://git.davepedu.com/dave/pyods&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;</content></item><item><title>nginx upload size behavior</title><link>https://davepedu.com/blog/2018/12/14/nginx-upload-size-behavior/</link><pubDate>Fri, 14 Dec 2018 00:00:00 +0000</pubDate><author>dave@davepedu.com (dave)</author><guid>https://davepedu.com/blog/2018/12/14/nginx-upload-size-behavior/</guid><description>&lt;p&gt;I recently encountered some odd behavior in nginx involving file upload sizes. Nginx has long had a
&lt;code&gt;client_max_body_size&lt;/code&gt; setting which controls how large of an HTTP POST or file upload the server will accept by
comparing to the &lt;code&gt;Content-Length&lt;/code&gt; header. Attempting to send a request larger than this results in a
&lt;code&gt;413 Request Entity Too Large&lt;/code&gt; error from the server. But is this always the case?&lt;/p&gt;
&lt;p&gt;For starters, here&amp;rsquo;s the description of &lt;code&gt;client_max_body_size&lt;/code&gt;. From
&lt;a href="https://nginx.org/en/docs/http/ngx_http_core_module.html#client_max_body_size"&gt;nginx&amp;rsquo;s docs&lt;/a&gt;:&lt;/p&gt;</description><content>&lt;p&gt;I recently encountered some odd behavior in nginx involving file upload sizes. Nginx has long had a
&lt;code&gt;client_max_body_size&lt;/code&gt; setting which controls how large of an HTTP POST or file upload the server will accept by
comparing to the &lt;code&gt;Content-Length&lt;/code&gt; header. Attempting to send a request larger than this results in a
&lt;code&gt;413 Request Entity Too Large&lt;/code&gt; error from the server. But is this always the case?&lt;/p&gt;
&lt;p&gt;For starters, here&amp;rsquo;s the description of &lt;code&gt;client_max_body_size&lt;/code&gt;. From
&lt;a href="https://nginx.org/en/docs/http/ngx_http_core_module.html#client_max_body_size"&gt;nginx&amp;rsquo;s docs&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Sets the maximum allowed size of the client request body, specified in the “Content-Length” request header field. If
the size in a request exceeds the configured value, the 413 (Request Entity Too Large) error is returned to the
client. Please be aware that browsers cannot correctly display this error. Setting size to 0 disables checking of
client request body size.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;While this sounds pretty obvious, there&amp;rsquo;s some hidden behavior one might not expect.&lt;/p&gt;
&lt;p&gt;Take the following server configuration block for example:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-clike"&gt;server {
server_name subdomain.somehostname.com listen 443 ssl;
ssl_certificate "/some/file.crt";
ssl_certificate_key "/some/other/file.key";
ssl_protocols ...;
ssl_ciphers ...;
client_max_body_size 64m;
location / {
return 307 https://anothersubdomain.somehostname.com$request_uri;
}
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The function of this configuration is obvious - it&amp;rsquo;s an ssl server that will issue a 307 redirect to all requests,
redirecting them to another location. In my case this location would receive file uploads from legacy software; the 307
was needed to cause clients to retry the upload elsewhere. Of course, if the upload is larger than 64m, a 413 error is
returned instead.&lt;/p&gt;
&lt;p&gt;But what happens if we remove the location block?&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-clike"&gt;server {
server_name subdomain.somehostname.com listen 443 ssl;
ssl_certificate "/some/file.crt";
ssl_certificate_key "/some/other/file.key";
ssl_protocols ...;
ssl_ciphers ...;
client_max_body_size 64m;
return 307 https://anothersubdomain.somehostname.com$request_uri;
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;While this looks like it would function identically (and it does for the most part), there&amp;rsquo;s a subtle difference. Nginx
will no longer enforce the &lt;code&gt;client_max_body_size&lt;/code&gt; directive! Requests containing uploads of any size will be issued
a redirect.&lt;/p&gt;
&lt;p&gt;This is of course a relatively minor issue, as a redirecting server does not process any upload content. In other words,
the client is redirected before the upload process begins. In my case, I was moving from the latter config above to the
former due to needing some additional location blocks in the config.&lt;/p&gt;
&lt;p&gt;As it turns out, this behavior is &lt;a href="https://mailman.nginx.org/pipermail/nginx/2018-December/057293.html"&gt;likely undocumented&lt;/a&gt;.
Lesson learned - be careful how you set a &lt;code&gt;client_max_body_size&lt;/code&gt;!&lt;/p&gt;</content></item><item><title>isle of man TT</title><link>https://davepedu.com/photos/2018/06/14/isle-of-man-tt/</link><pubDate>Thu, 14 Jun 2018 00:00:00 +0000</pubDate><author>dave@davepedu.com (dave)</author><guid>https://davepedu.com/photos/2018/06/14/isle-of-man-tt/</guid><description>&lt;p&gt;Earlier this year, I traveled from my home in California to the Isle of Man, which is a 35 mile long island in the Irish
Sea, known for its motorsports events. The Isle of Man is a self-governing British Crown Dependency, meaning they are
effectively their own country, though they technically belong to the British monarchy.&lt;/p&gt;
&lt;p&gt;The Isle of Man is most well known for the &lt;a href="https://en.wikipedia.org/wiki/Isle_of_Man_TT"&gt;Isle Of Man Tourist Trophy&lt;/a&gt; -
a 2-week motorcycle racing event held on the island&amp;rsquo;s public roads. For those two weeks, the public roads close down
on weekdays, and are used instead as a roughly 38 mile long circuit raceway. In addition, many public events are held
during the race weeks. It&amp;rsquo;s quite the spectacle.&lt;/p&gt;</description><content>&lt;p&gt;Earlier this year, I traveled from my home in California to the Isle of Man, which is a 35 mile long island in the Irish
Sea, known for its motorsports events. The Isle of Man is a self-governing British Crown Dependency, meaning they are
effectively their own country, though they technically belong to the British monarchy.&lt;/p&gt;
&lt;p&gt;The Isle of Man is most well known for the &lt;a href="https://en.wikipedia.org/wiki/Isle_of_Man_TT"&gt;Isle Of Man Tourist Trophy&lt;/a&gt; -
a 2-week motorcycle racing event held on the island&amp;rsquo;s public roads. For those two weeks, the public roads close down
on weekdays, and are used instead as a roughly 38 mile long circuit raceway. In addition, many public events are held
during the race weeks. It&amp;rsquo;s quite the spectacle.&lt;/p&gt;
&lt;p&gt;The options for lodging on the island are actually pretty interesting - the local government runs a program called
&lt;a href="https://homestay.im/"&gt;Homestay&lt;/a&gt;, which encourages residents to rent out extra bedrooms to tourists. It&amp;rsquo;s a pretty great
experience - much more interesting than a hotel, and the rates can be lower too. This is what I did - I rented a bedroom
in the town of Peel, the third-largest town on the island. More on Peel below.&lt;/p&gt;
&lt;p&gt;This album covers sights from the island and along the way, so there is something for everyone.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://davepedu.com/photos/2018/06/14/isle-of-man-tt/img/cover_hu_ae721cb4a8b2aaa.jpg" alt="A custom motorcycle parked on the streets of Douglas"/&gt;&lt;/p&gt;
&lt;p&gt;A custom motorcycle parked on the streets of Douglas.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;I live in California, and the Isle of Man is about 5,000 miles away from me. Its airport is small so there is no direct
flight from here, so between my various connecting flights I traveled about 6,000 miles in total - one way. Fun fact -
the &amp;ldquo;halfway around the world&amp;rdquo; idiom if taken literally is about 12,000 miles, and I traveled half of that. Halfway to
halfway around the world!&lt;/p&gt;
&lt;p&gt;A layover of about a day at London Gatwick Airport allowed me to spend the day in London. I took the classic
double-decker bus tour, and a boat tour on the River Thames. I didn&amp;rsquo;t have a set agenda besides these items, and
effectively wandered otherwise. I first found myself in Hyde Park, which features many historical monuments.&lt;/p&gt;
&lt;h1 id="australian-war-memorial"&gt;Australian War Memorial&lt;/h1&gt;
&lt;p&gt;&lt;img src="https://davepedu.com/photos/2018/06/14/isle-of-man-tt/img/IMG_9000_hu_5f1bd509bc419393.jpg" alt="Australian War Memorial, London"/&gt;&lt;/p&gt;
&lt;p&gt;From &lt;a href="https://en.wikipedia.org/wiki/Australian_War_Memorial,_London"&gt;Wikipedia&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The Australian War Memorial in London is a memorial dedicated in 2003 to the 102,000 Australian dead of the First and
Second World Wars.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h1 id="new-zealand-war-memorial"&gt;New Zealand War Memorial&lt;/h1&gt;
&lt;p&gt;&lt;img src="https://davepedu.com/photos/2018/06/14/isle-of-man-tt/img/IMG_9015_hu_107fc27f3a92756.jpg" alt="New Zealand War Memorial, London"/&gt;&lt;/p&gt;
&lt;p&gt;From &lt;a href="https://en.wikipedia.org/wiki/New_Zealand_War_Memorial,_London"&gt;Wikipedia&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The New Zealand War Memorial in London is a memorial to the war dead of New Zealand in the First and Second World
Wars, unveiled in 2006. Officially named &amp;ldquo;Southern Stand&amp;rdquo;, the memorial was designed by architect John Hardwick-Smith
and sculptor Paul Dibble, both from New Zealand.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h1 id="big-ben"&gt;Big Ben&lt;/h1&gt;
&lt;p&gt;&lt;img src="https://davepedu.com/photos/2018/06/14/isle-of-man-tt/img/IMG_9066_hu_f9952889e0c7e7f0.jpg" alt="Big Ben, London"/&gt;&lt;/p&gt;
&lt;p&gt;Big Ben was undergoing some sort of construction work that required it to be nearly enclosed in scaffolding.
Unfortunately, you couldn&amp;rsquo;t see most of it. The Palace of Westminster is on the left.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://davepedu.com/photos/2018/06/14/isle-of-man-tt/img/IMG_9209_hu_7762c57ee6afcef.jpg" alt="Big Ben, London"/&gt;&lt;/p&gt;
&lt;p&gt;Close up of the clock face, behind which Big Ben - which is technically the name for the bell inside - sits.&lt;/p&gt;
&lt;h1 id="tower-bridge"&gt;Tower Bridge&lt;/h1&gt;
&lt;p&gt;&lt;img src="https://davepedu.com/photos/2018/06/14/isle-of-man-tt/img/IMG_9127_hu_ba332990bdba2dd.jpg" alt="Tower Bridge, London"/&gt;&lt;/p&gt;
&lt;p&gt;We were going to cross it on the bus earlier, but the driver informed us we could not because some sort of protest was
taking place on the bridge, blocking traffic. This is seen from the dock where the boat tours launched from.&lt;/p&gt;
&lt;h1 id="classic-london-black-cab"&gt;classic London black cab&lt;/h1&gt;
&lt;p&gt;&lt;img src="https://davepedu.com/photos/2018/06/14/isle-of-man-tt/img/IMG_9079_hu_466818bbcc87aa11.jpg" alt="Black cab"/&gt;&lt;/p&gt;
&lt;h1 id="london-victoria-station"&gt;London Victoria Station&lt;/h1&gt;
&lt;p&gt;&lt;img src="https://davepedu.com/photos/2018/06/14/isle-of-man-tt/img/2018-06-03_11.03.54_hu_cb051c1b070f4e41.jpg" alt="London Victoria Station"/&gt;&lt;/p&gt;
&lt;p&gt;Having spent the morning and early afternoon exploring London, the time came to catch my flight to the Isle of Man. I
took the Gatwick Express out of London back to Gatwick airport.&lt;/p&gt;
&lt;h1 id="race-day"&gt;race day&lt;/h1&gt;
&lt;p&gt;&lt;img src="https://davepedu.com/photos/2018/06/14/isle-of-man-tt/img/cover_hu_ae721cb4a8b2aaa.jpg" alt="A custom motorcycle parked on the streets of Douglas"/&gt;&lt;/p&gt;
&lt;p&gt;This isn&amp;rsquo;t a bike competing in the day&amp;rsquo;s races, but it was race day on the island! The number of motorcycles that
appeared on the island was impressive. Fields upon fields of parked bikes.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://davepedu.com/photos/2018/06/14/isle-of-man-tt/img/IMG_9302_hu_57b6a391818eeeff.jpg" alt="Fields of bikes"/&gt;&lt;/p&gt;
&lt;h1 id="douglas"&gt;Douglas&lt;/h1&gt;
&lt;p&gt;&lt;img src="https://davepedu.com/photos/2018/06/14/isle-of-man-tt/img/IMG_9340_hu_634549c4f4254645.jpg" alt="Zooming on the street"/&gt;&lt;/p&gt;
&lt;p&gt;The race was on! This is a racer speeding through Douglas, the largest town and capital of the Isle of Man. You can see
that they literally do just use the island&amp;rsquo;s public streets for the race course - and the public comes out to watch.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s difficult to describe how fast these racers move. It&amp;rsquo;s nothing like standing next to a highway - these bikes move
at more than double highway speed - on average! The record average speed for an entire lap is 136 MPH, held by Peter
Hickman. Of course, this average speed includes slower parts of the course. On simpler portions of the course, such as
straightaways, speeds over 200 MPH are frequently attained.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://davepedu.com/photos/2018/06/14/isle-of-man-tt/img/IMG_9330_hu_5a05708c4c5c6a82.jpg" alt="A racer speeding through Douglas"/&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="https://davepedu.com/photos/2018/06/14/isle-of-man-tt/img/IMG_9450_hu_dcea70ef66fcf905.jpg" alt="Cornering north of Douglas"/&gt;&lt;/p&gt;
&lt;h1 id="sidecar-race"&gt;sidecar race&lt;/h1&gt;
&lt;p&gt;&lt;img src="https://davepedu.com/photos/2018/06/14/isle-of-man-tt/img/IMG_9362_hu_c4a0c8396ef9b247.jpg" alt="A sidecar"/&gt;&lt;/p&gt;
&lt;p&gt;In addition to motorcycles, races with &amp;ldquo;sidecar&amp;rdquo; motorcycles are also run.&lt;/p&gt;
&lt;p&gt;Really, they aren&amp;rsquo;t motorcycles under the hood at all. It&amp;rsquo;s not a motorcycle with a sidecar added. It&amp;rsquo;s a completely
custom machine for this specific class of race.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;During the race week, races happen Monday / Wednesday / Friday, with Tuesday and Thursday being break/prep days. This
downtime in between race days gave me plenty of time to look at other sights on the Isle of Man.&lt;/p&gt;
&lt;h1 id="cat"&gt;cat&lt;/h1&gt;
&lt;p&gt;&lt;img src="https://davepedu.com/photos/2018/06/14/isle-of-man-tt/img/IMG_9393_hu_9ab2afb586b37ab4.jpg" alt="Cat"/&gt;&lt;/p&gt;
&lt;p&gt;Cat.&lt;/p&gt;
&lt;h1 id="douglas-promenade"&gt;Douglas Promenade&lt;/h1&gt;
&lt;p&gt;&lt;img src="https://davepedu.com/photos/2018/06/14/isle-of-man-tt/img/IMG_9381_hu_625e1c5d04b4ca2.jpg" alt="Douglas Promenade"/&gt;&lt;/p&gt;
&lt;p&gt;The island&amp;rsquo;s capital and largest city, Douglas, is situated on a small bay, and features a seafront walkway known as the
Promenade. During TT week, many events and shops set up here. The race doesn&amp;rsquo;t come though here, but this can be
considered the city&amp;rsquo;s downtown.&lt;/p&gt;
&lt;h1 id="tower-of-refuge"&gt;Tower of Refuge&lt;/h1&gt;
&lt;p&gt;&lt;img src="https://davepedu.com/photos/2018/06/14/isle-of-man-tt/img/IMG_9377_hu_67d6d6886d3b79b0.jpg" alt="Tower of Refuge, Douglas Bay"/&gt;&lt;/p&gt;
&lt;p&gt;The Tower of Refuge is a short distance out to sea in Douglas Bay, and can be seen from the Promenade. It was built as
shelter for shipwrecked sailors. The coastline along Douglas forms a nice bay, but this island, St Mary&amp;rsquo;s Isle aka
Conister Rock, is part of a submerged reef that posed significant risk to ships historically. So, in 1832, the Tower of
Refuge was built as a miniature castle as a shelter for shipwrecked sailors.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://davepedu.com/photos/2018/06/14/isle-of-man-tt/img/IMG_9397_hu_8887ae4e4cfefca3.jpg" alt="Isle of Man&amp;rsquo;s National symbol, a triskelion."/&gt;&lt;/p&gt;
&lt;p&gt;Bench along the Promenade, displaying the Isle of Man&amp;rsquo;s National symbol, a triskelion.&lt;/p&gt;
&lt;h1 id="suzuki-v-strom"&gt;Suzuki V-Strom&lt;/h1&gt;
&lt;p&gt;&lt;img src="https://davepedu.com/photos/2018/06/14/isle-of-man-tt/img/2018-06-04_17.38.14_hu_d29fa8782ed7ee84.jpg" alt="Suzuki V-Strom DL1000A"/&gt;&lt;/p&gt;
&lt;p&gt;Seen parked along the Promenade. This is the same model of bike I own back home. I don&amp;rsquo;t see many of them in the wild,
probably less than 10 instances since I&amp;rsquo;ve had it.&lt;/p&gt;
&lt;h1 id="town-of-peel-evening"&gt;Town of Peel, evening&lt;/h1&gt;
&lt;p&gt;&lt;img src="https://davepedu.com/photos/2018/06/14/isle-of-man-tt/img/IMG_9508_hu_534407259393e282.jpg" alt="Town of Peel"/&gt;&lt;/p&gt;
&lt;p&gt;I had rented a room in the town of Peel as mentioned earlier, so I returned here once it became evening. During TT week,
the island runs many and frequent bus routes, which made getting around the island really easy, and relatively
inexpensive. Peel is on the opposite side of the island compared to Douglas, but it wasn&amp;rsquo;t a very long bus ride - about
10 miles.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;m told that the word &amp;ldquo;peel&amp;rdquo; in the Manx language - a Gaelic Celtic language spoken by the island&amp;rsquo;s original
inhabitants - means &amp;ldquo;castle&amp;rdquo;, and as such, Peel&amp;rsquo;s bay and coastline is dominated by the ruins of a castle, to the right
side of the photo.&lt;/p&gt;
&lt;h1 id="same-spot-overlooking-peel-beach-next-morning"&gt;same spot overlooking Peel Beach, next morning&lt;/h1&gt;
&lt;p&gt;&lt;img src="https://davepedu.com/photos/2018/06/14/isle-of-man-tt/img/IMG_9525_hu_ef526e7c38e604c4.jpg" alt="Town of Peel"/&gt;&lt;/p&gt;
&lt;h1 id="peel-beach"&gt;Peel Beach&lt;/h1&gt;
&lt;p&gt;&lt;img src="https://davepedu.com/photos/2018/06/14/isle-of-man-tt/img/IMG_9547_hu_e5ad981391815d9a.jpg" alt="Eurasian Oystercatchers"/&gt;&lt;/p&gt;
&lt;p&gt;Eurasian Oystercatchers on Peel&amp;rsquo;s beach.&lt;/p&gt;
&lt;h1 id="peel-lighthouse"&gt;Peel Lighthouse&lt;/h1&gt;
&lt;p&gt;&lt;img src="https://davepedu.com/photos/2018/06/14/isle-of-man-tt/img/IMG_9552_hu_203e4ebc9b07efc2.jpg" alt=""/&gt;&lt;/p&gt;
&lt;p&gt;A small lighthouse on the breakwater that protects Peel&amp;rsquo;s marina.&lt;/p&gt;
&lt;h1 id="peel-castle"&gt;Peel Castle&lt;/h1&gt;
&lt;p&gt;&lt;img src="https://davepedu.com/photos/2018/06/14/isle-of-man-tt/img/IMG_9558_hu_9c0d836fb7069077.jpg" alt="Peel Castle"/&gt;&lt;/p&gt;
&lt;p&gt;Seen from Peel Beach. Nice sunlight this morning.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://davepedu.com/photos/2018/06/14/isle-of-man-tt/img/IMG_9556_hu_c933c4c8fd0b5953.jpg" alt="Peel Castle"/&gt;&lt;/p&gt;
&lt;p&gt;A wider shot.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://davepedu.com/photos/2018/06/14/isle-of-man-tt/img/IMG_9763_hu_18cdb9fae97346a6.jpg" alt="Peel Castle"/&gt;&lt;/p&gt;
&lt;p&gt;Looking into the castle&amp;rsquo;s courtyards from a nearby hill.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://davepedu.com/photos/2018/06/14/isle-of-man-tt/img/IMG_9690_hu_d2cdf0b06638f94f.jpg" alt="Peel Castle"/&gt;&lt;/p&gt;
&lt;p&gt;Inside the castle.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://davepedu.com/photos/2018/06/14/isle-of-man-tt/img/IMG_9729_hu_7d8994baa3afda5c.jpg" alt="Peel Castle"/&gt;&lt;/p&gt;
&lt;p&gt;Further inside the castle.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://davepedu.com/photos/2018/06/14/isle-of-man-tt/img/IMG_9704_hu_e9153de993fdc063.jpg" alt="Castle flag"/&gt;&lt;/p&gt;
&lt;p&gt;Another triskelion flying above the castle.&lt;/p&gt;
&lt;h1 id="coast-guard-station"&gt;Coast Guard station&lt;/h1&gt;
&lt;p&gt;&lt;img src="https://davepedu.com/photos/2018/06/14/isle-of-man-tt/img/IMG_9639_hu_52d2b72f40e97bd2.jpg" alt="Talus MB-H amphibious tractor"/&gt;&lt;/p&gt;
&lt;p&gt;The island&amp;rsquo;s Coast Guard operates a station on Peel beach. Here, they have a lifeboat to assist ships in distress. On a
moment&amp;rsquo;s notice, the boat is moved out of this enclosure and down a ramp into the sea. The tractor in the foreground is
a &lt;a href="https://en.wikipedia.org/wiki/Talus_MB-H_amphibious_tractor"&gt;Talus MB-H amphibious tractor&lt;/a&gt;.&lt;/p&gt;
&lt;h1 id="peel-from-above"&gt;Peel, from above&lt;/h1&gt;
&lt;p&gt;&lt;img src="https://davepedu.com/photos/2018/06/14/isle-of-man-tt/img/IMG_9804_hu_2d315534daf9919.jpg" alt="Peel, from above"/&gt;&lt;/p&gt;
&lt;p&gt;There is a large hill on the west side of Peel that gives a great vantage point to see the town from above.
Looking northeast.&lt;/p&gt;
&lt;h1 id="hiking-from-peel"&gt;hiking from Peel&lt;/h1&gt;
&lt;p&gt;&lt;img src="https://davepedu.com/photos/2018/06/14/isle-of-man-tt/img/IMG_9779_hu_563052232b26d114.jpg" alt="Coastal Path"/&gt;&lt;/p&gt;
&lt;p&gt;There is a hiking trail that encircles the island, known as the Coastal Path. I walked a portion of it departing from
Peel. A large percentage of the island&amp;rsquo;s coastline is impassable rocky cliffs like this, which makes the land of little
use beyond farming.&lt;/p&gt;
&lt;h1 id="livestock-gate"&gt;livestock gate&lt;/h1&gt;
&lt;p&gt;&lt;img src="https://davepedu.com/photos/2018/06/14/isle-of-man-tt/img/2018-06-05_13.13.14_hu_6fd681f94f27d169.jpg" alt="Livestock gate"/&gt;&lt;/p&gt;
&lt;p&gt;While the hiking trail is public, it passes through many different farms, each separated by short rock walls. These
peculiar gates are installed along the wall to allow hikers to pass though. The gate is designed with an unlatched and
movable swinging portion in the center. It&amp;rsquo;s shaped such that a livestock animals - sheep and goats were common - cannot
pass through the gate, but humans can.&lt;/p&gt;
&lt;h1 id="ramsey-another-race-day"&gt;Ramsey, another race day&lt;/h1&gt;
&lt;p&gt;&lt;img src="https://davepedu.com/photos/2018/06/14/isle-of-man-tt/img/IMG_9823_hu_19d4cf55500d25a2.jpg" alt="Cornering in downtown Ramsey"/&gt;&lt;/p&gt;
&lt;p&gt;The next day, I found my way to the town of Ramsey, near the north end of the island. My impression of Ramsey is that it
is more of a blue-collar working town, particularly around the fishing industry.&lt;/p&gt;
&lt;h1 id="conrods"&gt;Conrod&amp;rsquo;s&lt;/h1&gt;
&lt;p&gt;&lt;img src="https://davepedu.com/photos/2018/06/14/isle-of-man-tt/img/IMG_0026_hu_c8984819856f2ad1.jpg" alt="Conrod&amp;rsquo;s coffee shop"/&gt;&lt;/p&gt;
&lt;p&gt;I saw this cute and on-theme coffee shop, sign complete with engine parts and another triskelion.&lt;/p&gt;
&lt;h1 id="action"&gt;action!&lt;/h1&gt;
&lt;p&gt;&lt;img src="https://davepedu.com/photos/2018/06/14/isle-of-man-tt/img/IMG_9996_hu_c699e1161eb03901.jpg" alt="Racing underway"/&gt;&lt;/p&gt;
&lt;p&gt;Ramsey was a great place to watch from, featuring several straightaways and hairpin corners.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://davepedu.com/photos/2018/06/14/isle-of-man-tt/img/IMG_9958_hu_dc92636222a678fe.jpg" alt="Racing underway"/&gt;&lt;/p&gt;
&lt;p&gt;One particular corner had a pub&amp;rsquo;s outdoor seating area situated in it. It was a great place to watch the bikers whiz by,
mere inches away, while you were being served food and drink.&lt;/p&gt;
&lt;h1 id="isle-of-man-motor-museum"&gt;Isle of Man Motor Museum&lt;/h1&gt;
&lt;p&gt;&lt;img src="https://davepedu.com/photos/2018/06/14/isle-of-man-tt/img/IMG_0192_hu_56b347f06904377.jpg" alt="Inside the Isle of Man Motor Museum"/&gt;&lt;/p&gt;
&lt;p&gt;Located relatively close to Ramsey is the &lt;a href="https://www.isleofmanmotormuseum.com/"&gt;Isle of Man Motor Museum&lt;/a&gt;. Its indoor
showroom features countless rare - but also some common - cars, trucks, motorcycles, military equipment, and other
vehicles. Active restoration work was going on within the museum&amp;rsquo;s workshops as well.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://davepedu.com/photos/2018/06/14/isle-of-man-tt/img/IMG_0157_hu_74eff7edc489206b.jpg" alt="Peel P50"/&gt;&lt;/p&gt;
&lt;p&gt;The Peel P50 - &lt;a href="https://www.youtube.com/watch?v=dJfSS0ZXYdo"&gt;as seen on Top Gear&lt;/a&gt; - claimed the title of being the
smallest production car in the world could also be seen here.&lt;/p&gt;
&lt;h1 id="under-the-queens-pier"&gt;under the Queen&amp;rsquo;s Pier&lt;/h1&gt;
&lt;p&gt;&lt;img src="https://davepedu.com/photos/2018/06/14/isle-of-man-tt/img/IMG_0209_hu_fc8d79ffc946a38.jpg" alt="Under the Queen&amp;rsquo;s Pier"/&gt;&lt;/p&gt;
&lt;p&gt;The Queen&amp;rsquo;s Pier in Ramsey was built in the 1880s as a place for ships to pick up and drop off passengers. It was closed
in the 1990s due to safety concerns and remained closed for about 25 years. At the time of my visit, restoration work
was just getting started, and the pier has since reopened.&lt;/p&gt;
&lt;p&gt;Towards the bottom of the photo, you can see how rusted out the metal supports are.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://davepedu.com/photos/2018/06/14/isle-of-man-tt/img/IMG_0250_hu_61e24d7f017c49ed.jpg" alt="Queen&amp;rsquo;s Pier"/&gt;&lt;/p&gt;
&lt;p&gt;Another view of the Queen&amp;rsquo;s Pier, from a distance so that we may see its length.&lt;/p&gt;
&lt;h1 id="electric-rail"&gt;electric rail&lt;/h1&gt;
&lt;p&gt;&lt;img src="https://davepedu.com/photos/2018/06/14/isle-of-man-tt/img/IMG_0244_hu_9134d2bc6eca73a.jpg" alt="Manx electric tramcar"/&gt;&lt;/p&gt;
&lt;p&gt;The island runs a historical electric railway, called the Manx Electric Railway. Again dating from the late 19th
century, the railway runs on 550 volts DC and features roughly 25 electric trams. It holds the title of being the the
oldest electric tram line in the world in the sense that most of its electric trams are original, some dating back over
a century.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://davepedu.com/photos/2018/06/14/isle-of-man-tt/img/IMG_0247_hu_ba16fce4959a3f46.jpg" alt="Tramcar insignia closeup"/&gt;&lt;/p&gt;
&lt;p&gt;A close-up of the hand-painted insignia on the side of the electric tram.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://davepedu.com/photos/2018/06/14/isle-of-man-tt/img/IMG_0261_hu_f9f8ae6f70ef2ea1.jpg" alt="Countryside farms"/&gt;&lt;/p&gt;
&lt;p&gt;The railway snakes along the eastern coast, providing excellent scenic views of some quieter parts of the island.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://davepedu.com/photos/2018/06/14/isle-of-man-tt/img/IMG_0273_hu_98e376eed1aa660a.jpg" alt="Countryside houses"/&gt;&lt;/p&gt;
&lt;h1 id="laxey-and-laxey-wheel"&gt;Laxey and Laxey Wheel&lt;/h1&gt;
&lt;p&gt;&lt;img src="https://davepedu.com/photos/2018/06/14/isle-of-man-tt/img/IMG_0421_hu_8d452e0394d3049f.jpg" alt="Laxey Wheel, up close"/&gt;&lt;/p&gt;
&lt;p&gt;The electric rail eventually brought me to the village of Laxey, where there is a small station and junction. From here,
one can take another train up Snaefell Mountain, the highest peak on the island, or view some attractions in Laxey.
Pictured above is Laxey Wheel, and enormous waterwheel. It holds the title of being the largest surviving original
working waterwheel in the world, built in 1854.&lt;/p&gt;
&lt;p&gt;Laxey is a historic mining town. In the 19th and early 20th century, large amounts of lead and zinc were mined here. The
ore was washed on various washing floors - basically wide open spaces with flowing water - that still exist today.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://davepedu.com/photos/2018/06/14/isle-of-man-tt/img/IMG_9982_hu_a7da617d1ab9d374.jpg" alt="Laxey Wheel, from afar"/&gt;&lt;/p&gt;
&lt;p&gt;Laxey Wheel served as a water pump to deliver water to these floors and other mining operations. The wheel itself is
about 72 feet in diameter, 6 feet wide, and spins at 3 rotations per minute.&lt;/p&gt;
&lt;p&gt;The wheel is now a National Heritage site.&lt;/p&gt;
&lt;h1 id="snaefell-mountain-summit"&gt;Snaefell Mountain Summit&lt;/h1&gt;
&lt;p&gt;&lt;img src="https://davepedu.com/photos/2018/06/14/isle-of-man-tt/img/IMG_0363_hu_c99d2734f3e0751c.jpg" alt="Snaefell Mountain Summit"/&gt;&lt;/p&gt;
&lt;p&gt;From Laxey, I took the electric rail up to the summit of Snaefell Mountain. There is a small restaurant at the top, but
it seemed to have been closed for some time. You could go inside in case of bad weather, but no food was available.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://davepedu.com/photos/2018/06/14/isle-of-man-tt/img/IMG_0352_hu_762879baa099ebff.jpg" alt="Snaefell Mountain Summit station"/&gt;&lt;/p&gt;
&lt;p&gt;The railway takes you right to the top. No hiking required!&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ll be honest - taking a century-old vehicle up a mountain did seem a little scary. The railway was steep and wound
around the contours of the mountain, making working brakes absolutely critical. Looking back after some research, the
railway does have an excellent safety record, which puts my mind at ease.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://davepedu.com/photos/2018/06/14/isle-of-man-tt/img/IMG_0378_hu_7b5c85ebd512c013.jpg" alt="Manx electric tramcar"/&gt;&lt;/p&gt;
&lt;p&gt;An electric tram, departing.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://davepedu.com/photos/2018/06/14/isle-of-man-tt/img/IMG_0351_hu_53f84b9ffbbe2787.jpg" alt="Snaefell summit views"/&gt;&lt;/p&gt;
&lt;p&gt;Scenic view from the summit.&lt;/p&gt;
&lt;h1 id="calf-of-man"&gt;Calf of Man&lt;/h1&gt;
&lt;p&gt;&lt;img src="https://davepedu.com/photos/2018/06/14/isle-of-man-tt/img/IMG_9817_hu_ae327d1eae77ff24.jpg" alt="Looking east from the island&amp;rsquo;s southern tip"/&gt;&lt;/p&gt;
&lt;p&gt;Later, I made my way down to the southernmost tip of the main island. Not far from shore is another island, known as the
Calf of Man. The name comes from the island&amp;rsquo;s shape; it roughly looks like a lamb calf - distinct legs, a body, and a
head section.&lt;/p&gt;
&lt;p&gt;The Calf of Man is uninhabited, but there are a few historic structures on it.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://davepedu.com/photos/2018/06/14/isle-of-man-tt/img/IMG_9815_hu_96808d8d44fb075b.jpg" alt="Sea wildlife"/&gt;&lt;/p&gt;
&lt;p&gt;Sea wildlife, near the Calf of Man, in an area known as Cow Harbour.&lt;/p&gt;
&lt;h1 id="hiking-to-port-of-erin"&gt;hiking to Port of Erin&lt;/h1&gt;
&lt;p&gt;&lt;img src="https://davepedu.com/photos/2018/06/14/isle-of-man-tt/img/IMG_9864_hu_e4ae1c0a39f3f13.jpg" alt="Hiking trails to Port of Erin"/&gt;&lt;/p&gt;
&lt;p&gt;From the shorelines near the Calf of Man, hiking trails depart allowing you to walk either towards the east side of the
island, or the west side. I chose the west side, taking a trail that would eventually arrive at the Port of Erin.&lt;/p&gt;
&lt;p&gt;Despite being only about 3 miles long, I found the hike somewhat difficult. As you can see in the image above, the
coastline the trail follows slopes steeply towards the ocean. This made walking difficult as your ankles constantly
wanted to roll sideways down the hillside.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://davepedu.com/photos/2018/06/14/isle-of-man-tt/img/IMG_9832_hu_e332176943a422fb.jpg" alt="Wildlife along the trail"/&gt;&lt;/p&gt;
&lt;p&gt;Wildlife along the trail.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://davepedu.com/photos/2018/06/14/isle-of-man-tt/img/IMG_9829_hu_955e86de5f3d416b.jpg" alt="Trail signage"/&gt;&lt;/p&gt;
&lt;p&gt;Trail signage. &amp;ldquo;Raad Ny Foillan&amp;rdquo; - Manx for &amp;ldquo;The Way of the Gull&amp;rdquo; is the same trail seen earlier near Peel that
encircles the island. I believe this sign is marking the fork in the trail that takes you either on the trails along the
west or east side of the island. The coastline of the island meanders enough that despite the entire island being only
about 30 miles long, the trail&amp;rsquo;s length is nearly 100 miles.&lt;/p&gt;
&lt;h1 id="milners-tower"&gt;Milner&amp;rsquo;s Tower&lt;/h1&gt;
&lt;p&gt;&lt;img src="https://davepedu.com/photos/2018/06/14/isle-of-man-tt/img/IMG_9869_hu_b3d4bdda2dfcf02f.jpg" alt="Milner&amp;rsquo;s Tower"/&gt;&lt;/p&gt;
&lt;p&gt;When approaching the Port of Erin, a picturesque stone tower can be seen on a hill overlooking the port&amp;rsquo;s bay. Milner&amp;rsquo;s
Tower - built atop Bradda Head - was built to honor a local businessman, William Milner, in the mid 19th century, for
his love and support for the town.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://davepedu.com/photos/2018/06/14/isle-of-man-tt/img/IMG_9882_hu_699550708b16ab81.jpg" alt="Milner&amp;rsquo;s Tower, closer view"/&gt;&lt;/p&gt;
&lt;p&gt;Closer view of Milner&amp;rsquo;s Tower tower. It&amp;rsquo;s tall towers and narrow footprint gives it an extremely
fairytale-like appearance.&lt;/p&gt;
&lt;h1 id="port-of-erin-beach"&gt;Port of Erin beach&lt;/h1&gt;
&lt;p&gt;&lt;img src="https://davepedu.com/photos/2018/06/14/isle-of-man-tt/img/IMG_9920_hu_412adcb631a03e9a.jpg" alt="Port of Erin beach"/&gt;&lt;/p&gt;
&lt;p&gt;Port of Erin is an adorable little town, seemingly a hot spot for beach-going tourists.&lt;/p&gt;
&lt;h1 id="isle-of-man-railway"&gt;Isle of Man Railway&lt;/h1&gt;
&lt;p&gt;&lt;img src="https://davepedu.com/photos/2018/06/14/isle-of-man-tt/img/IMG_9939_hu_8a345ad574bc505a.jpg" alt="Isle of Man Railway engine"/&gt;&lt;/p&gt;
&lt;p&gt;Separate from the electric railway in the north of the island, the southern half of the island is serviced by a historic
steam railway. Today, the trains run from Port of Erin to Douglas. I took this train from Port of Erin, stopped in
Castletown along the way, and eventually made it back to Douglas where I called it a day.&lt;/p&gt;
&lt;h1 id="final-race-day"&gt;final race day&lt;/h1&gt;
&lt;p&gt;&lt;img src="https://davepedu.com/photos/2018/06/14/isle-of-man-tt/img/IMG_9691_hu_6d94becbb9bb7db6.jpg" alt="Race bike with tire heaters"/&gt;&lt;/p&gt;
&lt;p&gt;The next day was the final day of racing. On the final day, the main event - The Senior TT - is run. This is the event
the island is known for.&lt;/p&gt;
&lt;p&gt;Pictured here is a bike and crew participating in the race. Just like in other motorsports, the crews keep heaters on
the bike&amp;rsquo;s tires right up until go time, as seen here. Hot tires grip the roads better, allowing racers to corner faster
and make better time.&lt;/p&gt;
&lt;h1 id="the-end"&gt;the end&lt;/h1&gt;
&lt;p&gt;This concludes my time on the Isle of Man. I stayed on the island for almost two weeks and had time to see many of the
historic and cultural sites on the island, in addition to the motorsport races. The Tourist Trophy races have been run
for over a century now, deeply intertwining the two. Learning about the island&amp;rsquo;s history and culture was horizon
broadening. As someone who grew up in a small town, the layout and scale of the Isle of Man made me feel right at home.&lt;/p&gt;
&lt;p&gt;I was very fortunate to have been visiting at a time when some of the best weather in years was rolling though. The
British Isles are famously rainy and dreary, and the Isle of Man is no exception. However, we had clear weather nearly
every single day during my stay. I recall about an hour of rain during one cloudy day, but no more.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://davepedu.com/photos/2018/06/14/isle-of-man-tt/img/IMG_0136_hu_1f1e83a5e515051b.jpg" alt="Me, standing by The Winkie"/&gt;&lt;/p&gt;
&lt;p&gt;Here, on the northernmost point of the island, stands a lighthouse called The Winkie. That&amp;rsquo;s me standing on it!&lt;/p&gt;
&lt;h1 id="the-author"&gt;the author&lt;/h1&gt;
&lt;p&gt;&lt;img src="https://davepedu.com/photos/2018/06/14/isle-of-man-tt/img/IMG_0161_hu_8d1262801f0e982e.jpg" alt="The author of this blog"/&gt;&lt;/p&gt;
&lt;p&gt;The author of this blog, photographed at Jurby Junk, a sprawling &amp;ldquo;bric-a-brac&amp;rdquo; store.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;I hope you enjoyed reading about my trip. I sure enjoyed the trip itself, and writing this post. The race was amazing to
see, and staying with the locals rather than sleeping in a hotel made the trip a truly special experience I&amp;rsquo;ll
never forget.&lt;/p&gt;
&lt;p&gt;If you enjoyed my story and photos, or are planning a trip of your own, &lt;a href="https://davepedu.com/contact/"&gt;let me know&lt;/a&gt;!&lt;/p&gt;</content></item><item><title>shifting sands</title><link>https://davepedu.com/blog/2018/04/24/shifting-sands/</link><pubDate>Tue, 24 Apr 2018 00:00:00 +0000</pubDate><author>dave@davepedu.com (dave)</author><guid>https://davepedu.com/blog/2018/04/24/shifting-sands/</guid><description>&lt;p&gt;I&amp;rsquo;ve ran this blog in its current form for about 5 years now. It runs on Drupal 7, which while still current and
supported, dated in quite a few ways. Drupal has since released Drupal 8, which includes a massive overhaul to the
internals and theme layer. I&amp;rsquo;m undecided as to if I want to upgrade the site to Drupal 8, as it would effectively
require a re-structuring and re-write.&lt;/p&gt;</description><content>&lt;p&gt;I&amp;rsquo;ve ran this blog in its current form for about 5 years now. It runs on Drupal 7, which while still current and
supported, dated in quite a few ways. Drupal has since released Drupal 8, which includes a massive overhaul to the
internals and theme layer. I&amp;rsquo;m undecided as to if I want to upgrade the site to Drupal 8, as it would effectively
require a re-structuring and re-write.&lt;/p&gt;
&lt;p&gt;The various &lt;a href="https://research.checkpoint.com/uncovering-drupalgeddon-2/"&gt;&amp;ldquo;Drupalgeddon&amp;rdquo;&lt;/a&gt; vulnerabilities have made it
clear Drupal has holes and people are looking for them.&lt;/p&gt;
&lt;p&gt;I guess we&amp;rsquo;ll see what happens.&lt;/p&gt;</content></item><item><title>old code</title><link>https://davepedu.com/blog/2017/10/26/old-code/</link><pubDate>Thu, 26 Oct 2017 00:00:00 +0000</pubDate><author>dave@davepedu.com (dave)</author><guid>https://davepedu.com/blog/2017/10/26/old-code/</guid><description>&lt;p&gt;I recently came across the source code of a browser-based game I wrote around 2005, when I would have been 15 years old.
While the original database structure was unfortunately lost, I managed to piece together a working version by looking
at existing queries in the source code. With this, I was able to get the game running again! In this post, we&amp;rsquo;ll look at
what a naive PHP developer can create - and the mistakes they make.&lt;/p&gt;</description><content>&lt;p&gt;I recently came across the source code of a browser-based game I wrote around 2005, when I would have been 15 years old.
While the original database structure was unfortunately lost, I managed to piece together a working version by looking
at existing queries in the source code. With this, I was able to get the game running again! In this post, we&amp;rsquo;ll look at
what a naive PHP developer can create - and the mistakes they make.&lt;/p&gt;
&lt;h1 id="why-the-web"&gt;why the web&lt;/h1&gt;
&lt;p&gt;I can&amp;rsquo;t remember the original reason why I wanted to make a website, or how I got into web programming at all. I recall
taking an HTML class in high school, but by that time, I was already proficient in HTML and even writing PHP, CSS, and
some JavaScript.&lt;/p&gt;
&lt;p&gt;I was able to retrieve, via archive.org and other methods, some old examples of web sites I made around this time. And,
judging by them, I apparently wanted to make websites about forts in the woods that my friends and I built when we were
kids. No pictures, because digital cameras were uncommon and expensive back then, but some had MS Paint style maps drawn
of what we&amp;rsquo;ve built and where it is in the wilderness.&lt;/p&gt;
&lt;p&gt;I do recall another force that brought me into PHP and web programming: online MMORPGs. Like many young teens at the
time, I was addicted to the online game &lt;a href="https://en.wikipedia.org/wiki/RuneScape"&gt;RuneScape&lt;/a&gt;. And I also recall setting
up forums for the group of players I hung out with online - taking an open-source PHP forum -
&lt;a href="https://www.phpbb.com/"&gt;phpBB&lt;/a&gt; - and installing it on a free php/mysql web host. I recall installing mods - source code
mods to add features - into the forum software. This wasn&amp;rsquo;t automated with plugins like modern software. The &amp;ldquo;mods&amp;rdquo; were
source code patches. Often, if you had multiple mods, the diffs would conflict and you&amp;rsquo;d have to resolve differences
manually. I think this is where I first started looking at PHP.&lt;/p&gt;
&lt;p&gt;Beyond that, it was &amp;ldquo;cool&amp;rdquo; at the time on online forums to have a dynamic signature. Just like a signature in an email
client, a signature is a message that gets attached to the bottom of every post you make. You could embed images, so it
was possible to hotlink to an image that gets generated on the fly.&lt;/p&gt;
&lt;p&gt;But back to RuneScape forums. The most common kind of dynamic signature was one that shows the level of your character
in the video game. Players put work into games and feel proud of the achievements they make, and this is especially true
about RuneScape as it is notorious for its grueling time commitments. It is not surprising that such a thing became a
popular trend. Players want to show off their work, and a flashy image that does so under every post they make is a
great way to do it.&lt;/p&gt;
&lt;p&gt;Young me thought that was cool too, and young me thought it was even cooler if I could have a completely unique dynamic
signature, unlike anyone else&amp;rsquo;s. It would be showing off my programming achievement in addition to whatever the
signature itself displayed.&lt;/p&gt;
&lt;p&gt;And I did. And it worked. And I learned about things like caching, even though I didn&amp;rsquo;t even know the word for it at the
time. I found through trial and error that making my dynamic signature script call cURL and fetch information from
RuneScape&amp;rsquo;s website was too slow. Even back then, they would automatically throttle and block scrapers like mine. And so
caching became a necessity.&lt;/p&gt;
&lt;p&gt;The addiction grew from there. Both the game addiction and the programming addiction. The rest is history, as they say.&lt;/p&gt;
&lt;p&gt;Beyond RuneScape, I was also a fan of top-down monster-fighting RPGs such as the Dragon Quest/Warrior series. I recall
making other games in the past with ready-made game making software such as
&lt;a href="https://palmdb.net/app/kyles-quest-1"&gt;Kyle&amp;rsquo;s Quest&lt;/a&gt;, but never anything involving code or starting from scratch.
RuneScape&amp;rsquo;s 3D graphics were far too much for me to match, so I apparently chose a 2D top-down style comparable to these
other games.&lt;/p&gt;
&lt;h1 id="the-game"&gt;the game&lt;/h1&gt;
&lt;p&gt;&lt;img src="https://davepedu.com/blog/2017/10/26/old-code/img/screenshot_hu_9800480cec0ee418.png" alt="Screenshot of the game&amp;rsquo;s main screen"/&gt;&lt;/p&gt;
&lt;p&gt;The UI is simple - buttons to move in 4 directions. A button to wait which refreshes the page. Page loads were a little
slower in the 2000s, so I had a walking queue that used a little JavaScript to send many actions at once. I guess I
wasn&amp;rsquo;t the greatest speller then - &amp;ldquo;que&amp;rdquo; not &amp;ldquo;queue&amp;rdquo;, heh. On the battle screen, options to attack and run. That&amp;rsquo;s it!&lt;/p&gt;
&lt;p&gt;The game is technically an MMORPG: you see other players moving around the map with their name displayed over their
heads. There are level leader boards to drive competition between players. Being a web game, being turn-based made the
most sense. One page load equals one turn. However, the game had so few actual features - no chat, no inventory - it&amp;rsquo;s
hard to put it in a category at all, besides that of being just a fun programming challenge for young me at the time.&lt;/p&gt;
&lt;p&gt;I was never much of an artist, so the graphics were stolen from other games. I believe the trees and buildings and
general scenery came from the Nintendo Entertainment System version of EarthBound. The player character itself came from
&lt;a href="https://www.macintoshrepository.org/3533-a-day-at-work"&gt;A Day At Work&lt;/a&gt; for Mac OS. Other graphics, such as enemy
monsters, were arbitrary images, such as the monster.com monster. The website itself that housed the game was from a now
long defunct free template website.&lt;/p&gt;
&lt;p&gt;The gameplay loop was simple. The starting area was safe. Go to a dangerous area by going the only way you can go, into
the house and out the back door. Wander around until you encounter a monster. Fight it, die, and respawn in the starting
area. Or win, and gain experience points. Wander and fight again, or go back to the starting area to heal. That&amp;rsquo;s it!&lt;/p&gt;
&lt;h1 id="a-closer-look"&gt;a closer look&lt;/h1&gt;
&lt;p&gt;The game is overly simple, I have no disillusion about that. It is, however, technically a game engine. Content is
decoupled from the engine. I can&amp;rsquo;t recall if I understood this concept at the time. Probably not. Or perhaps, though
experimenting with RuneScape private servers, I had observed such a decoupling of content and engine and internalized it
without even knowing the name of the technique. I&amp;rsquo;ll never know.&lt;/p&gt;
&lt;p&gt;Just because it&amp;rsquo;s a game engine, doesn&amp;rsquo;t mean it&amp;rsquo;s a good game engine. I&amp;rsquo;ll talk about the funny parts first.&lt;/p&gt;
&lt;p&gt;Being a mid 2000s PHP app written by a teenager, it is obviously full of security issues and other uncleanliness that
would not surprise an experienced programmer.&lt;/p&gt;
&lt;p&gt;Apparently I didn&amp;rsquo;t know about PHP sessions data yet, because I used raw cookies to handle user login. &lt;a href="https://git.davepedu.com/dave/moprpg/src/commit/bc582502a3e375ef14447e075a4847df66a7ad7b/login.php#L14"&gt;And by raw, I
mean &lt;strong&gt;raw&lt;/strong&gt;&lt;/a&gt;:&lt;/p&gt;
&lt;pre class="line-numbers"&gt;&lt;code class="language-php"&gt;if($_POST['u']!="") {
$u=addslashes($_POST['u']);
$p=addslashes($_POST['p']);
if($u!="" &amp;&amp; $p!="") {
$sql="SELECT * FROM mrpg_users WHERE name='$u'";
$result = mysql_query($sql);
while($row = mysql_fetch_array($result)) {
if($row['password']==$p) {
$message="You have been logged in. Proceed to &lt;a href='game.php'&gt;the Game.&lt;/a&gt;&lt;br&gt;";
setcookie("mrpg-ethbnd", strtolower($u . "@seperator@" . $p), time()+525600);
} else {
$message="Invalid username or password.";
}
}
}
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The cookie name is hard-coded on line 10. It&amp;rsquo;s also hard-coded in every file. I knew about includes, there was one a few
lines up in this snippet&amp;rsquo;s context, but I will never know why I didn&amp;rsquo;t use one for something like this.&lt;/p&gt;
&lt;p&gt;But that&amp;rsquo;s only the beginning. Perhaps you noticed what the cookie is set to on line 10 as well. It&amp;rsquo;s the literal
username and password separated by a hard-coded string, &lt;code&gt;@seperator@&lt;/code&gt;. Very strange, and also reveals that raw passwords
were being stored in plain text. Terrible practice, but it did work. At least I understood the separator token had to be
something players were unlikely to type.&lt;/p&gt;
&lt;p&gt;I cannot recall why I chose &lt;code&gt;525600&lt;/code&gt; seconds as the cookie expiration time, either. 525,600 is the number of minutes in
a 365 day year, so I think a reasonable guess is that I wanted a year and thought the duration was in minutes. But as
written, the cookie expiration ends up being slightly longer than 6 days.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://git.davepedu.com/dave/moprpg/src/commit/bc582502a3e375ef14447e075a4847df66a7ad7b/confirm.php#L7"&gt;How this cookie was read isn&amp;rsquo;t much better&lt;/a&gt;.
In every game file, the same snippet was repeated again:&lt;/p&gt;
&lt;pre class="line-numbers"&gt;&lt;code class="language-php"&gt;$credsYo=$_COOKIE['mrpg-ethbnd'];
$credsYo=explode("@seperator@", $credsYo);
$u=strtolower($credsYo[0]);
$p=strtolower($credsYo[1]);
if($u=="") {
header("Location: index.php?login");
}
$result = mysql_query("SELECT * FROM mrpg_users WHERE name='$u'");
while($row = mysql_fetch_array($result)) {
if($row['password']!=$p) {
header("Location: index.php");
exit;
}
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;So many problems in so little code. The same hard-coded cookie name on line 1. With the same hard-coded separator token
on line 2. We only support lowercase passwords, apparently, because of the &lt;code&gt;strtolower()&lt;/code&gt; on line 5. Attempting to read
index 1 from the cookie credentials probably causes an error if the cookie isn&amp;rsquo;t set at all. It looks like we redirect
the browser on line 8 with the &lt;code&gt;header()&lt;/code&gt; call, but the script keeps running because there is no &lt;code&gt;exit;&lt;/code&gt;. If there&amp;rsquo;s no
cookie, we look up a blank user with a blank password on line 11. We check the plaintext password vs the database
plaintext password on line 13.&lt;/p&gt;
&lt;p&gt;But if all of this passes, we have actually verified which user is logged in. Barely.&lt;/p&gt;
&lt;p&gt;This is probably also vulnerable to SQL injection via the username as there is no attempt made to sanitize &lt;code&gt;$u&lt;/code&gt; the
username before inserting it directly into a SQL query. This vulnerable snippet
&lt;a href="https://git.davepedu.com/dave/moprpg/src/commit/bc582502a3e375ef14447e075a4847df66a7ad7b/lvlup.php#L16"&gt;appears everywhere&lt;/a&gt;!&lt;/p&gt;
&lt;p&gt;Many other places in the code rely on &lt;code&gt;addslashes()&lt;/code&gt; for SQL injection protection. While somewhat sufficient for plain
ASCII, it is likely this does not block SQL injection as this sanitation method isn&amp;rsquo;t fully sufficient. It can fail due
to text encoding misalignment and is ignorant of the SQL server&amp;rsquo;s supported escaping modes.&lt;/p&gt;
&lt;p&gt;These are the most egregious problems. Other lesser issues are more naiveties than actual problems&lt;/p&gt;
&lt;p&gt;The way the &amp;ldquo;walkable&amp;rdquo; status of the map is done
&lt;a href="https://git.davepedu.com/dave/moprpg/src/commit/bc582502a3e375ef14447e075a4847df66a7ad7b/walkable.php"&gt;is amusing&lt;/a&gt;:&lt;/p&gt;
&lt;pre class="line-numbers"&gt;&lt;code class="language-php"&gt;$walkable[56]=array(0,0,0,0,0,0,1,0,0,0,0,0,0,0,0);
$walkable[57]=array(0,0,0,0,1,1,1,1,1,0,0,0,0,0,0);
$walkable[58]=array(0,0,0,1,1,1,1,1,1,1,0,0,0,0,0);
$walkable[59]=array(0,0,1,1,1,1,1,1,1,1,1,1,0,0,0);
$walkable[60]=array(0,0,0,1,1,1,1,1,1,1,1,1,0,0,0);
$walkable[61]=array(0,1,1,1,1,1,1,1,1,1,1,1,1,0,0);
$walkable[62]=array(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0);&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This is an excerpt, but the above linked file has much more content just like this. A wall of textg. And what this wall
is, is a giant array of &lt;code&gt;1s&lt;/code&gt; and &lt;code&gt;0s&lt;/code&gt; that specify whether each coordinate in the game can be walked into. The array&amp;rsquo;s
indexes are the world map&amp;rsquo;s coordinates.&lt;/p&gt;
&lt;p&gt;This isn&amp;rsquo;t &lt;em&gt;too&lt;/em&gt; bad, actually. Probably fairly efficient, if it were to grow. And it&amp;rsquo;s simple.&lt;/p&gt;
&lt;p&gt;However, one of my pet peeves shows up in this file too:&lt;/p&gt;
&lt;pre class="line-numbers"&gt;&lt;code class="language-php"&gt;if($walkable[$y][$x]==1) {
return true;
} else {
return false;
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This should be simply &lt;code&gt;return $walkable[$y][$x]==1;&lt;/code&gt;! No need to if and else! Just return the boolean! Argh!&lt;/p&gt;
&lt;p&gt;When rendering the map, the game needs to fetch a list of the other players so it may know where to draw them on the
map. The code that does this
&lt;a href="https://git.davepedu.com/dave/moprpg/src/commit/bc582502a3e375ef14447e075a4847df66a7ad7b/coordim.php#L74"&gt;simply fetches all users&lt;/a&gt;!&lt;/p&gt;
&lt;pre class="line-numbers"&gt;&lt;code class="language-php"&gt;$result = mysql_query("SELECT name,x,y,lastact,dir FROM mrpg_users");&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;If this game ever saw a serious amount of players - which it did not - this would be slow as many many players images
would need to be rendered, even those invisible off-screen. And as more players joined, the map would render slower and
slower. A location filter should be used here, that excludes players too far away to be worth rendering.&lt;/p&gt;
&lt;p&gt;The map render script was otherwise somewhat slick. It utilized PHP&amp;rsquo;s &lt;a href="https://www.php.net/manual/en/book.image.php"&gt;GD&lt;/a&gt;
library, which is a set of APIs for manipulating images. You can load images, combine them, write text, crop, scale,
etc. Effectively, my game&amp;rsquo;s map rendering process is that it queried the DB for players, calculated the map pixel
position based on the current player, and drew each player on the map. Pretty simple.&lt;/p&gt;
&lt;p&gt;Other screens, such as battling monsters, had separate render scripts that retrieved and displayed information in the
same manner. Image-based instead of web and text based is an odd choice, but I guess my early experience with dynamic
forum signatures had me liking the GD library.&lt;/p&gt;
&lt;p&gt;Lastly, digging through the source code also revealed that each character had a
&lt;a href="https://git.davepedu.com/dave/moprpg/src/commit/bc582502a3e375ef14447e075a4847df66a7ad7b/includes/statsimage.php#L23"&gt;wide variety of different properties&lt;/a&gt;
&amp;hellip;most of which never actually got used for anything:&lt;/p&gt;
&lt;pre class="line-numbers"&gt;&lt;code class="language-php"&gt;imageString($im, 4, 50, 100, "PP: " . $row['pp'], $white);
imageString($im, 4, 50, 140, "Offense: " . $row['offense'], $white);
imageString($im, 4, 50, 180, "Defense: " . $row['defense'], $white);
imageString($im, 4, 50, 220, "Fight: " . $row['fight'], $white);
imageString($im, 4, 340, 100, "Speed: " . $row['speed'], $white);
imageString($im, 4, 340, 140, "Wisdom: " . $row['wisdom'], $white);
imageString($im, 4, 340, 180, "Strength: " . $row['strength'], $white);
imageString($im, 4, 340, 220, "Force: " . $row['force'], $white);
imageString($im, 5, 200, 260, "Level: " . level(2, $exp['level']), $white);
imageString($im, 5, 200, 275, "EXP: " . $exp['level'], $white);
imageString($im, 5, 190, 10, "HP: " . $row['hp'] . "/" . $row['maxhp'], $green);&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;HP, EXP, Offense and Strength were used, but the others did nothing. All of them
&lt;a href="https://git.davepedu.com/dave/moprpg/src/commit/bc582502a3e375ef14447e075a4847df66a7ad7b/lvlup.php#L64"&gt;increase when leveling up&lt;/a&gt;,
but still are never read.&lt;/p&gt;
&lt;h1 id="epilogue"&gt;epilogue&lt;/h1&gt;
&lt;p&gt;I hope I did an understandable job of explaining how the internals of my naive game work, and that the problems called
out that I wrote into it aren&amp;rsquo;t too offensive - remember, I was 15!&lt;/p&gt;
&lt;p&gt;I wrote a Docker Compose file should you want to run a copy of the game. In addition to the code vulnerabilities - which
I will not be fixing because they add charm! - the Docker images used are fairly outdated as well. So, it would be a bad
idea to put this on the public internet. But if you want to play, it&amp;rsquo;s available.&lt;/p&gt;
&lt;p&gt;Source code is available on my Git instance:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://git.davepedu.com/dave/moprpg"&gt;https://git.davepedu.com/dave/moprpg&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Think you achieved a high score in my game? &lt;a href="https://davepedu.com/contact/"&gt;Let me know directly&lt;/a&gt;.&lt;/p&gt;</content></item><item><title>single system image linux clustering</title><link>https://davepedu.com/blog/2017/08/10/single-system-image-linux-clustering/</link><pubDate>Thu, 10 Aug 2017 00:00:00 +0000</pubDate><author>dave@davepedu.com (dave)</author><guid>https://davepedu.com/blog/2017/08/10/single-system-image-linux-clustering/</guid><description>&lt;p&gt;Recently, I came across the concept of SSI - &amp;ldquo;Single System Image&amp;rdquo; - Linux clustering. In short, Single System Image is
an approach to clustering where many generic Linux computers, running a modified kernel, present themselves as one
larger computer with access to the combined sum of all the underlying systems&amp;rsquo; RAM, CPU cores, and other resources.&lt;/p&gt;
&lt;p&gt;It was a useful technique because it decoupled your workloads from your hardware, which is the same goal as today&amp;rsquo;s
cloud computing platforms. Your hardware can be generic, you just need enough total silicon for whatever your workload
is, and this technology makes it all work together.&lt;/p&gt;</description><content>&lt;p&gt;Recently, I came across the concept of SSI - &amp;ldquo;Single System Image&amp;rdquo; - Linux clustering. In short, Single System Image is
an approach to clustering where many generic Linux computers, running a modified kernel, present themselves as one
larger computer with access to the combined sum of all the underlying systems&amp;rsquo; RAM, CPU cores, and other resources.&lt;/p&gt;
&lt;p&gt;It was a useful technique because it decoupled your workloads from your hardware, which is the same goal as today&amp;rsquo;s
cloud computing platforms. Your hardware can be generic, you just need enough total silicon for whatever your workload
is, and this technology makes it all work together.&lt;/p&gt;
&lt;p&gt;This approach, along with some other nifty Linux boot tricks, allows for some interesting ways to scale. These ideas are
mostly superseded today by modern cloud computing or solutions like Kubernetes, but I think they are interesting enough
to be worth discussing.&lt;/p&gt;
&lt;p&gt;What I will attempt to describe in this article is a relatively unknown but easy to scale Linux clustering solution,
some interesting ways to run it, and use cases for the technology. Understanding past job scheduling technologies like
these make it easier to understand why modern cloud computing is designed how it is.&lt;/p&gt;
&lt;h1 id="kerrighed"&gt;Kerrighed&lt;/h1&gt;
&lt;p&gt;&lt;a href="https://web.archive.org/web/20170610085431/http://kerrighed.org/wiki/index.php/Main_Page"&gt;Kerrighed&lt;/a&gt; is one notable
open-source example of an SSI implementation. In order to discuss Kerrighed, we need to discuss SMP - Symmetric
Multiprocessing - first.&lt;/p&gt;
&lt;p&gt;In the context of Linux, SMP - Symmetric Multiprocessing - is the technology through which Linux gains its ability to
run on systems with multiple CPU cores or discrete processors, treating them as equals to share workload, memory, and
I/O devices. In other words: when writing a program to run on Linux, you generally need not worry about how many or what
kind of CPU cores the system running your program will have and how you will distribute your program&amp;rsquo;s workload across
them. This is because Linux is in charge of scheduling processes or threads - that is, assigning them - to CPUs, and
moving them to another CPU later if need be. Additionally, the word &amp;ldquo;symmetric&amp;rdquo; in SMP comes from the fact that no one
CPU is the &amp;ldquo;leader&amp;rdquo;; rather they all work together in a cooperative way. This is SMP.&lt;/p&gt;
&lt;p&gt;While Linux&amp;rsquo;s included SMP implementation runs on just a single system, Kerrighed ships as modifications to the Linux
kernel that, when applied, implements SMP across many systems instead of one. Kerrighed is integrated in such a way that
Linux tools previously meant to inspect just your local system - such as &lt;code&gt;ps&lt;/code&gt; to list processes - now operate across the
entire cluster. That is, &lt;code&gt;ps&lt;/code&gt; would show you all processes running across all nodes in the Kerrighed cluster.&lt;/p&gt;
&lt;p&gt;It goes deeper than that, too. A feature of SMP is that it will move processes to different cores depending on where
resources are available. Kerrighed does this too, but the moves can be to a completely different physical system.&lt;/p&gt;
&lt;p&gt;Because Kerrighed is implemented at the kernel level, this is completely transparent to the process being scheduled or
moved. This is how Kerrighed achieves binary compatibility with existing Linux programs. This is Kerrighed&amp;rsquo;s killer
feature - existing programs need no modification to take advantage of a Kerrighed cluster.&lt;/p&gt;
&lt;h1 id="diskless-boot"&gt;Diskless boot&lt;/h1&gt;
&lt;p&gt;Having a cluster of computers is great, but the challenge becomes managing the cluster. This section will discuss a
different kind of &amp;ldquo;single image&amp;rdquo; - booting a fleet of diskless systems from a single operating system image, with the
disk image being accessed as-needed over the network.&lt;/p&gt;
&lt;p&gt;While diskless booting is not inherently related to SSI clustering, it complements the model nicely by making it easy to
provision large numbers of identical nodes.&lt;/p&gt;
&lt;p&gt;I first heard about this technique in the following tutorial articles:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://web.archive.org/web/20180326020313/http://shitwefoundout.com/wiki/Diskless_ubuntu"&gt;https://web.archive.org/web/20180326020313/http://shitwefoundout.com/wiki/Diskless_ubuntu&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://adminotes.blogspot.com/2012/05/how-to-setup-diskless-ubuntu-1204-with.html"&gt;https://adminotes.blogspot.com/2012/05/how-to-setup-diskless-ubuntu-1204-with.html&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;A quick explanation of what these articles describe: all of the operating system&amp;rsquo;s root filesystem is shared via an NFS
server. The network that these machines run on is configured with TFTP, a technology for booting over the network. When
a host boots, it contacts the TFTP server and downloads initial configuration and a Linux kernel. These configs instruct
the host to mount the NFS share, and to boot the operating system from the NFS share.&lt;/p&gt;
&lt;p&gt;What is notable about the above approaches is how they handle writes to the filesystem. We can&amp;rsquo;t write to the NFS share
under this setup because it is shared across however many servers are in the cluster. Instead, a special filesystem
called &lt;a href="https://en.wikipedia.org/wiki/Aufs"&gt;AUFS&lt;/a&gt; comes into play. AUFS is a &amp;ldquo;layered&amp;rdquo; filesystem, in that it allows you
to combine multiple &amp;ldquo;layers&amp;rdquo; into one coherent filesystem.&lt;/p&gt;
&lt;p&gt;It is worth noting that AUFS inspired the more modern &lt;a href="https://docs.kernel.org/filesystems/overlayfs.html"&gt;OverlayFS&lt;/a&gt;,
which is probably a better choice today.&lt;/p&gt;
&lt;p&gt;In this case, the bottom-most, immutable layer is our NFS share. On top of that, is an in-memory tmpfs filesystem. AUFS
is instructed that writes should go to the tmpfs, and reads come from the NFS share. Of course, we want to be able to
read back things that we wrote, and that is exactly how AUFS works: since the tmpfs is the &amp;ldquo;top&amp;rdquo; layer, a file present
on it will be read instead of a file with an identical path on the &amp;ldquo;bottom&amp;rdquo; layer. This allows our network-booted
operating system to write whatever it needs without modifying the NFS share and affecting other hosts.&lt;/p&gt;
&lt;p&gt;Of course, memory is a sparse and valuable system resource, and consuming it for filesystem storage is a bit of a waste.
AUFS does not &lt;em&gt;need&lt;/em&gt; to use tmpfs; formatting an actual disk to a typical format like ext4 or xfs and using it as the
&amp;ldquo;top&amp;rdquo; layer is an option too.&lt;/p&gt;
&lt;h1 id="combining-it-all"&gt;Combining it all&lt;/h1&gt;
&lt;p&gt;Now that we understand our components, it should be clear how we will create our SSI cluster:&lt;/p&gt;
&lt;p&gt;Servers can be added to the cluster simply by plugging them into the network, enabling TFTP netboot, and turning them
on. Each system will boot into our pre-configured system image and join the Kerrighed cluster. Kerrighed, by doing its
task of emulating SMP, will balance processes onto newly joined servers and make their resources available to the
workload running on the cluster.&lt;/p&gt;
&lt;p&gt;The cluster is simple to manage: add machines simply by plugging them in and turning them on. Remove machines by
shutting them down and doing the opposite. Update machines by configuring TFTP to point to a different NFS share with an
updated system image and reboot each host.&lt;/p&gt;
&lt;p&gt;Additional components may be needed for the cluster to be useful - such as mounting a read/write NFS share for workload
data - but this covers the basics.&lt;/p&gt;
&lt;h1 id="use-cases"&gt;Use cases&lt;/h1&gt;
&lt;p&gt;Single System Image clustering is most useful for workloads that benefit from large amounts of parallel compute or
memory but are not designed to run across multiple machines. Because SSI systems aggregate memory across nodes, the
cluster can appear to applications as a system with a much larger pool of RAM. This can be useful for simulations, data
processing tasks, or other applications that need large datasets in memory. This overlaps with High Performance
Computing (HPC), which are generally scientific and engineering workloads. These are a natural fit for SSI systems as
many of these programs are already written to take advantage of multiple CPU cores, but not necessarily multiple
machines. An SSI cluster can allow these programs to scale beyond the limits of a single system by providing access to
additional CPU cores across the cluster without requiring the software to be rewritten.&lt;/p&gt;
&lt;p&gt;SSI clusters can also provide a degree of resilience. If a node becomes overloaded or unavailable, processes may be
migrated to other machines in the cluster, allowing workloads to continue running. While this does not replace dedicated
high-availability systems, it can help improve utilization and reduce the impact of individual node failures.&lt;/p&gt;
&lt;h1 id="modern-comparable-software"&gt;Modern comparable software&lt;/h1&gt;
&lt;p&gt;It was mentioned earlier on that Single System Image clustering is comparable to modern cloud computing. However, I
refer only to Kerrighed in this comparison; the diskless boot design described above is orthogonal to SSI clustering.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://popcornlinux.org/"&gt;Popcorn Linux&lt;/a&gt; seems to be the only open source, single-image clustering Linux distribution
that is reasonably up to date. However, Popcorn is not binary-compatible with existing Linux programs like Kerrighed is.
Instead, Popcorn uses a special LLVM-based compiler to compile programs in a specific way so that they may run on a
Popcorn cluster. However, Popcorn also offers support for mixing different Instruction Set Architectures within a
cluster and even supports migrating running processes to hosts with different instruction sets! Popcorn&amp;rsquo;s special
compiler is what makes this possible.&lt;/p&gt;
&lt;p&gt;Kubernetes is another modern comparable. Unlike Single System Image clustering, Kubernetes requires software to be
packaged in a specific way - Open Container Initiative Images aka Docker Images - and &amp;ldquo;migrates&amp;rdquo; workloads by
terminating them, moving required resources such as storage disks, and starting them anew on a new host. While
Kubernetes has nothing to do with Single System Image clustering, it is similar in that its primary role is to schedule
work on clustered hosts.&lt;/p&gt;
&lt;p&gt;And that is what all of these tools do - Kerrighed, Popcorn Linux, Kubernetes - you give it your hardware and your
desired workloads, and it figures out where to run it, and how to keep it online to the best of its ability. Single
system image computing has its roots all the way back in the 1980s, whereas Kubernetes is relatively new, but both serve
the same purpose, then and now.&lt;/p&gt;
&lt;p&gt;If this interest you, other systems that explored similar ideas from over the years, include
&lt;a href="https://en.wikipedia.org/wiki/MOSIX"&gt;MOSIX&lt;/a&gt; and &lt;a href="https://en.wikipedia.org/wiki/Sprite_%28operating_system%29"&gt;Sprite&lt;/a&gt;,
both of which experimented with process migration and distributed operating system design.&lt;/p&gt;</content></item><item><title>finally TLS, courtesy of Let's Encrypt!</title><link>https://davepedu.com/blog/2017/07/20/finally-tls-courtesy-of-lets-encrypt/</link><pubDate>Thu, 20 Jul 2017 00:00:00 +0000</pubDate><author>dave@davepedu.com (dave)</author><guid>https://davepedu.com/blog/2017/07/20/finally-tls-courtesy-of-lets-encrypt/</guid><description>&lt;p&gt;This blog - and some of my other sites - have finally been made secure, with some new TLS certificates courtesy of
&lt;a href="https://letsencrypt.org/"&gt;Let&amp;rsquo;s Encrypt&lt;/a&gt;. Browsing of my blog is now somewhat safer to network traffic inspection!&lt;/p&gt;</description><content>&lt;p&gt;This blog - and some of my other sites - have finally been made secure, with some new TLS certificates courtesy of
&lt;a href="https://letsencrypt.org/"&gt;Let&amp;rsquo;s Encrypt&lt;/a&gt;. Browsing of my blog is now somewhat safer to network traffic inspection!&lt;/p&gt;</content></item><item><title>on moving to the Bay Area and engaging with Startups</title><link>https://davepedu.com/blog/2017/07/12/on-moving-to-the-bay/</link><pubDate>Wed, 12 Jul 2017 00:00:00 +0000</pubDate><author>dave@davepedu.com (dave)</author><guid>https://davepedu.com/blog/2017/07/12/on-moving-to-the-bay/</guid><description>&lt;p&gt;I moved to the San Francisco Bay Area almost 3 years ago now. Having grown up in a small town (~2000 people) and gone
to school in a small city (Rochester metropolitan area, New York ~1,000,000), it was not unexpectedly quite the
shocking transition.&lt;/p&gt;
&lt;p&gt;Most of my observations of difference are fairly mundane - traffic patterns are different, more timid in some ways, and
more aggressive in others. Endless cool summer sun is easy to love. So many cultures and cuisines all in one place. A
beautiful setting, and at the same time, it&amp;rsquo;s ground zero for the information technology revolution upending the world.&lt;/p&gt;</description><content>&lt;p&gt;I moved to the San Francisco Bay Area almost 3 years ago now. Having grown up in a small town (~2000 people) and gone
to school in a small city (Rochester metropolitan area, New York ~1,000,000), it was not unexpectedly quite the
shocking transition.&lt;/p&gt;
&lt;p&gt;Most of my observations of difference are fairly mundane - traffic patterns are different, more timid in some ways, and
more aggressive in others. Endless cool summer sun is easy to love. So many cultures and cuisines all in one place. A
beautiful setting, and at the same time, it&amp;rsquo;s ground zero for the information technology revolution upending the world.&lt;/p&gt;
&lt;p&gt;I saw what the Bay&amp;rsquo;s tech industry had to offer and found my niche over time. I came here to work for HP, an ancient
giant in the hard and software industry, doing web development for marketing websites. I eventually found my way into
the Startup Lottery, designing and operating critical core infrastructure for &amp;ldquo;big data&amp;rdquo; applications.&lt;/p&gt;
&lt;p&gt;Startups are generally small companies that attempt to use computers and code to tackle challenges previously thought as
too hard for such a small team. And the Lottery is the right term for how startups ebb and flow in the Bay.&lt;/p&gt;
&lt;p&gt;Every so often, a &amp;ldquo;new thing&amp;rdquo; becomes the popular &amp;ldquo;new thing&amp;rdquo; and a thousand startups pop up around it. Doing &amp;ldquo;it&amp;rdquo;
directly, supporting &amp;ldquo;it&amp;rdquo;, documenting &amp;ldquo;it&amp;rdquo;, consulting about &amp;ldquo;it&amp;rdquo;, defending from &amp;ldquo;it&amp;rdquo;, whatever &amp;ldquo;it&amp;rdquo; may be. The
Dot-Com boom in the early 2000s, Web 2 and Social media in the later 2000s, early Apps in the 2010s, Big data in the mid
2010s, the list goes on. When it looks like there is not anyone left that wants to pay any more money for the old thing,
a new thing pops up and the cycle begins anew. A handful of those startups may survive, and actually become profitable
and valuable. And the few investors and few early employees in that company reap what is left of their particular thing,
which is still substantially much, even though everyone is paying attention to the &amp;ldquo;new thing&amp;rdquo; now.&lt;/p&gt;
&lt;p&gt;Once you understand that, you can see why working for a startup in Silicon Valley is like playing the lottery. You
probably won&amp;rsquo;t, but there&amp;rsquo;s a slim chance you could pick the right company, stick with it, and be rewarded.&lt;/p&gt;
&lt;p&gt;Even if that doesn&amp;rsquo;t happen, working for a startup is rewarding in other ways too. These companies understand they are
inherently unstable, and as people generally prefer stable occupations over unstable ones, make up for it with anything
from laid-back offices with free snacks to higher salaries. And as different types of perks appeal to different people,
each startup tends to have a different personality.&lt;/p&gt;
&lt;p&gt;As these companies are usually small, each employee is often in a greater position of responsibility. Large companies
tend to let employees hyperfocus in specific areas and make slow changes involving a large number of people. Startups
have fewer employees, which means each one must manage a larger slice of the company&amp;rsquo;s footprint.&lt;/p&gt;
&lt;p&gt;In practice, this kind of environment pushes us engineers to think in terms of scale and leverage much earlier in
their careers.&lt;/p&gt;
&lt;p&gt;The only practical way to operate is to gain this leverage through automation. Tasks that might be handled manually in
an older and larger organization quickly become unsustainable. You begin to look at systems not as one-off deployments
or fixes, but as processes that must run reliably over and over again with minimal human involvement.&lt;/p&gt;
&lt;p&gt;Automation is a force multiplier: the good decisions scale, but so do the bad ones. A poorly designed process that runs
once might cause a small inconvenience. A poorly designed process that runs automatically across an entire fleet can
cause a disaster in seconds. A process that works well in isolation may fail when invoked thousands of times
simultaneously. Because of that, building automation forces a special kind of discipline. You have to research the tools
you use, think through edge cases, make predictions with limited information and understand the systems you&amp;rsquo;re
interacting with much more deeply than if you were just executing tasks by hand.&lt;/p&gt;
&lt;p&gt;Over time, that kind of work develops a particular sort of experience. In larger organizations, thinking about systems
at that scale is often the responsibility of architects or senior infrastructure teams. In a startup environment, those
responsibilities fall much earlier in a person&amp;rsquo;s career. When a small group of engineers is responsible for keeping a
large and complex system running, everyone ends up participating in the design decisions that shape how it works.&lt;/p&gt;
&lt;p&gt;That puts you in an unusual position. You&amp;rsquo;re simultaneously operating the systems, building the automation around them,
and deciding how they should evolve. The feedback loop between design and operation becomes very tight, or even
nonexistent on a bad day. You feel the consequences of your decisions immediately, which encourages you to design things
in ways that are easier to operate, monitor, repair, and repeat.&lt;/p&gt;
&lt;p&gt;It also builds a habit of intentional experimentation. When your tools can affect large systems very quickly, you learn
to test ideas in small steps. Changes get staged, rehearsed, and validated before they touch anywhere important. The
process becomes less about heroics and more about methodical iteration.&lt;/p&gt;
&lt;p&gt;In that sense, working in a startup environment isn&amp;rsquo;t just about &amp;ldquo;moving fast&amp;rdquo;. It&amp;rsquo;s about learning how to build systems
that can move fast safely. And that combination of large responsibility, heavy reliance on automation, and constant
experimentation creates a kind of practical architectural experience that would otherwise take many more years, and a
much larger organization, to acquire.&lt;/p&gt;
&lt;p&gt;And it&amp;rsquo;s fun to build and drive big toys using other people&amp;rsquo;s money.&lt;/p&gt;
&lt;p&gt;In my current role, a large part of that philosophy shows up in the tools we create and use to operate our systems. I
spend a lot of time working with tools like Ansible and SaltStack, both of which are designed to control and coordinate
large groups of machines at once. Each brings its own model for turning a small amount of human effort into changes
across an entire environment. Used well, they make it possible for our small team to operate infrastructure of
surprising scale.&lt;/p&gt;
&lt;p&gt;And honestly? All of this - the Lottery, the scale, the rewards - adds up to a whole lot of fun. The confident, but
still nervous, satisfying feeling of seeing your change take effect on exponentially more systems is pretty addicting.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://davepedu.com/blog/2017/07/12/on-moving-to-the-bay/img/2014-09-13_13.15.13_cropped_hu_c45cd6d7157417fd.jpg" alt="Screenshot"/&gt;&lt;/p&gt;</content></item><item><title>pfSense and ESXi setup on OVH's network</title><link>https://davepedu.com/blog/2017/06/13/pfsense-and-esxi-on-ovh-network/</link><pubDate>Mon, 12 Jun 2017 00:00:00 +0000</pubDate><author>dave@davepedu.com (dave)</author><guid>https://davepedu.com/blog/2017/06/13/pfsense-and-esxi-on-ovh-network/</guid><description>&lt;p&gt;I host my projects on a dedicated server rented from &lt;a href="http://ovh.com/"&gt;OVH&lt;/a&gt;, I&amp;rsquo;m a huge VMware fan, so I&amp;rsquo;ve used their
free product, the &lt;a href="http://www.vmware.com/products/vsphere-hypervisor"&gt;ESXi Hypervisor&lt;/a&gt;, to cram lots of virtual machines
inside my one server. Since not all my virtual machines need internet-facing IPs and ESXi has only basic NAT capability,
an easy solution here is running a &lt;a href="https://www.pfsense.org/"&gt;pfSense&lt;/a&gt; install as a NAT router, as a virtual machine.
OVH&amp;rsquo;s network has some unique setup required for this specific situation. Unfortunately, this is best documented in
various blog posts across the internet.&lt;/p&gt;</description><content>&lt;p&gt;I host my projects on a dedicated server rented from &lt;a href="http://ovh.com/"&gt;OVH&lt;/a&gt;, I&amp;rsquo;m a huge VMware fan, so I&amp;rsquo;ve used their
free product, the &lt;a href="http://www.vmware.com/products/vsphere-hypervisor"&gt;ESXi Hypervisor&lt;/a&gt;, to cram lots of virtual machines
inside my one server. Since not all my virtual machines need internet-facing IPs and ESXi has only basic NAT capability,
an easy solution here is running a &lt;a href="https://www.pfsense.org/"&gt;pfSense&lt;/a&gt; install as a NAT router, as a virtual machine.
OVH&amp;rsquo;s network has some unique setup required for this specific situation. Unfortunately, this is best documented in
various blog posts across the internet.&lt;/p&gt;
&lt;h1 id="installation"&gt;Installation&lt;/h1&gt;
&lt;p&gt;You can grab the latest pfSense from here: &lt;a href="https://www.pfsense.org/download/index.html"&gt;https://www.pfsense.org/download/index.html&lt;/a&gt;. At the time of writing, the
latest and pfSense version I&amp;rsquo;m using is 2.3.4, though any version later than 2.1 should work nicely. Thorough IPv6
support was introduced in 2.1, but if you don&amp;rsquo;t need that I suppose an earlier version would be fine as well. So grab an
ISO and upload it to your ESXi machine.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Virtual machine creation&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;I used the 64-bit version of pfSense, so the &amp;ldquo;type&amp;rdquo; of virtual machine I used was &lt;strong&gt;Linux - Other Linux
(64-bit)&lt;/strong&gt;. pfSense is based on BSD, so any similiar configuration should be fine. 512mb of ram allocated will be more
than enough, but 256mb is doable. 4GB disk space allocated is fine as well. Next, other than adding a second network
adapter, the default options that ESXi presents will work fine. One specialty for OVH - in your control panel, you must
generate a MAC address for the address this host will be using - assign it to the public-facing network adapter. Also,
create a virtual switch and attach it to the &amp;ldquo;private&amp;rdquo; network adapter. This switch is where your private network and
virtual machines will live.&lt;/p&gt;
&lt;p&gt;Installing pfSense to the virtual machine is pretty straightforward, but [check out this guide]
(&lt;a href="https://doc.pfsense.org/index.php/Installing_pfSense"&gt;https://doc.pfsense.org/index.php/Installing_pfSense&lt;/a&gt;) if you need help.&lt;/p&gt;
&lt;h1 id="ovh-customization"&gt;OVH customization&lt;/h1&gt;
&lt;p&gt;After you finish pfSense&amp;rsquo;s command-line setup wizard, you won&amp;rsquo;t have internet connectivity. You need to add some routes
specific to your server&amp;rsquo;s in the OVH network. If your server&amp;rsquo;s primary IP is A.B.C.D and the IP 1.2.3.4 will be
assigned to this new host, run these commands in the shell:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;route add -net A.B.C.254 1.2.3.4&lt;/p&gt;
&lt;p&gt;route add default A.B.C.254&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;These commands will add routes needed to access your host&amp;rsquo;s gateway. You&amp;rsquo;ll have connectivity now, but need to make this
fix permanent. Log into the UI, and in pfSense&amp;rsquo;s package repo the package Shellcmd is a convenient way to do this.
Install it and add the two commands above as startup commands.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://davepedu.com/blog/2017/06/13/pfsense-and-esxi-on-ovh-network/img/pfsense_hu_ca9405783f3fe3ef.png" alt="Pfsense running in the VMware console" title="Pfsense running in the
VMware console" /&gt;&lt;/p&gt;
&lt;h1 id="wrap-up"&gt;Wrap up&lt;/h1&gt;
&lt;p&gt;If you&amp;rsquo;ve enabled the DHCP server on your pfSense install you can now create privately-networked virtual machines with
internet access via NAT, but with no exposure to the internet. Ports can be forwarded to public services and everything
else is hidden behind a layer of NAT.&lt;/p&gt;
&lt;p&gt;Next time: setting up IPv6 with &lt;a href="https://tunnelbroker.net/"&gt;TunnelBroker.net&lt;/a&gt;!&lt;/p&gt;
&lt;p&gt;Editor&amp;rsquo;s Note: originally composed in &lt;strong&gt;2014&lt;/strong&gt;, but rotted in the drafts bin&amp;hellip;&lt;/p&gt;</content></item><item><title>1Password on Linux</title><link>https://davepedu.com/blog/2017/02/28/1password-on-linux/</link><pubDate>Tue, 28 Feb 2017 00:00:00 +0000</pubDate><author>dave@davepedu.com (dave)</author><guid>https://davepedu.com/blog/2017/02/28/1password-on-linux/</guid><description>&lt;p&gt;I&amp;rsquo;ve been a long time user of &lt;a href="https://1password.com/"&gt;1Password&lt;/a&gt;, a password vault by AgileBits. However, it only
supports OS X and Windows; which is a problem for Linux desktop converts like myself. Their desktop application - a very
nice app and also required for the browser extension - is only for Windows and OS X. AgileBits also offers 1Password
Families which does support Linux, but you lose control over where your password data is stored.&lt;/p&gt;</description><content>&lt;p&gt;I&amp;rsquo;ve been a long time user of &lt;a href="https://1password.com/"&gt;1Password&lt;/a&gt;, a password vault by AgileBits. However, it only
supports OS X and Windows; which is a problem for Linux desktop converts like myself. Their desktop application - a very
nice app and also required for the browser extension - is only for Windows and OS X. AgileBits also offers 1Password
Families which does support Linux, but you lose control over where your password data is stored.&lt;/p&gt;
&lt;p&gt;In the past, 1Password had a browser-based user interface that could be served out of Dropbox or by opening the local
file from your browser (called 1PasswordAnywhere, a single HTML file). However, browser security has &amp;ldquo;improved&amp;rdquo; and now
block XHR requests to local filesystems and Dropbox discontinued the feature that allowed you to serve up this HTML file
privately. So
&lt;a href="https://discussions.agilebits.com/discussion/63045/moving-beyond-1passwordanywhere"&gt;AgileBits was forced to sunset&lt;/a&gt;
this feature.&lt;/p&gt;
&lt;p&gt;Despite this announcement, 1PasswordAnywhere still seems to work! And as long as you can browse the file tree of
1Password&amp;rsquo;s data, this method should work for you.&lt;/p&gt;
&lt;p&gt;I use Dropbox to sync my 1Password data, so I&amp;rsquo;ve opened a terminal and browsed to where the &lt;code&gt;1Password.agilekeychain&lt;/code&gt;
directory is kept. For me, that&amp;rsquo;s &lt;code&gt;~/Dropbox/Apps/1Password/1Password.agilekeychain/&lt;/code&gt;. Within this directory is where
&lt;code&gt;1PasswordAnywhere.html&lt;/code&gt; should be placed. I renamed mine to &lt;code&gt;index.html&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Next, set up an web server to serve up this directory. If you&amp;rsquo;re running Mac or Linux you probably already have Python,
and you can use its built-in minimal server:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-text"&gt;$ pwd
/home/dave/Dropbox/Apps/1Password/1Password.agilekeychain
$ python -m SimpleHTTPServer
Serving HTTP on 0.0.0.0 port 8000 ...&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Next, browse to &lt;a href="http://127.0.0.1:8000/"&gt;http://127.0.0.1:8000/&lt;/a&gt; and you&amp;rsquo;ll be presented with a working - but visually outdated -
1PasswordAnywhere instance! Of course, this isn&amp;rsquo;t the most secure - anyone on your network can access the web interface
too! To be fair - your password is needed to decrypt the data - though it would be better to serve only on localhost.
But I&amp;rsquo;ll leave this as homework for the reader.&lt;/p&gt;
&lt;p&gt;Also, first post of 2017!&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Update: I also stumbled across &lt;a href="https://github.com/jbreams/gonepass"&gt;gonepass&lt;/a&gt;, a native Linux desktop app intended to
replace the 1Password app. I&amp;rsquo;ve not tried it yet, so expect a follow-up article!&lt;/p&gt;
&lt;p&gt;Update 2: I downloaded and compiled gonepass on my system. It was pretty easy and works great! Definitely a safer idea
than the web-based version above.&lt;/p&gt;</content></item><item><title>upgrading my NAS, part 2: high availability</title><link>https://davepedu.com/blog/2016/07/21/upgrading-my-nas-part-2/</link><pubDate>Thu, 21 Jul 2016 00:00:00 +0000</pubDate><author>dave@davepedu.com (dave)</author><guid>https://davepedu.com/blog/2016/07/21/upgrading-my-nas-part-2/</guid><description>&lt;p&gt;In a &lt;a href="https://davepedu.com/blog/2015/11/23/building-a-distributed-redundant-data-storage-backbone-part-1-of-2/"&gt;previous post&lt;/a&gt;,
I described some of my early approaches to building a NAS for my home. In summary, I decided to use &lt;a href="https://en.wikipedia.org/wiki/Mdadm"&gt;Linux software raid&lt;/a&gt;
formatted with Linux&amp;rsquo;s built-in &lt;a href="https://en.wikipedia.org/wiki/Ext3"&gt;ext3&lt;/a&gt; format. This setup worked, but left me with
three main problems:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Volume size limitations: ext3 is capable of handling a maximum volume size of 16TB, when formatted with a 4kb block
size. It is theoretically possible to use a larger block size and thus a larger maximum volume size. However,
changing the block size requires reformatting the volume, which I could not do. Additionally, most hard drives
natively use a 4kb block size. Matching filesystem block size to hard drive block size generally achieves best
performance, for reasons such as avoiding a single write operation becoming multiple operations - read-modify-write.&lt;/p&gt;</description><content>&lt;p&gt;In a &lt;a href="https://davepedu.com/blog/2015/11/23/building-a-distributed-redundant-data-storage-backbone-part-1-of-2/"&gt;previous post&lt;/a&gt;,
I described some of my early approaches to building a NAS for my home. In summary, I decided to use &lt;a href="https://en.wikipedia.org/wiki/Mdadm"&gt;Linux software raid&lt;/a&gt;
formatted with Linux&amp;rsquo;s built-in &lt;a href="https://en.wikipedia.org/wiki/Ext3"&gt;ext3&lt;/a&gt; format. This setup worked, but left me with
three main problems:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Volume size limitations: ext3 is capable of handling a maximum volume size of 16TB, when formatted with a 4kb block
size. It is theoretically possible to use a larger block size and thus a larger maximum volume size. However,
changing the block size requires reformatting the volume, which I could not do. Additionally, most hard drives
natively use a 4kb block size. Matching filesystem block size to hard drive block size generally achieves best
performance, for reasons such as avoiding a single write operation becoming multiple operations - read-modify-write.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;High Availability: I enjoyed having a NAS so much, that I quickly began running additional servers that would mount
the NAS&amp;rsquo;s storage volume and use it for whatever large storage was needed. I can&amp;rsquo;t recall which network filesystem
I was using at the time, but it was one that required a re-mount after the server rebooted. NFS would make this less
of a problem. Anyway - reboots aside - I wanted a NAS that would remain available even after one of the computers
backing it failed, or needed to be rebooted for updates, for example.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Space limitations: physical space becomes a limiting factor for NAS side as well. Not many server chassis had
physical space for many hard drives, outside of rackable enterprise-grade systems that I couldn&amp;rsquo;t afford. Networks
allow multiple computers to communicate, so why would my filesystem be limited to one machine? I wanted a NAS that
could be &amp;ldquo;scaled up&amp;rdquo; by adding more computer servers and hard drives to it.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h1 id="glusterfs"&gt;GlusterFS&lt;/h1&gt;
&lt;p&gt;Enter GlusterFS - GlusterFS is a distributed network filesystem. GlusterFS provides data safety features similar to
RAID, but offered in a manner that spans multiple computers instead of multiple hard drives.&lt;/p&gt;
&lt;p&gt;The general way in which GlusterFS functions is simple: you format your hard drives with whatever filesystem you prefer
- I used ZFS - and GlusterFS creates a virtual filesystem on top of them. GlusterFS calls each sub-volume a &amp;ldquo;brick&amp;rdquo;,
and while each brick only spans one server, the virtual GlusterFS filesystem can incorporate multiple bricks from
multiple servers.&lt;/p&gt;
&lt;p&gt;What&amp;rsquo;s great about GlusterFS&amp;rsquo;s approach to storage is that it single-handedly solves each of the 3 issues above:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Volume size limitations: GlusterFS uses its own type of filesystem with astronomically high volume size limitations.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;High Availability: with a &amp;ldquo;replicated&amp;rdquo; GlusterFS volume, multiple copies of each file are stored on separate bricks.
Since separate bricks are on separate servers, the file remains accessible even if one server or hard drive fails.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Space limitations: since GlusterFS volumes can span across multiple servers, space becomes nearly limitless. You can
always plug more servers into the cluster.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h1 id="design"&gt;Design&lt;/h1&gt;
&lt;p&gt;I started with two servers, but later moved to four. Each could hold up to 8 hard drives. A common build at the time was
a mini-NAS, with a case by U-NAS, the NSC-800. For a motherboard/cpu combo, I chose AsRock Rack. I started with their
Mini-ITX form factor server motherboard, the &lt;a href="https://www.asrockrack.com/general/productdetail.asp?Model=C2750D4I"&gt;C2750D4I&lt;/a&gt;,
and later upgraded to the &lt;a href="https://www.asrockrack.com/general/productdetail.asp?Model=C3758D4I-4L"&gt;C3758D4I&lt;/a&gt;. Both
feature 8 cores, low power consumption, and small physical size.&lt;/p&gt;
&lt;p&gt;As mentioned above, I used ZFS to manage my server&amp;rsquo;s hard drives. Initially, I had 16 drives spread across two servers.
I divided these into 4 ZFS mirrors - like RAID 1 - of 2 drives each per server. Then, each ZFS mirror was replicated
with GlusterFS to a corresponding ZFS mirror on the other server. This worked well, but I later moved to two ZFS RAIDZ
pools per server, made from 4 drives each. This reduced the number of bricks which made GlusterFS simpler to manage.&lt;/p&gt;
&lt;h1 id="results"&gt;Results&lt;/h1&gt;
&lt;p&gt;Overall, this was a fairly reliable and reasonably performant NAS. Generally speaking, I was bottlenecked by drive speed
as I used &amp;ldquo;green&amp;rdquo; hard drives that put out less noise and heat and consumed less power at the expense of some
performance. Sequential access was great but one area the NAS really suffered in was random access. Listing large
directories was noticeably slow as well. Despite these issues, this NAS went on to serve me for many years!&lt;/p&gt;
&lt;p&gt;If you run a similar setup, &lt;a href="https://davepedu.com/contact/"&gt;let me know&lt;/a&gt;!&lt;/p&gt;</content></item><item><title>building a distributed / redundant data storage backbone - part 1 of x</title><link>https://davepedu.com/blog/2015/11/23/building-a-distributed-redundant-data-storage-backbone-part-1-of-2/</link><pubDate>Mon, 23 Nov 2015 00:00:00 +0000</pubDate><author>dave@davepedu.com (dave)</author><guid>https://davepedu.com/blog/2015/11/23/building-a-distributed-redundant-data-storage-backbone-part-1-of-2/</guid><description>&lt;p&gt;I&amp;rsquo;m not a fan of losing data, so I always have some sort of redundant storage at my disposal. At present I possess over
10TB of irreplaceable data, code, photographs, etc, (and much more replaceable data - though at an inconvenience) and
housing that data is no easy task. Be it for backups, media storage, or even temp space, having a huge pool of &amp;ldquo;safe&amp;rdquo;
storage is always a useful tool.&lt;/p&gt;</description><content>&lt;p&gt;I&amp;rsquo;m not a fan of losing data, so I always have some sort of redundant storage at my disposal. At present I possess over
10TB of irreplaceable data, code, photographs, etc, (and much more replaceable data - though at an inconvenience) and
housing that data is no easy task. Be it for backups, media storage, or even temp space, having a huge pool of &amp;ldquo;safe&amp;rdquo;
storage is always a useful tool.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;A bit of history first - we&amp;rsquo;ll get into the cutting edge stuff, with more technical detail, in part 2.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;My first adventure into this involved a number of SATA hard drives attached to a Mac Mini via USB 3.0. A Linux VM
assembled these drives into a RAID 5 array and served their contents over SMB (&amp;lsquo;server message block&amp;rsquo; - windows
sharing). Unfortunately this was in the early days of USB 3.0 so the pass-through adapter to the Linux virtual machine
only supported USB 2.0 speeds. So this array maxed out around 15MB/s constant write. Not great, but at least it&amp;rsquo;s safe.&lt;/p&gt;
&lt;h1 id="raid"&gt;RAID&lt;/h1&gt;
&lt;p&gt;Anyway, why RAID? &lt;a href="https://en.wikipedia.org/wiki/RAID"&gt;RAID&lt;/a&gt; is a immensely valuable idea that allows the use of many
independent hard or solid state drives for a single logical task. RAID can be used for speed gains beyond what an
individual drive can handle or failure proof storage where even the death of one or more drives causes no data loss -
and in the best case, no interruption to services using the data. There are many different kinds of traditional RAID,
with the most commonly used being:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;RAID 0 - performance gains by spreading load across multiple drives&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;RAID 1 - stability gains by mirroring data on two or more drives&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;RAID 5 - stability gains by smart use of striping &amp;amp; parity&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These (and other) levels of RAID can also be combined for a mix of the effects a single RAID level brings. For example,
RAID 10 (1+0, striped set of mirrors) or is fairly common.&lt;/p&gt;
&lt;p&gt;Traditional RAID - or &amp;ldquo;hardware&amp;rdquo; RAID - typically involves a hardware expansion card for the dedicated purpose of
handling RAID computations and drives. The RAID card would have a builtin processor, memory, and SATA ports to which the
involved disks would be attached to. Up until recently, this sort of configuration was necessary for top performance -
software RAID has been around for awhile, but historically there was a performance gap. A risk of hardware RAID has
always been the RAID card itself. RAID cards typically have an onboard memory module a battery to keep the memory
&amp;ldquo;alive&amp;rdquo;. If any of these parts cease to function - or any other part of the card - it&amp;rsquo;s possible that all data on the
RAID array can be lost! In my opinion, this isn&amp;rsquo;t a viable option. If you have your data on a single drive or a RAID
array controlled by one RAID card, either there is a single piece of hardware that can die and cause data loss.
Not good.&lt;/p&gt;
&lt;p&gt;Software RAID addresses this issue. It may have originally been created as a cheaper (and slower) alternative to
hardware RAID, but I believe nowadays software options are just as valid and performant has hardware RAID is. Software
RAID, in a nutshell, is, technically speaking, the same as hardware RAID, but with the computation and memory required
for operation handled by a system&amp;rsquo;s main CPU and memory as opposed to a dedicated piece of hardware. Done right, these
components can die or be swapped out with no fear of data loss.&lt;/p&gt;
&lt;p&gt;In my opinion the most notable advances in software RAID tech are as follows:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Mdadm - often referred to as &amp;lsquo;Linux software RAID&amp;rsquo; - was introduced in 2001 and quickly became the de facto software
RAID tool.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;ZFS - introduced in 2005 (and from my perspective, catching on in the early 2010s) was an entirely new beast.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;While ZFS is impressively (and I&amp;rsquo;m using it today!), let&amp;rsquo;s examine mdadm first so we can see how ZFS differs. Mdadm
offers block-level RAID. In other words, it doesn&amp;rsquo;t care which (and you have to use) filesystem is &amp;ldquo;on top&amp;rdquo; of it. On
Linux, mdadm consumes block devices and provides a virtual block device. So if the physical drives on your system are
&lt;code&gt;/dev/sda&lt;/code&gt; and &lt;code&gt;/dev/sdb&lt;/code&gt;, mdadm can consume these and provide &lt;code&gt;/dev/md0&lt;/code&gt;. From there, md0 can be treated just like a
plain block device - create partition(s) and format them with a filesystem of choice.&lt;/p&gt;
&lt;p&gt;My first and second storage arrays used Mdadm. It was an easy choice as tools that work with native block devices work
with mdadm virtual block devices. I could partition the virtual device with common disk management tools like fdisk and
use common filesystems like ext.&lt;/p&gt;
&lt;p&gt;And I did exactly that - my first array comprised of ext4 on top of mdadm. And it worked! &amp;hellip;until I needed an array
bigger than 16TB. Because of how ext4 was programmed and integrated into Linux operating systems it is incapable of
handling filesystems greater in size than 16TB when using 4k blocks. Considering it originated in the early 90s and
focused on backwards compatibility, there wasn&amp;rsquo;t a workaround. Back to the drawing board.&lt;/p&gt;
&lt;p&gt;What&amp;rsquo;s nice about Linux is that filesystems are ubiquitous - if the system can mount a filesystem, all programs on the
system that depend on file access and use it and all features it expects a filesystem to support. A beauty of modern
system design. An up-and-coming filesystem around the time I needed to expand was btrfs. With a maximum supported
filesystem size of 16EB (exabytes!), or a million times larger than ext4&amp;rsquo;s limits, I was sure I&amp;rsquo;d never outgrow it. So I
live-converted my array to btrfs and it was full steam ahead from there!&lt;/p&gt;
&lt;p&gt;Great! So in early 2011 I had a storage backbone capable of supporting an astronomical amount of data, the flexibility
to expand over time, and safety of software RAID. What more could be desired? Well, the world of computing constantly
improves itself so I wasn&amp;rsquo;t happy sticking with a single methodology. But more on that, and ZFS especially, in part 2!&lt;/p&gt;</content></item><item><title>subsonic database bloat &amp; corruption</title><link>https://davepedu.com/blog/2015/08/01/subsonic-database-bloat-corruption/</link><pubDate>Sat, 01 Aug 2015 00:00:00 +0000</pubDate><author>dave@davepedu.com (dave)</author><guid>https://davepedu.com/blog/2015/08/01/subsonic-database-bloat-corruption/</guid><description>&lt;p&gt;&lt;strong&gt;Update 04/08/2019:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ve had trouble with this method on newer versions of Subsonic and since the original technique was kind of bad - it
more or less triggers crash recovery - I decided to take another look. I ended up finding the
&lt;a href="https://packages.ubuntu.com/search?keywords=hsqldb-utils"&gt;hsqldb-utils&lt;/a&gt; package, which includes some command-line
utilities to manage HSQLDB databases.&lt;/p&gt;
&lt;p&gt;Unfortunately, this package doesn&amp;rsquo;t use the same version of HSQLDB as Subsonic - my Subsonic instance is using 1.8.0.5
(it lists this in the database&amp;rsquo;s &amp;lsquo;subsonic.properties&amp;rsquo; file), whereas the tools included in hsqldb-utils (on Ubuntu
Bionic, at least) are well past 2.0. But, this is at least a step in the right direction. We can download an older
version of the tools.&lt;/p&gt;</description><content>&lt;p&gt;&lt;strong&gt;Update 04/08/2019:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ve had trouble with this method on newer versions of Subsonic and since the original technique was kind of bad - it
more or less triggers crash recovery - I decided to take another look. I ended up finding the
&lt;a href="https://packages.ubuntu.com/search?keywords=hsqldb-utils"&gt;hsqldb-utils&lt;/a&gt; package, which includes some command-line
utilities to manage HSQLDB databases.&lt;/p&gt;
&lt;p&gt;Unfortunately, this package doesn&amp;rsquo;t use the same version of HSQLDB as Subsonic - my Subsonic instance is using 1.8.0.5
(it lists this in the database&amp;rsquo;s &amp;lsquo;subsonic.properties&amp;rsquo; file), whereas the tools included in hsqldb-utils (on Ubuntu
Bionic, at least) are well past 2.0. But, this is at least a step in the right direction. We can download an older
version of the tools.&lt;/p&gt;
&lt;p&gt;I found the following jars by scouring the internet, downloading them into /tools/ on my system.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;hsqldb-sqltool-jdk1.5-2.0.jar&lt;/li&gt;
&lt;li&gt;hsqldb-1.8.0.5.jar&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Without further ado, here&amp;rsquo;s the happy-path way to de-bloat your database:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Install Java. You probably already have this since you&amp;rsquo;re running Subsonic, right?&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Stop Subsonic. Subsonic must NOT be running while is operation is performed or database corruption is likely.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Change directories into the database dir. The path below could be different for you:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;cd /var/subsonic/db/&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Open the database using hsqldb-sqltool. Take note of the classpath (&lt;code&gt;-cp&lt;/code&gt;), this needs to be pointed at the two jars above:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;java -cp :/tools/hsqldb-sqltool-jdk1.5-2.0.jar:/tools/hsqldb-1.8.0.5.jar org.hsqldb.cmdline.SqlTool --inlineRc=url=jdbc:hsqldb:file:subsonic --driver org.hsqldb.jdbcDriver&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Run the compact function:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;sql&amp;gt; &lt;code&gt;SHUTDOWN COMPACT;&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Exit hsqldb-sqltool by pressing control-d.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;At this point, Subsonic&amp;rsquo;s database should be drastically smaller. In my case:&lt;/p&gt;
&lt;p&gt;Before:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-clike"&gt;# du -sh /srv/subsonic
3.2G /srv/subsonic&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;After:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-clike"&gt;# du -sh /srv/subsonic
14M /srv/subsonic&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;As insert/update/deletes queries are performed data moves around inside of HSQLDB&amp;rsquo;s data files. Over time, and similar
to a hard drive, data becomes fragmented and the database file size grows despite much of the space within it being &amp;ldquo;not
in use&amp;rdquo;. What we&amp;rsquo;ve done is rewrite the database file in an optimized way such that there is no &amp;ldquo;unused&amp;rdquo; space left
within the file; all the data is contiguous. It will bloat again over time, but that&amp;rsquo;s a concern for later. Do take a
look at &lt;a href="http://www.hsqldb.org/doc/1.8/guide/guide.html#N101DB"&gt;HSQLDB&amp;rsquo;s docs&lt;/a&gt; on the subject.&lt;/p&gt;
&lt;p&gt;I maintain a &lt;a href="https://git.davepedu.com/dave/docker-subsonic"&gt;Dockerfile for Subsonic&lt;/a&gt; and will eventually add this
database compression method as an automated task within the image. This should solve Subsonic database bloat &lt;a href="https://www.youtube.com/watch?v=gONcDfFrPis"&gt;once and
for all&lt;/a&gt;! Edit: This is complete! As start of the
&lt;a href="https://git.davepedu.com/dave/docker-subsonic/src/branch/master/start#L8"&gt;startup procedure&lt;/a&gt;, my Docker image will
compress the database at every startup.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Original article below. I highly recommend NOT doing this; it is here for posterity:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;I use &lt;a href="http://www.subsonic.org/"&gt;Subsonic&lt;/a&gt; media streamer install to stream my music library on my mobile phone, media
center, laptop etc. Recently, my web UI and other client apps stopped working and I noticed the subsonic server was
spewing database errors.&lt;/p&gt;
&lt;p&gt;Ugly things like:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-"&gt;87431 [btpool0-6] WARN org.mortbay.log - Nested in org.springframework.dao.TransientDataAccessResourceException: PreparedStatementCallback; SQL [ &lt;snip&gt; ]:
java.sql.SQLException: java.io.IOException:** S1000 Data file size limit is reached in statement** [update media_file set &lt;snip&gt;]
at org.hsqldb.jdbc.Util.throwError(Unknown Source)
at org.hsqldb.jdbc.jdbcPreparedStatement.executeUpdate(Unknown Source)
at org.springframework.jdbc.core.JdbcTemplate$2.doInPreparedStatement(JdbcTemplate.java:798)
at org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:591)
at org.springframework.jdbc.core.JdbcTemplate.update(JdbcTemplate.java:792)
at org.springframework.jdbc.core.JdbcTemplate.update(JdbcTemplate.java:850)&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&amp;hellip;With hundreds and hundreds of lines in each stack trace. Subsonic is written in Java and uses a database called
HSQLDB which has some sort of size-limiting upper limit that&amp;rsquo;s being hit. Since it is similar to SQLite, we can check
the size of the database files on disk. Inspecting these revealed it had grown to 2 gigabytes! Considering it holds only
a list of media files, a few playlists and some settings, there&amp;rsquo;s no reason it should be that large.&lt;/p&gt;
&lt;p&gt;Buried in HSQLDB&amp;rsquo;s docs I found a description of it&amp;rsquo;s startup procedure,
&lt;a href="http://www.hsqldb.org/doc/1.8/guide/apc.html"&gt;here&lt;/a&gt;. It turns out by simply changing the &amp;ldquo;modified&amp;rdquo; setting to &amp;ldquo;yes&amp;rdquo; in
the database&amp;rsquo;s property file, the engine triggers a database recovery routine (which actually restores an internal
backup). On Ubuntu linux, the database property file is &lt;code&gt;/var/subsonic/db/subsonic.properties&lt;/code&gt;. After starting subsonic
and letting it grind for a few minutes, I just needed to subsonic re-scan my media files and it was back in business!
Not one setting or play count was lost. After the cleanup, the database is all of 44 MB. Nice!&lt;/p&gt;
&lt;p&gt;I thought this was a pretty easy fix, but tricky to find. If anyone finds this helpful,
&lt;a href="https://davepedu.com/contact/"&gt;Let me know&lt;/a&gt;!&lt;/p&gt;</content></item><item><title>the pi-rack</title><link>https://davepedu.com/projects/2015/05/01/the-pi-rack/</link><pubDate>Fri, 01 May 2015 00:00:00 +0000</pubDate><author>dave@davepedu.com (dave)</author><guid>https://davepedu.com/projects/2015/05/01/the-pi-rack/</guid><description>&lt;p&gt;I recently acquired a low-end 3d printer (0.3mm extrusion head) and after a few practice prints I decided to put my new
skills to the test. I wanted to create a Raspberry Pi case, but I thought that would be a little boring. So the idea I
settled on was creating something akin to a &lt;a href="https://en.wikipedia.org/wiki/Blade_server"&gt;blade server&lt;/a&gt; - an enclosure
that takes care of power and LAN distribution, that houses several Pis. Overkill? Absolutely!&lt;/p&gt;</description><content>&lt;p&gt;I recently acquired a low-end 3d printer (0.3mm extrusion head) and after a few practice prints I decided to put my new
skills to the test. I wanted to create a Raspberry Pi case, but I thought that would be a little boring. So the idea I
settled on was creating something akin to a &lt;a href="https://en.wikipedia.org/wiki/Blade_server"&gt;blade server&lt;/a&gt; - an enclosure
that takes care of power and LAN distribution, that houses several Pis. Overkill? Absolutely!&lt;/p&gt;
&lt;p&gt;Armed with $10 calipers, a few RasPis to measure, and Sketchup Make, I created the Raspberry Pi Rack!&lt;/p&gt;
&lt;p&gt;&lt;img src="https://davepedu.com/projects/2015/05/01/the-pi-rack/img/IMG_6775_hu_b21fa6bbf51a154.jpg" alt="The Pi-Rack. 6 Raspberry Pis in one enclosure"/&gt;&lt;/p&gt;
&lt;p&gt;I posted many more photos, and even the source 3d model files you want to print your own, over on
&lt;a href="https://hackaday.io/project/5456-raspberry-pi-rack"&gt;Hackaday&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The bill of materials isn&amp;rsquo;t crazy either:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;4x 1/4&amp;quot; by 6&amp;quot; threaded rods e.g. from Lowes&lt;/li&gt;
&lt;li&gt;16x 1/4&amp;quot; lock nuts&lt;/li&gt;
&lt;li&gt;24x 1/4&amp;quot; by 1/2&amp;quot; nylon washers&lt;/li&gt;
&lt;li&gt;8x 1/4&amp;quot; wing nuts (for easy disassembly - optional, can add 8x lock nuts instead&lt;/li&gt;
&lt;li&gt;1x male molex connector (I salvaged mine from computer fan parts)&lt;/li&gt;
&lt;li&gt;1x prototype board&lt;/li&gt;
&lt;li&gt;1x 6-pin SIP header&lt;/li&gt;
&lt;li&gt;1x 6-pin SIP header, 90˚ angle&lt;/li&gt;
&lt;li&gt;2x 6-pin SIP header, straight&lt;/li&gt;
&lt;li&gt;12x M to F breadboard jumper&lt;/li&gt;
&lt;li&gt;12x F to F breadboard jumpers&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.amazon.com/gp/product/B0034CL3MA/"&gt;5 volt Ethernet switch&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;3x screw + nut sets (these are to secure the switch, I can&amp;rsquo;t recall what I used. Something like a M3x15mm screw).&lt;/li&gt;
&lt;li&gt;12x RJ45 (ethernet) headers&lt;/li&gt;
&lt;li&gt;Cat5/e cable (a couple feet should do the trick).&lt;/li&gt;
&lt;li&gt;ATX power supply. Any desktop computer has one of these.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Not counting the Raspberry Pis, all the supplies come in easily under $100. Not bad.&lt;/p&gt;
&lt;p&gt;Happy hacking!&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;This project was featured on the
&lt;a href="https://hackaday.com/2015/06/01/hot-swappable-raspberry-pi-rack/"&gt;Hackaday.com Homepage&lt;/a&gt;!&lt;/p&gt;</content></item><item><title>ch340g usb to ttl/serial for Mac</title><link>https://davepedu.com/blog/2015/04/08/ch340g-usb-to-ttl-serial-for-mac/</link><pubDate>Wed, 08 Apr 2015 00:00:00 +0000</pubDate><author>dave@davepedu.com (dave)</author><guid>https://davepedu.com/blog/2015/04/08/ch340g-usb-to-ttl-serial-for-mac/</guid><description>&lt;p&gt;I bought some USB to TTL adapters on the cheap from China. The OS X driver was a little difficult to track down, but
this thread cracked it: &lt;a href="http://forum.arduino.cc/index.php?topic=261375.0"&gt;http://forum.arduino.cc/index.php?topic=261375.0&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Okay, drivers for mac do exist.
I always see &lt;a href="https://www.wch-ic.com"&gt;www.wch-ic.com&lt;/a&gt; for the driver, but now I searched for &amp;ldquo;ch341ser_mac.zip&amp;rdquo; and found &lt;a href="https://www.wch.cn"&gt;www.wch.cn&lt;/a&gt;
The mac driver is here : &lt;a href="https://web.archive.org/web/20150307024048/http://www.wch.cn/downloads.php?name=pro&amp;amp;proid=178"&gt;http://www.wch.cn/downloads.php?name=pro&amp;amp;proid=178&lt;/a&gt;
The version is from wch.cn is a newer, you should try that one.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;And the second piece:&lt;/p&gt;</description><content>&lt;p&gt;I bought some USB to TTL adapters on the cheap from China. The OS X driver was a little difficult to track down, but
this thread cracked it: &lt;a href="http://forum.arduino.cc/index.php?topic=261375.0"&gt;http://forum.arduino.cc/index.php?topic=261375.0&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Okay, drivers for mac do exist.
I always see &lt;a href="https://www.wch-ic.com"&gt;www.wch-ic.com&lt;/a&gt; for the driver, but now I searched for &amp;ldquo;ch341ser_mac.zip&amp;rdquo; and found &lt;a href="https://www.wch.cn"&gt;www.wch.cn&lt;/a&gt;
The mac driver is here : &lt;a href="https://web.archive.org/web/20150307024048/http://www.wch.cn/downloads.php?name=pro&amp;amp;proid=178"&gt;http://www.wch.cn/downloads.php?name=pro&amp;amp;proid=178&lt;/a&gt;
The version is from wch.cn is a newer, you should try that one.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;And the second piece:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Just to summarise. On Yosemite:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;install the driver&lt;/li&gt;
&lt;li&gt;Run this [in a terminal]: &lt;code&gt;sudo nvram boot-args=&amp;quot;kext-dev-mode=1&amp;quot;&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;p&gt;Aaand it works!&lt;/p&gt;
&lt;p&gt;&lt;img src="https://davepedu.com/blog/2015/04/08/ch340g-usb-to-ttl-serial-for-mac/img/wch_hu_4d5f22f6766af978.png" alt=""/&gt;&lt;/p&gt;
&lt;p&gt;Also known as &lt;code&gt;/dev/tty.wchusbserial1410&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Edit: Because I keep forgetting what settings to use in the Arduino IDE for this programmer, I&amp;rsquo;ve added them. Note that
these settings are for this
&lt;a href="https://hackaday.com/2013/07/10/build-a-bare-bones-arduino-clone-which-maximizes-its-use-of-real-estate/"&gt;homebrew arduino&lt;/a&gt;,
which supports the ATmega168 and ATmega328. I&amp;rsquo;m using the cheaper 168, but remember to use the right setting if you&amp;rsquo;ve
got the faster chip.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Tools &amp;gt; Board: Arduino Pro or Pro Mini&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Tools &amp;gt; Processor: ATmega168 (5v, 16mhz)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Programmer: AVR ISP&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;</content></item><item><title>arduino pong, part 1: the display</title><link>https://davepedu.com/blog/2015/03/17/arduino-pong-part-1-the-display/</link><pubDate>Tue, 17 Mar 2015 00:00:00 +0000</pubDate><author>dave@davepedu.com (dave)</author><guid>https://davepedu.com/blog/2015/03/17/arduino-pong-part-1-the-display/</guid><description>&lt;p&gt;As mentioned in my last post, I&amp;rsquo;ve been learning up electronics with the Arduino. I found a few 5 by 7 LED matrices at a
local surplus store, pictured below, so I figured another fun beginner project would be creating a clone of pong using a
few of these for the screen.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://davepedu.com/blog/2015/03/17/arduino-pong-part-1-the-display/img/led_matrix2_hu_1aba5875d30003ac.jpg" alt="5 by 7 LED matrix"/&gt;&lt;/p&gt;
&lt;p&gt;After some quick research - googling the part number - I found a datasheet, read it, and learned that the LEDs in these
units are arranged in a grid pattern, with rows sharing anodes and columns sharing cathodes. Meaning, to light up all
the LEDs in a single row, we provide power to only that row and ground all seven columns. The way this is designed, we
can decide what LEDs we want lit in only a single row (or column) at a time. So, when an image is displayed using the
entire matrix what actually happens is a single column/row is lit up at a single instant and this is applied to reach
row as are scanned down through hundreds of times per second, so it looks to the human eye that the LEDs are on
constantly. This is called the persistence of vision effect. Another nice advantage of this is power savings - even
though an LED in the grid appears to be on constantly, it&amp;rsquo;s actually only on for 1/5th of real time (or 1/7ths depending
on how you want to use the matrix - scanning by row or column).&lt;/p&gt;</description><content>&lt;p&gt;As mentioned in my last post, I&amp;rsquo;ve been learning up electronics with the Arduino. I found a few 5 by 7 LED matrices at a
local surplus store, pictured below, so I figured another fun beginner project would be creating a clone of pong using a
few of these for the screen.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://davepedu.com/blog/2015/03/17/arduino-pong-part-1-the-display/img/led_matrix2_hu_1aba5875d30003ac.jpg" alt="5 by 7 LED matrix"/&gt;&lt;/p&gt;
&lt;p&gt;After some quick research - googling the part number - I found a datasheet, read it, and learned that the LEDs in these
units are arranged in a grid pattern, with rows sharing anodes and columns sharing cathodes. Meaning, to light up all
the LEDs in a single row, we provide power to only that row and ground all seven columns. The way this is designed, we
can decide what LEDs we want lit in only a single row (or column) at a time. So, when an image is displayed using the
entire matrix what actually happens is a single column/row is lit up at a single instant and this is applied to reach
row as are scanned down through hundreds of times per second, so it looks to the human eye that the LEDs are on
constantly. This is called the persistence of vision effect. Another nice advantage of this is power savings - even
though an LED in the grid appears to be on constantly, it&amp;rsquo;s actually only on for 1/5th of real time (or 1/7ths depending
on how you want to use the matrix - scanning by row or column).&lt;/p&gt;
&lt;p&gt;I wanted a bigger screen than a single 5 by 7 matrix, so I&amp;rsquo;m stacking two right next to each other to form a 10 by 7
display. Since we now have 10 rows, we have 10 input pins. I can tie the ground (output) connection of correlating
columns together so the entire screen still has only 7 ground pins. So, however we choose to run the screen&amp;rsquo;s logic, we
will need to &amp;ldquo;push&amp;rdquo; 17 bits to the screen - 10 to describe the input state, and 7 to describe the output state. Let&amp;rsquo;s
focus on setting that up first.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://davepedu.com/blog/2015/03/17/arduino-pong-part-1-the-display/img/chips2_hu_39a7f5248c89a035.jpg" alt="Microchips I&amp;rsquo;ll be using"/&gt;&lt;/p&gt;
&lt;p&gt;I decided to use a 74HC shift register, pictured on the right above. This chip provides 8 outputs that we can control
using just three inputs. The shift register has an internal state, known as a register. When we &amp;ldquo;push&amp;rdquo; bits into the
chip they are added at the beginning of the register, and existing bits cascade down (bit 0 moves to position 1, bit 1
moves to position 2, etc. The bit in the last position is dropped). Our data bits are signaled at pin 14, known as &lt;code&gt;SI&lt;/code&gt;
or &amp;ldquo;serial data input&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;To tell the chip that there&amp;rsquo;s a bit waiting on &lt;code&gt;SI&lt;/code&gt;, we change pin 12 (&lt;code&gt;RCK&lt;/code&gt; or &amp;ldquo;storage register clock input&amp;rdquo;) from 0v
(low) to 5v (high). The chip recognizes this change and copies the single bit from &lt;code&gt;SI&lt;/code&gt; to the internal register. Then,
when we want the chips&amp;rsquo; 8 output pins to reflect the state of the internal register, we simply change pin 11 (&lt;code&gt;SCK&lt;/code&gt; or
&amp;ldquo;shift register clock input&amp;rdquo;) to high. Again, the chip recognizes this change and updates the output.&lt;/p&gt;
&lt;p&gt;There&amp;rsquo;s also pins to clear the register or toggle the chips ability to output anything at all but for we don&amp;rsquo;t need that
functionality. They can be ignored by either tying them to ground or a 5v source, whichever the chip demands.&lt;/p&gt;
&lt;p&gt;However, we will use another feature of the 74HC, located on pin 9 (&lt;code&gt;QH&lt;/code&gt; or &amp;ldquo;serial data output&amp;rdquo;). When the 8ths bit is
cascaded and &amp;ldquo;dropped&amp;rdquo; off the chip, it&amp;rsquo;s actually sent out this pin. If the bit is 1, this pin will go high for an
instant, and stay LOW for a 0. This is exactly the kind of input the chip expects on pin 14, &lt;code&gt;SI&lt;/code&gt;! This lets us chain as
many shift registers as we want - &lt;code&gt;QH&lt;/code&gt; of the leading chip to &lt;code&gt;SI&lt;/code&gt; of the following chip, rinse and repeat. We also need
to connect &lt;code&gt;RCK&lt;/code&gt; and &lt;code&gt;SCK&lt;/code&gt; of all the shift registers used, as the signal to read the next bit and display state all
need to happen in sync.&lt;/p&gt;
&lt;p&gt;Since I need 17 bits to control my screen, I&amp;rsquo;ll need to chain 3 shift registers. On a breadboard, that looks a
like this:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://davepedu.com/blog/2015/03/17/arduino-pong-part-1-the-display/img/registers_hu_81a18ebf9f99e469.jpg" alt="Shift register array"/&gt;&lt;/p&gt;
&lt;p&gt;Quick explanation:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;The dense mixed color wires across the bottom are outputs, as well as the two green wires, and the grey one in
column 57.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The vertical orange wires provide +5v to the chips power input and other pins that need a constant 5v.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The yellow wires ground pins called &amp;ldquo;Enable&amp;rdquo; which literally enable the chip&amp;rsquo;s functionality.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Pin 8 (lower right of each chip) is grounded (not pictured).&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The three blue wires in the upper right are &lt;code&gt;SI&lt;/code&gt;, &lt;code&gt;RCK&lt;/code&gt;, and &lt;code&gt;SCK&lt;/code&gt; (left to right). These would go to
our microcontroller.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The horizontal orange wires is connecting &lt;code&gt;QH&lt;/code&gt; of the leading chip to &lt;code&gt;SI&lt;/code&gt; of the following chip (two instances
of these).&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The horizontal grey wires connect the &lt;code&gt;RCK&lt;/code&gt; pins of all 3 chips together, and the &lt;code&gt;SCK&lt;/code&gt;s all together.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We now have the ability to selectively provide power to the 10 rows of our matrix, but what about grounding the 7
columns? We need to be able to translate the 5v signal from 7 of our bits into a some logic: 5v means ground the
corresponding column, and 0v means leaving the column unconnected. A basic way to do this would be a relay. A relay is
like a pushbutton, but to &amp;ldquo;hold&amp;rdquo; the switch on we provide current to the relay, which it forces through a coil to create
a magnetic field pulling two pieces of metal together to complete a circuit.&lt;/p&gt;
&lt;p&gt;However, there are two problems that prevents using a relay for this case: Relays are slow: it physically moves a piece
of metal to connect the circuit. We need to toggle the state hundreds of times per second; this would wear out a relay
very quickly, if one could even keep up. Second problem, relays require higher current to trigger. More than we can
safely provide from the 74HC registers at least. Sure, we could toggle a transistor using our register, and use that to
control a strong power source, but that&amp;rsquo;s unnecessary complication.&lt;/p&gt;
&lt;p&gt;The solution: Darlington pairs. These are simply two transistors connected in a way where we can toggle grounding of one
input by providing voltage to the other. Transistors require much less power than relays, and can act much faster. We
have 7 outputs to control so we&amp;rsquo;d need 14 transistors in 7 pairs. Unfortunately this would take up a large amount of
physical space, at least 21 rows on our breadboard. Too much.&lt;/p&gt;
&lt;p&gt;Enter the ULN2803 (left, pictured above). This chip contains 8 Darlington pairs is one tiny package - perfect! Mine
happens to be a Toshiba brand, but they come in many, many flavors. We simply connect the 7 outputs from our screen to
pins 12-18, and 7 connections from our shift register bank to input pins 1-7. And, most importantly, ground pin 9. Now,
to write some code!&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-c"&gt;#define DS_PIN 2
#define STCP_PIN 10
#define SHCP_PIN 11
#define NUMREG 17
boolean registers[NUMREG];
boolean screen[10][7] = {
{1,1,1,1,1,1,1},
{1,0,0,0,0,0,1},
{1,0,0,0,0,0,1},
{1,0,0,0,0,0,1},
{1,1,1,1,1,1,1},
{1,0,0,0,0,0,0},
{1,0,0,0,0,0,0},
{1,0,0,0,0,0,0},
{1,0,0,0,0,0,0},
{1,0,0,0,0,0,0}
};
void setup() {
// Mark 3 pins for output
pinMode(DS_PIN, OUTPUT);
pinMode(STCP_PIN, OUTPUT);
pinMode(SHCP_PIN, OUTPUT);
}
void writereg() {
// Writes the 17 registers to the shift register bank
digitalWrite(STCP_PIN, LOW);
for(int i=NUMREG-1;i&gt;=0;i--) {
digitalWrite(SHCP_PIN, LOW);
digitalWrite(DS_PIN, registers[i]);
digitalWrite(SHCP_PIN, HIGH);
}
digitalWrite(STCP_PIN, HIGH);
}
void writescreen() {
// Reads the screen array and manipulates regsters[] for each row
// For each column in the row
for(int i=0;i&lt;=10;i++) {
// First, blank the registers
for(int rs=0;rs&lt;17;rs++) registers[rs]=false;
// Mark the column on
registers[9-i]=true;
for(int r=0;r&lt;7;r++) {
// mark any leds on
if(screen[i][r]) registers[r+10]=true;
}
// Push the result to the screen
writereg();
// Leave this frame on for a short time
delayMicroseconds(100);
}
}
void loop() {
// Write one frame to the screen, forever
writescreen();
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This code is pretty simple too. First, we define which pins are what. Then, create an array of 17 booleans that will be
what we manipulate in the chips memory before pushing it out to the shift registers. Our VRAM, if you will. In my setup,
the first 10 bits represent row state, and the last 7 represent column state. For ease of use, we&amp;rsquo;ll create a 10 by 7
multidimensional array of booleans, 1 to 1 scale with LEDs on our screen. I set it up to show a &amp;ldquo;P&amp;rdquo; (for pong!) by
default, and to confirm all our rows and columns are connected and working.&lt;/p&gt;
&lt;p&gt;In setup(), we tell the arduino which pins are which. Writereg()&amp;rsquo;s purpose is to push our in-memory array of 17 bits out
to the shift registers. Writescreen() calls writereg() once per row of the screen, for one screen &amp;ldquo;refresh&amp;rdquo;. Loop()
calls this forever so we see the P on our screen.&lt;/p&gt;
&lt;p&gt;Bingo!&lt;/p&gt;
&lt;p&gt;&lt;img src="https://davepedu.com/blog/2015/03/17/arduino-pong-part-1-the-display/img/pongp_hu_c4396e5adfaebf3b.jpg" alt="The display showing the letter P"/&gt;&lt;/p&gt;
&lt;p&gt;Coming up in part two: Tracking user input!&lt;/p&gt;</content></item><item><title>nintendo 64 joystick pinout for arduino</title><link>https://davepedu.com/blog/2015/03/11/nintendo-64-joystick-pinout-for-arduino/</link><pubDate>Wed, 11 Mar 2015 00:00:00 +0000</pubDate><author>dave@davepedu.com (dave)</author><guid>https://davepedu.com/blog/2015/03/11/nintendo-64-joystick-pinout-for-arduino/</guid><description>&lt;p&gt;I&amp;rsquo;ve been learning electronics and playing with an &lt;a href="https://arduino.cc/"&gt;Arduino&lt;/a&gt; lately. I realized today I have a
functional, but worn mostly out, Nintendo 64 joystick. For an electronics beginner like me, it would be a fun challenge
to figure out how it works.&lt;/p&gt;
&lt;p&gt;On a side note: I have a spare joystick because I replaced the ones in my Nintendo 64 controllers with a redesigned and
improved replacement stick. The usual getting spots carry such a replacement joystick module for the Nintendo 64. It is
engineered better than the original - it should never wear out!&lt;/p&gt;</description><content>&lt;p&gt;I&amp;rsquo;ve been learning electronics and playing with an &lt;a href="https://arduino.cc/"&gt;Arduino&lt;/a&gt; lately. I realized today I have a
functional, but worn mostly out, Nintendo 64 joystick. For an electronics beginner like me, it would be a fun challenge
to figure out how it works.&lt;/p&gt;
&lt;p&gt;On a side note: I have a spare joystick because I replaced the ones in my Nintendo 64 controllers with a redesigned and
improved replacement stick. The usual getting spots carry such a replacement joystick module for the Nintendo 64. It is
engineered better than the original - it should never wear out!&lt;/p&gt;
&lt;p&gt;Inside of the joystick module, is nice and simple single-sided board. I couldn&amp;rsquo;t conjure up a datasheet from googling
markings on any single component on the inside, so we&amp;rsquo;re on our own! The circuitry inside looked like this:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://davepedu.com/blog/2015/03/11/nintendo-64-joystick-pinout-for-arduino/img/pinout_hu_f640f83e440bec43.png" alt="Joystick PCB with traces"/&gt;&lt;/p&gt;
&lt;p&gt;Simply put, pins 2 and 3 are power and ground (respectively), 1 &amp;amp; 4 are data for left/right, and 5 &amp;amp; 6 for up/down. I
measured approximately 3.3 volts across pins 2 &amp;amp; 3 in a working controller, so we&amp;rsquo;re in luck! The arduino provides a
3.3v rail. I measured that the joystick circuit pulls about 15mA, so we&amp;rsquo;re more than in the clear to power it from the
Arduino. R1 / R2 are 320Ω; I didn&amp;rsquo;t have equipment to measure C1 / C2.&lt;/p&gt;
&lt;p&gt;In the image above, two slotted discs pass through the cutouts near U1 and U2, one for each axis. The movement of the
joystick is geared to each slotted disk, so the precision actually looks pretty high.&lt;/p&gt;
&lt;p&gt;I also don&amp;rsquo;t own an oscilloscope so figuring out the format that the sensors return data could be pretty tricky -
guesswork, really. But again, we&amp;rsquo;re in luck! Someone&amp;rsquo;s created an easy way to &lt;a href="https://www.instructables.com/id/Arduino-Oscilloscope-poor-mans-Oscilloscope/"&gt;use the arduino as a basic
oscilloscope&lt;/a&gt;! Aren&amp;rsquo;t general purpose
processors awesome?&lt;/p&gt;
&lt;p&gt;However, viewing the output from any data pin all looked the same - a flat HIGH or LOW signal when the stick is moving,
and rapid oscilation between high and low during motion. The demo code only supports a single input. Theoretically, it
could be expanded to 6 as the Arduino contains 6 analog inputs, but I&amp;rsquo;m not wanting to dive into that yet. After some
research, I found that the sensors in the joystick appear to be two linear sensors! This kind of sensor uses optics to
track when a slotted medium interrupts some beams of light and encodes the result into two waveforms who&amp;rsquo;s period
indicates speed of motion and offset from eachother determines direction of motion.
&lt;a href="https://www.youtube.com/watch?v=0QLZCfqUeg4"&gt;This video&lt;/a&gt; does a great job of explaining it, and even provides
sample code.&lt;/p&gt;
&lt;p&gt;So, how to use this component with an Arduino? Pins 2 &amp;amp; 3 can be connected directly to the 3.3v rail &amp;amp; ground. 1 &amp;amp; 4 can
go to any digital inputs; 5 &amp;amp; 6 must go to digital inputs that support interrupts.&lt;/p&gt;
&lt;p&gt;I modified code from the video linked above to create the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-c"&gt;#define encoderIx 2
#define encoderQx 3
#define encoderIy 18
#define encoderQy 19
volatile int countx;
volatile int county;
void setup() {
Serial.begin(9600);
countx=0;
county=0;
pinMode(encoderIx, INPUT);
pinMode(encoderQx, INPUT);
pinMode(encoderIy, INPUT);
pinMode(encoderQy, INPUT);
attachInterrupt(0, handleEncoderX, CHANGE);
attachInterrupt(5, handleEncoderY, CHANGE);
}
void loop() {
Serial.print("X: ");
Serial.print(countx);
Serial.print(" Y: ");
Serial.println(county);
delay(10);
}
void handleEncoderX() {
if(digitalRead(encoderIx) == digitalRead(encoderQx)) {
countx++;
} else {
countx--;
}
}
void handleEncoderY() {
if(digitalRead(encoderIy) == digitalRead(encoderQy)) {
county++;
} else {
county--;
}
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;How this works is pretty simple - X and Y are handled separately, and stored in two ints. We&amp;rsquo;ll look at just a single
axis. When the signal to the interrupt-supporting pin is changed, it is compared to the other pin on the same axis. The
joystick&amp;rsquo;s linear encoders express forward/backward as one wave lagging or leading the other - and at the instant the
interrupt function is ran, comparing the interrupt pin to the 2nd input from the same sensor allows us to determine
this. Depending if the other input is high or low, we either increment or decrement the position counter for this axis.&lt;/p&gt;
&lt;p&gt;The sample code above will print the X and Y position of the stick to the arduino&amp;rsquo;s serial console. I used an arduino
mega with the code above; the interrupt-supporting pin numbers might not necessarily be the same for other
arduino models.&lt;/p&gt;
&lt;p&gt;Build anything with this? &lt;a href="https://davepedu.com/contact/"&gt;Let me know&lt;/a&gt;, and I&amp;rsquo;ll post it here.&lt;/p&gt;
&lt;p&gt;Happy hacking!&lt;/p&gt;</content></item><item><title>coast to coast</title><link>https://davepedu.com/photos/2014/10/31/coast-to-coast/</link><pubDate>Thu, 30 Oct 2014 00:00:00 +0000</pubDate><author>dave@davepedu.com (dave)</author><guid>https://davepedu.com/photos/2014/10/31/coast-to-coast/</guid><description>&lt;p&gt;In 2014, I graduated from the Rochester Institute of Technology, and shortly moved to the Bay Area, California. I drove
my car, filled with all of my possessions, from New York to California. These are some of the sights I saw along
the way.&lt;/p&gt;
&lt;h2 id="first-stop"&gt;First stop!&lt;/h2&gt;
&lt;p&gt;&lt;img src="https://davepedu.com/photos/2014/10/31/coast-to-coast/img/2014-06-12_09.13.03_hu_3dd89eacb461be.jpg" alt="First stop!"/&gt;&lt;/p&gt;
&lt;p&gt;Our first stop was at the Indianapolis Motor Speedway Museum, where we saw the 1911 Marmon &amp;ldquo;Wasp&amp;rdquo;, winner of the first
Indianapolis 500, as well as a few other classic race cars.&lt;/p&gt;</description><content>&lt;p&gt;In 2014, I graduated from the Rochester Institute of Technology, and shortly moved to the Bay Area, California. I drove
my car, filled with all of my possessions, from New York to California. These are some of the sights I saw along
the way.&lt;/p&gt;
&lt;h2 id="first-stop"&gt;First stop!&lt;/h2&gt;
&lt;p&gt;&lt;img src="https://davepedu.com/photos/2014/10/31/coast-to-coast/img/2014-06-12_09.13.03_hu_3dd89eacb461be.jpg" alt="First stop!"/&gt;&lt;/p&gt;
&lt;p&gt;Our first stop was at the Indianapolis Motor Speedway Museum, where we saw the 1911 Marmon &amp;ldquo;Wasp&amp;rdquo;, winner of the first
Indianapolis 500, as well as a few other classic race cars.&lt;/p&gt;
&lt;h2 id="kansas"&gt;Kansas&lt;/h2&gt;
&lt;p&gt;&lt;img src="https://davepedu.com/photos/2014/10/31/coast-to-coast/img/IMG_5557_hu_ffa1cea3507a3f52.jpg" alt="Kansas"/&gt;&lt;/p&gt;
&lt;p&gt;&amp;hellip;Is less flat than I was led to believe, but still pretty flat. And thousands of these windmills.&lt;/p&gt;
&lt;h2 id="castle-rock-co"&gt;Castle Rock, CO&lt;/h2&gt;
&lt;p&gt;&lt;img src="https://davepedu.com/photos/2014/10/31/coast-to-coast/img/IMG_5608_hu_7787f6b311a2a54c.jpg" alt="Castle Rock, CO"/&gt;&lt;/p&gt;
&lt;p&gt;Our third stop. An adorable town south of Denver.&lt;/p&gt;
&lt;h2 id="climbing-the-rock"&gt;Climbing the rock&lt;/h2&gt;
&lt;p&gt;&lt;img src="https://davepedu.com/photos/2014/10/31/coast-to-coast/img/IMG_5684_hu_1c04f3d5da4ba17f.jpg" alt="Climbing the rock"/&gt;&lt;/p&gt;
&lt;p&gt;We climbed the &amp;lsquo;Castle Rock&amp;rsquo;. Great view of what was going on in town.&lt;/p&gt;
&lt;h2 id="looking-west"&gt;Looking west&lt;/h2&gt;
&lt;p&gt;&lt;img src="https://davepedu.com/photos/2014/10/31/coast-to-coast/img/IMG_5642_hu_2964cf27992125b.jpg" alt="Looking west"/&gt;&lt;/p&gt;
&lt;p&gt;From the top of castle rock, we could clearly see the Rockies looming in the distance, snow peaked in June. We&amp;rsquo;re going over that.&lt;/p&gt;
&lt;h2 id="the-peaks"&gt;The peaks&lt;/h2&gt;
&lt;p&gt;&lt;img src="https://davepedu.com/photos/2014/10/31/coast-to-coast/img/2014-06-14_13.09.43_hu_877bd3a83168a2a2.jpg" alt="The peaks"/&gt;&lt;/p&gt;
&lt;p&gt;We stopped in one of the mountain towns, along our approach to the summit, for gas, near the tipping point.&lt;/p&gt;
&lt;h2 id="nature-in-the-rockies"&gt;Nature in the Rockies&lt;/h2&gt;
&lt;p&gt;&lt;img src="https://davepedu.com/photos/2014/10/31/coast-to-coast/img/IMG_5701_hu_d80f1ac056b09791.jpg" alt="Nature in the Rockies"/&gt;&lt;/p&gt;
&lt;p&gt;Can&amp;rsquo;t beat highway rest stops with a view like this.&lt;/p&gt;
&lt;h2 id="cliff"&gt;Cliff&lt;/h2&gt;
&lt;p&gt;&lt;img src="https://davepedu.com/photos/2014/10/31/coast-to-coast/img/IMG_5752_hu_3a238c5219bde65d.jpg" alt="Cliff"/&gt;&lt;/p&gt;
&lt;p&gt;I think this was on the west side of the Rockies.&lt;/p&gt;
&lt;h2 id="utah"&gt;Utah&lt;/h2&gt;
&lt;p&gt;&lt;img src="https://davepedu.com/photos/2014/10/31/coast-to-coast/img/IMG_5797_hu_d5d62a593a973581.jpg" alt="Utah"/&gt;&lt;/p&gt;
&lt;p&gt;Made it into Utah. Amazing how much more dry it was. (And my awesome car!)&lt;/p&gt;
&lt;h2 id="more-utah"&gt;More Utah&lt;/h2&gt;
&lt;p&gt;&lt;img src="https://davepedu.com/photos/2014/10/31/coast-to-coast/img/IMG_5816_hu_9e2a32a9dd6cc832.jpg" alt="More Utah"/&gt;&lt;/p&gt;
&lt;h2 id="crazy-rocks-in-utah"&gt;Crazy rocks in Utah&lt;/h2&gt;
&lt;p&gt;&lt;img src="https://davepedu.com/photos/2014/10/31/coast-to-coast/img/IMG_5841_hu_e25ea0b843b33cd2.jpg" alt="Crazy rocks in Utah"/&gt;&lt;/p&gt;
&lt;h2 id="salt-flats-in-nevada"&gt;Salt flats in Nevada&lt;/h2&gt;
&lt;p&gt;&lt;img src="https://davepedu.com/photos/2014/10/31/coast-to-coast/img/IMG_5933_hu_6ef345572f03080d.jpg" alt="Salt flats in Nevada"/&gt;&lt;/p&gt;
&lt;p&gt;Flats isn&amp;rsquo;t an exaggeration. Hard to walk on, too. It&amp;rsquo;s difficult like beach sand, but slippery and sticky.&lt;/p&gt;
&lt;h2 id="honda"&gt;Honda&lt;/h2&gt;
&lt;p&gt;&lt;img src="https://davepedu.com/photos/2014/10/31/coast-to-coast/img/IMG_5937_hu_7b42d4b1a99fbe4.jpg" alt="Honda"/&gt;&lt;/p&gt;
&lt;p&gt;Another shot :-)&lt;/p&gt;
&lt;h2 id="2-days-later-in-california"&gt;2 days later, in California&lt;/h2&gt;
&lt;p&gt;&lt;img src="https://davepedu.com/photos/2014/10/31/coast-to-coast/img/IMG_5950_hu_bdb0f449d7a6941b.jpg" alt="2 days later, in California"/&gt;&lt;/p&gt;
&lt;p&gt;Emigrant Gap&lt;/p&gt;
&lt;h2 id="southwest-of-the-bay"&gt;Southwest of the bay&lt;/h2&gt;
&lt;p&gt;&lt;img src="https://davepedu.com/photos/2014/10/31/coast-to-coast/img/IMG_5989_hu_68404d3bddb13742.jpg" alt="Southwest of the bay"/&gt;&lt;/p&gt;
&lt;p&gt;Looking directly at the area I&amp;rsquo;ll be living/working in!&lt;/p&gt;
&lt;h2 id="pacific-ocean"&gt;Pacific Ocean&lt;/h2&gt;
&lt;p&gt;&lt;img src="https://davepedu.com/photos/2014/10/31/coast-to-coast/img/IMG_6013_hu_7dfbe862cd7bdc9a.jpg" alt="Pacific Ocean"/&gt;&lt;/p&gt;
&lt;p&gt;Finally ran out of road.&lt;/p&gt;
&lt;h2 id="beach-cave"&gt;Beach Cave&lt;/h2&gt;
&lt;p&gt;&lt;img src="https://davepedu.com/photos/2014/10/31/coast-to-coast/img/IMG_6045_hu_92fdc4cfa821e3a3.jpg" alt="Beach Cave"/&gt;&lt;/p&gt;
&lt;h2 id="obligatory-golden-gate-photo"&gt;Obligatory Golden Gate photo&lt;/h2&gt;
&lt;p&gt;&lt;img src="https://davepedu.com/photos/2014/10/31/coast-to-coast/img/IMG_6220_hu_81e0ca9b4f074a19.jpg" alt="Obligatory Golden Gate photo"/&gt;&lt;/p&gt;
&lt;p&gt;You knew it was coming.&lt;/p&gt;
&lt;h2 id="learn2bridge"&gt;Learn2bridge&lt;/h2&gt;
&lt;p&gt;&lt;img src="https://davepedu.com/photos/2014/10/31/coast-to-coast/img/IMG_6277_hu_9d29fafef222360a.jpg" alt="Learn2bridge"/&gt;&lt;/p&gt;
&lt;h2 id="downtown-san-francisco"&gt;Downtown San Francisco&lt;/h2&gt;
&lt;p&gt;&lt;img src="https://davepedu.com/photos/2014/10/31/coast-to-coast/img/IMG_6309_hu_817037e2980cf137.jpg" alt="Downtown San Francisco"/&gt;&lt;/p&gt;
&lt;p&gt;Shot from Coit tower&lt;/p&gt;
&lt;h2 id="lombard-street"&gt;Lombard Street&lt;/h2&gt;
&lt;p&gt;&lt;img src="https://davepedu.com/photos/2014/10/31/coast-to-coast/img/IMG_6352_hu_fe42e9b4b2105897.jpg" alt="Lombard Street"/&gt;&lt;/p&gt;
&lt;p&gt;You can bet I went and drove down it after.&lt;/p&gt;
&lt;h2 id="santa-cruz"&gt;Santa Cruz&lt;/h2&gt;
&lt;p&gt;&lt;img src="https://davepedu.com/photos/2014/10/31/coast-to-coast/img/2014-06-17_20.58.07_hu_93acb2136fb3b5a0.jpg" alt="Santa Cruz"/&gt;&lt;/p&gt;
&lt;p&gt;South of the Bay&lt;/p&gt;
&lt;h2 id="my-new-office"&gt;My new office!&lt;/h2&gt;
&lt;p&gt;&lt;img src="https://davepedu.com/photos/2014/10/31/coast-to-coast/img/2014-07-07_17.26.23_hu_290510f2e6484bd4.jpg" alt="My new office!"/&gt;&lt;/p&gt;
&lt;p&gt;Yeah buddy.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;All in all, a wonderful drive that took about a week. I was lucky enough to have my father come along with my for the
trip. It was a lot of fun, and it was great to see this beautiful country.&lt;/p&gt;</content></item><item><title>internet radio DVR</title><link>https://davepedu.com/projects/2014/08/30/internet-radio-dvr/</link><pubDate>Sat, 30 Aug 2014 00:00:00 +0000</pubDate><author>dave@davepedu.com (dave)</author><guid>https://davepedu.com/projects/2014/08/30/internet-radio-dvr/</guid><description>&lt;p&gt;Anyone who remembers watching cable TV also remembers the DVR - Digital Video Recorder - a device that lets you record
live TV and save it for later playback. It&amp;rsquo;s useful for catching episodes of shows that air when you are busy or
sporting events overseas that air late at night for your timezone.&lt;/p&gt;
&lt;p&gt;Well, I listen to a handful of shows that only appear on live internet radio, and I wanted a similar way to record and
play them back later. So, I wrote this application.&lt;/p&gt;</description><content>&lt;p&gt;Anyone who remembers watching cable TV also remembers the DVR - Digital Video Recorder - a device that lets you record
live TV and save it for later playback. It&amp;rsquo;s useful for catching episodes of shows that air when you are busy or
sporting events overseas that air late at night for your timezone.&lt;/p&gt;
&lt;p&gt;Well, I listen to a handful of shows that only appear on live internet radio, and I wanted a similar way to record and
play them back later. So, I wrote this application.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://davepedu.com/projects/2014/08/30/internet-radio-dvr/img/streamrecord_hu_dc1e25b48aa9dd1d.png" alt="Screenshot"/&gt;&lt;/p&gt;
&lt;p&gt;This application provides a web interface where you may configure audio stream URLs and the schedules at which they
should be recorded. Each recording is saved as an MP3 file, and an RSS feed that can be consumed by any Podcast player
app provides easy access.&lt;/p&gt;
&lt;p&gt;Under the hood, this application uses FFmpeg to do most of the media recording and manipulation.&lt;/p&gt;
&lt;p&gt;Source code is available on my Git instance:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Base application: &lt;a href="https://git.davepedu.com/dave/streamrecord"&gt;https://git.davepedu.com/dave/streamrecord&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Docker image: &lt;a href="https://git.davepedu.com/dave/docker-streamrecord"&gt;https://git.davepedu.com/dave/docker-streamrecord&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;</content></item><item><title>coda 2, git, and drupal</title><link>https://davepedu.com/blog/2014/05/21/coda-2-git-and-drupal/</link><pubDate>Wed, 21 May 2014 00:00:00 +0000</pubDate><author>dave@davepedu.com (dave)</author><guid>https://davepedu.com/blog/2014/05/21/coda-2-git-and-drupal/</guid><description>&lt;p&gt;&lt;a href="https://panic.com/coda"&gt;Coda 2&lt;/a&gt; (Previously, just Coda) has been my &amp;ldquo;daily driver&amp;rdquo; suite for web development for around
4 years now. It&amp;rsquo;s an absolutely wonderful editor by Panic, and well worth the price tag in my opinion. It has an editor,
(s)ftp client, git client, terminal, mysql database manager, and a whole lot more all in one package.&lt;/p&gt;
&lt;p&gt;My workflow when developing a Drupal-based website is usually something like this:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Create a new drupal install on my local machine.&lt;/p&gt;</description><content>&lt;p&gt;&lt;a href="https://panic.com/coda"&gt;Coda 2&lt;/a&gt; (Previously, just Coda) has been my &amp;ldquo;daily driver&amp;rdquo; suite for web development for around
4 years now. It&amp;rsquo;s an absolutely wonderful editor by Panic, and well worth the price tag in my opinion. It has an editor,
(s)ftp client, git client, terminal, mysql database manager, and a whole lot more all in one package.&lt;/p&gt;
&lt;p&gt;My workflow when developing a Drupal-based website is usually something like this:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Create a new drupal install on my local machine.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Build the theme and database setup locally&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;When it&amp;rsquo;s ready for a demo or for whatever displaying, transfer the theme &amp;amp; site files to (typically) the multisite
install where the site will live. This usually means manually transferring the local database as well. If you&amp;rsquo;re
familiar with drupal you&amp;rsquo;ll know this means tracking down and changing Drupal&amp;rsquo;s internal filesystem settings.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Repeat steps 2 &amp;amp; 3 until the site is done.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;I wanted to remove some of the manual steps, and use Git for version control. I use my
&lt;a href="https://git.davepedu.com/dave"&gt;own git instance&lt;/a&gt;, but you can also use &lt;a href="https://bitbucket.org/"&gt;Bitbucket&lt;/a&gt;. So, here&amp;rsquo;s
how to do that in Coda 2! This is based on the assumption you already have a site started, if you&amp;rsquo;re starting from
scratch it would be the same procedure, though. I&amp;rsquo;ll also assume you&amp;rsquo;re familiar with at least the basics of using a
unix/linux command line.&lt;/p&gt;
&lt;h1 id="set-up-a-local-git-repository-for-the-drupal-site"&gt;Set up a Local Git Repository for the Drupal site&lt;/h1&gt;
&lt;p&gt;I&amp;rsquo;ll admit I&amp;rsquo;m not great at using command line git, and have usually stuck with using graphical clients such as Gitbox.
But I promise, after this initial setup you can stick with the same tools!&lt;/p&gt;
&lt;p&gt;First, in a terminal, change directory to your site&amp;rsquo;s main folder. This would be it&amp;rsquo;s directory in the drupal Sites
folder, that&amp;rsquo;s typically named after your domain. I&amp;rsquo;ll be using this blog&amp;rsquo;s setup as an example. We will first create a
&lt;strong&gt;.gitignore&lt;/strong&gt; file so items that don&amp;rsquo;t need to be tracked (such as configuration and temporary files) won&amp;rsquo;t be. My
.gitignore typically looks like:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-"&gt;files/css
files/ctools
files/custom_search
files/js
files/less
files/styles
settings.php&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Including &lt;code&gt;settings.php&lt;/code&gt; is the crucial thing here, this is where the database password is stored! Now that it is being
kept secret, we can create the local git repository:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-"&gt;git init&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This created a basic git repository based in the current directory.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-"&gt;git remote add origin https://dpedu@bitbucket.org/dpedu/projectname.git&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Tell git that this project&amp;rsquo;s origin (or master repository, so to speak) is located on Bitbucket. Substituting Github or
another git host is doable.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-"&gt;git add --all&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Add all existing files to version control&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-"&gt;git commit -m 'Initial commit'&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Commit all existing files&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-"&gt;git push -u origin --all&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Transfer the commit we just made to the origin, aka Bitbucket.&lt;/p&gt;
&lt;p&gt;Great! Now we have a local Git repository and a master one on a hosted Git service.&lt;/p&gt;
&lt;h1 id="set-up-coda-2-to-use-our-repositories"&gt;Set up Coda 2 to use our repositories&lt;/h1&gt;
&lt;p&gt;The Coda setup is pretty typical. You want to set Local Root to the same directory where we just made a local
repository, though.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://davepedu.com/blog/2014/05/21/coda-2-git-and-drupal/img/dpedu-config_hu_7b604ef8f7d72898.png" alt=""/&gt;&lt;/p&gt;
&lt;p&gt;Next, click the Source icon.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://davepedu.com/blog/2014/05/21/coda-2-git-and-drupal/img/dpedu-config-git_hu_568939d966750f16.png" alt=""/&gt;&lt;/p&gt;
&lt;p&gt;On this screen, you&amp;rsquo;ll only need to change Off to On, select &lt;strong&gt;origin&lt;/strong&gt; in the dropdown, and put your Bitbucket password
in the password field. Coda should automatically know the git URL, as it&amp;rsquo;s saved in the repository&amp;rsquo;s metdata.&lt;/p&gt;
&lt;p&gt;Great! You can save this profile and get to work!&lt;/p&gt;
&lt;h1 id="using-coda"&gt;Using Coda&lt;/h1&gt;
&lt;p&gt;Once you open up the profile we just created, you&amp;rsquo;ll see the local filesystem and remote filesystem within Coda. I like
to use the righthand sidebar for just about everything. The files tab shows your theme files, the version control tab
(Coda calls it &amp;ldquo;SCM&amp;rdquo;) is used for working with git, and the publishing tab is used for pushing your local files to the
remote server. You can set up git hooks to do this pushing automatically, but I prefer not to when working with Drupal.&lt;/p&gt;
&lt;p&gt;After you save changes to a file in Coda, it will be listed in the version control and publishing tab, along with a
counter the number of files changed can be seen at a glance.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://davepedu.com/blog/2014/05/21/coda-2-git-and-drupal/img/coda-pending_hu_4aca91ad05716a49.png" alt=""/&gt;&lt;/p&gt;
&lt;p&gt;Right clicking on the file reveals options to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Compare it to the latest or any revison using FileMerge, a tool shipped with OS X.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Add it to the next commit&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Commit it right now&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Revert, branch, merge and all the other git actions.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Adding &amp;amp; Committing it work exactly how you would think. At the bottom of this sidebar is the button to push any local
commits to the origin (internet-host hosted) repository.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://davepedu.com/blog/2014/05/21/coda-2-git-and-drupal/img/coda-push_hu_750117cd0071d393.png" alt=""/&gt;&lt;/p&gt;
&lt;p&gt;Once you select this, your committed changes are pushed to bitbucket!&lt;/p&gt;
&lt;p&gt;&lt;img src="https://davepedu.com/blog/2014/05/21/coda-2-git-and-drupal/img/coda-bitbucket_hu_674b8c1fbdd4fea1.png" alt=""/&gt;&lt;/p&gt;
&lt;p&gt;Cool! Your development setup is now fully integrated with git. Want to push your local version to the remote web server,
configured when settng up the site profile in Coda? The publishing tab has the option to Publish All, which uploads any
changed files.&lt;/p&gt;
&lt;h1 id="the-database"&gt;The database&lt;/h1&gt;
&lt;p&gt;Drupal heavily relies on a database for all of it&amp;rsquo;s content, and probably far too much configuration. Moving your local
database to a live server can be a bit of a pain, so here&amp;rsquo;s a little script I use to help make it easier:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-shell"&gt;#!/bin/sh
# Dump database
/Applications/XAMPP/xamppfiles/bin/mysqldump --hex-blob --extended-insert=FALSE -u localmysqluser -plocalmysqlpassword localdatabasename &gt; dump_raw.sql
# Remove cache insert queries
sed '/^INSERT INTO `drupal_cache/ d' &lt; dump_raw.sql &gt; dump.1.sql
# Remove watchdog insert queries
sed '/^INSERT INTO `drupal_watchdog`/ d' &lt; dump.1.sql &gt; dump.sql
# Remove temp files created above
rm dump_raw.sql dump.1.sql
# Append queries to update some drupal settings to values that will work on the live server
echo "UPDATE \`drupal_variable\` SET \`value\`='s:27:\\\"sites/dpedu.io/privatefiles\\\";' WHERE \`name\`='file_private_path';" &gt;&gt; dump.sql
echo "UPDATE \`drupal_variable\` SET \`value\`='s:20:\\\"sites/dpedu.io/files\\\";' WHERE \`name\`='file_public_path';" &gt;&gt; dump.sql
echo "UPDATE \`drupal_variable\` SET \`value\`='s:4:\\\"/tmp\\\";' WHERE \`name\`='file_temporary_path';" &gt;&gt; dump.sql
# Transfer SQL dump file to my homedir on saturn
scp dump.sql dave@dpedu.io:~/
# Connect to server, import the dump file, delete the file on server
ssh dave@dpedu.io 'mysql -u remotemysqluser remotedatabasename &lt; dump.sql ; rm dump.sql'
# Delete local file
rm dump.sql
# Done !
echo "Done"&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Neat! So what does this do?&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Dumps your local database to &amp;lsquo;dump_raw.sql&amp;rsquo;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Using sed (and some temporary files), removes inserts to any of the various drupal_cache and drupal_watchdog tables. The cache tables need to be cleared in such a move anyways, and the watchdog log messages really aren&amp;rsquo;t too vital and often more than double the dump size.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Adds 3 queries to update drupal&amp;rsquo;s internal filesystem information to be executed after the database is imported.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Transfers the sql dump file to the remote server. My SQL server isn&amp;rsquo;t accessible from the internet. Security!&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;On the remote server, initiates an import of the dump file.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Cleans up after itself.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;So now your local install is perfectly mirrored, within seconds.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Found this useful? Have a better setup? &lt;a href="https://davepedu.com/contact/"&gt;Write to me&lt;/a&gt; to brag about it.&lt;/p&gt;</content></item><item><title>RIT Project Lead The Way Website</title><link>https://davepedu.com/projects/2014/03/30/rit-project-lead-the-way-website/</link><pubDate>Sun, 30 Mar 2014 00:00:00 +0000</pubDate><author>dave@davepedu.com (dave)</author><guid>https://davepedu.com/projects/2014/03/30/rit-project-lead-the-way-website/</guid><description>&lt;p&gt;&lt;img src="https://davepedu.com/projects/2014/03/30/rit-project-lead-the-way-website/img/pltw-full_hu_dc9295490c83c6b0.png" alt="Screenshot"/&gt;&lt;/p&gt;
&lt;p&gt;A website Project Lead The Way Website, where the Rochester Institute of Technology serves as New York State&amp;rsquo;s
National Affiliate.&lt;/p&gt;
&lt;p&gt;This website has since been decommissioned and replaced with an updated design, but my work may still be viewed
via &lt;a href="https://web.archive.org/web/20140330001627/https://www.rit.edu/emcs/pltw/"&gt;archive.org&lt;/a&gt;.&lt;/p&gt;</description><content>&lt;p&gt;&lt;img src="https://davepedu.com/projects/2014/03/30/rit-project-lead-the-way-website/img/pltw-full_hu_dc9295490c83c6b0.png" alt="Screenshot"/&gt;&lt;/p&gt;
&lt;p&gt;A website Project Lead The Way Website, where the Rochester Institute of Technology serves as New York State&amp;rsquo;s
National Affiliate.&lt;/p&gt;
&lt;p&gt;This website has since been decommissioned and replaced with an updated design, but my work may still be viewed
via &lt;a href="https://web.archive.org/web/20140330001627/https://www.rit.edu/emcs/pltw/"&gt;archive.org&lt;/a&gt;.&lt;/p&gt;</content></item><item><title>RIT University Arenas Website</title><link>https://davepedu.com/projects/2014/03/28/rit-university-arenas-website/</link><pubDate>Fri, 28 Mar 2014 00:00:00 +0000</pubDate><author>dave@davepedu.com (dave)</author><guid>https://davepedu.com/projects/2014/03/28/rit-university-arenas-website/</guid><description>&lt;p&gt;&lt;img src="https://davepedu.com/projects/2014/03/28/rit-university-arenas-website/img/arenas-full_hu_3c6921bd65db3fa2.png" alt="Screenshot"/&gt;&lt;/p&gt;
&lt;p&gt;A massive Drupal website, that took us nearly two months to design and develop! It&amp;rsquo;s fully responsive, has some great
animation and attention to detail, and a good number of photos from previous events at RIT.&lt;/p&gt;
&lt;p&gt;This website has since been decommissioned and replaced with an updated design, but my work may still be viewed
via &lt;a href="https://web.archive.org/web/20140722131131/https://www.rit.edu/fa/arenas/"&gt;archive.org&lt;/a&gt;.&lt;/p&gt;</description><content>&lt;p&gt;&lt;img src="https://davepedu.com/projects/2014/03/28/rit-university-arenas-website/img/arenas-full_hu_3c6921bd65db3fa2.png" alt="Screenshot"/&gt;&lt;/p&gt;
&lt;p&gt;A massive Drupal website, that took us nearly two months to design and develop! It&amp;rsquo;s fully responsive, has some great
animation and attention to detail, and a good number of photos from previous events at RIT.&lt;/p&gt;
&lt;p&gt;This website has since been decommissioned and replaced with an updated design, but my work may still be viewed
via &lt;a href="https://web.archive.org/web/20140722131131/https://www.rit.edu/fa/arenas/"&gt;archive.org&lt;/a&gt;.&lt;/p&gt;</content></item><item><title>RIT Board of Trustees Intranet Portal</title><link>https://davepedu.com/projects/2014/04/15/rit-board-of-trustees-intranet-portal/</link><pubDate>Tue, 28 Jan 2014 00:00:00 +0000</pubDate><author>dave@davepedu.com (dave)</author><guid>https://davepedu.com/projects/2014/04/15/rit-board-of-trustees-intranet-portal/</guid><description>&lt;p&gt;The RIT Board of Trustees Intranet Portal is a CodeIgniter based web application that I designed and developed. It is an
intranet website that is used to disseminate meeting materials for the Rochester Institute of Technology&amp;rsquo;s Board of
Trustees meetings, as well as poll its members for attendance, feedback, etc.&lt;/p&gt;
&lt;p&gt;Code from this project was re-used for another project, the RIT President&amp;rsquo;s Roundtable, an advisory committee for the
President of RIT.&lt;/p&gt;</description><content>&lt;p&gt;The RIT Board of Trustees Intranet Portal is a CodeIgniter based web application that I designed and developed. It is an
intranet website that is used to disseminate meeting materials for the Rochester Institute of Technology&amp;rsquo;s Board of
Trustees meetings, as well as poll its members for attendance, feedback, etc.&lt;/p&gt;
&lt;p&gt;Code from this project was re-used for another project, the RIT President&amp;rsquo;s Roundtable, an advisory committee for the
President of RIT.&lt;/p&gt;</content></item><item><title>RIT Center for Urban Entrepreneurship Website</title><link>https://davepedu.com/projects/2014/01/28/rit-center-for-urban-entrepreneurship-website/</link><pubDate>Tue, 28 Jan 2014 00:00:00 +0000</pubDate><author>dave@davepedu.com (dave)</author><guid>https://davepedu.com/projects/2014/01/28/rit-center-for-urban-entrepreneurship-website/</guid><description>&lt;p&gt;&lt;img src="https://davepedu.com/projects/2014/01/28/rit-center-for-urban-entrepreneurship-website/img/cue-full_hu_fb38539a768835b1.png" alt="Screenshot"/&gt;&lt;/p&gt;
&lt;p&gt;Another beautiful Drupal website, this time for RIT&amp;rsquo;s Center for Urban Entrepreneurship in downtown Rochester.&lt;/p&gt;
&lt;p&gt;This one was designed in-house at &lt;a href="https://www.rit.edu/production"&gt;RIT Production Services&lt;/a&gt;, with a nice Responsive
theme for mobile phones and tablets as well.&lt;/p&gt;
&lt;p&gt;This website has since been decommissioned and replaced with an updated design, but my work may still be viewed
via &lt;a href="https://web.archive.org/web/20140811062842/http://www.rit.edu/business/cue/"&gt;archive.org&lt;/a&gt;.&lt;/p&gt;</description><content>&lt;p&gt;&lt;img src="https://davepedu.com/projects/2014/01/28/rit-center-for-urban-entrepreneurship-website/img/cue-full_hu_fb38539a768835b1.png" alt="Screenshot"/&gt;&lt;/p&gt;
&lt;p&gt;Another beautiful Drupal website, this time for RIT&amp;rsquo;s Center for Urban Entrepreneurship in downtown Rochester.&lt;/p&gt;
&lt;p&gt;This one was designed in-house at &lt;a href="https://www.rit.edu/production"&gt;RIT Production Services&lt;/a&gt;, with a nice Responsive
theme for mobile phones and tablets as well.&lt;/p&gt;
&lt;p&gt;This website has since been decommissioned and replaced with an updated design, but my work may still be viewed
via &lt;a href="https://web.archive.org/web/20140811062842/http://www.rit.edu/business/cue/"&gt;archive.org&lt;/a&gt;.&lt;/p&gt;</content></item><item><title>kennedy space center, cape canaveral, florida</title><link>https://davepedu.com/photos/2014/01/17/kennedy-space-center/</link><pubDate>Fri, 17 Jan 2014 00:00:00 +0000</pubDate><author>dave@davepedu.com (dave)</author><guid>https://davepedu.com/photos/2014/01/17/kennedy-space-center/</guid><description>&lt;p&gt;Last November, I had the opportunity to visit the Kennedy Space Center in Florida, while visiting relatives living in
the area. I had never been there before, so I went for it!&lt;/p&gt;
&lt;p&gt;I may be a tech geek, but these amazing machines fascinate me. The sheer amount of research, time, and trial-and-error
fueling these machines is simply incredible. Staring at the results of the most advanced transportation machines made by
mankind is mind boggling.&lt;/p&gt;</description><content>&lt;p&gt;Last November, I had the opportunity to visit the Kennedy Space Center in Florida, while visiting relatives living in
the area. I had never been there before, so I went for it!&lt;/p&gt;
&lt;p&gt;I may be a tech geek, but these amazing machines fascinate me. The sheer amount of research, time, and trial-and-error
fueling these machines is simply incredible. Staring at the results of the most advanced transportation machines made by
mankind is mind boggling.&lt;/p&gt;
&lt;p&gt;If you have any interested in space exploration or that sort of thing, check it out!&lt;/p&gt;
&lt;h2 id="entry"&gt;Entry&lt;/h2&gt;
&lt;p&gt;&lt;img src="https://davepedu.com/photos/2014/01/17/kennedy-space-center/img/nasa-2013-11-14-10.20_hu_2c34e1be72673bf3.jpg" alt="Entry"/&gt;&lt;/p&gt;
&lt;p&gt;Right at the entrance, we were greeted by an array of rockets used over the years. The specific names of all the models
escape me, but the largest one is the legendary Saturn 5 model - the most powerful machine ever built by man.&lt;/p&gt;
&lt;h2 id="motor-closeup"&gt;Motor Closeup&lt;/h2&gt;
&lt;p&gt;&lt;img src="https://davepedu.com/photos/2014/01/17/kennedy-space-center/img/nasa-2013-11-14-10.24_hu_6e73ff18ba58f3d.jpg" alt="Motor Closeup"/&gt;&lt;/p&gt;
&lt;p&gt;The business end of this rocket looks more like a tangled mess of tubes and wires, but its a very advanced and
complicated machine.&lt;/p&gt;
&lt;h2 id="stone-ball-skymap"&gt;Stone Ball Skymap&lt;/h2&gt;
&lt;p&gt;&lt;img src="https://davepedu.com/photos/2014/01/17/kennedy-space-center/img/nasa-2013-11-14-10.32_hu_dba4914157cdc8e0.jpg" alt="Stone Ball Skymap"/&gt;&lt;/p&gt;
&lt;p&gt;Instead of a boring fountain, the center had a map of the cosmos carved into a 2 ton granite sphere, suspended and
slowly rotating on a water base. With enough force you could spin it a different direction!&lt;/p&gt;
&lt;h2 id="atlantis-base"&gt;Atlantis Base&lt;/h2&gt;
&lt;p&gt;&lt;img src="https://davepedu.com/photos/2014/01/17/kennedy-space-center/img/nasa-2013-11-14-10.35_hu_9eb6ad38b949c88e.jpg" alt="Atlantis Base"/&gt;&lt;/p&gt;
&lt;p&gt;The solid state rockets and liquid fuel tank for the Atlantis Space shuttle, The picture doesn&amp;rsquo;t do any justice to just how massive this is.&lt;/p&gt;
&lt;h2 id="facility-panorama"&gt;Facility Panorama&lt;/h2&gt;
&lt;figure class="center" &gt;
&lt;a href="https://davepedu.com/photos/2014/01/17/kennedy-space-center/img/nasa-2013-11-14-11.26_hu_bc1108b999a9d24c.jpg" target="_blank" rel="noopener"&gt;
&lt;img src="https://davepedu.com/photos/2014/01/17/kennedy-space-center/img/nasa-2013-11-14-11.26_hu_8c80567222a7dd1b.jpg" alt="Facility Panorama" /&gt;
&lt;/a&gt;
&lt;/figure&gt;
&lt;p&gt;A panorma with a view of several facilities. The location this was shot from was formerly a public viewing area for
space launches, but has been deemed too dangerous since then.&lt;/p&gt;
&lt;p&gt;Click the image to see full-size for a better view.&lt;/p&gt;
&lt;h2 id="crawler"&gt;Crawler&lt;/h2&gt;
&lt;p&gt;&lt;img src="https://davepedu.com/photos/2014/01/17/kennedy-space-center/img/nasa-2013-11-14-11.52_hu_4476e3da9435a058.jpg" alt="Crawler"/&gt;&lt;/p&gt;
&lt;p&gt;One of NASA&amp;rsquo;s two land crawlers, currently being outfitted for SpaceX missions.&lt;/p&gt;
&lt;h2 id="vehicle-assembly-plant"&gt;Vehicle Assembly Plant&lt;/h2&gt;
&lt;p&gt;&lt;img src="https://davepedu.com/photos/2014/01/17/kennedy-space-center/img/nasa-2013-11-14-11.57_hu_2fe8971e46f84e96.jpg" alt="Vehicle Assembly Plant"/&gt;&lt;/p&gt;
&lt;p&gt;This building is where every NASA space vehicle is assembled. Again, the picture does no justice to the height of
this building.&lt;/p&gt;
&lt;h2 id="another-crawler"&gt;Another Crawler&lt;/h2&gt;
&lt;p&gt;&lt;img src="https://davepedu.com/photos/2014/01/17/kennedy-space-center/img/nasa-2013-11-14-12.23_hu_609663d782e390e0.jpg" alt="Another Crawler"/&gt;&lt;/p&gt;
&lt;p&gt;Another one of NASA&amp;rsquo;s land crawlers, hiding inside.&lt;/p&gt;
&lt;h2 id="seaside-bunker"&gt;Seaside Bunker&lt;/h2&gt;
&lt;p&gt;&lt;img src="https://davepedu.com/photos/2014/01/17/kennedy-space-center/img/nasa-2013-11-14-12.42_hu_c0b2e082b737325b.jpg" alt="Seaside Bunker"/&gt;&lt;/p&gt;
&lt;p&gt;Looking out into the Atlantic, one of the main launch platforms would have been behind me. According to our guide this
little bunker was used to house sensitive equipment for detecting electricity in the air, as lightning is a grave threat
to launches.&lt;/p&gt;
&lt;h2 id="lightning-towers"&gt;Lightning Towers&lt;/h2&gt;
&lt;p&gt;&lt;img src="https://davepedu.com/photos/2014/01/17/kennedy-space-center/img/nasa-2013-11-14-12.43_hu_68a52f4ebd041c75.jpg" alt="Lightning Towers"/&gt;&lt;/p&gt;
&lt;p&gt;3 lightning towers designed to direct bolts away from the launch pad that they surround.&lt;/p&gt;
&lt;h2 id="saturn-5-rocket-base"&gt;Saturn 5 Rocket Base&lt;/h2&gt;
&lt;p&gt;&lt;img src="https://davepedu.com/photos/2014/01/17/kennedy-space-center/img/nasa-2013-11-14-13.37_hu_a6b1e9f33ca09e93.jpg" alt="Saturn 5 Rocket Base"/&gt;&lt;/p&gt;
&lt;p&gt;The business end of a decomissioned Saturn 5. Walking the length of this rocket takes minutes! 20 tons of fuel per
minute would flow through here.&lt;/p&gt;
&lt;h2 id="saturn-5-second-stage"&gt;Saturn 5, second stage&lt;/h2&gt;
&lt;p&gt;&lt;img src="https://davepedu.com/photos/2014/01/17/kennedy-space-center/img/nasa-2013-11-14-13.43_hu_e47b28ba4e19429.jpg" alt="Saturn 5, second stage"/&gt;&lt;/p&gt;
&lt;h2 id="command-module"&gt;Command Module&lt;/h2&gt;
&lt;p&gt;&lt;img src="https://davepedu.com/photos/2014/01/17/kennedy-space-center/img/nasa-2013-11-14-13.50_hu_f7624686e0a6b3f7.jpg" alt="Command Module"/&gt;&lt;/p&gt;
&lt;h2 id="apollo-guidelines"&gt;Apollo Guidelines&lt;/h2&gt;
&lt;figure class="center" &gt;
&lt;a href="https://davepedu.com/photos/2014/01/17/kennedy-space-center/img/nasa-2013-11-14-14.37_hu_47f1b8d9dd816b3.jpg" target="_blank" rel="noopener"&gt;
&lt;img src="https://davepedu.com/photos/2014/01/17/kennedy-space-center/img/nasa-2013-11-14-14.37_hu_7f3f4a8a087f9ce8.jpg" alt="Apollo Guidelines" /&gt;
&lt;/a&gt;
&lt;/figure&gt;
&lt;p&gt;A list of mission status and abort codes from the Apollo era. Click the image to see full-size for an easier read.&lt;/p&gt;
&lt;h2 id="space-shuttle-atlantis"&gt;Space Shuttle Atlantis&lt;/h2&gt;
&lt;p&gt;&lt;img src="https://davepedu.com/photos/2014/01/17/kennedy-space-center/img/nasa-2013-11-14-15.53_hu_1bfcc28142a51a81.jpg" alt="Space Shuttle Atlantis"/&gt;&lt;/p&gt;
&lt;p&gt;The retired Space Shuttle Atlantis. 🇺🇸 &amp;lsquo;Murica!&lt;/p&gt;
&lt;h2 id="birds"&gt;Birds!&lt;/h2&gt;
&lt;p&gt;&lt;img src="https://davepedu.com/photos/2014/01/17/kennedy-space-center/img/nasa-2013-11-17-08.04_hu_d922218703fbfa59.jpg" alt="Birds!"/&gt;&lt;/p&gt;
&lt;p&gt;That&amp;rsquo;s the last of the space pictures, I took a few quick shots of birds outside my hotel a few days later on the
same trip.&lt;/p&gt;
&lt;h2 id="a-pelican"&gt;A Pelican&lt;/h2&gt;
&lt;p&gt;&lt;img src="https://davepedu.com/photos/2014/01/17/kennedy-space-center/img/nasa-2013-11-17-08.05_hu_fbd47c66aa85a179.jpg" alt="A Pelican"/&gt;&lt;/p&gt;
&lt;p&gt;&amp;ldquo;What you lookin&amp;rsquo; at?&amp;rdquo;&lt;/p&gt;
&lt;h2 id="lucky-shot"&gt;Lucky Shot!&lt;/h2&gt;
&lt;p&gt;&lt;img src="https://davepedu.com/photos/2014/01/17/kennedy-space-center/img/nasa-2013-11-17-08.06_hu_24978f6f0c77118a.jpg" alt="Lucky Shot!"/&gt;&lt;/p&gt;
&lt;p&gt;A gull dive-bombing to catch a fish, at the moment of impact. Took quite a few trys to capture this!&lt;/p&gt;</content></item><item><title>RIT KGCOE Website</title><link>https://davepedu.com/projects/2013/12/01/rit-kgcoe-website/</link><pubDate>Sun, 01 Dec 2013 00:00:00 +0000</pubDate><author>dave@davepedu.com (dave)</author><guid>https://davepedu.com/projects/2013/12/01/rit-kgcoe-website/</guid><description>&lt;p&gt;&lt;img src="https://davepedu.com/projects/2013/12/01/rit-kgcoe-website/img/kgcoe-full_hu_3b681aa18901f5ad.png" alt="Screenshot"/&gt;&lt;/p&gt;
&lt;p&gt;This was actually a project I completed about a year prior, but we decided to revisit it to update the site and add
responsive design features - try the site on a smartphone, it&amp;rsquo;s excellent!&lt;/p&gt;
&lt;p&gt;The layout was designed in house and I did 100% of the development work. The old layout dated back to 2002, so it was
obvious this site needed a refresh! It&amp;rsquo;s amazing how much different the web looks, now.&lt;/p&gt;</description><content>&lt;p&gt;&lt;img src="https://davepedu.com/projects/2013/12/01/rit-kgcoe-website/img/kgcoe-full_hu_3b681aa18901f5ad.png" alt="Screenshot"/&gt;&lt;/p&gt;
&lt;p&gt;This was actually a project I completed about a year prior, but we decided to revisit it to update the site and add
responsive design features - try the site on a smartphone, it&amp;rsquo;s excellent!&lt;/p&gt;
&lt;p&gt;The layout was designed in house and I did 100% of the development work. The old layout dated back to 2002, so it was
obvious this site needed a refresh! It&amp;rsquo;s amazing how much different the web looks, now.&lt;/p&gt;
&lt;p&gt;The KGCOE website was the first of a new family of websites - or a group of websites with a similar look and feel. The
layout was adapted for several more Engineering websites at RIT, including Computer Engineering and Mechanical
Engineering. These sites also share some content - Staff and facility listings, for example - and that was interesting
to put together.&lt;/p&gt;
&lt;p&gt;Overall, I&amp;rsquo;m pretty happy with how the site came out, and working with the school&amp;rsquo;s Engineering department was great!&lt;/p&gt;
&lt;p&gt;Additionally, this website &lt;a href="https://www.rit.edu/news/rit-production-services-work-honored"&gt;won a Silver Award&lt;/a&gt;, given by
&lt;a href="https://www.w3award.com/about/"&gt;The w3 Awards&lt;/a&gt; in its Best of the Web competition.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The new College of Engineering website was honored for its homepage design, the structure and navigation of the site
as well as its visual appeal. Undergoing a complete redesign in the past academic year, the site incorporates
multiple carousel images and stories of student and faculty work, separate informational sections for prospective
students and corporate partners, testimonials as well as in-depth information about majors and programs offered in the
college. Contributors for the site include: Bhalla; Bastian; &lt;strong&gt;David Pedu&lt;/strong&gt;, Web programmer; and Karen Ester,
communications specialist in the Kate Gleason College of Engineering.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Two of our Web projects were recognized by W3 last year, so it is great to get continued recognition from them this
year as well. It’s a testament to the immense talent that we have internally at RIT,” said Raman Bhalla, assistant
director, Web/IT services.&lt;/p&gt;
&lt;p&gt;- &lt;a href="https://www.rit.edu/news/websites-win-awards"&gt;RIT News&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This website has since been decommissioned and replaced with an updated design, but my work may still be viewed
via &lt;a href="https://web.archive.org/web/20150608154845/http://www.rit.edu/kgcoe/"&gt;archive.org&lt;/a&gt;.&lt;/p&gt;</content></item><item><title>RIT Innovative Learning Institute Website</title><link>https://davepedu.com/projects/2013/11/24/rit-innovative-learning-institute-website/</link><pubDate>Sun, 24 Nov 2013 00:00:00 +0000</pubDate><author>dave@davepedu.com (dave)</author><guid>https://davepedu.com/projects/2013/11/24/rit-innovative-learning-institute-website/</guid><description>&lt;p&gt;&lt;img src="https://davepedu.com/projects/2013/11/24/rit-innovative-learning-institute-website/img/ili-full_hu_e460357b016cb87a.png" alt="Screenshot"/&gt;&lt;/p&gt;
&lt;p&gt;The Innovative Learning Institute at RIT is a new department, under the Center for Multidisciplinary Studies branch.
The layout is very similar to the CMS website, and was also designed by &lt;a href="https://dwaiter.com/"&gt;Dumbwaiter Design&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;This website has since been decommissioned and replaced with an updated design, but my work may still be viewed
via &lt;a href="https://web.archive.org/web/20141222131340/http://www.rit.edu/ili/"&gt;archive.org&lt;/a&gt;.&lt;/p&gt;</description><content>&lt;p&gt;&lt;img src="https://davepedu.com/projects/2013/11/24/rit-innovative-learning-institute-website/img/ili-full_hu_e460357b016cb87a.png" alt="Screenshot"/&gt;&lt;/p&gt;
&lt;p&gt;The Innovative Learning Institute at RIT is a new department, under the Center for Multidisciplinary Studies branch.
The layout is very similar to the CMS website, and was also designed by &lt;a href="https://dwaiter.com/"&gt;Dumbwaiter Design&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;This website has since been decommissioned and replaced with an updated design, but my work may still be viewed
via &lt;a href="https://web.archive.org/web/20141222131340/http://www.rit.edu/ili/"&gt;archive.org&lt;/a&gt;.&lt;/p&gt;</content></item><item><title>oktoberfest 2013</title><link>https://davepedu.com/photos/2013/10/17/oktoberfest-2013/</link><pubDate>Thu, 17 Oct 2013 00:00:00 +0000</pubDate><author>dave@davepedu.com (dave)</author><guid>https://davepedu.com/photos/2013/10/17/oktoberfest-2013/</guid><description>&lt;p&gt;Every October, the ski resort at Whiteface Mountain has a small Oktoberfest. Traditional German music, food, beer, and
demonstrations are featured, and many local vendors come to show off their homemade crafts.&lt;/p&gt;
&lt;p&gt;This is the first time I&amp;rsquo;ve gone in many years, and the first time I went being older than 21! It&amp;rsquo;s a ton of fun and is
definitely worth checking out if you&amp;rsquo;re in the area during the skiing offseason.&lt;/p&gt;</description><content>&lt;p&gt;Every October, the ski resort at Whiteface Mountain has a small Oktoberfest. Traditional German music, food, beer, and
demonstrations are featured, and many local vendors come to show off their homemade crafts.&lt;/p&gt;
&lt;p&gt;This is the first time I&amp;rsquo;ve gone in many years, and the first time I went being older than 21! It&amp;rsquo;s a ton of fun and is
definitely worth checking out if you&amp;rsquo;re in the area during the skiing offseason.&lt;/p&gt;
&lt;p&gt;The atmosphere at these events is always awesome, it&amp;rsquo;s friendly and inviting. And I&amp;rsquo;ll never forget the angry-sounding
accordionist saying &amp;ldquo;Drink the beer, Now&amp;rdquo; in his thick accent.&lt;/p&gt;
&lt;h2 id="whiteface-lodge"&gt;Whiteface Lodge&lt;/h2&gt;
&lt;p&gt;&lt;img src="https://davepedu.com/photos/2013/10/17/oktoberfest-2013/img/oktoberfest2013_01_hu_aaa645514e49b68b.jpg" alt="Whiteface Lodge"/&gt;&lt;/p&gt;
&lt;p&gt;Deck of the Whiteface Lodge, at the foot of Little Whiteface and Whiteface mountains&lt;/p&gt;
&lt;h2 id="grounds"&gt;Grounds&lt;/h2&gt;
&lt;p&gt;&lt;img src="https://davepedu.com/photos/2013/10/17/oktoberfest-2013/img/oktoberfest2013_02_hu_7858051772759ecb.jpg" alt="Grounds"/&gt;&lt;/p&gt;
&lt;p&gt;Looking out over the grounds of the area, many local vendors set up tents to show off their homemade products every year.&lt;/p&gt;
&lt;h2 id="cloudsplitter-gondola"&gt;Cloudsplitter Gondola&lt;/h2&gt;
&lt;p&gt;&lt;img src="https://davepedu.com/photos/2013/10/17/oktoberfest-2013/img/oktoberfest2013_06_hu_af3a7cb3b6b014e4.jpg" alt="Cloudsplitter Gondola"/&gt;&lt;/p&gt;
&lt;p&gt;The quickest way to the top of Little Whiteface!&lt;/p&gt;
&lt;h2 id="yodelers"&gt;Yodelers&lt;/h2&gt;
&lt;p&gt;&lt;img src="https://davepedu.com/photos/2013/10/17/oktoberfest-2013/img/oktoberfest2013_11_hu_14e4db44b7166be7.jpg" alt="Yodelers"/&gt;&lt;/p&gt;
&lt;p&gt;A group of 3 yodelers and their alphorns, these instruments were traditionally used for communicating mountaintop to
mountaintop in the past.&lt;/p&gt;
&lt;h2 id="yodeler-show"&gt;Yodeler Show&lt;/h2&gt;
&lt;p&gt;&lt;img src="https://davepedu.com/photos/2013/10/17/oktoberfest-2013/img/oktoberfest2013_13_hu_95e03ac3c59820f1.jpg" alt="Yodeler Show"/&gt;&lt;/p&gt;
&lt;p&gt;Inside the Whiteface Lodge, the Yodelers teamed up with a traditional German band, DSB die Schlauberger.&lt;/p&gt;
&lt;h2 id="traditional-accordion-player"&gt;Traditional Accordion Player&lt;/h2&gt;
&lt;p&gt;&lt;img src="https://davepedu.com/photos/2013/10/17/oktoberfest-2013/img/oktoberfest2013_15_hu_90f919e98cbe8af8.jpg" alt="Traditional Accordion Player"/&gt;&lt;/p&gt;
&lt;p&gt;I really wish this photo came out better. Oh well!&lt;/p&gt;
&lt;h2 id="selfie"&gt;Selfie!&lt;/h2&gt;
&lt;p&gt;&lt;img src="https://davepedu.com/photos/2013/10/17/oktoberfest-2013/img/oktoberfest2013_16_hu_8dda570df83b98a3.jpg" alt="Selfie!"/&gt;&lt;/p&gt;
&lt;p&gt;This guy was awesome.&lt;/p&gt;
&lt;h2 id="up-the-cloudsplitter"&gt;Up the cloudsplitter!&lt;/h2&gt;
&lt;p&gt;&lt;img src="https://davepedu.com/photos/2013/10/17/oktoberfest-2013/img/oktoberfest2013_17_hu_277785de71f39b13.jpg" alt="Up the cloudsplitter!"/&gt;&lt;/p&gt;
&lt;p&gt;Can&amp;rsquo;t beat that view! This is why I love the Adirondacks.&lt;/p&gt;
&lt;h2 id="looking-down"&gt;Looking down&lt;/h2&gt;
&lt;p&gt;&lt;img src="https://davepedu.com/photos/2013/10/17/oktoberfest-2013/img/oktoberfest2013_18_hu_a4c7d15de7c2e77b.jpg" alt="Looking down"/&gt;&lt;/p&gt;
&lt;p&gt;Halfway up the mountain, looking down.&lt;/p&gt;
&lt;h2 id="base-camp"&gt;Base camp&lt;/h2&gt;
&lt;p&gt;&lt;img src="https://davepedu.com/photos/2013/10/17/oktoberfest-2013/img/oktoberfest2013_19_hu_f1256f0d29ce6e51.jpg" alt="Base camp"/&gt;&lt;/p&gt;
&lt;p&gt;A better view of the festivities below. The line across the picture is the arm-size cable hauling our gondola to the summit.&lt;/p&gt;
&lt;h2 id="almost-to-the-top"&gt;Almost to the top!&lt;/h2&gt;
&lt;p&gt;&lt;img src="https://davepedu.com/photos/2013/10/17/oktoberfest-2013/img/oktoberfest2013_21_hu_5a9518e356eb49c9.jpg" alt="Almost to the top!"/&gt;&lt;/p&gt;
&lt;p&gt;Nearly there! And an amazing view of the surrounding mountains.&lt;/p&gt;
&lt;h2 id="yodeling"&gt;Yodeling&lt;/h2&gt;
&lt;p&gt;&lt;img src="https://davepedu.com/photos/2013/10/17/oktoberfest-2013/img/oktoberfest2013_22_hu_25d1c5528785fee.jpg" alt="Yodeling"/&gt;&lt;/p&gt;
&lt;p&gt;We met our yodeling friends from earlier, they were demonstrating how sound from their alphorns could be bounced off other mountains and heard echoing. Very neat demo!&lt;/p&gt;
&lt;h2 id="whiteface"&gt;Whiteface&lt;/h2&gt;
&lt;p&gt;&lt;img src="https://davepedu.com/photos/2013/10/17/oktoberfest-2013/img/oktoberfest2013_23_hu_b244cffc1f69396e.jpg" alt="Whiteface"/&gt;&lt;/p&gt;
&lt;p&gt;Facing the opposite direction as the previous photo, the view of Whiteface from Little Whiteface.&lt;/p&gt;
&lt;h2 id="alphorns"&gt;Alphorns&lt;/h2&gt;
&lt;p&gt;&lt;img src="https://davepedu.com/photos/2013/10/17/oktoberfest-2013/img/oktoberfest2013_24_hu_d5e0c5a0b834b27.jpg" alt="Alphorns"/&gt;&lt;/p&gt;
&lt;p&gt;The business end of some of these horns. Looking east.&lt;/p&gt;
&lt;h2 id="locking-horns"&gt;Locking horns&lt;/h2&gt;
&lt;p&gt;&lt;img src="https://davepedu.com/photos/2013/10/17/oktoberfest-2013/img/oktoberfest2013_25_hu_2f24ec2ecdd36efb.jpg" alt="Locking horns"/&gt;&lt;/p&gt;
&lt;h2 id="horn-artwork"&gt;Horn artwork&lt;/h2&gt;
&lt;p&gt;&lt;img src="https://davepedu.com/photos/2013/10/17/oktoberfest-2013/img/oktoberfest2013_26_hu_28f9f71949ea2537.jpg" alt="Horn artwork"/&gt;&lt;/p&gt;
&lt;p&gt;Very pretty!&lt;/p&gt;
&lt;h2 id="trails"&gt;Trails&lt;/h2&gt;
&lt;p&gt;&lt;img src="https://davepedu.com/photos/2013/10/17/oktoberfest-2013/img/oktoberfest2013_27_hu_893d3782bb3653b1.jpg" alt="Trails"/&gt;&lt;/p&gt;
&lt;p&gt;Facing north, a good view of the ski trails connecting the two mountains.&lt;/p&gt;
&lt;h2 id="summit"&gt;Summit&lt;/h2&gt;
&lt;p&gt;&lt;img src="https://davepedu.com/photos/2013/10/17/oktoberfest-2013/img/oktoberfest2013_28_hu_dc1107bce43f4e79.jpg" alt="Summit"/&gt;&lt;/p&gt;
&lt;p&gt;A short distance from the gondola drop-off, the peak of Little Whiteface.&lt;/p&gt;
&lt;h2 id="tribute"&gt;Tribute&lt;/h2&gt;
&lt;p&gt;&lt;img src="https://davepedu.com/photos/2013/10/17/oktoberfest-2013/img/oktoberfest2013_29_hu_82f98e2d4b2fd05.jpg" alt="Tribute"/&gt;&lt;/p&gt;
&lt;p&gt;A tribute to soldiers of a local division in WWII, a gift from Italy.&lt;/p&gt;
&lt;h2 id="olympic-ski-jumps"&gt;Olympic Ski Jumps&lt;/h2&gt;
&lt;p&gt;&lt;img src="https://davepedu.com/photos/2013/10/17/oktoberfest-2013/img/oktoberfest2013_32_hu_b8c265831ebccd92.jpg" alt="Olympic Ski Jumps"/&gt;&lt;/p&gt;
&lt;p&gt;Not the best photo, but I think it came out alright considering the range was about 12 miles.&lt;/p&gt;
&lt;h2 id="olympic-bobsled-runs"&gt;Olympic Bobsled Runs&lt;/h2&gt;
&lt;p&gt;&lt;img src="https://davepedu.com/photos/2013/10/17/oktoberfest-2013/img/oktoberfest2013_35_hu_70a32f9d8a1d48d3.jpg" alt="Olympic Bobsled Runs"/&gt;&lt;/p&gt;
&lt;p&gt;Another long distance shot at about 8 miles, the Olympic Bobsled run at Mt. Van Hoevenberg.&lt;/p&gt;
&lt;h2 id="lake-placid"&gt;Lake Placid&lt;/h2&gt;
&lt;p&gt;&lt;img src="https://davepedu.com/photos/2013/10/17/oktoberfest-2013/img/oktoberfest2013_39_hu_9e6f18673e0aa45f.jpg" alt="Lake Placid"/&gt;&lt;/p&gt;
&lt;p&gt;Facing west, the view of Lake Placid from the mountain.&lt;/p&gt;
&lt;h2 id="ticket"&gt;Ticket&lt;/h2&gt;
&lt;p&gt;&lt;img src="https://davepedu.com/photos/2013/10/17/oktoberfest-2013/img/oktoberfest2013_40_hu_d20d93d5aeabd083.jpg" alt="Ticket"/&gt;&lt;/p&gt;
&lt;p&gt;My &amp;ldquo;Cannon&amp;rdquo; and my ticket.&lt;/p&gt;</content></item><item><title>RIT Center for Multidisciplinary Studies Website</title><link>https://davepedu.com/projects/2013/10/14/rit-center-for-multidisciplinary-studies-website/</link><pubDate>Mon, 14 Oct 2013 00:00:00 +0000</pubDate><author>dave@davepedu.com (dave)</author><guid>https://davepedu.com/projects/2013/10/14/rit-center-for-multidisciplinary-studies-website/</guid><description>&lt;p&gt;&lt;img src="https://davepedu.com/projects/2013/10/14/rit-center-for-multidisciplinary-studies-website/img/cms-full_hu_eeece988d35d0071.png" alt="Screenshot"/&gt;&lt;/p&gt;
&lt;p&gt;The CMS Website was created for RIT&amp;rsquo;s Center for Multidisciplenary Studies, and was graphically designed by
&lt;a href="https://dwaiter.com/"&gt;Dumbwaiter Design&lt;/a&gt; and developed by myself. It&amp;rsquo;s based on Drupal to allow the CMS staff to easily
create and post content to the website.&lt;/p&gt;
&lt;p&gt;This project was a new challenge - typically, all of our designs were created in-house. Which is very convenient, if I
need anything changed or clarification it&amp;rsquo;s a 10 second walk to talk to the designer. But, working with the CMS staff
and Dumbwaiter Design went absolutely great!&lt;/p&gt;</description><content>&lt;p&gt;&lt;img src="https://davepedu.com/projects/2013/10/14/rit-center-for-multidisciplinary-studies-website/img/cms-full_hu_eeece988d35d0071.png" alt="Screenshot"/&gt;&lt;/p&gt;
&lt;p&gt;The CMS Website was created for RIT&amp;rsquo;s Center for Multidisciplenary Studies, and was graphically designed by
&lt;a href="https://dwaiter.com/"&gt;Dumbwaiter Design&lt;/a&gt; and developed by myself. It&amp;rsquo;s based on Drupal to allow the CMS staff to easily
create and post content to the website.&lt;/p&gt;
&lt;p&gt;This project was a new challenge - typically, all of our designs were created in-house. Which is very convenient, if I
need anything changed or clarification it&amp;rsquo;s a 10 second walk to talk to the designer. But, working with the CMS staff
and Dumbwaiter Design went absolutely great!&lt;/p&gt;
&lt;p&gt;This website has since been decommissioned and replaced with an updated design, but my work may still be viewed
via &lt;a href="https://web.archive.org/web/20140209151119/http://www.rit.edu/academicaffairs/cms/"&gt;archive.org&lt;/a&gt;.&lt;/p&gt;</content></item><item><title>exploring benson mines</title><link>https://davepedu.com/photos/2013/10/01/exploring-benson-mines/</link><pubDate>Tue, 01 Oct 2013 00:00:00 +0000</pubDate><author>dave@davepedu.com (dave)</author><guid>https://davepedu.com/photos/2013/10/01/exploring-benson-mines/</guid><description>&lt;p&gt;&lt;a href="https://en.wikipedia.org/wiki/Benson_Mines"&gt;Benson Mines&lt;/a&gt; is a long-abandoned iron mine near Watertown, NY.&lt;/p&gt;
&lt;p&gt;The mine was shut down in 1978 and the buildings and have been left to nature ever since. It&amp;rsquo;s a great visualization of
nature slowly reclaiming it&amp;rsquo;s land.&lt;/p&gt;
&lt;p&gt;I stopped by with a friend on a rainy day on my way back to Rochester from the Adirondacks. The rain was moderate at
times and the buildings were alive with the sound of dripping water.&lt;/p&gt;</description><content>&lt;p&gt;&lt;a href="https://en.wikipedia.org/wiki/Benson_Mines"&gt;Benson Mines&lt;/a&gt; is a long-abandoned iron mine near Watertown, NY.&lt;/p&gt;
&lt;p&gt;The mine was shut down in 1978 and the buildings and have been left to nature ever since. It&amp;rsquo;s a great visualization of
nature slowly reclaiming it&amp;rsquo;s land.&lt;/p&gt;
&lt;p&gt;I stopped by with a friend on a rainy day on my way back to Rochester from the Adirondacks. The rain was moderate at
times and the buildings were alive with the sound of dripping water.&lt;/p&gt;
&lt;p&gt;Without further ado, the album:&lt;/p&gt;
&lt;h2 id="entrance"&gt;Entrance&lt;/h2&gt;
&lt;p&gt;&lt;img src="https://davepedu.com/photos/2013/10/01/exploring-benson-mines/img/IMG_4232_hu_7a02132a3f260706.JPG" alt="Entrance to the facility - and my car!"/&gt;&lt;/p&gt;
&lt;p&gt;Entrance to the facility - and my car!&lt;/p&gt;
&lt;h2 id="machine-shop"&gt;Machine Shop&lt;/h2&gt;
&lt;p&gt;&lt;img src="https://davepedu.com/photos/2013/10/01/exploring-benson-mines/img/IMG_4146_hu_eccf140cb91a1d79.JPG" alt="Judging from the abundance of 440 volt hookups in the building, this likely was some sort of machine shop."/&gt;&lt;/p&gt;
&lt;p&gt;Judging from the abundance of 440 volt hookups in the building, this likely was some sort of machine shop.&lt;/p&gt;
&lt;h2 id="safety-for-all"&gt;Safety for All&lt;/h2&gt;
&lt;p&gt;&lt;img src="https://davepedu.com/photos/2013/10/01/exploring-benson-mines/img/IMG_4149_hu_27bfc81327e3f06b.JPG" alt="Safety for All"/&gt;&lt;/p&gt;
&lt;h2 id="no-entrance"&gt;No Entrance&lt;/h2&gt;
&lt;p&gt;&lt;img src="https://davepedu.com/photos/2013/10/01/exploring-benson-mines/img/IMG_4156_hu_1128b493ce2956e8.JPG" alt="Back door to the machine shop."/&gt;&lt;/p&gt;
&lt;p&gt;Back door to the machine shop.&lt;/p&gt;
&lt;h2 id="ore-silos"&gt;Ore Silos&lt;/h2&gt;
&lt;p&gt;&lt;img src="https://davepedu.com/photos/2013/10/01/exploring-benson-mines/img/IMG_4160_r_hu_c2482d9226533105.jpg" alt="Rusting silo between concrete silos for holding iron ore."/&gt;&lt;/p&gt;
&lt;p&gt;Rusting silo between concrete silos for holding iron ore. It looked like there was more machinery on top of the silos that had rusted away and collapsed.&lt;/p&gt;
&lt;h2 id="overgrown"&gt;Overgrown&lt;/h2&gt;
&lt;p&gt;&lt;img src="https://davepedu.com/photos/2013/10/01/exploring-benson-mines/img/IMG_4167_r_hu_81e23646b4af24f7.jpg" alt="Tree blocking a doorway."/&gt;&lt;/p&gt;
&lt;p&gt;Tree blocking a doorway.&lt;/p&gt;
&lt;h2 id="garage-door"&gt;Garage Door&lt;/h2&gt;
&lt;p&gt;&lt;img src="https://davepedu.com/photos/2013/10/01/exploring-benson-mines/img/IMG_4172_hu_8987a9da77bee4f3.JPG" alt="Outside view of another building showing the decay of the area."/&gt;&lt;/p&gt;
&lt;p&gt;Outside view of another building showing the decay of the area.&lt;/p&gt;
&lt;h2 id="burned-steps"&gt;Burned Steps&lt;/h2&gt;
&lt;p&gt;&lt;img src="https://davepedu.com/photos/2013/10/01/exploring-benson-mines/img/IMG_4174_hu_4f8e765c034d8f95.JPG" alt="Even older looking stairs - the mine has history back to the 1800s."/&gt;&lt;/p&gt;
&lt;p&gt;Even older looking stairs - the mine has history back to the 1800s, but this is probably not that old. The building on top of it appears to have burned, leaving behind electric wires and boxes.&lt;/p&gt;
&lt;h2 id="abandoned-excavator"&gt;Abandoned Excavator&lt;/h2&gt;
&lt;p&gt;&lt;img src="https://davepedu.com/photos/2013/10/01/exploring-benson-mines/img/IMG_4175_hu_84f83db6ddb393b0.JPG" alt="Likely a Massey Ferguson 450."/&gt;&lt;/p&gt;
&lt;p&gt;&amp;hellip;With no bucket. Likely a Massey Ferguson 450.&lt;/p&gt;
&lt;h2 id="abandoned-excavators-engine"&gt;Abandoned Excavator&amp;rsquo;s Engine&lt;/h2&gt;
&lt;p&gt;&lt;img src="https://davepedu.com/photos/2013/10/01/exploring-benson-mines/img/IMG_4180_hu_dfb9c7c553fb3fff.JPG" alt="Abandoned Excavator&amp;rsquo;s Engine"/&gt;&lt;/p&gt;
&lt;h2 id="machine-shop-roof"&gt;Machine Shop Roof&lt;/h2&gt;
&lt;p&gt;&lt;img src="https://davepedu.com/photos/2013/10/01/exploring-benson-mines/img/IMG_4184_hu_2a2192206457fe3d.JPG" alt="Outdoor view of the first building, the roof on this side is nearly rotted through."/&gt;&lt;/p&gt;
&lt;p&gt;Outdoor view of the first building, the roof on this side is nearly rotted through. Most of the buildings were built with cinderblock walls, metal trusses, and wooden roofing.&lt;/p&gt;
&lt;h2 id="workshop"&gt;Workshop&lt;/h2&gt;
&lt;p&gt;&lt;img src="https://davepedu.com/photos/2013/10/01/exploring-benson-mines/img/IMG_4199_hu_733ae363937ccadd.JPG" alt="Inside another building, some sort of workshop."/&gt;&lt;/p&gt;
&lt;p&gt;Inside another building, some sort of workshop.&lt;/p&gt;
&lt;h2 id="workers-desk"&gt;Worker&amp;rsquo;s Desk&lt;/h2&gt;
&lt;p&gt;&lt;img src="https://davepedu.com/photos/2013/10/01/exploring-benson-mines/img/IMG_4201_hu_d1fb971abb21971d.JPG" alt="Worker&amp;rsquo;s Desk"/&gt;&lt;/p&gt;
&lt;h2 id="transformer-gauge"&gt;Transformer Gauge&lt;/h2&gt;
&lt;p&gt;&lt;img src="https://davepedu.com/photos/2013/10/01/exploring-benson-mines/img/IMG_4208_hu_593a5a78edc36050.JPG" alt="The guts of the transformer were missing, probably stolen by copper thieves."/&gt;&lt;/p&gt;
&lt;p&gt;The guts of the transformer were missing, probably stolen by copper thieves.&lt;/p&gt;
&lt;h2 id="office-building"&gt;Office Building&lt;/h2&gt;
&lt;p&gt;&lt;img src="https://davepedu.com/photos/2013/10/01/exploring-benson-mines/img/IMG_4217_hu_265fcc4ab434cdc2.JPG" alt="One of the few two-story buildings, with major fire damage inside."/&gt;&lt;/p&gt;
&lt;p&gt;One of the few two-story buildings, with major fire damage inside.&lt;/p&gt;
&lt;h2 id="another-office"&gt;Another office&lt;/h2&gt;
&lt;p&gt;&lt;img src="https://davepedu.com/photos/2013/10/01/exploring-benson-mines/img/IMG_4221_hu_509592ea6c6da677.JPG" alt="Some sort of office area, completely rotted out."/&gt;&lt;/p&gt;
&lt;p&gt;Some sort of office area, completely rotted out.&lt;/p&gt;
&lt;h2 id="bathroom-and-sink"&gt;Bathroom and Sink&lt;/h2&gt;
&lt;p&gt;&lt;img src="https://davepedu.com/photos/2013/10/01/exploring-benson-mines/img/IMG_4224_hu_fcbfec730b96a7ef.JPG" alt="Likely decimated by vandals."/&gt;&lt;/p&gt;
&lt;p&gt;Likely decimated by vandals.&lt;/p&gt;
&lt;h2 id="outside"&gt;Outside&lt;/h2&gt;
&lt;p&gt;&lt;img src="https://davepedu.com/photos/2013/10/01/exploring-benson-mines/img/IMG_4235_hu_8296f0a256e9e42b.JPG" alt="The office/bathroom is inside this building."/&gt;&lt;/p&gt;
&lt;p&gt;The office/bathroom is inside this building, and the first building in the album is visible through the trees in the background.&lt;/p&gt;
&lt;h2 id="exit-entrance"&gt;Exit (Entrance)&lt;/h2&gt;
&lt;p&gt;&lt;img src="https://davepedu.com/photos/2013/10/01/exploring-benson-mines/img/IMG_4239_hu_f402c4a35b19cdb6.JPG" alt="Exit (Entrance)"/&gt;&lt;/p&gt;
&lt;h3 id="2025-update"&gt;2025 Update&lt;/h3&gt;
&lt;p&gt;Benson Mines has had a complete transformation over the last couple years. The site was sold to a new owner, who
demolished almost every building, clearing the site. More recently, the site has been transferred to a new developer
who &lt;a href="https://www.adirondackexplorer.org/communities/economy/solar-developer-chosen-benson-mines/"&gt;plans to create a 12-megawatt solar energy generation facility&lt;/a&gt;!
While it appears Benson Mines&amp;rsquo; days as a mine are over, it will live on as a new and futuristic facility.&lt;/p&gt;</content></item><item><title>test blog post</title><link>https://davepedu.com/blog/2013/09/30/test-blog-post/</link><pubDate>Mon, 30 Sep 2013 00:00:00 +0000</pubDate><author>dave@davepedu.com (dave)</author><guid>https://davepedu.com/blog/2013/09/30/test-blog-post/</guid><description>&lt;p&gt;Hello! Welcome to my blog.&lt;/p&gt;
&lt;p&gt;This is a test blog post to help develop the system.&lt;/p&gt;</description><content>&lt;p&gt;Hello! Welcome to my blog.&lt;/p&gt;
&lt;p&gt;This is a test blog post to help develop the system.&lt;/p&gt;</content></item><item><title>RIT Wildenhain Exhibition website</title><link>https://davepedu.com/projects/2012/11/04/rit-wildenhain-exhibition-website/</link><pubDate>Sun, 04 Nov 2012 00:00:00 +0000</pubDate><author>dave@davepedu.com (dave)</author><guid>https://davepedu.com/projects/2012/11/04/rit-wildenhain-exhibition-website/</guid><description>&lt;p&gt;&lt;img src="https://davepedu.com/projects/2012/11/04/rit-wildenhain-exhibition-website/img/wild-full_hu_fe21f055438eb95d.png" alt="Screenshot"/&gt;&lt;/p&gt;
&lt;p&gt;A website showcasing the work of mid century artist Frans Wildenhain, built on WordPress. Features a prominent art photo
gallery, and a WordPress blog.&lt;/p&gt;
&lt;p&gt;Additionally, this website &lt;a href="https://www.rit.edu/news/websites-win-awards"&gt;won a Silver Award&lt;/a&gt;, given by
&lt;a href="https://www.w3award.com/about/"&gt;The w3 Awards&lt;/a&gt; in its Best of the Web competition.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The Frans Wildenhain Exhibition website was designed to feature the fall 2012 exhibition of mid-century modern pottery
by Bauhaus-trained ceramist Frans Wildenhain.&lt;/p&gt;
&lt;p&gt;&amp;ldquo;The site had to be designed to attract and hold the diverse interests of an international audience,&amp;rdquo; says Bruce
Austin, professor of communication. “The efforts of the Web/IT team led to a website every bit as innovative, exciting
and dynamic as the art on exhibit.&amp;quot;&lt;/p&gt;</description><content>&lt;p&gt;&lt;img src="https://davepedu.com/projects/2012/11/04/rit-wildenhain-exhibition-website/img/wild-full_hu_fe21f055438eb95d.png" alt="Screenshot"/&gt;&lt;/p&gt;
&lt;p&gt;A website showcasing the work of mid century artist Frans Wildenhain, built on WordPress. Features a prominent art photo
gallery, and a WordPress blog.&lt;/p&gt;
&lt;p&gt;Additionally, this website &lt;a href="https://www.rit.edu/news/websites-win-awards"&gt;won a Silver Award&lt;/a&gt;, given by
&lt;a href="https://www.w3award.com/about/"&gt;The w3 Awards&lt;/a&gt; in its Best of the Web competition.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The Frans Wildenhain Exhibition website was designed to feature the fall 2012 exhibition of mid-century modern pottery
by Bauhaus-trained ceramist Frans Wildenhain.&lt;/p&gt;
&lt;p&gt;&amp;ldquo;The site had to be designed to attract and hold the diverse interests of an international audience,&amp;rdquo; says Bruce
Austin, professor of communication. “The efforts of the Web/IT team led to a website every bit as innovative, exciting
and dynamic as the art on exhibit.&amp;quot;&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&amp;ldquo;These awards are a testament to the talented staff we have here at RIT,&amp;rdquo;&amp;quot; says Raman Bhalla, assistant director for
Web/IT services. In addition to Bhalla, the Web/IT team includes Megan Bastian, Harry Groover, Gage LaFleur,
Elizabeth Lamark, &lt;strong&gt;David Pedu&lt;/strong&gt; and Dan Roder.&lt;/p&gt;
&lt;p&gt;- &lt;a href="https://www.rit.edu/news/websites-win-awards"&gt;RIT News&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This website has since been decommissioned and replaced with an updated design, but my work may still be viewed
via &lt;a href="https://web.archive.org/web/20140116214235/https://www.rit.edu/cla/wild/"&gt;archive.org&lt;/a&gt;.&lt;/p&gt;</content></item><item><title>about this website</title><link>https://davepedu.com/about-this-website/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><author>dave@davepedu.com (dave)</author><guid>https://davepedu.com/about-this-website/</guid><description>&lt;h1 id="content"&gt;content&lt;/h1&gt;
&lt;p&gt;All text, images, and other media on this website are original works, unless otherwise noted. Any third-party material
is used with attribution.&lt;/p&gt;
&lt;p&gt;No content on this website was produced using Artificial Intelligence.&lt;/p&gt;
&lt;h1 id="attributions"&gt;attributions&lt;/h1&gt;
&lt;p&gt;Site generated with &lt;a href="https://github.com/gohugoio/hugo"&gt;Hugo&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Hugo theme adapted from &lt;a href="https://github.com/mirus-ua/hugo-theme-re-terminal"&gt;re-Terminal&lt;/a&gt; by &lt;a href="https://github.com/mirus-ua"&gt;Mirus&lt;/a&gt;.&lt;/p&gt;</description><content>&lt;h1 id="content"&gt;content&lt;/h1&gt;
&lt;p&gt;All text, images, and other media on this website are original works, unless otherwise noted. Any third-party material
is used with attribution.&lt;/p&gt;
&lt;p&gt;No content on this website was produced using Artificial Intelligence.&lt;/p&gt;
&lt;h1 id="attributions"&gt;attributions&lt;/h1&gt;
&lt;p&gt;Site generated with &lt;a href="https://github.com/gohugoio/hugo"&gt;Hugo&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Hugo theme adapted from &lt;a href="https://github.com/mirus-ua/hugo-theme-re-terminal"&gt;re-Terminal&lt;/a&gt; by &lt;a href="https://github.com/mirus-ua"&gt;Mirus&lt;/a&gt;.&lt;/p&gt;</content></item><item><title>contact</title><link>https://davepedu.com/contact/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><author>dave@davepedu.com (dave)</author><guid>https://davepedu.com/contact/</guid><description>&lt;p&gt;&lt;strong&gt;direct&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Email: &lt;a href="mailto:web@davepedu.com?subject=Website%20contact"&gt;web@davepedu.com&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;common&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;GitHub: &lt;a href="https://github.com/dpedu"&gt;@dpedu&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;LinkedIn: &lt;a href="https://www.linkedin.com/in/davepedu"&gt;/davepedu&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;𝕏 / Twitter: &lt;a href="https://twitter.com/dpedu"&gt;@dpedu&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Bluesky: &lt;a href="https://bsky.app/profile/davepedu.com"&gt;@davepedu.com&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Facebook: &lt;a href="https://www.facebook.com/dave.pedu"&gt;/dave.pedu&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;other&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Strava: &lt;a href="http://www.strava.com/athletes/751790"&gt;Link&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Steam: &lt;a href="https://steamcommunity.com/id/moptop650"&gt;moptop650&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Last.fm: &lt;a href="http://www.last.fm/user/Moptop650"&gt;Moptop650&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;retired&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Google+: &lt;a href="https://web.archive.org/web/20161119133525/https://plus.google.com/101304612904185407141"&gt;Archive&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Skype: d.pedu&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;feeds&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;RSS: &lt;a href="https://davepedu.com/index.xml"&gt;Link&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;</description><content>&lt;p&gt;&lt;strong&gt;direct&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Email: &lt;a href="mailto:web@davepedu.com?subject=Website%20contact"&gt;web@davepedu.com&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;common&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;GitHub: &lt;a href="https://github.com/dpedu"&gt;@dpedu&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;LinkedIn: &lt;a href="https://www.linkedin.com/in/davepedu"&gt;/davepedu&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;𝕏 / Twitter: &lt;a href="https://twitter.com/dpedu"&gt;@dpedu&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Bluesky: &lt;a href="https://bsky.app/profile/davepedu.com"&gt;@davepedu.com&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Facebook: &lt;a href="https://www.facebook.com/dave.pedu"&gt;/dave.pedu&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;other&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Strava: &lt;a href="http://www.strava.com/athletes/751790"&gt;Link&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Steam: &lt;a href="https://steamcommunity.com/id/moptop650"&gt;moptop650&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Last.fm: &lt;a href="http://www.last.fm/user/Moptop650"&gt;Moptop650&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;retired&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Google+: &lt;a href="https://web.archive.org/web/20161119133525/https://plus.google.com/101304612904185407141"&gt;Archive&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Skype: d.pedu&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;feeds&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;RSS: &lt;a href="https://davepedu.com/index.xml"&gt;Link&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;</content></item></channel></rss>