I’ve long held that software being open source1 is necessary, but not sufficient. Using copyright and contract law to enshrine the freedom to use your software instead of its normal purpose to ensure privatization is a brilliant move, but licenses like the popular Apache, BSD, or MIT licenses are easily abused by large companies to extract value from free labor that they would otherwise have to pay workers for. Further more, when a company chooses to violate the license outright, there is no reasonable mechanism for enforcement in a judicial environment where the license abuser most likely has the resources to bankrupt the software author and continue using their software as they see fit.
The so called “copyleft” licenses that have arisen from the Free Software movement such as the popular GPL license attempt to fix some of these problems by requiring any changes made to the software also be released under the same license. In this way companies that benefit financially from their use of the software must give back to the community in some way, preventing them from using their power to close off the commons and become landlords.
Furthermore, After a recent ruling in a U.S. state court in SFC v. Vizio, groups like the Software Freedom Conservancy are now considered third party beneficiaries to the license, allowing them to enforce license compliance on behalf of the authors. While this does not completely solve the power imbalance between large companies like Vizio who are known to abuse open source software and the authors who have more limited resources, it does level the playing field somewhat since non-profits like the SFC have a bigger platform on which to fundraise and can retain lawyers for much longer than an individual could.
Unfortunately, this still allows companies to make massive profits while not paying anything back to the workers who created the software.
A commons is a finite resource that is managed by a group for the collective benefit of the group. In our current society many (maybe even “most”) of us are only familiar with this concept through the “tragedy of the commons” which states that rational actors will act in self interest to exploit the commons for profit, eventually exhausting the limited resource.
Ostrom’s law, named after economist Elinor Ostrom and stated by Lee Anne Fennell, says:
A resource arrangement that works in practice can work in theory.
This rather flippant statement challenges the “tragedy” argument by pointing out that there are many historical examples of sustainable commons. Ostrom studied these examples and argued that the initial economic model behind the “tragedy” concept is flawed. This led to the identification of 8 criteria that should be met in a successfully managed commons.
What would it take to build a software commons that could be sustainably managed by a diverse community of individuals and software projects? And what would it take for that commons to not be exploitable for corporate interests? Let’s go through each of the 8 criteria and find out.
1. Define clear group boundaries.
Who gets to use the finite resource held in common and what are the limits of the group? At first blush software commons fail this rule to an extreme degree by allowing anyone, whether they contribute anything or not, to use the software. However, this makes the fatal assumption that the resource being held in common is the software itself, but software is information that can be copied ad infinitum. To quote Stewart Brand: “information wants to be free”.
The only limit to the software life span is how long it continues to be maintained, updated, and improved: In a software commons the resource isn't the software, it's contributor time and attention.
Most open source software is developed as a hobby—a side project—that takes a back seat the moment something comes up with work, or when the author gets burned out from being asked for things with no one else around to help. One way to do this is by compensating them fairly for their labor, a concept that many large open source consumers seem to struggle with. The goal of building a sustainable software commons then boils down to: how are you going to pay for it?
2. Match rules governing use of common goods to local needs and conditions.
Probably the best known formulation of the “tragedy of the commons” comes from an 1833 pamphlet by economist William Forster Lloyd. In it Lloyd describes cow herders grazing their cattle on common land. Because the land is not privatized, he surmises, herders are incentivized to put more cattle on the land to turn a bigger profit. When one neighbor does this, the others must as well to remain competitive. This cycle will continue until the land has been used up.
What this model fails to consider is that the herders can communicate with one another and come to an agreement about how best to manage the land and that, more importantly, they all have an incentive to do so. Furthermore, external rules may also apply to the system. For example, the communal grazing land in England at the time was governed by a law that no herder could graze more cattle on common land than they had the ability to overwinter, keeping the number of grazing cattle to a sustainable level.
Open source communities have many mechanisms for communication and rule making, but only one way of ensuring that other projects in the ecosystem also meet those needs: the software license. This means that our license should be govern the group boundaries mentioned in the previous section as well as the externalities of how non-contributors and external actors use the software. Software licenses should directly determine how we get paid up front without allowing large companies to take advantage of the output of the workers labor or requiring each project to negotiate (or beg) for donations separately.
Furthermore, because different software products will command different prices, we want to avoid a rush-to-the-bottom where software in the commons must compete on price, lowering the amount for everyone. To avoid this, the price should be set as a general rule across the board for all software that agrees to become part of the commons. If we make this price a percentage of the revenue that companies earn from their products using the software, and that percentage remains fixed no matter how many individual projects they use from the commons, the value of the commons will go up as the number of useful projects in it goes up. This also lets us command a higher than usual cost for our labor because the company is paying for use of the entire commons, not each individual piece of software. This in turn further encourages them to use other software in the commons and spread the payment to those projects as well.
Finally, if a project has no revenue yet: they don’t owe anything to the projects on which they’re building, allowing anyone to get started with your project for free until they do start generating revenue (if that is the intent). This keeps your project open and available for individuals and other commons projects!
3. Ensure that those affected by the rules can participate in modifying the rules.
Software licenses are mostly fixed and static, but the cost of labor is not. This means that if the license is also to govern how software is paid for we need a mechanism for changing that price. As the commons becomes more valuable (more people contribute software to it), the price of that software should rise to match. However, if one centralized organization that maintains the license were to set the cost of the software it would place them in an extreme position of power over the license users, discouraging them from contributing to the commons.
Instead the software projects themselves must set the price and exceptions to that price collaboratively.
4. Make sure the rule-making rights of community members are respected by outside authorities.
The license is already part of an external legal framework enforced by outside authorities. It acts as a contract between the software community, the users, and any third party developers who are not active in the main project community.
Most licenses only cover how others can use and modify the software, but do not make mention of internal project governance. Projects may make use of some form of democracy or they may use the controversial “Beneficial Dictator for Life” (BDFL) model in which a single individual controls all decisions about money, development, etc. Either way there are generally no provisions to stop one or more strong personalities from coming in and changing how the project is governed.
Baking democratic decision making into the license itself allows projects that are not large enough to have other legal frameworks such as a business vehicle with articles of incorporation and bylaws to legally enforce that the rights of community members are respected.
Other projects such as the experimental Cross License Collaborative model take a similar approach to mixing the license with governance. It may be possible to write a license that indicates the right to some form of democratic decision making so that existing models like the cross license model can be used in conjunction with the fiscal parts of the license.
5. Develop a system, carried out by community members, for monitoring members’ behavior.
You may have noticed that in the previous section we started discussing democracy among individual project members whereas previously we had been discussing a confederation of projects as a group making decisions about a shared license together.
The fact of the matter is that the commons must be maintained at every level. Individual software communities must make their own decisions as they see fit, but projects must also manage shared resources and infrastructure democratically.
At both levels we need a way to make sure that everyone else is playing by the agreed upon rules. Putting such a system in the license gives it legal teeth and creates a contract between individual projects that use the license whether they depend on each other or not, ensuring that everyone both has a say in the rules, and in enforcing them.
6. Use graduated sanctions for rule violators.
Sanctions are always tough. No one wants to be a cop — or, if they do, you probably don’t want them as a member of your project. Jumping straight to the nuclear option of banning or taking legal action against those who make minor license violations is bad for the commons as whole, because this means less chance for generating revenue and fewer contributors. Taking legal action is also expensive and, for smaller violations that don’t involve recuperating lost revenue, is likely not worth pursuing. Instead we want to try and bring people back into the fold by deciding on a set of rules and sanctions collectively that gradually increase with the level of the offence.
As with the previous sections, the license should reference these sanctions so that, in the worst case, there are legal protections for everyone involved.
7. Provide accessible, low-cost means for dispute resolution.
This is one of the hardest but most important rules to meet. With most software licenses it’s up to the software author or copyright holder to sue in a court of law for license enforcement, but this is beyond the means of most authors, making the license effectively useless against bad actors. The previously mentioned SFC v. Vizio case has made this somewhat better for the GPL specifically, but non-profits like the SFC still don’t have the same resources as the big companies and can’t take every single license enforcement case. This means that we need to cast a wide net in terms of license beneficiaries (those who have standing to sue), and try to build in other mechanisms for enforcement that are easier as mentioned in the previous section.
Almost all software EULAs provided with proprietary software include a forced arbitration clause in which you waive your right to sue. Disputes are then resolved by a less expensive process known as arbitration. In EULAs these clauses exist because arbitration is almost always more friendly to the big business trying to make you give up your rights, but we can borrow aspects of this strategy and use them for good as well.
A software commons should use forced arbitration to its advantage. This could be by performing arbitration with specific organizations that have agreed to act as the arbiters and allowing the commons community to vote on adding or removing those organizations from the list of acceptable ones, or by creating a jury of other projects in the commons that have no connection or dependency on the disputed project to arbitrate disputes. By joining the commons and becoming a beneficiary of the license each project could agree to a sortition system whereby they may be selected to help arbitrate disputes at any time. If, at a later date, they wind up in a dispute with another project or an external entity they would also have the benefit of the decision of a jury chosen by sortition and who’s decision has legal legitimacy thanks to the license.
8. Build responsibility for governing the common resource in nested tiers from the lowest level up to the entire interconnected system.
This is one of the easiest rules to apply to software because so much software works this way already. Unfortunately, many people interpret this rule to mean “form hierarchies of power,” but nothing could be further from the truth.
Instead each individual project is a tier that governs its own internal affairs as it sees fit: though hopefully that includes some form of democracy and consent based decision making. These projects are then confederated into a higher tier bound together by the license. Within the larger tier otherwise separate projects may decide to work together as one larger body, maybe as members of the same fiscal host or support organizations. Across these organizations and individual projects unions of otherwise unrelated projects might form where shared interests overlap. These tiers generally resemble the circles of sociocracy, but the exact governance method isn’t important so long as the projects decide together how they will make decisions without coercion or hierarchy.
To summarize, a sustainable software commons would require a new license or license addendum that provides (at a minimum) the following guarantees:
I’m aware that most companies would simply refuse to use any software that adopted such a license, and that this is all a bit abstract and may be difficult to do across legal jurisdictions. There are also a huge number of open questions about how exactly such a license would function across projects with a near infinite variety of governance structures, sizes, and tooling. Regardless, as an experiment I think the general idea is worth pursuing.
Want to explore these ideas further? Reach out and let’s collaborate!
Throughout this article I’ve used the term “open source” as a catch all to include open source software and free software. However, it should be noted that, if such a license were ever written, it would likely not meet the definition of open source as defined by the Open Source Initiative (OSI). This license would also have significant overlap with cooperative software, though I don’t think the two necessarily share a strict relationship. Perhaps a new term can be thought of for software that is part of this commons? ↩︎
I recently attended the inaugural Free and Open Source Software Yearly (FOSSY) conference where I gave a talk in the XMPP track. Though my talk was just a brief technical overview of the XMPP protocol, I also gave some quick ending remarks about why I think it’s the correct choice to use as a universal standardized chat protocol. The closing remarks were written about the XMPP protocol in particular, but they are also a reflection on free and open source software more generally. This post is an adapted form of that closing statement. If you’d like to see the original talk instead, it can be found on the Internet Archive.
Before we end, I’d like to take a little detour and make a brief observation on our role and our responsibilities as open source developers, advocates, and users. It may sound, at first, like I’ve veered off the tracks, but bear with me and it will all come back around to Open Source and XMPP in the end.
I recently read Becky Chamber’s “A Psalm for the Wild-Built”. Among the many beautiful and healing observations in the book, one phrase, stood out to me. It was possibly said in jest to make folks like us laugh, and maybe cry in equal measure. It was little more than a brief description of the main character’s mobile phone, but it resonated with me in a powerful way and I hope it will for you too:
“A reliable device, built to last a lifetime, as all computers were.”
Of course, this is clearly not how computers are designed today. Most of the time, I wouldn’t even go so far as to apply the adjective “reliable” to any of the software I use, open source or otherwise. But Utopian thinking isn’t about showing us where we are, or even portraying the world as it could be: it’s about inspiring us to apply our progressive ideals to create change.
Writing software is an inherently political act. While others choose to form hierarchical corporations that restrict access to the products of their workers labor, we choose to share our work freely and build cooperatively with others. That’s why we’re here at FOSSY. Being here is a statement of our values. Even the small, personal project thrown up on a code hosting website and built for no one but the author puts that person in a position of political power: when others use the software, the authors choices affect them. Whether its the choice to support or not support a screen reader, or the choice to use or not use a large framework that will only run on the latest-and-greatest (read: “expensive, unattainable for many”) machines. The author may not have made these choices consciously, but, over time, their effects will be felt by many never the less.
Therefore our primary responsibility as open source developers isn’t to shareholders or board members, or just to our current users and contributors: it’s to all the people who will use it tomorrow (and tomorrow, and tomorrow). Proprietary software stops working when the company behind it goes out of business, or when the operating system or architecture it was designed for becomes to expensive to maintain and starts affecting the bottom line, or when the VC money dries up. Meanwhile, the open source project, even once abandoned, can be updated and re-built a generation from now. It can be made more inclusive, it can be made more reliable, it can be made more portable. Unlike its closed-source alternatives, it’s repairable. When we chose to use or build closed-source software we are choosing convenience over repairability. It’s an understandable and powerful draw, whether we’re a user or a developer. However, when we choose to provide our software freely and build it cooperatively, we are choosing to support the future, and to reject convenience culture and disposable software.
It may be that you disagree with me and think that all software should be, as your license likely states, “provided as-is” without any responsibility on the part of the developer, and that’s fine. However you feel though, by writing the software you are sending a message to many people. Whether you like it or not, some will interpret it one way, some will interpret it another. The next time you open your laptop I hope you’ll think about what message you intend to send, and how it will be perceived. This is the essence of communication.
This is why I choose to use XMPP over the many proprietary or venture capital funded alternatives that come and go every day. However, as someone reminded me in the bar last night, being open source is itself necessary, but not sufficient. Our software will last far longer than its proprietary, or open-source but VC funded counterparts, and the decisions we make will reach much further into the future than even the largest companies can ever hope to achieve. Take pride in that, and build your software to reflect your values: build it to last.
Thank you for your time, and enjoy the rest of the XMPP track, and the rest of the conference.
Unfortunately I was running over time and ended up skipping the Q&A portion and my last few slides. Unrealized by me at the time, this included a thank you to the organization that sponsored me to be there: Cheogram. Cheogram is a bridge between the XMPP network and the telephone network, allowing you to send text messages and make calls from any XMPP client. They also run the phone company jmp.chat where you can get a phone number or SIM card and associate it with your XMPP account without doing all the work of setting it up yourself. If you end up using them, please consider using my registration code so we can both get a free month, and thanks again to Cheogram for sending me to the conference!
For one free month, my referral code for jmp.chat is BC6ZHFMA
vim(1)
, more or less extension-less with only very basic config tweaks so
that it feels similar no matter what machine I’m on; shortcuts are only
allowed after I’m already familiar with the default way of doing things.tmux(1)
BC6ZHFMA
)mutt(1)
The two parks I’m going to review today are also connected by the M2R trail in addition to the Concord Road Trail, but unlike the previous parks these are linear parks that are integrated directly into the trails!
Since the linear parks aren’t very large and don’t have much in the way of ammenities to talk about, we’ll veer outside of our Smyrna focus and discuss a few other highlights of the Concord Road Trail and the southern portion of the M2R trail, starting with the Chattahoochee River.
The southern terminus of the M2R trail is at the Chattahoochee River National Recreation Area’s Paces Mill Unit. In addition to the paved walking and biking trails, the park has several miles of unpaved hiking trail, fishing, and of course the river itself. Dogs are allowed and bags are available near the entrance. If you head north on the paved Rottenwood Creek Trail you’ll eventually connect to the Palisades West Trails, the Bob Callan Trail, and the Akers Mill East Trail, giving you access to one of the largest connected mixed-use trail systems in the Atlanta area!
If, instead, you head out of the park to the south on the M2R trail you’ll quickly turn back north into the urban sprawl of the Atlanta suburbs. In approximately 2km you’ll reach the Cumberland Transfer Center where you can catch a bus to most anywhere in Cobb, or transfer to MARTA in Atlanta-proper. At this point the trail also forks for a more direct route to the Silver Comet Trail using the Silver Comet Cumberland Connector trail. We may take that trail another day, but for now we’ll continue north on the M2R trail. Just a bit further north there are also short connector trails to Cobb Galleria Center (an exhibit hall and convention center) and The Battery, a mixed-use development surrounding the Atlanta Braves baseball stadium.
It’s at this point that the trail turns west along Spring Road where it coincides with the Spring Road Trail that connects to the previously-reviewed Jonquil Park (a total ride of ~3.7km). Shortly thereafter we reach our first actual un-reviewed Smyrna park: the Spring Road Linear Park.
The Spring Road Linear Park stretches 1.1km along the M2R Trail and is easily accessed by both bike (of course) and bus via CobbLinc Route 25.
The park does not have a sign or other markers, but does have several nice pull offs with benches that make a good stop over point on your way home to or from the buses at the Cumberland Transfer Center. If you’re out walking the dog public trash cans and dog-poo bags are available on the east end of the park, but do keep in mind that the main trail is mixed-use so dogs should be kept on one side of the trail to avoid incidents with bikes.
After a short climb the trail turns north again and intersects with the Concord Road Trail and the Atlanta Road Trail. We could veer just off the trail near this point to reach Durham Park, the subject of a future review, but instead we’ll continue west, transitioning to the Concord Road Trail to reach our next park: Concord Road Linear Park.
The Concord Road Linear Park sits in the middle of the mid-century Smyrna Heights neighborhood and has something special that’s not often found in poorly designed suburban neighborhoods: (limited) mixed-use zoning! A restaurant and bar (currently seafood) sits at the edge of the park along with a bike repair stand and bike parking.
It’s worth commending Smyrna for creating this park at all, it may be small but in addition to the mixed-use zoning it did something that’s also not often seen in the burbs: it removed part of Evelyn Street, disconnecting it from the nearest arterial road! In the war-on-cars this is a small but important victory that creates a quality-of-life improvement for everyone in the neighborhood, whether they bike, walk the dog, or just take a stroll over to the restaurants in the town square without having to be molested by cars.
In our next review we’ll turn back and continue up the M2R trail to reach a few other parks, but if we were to continue we’d find that the Concord Road Trail continues for another 4km until it terminates at the Silver Comet Trail’s Concord Road Trail Head. This trail head sits at mile marker 2.6 on the Silver Comet Trail, right by the Concord Covered Bridge Historic District.
The Silver Comet will likely be covered in future posts, so for now I’ll leave it there. Thanks for bearing with me while we take a detour away from the City of Smyrna’s parks, next time the majority of the post will be about parks within the city, I promise.
]]>Dear comrades,
I’m writing to you from my favorite window ledge in the Smyrna Public Library. From my seat in the window I can see a public park with an outdoor amphitheater, a small pond, and the sidewalk across the plaza to the shops in the main town square. Parents with strollers are meandering around the pond while a young couple, obviously on a date, are walking towards the park from a coffee shop in the square. A bicyclist breezes by on the multi-use trail. If you were to follow the cyclist just a little past my line of site, you would encounter a bike trail that, were you to follow it from here to its terminus, would take you all the way to Alabama without you ever having to venture out into automobile traffic.
All of the organizations and groups addressed in this letter share something in common with this tableau, and even with the library window ledge where I’m sitting to write this letter. You all provide an important function in my life, one that I have far too little of: a place to go.
Nathan J. Robinson in his book Why You Should be a Socialist says:
People of my generation often feel very alone. We are isolated and depressed, and we want a world where people don’t just sit alone in their apartments watching Netflix, but have places to go. Places that are free and welcoming, where you don’t have to worry about whether you can afford to stay.
He’s not just talking about physical places, of course. What he’s saying is that we want community and that we want it as a matter of right, not only if we’re wealthy enough. The way we accomplish this is through social infrastructure, and more importantly, free social infrastructure. That’s what you all provide to me and others: social infrastructure; a place to go.
I can hear the criticisms already, “but Sam, free isn’t ever really free; someone has to pay for it at some point!” For the sake of brevity I’ll elide over the taxes, the labor, the co-ops, the non-profits, the donors, the governments, and the laborers who make a thing possible. The important part of what I’m calling “free” is that it’s free at the point of use for those who can least afford it, and that the burden for it is not shouldered by them. Free in this sense means an act of solidarity that allows everyone to benefit from the labor (or dollars) of others.
In a few hours I will head out the door to attend a pay-what-you-can dance. This will be my only regularly-scheduled free social interaction for the week, meaning that many weeks this will be my only social interaction for the week. Without places like this, my life would consist almost entirely of work and sleep: a somewhat dismal existence—though the “sleep” part has its merits.
In the world of software development it’s common to talk about software that’s “free as in beer” (gratis) vs. “free as in freedom” (libre). My definition of “free” above appears at first glance to simply be gratis, ie. “does not cost money”. Places to go, however, must also have a different kind of freedom that’s similar to libre, or the anarchist or communist concepts of free association. The first cooperative principal summarizes it well (although we’re not talking exclusively about co-ops here):
Voluntary and Open Membership
Cooperatives are voluntary organisations, open to all persons able to use their services and willing to accept the responsibilities of membership, without gender, social, racial, political or religious discrimination.
That is to say that free social infrastructure must not only not require money at the point of use, but it must also be a welcoming place for all people: open by default.
Finally, Though most of you, my social infrastructure, are public or not-for-profit organizations, free in this context doesn’t necessarily even mean non-commercial. A good example of commercial social infrastructure that I rather detest, but which none-the-less still meets my definition of “free” is the Mall. I think that it was Eric Klinenberg in his book Palaces for the People (“how social infrastructure can help fight inequality, polarization, and the decline of civic life”) who introduced me to the idea that malls, those great monuments to American Consumerism, can also be free social infrastructure.
If you grew up in the United States, you’re likely familiar with the trope of teenagers hanging out at malls, something that ostensibly keeps them off the streets and out of trouble. In the small town I grew up in, this was indeed a popular past time the moment you were old enough to drive after dark. The mall was a place you could go without being expected to spend any money (though they’d happily take some if you had it), a place you could just exist, a place to go.
On the other end of the age spectrum it’s not uncommon to find the elderly walking the mall as a way to get away from home and get some exercise, or the houseless passing through to get out of the heat or cold for a few minutes. It’s a place to get out and be among people where all are welcome (in theory, anyways; there are plenty of individual malls that go overboard on security guards and gate keeping).
This then is what I mean when I say that you are “free”. Both a place of freedom, and a place where I don’t have to count how many hours I was able to work last week to decide if I can attend. You are a place without a constant stream of transactions, judgements, or borders. You help provide what Robinson calls a “decommodified life”.
My current financial situation and definitions of “free” aside, having places to go is important not for the physical place itself, but for the community building that the place encourages.
In his forward to Creating Cohousing by Kathryn McCamant and Charles Durrett, Bill McKibben writes:
For fifty years, our economic mission in America, at its core, has been to build bigger houses farther apart from each other. And boy have we succeeded: a nation of starter castles for entry-level monarchs, built at such remove one from the next that the car is unavoidable.
We live in a nation where “knowing our neighbors” means we say hi on occasion if we happen to walk out the door towards our cars at the same time, and where our living spaces isolate us instead of encouraging communities to form. This poor use of space and of the built environment is one of the reasons for the loneliness that Robinson mentions in the quotation at the beginning of this letter. Unlike our neighborhoods and cities, you are built to encourage community, and this is why you are so important. When you can’t walk out your door and feel part of a community, you need to find that elsewhere.
With that rather lengthy explanation out of the way, let me finally get to the purpose of this letter: to say “thank you” and express my love for what you do. When I’m having a bad day, the thought that I’ll be able to attend a Swing Dance later, or a group bike ride, isn’t actually what perks me back up: It’s the thought of doing that with other people. Others who help each other even when they hardly know each other. Others who begin their relationship sharing no more in common than one day a week on the same activity, but who may grow in time to be close friends.
Whether it’s in a park, a bar, a library, or a dance hall it’s a recognition of our shared humanity and the bringing together of vastly different people through a meal, a cause, or a love of the outdoors that bridges divides and creates community. I thank you for doing that for me, and for being there for me on the bad days. I only hope that I can do the same for you.
In Cooperation and Solidarity,
Sam
Update 2023-04-07: Sadly one of the organizations I mentioned in this post has recently stopped being pay-what-you-can. They do still have a tiered structure, and I can’t blame them since I’m sure it was hard to pay the rent, but unfortunately it can’t be a regular weekly event for me anymore so I have updated this post to not mention them by name so as to not advertise incorrect prices. I still whole heartedly endorse them and wish them success, and hope that one day I’ll be able to afford to attend regularly again.
]]>I live in a suburb of Atlanta called Smyrna1. Though Smyrna has been ranked favorably compared to other towns in the U.S.2, it suffers from many of the same problems as other suburbs: wide, fast, roads that aren’t very pedestrian or bicycle (or even car) friendly and a lack of public transit make it hard to get anywhere3, most of the businesses in town are fast food franchises and large chains that siphon money out of the local economy, bad zoning makes it hard to have amenities near where you live, etc. However, it also has a lot going for it, and this is a review of one of the things that makes Smyrna a good place to live: its public parks.
Bike into any neighborhood, or down any previously unnoticed side-street in Smyrna and chances are that you will stumble upon a park. A quick glance at the map shows 10 city run public parks within a 1 mile walk of my house including two pocket parks, a dog park, tennis courts, a swimming pool, volley ball courts, soccer fields, baseball pitches, a linear park, batting cages, and various other multi-use fields and walking trails. And yet, I’ve only been to about half of them (and a much lower percentage of the cities parks overall)
I’ve decided to change that. I’m going to attempt to visit all of Smyrna’s parks and, if the first few go well, attempt to review them. I’ll look at amenities, activities, maintenance, and how easy they are to access without a car. Without further introduction, let’s jump right in with the first two city parks.
Rose Garden is a small-to-medium sized, sports focused, park connected to an elementary school. It boasts public restrooms, playground equipment, basketball, tennis, and volleyball courts, and a general use field. The restrooms, coupled with the two covered pavilions with picnic tables and a grill, make this an ideal park for a family gathering or cookout. The pavilions form a central focus around which the tennis, volleyball, and playground equipment all reside so that older members of the group can watch the game or the children playing without having to leave their seat.
Up the hill, the two concrete basketball courts are surrounded by a chain-link fence so that you don’t have to worry about constantly running down the hill after your ball when you miss a shot.
Though there is a path in the park, its not very long and not circular, so if you’re just looking for a place to walk the dog I recommend taking the nearby trail through the Spring Rd Linear Park (review coming in a later post!) to the next park on our list.
Roughly a mile away by the Mountain-to-River Trail is Jonquil Park. The park is named after the daffodil that grows throughout Smyrna which also gives the city its nickname, “The Jonquil City”. Like Rose Garden, Jonquil is also a mostly sports focused park though it is more restrictive in its use. Its roughly 14 acres are mostly taken up by 7 soccer fields (goals of various sizes are stored on site), though it also has a playground and grilling area. Unfortunately, this park requires a permit to use any of the fields or pavilions, making it very unfriendly to the citizenry (though most people ignore the signs and use the fields as they see fit). The park has public restrooms that remain open throughout the day and stadium style lighting on all of the fields.
The park also includes a paved walking trail that makes it a good place to do laps or walk the dog. Regular dog waste bag bins, trash cans, and benches are scattered along the trail.
Right next to the park is Fire Station #3 which has a public exercise area (or “fitness court” as they call it) where the fire fighters and the general public can work out on the equipment.
The proximity of both of these parks to the Mountain-to-River Trail makes them easy to access by bicycle from any neighborhood near the Cobb County Trail system. Jonquil is on the trail and will not require any road riding, while Rose Garden is slightly off the trail. However, Jonquil is only accessible from the Mountain-to-River Trail or Spring Road SE, those coming from the North may have to bike around to connect to the trail or find an off-road cut through to get into the neighborhood. Rose Garden on the other hand is accessible by slow streets from any direction, though there are no designated bike paths between it and the multi-use trail or the surrounding arterial roads.
Smyrna (n.): an unknown, normally phlegm colored, substance found on the bottom of a shoe that invokes a disgust reaction, ie. “uggh, gross, I stepped in a smyrna”. Also a Greek city in antiquity that is now İzmir, Turkey, for which the Georgia town is named. ↩︎
Smyrna, Georgia: MONEY’s No. 44 Best Place to Live in 2018 ↩︎
It should be noted that there are great groups like Cobb 4 Transit that are trying to change this! ↩︎
With gas prices as high as they are I recently decided to sell my Honda S2000, Vela. Though I normally say that there is never a reason to buy a new vehicle when a used one can be had that’s just as good, depreciates less, and is cheaper, I’ve decided to break my own rule and ordered a new truck (more on that in a later post). However, even though I placed my order in October of 2021 I have yet to hear anything from the manufacturer. I was getting tired of the almost 2 hour each way commute by bicycle-bus-train-bicycle, so I decided to get a temporary vehicle until my truck comes. I wanted something that was cheap, had good gas mileage, and that was easy to repair so that I wouldn’t have to pay a mechanic to work on it if I ran into trouble, so I decided to go with a motorcycle. I ended up finding a Honda CB1100 with ~76k miles at a reasonable price and went for it!
Since I named each of my cars after constellations, I decided to name my motorcycle after an individual star: Sirius.
The first thing I did is put some new rubber on it, jacking it up was interesting, to say the least.
I went with the Metzeler Roadtec Z8 Interact Tires after several recommendations from local shops. They were the same as the worn ones which were already on the bike and were one of the few tires I could find in the relatively odd combination of 110/80-18 and 140/70-18. I put ~40 miles/day on the bike commuting to work, so some fresh tires were important.
I’m not planning on building the bike out in any particular way (nor do I have the money), so I’m not sure that this will turn into a longer build blog, but I hope to get lots of high-gas-mileage and fun miles out of Sirius. As always, I’ll update this post with any modifications I make, or possibly with short trip reports that aren’t worth a full post on their own. In particular, I’m looking forward to a ride in the North East Georgia mountains sometime very soon!
Until then, ride on!
I haven’t done much “fun” riding since getting Sirius, mostly just my daily highway commute to and from work. Similarly, I haven’t done many fun mods outside of basic maintenance (oil changes, brake pads, etc. that are worth mentioning on this blog. Today though I did something that, while still boring maintenance, at least looks good: updated to an LED headlamp! A few days ago my headlamp went out, so I picked up an h4 style LED lamp with a built-in fan for $30 at a local motorcycle dealer. It cost a lot more than a halogen bulb, but should also last a lot longer and be a lot brighter.
Here’s a quick comparison of the color profile of each bulb:
While I was pulling the headlamp assembly off I also noticed that one of the horn ground wires had broken, making it half as loud as it should be. This was a good opportunity to fix another minor problem that had probably been happening since I’ve had the bike!
One of the downsides to buying a CB1100 is that they weren’t very popular, and no one makes accessories specifically designed for them. I’d been trying to find a way to mount a set of soft panniers for a while, but could never find anything that would work easily without a custom bracket and re-locating the tail lights.
Luckily, I found a set of hard panniers with lights built in at a local motorcycle shop that sells used parts. For $20 plus about half that for a can of automotive grade spray paint and primer I was able to create a set.
The first step was to remove all the hardware and sand them down:
Then they got sprayed down with primer and glossy automotive-grade paint.
After a few coats they were ready to be re-assembled and installed on the bike. However, that will have to wait until later when I can figure out the best way to re-locate or remove the existing turn signals while still making it possible to put them back on later.
Yesterday I took a quick diversion from my normal Saturday schedule to go on my first group ride! We met up with the Iron Horse Motorcycle Club (not the Veteran club, a local group) at their garage in Marietta. After a quick safety and route briefing, a few of us took a short jaunt out to a local lunch spot, then to the old paper mill at Sope Creek.
Sadly, I forgot to record a GPX track, but I did snag a picture of the bikes after it turned out the parking lot was full (we had to leave our one three wheel rider behind at this point, sadly):
It was a short ride, but also a very friendly group and it made for a great first-group-ride experience.
]]>That being said, though it’s consuming me a bit right now, I’m not just looking to make a paycheck out of this. I’d like to create the kind of place where anyone can feel comfortable applying and not feel like they have to be on their toes to pass a culture fit interview conducted exclusively by 20-or-30-something white men with beards. I also like the idea of a business that doesn’t try to operate on the thinnest of margins and focus exclusively on growth at all costs.
It’s important to me that the whole co-op be comfortable with (and know about) any clients we take. I’m not against discussing most things, and am open to being convinced otherwise, but I would likely vote against Defense/Police/ICE work, Right wing political campaigns, adtech, etc.
That being said, I would also not vote against someone joining the co-op who disagrees with me on any of those points.
A quick aside on the word “free speech” that Redoak mentions in his writeup: Organizations that aim to promote “free speech” but really just mean “we don’t moderate and we’re trying to co-opt the term ‘free speech’ and make it meaningless” I would vote against, but organizations that actually understand what free speech means (ACLU, EFF, FFRF, etc.) I would like to support.
One thing I’ve thought about doing for a while is teaching. It’s always been something I enjoy, but never something I’ve had the opportunity to do much of. I’d like to eventually be able to expand the co-op to include training, and I’d particularly like us to teach both introductory courses (probably at a steep discount for individuals wanting to learn about software) and more team-oriented corporate courses (the actual money maker, and hopefully we can improve the state of software for the users).
We will likely make use of a great deal of open source, and if we’re ever successful it would be important to me that we give back to those who made our success possible. The cooperative principals should be applied to software as well, so if we use an open source library and make money from doing so I would argue we should take some portion of that (in capital or labor) and set it aside to donate upstream if we are able to do so.
We’d likely have to take work where we can get it of course, but here are some areas I’d personally love to work in one day:
I am not good at putting myself out there and talking to clients. Selfishly, this is another reason I’d prefer to look for freelance work with other people who may be able to do this better than I can. However, it’s also something I’d like to learn.
In general, I don’t feel like I’ve learned much at all from my last few jobs, so having a team around me that has a lot of different skills (and hopefully is okay sharing them) is something that appeals to me a lot in general, and I hope I’d have something to contribute to the groups knowledge in return.
I am a backend developer who has worked primarily in Go. I have been working in this space since approximately 2013. I have also worked extensively in Rust, Python, and (to a lesser extent) Clojure among other languages.
I also have experience in realtime communications and have served on the XMPP Standards Foundation (XSF) council (the technical governing body) as well as an editor for the XEP series of documents.
To a lesser extent I have been minimally involved at the IETF with the PRECIS (internationalization), KITTEN (authorization), and TLS (TLS) working groups.
On a scale of 1–5 where 5 is “expert” and 1 is “don’t make me do this, the client will regret it” and an asterisk is “needs refreshing to get back to this number”:
More recently, I became aware of a post about the “philosophy and understanding of the role of computing and software in our society”:
it’s 2021 and we are still talking about “free software” instead of:
- community software
- liberated software
- anti-capitalist software
- transformative justice software it’s been 35 years, and our philosophy and understanding of the role of computing and software in our society has changed drastically. The FSF and the culture of turning RMS into a ‘great man’ haven’t changed in decades.
This got me thinking about the values I apply to software development, governance, and distribution. I realized that they’re the same values I apply when searching for services to use, or starting a business: cooperative values. Naturally this would be called “cooperative software” or “cooperative technology”: software (or technology more generally) that roughly follows the principals of the 1995 “Statement on the Cooperative Identity”.
Finally, today, I became aware of an early draft of an essay titled “Towards A Communal Software Movement” and the authors decision to rename it “cooperative software”. Since others are thinking about this too, I wondered if we might all come together and try to better define what we mean when we use the term. To start, here is my small contribution.
Cooperative technology is technology that is jointly-owned and democratically-controlled in accordance with cooperative principals.
If we are talking specifically about software I believe that this means that all Cooperative Software is FOSS, but not all FOSS is Cooperative.
For example, the Go programming language is Open Source Software (OSS) because it is released under a BSD style license and accepts contributions from the community. However, it is not Cooperative Software because it is not democratically controlled by members, instead decisions are made by Google employees. Similarly the Benevolent dictator for life (BDFL) model of governance used by Clojure and Linux means that these projects are not Cooperative Software.
Some projects use a meritocracy model that appears to be cooperative at first blush because it involves individuals working together to establish consensus. However, if only developers who showed a certain level of merit are allowed to govern the project, the users of the software, the designers, the technical writers, etc. become ineligible to participate. This violates the cooperative principle of voluntary and open membership.
Just like there are Worker Cooperatives and Consumer Cooperatives in the business world, Cooperative Technology provides a great deal of flexibility in governance while still ensuring equity. A cooperative business can survive in a capitalist market economy, but doesn’t re-enforce the inequities inherent in such a system. It also doesn’t require any major changes to adapt if it becomes a part of a more equitable system in the future.
Co-ops also help prevent the cult of personality that sometimes forms around individual founders because they strive to be inclusive. Even if a member does become a polarizing figure, their impact is limited due to the 1-person-1-vote principal.
If the software communicates over the network but uses a network protocol that is not documented (so other software cannot communicate with it without reverse engineering the protocol from the code and hoping it does not change and break them), can it still be Cooperative Technology? Likewise, are programming languages that have a reference implementation instead of a spec Cooperative if they meet the rest of the definition, or does this make it too difficult to create alternative implementations? Is this a violation of the “Concern for Community” principle, or the general value of solidarity?
What licenses are acceptable for Cooperative Software to use? These licenses presumably must guarantee user freedoms, but also not hurt or exclude users. Maybe any license is fine and Cooperative Software is more about governance, but I’d also argue that licenses with a viral component are incompatible because it reduces the ability to cooperate with other Cooperative Software using an incompatible license. Cooperatives realize that not everyone will agree on the exact way that the cooperative should be run and they seek to reach consensus, not legally force other groups to agree with them or self-segregate.
What types of cooperative governance map well to developing technology, and which ones make decision making too difficult? In non-cooperative software if every user feature request is honored we just get Jira: over complicated and nobody really knows what it’s for or how to use it. But in cooperative software if users must all come together to reach consensus, does this help them all distill their features and use cases down to the most fundamental form?
Once we begin to answer these questions, should there be a steward for the definition of Cooperative Technology in the vein of the Open Source Initiative (OSI) or the Free Software Foundation (FSF)?
If you’re interested in helping answer these questions, or in improving the definition of Co-op Technology, consider joining the co-op group chat: co-op@mellium.chat.
]]>mellium.im/xmpp
project, an XMPP library for Go.
The most up-to-date version of this document can always be found at
mellium.im/docs/ARCHITECTURE
.
This post provides a high-level architectural overview of the
mellium.im/xmpp
module.
Its target audience is contributors looking to familiarize themselves with the
codebase.
The Mellium project is designed to be very modular.
It is itself a complete XMPP implementation with many features useful in instant
messaging, but it is also designed for users who have special requirements to be
able to reuse only small pieces in their own libraries and services.
Broadly speaking, the module can be divided into three ’tiers’ of packages.
The highest level packages implement XEPs or individual features.
These packages import the mid-level xmpp
package to perform actions on an
XMPP session such as handling a request for history, or sending a ping.
High level packages and the mid-level xmpp
package import the lowest level of
packages.
These are the basic building blocks of XMPP such as the primitive stanza types
defined in stanza
, dialing a socket with dial
, or the jid
package
for handling XMPP addresses.
Packages may only import packages at the same or a lower level to avoid circular
imports.
These boundaries aren’t strictly defined, but it can be helpful to think about them when creating new packages or deciding where to implement some functionality.
Dialing TCP connections is implemented in its own package, dial
.
Discovering locations to dial is handled by internal/discover
so that it can
be used by dial
for finding TCP endpoints but also by the websocket
package for finding WebSocket endpoints.
The jid
package contains functionality for handling XMPP addresses,
historically known as “Jabber Identifiers” (JIDs).
It is used by almost every package in the module and will likely be imported by
almost every user of the module.
Multiple APIs for creating stanzas and stanza-level errors are present in the
stanza
package.
APIs for creating and parsing stream headers and stream-level errors are present
in the stream
package.
If you are handling anything related to payloads sent over the wire (but not
related to a specific XEP), chances are it lives in one of these two packages or
in the related internal/stream
package which contains other stream related
helper functions that aren’t generally useful or flexible enough to be part of
the public API.
The main xmpp
package contains just enough functionality to get a connection
up and running.
Within this package the main files are:
session.go
which includes the session creation and negotiation logic,negotiator.go
which implements the default handshake used when creating
sessions and the modified WebSocket handshake, andfeatures.go
which contains types for creating stream features and the logic
used by the default negotiator pick and negotiate individual features.Individual stream features are in separate files that match their name such as
bind.go
or starttls.go
.
The mux
package is also arguably a mid-level package.
It is imported by most high-level feature packages and provides an
xmpp.Handler
that can be used to route events to other handlers based on the
type of the element and/or its payload.
If you are asked to make modifications to routing logic, this is the place to
look.
XEPs are normally implemented in packages named after their functionality.
This may be the XEPs name or short name if it is large enough to warrant its own
package.
For example, XEP-0313: Message Archive Management might be
implemented in the mam
package.
Smaller XEPs or large features that are spread across many XEPs may be
implemented together, for example, XEP-0082: XMPP Date and Time
Profiles and XEP-0202: Entity Time are both implemented
in the xtime
package.
For more information on naming and writing feature packages see “Implementing XMPP Extensions”. If you are looking for a specific XEP and the package that implements it see: mellium.im/docs/xeps.
]]>XMPP addresses, more commonly known as JIDs, are defined in RFC 7622 and identify clients, servers,
and other entities on the XMPP network.
They look like an email address, but sometimes have an optional “resourcepart”
at the end that identifies a particular client logged in as the account
represented by the rest of the address (since XMPP may have multiple clients
connected per account).
For example, my JID with a resourcepart blog
would be:
sam@samwhited.com/blog
Unlike email where the account is the lowest level of entity that can be addressed, individual clients on the XMPP network are globally addressable thanks to the addition of the resourcepart.
JIDs are sometimes broken down into two types: the “full JID” and the “bare JID”. Full JIDs always have a resource part and uniquely identify an individual client or session on the network:
romeo@example.org/orchard
example.org/da863ab
Bare JIDs never have a resourcepart and identify an account or host on the network:
romeo@example.org
example.org
From a protocol perspective nothing can be gleaned from the lack or presence of a resourcepart or localpart other than the fact that this is an account which may have multiple network entities with current sessions or that it addresses a specific entity.
To split a JID into its component parts (the localpart, domainpart, and
resourcepart), the following algorithm should be used (where the localpart is
represented by lp
, the resourcepart by rp
, and the domainpart by dp
and ∈
is used to check if the given character is included in the string):
In textual form:
Note that the localpart and resourcepart are optional and may result in empty strings (you may have a jid that is just a domainpart).
And finally, let’s see an example in code form using Go:
In Go, the mellium.im/xmpp/jid
package implements operations on JIDs.
To Split a JID string into its component parts the SplitString
function may
be used:
lp, dp, rp, err := SplitString("romeo@example.net")
No validation is performed by SplitString
and the parts are not guaranteed to
be valid.
To manually split a string without depending on the jid
package, the
underlying code (with annotations from RFC 7622) looks like this:
func SplitString(s string) (localpart, domainpart, resourcepart string, err error) {
// RFC 7622 §3.1. Fundamentals:
//
// Implementation Note: When dividing a JID into its component parts,
// an implementation needs to match the separator characters '@' and
// '/' before applying any transformation algorithms, which might
// decompose certain Unicode code points to the separator characters.
//
// so let's do that now. First we'll parse the domainpart using the rules
// defined in §3.2:
//
// The domainpart of a JID is the portion that remains once the
// following parsing steps are taken:
//
// 1. Remove any portion from the first '/' character to the end of the
// string (if there is a '/' character present).
sep := strings.Index(s, "/")
if sep == -1 {
resourcepart = ""
} else {
// If the resource part exists, make sure it isn't empty.
if sep == len(s)-1 {
err = errNoResourcepart
return
}
resourcepart = s[sep+1:]
s = s[:sep]
}
// 2. Remove any portion from the beginning of the string to the first
// '@' character (if there is an '@' character present).
sep = strings.Index(s, "@")
switch {
case sep == -1:
// There is no @ sign, and therefore no localpart.
localpart = ""
domainpart = s
case sep == 0:
// The JID starts with an @ sign (invalid empty localpart)
err = errNoLocalpart
return
default:
domainpart = s[sep+1:]
localpart = s[:sep]
}
// We'll throw out any trailing dots on domainparts, since they're ignored:
//
// If the domainpart includes a final character considered to be a label
// separator (dot) by [RFC1034], this character MUST be stripped from
// the domainpart before the JID of which it is a part is used for the
// purpose of routing an XML stanza, comparing against another JID, or
// constructing an XMPP URI or IRI [RFC5122]. In particular, such a
// character MUST be stripped before any other canonicalization steps
// are taken.
domainpart = strings.TrimSuffix(domainpart, ".")
return
}
Unlike emails, JIDs were defined with Internationalization (i18n) in mind using the Preparation, Enforcement, and Comparison of Internationalized Strings (PRECIS) framework. PRECIS is defined in RFC 8264 and is a framework for comparing strings safely in a variety of contexts. For instance, imagine you have registered the nickname “Richard IV” (Latin capital letters I, Vee) in a group chat: Using PRECIS the chat application could ensure that no one else comes along and registers the nickname “Richard Ⅳ” (Unicode Roman Numeral 4) and uses it to impersonate you.
The algorithm for validating a JID that has already been split into its localpart, domainpart, and resourcepart is as follows:
In textual form:
The final Validations
step should perform the following actions (in no
particular order):
"&'/:<>@
.[::1]
instead of ::1
).As a user of XMPP most of the time addresses will just look like emails and you won’t need to ever see them except occasionally adding one to your address book. Even as a developer of XMPP you mostly won’t need to worry about the details of splitting or validating JIDs as robust libraries for handling XMPP addresses exist in most or all popular programming languages. However, sometimes it’s fun to take a bit of a dive into seemingly simple technologies and know how they work under the hood. Hopefully this article gave you a deeper appreciation of how to handle addresses in XMPP.
Need an experienced freelancer who knows XMPP, or a whole team to manage a project? I’d love to chat! Follow up with Willow Bark Co-op!
]]>But first: Why XMPP?
I recently got asked (and I’m paraphrasing here): “why would you write an XMPP library when trendier technologies are available?” The quick answer is that XMPP is still the most widely used federated instant messaging protocol available on the internet. Despite the demise of XMPP in a few big chat services that users were familiar with (HipChat, Google Talk, Facebook Messenger), it is still widely used in video games (WarFace, Nintendo Switch presence notifications, etc.), in current chat products (Zoom, WhatsApp, Jitsi Meet, etc.), and in industry. XMPP is a core part of internet infrastructure and has a robust ecosystem of both free and commercial products, servers, and clients built around it. We need this infrastructure in Go.
Now, on to the status update.
Last year the main Go module of the Mellium project, mellium.im/xmpp
received a huge number of updates, bug fixes, and API overhauls, many of which
will be released in the upcoming v0.18.0
release.
Some of the highlights include:
The full (much longer) list can be found in the changelog.
The change to mellium.im/xmpp
that I’m most proud of in the past year might be
the Message Styling implementation which can be found at
mellium.im/xmpp/styling
.
The actual styling implementation comprises three parts: the Style
type, the
Scan
function, and the Decoder
type.
The Scan
function is a bufio.SplitFunc
that is used to tokenize the byte
stream internally.
It is exported as a convenience to allow new message styling implementations
that behave differently or have other extensions built on top of it.
The Style
type is a uint32
that represents a bitmask of styles such as
“strong and emph” or “pre-formatted text and block quoted”.
It only contains information about the styles, not about metadata related to
the styles or the token stream.
For example, given a style you can know that the text is in a blockquote, but
not the level of nesting (if any) of the blockquote, or you might know that it
is in a strong span, but not where the start and endpoints of that span are.
The package provides the various styles and a handful of masks for various
common combinations as constants for the users convenience.
Finally, the Decoder
type pairs the Scan
function with the Style
type by
scanning for tokens and keeping track of the stream state.
It allows you to get the style of each token popped, and keeps track of metadata
about the styles such as the level of nesting in block quotes or the tag on a
fenced code block.
Here is an example of quickly tokenizing an input stream:
r := strings.NewReader(`The full title is
_Twelfth Night, or What You Will_
but *most* people shorten it.`)
d := styling.NewDecoder(r)
var err error
var tok styling.Token
for {
tok, err = d.Token()
if err != nil {
break
}
fmt.Printf("Token: %q\n", tok.Data)
}
if err != nil && err != io.EOF {
panic(err)
}
// Token: "The full title is\n"
// Token: "_"
// Token: "Twelfth Night, or What You Will"
// Token: "_"
// Token: "\n"
// Token: "but "
// Token: "*"
// Token: "most"
// Token: "*"
// Token: " people shorten it."
A more extensive example for converting styling tokens to HTML can be found in the package examples. For more, see my retrospective post, Message Styling.
XMPP servers likely want to serve more than one domain, but prior to the
upcoming v0.18.0 release this was difficult or impossible.
This required changes to the Negotiator
type to allow input and output
streams to be stored on the session, and to the built-in Negotiator
’s
StreamConfig
type which is used for configuring options on the built in
default negotiator to allow the user to select the features in a callback.
Unfortunately, these are breaking changes, but it should be one of the last
major API changes before we can begin thinking about stabilizing the API in
v1.0.0.
The S2S
option was also removed from the StreamConfig
in favor of new
functions for receiving and negotiating a stream which mark the s2s state bit on
the session before beginning negotiation.
This removes a long standing redundancy, and also a bug where the first stream
feature negotiated wouldn’t know that the session was a server-to-server session
because it couldn’t be set until the negotiator function returned for the first
time.
The current list of stream negotiation functions is:
DialClientSession
DialServerSession
DialSession
NewClientSession
NewServerSession
NewSession
ReceiveClientSession
ReceiveServerSession
ReceiveSession
While it’s not an exciting user-facing feature, one of the most valuable changes
this year has been the addition of integration testing against Prosody,
Ejabberd, McAbber (Loudmouth), and sendxmpp(1)
.
We’ve already caught several issues both in Mellium and in some of the services
we’re testing against thanks to the new tests!
This is all facilitated by the internal/integration
package which provides a
framework for starting an external application, generating certificates, and
passing around file handles.
It works by creating a temporary directory where any config files can be stored,
then launching the process using that temp directory as a working directory.
It also provides generic options for writing files to that directory that can be
used by the child packages, such as prosody
, to write more specific options
that write the prosody config file.
The integration testing packages also provide functionality for running
sub-tests, each one of which will get its own child process with all the
configuration automatically.
For example, to test sending a ping to Prosody and Ejabberd and receiving a ping
on Prosody we might write something like the following:
func TestIntegrationSendPing(t *testing.T) {
prosodyRun := prosody.Test(context.TODO(), t,
integration.Log(),
prosody.ListenC2S(),
)
prosodyRun(integrationSendPing)
prosodyRun(integrationRecvPing)
ejabberdRun := ejabberd.Test(context.TODO(), t,
integration.Log(),
ejabberd.ListenC2S(),
)
ejabberdRun(integrationSendPing)
}
func integrationSendPing(ctx context.Context, t *testing.T, cmd *integration.Cmd) {
…
}
func integrationRecvPing(ctx context.Context, t *testing.T, cmd *integration.Cmd) {
…
}
More complex options for loading Prosody extensions, generating and using certificates and enabling TLS, changing the default username and server host, etc. are also available.
mellium.im/xmpp
docs and websiteThis post is a mix of an FAQ, a retrospective, and a look forward to what I would change if I were starting a new styling spec without prior art.
But first:
Message styling is very small, comprising two block level styles and four inline styles that operate on spans of text. The block styles are:
> Block quote
and
```optional tag Pre-formatted text block ```
And the inline styles are:
*strong emphasis*, _emphasis_,
`pre-formatted span`
, and
~strike through~.
Exactly how each of these are displayed depends on the rendering client. Strong emphasis may mean different things to different people, but it’s the intent that matters, not the exact style. In general strong emph is bold, emph is italicized, pre-formatted spans are monospace, and strike through is exactly what it says on the tin.
One of the things I like the least about message styling is that it can’t be easily tokenized. It is not possible to know if any given asterisk is a strong emph styling directive without scanning the rest of the line on which it resides. This was a deliberate choice made on the assumption that it’s more important not to accidentally style the rest of a line because of a false positive “*” existing in the text, and that message styling documents would likely be relatively short. Even in the worst case (one very long line with a styling directive at the beginning) you will run up against the max message length before it becomes a problem.
However, insofar as writing a lexer or defining a formal grammar goes it would be much easier if styling were context free, allowing it to be parsed with pushdown automata, or even regular so you could use a simple regular expression. Doing so would likely increase false positives though, and means we would need a way to disable styling for individual spans, which brings me to my next woe.
The current approach to disabling styles takes a “default on” approach and an XML payload that allows you to disable message styling for entire messages. Personally, I don’t think having a way to disable styling for an entire message on the sending side was necessary, but a lot of people in the XSF thought the sending side should have complete control and have a way to turn styling on or off or leave it unspecified. The current “advertise support then be on by default but allow turning it off per message” approach was a compromise that I was willing to live with, and I’m reasonably happy with it.
However, having a way to disable styling on individual spans within a message would be much nicer from a UI perspective. This would allow you to implement an editing experience where you type in some text that should be styled such as “*strong*”, it is automatically styled, and then if you type a backspace instead of removing the final asterisk the text goes back to being unstyled without affecting every other span in the message.
A number of workarounds were tried where zero-width spaces were inserted between the styling directives and the text so that they no longer followed the rules and became normal characters, but none was found that didn’t feel like a hack and the idea was eventually abandoned. I also considered the idea of allowing you to disable styling using a payload outside of the message body, but there is no well-defined way in XMPP (yet) to reference text inside the body, and I decided this could always be added later as a separate spec if necessary.
If I were doing it again, I would design from the ground up with the ability to disable individual spans’ styling while still not requiring any extra XML payloads.
The blockquote syntax in message styling (lines starting with one or more “>”) is taken more or less directly from markdown, which takes it from the defacto standard used by many email clients. Unfortunately, block quotes are another reason that parsing message styling is somewhat tricky as they are inconsistent with the other styles in that they do not have any sort of “end” marker.
This is outside the scope of Message Styling which was trying to formalize an existing styling language, but if I were implementing a new styling language from scratch I would consider inventing something new at the cost of breaking the users excpectations. Maybe something like this:
>>info
Quote
<<
This has a token that marks the end of the quote and contains an info string
like pre-formatted text blocks.
This also leads to a natural inline quote version which would be a nice addition
that doesn’t exist today.
The inline quote would look something like >this<
.
A question I get asked relatively often is “why doesn’t message styling include lists?” While there is no specific styling directive for lists, you can still support them. From a UI perspective if you click a bold button your client might wrap the selected text in asterisks. Message styling is simple, you don’t need to do anything except add a bit of text in the input to add styles. The same can be done for lists without defining any additional styling directives. For example, when you click the lists button your client could insert a “•” (U+2022 BULLET) and a tab at the beginning of the line, then add another the next time you return. For ordered lists you could use simple numbers with periods (ie. “1.”), or the various characters from the Unicode enclosed alphanumerics block such as “⒈” (U+2488 DIGIT ONE FULL STOP) or “⓵” (U+24F5 DOUBLE CIRCLED DIGIT ONE).
• This works pretty well
• I think
⑴ One
⑵ Two
⑶ Three
However, if I were doing it again I probably would make it easy for users to enter a character that exists on their keyboard and have it converted to nicer looking bullets in a way that they can expect to work across clients (clients are still free to do this of course by converting “-” or “*” to “•”, and I encourage it, it’s just not part of the spec).
I’ve been asked a few times now why we chose strike through as one of the styles instead of, say, underline. This was added for compatibility with existing commercial services that support it in their clients. It’s not hard to add additional span directives once you’ve implemented one already, so we went ahead and added it in case anyone was using it. We did not add underline because I couldn’t think of any particularly interesting use cases for it and it wasn’t widely supported by other messengers (Gajim was a notable exception, and I’m afraid we created some conflicts with their existing styles, for which I’m sorry). I’d probably keep this the same if I were starting over unless someone presented a compelling use case.
The span directives that I picked are also something I probably wouldn’t change because the goal was to maintain compatibility with existing systems as much as possible, but which if given a clean break I might reconsider. If it were entirely up to me I think I would at least change italics to something like /emph/ because the slashes remind me of italicized text and maybe reconsider adding underline just because _underline _ looks so much more like underline than emphasis (this also would have been compatible with Gajim at the time).
One complaint I had from a small number of people while developing the spec is that it didn’t come with a formal grammar. While I would personally love to have a formal grammar for Message Styling, I probably wouldn’t want to put one in the spec even if we had one. A lot of IETF specs are full of augmented Backus–Naur form (ABNF) and for the most part I think all it does is intimidate users and make the spec harder to read. Maybe including it as an appendix would be okay.
Never the less, having a formal grammar would be nice, so why didn’t I write one even if it wasn’t going in the XEP? Ignoring for a moment that I just have no desire to learn any of the mess of metalanguages used to specify formal grammars or the tools required to actually test and validate it just for a spec that won’t include it anyways, it may also be impractical due to ambiguities in Message Styling that don’t make it a very good fit for most formal grammar meta languages.
For example: there are no errors or invalid tokens in message styling, so if you specify a formal grammar that includes “*” as a token that matches to the next “*”, you also have to add a rule for normal text that can match “*foo”, “foo*”, and any number of other combinations. Even for a small styling language this makes the grammar bloat significantly. Most meta languages meant for specifying formal grammars are designed for context free grammars, which message styling is not.
The TL;DR is that a formal grammar would be a lot of work that will only ever be looked at by one or two people. Most developers doing implementations will never touch it. The cost-benefit analysis just doesn’t add up.
Though this post is a retrospective of my own work, and therefore somewhat critical, overall I’m very happy with how Message Styling turned out. It accomplishes its original goal: standardize existing formatting used by many messengers both inside and outside the XMPP ecosystem, and has already been adopted by a number of clients.
I’m aware of implementations (some may not be released at the time of this writing or may not support all styles) in:
Styling is also recommended by Modern XMPP for formatting messages and is mentioned in the 2021 XMPP Compliance Suites, so I’m very happy with the rate of adoption.
Finally, if you’d like to play with message styling yourself, you can try my Go
library mellium.im/xmpp/styling
.
Update 2021-02-08: added a section on having a formal grammar.
]]>