From 21a2b64f82b0412bded67e615b3a4f003d45bc30 Mon Sep 17 00:00:00 2001 From: Joep Meindertsma Date: Thu, 29 Jul 2021 12:25:06 +0200 Subject: [PATCH 001/140] Update mdbook --- .github/workflows/blank.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/blank.yml b/.github/workflows/blank.yml index e38898f..06e9182 100644 --- a/.github/workflows/blank.yml +++ b/.github/workflows/blank.yml @@ -14,7 +14,7 @@ jobs: - name: Setup mdBook uses: peaceiris/actions-mdbook@v1 with: - mdbook-version: '0.3.7' + mdbook-version: '0.4.11' # mdbook-version: 'latest' - run: mdbook build From 58cf183ccc0bb79d986a67d446c9d7b6a09748f1 Mon Sep 17 00:00:00 2001 From: Joep Meindertsma Date: Thu, 29 Jul 2021 12:27:49 +0200 Subject: [PATCH 002/140] Revert mdbook version - build fix --- .github/workflows/blank.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/blank.yml b/.github/workflows/blank.yml index 06e9182..e38898f 100644 --- a/.github/workflows/blank.yml +++ b/.github/workflows/blank.yml @@ -14,7 +14,7 @@ jobs: - name: Setup mdBook uses: peaceiris/actions-mdbook@v1 with: - mdbook-version: '0.4.11' + mdbook-version: '0.3.7' # mdbook-version: 'latest' - run: mdbook build From c838fbad1b9c6b5aca19838c9c94113318f2c6fc Mon Sep 17 00:00:00 2001 From: Joep Meindertsma Date: Thu, 29 Jul 2021 12:32:00 +0200 Subject: [PATCH 003/140] Try linkcheck #54 --- .github/workflows/blank.yml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/.github/workflows/blank.yml b/.github/workflows/blank.yml index e38898f..e8a8a1a 100644 --- a/.github/workflows/blank.yml +++ b/.github/workflows/blank.yml @@ -14,8 +14,12 @@ jobs: - name: Setup mdBook uses: peaceiris/actions-mdbook@v1 with: - mdbook-version: '0.3.7' - # mdbook-version: 'latest' + mdbook-version: '0.4.11' + + - name: Install mdbook-linkcheck + run: | + curl -LSfs https://japaric.github.io/trust/install.sh | \ + sh -s -- --git Michael-F-Bryan/mdbook-linkcheck - run: mdbook build From ecd457be4eba40cad10569b252dfc85ed5103823 Mon Sep 17 00:00:00 2001 From: Joep Meindertsma Date: Mon, 19 Jul 2021 17:48:06 +0200 Subject: [PATCH 004/140] Mention arthur --- src/get-involved.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/get-involved.md b/src/get-involved.md index 53f140c..5a3ce10 100644 --- a/src/get-involved.md +++ b/src/get-involved.md @@ -21,5 +21,6 @@ Things you can do: - **Ruben Verborgh** (for doing great work with RDF, such as the TPF spec) - **Pat McBennett** (for lots of valuable feedback on initial Atomic Data docs) - **Manu Sporny** (for his work on JSON-LD, which was an important inspiration for JSON-AD) -- **Jonas Smedegaard** (for the various intersting talks we had and the feedback he provided) +- **Jonas Smedegaard** (for the various interesting talks we had and the feedback he provided) +- **Arthur Dingemans** (for sharing his thoughts, providing feedback and his valuable suggestions) - All the other people who contributed to linked data related standards From 3f7cf5de61af8e8e8414a5d44b7fedb230e4318d Mon Sep 17 00:00:00 2001 From: Joep Meindertsma Date: Mon, 2 Aug 2021 18:08:12 +0200 Subject: [PATCH 005/140] Verfiable Credentials --- src/SUMMARY.md | 1 + .../verifiable-credentials.md | 20 --------- src/usecases/verifiable-credentials.md | 41 +++++++++++++++++++ 3 files changed, 42 insertions(+), 20 deletions(-) delete mode 100644 src/interoperability/verifiable-credentials.md create mode 100644 src/usecases/verifiable-credentials.md diff --git a/src/SUMMARY.md b/src/SUMMARY.md index 55dc8ee..60de1ed 100644 --- a/src/SUMMARY.md +++ b/src/SUMMARY.md @@ -33,6 +33,7 @@ * [Possible use cases](usecases/intro.md) * [Personal Data Store](usecases/personal-data-store.md) * [Surveys](usecases/surveys.md) + * [Verifiable Credentials](usecases/verifiable-credentials.md) * [Software and libraries](tooling.md) ----------- diff --git a/src/interoperability/verifiable-credentials.md b/src/interoperability/verifiable-credentials.md deleted file mode 100644 index 52f53a3..0000000 --- a/src/interoperability/verifiable-credentials.md +++ /dev/null @@ -1,20 +0,0 @@ -# Atomic Data and Verifiable Credentials - -Verifiable Credentials are pieces of information that have cryptographic proof by some reliable third party. -For example, you could have a credential that proves your degree, signed by your education. -These credentials an enable privacy-friendly transactions where a credential owner can prove being part of some group, without needing to actually identify themselves. -For example, you could prove that you're over 18 by showing a credential issued by your government, without actually having to show your ID card with your birthdate. -Verifiable Credentials are still not that widely used, but various projects exists that have had moderate success in implementing it. - -## W3C Verifiable Credentials spec - -The W3C Verifiable Credentials specification has helped to create a spec to describe credentials, but it still leaves some important work to be done. -Most - -## Self-sovereign identity - -Atomic Data is designed to give people more control over their own personal data. -Part of this, is being able to prove things about your identity to others, without relying on some third party to acknowledge this every single time. -This is where verifiable credentials come in. - -Visit the [issue on github](https://github.com/ontola/atomic-data-docs/issues/22) to join the discussion about this subject. diff --git a/src/usecases/verifiable-credentials.md b/src/usecases/verifiable-credentials.md new file mode 100644 index 0000000..a59f64e --- /dev/null +++ b/src/usecases/verifiable-credentials.md @@ -0,0 +1,41 @@ +# Atomic Data and Verifiable Credentials / SSI + +## What are Verifiable Credentials / Self-Sovereign Identity + +Verifiable Credentials are pieces of information that have cryptographic proof by some reliable third party. +For example, you could have a credential that proves your degree, signed by your education. +These credentials an enable privacy-friendly transactions where a credential owner can prove being part of some group, without needing to actually identify themselves. +For example, you could prove that you're over 18 by showing a credential issued by your government, without actually having to show your ID card with your birthdate. +Verifiable Credentials are still not that widely used, but various projects exists that have had moderate success in implementing it. + +## What makes Atomic Data suitable for this + +Firstly, Atomic Commits are already verifiable using signatures that contain all the needed information. +Secondly, Atomic Schema can be used for standardizing Credential Schemas. + +## Every Atomic Commit is a Verifiable Credential + +Every time an Agent updates a Resource, a Commit is made. +This Commit is cryptographically signed by an Agent, just like how Verfifiable Credentials are signed. +In essence, this means that _all atomic data created through commits is fully verifiable_. + +How could this verification work? + +- **Find the Commit** that has created / edited the value that you want to verify. This can be made easier with a specialized Endpoint that takes a `resource`, `property` and `signer` and returns the associated Commit(s). +- **Check the signer of the Commit**. Is that an Agent that you trust? +- **Verify the signature** of the Commit using the public key of the Agent. + +Sometimes, credentials need to be revoked. +How could revocation work? + +- **Find the Commit** (see above) +- **Get the signer** (see above) +- **Find the `/isRevoked` Endpoint of that signer**, send a Request there to make sure the linked Commit is still valid and not revoked. + +Visit the [issue on github](https://github.com/ontola/atomic-data-docs/issues/22) to join the discussion about this subject. + +## Use Atomic Schema for standardizing Credentials + +If you are a Verifier who wants to check someone's _birthdate_, you'll probably expect a certain datatype in return, such as a [date](https://atomicdata.dev/datatypes/date) that is formatted in some specific way. +Atomic Schema makes it possible to express which _properties_ are [required](https://atomicdata.dev/properties/requires) in a certain [Class](https://atomicdata.dev/classes/Class), and it also makes it possible to describe which [datatype](https://atomicdata.dev/classes/Datatype) is linked to a specific [Property](https://atomicdata.dev/classes/Property). +Combined, they allow for fine-grained descriptions of models / classes / schemas. From 1014299268ebf38df89e2b53acf800b7ee3f4fdb Mon Sep 17 00:00:00 2001 From: Joep Meindertsma Date: Mon, 2 Aug 2021 18:09:37 +0200 Subject: [PATCH 006/140] Remove linkcheck from build --- .github/workflows/blank.yml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/.github/workflows/blank.yml b/.github/workflows/blank.yml index e8a8a1a..489f5ac 100644 --- a/.github/workflows/blank.yml +++ b/.github/workflows/blank.yml @@ -16,11 +16,6 @@ jobs: with: mdbook-version: '0.4.11' - - name: Install mdbook-linkcheck - run: | - curl -LSfs https://japaric.github.io/trust/install.sh | \ - sh -s -- --git Michael-F-Bryan/mdbook-linkcheck - - run: mdbook build - name: Deploy From c29d584c28538432deee629862a438fc9a27d92f Mon Sep 17 00:00:00 2001 From: Joep Meindertsma Date: Mon, 2 Aug 2021 18:10:43 +0200 Subject: [PATCH 007/140] Linkcheck optional build --- book.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/book.toml b/book.toml index af3a367..cd557f1 100644 --- a/book.toml +++ b/book.toml @@ -10,3 +10,4 @@ git-repository-url = "https://github.com/ontola/atomic-data" git-repository-icon = "fa-github" [output.linkcheck] +optional = true From 604e9e41cc74a35222037c90dc7c0c037793d7e8 Mon Sep 17 00:00:00 2001 From: Joep Meindertsma Date: Mon, 2 Aug 2021 18:13:22 +0200 Subject: [PATCH 008/140] Links in VC --- src/usecases/verifiable-credentials.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/usecases/verifiable-credentials.md b/src/usecases/verifiable-credentials.md index a59f64e..17d482b 100644 --- a/src/usecases/verifiable-credentials.md +++ b/src/usecases/verifiable-credentials.md @@ -10,12 +10,12 @@ Verifiable Credentials are still not that widely used, but various projects exis ## What makes Atomic Data suitable for this -Firstly, Atomic Commits are already verifiable using signatures that contain all the needed information. -Secondly, Atomic Schema can be used for standardizing Credential Schemas. +Firstly, [Atomic Commit](../commits/intro.md) are already verifiable using signatures that contain all the needed information. +Secondly, [Atomic Schema](../schema/intro.md) can be used for standardizing Credential Schemas. ## Every Atomic Commit is a Verifiable Credential -Every time an Agent updates a Resource, a Commit is made. +Every time an Agent updates a Resource, an [Atomic Commit](../commits/intro.md) is made. This Commit is cryptographically signed by an Agent, just like how Verfifiable Credentials are signed. In essence, this means that _all atomic data created through commits is fully verifiable_. @@ -37,5 +37,5 @@ Visit the [issue on github](https://github.com/ontola/atomic-data-docs/issues/22 ## Use Atomic Schema for standardizing Credentials If you are a Verifier who wants to check someone's _birthdate_, you'll probably expect a certain datatype in return, such as a [date](https://atomicdata.dev/datatypes/date) that is formatted in some specific way. -Atomic Schema makes it possible to express which _properties_ are [required](https://atomicdata.dev/properties/requires) in a certain [Class](https://atomicdata.dev/classes/Class), and it also makes it possible to describe which [datatype](https://atomicdata.dev/classes/Datatype) is linked to a specific [Property](https://atomicdata.dev/classes/Property). +[Atomic Schema](../schema/intro.md) makes it possible to express which _properties_ are [required](https://atomicdata.dev/properties/requires) in a certain [Class](https://atomicdata.dev/classes/Class), and it also makes it possible to describe which [datatype](https://atomicdata.dev/classes/Datatype) is linked to a specific [Property](https://atomicdata.dev/classes/Property). Combined, they allow for fine-grained descriptions of models / classes / schemas. From 9b3f0c561bbeb659d4488bdc6d692002096e8476 Mon Sep 17 00:00:00 2001 From: Joep Meindertsma Date: Thu, 5 Aug 2021 11:10:34 +0200 Subject: [PATCH 009/140] Improve motivation document --- src/motivation.md | 54 +++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 47 insertions(+), 7 deletions(-) diff --git a/src/motivation.md b/src/motivation.md index 2e6804d..b7edf50 100644 --- a/src/motivation.md +++ b/src/motivation.md @@ -1,19 +1,59 @@ # Motivation: Why Atomic Data? -Linked data (RDF / the semantic web) enables us to use the web as a large, decentralized graph database. +## Give people more control over their data + +The world wide web was designed by Tim Berners-Lee to be a decentralized network of servers that help people share information. +As I'm writing this, it is exactly 30 years ago that the first website has launched. +Unfortunately, the web today is not the decentralized network it was supposed to be. +A handful of large tech companies are in control of how the internet is evolving, and where and how our data is being stored. +The various services that companies like Google and Microsoft offer (often for free) integrate really well with their other services, but are mostly designed to _lock you in_. +Vendor lock-in means that it is often difficult to take your information from one app to another. +This limits innovation, and limits users to decide how they want to interact with their data. +Companies often have incentives that are not fully aligned with what users want. +For example, Facebook sorts your newsfeed not to make you satisfied, but to make you spend as much time looking at ads. +They don't want you to be able to control your own newsfeed. +Even companies like Apple, that don't have an ad-revenue model, still have a reason to (and very much do) lock you in. +To make things even worse, even open-source projects made by volunteers often to don't work well together. +That's not because of bad intentions, that's because it is _hard_ to make things interoperable. + +If we want to change this, we need open tech that works really well together. +And if we want that, we need to _standardize_. +The existing standards are well-suited for documents and webpages, but not for structured personal data. +If we want to have that, we need to standardize the _read-write web_, which includes standardizing how items are changed, how their types are checked, how we query lists, and more. +I want all people to have a (virtual) private server that contains their own data, that they control. +This [Personal Data Store](usecases/personal-data-store.md) could very well be an old smartphone with a broken screen that is always on, running next to your router. + +Atomic Data is designed to be a standard that achieves this. +But we need more than a standard to get adoption - we need implementations. +That's why I've been working on a server, various libraries, a GUI and [more](tooling.md) - all MIT licensed. +If Atomic Data will be successful, there will likely be other, better implementations. + +## Linked data is awesome, but it is too difficult for developers in its current form + +[Linked data](https://ontola.io/what-is-linked-data/) (RDF / the semantic web) enables us to use the web as a large, decentralized graph database. Using links everywhere in data has amazing merits: links remove ambiguity, they enable exploration, they enable connected datasets. -Linked Data could help to democratize the web by decentralizing information storage, and giving people more control. -The Solid Project by Tim Berners-Lee is a great example of why linked data can help to create a more decentralized web. +But the existing specs are too difficult to use, and that is harming adoption. -At [Ontola](https://ontola.io/), we've been working with linked data quite intensely for the last couple of years. +At my company [Ontola](https://ontola.io/), we've been working with linked data quite intensely for the last couple of years. We went all-in on RDF, and challenged ourselves to create software that communicates exclusively using it. That has been an inspiring, but at times also a frustrating journey. -While building various production grade apps (e.g. our e-democracy platform [Argu.co](https://argu.co/), which is used by various governments), we had to [solve many problems](https://ontola.io/blog/full-stack-linked-data/). -How to properly model data in RDF? How to deal with sequences? How to communicate state changes? Converting RDF to HTML? Typing? CORS? +While building our e-democracy platform [Argu.co](https://argu.co/), we had to [solve many RDF related problems](https://ontola.io/blog/full-stack-linked-data/). +How to properly model data in RDF? How to deal with [sequences](https://ontola.io/blog/ordered-data-in-rdf/)? How to communicate state changes? Which [serialization format](https://ontola.io/blog/rdf-serialization-formats/) to use? How to convert [RDF to HTML, and build a front-end](https://ontola.io/blog/rdf-solid-react-tutorial-link/)? We tackled some of these problems by having a tight grip on the data that we create (e.g. we know the type of data, because we control the resources), and another part is creating new protocols, formats, tools, and libraries. But it took a long time, and it was hard. It's been almost 15 years since the [introduction of linked data](https://www.w3.org/DesignIssues/LinkedData.html), and its adoption has been slow. We know that some of its merits are undeniable, and we truly want the semantic web to succeed. -We believe the lack of growth partially has to do with a lack of tooling, but also with [some problems that lie in the RDF data model](interoperability/rdf.md#why-these-changes). +I believe the lack of growth partially has to do with a lack of tooling, but also with some problems that lie in the RDF data model. Atomic Data aims to take the best parts from RDF, and learn from the past to make a more developer-friendly, performant and reliable data model to achieve a truly linked web. +Read more about [how Atomic Data relates to RDF, and why these changes have been made](interoperability/rdf.md). + +## Make it easier for developers to build really good, interoperable apps + +Every time a developer builds an application, they have to figure a lot of things out. +How to design the API, how to implement forms, how to deal with authentication, authorization, versioning, search... +By having a more complete, strict standard, Atomic Data aims to decrease this burden. +[Atomic Schema](schema/intro.md) enables developers to easily share their datamodels, and re-use those from others. +[Atomic Commits](commits/intro.md) helps developers to deal with versioning, history, undo and audit logs. +[Atomic Hierarchies](hierarchy.md) provides an intuitive model for authorization and access control. +And finally, the [existing](tooling.md) open source libraries, server, GUI and templates help developers to have these features without writing them. From 7e3dbd6d902fa73d13553bd110e312ded484153d Mon Sep 17 00:00:00 2001 From: Joep Meindertsma Date: Thu, 19 Aug 2021 16:25:22 +0200 Subject: [PATCH 010/140] JSON AD improved --- src/core/json-ad.md | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/core/json-ad.md b/src/core/json-ad.md index a375529..865ff10 100644 --- a/src/core/json-ad.md +++ b/src/core/json-ad.md @@ -1,12 +1,14 @@ # JSON-AD: The Atomic Data serialization format -`JSON-AD` is the _default_ serialization format for Atomic Data. -It is what the current [Rust](https://github.com/joepio/atomic) and [Typescript / React](https://github.com/joepio/atomic-data-browser) implementation use to communicate. -It is a [JSON](https://www.ecma-international.org/publications-and-standards/standards/ecma-404/) with a lot of links in it and the following rules: +Although you an use various serialization formats for Atomic Data, `JSON-AD` is the _default_ serialization format. +It is what the current [Rust](https://github.com/joepio/atomic) and [Typescript / React](https://github.com/joepio/atomic-data-browser) implementations use to communicate. +It is designed to feel familiar to developers an to be easy and performant to parse and serialize. -- Every Object is a Resource. -- Every Key is a Property URL. -- The `@id` field is special: it defines the Subject of the Resource. +It uses [JSON](https://www.ecma-international.org/publications-and-standards/standards/ecma-404/), but has some additional constraints: + +- Every Object is a `Resource`. +- Every Key is a [`Property`](https://atomicdata.dev/classes/Property) URL. +- The `@id` field is special: it defines the `Subject` of the `Resource`. - JSON arrays are mapped to [Resource Arrays](https://atomicdata.dev/datatypes/resourceArray) - Numbers can be [Integers](https://atomicdata.dev/datatypes/integer), [Timestamps](https://atomicdata.dev/datatypes/timestamp) or [Floats](https://atomicdata.dev/datatypes/float). - JSON booleans map to [Booleans](https://atomicdata.dev/datatypes/boolean). From 63d80134f7c199bfd95ff32d3591cec9348dc4dc Mon Sep 17 00:00:00 2001 From: Joep Meindertsma Date: Thu, 19 Aug 2021 16:25:34 +0200 Subject: [PATCH 011/140] Add e-commerce --- src/SUMMARY.md | 3 ++- src/usecases/e-commerce.md | 50 +++++++++++++++++++++++++++++++++----- src/usecases/intro.md | 8 +++++- 3 files changed, 53 insertions(+), 8 deletions(-) diff --git a/src/SUMMARY.md b/src/SUMMARY.md index 60de1ed..bb9998f 100644 --- a/src/SUMMARY.md +++ b/src/SUMMARY.md @@ -30,8 +30,9 @@ * [SQL](interoperability/sql.md) * [Graph Databases](interoperability/graph-database.md) * [Your existing project](interoperability/upgrade.md) -* [Possible use cases](usecases/intro.md) +* [Potential use cases](usecases/intro.md) * [Personal Data Store](usecases/personal-data-store.md) + * [E-commerce & marketplaces](usecases/e-commerce.md) * [Surveys](usecases/surveys.md) * [Verifiable Credentials](usecases/verifiable-credentials.md) * [Software and libraries](tooling.md) diff --git a/src/usecases/e-commerce.md b/src/usecases/e-commerce.md index de98cb8..9bd542c 100644 --- a/src/usecases/e-commerce.md +++ b/src/usecases/e-commerce.md @@ -1,14 +1,50 @@ -# Atomic Data for e-commerce +# Atomic Data for e-commerce & marketplaces + +Buying good and services on the internet is currently responsible for about 15% of all commerce, and is steadily climbing. +The internet makes it easier to find products, compare prices, get information and reviews, and finally order something. +But the current e-commerce situation is far from perfect, as large corporations tend to monopolize, which means that we have less competition which ultimately harms prices and quality for consumers. +Atomic Data can help empower smaller businesses, make searching for specific things way easier and ultimately make things cheaper for everyone. + +## Decentralize platform / sharing economy service marketplaces + +Platforms like Uber, AirBNB and SnapCar are virtual marketplaces that help people share and find services. +These platforms are responsible for: + +1. providing an interface for **managing offers** (e.g. describe your car, add specifications and pricing) +2. **hosting** the data of the offers themselves (make the data available on the internet) +3. providing a **search interface** (which means indexing the data from all the existing offers) +4. facilitating the **transaction** / payments +5. provide **trust** through reviews and warranties (e.g. refunds if the seller fails to deliver) + +The fact that these responsibilities are almost always combined in a single platforms leads to vendor lock-in and an uncompetitive landscape, which ultimately harms consumers. +Currently, if you want to manage your listing / offer on various platforms, you need to manually adjust it on all these various platforms. +Some companies even prohibit offering on multiple platforms (which is a legal problem, not a technical one). +This means that the biggest (most known) platforms have the most listings, so if you're looking for a house / car / rental / meal, you're likely to go for the biggest business - because that's the one that has the biggest assortment. + +Compare this to how the web works: every browser should support every type of webpage, and it does not matter where the webpage is hosted. +I can browse a webpage written on a mac on my windows machine, and I can read a webpage hosted by amazon on an google device. +It does not matter, because the web is _standardized_ and _open_, instead of being _centralized_ and managed by one single company as _proprietary_ data. +This openness of the web means that we get search engines like Google and Bing that _scrape_ the web and add it to their index. +This results in a dynamic where those who want to sell their stuff will need to share their stuff using an open standard (for webpages things like HTML and sometimes a bit of metadata), so crawlers can properly index the webpages. +We could do the same thing for _structured data_ instead of _pages_, and that's what Atomic Data is all about. + +Let's discuss a more practical example of what this could mean. +Consider a restaurant owner who currently uses UberEats as their delivery platform. +Using Atomic Data, they could define their menu on their own website. +The Atomic Schema specification makes it easy to standardize how the data of a menu item looks like (e.g. price, image, title, allergens, vegan...). +Several platforms (potentially modern variants of platforms like JustEat / UberEats) could then crawl this standardized Atomic Data, index it, and make it easily searchable. +The customer would use one (or multiple) of these platforms, that would probably have the _exact same_ offers. +Where these platforms might differ, is in their own service offering, such as delivery speed or price. +This would result in a more competitive and free market, where customers would be able to pick a platform based on their service price and quality, instead of their list of offerings. +It would empower the small business owner to be far more flexible in which service they will do business with. ## Highly personalized and customizable search -Searching for things on the internet is still not that great. +Searching for products on the internet is mostly limited to text search. If we want to buy a jacket, we see tonnes of jackets that are not even available in our own size. Every single website has their own way of searching and filtering. -Imagine creating a search description in _one_ application, and sending that to _multiple suppliers_, after you'll receive a fully personalized and optimized list of articles. -No duplicate articles, every article with price comparison. - +Imagine making a search query in _one_ application, and sending that to _multiple suppliers_, after you'll receive a fully personalized and optimized list of products. Browsing in an application that you like to use, not bound to any one specific store, that doesn't track you, and doesn't show advertisements. It is a tool that helps you to find what you need, and it is the job of producers to accurately describe their products in a format that your product browser can understand. @@ -21,15 +57,17 @@ Describing this in a machine-readable and predictable format as data is the next This is, of course, where Atomic Schema could help. Atomic-server could be the connected, open source database that suppliers use to describe their products as data. + diff --git a/src/usecases/intro.md b/src/usecases/intro.md index a4116f9..25e058d 100644 --- a/src/usecases/intro.md +++ b/src/usecases/intro.md @@ -1,5 +1,11 @@ -# Atomic Data Use Cases +# Potential Atomic Data Use Cases Most of this book is either abstract or technical, but this section aims to be different. In this section, we'll present concrete examples of things that can be built with Atomic Data. Although you could use Atomic Data for pretty much any type of application, it is especially valuable where **data re-use**, **standardization**, and **data ownership** are important. + + +* [Personal Data Store](usecases/personal-data-store.md) +* [E-commerce & marketplaces](usecases/e-commerce.md) +* [Surveys](usecases/surveys.md) +* [Verifiable Credentials](usecases/verifiable-credentials.md) From fcd4df58b8309595a601a61ae01bd4e5a4586eb7 Mon Sep 17 00:00:00 2001 From: Joep Meindertsma Date: Thu, 26 Aug 2021 14:50:45 +0200 Subject: [PATCH 012/140] Various improvements --- src/SUMMARY.md | 3 +- src/atomic-data-overview.md | 2 +- src/commits/intro.md | 2 +- src/core/concepts.md | 18 ++++- src/core/intro.md | 15 ----- src/core/json-ad.md | 13 +++- src/motivation.md | 4 ++ src/usecases/intro.md | 8 +-- src/usecases/knowledge-graph.md | 16 +++++ src/usecases/self-integrating-systems.md | 1 + src/usecases/semantic-web.md | 85 ++++++++++++++++++++++++ 11 files changed, 138 insertions(+), 29 deletions(-) delete mode 100644 src/core/intro.md create mode 100644 src/usecases/knowledge-graph.md create mode 100644 src/usecases/self-integrating-systems.md create mode 100644 src/usecases/semantic-web.md diff --git a/src/SUMMARY.md b/src/SUMMARY.md index bb9998f..b98ff55 100644 --- a/src/SUMMARY.md +++ b/src/SUMMARY.md @@ -3,8 +3,7 @@ * [Atomic Data Overview](atomic-data-overview.md) * [Motivation](motivation.md) * [When (not) to use it](when-to-use.md) -* [Core](core/intro.md) - * [Concepts](core/concepts.md) +* [What is Atomic Data?](core/concepts.md) * [Serialization](core/serialization.md) * [JSON-AD](core/json-ad.md) * [Querying](core/querying.md) diff --git a/src/atomic-data-overview.md b/src/atomic-data-overview.md index 486271e..594fd00 100644 --- a/src/atomic-data-overview.md +++ b/src/atomic-data-overview.md @@ -7,7 +7,7 @@ It uses links to connect pieces of data, and therefore makes it easier to connec Atomic Data is especially suitable for knowledge graphs, distributed datasets, semantic data, p2p applications, decentralized apps and linked open data. It is designed to be highly extensible, easy to use, and to make the process of domain specific standardization as simple as possible. -Atomic Data is [Linked Data](https://ontola.io/what-is-linked-data/), as it is a more strict subset of RDF. +Atomic Data is [Linked Data](https://ontola.io/what-is-linked-data/), as it is a [strict subset of RDF](interoperability/rdf.md). It is typed (you know if something is a `string`, `number`, `date`, `URL`, etc.) and extensible through [Atomic Schema](schema/intro.md), which means that you can define your own Classes, Properties and Datatypes. The default serialization format for Atomic Data is [JSON-AD](core/json-ad.md), which is simply JSON where each key is a URL of an Atomic Property. diff --git a/src/commits/intro.md b/src/commits/intro.md index b231cc9..1f6fd46 100644 --- a/src/commits/intro.md +++ b/src/commits/intro.md @@ -2,7 +2,7 @@ _Disclaimer: Work in progress, prone to change._ -Atomic Commits is a proposed standard for communicating state changes (events / transactions / patches / deltas / mutations) of [Atomic Data](../core/intro.md). +Atomic Commits is a proposed standard for communicating state changes (events / transactions / patches / deltas / mutations) of [Atomic Data](../core/concepts.md). It is the part of Atomic Data that is concerned with writing, editing, removing and updating information. ## Design goals diff --git a/src/core/concepts.md b/src/core/concepts.md index 19241f2..7b690c1 100644 --- a/src/core/concepts.md +++ b/src/core/concepts.md @@ -1,4 +1,4 @@ -# Atomic Data Core: Concepts +# What is Atomic Data? ## Atomic Data @@ -9,6 +9,18 @@ It is _typed_ data model, which means that every value should be validated and p It is a directed, labeled graph, similar to RDF, so contrary to some other (labeled) graph data models (e.g. NEO4j), a relationship between two items (Resources) does not have attributes. +## Design goals + +* **Browsable**: Data should explicitly link to other pieces of data, and these links should be followable. +* **Semantic**: Every data Atom and relation has a clear semantic meaning. +* **Interoperable**: Plays nice with other data formats (e.g. JSON, XML, and all RDF formats). +* **Open**: Free to use, open source, no strings attached. +* **Clear Ownership**: The data shows who is in control of the data, so new versions of the data can easily be retrieved. +* **Mergeable**: Any two sets of Atoms can be merged into a single graph without any merge conflicts / name collisions. +* **Extensible**: Anyone can define their own data types and create Atoms with it. +* **ORM-friendly**: Navigate a _decentralized_ graph by using dot.syntax, similar to how you navigate a JSON object in javascript. +* **Typed**: All valid Atomic data has an unambiguous, static datatype. Models expressed in Atomic Data can be mapped to programming language models, such as `structs` or `interfaces` in Typescript / Rust / Go. + ## Resource A Resource is a bunch of information about a thing, referenced by a single link (the Subject). @@ -21,7 +33,7 @@ A Property can only occur once in every Resource. ## Atom (or Atomic Triple) Every Resource is composed of Atoms. -The Atom is the smallest possible piece of _meaningful_ data / information. +The Atom is the smallest possible piece of _meaningful_ data / information (hence the name). You can think of an Atom as a single cell in a spreadsheet or database. An Atom consists of three fields: @@ -76,7 +88,7 @@ In the JSON-AD example above, we have: - two **Resources**, describing two different **Subjects**: `https://example.com/arnold` and `https://example.com/britta`. - three different **Properties** (`https://example.com/properties/bornAt`, `https://example.com/properties/firstName`, and `https://example.com/properties/bestFriend`) - four **Values** (`1991-01-20`, `Arnold`, `https://example.com/britta` and `Britta`) -- four **Atoms** +- four **Atoms** - every row is one Atom. All Subjects and Properties are Atomic URLs: they are links that point to more Atomic Data. One of the Values is a URL, too, but we also have values like `Arnold` and `1991-01-20`. diff --git a/src/core/intro.md b/src/core/intro.md deleted file mode 100644 index eec4855..0000000 --- a/src/core/intro.md +++ /dev/null @@ -1,15 +0,0 @@ -# Atomic Data Core - -The Atomic Data Core describes the fundamental data model of Atomic Data. - -## Design goals - -* **Browsable**: Data should explicitly link to other pieces of data, and these links should be followable. -* **Semantic**: Every data Atom and relation has a clear semantic meaning. -* **Interoperable**: Plays nice with other data formats (e.g. JSON, XML, and all RDF formats). -* **Open**: Free to use, open source, no strings attached. -* **Clear Ownership**: The data shows who is in control of the data, so new versions of the data can easily be retrieved. -* **Mergeable**: Any two sets of Atoms can be merged into a single graph without any merge conflicts / name collisions. -* **Extensible**: Anyone can define their own data types and create Atoms with it. -* **ORM-friendly**: Navigate a _decentralized_ graph by using dot.syntax, similar to how you navigate a JSON object in javascript. -* **Typed**: All valid Atomic data has an unambiguous, static datatype. Models expressed in Atomic Data can be mapped to programming language models, such as `structs` or `interfaces` in Typescript / Rust / Go. diff --git a/src/core/json-ad.md b/src/core/json-ad.md index 865ff10..d3e5290 100644 --- a/src/core/json-ad.md +++ b/src/core/json-ad.md @@ -3,18 +3,19 @@ Although you an use various serialization formats for Atomic Data, `JSON-AD` is the _default_ serialization format. It is what the current [Rust](https://github.com/joepio/atomic) and [Typescript / React](https://github.com/joepio/atomic-data-browser) implementations use to communicate. It is designed to feel familiar to developers an to be easy and performant to parse and serialize. +It is inspired by [JSON-LD](https://json-ld.org/). It uses [JSON](https://www.ecma-international.org/publications-and-standards/standards/ecma-404/), but has some additional constraints: -- Every Object is a `Resource`. -- Every Key is a [`Property`](https://atomicdata.dev/classes/Property) URL. +- Every single Object is a `Resource`. +- Every Key is a [`Property`](https://atomicdata.dev/classes/Property) URL. Other keys are invalid. Each Property URL must resolve to an online Atomic Data Property. - The `@id` field is special: it defines the `Subject` of the `Resource`. - JSON arrays are mapped to [Resource Arrays](https://atomicdata.dev/datatypes/resourceArray) - Numbers can be [Integers](https://atomicdata.dev/datatypes/integer), [Timestamps](https://atomicdata.dev/datatypes/timestamp) or [Floats](https://atomicdata.dev/datatypes/float). - JSON booleans map to [Booleans](https://atomicdata.dev/datatypes/boolean). - JSON strings can be many datatypes, including [String](https://atomicdata.dev/datatypes/string), [Markdown](https://atomicdata.dev/datatypes/markdown), [Date](https://atomicdata.dev/datatypes/date) or other. - Nested JSON Objects are Nested Resources. A Nested Resource can either be anonymous (without an `@id` subject) or a regular Nested Resource with an `@id` subject. -- When you want to describe multiple Resources in one JSON-AD document, use an array as the root item. +- The root data structure must either be a regular Resource, or an Array. When you want to describe multiple Resources in one JSON-AD document, use an array as the root item. Let's look at an example JSON-AD Resource: @@ -30,6 +31,8 @@ Let's look at an example JSON-AD Resource: } ``` +The mime type (for HTTP content negotiation) is `application/ad+json` ([registration ongoing](https://github.com/ontola/atomic-data-docs/issues/60)). + ## JSON-AD Parsers, serializers and other libraries - **Typescript / Javacript**: [@tomic/lib](https://www.npmjs.com/package/@tomic/lib) JSON-AD parser + in-memory store. Works with [@tomic/react](https://www.npmjs.com/package/@tomic/lib) for rendering Atomic Data in React. @@ -45,3 +48,7 @@ When you need deterministic serialization of Atomic Data (e.g. when calculating 1. The JSON-AD is minified: no newlines, no spaces. The last two steps of this process is more formally defined by the JSON Canonicalization Scheme (JCS, [rfc8785](https://tools.ietf.org/html/rfc8785)). + +## Interoperability with JSON and JSON-LD + +[Read more about this subject](../interoperability/json). diff --git a/src/motivation.md b/src/motivation.md index b7edf50..12a3e2e 100644 --- a/src/motivation.md +++ b/src/motivation.md @@ -1,5 +1,9 @@ # Motivation: Why Atomic Data? + ## Give people more control over their data The world wide web was designed by Tim Berners-Lee to be a decentralized network of servers that help people share information. diff --git a/src/usecases/intro.md b/src/usecases/intro.md index 25e058d..40468b9 100644 --- a/src/usecases/intro.md +++ b/src/usecases/intro.md @@ -5,7 +5,7 @@ In this section, we'll present concrete examples of things that can be built wit Although you could use Atomic Data for pretty much any type of application, it is especially valuable where **data re-use**, **standardization**, and **data ownership** are important. -* [Personal Data Store](usecases/personal-data-store.md) -* [E-commerce & marketplaces](usecases/e-commerce.md) -* [Surveys](usecases/surveys.md) -* [Verifiable Credentials](usecases/verifiable-credentials.md) +* [Personal Data Store](./personal-data-store.md) +* [E-commerce & marketplaces](./e-commerce.md) +* [Surveys](./surveys.md) +* [Verifiable Credentials](./verifiable-credentials.md) diff --git a/src/usecases/knowledge-graph.md b/src/usecases/knowledge-graph.md new file mode 100644 index 0000000..00cb90e --- /dev/null +++ b/src/usecases/knowledge-graph.md @@ -0,0 +1,16 @@ +# Atomic Data for (semantic) knowledge graph management + +Knowledge graphs are information structures that help organizations to organize their knowledge using a graph model. +Graphs are especially useful for structuring knowledge, as they allow having links between resources which makes relationships understandable and browsable. + +Atomic Data is a Graph structure, and [Atomic-Server](https://crates.io/crates/atomic-server/) is an open source Graph database. + +## The entire web as one knowledge graph + +Atomic Data uses URLs to identify resources. +This means that it + +## Type-safe, decentralized data structures + +Contrary to many other types of graph systems, Atomic Data ensures type-safety by having a built-in schema language ([Atomic Schema](../schema/intro.md)). +This means that it is very easy to share and re-use data models, which helps you standardize the classes and properties that you use. diff --git a/src/usecases/self-integrating-systems.md b/src/usecases/self-integrating-systems.md new file mode 100644 index 0000000..6d7d7aa --- /dev/null +++ b/src/usecases/self-integrating-systems.md @@ -0,0 +1 @@ +See https://www.nitrd.gov/nitrdgroups/images/b/ba/Steven_ray_the_future_of_software.pdf diff --git a/src/usecases/semantic-web.md b/src/usecases/semantic-web.md new file mode 100644 index 0000000..3d5847c --- /dev/null +++ b/src/usecases/semantic-web.md @@ -0,0 +1,85 @@ +# Atomic Data for the Semantic Web + +The term 'Semantic Web' was popularized in [a paper of the same name](https://www-sop.inria.fr/acacia/cours/essi2006/Scientific%20American_%20Feature%20Article_%20The%20Semantic%20Web_%20May%202001.pdf) published in 2001 by three people, including the inventor of the World Wide Web: Tim Berners-Lee. +In this paper, a vision was shared for how a higher degree of standardization on the internet could lead to a bunch of interesting innovations. +For example, it describes how an appointment for a hairdresser is scheduled automatically, by using information about the preferences and location of the individual. +By making the web machine-readable, we could get far more interoperability and therefore new applications that make our lives easier. +All of this would have been made possible by using linked data. + +It has been 20 years since this paper, and it is indeed easier then ever to make an appointment with a hairdresser. +However, this problem and many similar ones have not been solved by the semantic web: they have been solved by big companies that know everything about us, and have monopolized so much of the data on the internet. +Tech giants like Google and Microsoft have created ecosystems that integrate many types of (free) services, have huge databases containing all kinds of related stuff, and as a result, provide us with nice experiences. +A high degree of _centralization_, instead of _standardization_, turned out to be a sufficient solution, too. +But of course, this centralized approach comes at a serious cost. +First problem is we get _vendor lock-in_, which means that it becomes harder to switch from service to service. +We can't take our data from Whatsapp and take it to Telegram, for example, or our Twitter feed to Mastadon. +The second problem is that + +The Semantic Web wasn't just an idea and a paper - there were a lot of standards involved, all of which were properly defined and managed by the W3C, the organization that standardizes so much of our web. +But the adoption of most of these standards is pretty low, unfortunately. + +## Why the semantic web didn't take off + +Before we'll discuss why Semantic Web related standards (most importantly its core data model: RDF) aren't being used too much, you need to know that I have company called Ontola which has been specialized in semantic web technologies. +We love this vision of a semantic web, and have a strong dedication to make this a reality. +We've built many libraries, full stack apps and services on RDF, and I really do think we've built technically unique products. +By going through this process, we discovered how technologically hard it is to actually build semantic web apps. +I'm actually pretty sure that we're one of the very few companies that have built a full SAAS platform (the e-democracy platform [Argu.co](https://argu.co/)) that communicates exclusively with its back-end by using RDF. +You can read more about this journey in [full-stack linked data](https://ontola.io/blog/full-stack-linked-data/), but here I'll summarize why this was such a challenging and costly endeavor. + +### Standards without working implementations + +The Semantic Web community actually built + +### A lack of proper RDF tools + +### No business incentive to make data highly accessible + +If you're a software company that builds a product, you probably want people to keep using your product. +Investing in an awesome export feature where your customer can easily switch to a competitor is often a risky move. +This problem is of course not unique to the semantic web, but it is + +### Quirks in the RDF data model + +- No native support for arrays, which leads to a lot of confusion. I've written an [article comparing various approaches](https://ontola.io/blog/ordered-data-in-rdf/) on how to deal with this as an RDF developer. +- Subject-object combinations in RDF are not necessarily unique (contrary to key-value combinations in any Map or JSON object, for example), which makes querying and storing RDF hard. +- Named Graphs add another layer of complexity for identifying where data comes from, and makes querying and storing RDF again even harder. + +### Too much academic focus on reasoning, not enough on data models + +> Instead of the “let’s just build something that works” attitude that made the Web (and the Internet) such a roaring success, they brought the formalizing mindset of mathematicians and the institutional structures of academics and defense contractors. They formed committees to form working groups to write drafts of ontologies that carefully listed (in 100-page Word documents) all possible things in the universe and the various properties they could have, and they spent hours in Talmudic debates over whether a washing machine was a kitchen appliance or a household cleaning device + +- https://en.wikisource.org/wiki/Page:Aaron_Swartz_s_A_Programmable_Web_An_Unfinished_Work.pdf/15 + +### No schema language + +Being able to _check and validate_ the types of data is very useful when you want people to reach consensus on how to model things. +RDF Schema was not really a schema language. + +### Confusing terminology and documentation + +While learning the Semantic Web, a whole bunch of new concepts need to be learned. + Terms like + +### Too much new languages and serialization formats + +The Semantic Web and RDF are both older than JSON, and focused mostly on XML. +The First RDF serialization format (RDF/XML) was hard to read, hard to parse, very confusing and basically tried to combine the worst of graph-based and document-based data models. +After that, many new serialization formats appeared (N3, Turtle, JSON-LD) that made it even more confusing for developers to adopt this technology. +[Read this](https://ontola.io/blog/rdf-serialization-formats/) if you want to know more about RDF serialization formats. + +### Other reading + +- http://inamidst.com/whits/2008/ditching +- https://en.wikisource.org/wiki/Page:Aaron_Swartz_s_A_Programmable_Web_An_Unfinished_Work.pdf/15 +- https://twobithistory.org/2018/05/27/semantic-web.html + +## Why Atomic Data might give the Semantic Web a second chance + +When creating Atomic Data, I tried to learn from what went wrong with the Semantic Web. + +- Focus on developer experience from the start. +- **Minimize new serialization formats / languages**. Use things that people love. That's why Atomic Data uses JSON as its core serialization format, and keeps export support for all RDF formats. +- **Build applications, libraries and tools while writing the spec**. As a process, this means that every time the spec might result in a bad developer experience, I can update the specification. +- Have a schema language built in, include it in reference libraries. This results in all data being fully type safe. +- Have Subject-predicate / key-value uniqueness. From cc85247276cca7305fbf8c72578c8d2ab20ea7f8 Mon Sep 17 00:00:00 2001 From: Joep Meindertsma Date: Thu, 26 Aug 2021 18:22:16 +0200 Subject: [PATCH 013/140] Fix Collections definition --- src/core/querying.md | 2 +- src/when-to-use.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/querying.md b/src/core/querying.md index 5423325..417ec1c 100644 --- a/src/core/querying.md +++ b/src/core/querying.md @@ -2,7 +2,7 @@ There are multiple ways of getting Atomic Data into some system: -- [**Atomic Collections**](../schema/collections.md) is a simple way to traverse Atomic Graphs and target specific values +- [**Atomic Collections**](../schema/collections.md) can filter, sort and paginate resources - [**Atomic Paths**](paths.md) is a simple way to traverse Atomic Graphs and target specific values - [**Subject Fetching**](#subject-fetching-http) requests a single subject right from its source - [**Triple Pattern Fragments**](#triple-pattern-fragments) allows querying for specific (combinations of) Subject, Property and Value. diff --git a/src/when-to-use.md b/src/when-to-use.md index f52b24c..1eb6bb0 100644 --- a/src/when-to-use.md +++ b/src/when-to-use.md @@ -14,4 +14,4 @@ - **Internal use only**. If you're not sharing structured data, Atomic Data will probably only make things harder for you. - **Big Data**. If you're dealing with TeraBytes of data, you probably don't want to use Atomic Data. The added cost of schema validation and the lack of distributed / large scale persistence tooling makes it not the right choice. -- **Video / Audio / 3D**. These should have unique, optimized binary representations and have very strict, static schemas. The advantages of linke data do little to improve this, unless it's just for metadata. +- **Video / Audio / 3D**. These should have unique, optimized binary representations and have very strict, static schemas. The advantages of atomic / linked data do little to improve this, unless it's just for metadata. From 72634b0ced382e375643e11c33db7c6e09e7ce96 Mon Sep 17 00:00:00 2001 From: Joep Meindertsma Date: Mon, 30 Aug 2021 15:05:53 +0200 Subject: [PATCH 014/140] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index d4a85f6..6193ce2 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ _Atomic Data is a specification for sharing, modifying and modeling graph data._ View it on [docs.atomicdata.dev](https://docs.atomicdata.dev). -If you're looking for an (early) implementation of Atomic data, check out [atomic](https://github.com/joepio/atomic) (server + cli + lib) and [atomic-data-browser](https://github.com/joepio/atomic-data-browser) (react / typescript). +If you're looking for **implementations of Atomic Data**, check out [atomic-data-rust](https://github.com/joepio/atomic-data-rust) (server + cli + lib written in Rust) and [atomic-data-browser](https://github.com/joepio/atomic-data-browser) (react / typescript). ## About this repo From 81a9070466d22f3f8ac48afaff19413b9c33ae0a Mon Sep 17 00:00:00 2001 From: Joep Meindertsma Date: Thu, 29 Jul 2021 13:35:41 +0200 Subject: [PATCH 015/140] Add verifiable credentials --- src/SUMMARY.md | 1 + 1 file changed, 1 insertion(+) diff --git a/src/SUMMARY.md b/src/SUMMARY.md index b98ff55..f61c02f 100644 --- a/src/SUMMARY.md +++ b/src/SUMMARY.md @@ -28,6 +28,7 @@ * [IPFS](interoperability/ipfs.md) * [SQL](interoperability/sql.md) * [Graph Databases](interoperability/graph-database.md) + * [Verifiable Credentials](interoperability/verifiable-credentials.md) * [Your existing project](interoperability/upgrade.md) * [Potential use cases](usecases/intro.md) * [Personal Data Store](usecases/personal-data-store.md) From 1ac48657db7f25ebe6fcbd4b1fd527f102c557f2 Mon Sep 17 00:00:00 2001 From: Joep Meindertsma Date: Thu, 29 Jul 2021 13:53:21 +0200 Subject: [PATCH 016/140] Optional linkcheck, udpate mdbook --- .github/workflows/blank.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/blank.yml b/.github/workflows/blank.yml index 489f5ac..cd247b4 100644 --- a/.github/workflows/blank.yml +++ b/.github/workflows/blank.yml @@ -14,7 +14,8 @@ jobs: - name: Setup mdBook uses: peaceiris/actions-mdbook@v1 with: - mdbook-version: '0.4.11' + # mdbook-version: '0.3.7' + mdbook-version: 'latest' - run: mdbook build From 981c83c617055c9ab7bd198d6d14b5b6c799c15c Mon Sep 17 00:00:00 2001 From: Joep Meindertsma Date: Thu, 2 Sep 2021 10:54:44 +0200 Subject: [PATCH 017/140] Solid SQL vc-old --- src/interoperability/solid.md | 16 +++++++++++----- src/interoperability/sql.md | 22 +++++++++++++++++----- src/interoperability/upgrade.md | 2 +- src/interoperability/vc-old.md | 32 ++++++++++++++++++++++++++++++++ 4 files changed, 61 insertions(+), 11 deletions(-) create mode 100644 src/interoperability/vc-old.md diff --git a/src/interoperability/solid.md b/src/interoperability/solid.md index dd2d66c..fca6cec 100644 --- a/src/interoperability/solid.md +++ b/src/interoperability/solid.md @@ -17,10 +17,11 @@ But there are some important **differences**, too, which will be explained in mo - Atomic Data uses a strict built-in schema to ensure type safety. - Atomic Data standardizes state changes (which also provides version control / history, audit trails) - Atomic Data is more easily serializable to other formats (like JSON) -- Atomic Data is less mature, and currently lacks things like authentication and hierarchy +- Atomic Data has a different model for Authorzation and Hierarchies +- Atomic Data is less mature, and currently lacks things like authentication for read Access -Disclaimer: I've been quite involved in the development of Solid, and have a lot of respect for all the people who are working on it. -The following is not meant as a critique on Solid, let alone the individuals working on it. +_Disclaimer: I've been quite involved in the development of Solid, and have a lot of respect for all the people who are working on it. +The following is not meant as a critique on Solid, let alone the individuals working on it._ ## Atomic Data is type-safe, because of its built-in schema @@ -63,13 +64,18 @@ Atomic Data uses `shortnames` to map properties to short, human-readable strings For more information about these differences, see the previous [RDF chapter](./rdf.md). +## Hierarchy model and authorization + +Atomic Data uses `parent-child` hierarchies to model data and performan authorization checks. +This closely resembles how filesystems work, and is therefore familiar to most users. +Solid uses + ## Solid is more mature Atomic Data has significant gaps at this moment - not just in the implementations, but also in the spec. This makes it not yet usable for most applications. Here's a list of things missing in Atomic Data, with links to their open issues and links to their existing Solid counterpart. -- No way to restrict access to reading content. Only for writing content with Commits. [WAC](https://github.com/solid/web-access-control-spec) in Solid. Also, [No hierarchy model](https://github.com/ontola/atomic-data/issues/18). [ShapeTrees in Solid](https://shapetrees.org/TR/specification/index.html#ecosystem). (We're working on an implementation of a hierarchy with authorization, see [issue](https://github.com/ontola/atomic-data/issues/18)) +- No way yet to restrict access to reading content. Only for writing content with Commits. [WAC](https://github.com/solid/web-access-control-spec) in Solid. - No inbox or [notifications](https://www.w3.org/TR/ldn/) ([issue](https://github.com/ontola/atomic-data/issues/28)) -- No way to discover content from user ID. - No support from a big community, a well-funded business or the inventor of the world wide web. diff --git a/src/interoperability/sql.md b/src/interoperability/sql.md index 7061a60..090ad79 100644 --- a/src/interoperability/sql.md +++ b/src/interoperability/sql.md @@ -6,14 +6,26 @@ Atomic Data has some characteristics that make it similar and different from SQL - Atomic Data separates _reading_ and _writing_, whereas SQL has one language for both. - Atomic Data has a standardized way of storing changes ([Commits](../commits/intro.md)) -## Dynamic vs static schema +## Tables and Rows vs. Classes and Properties At its core, SQL is a query language based around _tables_ and _rows_. -The _tables_ in SQL are similar to _classes_ in Atomic Data: they both define a set of _properties_ which an item could have. +The _tables_ in SQL are similar to `Classes` in Atomic Data: they both define a set of `properties` which an item could have. +Every single item in a table is called a _row_ in SQL, and a `Resource` in Atomic Data. + +## Identifiers: numbers vs. URLs + +In SQL, rows have numbers as identifiers, whereas in Atomic Data, every resource has a resolvable HTTP URL as an identifier. +This allows Atomic Data records to be easily re-used by other systems, as there is a guarantee that identifiers will be globally unique. + +## Dynamic vs static schema + In SQL, the schema of the database defines which shape the data can have, which properties are required, what datatypes they have. In Atomic Data, the schema exists as a Resource on the web, which means that they can be retrieved using HTTP. -SQL is a centralized, closed system. -Atomic Data is a decentralized, open system. +An Atomic Database (such as [Atomic-Server](https://crates.io/crates/atomic-server)) uses a _dynamic schema_, +which means that any Resource can have different properties, and the properties themselves can be validated, even when the server is not aware of these properties beforehand. +In SQL, you'd have to manually adjust the schema of your database to add a new property. +Atomic Data is a decentralized, open system, which can read new schema data from other sources. +SQL is a centralized, closed system, which relies on the DB manager to define the schema. ## Querying @@ -40,7 +52,7 @@ So in a way, Atomic Data tries to combine best of both worlds: the extendibility ### Is Atomic Data transactional / ACID? -Well, if you use Atomic-Server, then you can only write to the server by using Atomic Commits, which are in fact transactions. +Yes, if you use Atomic-Server, then you can only write to the server by using Atomic Commits, which are in fact transactions. This means that if part of the transaction fails, it is reverted - transactions are only applied when they are 100% OK. This prevents inconsistent DB states. diff --git a/src/interoperability/upgrade.md b/src/interoperability/upgrade.md index 75851e0..37846f4 100644 --- a/src/interoperability/upgrade.md +++ b/src/interoperability/upgrade.md @@ -5,7 +5,7 @@ The only thing that matters, is how you make the data accessible to others: the You can keep your existing software and logic, but simply change the last little part of your API. In short, this is what you'll have to do: -- Map all properties of resources to Atomic Properties. Either use existing ones, or create new ones and make them accessible (using any Atomic Server, as long as the URLs of the properties resolve). +- Map all properties of resources to Atomic Properties. Either use [existing ones](https://atomicdata.dev/properties), or [create new ones](https://atomicdata.dev/app/new?classSubject=https%3A%2F%2Fatomicdata.dev%2Fclasses%2FProperty&parent=https%3A%2F%2Fatomicdata.dev%2Fagents%2F8S2U%2FviqkaAQVzUisaolrpX6hx%2FG%2FL3e2MTjWA83Rxk%3D&newSubject=https%3A%2F%2Fatomicdata.dev%2Fproperty%2Fsu98ox6tvkh) and make them accessible (using any Atomic Server, as long as the URLs of the properties resolve). - Make sure that when the user requests some URL, that you return that resource as a JSON-AD object (at the very least if the user requests it using an HTTP `Accept` header). Don't feel obliged to implement all parts of the Atomic Data spec, such as Collections and Commits. diff --git a/src/interoperability/vc-old.md b/src/interoperability/vc-old.md new file mode 100644 index 0000000..f06f0e4 --- /dev/null +++ b/src/interoperability/vc-old.md @@ -0,0 +1,32 @@ +# Atomic Data and Verifiable Credentials + +Verifiable Credentials are pieces of information that have cryptographic proof by some reliable third party. +For example, you could have a credential that proves your degree, signed by your education. +These credentials an enable privacy-friendly transactions where a credential owner can prove being part of some group, without needing to actually identify themselves. +For example, you could prove that you're over 18 by showing a credential issued by your government, without actually having to show your ID card with your birthdate. +Verifiable Credentials are still not that widely used, but various projects exists that have had moderate success in implementing it. + +In Atomic Data, _all information created with Atomic Commits is verifiable_. +Atomic Commits are signed by specific individuals, and these signatures can be verified with the Public Key from the Agent who signed the Commit. + +## W3C Verifiable Credentials spec + +The W3C Verifiable Credentials (W3CVC) specification has helped to create a spec to describe credentials. +However, the approach is fundamentally different from how Atomic Data works. +In the W3CVC spec, every credential is a resource. +In Atomic Data, having a new type of `Credential` class that maps to W3CVC Credentials is definitely possible, but it is also highly redundant, as Commits already provide the same information. +That's why we've opted for only signing Commits. + +In Atomic Commits, the _change in information_ is signed, instead of the _state_ of the data. +This is by design, as storing signed state changes allows for fully verifiable and reversible history / version control with audit logs. + +## Verifying data with Atomic Commits + +If you want to know whether a specific value that you see is signed by a specific Agent, you need to find the Commit that created the value. + +This can be achieved by performing a TPF query or using a Collection. +The easiest way to do this, is by using the [`/all-versions` Endpoint](https://atomicdata.dev/all-versions) and finding the Signer of the version that is relevant to your question. + +In the near future, we will introduce a `/verify` Endpoint that will allow you to verify a specific value. + +Visit the [issue on github](https://github.com/ontola/atomic-data-docs/issues/22) to join the discussion about this subject. From c91e0adf10edb1e033eb378f05a3d74d984167b6 Mon Sep 17 00:00:00 2001 From: Joep Meindertsma Date: Thu, 9 Sep 2021 09:41:52 +0200 Subject: [PATCH 018/140] Semantic web and minor things --- src/atomic-data-overview.md | 4 ++-- src/core/concepts.md | 19 +++++++++++-------- src/schema/intro.md | 14 ++++++++------ src/usecases/semantic-web.md | 19 +++++++++++++------ 4 files changed, 34 insertions(+), 22 deletions(-) diff --git a/src/atomic-data-overview.md b/src/atomic-data-overview.md index 594fd00..0c03178 100644 --- a/src/atomic-data-overview.md +++ b/src/atomic-data-overview.md @@ -1,6 +1,6 @@ ![# Atomic Data Docs - Overview](assets/atomic_data_logo_stroke.svg) -Atomic Data is a specification for sharing, modifying and modeling graph data. +Atomic Data is a modular specification for sharing, modifying and modeling graph data. It uses links to connect pieces of data, and therefore makes it easier to connect datasets to each other - even when these datasets exist on separate machines. @@ -8,7 +8,7 @@ Atomic Data is especially suitable for knowledge graphs, distributed datasets, s It is designed to be highly extensible, easy to use, and to make the process of domain specific standardization as simple as possible. Atomic Data is [Linked Data](https://ontola.io/what-is-linked-data/), as it is a [strict subset of RDF](interoperability/rdf.md). -It is typed (you know if something is a `string`, `number`, `date`, `URL`, etc.) and extensible through [Atomic Schema](schema/intro.md), which means that you can define your own Classes, Properties and Datatypes. +It is type-safe (you know if something is a `string`, `number`, `date`, `URL`, etc.) and extensible through [Atomic Schema](schema/intro.md), which means that you can define your own Classes, Properties and Datatypes. The default serialization format for Atomic Data is [JSON-AD](core/json-ad.md), which is simply JSON where each key is a URL of an Atomic Property. These Properties are responsible for setting the `datatype` (to ensure type-safety) and setting `shortnames` (which help to keep names short, for example in JSON serialization) and `descriptions` (which provide semantic explanations of what a property should be used for). diff --git a/src/core/concepts.md b/src/core/concepts.md index 7b690c1..fc6ca7f 100644 --- a/src/core/concepts.md +++ b/src/core/concepts.md @@ -1,9 +1,12 @@ # What is Atomic Data? -## Atomic Data +## Atomic Data Core -Atomic Data is a data model for sharing information on the web. -It can be used to express any type of information, including personal data, vocabularies, metadata, documents, files and more. +Atomic Data is a modular specification for sharing information on the web. +Since Atomic Data is a _modular_ specification, you can mostly take what you want to use, and ignore the rest. +The _Core_ part, however,is the only _required_ part of the specification, as all others depend on it. + +Atomic Data Core can be used to express any type of information, including personal data, vocabularies, metadata, documents, files and more. It's designed to be easily serializable to both JSON and linked data formats. It is _typed_ data model, which means that every value should be validated and predictable. @@ -15,15 +18,15 @@ It is a directed, labeled graph, similar to RDF, so contrary to some other (labe * **Semantic**: Every data Atom and relation has a clear semantic meaning. * **Interoperable**: Plays nice with other data formats (e.g. JSON, XML, and all RDF formats). * **Open**: Free to use, open source, no strings attached. -* **Clear Ownership**: The data shows who is in control of the data, so new versions of the data can easily be retrieved. +* **Clear Ownership**: The data shows who (or which domain) is in control of the data, so new versions of the data can easily be retrieved. * **Mergeable**: Any two sets of Atoms can be merged into a single graph without any merge conflicts / name collisions. * **Extensible**: Anyone can define their own data types and create Atoms with it. -* **ORM-friendly**: Navigate a _decentralized_ graph by using dot.syntax, similar to how you navigate a JSON object in javascript. -* **Typed**: All valid Atomic data has an unambiguous, static datatype. Models expressed in Atomic Data can be mapped to programming language models, such as `structs` or `interfaces` in Typescript / Rust / Go. +* **ORM-friendly**: Navigate a _decentralized_ graph by using `dot.syntax`, similar to how you navigate a JSON object in javascript. +* **Type-safe**: All valid Atomic data has an unambiguous, static datatype. ## Resource -A Resource is a bunch of information about a thing, referenced by a single link (the Subject). +A _Resource_ is a bunch of information about a thing, referenced by a single link (the Subject). Formally, it is a set of Atoms (i.e. a Graph) that share a Subject URL. You can think of a Resource as a single row in a spreadsheet or database. In practice, Resources can be anything - a Person, a Blogpost, a Todo item. @@ -32,7 +35,7 @@ A Property can only occur once in every Resource. ## Atom (or Atomic Triple) -Every Resource is composed of Atoms. +Every Resource is composed of _Atoms_. The Atom is the smallest possible piece of _meaningful_ data / information (hence the name). You can think of an Atom as a single cell in a spreadsheet or database. An Atom consists of three fields: diff --git a/src/schema/intro.md b/src/schema/intro.md index c664e7c..ecc5a30 100644 --- a/src/schema/intro.md +++ b/src/schema/intro.md @@ -1,8 +1,9 @@ # Atomic Schema Atomic Schema is the proposed standard for specifying classes, properties and datatypes in Atomic Data. -You can compare it to what XSD is for XML. -Atomic Schema deals with validating and constraining the shape of data - it checks if all required properties are present, and whether the values conform to the datatype requirements (e.g. `datetime`, or `URL`). +You can compare it to UML diagrams, or what XSD is for XML. +Atomic Schema deals with validating and constraining the shape of data. +It is designed for checking if all the required properties are present, and whether the values conform to the datatype requirements (e.g. `datetime`, or `URL`). This section will define various Classes, Properties and Datatypes (discussed in [Atomic Core: Concepts](../core/concepts.md)). @@ -20,12 +21,13 @@ This section will define various Classes, Properties and Datatypes (discussed in In short, Atomic Schema works like this: -The Property _field_ in an Atom links to a **Property _Resource_**. It is important that the URL to the Property Resource resolves. +The Property _field_ in an Atom, or the _key_ in a JSON-AD object, links to a **Property _Resource_**. +It is important that the URL to the Property Resource resolves, as others can re-use it and check its datatype. This Property does three things: -1. it tells something about its semantic meaning, and links to a Datatype. -1. it links to a Datatype or Class, which indicates which Value is acceptable. -1. it provides a Shortname, which is used for ORM. +1. it links to a **Datatype** which indicates which Value is acceptable. +1. it has a **description** which tells you what the property means, what the relationship between the Subject and the Value means. +1. it provides a **Shortname**, which is sometimes used as an alternative to the full URL of the Property. **DataTypes** define the shape of the Value, e.g. a Number (`124`) or Boolean (`true`). diff --git a/src/usecases/semantic-web.md b/src/usecases/semantic-web.md index 3d5847c..2c97a2e 100644 --- a/src/usecases/semantic-web.md +++ b/src/usecases/semantic-web.md @@ -2,18 +2,23 @@ The term 'Semantic Web' was popularized in [a paper of the same name](https://www-sop.inria.fr/acacia/cours/essi2006/Scientific%20American_%20Feature%20Article_%20The%20Semantic%20Web_%20May%202001.pdf) published in 2001 by three people, including the inventor of the World Wide Web: Tim Berners-Lee. In this paper, a vision was shared for how a higher degree of standardization on the internet could lead to a bunch of interesting innovations. -For example, it describes how an appointment for a hairdresser is scheduled automatically, by using information about the preferences and location of the individual. +For example, it describes how an appointment for a doctor is scheduled automatically by a "semantic agent", by checking the location of the person, comparing that to doctors in the area, getting reviews and checking the availability in the calendar. By making the web machine-readable, we could get far more interoperability and therefore new applications that make our lives easier. All of this would have been made possible by using linked data. -It has been 20 years since this paper, and it is indeed easier then ever to make an appointment with a hairdresser. -However, this problem and many similar ones have not been solved by the semantic web: they have been solved by big companies that know everything about us, and have monopolized so much of the data on the internet. -Tech giants like Google and Microsoft have created ecosystems that integrate many types of (free) services, have huge databases containing all kinds of related stuff, and as a result, provide us with nice experiences. +It has been 20 years since this paper, and it is indeed easier then ever to make an appointment with a professional. +If can yell "hairdresser" at my phone, and I instantly see the nearest one with a high rating with a 'book now' button that checks our calendars. +So... we made it? +Unfortunately, this problem and many similar ones have not been solved by the semantic web: they have been solved by big companies that know everything about us, and have monopolized so much of the data on the internet. +Tech giants like Google and Microsoft have created ecosystems that integrate many types of (free) services, have huge databases containing all kinds of related stuff, and as a result, provide us with nice user experiences. A high degree of _centralization_, instead of _standardization_, turned out to be a sufficient solution, too. But of course, this centralized approach comes at a serious cost. -First problem is we get _vendor lock-in_, which means that it becomes harder to switch from service to service. +The first problem is we get _vendor lock-in_, which means that it becomes harder to switch from service to service. We can't take our data from Whatsapp and take it to Telegram, for example, or our Twitter feed to Mastadon. -The second problem is that +The second problem is that our usage goals do not align with the tech giants. +We might want to see a list of recent activity from our friends when we open facebook, but facebook's investors might want us to simply look at as much ads as possible. + +But of course, the internet isn't just tech giants - there are a lot of enthousiasts that really want to see the decentralized, semantic web succeed. The Semantic Web wasn't just an idea and a paper - there were a lot of standards involved, all of which were properly defined and managed by the W3C, the organization that standardizes so much of our web. But the adoption of most of these standards is pretty low, unfortunately. @@ -33,6 +38,8 @@ The Semantic Web community actually built ### A lack of proper RDF tools +A lack + ### No business incentive to make data highly accessible If you're a software company that builds a product, you probably want people to keep using your product. From cd7530b4825abd55c329ad2dd10da7dafdd1ebdb Mon Sep 17 00:00:00 2001 From: Joep Meindertsma Date: Fri, 10 Sep 2021 12:10:27 +0200 Subject: [PATCH 019/140] ipfs --- src/interoperability/ipfs.md | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/src/interoperability/ipfs.md b/src/interoperability/ipfs.md index 0e165e7..bd29a34 100644 --- a/src/interoperability/ipfs.md +++ b/src/interoperability/ipfs.md @@ -7,20 +7,30 @@ Instead of using an HTTP URL like `http://example.com/helloworld`, it uses the I IPFS identifies things based on their unique content hash (the long, seemingly random string) using a thing called a Merkle DAG ([this great article](https://medium.com/textileio/whats-really-happening-when-you-add-a-file-to-ipfs-ae3b8b5e4b0f#:~:text=In%20practice%2C%20content%20addressing%20systems,function%2C%20to%20produce%20a%20digest.&text=From%20raw%20image%20to%20cryptographic%20digest%20to%20content%20id%20(multihash).) explains it nicely). This is called a [CID](https://github.com/multiformats/cid), or Content ID. This simple idea (plus some not so simple network protocols) allows for decentralized, temper-proof storage of data. -This fixes some issues with HTTP that are related to its centralized philosophy: no more 404s! +This fixes some issues with HTTP that are related to its centralized philosophy: **no more 404s**! -## Why is IPFS especially interesting for Atomic Data +## Why is IPFS interesting for Atomic Data Atomic Data is highly dependent on the availability of Resources, especially Properties and Datatypes. -These resources are meant to be re-used a lot, and that would make everything expensive. +These resources are meant to be re-used a lot, and when these go offline or change (for whatever reason), it could cause issues and confusion. +IPFS guarantees that these resources are entirely static, which means that they cannot change. +This is useful when dealing with Properties, as a change in datatype could break things. +IPFS also allows for location-independent fetching, which means that resources can be retrieved from any location, as long as it's online. +This Peer-to-peer functionality is a very fundamental advantage of IPFS over HTTP, especially when the resources are very likely to be re-use, which is _especially_ the case for Atomic Data Properties. ## Considerations using IPFS URLs -They are static, their contents can never change. -This is great for some types of data, but horrible for others. -If you're describing a time-dependent thing (such as a person's job), -If you're describing personal, private information, its also a bad idea to use IPFS, because it's designed to be permanent. -Also, IPFS is not as fast as HTTP - at least for now. +IPFS URLs are **static**, which means that their contents can never change. +This is great for some types of data, but not so much for others. +If you're describing a time-dependent thing (such as a person's job), you'll probably want to know what the _current_ value is, and that is not possible when you only have an IPFS identifier. +This can be fixed by including an HTTP URL in IPFS bodies. + +IPFS data is also **hard to remove**, as it tends to be replicated across machines. +If you're describing personal, private information, it can therefore be a bad idea to use IPFS. + +And finally, its **performance** is typically not as good as HTTP. +If you know the IPFS gateway that hosts the IPFS resource that you're looking for, things improve drastically. +Luckily for Atomic Data, this is often the case, as we know the HTTP url of the server and could try whether that server has an IPFS gateway. ## Atomic Data and IPLD From 568f524f345852d581007794564e7b09af620453 Mon Sep 17 00:00:00 2001 From: Joep Meindertsma Date: Wed, 22 Sep 2021 10:16:58 +0200 Subject: [PATCH 020/140] Add newsletter --- src/SUMMARY.md | 1 + src/acknowledgements.md | 16 ++++++++++++++ src/atomic-data-overview.md | 1 - src/commits/compare.md | 5 +++++ src/core/json-ad.md | 2 +- src/get-involved.md | 15 ------------- src/newsletter.md | 42 +++++++++++++++++++++++++++++++++++++ 7 files changed, 65 insertions(+), 17 deletions(-) create mode 100644 src/acknowledgements.md create mode 100644 src/newsletter.md diff --git a/src/SUMMARY.md b/src/SUMMARY.md index f61c02f..373aae2 100644 --- a/src/SUMMARY.md +++ b/src/SUMMARY.md @@ -39,4 +39,5 @@ ----------- +[Newsletter](newsletter.md) [Get involved](get-involved.md) diff --git a/src/acknowledgements.md b/src/acknowledgements.md new file mode 100644 index 0000000..b2905db --- /dev/null +++ b/src/acknowledgements.md @@ -0,0 +1,16 @@ +# Acknowledgements + +## Authors: + +- **Joep Meindertsma** ([joepio](https://github.com/joepio/) from [Ontola.io](https://ontola.io/)) + +## Special thanks to: + +- **Thom van Kalkeren** (my colleague, friend and programming mentor who came up with many great ideas on how to work with RDF, such as [HexTuples](https://github.com/ontola/hextuples) and [linked-delta](https://github.com/ontola/linked-delta)) +- **Tim Berners-Lee** (for everything he did for linked data and the web) +- **Ruben Verborgh** (for doing great work with RDF, such as the TPF spec) +- **Pat McBennett** (for lots of valuable feedback on initial Atomic Data docs) +- **Manu Sporny** (for his work on JSON-LD, which was an important inspiration for JSON-AD) +- **Jonas Smedegaard** (for the various interesting talks we had and the feedback he provided) +- **Arthur Dingemans** (for sharing his thoughts, providing feedback and his valuable suggestions) +- All the other people who contributed to linked data related standards diff --git a/src/atomic-data-overview.md b/src/atomic-data-overview.md index 0c03178..5e9d02e 100644 --- a/src/atomic-data-overview.md +++ b/src/atomic-data-overview.md @@ -34,7 +34,6 @@ If you'd rather play and discover for yourself, play with the existing open sour - Use the Rust library: [atomic-lib](https://github.com/joepio/atomic) Make sure to [join our Discord](https://discord.gg/a72Rv2P) if you'd like to discuss Atomic Data with others. -Keep in mind that none of the Atomic Data project has reached a v1, which means that breaking changes can happen. ## Status diff --git a/src/commits/compare.md b/src/commits/compare.md index 0bf4f64..44f6cb3 100644 --- a/src/commits/compare.md +++ b/src/commits/compare.md @@ -243,6 +243,11 @@ The result It uses the [JSON-Pointer spec](http://tools.ietf.org/html/rfc6901) for denoting `path`s. It has quite a bunch of implementations, in various languages. + ## Atomic Commits diff --git a/src/core/json-ad.md b/src/core/json-ad.md index d3e5290..d54d606 100644 --- a/src/core/json-ad.md +++ b/src/core/json-ad.md @@ -51,4 +51,4 @@ The last two steps of this process is more formally defined by the JSON Canonica ## Interoperability with JSON and JSON-LD -[Read more about this subject](../interoperability/json). +[Read more about this subject](../interoperability/json.md). diff --git a/src/get-involved.md b/src/get-involved.md index 5a3ce10..05ac2c2 100644 --- a/src/get-involved.md +++ b/src/get-involved.md @@ -9,18 +9,3 @@ Things you can do: - Clone the [Book Repo](https://github.com/ontola/atomic-data/) and read some of the inline comments, which might help start some discussions - Drop an [issue on Github](https://github.com/ontola/atomic-data/issues) to share your suggestions or criticism - Join our [W3C Community Group](https://www.w3.org/community/atomic-data/) - -## Authors: - -- Joep Meindertsma ([joepio](https://github.com/joepio/) from [Ontola.io](https://ontola.io/)) - -## Special thanks to: - -- **Thom van Kalkeren** (my colleague, friend and programming mentor who came up with many great ideas on how to work with RDF, such as [HexTuples](https://github.com/ontola/hextuples) and [linked-delta](https://github.com/ontola/linked-delta)) -- **Tim Berners-Lee** (for everything he did for linked data and the web) -- **Ruben Verborgh** (for doing great work with RDF, such as the TPF spec) -- **Pat McBennett** (for lots of valuable feedback on initial Atomic Data docs) -- **Manu Sporny** (for his work on JSON-LD, which was an important inspiration for JSON-AD) -- **Jonas Smedegaard** (for the various interesting talks we had and the feedback he provided) -- **Arthur Dingemans** (for sharing his thoughts, providing feedback and his valuable suggestions) -- All the other people who contributed to linked data related standards diff --git a/src/newsletter.md b/src/newsletter.md new file mode 100644 index 0000000..10061bb --- /dev/null +++ b/src/newsletter.md @@ -0,0 +1,42 @@ +# Subscribe to the Atomic Data newsletter + +We'll send you an update (max once per month) when there's something relevant to share, such as + +- Major changes to the specification +- Major new releases (with new features) +- Use-cases, implementations +- Tutorials, blog posts +- Organizational / funding news + + + + +
+
+
+ +
* indicates required
+
+ + +
+
+ + +
+
+ + +
+ +
+
+
+
+ + From 874cb2c800fa4256fb715eb8fdaa0f846afc98fd Mon Sep 17 00:00:00 2001 From: Joep Meindertsma Date: Wed, 22 Sep 2021 15:27:59 +0200 Subject: [PATCH 021/140] Fix newsletter --- src/newsletter.md | 33 +-------------------------------- 1 file changed, 1 insertion(+), 32 deletions(-) diff --git a/src/newsletter.md b/src/newsletter.md index 10061bb..331100d 100644 --- a/src/newsletter.md +++ b/src/newsletter.md @@ -8,35 +8,4 @@ We'll send you an update (max once per month) when there's something relevant to - Tutorials, blog posts - Organizational / funding news - - - -
-
-
- -
* indicates required
-
- - -
-
- - -
-
- - -
- -
-
-
-
- - +[Click here to sign up to the Atomic Data Newsletter](http://eepurl.com/hHcRA1) From 2600d9c6c49e44a68fdb8713b0ce51bc492b4f93 Mon Sep 17 00:00:00 2001 From: Joep Meindertsma Date: Sat, 25 Sep 2021 11:45:30 +0200 Subject: [PATCH 022/140] #65 mention query params in commits --- src/commits/concepts.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/commits/concepts.md b/src/commits/concepts.md index 75de9aa..b369db4 100644 --- a/src/commits/concepts.md +++ b/src/commits/concepts.md @@ -10,7 +10,7 @@ It is cryptographically signed by an [Agent](https://atomicdata.dev/classes/Agen The **required fields** are: -- `subject` - The thing being changed. A Resource Subject URL that the Commit is providing information about. +- `subject` - The thing being changed. A Resource Subject URL (HTTP identifier) that the Commit is changing about. A Commit Subject must not contain query parameters, as these are reserved for dynamic resources. - `signer` - Who's making the change. The Atomic URL of the Author's profile - which in turn must contain a `publicKey`. - `signature` - Cryptographic proof of the change. A hash of the JSON-AD serialized Commit (without the `signature` field), signed by the Agent's `private-key`. This proves that the author is indeed the one who created this exact commit. The signature of the Commit is also used as the identifier of the commit. - `created-at` - When the change was made. A UNIX timestamp number of when the commit was created. From 09f18d099236e7b082b5f717a0dfb0a3d62233c0 Mon Sep 17 00:00:00 2001 From: Joep Meindertsma Date: Sun, 3 Oct 2021 20:19:55 +0200 Subject: [PATCH 023/140] Nested resources and endpoints --- src/core/concepts.md | 3 ++- src/core/json-ad.md | 14 ++++++++++++-- src/endpoints.md | 7 +++++++ src/schema/collections.md | 1 + 4 files changed, 22 insertions(+), 3 deletions(-) diff --git a/src/core/concepts.md b/src/core/concepts.md index fc6ca7f..5e0c18a 100644 --- a/src/core/concepts.md +++ b/src/core/concepts.md @@ -153,6 +153,7 @@ In the following JSON-AD example, the `address` is a nested resource: } ``` -A Nested Resource often does not have its own subject (`@id`), but it _does_ have its own unique [path](./paths.md), which can be used as its identifier. +Nested Resources can be _named_ or _anonymous_. An _Anonymous Nested Resource_ does not have it's own `@id` field. +It _does_ have its own unique [path](./paths.md), which can be used as its identifier. In the next chapter, we'll explore how Atomic Data is serialized. diff --git a/src/core/json-ad.md b/src/core/json-ad.md index d54d606..6a34d13 100644 --- a/src/core/json-ad.md +++ b/src/core/json-ad.md @@ -14,8 +14,8 @@ It uses [JSON](https://www.ecma-international.org/publications-and-standards/sta - Numbers can be [Integers](https://atomicdata.dev/datatypes/integer), [Timestamps](https://atomicdata.dev/datatypes/timestamp) or [Floats](https://atomicdata.dev/datatypes/float). - JSON booleans map to [Booleans](https://atomicdata.dev/datatypes/boolean). - JSON strings can be many datatypes, including [String](https://atomicdata.dev/datatypes/string), [Markdown](https://atomicdata.dev/datatypes/markdown), [Date](https://atomicdata.dev/datatypes/date) or other. -- Nested JSON Objects are Nested Resources. A Nested Resource can either be anonymous (without an `@id` subject) or a regular Nested Resource with an `@id` subject. -- The root data structure must either be a regular Resource, or an Array. When you want to describe multiple Resources in one JSON-AD document, use an array as the root item. +- Nested JSON Objects are Nested Resources. A Nested Resource can either be _Anonymous_ (without an `@id` subject) or a Named Nested Resource with an `@id` subject. Everywhere a Subject URL can be used as a value (i.e. all properties with the datatype [atomicURL](https://atomicdata.dev/datatypes/atomicURL)), a Nested Resource can be used instead. This also means that an item in an `ResourceArray` can be a Nested Resource. +- The root data structure must either be a Named Resource (with an `@id`), or an Array containing Named Resources. When you want to describe multiple Resources in one JSON-AD document, use an array as the root item. Let's look at an example JSON-AD Resource: @@ -33,6 +33,16 @@ Let's look at an example JSON-AD Resource: The mime type (for HTTP content negotiation) is `application/ad+json` ([registration ongoing](https://github.com/ontola/atomic-data-docs/issues/60)). +## Nested, Anonymous and Named resources + +In JSON-AD, a Resource can be respresented in multiple ways: + +- **Subject**: A URL string, such as `https://atomicdata.dev/classes/Class`. +- **Named Resource**: A JSON Object with an `@id` field containing the Subject. +- **Anonymous Nested Resource** A JSON Object without an `@id` field. This is only possible if it is a Nested Resource, which means that it has a parent Resource. + +Note that this is also valid for `ResourceArrays`, which usually only contain Subjects, but are allowed to contain Nested Resources. + ## JSON-AD Parsers, serializers and other libraries - **Typescript / Javacript**: [@tomic/lib](https://www.npmjs.com/package/@tomic/lib) JSON-AD parser + in-memory store. Works with [@tomic/react](https://www.npmjs.com/package/@tomic/lib) for rendering Atomic Data in React. diff --git a/src/endpoints.md b/src/endpoints.md index fad3082..e8bbce8 100644 --- a/src/endpoints.md +++ b/src/endpoints.md @@ -12,6 +12,13 @@ The most important property in an Endpoint is [`parameters`](https://atomicdata. You can find a list of Endpoints supported by Atomic-Server on [atomicdata.dev/endpoints](https://atomicdata.dev/endpoints). +Endpoint Resources are _dynamic_, because their properties could be calculated server-side. +When a Property tends to be calculated server-side, they will have a [`isDynamic` property](https://atomicdata.dev/properties/isDynamic) set to `true`, which tells the client that it's probably useless to try to overwrite it. + +A Server can also send a partial Resource for an Endpoint to the client, which means that some properties may be missing. +When this is the case, the Resource will have an [`incomplete`](https://atomicdata.dev/properties/incomplete) property set to `true`. +This tells the client that it has to individually fetch the resource from the server to get the full body. + ## Design Goals - **Familiar API**: should look like something that most developers already know diff --git a/src/schema/collections.md b/src/schema/collections.md index 705bc9c..fc796d1 100644 --- a/src/schema/collections.md +++ b/src/schema/collections.md @@ -9,6 +9,7 @@ For dealing with these problems, we have Atomic Collections. An Atomic Collection is a Resource that links to a set of resources. Note that Collections are designed to be _dynamic resources_, often (partially) generated at runtime. +Collections are [Endpoints](../endpoints.md), which means that part of their properties are calculated server-side. Collections have various filters (`subject`, `property`, `value`) that can help to build a useful query. - `members`: How many items (members) are visible per page. From c022588b2d592df355a938a99eb606cc099eb622 Mon Sep 17 00:00:00 2001 From: Joep Meindertsma Date: Tue, 5 Oct 2021 15:50:17 +0200 Subject: [PATCH 024/140] Typo --- src/core/concepts.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/concepts.md b/src/core/concepts.md index 5e0c18a..7b6e9a6 100644 --- a/src/core/concepts.md +++ b/src/core/concepts.md @@ -4,7 +4,7 @@ Atomic Data is a modular specification for sharing information on the web. Since Atomic Data is a _modular_ specification, you can mostly take what you want to use, and ignore the rest. -The _Core_ part, however,is the only _required_ part of the specification, as all others depend on it. +The _Core_ part, however, is the _only required_ part of the specification, as all others depend on it. Atomic Data Core can be used to express any type of information, including personal data, vocabularies, metadata, documents, files and more. It's designed to be easily serializable to both JSON and linked data formats. From f2ff8daa2c6a4bea575a618453825b5aebfcfc7d Mon Sep 17 00:00:00 2001 From: Joep Meindertsma Date: Tue, 5 Oct 2021 15:50:47 +0200 Subject: [PATCH 025/140] Add websockets --- src/SUMMARY.md | 1 + src/websockets.md | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+) create mode 100644 src/websockets.md diff --git a/src/SUMMARY.md b/src/SUMMARY.md index 373aae2..bc22554 100644 --- a/src/SUMMARY.md +++ b/src/SUMMARY.md @@ -21,6 +21,7 @@ * [Compared to](commits/compare.md) * [Hierarchy and authorization](hierarchy.md) * [Endpoints](endpoints.md) +* [WebSockets](websockets.md) * [Interoperability](interoperability/intro.md) * [RDF](interoperability/rdf.md) * [Solid](interoperability/solid.md) diff --git a/src/websockets.md b/src/websockets.md new file mode 100644 index 0000000..d5334c2 --- /dev/null +++ b/src/websockets.md @@ -0,0 +1,18 @@ +# WebSockets in Atomic Data + +WebSockets are a very fast and efficient way to have a client and server communicate in an asynchronous fashion. +They are used in Atomic Data to allow real-time updates, which makes it possible to create things like collaborative applications and multiplayer games. +These have been implemented in `atomic-server` and `atomic-data-browser` (powered by `@tomic/lib`). + +## Initializing a WebSocket connection + +Send an HTTP `GET` request to the `/ws` endpoint of an `atomic-server`. The Server should update that request to a secure WebSocket (`wss`) connection. + +## Client to server messages + +- `SUBSCRIBE ${subject}` tells the Server that you'd like to receive Commits about this Subject. +- `UNSUBSCRIBE ${subject}` tells the Server that you'd like to stop receiving Commits about this Subject. + +## Server to client messages + +- `COMMIT ${CommitBody}` an entire Commit for a resource that you're subscribed to From 28167b0778cd7c6ad72e8a199d3d0e9b7960f18f Mon Sep 17 00:00:00 2001 From: Joep Meindertsma Date: Sat, 9 Oct 2021 12:35:19 +0200 Subject: [PATCH 026/140] Update README.md --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index 6193ce2..4665284 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,12 @@ If you're looking for **implementations of Atomic Data**, check out [atomic-data ## About this repo This repository holds the markdown book for the Atomic Data standard. +It serves two purposes: + +- A central hub for all Atomic Data documentation, tutorials, guides and other written content +- A place to discuss the **specification** - that should happen in this issue tracker. + +## Running locally You can run it locally using [mdBook](https://github.com/rust-lang/mdBook) From 9afe374b99aa78290554bb222376d3bcb1a08d4a Mon Sep 17 00:00:00 2001 From: Joep Meindertsma Date: Sat, 9 Oct 2021 12:36:30 +0200 Subject: [PATCH 027/140] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 4665284..2d11004 100644 --- a/README.md +++ b/README.md @@ -12,8 +12,8 @@ If you're looking for **implementations of Atomic Data**, check out [atomic-data This repository holds the markdown book for the Atomic Data standard. It serves two purposes: -- A central hub for all Atomic Data documentation, tutorials, guides and other written content -- A place to discuss the **specification** - that should happen in this issue tracker. +- A central hub for **written content** such as documentation, the specification, tutorials and guides. +- A place to **discuss the specification** - that should happen in this issue tracker. ## Running locally From 0203dcd25a2571ed1892281ca865170dc56d1244 Mon Sep 17 00:00:00 2001 From: Joep Meindertsma Date: Thu, 28 Oct 2021 16:21:51 +0200 Subject: [PATCH 028/140] #71 add titles, fix acknowledgements --- src/SUMMARY.md | 2 +- src/agents.md | 1 + src/commits/compare.md | 1 + src/commits/concepts.md | 1 + src/commits/intro.md | 1 + src/core/json-ad.md | 1 + src/core/paths.md | 1 + src/core/querying.md | 1 + src/core/serialization.md | 1 + src/endpoints.md | 1 + src/get-involved.md | 1 + src/hierarchy.md | 1 + src/interoperability/graph-database.md | 1 + src/interoperability/intro.md | 1 + src/interoperability/ipfs.md | 1 + src/interoperability/json.md | 1 + src/interoperability/rdf.md | 1 + src/interoperability/solid.md | 1 + src/interoperability/sql.md | 1 + src/interoperability/upgrade.md | 1 + src/interoperability/verifiable-credentials.md | 2 ++ src/invitations.md | 1 + src/motivation.md | 1 + src/newsletter.md | 1 + src/schema/classes.md | 1 + src/schema/collections.md | 1 + src/schema/datatypes.md | 1 + src/schema/faq.md | 1 + src/schema/intro.md | 1 + src/schema/translations.md | 1 + src/tooling.md | 3 ++- src/usecases/e-commerce.md | 1 + src/usecases/intro.md | 3 ++- src/usecases/personal-data-store.md | 1 + src/usecases/surveys.md | 1 + src/usecases/verifiable-credentials.md | 1 + src/websockets.md | 1 + src/when-to-use.md | 1 + 38 files changed, 41 insertions(+), 3 deletions(-) create mode 100644 src/interoperability/verifiable-credentials.md diff --git a/src/SUMMARY.md b/src/SUMMARY.md index bc22554..91daca6 100644 --- a/src/SUMMARY.md +++ b/src/SUMMARY.md @@ -29,7 +29,6 @@ * [IPFS](interoperability/ipfs.md) * [SQL](interoperability/sql.md) * [Graph Databases](interoperability/graph-database.md) - * [Verifiable Credentials](interoperability/verifiable-credentials.md) * [Your existing project](interoperability/upgrade.md) * [Potential use cases](usecases/intro.md) * [Personal Data Store](usecases/personal-data-store.md) @@ -40,5 +39,6 @@ ----------- +[Acknowledgements](acknowledgements.md) [Newsletter](newsletter.md) [Get involved](get-involved.md) diff --git a/src/agents.md b/src/agents.md index a1b9d0a..4b76e9e 100644 --- a/src/agents.md +++ b/src/agents.md @@ -1,3 +1,4 @@ +{{#title Atomic Data Agents - Users and identities }} # Atomic Agents Atomic Agents are used for _authentication_: to set an identity and prove who an actor actually is. diff --git a/src/commits/compare.md b/src/commits/compare.md index 44f6cb3..1a986c7 100644 --- a/src/commits/compare.md +++ b/src/commits/compare.md @@ -1,3 +1,4 @@ +{{#title Atomic Commits compared to other (RDF) delta models}} # Atomic Commits compared to other (RDF) delta models Let's compare the [Atomic Commit](concepts.md) approach with some existing protocols for communicating state changes / patches / mutations / deltas in linked data, JSON and text files. diff --git a/src/commits/concepts.md b/src/commits/concepts.md index b369db4..7f2c553 100644 --- a/src/commits/concepts.md +++ b/src/commits/concepts.md @@ -1,3 +1,4 @@ +{{#title Atomic Commits: Concepts}} # Atomic Commits: Concepts ## Commit diff --git a/src/commits/intro.md b/src/commits/intro.md index 1f6fd46..bf55f55 100644 --- a/src/commits/intro.md +++ b/src/commits/intro.md @@ -1,3 +1,4 @@ +{{#title Atomic Commits - Event standard for Atomic Data}} # Atomic Commits _Disclaimer: Work in progress, prone to change._ diff --git a/src/core/json-ad.md b/src/core/json-ad.md index 6a34d13..31ce1db 100644 --- a/src/core/json-ad.md +++ b/src/core/json-ad.md @@ -1,3 +1,4 @@ +{{#title JSON-AD: The Atomic Data serialization format}} # JSON-AD: The Atomic Data serialization format Although you an use various serialization formats for Atomic Data, `JSON-AD` is the _default_ serialization format. diff --git a/src/core/paths.md b/src/core/paths.md index 1f82f05..2836153 100644 --- a/src/core/paths.md +++ b/src/core/paths.md @@ -1,3 +1,4 @@ +{{#title Atomic Data Paths}} # Atomic Paths An Atomic Path is a string that consists of at least one URL, followed by one or more URLs or Shortnames. diff --git a/src/core/querying.md b/src/core/querying.md index 417ec1c..aaa90ac 100644 --- a/src/core/querying.md +++ b/src/core/querying.md @@ -1,3 +1,4 @@ +{{#title Querying Atomic Data}} # Querying Atomic Data There are multiple ways of getting Atomic Data into some system: diff --git a/src/core/serialization.md b/src/core/serialization.md index 48a693a..1ae08a8 100644 --- a/src/core/serialization.md +++ b/src/core/serialization.md @@ -1,3 +1,4 @@ +{{#title Serialization of Atomic Data}} # Serialization of Atomic Data Atomic Data is not necessarily bound to a single serialization format. diff --git a/src/endpoints.md b/src/endpoints.md index e8bbce8..c47b536 100644 --- a/src/endpoints.md +++ b/src/endpoints.md @@ -1,3 +1,4 @@ +{{#title Atomic Data Endpoitns - specity how HTTP endpoints behave}} # Atomic Endpoints _URL: https://atomicdata.dev/classes/Endpoint_ diff --git a/src/get-involved.md b/src/get-involved.md index 05ac2c2..19efed4 100644 --- a/src/get-involved.md +++ b/src/get-involved.md @@ -1,3 +1,4 @@ +{{#title Atomic Data - Get Involved}} # Get involved Atomic Data is an open specification, and that means that you're very welcome to share your thoughts and help make this standard as good as possible. diff --git a/src/hierarchy.md b/src/hierarchy.md index c96c914..1c48004 100644 --- a/src/hierarchy.md +++ b/src/hierarchy.md @@ -1,3 +1,4 @@ +{{#title Atomic Data Hierarchy, rights and authorization }} # Hierarchy, rights and authorization Hierarchies help make information easier to find and understand. diff --git a/src/interoperability/graph-database.md b/src/interoperability/graph-database.md index d8e4e74..b93aaeb 100644 --- a/src/interoperability/graph-database.md +++ b/src/interoperability/graph-database.md @@ -1,3 +1,4 @@ +{{#title How does Atomic Data relate to Graph Databases?}} # Atomic Data and Graph Databases Atomic Data fundamentally is a _graph data model_. diff --git a/src/interoperability/intro.md b/src/interoperability/intro.md index 87c6e85..5591823 100644 --- a/src/interoperability/intro.md +++ b/src/interoperability/intro.md @@ -1,3 +1,4 @@ +{{#title Atomic Data Interoperability - Relation to other technology}} # Interoperability: Relation to other technology Atomic data is designed to be easy to use in existing projects, and be interoperable with existing formats. diff --git a/src/interoperability/ipfs.md b/src/interoperability/ipfs.md index bd29a34..9608106 100644 --- a/src/interoperability/ipfs.md +++ b/src/interoperability/ipfs.md @@ -1,3 +1,4 @@ +{{#title How does Atomic Data relate to IPFS?}} # Atomic Data and IPFS ## What is IPFS diff --git a/src/interoperability/json.md b/src/interoperability/json.md index 9a333f0..9d7a95c 100644 --- a/src/interoperability/json.md +++ b/src/interoperability/json.md @@ -1,3 +1,4 @@ +{{#title How does Atomic Data relate to JSON?}} # How does Atomic Data relate to JSON? Because JSON is so popular, Atomic Data is designed with JSON in mind. diff --git a/src/interoperability/rdf.md b/src/interoperability/rdf.md index 975f11c..5d330cd 100644 --- a/src/interoperability/rdf.md +++ b/src/interoperability/rdf.md @@ -1,3 +1,4 @@ +{{#title How does Atomic Data relate to RDF?}} # How does Atomic Data relate to RDF? RDF (the [Resource Description Framework](https://www.w3.org/TR/rdf-primer/)) is a W3C specification from 1999 that describes the original data model for linked data. diff --git a/src/interoperability/solid.md b/src/interoperability/solid.md index fca6cec..f169f5e 100644 --- a/src/interoperability/solid.md +++ b/src/interoperability/solid.md @@ -1,3 +1,4 @@ +{{#title How does Atomic Data relate to Solid?}} # Atomic Data and Solid The [Solid project](https://solidproject.org/) is an initiative by the inventor of linked data and the world wide web: sir Tim Berners-Lee. diff --git a/src/interoperability/sql.md b/src/interoperability/sql.md index 090ad79..3e7326f 100644 --- a/src/interoperability/sql.md +++ b/src/interoperability/sql.md @@ -1,3 +1,4 @@ +{{#title How does Atomic Data relate to SQL?}} # Atomic Data and SQL Atomic Data has some characteristics that make it similar and different from SQL. diff --git a/src/interoperability/upgrade.md b/src/interoperability/upgrade.md index 37846f4..4fdb76f 100644 --- a/src/interoperability/upgrade.md +++ b/src/interoperability/upgrade.md @@ -1,3 +1,4 @@ +{{#title Upgrade your existing application to Atomic Data}} # Upgrade your existing application to Atomic Data If you want to make your existing project compatible with Atomic Data, you probably don't have to get rid of your existing storage / DB implementation. diff --git a/src/interoperability/verifiable-credentials.md b/src/interoperability/verifiable-credentials.md new file mode 100644 index 0000000..751fb01 --- /dev/null +++ b/src/interoperability/verifiable-credentials.md @@ -0,0 +1,2 @@ +{{#title How does Atomic Data relate to Verifiable Crendentials?}} +# Verifiable Credentials diff --git a/src/invitations.md b/src/invitations.md index 7b6f487..7027ed2 100644 --- a/src/invitations.md +++ b/src/invitations.md @@ -1,3 +1,4 @@ +{{#title Atomic Data Invitations - Sharing using Tokens }} # Invitations & Tokens _Discussion: https://github.com/ontola/atomic-data/issues/23_ diff --git a/src/motivation.md b/src/motivation.md index 12a3e2e..0ad31ff 100644 --- a/src/motivation.md +++ b/src/motivation.md @@ -1,3 +1,4 @@ +{{#title Motivation for creating Atomic Data}} # Motivation: Why Atomic Data? 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

pK4d^Gi^9)L;F=Kz%BA7p((%{O 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£>{Cc{+0T@N4gDsXYa;?@rZSr@Ttro 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 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/atomic-data-overview.md b/src/atomic-data-overview.md index 5e9d02e..111c003 100644 --- a/src/atomic-data-overview.md +++ b/src/atomic-data-overview.md @@ -1,8 +1,10 @@ ![# Atomic Data Docs - Overview](assets/atomic_data_logo_stroke.svg) -Atomic Data is a modular specification for sharing, modifying and modeling graph data. +Atomic Data is a modular specification for sharing, modifying and modeling graph data. It combines the ease of use of JSON, the connectivity of RDF (linked data) and the reliability of type-safety. -It uses links to connect pieces of data, and therefore makes it easier to connect datasets to each other - even when these datasets exist on separate machines. +![Venn diagram showing Atomic Data is the combination of JSON, RDF and Type-Safety](assets/venn.svg) + +Atomic Data uses links to connect pieces of data, and therefore makes it easier to connect datasets to each other - even when these datasets exist on separate machines. Atomic Data is especially suitable for knowledge graphs, distributed datasets, semantic data, p2p applications, decentralized apps and linked open data. It is designed to be highly extensible, easy to use, and to make the process of domain specific standardization as simple as possible. diff --git a/src/interoperability/rdf.md b/src/interoperability/rdf.md index 5d330cd..f3fb0bb 100644 --- a/src/interoperability/rdf.md +++ b/src/interoperability/rdf.md @@ -23,10 +23,10 @@ However, it does differ in some fundamental ways. ## Why these changes? -I love RDF, and have been working with it for quite some time now. -I started a company that specializes in Linked Data, and we use it extensively in our products and services. +I have been working with RDF for quite some time now, and absolutely believe in some of the core premises of RDF. +I started a company that specializes in Linked Data ([Ontola](https://ontola.io)), and we use it extensively in our products and services. Using URIs (and more-so URLs, which are URIs that can be fetched) for everything is a great idea, since it helps with interoperability and enables truly decentralized knowledge graphs. -However, some of the characteristics of RDF might have contributed to its relative lack of adoption. +However, some of the characteristics of RDF make it hard to use, and have probably contributed to its relative lack of adoption. ### It's too hard to select a specific value (object) in RDF @@ -101,12 +101,19 @@ This more closely resembles common CS terminology. ([discussion](https://github. ### Subject + Predicate uniqueness -In RDF, it's very much possible for a graph to contain multiple statements that share both a `subject` and a `predicate`. -One of the reasons this is possible, is because RDF graphs should always be mergeable. -However, this introduces some extra complexity for data users. +As discussed above, in RDF, it's very much possible for a graph to contain multiple statements that share both a `subject` and a `predicate`. +This is probably because of two reasons: + +1. RDF graphs must always be **mergeable** (just like Atomic Data). +1. Anyone can make **any statement** about **any subject** (_unlike_ Atomic Data, see next section). + +However, this introduces a lot extra complexity for data users (see above), which makes it not very attractive to use RDF in any client. Whereas most languages and datatypes have `key-value` uniqueness that allow for unambiguous value selection, RDF clients have to deal with the possibility that multiple triples with the same `subject-predicate` combination might exist. +It also introduces a different problem: How should you interpret a set of `subject-predicate` combinations? +Does this represent a non-ordered collection, or did something to wrong with setting values?\ +In the RDF world, I've seen many occurences of both. -Atomic Data requires `subject-property` uniqueness, which means that this is no longer an issue for clients. +Atomic Data requires `subject-property` uniqueness, which means that these issues are no more. However, in order to guarantee this, and still retain _graph merge-ability_ we also need to limit who creates statements about a subject: ### Limiting subject usage @@ -159,13 +166,18 @@ When a value is a URL, we don't call it a named node, but we simply use a URL da ### Requiring URLs -RDF allows any type of URIs for `subject` and `predicate` value, which means they can be URLs, but don't have to be. This means they don't always resolve, or even function as locators. The links don't work, and that restricts how useful the links are. Atomic Data takes a different approach: these links MUST Resolve. Requiring Properties to resolve is part of what enables the type system of Atomic Schema - they provide the `shortname` and `datatype`. +A URL (Uniform Resource _Locator_) is a specific and cooler version of a URI (Uniform Resource _Identifier_), because a URL tells you where you can find more information about this thing (hence _Locator_). + +RDF allows any type of URIs for `subject` and `predicate` value, which means they can be URLs, but don't have to be. +This means they don't always resolve, or even function as locators. +The links don't work, and that restricts how useful the links are. +Atomic Data takes a different approach: these links MUST Resolve. Requiring [Properties](https://atomicdata.dev/classes/Property) to resolve is part of what enables the type system of Atomic Schema - they provide the `shortname` and `datatype`. -Requiring URLs makes things easier for data users, at the cost of the data producer. -With Atomic Data, the data producer MUST offer the triples at the URL of the subject. -This is a challenge - especially with the current (lack of) tooling. +Requiring URLs makes things easier for data users, but makes things a bit more difficult for the data producer. +With Atomic Data, the data producer MUST offer the data at the URL of the subject. +This is a challenge that requires tooling, which is why I've built [Atomic-Server](https://crates.io/crates/atomic-server): an easy to use, performant, open source data management sytem. -However - making sure that links _actually work_ offer tremendous benefits for data consumers, and that advantage is often worth the extra trouble. +Making sure that links _actually work_ offer tremendous benefits for data consumers, and that advantage is often worth the extra trouble. ### Replace blank nodes with paths diff --git a/src/tooling.md b/src/tooling.md index c3e35ea..64d2093 100644 --- a/src/tooling.md +++ b/src/tooling.md @@ -1,11 +1,13 @@ {{#title Software and libraries for Atomic Data}} # Software and libraries for Atomic Data +Although Atomic Data is a specification, it also has reference implementations: + Open source (MIT licenced) software for Atomic Data: -- Server: [atomic-server](https://github.com/joepio/atomic) -- Front-end browser: [atomic-data-browser](https://github.com/joepio/atomic-data-browser) -- CLI (atomic-cli): [atomic-cli](https://github.com/joepio/atomic) +- **Server + Database**: [atomic-server](https://github.com/joepio/atomic) +- **GUI**: [atomic-data-browser](https://github.com/joepio/atomic-data-browser) +- **CLI**: [atomic-cli](https://github.com/joepio/atomic) Libraries (MIT licenced) to build apps with: From 3b088a529dc3cdd5786d3cfb9807d331763ce5d4 Mon Sep 17 00:00:00 2001 From: Joep Meindertsma Date: Sun, 31 Oct 2021 13:20:59 +0100 Subject: [PATCH 030/140] Add vscode task --- .vscode/tasks.json | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 .vscode/tasks.json diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 0000000..d57ebc6 --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,10 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "type": "shell", + "label": "docs atomic data (mdbook serve)", + "command": "mdbook serve", + } + ] +} From 908244e73a64b0ff8e83282f56368ebfe878fc21 Mon Sep 17 00:00:00 2001 From: Joep Meindertsma Date: Thu, 11 Nov 2021 18:32:57 +0100 Subject: [PATCH 031/140] update acknowledgements --- src/acknowledgements.md | 1 + 1 file changed, 1 insertion(+) diff --git a/src/acknowledgements.md b/src/acknowledgements.md index b2905db..83f3d90 100644 --- a/src/acknowledgements.md +++ b/src/acknowledgements.md @@ -13,4 +13,5 @@ - **Manu Sporny** (for his work on JSON-LD, which was an important inspiration for JSON-AD) - **Jonas Smedegaard** (for the various interesting talks we had and the feedback he provided) - **Arthur Dingemans** (for sharing his thoughts, providing feedback and his valuable suggestions) +- **Anja Koopman** (for all her support, even when this project ate away days and nights of our time together) - All the other people who contributed to linked data related standards From acaa13a47ab6ec6526a5f85c2f3a70bc8a44cee0 Mon Sep 17 00:00:00 2001 From: Joep Meindertsma Date: Sat, 13 Nov 2021 23:11:07 +0100 Subject: [PATCH 032/140] Fix typo #70 --- src/invitations.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/invitations.md b/src/invitations.md index 7027ed2..d75cb3a 100644 --- a/src/invitations.md +++ b/src/invitations.md @@ -14,7 +14,7 @@ In order to make this process of inviting others as simple as possible, we've co ## Flow -1. The Owner or a resource creates an [Invite](https://atomicdata.dev/classes/Invite). This Invite points to a `target` Resource, provides read writes by default but can additionally add `write` rights, contains a bunch of `usagesLeft`. +1. The Owner or a resource creates an [Invite](https://atomicdata.dev/classes/Invite). This Invite points to a `target` Resource, provides `read` rights by default but can additionally add `write` rights, contains a bunch of `usagesLeft`. 1. The Guest opens the Invite URL. This returns the Invite resource, which provides the client with the information needed to do the next request which adds the actual rights. 1. The browser client app might generate a set of keys, or use an existing one. It sends the Agent URL to the Invite in a query param. 1. The server will respond with a Redirect resource, which links to the newly granted `target` resource. From 2166c422e2c2321800bcfab9f687cdc7408b1272 Mon Sep 17 00:00:00 2001 From: Joep Meindertsma Date: Sun, 14 Nov 2021 18:25:30 +0100 Subject: [PATCH 033/140] #55 authentication and websockets --- src/SUMMARY.md | 14 ++++++++++-- src/agents.md | 5 ++-- src/authentication.md | 43 +++++++++++++++++++++++++++++++++++ src/commits/intro.md | 2 +- src/hierarchy.md | 11 ++++++--- src/interoperability/solid.md | 12 ++++++---- src/schema/faq.md | 5 ++++ src/websockets.md | 3 +++ 8 files changed, 83 insertions(+), 12 deletions(-) create mode 100644 src/authentication.md diff --git a/src/SUMMARY.md b/src/SUMMARY.md index 91daca6..39fc16f 100644 --- a/src/SUMMARY.md +++ b/src/SUMMARY.md @@ -3,6 +3,9 @@ * [Atomic Data Overview](atomic-data-overview.md) * [Motivation](motivation.md) * [When (not) to use it](when-to-use.md) + +# Specification (core) + * [What is Atomic Data?](core/concepts.md) * [Serialization](core/serialization.md) * [JSON-AD](core/json-ad.md) @@ -13,15 +16,22 @@ * [Datatypes](schema/datatypes.md) * [Translations](schema/translations.md) * [FAQ](schema/faq.md) + +# Specification (extended) + * [Collections, filtering, sorting](schema/collections.md) -* [Agents and authentication](agents.md) +* [Agents](agents.md) +* [Hierarchy and authorization](hierarchy.md) +* [Authentication](authentication.md) * [Invitations and sharing](invitations.md) * [Commits (writing data)](commits/intro.md) * [Concepts](commits/concepts.md) * [Compared to](commits/compare.md) -* [Hierarchy and authorization](hierarchy.md) * [Endpoints](endpoints.md) * [WebSockets](websockets.md) + +# Using Atomic Data + * [Interoperability](interoperability/intro.md) * [RDF](interoperability/rdf.md) * [Solid](interoperability/solid.md) diff --git a/src/agents.md b/src/agents.md index 4b76e9e..35d9b41 100644 --- a/src/agents.md +++ b/src/agents.md @@ -1,10 +1,10 @@ {{#title Atomic Data Agents - Users and identities }} # Atomic Agents -Atomic Agents are used for _authentication_: to set an identity and prove who an actor actually is. +Atomic Agents are used for [authentication](./authentication.md): to set an identity and prove who an actor actually is. Agents can represent both actual individuals, or machines that interact with data. Agents are the entities that can get write / read rights. -Agents are used to sign [Commits](commits/intro.md) and to accept [Invites](invitations.md). +Agents are used to sign Requests and [Commits](commits/intro.md) and to accept [Invites](invitations.md). ## Design goals @@ -12,6 +12,7 @@ Agents are used to sign [Commits](commits/intro.md) and to accept [Invites](invi - **Easy**: It should be easy to work with, code with, and use - **Privacy-friendly**: Agents should allow for privacy friendly workflows - **Verifiable**: Others should be able to verify who did what +- **Secure**: Resistant to attacks by malicious others ## The Agent model diff --git a/src/authentication.md b/src/authentication.md new file mode 100644 index 0000000..7d1758d --- /dev/null +++ b/src/authentication.md @@ -0,0 +1,43 @@ +# Authentication in Atomic Data + +Atomic Data uses [Hierarchies](hierarchy.md) to describe who gets to access some resource, and who can edit it. +When an Agent wants to _edit_ a resource, they have to send a signed [Commit](commits/intro.md). +But how do we deal with _reading_ data, how do we know who is trying to get access? + +Authentication is done by signing individual (HTTP) requests with the Agent's private key. + +## Sending a request + +Here's an example (js) client side implementation with comments: + +```ts +// The Private Key of the agent is used for signing +// https://atomicdata.dev/properties/publicKey +const privateKey = "someBase64Key"; +// The current time as milliseconds since unix epoch +const timestamp = Math.round(new Date().getTime());; +// This is what you will need to sign. +// The timestmap is to limit the harm of a man-in-the-middle attack. +// The `subject` is the full HTTP url that is to be fetched. +const message = `${subject} ${timestamp}`; +// Sign using Ed25519 +const signed = await signToBase64(message, privateKey); +// Set all of these headers +headers.set('x-atomic-public-key', await agent.getPublicKey()); +headers.set('x-atomic-signature', signed); +headers.set('x-atomic-timestamp', timestamp.toString()); +headers.set('x-atomic-agent', agent?.subject); +``` + +## Handling a request + +- If none of the `x-atomic` HTTP headers are present, the server assigns the [PublicAgent](https://atomicdata.dev/agents/publicAgent) to the request. This Agent represents any guest who is not signed in. +- If some (but not all) of the `x-atomic` headers are present, the server will return with a `500`. +- The server must check the timestamp (max 10 seconds difference). +- The server must check whether the public key matches the one from the Agent. +- The server must check if the signature is valid. +- The server must check if the request resource can be shared + +## Authentication for [websockets](websockets.md) + +- Since there's only a single HTTP request, we don't have a subject to fetch. Use `ws` as a subject, so sign a string like `ws 12940791247`. diff --git a/src/commits/intro.md b/src/commits/intro.md index bf55f55..b9ee58c 100644 --- a/src/commits/intro.md +++ b/src/commits/intro.md @@ -3,7 +3,7 @@ _Disclaimer: Work in progress, prone to change._ -Atomic Commits is a proposed standard for communicating state changes (events / transactions / patches / deltas / mutations) of [Atomic Data](../core/concepts.md). +Atomic Commits is a specification for communicating _state changes_ (events / transactions / patches / deltas / mutations) of [Atomic Data](../core/concepts.md). It is the part of Atomic Data that is concerned with writing, editing, removing and updating information. ## Design goals diff --git a/src/hierarchy.md b/src/hierarchy.md index 1c48004..673290a 100644 --- a/src/hierarchy.md +++ b/src/hierarchy.md @@ -25,10 +25,15 @@ Although you are free to use Atomic Data with your own custom authorization syst - Rights are _additive_, which means that the rights add up. If a Resource itself has no `write` Atom containing your Agent, but it's `parent` _does_ have one, you will still get the `write` right. - Rights cannot be removed by children or parents - they can only be added. -## Limitations of the current Authorization model +## Authentication + +See [authentication](./authentication.md). + +## Current limitations of the current Authorization model + +The specification is growing (and please contribute in the [docs repo](https://github.com/ontola/atomic-data-docs/issues)), but the current specification lacks some features: - Rights can only be added, but not removed in a higher item of a hierarchy. This means that you cannot have a secret folder inside a public folder. - No model for representing groups of Agents, or other runtime checks for authorization. -- No way to limit access to reading / writing specific properties -- No way to limit delete access +- No way to limit delete access seperately from write rights - No way to request a set of rights for a Resource diff --git a/src/interoperability/solid.md b/src/interoperability/solid.md index f169f5e..345fa4d 100644 --- a/src/interoperability/solid.md +++ b/src/interoperability/solid.md @@ -65,11 +65,16 @@ Atomic Data uses `shortnames` to map properties to short, human-readable strings For more information about these differences, see the previous [RDF chapter](./rdf.md). -## Hierarchy model and authorization +## Hierarchy model, authorization, authentication -Atomic Data uses `parent-child` hierarchies to model data and performan authorization checks. +Atomic Data identities (Agents) are a combination of HTTP based, and cryptography (public / private key) based. +In Atomic, all actions (from GET requests to Commits) are signed using the private key of the Agent. +This makes Atomic Data a bit more unconventional, but also makes its auth mechanism very decentralized and lightweight. + +Solid uses HTTP based WebID identifiers combined with an OIDC flow. + +Atomic Data uses `parent-child` [hierarchies](../hierarchy.md) to model data and performan authorization checks. This closely resembles how filesystems work, and is therefore familiar to most users. -Solid uses ## Solid is more mature @@ -77,6 +82,5 @@ Atomic Data has significant gaps at this moment - not just in the implementation This makes it not yet usable for most applications. Here's a list of things missing in Atomic Data, with links to their open issues and links to their existing Solid counterpart. -- No way yet to restrict access to reading content. Only for writing content with Commits. [WAC](https://github.com/solid/web-access-control-spec) in Solid. - No inbox or [notifications](https://www.w3.org/TR/ldn/) ([issue](https://github.com/ontola/atomic-data/issues/28)) - No support from a big community, a well-funded business or the inventor of the world wide web. diff --git a/src/schema/faq.md b/src/schema/faq.md index 43344a9..9259406 100644 --- a/src/schema/faq.md +++ b/src/schema/faq.md @@ -7,6 +7,11 @@ A property only has one single Datatype. However, feel free to create a new kind of Datatype that, in turn, refers to other Datatypes. Perhaps Generics, or Option like types should be part of the Atomic Base Datatypes. +## Do you have an `enum` datatype? + +In Atomic Data, `enum` is not a datatype, but it's a constraint that can be added to properties that have +You can set [`allows-only`](https://atomicdata.dev/properties/allowsOnly) on a Property, and use that to limit which values are allowed. + ## How should a client deal with Shortname collisions? Atomic Data guarantees Subject-Property uniqueness, which means that Valid Resources are guaranteed to have only one of each Property. diff --git a/src/websockets.md b/src/websockets.md index c6c5947..fdc10f8 100644 --- a/src/websockets.md +++ b/src/websockets.md @@ -8,12 +8,15 @@ These have been implemented in `atomic-server` and `atomic-data-browser` (powere ## Initializing a WebSocket connection Send an HTTP `GET` request to the `/ws` endpoint of an `atomic-server`. The Server should update that request to a secure WebSocket (`wss`) connection. +Use `x-atomic` [authentication headers (read more here)](./authentication.md) and use `ws` as a subject when signing. ## Client to server messages - `SUBSCRIBE ${subject}` tells the Server that you'd like to receive Commits about this Subject. - `UNSUBSCRIBE ${subject}` tells the Server that you'd like to stop receiving Commits about this Subject. +- `GET ${subject}` fetch an individual resource. ## Server to client messages - `COMMIT ${CommitBody}` an entire Commit for a resource that you're subscribed to +- `RESOURCE ${CommitBody}` a resource as a response to a GET request. From 7d7366095e32b60305e78472bef1fa57afdf1ec6 Mon Sep 17 00:00:00 2001 From: Joep Meindertsma Date: Sun, 14 Nov 2021 20:44:35 +0100 Subject: [PATCH 034/140] Improve order, design goals --- src/SUMMARY.md | 6 +++--- src/atomic-data-overview.md | 2 +- src/authentication.md | 14 ++++++++++++++ 3 files changed, 18 insertions(+), 4 deletions(-) diff --git a/src/SUMMARY.md b/src/SUMMARY.md index 39fc16f..b933240 100644 --- a/src/SUMMARY.md +++ b/src/SUMMARY.md @@ -19,7 +19,6 @@ # Specification (extended) -* [Collections, filtering, sorting](schema/collections.md) * [Agents](agents.md) * [Hierarchy and authorization](hierarchy.md) * [Authentication](authentication.md) @@ -27,8 +26,9 @@ * [Commits (writing data)](commits/intro.md) * [Concepts](commits/concepts.md) * [Compared to](commits/compare.md) -* [Endpoints](endpoints.md) * [WebSockets](websockets.md) +* [Endpoints](endpoints.md) +* [Collections, filtering, sorting](schema/collections.md) # Using Atomic Data @@ -45,7 +45,7 @@ * [E-commerce & marketplaces](usecases/e-commerce.md) * [Surveys](usecases/surveys.md) * [Verifiable Credentials](usecases/verifiable-credentials.md) -* [Software and libraries](tooling.md) +* [**Software and libraries**](tooling.md) ----------- diff --git a/src/atomic-data-overview.md b/src/atomic-data-overview.md index 111c003..0f7a0fd 100644 --- a/src/atomic-data-overview.md +++ b/src/atomic-data-overview.md @@ -19,7 +19,7 @@ Atomic Data has a standard for communicating state changes called [Commits](comm These Commits are signed using cryptographic keys, which ensures that every change can be audited. Commits are also used to construct a history of versions. -[Agents](agents.md) are Users that enable authentication. +[Agents](agents.md) are Users that enable [authentication](authentication.md). Atomic Data can be traversed using [Paths](core/paths.md), or queried using [Collections](schema/collections.md). [Hierarchies](hierarchy.md) are used for authorization and keeping data organized. [Invites](invitations.md) can be used to easily create new users and provide them with rights. diff --git a/src/authentication.md b/src/authentication.md index 7d1758d..499d688 100644 --- a/src/authentication.md +++ b/src/authentication.md @@ -4,6 +4,16 @@ Atomic Data uses [Hierarchies](hierarchy.md) to describe who gets to access some When an Agent wants to _edit_ a resource, they have to send a signed [Commit](commits/intro.md). But how do we deal with _reading_ data, how do we know who is trying to get access? +## Design goals + +- **Secure**: Because, what's the point of authentication if it's not? +- **Ease of use**: Setting up an identity should not require _any_ effort, and proving identity should be minimal effort. +- **Anonimity allowed**: Users should be able to have multiple identities, some of which are fully anonymous. +- **Self-sovereign**: No dependency on servers that user's don't control. Or at least, minimise this. +- **Dummy-proof**: We need a mechanism for dealing with forgetting passwords / client devices losing data. +- **Compatible with Commits**: Atomic Commits require clients to sign things. Ideally, this functionality / strategy would also fit with the new model. +- **Fast**: Of course, authentication will slow things down. But let's keep that to a minimum. + Authentication is done by signing individual (HTTP) requests with the Agent's private key. ## Sending a request @@ -41,3 +51,7 @@ headers.set('x-atomic-agent', agent?.subject); ## Authentication for [websockets](websockets.md) - Since there's only a single HTTP request, we don't have a subject to fetch. Use `ws` as a subject, so sign a string like `ws 12940791247`. + +## Limitations / considerations + +- Since we need the Private Key to sign Commits and requests, the client should have this available. This means the client software as well as the user should deal with key management. From f1a4ecc6425ac8a8284898c6b336d3256acf8518 Mon Sep 17 00:00:00 2001 From: Joep Meindertsma Date: Sun, 14 Nov 2021 20:45:05 +0100 Subject: [PATCH 035/140] WIP get started guide --- src/get-started.md | 41 ++++++++++++++++++++++++++ src/usecases/standardization-bodies.md | 5 ++++ 2 files changed, 46 insertions(+) create mode 100644 src/get-started.md create mode 100644 src/usecases/standardization-bodies.md diff --git a/src/get-started.md b/src/get-started.md new file mode 100644 index 0000000..2f0a997 --- /dev/null +++ b/src/get-started.md @@ -0,0 +1,41 @@ +{{#title Get started with Atomic Data}} +# Get started with Atomic Data + +There's a couple of levels at which you can start working with Atomic Data (from easy to hard): + +- **Play with the demo**: Create an Agent, edit a document. +- **Host your own Atomic-Server**. +- **Create a react app with the template** +- **Set up the full dev environment**. +- **Create a library for Atomic Data**. + +## Play with the demo + +- Open [the Invite](https://atomicdata.dev/invites/1) on `atomicdata.dev` +- Press `Accept`. Now, the front-end app will generate a Private-Public Key pair. The public key will be sent to the server, which creates an Agent for you. +- You're now signed in! You can edit the document in your screen. +- Edit your Agent by going to [user settings](https://atomicdata.dev/app/agent) +- Copy your `secret`, and save it somewhere safe. You can use this to sign in on a different machine. +- Press `edit user` to add your name and perhaps a bio. +- When you're done, visit user settings again and press `sign out` to erase your credentials and end the session. + +## Host your own Atomic-Sesrver (locally) + +- If you have docker running, you can use this one-liner: `docker run -p 80:80 -p 443:443 -v atomic-storage:/atomic-storage joepmeneer/atomic-server` +- Now, visit `localhost` in your browser to access your server. +- It's now only available locally. If you want to get it on the _internet_, you need to set up a domain name, and make sure its traffic is routed to your computer (search `port forwarding`). + +## Host your own Atomic-Server (on a VPS) + +- **Set up a domain name** by using one of the many services that do this for you. +- **Get a virtual private server (VPS)** on which you can run `atomic-server`. We are running atomicdata.dev on the cheapest VPS we could find: $3.50 / month at [Vultr.com (use this link to give us $10 bucks of hosting credit)](https://www.vultr.com/?ref=8970814-8H). + + + +- Browser app [atomic-data-browser](https://github.com/joepio/atomic-data-browser) ([demo on atomicdata.dev](https://atomicdata.dev)) +- Build a react app using [typescript & react libraries](https://github.com/joepio/atomic-data-ts). Start with the [react template on codesandbox](https://codesandbox.io/s/atomic-data-react-template-4y9qu?file=/src/MyResource.tsx) +- Host your own [atomic-server](https://github.com/joepio/atomic) (powers [atomicdata.dev](https://atomicdata.dev), run with `docker run -p 80:80 -p 443:443 -v atomic-storage:/atomic-storage joepmeneer/atomic-server`) +- Discover the command line tool: [atomic-cli](https://github.com/joepio/atomic) (`cargo install atomic-cli`) +- Use the Rust library: [atomic-lib](https://github.com/joepio/atomic) + +Make sure to [join our Discord](https://discord.gg/a72Rv2P) if you'd like to discuss Atomic Data with others. diff --git a/src/usecases/standardization-bodies.md b/src/usecases/standardization-bodies.md new file mode 100644 index 0000000..feb99ea --- /dev/null +++ b/src/usecases/standardization-bodies.md @@ -0,0 +1,5 @@ +{{#title Atomic Data for Standardization bodies}} +# Atomic Data for Standardization bodies + +Most industry domains work on standardization. + (health, pharmaseuyical studies From 702c881ae32f17c42fdd6e13d0b71e605cf9ab42 Mon Sep 17 00:00:00 2001 From: Joep Meindertsma Date: Sun, 14 Nov 2021 20:45:16 +0100 Subject: [PATCH 036/140] Minor improvements --- src/assets/venn.png | Bin 136291 -> 0 bytes src/schema/classes.md | 17 ++++------------- src/trust.md | 2 +- 3 files changed, 5 insertions(+), 14 deletions(-) delete mode 100644 src/assets/venn.png diff --git a/src/assets/venn.png b/src/assets/venn.png deleted file mode 100644 index 79650c2d1fa7863fb79c67a0623842553206c640..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 136291 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%0DlE9RqdpRj^95KBBhH6f@KOeQ;tj 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

pK4d^Gi^9)L;F=Kz%BA7p((%{O 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£>{Cc{+0T@N4gDsXYa;?@rZSr@Ttro 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 Date: Mon, 13 Dec 2021 12:07:22 +0100 Subject: [PATCH 037/140] Food label usecase --- src/SUMMARY.md | 1 + src/usecases/food-labels.md | 38 +++++++++++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+) create mode 100644 src/usecases/food-labels.md diff --git a/src/SUMMARY.md b/src/SUMMARY.md index b933240..9078ed8 100644 --- a/src/SUMMARY.md +++ b/src/SUMMARY.md @@ -45,6 +45,7 @@ * [E-commerce & marketplaces](usecases/e-commerce.md) * [Surveys](usecases/surveys.md) * [Verifiable Credentials](usecases/verifiable-credentials.md) + * [Food labels](usecases/food-labels.md) * [**Software and libraries**](tooling.md) ----------- diff --git a/src/usecases/food-labels.md b/src/usecases/food-labels.md new file mode 100644 index 0000000..87068a6 --- /dev/null +++ b/src/usecases/food-labels.md @@ -0,0 +1,38 @@ +# Atomic Data for food label standardization + +In most countries, food producers are required to provide nutritional information on the packages of products, which helps citizens to make informed decisions about what to eat. +But how about we upgrade these labels to machine-readable, atomic data? +We could describe products using Atomic Data, and put their identifiers (Subject URLs) as QR codes on packages. +Imagine these scenarios: + +## Scan labels to get detailed, reliable, interactive information + +You want to know more about some new cereal you've just bought. +You scan the QR code on the package. +A web app opens that shows detailed, yet highly visual information about its nutritional value. +The screen is no longer limited to what realistically fits on a package. +The elements are interactive, and provide explanations. +Everything is translated to the user's language. +If the food is (soon to be) expired, the app will clearly and visually alert you. +Click on the question mark next to `granulated sugars`, and you get an explanation of what this means to your health. +E-numbers are clickable, too, and help you instantly understand far more about what they represent. +When AR glasses become technologically feasible, you could even help people make better decisions while doing grocery shopping. + +Using _links_ instead of _names_ helps to guide consumers to _trustworthy_ pages that communicate clearly. +The alternative is that they use search engines, and maybe end up reading misinformation. + +## Provide nutritional advice based on shopping behavior + +You order a bunch of products on your favorite groceries delivery app. +When going to the payment screen, you are shown a nutritional overview of your order. +You see that with this diet, you might have a deficit of the Lysene amino acid. +The shopping cart suggest adding egg, dairy or soy to your diet. +This can be done, because the groceries app can easily check detailed information about the food in your shopping cart, and reason about your dietary intake. + +## How to achieve all this + +1. The governing body (e.g. the European Commision) should set up an [Atomic Server](https://github.com/joepio/atomic-data-rust/) and host it on some recognizable domain. +1. Create the [Class](https://atomicdata.dev/classes/Class) for a food product, containing the same (or more) information that is shown on food packages. +1. Create the Class for Ingredient. +1. Create instances for various Ingredients. Start with the E-numbers, work your way up to all kinds of used ingredients. Add Translations. +1. Give instructions to Producers on how to describe their Products. Give them to option to host their own Server and control their own data, and give them the option to use some EU server. From 3feb425ef94fefa9d4cf4cd2a0724b3f257ab87f Mon Sep 17 00:00:00 2001 From: Joep Meindertsma Date: Mon, 13 Dec 2021 12:08:01 +0100 Subject: [PATCH 038/140] Add instruction for cargo --- src/get-started.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/get-started.md b/src/get-started.md index 2f0a997..4aa0ffc 100644 --- a/src/get-started.md +++ b/src/get-started.md @@ -21,7 +21,7 @@ There's a couple of levels at which you can start working with Atomic Data (from ## Host your own Atomic-Sesrver (locally) -- If you have docker running, you can use this one-liner: `docker run -p 80:80 -p 443:443 -v atomic-storage:/atomic-storage joepmeneer/atomic-server` +- If you have docker running, you can use this one-liner: `docker run -p 80:80 -p 443:443 -v atomic-storage:/atomic-storage joepmeneer/atomic-server` (or use `cargo install atomic-server`, or the [binaries](https://github.com/joepio/atomic-data-rust/releases/)) - Now, visit `localhost` in your browser to access your server. - It's now only available locally. If you want to get it on the _internet_, you need to set up a domain name, and make sure its traffic is routed to your computer (search `port forwarding`). From ebde15d8fcad138bdc05c59fbb2369c58754a27b Mon Sep 17 00:00:00 2001 From: Petra Jaros Date: Mon, 13 Dec 2021 10:34:07 -0500 Subject: [PATCH 039/140] Typo: SRARQL -> SPARQL --- src/core/querying.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/querying.md b/src/core/querying.md index aaa90ac..252986b 100644 --- a/src/core/querying.md +++ b/src/core/querying.md @@ -7,7 +7,7 @@ There are multiple ways of getting Atomic Data into some system: - [**Atomic Paths**](paths.md) is a simple way to traverse Atomic Graphs and target specific values - [**Subject Fetching**](#subject-fetching-http) requests a single subject right from its source - [**Triple Pattern Fragments**](#triple-pattern-fragments) allows querying for specific (combinations of) Subject, Property and Value. -- [**SRARQL**](#SPARQL) is a powerful Query language for traversing linked data graphs +- [**SPARQL**](#SPARQL) is a powerful Query language for traversing linked data graphs ## Atomic Paths From 07edffea8abe509295bb9b290492b4199bb4c7d1 Mon Sep 17 00:00:00 2001 From: Petra Jaros Date: Mon, 13 Dec 2021 10:41:44 -0500 Subject: [PATCH 040/140] Typo/word choice: Audibility -> Auditability "Audibility" is the ability to be heard, while "auditability" is the ability to be audited. https://en.wiktionary.org/wiki/audibility https://en.wiktionary.org/wiki/auditability --- src/when-to-use.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/when-to-use.md b/src/when-to-use.md index 4edaef4..bb11f58 100644 --- a/src/when-to-use.md +++ b/src/when-to-use.md @@ -8,7 +8,7 @@ - **High interoperability requirements**. When multiple groups of people have to use the same schema, Atomic Data provides easy ways to constrain and validate the data and ensure type safety. - **Multi-class / multi-model**. Contrary to (SQL) tables, Atomic Data allows a single thing to have multiple classes, each with their own properties. - **Connected / decentralized data**. With Atomic Data, you use URLs to point to things on other computers. This makes it possible to connect datasets very explicitly, without creating copies. Very useful for decentralized social networks, for example. -- **Audibility & Versioning**. Using Atomic Commits, we can store all changes to data as transactions that can be replayed. This creates a complete audit log and history. +- **Auditability & Versioning**. Using Atomic Commits, we can store all changes to data as transactions that can be replayed. This creates a complete audit log and history. - **JSON or RDF as Output**. Atomic Data serializes to idiomatic, clean JSON as well as various RDF formats (Turtle / JSON-LD / n-triples / RDF/XML). ## When not to use Atomic Data From 2b6054423451f4a5750c43fdbf537f96cb284002 Mon Sep 17 00:00:00 2001 From: Joep Meindertsma Date: Wed, 15 Dec 2021 08:41:14 +0100 Subject: [PATCH 041/140] Add auth limitation dids --- src/authentication.md | 1 + 1 file changed, 1 insertion(+) diff --git a/src/authentication.md b/src/authentication.md index 499d688..006dd97 100644 --- a/src/authentication.md +++ b/src/authentication.md @@ -55,3 +55,4 @@ headers.set('x-atomic-agent', agent?.subject); ## Limitations / considerations - Since we need the Private Key to sign Commits and requests, the client should have this available. This means the client software as well as the user should deal with key management. +- When using the Agent's subject to authenticate somwehere, the authorizer must be able to check what the public key of the agent is. This means the agent must be publicly resolvable. This is one of the reasons we should work towards a server-independent identifier, probably as base64 string that contains the public key (and, optionally, also the https identifier). See the [github issue on DIDs](https://github.com/ontola/atomic-data-docs/issues/59). From be85bc2cbd782cfb6ef674d4199e5b8ffaaf8fa2 Mon Sep 17 00:00:00 2001 From: Joep Meindertsma Date: Mon, 20 Dec 2021 10:03:41 +0100 Subject: [PATCH 042/140] Improve venn bg --- src/assets/venn.svg | 70 +++++++++-------------------------------- src/assets/venn_old.svg | 58 ++++++++++++++++++++++++++++++++++ src/plugins.md | 7 +++++ 3 files changed, 79 insertions(+), 56 deletions(-) create mode 100644 src/assets/venn_old.svg diff --git a/src/assets/venn.svg b/src/assets/venn.svg index 9bff990..29269cd 100644 --- a/src/assets/venn.svg +++ b/src/assets/venn.svg @@ -1,58 +1,16 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + diff --git a/src/assets/venn_old.svg b/src/assets/venn_old.svg new file mode 100644 index 0000000..9bff990 --- /dev/null +++ b/src/assets/venn_old.svg @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/plugins.md b/src/plugins.md index 8041fe0..7076f91 100644 --- a/src/plugins.md +++ b/src/plugins.md @@ -26,3 +26,10 @@ When a plugin is installed, the Server needs to be aware of when the functionali - Periodically (if so, when?) - On a certain endpoint (which endpoint? One or multiple?) - As a middleware when (specific) resources are created / read / updated. + +## Hooks + +### BeforeCommit + +Is run before a Commit is applied. +Useful for performing authorization or data shape checks. From d0e1708f773991b8e46531f01ffb6b103a12ad82 Mon Sep 17 00:00:00 2001 From: Joep Meindertsma Date: Mon, 20 Dec 2021 11:24:28 +0100 Subject: [PATCH 043/140] #57 File upload and download --- src/SUMMARY.md | 1 + src/files.md | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 34 insertions(+) create mode 100644 src/files.md diff --git a/src/SUMMARY.md b/src/SUMMARY.md index 9078ed8..464ca0a 100644 --- a/src/SUMMARY.md +++ b/src/SUMMARY.md @@ -29,6 +29,7 @@ * [WebSockets](websockets.md) * [Endpoints](endpoints.md) * [Collections, filtering, sorting](schema/collections.md) +* [Uploading and downloading files](files.md) # Using Atomic Data diff --git a/src/files.md b/src/files.md new file mode 100644 index 0000000..04e2034 --- /dev/null +++ b/src/files.md @@ -0,0 +1,33 @@ +{{#title Uploading, downloading and describing files with Atomic Data}} +# Uploading, downloading and describing files with Atomic Data + +The Atomic Data model (Atomic Schema) is great for describing structured data, but for many types of existing data, we already have a different way to represent them: files. +In Atomic Data, files have two URLs. +One _describes_ the file and its metadata, and the other is a URL that downloads the file. +This allows us to present a better view when a user wants to take a look at some file, and lear about its context before downloading it. + +## The File class + +_url: [https://atomicdata.dev/classes/File](https://atomicdata.dev/classes/File)_ + +Files always have a downloadURL. +They often also have a filename, a filesize, a checksum, a mimetype, and an internal ID (more on that later). +They also often have a [`parent`](https://atomicdata.dev/properties/parent), which can be used to set permissions / rights. + +## Uploading a file + +In `atomic-server`, a `/upload` endpoint exists for uploading a file. + +- Decide where you want to add the file in the [hierarchy](hierarchy.md) of your server. You can add a file to any resource - your file will refer to this resource as its [`parent`](https://atomicdata.dev/properties/parent). Make sure you have `write` rights on this parent. +- Use that parent to add a query parameter to the server's `/upload` endpoint, e.g. `/upload?parent=https%3A%2F%2Fatomicdata.dev%2Ffiles`. +- Send an HTTP `POST` request to the server's `/upload` endpoint containing [`multi-part-form-data`](https://developer.mozilla.org/en-US/docs/Web/API/FormData/Using_FormData_Objects). You can upload multiple files in one request. Add [authentication](authentication.md) headers, and sign the HTTP request with the +- The server will check your authentication headers, your permissions, and will persist your uploaded file(s). It will now create File resources. +- The server will reply with an array of created Atomic Data Files + +## Downloading a file + +Simply send an HTTP GET request to the File's [`download-url`](https://atomicdata.dev/properties/downloadURL) (make sure to authenticate this request). + +- [Discussion on specification](https://github.com/ontola/atomic-data-docs/issues/57) +- [Discussion on Rust server implementation](https://github.com/joepio/atomic-data-rust/issues/72) +- [Discussion on Typescript client implementation](https://github.com/joepio/atomic-data-browser/issues/121) From 2ff80ce3b11f6c68a6948a07a45a8e59f1754831 Mon Sep 17 00:00:00 2001 From: Joep Meindertsma Date: Mon, 20 Dec 2021 11:49:42 +0100 Subject: [PATCH 044/140] Websockets code examples --- src/websockets.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/websockets.md b/src/websockets.md index fdc10f8..a296ac2 100644 --- a/src/websockets.md +++ b/src/websockets.md @@ -20,3 +20,8 @@ Use `x-atomic` [authentication headers (read more here)](./authentication.md) an - `COMMIT ${CommitBody}` an entire Commit for a resource that you're subscribed to - `RESOURCE ${CommitBody}` a resource as a response to a GET request. + +## Example implementations + +- [Example client implementation in Typescript (@tomic/lib).](https://github.com/joepio/atomic-data-browser/blob/main/lib/src/websockets.ts) +- [Example server implementation in Rust using Actix-Web](https://github.com/joepio/atomic-data-rust/blob/master/server/src/handlers/web_sockets.rs) From 4f438e6dac0be46d6b551fb36668400a531d499a Mon Sep 17 00:00:00 2001 From: Joep Meindertsma Date: Fri, 24 Dec 2021 14:32:12 +0100 Subject: [PATCH 045/140] Added serialization, acknolewdgements, query opts --- src/acknowledgements.md | 2 ++ src/core/querying.md | 25 ++++++++++---- src/core/serialization.md | 72 +++++++++++++++++++++++++++++++++++++-- 3 files changed, 90 insertions(+), 9 deletions(-) diff --git a/src/acknowledgements.md b/src/acknowledgements.md index 83f3d90..e88ec6b 100644 --- a/src/acknowledgements.md +++ b/src/acknowledgements.md @@ -14,4 +14,6 @@ - **Jonas Smedegaard** (for the various interesting talks we had and the feedback he provided) - **Arthur Dingemans** (for sharing his thoughts, providing feedback and his valuable suggestions) - **Anja Koopman** (for all her support, even when this project ate away days and nights of our time together) +- **Alex Mikhalev** (for sharing many inspiring project and ideas) +- **Daniel Lutrha** (for inspiring me to be more ambitious and for providing lots of technical ideas) - All the other people who contributed to linked data related standards diff --git a/src/core/querying.md b/src/core/querying.md index 252986b..9df23b7 100644 --- a/src/core/querying.md +++ b/src/core/querying.md @@ -3,17 +3,12 @@ There are multiple ways of getting Atomic Data into some system: +- [**Subject Fetching**](#subject-fetching-http) requests a single subject right from its source - [**Atomic Collections**](../schema/collections.md) can filter, sort and paginate resources - [**Atomic Paths**](paths.md) is a simple way to traverse Atomic Graphs and target specific values -- [**Subject Fetching**](#subject-fetching-http) requests a single subject right from its source - [**Triple Pattern Fragments**](#triple-pattern-fragments) allows querying for specific (combinations of) Subject, Property and Value. - [**SPARQL**](#SPARQL) is a powerful Query language for traversing linked data graphs -## Atomic Paths - -An Atomic Path is a string that consist of one or more URLs, which when traversed point to an item. -For more information, see [Atomic Paths](paths.md). - ## Subject fetching (HTTP) The simplest way of getting Atomic Data when the Subject is an HTTP URL, is by sending a GET request to the subject URL. @@ -39,6 +34,21 @@ Connection: Closed The server MAY also include other resources, if they are deemed relevant. +## Atomic Collections + +Collections are Resources that provide simple query options, such as filtering by Property or Value, and sorting. +They also paginate resources. +Under the hood, Collections are powered by Triple Pattern Fragments. +Use query parameters to traverse pages, filter, or sort. + +[Read more about Collections](../schema/collections.md) + +## Atomic Paths + +An Atomic Path is a string that consist of one or more URLs, which when traversed point to an item. + +[Read more about Atomic Paths](paths.md) + ## Triple Pattern Fragments [Triple Pattern Fragments](https://linkeddatafragments.org/specification/triple-pattern-fragments/) (TPF) is an interface for querying RDF. @@ -71,6 +81,7 @@ Connection: Closed [SPARQL](https://www.w3.org/TR/rdf-sparql-query/) is a powerful RDF query language. Since all Atomic Data is also valid RDF, it should be possible to query Atomic Data using SPARQL. +None of the exsisting implementations support a SPARQL endpoint, though. - Convert / serialize Atomic Data to RDF (for example by using the `/tpf` endpoint and an `accept` header: `curl -i -H "Accept: text/turtle" "https://atomicdata.dev/tpf"`) -- Load it into a SPARQL engine (e.g. ) +- Load it into a SPARQL engine of your choice diff --git a/src/core/serialization.md b/src/core/serialization.md index 1ae08a8..187ec7f 100644 --- a/src/core/serialization.md +++ b/src/core/serialization.md @@ -8,10 +8,41 @@ It's fundamentally a data model, and that's an important distinction to make. However, it's recommended to use [`JSON-AD`](json-ad.md) (more about that on the next page), which is specifically designed to be a simple, complete and performant format for Atomic Data. +```json +{ + "@id": "https://atomicdata.dev/properties/description", + "https://atomicdata.dev/properties/datatype": "https://atomicdata.dev/datatypes/markdown", + "https://atomicdata.dev/properties/description": "A textual description of something. When making a description, make sure that the first few words tell the most important part. Give examples. Since the text supports markdown, you're free to use links and more.", + "https://atomicdata.dev/properties/isA": [ + "https://atomicdata.dev/classes/Property" + ], + "https://atomicdata.dev/properties/parent": "https://atomicdata.dev/properties", + "https://atomicdata.dev/properties/shortname": "description" +} +``` + +[Read more about JSON-AD](json-ad.md) + ## JSON (simple) Atomic Data is designed to be serializable to clean, simple [JSON](../interoperability/json.md), for usage in (client) apps that don't need to know the full URLs of properties. +````json +{ + "@id": "https://atomicdata.dev/properties/description", + "datatype": "https://atomicdata.dev/datatypes/markdown", + "description": "A textual description of something. When making a description, make sure that the first few words tell the most important part. Give examples. Since the text supports markdown, you're free to use links and more.", + "is-a": [ + "https://atomicdata.dev/classes/Property" + ], + "parent": "https://atomicdata.dev/properties", + "shortname": "description" +} +```` + +[Read more about JSON and Atomic Data](json-ad.md) + + ## RDF serialization formats Since Atomic Data is a strict subset of RDF, RDF serialization formats can be used to communicate and store Atomic Data, such as N-Triples, Turtle, HexTuples, JSON-LD and [other RDF serialization formats](https://ontola.io/blog/rdf-serialization-formats/). @@ -19,6 +50,43 @@ However, not all valid RDF is valid Atomic Data. Atomic Data is more strict. Read more about serializing Atomic Data to RDF in the [RDF interoperability section](../interoperability/rdf.md). -## Experimental serialization formats +JSON-LD: + +```json +{ + "@context": { + "datatype": { + "@id": "https://atomicdata.dev/properties/datatype", + "@type": "@id" + }, + "description": "https://atomicdata.dev/properties/description", + "is-a": { + "@container": "@list", + "@id": "https://atomicdata.dev/properties/isA" + }, + "parent": { + "@id": "https://atomicdata.dev/properties/parent", + "@type": "@id" + }, + "shortname": "https://atomicdata.dev/properties/shortname" + }, + "@id": "https://atomicdata.dev/properties/description", + "datatype": "https://atomicdata.dev/datatypes/markdown", + "description": "A textual description of something. When making a description, make sure that the first few words tell the most important part. Give examples. Since the text supports markdown, you're free to use links and more.", + "is-a": [ + "https://atomicdata.dev/classes/Property" + ], + "parent": "https://atomicdata.dev/properties", + "shortname": "description" +} +``` + +Turtle / N-Triples: -Some experimental ideas for Atomic Data serialization are [written here](https://github.com/ontola/atomic-data/blob/master/src/experimental-serialization.md). +```turtle + . + . + "description"^^ . + "https://atomicdata.dev/classes/Property"^^ . + "A textual description of something. When making a description, make sure that the first few words tell the most important part. Give examples. Since the text supports markdown, you're free to use links and more."^^ . +``` From 99aa4cef3759dbc5d4b5226b7073661ed36dede6 Mon Sep 17 00:00:00 2001 From: Joep Meindertsma Date: Fri, 24 Dec 2021 14:54:43 +0100 Subject: [PATCH 046/140] Authentication docs --- src/authentication.md | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/src/authentication.md b/src/authentication.md index 006dd97..09dc9fa 100644 --- a/src/authentication.md +++ b/src/authentication.md @@ -16,27 +16,37 @@ But how do we deal with _reading_ data, how do we know who is trying to get acce Authentication is done by signing individual (HTTP) requests with the Agent's private key. +## HTTP Headers + +All of the following headers are required, if you need authentication. + +- `x-atomic-public-key`: The base64 public key (Ed25519) of the Agent sending the request +- `x-atomic-signature`: A base64 signature of the following string: `{subject} {timestamp}` +- `x-atomic-timestamp`: The current time (when sending the request) as milliseconds since unix epoch +- `x-atomic-agent`: The subject URL of the Agent sending the request. + ## Sending a request Here's an example (js) client side implementation with comments: ```ts // The Private Key of the agent is used for signing -// https://atomicdata.dev/properties/publicKey +// https://atomicdata.dev/properties/privateKey const privateKey = "someBase64Key"; -// The current time as milliseconds since unix epoch const timestamp = Math.round(new Date().getTime());; // This is what you will need to sign. // The timestmap is to limit the harm of a man-in-the-middle attack. // The `subject` is the full HTTP url that is to be fetched. const message = `${subject} ${timestamp}`; -// Sign using Ed25519 +// Sign using Ed25519, see example implementation here: https://github.com/joepio/atomic-data-browser/blob/30b2f8af59d25084de966301cb6bd1ed90c0eb78/lib/src/commit.ts#L176 const signed = await signToBase64(message, privateKey); // Set all of these headers +const headers = new Headers; headers.set('x-atomic-public-key', await agent.getPublicKey()); headers.set('x-atomic-signature', signed); headers.set('x-atomic-timestamp', timestamp.toString()); headers.set('x-atomic-agent', agent?.subject); +const response = await fetch(subject, {headers}); ``` ## Handling a request From 2d0815c90b90746c7ce04706f948f60a8c39e805 Mon Sep 17 00:00:00 2001 From: Joep Meindertsma Date: Fri, 24 Dec 2021 21:30:43 +0100 Subject: [PATCH 047/140] #84 extended core --- src/SUMMARY.md | 1 + src/atomic-data-overview.md | 20 ++++++++++++-------- src/core/concepts.md | 2 ++ src/extended.md | 17 +++++++++++++++++ src/files.md | 2 +- src/schema/datatypes.md | 2 +- src/schema/faq.md | 8 ++++---- src/when-to-use.md | 3 +-- 8 files changed, 39 insertions(+), 16 deletions(-) create mode 100644 src/extended.md diff --git a/src/SUMMARY.md b/src/SUMMARY.md index 464ca0a..d448cc3 100644 --- a/src/SUMMARY.md +++ b/src/SUMMARY.md @@ -19,6 +19,7 @@ # Specification (extended) +* [Atomic Data Extended](extended.md) * [Agents](agents.md) * [Hierarchy and authorization](hierarchy.md) * [Authentication](authentication.md) diff --git a/src/atomic-data-overview.md b/src/atomic-data-overview.md index 0f7a0fd..d8ba462 100644 --- a/src/atomic-data-overview.md +++ b/src/atomic-data-overview.md @@ -1,6 +1,6 @@ ![# Atomic Data Docs - Overview](assets/atomic_data_logo_stroke.svg) -Atomic Data is a modular specification for sharing, modifying and modeling graph data. It combines the ease of use of JSON, the connectivity of RDF (linked data) and the reliability of type-safety. +**Atomic Data is a modular specification for sharing, modifying and modeling graph data. It combines the ease of use of JSON, the connectivity of RDF (linked data) and the reliability of type-safety.**queried using [Collections](schema/collections.md). ![Venn diagram showing Atomic Data is the combination of JSON, RDF and Type-Safety](assets/venn.svg) @@ -15,14 +15,18 @@ It is type-safe (you know if something is a `string`, `number`, `date`, `URL`, e The default serialization format for Atomic Data is [JSON-AD](core/json-ad.md), which is simply JSON where each key is a URL of an Atomic Property. These Properties are responsible for setting the `datatype` (to ensure type-safety) and setting `shortnames` (which help to keep names short, for example in JSON serialization) and `descriptions` (which provide semantic explanations of what a property should be used for). -Atomic Data has a standard for communicating state changes called [Commits](commits/intro.md). -These Commits are signed using cryptographic keys, which ensures that every change can be audited. -Commits are also used to construct a history of versions. +## Atomic Data Extended -[Agents](agents.md) are Users that enable [authentication](authentication.md). -Atomic Data can be traversed using [Paths](core/paths.md), or queried using [Collections](schema/collections.md). -[Hierarchies](hierarchy.md) are used for authorization and keeping data organized. -[Invites](invitations.md) can be used to easily create new users and provide them with rights. +Atomic Data Extended is a set of extra modules (on top of Atomic Data Core) that deal with data that changes over time, authentication, and authorization. + +- [Commits](commits/intro.md) communicate state changes. These Commits are signed using cryptographic keys, which ensures that every change can be audited. Commits are also used to construct a history of versions. +- [Agents](agents.md) are Users that enable [authentication](authentication.md). +- [Collections](schema/collections.md) add query options, filetering, sorting and pagination. +- [Paths](core/paths.md) allow you to traverse graphs. +- [Hierarchies](hierarchy.md) are used for authorization and keeping data organized. +- [Invites](invitations.md) can be used to easily create new users and provide them with rights. +- [WebSockets](websockets.md) allow for real-time updates. +- [Endpoints](endpoints.md) provide machine-readable descriptions of web services. ## Get Started diff --git a/src/core/concepts.md b/src/core/concepts.md index 7b6e9a6..d986cee 100644 --- a/src/core/concepts.md +++ b/src/core/concepts.md @@ -24,6 +24,8 @@ It is a directed, labeled graph, similar to RDF, so contrary to some other (labe * **ORM-friendly**: Navigate a _decentralized_ graph by using `dot.syntax`, similar to how you navigate a JSON object in javascript. * **Type-safe**: All valid Atomic data has an unambiguous, static datatype. +# Concepts + ## Resource A _Resource_ is a bunch of information about a thing, referenced by a single link (the Subject). diff --git a/src/extended.md b/src/extended.md new file mode 100644 index 0000000..f918f31 --- /dev/null +++ b/src/extended.md @@ -0,0 +1,17 @@ +{{#title Atomic Data Extended specification}} +# Atomic Data Extended + +Atomic Data is a _modular_ specification, which means that you can choose to implement parts of it. +All parts of Extended are _optional_ to implement. +The _Core_ of the specification (described in the previous chapter) is required for all of the Extended spec to work, but not the other way around. + +However, many of the parts of Extended do rely on _eachother_. + +- [Commits](commits/intro.md) communicate state changes. These Commits are signed using cryptographic keys, which ensures that every change can be audited. Commits are also used to construct a history of versions. +- [Agents](agents.md) are Users that enable [authentication](authentication.md). +- [Collections](schema/collections.md) add query options, filetering, sorting and pagination. +- [Paths](core/paths.md) allow you to traverse graphs. +- [Hierarchies](hierarchy.md) are used for authorization and keeping data organized. +- [Invites](invitations.md) can be used to easily create new users and provide them with rights. +- [WebSockets](websockets.md) allow for real-time updates. +- [Endpoints](endpoints.md) provide machine-readable descriptions of web services. diff --git a/src/files.md b/src/files.md index 04e2034..2c9fc96 100644 --- a/src/files.md +++ b/src/files.md @@ -4,7 +4,7 @@ The Atomic Data model (Atomic Schema) is great for describing structured data, but for many types of existing data, we already have a different way to represent them: files. In Atomic Data, files have two URLs. One _describes_ the file and its metadata, and the other is a URL that downloads the file. -This allows us to present a better view when a user wants to take a look at some file, and lear about its context before downloading it. +This allows us to present a better view when a user wants to take a look at some file, and learn about its context before downloading it. ## The File class diff --git a/src/schema/datatypes.md b/src/schema/datatypes.md index 8dad1d7..aa119ac 100644 --- a/src/schema/datatypes.md +++ b/src/schema/datatypes.md @@ -3,7 +3,7 @@ The Atomic Datatypes consist of some of the most commonly used [Datatypes](classes.md#Datatype). -Please visit for the latest list of official Datatypes. +_Note: Please visit for the latest list of official Datatypes._ ## Slug diff --git a/src/schema/faq.md b/src/schema/faq.md index 9259406..97f6dbc 100644 --- a/src/schema/faq.md +++ b/src/schema/faq.md @@ -9,14 +9,14 @@ Perhaps Generics, or Option like types should be part of the Atomic Base Datatyp ## Do you have an `enum` datatype? -In Atomic Data, `enum` is not a datatype, but it's a constraint that can be added to properties that have +In Atomic Data, `enum` is not a datatype, but it's a constraint that can be added to properties that have. You can set [`allows-only`](https://atomicdata.dev/properties/allowsOnly) on a Property, and use that to limit which values are allowed. ## How should a client deal with Shortname collisions? Atomic Data guarantees Subject-Property uniqueness, which means that Valid Resources are guaranteed to have only one of each Property. Properties offer Shortnames, which are short strings. -These strings SHOULD be unique inside Classes, but these are not guaranteed to be unique inside all Resources. +These strings should be unique inside Classes, but these are not guaranteed to be unique inside all Resources. Note that Resources can have multiple Classes, and through that, they can have colliding Shortnames. Resources are also free to include Properties from other Classes, and their Shortnames, too, might collide. @@ -35,7 +35,7 @@ Let's assume that `https://example.com/name` and `https://another.example.com/so What if a client tries something such as `people123.name`? To consistently return a single value, we need some type of _precedence_: -1. The earlier Class mentioned in the [`isA`](https://atomicdata.dev/properties/isA) Property of the resource. Resources can have multiple classes, but they appear in an ordered ResourceArray. Classes, internally SHOULD have no key collisions in required and recommended properties, which means that they might have. If these exist internally, sort the properties by how they are ordered in the `isA` array - first item is preferred. +1. The earlier Class mentioned in the [`isA`](https://atomicdata.dev/properties/isA) Property of the resource. Resources can have multiple classes, but they appear in an ordered ResourceArray. Classes, internally should have no key collisions in required and recommended properties, which means that they might have. If these exist internally, sort the properties by how they are ordered in the `isA` array - first item is preferred. 1. When the Properties are not part of any of the mentioned Classes, use Alphabetical sorting of the Property URL. When shortname collisions are possible, it's recommended to not use the shortname, but use the URL of the Property: @@ -59,7 +59,7 @@ Another approach, is using [foreign keys (see issue)](https://github.com/ontola/ ## How does Atomic Schema relate to RDF / SHACL / SheX / OWL / RDFS? -Atomic Schema is _the_ schema language for atomic data, whereas RDF has a couple of competing ones, which all vary greatly. +Atomic Schema is _the_ schema language for Atomic Data, whereas RDF has a couple of competing ones, which all vary greatly. In short, OWL is not designed for schema validation, but SHACL and SheX can maybe be compared to Atomic Schema. An important difference is that SHACL and SheX have to deal with all the complexities of RDF, whereas Atomic Data is more constrained. diff --git a/src/when-to-use.md b/src/when-to-use.md index bb11f58..59e8410 100644 --- a/src/when-to-use.md +++ b/src/when-to-use.md @@ -4,9 +4,8 @@ ## When should you use Atomic Data - **Flexible schemas**. When dealing with structured wikis or semantic data, various instances of things will have different attributes. Atomic Data allows _any_ kind of property on _any_ resource. -- **High-value open data**. Atomic Data is a bit harder to create than plain JSON, for example, but it is easier to re-use and understand. It's use of URLs for properties makes data self-documenting. +- **Open data**. Atomic Data is a bit harder to create than plain JSON, for example, but it is easier to re-use and understand. It's use of URLs for properties makes data self-documenting. - **High interoperability requirements**. When multiple groups of people have to use the same schema, Atomic Data provides easy ways to constrain and validate the data and ensure type safety. -- **Multi-class / multi-model**. Contrary to (SQL) tables, Atomic Data allows a single thing to have multiple classes, each with their own properties. - **Connected / decentralized data**. With Atomic Data, you use URLs to point to things on other computers. This makes it possible to connect datasets very explicitly, without creating copies. Very useful for decentralized social networks, for example. - **Auditability & Versioning**. Using Atomic Commits, we can store all changes to data as transactions that can be replayed. This creates a complete audit log and history. - **JSON or RDF as Output**. Atomic Data serializes to idiomatic, clean JSON as well as various RDF formats (Turtle / JSON-LD / n-triples / RDF/XML). From 2ec5efd008bf2e8545d6d9461c0a244a853fbb7e Mon Sep 17 00:00:00 2001 From: Joep Meindertsma Date: Mon, 27 Dec 2021 11:59:50 +0100 Subject: [PATCH 048/140] Improve extended table --- src/atomic-data-overview.md | 9 +-------- src/extended-table.md | 9 +++++++++ src/extended.md | 11 ++--------- src/get-involved.md | 6 +++--- 4 files changed, 15 insertions(+), 20 deletions(-) create mode 100644 src/extended-table.md diff --git a/src/atomic-data-overview.md b/src/atomic-data-overview.md index d8ba462..f2b0af0 100644 --- a/src/atomic-data-overview.md +++ b/src/atomic-data-overview.md @@ -19,14 +19,7 @@ These Properties are responsible for setting the `datatype` (to ensure type-safe Atomic Data Extended is a set of extra modules (on top of Atomic Data Core) that deal with data that changes over time, authentication, and authorization. -- [Commits](commits/intro.md) communicate state changes. These Commits are signed using cryptographic keys, which ensures that every change can be audited. Commits are also used to construct a history of versions. -- [Agents](agents.md) are Users that enable [authentication](authentication.md). -- [Collections](schema/collections.md) add query options, filetering, sorting and pagination. -- [Paths](core/paths.md) allow you to traverse graphs. -- [Hierarchies](hierarchy.md) are used for authorization and keeping data organized. -- [Invites](invitations.md) can be used to easily create new users and provide them with rights. -- [WebSockets](websockets.md) allow for real-time updates. -- [Endpoints](endpoints.md) provide machine-readable descriptions of web services. +{{#include extended-table.md}} ## Get Started diff --git a/src/extended-table.md b/src/extended-table.md new file mode 100644 index 0000000..47f7a39 --- /dev/null +++ b/src/extended-table.md @@ -0,0 +1,9 @@ +- [Commits](commits/intro.md) communicate state changes. These Commits are signed using cryptographic keys, which ensures that every change can be audited. Commits are also used to construct a history of versions. +- [Agents](agents.md) are Users that enable [authentication](authentication.md). They are Resources with their own Public and Private keys, which they use to identify themselves. +- [Collections](schema/collections.md): querying, filetering, sorting and pagination. +- [Paths](core/paths.md): traverse graphs. +- [Hierarchies](hierarchy.md) used for authorization and keeping data organized. Similar to folder structures on filesystems. +- [Invites](invitations.md): create new users and provide them with rights. +- [WebSockets](websockets.md): real-time updates. +- [Endpoints](endpoints.md): provide machine-readable descriptions of web services. +- [Files](files.md): upload, download and metadata for files. diff --git a/src/extended.md b/src/extended.md index f918f31..d779038 100644 --- a/src/extended.md +++ b/src/extended.md @@ -5,13 +5,6 @@ Atomic Data is a _modular_ specification, which means that you can choose to imp All parts of Extended are _optional_ to implement. The _Core_ of the specification (described in the previous chapter) is required for all of the Extended spec to work, but not the other way around. -However, many of the parts of Extended do rely on _eachother_. +However, many of the parts of Extended do depend on _eachother_. -- [Commits](commits/intro.md) communicate state changes. These Commits are signed using cryptographic keys, which ensures that every change can be audited. Commits are also used to construct a history of versions. -- [Agents](agents.md) are Users that enable [authentication](authentication.md). -- [Collections](schema/collections.md) add query options, filetering, sorting and pagination. -- [Paths](core/paths.md) allow you to traverse graphs. -- [Hierarchies](hierarchy.md) are used for authorization and keeping data organized. -- [Invites](invitations.md) can be used to easily create new users and provide them with rights. -- [WebSockets](websockets.md) allow for real-time updates. -- [Endpoints](endpoints.md) provide machine-readable descriptions of web services. +{{#include extended-table.md}} diff --git a/src/get-involved.md b/src/get-involved.md index 19efed4..a2a45ac 100644 --- a/src/get-involved.md +++ b/src/get-involved.md @@ -6,7 +6,7 @@ Atomic Data is an open specification, and that means that you're very welcome to Things you can do: - Join the [Discord server](https://discord.gg/a72Rv2P) for voice / text chat -- Start playing with / contributing to the [`atomic-server / atomic-cli`](https://github.com/joepio/atomic) implementation written in Rust. -- Clone the [Book Repo](https://github.com/ontola/atomic-data/) and read some of the inline comments, which might help start some discussions -- Drop an [issue on Github](https://github.com/ontola/atomic-data/issues) to share your suggestions or criticism +- Start playing with / contributing to [the implementations](tooling.md) +- Drop an [issue on Github](https://github.com/ontola/atomic-data-docs/issues) to share your suggestions or criticism of this book / spec +- Subscribe to the [newsletter](newsletter.md) - Join our [W3C Community Group](https://www.w3.org/community/atomic-data/) From c97ff293cd350f20fd03047f9b1e28fcfb929a25 Mon Sep 17 00:00:00 2001 From: Joep Meindertsma Date: Tue, 28 Dec 2021 13:53:25 +0100 Subject: [PATCH 049/140] improve incomplete resources descirption --- src/endpoints.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/endpoints.md b/src/endpoints.md index c47b536..f0ad6ef 100644 --- a/src/endpoints.md +++ b/src/endpoints.md @@ -16,10 +16,15 @@ You can find a list of Endpoints supported by Atomic-Server on [atomicdata.dev/e Endpoint Resources are _dynamic_, because their properties could be calculated server-side. When a Property tends to be calculated server-side, they will have a [`isDynamic` property](https://atomicdata.dev/properties/isDynamic) set to `true`, which tells the client that it's probably useless to try to overwrite it. -A Server can also send a partial Resource for an Endpoint to the client, which means that some properties may be missing. +## Incomplete resources + +A Server can also send one or more partial Resources for an Endpoint to the client, which means that some properties may be missing. When this is the case, the Resource will have an [`incomplete`](https://atomicdata.dev/properties/incomplete) property set to `true`. This tells the client that it has to individually fetch the resource from the server to get the full body. +One scenario where this happens, is when fetching Collections that have other Collections as members. +If we would not have incomplete resources, the server would have to perform expensive computations even if the data is not needed by the client. + ## Design Goals - **Familiar API**: should look like something that most developers already know From bdaa356a5a180c4579a9d8be493a1cf05cd6d746 Mon Sep 17 00:00:00 2001 From: Joep Meindertsma Date: Tue, 4 Jan 2022 14:25:48 +0100 Subject: [PATCH 050/140] Mention raycast --- src/tooling.md | 5 +++++ src/usecases/intro.md | 9 +++++---- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/tooling.md b/src/tooling.md index 64d2093..2e418d3 100644 --- a/src/tooling.md +++ b/src/tooling.md @@ -76,6 +76,11 @@ Visit https://github.com/joepio/atomic for more info [repository + issue tracker](https://github.com/joepio/atomic). + +### Raycast extension: Full-text search from your desktop + +[Install here](https://www.raycast.com/joepio/atomic). + ## Libraries ### `@tomic/lib` and `@tomic/react` diff --git a/src/usecases/intro.md b/src/usecases/intro.md index b1dfba8..f399883 100644 --- a/src/usecases/intro.md +++ b/src/usecases/intro.md @@ -6,7 +6,8 @@ In this section, we'll present concrete examples of things that can be built wit Although you could use Atomic Data for pretty much any type of application, it is especially valuable where **data re-use**, **standardization**, and **data ownership** are important. -* [Personal Data Store](./personal-data-store.md) -* [E-commerce & marketplaces](./e-commerce.md) -* [Surveys](./surveys.md) -* [Verifiable Credentials](./verifiable-credentials.md) +* [Personal Data Store](personal-data-store.md) +* [E-commerce & marketplaces](e-commerce.md) +* [Surveys](surveys.md) +* [Verifiable Credentials](verifiable-credentials.md) +* [Food labels](food-labels.md) From f51e1d2916d8a2a853fc15a9cb53591cc17c22e4 Mon Sep 17 00:00:00 2001 From: Joep Meindertsma Date: Fri, 7 Jan 2022 16:15:53 +0100 Subject: [PATCH 051/140] Authorization --- src/hierarchy.md | 5 ++++- src/usecases/education.md | 6 ++++++ 2 files changed, 10 insertions(+), 1 deletion(-) create mode 100644 src/usecases/education.md diff --git a/src/hierarchy.md b/src/hierarchy.md index 673290a..01f75cc 100644 --- a/src/hierarchy.md +++ b/src/hierarchy.md @@ -21,7 +21,10 @@ Although you are free to use Atomic Data with your own custom authorization syst - Any Resource can be a `parent` of some other Resource, as long as both Resources exists on the same Atomic Server. - Inversely, every Resource could have `children`. - Only [`Drive`](https://atomicdata.dev/classes/Drive)s (Resources with the class `Drive`) are allowed to be a top-level parent. -- Any Resource might have `read` and `write` Atoms. These both contain a list of Agents. These Agents will be granted the rights to edit (using Commits) or read / use the Resources. + +## Authorization + +- Any Resource might have [`read`](https://atomicdata.dev/properties/read) and [`write`](https://atomicdata.dev/properties/write) Atoms. These both contain a list of Agents. These Agents will be granted the rights to edit (using Commits) or read / use the Resources. - Rights are _additive_, which means that the rights add up. If a Resource itself has no `write` Atom containing your Agent, but it's `parent` _does_ have one, you will still get the `write` right. - Rights cannot be removed by children or parents - they can only be added. diff --git a/src/usecases/education.md b/src/usecases/education.md new file mode 100644 index 0000000..e5b43bb --- /dev/null +++ b/src/usecases/education.md @@ -0,0 +1,6 @@ +# Atomic Data for Eduction - standardized, modular e-learling + +The Atomic Data specification can help make online educational content more modular and standardized. + +- Seperate learning goals from how they are achieved. Some might prefer watching a video, others may want to read. Both can describe the same topic, and share the same test. +- Create links between topics so students know which knowledge is needed to advance to the next topic. From 0c6f5e9ba093e629726518a6cebb9eef883e2db7 Mon Sep 17 00:00:00 2001 From: Joep Meindertsma Date: Tue, 11 Jan 2022 16:18:26 +0100 Subject: [PATCH 052/140] Fix intro --- src/atomic-data-overview.md | 2 +- src/usecases/vocabulary-management.md | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) create mode 100644 src/usecases/vocabulary-management.md diff --git a/src/atomic-data-overview.md b/src/atomic-data-overview.md index f2b0af0..45b9c14 100644 --- a/src/atomic-data-overview.md +++ b/src/atomic-data-overview.md @@ -1,6 +1,6 @@ ![# Atomic Data Docs - Overview](assets/atomic_data_logo_stroke.svg) -**Atomic Data is a modular specification for sharing, modifying and modeling graph data. It combines the ease of use of JSON, the connectivity of RDF (linked data) and the reliability of type-safety.**queried using [Collections](schema/collections.md). +**Atomic Data is a modular specification for sharing, modifying and modeling graph data. It combines the ease of use of JSON, the connectivity of RDF (linked data) and the reliability of type-safety.** ![Venn diagram showing Atomic Data is the combination of JSON, RDF and Type-Safety](assets/venn.svg) diff --git a/src/usecases/vocabulary-management.md b/src/usecases/vocabulary-management.md new file mode 100644 index 0000000..30125a7 --- /dev/null +++ b/src/usecases/vocabulary-management.md @@ -0,0 +1,4 @@ +{{#title Atomic Data for Vocabulary & Taxonomy management}} +# Atomic Data for Vocabulary & Taxonomy management + +Describing abstract concepts, data models, and terms in a consistent way helps organisations From b6b59c4080ade4e62a40d08be4e464be8a5772cc Mon Sep 17 00:00:00 2001 From: Joep Meindertsma Date: Thu, 13 Jan 2022 16:23:07 +0100 Subject: [PATCH 053/140] Auth improvements --- src/atomic-data-overview.md | 2 +- src/authentication.md | 21 +++++++++++++++------ 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/src/atomic-data-overview.md b/src/atomic-data-overview.md index 45b9c14..375b33c 100644 --- a/src/atomic-data-overview.md +++ b/src/atomic-data-overview.md @@ -10,7 +10,7 @@ Atomic Data is especially suitable for knowledge graphs, distributed datasets, s It is designed to be highly extensible, easy to use, and to make the process of domain specific standardization as simple as possible. Atomic Data is [Linked Data](https://ontola.io/what-is-linked-data/), as it is a [strict subset of RDF](interoperability/rdf.md). -It is type-safe (you know if something is a `string`, `number`, `date`, `URL`, etc.) and extensible through [Atomic Schema](schema/intro.md), which means that you can define your own Classes, Properties and Datatypes. +It is type-safe (you know if something is a `string`, `number`, `date`, `URL`, etc.) and extensible through [Atomic Schema](schema/intro.md), which means that you can re-use or define your own Classes, Properties and Datatypes. The default serialization format for Atomic Data is [JSON-AD](core/json-ad.md), which is simply JSON where each key is a URL of an Atomic Property. These Properties are responsible for setting the `datatype` (to ensure type-safety) and setting `shortnames` (which help to keep names short, for example in JSON serialization) and `descriptions` (which provide semantic explanations of what a property should be used for). diff --git a/src/authentication.md b/src/authentication.md index 09dc9fa..3909146 100644 --- a/src/authentication.md +++ b/src/authentication.md @@ -1,18 +1,21 @@ # Authentication in Atomic Data -Atomic Data uses [Hierarchies](hierarchy.md) to describe who gets to access some resource, and who can edit it. -When an Agent wants to _edit_ a resource, they have to send a signed [Commit](commits/intro.md). +Authentication means knowing _who_ is doing something, either getting access or creating some new data. +When an Agent wants to _edit_ a resource, they have to send a signed [Commit](commits/intro.md), and the signatures are checked in order to authorize a Commit. + But how do we deal with _reading_ data, how do we know who is trying to get access? +That's what this page will explain. +The short answer is: **By signing the HTTP request**. ## Design goals - **Secure**: Because, what's the point of authentication if it's not? -- **Ease of use**: Setting up an identity should not require _any_ effort, and proving identity should be minimal effort. +- **Easy to use**: Setting up an identity should not require _any_ effort, and proving identity should be minimal effort. - **Anonimity allowed**: Users should be able to have multiple identities, some of which are fully anonymous. - **Self-sovereign**: No dependency on servers that user's don't control. Or at least, minimise this. - **Dummy-proof**: We need a mechanism for dealing with forgetting passwords / client devices losing data. - **Compatible with Commits**: Atomic Commits require clients to sign things. Ideally, this functionality / strategy would also fit with the new model. -- **Fast**: Of course, authentication will slow things down. But let's keep that to a minimum. +- **Fast**: Of course, authentication will always slow things down. But let's keep that to a minimum. Authentication is done by signing individual (HTTP) requests with the Agent's private key. @@ -62,7 +65,13 @@ const response = await fetch(subject, {headers}); - Since there's only a single HTTP request, we don't have a subject to fetch. Use `ws` as a subject, so sign a string like `ws 12940791247`. +## Hierarchies for authorization + +Atomic Data uses [Hierarchies](hierarchy.md) to describe who gets to access some resource, and who can edit it. + ## Limitations / considerations -- Since we need the Private Key to sign Commits and requests, the client should have this available. This means the client software as well as the user should deal with key management. -- When using the Agent's subject to authenticate somwehere, the authorizer must be able to check what the public key of the agent is. This means the agent must be publicly resolvable. This is one of the reasons we should work towards a server-independent identifier, probably as base64 string that contains the public key (and, optionally, also the https identifier). See the [github issue on DIDs](https://github.com/ontola/atomic-data-docs/issues/59). +- Since we need the Private Key to sign Commits and requests, the client should have this available. This means the client software as well as the user should deal with key management, and that can be a security risk in some contexts (such as a web browser). [See issue #49](https://github.com/ontola/atomic-data-docs/issues/49). +- When using the Agent's subject to authenticate somwehere, the authorizer must be able to check what the public key of the agent is. This means the agent must be publicly resolvable. This is one of the reasons we should work towards a server-independent identifier, probably as base64 string that contains the public key (and, optionally, also the https identifier). See [issue #59 on DIDs](https://github.com/ontola/atomic-data-docs/issues/59). +- Signing every single request takes a bit of time. We picked a fast algorithm (Ed25519) to minimize this cost. +- We'll probably introduce some form of token-based-authentication in the future. [See #87](https://github.com/ontola/atomic-data-docs/issues/87) From fd9e87bb52ca5d2484459c4e8f269e6640937970 Mon Sep 17 00:00:00 2001 From: Joep Meindertsma Date: Sat, 15 Jan 2022 18:06:22 +0100 Subject: [PATCH 054/140] Improve Collections docs --- src/schema/collections.md | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/src/schema/collections.md b/src/schema/collections.md index eb2795b..cdf5f3c 100644 --- a/src/schema/collections.md +++ b/src/schema/collections.md @@ -13,16 +13,15 @@ Note that Collections are designed to be _dynamic resources_, often (partially) Collections are [Endpoints](../endpoints.md), which means that part of their properties are calculated server-side. Collections have various filters (`subject`, `property`, `value`) that can help to build a useful query. -- `members`: How many items (members) are visible per page. -- `subject`: Filter results by a property URL. -- `property`: Filter results by a property URL. -- `value`: Filter results by a Value. -- `sort_by`: A property URL by which to sort. -- `sort_desc`: Sort descending, instead of ascending. Defaults to `false`. -- `current_page`: The number of the current page. -- `page_size`: How many items (members) are visible per page. -- `total_pages`: How many pages there are for the current collection. -- `total_items`: How many items (members) are visible per page. +- [`members`](https://atomicdata.dev/properties/collection/members): How many items (members) are visible per page. +- [`property`](https://atomicdata.dev/properties/collection/property): Filter results by a property URL. +- [`value`](https://atomicdata.dev/properties/collection/value): Filter results by a Value. Combined with `property`, you can create powerful queries. +- [`sort_by`](https://atomicdata.dev/properties/collection/sortBy): A property URL by which to sort. Defaults to the `subject`. +- [`sort_desc`](https://atomicdata.dev/properties/collection/sortDesc): Sort descending, instead of ascending. Defaults to `false`. +- [`current_page`](https://atomicdata.dev/properties/collection/currentPage): The number of the current page. +- [`page_size`](https://atomicdata.dev/properties/collection/pageSize): How many items (members) are visible per page. +- [`total_pages`](https://atomicdata.dev/properties/collection/totalPages): How many pages there are for the current collection. +- [`total_members`](https://atomicdata.dev/properties/collection/totalMembers): How many items (members) are visible per page. ## Persisting Properties vs Query Parameters From 284bf4f05724d6562c8af304c576bc5ab3e54b7b Mon Sep 17 00:00:00 2001 From: Joep Meindertsma Date: Wed, 2 Feb 2022 21:45:48 +0100 Subject: [PATCH 055/140] #90 an an --- src/core/json-ad.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/json-ad.md b/src/core/json-ad.md index 31ce1db..61d6820 100644 --- a/src/core/json-ad.md +++ b/src/core/json-ad.md @@ -1,9 +1,9 @@ {{#title JSON-AD: The Atomic Data serialization format}} # JSON-AD: The Atomic Data serialization format -Although you an use various serialization formats for Atomic Data, `JSON-AD` is the _default_ serialization format. +Although you can use various serialization formats for Atomic Data, `JSON-AD` is the _default_ serialization format. It is what the current [Rust](https://github.com/joepio/atomic) and [Typescript / React](https://github.com/joepio/atomic-data-browser) implementations use to communicate. -It is designed to feel familiar to developers an to be easy and performant to parse and serialize. +It is designed to feel familiar to developers and to be easy and performant to parse and serialize. It is inspired by [JSON-LD](https://json-ld.org/). It uses [JSON](https://www.ecma-international.org/publications-and-standards/standards/ecma-404/), but has some additional constraints: From d31c8d781f4077d160fc0fae8c106f551e13f7cb Mon Sep 17 00:00:00 2001 From: Joep Meindertsma Date: Tue, 8 Feb 2022 10:21:16 +0100 Subject: [PATCH 056/140] Improve nested explanation, graphs, typo --- src/core/concepts.md | 24 +++++------------------- src/core/json-ad.md | 17 +++++++++++++++++ 2 files changed, 22 insertions(+), 19 deletions(-) diff --git a/src/core/concepts.md b/src/core/concepts.md index d986cee..6edf70e 100644 --- a/src/core/concepts.md +++ b/src/core/concepts.md @@ -8,9 +8,7 @@ The _Core_ part, however, is the _only required_ part of the specification, as a Atomic Data Core can be used to express any type of information, including personal data, vocabularies, metadata, documents, files and more. It's designed to be easily serializable to both JSON and linked data formats. -It is _typed_ data model, which means that every value should be validated and predictable. - -It is a directed, labeled graph, similar to RDF, so contrary to some other (labeled) graph data models (e.g. NEO4j), a relationship between two items (Resources) does not have attributes. +It is a _typed_ data model, which means that every value must be validated by their datatype. ## Design goals @@ -137,25 +135,13 @@ A Graph is a collection of Atoms. A Graph can describe various subjects, and may or may not be related. Graphs can have several characteristics (Schema Complete, Valid, Closed) +In mathematial graph terminology, a graph consists of _nodes_ and _edges_. +The Atomic Data model is a so called _directed graph_, which means that relationships are by default one-way. +In Atomic Data, every node is a `Resource`, and every edge is a `Property`. + ## Nested Resource A Nested Resource only exists inside of another resource. It does not have its own subject. -In the following JSON-AD example, the `address` is a nested resource: - -```json -{ - "@id": "https://example.com/arnold", - "https://example.com/properties/address": { - "https://example.com/properties/firstLine": "Longstreet 22", - "https://example.com/properties/city": "Watertown", - "https://example.com/properties/country": "the Netherlands", - } -} -``` - -Nested Resources can be _named_ or _anonymous_. An _Anonymous Nested Resource_ does not have it's own `@id` field. -It _does_ have its own unique [path](./paths.md), which can be used as its identifier. - In the next chapter, we'll explore how Atomic Data is serialized. diff --git a/src/core/json-ad.md b/src/core/json-ad.md index 61d6820..fc4fab9 100644 --- a/src/core/json-ad.md +++ b/src/core/json-ad.md @@ -44,6 +44,23 @@ In JSON-AD, a Resource can be respresented in multiple ways: Note that this is also valid for `ResourceArrays`, which usually only contain Subjects, but are allowed to contain Nested Resources. +In the following JSON-AD example, the `address` is a nested resource: + +```json +{ + "@id": "https://example.com/arnold", + "https://example.com/properties/address": { + "https://example.com/properties/firstLine": "Longstreet 22", + "https://example.com/properties/city": "Watertown", + "https://example.com/properties/country": "the Netherlands", + } +} +``` + +Nested Resources can be _named_ or _anonymous_. An _Anonymous Nested Resource_ does not have it's own `@id` field. +It _does_ have its own unique [path](./paths.md), which can be used as its identifier. +The `path` of the example above is `https://example.com/arnold https://example.com/properties/address`. + ## JSON-AD Parsers, serializers and other libraries - **Typescript / Javacript**: [@tomic/lib](https://www.npmjs.com/package/@tomic/lib) JSON-AD parser + in-memory store. Works with [@tomic/react](https://www.npmjs.com/package/@tomic/lib) for rendering Atomic Data in React. From e356f4255c12bbeeb013c35c25f11134e43bd331 Mon Sep 17 00:00:00 2001 From: hoijui Date: Wed, 9 Feb 2022 11:55:52 +0100 Subject: [PATCH 057/140] Fixes a typo in motivation.md --- src/motivation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/motivation.md b/src/motivation.md index 0ad31ff..4df0d23 100644 --- a/src/motivation.md +++ b/src/motivation.md @@ -18,7 +18,7 @@ Companies often have incentives that are not fully aligned with what users want. For example, Facebook sorts your newsfeed not to make you satisfied, but to make you spend as much time looking at ads. They don't want you to be able to control your own newsfeed. Even companies like Apple, that don't have an ad-revenue model, still have a reason to (and very much do) lock you in. -To make things even worse, even open-source projects made by volunteers often to don't work well together. +To make things even worse, even open-source projects made by volunteers often don't work well together. That's not because of bad intentions, that's because it is _hard_ to make things interoperable. If we want to change this, we need open tech that works really well together. From 51ded2992617f3d292af430b86f8cbc753de3c9e Mon Sep 17 00:00:00 2001 From: Joep Meindertsma Date: Mon, 14 Feb 2022 17:45:56 +0100 Subject: [PATCH 058/140] Add n3 patch --- src/commits/compare.md | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/src/commits/compare.md b/src/commits/compare.md index 1a986c7..cfa776f 100644 --- a/src/commits/compare.md +++ b/src/commits/compare.md @@ -23,9 +23,26 @@ It is designed for collaborating on open source projects, which means dealing wi ## RDF mutation systems +Let's move on to specifications that mutate RDF specifically: + +### .n3 Patch + +N3 Patch is [part of the Solid spec](https://solidproject.org/TR/protocol#writing-resources), since december 2021. + +It uses the N3 serialization format to describe changes to RDF documents. + +``` +@prefix solid: + +<> solid:patches ; + solid:where { ?a . }; + solid:inserts { ?a . }; + solid:deletes { ?a . }. +``` + ### RDF-Delta -[https://afs.github.io/rdf-delta/]() +[https://afs.github.io/rdf-delta/](https://afs.github.io/rdf-delta/) Describes changes (RDF Patches) in a specialized turtle-like serialization format. @@ -139,7 +156,7 @@ An N-Quads serialized delta format. Methods are URLs, which means they are extensible. Does not specify how to bundle lines. Used in production of a web app that we're working on ([Argu.co](https://argu.co)). -Designed with simplicity (no new serialization format, simple to parse) and performance in mind. +Designed with simplicity (no new serialization format, simple to parse) and performance in mind by my colleague Thom van Kalkeren. ``` Initial state: @@ -156,7 +173,7 @@ New state: ``` -## JSON-LD-PATCH +### JSON-LD-PATCH [https://github.com/digibib/ls.ext/wiki/JSON-LD-PATCH]() @@ -250,7 +267,7 @@ It has quite a bunch of implementations, in various languages. [m-ld](https://m-ld.org/doc/) is a specification that uses JSON-LD to enable real-time collaboration. It is fundamentally decentralized --> -## Atomic Commits +## Atomic Commits - how it's different and why it exists Let's talk about the differences between the concepts above and Atomic Commits. From 7db1e3dfe5533bf8a04b037a45d7f9a00c70a770 Mon Sep 17 00:00:00 2001 From: Joep Meindertsma Date: Mon, 14 Feb 2022 17:55:17 +0100 Subject: [PATCH 059/140] Update solid compare --- src/interoperability/solid.md | 36 +++++++++++++++++++++++++++++++---- 1 file changed, 32 insertions(+), 4 deletions(-) diff --git a/src/interoperability/solid.md b/src/interoperability/solid.md index 345fa4d..08944f9 100644 --- a/src/interoperability/solid.md +++ b/src/interoperability/solid.md @@ -53,6 +53,8 @@ Atomic Data has a **uniform write API**. All changes to data are done by posting Commits to the `/commits` endpoint of a Server. This removes the need to think about differences between all sorts of HTTP methods like POST / PUT / PATCH, and how servers should reply to that. +_EDIT: as of december 2021, Solid has introduced `.n3 patch` for standardizing state changes. Although this adds a uniform way of describing changes, it still lacks the power of Atomic Commits. It does not specify signatures, mention versioning, or deals with persisting changesets. On top of that, it is quite difficult to read or parse, being `.n3`._ + ## Atomic Data is more easily serializable to other formats (like JSON) Atomic Data is designed with the modern developer in mind. @@ -76,11 +78,37 @@ Solid uses HTTP based WebID identifiers combined with an OIDC flow. Atomic Data uses `parent-child` [hierarchies](../hierarchy.md) to model data and performan authorization checks. This closely resembles how filesystems work, and is therefore familiar to most users. -## Solid is more mature +## Atomic Data and Solid implementations + +Both Atomic Data and Solid are specifications that have different implementations. + +[Atomic-Server](https://github.com/joepio/atomic-data-rust/) is a database + server that can be considered a serious alternative to Solid Pods. +It was definitely built to be one, at least. +I believe that as of today (february 2022), Atomic-Server has quite a few advantages over existing Solid implementations. + +It has some advantages over existing Solid implementations: + +- **Dynamic schema validation** / type checking using [Atomic Schema](https://docs.atomicdata.dev/schema/intro.html). Combines safety of structured data with the +- **Fast** (1ms responses on my laptop) +- **Lightweight** (15MB binary, no runtime dependencies) +- **Runs everywhere** (linux, windows, mac, arm) +- **HTTPS + HTTP2 support** with Built-in LetsEncrypt handshake. +- **Browser GUI included** powered by [atomic-data-browser](https://github.com/joepio/atomic-data-browser). Features dynamic forms, tables, authentication, theming and more. Easy to use! +- ↩**Event-sourced versioning** / history powered by [Atomic Commits](https://docs.atomicdata.dev/commits/intro.html) +- **Many serialization options**: to JSON, [JSON-AD](https://docs.atomicdata.dev/core/serialization.html#json-ad), and various Linked Data / RDF formats (RDF/XML, N-Triples / Turtle / JSON-LD). +- **Full-text search** with fuzzy search and various operators, often <3ms responses. +- **Pagination, sorting and filtering** using [Atomic Collections](https://docs.atomicdata.dev/schema/collections.html) +- **Authorization** (read / write permissions) and Hierarchical structures powered by [Atomic Hierarchy](https://docs.atomicdata.dev/hierarchy.html) +- **Invite and sharing system** with [Atomic Invites](https://docs.atomicdata.dev/invitations.html) +- **File management** Upload, download and preview attachments. +- **Desktop app** Easy desktop installation, with status bar icon, powered by [tauri](https://github.com/tauri-apps/tauri/). +- **MIT licensed** So fully open-source and free forever! + +## Things that Atomic Data misses, but Solid has -Atomic Data has significant gaps at this moment - not just in the implementations, but also in the spec. -This makes it not yet usable for most applications. +Atomic Data is not even two years old, and although progress has been fast, it does lack some specifications. Here's a list of things missing in Atomic Data, with links to their open issues and links to their existing Solid counterpart. -- No inbox or [notifications](https://www.w3.org/TR/ldn/) ([issue](https://github.com/ontola/atomic-data/issues/28)) +- No inbox or [notifications](https://www.w3.org/TR/ldn/) yet ([issue](https://github.com/ontola/atomic-data/issues/28)) +- No OIDC support yet. ([issue](https://github.com/joepio/atomic-data-rust/issues/277)) - No support from a big community, a well-funded business or the inventor of the world wide web. From 09407c3406a8e671d943e96fe8e9a5bbc4e7fa7c Mon Sep 17 00:00:00 2001 From: Joep Meindertsma Date: Mon, 14 Feb 2022 17:55:25 +0100 Subject: [PATCH 060/140] job matching wip --- src/usecases/job-matching.md | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 src/usecases/job-matching.md diff --git a/src/usecases/job-matching.md b/src/usecases/job-matching.md new file mode 100644 index 0000000..1394c28 --- /dev/null +++ b/src/usecases/job-matching.md @@ -0,0 +1,6 @@ +WIP + +# Atomic Data for job matching and vacancies + +- https://sfia-online.org/en/about-sfia/sfia-guiding-principles +- From caee092c13a3c89c4691b87feb9aab03dc01f361 Mon Sep 17 00:00:00 2001 From: Joep Meindertsma Date: Mon, 14 Feb 2022 18:00:51 +0100 Subject: [PATCH 061/140] Solid corrections --- src/interoperability/solid.md | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/interoperability/solid.md b/src/interoperability/solid.md index 08944f9..62486d1 100644 --- a/src/interoperability/solid.md +++ b/src/interoperability/solid.md @@ -22,6 +22,7 @@ But there are some important **differences**, too, which will be explained in mo - Atomic Data is less mature, and currently lacks things like authentication for read Access _Disclaimer: I've been quite involved in the development of Solid, and have a lot of respect for all the people who are working on it. +Solid and RDF have been important inspirations for the design of Atomic Data. The following is not meant as a critique on Solid, let alone the individuals working on it._ ## Atomic Data is type-safe, because of its built-in schema @@ -86,21 +87,16 @@ Both Atomic Data and Solid are specifications that have different implementation It was definitely built to be one, at least. I believe that as of today (february 2022), Atomic-Server has quite a few advantages over existing Solid implementations. -It has some advantages over existing Solid implementations: - - **Dynamic schema validation** / type checking using [Atomic Schema](https://docs.atomicdata.dev/schema/intro.html). Combines safety of structured data with the - **Fast** (1ms responses on my laptop) - **Lightweight** (15MB binary, no runtime dependencies) -- **Runs everywhere** (linux, windows, mac, arm) - **HTTPS + HTTP2 support** with Built-in LetsEncrypt handshake. - **Browser GUI included** powered by [atomic-data-browser](https://github.com/joepio/atomic-data-browser). Features dynamic forms, tables, authentication, theming and more. Easy to use! - ↩**Event-sourced versioning** / history powered by [Atomic Commits](https://docs.atomicdata.dev/commits/intro.html) - **Many serialization options**: to JSON, [JSON-AD](https://docs.atomicdata.dev/core/serialization.html#json-ad), and various Linked Data / RDF formats (RDF/XML, N-Triples / Turtle / JSON-LD). - **Full-text search** with fuzzy search and various operators, often <3ms responses. - **Pagination, sorting and filtering** using [Atomic Collections](https://docs.atomicdata.dev/schema/collections.html) -- **Authorization** (read / write permissions) and Hierarchical structures powered by [Atomic Hierarchy](https://docs.atomicdata.dev/hierarchy.html) - **Invite and sharing system** with [Atomic Invites](https://docs.atomicdata.dev/invitations.html) -- **File management** Upload, download and preview attachments. - **Desktop app** Easy desktop installation, with status bar icon, powered by [tauri](https://github.com/tauri-apps/tauri/). - **MIT licensed** So fully open-source and free forever! From 371aa84814588fb9335c573668fc038bee176a81 Mon Sep 17 00:00:00 2001 From: Joep Meindertsma Date: Mon, 14 Feb 2022 19:06:17 +0100 Subject: [PATCH 062/140] Solid typos --- src/interoperability/solid.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/interoperability/solid.md b/src/interoperability/solid.md index 62486d1..87932d6 100644 --- a/src/interoperability/solid.md +++ b/src/interoperability/solid.md @@ -83,7 +83,7 @@ This closely resembles how filesystems work, and is therefore familiar to most u Both Atomic Data and Solid are specifications that have different implementations. -[Atomic-Server](https://github.com/joepio/atomic-data-rust/) is a database + server that can be considered a serious alternative to Solid Pods. +[Atomic-Server](https://github.com/joepio/atomic-data-rust/) is a database + server written in the Rust programming language, that can be considered an alternative to Solid Pod implementations. It was definitely built to be one, at least. I believe that as of today (february 2022), Atomic-Server has quite a few advantages over existing Solid implementations. @@ -92,7 +92,7 @@ I believe that as of today (february 2022), Atomic-Server has quite a few advant - **Lightweight** (15MB binary, no runtime dependencies) - **HTTPS + HTTP2 support** with Built-in LetsEncrypt handshake. - **Browser GUI included** powered by [atomic-data-browser](https://github.com/joepio/atomic-data-browser). Features dynamic forms, tables, authentication, theming and more. Easy to use! -- ↩**Event-sourced versioning** / history powered by [Atomic Commits](https://docs.atomicdata.dev/commits/intro.html) +- **Event-sourced versioning** / history powered by [Atomic Commits](https://docs.atomicdata.dev/commits/intro.html) - **Many serialization options**: to JSON, [JSON-AD](https://docs.atomicdata.dev/core/serialization.html#json-ad), and various Linked Data / RDF formats (RDF/XML, N-Triples / Turtle / JSON-LD). - **Full-text search** with fuzzy search and various operators, often <3ms responses. - **Pagination, sorting and filtering** using [Atomic Collections](https://docs.atomicdata.dev/schema/collections.html) From bd75a6cf9fd70c60fbfdbfbd261117963682af76 Mon Sep 17 00:00:00 2001 From: Joep Meindertsma Date: Mon, 14 Feb 2022 19:24:58 +0100 Subject: [PATCH 063/140] typo solid 2 --- src/interoperability/solid.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/interoperability/solid.md b/src/interoperability/solid.md index 87932d6..1cd2034 100644 --- a/src/interoperability/solid.md +++ b/src/interoperability/solid.md @@ -87,7 +87,7 @@ Both Atomic Data and Solid are specifications that have different implementation It was definitely built to be one, at least. I believe that as of today (february 2022), Atomic-Server has quite a few advantages over existing Solid implementations. -- **Dynamic schema validation** / type checking using [Atomic Schema](https://docs.atomicdata.dev/schema/intro.html). Combines safety of structured data with the +- **Dynamic schema validation** / type checking using [Atomic Schema](https://docs.atomicdata.dev/schema/intro.html), combining the best of RDF, JSON and type safety. - **Fast** (1ms responses on my laptop) - **Lightweight** (15MB binary, no runtime dependencies) - **HTTPS + HTTP2 support** with Built-in LetsEncrypt handshake. From 41f7a5250ce728a36516cc7f7d021c4125b5ea86 Mon Sep 17 00:00:00 2001 From: Joep Meindertsma Date: Tue, 15 Feb 2022 10:50:43 +0100 Subject: [PATCH 064/140] Solid dependencies --- src/interoperability/solid.md | 35 ++++++++++++++++++++++++++++++----- 1 file changed, 30 insertions(+), 5 deletions(-) diff --git a/src/interoperability/solid.md b/src/interoperability/solid.md index 1cd2034..6f1161c 100644 --- a/src/interoperability/solid.md +++ b/src/interoperability/solid.md @@ -10,7 +10,7 @@ In many ways, it has **similar goals** to Atomic Data: Technically, both are also similar: -- Usage of personal servers, or PODs (Personal Online Datastores). Both Atomic Data and Solid aim to provide users with a highly personal server where all sorts of data can be stored. +- Usage of **personal servers**, or PODs (Personal Online Datastores). Both Atomic Data and Solid aim to provide users with a highly personal server where all sorts of data can be stored. - Usage of **linked data**. All Atomic Data is valid RDF, which means that **all Atomic Data is compatible with Solid**. However, the other way around is more difficult. In other words, if you choose to use Atomic Data, you can always put it in your Solid Pod. But there are some important **differences**, too, which will be explained in more detail below. @@ -19,7 +19,8 @@ But there are some important **differences**, too, which will be explained in mo - Atomic Data standardizes state changes (which also provides version control / history, audit trails) - Atomic Data is more easily serializable to other formats (like JSON) - Atomic Data has a different model for Authorzation and Hierarchies -- Atomic Data is less mature, and currently lacks things like authentication for read Access +- Atomic Data does not depend on existing semantic web specifications +- Atomic Data is a smaller and younger project, and as of now a one-man show _Disclaimer: I've been quite involved in the development of Solid, and have a lot of respect for all the people who are working on it. Solid and RDF have been important inspirations for the design of Atomic Data. @@ -58,7 +59,7 @@ _EDIT: as of december 2021, Solid has introduced `.n3 patch` for standardizing s ## Atomic Data is more easily serializable to other formats (like JSON) -Atomic Data is designed with the modern developer in mind. +Atomic Data is designed with the modern (web)developer in mind. One of the things that developers expect, is to be able to traverse (JSON) objects easily. Doing this with RDF is not easily possible, because doing this requires _subject-predicate uniqueness_. Atomic Data does not have this problem (properties _must_ be unique), which means that traversing objects becomes easy. @@ -79,13 +80,37 @@ Solid uses HTTP based WebID identifiers combined with an OIDC flow. Atomic Data uses `parent-child` [hierarchies](../hierarchy.md) to model data and performan authorization checks. This closely resembles how filesystems work, and is therefore familiar to most users. -## Atomic Data and Solid implementations +## No dependency on existing semantic web specifications + +The Solid specification (although still in draft) builds on a 20+ year legacy of committee meetings on semantic web standards such as RDF, SPARQL, OWL and XML. +I think the process of designing specifications in [various (fragmented) committees](https://en.wikipedia.org/wiki/Design_by_committee) has led to a set of specifications that lack simplicity and consistency. +Many of these specifications have been written long before there were actual implementations. +Much of the effort was spent on creating highly formal and abstract descriptions of common concepts, but too little was spent on making specs that are easy to use and solve actual problems for developers. + +Aaron Scharz (co-founder or reddit, inventor of RSS and Markdown) wrote this in his [unfinished book 'A Programmable Web'](https://ieeexplore.ieee.org/document/6814657): + +> Instead of the “let’s just build something that works” attitude that made the Web (and the Internet) such a roaring success, they brought the formalizing mindset of mathematicians and the institutional structures of academics and defense +contractors. +> They formed committees to form working groups to write drafts of ontologies that carefully listed (in 100-page Word documents) all possible things in the universe and the various properties they could have, and they spent hours in Talmudic debates over whether a washing machine was a kitchen appliance or a household cleaning device. + +(The book is a great read on this topic, by the way!) + +So, in a nutshell, I think this legacy makes Solid unnecessarily hard to use for developers, for the following reasons: + +- **RDF Quirks**: Solid has to deal with all the [complexities of the RDF data model](./rdf.md), such as blank nodes, named graphs, subject-predicate duplication. +- **Multiple (uncommon) serialization formats** need to be understood, such as `n3`, `shex` and potentially all the various RDF serialization formats. These will feel foreign to most (even very experienced) developers and can have a high degree of complexity. +- **A heritage of broken URLs**. Although a lot if RDF data exists, only a small part of it is actually resolvable as machine-readable RDF. The large majority won't give you the data when sending a HTTP GET request with the correct `Accept` headers to the subject's URL. Much of it is stored in documents on a different URL (`named graphs`), or behind some SPARQL endpoint that you will first need to find. Solid builds on a lot of standards that have these problems. +- **Confusing specifications**. Reading up on RDF, Solid, and the Semantic Web can be a daunting (yet adventurous) task. I've seen many people traverse a similar path as I did: read the RDF specs, dive into OWL, install protege, create ontologies, try doing things that OWL doesn't do (validate data), read more complicated specs that don't help to clear things, become frustrated... It's a bit of a rabbit hole, and I'd like to prevent people from falling into it. There's a lot of interesting ideas there, but it is not a pragmatic framework to develop interoperable apps with. + +## Atomic Data and Solid server implementations Both Atomic Data and Solid are specifications that have different implementations. +Some open source Solid implementations are the [Node Solid Server](https://github.com/solid/node-solid-server), the [Community Solid Server](https://github.com/solid/community-server) (also nodejs based) and the [DexPod](https://gitlab.com/ontola/dexpod) (Ruby on Rails based). [Atomic-Server](https://github.com/joepio/atomic-data-rust/) is a database + server written in the Rust programming language, that can be considered an alternative to Solid Pod implementations. It was definitely built to be one, at least. -I believe that as of today (february 2022), Atomic-Server has quite a few advantages over existing Solid implementations. +It implements every part of the Atomic Data specification. +I believe that as of today (february 2022), Atomic-Server has quite a few advantages over existing Solid implementations: - **Dynamic schema validation** / type checking using [Atomic Schema](https://docs.atomicdata.dev/schema/intro.html), combining the best of RDF, JSON and type safety. - **Fast** (1ms responses on my laptop) From 115bfcd927bff6ef1bc208a077404db80a5cc12f Mon Sep 17 00:00:00 2001 From: Joep Meindertsma Date: Tue, 15 Feb 2022 13:07:33 +0100 Subject: [PATCH 065/140] Upgrade guide --- src/SUMMARY.md | 4 +-- src/interoperability/intro.md | 12 +++++---- src/interoperability/solid.md | 24 ++++++++++++----- src/interoperability/upgrade.md | 47 ++++++++++++++++++++++++++++----- 4 files changed, 67 insertions(+), 20 deletions(-) diff --git a/src/SUMMARY.md b/src/SUMMARY.md index d448cc3..7b9e762 100644 --- a/src/SUMMARY.md +++ b/src/SUMMARY.md @@ -34,14 +34,14 @@ # Using Atomic Data -* [Interoperability](interoperability/intro.md) +* [Interoperability and comparisons](interoperability/intro.md) + * [Making your existing project compatible](interoperability/upgrade.md) * [RDF](interoperability/rdf.md) * [Solid](interoperability/solid.md) * [JSON](interoperability/json.md) * [IPFS](interoperability/ipfs.md) * [SQL](interoperability/sql.md) * [Graph Databases](interoperability/graph-database.md) - * [Your existing project](interoperability/upgrade.md) * [Potential use cases](usecases/intro.md) * [Personal Data Store](usecases/personal-data-store.md) * [E-commerce & marketplaces](usecases/e-commerce.md) diff --git a/src/interoperability/intro.md b/src/interoperability/intro.md index 5591823..8c33a1e 100644 --- a/src/interoperability/intro.md +++ b/src/interoperability/intro.md @@ -1,9 +1,13 @@ -{{#title Atomic Data Interoperability - Relation to other technology}} +{{#title Atomic Data Interoperability - Relationship and comparison to other technology}} # Interoperability: Relation to other technology Atomic data is designed to be easy to use in existing projects, and be interoperable with existing formats. This section will discuss how Atomic Data differs from or is similar to various data formats and paradigms, and how it can interoperate. +## Upgrade guide + +* [Upgrade](upgrade.md): How to make your existing (server-side) application serve Atomic Data. From easy, to hard. + ## Data formats * [JSON](json.md): Atomic Data is designed to be easily serializable to clean, idiomatic JSON. However, if you want to turn JSON into Atomic Data, you'll have to make sure that all keys in the JSON object are URLs that link to Atomic Properties, and the data itself also has to be available at its Subject URL. @@ -11,12 +15,10 @@ This section will discuss how Atomic Data differs from or is similar to various ## Protocols +* [Solid](solid.md): A set of specifications that has many similarities with Atomic Data * [IPFS](ipfs.md): Content-based addressing to prevent 404s and centralization ## Database paradigms * [SQL](sql.md): How Atomic Data differs from and could interact with SQL databases - -## Upgrade guide - -* [Upgrade](upgrade.md): How to make your existing server-side application compatible with Atomic Data +* [Graph](graph-database.md): How it differs from some labeled property graphs, such as Neo4j diff --git a/src/interoperability/solid.md b/src/interoperability/solid.md index 6f1161c..c889294 100644 --- a/src/interoperability/solid.md +++ b/src/interoperability/solid.md @@ -15,10 +15,10 @@ Technically, both are also similar: But there are some important **differences**, too, which will be explained in more detail below. -- Atomic Data uses a strict built-in schema to ensure type safety. +- Atomic Data uses a strict built-in schema to ensure type safety - Atomic Data standardizes state changes (which also provides version control / history, audit trails) - Atomic Data is more easily serializable to other formats (like JSON) -- Atomic Data has a different model for Authorzation and Hierarchies +- Atomic Data has different models for authentication, authorzation and hierarchies - Atomic Data does not depend on existing semantic web specifications - Atomic Data is a smaller and younger project, and as of now a one-man show @@ -69,16 +69,28 @@ Atomic Data uses `shortnames` to map properties to short, human-readable strings For more information about these differences, see the previous [RDF chapter](./rdf.md). -## Hierarchy model, authorization, authentication +## Authentication + +Both Solid an Atomic Data use URLs to refer to individuals / users / Agents. + +Solid's identity system is called WebID. +There are multiple supported authentication protocols, the most common being [WebID-OIDC](https://github.com/solid/webid-oidc-spec). + +Atomic Data's [authentication model](../authentication.md) is more similar to how SSH works. Atomic Data identities (Agents) are a combination of HTTP based, and cryptography (public / private key) based. In Atomic, all actions (from GET requests to Commits) are signed using the private key of the Agent. This makes Atomic Data a bit more unconventional, but also makes its auth mechanism very decentralized and lightweight. -Solid uses HTTP based WebID identifiers combined with an OIDC flow. +## Hierarchy and authorization + +Atomic Data uses `parent-child` [hierarchies](../hierarchy.md) to model data structures and perform authorization checks. +This closely resembles how filesystems work (including things like Google Drive). +Per resource, `write` and `read` rights can be defined, which both contain lists of Agents. + +Solid is working on the [Shape Trees](https://shapetrees.org/TR/specification/) spec, which also describes hierarchies. +It uses SHEX to perform shape validation, similar to how Atomic Schema does. -Atomic Data uses `parent-child` [hierarchies](../hierarchy.md) to model data and performan authorization checks. -This closely resembles how filesystems work, and is therefore familiar to most users. ## No dependency on existing semantic web specifications diff --git a/src/interoperability/upgrade.md b/src/interoperability/upgrade.md index 4fdb76f..3f2540d 100644 --- a/src/interoperability/upgrade.md +++ b/src/interoperability/upgrade.md @@ -1,14 +1,47 @@ -{{#title Upgrade your existing application to Atomic Data}} -# Upgrade your existing application to Atomic Data +{{#title Upgrade your existing application to serve Atomic Data}} +# Upgrade your existing application to serve Atomic Data + +You don't have to use [Atomic-Server](https://crates.io/crates/atomic-server) and ditch your existing projects or apps, if you want to adhere to Atomic Data specs. + +As the Atomic Data spec is modular, you can start out simply and conform to more specs as needed: + +1. Map your JSON keys to new or existing Atomic Data properties +2. Add `@id` fields to your resources, make sure these URLs resolve using HTTP +3. Implement parts of the Extended spec + +There's a couple of levels you can go to, when adhering to the Atomic Data spec. + +## Easy: map your JSON keys to Atomic Data Properties If you want to make your existing project compatible with Atomic Data, you probably don't have to get rid of your existing storage / DB implementation. -The only thing that matters, is how you make the data accessible to others: the serialization. +The only thing that matters, is how you make the data accessible to others: the _serialization_. You can keep your existing software and logic, but simply change the last little part of your API. + In short, this is what you'll have to do: -- Map all properties of resources to Atomic Properties. Either use [existing ones](https://atomicdata.dev/properties), or [create new ones](https://atomicdata.dev/app/new?classSubject=https%3A%2F%2Fatomicdata.dev%2Fclasses%2FProperty&parent=https%3A%2F%2Fatomicdata.dev%2Fagents%2F8S2U%2FviqkaAQVzUisaolrpX6hx%2FG%2FL3e2MTjWA83Rxk%3D&newSubject=https%3A%2F%2Fatomicdata.dev%2Fproperty%2Fsu98ox6tvkh) and make them accessible (using any Atomic Server, as long as the URLs of the properties resolve). -- Make sure that when the user requests some URL, that you return that resource as a JSON-AD object (at the very least if the user requests it using an HTTP `Accept` header). +Map all properties of resources to Atomic Properties. +Either use [existing ones](https://atomicdata.dev/properties), or [create new ones](https://atomicdata.dev/app/new?classSubject=https%3A%2F%2Fatomicdata.dev%2Fclasses%2FProperty&parent=https%3A%2F%2Fatomicdata.dev%2Fagents%2F8S2U%2FviqkaAQVzUisaolrpX6hx%2FG%2FL3e2MTjWA83Rxk%3D&newSubject=https%3A%2F%2Fatomicdata.dev%2Fproperty%2Fsu98ox6tvkh). +This means: take your JSON objects, and change things like `name` to `https://atomicdata.dev/properties/name`. + +That's it, you've done the most important step! + +- Every field has a clear **semantic meaning** and **datatype** +- Your data can now be **easily imported** by Atomic Data systems + +## Medium: add `@id` URLs that properly resolve + +Make sure that when the user requests some URL, that you return that resource as a JSON-AD object (at the very least if the user requests it using an HTTP `Accept: application/ad+json` header). + +- Your data can now be **linked to** by external data sources, it can become part of a **web of data**! + +## Hard: implement Atomic Data Extended protocols + +You can go all out, and implement Commits, Hierarchies, Authentication, Collections and more. +I'd suggest starting with Commits. +Check out the [Atomic-Server source code](https://github.com/joepio/atomic-data-rust/tree/master/server) to get inspired on how to do this. + +## Reach out for help -Don't feel obliged to implement all parts of the Atomic Data spec, such as Collections and Commits. +If you need any help, get in touch in our [Discord](https://discord.gg/a72Rv2P). -If you need any help, get in touch in our [Discord](https://discord.gg/a72Rv2P) +Also, share thoughts on making this process easier in [this issue on github](https://github.com/ontola/atomic-data-docs/issues/95). From 88843e02e07cf34687096bb4040294c64c4a6f1e Mon Sep 17 00:00:00 2001 From: Joep Meindertsma Date: Tue, 15 Feb 2022 13:48:35 +0100 Subject: [PATCH 066/140] upgrade improvements --- src/interoperability/upgrade.md | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/interoperability/upgrade.md b/src/interoperability/upgrade.md index 3f2540d..52c687a 100644 --- a/src/interoperability/upgrade.md +++ b/src/interoperability/upgrade.md @@ -7,7 +7,7 @@ As the Atomic Data spec is modular, you can start out simply and conform to more 1. Map your JSON keys to new or existing Atomic Data properties 2. Add `@id` fields to your resources, make sure these URLs resolve using HTTP -3. Implement parts of the Extended spec +3. Implement parts of the [Extended spec](../extended.md) There's a couple of levels you can go to, when adhering to the Atomic Data spec. @@ -25,23 +25,25 @@ This means: take your JSON objects, and change things like `name` to `https://at That's it, you've done the most important step! +Now your data is already more interoperable: + - Every field has a clear **semantic meaning** and **datatype** - Your data can now be **easily imported** by Atomic Data systems ## Medium: add `@id` URLs that properly resolve -Make sure that when the user requests some URL, that you return that resource as a JSON-AD object (at the very least if the user requests it using an HTTP `Accept: application/ad+json` header). +Make sure that when the user requests some URL, that you return that resource as a [JSON-AD](../core/json-ad.md) object (at the very least if the user requests it using an HTTP `Accept: application/ad+json` header). - Your data can now be **linked to** by external data sources, it can become part of a **web of data**! ## Hard: implement Atomic Data Extended protocols -You can go all out, and implement Commits, Hierarchies, Authentication, Collections and more. -I'd suggest starting with Commits. +You can go all out, and implement Commits, Hierarchies, Authentication, Collections and [more](https://docs.atomicdata.dev/extended.html). +I'd suggest starting with [Commits](../commits.md), as these allow users to modify data whilst maintaining versioning and auditability. Check out the [Atomic-Server source code](https://github.com/joepio/atomic-data-rust/tree/master/server) to get inspired on how to do this. ## Reach out for help -If you need any help, get in touch in our [Discord](https://discord.gg/a72Rv2P). +If you need any help, join our [Discord](https://discord.gg/a72Rv2P). -Also, share thoughts on making this process easier in [this issue on github](https://github.com/ontola/atomic-data-docs/issues/95). +Also, share your thoughts on creating Atomic Data in [this issue on github](https://github.com/ontola/atomic-data-docs/issues/95). From 038eb968ea0f9036edab64c17d059196578bc049 Mon Sep 17 00:00:00 2001 From: Joep Meindertsma Date: Fri, 18 Feb 2022 23:22:15 +0100 Subject: [PATCH 067/140] #80 authorize commit reading --- src/hierarchy.md | 1 + 1 file changed, 1 insertion(+) diff --git a/src/hierarchy.md b/src/hierarchy.md index 01f75cc..657e79f 100644 --- a/src/hierarchy.md +++ b/src/hierarchy.md @@ -27,6 +27,7 @@ Although you are free to use Atomic Data with your own custom authorization syst - Any Resource might have [`read`](https://atomicdata.dev/properties/read) and [`write`](https://atomicdata.dev/properties/write) Atoms. These both contain a list of Agents. These Agents will be granted the rights to edit (using Commits) or read / use the Resources. - Rights are _additive_, which means that the rights add up. If a Resource itself has no `write` Atom containing your Agent, but it's `parent` _does_ have one, you will still get the `write` right. - Rights cannot be removed by children or parents - they can only be added. +- `Commits` can not be edited. They can be `read` if the Agent has rights to read the [`subject`](https://atomicdata.dev/properties/subject) of the `Commit`. ## Authentication From 56b19f2993864bbab7162de072b61eecf29076e3 Mon Sep 17 00:00:00 2001 From: Joep Meindertsma Date: Sun, 20 Feb 2022 11:45:09 +0100 Subject: [PATCH 068/140] #99 dependencies --- src/interoperability/ipfs.md | 4 ++++ src/schema/faq.md | 14 ++++++++++++++ 2 files changed, 18 insertions(+) diff --git a/src/interoperability/ipfs.md b/src/interoperability/ipfs.md index 9608106..ccddcfc 100644 --- a/src/interoperability/ipfs.md +++ b/src/interoperability/ipfs.md @@ -37,3 +37,7 @@ Luckily for Atomic Data, this is often the case, as we know the HTTP url of the IPLD (not IPFS) stands for InterPlanetary Linked Data, but is not related to RDF. The scope seems fundamentally different from RDF, too, but I have to read more about this. + +## Share your thoughts + +Discuss on [this issue](https://github.com/ontola/atomic-data-docs/issues/42). diff --git a/src/schema/faq.md b/src/schema/faq.md index 97f6dbc..976fe7c 100644 --- a/src/schema/faq.md +++ b/src/schema/faq.md @@ -64,3 +64,17 @@ In short, OWL is not designed for schema validation, but SHACL and SheX can mayb An important difference is that SHACL and SheX have to deal with all the complexities of RDF, whereas Atomic Data is more constrained. For more information, see [RDF interoperability](../interoperability/rdf.md). + +## What are the risks of using Schema data hosted somewhere else? + +Every time you use an external URL in your data, you kind of create a dependency. +This is fundamental to linked data. +In Atomic Data, not having access to the Property in some JSON-AD resource will lead to now knowing how to interpret the data itself. +You will no longer know what the Datatype was (other than the native JSON datatype, of course), or what the semantic meaning was of the relationship. + +There are multiple ways we can deal with this: + +- **Cache dependencies**: Atomic Server already stores a copy of every class and property that it uses by default. The `/path` endpoint then allows clients to fetch these from servers that have cached it. If the source goes offline, the validations can still be performed by the server. However, it might be a good idea to migrate the data to a hosted ontology, e.g. by cloning the cached ontology. +- **Content-addressing**: using non-HTTP identifiers, such as with [IPFS](../interoperability/ipfs.md). + +[See discussion](https://github.com/ontola/atomic-data-docs/issues/99). From 099038acb5bbd936258942a83f3542ce9249b321 Mon Sep 17 00:00:00 2001 From: Joep Meindertsma Date: Sun, 20 Feb 2022 11:47:39 +0100 Subject: [PATCH 069/140] #98 JSON-AD required --- src/core/json-ad.md | 2 +- src/core/serialization.md | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/core/json-ad.md b/src/core/json-ad.md index fc4fab9..95718b7 100644 --- a/src/core/json-ad.md +++ b/src/core/json-ad.md @@ -1,7 +1,7 @@ {{#title JSON-AD: The Atomic Data serialization format}} # JSON-AD: The Atomic Data serialization format -Although you can use various serialization formats for Atomic Data, `JSON-AD` is the _default_ serialization format. +Although you can use various serialization formats for Atomic Data, `JSON-AD` is the _default_ and _only required_ serialization format. It is what the current [Rust](https://github.com/joepio/atomic) and [Typescript / React](https://github.com/joepio/atomic-data-browser) implementations use to communicate. It is designed to feel familiar to developers and to be easy and performant to parse and serialize. It is inspired by [JSON-LD](https://json-ld.org/). diff --git a/src/core/serialization.md b/src/core/serialization.md index 187ec7f..63985fc 100644 --- a/src/core/serialization.md +++ b/src/core/serialization.md @@ -3,10 +3,11 @@ Atomic Data is not necessarily bound to a single serialization format. It's fundamentally a data model, and that's an important distinction to make. +It can be serialized in different ways, but there is only one required: `JSON-AD`. ## JSON-AD -However, it's recommended to use [`JSON-AD`](json-ad.md) (more about that on the next page), which is specifically designed to be a simple, complete and performant format for Atomic Data. +[`JSON-AD`](json-ad.md) (more about that on the next page) is specifically designed to be a simple, complete and performant format for Atomic Data. ```json { From daffccb636f80f9547f246aa0220b22f678fa8d1 Mon Sep 17 00:00:00 2001 From: Joep Meindertsma Date: Wed, 23 Feb 2022 22:18:49 +0100 Subject: [PATCH 070/140] Imrove SQL comparison --- src/interoperability/sql.md | 44 +++++++++++++++++++++++++++---------- 1 file changed, 33 insertions(+), 11 deletions(-) diff --git a/src/interoperability/sql.md b/src/interoperability/sql.md index 3e7326f..b55cdfa 100644 --- a/src/interoperability/sql.md +++ b/src/interoperability/sql.md @@ -3,20 +3,17 @@ Atomic Data has some characteristics that make it similar and different from SQL. -- Atomic Data has a _dynamic_ schema. Any Resource could have different properties. However, the properties themselves are validated (contrary to most NOSQL solutions) +- Atomic Data has a _dynamic_ schema. Any Resource could have different properties, so you can **add new properties** to your data without performing any migrations. However, the properties themselves are still validated (contrary to most NoSQL solutions) +- Atomic Data uses **HTTP URLs** in its data, which means it's easy to **share and reuse**. - Atomic Data separates _reading_ and _writing_, whereas SQL has one language for both. -- Atomic Data has a standardized way of storing changes ([Commits](../commits/intro.md)) +- Atomic Data has a standardized way of **storing changes** ([Commits](../commits/intro.md)) ## Tables and Rows vs. Classes and Properties At its core, SQL is a query language based around _tables_ and _rows_. The _tables_ in SQL are similar to `Classes` in Atomic Data: they both define a set of `properties` which an item could have. Every single item in a table is called a _row_ in SQL, and a `Resource` in Atomic Data. - -## Identifiers: numbers vs. URLs - -In SQL, rows have numbers as identifiers, whereas in Atomic Data, every resource has a resolvable HTTP URL as an identifier. -This allows Atomic Data records to be easily re-used by other systems, as there is a guarantee that identifiers will be globally unique. +One difference is that in Atomic Data, you can add new properties to resources, without making changes to any tables (migrations). ## Dynamic vs static schema @@ -28,6 +25,18 @@ In SQL, you'd have to manually adjust the schema of your database to add a new p Atomic Data is a decentralized, open system, which can read new schema data from other sources. SQL is a centralized, closed system, which relies on the DB manager to define the schema. +## Identifiers: numbers vs. URLs + +In SQL, rows have numbers as identifiers, whereas in Atomic Data, every resource has a resolvable HTTP URL as an identifier. +URLs are great identifiers, because you can open them and get more information about something. +This means that with Atomic Data, other systems can re-use your data by referencing to it, and you can re-use data from other systems, too. +With Atomic Data, you're making your data part of a bigger _web of data_, which opens up a lot of possibilities. + +## Atomic Server combines server and database + +If you're building an App with SQL, you will always need some server that connects to your database. +If you're building an App with Atomic Server, the database can function as your server, too. It deals with authentication, authorization, and more. + ## Querying The SQL query language is for both _reading_ and _writing_ data. @@ -40,6 +49,20 @@ In SQL, the one creating the query basically defines the shape of a table that i Atomic Data does not offer such functionality. So if you need to create custom tables at runtime, you might be better off using SQL, or move your Atomic Data to a query system. +## Convert an SQL database to Atomic Data + +If you want to make your existing SQL project serve Atomic Data, you can keep your existing SQL database, see [the upgrade guide](upgrade.md). +It basically boils down to mapping the rows (properties) in your SQL tables to Atomic Data [Properties](https://atomicdata.dev/classes/Property). + +When you want to _import arbitrary Atomic Data_, though, it might be easier to use `atomic-server`. +If you want to store arbitrary Atomic Data in a SQL database, you might be best off by creating a `Resources` table with a `subject` and a `propertyValues` column, or create both a `properties` table and a `resources` one. + +## Limitations of Atomic Data + +- SQL is far more common, many people will know how to use it. +- SQL databases are battle-tested and has been powering countless of products for tens of years, whereas Atomic Server is at this moment in beta. +- SQL databases have a more powerful and expressive query language, where you can define tables in your query and combine resources. + ## FAQ ### Is Atomic Data NOSQL or SQL? @@ -57,8 +80,7 @@ Yes, if you use Atomic-Server, then you can only write to the server by using At This means that if part of the transaction fails, it is reverted - transactions are only applied when they are 100% OK. This prevents inconsistent DB states. -### Can I use a SQL database with Atomic Data? +### How does Atomic Server build indexes for its resources if the schema is not known in advance -Yes, if you want to make your existing project serve Atomic Data, you can keep your existing SQL database, see [the upgrade guide](upgrade.md). -When you want to _import arbitrary Atomic Data_, it might be easier to use `atomic-server`. -If you want to store arbitrary Atomic Data in a SQL database, you might be best off by creating a `Resources` table with a `subject` and a `propertyValues` column, or create both a `properties` table and a `resources` one. +It creates indexed collections when users perform queries. +This means that the first time your perform some type of query (that sorts and filters by some properties), it will be slow, but the next time you perform a similar query, it will be fast. From 909ddd73342e1ba36589106d9be190388075b518 Mon Sep 17 00:00:00 2001 From: Joep Meindertsma Date: Wed, 23 Feb 2022 22:19:01 +0100 Subject: [PATCH 071/140] better JSON-AD --- src/core/json-ad.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/core/json-ad.md b/src/core/json-ad.md index 95718b7..8abb8e3 100644 --- a/src/core/json-ad.md +++ b/src/core/json-ad.md @@ -10,12 +10,12 @@ It uses [JSON](https://www.ecma-international.org/publications-and-standards/sta - Every single Object is a `Resource`. - Every Key is a [`Property`](https://atomicdata.dev/classes/Property) URL. Other keys are invalid. Each Property URL must resolve to an online Atomic Data Property. -- The `@id` field is special: it defines the `Subject` of the `Resource`. +- The `@id` field is special: it defines the `Subject` of the `Resource`. If you send an HTTP GET request there with an `content-type: application/ad+json` header, you should get the full JSON-AD resource. - JSON arrays are mapped to [Resource Arrays](https://atomicdata.dev/datatypes/resourceArray) - Numbers can be [Integers](https://atomicdata.dev/datatypes/integer), [Timestamps](https://atomicdata.dev/datatypes/timestamp) or [Floats](https://atomicdata.dev/datatypes/float). - JSON booleans map to [Booleans](https://atomicdata.dev/datatypes/boolean). - JSON strings can be many datatypes, including [String](https://atomicdata.dev/datatypes/string), [Markdown](https://atomicdata.dev/datatypes/markdown), [Date](https://atomicdata.dev/datatypes/date) or other. -- Nested JSON Objects are Nested Resources. A Nested Resource can either be _Anonymous_ (without an `@id` subject) or a Named Nested Resource with an `@id` subject. Everywhere a Subject URL can be used as a value (i.e. all properties with the datatype [atomicURL](https://atomicdata.dev/datatypes/atomicURL)), a Nested Resource can be used instead. This also means that an item in an `ResourceArray` can be a Nested Resource. +- Nested JSON Objects are Nested Resources. A Nested Resource can either be _Anonymous_ (without an `@id` subject) or a Named Nested Resource (with an `@id` subject). Everywhere a Subject URL can be used as a value (i.e. all properties with the datatype [atomicURL](https://atomicdata.dev/datatypes/atomicURL)), a Nested Resource can be used instead. This also means that an item in an `ResourceArray` can be a Nested Resource. - The root data structure must either be a Named Resource (with an `@id`), or an Array containing Named Resources. When you want to describe multiple Resources in one JSON-AD document, use an array as the root item. Let's look at an example JSON-AD Resource: @@ -59,7 +59,7 @@ In the following JSON-AD example, the `address` is a nested resource: Nested Resources can be _named_ or _anonymous_. An _Anonymous Nested Resource_ does not have it's own `@id` field. It _does_ have its own unique [path](./paths.md), which can be used as its identifier. -The `path` of the example above is `https://example.com/arnold https://example.com/properties/address`. +The `path` of the anonymous resource in the example above is `https://example.com/arnold https://example.com/properties/address`. ## JSON-AD Parsers, serializers and other libraries From 4f4e5dfa4de8d3e1c718031261789f2208bd6fed Mon Sep 17 00:00:00 2001 From: hoijui Date: Wed, 9 Feb 2022 13:54:32 +0100 Subject: [PATCH 072/140] core/concepts.md - typo --- src/core/concepts.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/concepts.md b/src/core/concepts.md index 6edf70e..4736ebe 100644 --- a/src/core/concepts.md +++ b/src/core/concepts.md @@ -132,7 +132,7 @@ This includes URLs, strings, integers, dates and more. ## Graph A Graph is a collection of Atoms. -A Graph can describe various subjects, and may or may not be related. +A Graph can describe various subjects, which may or may not be related. Graphs can have several characteristics (Schema Complete, Valid, Closed) In mathematial graph terminology, a graph consists of _nodes_ and _edges_. From de3c7cccd2e72e12f8a7b839f0e4ca8a7a8782c1 Mon Sep 17 00:00:00 2001 From: Joep Meindertsma Date: Fri, 25 Feb 2022 10:56:59 +0100 Subject: [PATCH 073/140] Fix confusing concepts #92 --- src/core/concepts.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/concepts.md b/src/core/concepts.md index 6edf70e..b1f4aff 100644 --- a/src/core/concepts.md +++ b/src/core/concepts.md @@ -89,8 +89,8 @@ The `@id` field denotes the Subject of each Resource, which is also the URL that In the JSON-AD example above, we have: - two **Resources**, describing two different **Subjects**: `https://example.com/arnold` and `https://example.com/britta`. -- three different **Properties** (`https://example.com/properties/bornAt`, `https://example.com/properties/firstName`, and `https://example.com/properties/bestFriend`) -- four **Values** (`1991-01-20`, `Arnold`, `https://example.com/britta` and `Britta`) +- three different **Properties** (`https://example.com/properties/lastname`, `https://example.com/properties/birthDate`, and `https://example.com/properties/bestFriend`) +- four **Values** (`Peters`, `1991-01-20`, `https://example.com/britta` and `Smalls`) - four **Atoms** - every row is one Atom. All Subjects and Properties are Atomic URLs: they are links that point to more Atomic Data. From 0343c7d236d2dccd0fe845743da673aef09289b2 Mon Sep 17 00:00:00 2001 From: Joep Meindertsma Date: Wed, 2 Mar 2022 17:41:25 +0100 Subject: [PATCH 074/140] Improve property description --- src/core/concepts.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/core/concepts.md b/src/core/concepts.md index 731d10e..bbb00e3 100644 --- a/src/core/concepts.md +++ b/src/core/concepts.md @@ -1,3 +1,4 @@ +{{#title What is Atomic Data?}} # What is Atomic Data? ## Atomic Data Core @@ -118,9 +119,11 @@ In JSON-AD, the Subject is denoted by `@id`. The Property field is the second part of an Atom. It is a URL that points to an Atomic [Property](../schema/classes.md#Property). -For example `https://example.com/createdAt` or `https://example.com/firstName`. +Examples can be found at https://atomicdata.dev/properties. -The Property field MUST be a URL, and that URL MUST resolve to an Atomic Property, which contains information about the Datatype. +The Property field MUST be a URL, and that URL MUST resolve (it must be publicly available) to an Atomic Property. +The Property is perhaps the most important concept in Atomic Data, as it is what enables the type safety (thanks to [`datatype`](https://atomicdata.dev/properties/datatype)) and the JSON compatibility (thanks to [`shortname`](https://atomicdata.dev/properties/shortname)). +We also use Properties for rendering fields in a form, because the Datatype, shortname and description helps us to create an intuitive, easy to understand input for users. ## Value field From 846b597c5afb1c88425c6febb498f3630b03f1c4 Mon Sep 17 00:00:00 2001 From: Joep Meindertsma Date: Wed, 2 Mar 2022 17:44:36 +0100 Subject: [PATCH 075/140] Improve motivation and home --- src/atomic-data-overview.md | 20 ++++++++++++++++++-- src/interoperability/git.md | 3 ++- src/motivation.md | 21 +++++++++++++++++++-- 3 files changed, 39 insertions(+), 5 deletions(-) diff --git a/src/atomic-data-overview.md b/src/atomic-data-overview.md index 375b33c..bfa3a2d 100644 --- a/src/atomic-data-overview.md +++ b/src/atomic-data-overview.md @@ -1,3 +1,4 @@ +{{#title Atomic Data}} ![# Atomic Data Docs - Overview](assets/atomic_data_logo_stroke.svg) **Atomic Data is a modular specification for sharing, modifying and modeling graph data. It combines the ease of use of JSON, the connectivity of RDF (linked data) and the reliability of type-safety.** @@ -6,8 +7,12 @@ Atomic Data uses links to connect pieces of data, and therefore makes it easier to connect datasets to each other - even when these datasets exist on separate machines. -Atomic Data is especially suitable for knowledge graphs, distributed datasets, semantic data, p2p applications, decentralized apps and linked open data. -It is designed to be highly extensible, easy to use, and to make the process of domain specific standardization as simple as possible. +Atomic Data has been designed with [three goals in mind](motivation.md): + +- Give people more control over their data +- Make linked data easier to use +- Make it easier for developers to build highly interoperable apps +- Make standardization easier and cheaper Atomic Data is [Linked Data](https://ontola.io/what-is-linked-data/), as it is a [strict subset of RDF](interoperability/rdf.md). It is type-safe (you know if something is a `string`, `number`, `date`, `URL`, etc.) and extensible through [Atomic Schema](schema/intro.md), which means that you can re-use or define your own Classes, Properties and Datatypes. @@ -15,6 +20,17 @@ It is type-safe (you know if something is a `string`, `number`, `date`, `URL`, e The default serialization format for Atomic Data is [JSON-AD](core/json-ad.md), which is simply JSON where each key is a URL of an Atomic Property. These Properties are responsible for setting the `datatype` (to ensure type-safety) and setting `shortnames` (which help to keep names short, for example in JSON serialization) and `descriptions` (which provide semantic explanations of what a property should be used for). +[Read more about Atomic Data Core](core/concepts.md) + +## Atomizing: how to create, convert and host Atomic Data + +Atomic Data has been designed to be very easy to create and host. +In the Atomizing section, we'll show you how you can create Atomic Data in three ways: + +- Using Atomic Server, from your browser +- By creating JSON-AD (and optionally importing it) +- By including it in your own app + ## Atomic Data Extended Atomic Data Extended is a set of extra modules (on top of Atomic Data Core) that deal with data that changes over time, authentication, and authorization. diff --git a/src/interoperability/git.md b/src/interoperability/git.md index 5af2229..870aa1e 100644 --- a/src/interoperability/git.md +++ b/src/interoperability/git.md @@ -1,4 +1,5 @@ -# Atomic data and Git +{{#title Atomic Data and Git}} +# Atomic Data and Git ## How to manage Atomic Data using GIT diff --git a/src/motivation.md b/src/motivation.md index 4df0d23..27c7d82 100644 --- a/src/motivation.md +++ b/src/motivation.md @@ -5,6 +5,7 @@ Every time a developer builds an app, they have to define their data model. Almost every new app has a `User` with a `name` and an `e-mail` field, and so on. --> + ## Give people more control over their data The world wide web was designed by Tim Berners-Lee to be a decentralized network of servers that help people share information. @@ -53,7 +54,23 @@ I believe the lack of growth partially has to do with a lack of tooling, but als Atomic Data aims to take the best parts from RDF, and learn from the past to make a more developer-friendly, performant and reliable data model to achieve a truly linked web. Read more about [how Atomic Data relates to RDF, and why these changes have been made](interoperability/rdf.md). -## Make it easier for developers to build really good, interoperable apps +## Make standardization easier and cheaper + +Standards for data sharing are great, but creating one can be very costly endeavor. +Committees with stakeholders write endless documents describing the intricacies of domain models, which fields are allowed and which are required, and how data is serialized. +In virtually all cases, these documents are only written for humans - and not for computers. +Machine readable ways to describe data models like UML diagrams and OpenAPI specifications (also known as Swagger) help to have machine-readable descriptions, but these are still not _really_ used by machines - they are mostly only used to generate _visualizations for humans_. +This ultimately means that implementations of a standard have to be _manually checked_ for compliance, which often results in small (yet important) differences that severely limit interoperability. +These implementations will also often want to _extend_ the original definitions, but they are almost always unable to describe _what_ they have extended. + +Standardizing with Atomic Data solves these issues. +Atomic Data takes the semantic value of ontologies, and merges it with a machine-readable [schemas](schema/intro.md). +This makes standards created using Atomic Data easy to read for humans, and easy to validate for computers (which guarantees interoperability). +Atomic Data has a highly standardized protocol for fetching data, which means that Atomic Schemas can link to each other, and _re-use existing Properties_. +For developers (the people who need to actually implement and use the data that has been standardized), this means their job becomes easier. +Because Properties have URLs, it becomes trivial to _add new Properties_ that were initially not in the main specification, without sacrificing type safety and validation abilities. + +## Make it easier for developers to build feature-rich, interoperable apps Every time a developer builds an application, they have to figure a lot of things out. How to design the API, how to implement forms, how to deal with authentication, authorization, versioning, search... @@ -61,4 +78,4 @@ By having a more complete, strict standard, Atomic Data aims to decrease this bu [Atomic Schema](schema/intro.md) enables developers to easily share their datamodels, and re-use those from others. [Atomic Commits](commits/intro.md) helps developers to deal with versioning, history, undo and audit logs. [Atomic Hierarchies](hierarchy.md) provides an intuitive model for authorization and access control. -And finally, the [existing](tooling.md) open source libraries, server, GUI and templates help developers to have these features without writing them. +And finally, the [existing open source Atomic Data software](tooling.md) (such as a server + database, a browser GUI, various libraries and React templates) help developers to have these features without having to do the heavy lifting themselves. From e3ded8404a9c3ab45d640dc18b6e3c96bab292f8 Mon Sep 17 00:00:00 2001 From: Joep Meindertsma Date: Wed, 2 Mar 2022 23:12:31 +0100 Subject: [PATCH 076/140] Fix url --- src/interoperability/upgrade.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/interoperability/upgrade.md b/src/interoperability/upgrade.md index 52c687a..019dca9 100644 --- a/src/interoperability/upgrade.md +++ b/src/interoperability/upgrade.md @@ -39,7 +39,7 @@ Make sure that when the user requests some URL, that you return that resource as ## Hard: implement Atomic Data Extended protocols You can go all out, and implement Commits, Hierarchies, Authentication, Collections and [more](https://docs.atomicdata.dev/extended.html). -I'd suggest starting with [Commits](../commits.md), as these allow users to modify data whilst maintaining versioning and auditability. +I'd suggest starting with [Commits](../commits/intro.md), as these allow users to modify data whilst maintaining versioning and auditability. Check out the [Atomic-Server source code](https://github.com/joepio/atomic-data-rust/tree/master/server) to get inspired on how to do this. ## Reach out for help From fd5cb805be5d48fc538659ffa5581df340d681ad Mon Sep 17 00:00:00 2001 From: Joep Meindertsma Date: Tue, 19 Apr 2022 17:30:45 +0200 Subject: [PATCH 077/140] #93 add atomizing chapter (#106) * #93 add atomizing chapter * WIP atomizing * #93 improve create json-ad * #109 authorization typo * Goals typo * WIP #93 atomizing * typos * #93 improve atomic-server instructions * typos * #93 atomizing in overview --- src/SUMMARY.md | 10 ++- src/atomic-data-overview.md | 25 +++---- src/atomic-server.md | 114 ++++++++++++++++++++++++++++ src/atomizing.md | 21 ++++++ src/create-json-ad.md | 137 ++++++++++++++++++++++++++++++++++ src/endpoints.md | 2 +- src/get-involved.md | 8 ++ src/interoperability/solid.md | 4 +- src/motivation.md | 12 ++- 9 files changed, 313 insertions(+), 20 deletions(-) create mode 100644 src/atomic-server.md create mode 100644 src/atomizing.md create mode 100644 src/create-json-ad.md diff --git a/src/SUMMARY.md b/src/SUMMARY.md index 7b9e762..a09fa2a 100644 --- a/src/SUMMARY.md +++ b/src/SUMMARY.md @@ -32,10 +32,16 @@ * [Collections, filtering, sorting](schema/collections.md) * [Uploading and downloading files](files.md) -# Using Atomic Data +# Create Atomic Data + +* [Atomizing](atomizing.md) + * [Using Atomic-Server](atomic-server.md) + * [Creating a JSON-AD file](create-json-ad.md) + * [Upgrade your existing project](interoperability/upgrade.md) + +# Use Atomic Data * [Interoperability and comparisons](interoperability/intro.md) - * [Making your existing project compatible](interoperability/upgrade.md) * [RDF](interoperability/rdf.md) * [Solid](interoperability/solid.md) * [JSON](interoperability/json.md) diff --git a/src/atomic-data-overview.md b/src/atomic-data-overview.md index bfa3a2d..77226ed 100644 --- a/src/atomic-data-overview.md +++ b/src/atomic-data-overview.md @@ -7,7 +7,7 @@ Atomic Data uses links to connect pieces of data, and therefore makes it easier to connect datasets to each other - even when these datasets exist on separate machines. -Atomic Data has been designed with [three goals in mind](motivation.md): +Atomic Data has been designed with [the following goals in mind](motivation.md): - Give people more control over their data - Make linked data easier to use @@ -22,25 +22,22 @@ These Properties are responsible for setting the `datatype` (to ensure type-safe [Read more about Atomic Data Core](core/concepts.md) -## Atomizing: how to create, convert and host Atomic Data - -Atomic Data has been designed to be very easy to create and host. -In the Atomizing section, we'll show you how you can create Atomic Data in three ways: - -- Using Atomic Server, from your browser -- By creating JSON-AD (and optionally importing it) -- By including it in your own app - ## Atomic Data Extended Atomic Data Extended is a set of extra modules (on top of Atomic Data Core) that deal with data that changes over time, authentication, and authorization. {{#include extended-table.md}} -## Get Started +## Atomizing: how to create, convert and host Atomic Data + +Atomic Data has been designed to be very easy to create and host. +In the Atomizing section, we'll show you how you can create Atomic Data in three ways: + +- [Using Atomic Server, from your browser](atomic-server.md) +- [By creating JSON-AD (and optionally importing it)](create-json-ad.md) +- [By upgrading your existing application](interoperability/upgrade.md) -If you want to read more about how Atomic Data works - read on. -If you'd rather play and discover for yourself, play with the existing open source [tooling](tooling.md): +## Tools & libraries - Browser app [atomic-data-browser](https://github.com/joepio/atomic-data-browser) ([demo on atomicdata.dev](https://atomicdata.dev)) - Build a react app using [typescript & react libraries](https://github.com/joepio/atomic-data-ts). Start with the [react template on codesandbox](https://codesandbox.io/s/atomic-data-react-template-4y9qu?file=/src/MyResource.tsx) @@ -48,6 +45,8 @@ If you'd rather play and discover for yourself, play with the existing open sour - Discover the command line tool: [atomic-cli](https://github.com/joepio/atomic) (`cargo install atomic-cli`) - Use the Rust library: [atomic-lib](https://github.com/joepio/atomic) +## Get involved + Make sure to [join our Discord](https://discord.gg/a72Rv2P) if you'd like to discuss Atomic Data with others. ## Status diff --git a/src/atomic-server.md b/src/atomic-server.md new file mode 100644 index 0000000..46c0b67 --- /dev/null +++ b/src/atomic-server.md @@ -0,0 +1,114 @@ +# Creating Atomic Data using Atomic-Server + +[`Atomic-Server`](https://github.com/joepio/atomic-data-rust/blob/master/server/README.md) is the _reference implementation_ of the Atomic Data Core + Extended specification. +It was developed parallel to this specification, and it served as a testing ground for various ideas (some of which didn't work, and some of which ended up in the spec). + +Atomic-Server is a graph database server for storing and sharing typed linked data. +It's free, open source (MIT license), and has a ton of features: + +- ⚛️ **Dynamic schema validation** / type checking using [Atomic Schema](https://docs.atomicdata.dev/schema/intro.html). Combines safety of structured data with the +- 🚀 **Fast** (1ms responses on my laptop) +- 🪶 **Lightweight** (15MB binary, no runtime dependencies) +- 💻 **Runs everywhere** (linux, windows, mac, arm) +- 🌐 **Embedded server** with support for HTTP / HTTPS / HTTP2.0 and Built-in LetsEncrypt handshake. +- 🎛️ **Browser GUI included** powered by [atomic-data-browser](https://github.com/joepio/atomic-data-browser). Features dynamic forms, tables, authentication, theming and more. +- 💾 **Event-sourced versioning** / history powered by [Atomic Commits](https://docs.atomicdata.dev/commits/intro.html) +- 🔄 **Synchronization using websockets**: communicates state changes with a client. Send a `wss` request to `/ws` to open a webscocket. +- 🧰 **Many serialization options**: to JSON, [JSON-AD](https://docs.atomicdata.dev/core/json-ad.html), and various Linked Data / RDF formats (RDF/XML, N-Triples / Turtle / JSON-LD). +- 🔎 **Full-text search** with fuzzy search and various operators, often <3ms responses. +- 📖 **Pagination, sorting and filtering** using [Atomic Collections](https://docs.atomicdata.dev/schema/collections.html) +- 🔐 **Authorization** (read / write permissions) and Hierarchical structures powered by [Atomic Hierarchy](https://docs.atomicdata.dev/hierarchy.html) +- 📲 **Invite and sharing system** with [Atomic Invites](https://docs.atomicdata.dev/invitations.html) +- 📂 **File management**: Upload, download and preview attachments. +- 🖥️ **Desktop app**: Easy desktop installation, with status bar icon, powered by [tauri](https://github.com/tauri-apps/tauri/). + +## Running Atomic-Server locally (optional) + +In this guide, we'll can simply use `atomicdata.dev` in our browser without installing anything. +So you can skip this step and go to _Creating your first Atomic Data_. +But if you want to, you can run Atomic-Server on your machine in a couple of ways: + +- **Using a desktop installer**: download a desktop release from the [`releases`](https://github.com/joepio/atomic-data-rust/releases) page and install it using your desktop GUI. +- **Using a binary**: download a binary release from the [`releases`](https://github.com/joepio/atomic-data-rust/releases) page and open it using a terminal. +- **Using Docker** is probably the quickest: `docker run -p 80:80 -p 443:443 -v atomic-storage:/atomic-storage joepmeneer/atomic-server`. +- **Using Cargo**: `cargo install atomic-server` and then run `atomic-server` to start. + +_[Atomic-Server's README](https://github.com/joepio/atomic-data-rust/blob/master/server/README.md) contains more (and up-to-date) information about how to use it!_ + +Open your server in your browser. +By default, that's [`http://localhost:9883`](http://localhost:9883). +Fun fact: `⚛` is HTML entity code for the Atom icon: ⚛. + +The first screen should show you your [_Drive_](https://atomicdata.dev/classes/Drive). +You can think of this as your root folder. +It is the resource hosted at the root URL, effectively being the home page of your server. + +There's an instruction on the screen about the `/setup` page. +Click this, and you'll get a screen showing an [_Invite_](https://atomicdata.dev/classes/Invite). +Normally, you could `Accept as new user`, but since you're running on `localhost`, you won't be able to use the newly created Agent on non-local Atomic-Servers. +Therefore, it may be best to create an Agent on some _other_ running server, such as the [demo Invite on AtomicData.dev](https://atomicdata.dev/invites/1). +And after that, copy the Secret from the `User settings` panel from AtomicData.dev, go back to your `localhost` version, and press `sign in`. +Paste the Secret, and voila! You're signed in. + +Now, again go to `/setup`. This time, you can `Accept as {user}`. +After clicking, your Agent has gotten `write` rights for the Drive! +You can verify this by hovering over the description field, clicking the edit icon, and making a few changes. +You can also press the menu button (three dots, top left) and press `Data view` to see your agent after the `write` field. +Note that you can now edit every field. +You can also fetch your data now as various formats. + +Try checking out the other features in the menu bar, and check out the `collections`. + +Again, check out the [README](https://github.com/joepio/atomic-data-rust/blob/master/server/README.md) for more information and guides! + +Now, let's create some data. + +## Creating an Agent + +Before you can create new things on AtomicData.dev, you'll need an _Agent_. +This is your virtual User, which can create, sign and own things. + +Simply open the [demo invite](https://atomicdata.dev/invites/1) and press accept. +And you're done! + + +## Creating your first Atomic Data + +Now let's create a [_Class_](https://atomicdata.dev/classes/Class). +A Class represents an abstract concept, such as a `BlogPost` (which we'll do here). +We can do this in a couple of ways: + +- Press the `new resource` button on the left menu, and selecting Class +- Opening [Class](https://atomicdata.dev/classes/Class) and pressing `new class` +- Going to the [Classes Collection](https://atomicdata.dev/classes/) and pressing the plus icon + +The result is the same: we end up with a form in which we can fill in some details. + +Let's add a shortname, and then a description. + +After that, we'll add the `required` properties. +This form you're looking at is constructed by using the `required` and `recommended` Properties defined in `Class`. +We can use these same fields to generate our BlogPost resource! +Which fields would be required in a `BlogPost`? +A `name`, and a `description`, probably. + +So let's search for these Properties, and add them. + +Now, we can skip the `recommended` properties, and get right to saving our newly created `BlogPost` class. +So, press save, and now look at what you created. + +Notice a couple of things: + +- Your Class has its own URL. +- It has a `parent`, shown in the top of the screen. This has impact on the visibility and rights of your Resource. We'll get to that [later in the documentation](./hierarchy.md). + +Now, use the context menu to open the `Data View`. +This gives you some more insight into your newly created data, and various ways in which you van serialize it. + +## There's more! + +This was just a very brief introduction to Atomic Server, and its features. +There's quite a bit that we didn't dive in to, such as versioning, file uploads, the collaborative document editor and more... +But by clicking around you're likely to discover these features for yourself. + +In the next page, we'll dive into how you can create an publish JSON-AD files. diff --git a/src/atomizing.md b/src/atomizing.md new file mode 100644 index 0000000..c483f81 --- /dev/null +++ b/src/atomizing.md @@ -0,0 +1,21 @@ +# Atomizing: How to create and publish Atomic Data + +Now that we're familiar with the basics of Atomic Data Core and its Schema, it's time to create some Atomic Data! +We call the process of turning data into Atomic Data _Atomizing_. +During this process, we **upgrade the data quality**. +Our information becomes more valuable. +Let's summarize what the advantages are: + +- Your data becomes **available on the web** (publicly, if you want it to) +- It can now **link to other data**, an become part of a bigger web of data +- It becomes **strictly typed**, so developers can easily and safely re-use it in their software +- It becomes **easier to understand**, because people can look at the Properties and see what they mean +- It can be **easily converted** into many formats (JSON, Turtle, CSV, XML, more...) + +## Three ways to Atomize data + +In general, there are three ways to create Atomic Data: + +- [Using the **Atomic-Server** app + GUI](./atomic-server.md) (easy, only for direct user input) +- [Create an **importable JSON-AD file**](./create-json-ad.md) (medium, useful if you want to convert existing data) +- [Make your existing service / app **host and serialize Atomic Data**](./interoperability/upgrade.md) (hard, if you want to make your entire app be part of the Atomic Web!) diff --git a/src/create-json-ad.md b/src/create-json-ad.md new file mode 100644 index 0000000..cc495e3 --- /dev/null +++ b/src/create-json-ad.md @@ -0,0 +1,137 @@ +# How to create and publish a JSON-AD file + +[JSON-AD](core/json-ad.md) is the default serialization format of Atomic Data. +It's just JSON, but with some extra requirements. + +Most notably, all keys are links to [Atomic Properties](https://atomicdata.dev/classes/Property). +These Properties must be actually hosted somewhere on the web, so other people can visit them to read more about them. + +Ideally, in JSON-AD, each Resource has its own `@id`. +This is the URL of the resource. +This means that if someone visits that `@id`, they should get the resource they are requesting. +That's great for people re-using your data, but as a data provider, implementing this can be a bit of a hassle. +That's why there is a different way that allows you to create Atomic Data _without manually hosting every resource_. + +## Creating JSON-AD without hosting individual resources yourself + +In this section, we'll create a single JSON-AD file containing various resources. +This file can then be published, shared and stored like any other. + +The goal of this preparation, is to ultimately import it somewhere else. +We'll be importing it to Atomic-Server. +Atomic-Server will create URLs for every single resource upon importing it. +This way, we only deal with the JSON-AD and the data structure, and we let Atomic-Server take care of hosting the data. + +Let's create a BlogPost. +We know the fields that we need: a `name` and some `body`. +But we can't use these keys in Atomic Data, we should use URLs that point to Properties. +We can either create new Properties (see the Atomic-Server tutorial), or we can use existing ones, for example by searching on [AtomicData.dev/properties](https://atomicdata.dev/properties). + +## Setting the first values + +```json +{ + "https://atomicdata.dev/properties/name": "Writing my first blogpost", + "https://atomicdata.dev/properties/description": "Hi! I'm a blogpost. I'm also machine readable!", +} +``` + +## Adding a Class + +Classes help others understanding what a Resource's type is, such as BlogPost or Person. +In Atomic Data, Resources can have multiple classes, so we should use an Array, like so: + +```json +{ + "https://atomicdata.dev/properties/name": "Writing my first blogpost", + "https://atomicdata.dev/properties/description": "Hi! I'm a blogpost. I'm also machine readable!", + "https://atomicdata.dev/properties/isA": ["https://atomicdata.dev/classes/Blogpost"], +} +``` + +Adding a Class helps people to understand the data, and it can provide guarantees to the data users about the _shape_ of the data: they now know which fields are _required_ or _recommended_. +We can also use Classes to render Forms, which can be useful when the data should be edited later. +For example, the BlogPost item + +## Using exsisting Ontologies, Classes and Ontologies + +Ontologies are groups of concepts that describe some domain. +For example, we could have an Ontology for Blogs that links to a bunch of related _Classes_, such as BlogPost and Person. +Or we could have a Recipy Ontology that describes Ingredients, Steps and more. + +At this moment, there are relatively few Classes created in Atomic Data. +You can find most on [atomicdata.dev/classes](https://atomicdata.dev/classes). + +So possibly the best way forward for you, is to define a Class using the Atomic Data Browser's tools for making resources. + +## Multiple items + +If we want to have _multiple_ items, we can simply use a JSON Array at the root, like so: + +```json +[{ + "https://atomicdata.dev/properties/name": "Writing my first blogpost", + "https://atomicdata.dev/properties/description": "Hi! I'm a blogpost. I'm also machine readable!", + "https://atomicdata.dev/properties/isA": ["https://atomicdata.dev/classes/Blogpost"], +},{ + "https://atomicdata.dev/properties/name": "Another blogpost", + "https://atomicdata.dev/properties/description": "I'm writing so much my hands hurt.", + "https://atomicdata.dev/properties/isA": ["https://atomicdata.dev/classes/Blogpost"], +}] +``` + +## Preventing duplication with `localId` + +When we want to _publish_ Atomic Data, we also want someone else to be able to _import_ it. +An important thing to prevent, is _data duplication_. +If you're importing a list of Blog posts, for example, you'd want to only import every article _once_. + +The way to preventing duplication, is by adding a `localId`. +This `localId` is used by the importer to find out if it has already imported it before. +So we, as data producers, need to make sure that our `localId` is _unique_ and _does not change_! +We can use any type of string that we like, as long as it conforms to these requirements. +Let's use a unique _slug_, a short name that is often used in URLs. + +```json +{ + "https://atomicdata.dev/properties/name": "Writing my first blogpost", + "https://atomicdata.dev/properties/description": "Hi! I'm a blogpost. I'm also machine readable!", + "https://atomicdata.dev/properties/isA": ["https://atomicdata.dev/classes/Blogpost"], + "https://atomicdata.dev/properties/localId": "my-first-blogpost", +} +``` + +## Describing relationships between resources using `localId` + +Let's say we also want to describe the `author` of the BlogPost, and give them an e-mail, a profile picture and some biography. +This means we need to create a new Resource for each Author, and again have to think about the properties relevant for Author. +We'll also need to create a link from BlogPost to Author, and perhaps the other way around, too. + +Normally, when we link things in Atomic Data, we can only use full URLs. +But, since we don't have URLs yet for our Resources, we'll need a different solution. +Again, this is where we can use `localId`! +We can simply refer to the `localId`, instead of some URL that does not exist yet. + +```json +[{ + "https://atomicdata.dev/properties/name": "Writing my first blogpost", + "https://atomicdata.dev/properties/description": "Hi! I'm a blogpost. I'm also machine readable!", + "https://atomicdata.dev/properties/author": "jon", + "https://atomicdata.dev/properties/isA": ["https://atomicdata.dev/classes/Blogpost"], + "https://atomicdata.dev/properties/localId": "my-first-blogpost", +},{ + "https://atomicdata.dev/properties/name": "Another blogpost", + "https://atomicdata.dev/properties/description": "I'm writing so much my hands hurt.", + "https://atomicdata.dev/properties/author": "jon", + "https://atomicdata.dev/properties/isA": ["https://atomicdata.dev/classes/Blogpost"], + "https://atomicdata.dev/properties/localId": "another-blogpost", +},{ + "https://atomicdata.dev/properties/name": "Jon Author", + "https://atomicdata.dev/properties/isA": ["https://atomicdata.dev/classes/Person"], + "https://atomicdata.dev/properties/localId": "jon", +}] +``` + +## Importing data using Atomic Sever + +_currently under development_ diff --git a/src/endpoints.md b/src/endpoints.md index f0ad6ef..1c62db0 100644 --- a/src/endpoints.md +++ b/src/endpoints.md @@ -1,4 +1,4 @@ -{{#title Atomic Data Endpoitns - specity how HTTP endpoints behave}} +{{#title Atomic Data Endpoints - describe how RESTful HTTP APIs behave}} # Atomic Endpoints _URL: https://atomicdata.dev/classes/Endpoint_ diff --git a/src/get-involved.md b/src/get-involved.md index a2a45ac..c1d131d 100644 --- a/src/get-involved.md +++ b/src/get-involved.md @@ -10,3 +10,11 @@ Things you can do: - Drop an [issue on Github](https://github.com/ontola/atomic-data-docs/issues) to share your suggestions or criticism of this book / spec - Subscribe to the [newsletter](newsletter.md) - Join our [W3C Community Group](https://www.w3.org/community/atomic-data/) + + + diff --git a/src/interoperability/solid.md b/src/interoperability/solid.md index c889294..27d3a90 100644 --- a/src/interoperability/solid.md +++ b/src/interoperability/solid.md @@ -18,7 +18,7 @@ But there are some important **differences**, too, which will be explained in mo - Atomic Data uses a strict built-in schema to ensure type safety - Atomic Data standardizes state changes (which also provides version control / history, audit trails) - Atomic Data is more easily serializable to other formats (like JSON) -- Atomic Data has different models for authentication, authorzation and hierarchies +- Atomic Data has different models for authentication, authorization and hierarchies - Atomic Data does not depend on existing semantic web specifications - Atomic Data is a smaller and younger project, and as of now a one-man show @@ -126,7 +126,7 @@ I believe that as of today (february 2022), Atomic-Server has quite a few advant - **Dynamic schema validation** / type checking using [Atomic Schema](https://docs.atomicdata.dev/schema/intro.html), combining the best of RDF, JSON and type safety. - **Fast** (1ms responses on my laptop) -- **Lightweight** (15MB binary, no runtime dependencies) +- **Lightweight** (8MB download, no runtime dependencies) - **HTTPS + HTTP2 support** with Built-in LetsEncrypt handshake. - **Browser GUI included** powered by [atomic-data-browser](https://github.com/joepio/atomic-data-browser). Features dynamic forms, tables, authentication, theming and more. Easy to use! - **Event-sourced versioning** / history powered by [Atomic Commits](https://docs.atomicdata.dev/commits/intro.html) diff --git a/src/motivation.md b/src/motivation.md index 27c7d82..4016197 100644 --- a/src/motivation.md +++ b/src/motivation.md @@ -1,10 +1,18 @@ {{#title Motivation for creating Atomic Data}} # Motivation: Why Atomic Data? - +Almost every new app has a `User` with a `name` and an `e-mail` field, and so on. + +## Waste less time + +- + +--> ## Give people more control over their data From e839df467ab94abe6b8493af2a5220ee74b6edee Mon Sep 17 00:00:00 2001 From: Joep Meindertsma Date: Tue, 19 Apr 2022 16:40:40 +0200 Subject: [PATCH 078/140] Add a discord widget --- theme/index.hbs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/theme/index.hbs b/theme/index.hbs index f9d3c5a..a2c46d7 100644 --- a/theme/index.hbs +++ b/theme/index.hbs @@ -293,6 +293,13 @@ {{/if}} {{/if}} + {{!-- Discord widget "Crate" by WidgetBot --}} + From 6028534eaa81a0f508f017a51c06159c0039ab2e Mon Sep 17 00:00:00 2001 From: Joep Meindertsma Date: Tue, 19 Apr 2022 16:41:47 +0200 Subject: [PATCH 079/140] ignore DS_Store --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 5a0bf03..41d4f2a 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ /book +.DS_Store From e8d269dca43c9193cfa040f8d85d345ca46d57c0 Mon Sep 17 00:00:00 2001 From: Joep Meindertsma Date: Tue, 19 Apr 2022 16:43:43 +0200 Subject: [PATCH 080/140] #50 mention push --- src/commits/concepts.md | 1 + 1 file changed, 1 insertion(+) diff --git a/src/commits/concepts.md b/src/commits/concepts.md index 7f2c553..80b2767 100644 --- a/src/commits/concepts.md +++ b/src/commits/concepts.md @@ -21,6 +21,7 @@ The **optional method fields** describe how the data must be changed: - `destroy` - If true, the existing Resource will be removed. - `remove` - an array of Properties that need to be removed (including their values). - `set` - a Nested Resource which contains all the new or edited fields. +- `push` - a Nested Resource which contains all the fields that are _appended_ to. This means adding items to a new or existing ResourceArray. These commands are executed in the order above. This means that you can set `destroy` to `true` and include `set`, which empties the existing resource and sets new values. From 5cf1a10971f126fd52e40d01f5c9836a8cb65f25 Mon Sep 17 00:00:00 2001 From: Joep Meindertsma Date: Wed, 20 Apr 2022 13:49:50 +0200 Subject: [PATCH 081/140] Update ubuntu build --- .github/workflows/blank.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/blank.yml b/.github/workflows/blank.yml index cd247b4..ef497f7 100644 --- a/.github/workflows/blank.yml +++ b/.github/workflows/blank.yml @@ -7,7 +7,7 @@ on: jobs: deploy: - runs-on: ubuntu-18.04 + runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 From 680d11eca34f933ab62083525de6a506645d8c79 Mon Sep 17 00:00:00 2001 From: Joep Meindertsma Date: Wed, 20 Apr 2022 17:57:17 +0200 Subject: [PATCH 082/140] Mention development of importer --- src/create-json-ad.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/create-json-ad.md b/src/create-json-ad.md index cc495e3..1eaae19 100644 --- a/src/create-json-ad.md +++ b/src/create-json-ad.md @@ -134,4 +134,4 @@ We can simply refer to the `localId`, instead of some URL that does not exist ye ## Importing data using Atomic Sever -_currently under development_ +_currently [under development](https://github.com/joepio/atomic-data-rust/issues/390)_ From 7a454cd399c2648ca694a98489d8d3ca8b629812 Mon Sep 17 00:00:00 2001 From: Joep Meindertsma Date: Sun, 22 May 2022 21:36:47 +0200 Subject: [PATCH 083/140] Update github repo URLs --- README.md | 4 ++-- src/SUMMARY.md | 2 ++ src/atomic-data-overview.md | 10 ++++---- src/atomic-server.md | 12 +++++----- src/authentication.md | 2 +- src/commits/concepts.md | 2 +- src/core/json-ad.md | 2 +- src/create-json-ad.md | 2 +- src/files.md | 4 ++-- src/get-started.md | 12 +++++----- src/headless-cms.md | 1 + src/interoperability/rdf.md | 2 +- src/interoperability/solid.md | 2 +- src/tooling.md | 18 +++++++-------- src/usecases/headless-cms.md | 43 +++++++++++++++++++++++++++++++++++ src/usecases/intro.md | 1 + src/usecases/react.md | 14 ++++++++++++ src/usecases/science.md | 2 +- src/websockets.md | 2 +- 19 files changed, 99 insertions(+), 38 deletions(-) create mode 100644 src/headless-cms.md create mode 100644 src/usecases/headless-cms.md create mode 100644 src/usecases/react.md diff --git a/README.md b/README.md index 2d11004..2afb9f9 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ _Atomic Data is a specification for sharing, modifying and modeling graph data._ View it on [docs.atomicdata.dev](https://docs.atomicdata.dev). -If you're looking for **implementations of Atomic Data**, check out [atomic-data-rust](https://github.com/joepio/atomic-data-rust) (server + cli + lib written in Rust) and [atomic-data-browser](https://github.com/joepio/atomic-data-browser) (react / typescript). +If you're looking for **implementations of Atomic Data**, check out [atomic-data-rust](https://github.com/atomicdata-dev/atomic-data-rust) (server + cli + lib written in Rust) and [atomic-data-browser](https://github.com/atomicdata-dev/atomic-data-browser) (react / typescript). ## About this repo @@ -13,7 +13,7 @@ This repository holds the markdown book for the Atomic Data standard. It serves two purposes: - A central hub for **written content** such as documentation, the specification, tutorials and guides. -- A place to **discuss the specification** - that should happen in this issue tracker. +- A place to **discuss the specification** - that should happen in this issue tracker. ## Running locally diff --git a/src/SUMMARY.md b/src/SUMMARY.md index a09fa2a..0f62b55 100644 --- a/src/SUMMARY.md +++ b/src/SUMMARY.md @@ -49,6 +49,8 @@ * [SQL](interoperability/sql.md) * [Graph Databases](interoperability/graph-database.md) * [Potential use cases](usecases/intro.md) + * [As a Headless CMS](usecases/headless-cms.md) + * [In a React project](usecases/react.md) * [Personal Data Store](usecases/personal-data-store.md) * [E-commerce & marketplaces](usecases/e-commerce.md) * [Surveys](usecases/surveys.md) diff --git a/src/atomic-data-overview.md b/src/atomic-data-overview.md index 77226ed..16aa5ad 100644 --- a/src/atomic-data-overview.md +++ b/src/atomic-data-overview.md @@ -39,11 +39,11 @@ In the Atomizing section, we'll show you how you can create Atomic Data in three ## Tools & libraries -- Browser app [atomic-data-browser](https://github.com/joepio/atomic-data-browser) ([demo on atomicdata.dev](https://atomicdata.dev)) -- Build a react app using [typescript & react libraries](https://github.com/joepio/atomic-data-ts). Start with the [react template on codesandbox](https://codesandbox.io/s/atomic-data-react-template-4y9qu?file=/src/MyResource.tsx) -- Host your own [atomic-server](https://github.com/joepio/atomic) (powers [atomicdata.dev](https://atomicdata.dev), run with `docker run -p 80:80 -p 443:443 -v atomic-storage:/atomic-storage joepmeneer/atomic-server`) -- Discover the command line tool: [atomic-cli](https://github.com/joepio/atomic) (`cargo install atomic-cli`) -- Use the Rust library: [atomic-lib](https://github.com/joepio/atomic) +- Browser app [atomic-data-browser](https://github.com/atomicdata-dev/atomic-data-browser) ([demo on atomicdata.dev](https://atomicdata.dev)) +- Build a react app using [typescript & react libraries](https://github.com/atomicdata-dev/atomic-data-browser). Start with the [react template on codesandbox](https://codesandbox.io/s/atomic-data-react-template-4y9qu?file=/src/MyResource.tsx) +- Host your own [atomic-server](https://github.com/atomicdata-dev/atomic-data-rust) (powers [atomicdata.dev](https://atomicdata.dev), run with `docker run -p 80:80 -p 443:443 -v atomic-storage:/atomic-storage joepmeneer/atomic-server`) +- Discover the command line tool: [atomic-cli](https://github.com/atomicdata-dev/atomic-data-rust) (`cargo install atomic-cli`) +- Use the Rust library: [atomic-lib](https://github.com/atomicdata-dev/atomic-data-rust) ## Get involved diff --git a/src/atomic-server.md b/src/atomic-server.md index 46c0b67..3220b58 100644 --- a/src/atomic-server.md +++ b/src/atomic-server.md @@ -1,6 +1,6 @@ # Creating Atomic Data using Atomic-Server -[`Atomic-Server`](https://github.com/joepio/atomic-data-rust/blob/master/server/README.md) is the _reference implementation_ of the Atomic Data Core + Extended specification. +[`Atomic-Server`](https://github.com/atomicdata-dev/atomic-data-rust/blob/master/server/README.md) is the _reference implementation_ of the Atomic Data Core + Extended specification. It was developed parallel to this specification, and it served as a testing ground for various ideas (some of which didn't work, and some of which ended up in the spec). Atomic-Server is a graph database server for storing and sharing typed linked data. @@ -11,7 +11,7 @@ It's free, open source (MIT license), and has a ton of features: - 🪶 **Lightweight** (15MB binary, no runtime dependencies) - 💻 **Runs everywhere** (linux, windows, mac, arm) - 🌐 **Embedded server** with support for HTTP / HTTPS / HTTP2.0 and Built-in LetsEncrypt handshake. -- 🎛️ **Browser GUI included** powered by [atomic-data-browser](https://github.com/joepio/atomic-data-browser). Features dynamic forms, tables, authentication, theming and more. +- 🎛️ **Browser GUI included** powered by [atomic-data-browser](https://github.com/atomicdata-dev/atomic-data-browser). Features dynamic forms, tables, authentication, theming and more. - 💾 **Event-sourced versioning** / history powered by [Atomic Commits](https://docs.atomicdata.dev/commits/intro.html) - 🔄 **Synchronization using websockets**: communicates state changes with a client. Send a `wss` request to `/ws` to open a webscocket. - 🧰 **Many serialization options**: to JSON, [JSON-AD](https://docs.atomicdata.dev/core/json-ad.html), and various Linked Data / RDF formats (RDF/XML, N-Triples / Turtle / JSON-LD). @@ -28,12 +28,12 @@ In this guide, we'll can simply use `atomicdata.dev` in our browser without inst So you can skip this step and go to _Creating your first Atomic Data_. But if you want to, you can run Atomic-Server on your machine in a couple of ways: -- **Using a desktop installer**: download a desktop release from the [`releases`](https://github.com/joepio/atomic-data-rust/releases) page and install it using your desktop GUI. -- **Using a binary**: download a binary release from the [`releases`](https://github.com/joepio/atomic-data-rust/releases) page and open it using a terminal. +- **Using a desktop installer**: download a desktop release from the [`releases`](https://github.com/atomicdata-dev/atomic-data-rust/releases) page and install it using your desktop GUI. +- **Using a binary**: download a binary release from the [`releases`](https://github.com/atomicdata-dev/atomic-data-rust/releases) page and open it using a terminal. - **Using Docker** is probably the quickest: `docker run -p 80:80 -p 443:443 -v atomic-storage:/atomic-storage joepmeneer/atomic-server`. - **Using Cargo**: `cargo install atomic-server` and then run `atomic-server` to start. -_[Atomic-Server's README](https://github.com/joepio/atomic-data-rust/blob/master/server/README.md) contains more (and up-to-date) information about how to use it!_ +_[Atomic-Server's README](https://github.com/atomicdata-dev/atomic-data-rust/blob/master/server/README.md) contains more (and up-to-date) information about how to use it!_ Open your server in your browser. By default, that's [`http://localhost:9883`](http://localhost:9883). @@ -59,7 +59,7 @@ You can also fetch your data now as various formats. Try checking out the other features in the menu bar, and check out the `collections`. -Again, check out the [README](https://github.com/joepio/atomic-data-rust/blob/master/server/README.md) for more information and guides! +Again, check out the [README](https://github.com/atomicdata-dev/atomic-data-rust/blob/master/server/README.md) for more information and guides! Now, let's create some data. diff --git a/src/authentication.md b/src/authentication.md index 3909146..0c90e5e 100644 --- a/src/authentication.md +++ b/src/authentication.md @@ -41,7 +41,7 @@ const timestamp = Math.round(new Date().getTime());; // The timestmap is to limit the harm of a man-in-the-middle attack. // The `subject` is the full HTTP url that is to be fetched. const message = `${subject} ${timestamp}`; -// Sign using Ed25519, see example implementation here: https://github.com/joepio/atomic-data-browser/blob/30b2f8af59d25084de966301cb6bd1ed90c0eb78/lib/src/commit.ts#L176 +// Sign using Ed25519, see example implementation here: https://github.com/atomicdata-dev/atomic-data-browser/blob/30b2f8af59d25084de966301cb6bd1ed90c0eb78/lib/src/commit.ts#L176 const signed = await signToBase64(message, privateKey); // Set all of these headers const headers = new Headers; diff --git a/src/commits/concepts.md b/src/commits/concepts.md index 80b2767..0e3fab6 100644 --- a/src/commits/concepts.md +++ b/src/commits/concepts.md @@ -83,7 +83,7 @@ Congratulations, you've just created a valid Commit! Here are currently working implementations of this process, including serialization and signing (links are permalinks). - [in Rust (atomic-lib)](https://github.com/joepio/atomic/blob/ceb88c1ae58811f2a9e6bacb7eaa39a2a7aa1513/lib/src/commit.rs#L81). -- [in Typescript / Javascript (atomic-data-browser)](https://github.com/joepio/atomic-data-browser/blob/fc899bb2cf54bdff593ee6b4debf52e20a85619e/src/atomic-lib/commit.ts#L51). +- [in Typescript / Javascript (atomic-data-browser)](https://github.com/atomicdata-dev/atomic-data-browser/blob/fc899bb2cf54bdff593ee6b4debf52e20a85619e/src/atomic-lib/commit.ts#L51). If you want validate your implementation, check out the tests for these two projects. diff --git a/src/core/json-ad.md b/src/core/json-ad.md index 8abb8e3..c69bf13 100644 --- a/src/core/json-ad.md +++ b/src/core/json-ad.md @@ -2,7 +2,7 @@ # JSON-AD: The Atomic Data serialization format Although you can use various serialization formats for Atomic Data, `JSON-AD` is the _default_ and _only required_ serialization format. -It is what the current [Rust](https://github.com/joepio/atomic) and [Typescript / React](https://github.com/joepio/atomic-data-browser) implementations use to communicate. +It is what the current [Rust](https://github.com/atomicdata-dev/atomic-data-browser) and [Typescript / React](https://github.com/atomicdata-dev/atomic-data-browser) implementations use to communicate. It is designed to feel familiar to developers and to be easy and performant to parse and serialize. It is inspired by [JSON-LD](https://json-ld.org/). diff --git a/src/create-json-ad.md b/src/create-json-ad.md index 1eaae19..76a31f1 100644 --- a/src/create-json-ad.md +++ b/src/create-json-ad.md @@ -134,4 +134,4 @@ We can simply refer to the `localId`, instead of some URL that does not exist ye ## Importing data using Atomic Sever -_currently [under development](https://github.com/joepio/atomic-data-rust/issues/390)_ +_currently [under development](https://github.com/atomicdata-dev/atomic-data-rust-data-rust/issues/390)_ diff --git a/src/files.md b/src/files.md index 2c9fc96..eb9928d 100644 --- a/src/files.md +++ b/src/files.md @@ -29,5 +29,5 @@ In `atomic-server`, a `/upload` endpoint exists for uploading a file. Simply send an HTTP GET request to the File's [`download-url`](https://atomicdata.dev/properties/downloadURL) (make sure to authenticate this request). - [Discussion on specification](https://github.com/ontola/atomic-data-docs/issues/57) -- [Discussion on Rust server implementation](https://github.com/joepio/atomic-data-rust/issues/72) -- [Discussion on Typescript client implementation](https://github.com/joepio/atomic-data-browser/issues/121) +- [Discussion on Rust server implementation](https://github.com/atomicdata-dev/atomic-data-rust/issues/72) +- [Discussion on Typescript client implementation](https://github.com/atomicdata-dev/atomic-data-browser/issues/121) diff --git a/src/get-started.md b/src/get-started.md index 4aa0ffc..11f8fe8 100644 --- a/src/get-started.md +++ b/src/get-started.md @@ -21,7 +21,7 @@ There's a couple of levels at which you can start working with Atomic Data (from ## Host your own Atomic-Sesrver (locally) -- If you have docker running, you can use this one-liner: `docker run -p 80:80 -p 443:443 -v atomic-storage:/atomic-storage joepmeneer/atomic-server` (or use `cargo install atomic-server`, or the [binaries](https://github.com/joepio/atomic-data-rust/releases/)) +- If you have docker running, you can use this one-liner: `docker run -p 80:80 -p 443:443 -v atomic-storage:/atomic-storage joepmeneer/atomic-server` (or use `cargo install atomic-server`, or the [binaries](https://github.com/atomicdata-dev/atomic-data-rust/releases/)) - Now, visit `localhost` in your browser to access your server. - It's now only available locally. If you want to get it on the _internet_, you need to set up a domain name, and make sure its traffic is routed to your computer (search `port forwarding`). @@ -32,10 +32,10 @@ There's a couple of levels at which you can start working with Atomic Data (from -- Browser app [atomic-data-browser](https://github.com/joepio/atomic-data-browser) ([demo on atomicdata.dev](https://atomicdata.dev)) -- Build a react app using [typescript & react libraries](https://github.com/joepio/atomic-data-ts). Start with the [react template on codesandbox](https://codesandbox.io/s/atomic-data-react-template-4y9qu?file=/src/MyResource.tsx) -- Host your own [atomic-server](https://github.com/joepio/atomic) (powers [atomicdata.dev](https://atomicdata.dev), run with `docker run -p 80:80 -p 443:443 -v atomic-storage:/atomic-storage joepmeneer/atomic-server`) -- Discover the command line tool: [atomic-cli](https://github.com/joepio/atomic) (`cargo install atomic-cli`) -- Use the Rust library: [atomic-lib](https://github.com/joepio/atomic) +- Browser app [atomic-data-browser](https://github.com/atomicdata-dev/atomic-data-browser) ([demo on atomicdata.dev](https://atomicdata.dev)) +- Build a react app using [typescript & react libraries](https://github.com/atomicdata-dev/atomic-data-browser). Start with the [react template on codesandbox](https://codesandbox.io/s/atomic-data-react-template-4y9qu?file=/src/MyResource.tsx) +- Host your own [atomic-server](https://github.com/atomicdata-dev/atomic-data-browser) (powers [atomicdata.dev](https://atomicdata.dev), run with `docker run -p 80:80 -p 443:443 -v atomic-storage:/atomic-storage joepmeneer/atomic-server`) +- Discover the command line tool: [atomic-cli](https://github.com/atomicdata-dev/atomic-data-rust) (`cargo install atomic-cli`) +- Use the Rust library: [atomic-lib](https://github.com/atomicdata-dev/atomic-data-rust) Make sure to [join our Discord](https://discord.gg/a72Rv2P) if you'd like to discuss Atomic Data with others. diff --git a/src/headless-cms.md b/src/headless-cms.md new file mode 100644 index 0000000..0af7c1f --- /dev/null +++ b/src/headless-cms.md @@ -0,0 +1 @@ +# Using as a Headless CMS diff --git a/src/interoperability/rdf.md b/src/interoperability/rdf.md index f3fb0bb..dad60b1 100644 --- a/src/interoperability/rdf.md +++ b/src/interoperability/rdf.md @@ -275,7 +275,7 @@ This tooling should help to create URLs, Properties, and host everything on an e ## Convert Atomic data to RDF Since all Atomic Data is also valid RDF, it's trivial to convert / serialize Atoms to RDF. -This is why [atomic](https://github.com/joepio/atomic) can serialize Atomic Data to RDF. (For example, try `atomic-cli get https://atomicdata.dev/properties/description --as n3`) +This is why [atomic](https://github.com/atomicdata-dev/atomic-data-browser) can serialize Atomic Data to RDF. (For example, try `atomic-cli get https://atomicdata.dev/properties/description --as n3`) However, contrary to Atomic Data, RDF has optional Language and Datatype elements in every statement. It is good practice to use these RDF concepts when serializing Atomic Data into Turtle / RDF/XML, or other [RDF serialization formats](https://ontola.io/blog/rdf-serialization-formats/). diff --git a/src/interoperability/solid.md b/src/interoperability/solid.md index 27d3a90..febb737 100644 --- a/src/interoperability/solid.md +++ b/src/interoperability/solid.md @@ -128,7 +128,7 @@ I believe that as of today (february 2022), Atomic-Server has quite a few advant - **Fast** (1ms responses on my laptop) - **Lightweight** (8MB download, no runtime dependencies) - **HTTPS + HTTP2 support** with Built-in LetsEncrypt handshake. -- **Browser GUI included** powered by [atomic-data-browser](https://github.com/joepio/atomic-data-browser). Features dynamic forms, tables, authentication, theming and more. Easy to use! +- **Browser GUI included** powered by [atomic-data-browser](https://github.com/atomicdata-dev/atomic-data-browser). Features dynamic forms, tables, authentication, theming and more. Easy to use! - **Event-sourced versioning** / history powered by [Atomic Commits](https://docs.atomicdata.dev/commits/intro.html) - **Many serialization options**: to JSON, [JSON-AD](https://docs.atomicdata.dev/core/serialization.html#json-ad), and various Linked Data / RDF formats (RDF/XML, N-Triples / Turtle / JSON-LD). - **Full-text search** with fuzzy search and various operators, often <3ms responses. diff --git a/src/tooling.md b/src/tooling.md index 2e418d3..ad939e4 100644 --- a/src/tooling.md +++ b/src/tooling.md @@ -5,9 +5,9 @@ Although Atomic Data is a specification, it also has reference implementations: Open source (MIT licenced) software for Atomic Data: -- **Server + Database**: [atomic-server](https://github.com/joepio/atomic) -- **GUI**: [atomic-data-browser](https://github.com/joepio/atomic-data-browser) -- **CLI**: [atomic-cli](https://github.com/joepio/atomic) +- **Server + Database**: [atomic-server](https://github.com/atomicdata-dev/atomic-data-rust) +- **GUI**: [atomic-data-browser](https://github.com/atomicdata-dev/atomic-data-browser) +- **CLI**: [atomic-cli](https://github.com/atomicdata-dev/atomic-data-rust) Libraries (MIT licenced) to build apps with: @@ -30,7 +30,7 @@ One liner: `$ docker run -p 80:80 -p 443:443 -v atomic-storage:/atomic-storage j [demo](https://atomicdata.dev/) -[repository + issue tracker](https://github.com/joepio/atomic). +[repository + issue tracker](https://github.com/atomicdata-dev/atomic-data-browser). ### `atomic-data-browser` @@ -42,7 +42,7 @@ Data browser, powered by `@tomic/lib` and `@tomic/react`. [demo](https://atomicdata.dev/) (same as `atomic-server`) -[repository + issue tracker](https://github.com/joepio/atomic-data-browser). +[repository + issue tracker](https://github.com/atomicdata-dev/atomic-data-browser). ### `atomic-cli` @@ -71,15 +71,15 @@ SUBCOMMANDS: set Update an Atom's value. Uses Commits. tpf Finds Atoms using Triple Pattern Fragments. -Visit https://github.com/joepio/atomic for more info +Visit https://github.com/atomicdata-dev/atomic-data-browser for more info ``` -[repository + issue tracker](https://github.com/joepio/atomic). +[repository + issue tracker](https://github.com/atomicdata-dev/atomic-data-browser). ### Raycast extension: Full-text search from your desktop -[Install here](https://www.raycast.com/joepio/atomic). +[Install here](https://www.raycast.com/atomicdata-dev/atomic-data-browser). ## Libraries @@ -101,7 +101,7 @@ Library that powers `atomic-server` and `atomic-cli`. Features: - Path traversal - Basic validation -[repository + issue tracker](https://github.com/joepio/atomic). +[repository + issue tracker](https://github.com/atomicdata-dev/atomic-data-browser). ## Want to add to this list? Some ideas for tooling diff --git a/src/usecases/headless-cms.md b/src/usecases/headless-cms.md new file mode 100644 index 0000000..f0e4c0d --- /dev/null +++ b/src/usecases/headless-cms.md @@ -0,0 +1,43 @@ +# Using Atomic-Server as an open source headless CMS + +## Why people are switching to Headless CMS + +Traditionally, content management systems were responsible for both managing the content as well as producing the actual HTML views that the user saw. +This approach has some issues regarding performance and flexibility that headless CMS tools solve. + +- **Great performance**. We want pages to load in milliseconds, not seconds. Headless CMS tools + JAMSTACK style architectures are designed to give both performant initial page loads, as well as consecutive / dynamic loads. +- **High flexibility**. Designs change, and front-end developers want to use the tools that they know and love to create these designs effectively. With a headless CMS, you can build the front-end with the tools that you want, and make it look exactly like you want. +- **Easier content management**. Not every CMS is as fun and easy to use, as an admin, as others. Headless CMS tools focus on the admin side of things, so the front-end devs don't have to work on the back-end as well. + +## Atomic Server + +The [Atomic-Server](https://github.com/joepio/atomic-data-rust/blob/master/server/README.md) project may be the right choice for you if you're looking for a Headless CMS: + +- **Free and open source**. MIT licensed, no strings attached. +- **Easy to use API**. Atomic-Server is built using the [Atomic Data specification](../atomic-data-overview.md). It is well-documented, and uses conventions that most web developers are already familiar with. +- **Typescript & React libraries**. Use the existing react hooks to make your own fully editable, live-reloaded web application. +- **Fast**. 1ms responses on my laptop. It's written in Rust, so it squeezes out every cycle of your server. +- **Lightweight**. It's a single 8MB binary, no external dependencies needed. +- **Easy to setup**. Just run the binary and open the address. Even HTTPS support is built-in. +- **Clean, powerful admin GUI**. The Atomic-Data-Browser front-end gives you a very easy interface to manage your content. +- **Share your data models**. Atomic Data is designed to achieve a more decentralized web. You can easily re-use existing data models, or share the ones you built. +- **Files / Attachments**. Upload and preview files. +- **Pagination / sorting / filtering**. Query your data. +- **Versioning**. Built-in history, where each transaction is saved. +- **Websockets**. If you need live updates and highly interactive apps (collaborative documents and chatrooms), we've got your back. +- **Full-text search**. No need for a big elasticsearch server - atomic-server has one built-in. + +## Limitations + +- No support for image resizing, [as of now](https://github.com/joepio/atomic-data-rust/issues/257) + +## Setting up the server + +- One-liners: `cargo install atomic-server` or `docker run -p 80:80 -p 443:443 -v atomic-storage:/atomic-storage joepmeneer/atomic-server` +- Check out the [readme!](https://github.com/joepio/atomic-data-rust/blob/master/server/README.md) + +## Using the data in your (React / NextJS) app + +The `@tomic/lib` and `@tomic/react` typescript NPM libraries can be used in any JS project. + +In the next section, we'll discuss how to use Atomic-Server in your React project. diff --git a/src/usecases/intro.md b/src/usecases/intro.md index f399883..0db97c4 100644 --- a/src/usecases/intro.md +++ b/src/usecases/intro.md @@ -6,6 +6,7 @@ In this section, we'll present concrete examples of things that can be built wit Although you could use Atomic Data for pretty much any type of application, it is especially valuable where **data re-use**, **standardization**, and **data ownership** are important. +* [Headless CMS](headless-cms.md) * [Personal Data Store](personal-data-store.md) * [E-commerce & marketplaces](e-commerce.md) * [Surveys](surveys.md) diff --git a/src/usecases/react.md b/src/usecases/react.md new file mode 100644 index 0000000..eff02d7 --- /dev/null +++ b/src/usecases/react.md @@ -0,0 +1,14 @@ +# Using Atomic Data in a React project + +Atomic-Server is made to + +The `@tomic/lib` and `@tomic/react` typescript NPM libraries can be used in any JS project, but in this guide, we'll assume you're using NextJS. + +Check out the [template on CodeSandbox](https://codesandbox.io/s/atomic-data-react-template-4y9qu?file=/src/MyResource.tsx:0-1223): + + diff --git a/src/usecases/science.md b/src/usecases/science.md index 574c876..9eb9c00 100644 --- a/src/usecases/science.md +++ b/src/usecases/science.md @@ -9,4 +9,4 @@ - Scientific publications are a slow moving field - Publications tend to favor PDF, which is hard to make machine readable - Extending a syntax like LaTeX might provide a short path to referring to atomic data in publications -- Getting scientists to host the atomic data is going to be one of the most difficult aspects +- Getting scientists to host the atomic data is going to be one of the most difficult aspect diff --git a/src/websockets.md b/src/websockets.md index a296ac2..f899554 100644 --- a/src/websockets.md +++ b/src/websockets.md @@ -23,5 +23,5 @@ Use `x-atomic` [authentication headers (read more here)](./authentication.md) an ## Example implementations -- [Example client implementation in Typescript (@tomic/lib).](https://github.com/joepio/atomic-data-browser/blob/main/lib/src/websockets.ts) +- [Example client implementation in Typescript (@tomic/lib).](https://github.com/atomicdata-dev/atomic-data-browser/blob/main/lib/src/websockets.ts) - [Example server implementation in Rust using Actix-Web](https://github.com/joepio/atomic-data-rust/blob/master/server/src/handlers/web_sockets.rs) From 9faad3a8b2539970dcf09790adad593f9326edb5 Mon Sep 17 00:00:00 2001 From: Joep Meindertsma Date: Wed, 8 Jun 2022 10:44:49 +0200 Subject: [PATCH 084/140] Create LICENSE (#115) --- LICENSE | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 LICENSE diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..cf62a6d --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2022 Joep Meindertsma + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. From 041763336dee26a93466713eaa712f9a27f58b11 Mon Sep 17 00:00:00 2001 From: Joep Meindertsma Date: Sat, 18 Jun 2022 13:34:00 +0200 Subject: [PATCH 085/140] Add roadmap strategy history page --- src/SUMMARY.md | 1 + src/motivation.md | 2 ++ src/roadmap.md | 54 ++++++++++++++++++++++++++++ src/usecases/knowledge-graph.md | 16 --------- src/usecases/knowledge-management.md | 36 +++++++++++++++++++ 5 files changed, 93 insertions(+), 16 deletions(-) create mode 100644 src/roadmap.md delete mode 100644 src/usecases/knowledge-graph.md create mode 100644 src/usecases/knowledge-management.md diff --git a/src/SUMMARY.md b/src/SUMMARY.md index 0f62b55..014adf1 100644 --- a/src/SUMMARY.md +++ b/src/SUMMARY.md @@ -2,6 +2,7 @@ * [Atomic Data Overview](atomic-data-overview.md) * [Motivation](motivation.md) + * [Strategy, history and roadmap](roadmap.md) * [When (not) to use it](when-to-use.md) # Specification (core) diff --git a/src/motivation.md b/src/motivation.md index 4016197..9feddcc 100644 --- a/src/motivation.md +++ b/src/motivation.md @@ -82,6 +82,8 @@ Because Properties have URLs, it becomes trivial to _add new Properties_ that we Every time a developer builds an application, they have to figure a lot of things out. How to design the API, how to implement forms, how to deal with authentication, authorization, versioning, search... +A lot of time is essentially wasted on solving these issues time and time again. + By having a more complete, strict standard, Atomic Data aims to decrease this burden. [Atomic Schema](schema/intro.md) enables developers to easily share their datamodels, and re-use those from others. [Atomic Commits](commits/intro.md) helps developers to deal with versioning, history, undo and audit logs. diff --git a/src/roadmap.md b/src/roadmap.md new file mode 100644 index 0000000..0eb4841 --- /dev/null +++ b/src/roadmap.md @@ -0,0 +1,54 @@ +# Mission, history and roadmap for Atomic Data + +We have the ambition ro make the internet more interoperable. +In order to do this, we need people to have a good understanding of how we get there. +In this document, discuss the strategic principles we use, the steps we took, and the path forward. + +## Strategy for adoption + +- **Work on both specification and implementations (both client and server side) simultaneously** to make sure all ideas are both easily explainable and properly implementable. Don't design a spec with a large committee over many months, only to learn that it has implementation issues later on. +- **Create libraries whenever possible.** Enable other developers to re-use the technology in their own stacks. Keep the code as modular as possible. +- **Document everything**. Not just your APIs - also your ideas, considerations and decisions. +- **Do everything public**. All code is open source, all issues are publicly visible. Allow outsiders to learn everything and start contributing. +- **Make apps that stand on their own**. Atomic Data may be an abstract, technical story, but we still need end-user friendly applications that solve actual problems if we want to get as much adoption as possible. +- **Let realistic use cases guide API design**. Don't fall victim to spending too much time for extremely rare edge-cases, while ignoring more common issues and wishes. +- **Familiarity first**. Make tools and specs that feel familiar, build libraries for popular frameworks, and stick to conventions whenever possible. + +## History + +- **First draft of specification** (2020-06). Atomic Data started as an unnamed bundle of ideas and best practices to improve how we work with linked data, but quickly turned into a single (draft) specification. The idea was to start with a cohesive and easy to understand documentation, and use that as a stepping stone for writing the first code. After this, the code and specification should both be worked on simultaneously to make sure ideas are both easily explainable and properly implementable. Many of the earliest ideas were changed to make implementation easier. +- **[atomic-cli](https://crates.io/crates/atomic-cli) + [atomic-lib](https://docs.rs/atomic_lib/0.32.1/atomic_lib/)** (2020-07). The CLI functioned as the first platform to explore some of the most core ideas of Atomic Data, such as Properties and fetching. `atomic_lib` is the place where most logic resides. Written in Rust. +- **[Atomic-Server](https://github.com/joepio/atomic-data-rust/)** (2020-08). The server (using the same `atomic_lib` as the CLI) should be a fast, lightweight server that must be easy to set-up. Functions as a graph database with no dependencies. +- **[Collections](schema/collections.md)** (2020-10). Allows users to perform basic queries, filtering, sorting and pagination. +- **[Commits](commits/intro.md)** (2020-11). Allow keeping track of an event-sourced log of all activities that mutate resources, which in turn allows for versioning and adding new types of indexes later on. +- **[JSON-AD](core/json-ad.md)** (2021-02). Instead of the earlier proposed serialization format `.ad3`, we moved to the more familiar `json-ad`. +- **[Atomic-Data-Browser](https://github.com/atomicdata-dev/atomic-data-browser)** (2021-02). We wanted typescript and react libraries, as well as a nice interactive GUI that works in the browser. It should implement all relevant parts of the specification. +- **[Endpoints](endpoints.md)** (2021-03). Machine readable API endpoints (think Swagger / OpenAPI spec) for things like versioning, path traversal and more. +- **Classes and Properties editable from the browser** (2021-04). The data-browser is now powerful enough to use for managing the core ontological data of the project. +- **[Hierarchies](hierarchy.md) & [Invitations](invitations.md)** (2021-06). Users can set rights, structure Resources and invite new people to collaborate. +- **[Websockets](websockets.md)** (2021-08). Live synchronization between client and server. +- **Use case: Document Editor** (2021-09). Notion-like editor with real-time synchronization. +- **Full-text search** (2021-11). Powered by Tantivy. +- **Authentication for read access** (2021-11). Allows for private data. +- **Desktop support** (2021-12). Run Atomic-Server on the desktop, powered by Tauri. Easier install UX, system tray icon. +- **File management** (2021-12). Upload, download and view Files. +- **Indexed queries** (2022-01). Huge performance increase for queries. Allows for far bigger datasets. +- **Use case: ChatRoom** (2022-04). Group chat application. To make this possible, we had to extend the Commit model with a `push` action, and allow Plugins to create new Commits. + +## Where we're at + +Most of the specification seems to become pretty stable. +The implementations are working better every day, although 1.0 releases are still quite a bit far away. +At this point, the most important thing is to get developers to try out Atomic Data and provide feedback. +That means not only make it easy to install the tools, but also allow people to make Atomic Data _without_ using any of our own tools. +That's why we're now working on the JSON-AD and Atomizer projects (see below). + +## Roadmap + +- **[JSON-AD Importer](https://github.com/atomicdata-dev/atomic-data-rust/issues/390)** (2022-Q3). Publishing and consuming Atomic Data becomes a whole lot easier. +- **[Atomizer](https://github.com/atomicdata-dev/atomic-data-rust/issues/434)** (2022-Q4). Import files and automatically turn these into Atomic Data. +- **Improved modelling tools** (2022-Q4). Makes it even easier to create Classes and define Properties. +- **Video(s) about Atomic Data** (2022-Q4). Explain what Atomic Data is, why we're doing this, and how to get started. +- **[Atomic-server plugins](https://github.com/atomicdata-dev/atomic-data-rust/issues/73)** (2023). Let developers design new features without having to make PRs in Atomic-Server, and let users install apps without re-compiling (or even restarting) anything. +- **Atomic-browser plugins** (2023). Create new views for Classes. +- **1.0 release** (2024). Mark the specification, the server [(tracking issue)](https://github.com/atomicdata-dev/atomic-data-rust/milestone/5) and the browser as _stable_. It is possible that the Spec will become 1.0 before any implementation is stable. diff --git a/src/usecases/knowledge-graph.md b/src/usecases/knowledge-graph.md deleted file mode 100644 index 00cb90e..0000000 --- a/src/usecases/knowledge-graph.md +++ /dev/null @@ -1,16 +0,0 @@ -# Atomic Data for (semantic) knowledge graph management - -Knowledge graphs are information structures that help organizations to organize their knowledge using a graph model. -Graphs are especially useful for structuring knowledge, as they allow having links between resources which makes relationships understandable and browsable. - -Atomic Data is a Graph structure, and [Atomic-Server](https://crates.io/crates/atomic-server/) is an open source Graph database. - -## The entire web as one knowledge graph - -Atomic Data uses URLs to identify resources. -This means that it - -## Type-safe, decentralized data structures - -Contrary to many other types of graph systems, Atomic Data ensures type-safety by having a built-in schema language ([Atomic Schema](../schema/intro.md)). -This means that it is very easy to share and re-use data models, which helps you standardize the classes and properties that you use. diff --git a/src/usecases/knowledge-management.md b/src/usecases/knowledge-management.md new file mode 100644 index 0000000..d2e838c --- /dev/null +++ b/src/usecases/knowledge-management.md @@ -0,0 +1,36 @@ +# Atomic Data for (semantic) knowledge graph management + +Knowledge **management** is about making valuable knowledge easily findable, so everybody in an organization can be as effective as possible. +Knowledge **graphs** are information structures that help organizations to organize their knowledge using a graph model. +Graphs are especially useful for structuring knowledge, as they allow having links between resources which makes relationships understandable and makes data browsable. + +Atomic Data is a Graph structure, and [Atomic-Server](https://crates.io/crates/atomic-server/) is an open source Graph database / knowledge management system. + +## Knowledge management systems + +How do organizations store and share knowledge? +Some rely completely on their brains and social networks: if you want to know how the copier works, ask Sara. +But most use digital documents - more often than not in the cloud. +If your knowledge is digital and online available, people can retrieve it from anywhere at great speed. +Being able to search and browse through information is essential to making it effortless to retrieve. + +But good knowledge management systems are not just static: they have lives of their own. +Knowledge changes over time. +People add documents, make changes, move things. + + +## The entire web as one knowledge graph + +Atomic Data uses URLs to identify resources. +This means that it + +## Type-safe, decentralized data structures + +Contrary to many other types of graph systems, Atomic Data ensures type-safety by having a built-in schema language ([Atomic Schema](../schema/intro.md)). +This means that it is very easy to share and re-use data models, which helps you standardize the classes and properties that you use. + + +## Alternatives + +- **LinkedDataHub** by Atomgraph (unrelated, don't mind the name similarities): knowledge graph management tool that also supports RDF. Open source. +- ** From 7bf96185c7cff4f9599b177ea78e0a6224b2c876 Mon Sep 17 00:00:00 2001 From: Joep Meindertsma Date: Sat, 18 Jun 2022 13:35:40 +0200 Subject: [PATCH 086/140] Mention headless CMS usecase --- src/roadmap.md | 1 + 1 file changed, 1 insertion(+) diff --git a/src/roadmap.md b/src/roadmap.md index 0eb4841..a294cc7 100644 --- a/src/roadmap.md +++ b/src/roadmap.md @@ -50,5 +50,6 @@ That's why we're now working on the JSON-AD and Atomizer projects (see below). - **Improved modelling tools** (2022-Q4). Makes it even easier to create Classes and define Properties. - **Video(s) about Atomic Data** (2022-Q4). Explain what Atomic Data is, why we're doing this, and how to get started. - **[Atomic-server plugins](https://github.com/atomicdata-dev/atomic-data-rust/issues/73)** (2023). Let developers design new features without having to make PRs in Atomic-Server, and let users install apps without re-compiling (or even restarting) anything. +- **Use case: headless CMS** (2023). Use Atomic-Server to host and edit data that is being read by a front-end JAMSTACK type of tool, such as NextJS or SvelteKit. - **Atomic-browser plugins** (2023). Create new views for Classes. - **1.0 release** (2024). Mark the specification, the server [(tracking issue)](https://github.com/atomicdata-dev/atomic-data-rust/milestone/5) and the browser as _stable_. It is possible that the Spec will become 1.0 before any implementation is stable. From 2fb15e93bf7ce10d7b7b3609fcc8df61e8fa09fe Mon Sep 17 00:00:00 2001 From: Joep Meindertsma Date: Sat, 18 Jun 2022 13:38:33 +0200 Subject: [PATCH 087/140] Contribute roadmap --- src/roadmap.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/roadmap.md b/src/roadmap.md index a294cc7..92ba507 100644 --- a/src/roadmap.md +++ b/src/roadmap.md @@ -1,8 +1,10 @@ -# Mission, history and roadmap for Atomic Data +# Strategy, history and roadmap for Atomic Data We have the ambition ro make the internet more interoperable. -In order to do this, we need people to have a good understanding of how we get there. +We want Atomic Data to be a commonly used specification, enabling a vast amount of applications to work together and share information. +This means we need a lot of people to understand and contribute to Atomic Data. In this document, discuss the strategic principles we use, the steps we took, and the path forward. +This should help you understand how and where you may be able to contribute. ## Strategy for adoption From 15e22cef0a2f8cf4c5840bc254cd060ac3e5ce7e Mon Sep 17 00:00:00 2001 From: Joep Meindertsma Date: Sat, 18 Jun 2022 13:49:19 +0200 Subject: [PATCH 088/140] Add write permission to github action --- .github/workflows/blank.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/blank.yml b/.github/workflows/blank.yml index ef497f7..e8f4588 100644 --- a/.github/workflows/blank.yml +++ b/.github/workflows/blank.yml @@ -1,5 +1,7 @@ name: github pages +permissions: write-all + on: push: branches: From 996edf46808fa1a61be7eac0f50eb85287b0c6f4 Mon Sep 17 00:00:00 2001 From: Joep Meindertsma Date: Mon, 20 Jun 2022 12:16:51 +0200 Subject: [PATCH 089/140] #120 edit on github footer --- .github/workflows/blank.yml | 3 ++- book.toml | 9 +++++++-- open-in.css | 6 ++++++ 3 files changed, 15 insertions(+), 3 deletions(-) create mode 100644 open-in.css diff --git a/.github/workflows/blank.yml b/.github/workflows/blank.yml index e8f4588..d1f0897 100644 --- a/.github/workflows/blank.yml +++ b/.github/workflows/blank.yml @@ -16,9 +16,10 @@ jobs: - name: Setup mdBook uses: peaceiris/actions-mdbook@v1 with: - # mdbook-version: '0.3.7' mdbook-version: 'latest' + - run: cargo install mdbook-open-on-gh + - run: mdbook build - name: Deploy diff --git a/book.toml b/book.toml index cd557f1..01c77a8 100644 --- a/book.toml +++ b/book.toml @@ -5,9 +5,14 @@ description = "Documentation for the Atomic Data standard." language = "en" [output.html] -google-analytics = "UA-121994595-2" -git-repository-url = "https://github.com/ontola/atomic-data" git-repository-icon = "fa-github" +git-repository-url = "https://github.com/atomicdata-dev/atomic-data-docs" +additional-css = ["open-in.css"] [output.linkcheck] optional = true + +[preprocessor.open-on-gh] +command = "mdbook-open-on-gh" +renderer = ["html"] +text = "mdbook-open-on-gh" diff --git a/open-in.css b/open-in.css new file mode 100644 index 0000000..7c46d76 --- /dev/null +++ b/open-in.css @@ -0,0 +1,6 @@ +footer { + font-size: 0.8em; + text-align: center; + border-top: 1px solid black; + padding: 5px 0; +} From afa8b407df22ef8b63d0b2b8dcb4ce7476153f98 Mon Sep 17 00:00:00 2001 From: Joep Meindertsma Date: Mon, 20 Jun 2022 12:17:02 +0200 Subject: [PATCH 090/140] Get rid of analytics warning --- theme/index.hbs | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/theme/index.hbs b/theme/index.hbs index a2c46d7..97b41cf 100644 --- a/theme/index.hbs +++ b/theme/index.hbs @@ -222,24 +222,20 @@ {{/if}} - {{#if google_analytics}} + - {{/if}} {{#if playground_line_numbers}} + From 8ac3a78a3127e03aaa7bcd9e8ce29d55d70c2e08 Mon Sep 17 00:00:00 2001 From: Joep Meindertsma Date: Wed, 29 Jun 2022 08:14:26 +0200 Subject: [PATCH 094/140] Add external links page --- src/links.md | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 src/links.md diff --git a/src/links.md b/src/links.md new file mode 100644 index 0000000..009836f --- /dev/null +++ b/src/links.md @@ -0,0 +1,4 @@ +# List of links of Atomic Data on the web + +- [Awesome Knowledge Graph](https://github.com/totogo/awesome-knowledge-graph) +- [Awesome Semantic Web](https://github.com/semantalytics/awesome-semantic-web) From 6695b797b4d91bcc049c3482ff8a7d5b94b65c24 Mon Sep 17 00:00:00 2001 From: Joep Meindertsma Date: Wed, 29 Jun 2022 08:14:38 +0200 Subject: [PATCH 095/140] Improvements to knowledge management --- src/usecases/knowledge-management.md | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/usecases/knowledge-management.md b/src/usecases/knowledge-management.md index d2e838c..344f1a8 100644 --- a/src/usecases/knowledge-management.md +++ b/src/usecases/knowledge-management.md @@ -18,17 +18,22 @@ But good knowledge management systems are not just static: they have lives of th Knowledge changes over time. People add documents, make changes, move things. +## Why use Atomic-Server as a knowledge management system -## The entire web as one knowledge graph +### The entire web as one knowledge graph Atomic Data uses URLs to identify resources. This means that it -## Type-safe, decentralized data structures +### Type-safe, decentralized data structures Contrary to many other types of graph systems, Atomic Data ensures type-safety by having a built-in schema language ([Atomic Schema](../schema/intro.md)). This means that it is very easy to share and re-use data models, which helps you standardize the classes and properties that you use. +## Non-goals of Atomic-Server + +- Deep, specific query requirements +- Time-series data / data visualization ## Alternatives From 92ef69425438c3dfc3d9e82cc96f4dc19a686176 Mon Sep 17 00:00:00 2001 From: Joep Meindertsma Date: Wed, 29 Jun 2022 08:36:43 +0200 Subject: [PATCH 096/140] Rename master to main --- .github/workflows/blank.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/blank.yml b/.github/workflows/blank.yml index d1f0897..f28b1da 100644 --- a/.github/workflows/blank.yml +++ b/.github/workflows/blank.yml @@ -5,7 +5,7 @@ permissions: write-all on: push: branches: - - master + - main jobs: deploy: From f55c292a163117d28ee51a74654ce6237192c5be Mon Sep 17 00:00:00 2001 From: Joep Meindertsma Date: Wed, 23 Feb 2022 22:19:54 +0100 Subject: [PATCH 097/140] #62 previousCommit (WIP) --- src/commits/concepts.md | 31 ++++++++++++++++++++++++++----- 1 file changed, 26 insertions(+), 5 deletions(-) diff --git a/src/commits/concepts.md b/src/commits/concepts.md index 0e3fab6..b44acd2 100644 --- a/src/commits/concepts.md +++ b/src/commits/concepts.md @@ -51,6 +51,7 @@ Let's look at an example Commit: }, "https://atomicdata.dev/properties/signature": "3n+U/3OvymF86Ha6S9MQZtRVIQAAL0rv9ZQpjViht4emjnqKxj4wByiO9RhfL+qwoxTg0FMwKQsNg6d0QU7pAw==", "https://atomicdata.dev/properties/signer": "https://surfy.ddns.net/agents/9YCs7htDdF4yBAiA4HuHgjsafg+xZIrtZNELz4msCmc=", + "https://atomicdata.dev/properties/previousCommit": "https://surfy.ddns.net/commits/9YCs7htDdF4yBAiA4HuHgjsafg+xZIrtZNELz4msCmc=", "https://atomicdata.dev/properties/subject": "https://atomicdata.dev/test" } ``` @@ -87,10 +88,30 @@ Here are currently working implementations of this process, including serializat If you want validate your implementation, check out the tests for these two projects. +### Applying the Commit + +If you're on the receiving end of a Commit (e.g. if you're writing a server or a client who has to parse Commits), you will _apply_ the Commit to your Store. +If you have to _persist_ the Commit, you must perform all of the checks. +If you're writing a client, and you trust the source of the Commit, you can probably skip the validation steps. + +Here's how you apply a Commit: + +1. Check if the Subject URL is valid +2. Validate the signature. This means serialize the Commit deterministically (see above), check the Agent's publickey (you might need to fetch this one), verify if the signature matches. +3. Check if the timestamp matches is OK. I think an acceptable window is 10 seconds. +4. If the Commit is for an existing resource, get it. +5. Validate the Rights of the one making the Commit. +6. Check if the `previousCommit` of the Commit matches with the `previousCommit` of the Resource. +7. Iterate over the `set` fields. Overwrite existing, or add the new Values. Make sure the Datatypes match with the respective Properties. +8. Iterate over the `remove` fields. Remove existing properties. +9. If the Resource has one or more classes, check if the required Properties are there. +10. You might want to perform some custom validations now (e.g. if you accept an Invite, you should make sure that the one creating the Invite has the correct rights to actually make it!) +11. Store the created Commit as a Resource, and store the modified Resource! + ## Limitations -- Commits adjust only one Resource at a time, which means that you cannot change multiple in one commit. -- The one creating the Commit will need to sign it, which may make clients that write data more complicated than you'd like. -- Commits require signatures, which means key management. Doing this securely is no trivial matter. -- The signatures require JSON-AD serialization -- If your implementation stores all Commits, this means +- Commits adjust **only one Resource at a time**, which means that you cannot change multiple in one commit. +- The one creating the Commit will **need to sign it**, which may make clients that write data more complicated than you'd like. You can also let Servers write Commits, but this makes them less verifiable / decentralized. +- Commits require signatures, which means **key management**. Doing this securely is no trivial matter. +- The signatures **require JSON-AD** serialization +- If your implementation persists all Commits, you might need to **store a lot of data**. From 51cd1fb2620ad4f7ee141abde706bda5dc10f965 Mon Sep 17 00:00:00 2001 From: Joep Meindertsma Date: Mon, 29 Aug 2022 09:29:00 +0200 Subject: [PATCH 098/140] #133 websocket authentication --- src/authentication.md | 19 +++++++++++++++++-- src/core/concepts.md | 6 +++--- src/roadmap.md | 4 ++-- src/websockets.md | 7 +++++-- 4 files changed, 27 insertions(+), 9 deletions(-) diff --git a/src/authentication.md b/src/authentication.md index 0c90e5e..b9b82dd 100644 --- a/src/authentication.md +++ b/src/authentication.md @@ -61,9 +61,24 @@ const response = await fetch(subject, {headers}); - The server must check if the signature is valid. - The server must check if the request resource can be shared -## Authentication for [websockets](websockets.md) +## Authentication Resources (used in [websockets](websockets.md)) + +An _Authentication Resource_ is a JSON-AD object containing all the information a Server needs to make sure a valid Agent requests a session at some point in time. +It's fields are very similar to the headers shown above. +The signature still is a base64 signature of the following string: `{requestedSubject} {timestamp}`. +In Websocket connections, we use the `wss` address as the `requestedSubject`. + +```json +{ + "https://atomicdata.dev/properties/auth/agent": "http://example.com/agents/N32zQnZHoj1LbTaWI5CkA4eT2AaJNBPhWcNriBgy6CE=", + "https://atomicdata.dev/properties/auth/requestedSubject": "wss://example.com/ws", + "https://atomicdata.dev/properties/auth/publicKey": "N32zQnZHoj1LbTaWI5CkA4eT2AaJNBPhWcNriBgy6CE=", + "https://atomicdata.dev/properties/auth/timestamp": 1661757470002, + "https://atomicdata.dev/properties/auth/signature": "19Ce38zFu0E37kXWn8xGEAaeRyeP6EK0S2bt03s36gRrWxLiBbuyxX3LU9qg68pvZTzY3/P3Pgxr6VrOEvYAAQ==" +} +``` -- Since there's only a single HTTP request, we don't have a subject to fetch. Use `ws` as a subject, so sign a string like `ws 12940791247`. +The Authentication Resource is sent after the websocket is opened, like so: `AUTHENTICATE {authenticationResource}`. ## Hierarchies for authorization diff --git a/src/core/concepts.md b/src/core/concepts.md index bbb00e3..faf2519 100644 --- a/src/core/concepts.md +++ b/src/core/concepts.md @@ -27,7 +27,7 @@ It is a _typed_ data model, which means that every value must be validated by th ## Resource -A _Resource_ is a bunch of information about a thing, referenced by a single link (the Subject). +A _Resource_ is a bunch of information about a thing, referenced by a single link (the _Subject_). Formally, it is a set of Atoms (i.e. a Graph) that share a Subject URL. You can think of a Resource as a single row in a spreadsheet or database. In practice, Resources can be anything - a Person, a Blogpost, a Todo item. @@ -41,8 +41,8 @@ The Atom is the smallest possible piece of _meaningful_ data / information (henc You can think of an Atom as a single cell in a spreadsheet or database. An Atom consists of three fields: -* **[Subject](#subject-field)**: the Thing that the atom is providing information about. -* **[Property](#property-field)**: the property of the Thing that the atom is about (will always be a URL to a [Property](../schema/classes.md#property)). +* **[Subject](#subject-field)**: the thing that the atom is providing information about. This is typically also the URL where we can find more information about it. +* **[Property](#property-field)**: the property of the thing that the atom is about (will always be a URL to a [Property](../schema/classes.md#property)). * **[Value](#value-field)**: the new piece of information about the Atom. If you're familiar with RDF, you'll notice similarities. diff --git a/src/roadmap.md b/src/roadmap.md index 6327550..384c4f8 100644 --- a/src/roadmap.md +++ b/src/roadmap.md @@ -12,7 +12,7 @@ This should help you understand how and where you may be able to contribute. - **Create libraries whenever possible.** Enable other developers to re-use the technology in their own stacks. Keep the code as modular as possible. - **Document everything**. Not just your APIs - also your ideas, considerations and decisions. - **Do everything public**. All code is open source, all issues are publicly visible. Allow outsiders to learn everything and start contributing. -- **Make apps that stand on their own**. Atomic Data may be an abstract, technical story, but we still need end-user friendly applications that solve actual problems if we want to get as much adoption as possible. +- **Make an all-in-one workspace app that stand on its own**. Atomic Data may be an abstract, technical story, but we still need end-user friendly applications that solve actual problems if we want to get as much adoption as possible. - **Let realistic use cases guide API design**. Don't fall victim to spending too much time for extremely rare edge-cases, while ignoring more common issues and wishes. - **Familiarity first**. Make tools and specs that feel familiar, build libraries for popular frameworks, and stick to conventions whenever possible. @@ -36,6 +36,7 @@ This should help you understand how and where you may be able to contribute. - **File management** (2021-12). Upload, download and view Files. - **Indexed queries** (2022-01). Huge performance increase for queries. Allows for far bigger datasets. - **Use case: ChatRoom** (2022-04). Group chat application. To make this possible, we had to extend the Commit model with a `push` action, and allow Plugins to create new Commits. +- **[JSON-AD Publishing and Importing](create-json-ad.md)** (2022-08). Creating and consuming Atomic Data becomes a whole lot easier. ## Where we're at @@ -47,7 +48,6 @@ That's why we're now working on the JSON-AD and Atomizer projects (see below). ## Roadmap -- **[JSON-AD Importer](https://github.com/atomicdata-dev/atomic-data-rust/issues/390)** (2022-Q3). Publishing and consuming Atomic Data becomes a whole lot easier. - **[Atomizer](https://github.com/atomicdata-dev/atomic-data-rust/issues/434)** (2022-Q4). Import files and automatically turn these into Atomic Data. - **Improved modelling tools** (2022-Q4). Makes it even easier to create Classes and define Properties. - **Video(s) about Atomic Data** (2022-Q4). Explain what Atomic Data is, why we're doing this, and how to get started. diff --git a/src/websockets.md b/src/websockets.md index f899554..ba4e348 100644 --- a/src/websockets.md +++ b/src/websockets.md @@ -4,6 +4,7 @@ WebSockets are a very fast and efficient way to have a client and server communicate in an asynchronous fashion. They are used in Atomic Data to allow real-time updates, which makes it possible to create things like collaborative applications and multiplayer games. These have been implemented in `atomic-server` and `atomic-data-browser` (powered by `@tomic/lib`). +All messages use `JSON-AD`. ## Initializing a WebSocket connection @@ -15,11 +16,13 @@ Use `x-atomic` [authentication headers (read more here)](./authentication.md) an - `SUBSCRIBE ${subject}` tells the Server that you'd like to receive Commits about this Subject. - `UNSUBSCRIBE ${subject}` tells the Server that you'd like to stop receiving Commits about this Subject. - `GET ${subject}` fetch an individual resource. +- `AUTHENTICATE ${authenticationResource}` to set a user session for this websocket and allow authorized requests. The `authenticationResource` is a JSON-AD resource containing the signature and more, see [Authentication](../src/authentication.md). No response is given if the request is valid, an error is returned if something is wrong. ## Server to client messages -- `COMMIT ${CommitBody}` an entire Commit for a resource that you're subscribed to -- `RESOURCE ${CommitBody}` a resource as a response to a GET request. +- `COMMIT ${CommitBody}` an entire [Commit](../src/commits/concepts.md) for a resource that you're subscribed to. +- `RESOURCE ${Resource}` a resource as a response to a GET request. +- `ERROR ${ErrorBody}` an Error resource is sent whenever something goes wrong. ## Example implementations From 6dbf340d79f5db92b7bab6c253d6ad3eb15899db Mon Sep 17 00:00:00 2001 From: Joep Meindertsma Date: Tue, 30 Aug 2022 09:25:18 +0200 Subject: [PATCH 099/140] Improved websockets specs --- src/websockets.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/websockets.md b/src/websockets.md index ba4e348..a145cf2 100644 --- a/src/websockets.md +++ b/src/websockets.md @@ -4,12 +4,12 @@ WebSockets are a very fast and efficient way to have a client and server communicate in an asynchronous fashion. They are used in Atomic Data to allow real-time updates, which makes it possible to create things like collaborative applications and multiplayer games. These have been implemented in `atomic-server` and `atomic-data-browser` (powered by `@tomic/lib`). -All messages use `JSON-AD`. ## Initializing a WebSocket connection Send an HTTP `GET` request to the `/ws` endpoint of an `atomic-server`. The Server should update that request to a secure WebSocket (`wss`) connection. Use `x-atomic` [authentication headers (read more here)](./authentication.md) and use `ws` as a subject when signing. +The `WebSocket-Protocol` is `AtomicData`. ## Client to server messages @@ -21,8 +21,8 @@ Use `x-atomic` [authentication headers (read more here)](./authentication.md) an ## Server to client messages - `COMMIT ${CommitBody}` an entire [Commit](../src/commits/concepts.md) for a resource that you're subscribed to. -- `RESOURCE ${Resource}` a resource as a response to a GET request. -- `ERROR ${ErrorBody}` an Error resource is sent whenever something goes wrong. +- `RESOURCE ${Resource}` a JSON-AD Resource as a response to a GET request. If there is something wrong with this request (e.g. 404), return a `Error` Resource with the requested subject, similar to how the HTTP protocol server does this.` +- `ERROR ${ErrorBody}` an Error resource is sent whenever something goes wrong. The `ErrorBody` is a plaintext, typically English description of what went wrong. ## Example implementations From 947af01cf712fd93069447d23f168e61a127e9a5 Mon Sep 17 00:00:00 2001 From: Joep Meindertsma Date: Tue, 30 Aug 2022 09:25:30 +0200 Subject: [PATCH 100/140] Remove non-existing TranslationBox --- src/SUMMARY.md | 1 - src/interoperability/rdf.md | 2 +- src/schema/datatypes.md | 1 - 3 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/SUMMARY.md b/src/SUMMARY.md index 014adf1..bb9820f 100644 --- a/src/SUMMARY.md +++ b/src/SUMMARY.md @@ -15,7 +15,6 @@ * [Schema](schema/intro.md) * [Classes](schema/classes.md) * [Datatypes](schema/datatypes.md) - * [Translations](schema/translations.md) * [FAQ](schema/faq.md) # Specification (extended) diff --git a/src/interoperability/rdf.md b/src/interoperability/rdf.md index dad60b1..56d6320 100644 --- a/src/interoperability/rdf.md +++ b/src/interoperability/rdf.md @@ -16,7 +16,7 @@ However, it does differ in some fundamental ways. - Atomic requires URL (not URI) values in its `subjects` and `properties` (predicates), which means that they should be resolvable. Properties must resolve to an `Atomic Property`, which describes its datatype. - Atomic only allows those who control a resource's `subject` URL endpoint to edit the data. This means that you can't add triples about something that you don't control. - Atomic has no separate `datatype` field, but it requires that `Properties` (the resources that are shown when you follow a `predicate` value) specify a datatype. However, it is allowed to serialize the datatype explicitly, of course. -- Atomic has no separate `language` field, but it does support [Translation Resources](../schema/translations.md). +- Atomic has no separate `language` field. - Atomic has a native Event (state changes) model ([Atomic Commits](../commits/intro.md)), which enables communication of state changes - Atomic has a native Schema model ([Atomic Schema](../schema/intro.md)), which helps developers to know what data types they can expect (string, integer, link, array) - Atomic does not support Named Graphs. These should not be needed, because all statements should be retrievable by fetching the Subject of a resource. However, it _is_ allowed to include other resources in a response. diff --git a/src/schema/datatypes.md b/src/schema/datatypes.md index aa119ac..e5de5f3 100644 --- a/src/schema/datatypes.md +++ b/src/schema/datatypes.md @@ -33,7 +33,6 @@ _URL: `https://atomicdata.dev/datatypes/string`_ UTF-8 String, no max character count. Newlines use backslash escaped `\n` characters. -Should not contain language specific data, use a [TranslationBox](translations.md) instead. e.g. `String time! \n Second line!` From 7fb00f927a873d35b92485450c73ecea66a20dea Mon Sep 17 00:00:00 2001 From: Joep Meindertsma Date: Mon, 12 Sep 2022 10:37:32 +0200 Subject: [PATCH 101/140] Include atomic-server in graph description --- src/interoperability/graph-database.md | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/src/interoperability/graph-database.md b/src/interoperability/graph-database.md index b93aaeb..bfc66cb 100644 --- a/src/interoperability/graph-database.md +++ b/src/interoperability/graph-database.md @@ -1,10 +1,23 @@ {{#title How does Atomic Data relate to Graph Databases?}} # Atomic Data and Graph Databases -Atomic Data fundamentally is a _graph data model_. +Atomic Data is fundamentally a _graph data model_. We can think of Atomic Resources as _nodes_, and links to other resources through _properties_ as _edges_. -In this section, we'll explore how Atomic Data relates to some graph technologies. +In the first section, we'll take a look at Atomic-Server as a Graph Database. +After that, we'll explore how Atomic Data relates to some graph technologies. + +## Atomic-Server as a database + +- **Built-in REST**. Everything is done over HTTP, there's no new query language or serialization to learn. It's all JSON. +- **All resources have HTTP URLs**. This means that every single thing is identified by where it can be be found. Makes it easy to share data, if you want to! +- **Sharable and re-usable data models**. Atomic Schema helps you share and re-use data models by simply pointing to URLs. +- **Authorization built-in**. Managing rights in a hierarchy (similar to how tools like Google Drive or filesystems work) enable you to have a high degree of control over read / write rights. +- **Built-in easy to use GUI**. Managing content on Atomic-Server can be done by anyone, as its GUI is extremely easy to use and has a ton of features. +- **Dynamic indexing**. Indexes are created by performing Queries, resulting in great performance - without needing to manually configure indexing. +- **Synchronization over WebSockets**. All changes (called [Commits](../commits/intro.md)) can be synchronized over WebSockets, allowing you to build realtime collaborative tools. +- **Event-sourced**. All changes are stored and reversible, giving you a full versioned history. +- **Open source**. All code is MIT-licensed. ## Comparing Atomic Data to Neo4j @@ -45,7 +58,7 @@ This means that with Atomic Data, we get _versioning + audit trails_ for all dat ### Schema language and type safety In Neo4j, constraints can be added to the database by -Atomic Data uses Atomic Schema for validating datatypes and required properties in resources. +Atomic Data uses [Atomic Schema](../schema/intro.md) for validating datatypes and required properties in [Classes](../schema/classes.md). ### Other differences From 805ba46a1d3b07dee0642643c5260f756c697010 Mon Sep 17 00:00:00 2001 From: Joep Meindertsma Date: Sat, 17 Sep 2022 10:29:45 +0200 Subject: [PATCH 102/140] Fix link example classes --- src/schema/classes.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/schema/classes.md b/src/schema/classes.md index b34f7cc..1d0d43e 100644 --- a/src/schema/classes.md +++ b/src/schema/classes.md @@ -98,4 +98,4 @@ Example: } ``` -Visit https://atomicdata.dev/collections/class for the a list of example Classes. +Check out a [list of example Classes](https://atomicdata.dev/classes/). From 720e1e652f3e18efd6084619bacdd621867329cd Mon Sep 17 00:00:00 2001 From: Joep Meindertsma Date: Sat, 17 Sep 2022 10:53:09 +0200 Subject: [PATCH 103/140] Update hierarchies --- src/endpoints.md | 2 +- src/hierarchy.md | 24 ++++++++++++++++-------- 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/src/endpoints.md b/src/endpoints.md index 1c62db0..6119100 100644 --- a/src/endpoints.md +++ b/src/endpoints.md @@ -30,4 +30,4 @@ If we would not have incomplete resources, the server would have to perform expe - **Familiar API**: should look like something that most developers already know - **Auto-generate forms**: a front-end app should present Endpoints as forms that non-developers can interact with -[Discussion in issue tracker](https://github.com/ontola/atomic-data-docs/issues/15). +[Discussion in issue tracker](https://github.com/atomicdata-dev/atomic-data-docs/issues/15). diff --git a/src/hierarchy.md b/src/hierarchy.md index 657e79f..2d15a27 100644 --- a/src/hierarchy.md +++ b/src/hierarchy.md @@ -7,7 +7,7 @@ Your computer probably has a bunch of _drives_ and deeply nested _folders_ that We generally use these hierarchical elements to keep data organized, and to keep a tighter grip on rights management. For example, sharing a specific folder with a team, but a different folder could be private. -Although you are free to use Atomic Data with your own custom authorization system, we have a standardized model that is currently being used by some of the tools that we've built. +Although you are free to use Atomic Data with your own custom authorization system, we have a standardized model that is currently being used by Atomic-Server. ## Design goals @@ -17,10 +17,10 @@ Although you are free to use Atomic Data with your own custom authorization syst ## Atomic Hierarchy Model -- Every Resource SHOULD have a [`parent`](https://atomicdata.dev/properties/parent). +- Every Resource SHOULD have a [`parent`](https://atomicdata.dev/properties/parent). There are some exceptions to this, which are discussed below. - Any Resource can be a `parent` of some other Resource, as long as both Resources exists on the same Atomic Server. -- Inversely, every Resource could have `children`. -- Only [`Drive`](https://atomicdata.dev/classes/Drive)s (Resources with the class `Drive`) are allowed to be a top-level parent. +- Grants / rights given in a `parent` also apply to all children, and their children. +- There are few Classes that do not require `parent`s: ## Authorization @@ -29,15 +29,23 @@ Although you are free to use Atomic Data with your own custom authorization syst - Rights cannot be removed by children or parents - they can only be added. - `Commits` can not be edited. They can be `read` if the Agent has rights to read the [`subject`](https://atomicdata.dev/properties/subject) of the `Commit`. +## Top-level resources + +Some resources are special, as they do not require a `parent`: + +- [`Drive`](https://atomicdata.dev/classes/Drive)s are top-level items in the hierarchy: they do not have a `parent`. +- [`Agent`](https://atomicdata.dev/classes/Agent)s are top-level items because they are not `owned` by anything. They can always `read` and `write` themselves. +- [`Commit`](https://atomicdata.dev/classes/Commit)s are immutable, so they should never be edited by anyone. That's why they don't have a place in the hierarchy. Their `read` rights are determined by their subject. + ## Authentication -See [authentication](./authentication.md). +Authentication is about proving _who you are_, which is often the first step for authorization. See [authentication](./authentication.md). -## Current limitations of the current Authorization model +## Current limitations of the Authorization model -The specification is growing (and please contribute in the [docs repo](https://github.com/ontola/atomic-data-docs/issues)), but the current specification lacks some features: +The specification is growing (and please contribute in the [docs repo](https://github.com/atomicdata-dev/atomic-data-docs/issues)), but the current specification lacks some features: - Rights can only be added, but not removed in a higher item of a hierarchy. This means that you cannot have a secret folder inside a public folder. - No model for representing groups of Agents, or other runtime checks for authorization. -- No way to limit delete access seperately from write rights +- No way to limit delete access or invite rights separately from write rights - No way to request a set of rights for a Resource From ac7025913b8c698227d5d634ece1b0406be9a658 Mon Sep 17 00:00:00 2001 From: Joep Meindertsma Date: Mon, 19 Sep 2022 16:42:23 +0200 Subject: [PATCH 104/140] hierarchy change in navigation --- src/SUMMARY.md | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/SUMMARY.md b/src/SUMMARY.md index bb9820f..80c5e83 100644 --- a/src/SUMMARY.md +++ b/src/SUMMARY.md @@ -12,25 +12,25 @@ * [JSON-AD](core/json-ad.md) * [Querying](core/querying.md) * [Paths](core/paths.md) -* [Schema](schema/intro.md) - * [Classes](schema/classes.md) - * [Datatypes](schema/datatypes.md) - * [FAQ](schema/faq.md) + * [Schema](schema/intro.md) + * [Classes](schema/classes.md) + * [Datatypes](schema/datatypes.md) + * [FAQ](schema/faq.md) # Specification (extended) * [Atomic Data Extended](extended.md) -* [Agents](agents.md) -* [Hierarchy and authorization](hierarchy.md) -* [Authentication](authentication.md) -* [Invitations and sharing](invitations.md) -* [Commits (writing data)](commits/intro.md) - * [Concepts](commits/concepts.md) - * [Compared to](commits/compare.md) -* [WebSockets](websockets.md) -* [Endpoints](endpoints.md) -* [Collections, filtering, sorting](schema/collections.md) -* [Uploading and downloading files](files.md) + * [Agents](agents.md) + * [Hierarchy and authorization](hierarchy.md) + * [Authentication](authentication.md) + * [Invitations and sharing](invitations.md) + * [Commits (writing data)](commits/intro.md) + * [Concepts](commits/concepts.md) + * [Compared to](commits/compare.md) + * [WebSockets](websockets.md) + * [Endpoints](endpoints.md) + * [Collections, filtering, sorting](schema/collections.md) + * [Uploading and downloading files](files.md) # Create Atomic Data From 6b488eaed2e967ef8e850a1dd9586a3753ef7200 Mon Sep 17 00:00:00 2001 From: Joep Meindertsma Date: Mon, 19 Sep 2022 16:42:28 +0200 Subject: [PATCH 105/140] improve react / js page --- src/usecases/react.md | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/usecases/react.md b/src/usecases/react.md index eff02d7..6635019 100644 --- a/src/usecases/react.md +++ b/src/usecases/react.md @@ -1,8 +1,10 @@ -# Using Atomic Data in a React project +# Using Atomic Data in a JS / TS React project -Atomic-Server is made to +Atomic Data has been designed with front-end development in mind. +The open source [Atomic-Data-Browser](https://github.com/atomicdata-dev/atomic-data-browser), which is feature-packed with chatrooms, a real-time collaborative rich text editor, tables and more, is powered by two libraries: -The `@tomic/lib` and `@tomic/react` typescript NPM libraries can be used in any JS project, but in this guide, we'll assume you're using NextJS. +- `@tomic/lib` ([docs](https://atomicdata-dev.github.io/atomic-data-browser/docs/modules/_tomic_lib.html)) is the core library, containing logic for fetching and storing data, keeping things in sync using websockets, and signing [commits](../commits/intro.md). +- `@tomic/react` ([docs](https://atomicdata-dev.github.io/atomic-data-browser/docs/modules/_tomic_react.html)) is the react library, featuring various useful hooks that mimic `useState`, giving you real-time updates through your app. Check out the [template on CodeSandbox](https://codesandbox.io/s/atomic-data-react-template-4y9qu?file=/src/MyResource.tsx:0-1223): @@ -12,3 +14,5 @@ Check out the [template on CodeSandbox](https://codesandbox.io/s/atomic-data-rea allow="accelerometer; ambient-light-sensor; camera; encrypted-media; geolocation; gyroscope; hid; microphone; midi; payment; usb; vr; xr-spatial-tracking" sandbox="allow-forms allow-modals allow-popups allow-presentation allow-same-origin allow-scripts" > + +Feeling stuck? [Post an issue](https://github.com/atomicdata-dev/atomic-data-browser/issues/new) or [join the discord](https://discord.gg/a72Rv2P). From bd90f9697d864066fbcf499b62ef0b3d89f06eea Mon Sep 17 00:00:00 2001 From: Joep Meindertsma Date: Mon, 26 Sep 2022 16:19:30 +0200 Subject: [PATCH 106/140] Update links to atomicdata-dev repo --- src/commits/concepts.md | 2 +- src/core/paths.md | 2 +- src/interoperability/json.md | 2 +- src/interoperability/solid.md | 4 ++-- src/interoperability/upgrade.md | 2 +- src/roadmap.md | 2 +- src/usecases/food-labels.md | 2 +- src/usecases/headless-cms.md | 6 +++--- src/usecases/personal-data-store.md | 2 +- src/usecases/surveys.md | 2 +- src/websockets.md | 2 +- 11 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/commits/concepts.md b/src/commits/concepts.md index b44acd2..dad713f 100644 --- a/src/commits/concepts.md +++ b/src/commits/concepts.md @@ -83,7 +83,7 @@ Congratulations, you've just created a valid Commit! Here are currently working implementations of this process, including serialization and signing (links are permalinks). -- [in Rust (atomic-lib)](https://github.com/joepio/atomic/blob/ceb88c1ae58811f2a9e6bacb7eaa39a2a7aa1513/lib/src/commit.rs#L81). +- [in Rust (atomic-lib)](https://github.com/atomicdata-dev/atomic-data-rust/blob/ceb88c1ae58811f2a9e6bacb7eaa39a2a7aa1513/lib/src/commit.rs#L81). - [in Typescript / Javascript (atomic-data-browser)](https://github.com/atomicdata-dev/atomic-data-browser/blob/fc899bb2cf54bdff593ee6b4debf52e20a85619e/src/atomic-lib/commit.ts#L51). If you want validate your implementation, check out the tests for these two projects. diff --git a/src/core/paths.md b/src/core/paths.md index 2836153..25112d5 100644 --- a/src/core/paths.md +++ b/src/core/paths.md @@ -110,4 +110,4 @@ This means that we still have a unique, globally resolvable identifier - yay! ## Try for yourself -Install the [`atomic-cli`](https://github.com/joepio/atomic/blob/master/cli/README.md) software and run `atomic-cli get https://atomicdata.dev/classes/Class description`. +Install the [`atomic-cli`](https://github.com/atomicdata-dev/atomic-data-rust/blob/master/cli/README.md) software and run `atomic-cli get https://atomicdata.dev/classes/Class description`. diff --git a/src/interoperability/json.md b/src/interoperability/json.md index 9d7a95c..b31b344 100644 --- a/src/interoperability/json.md +++ b/src/interoperability/json.md @@ -71,7 +71,7 @@ The Property keys (e.g. "https://example.com/properties/name") need to resolve t } ``` -In practice, the easiest approach to make this conversion, is to create the data and host it using software like [Atomic Server](https://github.com/joepio/atomic/blob/master/server/README.md). +In practice, the easiest approach to make this conversion, is to create the data and host it using software like [Atomic Server](https://github.com/atomicdata-dev/atomic-data-rust/blob/master/server/README.md). ## From Atomic Data to JSON-LD diff --git a/src/interoperability/solid.md b/src/interoperability/solid.md index febb737..28dcc37 100644 --- a/src/interoperability/solid.md +++ b/src/interoperability/solid.md @@ -119,7 +119,7 @@ So, in a nutshell, I think this legacy makes Solid unnecessarily hard to use for Both Atomic Data and Solid are specifications that have different implementations. Some open source Solid implementations are the [Node Solid Server](https://github.com/solid/node-solid-server), the [Community Solid Server](https://github.com/solid/community-server) (also nodejs based) and the [DexPod](https://gitlab.com/ontola/dexpod) (Ruby on Rails based). -[Atomic-Server](https://github.com/joepio/atomic-data-rust/) is a database + server written in the Rust programming language, that can be considered an alternative to Solid Pod implementations. +[Atomic-Server](https://github.com/atomicdata-dev/atomic-data-rust/) is a database + server written in the Rust programming language, that can be considered an alternative to Solid Pod implementations. It was definitely built to be one, at least. It implements every part of the Atomic Data specification. I believe that as of today (february 2022), Atomic-Server has quite a few advantages over existing Solid implementations: @@ -143,5 +143,5 @@ Atomic Data is not even two years old, and although progress has been fast, it d Here's a list of things missing in Atomic Data, with links to their open issues and links to their existing Solid counterpart. - No inbox or [notifications](https://www.w3.org/TR/ldn/) yet ([issue](https://github.com/ontola/atomic-data/issues/28)) -- No OIDC support yet. ([issue](https://github.com/joepio/atomic-data-rust/issues/277)) +- No OIDC support yet. ([issue](https://github.com/atomicdata-dev/atomic-data-rust/issues/277)) - No support from a big community, a well-funded business or the inventor of the world wide web. diff --git a/src/interoperability/upgrade.md b/src/interoperability/upgrade.md index 019dca9..fc8fd68 100644 --- a/src/interoperability/upgrade.md +++ b/src/interoperability/upgrade.md @@ -40,7 +40,7 @@ Make sure that when the user requests some URL, that you return that resource as You can go all out, and implement Commits, Hierarchies, Authentication, Collections and [more](https://docs.atomicdata.dev/extended.html). I'd suggest starting with [Commits](../commits/intro.md), as these allow users to modify data whilst maintaining versioning and auditability. -Check out the [Atomic-Server source code](https://github.com/joepio/atomic-data-rust/tree/master/server) to get inspired on how to do this. +Check out the [Atomic-Server source code](https://github.com/atomicdata-dev/atomic-data-rust/tree/master/server) to get inspired on how to do this. ## Reach out for help diff --git a/src/roadmap.md b/src/roadmap.md index 384c4f8..8c97f28 100644 --- a/src/roadmap.md +++ b/src/roadmap.md @@ -20,7 +20,7 @@ This should help you understand how and where you may be able to contribute. - **First draft of specification** (2020-06). Atomic Data started as an unnamed bundle of ideas and best practices to improve how we work with linked data, but quickly turned into a single (draft) specification. The idea was to start with a cohesive and easy to understand documentation, and use that as a stepping stone for writing the first code. After this, the code and specification should both be worked on simultaneously to make sure ideas are both easily explainable and properly implementable. Many of the earliest ideas were changed to make implementation easier. - **[atomic-cli](https://crates.io/crates/atomic-cli) + [atomic-lib](https://docs.rs/atomic_lib/0.32.1/atomic_lib/)** (2020-07). The CLI functioned as the first platform to explore some of the most core ideas of Atomic Data, such as Properties and fetching. `atomic_lib` is the place where most logic resides. Written in Rust. -- **[Atomic-Server](https://github.com/joepio/atomic-data-rust/)** (2020-08). The server (using the same `atomic_lib` as the CLI) should be a fast, lightweight server that must be easy to set-up. Functions as a graph database with no dependencies. +- **[Atomic-Server](https://github.com/atomicdata-dev/atomic-data-rust/)** (2020-08). The server (using the same `atomic_lib` as the CLI) should be a fast, lightweight server that must be easy to set-up. Functions as a graph database with no dependencies. - **[Collections](schema/collections.md)** (2020-10). Allows users to perform basic queries, filtering, sorting and pagination. - **[Commits](commits/intro.md)** (2020-11). Allow keeping track of an event-sourced log of all activities that mutate resources, which in turn allows for versioning and adding new types of indexes later on. - **[JSON-AD](core/json-ad.md)** (2021-02). Instead of the earlier proposed serialization format `.ad3`, we moved to the more familiar `json-ad`. diff --git a/src/usecases/food-labels.md b/src/usecases/food-labels.md index 87068a6..aae60af 100644 --- a/src/usecases/food-labels.md +++ b/src/usecases/food-labels.md @@ -31,7 +31,7 @@ This can be done, because the groceries app can easily check detailed informatio ## How to achieve all this -1. The governing body (e.g. the European Commision) should set up an [Atomic Server](https://github.com/joepio/atomic-data-rust/) and host it on some recognizable domain. +1. The governing body (e.g. the European Commision) should set up an [Atomic Server](https://github.com/atomicdata-dev/atomic-data-rust/) and host it on some recognizable domain. 1. Create the [Class](https://atomicdata.dev/classes/Class) for a food product, containing the same (or more) information that is shown on food packages. 1. Create the Class for Ingredient. 1. Create instances for various Ingredients. Start with the E-numbers, work your way up to all kinds of used ingredients. Add Translations. diff --git a/src/usecases/headless-cms.md b/src/usecases/headless-cms.md index f0e4c0d..897e846 100644 --- a/src/usecases/headless-cms.md +++ b/src/usecases/headless-cms.md @@ -11,7 +11,7 @@ This approach has some issues regarding performance and flexibility that headles ## Atomic Server -The [Atomic-Server](https://github.com/joepio/atomic-data-rust/blob/master/server/README.md) project may be the right choice for you if you're looking for a Headless CMS: +The [Atomic-Server](https://github.com/atomicdata-dev/atomic-data-rust/blob/master/server/README.md) project may be the right choice for you if you're looking for a Headless CMS: - **Free and open source**. MIT licensed, no strings attached. - **Easy to use API**. Atomic-Server is built using the [Atomic Data specification](../atomic-data-overview.md). It is well-documented, and uses conventions that most web developers are already familiar with. @@ -29,12 +29,12 @@ The [Atomic-Server](https://github.com/joepio/atomic-data-rust/blob/master/serve ## Limitations -- No support for image resizing, [as of now](https://github.com/joepio/atomic-data-rust/issues/257) +- No support for image resizing, [as of now](https://github.com/atomicdata-dev/atomic-data-rust/issues/257) ## Setting up the server - One-liners: `cargo install atomic-server` or `docker run -p 80:80 -p 443:443 -v atomic-storage:/atomic-storage joepmeneer/atomic-server` -- Check out the [readme!](https://github.com/joepio/atomic-data-rust/blob/master/server/README.md) +- Check out the [readme!](https://github.com/atomicdata-dev/atomic-data-rust/blob/master/server/README.md) ## Using the data in your (React / NextJS) app diff --git a/src/usecases/personal-data-store.md b/src/usecases/personal-data-store.md index fec1dee..b9418fd 100644 --- a/src/usecases/personal-data-store.md +++ b/src/usecases/personal-data-store.md @@ -11,7 +11,7 @@ Many services don't even provide export functionality, and even if they do, the Atomic Data could help to re-introduce data ownership. Because the specification helps to standardize information, it becomes easier to make data interoperable. -And even more important: Apps don't need their own back-end - they can use the same personal data store: an Atomic Server (such as [this one](https://github.com/joepio/atomic/blob/master/server/README.md)). +And even more important: Apps don't need their own back-end - they can use the same personal data store: an Atomic Server (such as [this one](https://github.com/atomicdata-dev/atomic-data-rust/blob/master/server/README.md)). Realizing this goal requires quite a bit of work, though. This specification needs to mature, and we need reliable implementations. diff --git a/src/usecases/surveys.md b/src/usecases/surveys.md index efc53a1..d07abec 100644 --- a/src/usecases/surveys.md +++ b/src/usecases/surveys.md @@ -50,6 +50,6 @@ The user only sees invitations that are highly relevant, without sharing _any_ i The Atomic Data specification solves at least part of this problem. [Paths](../core/paths.md) are used to describe the queries that researchers make. -[Atomic Server](https://github.com/joepio/atomic/blob/master/server/README.md) can be used as the personal online data store. +[Atomic Server](https://github.com/atomicdata-dev/atomic-data-rust/blob/master/server/README.md) can be used as the personal online data store. However, we still need to specify the process of sending a request to an individual (probably by introducing an [inbox](https://github.com/ontola/atomic-data/issues/28)) diff --git a/src/websockets.md b/src/websockets.md index a145cf2..16e4b51 100644 --- a/src/websockets.md +++ b/src/websockets.md @@ -27,4 +27,4 @@ The `WebSocket-Protocol` is `AtomicData`. ## Example implementations - [Example client implementation in Typescript (@tomic/lib).](https://github.com/atomicdata-dev/atomic-data-browser/blob/main/lib/src/websockets.ts) -- [Example server implementation in Rust using Actix-Web](https://github.com/joepio/atomic-data-rust/blob/master/server/src/handlers/web_sockets.rs) +- [Example server implementation in Rust using Actix-Web](https://github.com/atomicdata-dev/atomic-data-rust/blob/master/server/src/handlers/web_sockets.rs) From 134bab22e873d1d4aece721af6224ca56ed75c5d Mon Sep 17 00:00:00 2001 From: Joep Meindertsma Date: Wed, 5 Oct 2022 15:13:01 +0200 Subject: [PATCH 107/140] WIP auth cookies --- src/authentication.md | 98 ++++++++++++++++++++++++++++++------------- src/websockets.md | 8 +++- 2 files changed, 74 insertions(+), 32 deletions(-) diff --git a/src/authentication.md b/src/authentication.md index b9b82dd..4741f1a 100644 --- a/src/authentication.md +++ b/src/authentication.md @@ -4,8 +4,11 @@ Authentication means knowing _who_ is doing something, either getting access or When an Agent wants to _edit_ a resource, they have to send a signed [Commit](commits/intro.md), and the signatures are checked in order to authorize a Commit. But how do we deal with _reading_ data, how do we know who is trying to get access? -That's what this page will explain. -The short answer is: **By signing the HTTP request**. +There are two ways users can authenticate themselves: + +- Signing an `Authentication Resource` and using that as a cookie +- Opening a WebSocket, and passing an `Authentication Resource`. +- Signing every single HTTP request (more secure, less flexible) ## Design goals @@ -17,9 +20,64 @@ The short answer is: **By signing the HTTP request**. - **Compatible with Commits**: Atomic Commits require clients to sign things. Ideally, this functionality / strategy would also fit with the new model. - **Fast**: Of course, authentication will always slow things down. But let's keep that to a minimum. -Authentication is done by signing individual (HTTP) requests with the Agent's private key. +## Authentication Resources + +An _Authentication Resource_ is a JSON-AD object containing all the information a Server needs to make sure a valid Agent requests a session at some point in time. +These are used both in Cookie-based auth, as well as in [WebSockets](websockets.md) + +We use the following fields (be sure to use the full URLs in the resource, see the example below): + +- `requestedSubject`: The URL of the requested resource. + - If we're authenticating a *WebSocket*, we use the `wss` address as the `requestedSubject`. (e.g. `wss://example.com/ws`) + - If we're authenticating a *Cookie*, we use the origin of the server (e.g. `https://example.com`) + - If we're authentication a *single HTTP request*, use the same URL as the `GET` address (e.g. `https://example.com/myResource`) +- `agent`: The URL of the Agent requesting the subject and signing this Authentication Resource. +- `publicKey`: base64 serialized ED25519 public key of the agent. +- `signature`: base64 serialized ED25519 signature of the following string: `{requestedSubject} {timestamp}` (without the brackets), signed by the private key of the Agent. +- `timestamp`: Unix timestamp of when the Authentication was signed +- `validUntil` (optional): Unix timestamp of when the Authentication should be no longer valid. If not provided, the server will default to 30 seconds from the `timestamp`. + +Here's what a JSON-AD Authentication Resource looks like for a WebSocket: + +```json +{ + "https://atomicdata.dev/properties/auth/agent": "http://example.com/agents/N32zQnZHoj1LbTaWI5CkA4eT2AaJNBPhWcNriBgy6CE=", + "https://atomicdata.dev/properties/auth/requestedSubject": "wss://example.com/ws", + "https://atomicdata.dev/properties/auth/publicKey": "N32zQnZHoj1LbTaWI5CkA4eT2AaJNBPhWcNriBgy6CE=", + "https://atomicdata.dev/properties/auth/timestamp": 1661757470002, + "https://atomicdata.dev/properties/auth/signature": "19Ce38zFu0E37kXWn8xGEAaeRyeP6EK0S2bt03s36gRrWxLiBbuyxX3LU9qg68pvZTzY3/P3Pgxr6VrOEvYAAQ==" +} +``` + +## Atomic Cookies Authentication + +In this approach, the client creates and signs a Resource that proves that an Agent wants to access a certain server for some amount of time. +This Authentication Resource is stored as a cookie, and passed along in every HTTP request to the server. + +### Setting the cookie -## HTTP Headers +1. Create a signed Authentication object, as described above. +2. Serialize it as JSON-AD, then as a base64 string. +3. Store it in a Cookie + 1. It needs to be + 2. The expiration date of the cookie should be set, and should match the expiration date of the Authentication Resource. + 3. Set the `Secure` attribute to prevent Man-in-the-middle attacks over HTTP + 4. Set the `HttpOnly` attribute - this prevents acces to the token from the JS context. + +## Authenticating Websockets + +After [opening a WebSocket connection](websockets.md), create an Authentication Resource. +Send a message like so: `AUTHENTICATE {authenticationResource}`. +The server will only respond if there is something wrong. + +## Per-Request Signing + +Atomic Data allows **signing every HTTP request**. +This method is most secure, since a MITM attack would only give access to the specific resource requested, and only for a short amount of time. +Note that signing every single request takes a bit of time. +We picked a fast algorithm (Ed25519) to minimize this cost. + +### HTTP Headers All of the following headers are required, if you need authentication. @@ -28,7 +86,7 @@ All of the following headers are required, if you need authentication. - `x-atomic-timestamp`: The current time (when sending the request) as milliseconds since unix epoch - `x-atomic-agent`: The subject URL of the Agent sending the request. -## Sending a request +### Sending a request Here's an example (js) client side implementation with comments: @@ -52,33 +110,14 @@ headers.set('x-atomic-agent', agent?.subject); const response = await fetch(subject, {headers}); ``` -## Handling a request +## Verifying an Authentication - If none of the `x-atomic` HTTP headers are present, the server assigns the [PublicAgent](https://atomicdata.dev/agents/publicAgent) to the request. This Agent represents any guest who is not signed in. - If some (but not all) of the `x-atomic` headers are present, the server will return with a `500`. -- The server must check the timestamp (max 10 seconds difference). +- The server must check if the `validUntil` has not yet passed. - The server must check whether the public key matches the one from the Agent. - The server must check if the signature is valid. -- The server must check if the request resource can be shared - -## Authentication Resources (used in [websockets](websockets.md)) - -An _Authentication Resource_ is a JSON-AD object containing all the information a Server needs to make sure a valid Agent requests a session at some point in time. -It's fields are very similar to the headers shown above. -The signature still is a base64 signature of the following string: `{requestedSubject} {timestamp}`. -In Websocket connections, we use the `wss` address as the `requestedSubject`. - -```json -{ - "https://atomicdata.dev/properties/auth/agent": "http://example.com/agents/N32zQnZHoj1LbTaWI5CkA4eT2AaJNBPhWcNriBgy6CE=", - "https://atomicdata.dev/properties/auth/requestedSubject": "wss://example.com/ws", - "https://atomicdata.dev/properties/auth/publicKey": "N32zQnZHoj1LbTaWI5CkA4eT2AaJNBPhWcNriBgy6CE=", - "https://atomicdata.dev/properties/auth/timestamp": 1661757470002, - "https://atomicdata.dev/properties/auth/signature": "19Ce38zFu0E37kXWn8xGEAaeRyeP6EK0S2bt03s36gRrWxLiBbuyxX3LU9qg68pvZTzY3/P3Pgxr6VrOEvYAAQ==" -} -``` - -The Authentication Resource is sent after the websocket is opened, like so: `AUTHENTICATE {authenticationResource}`. +- The server should check if the request resource can be accessed by the Agent using [hierarchy](hierarchy.md) (e.g. check `read` right in the resource or its parents). ## Hierarchies for authorization @@ -87,6 +126,5 @@ Atomic Data uses [Hierarchies](hierarchy.md) to describe who gets to access some ## Limitations / considerations - Since we need the Private Key to sign Commits and requests, the client should have this available. This means the client software as well as the user should deal with key management, and that can be a security risk in some contexts (such as a web browser). [See issue #49](https://github.com/ontola/atomic-data-docs/issues/49). -- When using the Agent's subject to authenticate somwehere, the authorizer must be able to check what the public key of the agent is. This means the agent must be publicly resolvable. This is one of the reasons we should work towards a server-independent identifier, probably as base64 string that contains the public key (and, optionally, also the https identifier). See [issue #59 on DIDs](https://github.com/ontola/atomic-data-docs/issues/59). -- Signing every single request takes a bit of time. We picked a fast algorithm (Ed25519) to minimize this cost. -- We'll probably introduce some form of token-based-authentication in the future. [See #87](https://github.com/ontola/atomic-data-docs/issues/87) +- When using the Agent's subject to authenticate somewehere, the authorizer must be able to check what the public key of the agent is. This means the agent must be publicly resolvable. This is one of the reasons we should work towards a server-independent identifier, probably as base64 string that contains the public key (and, optionally, also the https identifier). See [issue #59 on DIDs](https://github.com/ontola/atomic-data-docs/issues/59). +- We'll probably also introduce some form of token-based-authentication created server side in the future. [See #87](https://github.com/ontola/atomic-data-docs/issues/87) diff --git a/src/websockets.md b/src/websockets.md index 16e4b51..eb07573 100644 --- a/src/websockets.md +++ b/src/websockets.md @@ -16,14 +16,18 @@ The `WebSocket-Protocol` is `AtomicData`. - `SUBSCRIBE ${subject}` tells the Server that you'd like to receive Commits about this Subject. - `UNSUBSCRIBE ${subject}` tells the Server that you'd like to stop receiving Commits about this Subject. - `GET ${subject}` fetch an individual resource. -- `AUTHENTICATE ${authenticationResource}` to set a user session for this websocket and allow authorized requests. The `authenticationResource` is a JSON-AD resource containing the signature and more, see [Authentication](../src/authentication.md). No response is given if the request is valid, an error is returned if something is wrong. +- `AUTHENTICATE ${authenticationResource}` to set a user session for this websocket and allow authorized messages. The `authenticationResource` is a JSON-AD resource containing the signature and more, see [Authentication](../src/authentication.md). ## Server to client messages - `COMMIT ${CommitBody}` an entire [Commit](../src/commits/concepts.md) for a resource that you're subscribed to. -- `RESOURCE ${Resource}` a JSON-AD Resource as a response to a GET request. If there is something wrong with this request (e.g. 404), return a `Error` Resource with the requested subject, similar to how the HTTP protocol server does this.` +- `RESOURCE ${Resource}` a JSON-AD Resource as a response to a `GET` message. If there is something wrong with this request (e.g. 404), return a `Error` Resource with the requested subject, similar to how the HTTP protocol server does this.` - `ERROR ${ErrorBody}` an Error resource is sent whenever something goes wrong. The `ErrorBody` is a plaintext, typically English description of what went wrong. +## Considerations + +- For many messages, there is no response to give if things are processed correctly. If a message is unknown or there is a different problem, return an `ERROR`. + ## Example implementations - [Example client implementation in Typescript (@tomic/lib).](https://github.com/atomicdata-dev/atomic-data-browser/blob/main/lib/src/websockets.ts) From ce15bbbb0ccefc8da0a1c3de2a1d3b3f41440876 Mon Sep 17 00:00:00 2001 From: Joep Meindertsma Date: Tue, 11 Oct 2022 10:07:03 +0200 Subject: [PATCH 108/140] Update authentication.md --- src/authentication.md | 1 - 1 file changed, 1 deletion(-) diff --git a/src/authentication.md b/src/authentication.md index 4741f1a..e8386df 100644 --- a/src/authentication.md +++ b/src/authentication.md @@ -62,7 +62,6 @@ This Authentication Resource is stored as a cookie, and passed along in every HT 1. It needs to be 2. The expiration date of the cookie should be set, and should match the expiration date of the Authentication Resource. 3. Set the `Secure` attribute to prevent Man-in-the-middle attacks over HTTP - 4. Set the `HttpOnly` attribute - this prevents acces to the token from the JS context. ## Authenticating Websockets From f708cfc7cdbf67eae7a5f24357a71b7e989d6417 Mon Sep 17 00:00:00 2001 From: Joep Meindertsma Date: Fri, 14 Oct 2022 16:17:50 +0200 Subject: [PATCH 109/140] Add cookie name #87 --- src/authentication.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/authentication.md b/src/authentication.md index e8386df..0c9f4d9 100644 --- a/src/authentication.md +++ b/src/authentication.md @@ -58,8 +58,8 @@ This Authentication Resource is stored as a cookie, and passed along in every HT 1. Create a signed Authentication object, as described above. 2. Serialize it as JSON-AD, then as a base64 string. -3. Store it in a Cookie - 1. It needs to be +3. Store it in a Cookie: + 1. Name the cookie `atomic_session` 2. The expiration date of the cookie should be set, and should match the expiration date of the Authentication Resource. 3. Set the `Secure` attribute to prevent Man-in-the-middle attacks over HTTP From 00c97d73f910e0bf8da5db1df4f247c6aabba4b9 Mon Sep 17 00:00:00 2001 From: Joep Meindertsma Date: Fri, 16 Dec 2022 18:05:26 +0100 Subject: [PATCH 110/140] Added AI usecase, various small updates --- src/SUMMARY.md | 1 + src/commits/concepts.md | 2 +- src/hierarchy.md | 6 ++-- src/interoperability/sql.md | 1 + src/usecases/ai.md | 31 +++++++++++++++++++ src/usecases/governmental-open-data.md | 4 +++ src/usecases/headless-cms.md | 1 + src/usecases/intro.md | 4 ++- src/usecases/self-integrating-applications.md | 12 +++++++ 9 files changed, 57 insertions(+), 5 deletions(-) create mode 100644 src/usecases/ai.md create mode 100644 src/usecases/governmental-open-data.md create mode 100644 src/usecases/self-integrating-applications.md diff --git a/src/SUMMARY.md b/src/SUMMARY.md index 80c5e83..92a9dba 100644 --- a/src/SUMMARY.md +++ b/src/SUMMARY.md @@ -52,6 +52,7 @@ * [As a Headless CMS](usecases/headless-cms.md) * [In a React project](usecases/react.md) * [Personal Data Store](usecases/personal-data-store.md) + * [Artificial Intelligence](usecases/ai.md) * [E-commerce & marketplaces](usecases/e-commerce.md) * [Surveys](usecases/surveys.md) * [Verifiable Credentials](usecases/verifiable-credentials.md) diff --git a/src/commits/concepts.md b/src/commits/concepts.md index dad713f..3f912f1 100644 --- a/src/commits/concepts.md +++ b/src/commits/concepts.md @@ -110,7 +110,7 @@ Here's how you apply a Commit: ## Limitations -- Commits adjust **only one Resource at a time**, which means that you cannot change multiple in one commit. +- Commits adjust **only one Resource at a time**, which means that you cannot change multiple in one commit. ([issue](https://github.com/atomicdata-dev/atomic-data-docs/issues/130)) - The one creating the Commit will **need to sign it**, which may make clients that write data more complicated than you'd like. You can also let Servers write Commits, but this makes them less verifiable / decentralized. - Commits require signatures, which means **key management**. Doing this securely is no trivial matter. - The signatures **require JSON-AD** serialization diff --git a/src/hierarchy.md b/src/hierarchy.md index 2d15a27..da94a71 100644 --- a/src/hierarchy.md +++ b/src/hierarchy.md @@ -45,7 +45,7 @@ Authentication is about proving _who you are_, which is often the first step for The specification is growing (and please contribute in the [docs repo](https://github.com/atomicdata-dev/atomic-data-docs/issues)), but the current specification lacks some features: -- Rights can only be added, but not removed in a higher item of a hierarchy. This means that you cannot have a secret folder inside a public folder. -- No model for representing groups of Agents, or other runtime checks for authorization. -- No way to limit delete access or invite rights separately from write rights +- Rights can only be added, but not removed in the hierarchy. This means that you cannot have a secret folder inside a public folder. +- No model for representing groups of Agents, or other runtime checks for authorization. ([issue](https://github.com/atomicdata-dev/atomic-data-docs/issues/73)) +- No way to limit delete access or invite rights separately from write rights ([issue](https://github.com/atomicdata-dev/atomic-data-docs/issues/82)) - No way to request a set of rights for a Resource diff --git a/src/interoperability/sql.md b/src/interoperability/sql.md index b55cdfa..08ecc9e 100644 --- a/src/interoperability/sql.md +++ b/src/interoperability/sql.md @@ -62,6 +62,7 @@ If you want to store arbitrary Atomic Data in a SQL database, you might be best - SQL is far more common, many people will know how to use it. - SQL databases are battle-tested and has been powering countless of products for tens of years, whereas Atomic Server is at this moment in beta. - SQL databases have a more powerful and expressive query language, where you can define tables in your query and combine resources. +- Atomic Data doesn't have a [mutli-node / distributed option](https://github.com/atomicdata-dev/atomic-data-rust/issues/213) ## FAQ diff --git a/src/usecases/ai.md b/src/usecases/ai.md new file mode 100644 index 0000000..e2535df --- /dev/null +++ b/src/usecases/ai.md @@ -0,0 +1,31 @@ +# Atomic Data & Artificial Intelligence + +Recent developments in machine learning (and specifically deep neural networks) have shown how powerful and versatile AI can be. +Both Atomic Data and AI can be used to store and query knowledge, but we think of these technologies as complementary due to their unique characteristics: + +- Artificial Intelligence can make sense of (unstructured) data, so you can feed it any type of data. However, AIs often produce unpredictable and sometimes incorrect results. +- Atomic Data helps to make data interoperable, reliable and predictable. However, it requires very strict inputs. + +There are two ways in which Atomic Data and AI can help each other: + +- AI can help to make creating Atomic Data easier. +- Atomic Data can help train AIs. +- Atomic Data can provide AIs with reliable, machine readable data for answering questions. + +## Make it easier to create Atomic Data using AI + +While writing text, an AI might help make suggestions to disambiguate whatever it is you're writing about. +For example, you may mention `John` and your knowledge graph editor (like `atomic-server`) could suggest `John Wayne` or `John Cena`. +When making your selection, a link will be created which helps to make your knowledge graph more easily browsable. +AI could help make these suggestions through context-aware _entity recognition_. + +## Train AIs with Atomic Data + +During training, you could feed Atomic Data to your AI to help it construct a reliable, consistent model of the knowledge relevant to your organization or domain. +You could use `atomic-server` as the knowledge store, and iterate over your resources and let your AI parse them. + +## Provide AI with query access to answer questions + +Instead of training your AI, you might provide your AI with an interface to perform queries. +Note that at this moment, I'm not aware of any AIs that can actually construct and execute queries, but because of recent advancements (e.g. ChatGPT), we know that there now exist AIs that can create SQL queries based on human text. +In the future, you might let your AI query your `atomic-server` to find reliable and up-to-date answers to your questions. diff --git a/src/usecases/governmental-open-data.md b/src/usecases/governmental-open-data.md new file mode 100644 index 0000000..27a6ba0 --- /dev/null +++ b/src/usecases/governmental-open-data.md @@ -0,0 +1,4 @@ +# Publishing governmental Open Data as Atomic Data + +- More information is more better +- diff --git a/src/usecases/headless-cms.md b/src/usecases/headless-cms.md index 897e846..e9bed48 100644 --- a/src/usecases/headless-cms.md +++ b/src/usecases/headless-cms.md @@ -30,6 +30,7 @@ The [Atomic-Server](https://github.com/atomicdata-dev/atomic-data-rust/blob/mast ## Limitations - No support for image resizing, [as of now](https://github.com/atomicdata-dev/atomic-data-rust/issues/257) +- No GraphQL support [(see issue)](https://github.com/atomicdata-dev/atomic-data-rust/issues/251) ## Setting up the server diff --git a/src/usecases/intro.md b/src/usecases/intro.md index 0db97c4..ac26c68 100644 --- a/src/usecases/intro.md +++ b/src/usecases/intro.md @@ -6,8 +6,10 @@ In this section, we'll present concrete examples of things that can be built wit Although you could use Atomic Data for pretty much any type of application, it is especially valuable where **data re-use**, **standardization**, and **data ownership** are important. -* [Headless CMS](headless-cms.md) +* [As a Headless CMS](headless-cms.md) +* [In a React project](react.md) * [Personal Data Store](personal-data-store.md) +* [Artificial Intelligence](ai.md) * [E-commerce & marketplaces](e-commerce.md) * [Surveys](surveys.md) * [Verifiable Credentials](verifiable-credentials.md) diff --git a/src/usecases/self-integrating-applications.md b/src/usecases/self-integrating-applications.md new file mode 100644 index 0000000..78f35ec --- /dev/null +++ b/src/usecases/self-integrating-applications.md @@ -0,0 +1,12 @@ +# Self-integrating applications with Atomic Data + +Our digital workspaces are increasingly dependent on integrations. +Receive ticket updates in your group chat app, send an e-mail when a new lead is added, save uploaded files to a specific folder in your cloud storage. +Many SAAS platforms offer these types of integrations as simple click-to-enable features. +However, writing integrations as a developer pretty much always involves manual labor. +Integrations are costly, as developers will need to read API specifications, interpret them correctly, +map the properties, implement the integration and then add tests to make sure it doesn't break. + +Tools like the OpenAPI specification make it easier to render + +We call application _self-integrating_ if there is no labor involved with linking one app to the other. From 91c233b1e17be483f3369274347681e3cc159e47 Mon Sep 17 00:00:00 2001 From: Joep Meindertsma Date: Fri, 16 Dec 2022 18:24:15 +0100 Subject: [PATCH 111/140] fix ci --- .github/workflows/blank.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/blank.yml b/.github/workflows/blank.yml index f28b1da..1fbd601 100644 --- a/.github/workflows/blank.yml +++ b/.github/workflows/blank.yml @@ -18,7 +18,7 @@ jobs: with: mdbook-version: 'latest' - - run: cargo install mdbook-open-on-gh + - run: cargo install mdbook-open-on-gh --version 2.2.0 - run: mdbook build From 89c24a560fbb6fd73d3463f2fdcc60954d804845 Mon Sep 17 00:00:00 2001 From: Joep Meindertsma Date: Fri, 16 Dec 2022 19:57:57 +0100 Subject: [PATCH 112/140] try older mdbook open gh --- .github/workflows/blank.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/blank.yml b/.github/workflows/blank.yml index 1fbd601..e74fca1 100644 --- a/.github/workflows/blank.yml +++ b/.github/workflows/blank.yml @@ -18,7 +18,7 @@ jobs: with: mdbook-version: 'latest' - - run: cargo install mdbook-open-on-gh --version 2.2.0 + - run: cargo install mdbook-open-on-gh --version 2.1.0 - run: mdbook build From fda3ed779cdd9cc9e1b363d62e383759322e50b8 Mon Sep 17 00:00:00 2001 From: Robin Vobruba Date: Sun, 18 Dec 2022 13:46:09 +0200 Subject: [PATCH 113/140] Separates Summary footer parts visually Without this, it looks like a single sentence that makes no sense. --- src/SUMMARY.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/SUMMARY.md b/src/SUMMARY.md index 92a9dba..bc86ea5 100644 --- a/src/SUMMARY.md +++ b/src/SUMMARY.md @@ -61,6 +61,6 @@ ----------- -[Acknowledgements](acknowledgements.md) -[Newsletter](newsletter.md) +[Acknowledgements](acknowledgements.md) | +[Newsletter](newsletter.md) | [Get involved](get-involved.md) From c54f1b4bd66cbadde66963719f53121e80c776f7 Mon Sep 17 00:00:00 2001 From: Robin Vobruba Date: Sun, 18 Dec 2022 16:48:27 +0200 Subject: [PATCH 114/140] Typos --- src/atomic-data-overview.md | 2 +- src/extended-table.md | 4 ++-- src/interoperability/rdf.md | 2 +- src/schema/datatypes.md | 2 +- src/schema/translations.md | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/atomic-data-overview.md b/src/atomic-data-overview.md index 16aa5ad..fc728db 100644 --- a/src/atomic-data-overview.md +++ b/src/atomic-data-overview.md @@ -51,7 +51,7 @@ Make sure to [join our Discord](https://discord.gg/a72Rv2P) if you'd like to dis ## Status -Keep in mind that none of the Atomic Data project has reached a v1, which means that breaking changes can happen. +Keep in mind that none of the Atomic Data projects has reached a v1, which means that breaking changes can happen. ## Reading these docs diff --git a/src/extended-table.md b/src/extended-table.md index 47f7a39..4a57309 100644 --- a/src/extended-table.md +++ b/src/extended-table.md @@ -1,8 +1,8 @@ - [Commits](commits/intro.md) communicate state changes. These Commits are signed using cryptographic keys, which ensures that every change can be audited. Commits are also used to construct a history of versions. - [Agents](agents.md) are Users that enable [authentication](authentication.md). They are Resources with their own Public and Private keys, which they use to identify themselves. -- [Collections](schema/collections.md): querying, filetering, sorting and pagination. +- [Collections](schema/collections.md): querying, filtering, sorting and pagination. - [Paths](core/paths.md): traverse graphs. -- [Hierarchies](hierarchy.md) used for authorization and keeping data organized. Similar to folder structures on filesystems. +- [Hierarchies](hierarchy.md) used for authorization and keeping data organized. Similar to folder structures on file-systems. - [Invites](invitations.md): create new users and provide them with rights. - [WebSockets](websockets.md): real-time updates. - [Endpoints](endpoints.md): provide machine-readable descriptions of web services. diff --git a/src/interoperability/rdf.md b/src/interoperability/rdf.md index 56d6320..bc5a6ab 100644 --- a/src/interoperability/rdf.md +++ b/src/interoperability/rdf.md @@ -110,7 +110,7 @@ This is probably because of two reasons: However, this introduces a lot extra complexity for data users (see above), which makes it not very attractive to use RDF in any client. Whereas most languages and datatypes have `key-value` uniqueness that allow for unambiguous value selection, RDF clients have to deal with the possibility that multiple triples with the same `subject-predicate` combination might exist. It also introduces a different problem: How should you interpret a set of `subject-predicate` combinations? -Does this represent a non-ordered collection, or did something to wrong with setting values?\ +Does this represent a non-ordered collection, or did something go wrong while setting values?\ In the RDF world, I've seen many occurences of both. Atomic Data requires `subject-property` uniqueness, which means that these issues are no more. diff --git a/src/schema/datatypes.md b/src/schema/datatypes.md index e5de5f3..40a0483 100644 --- a/src/schema/datatypes.md +++ b/src/schema/datatypes.md @@ -97,7 +97,7 @@ e.g. `1991-01-20` _URL: `https://atomicdata.dev/datatypes/timestamp`_ Similar to [Unix Timestamp](https://www.unixtimestamp.com/). -Milliseconds since midnight UTC 1970 jan 01 (aka the [Unix Epoch](https://en.wikipedia.org/wiki/Unix_time)). +Milliseconds since midnight UTC 1970 Jan 01 (aka the [Unix Epoch](https://en.wikipedia.org/wiki/Unix_time)). Use this for most DateTime fields. Signed 64 bit integer (instead of 32 bit in Unix systems). diff --git a/src/schema/translations.md b/src/schema/translations.md index b933af9..8c829ad 100644 --- a/src/schema/translations.md +++ b/src/schema/translations.md @@ -10,7 +10,7 @@ Dealing with translations can be hard. _URL: `https://atomicdata.dev/classes/TranslationBox` (does not resolve yet)_ -A TranslationBox is a collection of translated strings, uses to provide multiple translations. +A TranslationBox is a collection of translated strings, used to provide multiple translations. It has a long list of optional properties, each corresponding to some language. Each possible language Property uses the following URL template: `https://atomicdata.dev/languages/{langguageTag}`. Use a [BCP 47](http://www.rfc-editor.org/rfc/bcp/bcp47.txt) language tag, e.g. `nl` or `en-US`. From d00c5da43be0f6772e702b053a39c24159e850d3 Mon Sep 17 00:00:00 2001 From: Robin Vobruba Date: Sun, 18 Dec 2022 16:49:02 +0200 Subject: [PATCH 115/140] Adds missing link to discussion about arrays --- src/schema/datatypes.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/schema/datatypes.md b/src/schema/datatypes.md index 40a0483..33c7486 100644 --- a/src/schema/datatypes.md +++ b/src/schema/datatypes.md @@ -110,6 +110,7 @@ _URL: `https://atomicdata.dev/datatypes/resourceArray`_ Sequential, ordered list of Atomic URIs. Serialized as a JSON array with strings. Note that other types of arrays are not included in this spec, but can be perfectly valid. -([discussion]()) + +([Discussion](https://github.com/atomicdata-dev/atomic-data-docs/issues/127)) - e.g. `["https://example.com/1", "https://example.com/1"]` From 7c3d5b133d5284c70aaffa1ad8d3b0550f6f9316 Mon Sep 17 00:00:00 2001 From: Robin Vobruba Date: Sun, 18 Dec 2022 16:49:50 +0200 Subject: [PATCH 116/140] Unifies links to discussions --- src/endpoints.md | 2 +- src/invitations.md | 2 +- src/schema/faq.md | 2 +- src/schema/translations.md | 3 ++- src/usecases/verifiable-credentials.md | 2 +- 5 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/endpoints.md b/src/endpoints.md index 6119100..cefbc13 100644 --- a/src/endpoints.md +++ b/src/endpoints.md @@ -30,4 +30,4 @@ If we would not have incomplete resources, the server would have to perform expe - **Familiar API**: should look like something that most developers already know - **Auto-generate forms**: a front-end app should present Endpoints as forms that non-developers can interact with -[Discussion in issue tracker](https://github.com/atomicdata-dev/atomic-data-docs/issues/15). +([Discussion](https://github.com/atomicdata-dev/atomic-data-docs/issues/15)) diff --git a/src/invitations.md b/src/invitations.md index d75cb3a..a96a1d8 100644 --- a/src/invitations.md +++ b/src/invitations.md @@ -1,7 +1,7 @@ {{#title Atomic Data Invitations - Sharing using Tokens }} # Invitations & Tokens -_Discussion: https://github.com/ontola/atomic-data/issues/23_ +([Discussion](https://github.com/ontola/atomic-data/issues/23)) At some point on working on something in a web application, you're pretty likely to share that, often not with the entire world. In order to make this process of inviting others as simple as possible, we've come up with an Invitation standard. diff --git a/src/schema/faq.md b/src/schema/faq.md index 976fe7c..41692e9 100644 --- a/src/schema/faq.md +++ b/src/schema/faq.md @@ -77,4 +77,4 @@ There are multiple ways we can deal with this: - **Cache dependencies**: Atomic Server already stores a copy of every class and property that it uses by default. The `/path` endpoint then allows clients to fetch these from servers that have cached it. If the source goes offline, the validations can still be performed by the server. However, it might be a good idea to migrate the data to a hosted ontology, e.g. by cloning the cached ontology. - **Content-addressing**: using non-HTTP identifiers, such as with [IPFS](../interoperability/ipfs.md). -[See discussion](https://github.com/ontola/atomic-data-docs/issues/99). +([Discussion](https://github.com/ontola/atomic-data-docs/issues/99)) diff --git a/src/schema/translations.md b/src/schema/translations.md index 8c829ad..7cb1065 100644 --- a/src/schema/translations.md +++ b/src/schema/translations.md @@ -4,7 +4,8 @@ _Status: design / concept stage_ Dealing with translations can be hard. -([See discussion on this subject here.](https://github.com/ontola/atomic-data/issues/6)) + +([Discussion](https://github.com/ontola/atomic-data/issues/6)) ## TranslationBox diff --git a/src/usecases/verifiable-credentials.md b/src/usecases/verifiable-credentials.md index 43caa3b..79c58e1 100644 --- a/src/usecases/verifiable-credentials.md +++ b/src/usecases/verifiable-credentials.md @@ -33,7 +33,7 @@ How could revocation work? - **Get the signer** (see above) - **Find the `/isRevoked` Endpoint of that signer**, send a Request there to make sure the linked Commit is still valid and not revoked. -Visit the [issue on github](https://github.com/ontola/atomic-data-docs/issues/22) to join the discussion about this subject. +([Discussion](https://github.com/ontola/atomic-data-docs/issues/22)) ## Use Atomic Schema for standardizing Credentials From b8d7d1669f593b8547e675119a89320d46b70b00 Mon Sep 17 00:00:00 2001 From: Robin Vobruba Date: Sun, 18 Dec 2022 16:50:34 +0200 Subject: [PATCH 117/140] Marks date-format as code --- src/schema/datatypes.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/schema/datatypes.md b/src/schema/datatypes.md index 33c7486..a450928 100644 --- a/src/schema/datatypes.md +++ b/src/schema/datatypes.md @@ -88,7 +88,7 @@ Use a single bit one boolean. ## Date ISO date _without time_. -YYYY-MM-DD. +`YYYY-MM-DD`. e.g. `1991-01-20` From 035a400975a779094a4768e0416cb7e0473a77e7 Mon Sep 17 00:00:00 2001 From: Robin Vobruba Date: Sun, 18 Dec 2022 16:53:28 +0200 Subject: [PATCH 118/140] Adds two commas for better mind-guidance --- src/interoperability/rdf.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/interoperability/rdf.md b/src/interoperability/rdf.md index bc5a6ab..0d72c79 100644 --- a/src/interoperability/rdf.md +++ b/src/interoperability/rdf.md @@ -114,7 +114,7 @@ Does this represent a non-ordered collection, or did something go wrong while se In the RDF world, I've seen many occurences of both. Atomic Data requires `subject-property` uniqueness, which means that these issues are no more. -However, in order to guarantee this, and still retain _graph merge-ability_ we also need to limit who creates statements about a subject: +However, in order to guarantee this, and still retain _graph merge-ability_, we also need to limit who creates statements about a subject: ### Limiting subject usage @@ -138,7 +138,7 @@ This means that someone using RDF data about domain B cannot know that domain B Knowing _where data comes from_ is one of the great things about URIs, but RDF does not require that you can think of subjects as the source of data. Many subjects in RDF don't actually resolve to all the known triples of the statement. It would make the conceptual model way simpler if statements about a subject could only be made from the source of the domain owner of the subject. -When triples are created about a resource in a place other than where the subject is hosted, these triples are hard to share. +When triples are created about a resource, in a place other than where the subject is hosted, these triples are hard to share. The way RDF projects deal with this, is by using _named graphs_. As a consequence, all systems that use these triples should keep track of another field for every atom. From 31485473461e9bfc755f7bcb1012da861195d4f9 Mon Sep 17 00:00:00 2001 From: Robin Vobruba Date: Sun, 18 Dec 2022 16:52:55 +0200 Subject: [PATCH 119/140] Improves readability(?) (Please double-check!) --- src/interoperability/rdf.md | 4 ++-- src/schema/translations.md | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/interoperability/rdf.md b/src/interoperability/rdf.md index 0d72c79..08638c4 100644 --- a/src/interoperability/rdf.md +++ b/src/interoperability/rdf.md @@ -142,7 +142,7 @@ When triples are created about a resource, in a place other than where the subje The way RDF projects deal with this, is by using _named graphs_. As a consequence, all systems that use these triples should keep track of another field for every atom. -To make things worse, it makes `subject-predicate` _impossible_ to guarantee. +To make things worse, it makes `subject-predicate` uniqueness _impossible_ to guarantee. That's a high price to pay. I've asked two RDF developers (who did not know each other) working on RDF about limiting subject usage, and both were critical. @@ -158,7 +158,7 @@ In RDF, an `object` can either be a `named node`, `blank node` or `literal`. A ` Although RDF statements are often called `triples`, a single statement can consist of five fields: `subject`, `predicate`, `object`, `language`, `datatype`. Having five fields is way more than most information systems. Usually we have just `key` and `value`. This difference leads to compatibility issues when using RDF in applications. -In practice, clients have to run a lot of checks before they can use the data - which makes RDF in most contexts harder to use than something such as JSON. +In practice, clients have to run a lot of checks before they can use the data - which makes RDF in most contexts harder to use than something like JSON. Atomic Data drops the `named node` / `literal` distinction. We just have `values`, and they are interpreted by looking at the `datatype`, which is defined in the `property`. diff --git a/src/schema/translations.md b/src/schema/translations.md index 7cb1065..dc1193a 100644 --- a/src/schema/translations.md +++ b/src/schema/translations.md @@ -28,6 +28,6 @@ For example: } ``` -Every single property used for Translation strings are instances of the Translation class. +Every single value used for Translation strings is an instance of the Translation class. A translation string uses the [MDString](https://atomicdata.dev/datatypes/markdown) datatype, which means it allows Markdown syntax. From 5cedcbc4241771ebce27a51b085101d6414e295d Mon Sep 17 00:00:00 2001 From: Joep Meindertsma Date: Sun, 18 Dec 2022 18:07:27 +0100 Subject: [PATCH 120/140] #143 temp disable mdbook-open-on-gh --- .github/workflows/blank.yml | 3 ++- book.toml | 11 ++++++----- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/.github/workflows/blank.yml b/.github/workflows/blank.yml index e74fca1..8b6cade 100644 --- a/.github/workflows/blank.yml +++ b/.github/workflows/blank.yml @@ -18,7 +18,8 @@ jobs: with: mdbook-version: 'latest' - - run: cargo install mdbook-open-on-gh --version 2.1.0 + # https://github.com/atomicdata-dev/atomic-data-docs/issues/143 + # - run: cargo install mdbook-open-on-gh - run: mdbook build diff --git a/book.toml b/book.toml index 3848b54..cb9090f 100644 --- a/book.toml +++ b/book.toml @@ -12,8 +12,9 @@ additional-css = ["open-in.css"] [output.linkcheck] optional = true -[preprocessor.open-on-gh] -command = "mdbook-open-on-gh" -renderer = ["html"] -text = "mdbook-open-on-gh" -open-on-text = "[Suggest edits for this page on GitHub.]" +# Temp disable, see https://github.com/atomicdata-dev/atomic-data-docs/issues/143 +# [preprocessor.open-on-gh] +# command = "mdbook-open-on-gh" +# renderer = ["html"] +# text = "mdbook-open-on-gh" +# open-on-text = "[Suggest edits for this page on GitHub.]" From 724dd798b779d9c98a05c3604353ee3360e85f58 Mon Sep 17 00:00:00 2001 From: Joep Meindertsma Date: Sun, 18 Dec 2022 20:53:09 +0100 Subject: [PATCH 121/140] #143 re-enable mdbook-open-on-gh --- .github/workflows/blank.yml | 3 +-- book.toml | 11 +++++------ 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/.github/workflows/blank.yml b/.github/workflows/blank.yml index 8b6cade..f28b1da 100644 --- a/.github/workflows/blank.yml +++ b/.github/workflows/blank.yml @@ -18,8 +18,7 @@ jobs: with: mdbook-version: 'latest' - # https://github.com/atomicdata-dev/atomic-data-docs/issues/143 - # - run: cargo install mdbook-open-on-gh + - run: cargo install mdbook-open-on-gh - run: mdbook build diff --git a/book.toml b/book.toml index cb9090f..3848b54 100644 --- a/book.toml +++ b/book.toml @@ -12,9 +12,8 @@ additional-css = ["open-in.css"] [output.linkcheck] optional = true -# Temp disable, see https://github.com/atomicdata-dev/atomic-data-docs/issues/143 -# [preprocessor.open-on-gh] -# command = "mdbook-open-on-gh" -# renderer = ["html"] -# text = "mdbook-open-on-gh" -# open-on-text = "[Suggest edits for this page on GitHub.]" +[preprocessor.open-on-gh] +command = "mdbook-open-on-gh" +renderer = ["html"] +text = "mdbook-open-on-gh" +open-on-text = "[Suggest edits for this page on GitHub.]" From 580494139d64df4ec219331c779c726064f08710 Mon Sep 17 00:00:00 2001 From: Adam Gustafson Date: Mon, 9 Jan 2023 15:26:15 -0700 Subject: [PATCH 122/140] Fixed spelling --- src/roadmap.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/roadmap.md b/src/roadmap.md index 8c97f28..fb288c4 100644 --- a/src/roadmap.md +++ b/src/roadmap.md @@ -1,6 +1,6 @@ # Strategy, history and roadmap for Atomic Data -We have the ambition ro make the internet more interoperable. +We have the ambition to make the internet more interoperable. We want Atomic Data to be a commonly used specification, enabling a vast amount of applications to work together and share information. This means we need a lot of people to understand and contribute to Atomic Data. In this document, discuss the strategic principles we use, the steps we took, and the path forward. From a25bd42ed5556b24d271796fd80b057b60e988ed Mon Sep 17 00:00:00 2001 From: Joep Meindertsma Date: Mon, 16 Jan 2023 10:00:00 +0100 Subject: [PATCH 123/140] Update roadmap --- src/roadmap.md | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/roadmap.md b/src/roadmap.md index fb288c4..35c1d5e 100644 --- a/src/roadmap.md +++ b/src/roadmap.md @@ -37,6 +37,7 @@ This should help you understand how and where you may be able to contribute. - **Indexed queries** (2022-01). Huge performance increase for queries. Allows for far bigger datasets. - **Use case: ChatRoom** (2022-04). Group chat application. To make this possible, we had to extend the Commit model with a `push` action, and allow Plugins to create new Commits. - **[JSON-AD Publishing and Importing](create-json-ad.md)** (2022-08). Creating and consuming Atomic Data becomes a whole lot easier. +- **[@tomic/svelte](https://github.com/atomicdata-dev/atomic-svelte)** (2022-12). Library for integrating Atomic Data with Svelte(Kit). ## Where we're at @@ -48,11 +49,12 @@ That's why we're now working on the JSON-AD and Atomizer projects (see below). ## Roadmap -- **[Atomizer](https://github.com/atomicdata-dev/atomic-data-rust/issues/434)** (2022-Q4). Import files and automatically turn these into Atomic Data. -- **Improved modelling tools** (2022-Q4). Makes it even easier to create Classes and define Properties. -- **Video(s) about Atomic Data** (2022-Q4). Explain what Atomic Data is, why we're doing this, and how to get started. -- **[Atomic-server plugins](https://github.com/atomicdata-dev/atomic-data-rust/issues/73)** (2023). Let developers design new features without having to make PRs in Atomic-Server, and let users install apps without re-compiling (or even restarting) anything. -- **Use case: headless CMS** (2023). Use Atomic-Server to host and edit data that is being read by a front-end JAMSTACK type of tool, such as NextJS or SvelteKit. -- **Atomic-browser plugins** (2023). Create new views for Classes. -- **1.0 release** (2024). Mark the specification, the server [(tracking issue)](https://github.com/atomicdata-dev/atomic-data-rust/milestone/5) and the browser as _stable_. It is possible that the Spec will become 1.0 before any implementation is stable. +- **Video(s) about Atomic Data** (2023 Q1). Explain what Atomic Data is, why we're doing this, and how to get started. +- **[Atomic Tables](https://github.com/atomicdata-dev/atomic-data-browser/issues/25)** (2023 Q2). A powerful table editor with keyboard / copy / paste / sort support that makes it easier to model and edit data. +- **[E-mail registration]()** (2023 Q1). This makes it easier for users to get started, and de-emphasizes the importance of private key management, as user can register new Private Keys using their e-mail address. +- **Headless CMS tooling** (2023). Use Atomic-Server to host and edit data that is being read by a front-end JAMSTACK type of tool, such as NextJS or SvelteKit. +- **[Atomizer](https://github.com/atomicdata-dev/atomic-data-rust/issues/434)** (2023). Import files and automatically turn these into Atomic Data. +- **[Atomic-server plugins](https://github.com/atomicdata-dev/atomic-data-rust/issues/73)** (2024). Let developers design new features without having to make PRs in Atomic-Server, and let users install apps without re-compiling (or even restarting) anything. +- **Atomic-browser plugins** (2024). Create new views for Classes. +- **1.0 release** (2024). Mark the specification, the server [(tracking issue)](https://github.com/atomicdata-dev/atomic-data-rust/milestone/5) and the browser as _stable_. It is possible that the Spec will become 1.0 before any implementation is stable. Read the [STATUS.md](https://github.com/atomicdata-dev/atomic-data-rust/blob/master/server/STATUS.md) document for an up-to-date list of features that are already stable. - **Model Marketplace** (2024). A place where user can easily find, compare and use Classes, Properties and Ontologies. From 95a8be36eb82ea073565f0724e8dbb06a9d55157 Mon Sep 17 00:00:00 2001 From: Joep Meindertsma Date: Mon, 16 Jan 2023 10:21:47 +0100 Subject: [PATCH 124/140] update roadmap --- src/roadmap.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/roadmap.md b/src/roadmap.md index 35c1d5e..272137b 100644 --- a/src/roadmap.md +++ b/src/roadmap.md @@ -51,10 +51,10 @@ That's why we're now working on the JSON-AD and Atomizer projects (see below). - **Video(s) about Atomic Data** (2023 Q1). Explain what Atomic Data is, why we're doing this, and how to get started. - **[Atomic Tables](https://github.com/atomicdata-dev/atomic-data-browser/issues/25)** (2023 Q2). A powerful table editor with keyboard / copy / paste / sort support that makes it easier to model and edit data. -- **[E-mail registration]()** (2023 Q1). This makes it easier for users to get started, and de-emphasizes the importance of private key management, as user can register new Private Keys using their e-mail address. +- **[E-mail registration](https://github.com/atomicdata-dev/atomic-data-rust/issues/276)** (2023 Q1). This makes it easier for users to get started, and de-emphasizes the importance of private key management, as user can register new Private Keys using their e-mail address. - **Headless CMS tooling** (2023). Use Atomic-Server to host and edit data that is being read by a front-end JAMSTACK type of tool, such as NextJS or SvelteKit. - **[Atomizer](https://github.com/atomicdata-dev/atomic-data-rust/issues/434)** (2023). Import files and automatically turn these into Atomic Data. +- **Model Marketplace** (2023 Q4). A place where user can easily find, compare and use Classes, Properties and Ontologies. - **[Atomic-server plugins](https://github.com/atomicdata-dev/atomic-data-rust/issues/73)** (2024). Let developers design new features without having to make PRs in Atomic-Server, and let users install apps without re-compiling (or even restarting) anything. - **Atomic-browser plugins** (2024). Create new views for Classes. - **1.0 release** (2024). Mark the specification, the server [(tracking issue)](https://github.com/atomicdata-dev/atomic-data-rust/milestone/5) and the browser as _stable_. It is possible that the Spec will become 1.0 before any implementation is stable. Read the [STATUS.md](https://github.com/atomicdata-dev/atomic-data-rust/blob/master/server/STATUS.md) document for an up-to-date list of features that are already stable. -- **Model Marketplace** (2024). A place where user can easily find, compare and use Classes, Properties and Ontologies. From e8151e395a0e892eddff1c2315daa980ab19ea50 Mon Sep 17 00:00:00 2001 From: Joep Meindertsma Date: Mon, 30 Jan 2023 14:47:05 +0100 Subject: [PATCH 125/140] Update invitations.md --- src/invitations.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/invitations.md b/src/invitations.md index a96a1d8..278e5e9 100644 --- a/src/invitations.md +++ b/src/invitations.md @@ -21,3 +21,8 @@ In order to make this process of inviting others as simple as possible, we've co 1. The Guest will now be able to access the Resource. Try it on [https://atomicdata.dev/invites/1](https://atomicdata.dev/invites/1) + +## Limitations and gotcha's + +- The one creating the Invite has to take security in consideration. Some URLs can be easily guessed! When implementing Invitations, make sure to use a good amount of randomness when creating the Subject. +- Make sure that the invite is not publicly discoverable (e.g. through a Collection), this can happen if you set the `parent` of the invite to a public resource. From ec5aa341cc91c595fbfecccc8f6c5a7d8baac132 Mon Sep 17 00:00:00 2001 From: holoncom Date: Fri, 10 Feb 2023 10:32:32 +0100 Subject: [PATCH 126/140] various small changes to help a newbe --- src/atomic-server.md | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/src/atomic-server.md b/src/atomic-server.md index 3220b58..8d58bf5 100644 --- a/src/atomic-server.md +++ b/src/atomic-server.md @@ -1,5 +1,15 @@ # Creating Atomic Data using Atomic-Server +Here is everything you need to get started: + + - [Atomic-Server and its features](#atomic-server-and-its-features) + - [Running Atomic-Server locally (optional)](#running-atomic-server-locally-optional) + - [Creating an Agent](#creating-an-agent) + - [Creating your first Atomic Data](#creating-your-first-atomic-data) + - [Next steps](#theres-more) + +## Atomic-Server and its features + [`Atomic-Server`](https://github.com/atomicdata-dev/atomic-data-rust/blob/master/server/README.md) is the _reference implementation_ of the Atomic Data Core + Extended specification. It was developed parallel to this specification, and it served as a testing ground for various ideas (some of which didn't work, and some of which ended up in the spec). @@ -78,13 +88,13 @@ Now let's create a [_Class_](https://atomicdata.dev/classes/Class). A Class represents an abstract concept, such as a `BlogPost` (which we'll do here). We can do this in a couple of ways: -- Press the `new resource` button on the left menu, and selecting Class +- Press the `+ icon` button on the left menu (only visible when logged in), and selecting Class - Opening [Class](https://atomicdata.dev/classes/Class) and pressing `new class` - Going to the [Classes Collection](https://atomicdata.dev/classes/) and pressing the plus icon The result is the same: we end up with a form in which we can fill in some details. -Let's add a shortname, and then a description. +Let's add a shortname (singular), and then a description. After that, we'll add the `required` properties. This form you're looking at is constructed by using the `required` and `recommended` Properties defined in `Class`. @@ -92,7 +102,7 @@ We can use these same fields to generate our BlogPost resource! Which fields would be required in a `BlogPost`? A `name`, and a `description`, probably. -So let's search for these Properties, and add them. +So click on the `+ icon` under `requires` and search for these Properties to add them. Now, we can skip the `recommended` properties, and get right to saving our newly created `BlogPost` class. So, press save, and now look at what you created. @@ -102,8 +112,8 @@ Notice a couple of things: - Your Class has its own URL. - It has a `parent`, shown in the top of the screen. This has impact on the visibility and rights of your Resource. We'll get to that [later in the documentation](./hierarchy.md). -Now, use the context menu to open the `Data View`. -This gives you some more insight into your newly created data, and various ways in which you van serialize it. +Now, go to the navigation bar, which is by default at the bottom of the window. Use its context menu to open the `Data View`. +This view gives you some more insight into your newly created data, and various ways in which you can serialize it. ## There's more! From a494b0da122f9df92a0bbea9a6ea74bef2ee7248 Mon Sep 17 00:00:00 2001 From: Joep Meindertsma Date: Mon, 13 Feb 2023 12:23:20 +0100 Subject: [PATCH 127/140] Update usecases --- src/SUMMARY.md | 1 + src/usecases/data-catalog.md | 32 ++++++++++++++++++++++++++++++++ src/usecases/education.md | 29 ++++++++++++++++++++++++++--- src/usecases/headless-cms.md | 5 +++++ src/usecases/intro.md | 1 + src/usecases/zettelkasten.md | 2 ++ 6 files changed, 67 insertions(+), 3 deletions(-) create mode 100644 src/usecases/data-catalog.md create mode 100644 src/usecases/zettelkasten.md diff --git a/src/SUMMARY.md b/src/SUMMARY.md index bc86ea5..f58d1c6 100644 --- a/src/SUMMARY.md +++ b/src/SUMMARY.md @@ -56,6 +56,7 @@ * [E-commerce & marketplaces](usecases/e-commerce.md) * [Surveys](usecases/surveys.md) * [Verifiable Credentials](usecases/verifiable-credentials.md) + * [Data Catalog](usecases/data-catalog.md) * [Food labels](usecases/food-labels.md) * [**Software and libraries**](tooling.md) diff --git a/src/usecases/data-catalog.md b/src/usecases/data-catalog.md new file mode 100644 index 0000000..3a9df81 --- /dev/null +++ b/src/usecases/data-catalog.md @@ -0,0 +1,32 @@ +{{#title Atomic Server as a Data Catalog}} +# Using Atomic-Server as a Data Catalog + +A data catalog is a system that collects metadata - data about data. +They are inventories of datasets. + +They are often used to: + +- **Increase data-reuse of (open) datasets**. By making descriptions of datasets, you increase their discoverability. +- **Manage data quality**. The more datasets you have, the more you'll want to make sure they are usable. This could mean settings serialization requirements or schema compliance. +- **Manage compliance with privacy laws**. If you have datasets that contain GDPR-relevant data (personal data), you're legally required to maintain a list of where that data is stored, what you need it for and what you're doing with it. + +## Why Atomic Server could be great for Data Catalogs + +- Free & **open source**. MIT licensed! +- Many built-in **features**, like full-text search, versioning, live synchronization, rights management, +- Great **performance**. Requests take nanoseconds to milliseconds. +- Very **easy to setup**. One single binary, no weird runtime dependencies. +- Everything is linked data. Not just datasets (which you might), but also everything around them (users, comments, implementations) +- [Atomic Schema](../schema/intro.md) can be used to describe the shape of your datasets: the properties you use, which fields are required - things like that. Because Atomic Schema uses URLs, we can easily re-use properties and class definitions. This helps to make your datasets highly interoperable. + +## Atomic Server compared to CKAN + +- Atomic-Server is MIT licensed - which is more permissive than CKAN's AGPL license. +- Whereas CKAN needs an external database, a python runtime, solrd and a HTTPS server, Atomic-Server has all of these built-in! +- CKAN uses plain RDF, which has some [very important drawbacks](../interoperability/rdf.md). +- But... Atomic-Server still misses a few essentials: + +## What we should add to Atomic-Server before it's actually good + +- Add a model for datasets. This is absolutely essential. It could be based on (and link to) DCAT, but needs to be described using Atomic Schema. This step means we can generate forms for Datasets and we can validate their fields. +- Add views for datasets. Atomic-Server already renders decent views for unknown resources, but a specific view should be created for Datasets. diff --git a/src/usecases/education.md b/src/usecases/education.md index e5b43bb..f908ce2 100644 --- a/src/usecases/education.md +++ b/src/usecases/education.md @@ -1,6 +1,29 @@ # Atomic Data for Eduction - standardized, modular e-learling -The Atomic Data specification can help make online educational content more modular and standardized. +The Atomic Data specification can help make online educational content more **modular**. This has two direct benefits: -- Seperate learning goals from how they are achieved. Some might prefer watching a video, others may want to read. Both can describe the same topic, and share the same test. -- Create links between topics so students know which knowledge is needed to advance to the next topic. +- **Separate learning goals from how they are achieved**. Some might prefer watching a video, others may want to read. Both can describe the same topic, and share the same test. +- **Improve discoverability**. Create links between topics so students know which knowledge is needed to advance to the next topic. + +## Modular educational content + +We can think of **knowledge** as being building blocks that we need to do certain things. +And we can think of **lessons** as _teaching_ certain pieces of knowledge, while at the same time _requiring_ other pieces of knowledge. +For example, an algebra class might require that you already know how to multiply, add, etc. +Finally, we can think of **test** as _verifying_ if a piece of knowledge is properly understood. + +Describing our educational content in this fashion has a bunch of advantages. +For students, this means they can know in advance if they can get started with a course, or if they need to learn something else first. +Conversely, they can also discover new topics that depend on their previous piece of knowledge. +For teachers, this means they can re-use existing lessons for the courses. + +## What makes Atomic-Server a great tool for creating online courseware + +- Powerful built-in document editor +- Drag & drop file support +- Versioning +- Open source, so no vendor lock-in, and full customizability +- Real-time updates, great for collaboration +- Online by default, so no extra hassle with putting courses on the internet + +However, there diff --git a/src/usecases/headless-cms.md b/src/usecases/headless-cms.md index e9bed48..47f8196 100644 --- a/src/usecases/headless-cms.md +++ b/src/usecases/headless-cms.md @@ -42,3 +42,8 @@ The [Atomic-Server](https://github.com/atomicdata-dev/atomic-data-rust/blob/mast The `@tomic/lib` and `@tomic/react` typescript NPM libraries can be used in any JS project. In the next section, we'll discuss how to use Atomic-Server in your React project. + +## Compared to alternative open source headless CMS software + +- **Strapi**: Atomic-Server doesn't need an external database, is easier to setup, has live synchronization support and is way faster. However, Strapi has a plugin system, is more polished, and has GraphQL support. +- ** diff --git a/src/usecases/intro.md b/src/usecases/intro.md index ac26c68..76e0f57 100644 --- a/src/usecases/intro.md +++ b/src/usecases/intro.md @@ -11,6 +11,7 @@ Although you could use Atomic Data for pretty much any type of application, it i * [Personal Data Store](personal-data-store.md) * [Artificial Intelligence](ai.md) * [E-commerce & marketplaces](e-commerce.md) +* [Data Catalog](data-catalog.md) * [Surveys](surveys.md) * [Verifiable Credentials](verifiable-credentials.md) * [Food labels](food-labels.md) diff --git a/src/usecases/zettelkasten.md b/src/usecases/zettelkasten.md new file mode 100644 index 0000000..8d1b18d --- /dev/null +++ b/src/usecases/zettelkasten.md @@ -0,0 +1,2 @@ +{{#title Atomic Data for Zettelkasten}} +# Atomic Data for Zettelkasten From 0664eebb33cc2a3778dd7350c9a71ba07d3e8433 Mon Sep 17 00:00:00 2001 From: Joep Meindertsma Date: Mon, 13 Feb 2023 12:33:36 +0100 Subject: [PATCH 128/140] Improve docs for datacat --- .github/workflows/{blank.yml => deploy.yml} | 0 src/usecases/data-catalog.md | 4 ++-- 2 files changed, 2 insertions(+), 2 deletions(-) rename .github/workflows/{blank.yml => deploy.yml} (100%) diff --git a/.github/workflows/blank.yml b/.github/workflows/deploy.yml similarity index 100% rename from .github/workflows/blank.yml rename to .github/workflows/deploy.yml diff --git a/src/usecases/data-catalog.md b/src/usecases/data-catalog.md index 3a9df81..226e271 100644 --- a/src/usecases/data-catalog.md +++ b/src/usecases/data-catalog.md @@ -26,7 +26,7 @@ They are often used to: - CKAN uses plain RDF, which has some [very important drawbacks](../interoperability/rdf.md). - But... Atomic-Server still misses a few essentials: -## What we should add to Atomic-Server before it's actually good +## What we should add to Atomic-Server before it's a decent Data Catalog - Add a model for datasets. This is absolutely essential. It could be based on (and link to) DCAT, but needs to be described using Atomic Schema. This step means we can generate forms for Datasets and we can validate their fields. -- Add views for datasets. Atomic-Server already renders decent views for unknown resources, but a specific view should be created for Datasets. +- Add views for datasets. Atomic-Server already renders decent views for unknown resources, but a specific view should be created for Datasets. [Add a PR](https://github.com/atomicdata-dev/atomic-data-browser) if you have a React view! From eb772f0afa45381d8eeba8ccfbf2e020280ede72 Mon Sep 17 00:00:00 2001 From: Joep Meindertsma Date: Mon, 13 Feb 2023 12:55:37 +0100 Subject: [PATCH 129/140] Show education / e-learning usecase --- src/SUMMARY.md | 1 + src/usecases/education.md | 18 +++++++++++++----- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/src/SUMMARY.md b/src/SUMMARY.md index f58d1c6..25f3a52 100644 --- a/src/SUMMARY.md +++ b/src/SUMMARY.md @@ -57,6 +57,7 @@ * [Surveys](usecases/surveys.md) * [Verifiable Credentials](usecases/verifiable-credentials.md) * [Data Catalog](usecases/data-catalog.md) + * [Education](usecases/education.md) * [Food labels](usecases/food-labels.md) * [**Software and libraries**](tooling.md) diff --git a/src/usecases/education.md b/src/usecases/education.md index f908ce2..65c7ff6 100644 --- a/src/usecases/education.md +++ b/src/usecases/education.md @@ -5,12 +5,15 @@ The Atomic Data specification can help make online educational content more **mo - **Separate learning goals from how they are achieved**. Some might prefer watching a video, others may want to read. Both can describe the same topic, and share the same test. - **Improve discoverability**. Create links between topics so students know which knowledge is needed to advance to the next topic. -## Modular educational content +## Modular educational content - a model -We can think of **knowledge** as being building blocks that we need to do certain things. -And we can think of **lessons** as _teaching_ certain pieces of knowledge, while at the same time _requiring_ other pieces of knowledge. +We can think of **Knowledge** as being building blocks that we need to do certain things. +And we can think of **Lessons** as _teaching_ certain pieces of knowledge, while at the same time _requiring_ other pieces of knowledge. For example, an algebra class might require that you already know how to multiply, add, etc. -Finally, we can think of **test** as _verifying_ if a piece of knowledge is properly understood. +We can think of **Test** as _verifying_ if a piece of knowledge is properly understood. + +Now there's also a relationship between the **Student** and all of these things. +A student is following a bunch Lessons in which they've made some progress, has done some Tests which resulted in Scores. Describing our educational content in this fashion has a bunch of advantages. For students, this means they can know in advance if they can get started with a course, or if they need to learn something else first. @@ -26,4 +29,9 @@ For teachers, this means they can re-use existing lessons for the courses. - Real-time updates, great for collaboration - Online by default, so no extra hassle with putting courses on the internet -However, there +However, there is still a lot to do! + +- Turn the model described above into an actual Atomic Schema data model +- Build the GUI for the application +- Add plugins / extenders for things like doing tests (without giving the answer to students!) +- Create educational content From a9e62cbf5859013f9166e7168aecf57652d831f9 Mon Sep 17 00:00:00 2001 From: Joep Meindertsma Date: Mon, 13 Feb 2023 12:56:10 +0100 Subject: [PATCH 130/140] Update use cases list --- src/usecases/intro.md | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/src/usecases/intro.md b/src/usecases/intro.md index 76e0f57..2109138 100644 --- a/src/usecases/intro.md +++ b/src/usecases/intro.md @@ -6,12 +6,13 @@ In this section, we'll present concrete examples of things that can be built wit Although you could use Atomic Data for pretty much any type of application, it is especially valuable where **data re-use**, **standardization**, and **data ownership** are important. -* [As a Headless CMS](headless-cms.md) -* [In a React project](react.md) -* [Personal Data Store](personal-data-store.md) -* [Artificial Intelligence](ai.md) -* [E-commerce & marketplaces](e-commerce.md) -* [Data Catalog](data-catalog.md) -* [Surveys](surveys.md) -* [Verifiable Credentials](verifiable-credentials.md) -* [Food labels](food-labels.md) +* [As a Headless CMS](usecases/headless-cms.md) +* [In a React project](usecases/react.md) +* [Personal Data Store](usecases/personal-data-store.md) +* [Artificial Intelligence](usecases/ai.md) +* [E-commerce & marketplaces](usecases/e-commerce.md) +* [Surveys](usecases/surveys.md) +* [Verifiable Credentials](usecases/verifiable-credentials.md) +* [Data Catalog](usecases/data-catalog.md) +* [Education](usecases/education.md) +* [Food labels](usecases/food-labels.md) From 32c873b686e537b4e66b462cbd5037f77ff57a3f Mon Sep 17 00:00:00 2001 From: Joep Meindertsma Date: Mon, 13 Feb 2023 15:57:10 +0100 Subject: [PATCH 131/140] Improve e-commerce --- src/usecases/e-commerce.md | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/src/usecases/e-commerce.md b/src/usecases/e-commerce.md index 4dae5d4..14548aa 100644 --- a/src/usecases/e-commerce.md +++ b/src/usecases/e-commerce.md @@ -58,7 +58,21 @@ Describing this in a machine-readable and predictable format as data is the next This is, of course, where Atomic Schema could help. Atomic-server could be the connected, open source database that suppliers use to describe their products as data. - +- Build notifications support (see [issue](https://github.com/atomicdata-dev/atomic-data-rust/issues/77)) From da64548acd294705ef18814089e39b7e4d63553d Mon Sep 17 00:00:00 2001 From: Joep Meindertsma Date: Wed, 15 Feb 2023 10:40:38 +0100 Subject: [PATCH 132/140] Data catalog improvements --- src/usecases/crud.md | 1 + src/usecases/data-catalog.md | 19 +++++++++++++++---- 2 files changed, 16 insertions(+), 4 deletions(-) create mode 100644 src/usecases/crud.md diff --git a/src/usecases/crud.md b/src/usecases/crud.md new file mode 100644 index 0000000..4189252 --- /dev/null +++ b/src/usecases/crud.md @@ -0,0 +1 @@ +# Atomic-Server for CRUD applications diff --git a/src/usecases/data-catalog.md b/src/usecases/data-catalog.md index 226e271..183ce59 100644 --- a/src/usecases/data-catalog.md +++ b/src/usecases/data-catalog.md @@ -12,19 +12,30 @@ They are often used to: ## Why Atomic Server could be great for Data Catalogs +[Atomic-Server](https://docs.atomicdata.dev/atomic-server.html) is a powerful Database that can be used as a modern, powerful data catalog. It has a few advantages over others: + - Free & **open source**. MIT licensed! -- Many built-in **features**, like full-text search, versioning, live synchronization, rights management, +- Many built-in features, like **full-text search**, **history**, **live synchronization** and **rights management**. - Great **performance**. Requests take nanoseconds to milliseconds. - Very **easy to setup**. One single binary, no weird runtime dependencies. -- Everything is linked data. Not just datasets (which you might), but also everything around them (users, comments, implementations) -- [Atomic Schema](../schema/intro.md) can be used to describe the shape of your datasets: the properties you use, which fields are required - things like that. Because Atomic Schema uses URLs, we can easily re-use properties and class definitions. This helps to make your datasets highly interoperable. +- Everything is linked data. Not just datasets (which you might), but also everything around them (users, comments, implementations). +- Powerful **CMS capabilities**. With built in support for Tables and Documents, you can easily create webpages with articles or other types of resources using Atomic Server. +- [Atomic Schema](../schema/intro.md) can be used to describe the **shape of your datasets**: the properties you use, which fields are required - things like that. Because Atomic Schema uses URLs, we can easily re-use properties and class definitions. This helps to make your datasets highly interoperable. + +## When Atomic-Server is used for hosting the data, too + +Most datacatalogs only have metadata. However, if you convert your existing CSV / JSON / XML / ... datasets to _Atomic Data_, you can host them on Atomic-Server as well. This has a few advantages: + +- **Data previews** in the browser, users can navigate through the data without leaving the catalog. Data itself becomes browseable, too, which means you can traverse a graph by clicking on link values. +- **Standardized Querying** means you can easily, from the data catalog, can filter and sort the data. +- **Cross-dataset search**. Search queries can be performed over multiple Atomic Data servers at once, enabling searching over multiple datasets. This is also called _federated search_. ## Atomic Server compared to CKAN - Atomic-Server is MIT licensed - which is more permissive than CKAN's AGPL license. - Whereas CKAN needs an external database, a python runtime, solrd and a HTTPS server, Atomic-Server has all of these built-in! - CKAN uses plain RDF, which has some [very important drawbacks](../interoperability/rdf.md). -- But... Atomic-Server still misses a few essentials: +- But... Atomic-Server still misses a few essentials right now: ## What we should add to Atomic-Server before it's a decent Data Catalog From 7bf3800478408bce92d95bbc7bddffd138329af9 Mon Sep 17 00:00:00 2001 From: Joep Meindertsma Date: Thu, 2 Mar 2023 11:04:20 +0100 Subject: [PATCH 133/140] Fix links, ports docker --- src/atomic-data-overview.md | 2 +- src/atomic-server.md | 12 ++++++------ src/get-started.md | 4 ++-- src/interoperability/solid.md | 1 + src/tooling.md | 2 +- src/usecases/data-catalog.md | 3 ++- src/usecases/headless-cms.md | 3 ++- src/usecases/intro.md | 20 ++++++++++---------- 8 files changed, 25 insertions(+), 22 deletions(-) diff --git a/src/atomic-data-overview.md b/src/atomic-data-overview.md index fc728db..724f833 100644 --- a/src/atomic-data-overview.md +++ b/src/atomic-data-overview.md @@ -41,7 +41,7 @@ In the Atomizing section, we'll show you how you can create Atomic Data in three - Browser app [atomic-data-browser](https://github.com/atomicdata-dev/atomic-data-browser) ([demo on atomicdata.dev](https://atomicdata.dev)) - Build a react app using [typescript & react libraries](https://github.com/atomicdata-dev/atomic-data-browser). Start with the [react template on codesandbox](https://codesandbox.io/s/atomic-data-react-template-4y9qu?file=/src/MyResource.tsx) -- Host your own [atomic-server](https://github.com/atomicdata-dev/atomic-data-rust) (powers [atomicdata.dev](https://atomicdata.dev), run with `docker run -p 80:80 -p 443:443 -v atomic-storage:/atomic-storage joepmeneer/atomic-server`) +- Host your own [atomic-server](https://github.com/atomicdata-dev/atomic-data-rust) (powers [atomicdata.dev](https://atomicdata.dev), run with `docker run -p 80:80 -v atomic-storage:/atomic-storage joepmeneer/atomic-server`) - Discover the command line tool: [atomic-cli](https://github.com/atomicdata-dev/atomic-data-rust) (`cargo install atomic-cli`) - Use the Rust library: [atomic-lib](https://github.com/atomicdata-dev/atomic-data-rust) diff --git a/src/atomic-server.md b/src/atomic-server.md index 8d58bf5..1c6d408 100644 --- a/src/atomic-server.md +++ b/src/atomic-server.md @@ -2,11 +2,11 @@ Here is everything you need to get started: - - [Atomic-Server and its features](#atomic-server-and-its-features) - - [Running Atomic-Server locally (optional)](#running-atomic-server-locally-optional) - - [Creating an Agent](#creating-an-agent) - - [Creating your first Atomic Data](#creating-your-first-atomic-data) - - [Next steps](#theres-more) +- [Atomic-Server and its features](#atomic-server-and-its-features) +- [Running Atomic-Server locally (optional)](#running-atomic-server-locally-optional) +- [Creating an Agent](#creating-an-agent) +- [Creating your first Atomic Data](#creating-your-first-atomic-data) +- [There's more!](#theres-more) ## Atomic-Server and its features @@ -40,7 +40,7 @@ But if you want to, you can run Atomic-Server on your machine in a couple of way - **Using a desktop installer**: download a desktop release from the [`releases`](https://github.com/atomicdata-dev/atomic-data-rust/releases) page and install it using your desktop GUI. - **Using a binary**: download a binary release from the [`releases`](https://github.com/atomicdata-dev/atomic-data-rust/releases) page and open it using a terminal. -- **Using Docker** is probably the quickest: `docker run -p 80:80 -p 443:443 -v atomic-storage:/atomic-storage joepmeneer/atomic-server`. +- **Using Docker** is probably the quickest: `docker run -p 80:80 -v atomic-storage:/atomic-storage joepmeneer/atomic-server`. - **Using Cargo**: `cargo install atomic-server` and then run `atomic-server` to start. _[Atomic-Server's README](https://github.com/atomicdata-dev/atomic-data-rust/blob/master/server/README.md) contains more (and up-to-date) information about how to use it!_ diff --git a/src/get-started.md b/src/get-started.md index 11f8fe8..ebb4bc6 100644 --- a/src/get-started.md +++ b/src/get-started.md @@ -21,7 +21,7 @@ There's a couple of levels at which you can start working with Atomic Data (from ## Host your own Atomic-Sesrver (locally) -- If you have docker running, you can use this one-liner: `docker run -p 80:80 -p 443:443 -v atomic-storage:/atomic-storage joepmeneer/atomic-server` (or use `cargo install atomic-server`, or the [binaries](https://github.com/atomicdata-dev/atomic-data-rust/releases/)) +- If you have docker running, you can use this one-liner: `docker run -p 80:80 -v atomic-storage:/atomic-storage joepmeneer/atomic-server` (or use `cargo install atomic-server`, or the [binaries](https://github.com/atomicdata-dev/atomic-data-rust/releases/)) - Now, visit `localhost` in your browser to access your server. - It's now only available locally. If you want to get it on the _internet_, you need to set up a domain name, and make sure its traffic is routed to your computer (search `port forwarding`). @@ -34,7 +34,7 @@ There's a couple of levels at which you can start working with Atomic Data (from - Browser app [atomic-data-browser](https://github.com/atomicdata-dev/atomic-data-browser) ([demo on atomicdata.dev](https://atomicdata.dev)) - Build a react app using [typescript & react libraries](https://github.com/atomicdata-dev/atomic-data-browser). Start with the [react template on codesandbox](https://codesandbox.io/s/atomic-data-react-template-4y9qu?file=/src/MyResource.tsx) -- Host your own [atomic-server](https://github.com/atomicdata-dev/atomic-data-browser) (powers [atomicdata.dev](https://atomicdata.dev), run with `docker run -p 80:80 -p 443:443 -v atomic-storage:/atomic-storage joepmeneer/atomic-server`) +- Host your own [atomic-server](https://github.com/atomicdata-dev/atomic-data-browser) (powers [atomicdata.dev](https://atomicdata.dev), run with `docker run -p 80:80 -v atomic-storage:/atomic-storage joepmeneer/atomic-server`) - Discover the command line tool: [atomic-cli](https://github.com/atomicdata-dev/atomic-data-rust) (`cargo install atomic-cli`) - Use the Rust library: [atomic-lib](https://github.com/atomicdata-dev/atomic-data-rust) diff --git a/src/interoperability/solid.md b/src/interoperability/solid.md index 28dcc37..4f9e888 100644 --- a/src/interoperability/solid.md +++ b/src/interoperability/solid.md @@ -124,6 +124,7 @@ It was definitely built to be one, at least. It implements every part of the Atomic Data specification. I believe that as of today (february 2022), Atomic-Server has quite a few advantages over existing Solid implementations: + - **Dynamic schema validation** / type checking using [Atomic Schema](https://docs.atomicdata.dev/schema/intro.html), combining the best of RDF, JSON and type safety. - **Fast** (1ms responses on my laptop) - **Lightweight** (8MB download, no runtime dependencies) diff --git a/src/tooling.md b/src/tooling.md index ad939e4..c3170fc 100644 --- a/src/tooling.md +++ b/src/tooling.md @@ -26,7 +26,7 @@ Server for hosting Atomic Data. Uses `atomic-lib`. - Authorization, authentication, versioning, collections, pagination - Browser-friendly HTML presentation, JSON serialization, RDF serialization. -One liner: `$ docker run -p 80:80 -p 443:443 -v atomic-storage:/atomic-storage joepmeneer/atomic-server` +One liner: `$ docker run -p 80:80 -v atomic-storage:/atomic-storage joepmeneer/atomic-server` [demo](https://atomicdata.dev/) diff --git a/src/usecases/data-catalog.md b/src/usecases/data-catalog.md index 183ce59..a6ad231 100644 --- a/src/usecases/data-catalog.md +++ b/src/usecases/data-catalog.md @@ -26,7 +26,8 @@ They are often used to: Most datacatalogs only have metadata. However, if you convert your existing CSV / JSON / XML / ... datasets to _Atomic Data_, you can host them on Atomic-Server as well. This has a few advantages: -- **Data previews** in the browser, users can navigate through the data without leaving the catalog. Data itself becomes browseable, too, which means you can traverse a graph by clicking on link values. +- **Data previews** in the browser, users can navigate through the data without leaving the catalog. +- Data itself becomes **browseable**, too, which means you can traverse a graph by clicking on link values. - **Standardized Querying** means you can easily, from the data catalog, can filter and sort the data. - **Cross-dataset search**. Search queries can be performed over multiple Atomic Data servers at once, enabling searching over multiple datasets. This is also called _federated search_. diff --git a/src/usecases/headless-cms.md b/src/usecases/headless-cms.md index 47f8196..5aee16a 100644 --- a/src/usecases/headless-cms.md +++ b/src/usecases/headless-cms.md @@ -13,6 +13,7 @@ This approach has some issues regarding performance and flexibility that headles The [Atomic-Server](https://github.com/atomicdata-dev/atomic-data-rust/blob/master/server/README.md) project may be the right choice for you if you're looking for a Headless CMS: + - **Free and open source**. MIT licensed, no strings attached. - **Easy to use API**. Atomic-Server is built using the [Atomic Data specification](../atomic-data-overview.md). It is well-documented, and uses conventions that most web developers are already familiar with. - **Typescript & React libraries**. Use the existing react hooks to make your own fully editable, live-reloaded web application. @@ -34,7 +35,7 @@ The [Atomic-Server](https://github.com/atomicdata-dev/atomic-data-rust/blob/mast ## Setting up the server -- One-liners: `cargo install atomic-server` or `docker run -p 80:80 -p 443:443 -v atomic-storage:/atomic-storage joepmeneer/atomic-server` +- One-liners: `cargo install atomic-server` or `docker run -p 80:80 -v atomic-storage:/atomic-storage joepmeneer/atomic-server` - Check out the [readme!](https://github.com/atomicdata-dev/atomic-data-rust/blob/master/server/README.md) ## Using the data in your (React / NextJS) app diff --git a/src/usecases/intro.md b/src/usecases/intro.md index 2109138..de75bc9 100644 --- a/src/usecases/intro.md +++ b/src/usecases/intro.md @@ -6,13 +6,13 @@ In this section, we'll present concrete examples of things that can be built wit Although you could use Atomic Data for pretty much any type of application, it is especially valuable where **data re-use**, **standardization**, and **data ownership** are important. -* [As a Headless CMS](usecases/headless-cms.md) -* [In a React project](usecases/react.md) -* [Personal Data Store](usecases/personal-data-store.md) -* [Artificial Intelligence](usecases/ai.md) -* [E-commerce & marketplaces](usecases/e-commerce.md) -* [Surveys](usecases/surveys.md) -* [Verifiable Credentials](usecases/verifiable-credentials.md) -* [Data Catalog](usecases/data-catalog.md) -* [Education](usecases/education.md) -* [Food labels](usecases/food-labels.md) +* [As a Headless CMS](headless-cms.md) +* [In a React project](react.md) +* [Personal Data Store](personal-data-store.md) +* [Artificial Intelligence](ai.md) +* [E-commerce & marketplaces](e-commerce.md) +* [Surveys](surveys.md) +* [Verifiable Credentials](verifiable-credentials.md) +* [Data Catalog](data-catalog.md) +* [Education](education.md) +* [Food labels](food-labels.md) From d305f802901a55cb079cde250b688f3f2cfa2298 Mon Sep 17 00:00:00 2001 From: Kristoffer Andersson Date: Wed, 8 Mar 2023 14:04:06 +0100 Subject: [PATCH 134/140] Fix typos Correct 2 typos in heading. --- src/usecases/education.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/usecases/education.md b/src/usecases/education.md index 65c7ff6..e319de7 100644 --- a/src/usecases/education.md +++ b/src/usecases/education.md @@ -1,4 +1,4 @@ -# Atomic Data for Eduction - standardized, modular e-learling +# Atomic Data for Education - standardized, modular e-learning The Atomic Data specification can help make online educational content more **modular**. This has two direct benefits: From 053a77ab75fec21fd8f205ab66884667fb0a46ef Mon Sep 17 00:00:00 2001 From: Joep Meindertsma Date: Wed, 29 Mar 2023 22:46:27 +0200 Subject: [PATCH 135/140] Remove TPF --- src/core/querying.md | 30 +----------------------------- src/interoperability/vc-old.md | 2 +- src/schema/faq.md | 6 ++++++ src/tooling.md | 1 - 4 files changed, 8 insertions(+), 31 deletions(-) diff --git a/src/core/querying.md b/src/core/querying.md index 9df23b7..1e45763 100644 --- a/src/core/querying.md +++ b/src/core/querying.md @@ -49,39 +49,11 @@ An Atomic Path is a string that consist of one or more URLs, which when traverse [Read more about Atomic Paths](paths.md) -## Triple Pattern Fragments - -[Triple Pattern Fragments](https://linkeddatafragments.org/specification/triple-pattern-fragments/) (TPF) is an interface for querying RDF. -It works great for Atomic Data as well. - -An HTTP implementation of a TPF endpoint might accept a GET request to a URL such as this: - -`http://example.org/tpf?subject={subject}&property={property}&value={value}` - -Make sure to URL encode the `subject`, `property`, `value` strings. - -For example, let's search for all Atoms where the value is `test`. - -```HTTP -GET https://atomicdata.dev/tpf?value=0 HTTP/1.1 -Content-Type: text/turtle -``` - -This is the HTTP response: - -```HTTP -HTTP/1.1 200 OK -Content-Type: text/turtle -Connection: Closed - - "0"^^ . -``` - ## SPARQL [SPARQL](https://www.w3.org/TR/rdf-sparql-query/) is a powerful RDF query language. Since all Atomic Data is also valid RDF, it should be possible to query Atomic Data using SPARQL. None of the exsisting implementations support a SPARQL endpoint, though. -- Convert / serialize Atomic Data to RDF (for example by using the `/tpf` endpoint and an `accept` header: `curl -i -H "Accept: text/turtle" "https://atomicdata.dev/tpf"`) +- Convert / serialize Atomic Data to RDF (for example by using an `accept` header: `curl -i -H "Accept: text/turtle" "https://atomicdata.dev"`) - Load it into a SPARQL engine of your choice diff --git a/src/interoperability/vc-old.md b/src/interoperability/vc-old.md index f06f0e4..52a9919 100644 --- a/src/interoperability/vc-old.md +++ b/src/interoperability/vc-old.md @@ -24,7 +24,7 @@ This is by design, as storing signed state changes allows for fully verifiable a If you want to know whether a specific value that you see is signed by a specific Agent, you need to find the Commit that created the value. -This can be achieved by performing a TPF query or using a Collection. +This can be achieved by using a Collection. The easiest way to do this, is by using the [`/all-versions` Endpoint](https://atomicdata.dev/all-versions) and finding the Signer of the version that is relevant to your question. In the near future, we will introduce a `/verify` Endpoint that will allow you to verify a specific value. diff --git a/src/schema/faq.md b/src/schema/faq.md index 41692e9..4f619f9 100644 --- a/src/schema/faq.md +++ b/src/schema/faq.md @@ -78,3 +78,9 @@ There are multiple ways we can deal with this: - **Content-addressing**: using non-HTTP identifiers, such as with [IPFS](../interoperability/ipfs.md). ([Discussion](https://github.com/ontola/atomic-data-docs/issues/99)) + +## How do I deal with subclasses / inheritance? + +Atomic Data does not have a concept of inheritance. +However, you can use the `isA` property to link to _multiple Classes_ from a single resource. +This effectively diff --git a/src/tooling.md b/src/tooling.md index c3170fc..393559f 100644 --- a/src/tooling.md +++ b/src/tooling.md @@ -96,7 +96,6 @@ Library that powers `atomic-server` and `atomic-cli`. Features: - An in-memory store - Parsing (JSON-AD) / Serialization (JSON-AD, JSON-LD, TTL, N-Triples) - Commit validation and processing -- TPF queries - Constructing Collections - Path traversal - Basic validation From 50d56d3ffb14fabcab60d11681bebfe65a927401 Mon Sep 17 00:00:00 2001 From: Joep Meindertsma Date: Wed, 19 Apr 2023 14:58:16 +0200 Subject: [PATCH 136/140] Fix URLs for new atomic-server repo --- README.md | 2 +- src/atomic-data-overview.md | 6 +++--- src/atomic-server.md | 10 +++++----- src/commits/concepts.md | 2 +- src/core/paths.md | 2 +- src/create-json-ad.md | 14 +++++++------- src/files.md | 2 +- src/get-started.md | 6 +++--- src/interoperability/json.md | 2 +- src/interoperability/solid.md | 6 +++--- src/interoperability/sql.md | 2 +- src/interoperability/upgrade.md | 2 +- src/roadmap.md | 10 +++++----- src/tooling.md | 4 ++-- src/usecases/e-commerce.md | 2 +- src/usecases/food-labels.md | 2 +- src/usecases/headless-cms.md | 10 +++++----- src/usecases/personal-data-store.md | 2 +- src/usecases/surveys.md | 2 +- src/websockets.md | 2 +- 20 files changed, 45 insertions(+), 45 deletions(-) diff --git a/README.md b/README.md index 2afb9f9..18b778b 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ _Atomic Data is a specification for sharing, modifying and modeling graph data._ View it on [docs.atomicdata.dev](https://docs.atomicdata.dev). -If you're looking for **implementations of Atomic Data**, check out [atomic-data-rust](https://github.com/atomicdata-dev/atomic-data-rust) (server + cli + lib written in Rust) and [atomic-data-browser](https://github.com/atomicdata-dev/atomic-data-browser) (react / typescript). +If you're looking for **implementations of Atomic Data**, check out [atomic-server](https://github.com/atomicdata-dev/atomic-server) (server + cli + lib written in Rust) and [atomic-data-browser](https://github.com/atomicdata-dev/atomic-data-browser) (react / typescript). ## About this repo diff --git a/src/atomic-data-overview.md b/src/atomic-data-overview.md index 724f833..468bd5d 100644 --- a/src/atomic-data-overview.md +++ b/src/atomic-data-overview.md @@ -41,9 +41,9 @@ In the Atomizing section, we'll show you how you can create Atomic Data in three - Browser app [atomic-data-browser](https://github.com/atomicdata-dev/atomic-data-browser) ([demo on atomicdata.dev](https://atomicdata.dev)) - Build a react app using [typescript & react libraries](https://github.com/atomicdata-dev/atomic-data-browser). Start with the [react template on codesandbox](https://codesandbox.io/s/atomic-data-react-template-4y9qu?file=/src/MyResource.tsx) -- Host your own [atomic-server](https://github.com/atomicdata-dev/atomic-data-rust) (powers [atomicdata.dev](https://atomicdata.dev), run with `docker run -p 80:80 -v atomic-storage:/atomic-storage joepmeneer/atomic-server`) -- Discover the command line tool: [atomic-cli](https://github.com/atomicdata-dev/atomic-data-rust) (`cargo install atomic-cli`) -- Use the Rust library: [atomic-lib](https://github.com/atomicdata-dev/atomic-data-rust) +- Host your own [atomic-server](https://github.com/atomicdata-dev/atomic-server) (powers [atomicdata.dev](https://atomicdata.dev), run with `docker run -p 80:80 -v atomic-storage:/atomic-storage joepmeneer/atomic-server`) +- Discover the command line tool: [atomic-cli](https://github.com/atomicdata-dev/atomic-server) (`cargo install atomic-cli`) +- Use the Rust library: [atomic-lib](https://github.com/atomicdata-dev/atomic-server) ## Get involved diff --git a/src/atomic-server.md b/src/atomic-server.md index 1c6d408..cfe0295 100644 --- a/src/atomic-server.md +++ b/src/atomic-server.md @@ -10,7 +10,7 @@ Here is everything you need to get started: ## Atomic-Server and its features -[`Atomic-Server`](https://github.com/atomicdata-dev/atomic-data-rust/blob/master/server/README.md) is the _reference implementation_ of the Atomic Data Core + Extended specification. +[`Atomic-Server`](https://github.com/atomicdata-dev/atomic-server/blob/master/server/README.md) is the _reference implementation_ of the Atomic Data Core + Extended specification. It was developed parallel to this specification, and it served as a testing ground for various ideas (some of which didn't work, and some of which ended up in the spec). Atomic-Server is a graph database server for storing and sharing typed linked data. @@ -38,12 +38,12 @@ In this guide, we'll can simply use `atomicdata.dev` in our browser without inst So you can skip this step and go to _Creating your first Atomic Data_. But if you want to, you can run Atomic-Server on your machine in a couple of ways: -- **Using a desktop installer**: download a desktop release from the [`releases`](https://github.com/atomicdata-dev/atomic-data-rust/releases) page and install it using your desktop GUI. -- **Using a binary**: download a binary release from the [`releases`](https://github.com/atomicdata-dev/atomic-data-rust/releases) page and open it using a terminal. +- **Using a desktop installer**: download a desktop release from the [`releases`](https://github.com/atomicdata-dev/atomic-server/releases) page and install it using your desktop GUI. +- **Using a binary**: download a binary release from the [`releases`](https://github.com/atomicdata-dev/atomic-server/releases) page and open it using a terminal. - **Using Docker** is probably the quickest: `docker run -p 80:80 -v atomic-storage:/atomic-storage joepmeneer/atomic-server`. - **Using Cargo**: `cargo install atomic-server` and then run `atomic-server` to start. -_[Atomic-Server's README](https://github.com/atomicdata-dev/atomic-data-rust/blob/master/server/README.md) contains more (and up-to-date) information about how to use it!_ +_[Atomic-Server's README](https://github.com/atomicdata-dev/atomic-server) contains more (and up-to-date) information about how to use it!_ Open your server in your browser. By default, that's [`http://localhost:9883`](http://localhost:9883). @@ -69,7 +69,7 @@ You can also fetch your data now as various formats. Try checking out the other features in the menu bar, and check out the `collections`. -Again, check out the [README](https://github.com/atomicdata-dev/atomic-data-rust/blob/master/server/README.md) for more information and guides! +Again, check out the [README](https://github.com/atomicdata-dev/atomic-server) for more information and guides! Now, let's create some data. diff --git a/src/commits/concepts.md b/src/commits/concepts.md index 3f912f1..8584814 100644 --- a/src/commits/concepts.md +++ b/src/commits/concepts.md @@ -83,7 +83,7 @@ Congratulations, you've just created a valid Commit! Here are currently working implementations of this process, including serialization and signing (links are permalinks). -- [in Rust (atomic-lib)](https://github.com/atomicdata-dev/atomic-data-rust/blob/ceb88c1ae58811f2a9e6bacb7eaa39a2a7aa1513/lib/src/commit.rs#L81). +- [in Rust (atomic-lib)](https://github.com/atomicdata-dev/atomic-server/blob/ceb88c1ae58811f2a9e6bacb7eaa39a2a7aa1513/lib/src/commit.rs#L81). - [in Typescript / Javascript (atomic-data-browser)](https://github.com/atomicdata-dev/atomic-data-browser/blob/fc899bb2cf54bdff593ee6b4debf52e20a85619e/src/atomic-lib/commit.ts#L51). If you want validate your implementation, check out the tests for these two projects. diff --git a/src/core/paths.md b/src/core/paths.md index 25112d5..60f78a9 100644 --- a/src/core/paths.md +++ b/src/core/paths.md @@ -110,4 +110,4 @@ This means that we still have a unique, globally resolvable identifier - yay! ## Try for yourself -Install the [`atomic-cli`](https://github.com/atomicdata-dev/atomic-data-rust/blob/master/cli/README.md) software and run `atomic-cli get https://atomicdata.dev/classes/Class description`. +Install the [`atomic-cli`](https://github.com/atomicdata-dev/atomic-server/blob/master/cli/README.md) software and run `atomic-cli get https://atomicdata.dev/classes/Class description`. diff --git a/src/create-json-ad.md b/src/create-json-ad.md index 76a31f1..fa47583 100644 --- a/src/create-json-ad.md +++ b/src/create-json-ad.md @@ -45,7 +45,7 @@ In Atomic Data, Resources can have multiple classes, so we should use an Array, { "https://atomicdata.dev/properties/name": "Writing my first blogpost", "https://atomicdata.dev/properties/description": "Hi! I'm a blogpost. I'm also machine readable!", - "https://atomicdata.dev/properties/isA": ["https://atomicdata.dev/classes/Blogpost"], + "https://atomicdata.dev/properties/isA": ["https://atomicdata.dev/classes/Article"], } ``` @@ -72,11 +72,11 @@ If we want to have _multiple_ items, we can simply use a JSON Array at the root, [{ "https://atomicdata.dev/properties/name": "Writing my first blogpost", "https://atomicdata.dev/properties/description": "Hi! I'm a blogpost. I'm also machine readable!", - "https://atomicdata.dev/properties/isA": ["https://atomicdata.dev/classes/Blogpost"], + "https://atomicdata.dev/properties/isA": ["https://atomicdata.dev/classes/Article"], },{ "https://atomicdata.dev/properties/name": "Another blogpost", "https://atomicdata.dev/properties/description": "I'm writing so much my hands hurt.", - "https://atomicdata.dev/properties/isA": ["https://atomicdata.dev/classes/Blogpost"], + "https://atomicdata.dev/properties/isA": ["https://atomicdata.dev/classes/Article"], }] ``` @@ -96,7 +96,7 @@ Let's use a unique _slug_, a short name that is often used in URLs. { "https://atomicdata.dev/properties/name": "Writing my first blogpost", "https://atomicdata.dev/properties/description": "Hi! I'm a blogpost. I'm also machine readable!", - "https://atomicdata.dev/properties/isA": ["https://atomicdata.dev/classes/Blogpost"], + "https://atomicdata.dev/properties/isA": ["https://atomicdata.dev/classes/Article"], "https://atomicdata.dev/properties/localId": "my-first-blogpost", } ``` @@ -117,13 +117,13 @@ We can simply refer to the `localId`, instead of some URL that does not exist ye "https://atomicdata.dev/properties/name": "Writing my first blogpost", "https://atomicdata.dev/properties/description": "Hi! I'm a blogpost. I'm also machine readable!", "https://atomicdata.dev/properties/author": "jon", - "https://atomicdata.dev/properties/isA": ["https://atomicdata.dev/classes/Blogpost"], + "https://atomicdata.dev/properties/isA": ["https://atomicdata.dev/classes/Article"], "https://atomicdata.dev/properties/localId": "my-first-blogpost", },{ "https://atomicdata.dev/properties/name": "Another blogpost", "https://atomicdata.dev/properties/description": "I'm writing so much my hands hurt.", "https://atomicdata.dev/properties/author": "jon", - "https://atomicdata.dev/properties/isA": ["https://atomicdata.dev/classes/Blogpost"], + "https://atomicdata.dev/properties/isA": ["https://atomicdata.dev/classes/Article"], "https://atomicdata.dev/properties/localId": "another-blogpost", },{ "https://atomicdata.dev/properties/name": "Jon Author", @@ -134,4 +134,4 @@ We can simply refer to the `localId`, instead of some URL that does not exist ye ## Importing data using Atomic Sever -_currently [under development](https://github.com/atomicdata-dev/atomic-data-rust-data-rust/issues/390)_ +_currently [under development](https://github.com/atomicdata-dev/atomic-server/issues/390)_ diff --git a/src/files.md b/src/files.md index eb9928d..42cf2b3 100644 --- a/src/files.md +++ b/src/files.md @@ -29,5 +29,5 @@ In `atomic-server`, a `/upload` endpoint exists for uploading a file. Simply send an HTTP GET request to the File's [`download-url`](https://atomicdata.dev/properties/downloadURL) (make sure to authenticate this request). - [Discussion on specification](https://github.com/ontola/atomic-data-docs/issues/57) -- [Discussion on Rust server implementation](https://github.com/atomicdata-dev/atomic-data-rust/issues/72) +- [Discussion on Rust server implementation](https://github.com/atomicdata-dev/atomic-server/issues/72) - [Discussion on Typescript client implementation](https://github.com/atomicdata-dev/atomic-data-browser/issues/121) diff --git a/src/get-started.md b/src/get-started.md index ebb4bc6..e50bedb 100644 --- a/src/get-started.md +++ b/src/get-started.md @@ -21,7 +21,7 @@ There's a couple of levels at which you can start working with Atomic Data (from ## Host your own Atomic-Sesrver (locally) -- If you have docker running, you can use this one-liner: `docker run -p 80:80 -v atomic-storage:/atomic-storage joepmeneer/atomic-server` (or use `cargo install atomic-server`, or the [binaries](https://github.com/atomicdata-dev/atomic-data-rust/releases/)) +- If you have docker running, you can use this one-liner: `docker run -p 80:80 -v atomic-storage:/atomic-storage joepmeneer/atomic-server` (or use `cargo install atomic-server`, or the [binaries](https://github.com/atomicdata-dev/atomic-server/releases/)) - Now, visit `localhost` in your browser to access your server. - It's now only available locally. If you want to get it on the _internet_, you need to set up a domain name, and make sure its traffic is routed to your computer (search `port forwarding`). @@ -35,7 +35,7 @@ There's a couple of levels at which you can start working with Atomic Data (from - Browser app [atomic-data-browser](https://github.com/atomicdata-dev/atomic-data-browser) ([demo on atomicdata.dev](https://atomicdata.dev)) - Build a react app using [typescript & react libraries](https://github.com/atomicdata-dev/atomic-data-browser). Start with the [react template on codesandbox](https://codesandbox.io/s/atomic-data-react-template-4y9qu?file=/src/MyResource.tsx) - Host your own [atomic-server](https://github.com/atomicdata-dev/atomic-data-browser) (powers [atomicdata.dev](https://atomicdata.dev), run with `docker run -p 80:80 -v atomic-storage:/atomic-storage joepmeneer/atomic-server`) -- Discover the command line tool: [atomic-cli](https://github.com/atomicdata-dev/atomic-data-rust) (`cargo install atomic-cli`) -- Use the Rust library: [atomic-lib](https://github.com/atomicdata-dev/atomic-data-rust) +- Discover the command line tool: [atomic-cli](https://github.com/atomicdata-dev/atomic-server) (`cargo install atomic-cli`) +- Use the Rust library: [atomic-lib](https://github.com/atomicdata-dev/atomic-server) Make sure to [join our Discord](https://discord.gg/a72Rv2P) if you'd like to discuss Atomic Data with others. diff --git a/src/interoperability/json.md b/src/interoperability/json.md index b31b344..7e5a600 100644 --- a/src/interoperability/json.md +++ b/src/interoperability/json.md @@ -71,7 +71,7 @@ The Property keys (e.g. "https://example.com/properties/name") need to resolve t } ``` -In practice, the easiest approach to make this conversion, is to create the data and host it using software like [Atomic Server](https://github.com/atomicdata-dev/atomic-data-rust/blob/master/server/README.md). +In practice, the easiest approach to make this conversion, is to create the data and host it using software like [Atomic Server](https://github.com/atomicdata-dev/atomic-server/blob/master/server/README.md). ## From Atomic Data to JSON-LD diff --git a/src/interoperability/solid.md b/src/interoperability/solid.md index 4f9e888..274cd9b 100644 --- a/src/interoperability/solid.md +++ b/src/interoperability/solid.md @@ -119,12 +119,12 @@ So, in a nutshell, I think this legacy makes Solid unnecessarily hard to use for Both Atomic Data and Solid are specifications that have different implementations. Some open source Solid implementations are the [Node Solid Server](https://github.com/solid/node-solid-server), the [Community Solid Server](https://github.com/solid/community-server) (also nodejs based) and the [DexPod](https://gitlab.com/ontola/dexpod) (Ruby on Rails based). -[Atomic-Server](https://github.com/atomicdata-dev/atomic-data-rust/) is a database + server written in the Rust programming language, that can be considered an alternative to Solid Pod implementations. +[Atomic-Server](https://github.com/atomicdata-dev/atomic-server/) is a database + server written in the Rust programming language, that can be considered an alternative to Solid Pod implementations. It was definitely built to be one, at least. It implements every part of the Atomic Data specification. I believe that as of today (february 2022), Atomic-Server has quite a few advantages over existing Solid implementations: - + - **Dynamic schema validation** / type checking using [Atomic Schema](https://docs.atomicdata.dev/schema/intro.html), combining the best of RDF, JSON and type safety. - **Fast** (1ms responses on my laptop) - **Lightweight** (8MB download, no runtime dependencies) @@ -144,5 +144,5 @@ Atomic Data is not even two years old, and although progress has been fast, it d Here's a list of things missing in Atomic Data, with links to their open issues and links to their existing Solid counterpart. - No inbox or [notifications](https://www.w3.org/TR/ldn/) yet ([issue](https://github.com/ontola/atomic-data/issues/28)) -- No OIDC support yet. ([issue](https://github.com/atomicdata-dev/atomic-data-rust/issues/277)) +- No OIDC support yet. ([issue](https://github.com/atomicdata-dev/atomic-server/issues/277)) - No support from a big community, a well-funded business or the inventor of the world wide web. diff --git a/src/interoperability/sql.md b/src/interoperability/sql.md index 08ecc9e..dc582d1 100644 --- a/src/interoperability/sql.md +++ b/src/interoperability/sql.md @@ -62,7 +62,7 @@ If you want to store arbitrary Atomic Data in a SQL database, you might be best - SQL is far more common, many people will know how to use it. - SQL databases are battle-tested and has been powering countless of products for tens of years, whereas Atomic Server is at this moment in beta. - SQL databases have a more powerful and expressive query language, where you can define tables in your query and combine resources. -- Atomic Data doesn't have a [mutli-node / distributed option](https://github.com/atomicdata-dev/atomic-data-rust/issues/213) +- Atomic Data doesn't have a [mutli-node / distributed option](https://github.com/atomicdata-dev/atomic-server/issues/213) ## FAQ diff --git a/src/interoperability/upgrade.md b/src/interoperability/upgrade.md index fc8fd68..49fe51c 100644 --- a/src/interoperability/upgrade.md +++ b/src/interoperability/upgrade.md @@ -40,7 +40,7 @@ Make sure that when the user requests some URL, that you return that resource as You can go all out, and implement Commits, Hierarchies, Authentication, Collections and [more](https://docs.atomicdata.dev/extended.html). I'd suggest starting with [Commits](../commits/intro.md), as these allow users to modify data whilst maintaining versioning and auditability. -Check out the [Atomic-Server source code](https://github.com/atomicdata-dev/atomic-data-rust/tree/master/server) to get inspired on how to do this. +Check out the [Atomic-Server source code](https://github.com/atomicdata-dev/atomic-server/tree/master/server) to get inspired on how to do this. ## Reach out for help diff --git a/src/roadmap.md b/src/roadmap.md index 272137b..efa8b41 100644 --- a/src/roadmap.md +++ b/src/roadmap.md @@ -20,7 +20,7 @@ This should help you understand how and where you may be able to contribute. - **First draft of specification** (2020-06). Atomic Data started as an unnamed bundle of ideas and best practices to improve how we work with linked data, but quickly turned into a single (draft) specification. The idea was to start with a cohesive and easy to understand documentation, and use that as a stepping stone for writing the first code. After this, the code and specification should both be worked on simultaneously to make sure ideas are both easily explainable and properly implementable. Many of the earliest ideas were changed to make implementation easier. - **[atomic-cli](https://crates.io/crates/atomic-cli) + [atomic-lib](https://docs.rs/atomic_lib/0.32.1/atomic_lib/)** (2020-07). The CLI functioned as the first platform to explore some of the most core ideas of Atomic Data, such as Properties and fetching. `atomic_lib` is the place where most logic resides. Written in Rust. -- **[Atomic-Server](https://github.com/atomicdata-dev/atomic-data-rust/)** (2020-08). The server (using the same `atomic_lib` as the CLI) should be a fast, lightweight server that must be easy to set-up. Functions as a graph database with no dependencies. +- **[AtomicServer](https://github.com/atomicdata-dev/atomic-server/)** (2020-08). The server (using the same `atomic_lib` as the CLI) should be a fast, lightweight server that must be easy to set-up. Functions as a graph database with no dependencies. - **[Collections](schema/collections.md)** (2020-10). Allows users to perform basic queries, filtering, sorting and pagination. - **[Commits](commits/intro.md)** (2020-11). Allow keeping track of an event-sourced log of all activities that mutate resources, which in turn allows for versioning and adding new types of indexes later on. - **[JSON-AD](core/json-ad.md)** (2021-02). Instead of the earlier proposed serialization format `.ad3`, we moved to the more familiar `json-ad`. @@ -51,10 +51,10 @@ That's why we're now working on the JSON-AD and Atomizer projects (see below). - **Video(s) about Atomic Data** (2023 Q1). Explain what Atomic Data is, why we're doing this, and how to get started. - **[Atomic Tables](https://github.com/atomicdata-dev/atomic-data-browser/issues/25)** (2023 Q2). A powerful table editor with keyboard / copy / paste / sort support that makes it easier to model and edit data. -- **[E-mail registration](https://github.com/atomicdata-dev/atomic-data-rust/issues/276)** (2023 Q1). This makes it easier for users to get started, and de-emphasizes the importance of private key management, as user can register new Private Keys using their e-mail address. +- **[E-mail registration](https://github.com/atomicdata-dev/atomic-server/issues/276)** (2023 Q1). This makes it easier for users to get started, and de-emphasizes the importance of private key management, as user can register new Private Keys using their e-mail address. - **Headless CMS tooling** (2023). Use Atomic-Server to host and edit data that is being read by a front-end JAMSTACK type of tool, such as NextJS or SvelteKit. -- **[Atomizer](https://github.com/atomicdata-dev/atomic-data-rust/issues/434)** (2023). Import files and automatically turn these into Atomic Data. +- **[Atomizer](https://github.com/atomicdata-dev/atomic-server/issues/434)** (2023). Import files and automatically turn these into Atomic Data. - **Model Marketplace** (2023 Q4). A place where user can easily find, compare and use Classes, Properties and Ontologies. -- **[Atomic-server plugins](https://github.com/atomicdata-dev/atomic-data-rust/issues/73)** (2024). Let developers design new features without having to make PRs in Atomic-Server, and let users install apps without re-compiling (or even restarting) anything. +- **[Atomic-server plugins](https://github.com/atomicdata-dev/atomic-server/issues/73)** (2024). Let developers design new features without having to make PRs in Atomic-Server, and let users install apps without re-compiling (or even restarting) anything. - **Atomic-browser plugins** (2024). Create new views for Classes. -- **1.0 release** (2024). Mark the specification, the server [(tracking issue)](https://github.com/atomicdata-dev/atomic-data-rust/milestone/5) and the browser as _stable_. It is possible that the Spec will become 1.0 before any implementation is stable. Read the [STATUS.md](https://github.com/atomicdata-dev/atomic-data-rust/blob/master/server/STATUS.md) document for an up-to-date list of features that are already stable. +- **1.0 release** (2024). Mark the specification, the server [(tracking issue)](https://github.com/atomicdata-dev/atomic-server/milestone/5) and the browser as _stable_. It is possible that the Spec will become 1.0 before any implementation is stable. Read the [STATUS.md](https://github.com/atomicdata-dev/atomic-server/blob/master/server/STATUS.md) document for an up-to-date list of features that are already stable. diff --git a/src/tooling.md b/src/tooling.md index 393559f..df26799 100644 --- a/src/tooling.md +++ b/src/tooling.md @@ -5,9 +5,9 @@ Although Atomic Data is a specification, it also has reference implementations: Open source (MIT licenced) software for Atomic Data: -- **Server + Database**: [atomic-server](https://github.com/atomicdata-dev/atomic-data-rust) +- **Server + Database**: [atomic-server](https://github.com/atomicdata-dev/atomic-server) - **GUI**: [atomic-data-browser](https://github.com/atomicdata-dev/atomic-data-browser) -- **CLI**: [atomic-cli](https://github.com/atomicdata-dev/atomic-data-rust) +- **CLI**: [atomic-cli](https://github.com/atomicdata-dev/atomic-server) Libraries (MIT licenced) to build apps with: diff --git a/src/usecases/e-commerce.md b/src/usecases/e-commerce.md index 14548aa..98d7df4 100644 --- a/src/usecases/e-commerce.md +++ b/src/usecases/e-commerce.md @@ -84,4 +84,4 @@ When any of these manufacturers would publish a safety warning about a product t Before we have this, well need to: -- Build notifications support (see [issue](https://github.com/atomicdata-dev/atomic-data-rust/issues/77)) +- Build notifications support (see [issue](https://github.com/atomicdata-dev/atomic-server/issues/77)) diff --git a/src/usecases/food-labels.md b/src/usecases/food-labels.md index aae60af..a4efc9d 100644 --- a/src/usecases/food-labels.md +++ b/src/usecases/food-labels.md @@ -31,7 +31,7 @@ This can be done, because the groceries app can easily check detailed informatio ## How to achieve all this -1. The governing body (e.g. the European Commision) should set up an [Atomic Server](https://github.com/atomicdata-dev/atomic-data-rust/) and host it on some recognizable domain. +1. The governing body (e.g. the European Commision) should set up an [Atomic Server](https://github.com/atomicdata-dev/atomic-server/) and host it on some recognizable domain. 1. Create the [Class](https://atomicdata.dev/classes/Class) for a food product, containing the same (or more) information that is shown on food packages. 1. Create the Class for Ingredient. 1. Create instances for various Ingredients. Start with the E-numbers, work your way up to all kinds of used ingredients. Add Translations. diff --git a/src/usecases/headless-cms.md b/src/usecases/headless-cms.md index 5aee16a..a58984e 100644 --- a/src/usecases/headless-cms.md +++ b/src/usecases/headless-cms.md @@ -11,9 +11,9 @@ This approach has some issues regarding performance and flexibility that headles ## Atomic Server -The [Atomic-Server](https://github.com/atomicdata-dev/atomic-data-rust/blob/master/server/README.md) project may be the right choice for you if you're looking for a Headless CMS: +The [Atomic-Server](https://github.com/atomicdata-dev/atomic-server/blob/master/server/README.md) project may be the right choice for you if you're looking for a Headless CMS: - + - **Free and open source**. MIT licensed, no strings attached. - **Easy to use API**. Atomic-Server is built using the [Atomic Data specification](../atomic-data-overview.md). It is well-documented, and uses conventions that most web developers are already familiar with. - **Typescript & React libraries**. Use the existing react hooks to make your own fully editable, live-reloaded web application. @@ -30,13 +30,13 @@ The [Atomic-Server](https://github.com/atomicdata-dev/atomic-data-rust/blob/mast ## Limitations -- No support for image resizing, [as of now](https://github.com/atomicdata-dev/atomic-data-rust/issues/257) -- No GraphQL support [(see issue)](https://github.com/atomicdata-dev/atomic-data-rust/issues/251) +- No support for image resizing, [as of now](https://github.com/atomicdata-dev/atomic-server/issues/257) +- No GraphQL support [(see issue)](https://github.com/atomicdata-dev/atomic-server/issues/251) ## Setting up the server - One-liners: `cargo install atomic-server` or `docker run -p 80:80 -v atomic-storage:/atomic-storage joepmeneer/atomic-server` -- Check out the [readme!](https://github.com/atomicdata-dev/atomic-data-rust/blob/master/server/README.md) +- Check out the [readme!](https://github.com/atomicdata-dev/atomic-server) ## Using the data in your (React / NextJS) app diff --git a/src/usecases/personal-data-store.md b/src/usecases/personal-data-store.md index b9418fd..1e8ddb9 100644 --- a/src/usecases/personal-data-store.md +++ b/src/usecases/personal-data-store.md @@ -11,7 +11,7 @@ Many services don't even provide export functionality, and even if they do, the Atomic Data could help to re-introduce data ownership. Because the specification helps to standardize information, it becomes easier to make data interoperable. -And even more important: Apps don't need their own back-end - they can use the same personal data store: an Atomic Server (such as [this one](https://github.com/atomicdata-dev/atomic-data-rust/blob/master/server/README.md)). +And even more important: Apps don't need their own back-end - they can use the same personal data store: an Atomic Server (such as [this one](https://github.com/atomicdata-dev/atomic-serverob/master/server/README.md)). Realizing this goal requires quite a bit of work, though. This specification needs to mature, and we need reliable implementations. diff --git a/src/usecases/surveys.md b/src/usecases/surveys.md index d07abec..fb215be 100644 --- a/src/usecases/surveys.md +++ b/src/usecases/surveys.md @@ -50,6 +50,6 @@ The user only sees invitations that are highly relevant, without sharing _any_ i The Atomic Data specification solves at least part of this problem. [Paths](../core/paths.md) are used to describe the queries that researchers make. -[Atomic Server](https://github.com/atomicdata-dev/atomic-data-rust/blob/master/server/README.md) can be used as the personal online data store. +[AtomicServer](https://github.com/atomicdata-dev/atomic--rust/blob/master/server/README.md) can be used as the personal online data store. However, we still need to specify the process of sending a request to an individual (probably by introducing an [inbox](https://github.com/ontola/atomic-data/issues/28)) diff --git a/src/websockets.md b/src/websockets.md index eb07573..6248697 100644 --- a/src/websockets.md +++ b/src/websockets.md @@ -31,4 +31,4 @@ The `WebSocket-Protocol` is `AtomicData`. ## Example implementations - [Example client implementation in Typescript (@tomic/lib).](https://github.com/atomicdata-dev/atomic-data-browser/blob/main/lib/src/websockets.ts) -- [Example server implementation in Rust using Actix-Web](https://github.com/atomicdata-dev/atomic-data-rust/blob/master/server/src/handlers/web_sockets.rs) +- [Example server implementation in Rust using Actix-Web](https://github.com/atomicdata-dev/atomic-server/blob/master/server/src/handlers/web_sockets.rs) From fde5b4c68c6fa0d5ed85dac7a294b803e7ed7a1e Mon Sep 17 00:00:00 2001 From: Joep Meindertsma Date: Fri, 28 Apr 2023 16:31:13 +0200 Subject: [PATCH 137/140] Add bearer token instructions --- src/authentication.md | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/authentication.md b/src/authentication.md index 0c9f4d9..fee1d6b 100644 --- a/src/authentication.md +++ b/src/authentication.md @@ -29,7 +29,7 @@ We use the following fields (be sure to use the full URLs in the resource, see t - `requestedSubject`: The URL of the requested resource. - If we're authenticating a *WebSocket*, we use the `wss` address as the `requestedSubject`. (e.g. `wss://example.com/ws`) - - If we're authenticating a *Cookie*, we use the origin of the server (e.g. `https://example.com`) + - If we're authenticating a *Cookie* of *Bearer token*, we use the origin of the server (e.g. `https://example.com`) - If we're authentication a *single HTTP request*, use the same URL as the `GET` address (e.g. `https://example.com/myResource`) - `agent`: The URL of the Agent requesting the subject and signing this Authentication Resource. - `publicKey`: base64 serialized ED25519 public key of the agent. @@ -63,6 +63,17 @@ This Authentication Resource is stored as a cookie, and passed along in every HT 2. The expiration date of the cookie should be set, and should match the expiration date of the Authentication Resource. 3. Set the `Secure` attribute to prevent Man-in-the-middle attacks over HTTP +## Bearer Token Authentication + +Similar to creating the Cookie, except that we pass the base64 serialized Authentication Resource as a Bearer token in the `Authorization` header. + +```http +GET /myResource HTTP/1.1 +Authorization: Bearer {base64 serialized Authentication Resource} +``` + +In Data Browser, you can find the `token` tab in `/app/token` to create a token. + ## Authenticating Websockets After [opening a WebSocket connection](websockets.md), create an Authentication Resource. From f6bf266a7e6deae07339bf7599c83df6db7baca5 Mon Sep 17 00:00:00 2001 From: Gabriel Grant Date: Fri, 25 Aug 2023 05:43:08 -0400 Subject: [PATCH 138/140] schema/classes.md: linkify and fix URLs for list of datatypes --- src/schema/classes.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/schema/classes.md b/src/schema/classes.md index 1d0d43e..87e7206 100644 --- a/src/schema/classes.md +++ b/src/schema/classes.md @@ -52,7 +52,7 @@ Properties: - `binarySerialization` - (optional, AtomicURL, TranslationBox) how the datatype should be parsed / serialized as a byte array. - `binaryExample` - (optional, string) an example `binarySerialization` that should be parsed correctly. Should have the same contents as the stringExample. Required if binarySerialization is present on the DataType. -Visit https://atomicdata.dev/collections/datatype for a list of example Datatypes. +Visit [https://atomicdata.dev/datatypes](https://atomicdata.dev/datatypes) for a list of example Datatypes. ## Class From 8541249f2b6b033721bdfcf7463dd23a6368f61c Mon Sep 17 00:00:00 2001 From: Gabriel Grant Date: Fri, 25 Aug 2023 05:46:17 -0400 Subject: [PATCH 139/140] schema/classes.md: linkify URL for list of datatypes --- src/schema/classes.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/schema/classes.md b/src/schema/classes.md index 87e7206..4de4ebc 100644 --- a/src/schema/classes.md +++ b/src/schema/classes.md @@ -33,7 +33,7 @@ Properties of a Property instance: } ``` -Visit https://atomicdata.dev/properties for a list of example Properties. +Visit [https://atomicdata.dev/properties](https://atomicdata.dev/properties) for a list of example Properties. ## Datatype From 9b98125fb8d2ac4549ef5785f2cbf39a3b75f0f0 Mon Sep 17 00:00:00 2001 From: Joep Meindertsma Date: Mon, 4 Dec 2023 16:33:56 +0100 Subject: [PATCH 140/140] ARCHIVE --- README.md | 38 +------------------------------------- 1 file changed, 1 insertion(+), 37 deletions(-) diff --git a/README.md b/README.md index 18b778b..2117450 100644 --- a/README.md +++ b/README.md @@ -1,37 +1 @@ -![Atomic Data](src/assets/atomic_data_logo_stroke.svg) - -# `atomic-data-docs` - -_Atomic Data is a specification for sharing, modifying and modeling graph data._ - -View it on [docs.atomicdata.dev](https://docs.atomicdata.dev). -If you're looking for **implementations of Atomic Data**, check out [atomic-server](https://github.com/atomicdata-dev/atomic-server) (server + cli + lib written in Rust) and [atomic-data-browser](https://github.com/atomicdata-dev/atomic-data-browser) (react / typescript). - -## About this repo - -This repository holds the markdown book for the Atomic Data standard. -It serves two purposes: - -- A central hub for **written content** such as documentation, the specification, tutorials and guides. -- A place to **discuss the specification** - that should happen in this issue tracker. - -## Running locally - -You can run it locally using [mdBook](https://github.com/rust-lang/mdBook) - -```sh -# This requires at least Rust 1.39 and Cargo to be installed. Once you have installed Rust, type the following in the terminal: -cargo install mdbook -# Install mdbook-linkcheck to prevent broken links in your markdown. -cargo install mdbook-linkcheck -# Serve at localhost:3000, updates when files change. -mdbook serve -``` - -Publishing is done with Github actions - simply push the master branch. - -## Contributing - -Add an issue or open a PR! -All thoughts are welcome. -Also, check out the [Discord](https://discord.gg/a72Rv2P). +# MOVED TO [ATOMIC-SERVER REPO](https://github.com/atomicdata-dev/atomic-server/tree/develop/docs)