A long while ago I, Rob Kendrick, Clive Jones (and possibly others) sat
down and tried to come up with a way to store passwords a-la
Password Safe. However, being us, we wanted to ensure a number of
properties which password safes commonly don't have. We wanted to allow the
delegation of access to some subset of the passwords. We also wanted for it
to be reasonable to deny that there is content which has not been decrypted.
I was reminded of this work when I was discussing the concept of deniable
storage of secrets with a colleague (An idea I'll expand upon in another blog
post at another time). I am therefore presenting, with little change other
than formatting the design from years ago. I would be very interested if
anyone knows of software which meets the properties of the Caius system since
I would like to have one but simply don't trust myself (see another future
posting) to write it right now.
The following concepts are assumed to be understood:
Password Safe (originally counterpane, now a sourceforge project)
The 'Caius' system is a password-safe type system sporting hierarchical
delegable access to the data it stores. The 'Caius Fob' is the data-store for
The 'Caius Fob' is a file which consists of a header and then three
sections. The header identifies it as such a file, the first section lists a
number of 'External IDs' which can be used to access portions of the file. The
second section lists ACL entries as defined below. The third section of the
file is the encrypted data looked after by this file. It is not intended that
the holder of a CaiusFob be able to deny it is a CaiusFob, but it is expected
that it be possible to deny an ability to decrypt (perhaps by lacking a
password) any ACL entries. Given that the structure of the file is known,
it is necessary that there be external IDs for which the password or
GPG key is not valid or cannot decrypt an ACL entry, and ACL entries which
even if decrypted may not be valid, and ACL entries which even if decrypted
and valid may not be used to encode any data blocks.
An External ID
External ID ::=
Where TYPE is one of:
* 0: Unused (ID slot placeholder)
* 1: GPG key, where DATA is the keyid of the gpg key
* 2: Password, where DATA is some hash of a password which can be used
to derive a key for decrypting an ACL entry.
The list of external ids forms a numbered sequence where the index (0-based)
into the sequence is the External ID number. (EIDnr)
An ACL Entry
ACL Entry ::=
The EIDnr is the number of the External ID as explained above. The LENGTH
is the length of the DATA section which is a key-pair as explained below,
encrypted to the external id. The HMAC uses the authentication key in the
key-pair in the DATA section, and authenticates the EIDnr, LENGTH, DATA
One possibility for increasing deniability is to remove the EIDnr from this
part of the file, and assume that for every external ID you try to decrypt all
ACLs you've not succeeded in decrypting thus-far. This has the benefit of
being able to deny that an ACL entry ought to be decryptable with the
credentials you hold, but also an increased inability to know if you have
successfully unlocked everything appropriate to being able to fully manipulate
a CaiusFob. This tradeoff is currently set in favour of better understanding
of the content, but a future design feature might suggest EIDnr should always
be -1 to indicate "unknown, try every EID".
A key pair
Key Pair ::=
The ENCRYPTIONKEY is used to initialise the stream cipher for the data
section. The AUTHENTICATIONKEY is used to compute HMACs for the appropriate
ACL entries or data blocks (as defined below)
The data section
First consider a set of stream ciphers. There exists always one cipher which we
will call the NULL cipher. It is defined such that Cipher(Byte, Offset) ==
Byte and is always available. Then there is a cipher initialised for each key
pair we can successfully extract from the ACL entry section of the file.
Each of these ciphers is initialised and made ready such that the data section
can be xored with the bytes as they come out of the stream ciphers in an
attempt to decrypt the blocks. Essentially this is equivalent to decrypting the
entire data section with each cipher in turn to produce N proposed cleartexts
which can then be examined to find decrypted blocks.
Whenever a cipher, combined with the data stream in the file, manages to
produce a sequence of eight bytes of value zero, we have reached a
synchronisation point and what follows is a data block enciphered with which
ever cipher managed to reveal the synchronisation sequence.
Since it is vanishingly unlikely that you will find eight zeros in a row when
playing about with arbitrary cipher initialisation, we consider this to be an
acceptable synchronisation sequence.
Once we have found a sync sequence, we can know the length of this block and
thus we do not look for sync markers again until after the block we have just
A data block
Data block ::=
Such that each field is the obvious, DATA is DATALENGTH bytes of data, the
texture of which is defined by TYPE and PAD is PADLENGTH arbitrary bytes
which pad this block. HMAC is keyed using the authentication key associated
with the stream cipher managing to decrypt this and is over the DATALENGTH,
PADLENGTH, TYPE, DATA, PAD tuple.
If TYPE is zero then this is a ''free-space'' block and will typically
contain zero bytes of DATA and some number of padding. This is however
arbitrary and not enforced, the free space can be DATA if the implementer
prefers and implementations are encouraged if possible to randomise the
distribution of the consumed space between the DATA and PAD sections.
A node block
TYPE == 1 (Node)
MY_ID is a unique ID for this node. (generally a random number, probably 64
bits long, perhaps a UUID). PARENT_ID if not all NULLs is the ID for the
parent node. If all NULLs then this is the root of a heirarchy. NAME is a
NULL terminated byte string of one or more characters which is the name of this
node. It may consist only of any characters other than NULL and the
forward-slash character. NOTES is a byte string of zero or more characters,
NULL terminated. Note that the DATALENGTH of the data block clearly delimits
this field but the NULL is present to aid parsing.
A system block
TYPE == 2 (System)
PARENT_ID is the node to which this block belongs. It is required that any
system blocks you succeed in decrypting can be placed within a node you succeed
in decrypting. If the library encounters a system block which belongs to a node
it cannot find then this is considered to be a corrupt system block and will be
treated as though it could not be decrypted.
USERNAME is a byte string of one or more characters terminated by a NULL,
ditto for PASSWORD and as for a node block, the NOTES are NULL terminated
EXPIRYDATE is a byte string of characters in RFC-822 date format.
It is expected that any implementation of Caius will adhere to the following
guidelines in order to enhance the security of content over time.
Any time a block is invalidated (such as by the changing of a password, the
obsoleting of an entry, the changing of notes, names, or reparenting a node)
anywhere from one to all of the bits in the block can be changed. Trivially,
this includes the synchronisation sequence preceeding the block as if the
synchronisation sequence isn't present then the block does not exist.
Every time a CaiusFob is altered in any way, some amount of known
intra-block padding must be altered in a random way. Ideally this will be done
so that it looks like number 1 has happened somewhere else in the file as
well. Anywhere from zero to all of the padding can be thusly altered in any
No attempt will be made to write to any part of the file which cannot be
positively identified as padding unless the user has explicitly stated that
they will accept damage to data they cannot currently decrypt.
No indication will be given to the user if any part of the file was unable
to be decrypted or failed an HMAC check. Such data is simply incorrectly
decrypted and thus ignored.
Intrablock padding can be positively identified if you have two consecutive
blocks in a CaiusFob such that the number of bytes between them could not
possibly hold the simplest of free space blocks.
When appending a block to a CaiusFob it is encouraged to place up to 50% of
the size of the intrablock spacing before it as random padding, and up to 50%
afterwards also. Naturally anywhere between zero and the full amount is
acceptable, ideally the implementation would choose at random each time.
A friend and ex-colleague Francis Irving (@frabcus on Twitter) has
recently been on a bit of an anti C/C++ kick, including tweeting about the
problems which happen in software written in so called "insecure" languages,
and culminating in his promise website which boldly calls for people to
promise to not use C/C++ for new projects.
Recently I've not been programming enough. I'm still a member of the
NetSurf browser project, and I'm still (slowly) working on Gitano from
time to time. I am still (in theory) an upstream on the Cherokee Webserver
project (and I really do need to sit down and fix some bugs in logging) and any
number of smaller projects as well. I am still part of Debian and would
like to start making positive contributions beyond voting and egging others on,
but I have been somewhat burned out by everything going on in my life,
including both home and work. While I am hardly in any kind of mental trouble,
I've simply not had any tuits of late.
I find it very hard to make public promises which I know I am going to break.
Francis suggested that the promise can be broken which while it might not
devalue it for him (or you) it does for me. I do however think that public
promises are a good thing, especially when they foster useful discussion in
the communities I am part of, so from that point of view I very much support
Francis in his efforts.
Even given all of the above, I'd like to make a promise statement of my own.
I'd like to make it in public and hopefully that'll help me to keep it. I
know I could easily fail to live up to this promise, but I'm hoping I'll do
well and to some extent I'm relying on all you out there to help me keep it.
Given we're almost at the end of the month, I am making the promise now and I
want it to take effect starting on the 1st of February 2015.
I hereby promise that I will do better at contributing to all the projects
I am nominally part of, making at least one useful material contribution to
at least one project every week. I also promise to be more mindful of the
choices I make when choosing how to implement solutions to problems,
selecting appropriate languages and giving full consideration to how my
solution might be attacked if appropriate.
I can't and won't promise not to use C/C++ but if you honestly feel you can
make that promise, then I'm certain Francis would love for you to head over to
his promise website and pledge. Also regardless of your opinions, please do
join in the conversation, particularly regarding being mindful of attack
vectors whenever you write something.
Redis typically stores the entire data set in memory, using the operating system's virtual memory facilities if required. However, one can use Redis more like a cache or ring buffer by enabling a "maxmemory policy" where a RAM limit is set and then data is evicted when required based on a predefined algorithm.
This change enables entirely custom control over exactly what data to remove from RAM when this maxmemory limit is reached. This is an advantage over the existing policies of, say, removing entire keys based on the existing TTL, Least Recently Used (LRU) or random eviction strategies as it permits bespoke behaviours based on application-specific requirements, crucially without maintaining a private fork of Redis.
As an example behaviour of what is possible with this change, to remove the lowest ranked member of an arbitrary sorted set, you could load the following eviction policy script:
localbestkey=nillocalbestval=0fors=1,5dolocalkey=redis.call("RANDOMKEY")localtype_=redis.call("TYPE",key)iftype_.ok=="zset"thenlocaltail=redis.call("ZRANGE",key,"0","0","WITHSCORES")localval=tonumber(tail)ifnotbestkeyorval<bestvalthenbestkey=keybestval=valendendendifnotbestkeythen-- We couldn't find anything to remove, so return an errorreturnfalseendredis.call("ZREMRANGEBYRANK",bestkey,"0","0")returntrue
I expect this is obvious to many people but bahumbug To Phish, or Not to Phish? just woke me up to the fact that if Google hosts your company email then its Sender Policy Framework might make other Google-sent emails look legitimate for your domain. When combined with the unsupportive support of the big free webmail hosts, is this another black mark against SPF?
I’ve been meaning to move away from Movable Type for a while; they no longer provide the “Open Source” variant, I’ve had some issues with the commenting side of things (more the fault of spammers than Movable Type itself) and there are a few minor niggles that I wanted to resolve. Nothing has been particularly pressing me to move and I haven’t been blogging as much so while I’ve been keeping an eye open for a replacement I haven’t exerted a lot of energy into the process. I have a little bit of time at present so I asked around on IRC for suggestions. One was ikiwiki, which I use as part of helping maintain the SPI website (and think is fantastic for that), the other was Jekyll. Both are available as part of Debian Jessie.
Jekyll looked a bit fancier out of the box (I’m no web designer so pre-canned themes help me a lot), so I decided to spend some time investigating it a bit more. I’d found a Movable Type to ikiwiki converter which provided a starting point for exporting from the SQLite3 DB I was using for MT. Most of my posts are in markdown, the rest (mostly from my Blosxom days) are plain HTML, so there wasn’t any need to do any conversion on the actual content. A minor amount of poking convinced Jekyll to use the same URL format (permalink: /:year/:month/:title.html in the _config.yml did what I wanted) and I had to do a few bits of fix up for some images that had been uploaded into MT, but overall fairly simple stuff.
Next I had to think about comments. My initial thought was to just ignore them for the moment; they weren’t really working on the MT install that well so it’s not a huge loss. I then decided I should at least see what the options were. Google+ has the ability to embed in your site, so I had a play with that. It worked well enough but I didn’t really want to force commenters into the Google ecosystem. Next up was Disqus, which I’ve seen used in various places. It seems to allow logins via various 3rd parties, can cope with threading and deals with the despamming. It was easy enough to integrate to play with, and while I was doing so I discovered that it could cope with importing comments. So I tweaked my conversion script to generate a WXR based file of the comments. This then imported easily into Disqus (and also I double checked that the export system worked).
Anyway. Thanks to Tollef for the pointer (and others who made various suggestions). Hopefully I haven’t broken (or produced a slew of “new” posts for) any of the feed readers pointed at my site (but you should update to use feed.xml rather than any of the others - I may remove them in the future once I see usage has died down).
(On the off chance it’s useful to someone else the conversion script I ended up with is available. There’s a built in Jekyll importer that may be a better move, but I liked ending up with a git repository containing a commit for each post.)
I moved back from the California Bay Area to Belfast a while back and for various reasons it looks like I’m going to be here a while, so it made sense to have my belongings shipped over here. They haven’t quite arrived yet, and I’ll do another post about that process once they have, but I’ve been doing various tweets prefixed with “[shipping]” during the process. Various people I’ve spoken to (some who should know me better) thought this was happening manually. It wasn’t. If you care about how it was done, read on.
I’d been given details of the ship carrying my container, and searching for that turned up the excellent MarineTraffic which let me see the current location of the ship. Turns out ships broadcast their location using AIS and anyone with a receiver can see the info. Very cool, and I spent some time having a look at various bits of shipping around the UK out of interest. I also found the ship’s itinerary which give me some idea of where it would be calling and when. Step one was to start recording this data; it was time sensitive and I wanted to be able to see historical data. I took the easy route and set up a cron job to poll the location and itinerary on an hourly basis, and store the results. That meant I had the data over time, if my parsing turned out to miss something I could easily correct it, and that I wasn’t hammering Marine Traffic while writing the parsing code.
Next I wanted to parse the results, store them in a more manageable format than the HTML, and alert me when the ship docked somewhere or set off again. I’ve been trying to learn more Python rather than doing my default action of turning to Perl for these things, and this seemed like a simple enough project to try out. Beautiful Soup seemed to turn up top for HTML parsing in Python, so that formed the basis. Throwing the info into a database so I could do queries felt like the right move so I used SQLite - if this had been more than a one off I’d have considered looking at PostgreSQL and its GIS support. Finally Tweepy made it very easy to tweet from Python in about 4 lines of code. The whole thing weighed in at only 175 lines of code, mostly around pulling the info out of the HTML and then a little to deal with checking for state changes against the current status and the last piece of info in the database.
The pieces of information I chose to store were the time of the update (i.e. when the ship sent it, not when my script ran), reported area, reported state, the position + course, reported origin, reported destination and eta. The fact this is all in a database makes it very easy to do a few queries on the data.
How fast did the ship go?
sqlite> SELECT MAX(speed) FROM status;
What areas did it report?
sqlite> SELECT area FROM status GROUP BY area;
What statuses did we see?
sqlite> SELECT status FROM status GROUP BY status;
Underway using Engine
Finally having hourly data lets me draw a map of where the ship went. The data isn’t complete, because the free AIS info depends on the ship being close enough to a receiving station. That means there were a few days in the North Atlantic without updates, for example. However there’s enough to give a good idea of just how well traveled my belongings are, and it gave me an excuse to play with OpenLayers.
(Apologies if the zoom buttons aren’t working for you here; I think I need to kick the CSS in some manner I haven’t quite figured out yet.)
All I want for 2015 is a Free/Open Source Software social network which is:
easy to register on (no reCaptcha disability-discriminator or similar, a simple openID, activation emails that actually arrive);
has an email help address or online support or phone number or something other than the website which can be used if the registration system causes a problem;
can email when things happen that I might be interested in;
can email me summaries of what’s happened last week/month in case they don’t know what they’re interested in;
doesn’t email me too much (but this is rare);
interacts well with other websites (allows long-term members to post links, sends trackbacks or pingbacks to let the remote site know we’re talking about them, makes it easy for us to dent/tweet/link to the forum nicely, and so on);
isn’t full of spam (has limits on link-posting, moderators are contactable/accountable and so on, and the software gives them decent anti-spam tools);
lets me back up my data;
is friendly and welcoming and trolls are kept in check.
Is this too much to ask for? Does it exist already?
As I have noted before, 24 December is trivia’s birthday. Since my first post dates from 24 December 2006, today is trivia’s eighth birthday. It seems like only yesterday.
I haven’t posted much in the last few months. I have a lot of material I need to cover, and a backlog of articles I want (or at least wanted) to write so I will endeavour to get back into a writing routine as soon as I can. Meanwhile, since it is yet again christmas time, and it’s trivia’s birthday, I couldn’t let today pass unblogged.
One of our colleagues has been the target of a sustained campaign of harassment for the past several months. We have decided to publish this statement to publicly declare our support for her, for every member of our organization, and for every member of our community who experiences this harassment. She is not alone and her experience has catalyzed us to action. This statement is a start.
Roger asked those who deplored on-line harassment (of any person, for any reason) and who supported the Tor project’s action in publicly condemning the harassment of one of the Tor developers to add their name and voice to the blog post.
In the run up to the “Mozlandia” work week in Portland, and in reflection of the last three years of the Firefox OS project, for a bit of fun I’ve reworked a Bob Dylan song to celebrate our incredible journey so far.
Here’s a video featuring some of my memories from the last three years, with Siobhan (my fiancée) and me singing the song at you! There are even lyrics so you can sing along
Well after a year the SD card on the Raspberry Pi has failed, I noticed /var was unhappy when I tried to apply the recent Bash updates. Attempts at repair only made things worse and I suspect there is some physical issue.
I had minimised writes with logs in tmpfs and the frequently updated weather site sat in tmpfs too..logging to remote systems etc. So not quite sure what happened.
Of course this is all very inconvenient when your kit lives in another country, so at some point I guess I will have to build a new SD card and ship it out...for now we are back on Amazon EC2...yay for the elastic cloud \o/
What is a web app? What is the difference between a web app and a web site? What is the difference between a web app and a non-web app?
The presence of web browser-like user interface elements like a URL bar and navigation controls are likely to make a user feel like they’re using a web site rather than an app for example, whereas content which appears to run independently of the browser feels more like an app. Apps are generally assumed to have at least limited functionality without an Internet connection and tend to have the concept of residing in a self-contained way on the local device after being “installed”, rather than being navigated to somewhere on the Internet.
From a technical point of view there is in fact usually very little difference between a web site and a web app. Different platforms currently deal with the concept of “web apps” in all sorts of different, incompatible ways, but very often the main difference between a web site and web app is simply the presence of an “app manifest”. The app manifest is a file containing a collection of metadata which is used when “installing” the app to create an icon on a homescreen or launcher.
At the moment pretty much every platform has its own proprietary app manifest format, but the W3C has the beginnings of a proposed specification for a standard “Manifest for web applications” which is starting to get traction with multiple browser vendors.
Web Manifest – Describing an App
Below is an example of a web app manifest following the proposed standard format.
The manifest file is referenced inside the HTML of a web page using a link relation. This is cool because with this approach a web app doesn’t have to be distributed through a centrally controlled app store, it can be discovered and installed from any web page.
As you can see from the example, these basic pieces of metadata which describe things like a name, an icon and a start URL are not that interesting in themselves because these things can already be expressed in HTML in a web standard way. But there are some other other proposed properties which could be much more interesting.
Display Modes – Breaking out of the Browser
We said above that one thing that makes a web app feel more app like is when it runs outside of the browser, without common browser UI elements like the URL bar and navigation controls. The proposed “display” property of the manifest allows authors of web content which is designed to function without the need for these UI elements to express that they want their content to run outside of the browser.
The proposed display modes are “fullscreen”, “standalone”, “minimal-ui” and “browser”. The “browser” display mode opens the content in the user agent’s conventional method (e.g. a browser tab), but all of the other display modes open the content separate from the browser, with varying levels of browser UI.
There’s also a proposed “orientation” property which allows the content author to specify the default orientation (i.e. portrait/landscape) of their content.
App Scope – A Slice of the Web
In order for a web app to be treated separately from the rest of the web, we need to be able to define which parts of the web are part of the app, and which are not. The proposed “scope” property of the manifest defines the URL scope to which the manifest applies.
By default the scope of a web app is anything from the same origin as its manifest, but a single origin can also be sliced up into multiple apps or into app and non-app content.
Below is an example of a web app manifest with a defined scope.
From the user’s point of view they can browse around the web, seamlessly navigating between web apps and web sites until they come across something they want to keep on their device and use often. They can then slice off that part of the web by “bookmarking” or “installing” it on their device to create an icon on their homescreen or launcher. From that point on, that slice of the web will be treated separately from the browser in its own “app”.
Without a defined scope, a web app is just a web page opened in a browser window which can then be navigated to any URL. If that window doesn’t have any browser-like navigation controls or a URL bar then the user can get stranded at a dead on the web with no way to go back, or worse still can be fooled into thinking that a web page they thought was part of a web app they trust is actually from another, malicious, origin.
The web browser is like a catch-all app for browsing all of the parts of the web which the user hasn’t sliced off to use as a standalone app. Once a web app is registered with the user agent as managing a defined slice of the web, the user can seamlessly link into and out of installed web apps and the rest of the web as they please.
Service Workers – Going Offline
We said above that another characteristic users often associate with “apps” is their ability to work offline, in the absence of a connection to the Internet. This is historically something the web has done pretty badly at. AppCache was a proposed standard intended for this purpose, but there are many common problems and limitations of that technology which make it difficult or impractical to use in many cases.
A new, much more versatile, proposed standard is called Service Workers. Service Workers allow a script to be registered as managing a slice of the web, even whilst offline, by intercepting HTTP requests to URLs within a specified scope. A Service Worker can keep an offline cache of web resources and decide when to use the offline version and when to fetch a new version over the Internet.
The programmable nature of Service Workers make them an extremely versatile tool in adding app-like capabilities to web content and getting rid of the notion that using the web requires a persistent connection to the Internet. Service Workers have lots of support from multiple browser vendors and you can expect to see them coming to life soon.
The proposed “service_worker” property of the manifest allows a content author to define a Service Worker which should be registered with a specified URL scope when a web app is installed or bookmarked on a device. That means that in the process of installing a web app, an offline cache of web resources can be populated and other installation steps can take place.
Below is our example web app manifest with a Service Worker defined.
The reason this approach is commonly taken is that it allows operating system developers and content authors to side-step some of the current shortcomings of the web platform. Packaging all of the resources of an app into a single file which can be downloaded and installed on a device is the simplest way to solve the offline problem. It also has the convenience that the contents of that package can easily be reviewed and cryptographically signed by a trusted party in order to safely give the app privileged access to system functions which would currently be unsafe to expose to the web.
Unfortunately the packaged app approach misses out on many of the biggest benefits of the web, like its universal and inter-linked nature. You can’t hyperlink into a packaged app, and providing an updated version of the app requires a completely different mechanism to that of web content.
We have seen above how Service Workers hold some promise in finally solving the offline problem, but packages as a concept may still have some value on the web. The proposed “Packaging on the Web” specification is exploring ways to take advantage of some of the benefits of packages, whilst retaining all the benefits of URLs and the web.
This specification does not explore a new security model for exposing more privileged APIs to the web however, which in my view is the single biggest unsolved problem we now have left on the web as a platform.
In conclusion, a look at some of the latest emerging web standards tells us that the answer to the question “what is a web app?” is that a web app is simply a slice of the web which can be used separately from the browser.
With that in mind, web authors should design their content to work just as well inside and outside the browser and just as well offline as online.
Packaged apps are not web apps and are always a platform-specific solution. They should only be considered as a last resort for apps which need access to privileged functionality that can’t yet be safely exposed to the web. New web technologies will help negate the need for packages for offline functionality, but packages as a concept may still have a role on the web. A security model suitable for exposing more privileged functionality to the web is one of the last remaining unsolved challenges for the web as a platform.
The web is the biggest ecosystem of content that exists, far bigger than any proprietary walled garden of curated content. Lots of cool stuff is possible using web technologies to build experiences which users would consider “app like”, but creating a great user experience on the web doesn’t require replicating all of the other trappings of proprietary apps. The web has lots of unique benefits over other app platforms and is unrivalled in its scale, ubiquity, openness and diversity.
It’s important that as we invent cool new web technologies we remember to agree on standards for them which work cross-platform, so that we don’t miss out on these unique benefits.
So its been a few years since I’ve posted, because its been so much hard work, and we’ve been pushing really hard on some projects which I just can’t talk about – annoyingly. Anyways, March 20th , 2011 I talked about Continual Integration and Continual Deployment and the Cloud and discussed two main methods – having what we now call ‘Gold Standards’ vs continually updating.
The interesting thing is that as we’ve grown as a company, and as we’ve become more ‘Enterprise’, we’ve brought in more systems administrators and begun to really separate the deployments from the development. The other thing is we have separated our services out into multiple vertical strands, which have different roles. This means we have slightly different processes for Banking or Payment based modules then we do from marketing modules. We’re able to segregate operational and content from personally identifiable information – PII having much higher regulation on who can (and auditing of who does) access.
Several other key things had to change: for instance, things like SSL keys of the servers shouldn’t be kept in the development repo. Now, of course not, I hear you yell, but its a very blurry line. For instance, should the Django configuration be kept in the repo? Well, yes, because that defines the modules and things like URLs. Should the nginx config be kept in the repo? Well, oh. if you keep *that* in then you would keep your SSL certs in…
So the answer becomes having lots of repo’s. One repo per application (django wise), and one repo per deployment containing configurations. And then you start looking at build tools to bring, for a particular server or cluster of servers up and running.
The process (for our more secure, audited services) is looking like a tool to bring an AMI up, get everything installed and configured, and then take a snapshot, and then a second tool that takes that AMI (and all the others needed) and builds the VPC inside of AWS. Its a step away from the continual deployment strategy, but it is mostly automated.
So, I have a SEN account (it's part of the PSN), I have 2 videos with SEN, I have a broken PS3 so I can no deactivate video (you can only do that from the console itself, yes, really)... and the response from SEN has been abysmal, specifically:
As we take the security of SEN accounts very seriously, we are unable to provide support on this matter by e-mail
as we will need you to answer some security questions before we can investigate this further. We need you to
phone us in order to verify your account details because we're not allowed to verify details via e-mail.
I mean, seriously, they're going to verify my details over the phone better than over e-mail how exactly? All the contact details are tied to my e-mail account, I have logged in to their control panel and renamed the broken PS3 to "Broken PS3", I have given them the serial number of the PS3, and yet they insist that I need to call them, because apparently they're fucking stupid. I'm damned glad that I only ever got 2 videos from SEN, both of which I own on DVD now anyways, this kind of idiotic tie in to a system is badly wrong.
So, you phone the number... and now you get stuck with hold music for ever... oh, yeah, great customer service here guys. I mean, seriously, WTF.
OK - 10 minutes on the phone, and still being told "One of our advisors will be with you shortly". I get the feeling that I'll just be writing off the 2 videos that I no longer have access to.
I'm damned glad that I didn't decide to buy more content from that - at least you can reset the games entitlement once every six months without jumping through all these hoops (you have to reactivate each console that you still want to use, but hey).
After some discussion last night at PHP Hants about the fact that irc is a great facilitator of support / discussion, but largely ignored because there is rarely enough information for a new user to get going I decided it may be worth putting together a howto type post so here goes…
What is irc?
First of all, what on earth is it? I’m tempted to describe it as Twitter done right years before Twitter even existed, but I’m a geek and I’ve been using irc for years. It has a long heritage, but unlike the ubiquitous email it hasn’t made the transition into mainstream use. In terms of usage it has similarities to things like Twitter and Instant Messaging. Let’s take a quick look at this.
Twitter allows you to broadcast messages, they get published and anyone who is subscribed to your feed can read what you say. Everything is pretty instant, and if somebody is watching the screen at the right time they can respond straight away. Instant Messaging on the other hand, is more of a direct conversation with a single person, or sometimes a group of people, but it too is pretty instantaneous – assuming, of course, that there’s someone reading what you’ve said. Both of these techonologies are pretty familiar to many. If you go to the appropriate website you are given the opportunity to sign up and either use a web based client or download one.
It is much the same for irc in terms of usage, although conversations are grouped into channels which generally focus on a particular topic rather than being generally broadcast (Twitter) or more specifically directed (Instant Messaging). The downside is that in most cases you don’t get a web page with clear instructions of how to sign up, download a client and find where the best place is to join the conversation.
There are two things you need to get going with irc, a client and somewhere to connect to. Let’s put that into a more familiar context.
The client is what you use to connect with; this can be an application – so as an example Outlook or Thunderbird would be a mail client, or IE, Firefox, Chrome or Safari are examples of clients for web pages – or it can be a web page that does the same thing – so if you go to twitter.com and login you are using the web page as your Twitter client. Somewhere to connect to can be compared to a web address, or if you’ve got close enough to the configuration of your email to see the details, your mail server address.
Let’s start with the ‘somewhere to connect to‘ bit. Freenode is one of the most popular irc servers, so let’s take a look. First we’ll see what we can find out from their website, http://freenode.net/.
There’s a lot of very daunting information there for somebody new to irc, so ignore most of it and follow the Webchat link on the left.
That’s all very well and good, but what do we put in there? I guess the screenshot above gives a clue, but if you actually visit the page the entry boxes will be blank. Well first off there’s the Nickname, this can be pretty much anything you like, no need to register it – stick to the basics of letters, numbers and some simple punctuation (if you want to), keep it short and so long as nobody else is already using it you should be fine; if it doesn’t work try another. Channels is the awkward one, how do you know what channels there are? If you’re lucky you’re looking into this because you’ve been told there’s a channel there and hopefully you’ve been given the channel name. For now let’s just use the PHP Hants channel, so that would be #phph in the Channels box. Now all you need to do is type in the captcha, ignore the tick boxes and click Connect and you are on the irc channel and ready to chat. Down the right you’ll see a list of who else is there, and in the main window there will be a bit of introductory information (e.g. topic for the channel) and depending on how busy it is anything from nothing to a fast scrolling screen of text.
If you’ve miss typed there’s a chance you’ll end up in a channel specially created for you because it didn’t exist; don’t worry, just quit and try again (I’ll explain that process shortly).
For now all you really need to worry about is typing in text an posting it, this is as simple as typing it into the entry box at the bottom of the page and pressing return. Be polite, be patient and you’ll be fine. There are plenty of commands that you can use to do things, but for now the only one you need to worry about is the one to leave, this is:
Type it in the entry box, press return and you’ve disconnected from the server. The next thing to look into is using a client program since this is far more flexible, but I’ll save that for another post.
Actual progress on this Ph.D revision has been quite slow. My current
efforts are on improving the focus of the thesis. One of the
criticisms the examiners made (somewhat obliquely) was that it wasn&apost
very clear exactly what my subject was: musicology? music information
retrieval? computational musicology? And the reason for this was that
I failed to make that clear to myself. It was only at the writing up
stage, when I was trying to put together a coherent argument, that I
decided to try and make it a story about
music information retrieval (MIR). I tried to
argue that MIR&aposs existing evaluation work (which was largely modelled
information retrieval evaluation
from the text world) only took into account the music information
needs of recreational users of MIR systems, and that there was very
little in the way of studying the music information seeking behaviour
of "serious" users. However, the examiners didn&apost even accept that
information retrieval was an important problem for musicology,
nevermind that there was work to be done in examining music
information needs of music scholarship.
So I&aposm using this as an excuse to shift the focus away from MIR a
little and towards something more like computational musicology and
music informatics. I&aposm putting together a case study of a
computational musicology toolkit called
music21. Doing this allows me to focus
in more detail on a smaller and more distinct community of users
(rather than attempting to studying musicologists in general which was
another problematic feature of the thesis), it makes it much clearer
what kind of music research can be addressed using the technology
(all of MIR is either far too diverse or far too generic, depending
on how you want to spin it), and also allows me to work with the
actually Purcell Plus project
materials using the toolkit.
The other day we had a meeting at work with a former colleague (now at
QMUL) to discuss general
project progress. The
topics covered included the somewhat complicated workflow that we&aposre
using for doing optical music recognition (OMR) on
early printed music
sources. It includes mensural notation specific OMR software called
Aruspix. Aruspix itself is fairly
accurate in its output, but the reason why our workflow is non-trivial
is that the sources we&aposre working with are partbooks; that is, each
part (or voice) of a multi-part texture is written on its own part of
the page, or even on a different page. This is very different to
modern score notation in which each part is written in vertical
alignment. In these sources, we don&apost even know where separate pieces
begin and end, and they can actually begin in the middle of a
line. The aim is to go from the double page scans ("openings") to
distinct pieces with their complete and correctly aligned parts.
Anyway, our colleague from QMUL was very interested in this little
part of the project and suggested that we spend the afternoon, after
the style of good software engineering, formalising the workflow. So
that&aposs what we did. During the course of the conversation diagrams
were drawn on the whiteboard. However (and this was really the point
of this post) I made notes in Haskell. It occurred to me a few minutes
into the conversation that laying out some types and the operations
over those types that comprise our workflow is pretty much exactly the
kind of formal specification we needed.
Here&aposs what I typed:
module MusicalDocuments whereimport Data.Maybe-- A document comprises some number of openings (double page spreads)data Document = Document [Opening]-- An opening comprises one or two pages (usually two)data Opening = Opening (Page,Maybe Page)-- A page comprises multiple systemsdata Page = Page [System]-- Each part is the line for a particular voicedata Voice = Superius | Discantus | Tenor | Contratenor | Bassus
-- A part comprises a list of musical sybmols, but it may span mutliple systems--(including partial systems)data Part = Part [MusicalSymbol]-- A piece comprises some number of sectionsdata Piece = Piece [Section]-- A system is a collection of stavesdata System = System [Staff]-- A staff is a list of atomic graphical symbolsdata Staff = Staff [Glyph]-- A section is a collection of partsdata Section = Section [Part]-- These are the atomic components, MusicalSymbols are semantic and Glyphs are--syntactic (i.e. just image elements)data MusicalSymbol = MusicalSymbol
data Glyph = Glyph
-- If this were real, Image would abstract over some kind of binary formatdata Image = Image
-- One of the important properties we need in order to be able to construct pieces-- from the scanned components is to be able to say when objects of the some of the-- types are strictly contiguous, i.e. this staff immediately follows that staffclass Contiguous a where
immediatelyFollows :: a -> a ->Bool
immediatelyPrecedes :: a -> a ->Bool
immediatelyPrecedes a b = b `immediatelyFollows` a
instance Contiguous Staff where
immediatelyFollows :: Staff -> Staff ->Bool
immediatelyFollows =undefined-- Another interesting property of this data set is that there are a number of-- duplicate scans of openings, but nothing in the metadata that indicates this,-- so our workflow needs to recognise duplicatesinstance Eq Opening where(==) :: Opening -> Opening ->Bool(==) a b =undefined-- Maybe it would also be useful to have equality for staves too?instance Eq Staff where(==) :: Staff -> Staff ->Bool(==) a b =undefined-- The following functions actually represent the workflow
collate :: [Document]
scan :: Document -> [Image]
scan =undefinedsplit:: Image -> Opening
paginate :: Opening -> [Page]
omr :: Page -> [System]
segment :: System -> [Staff]
tokenize :: Staff -> [Glyph]
recogniseMusicalSymbol :: Glyph ->Maybe MusicalSymbol
part :: [Glyph] ->Maybe Part
part gs =ifnull symbols then Nothing else Just $ Part symbols
where symbols =mapMaybe recogniseMusicalSymbol gs
alignable :: Part -> Part ->Bool
piece :: [Part] ->Maybe Piece
I then added the comments and implemented the part function later
on. Looking at it now, I keep wondering whether the types of the
functions really make sense; especially where a return type is a type
that&aposs just a label for a list or pair.
I haven&apost written much Haskell code before, and given that I&aposve only
implemented one function here, I still haven&apost written much Haskell
code. But it seemed to be a nice way to formalise this procedure. Any
criticisms (or function implementations!) welcome.
I had a friend come to me with an interesting problem they were having in their office. Due to the Exchange server and Office licencing they have they are running Outlook 2003 on Windows 7 64bit Machines.
After Internet Explorer updates to IE11 it introduces a rather annoying bug into Outlook.
Typed emails often get cut off mid sentence when you click Send ! So only part of the email gets sent !
What I think is happening is that Outlook is reverting to a previously autosaved copy before sending.
Removing the IE11 update would probably fix it but perhaps the easiest way is to disable the "Autosave unsent email" option in Outlook.
Tools, Options, E-Mail Options, Advanced E-Mail Options, and disable the "Autosave unsent" option.
I purchased this book http://www.amazon.co.uk/dp/0738206679 (Linked, by Barabasi) on the 24th of December 2002, I had managed to make 6 or 7 aborted attempts at reading it to completion where life had suddenly got busy and just took over. This meant that I put the book down and didn't pick it up again until things were less hectic some time later and I started again.
Anyhow, I finally beat the book a few nights ago, my comprehension of it was pretty low anyhow but at least it is done. Just shows I need to read lots more given how little went in.
I finally made it back out onto the bike today for the first time since September last year. I'd spent some time ill in October and November which meant I had to stop exercising and as a result I've gained loads of weight over the winter and it turns out also become very unfit which can be verified by looking at the Strava ride from today: http://www.strava.com/activities/110354158
Anyhow, a nice thing about this ride is that I can record it on Strava and get this data about how unfit I have become, this is because last year I bought a Mio Cyclo 305 HC cycle computer http://eu.mio.com/en_gb/mio-cyclo-305-hc.htm from Halfords reduced to £144.50 (using a British Cycling discount). I was originally going to get a Garmin 500 but Amazon put the price up from £149.99 the day I was going to buy it to £199.99.
I knew when I got the Mio that it had a few issues surrounding usability and features but it was cheap enough at under £150 that I figured that even if I didn't get on with it I'd at least have a cadence sensor and heart rate monitor so I could just buy a Garmin 510 when they sorted out the firmware bugs with that and the price came down a bit which is still my longer term intention.
So it turns out a couple of weeks ago I plugged my Mio into a Windows VM when I was testing USB support and carried out a check for new firmware. I was rather surprised to see a new firmware update and new set of map data was available for download. So I installed it think I wasn't going to get any new features from it as Mio had released some new models but it turns out that the new firmware actually enables a single feature (amongst other things, they also tidied up the UI and sorted a few other bugs along with some other features) that makes the device massively more useful as it now also creates files in .fit format which can be uploaded directly to Strava.
This is massively useful for me as although the Mio always worked in Linux as the device is essentially just a USB mass storage device but you would have to do an intermediate step of having to use https://github.com/rhyas/GPXConverter to convert the files from the Mio-centric GPX format to something Strava would recognise. Now I can just browse to the folder and upload the file directly which is very handy.
All in it turns out that buying a Mio which reading reviews and forums were full of doom and gloom means I can wait even longer before considering replacement with a garmin.
So, got a beep this morning from our work monitoring system. One of our customers domain names is hosted with livedns.co.uk (which, as far as I can tell, is part of the Fasthosts franchise)... It appears that Fasthosts have managed to entirely break their DNS:
brettp@laptop:~$ host www.fasthosts.com
;; connection timed out; no servers could be reached
brettp@laptop:~$ whois fasthosts.com | grep -i "Name Server"
Name Server: NS1.FASTHOSTS.NET.UK
Name Server: NS2.FASTHOSTS.NET.UK
Name Server: NS1.FASTHOSTS.NET.UK
Name Server: NS2.FASTHOSTS.NET.UK
brettp@laptop:~$ whois fasthosts.net.uk | grep -A 2 "Name servers:"
brettp@laptop:~$ host -t ns fasthosts.net.uk 18.104.22.168
;; connection timed out; no servers could be reached
brettp@laptop:~$ host -t ns fasthosts.net.uk 22.214.171.124
;; connection timed out; no servers could be reached
So, that's fasthosts core nameservers not responding, good start! They also provide livedns.co.uk, so lets have a look at that:
brettp@laptop:~$ whois livedns.co.uk | grep -A 3 "Name servers:"
brettp@laptop:~$ host -t ns ns1.livedns.co.uk 126.96.36.199
;; connection timed out; no servers could be reached
brettp@laptop:~$ host -t ns ns1.livedns.co.uk 188.8.131.52
;; connection timed out; no servers could be reached
brettp@laptop:~$ host -t ns ns1.livedns.co.uk 184.108.40.206
;; connection timed out; no servers could be reached
So, erm, apparently that's all their DNS servers "Not entirely functioning correctly"! That's quite impressive!
It's New Year's Day 2014 and I'm reflecting on the music of past year.
Album wise there were several okay...ish releases in the world of Progressive Rock. Steven Wilson's The Raven That Refused To Sing not the absolute masterpiece some have eulogised a solid effort though but it did contain some filler. Motorpsyco entertained with Still Life With Eggplant not as good as their previous album but again a solid effort. Magenta as ever didn't disappoint with The 27 Club, wishing Tina Booth a swift recovery from her ill health.
The Three stand out albums in no particular order for me were Edison's Children's Final Breath Before November which almost made it as album of the year and Big Big Train with English Electric Full Power which combined last years Part One and this years Part Two with some extra goodies to make the whole greater than the sum of the parts. Also Adrian Jones of Nine Stones Close fame pulled one out of the bag with his side Project Jet Black Sea which was very different and a challenging listen, hard going at first but surprisingly very good. This man is one superb guitarist especially if you like emotion wrung out of the instrument like David Gilmore or Steve Rothery.
The moniker of Album of the Year this year goes to Fish for the incredible Feast of Consequences. A real return to form and his best work since Raingods With Zippos. The packaging of the deluxe edition with a splendid book featuring the wonderful artwork of Mark Wilkinson was superb. A real treat with a very thought provoking suite about the first world war really hammed home the saying "Lest we forget". A fine piece that needs to be heard every November 11th.
Gig wise again Fish at the Junction in Cambridge was great. His voice may not be what it was in 1985 but he is the consummate performer, very at home on the stage. As a raconteur between songs he is as every bit as entertaining as he is singing songs themselves.
The March Marillion Convention in Port Zealand, Holland where they performed their masterpiece Brave was very special as every performance of incredible album is. The Marillion Conventions are always special but Brave made this one even more special than it would normally be. Gig of the year goes again to Marillion at Aylesbury Friars in November. I had waited thirty years and forty odd shows to see them perform Garden Party segued into Market Square Heroes that glorious night it came to pass, I'm am now one very happy Progger or should that be Proggie? Nevermind Viva Progressive Rock!
I have been running a Code Club at my local Primary School for a while now, and thought it was about time I put details of a few tweaks I’ve made to the default Scratch install to make things easier. So here goes:
With the default install of Scratch (on Windows) projects are saved to the C: drive. For a network environment, with pupils work stored on a network drive so they always have access whichever machine they sit at, this isn’t exactly helpful. It also isn’t ideal that they can explore the C: drive in spite of profile restrictions (although it isn’t the end of the world as there is little they can do from Scratch).
After a bit of time with Google I found the answer, and since it didn’t immediately leap out at me when I was searching I thought I’d post it here (perhaps my Google Fu was weak that day). It is actually quite simple, especially for the average Code Club volunteer I should imagine; just edit the scratch.ini file. This is, as would be expected, located in:
Initially it looks like this:
Pretty standard stuff, but unfortunately no comments to indicate what else you can do with it. As it happens you can add the following two lines (for example):
To get this:
They do exactly what is says on the tin. If you click on the Home button in a file dialogue box then you only get the drive(s) specified. You can also put a full path in if you want to put the home directory further down the directory structure.
The VisibleDrives option restricts what you can see if you click on the Computer button in a file dialogue box. If you want to allow more visible drives then separate them with a comma.
You can do the same with a Mac (for the home drive), just use the appropriate directory format (i.e. no drive letter and the opposite direction slash).
There is more that you can do, so take a look at the Scratch documentation here. For example if you use a * in the directory path it is replaced by the name of the currently logged on user.
Depending on your network environment it may be handy for your Code Club to put the extra resources on a shared network drive and open up an extra drive in the VisibleDrives. One I haven’t tried yet it is the proxy setting, which I hope will allow me to upload projects to the Scratch website. It goes something like:
ProxyServer=[server name or IP address]
I wired up a MIDI In port along the lines of This one here, messed with the code a bit and voila (and potentially viola), I can play LV2 instrument plugins using a MIDI keyboard:
When I say "LV2 synth plugins", I should clarify that I'm only using the LV2 plugin C API, not the whole .ttl text file shebangle. I hope to get around to that at some point but it will be a while before you can directly plug LV2s into this and expect them to just work.
I wanted to print from my LinuxMint 14 (Cinnamon) PC via a shared Windows printer on my network. Problem is it isn’t found by the printers dialog in system settings. I thought I’d done all the normal things to get samba to play nice like rearranging the name resolve order in /etc/samba/smb.conf to a more sane bcast host lmhosts wins. Having host and wins, neither of which I’m using first in the order cocks things up some what. Every time I tried to search for the printer in the system setting dialog it told me “FirewallD is not running. Network printer detection needs services mdns, ipp, ipp-client and samba-client enabled on firewall.” So much scratching of the head there then, because as far as I can tell there ain’t no daemon by that name available!
It turns out thanks to /pseudomorph this has been a bug since LinuxMint12 (based on Ubuntu 11.10). It’s due to that particular daemon (Windows people daemon pretty much = service) being Fedora specific and should have no place in a Debian/Ubuntu based distribution. Bugs of this nature really should be ironed out sooner.
Anyway the simple fix is to use the more traditional approach using the older printer dialog which is accessed by inputting system-config-printer at the command line. Which works just fine so why the new (over a year old) printer config dialog that is inherently broken I ask myself.
The CUPS web interface also works apparently http://localhost:631/ in your favourite browser which should be there as long as CUPS is installed which it is in LinuxMint by default.
So come on Minty people get your bug squashing boots on and stamp on this one please.
Bug #871985 only affects Gnome3 so as long as its not affecting Unity that will be okay Canonical will it!
Lately, (well I say lately, I think it’s been the same for a few years now) I have been finding that it is very rare that an album comes along that affects me in a way that music I heard 10 years ago seem to. That is not to say that I have not heard any music that I like in that time, it just doesn’t seem to mean as music that has been in my life for years. What I am trying to work out is if that is a reflection on the state of music, of how I experience music or just me.
Buying music was always quite an experience. I would spend weeks, months and sometimes longer saving up to buy some new music. Whether I knew exactly what I wanted or just wanted “something else by this artist” I would spend some time browsing the racks weighing up what was the best value for my money. In the days before the internet, if you wanted to research an artist’s back catalogue, you were generally out of luck unless you had access to books about the artists. This lead to the thrill of finding a hidden gem in the racks that you didn’t know existed or had only heard rumours about. The anticipation of listening to the new music would build even more because I would have to wait until I had travelleled home before I could listen to my new purchases.
Nowadays, with the dizzying amount of music constantly pumped into our ears through the internet, radio, advertising and the plethora of styles and genres, it is difficult to sift through and find artists and music that really speak to you. Luckily, there are websites available to catalogue releases by artists so you are able to do thorough research and even preview your music before you purchase it. Of course the distribution methods have changed massively too. No longer do I have to wait until I can make it to a brick and mortar store to hand over my cash. I can now not only buy physical musical releases on CD or Vinyl online and have it delivered to my door, I can also buy digital music through iTunes, Amazon or Bandcamp or even stream the music straight to my ears through services like Spotify or Rdio. Whilst these online sales avenues are great for artists to be able to sell directly to their fans, I feel that some of the magic has been removed from the purchasing of music for me.
Listening to the music used to be an even greater event than purchasing it. After having spent the time saving up for the purchase, then the time carefully choosing the music to buy and getting it home, I would then sit myself down and listen to the music. I would immerse myself totally in the music and only listen to it (I might read the liner notes if I hadn’t exhausted them on the way home). It is difficult to imagine doing one thing for 45+ minutes without the constant interruptions from smartphones, tablet computers, games consoles and televisions these days. I can’t rememeber the last time I listened to music on good speakers or headphones (generally I listen on crappy computers speakers or to compressed audio on my iPhone through crappy headphones) without reading Twitter, replying to emails or reading copiuous amounts of information about the artists on Wikipedia. This all serves to distract from the actual enjoyment of just listening to the music.
The actual act of writing this blog post has called into sharp focus the main reason why music doesn’t seem to affect me nowadays as much as it used to – because I don’t experience it in the same way. My life has changed, I have more resposibilities and less time to just listen which makes the convenience and speed of buying digital music online much more appealing. You would think that this ‘instant music’ should be instantly satisfying but for some reason it doesn’t seem to work that way.
I wonder if I am the only one experiencing this? My tastes in music have definitely changed a lot over the last few years, but I still find it hard to find music that I want to listen to again and again. I’m hoping I’m not alone in this, alternatively I’m hoping someone might read this and recommend some awesome music to me and cure this weird musical apathy I appear to me suffering from.
It's difficult to use the terrace for a couple of weeks, because the black redstart family is in their summer residence at the top of a column under the roof. The chicks grow very fast, and the parents have to feed them frequently; when anyone goes out on the terrace they stop the feeding process and click shrill warnings to the chicks to stay still. I worry that if we disturb them too often or for too long the chicks will starve.
Black redstarts are called rougequeue noir (black red-tail) in French, but here they are known as rossignol des murailles (nightingale of the outside walls). Pretty!
The camera needs replacing, so there are no photos of Musatelier's rossignols des murailles, but you can see what they look like on http://fr.wikipedia.org/wiki/Rougequeue_noir.
Roundabouts are taken seriously here in France. Not so much as traffic measures (though it has been known for people to be cautioned by the local gendarmes for not signalling when leaving a roundabout, and quite rightly too), but as places to ornament.
A couple of years ago the roundabout at the edge of Mirambeau had a make-over which included an ironwork arch and a carrelet (fishing hut on stilts). Now it has a miniature vineyard as well, and roses and other plants for which this area is known.
I’ve been using this for a while and had it recorded on a private on a private wiki. I was just tidying up my hosting account and thought I’d get rid of the wiki and store any useful info from it on my blog
Clone full subversion history into git repository (warning, may take a long time depending on how many commits you have in your Subversion repository).