Document last revised: Wed Jul 29 08:02:28 2020 +0000
Currently, we’re looking into the following timeline:
As of this writing, the Type.World Project is in Beta phase.
The Beta status applies mostly to the Type.World JSON Protocol and the headless client module which is used by the GUI App, as well as central server, including documentation. This is what developers of API endpoints need in order to implement the system on their servers.
The GUI App is missing a small handful of features, but they are all about the user experience, and not related to the publisher’s side of work. The app is expected to develop significantly between October 2020 and March 2021.
If you have any problems in understanding the system that probably means that it hasn’t been explained well enough yet. In this case, please drop me a line at hello@type.world and explain your limitations. I will try to improve the documentation.
To start developing your own API Endpoint for the Type.World App, you need to:
typeworld://json+https//typeworldserver.com/flatapi/bZA2JbWHEAkFjako0Mtz/
In order to go online with your API Endpoint, consider the following check list:
APIKey
for my API Endpoint through the user account section at type.world/accountuniqueID
attributesThe Type.World technology aims to improve the user experience around font installations for end users, in turn improving the participating publishers’ market position. It defines a protocol that font publishers may implement on their web servers, serving data about which of their users are entitled to download and install certain fonts.
The Type.World App needs to be installed by a user once, and is then pointed at these publisher API endpoints (Application Programming Interface) through a link on the publisher’s website as an alternative download means next to the established way of providing the available fonts to download in zip archives. Over time, end users will accumulate font subscriptions from different publishers all in one app. The visual appearance of each publisher and foundry is customizable (color scheme and logos), so that a certain level of foundry corporate design adherence is ensured.
Initially, only metadata about which fonts are available to a user is received by the app, and not the fonts themselves, which means that they cannot be readily displayed in the app, only the meta data about their existence. Instead, publishers can define preview poster images that the users can check out next to PDF links describing and previewing the fonts in detail.
Upon the user’s request, the app will download and install the fonts which will be immediately accessible in all apps in the operating system. The installed fonts will remain installed when the app isn’t open or the computer is offline, although a select few features will require the app to keep running. Only new font installations or deletions of so called protected fonts require an internet connection.
Users will be notified of available font updates. And they can synchronize their subscriptions to other computers or invite other users to share a subscription.
Because the Type.World technology is also meant as an excercize in self empowerment for type publishers, the creation of all data that a subscription holds, both metadata about the fonts as well as font data itself, lies in the responsibility and hosting of the publisher. For example, it lies in the publisher’s responsibility to record and properly display how many seats a user has installed for a certain font license, and update the number after a font has been installed or deleted. The app will in this case only display that number to the user and not make any own calculations. Similarly, it is in the server’s responsibility to reject font installation requests once the license’s seats have been used up. Which also means that publishers may choose to allow a certain number of excess seats over the license’s limit without explicitly informing their users about it. The app will not observe any of that, only display the publisher server’s response to the user.
It needs to be understood that Type.World doesn’t offer a 100% ready turn-key solution. It defines the protocol and provides the end user app.
The fonts and subscriptions are decentrally hosted by each publisher for their users. The central Type.World server will handle the knowledge of subscriptions per Type.World user account for the purpose of backup, synchronization, and invitations.
The technology is aimed at the following three font publishing schemes:
Free Font providers can offer all of their fonts to be installable in the Type.World App.
Commercial font foundries, probably the most important scenario for this technology, will find three ways to serve fonts:
Custom font projects will be able to serve fonts to small workgroups, normally graphic designers within a branding firm. Font updates can be made available to the graphic designers fast and reliably, and the adoption level of font updates can be monitored on the server side. These fonts could be hosted either on API endpoints implemented by the type foundry themselves, or on third party services that support the protocol. The custom font scenario is the only one where it makes sense to hire an external service as a turn-key solution for hosting and serving the fonts because establishing which users get to install which fonts is a fast and straightforward process to set up manually on a web interface for custom jobs, whereas serving commercial fonts to anonymous customers requires the data knowledge of an online shop.
In summary, the technology provides the following benefits over established font download conduct:
https://awesomefonts.com
for the public web site and https://awesomefonts.com/typeworldapi/
for the API Endpoint.In order to serve fonts to a user of the Type.World App, the publisher needs to serve these fonts under a certain machine readable data protocol. While the app can theoretically read several different such protocols, currently only the Type.World JSON Protocol is implemented.
A subscription URL is served to a user, normally by clicking on a button on the publisher’s web site, and the Type.World App reads the necessary data from the API Endpoint.
Alternatively, a user can be invited to a subscription by another user via the app.
The Type.World JSON Protocol is hosted and documented on its Github page.
Like normal web sites, fonts can be served to a user either dynamically or statically.
Dynamic serving usually means to custom-tailor the generated data for a certain user. In case of commercial type foundries, the API Endpoint would only serve the fonts that a customer has actually purchased from the foundry, and not the entire rest of their catalogue. In many ways, the dynamically served data would mimic the user account or download section of that foundry’s web site where a customer has access to the fonts they have purchased.
In a first step, only the metadata about a user’s fonts would be requested by the app and served by the API endpoint, and later, upon the user’s request, the API Endpoint would serve the actual binary font files. Because commercial fonts need to be protected, they are not openly accessible on the publisher’s web server and instead only served upon request and after authentication.
Serving dynamic content requires a software to run on the publisher’s server and create the dynamic data on request. The programming language of that software is up to the publisher to decide. Any language can be used. Relevant to the exchange of data is the data protocol described here, the Type.World JSON Protocol.
Static serving could be an option for hosting free fonts without custom-tailoring the data to users. In this case, a dynamic server-side API Endpoint is not necessary to exist. A subscription can be served all in a single JSON file, with links to openly acessible binary font files present in the JSON data.
Here’s an example for a flat or static subscription:
This is the link that would be served to the app: typeworld://json+https//typeworldserver.com/flatapi/bZA2JbWHEAkFjako0Mtz/
(You can click on this link if you have the app installed)
To directly view the same subscription as raw JSON code in the browser, click here: https://typeworldserver.com/flatapi/bZA2JbWHEAkFjako0Mtz/
By clicking the Install in Type.World App button on your SSL-encrypted website, a URL of the following scheme gets handed off to the locally installed app through the custom protocol handler typeworld://
that the app has registered with the operating system.
typeworld://json+http[s]//[subscriptionID[:secretKey[:accessToken]]@]awesomefonts.com/api/
The URL parts in detail:
typeworld://
This is the protocol handler used by the Type.World app. The app advertises the handler to the operating system, and upon clicking such a link, the operating system calls the app and hands over the link.json
The protocol to be used within the Type.World app. Currently, only the Type.World JSON Protocol is available to use.https//
The transport protocol to be used, in this case SSL-encrypted HTTPS. Note: because valid URLs are only allowed to contain one ://
sequence which is already in use to denote the custom protocol handler typeworld://
, the colon :
will be stripped off of the URL in a browser, even if you define it. The Type.World app will internally convert https//
back to https://
.subscriptionID
uniquely identifies a subscription. In case of per-user subscriptions, you would probably use it to identify a user and then decide which fonts to serve them. The subscriptionID
should be an anonymous string and is optional for publicly accessible subscriptions (such as free fonts). Allowed characters: [a-z][A-Z][0-9]_-secretKey
matches with the subscriptionID
and is used to authenticate the request. This secret key is saved in the OS’s keychain. The secretKey
is optional for publicly accessible subscriptions (such as free fonts). The secret key is actually not necessary to authenticate the request against the server (because the subscriptionID
is supposed to be anonymous). Instead it’s necessary to store a secret key in the user’s OS keychain so that complete URLs are not openly visible. Allowed characters: [a-z][A-Z][0-9]_-accessToken
Single use access token. Allowed characters: [a-z][A-Z][0-9]_-awesomefonts.com/api/
is where your API endpoint sits and waits to serve fonts to your customers.There are several possible ways to combine access credentials, as follows:
A publicly accessible subscription without any access restrictions. This API endpoint has exactly one subscription to serve: typeworld://json+https//awesomefonts.com/api/
A publicly accessible subscription without secretKey
, but subscriptionID
still used to identify a particular subscription in this API endpoint: typeworld://json+https//subscriptionID@awesomefonts.com/api/
Example for a protected subscription:
typeworld://json+https//subscriptionID:secretKey@awesomefonts.com/api/
Example for a protected subscription with access token:
typeworld://json+https//subscriptionID:secretKey:accessToken@awesomefonts.com/api/
POST
RequestsTo avoid the subscription URL complete with the subscriptionID
and secretKey
showing up in server logs, your server should serve your protected JSON data only when replying to POST
requests, as request parameters will be transmitted in the HTTP headers and will be invisible to server logs.
The app will ask for the JSON responses at your API endpoint (https://awesomefonts.com/api/
in the above URL examples) and will hand over some or all of the following parameters through the HTTP headers’ POST fields:
commands
The commands to reply to, such as installableFonts
. It could also be a comma-separated list of commands requested to be served in one go to speed up HTTP traffic. The combination endpoint,installableFonts
will be requested by the app on initial access of a subscription, asking for metadata about the API Endpoint as well as the fonts offered by this subscription. This combination installFonts,installableFonts
and uninstallFonts,installableFonts
will be requested by the app when installing or uninstalling protected fonts. As the seat allowance is expected to change for installing/uninstalling protected fonts, the subscription needs to be updated directly after installing/uninstalling a font for updated display to the user. All requested commands will be nested inside a root command. It is mandatory to execute the commands on the server side in the order they were requested in: First the fonts are served or marked as deleted (installFonts
or uninstallFonts
), then the updated subscription is generated (installableFonts
).subscriptionID
The aforementioned ID to uniquely identify the fonts you serve.secretKey
The secret key to authenticate the requester.accessToken
The single use access token that you may use to identify whether a user was logged in to your website when first accessing a subscription. The access token is only ever served there in your website’s user account, and thrown away and replaced upon first access using this token. Afterwards users need to be verified with the central Type.World server. See "Security Design, Level 2" for details.anonymousAppID
is a key that uniquely and anonymously identifies a Type.World app installation. You should use this to track how often a font has been installed by a user through different app instances and reject requests once the limit has been reached.fonts
identifying the unique font ID and version number to install or uninstall as a comma separated list of slash-separated fontID/version
tuples, such as font1ID/font1version,font2ID/font2version,font3ID/font3version
.userEmail
and userName
in case the user has a Type.World user account and has explicitly agreed to reveal his/her identity on a per-subscription basis. This only makes sense in a trusted custom type development environment where the type designers may want to get in touch personally with the font’s users in a small work group, for instance in a branding agency. This tremendously streamlines everyone’s workflow. If necessary, a publisher in a trusted custom type development environment could reject the serving of subscriptions to requesters who are unidentified, e.g. requests with empty userEmail
and userName
parameters. There is currently no way to verify the correctness of the incoming two values for data privacy reasons. They need to be taken as is.GET
RequestsFor simplicity’s sake, you should reject incoming GET
requests altogether to force the requester into using POST
requests. This is for your own protection, as GET
requests complete with the subscriptionID
and secretKey
might show up in server logs and therefore pose an attack vector to your protected fonts and meta data.
I suggest to return a 405 Method Not Allowed
HTTP response for all GET
requests for serving subscription dynamically. It’s okay to serve subscriptions for GET
requests for static subscriptions of free fonts.
Whatever you do with your server, bear in mind that the parameters attached to the requests could be malformed to contain SQL injection attacks and the likes and need to be quarantined.
For protected subscriptions, the publisher provides a subscription link that contains a secret key to authenticate a subscription (Format C). An additional single-use access token may be added to the URL (Format CE) for initial access.
typeworld://json+https//subscriptionID:secretKey:accessToken@awesomefonts.com/api/
The security design outlined below consists of an unprotected base level and two optional security levels. The optional security level’s intention isn’t securing the leaking of fonts alone, but also very dominantly a user experience issue in the long run (See "Remote De-Authorization of App Instances by the User").
Subscription URLs for protected fonts must not be passed to users by email or other unencrypted means. Because they may contain the secret key for protected subscriptions, the links could be intercepted and the fonts leaked.
Instead, two ways are designated to grant a user access to a subscription:
inviteUserToSubscription
command) to invite users to a subscription, identified by their Type.World user account email address, which must be known to the publisher. The user will receive an email notifying them of the subscription invitation, and in the app they may accept or decline the invitation.
Again, no subscription link is transmitted except between the Type.World server and app over HTTPS. The primary use of this API command is for users to pass the subscription on to other users in the app. But a publisher could theoretically use it to invite their users to a subscription directly without providing a button on their website. However, that’s not very convenient, as the publisher needs to collect their users’ Type.World user account email addresses, which could be different from the user account email addresses registered with the publisher. A publisher inviting users directly could be interesting for custom type projects, but for normal access, the button on the publisher’s website is recommended.The secret keys are then stored in the operating system’s keychain app and subscription URLs stored in the user preferences are stripped of that secret key so that they cannot be easily read by third party code. Since keychain access is normally restricted to the app that created a key in the keychain, a user is normally prompted when a third party wants to read it out.
With it, the subscription URLs with their secret key are stored as securely on the user’s computer as any other password used and stored in the operating system.
Securing your subscription could involve three different levels of security, of which only two are intended for protected fonts. Of those two, different security levels can be achieved through different implementation effort by the publisher.
At the very bottom of security levels there is no access restriction to a subscription and its fonts.
URL Format A or Format B are used here. Anyone can access these fonts and its primary use would be free fonts.
As soon as a subscription contains a single protected font, the app will require to be linked to a Type.World user account so that the subscription can be recorded in that user account.
When the API Endpoint receives a request for either installableFonts
or installFonts
or uninstallFonts
commands, it should check with the central Type.World server API (verifyCredentials
command) whether that user account is linked to at least one app instance, or rather, whether the request origin’s app instance is linked to a user account. For the verification API call, the publisher hands the anonymousTypeWorldUserID
and anonymousAppID
parameters that it received by the app over to the central server.
Downsides:
The security of this approach is limited by the operating system’s keychain security and by links being transmitted by non-encypted means, such as in emails. A user could unintentionally grant access to a secret key request by third party code in the operating system. It is the operating system’s responsibility to prevent this from happening and/or notifying the user sufficiently. But a user also needs to pay sufficient attention. The same is true for all other passwords stored in the keychains and is the normal scenario for storing passwords.
Benefits:
While the security level of this approach is limited, a rather important benefit lies in User Experience: See "Remote De-Authorization of App Instances by the User"
The highest level of security can be achieved with some additional effort by the publisher. It involves the single use access token of the subscription URL Format CE for initial subscription access, and the inclusion of the subscription URL in the verifyCredentials
command with the Type.World server.
The access button in the publisher’s user account section contains the single use access token under the URL Format CE. This access token is stored in the user account with the publisher and only ever included in this download button. Once the user clicks the button and the app requests the subscription with the installableFonts
command for the first time, the access token is submitted along with the request in a accessToken
parameter. The publisher verifies that the request carries the correct access token for this user. If authentication was successful, the publisher invalidates this access token and assigns a fresh one, allowing future access only with the fresh token. (Make sure that the access button isn’t visible on your website when that happens, or that it will be reloaded with the fresh access token instantly upon clicking, because if something goes wrong in the communication after the access token has been invalidated, the user will want to click again and needs to be able to access the fresh access token).
Otherwise, the publisher’s server returns the insufficientPermission
response and the subscription isn’t added the user account.
The app throws away the single use access token and continues to only ever use the subscription URL in Format C.
All subsequent requests with the publisher’s API Endpoint are then authenticated using the already mentioned verifyCredentials
command, but with the additional subscription URL transmitted along with the request in the subscriptionURL
parameter. Then, the server verifies not only that a Type.World user account is linked to an app instance, but also that the user already holds that subscription.
In summary, a request is authenticated either by a valid access token or by a successful verifyCredentials
request using the optional subscriptionURL
parameter.
With this approach, secret subscription URLs (in Format C) could theoretically disseminate into the wild with no consequences, because they don’t contain the access token, and without it, users can’t add a subscription by its URL alone. The only way to share such a subscription is the invitation API.
Downsides:
This approach cannot prevent a user to invite other users to share a subscription, but the limiting of invitations was never intended. However:
Benefits:
This approach is the only way to make 100% sure that users are authentic. If a user violates any of the publisher’s terms, the publisher may revoke the access to a subscription using the invitation API. Along with revoking access to one or all of its initial users, all subsequent invitations by those users to even other users will be revoked as well.
Subscriptions are synchronized with the central server in a Type.World user account and users can choose to de-authorize all subscriptions for an entire app instance through the app preferences.
The main incentive for the user to de-authorize his/her older app instances that are not accessible any more on a stolen or bricked computer is to free up font installations, because the font installations of that lost computer are still counted in the publisher’s tracking of installed seats with regards to each font license.
Should the de-authorized app instance regain access to the internet (in case the computer is actually stolen rather than lost/broken), all protected fonts will be deleted from it instantly. And because all referenced publishers already know of the de-authorization (through the verifyCredentials
command), new font installations thereafter will also be impossible. While the fonts could theoretically continue to live on an offline computer indefinitely, this approach allows the user to at least free those seats themselves without personally interacting with the publisher to free those seats, or upgrading font licenses.
Therefore, the primary benefit of this is a user experience gain, because users don’t need to interact with the publisher when all this happens.
Because spreading subscription URLs by email (or other means) is potentially unsafe from eavesropping, the central Type.World server provides an invitation API using its JSON API under the inviteUserToSubscription
command (or directly in the app). Therefore, only users with a registered Type.World user account can be invited. Here, users will be identified by the email address of their Type.World user account (like Dropbox or Google Documents). There is no way to search the Type.World user database for users. Only valid and previously registered email addresses will be accepted.
It is not possible to provide this invitation infrastructure to users without a Type.World user account, because otherwise a notification about the invitation needs to be sent out by email which can be intercepted and accessed before the legitimate user gets access.
Without a Type.World user account, this notification, however formed, would be the key to the subscription. With a Type.World user account, the account itself is the key, and any form of notification of the invitation, such as by email, is meaningless without the previously existing user account.
Access to the verifyCredentials
command (amongst others) on the central Type.World server will be restricted to holders of a publisher-specific secret API key. The API Key can be obtained through the user account section on the Type.World website.
A customer purchases a font from a publisher. It is expected that the customer needs to be logged into the publisher’s web site and that the purchase is recorded in the publisher’s user account.
After the purchase is complete, the customer is offered two download options: The traditional ZIP file and a new Download in Type.World App button. Upon clicking on that new button, the Type.World App comes open and sets up the subscription in the app, or updates it if it was previously present. The newly purchased fonts are then available to install for the user.
The user needs to have downloaded and installed the app before this hand-off can take place. And in case of commercial fonts, the user also needs to have created a separate Type.World user account, so that the subscription’s existence for this user can be recorded by the Type.World system. The reason why this is mandatory for commercial fonts is explained under Remote De-Authorization of App Instances by the User.
However, a user only ever needs to install the app and set up a Type.World user account once. After that initial setup, the user experience is flawless.
Graphic designers are working in a branding firm to create a visual appearance for their client’s new brand. A type designer is commissioned by the branding firm to create a custom typeface for this new brand. The type designer uploads the font files to their Type.World font server. This could be their own server implementation or a turn-key service. The type designer then invites the branding firm’s project lead to a subscription in the Type.World App, who in turn invites all graphic designers in the firm to use the subscription in the Type.World App.
The type designer can regularly publish updates of the typeface which will be offered to download for the graphic designers in the app. As opposed to pushing new font files to designers by email, the type designer and the project lead can now observe which of their designers are working with which version of the commissioned typeface, and remind the designer to update the fonts if necessary. The knowledge of which users are using which version of the fonts is anonymous by default, but the font server may also request that the user’s identity is revealed to the server when installing fonts. This knowledge can be made available to the project lead by the font server in a secure way.
Free fonts can be offered to download for users, either in one subscription per family, or all fonts in one subscription.
The central type.world server operates its own API that offers various interactions with the system, such as the user validation:
Central Type.World Server API Documentation
The central Type.World server is hosted in Google’s cloud in a Google App Engine. Instances of this server are initiated and destroyed whenever the load balancer pleases. As a result, a server instance may disappear in the middle of your request, and the request therefore returned with either HTTP Code 500 Internal Server Error
or 503 Service Unavailable
. You need to be prepared for that, and repeat the request if necessary.
Internally, the typeworld.client
module uses a method called performRequest()
that loops over the request up to 10 times, making sure that the central server can be reached even if a request is returned with either 500
or 503
once. If you implement your server in Python, you are invited to make use of that method as well, or otherwise create your implementation, as long as you are aware of sudden server disappearances.
If you’re building your own API endpoint under the Type.World JSON protocol, we offer a remote API Endpoint Validator that will check your endpoint for proper functionality.
typeworld
Python module via PyPi with pip install typeworld
, you may invoke the validator from the command line: validateTypeWorldEndpoint typeworld://json+https//typeworldserver.com/api/bZA2JbWHEAkFjako0Mtz/ all
. This is the preferred method because it allows you to check your local development server that isn’t live on the internet. Also if you run it locally, it reduces strain on the central server. (That’s because of latency. These validations will take long, and the load balancer will think it needs to start another instance, which will affect our billing)The Type.World project aims to be a non-profit service to the type industry. However, there are costs, and those costs are expected to be shouldered by those factions in the industry that use the Type.World system in commercial work for themselves, be it custom fonts or selling commercial fonts. Even free font offerings don’t exist in a vacuum. They’re making money from ads, for instance, and therefore need to support the project.
https://awesomefonts.com/familyxyz/?affiliateID=hb6di3kes
. In this example, the string hb6di3kes
identifies Type.World as the origin. The foundry sets a cookie in the user’s browser for Type.World being the origin, and pays out a commission to Type.World for each sale that is generated thereafter that originated from Type.World. Type.World will be asking for a minimum commission of 5% for sales that originated from itself.Should the income be higher than all costs in the long term, it needs to be decided what to do with that money. Either the income is reduced (for example by lowering the commission percentage), or the excess money is spent on improving the system. Many ideas exist that could be supported by such excess money:
The following items aren’t yet implemented in the app, but planned:
wx
, a cross-platform Python GUI framework that the Type.World App uses. The limitation is expected to be lifted in wx
’s upcoming 4.1 release and thus Type.World is expected to look crisp on hi-resolution screens. Sadly, the timeline for that wx
update is not known. But it’s sure to happen, as wx
is a very popular and actively developed framework.