<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Code on Crossref</title><link>https://www-crossref-org.pluma.sjfc.edu/categories/code/</link><description>Recent content in Code on Crossref</description><generator>Hugo 0.139.4</generator><language>en-us</language><managingEditor>support@crossref.org (Crossref/Cazinc/Benoît Benedetti)</managingEditor><webMaster>support@crossref.org (Crossref/Cazinc/Benoît Benedetti)</webMaster><lastBuildDate>Mon, 18 Mar 2024 00:00:00 +0000</lastBuildDate><atom:link href="https://www-crossref-org.pluma.sjfc.edu/categories/code/" rel="self" type="application/rss+xml"/><item><title>Mending Chesterton's fence: open source decision-making</title><link>https://www-crossref-org.pluma.sjfc.edu/blog/mending-chestertons-fence-open-source-decision-making/</link><pubDate>Mon, 18 Mar 2024 00:00:00 +0000</pubDate><author>Joe Wass</author><guid>https://www-crossref-org.pluma.sjfc.edu/blog/mending-chestertons-fence-open-source-decision-making/</guid><description>&lt;p>When each line of code is written it is surrounded by a sea of context: who in the community this is for, what problem we&amp;rsquo;re trying to solve, what technical assumptions we&amp;rsquo;re making, what we already tried but didn&amp;rsquo;t work, how much coffee we&amp;rsquo;ve had today. All of these have an effect on the software we write.&lt;/p>
&lt;p>By the time the next person looks at that code, some of that context will have evaporated. There may be helpful code comments, tests, and specifications to explain how it should behave. But they don&amp;rsquo;t explain the path not taken, and why we didn&amp;rsquo;t take it. Or those occasions where the facts changed, so we changed our mind.&lt;/p>
&lt;p>Some parts of our system are as old as Crossref itself. Whilst our process still involves coffee, it&amp;rsquo;s safe to say that most of our working assumptions have changed, and for good reasons! We have to be very careful when working with our oldest code. We always consider why it was written that way, and what might have changed since. We&amp;rsquo;re always on the look out for &lt;a href="https://thoughtbot.com/blog/chestertons-fence" target="_blank">Chesterton&amp;rsquo;s Fence&lt;/a>!&lt;/p>
&lt;h2 id="leaving-a-trail">Leaving a Trail&lt;/h2>
&lt;p>We&amp;rsquo;re building a new generation of systems at Crossref, and as we go we&amp;rsquo;re being deliberate about supporting the people who will maintain it.&lt;/p>
&lt;p>When our oldest code was written, the software development team all worked in an office with a whiteboard or three, and the code was proprietary. Twenty years later, things are very different. The software development team is spread over 8 timezones. Thanks to &lt;a href="https://openscholarlyinfrastructure.org/" target="_blank">POSI&lt;/a>, all the new code we write is open source, so the next people to read that code might not even be Crossref staff.&lt;/p>
&lt;p>Working increasingly asynchronously, without that whiteboard, we need to record the options, collect evidence, and peer-review them within the team.&lt;/p>
&lt;p>So for the past couple of years the software team has maintained a &lt;a href="https://crossref.gitlab.io/engineering/decision-records/" target="_blank">decision register&lt;/a>. The first decision we recorded was that we should record decisions! Since then we have recorded the significant decisions as they arise. Plus some &lt;a href="https://crossref.gitlab.io/engineering/decision-records/dr-0003/" target="_blank">historical ones&lt;/a>.&lt;/p>
&lt;p>These aren&amp;rsquo;t functional specifications, which describe what the system should do. It&amp;rsquo;s the decisions and trade-offs we made along the way to get to the how. Look out for another blog post about specifications.&lt;/p>
&lt;p>By leaving a trail of explanations as we go, we make it easier for people to understand why code was written, and what has changed. We&amp;rsquo;re writing the story of our new systems. This makes it easier to alter the system in future in response to changes in our community, and the metadata they use.&lt;/p>
&lt;h2 id="difficult-decisions">Difficult Decisions&lt;/h2>
&lt;p>There are some fun challenges to building systems at Crossref. We have a lot of data. Our schema is very diverse, and has a vast amount of domain knowledge embedded in it. It&amp;rsquo;s changed over time to accommodate 20 years of scholarly publishing innovations. Our community is diverse too, from small one-person publishers with a handful of articles, through to large ones that publish millions.&lt;/p>
&lt;p>What might be an obvious decision for a database table with a thousand rows doesn&amp;rsquo;t always translate to a million. When you get to a billion, things change again. An initially sensible choice might not scale. And a scalable solution might look over-engineered if we had millions of DOIs, rather than hundreds of millions.&lt;/p>
&lt;p>The diversity of the data also poses challenges. A very simple feature might get complicated or expensive when it meets the heterogeneity of our metadata and membership. What might scale for journal article or grant metadata might not work for book chapters.&lt;/p>
&lt;p>The big decisions need careful discussion, experimentation, and justification.&lt;/p>
&lt;h2 id="2nf-or-not-2nf">2NF or not 2NF&lt;/h2>
&lt;p>One such recent decision was how we structure our SQL schema for the database that powers our new &amp;lsquo;relationships&amp;rsquo; REST API endpoint, currently in development.&lt;/p>
&lt;p>The data model is simple: we have a table of Relationships which connect pairs of Items. And each Item can have properties (such as a type). The way to model this is straightforward, following conventional &lt;a href="https://en.wikipedia.org/wiki/Database_normalization" target="_blank">normalization rules&lt;/a>:&lt;/p>
&lt;figure>&lt;img src="https://www-crossref-org.pluma.sjfc.edu/images/blog/2024/open-source-decision-making/1.svg"
alt="SQL Tables, normalised" width="100%">
&lt;/figure>
&lt;p>We built the API around it, and all was well.&lt;/p>
&lt;p>We then added a feature which lets you look up relationships based on the properties of the subject or object. For example &amp;ldquo;find citations where the subject is an article and the object is a dataset&amp;rdquo;. This design worked well in our initial testing. We loaded more data into it, and it continued to work well.&lt;/p>
&lt;p>And then, the context changed. Once we tested loading a billion relationships in the database, the performance dropped. The characteristics of the data: size, shape and distribution, reached a point where the database was unable to run queries in a timely way. The PostgreSQL query planner became unpredictable and occasionally produced some quite exciting query plans (to non-technical readers: databases are neither the time nor the place for excitement).&lt;/p>
&lt;p>This is a normal experience in scaling up a system. We expected that something like this would happen at some point, but you don&amp;rsquo;t know when it will happen until you try. We bounced around some ideas and came up with a couple of alternatives. Each made trade-offs around processing time, data storage and query flexibility. The best way to evaluate them was to use real data at a representative scale.&lt;/p>
&lt;p>One of the options was denormalisation. This is a conventional solution to this kind of problem, but was not our first choice as it involves extra machinery to keep the data up-to-date, and more storage. It would not have been the correct solution for a smaller dataset. But we had the evidence that the other two approaches would not scale predictably.&lt;/p>
&lt;figure>&lt;img src="https://www-crossref-org.pluma.sjfc.edu/images/blog/2024/open-source-decision-making/2.svg"
alt="SQL Tables, normalised" width="100%">
&lt;/figure>
&lt;br>
&lt;p>By combining the data into one table, we can serve up API requests much more predictably, and with much better performance. This code is now running with the right performance. Technical readers note that this diagram is simplified. The &lt;a href="https://gitlab.com/crossref/manifold/-/blob/62fc458b280e71c6b6221908fb7824bd3573726f/src/main/resources/db/migration/V1_0_0__initial_schema_creation.sql#L356" target="_blank">real SQL schema&lt;/a> is a little different.&lt;/p>
&lt;p>Without writing this history down, and explaining what we tried, someone might misunderstand the reason for the code and try to simplify it. Decision record &lt;a href="https://crossref.gitlab.io/engineering/decision-records/dr-0500/" target="_blank">DR-0500&lt;/a> guards against that.&lt;/p>
&lt;p>But one day, when the context changes, future developers will be able to come back and modify the code, because they understand why it was like that in the first place.&lt;/p></description></item><item><title>Renewed persistence</title><link>https://www-crossref-org.pluma.sjfc.edu/blog/renewed-persistence/</link><pubDate>Sat, 01 Apr 2023 00:00:00 +0000</pubDate><author>Joe Wass</author><guid>https://www-crossref-org.pluma.sjfc.edu/blog/renewed-persistence/</guid><description>&lt;p>&lt;strong>We believe in Persistent Identifiers. We believe in defence in depth. Today we&amp;rsquo;re excited to announce an upgrade to our data resilience strategy.&lt;/strong>&lt;/p>
&lt;div style="float:right;margin:10px">
&lt;figure>&lt;img src="https://www-crossref-org.pluma.sjfc.edu/images/blog/2023/2023-04-01-renewed-persistence/single-floppy-small.jpg"
alt="5¼ inch floppy disk with Crossref logo" width="200px">
&lt;/figure>
&lt;/div>
&lt;p>&lt;a href="https://en.wikipedia.org/wiki/Defense_in_depth_%28computing%29" target="_blank">Defence in depth&lt;/a> means layers of security and resilience, and that means layers of backups. For some years now, our last line of defence has been a reliable, tried-and-tested technology. One that&amp;rsquo;s been around for a while. Yes, I&amp;rsquo;m talking about the humble &lt;a href="https://en.wikipedia.org/wiki/Floppy_disk#8-inch_and_5%C2%BC-inch_disks" target="_blank">5¼ inch floppy disk&lt;/a>.&lt;/p>
&lt;p>This may come as surprise to some. When things go well, you&amp;rsquo;re probably never aware of them. In day to day use, the only time a typical Crossref user sees a floppy disk is when they click &amp;lsquo;save&amp;rsquo; (yes, some journals still require submissions in Microsoft Word).&lt;/p>
&lt;h2 id="history">History&lt;/h2>
&lt;p>But why?&lt;/p>
&lt;p>Let me take you back to the early days of Crossref. The technology scene was different. This data was too important to trust to new and unproven technologies like Zip disks, CD-Rs or USB Thumb Drives. So we started with punched cards.&lt;/p>
&lt;figure>&lt;img src="https://www-crossref-org.pluma.sjfc.edu/images/blog/2023/2023-04-01-renewed-persistence/punched-card.jpg"
alt="IBM 5081-style punched card." width="100%">&lt;figcaption>
&lt;p>IBM 5081-style punched card.&lt;/p>
&lt;/figcaption>
&lt;/figure>
&lt;p>Punched cards are reliable and durable as long as you don&amp;rsquo;t &lt;a href="https://en.wikipedia.org/wiki/Punched_card#Do_Not_Fold,_Spindle_or_Mutilate" target="_blank">fold, spindle or mutilate&lt;/a> them. But even in 2001 we knew that punched cards&amp;rsquo; days were numbered. The capacity of 80 characters kept DOIs short. Translating DOIs into &lt;a href="https://en.wikipedia.org/wiki/EBCDIC" target="_blank">EBCDIC&lt;/a> made &lt;a href="https://en.wikipedia.org/wiki/ASCII" target="_blank">ASCII&lt;/a> a challenge, let alone &lt;a href="https://en.wikipedia.org/wiki/Serial_Item_and_Contribution_Identifier" target="_blank">SICI&lt;/a>s. We kept a close eye on the nascent Unicode.&lt;/p>
&lt;h2 id="breathing-room">Breathing Room&lt;/h2>
&lt;p>In 2017 the change of DOI display guidelines from &lt;code>http://dx.doi.org.pluma.sjfc.edu&lt;/code> to &lt;code>https://doi-org.pluma.sjfc.edu&lt;/code> shortened each DOI by 2 characters, buying us some time. But eventually we knew we had to upgrade to something more modern.&lt;/p>
&lt;p>So we migrated to 5¼ inch floppy disks.&lt;/p>
&lt;figure>&lt;img src="https://www-crossref-org.pluma.sjfc.edu/images/blog/2023/2023-04-01-renewed-persistence/floppy-in-drive.jpg"
alt="5¼ Floppy disk in drive" width="100%">&lt;figcaption>
&lt;p>5¼ Floppy disk in drive&lt;/p>
&lt;/figcaption>
&lt;/figure>
&lt;p>At 640 KB per disk these were a huge improvement. We could fit around 20,000 DOIs on one floppy. Today we only need around 10,000 floppy disks to store all of our DOIs (not the metadata, just the DOIs). Surprisingly this only takes about 20 metres of shelf space to store.&lt;/p>
&lt;figure>&lt;img src="https://www-crossref-org.pluma.sjfc.edu/images/blog/2023/2023-04-01-renewed-persistence/desktop.jpg"
alt="laptop computer connected to floppy disk drive, pile of disks next to it" width="100%">&lt;figcaption>
&lt;p>Typical work from home setup. Getting ready to backup some DOIs!&lt;/p>
&lt;/figcaption>
&lt;/figure>
&lt;p>The move to working-from-home brought an unexpected benefit. Staff mail floppy disks to each other and keep them in constant rotation, which produces a distributed fault tolerant system.&lt;/p>
&lt;h2 id="persistence-means-change">Persistence Means Change&lt;/h2>
&lt;p>But it can&amp;rsquo;t last forever. DOIs registration shows no sign of slowing down. It&amp;rsquo;s clear we need a new, compact storage medium. So, after months of research, we&amp;rsquo;ve invested in new equipment.&lt;/p>
&lt;p>&lt;strong>Today we announce our migration to 3½ inch floppies.&lt;/strong>&lt;/p>
&lt;figure>&lt;img src="https://www-crossref-org.pluma.sjfc.edu/images/blog/2023/2023-04-01-renewed-persistence/new-equipment.jpg"
alt="dual format floppy disk drive, with 5¼ inch and 3½ inch floppy disks" width="100%">
&lt;/figure>
&lt;br>
&lt;p>If it goes to plan you won&amp;rsquo;t even notice the change.&lt;/p>
&lt;figure>&lt;img src="https://www-crossref-org.pluma.sjfc.edu/images/blog/2023/2023-04-01-renewed-persistence/old-new.jpg"
alt="two stacks of disks" width="100%">
&lt;/figure>
&lt;h3 id="image-credits">Image credits&lt;/h3>
&lt;p>Punched card: IBM 5081-style punched card. Derived from &lt;a href="https://en.wikipedia.org/wiki/File:Blue-punch-card-front-horiz_top-char-contrast-stretched.png" target="_blank">public domain by Gwern&lt;/a>.&lt;/p></description></item><item><title>Accidental release of internal passwords, &amp; API tokens for the Crossref system</title><link>https://www-crossref-org.pluma.sjfc.edu/blog/accidental-release-of-internal-passwords-api-tokens-for-the-crossref-system/</link><pubDate>Fri, 04 Oct 2019 00:00:00 +0000</pubDate><author>Geoffrey Bilder</author><guid>https://www-crossref-org.pluma.sjfc.edu/blog/accidental-release-of-internal-passwords-api-tokens-for-the-crossref-system/</guid><description>&lt;h2 id="tldr">TL;DR&lt;/h2>
&lt;p>On Wednesday, October 2nd, 2019 we discovered that we had accidentally pushed the main Crossref system as part of a docker image into a developer’s account on Docker Hub. The binaries and configuration files that made up the docker image included embedded passwords and API tokens that could have been used to compromise our systems and infrastructure. When we discovered this, we immediately secured the repo, changed all the passwords and secrets, and redeployed the system code. We have since been scanning all of our logs and systems to see if there has been any unusual activity that could be related to the exposure of the container.&lt;/p>
&lt;p>Please note that no external data e.g. member passwords or personal information were exposed; our source code contains only internal passwords and ‘secrets’ such as API tokens.&lt;/p>
&lt;p>Thankfully, the way in which these secrets were exposed (in compressed, binary files which were, in turn, in a Docker image) means that they were probably overlooked by the automated exploitation tools which focus on scanning source code. And, so far, we have seen nothing that would indicate that these passwords and secrets have been exploited. We will, of course, inform our members directly (and update this blog) if that changes.&lt;/p>
&lt;h2 id="more-than-you-probably-want-to-know">More than you probably want to know&lt;/h2>
&lt;p>If you are continuing to read this, my guess is that you might have questions like:&lt;/p>
&lt;ol>
&lt;li>Why are you doing something as silly as embedding secrets and passwords in your code?&lt;/li>
&lt;li>And wait a minute… I thought Crossref code was open source?&lt;/li>
&lt;li>And why is the director of strategic initiatives announcing this?&lt;/li>
&lt;/ol>
&lt;p>Let me answer these questions in random order.&lt;/p>
&lt;p>In March 2019 I took over Crossref’s technical teams when Chuck Koscher announced that he would be retiring at the end of the year. I’m now the director of technology &amp;amp; research.&lt;/p>
&lt;p>A few months earlier we had already concluded that a major portion of the Crossref system had accumulated 20 years of technical debt and that we were going to spend a significant portion of 2019 and 2020 paying down that debt.&lt;/p>
&lt;p>Specifically, a lot of the code that runs Crossref was inherited from a third party who developed it back in the early 2000s. This means that, even though any new systems that we’ve developed since 2007 have been open-source, the code for the oldest parts of the system has remained closed because it contained potentially proprietary code as well as a lot of deprecated coding practices. Also - the architecture, the tooling, and the development processes behind the Crossref system had not changed much in those twenty years. It was fantastic architecture, tooling, and code for its time. But architectures that scale to millions of records need to change to handle hundreds of millions of records. Processes that work for configuring one service need to change when you are managing dozens of services. And support tools that work for a few hundred members break down when you are dealing with tens of thousands of members.&lt;/p>
&lt;p>These parts of the Crossref system were decidedly not &lt;a href="https://en.wikipedia.org/wiki/Twelve-Factor_App_methodology" target="_blank">12 factor&lt;/a>. We were not using &lt;a href="https://en.wikipedia.org/wiki/DevOps" target="_blank">DevOps&lt;/a> or &lt;a href="https://en.wikipedia.org/wiki/Site_Reliability_Engineering" target="_blank">SRE&lt;/a> working practices to run them. And the bulk of that part of the system is still being run in a traditional data center.&lt;/p>
&lt;p>But since March we have been slowly fixing that. In incremental steps. Some of which are visible as a side effect of the security incident that precipitated this blog post. For example, one of our first moves was to move our development to &lt;a href="https://gitlab.com/crossref" target="_blank">Gitlab&lt;/a>. Even though a big chunk of the base Crossref code is still closed source, we saw moving to Gitlab as a priority because Gitlab offers a fantastic suite of tools to help automate and manage our deployments. Similarly, we have been Dockerizing the Crossref system so that it is easier to scale and run in different environments. And as part of this effort, we have spent a lot of time on the issue of how to best handle secrets. We knew our secrets management in this part of the codebase was horrible. We have been developing some experiments and infrastructure for handling these secrets securely. But we haven’t finished this work yet. And so the system slipped out into a public repo too early. Ironically, this too illustrates a fundamental change in the way we develop things. &lt;a href="https://www-crossref-org.pluma.sjfc.edu/truths/">Our default is to be open and transparent&lt;/a>. This case is currently an exception. An exception we want to eliminate, but one we are not ready to do yet. We have to audit and scrub the code first.&lt;/p>
&lt;p>Yes, this incident has been embarrassing. But not nearly as embarrassing as the fact that Crossref has succumbed to a technology industry cliche. That we spent so much time growing and focusing on new features for our members, that we neglected some of the creaking infrastructure of our infrastructure.&lt;/p>
&lt;p>And I should be clear about two things:&lt;/p>
&lt;p>First, not all of our code is like this. We have, for a long time, been building open source software and using modern best practices for secrets management in our newer subsystems and services. The problems described above are confined to twenty-year-old-code that we didn’t write in the first place and that we had been avoiding refactoring.&lt;/p>
&lt;p>And second, the technology team has been marvelous at responding to the challenge we face. They have adopted new processes and tools. They are learning new techniques. We are steadily chipping away at these problems.&lt;/p>
&lt;p>It is generally considered bad practice to praise or reward technology teams for fire-fighting instead of fire prevention, but this may be the exception that proves the rule.&lt;/p>
&lt;p>I was blown away by how the technology, product, and support teams worked together. When we discovered this problem, I sat at my desk in rural France and watched as staff from the UK, and all three US time zones shut down this problem in just a couple of hours. Obviously, I wish we hadn’t had the problem in the first place, but seeing their response did a great deal to encourage me that we are on the right track.&lt;/p>
&lt;p>In any case, it looks like we’ve been lucky. And we’ll be working even harder to refactor our code, tools, and processes so that this kind of thing doesn’t happen again.&lt;/p></description></item></channel></rss>