Credibil provides a framework — a collection of libraries — for building decentralized web applications (DWAs); a new class of applications built around individuals (and their agents).
DWAs turn the traditional application development model on its head. Data is stored and owned by application users rather than the application provider. This provides users with greater privacy and control over their data while reducing the risk of data breaches and misuse.
While compelling in its own right, the DWA model comes into its own when extended to AI agents acting on behalf of users. It provides a standards-based way for agents to identify themselves, access user data, and interact with other agents in a secure and accountable manner.
Foundations
Credibil open source libraries are organised around the three building blocks of decentralized web apps:
-
Decentralized Identifiers (DIDs) — tamper-resistant, self-owned identifiers similar to email addresses or usernames.
-
Verifiable Credentials (VCs) — digital credentials that provide cryptographically verifiable proof of things like name, age, drivers licence, etc..
-
Decentralized Web Nodes (DWNs) — replicated data storage and message relay nodes deployed in a mesh-like construct.
When combined, these building blocks enable the creation of user-centric, portable, and “agentic” applications.
User-centric
In contrast to traditional web applications, where user data is stored in centralized databases, DWAs store user data with individual users in a way that is controlled by the user and not a central authority.
A shift away from centralized identity and data systems is overdue. Traditional centralized models are fragile, creating honeypots for hackers and bad actors. And its not just external threats; internal security practices are often disappointingly lax, with administrative teams often having unfettered access to user data. This centralization of power not only compromises security but also puts users at the mercy of gatekeepers who can arbitrarily control access to critical services.
Portable
One side-effect of this approach to applications is the ability to easily move data or reuse data across applications. For example, a user could use the same digital identity across multiple applications without having to create new accounts or re-enter the same information.
Agentic
AI agents, acting on our behalf, are set to become increasingly common. These agents will need to interact with existing systems as well as other agents in a secure and trustworthy manner. The building blocks of decentralized web applications provide a foundation for secure and accountable interactions.
Implementation
The libraries are written in Rust and are designed for use in a variety of environments, including WebAssembly, mobile, and server-side applications.
Decentralized Identifiers
Decentralized Identifiers (DIDs) are self-generated and self-owned identifiers used to uniquely identify entities without recourse to a central authority. DIDs are universally discoverable — they are URIs that resolve to a DID Document containing information such as public key material and service endpoints for the owning entity.
In the context of Decentralised Web Apps (DWAs), DIDs are used to authenticate individuals (or organisations, devices, etc.) as well as provide and endpoints for messaging and data storage (DWN).
Supported DID methods
For simplicity of adoption, Credibil only supports did:web
and did:key
methods. While there are plans to add
support for did:dht
, developers are free to
use other DID methods, with the caveat that it should be supported by participating DWA parties.
See https://github.com/credibil/did for more detail on DID resolution and document creation.
Verifiable Credentials
Physical credentials are ubiquitous; driver’s licenses, university degrees, and passports to name but a few. Verifiable Credentials (VCs) provide a mechanism to express these sorts of credentials on the Web in a way that is cryptographically secure, privacy respecting, and machine-verifiable.
In the context of Decentralised Web Apps (DWAs) VCs can be used to make assertions, or claims, about the holder. In the example below an employer (Issuer) issues a credential to an employee (Holder) as proof of employment. The employee can then present this credential to their bank (Verifier) as proof of income.
Issuing and Verifying VCs
Credibil supports two approaches to issuing a VC. The first approach is based on the OpenID for Verifiable Credential Issuance (OpenID4VCI) and OpenID for Verifiable Presentations (OpenID4VP) standards. The second approach is less prescriptive, using instead Decentralized Web Node (DWN) message protocols to issue and present VCs.
OpenID
See https://github.com/credibil/vc for more detail.
DWN
See https://github.com/credibil/dwn/blob/216f0502b16559ed584b79cbd040ade7d8665381/tests/records_write.rs#L1418 for an example of issuing a VC using the DWN.
TODO: add more detail on using both methods.
Supported formats
Credibil supports W3C Verifiable Credentials and ISO mDL, credential formats with plans to add support for IETF SD-JWT VC.
Issuance
TODO: This page is a work in progress.
Presentation
TODO: This page is a work in progress.
Decentralized Web Nodes
Decentralized Web Node (DWN) is an emerging standard for data storage and message relay that supports entities (people, organisations, etc.) in sending and storing both messages and data.
Because a DWN is controlled by its owning entity, a wide variety of decentralised apps and protocols can be built to be privacy respecting as well as inter-operate across application boundaries.
Features
Notable features of DWNs:
-
Universally addressable — each DWN is linked to its owning entity using the entity’s DID. The corresponding DID document is used to expose the location of the DWN as a service endpoint.
-
Replicated — a DWN may be replicated using masterless eventually consistent replication devices such as IPFS or similar.
-
Secure — access is controlled using DID-based authentication and authorization. That is, incoming messages are signed using a key related to the sender’s DID. In addition, data may be secured using a public key derived from one of the owning entity’s public keys.
-
Semantic discovery — Discover any form of published data simply by knowing its semantic type.
-
Asynchronous message threads — messages are sent and received over a DID-encrypted universal network
-
Any identity type — DWNs can be owned by any type of entity, including people, organisations, machines, etc.
Decentralized Web Apps
Most digital activities between people, organizations, devices, and other entities require the exchange of messages and data. Decentralized web apps (DWAs) put users at the centre of these interactions by adopting a model that lets them store and manage access to their own data and messages.
Conceptually, users bring their data storage and messaging services (in the form of a DWN) to the application rather than them being provisioned on their behalf.
Anatomy of a DWA
Simply, DWAs are apps that store data and messages with their users rather than in a central location using app infrastructure. In order to do this effectively DWAs use standardised storage in the form of Decentralised Web Nodes (DWNs) and standardised authentication in the form of Decentralised Identifiers (DIDs).
Given a user’s DID, apps can access data, relay messages between users, and generally transact with others without reliance on location or provider-specific infrastructure, interfaces, or routing. Additionally, when well-known protocols are employed, apps can be built to interoperate with other apps, allowing users to share data and messages between apps.
Example App
Suspending disbelief for a moment, consider a world where DWN adoption is near ubiquiitous. Alice is planning a trip and wants to use a DWA to manage her travel plans. She downloads a DWA-aware travel app and logs in using her DID. The app then asks for permission to access her DWN to store her travel preferences and reservations.
As Alice uses the app to book flights, hotels, and activities, the app stores her reservations in her DWN. When she arrives at the airport, the app uses her DWN to retrieve her ticket and boarding pass. When she arrives at the hotel, the app uses her DWN to retrieve her reservation and check-in details.
And so on…
Normally, Alice’s preferences, tickets, reservations, and other travel data would be strewn across a range of hotel, airline, and travel apps. Apps built using DIDs + DWNs can unify experiences like this by selectively sharing Alice’s preferences and storing her reservations with her DWN.
AIdentity
Trust on the Internet has been steadily eroding and generative AI is now pouring petrol on the fire. It gives attackers tools to quickly create deep fakes so realistic they can even fool CFOs and cybersecurity professionals.
AI-based apps are starting to manifest as living agents - autonomous software entities that perceive their environment, make decisions, and take actions to achieve specific goals. These agents will become increasingly prevalent, offering both helpful assistance and, unfortunately, new avenues for criminal or abusive activities.
To navigate this landscape safely, both individuals and service providers can leverage the building blocks outlined above in conjunction with smart contracts and account abstraction. By using DIDs and VCs to confer trust to AI agents, we create a framework for secure and accountable AI interactions.
This approach offers several benefits:
- Verifiable interactions: Each agent’s actions can be tied to a specific, trusted identity.
- Granular permissions: Smart contracts can define precise rules for what an AI agent can and cannot do.
- Auditability: All interactions can be recorded using DWN-based tamper-resistant records.
Contextual Awareness
AI agents can also benefit from the decentralized web’s ability to store and share data in a contextual manner. By leveraging DWNs to store and retrieve data, agents have the means to persist information across steps when undertaking complex, multi-step workflows.
Additionally, agents have standards-based access to users’ personal information in order to make informed decisions without compromising privacy.
Reliable Data
The need for reliable, permissioned AI data sources is a contentious issue, with ongoing debates about data origin, rights, and usage.
The application of DWNs and DIDs at the data source level offers a solution:
- Provenance: DIDs can prove the origin of data, ensuring its authenticity.
- Rights management: Smart contracts associated with DIDs can encode usage rights and permissions.
- Verifiable value: The worth and reliability of data can be more accurately assessed when its source is known.
- Compliance: This approach helps in adhering to data protection regulations by providing clear trails of data usage and consent.
- Incentivization: A DID-based system could allow for fair compensation to data providers, encouraging the creation of high-quality datasets.
The building blocks of decentralized identity can be used to create a more transparent, ethical, and efficient ecosystem for AI data. This not only resolves many of the current data rights issues but also lays the groundwork for more advanced and trustworthy AI systems in the future.
DWN
TODO: This page is a work in progress.
Grant access
TODO: This page is a work in progress.
This example demonstrates how to create a grant for and use it to query for messages.
Grants are used to grant a user permission to access a specific method on a DWN.
Alice grants Bob permission to query messages (MessagesQuery
). The grant is persisted to Alice’s
DWN so all Bob needs to do is sign his message and specify the record_id
of the grant in order
to make use of it.
use credibil_dwn::client::grants::{GrantBuilder, Scope}; use credibil_dwn::{Method, StatusCode, endpoint}; use test_node::keystore::{self, Keyring}; use test_node::provider::ProviderImpl; static ALICE: LazyLock<Keyring> = LazyLock::new(|| keystore::new_keyring()); static BOB: LazyLock<Keyring> = LazyLock::new(|| keystore::new_keyring()); fn main() { let provider = ProviderImpl::new().await.expect("should create provider"); // Alice creates a grant scoped to `MessagesQuery` for Bob. let bob_grant = GrantBuilder::new() .granted_to(&BOB.did) .scope(Scope::Messages { method: Method::Query, protocol: None, }) .sign(&*ALICE) .build() .await .expect("should create grant"); // Alice persists the grant on her DWN. let reply = endpoint::handle(&ALICE.did, bob_grant.clone(), &provider).await.expect("should write"); assert_eq!(reply.status.code, StatusCode::ACCEPTED); // Bob uses the grant to query for the messages. let query = QueryBuilder::new() .permission_grant_id(&bob_grant.record_id) .sign(&*BOB) .build() .await .expect("should create write"); let reply = endpoint::handle(&ALICE.did, query, &provider).await.expect("should write"); assert_eq!(reply.status.code, StatusCode::OK); }
Configure a protocol
TODO: This page is a work in progress.
This example demonstrates how to install a protocol and write messages to it.
Alice installs a protocol that allows anyone using it to take any action on her DWN.
use std::io::Cursor; use credibil_dwn::client::protocols::{ConfigureBuilder, Definition}; use credibil_dwn::client::records::{Data, ProtocolBuilder, WriteBuilder}; use credibil_dwn::{Method, StatusCode, endpoint}; use test_node::keystore::{self, Keyring}; use test_node::provider::ProviderImpl; static ALICE: LazyLock<Keyring> = LazyLock::new(|| keystore::new_keyring()); static BOB: LazyLock<Keyring> = LazyLock::new(|| keystore::new_keyring()); fn main() { let provider = ProviderImpl::new().await.expect("should create provider"); // Alice configures a protocol to allow anyone to do anything on her DWN. let ffa = include_bytes!("protocols/free-for-all.json"); let definition = serde_json::from_slice(ffa).expect("should deserialize"); let configure = ConfigureBuilder::new() .definition(definition.clone()) .sign(&*ALICE) .build() .await .expect("should build"); // Alice installs the protocol on her DWN. let reply = endpoint::handle(&ALICE.did, configure, &provider) .await .expect("should configure protocol"); assert_eq!(reply.status.code, StatusCode::ACCEPTED); // Alice writes a message to the Records `free-for-all` interface. let data = br#"{"message": "test record write"}"#; let reader = Cursor::new(data.to_vec()); let write_any = WriteBuilder::new() .protocol(ProtocolBuilder { protocol: &definition.protocol, protocol_path: "post", parent_context_id: None, }) .schema(definition.types["post"].schema) .data(Data::Stream(reader)) .sign(&*ALICE) .build() .await .expect("should create write"); let reply = endpoint::handle(&ALICE.did, write_any.clone(), &provider).await.expect("should write"); assert_eq!(reply.status.code, StatusCode::ACCEPTED); }
Writing a record
TODO: This page is a work in progress.
Encrypting data
TODO: This page is a work in progress.
Issuing a credential
TODO: This page is a work in progress.
OpenID
TODO: This page is a work in progress.
Issuing a credential
TODO: This page is a work in progress.
Verifying a credential
TODO: This page is a work in progress.
DID
TODO: This page is a work in progress.
Creating a DID
TODO: This page is a work in progress.
Resolving a DID
TODO: This page is a work in progress.
Stability
Credibil’s stability is a reflection of the process of change in supported features, platforms, as well as internal structure. This section should provide some insight as to expectations of the maintainers regarding change in Credibil.
For Credibil, the stability of the API is primarily affected by:
-
The process of change — introducing or deprecating features and platforms
-
The number of supported features and platforms — the greater the number, the higher the risk a change will impact some users.
In the following sections , we outline our management of this change — from instigation through to release.
Platform Support
This page is intended to give a high-level overview of Credibil’s platform support along with a few Credibil aspirations. For more detail, see the support tiers which has more detail on what is supported and to what extent.
Credibil strives to support hardware that anyone wants to run WebAssembly on. Credibil maintainers support a number of “major” platforms but porting work may be required to support platforms that maintainers are not familiar with.
Out-of-the box Credibil supports:
- Linux: x86_64, aarch64
- MacOS: x86_64, aarch64
- Windows: x86_64
Other platforms such as Android, iOS, and the BSD family of OSes are not yet supported. PRs for porting are welcome and maintainers are happy to add more entries to the CI matrix for these platforms.
TODO: complete this section
Support for #![no_std]
The credibil-vc
crate supports being build on no_std platforms in Rust, but
only for a subset of its compile-time Cargo features. Currently supported features
are:
runtime
gc
component-model
Note that Credibil does not have a default
feature which means that when depending on
Credibil you’ll need to set features explicitly.
Credibil’s support for no_std requires the embedder to implement the equivalent of a C
header file to indicate how to perform basic OS operations such as allocating virtual
memory. This API can be found as credibil-vc-platform.h
in Credibil’s release artifacts or at
examples/min-platform/embedding/credibil-vc-platform.h
in the source tree. Note that this
API is not guaranteed to be stable at this time, it’ll need to be updated when Credibil
is updated.
Credibil’s runtime will use the symbols defined in this file meaning that if they’re not defined then a link-time error will be generated. Embedders are required to implement these functions in accordance with their documentation to enable Credibil to run on custom platforms.
Support Tiers
Credibil recognises three distinct tiers of platform and feature support. Each tier identifies the level of support that should be provided for a given platform or feature.
The description of these tiers are inspired by the Rust compiler’s support tiers for targets with some additional customization for feature support.
This section provides a framework for the evaluation of new features as well as support requires for existing features.
Keep in mind, this is merely a guide and should not be used to “lawyer” a change into Credibil on some technical detail.
Supported Platforms and Features
Tier 1 is classified as the highest level of support, confidence, and correctness for a component. Each tier encompasses all the guarantees of previous tiers.
Features classified under one tier may already meet the criteria for a higher tier. In such situations, it’s not intended to use these guidelines to justify removal of a feature.
Guidance is provided for phasing out unmaintained features but it should be clear under what circumstances work “can be avoided” for each tier.
Tier 1 - Production Ready
Category | Description |
---|---|
Target | aarch64-apple-darwin |
Target 1 | x86_64-unknown-linux-musl |
Target | wasm32-wasi |
Example feature | example feature |
Binary artifacts for MUSL can be statically linked, meaning that they are suitable for “run on any linux distribution” style use cases.
Tier 1 is intended to be the highest level of support in Credibil for included features, indicating that they are suitable for production environments. This conveys a high level of confidence within the Credibil project about the included features.
Tier 1 features include:
-
Continuous fuzzing is required for all features. This means that any existing fuzz targets must be running and exercising the new code paths. Where possible differential fuzzing should also be implemented to compare results with other implementations.
-
Continuous fuzzing is required for the architecture of supported targets.
-
CVEs and security releases will be performed as necessary for any bugs found in features and targets.
-
Major changes affecting this tier may require help from maintainers with specialized expertise, but otherwise it should be reasonable to expect most Credibil developers to be able to maintain Tier 1 features.
-
Major changes affecting Tier 1 features require an RFC and prior agreement on the change before an implementation is committed.
A major inclusion point for this tier is intended to be the continuous fuzzing requirement. This implies a significant commitment of resources for fixing issues, resources to execute, etc. Additionally this tier comes with the broadest expectation of “burden on everyone else” in terms of what changes everyone is generally expected to handle.
Features classified as Tier 1 are rarely, if ever, turned off or removed.
Tier 2 - Almost Production Ready
Category | Description | Missing Tier 1 Requirements |
---|---|---|
Target | aarch64-unknown-linux-musl | Continuous fuzzing |
Target | x86_64-apple-darwin | Continuous fuzzing |
Target | x86_64-pc-windows-msvc | Continuous fuzzing |
Target | x86_64-pc-windows-gnu | Clear owner of the target |
Target | Support for #![no_std] | Support beyond CI checks |
Tier 2 encompasses features and components which are well-maintained, tested well, but don’t necessarily meet the stringent criteria for Tier 1. Features in this category may already be “production ready” and safe to use.
Tier 2 features include:
-
Tests are run in CI for the Credibil project for this feature and everything passes. For example a Tier 2 platform runs in CI directly or via emulation. Features are otherwise fully tested on CI.
-
Complete implementations for anything that’s part of Tier 1. For example all Tier 2 targets must implement all of the Tier 1 WebAssembly proposals, and all Tier 2 features must be implemented on all Tier 1 targets.
-
Any Credibil developer could be expected to handle minor changes which affect Tier 2 features. For example, if an interface changes, the developer changing the interface should be able to handle the changes for Tier 2 architectures as long as the affected part is relatively minor.
-
For more significant changes, maintainers of a Tier 2 feature should be responsive (reply to requests within a week) and are available to accommodate architectural changes that affect their component. For example more expansive work beyond the previous point where contributors can’t easily handle changes are expected to be guided or otherwise implemented by Tier 2 maintainers.
-
Major changes otherwise requiring an RFC that affect Tier 2 components are required to consult Tier 2 maintainers in the course of the RFC. Major changes to Tier 2 components themselves do not require an RFC, however.
Tier 2 features are generally not turned off or disabled for long. Maintainers are required to be responsive to changes and will be notified of any unrelated change which affects their component. It’s recommended that if a component breaks for any reason due to an unrelated change that the maintainer either contributes to the PR-in-progress or otherwise has a schedule for the implementation of the feature.
Tier 3 - Not Production Ready
Category | Description | Missing Tier 2 Requirements |
---|---|---|
Target | aarch64-pc-windows-msvc | CI testing, unwinding, full-time maintainer |
Target | riscv64gc-unknown-linux-gnu | full-time maintainer |
In general, Tier 3 is the baseline for inclusion of code into the Credibil project. However, this does not mean it is the catch-all “if a patch is sent it will be merged” tier. Instead, the goal of this tier is to outline what is expected of contributors adding new features to Credibil which might be experimental at the time of addition.
Tier 3 not a tier where restrictions are releaxed, rather it already implies a significant commitment of effort to a feature being included within Credibil.
Tier 3 features include:
-
Inclusion of a feature does not impose unnecessary maintenance overhead on other components/features. Some examples of additions which would not be accepted are:
- An experimental feature that doubles the CI time for all PRs.
- A change which makes it significantly more difficult to make architectural changes to Credibil’s internal implementation.
- A change which makes building Credibil more difficult.
In general Tier 3 features are off-by-default at compile time but still tested-by-default on CI.
-
New features of Credibil cannot have major known bugs at the time of inclusion. Landing a feature requires the feature to be correct and bug-free as best can be evaluated at the time of inclusion. Inevitably, bugs will be found and that’s ok, but anything identified during review must be addressed.
-
Code included into the project must be of an acceptable level of quality relative to the rest of the codebase.
-
There must be a path to a feature being finished at the time of inclusion. Adding a new backend, for example, is a significant undertaking which may not be able to be done in a single PR. Partial implementations are acceptable as long as there’s a clear path for delivering the completed feature.
-
New components must have a clearly identified owner who is willing to be “on the hook” for review, updates to any internals, etc. For example, a new backend would need to have a maintainer who is willing to respond to changes in interfaces and the needs of Credibil.
Notably, this baseline level of support does not require any degree of testing, fuzzing, or verification. As a result, components classified as Tier 3 are generally not production-ready as they have not yet been ‘battle-tested’.
Tier 3 features may be disabled in CI or even removed from the repository. If a Tier 3 feature is preventing development of other features then:
- The owner will be notified.
- If no response is received within one week, the feature will be disabled in CI.
- If no response is received within one month, the feature may be removed from the repository.
Unsupported features and platforms
While this is not an exhaustive list, Credibil does not currently support the following features. While this documents Credibil’s current state, it does not mean Credibil does not want to ever support these features; rather design discussion and PRs are welcome for many of the below features to figure out how best to implement them and at least move them to Tier 3 above.
- Target: ARM 32-bit
- Target: FreeBSD
- Target: NetBSD/OpenBSD
- Target: i686 (32-bit Intel targets)
- Target: Android
- Target: MIPS
- Target: SPARC
- Target: PowerPC
- Target: RISC-V 32-bit
Release Process
Credibil is in dynamic development so the content of this page is subject to change, but we document our release process goals here for now.
This section serves as a high-level summary of Credibil’s process. A more detailed description of the process can be found in the contributing section.
Key takeways:
- A new version of Credibil will be made available once a month.
- KeyOps bugs and correctness fixes will be backported to the latest two releases of Credibil and issued as patch releases.
Once a month Credibil will issue a new version. This will be issued with a semver-major version update, such as 0.1.0 to 0.2.0.
A release is scheduled when an automated PR is sent to bump the version on the 5th of every month with the release effected when the PR is merged. The PR typically gets merged within a few days.
Breaking Changes
Each major release of Credibil reserves the right to break both behavior and API backwards-compatibility. This is not expected to happen frequently, however, and any breaking change will follow these criteria:
-
Minor breaking changes, either behavior or with APIs, will be documented in the
CHANGELOG.md
release notes. Minor changes will require some degree of consensus but are not required to go through the entire RFC process. -
Major breaking changes, such as major refactorings to the API, will be required to go through the RFC process. These changes are intended to be broadly communicated to those interested and provides an opportunity to give feedback about embeddings. Release notes will clearly indicate if any major breaking changes through accepted RFCs are included in a release.
Patching
Patch releases of Credibil will only be issued for security and critical correctness issues for on-by-default behavior in the previous releases. If Credibil is currently at version 0.2.0 then 0.2.1 and 0.1.1 will be issued as patch releases if a bug is found. Patch releases are guaranteed to maintain API and behavior backwards-compatibility and are intended to be trivial for users to upgrade to.
What’s released?
Currently, Credibil’s release process encompasses a single top-level credibil-vc
Rust
crate.
Other projects maintained by the Credibil maintainers will also likely be released, with the same version numbers, with the main Credibil project soon after a release is made.
KeyOps
TODO: include summary content here…
Disclosure Policy
This section addresses the security disclosure policy for Credibil projects.
The security report is received and is assigned a primary handler. This person will coordinate the fix and release process. The problem is confirmed and a list of all affected versions is determined. Code is audited to find any potential similar problems. Fixes are prepared for all releases which are still under maintenance. These fixes are not committed to the public repository but rather held locally pending the announcement.
A suggested embargo date for this vulnerability is chosen and a CVE (Common Vulnerabilities and Exposures) is requested for the vulnerability.
A pre-notification may be published on the security announcements mailing list, providing information about affected projects, severity, and the embargo date.
On the embargo date, the Credibil security mailing list is sent a copy of the announcement. The changes are pushed to the public repository and new builds are deployed.
Typically the embargo date will be set 72 hours from the time the CVE is issued. However, this may vary depending on the severity of the bug or difficulty in applying a fix.
This process can take some time, especially when coordination is required with maintainers of other projects. Every effort will be made to handle the bug in as timely a manner as possible; however, it’s important that we follow the release process above to ensure that the disclosure is handled in a consistent manner.
Project maintainers are encouraged to write a post-mortem for the Credibil blog, detailing the vulnerability and steps being taken to identify and prevent similar vulnerabilities in the future.
Publishing KeyOps Updates
KeyOps notifications will be distributed via the following methods.
- Zulip: https://credibil.zulipchat.com/#channels/440231/security-updates/
- Email: TODO: add mailing list information…
Security Bugs
If you are unsure whether an issue is a security vulnerability, always err on the side of caution and report it as a security vulnerability!
Bugs must affect a tier 1 platform or feature to be considered a security vulnerability.
KeyOps of the core libraries is paramount. Anything that undermines their ability to function correctly and securely is a security vulnerability.
On the other hand, execution that diverges from OpenID semantics (such as naming, lack of support for a particular RFC, etc.) are not considered security vulnerabilities so long as they do not guarantees implied by the existing implementation.
Denials of service when executing are considered security vulnerabilities. For example, an endpoint that goes into an infinite loop that never yields is considered a security vulnerability.
Any kind of memory unsafety (e.g. use-after-free bugs, out-of-bounds memory accesses, etc…) is always a security vulnerability.
Cheat Sheet: Is it a security vulnerability?
Type of bug | |
---|---|
| Yes |
| Yes |
| Yes |
| Yes |
| Yes |
| Yes |
| Yes |
| Yes |
| Yes |
| Yes |
| Yes |
| Yes |
| Yes |
| Yes |
| No |
| No |
| No |
N.B. We still want to fix every bug mentioned above even if it is not a security vulnerability! We appreciate when issues are filed for non-vulnerability bugs, particularly when they come with test cases and steps to reproduce!
Contributing
We’re excited to work on Credibil and hope you will be too! This guide should help you get up and running with Credibil development. But first, make sure you’ve read our Code of Conduct.
Credibil is ambitious, with much work to be done to achieve full compliance with the core OpenID specifications. And while we’re confident we can achieve our goals, we see many opportunities for others to get involved and help get there more quickly.
Join Our Chat
We chat about Credibil development on Zulip — join us!.
If you’re having trouble building Credibil, aren’t sure why a test is failing, or have any other questions, feel free to ask on Zulip. Not everything we hope to do with these projects is reflected in the code or documentation yet, so if you see things that seem missing or that don’t make sense, or even that just don’t work the way you expect them to, we’re also interested to hear about that!
As always, you’re more than welcome to open an issue too!
Finally, we have bi-weekly project meetings, hosted on Zoom, for Credibil. For more information, see our meetings agendas/minutes repository. Please feel free to contact us via Zulip if you’re interested in joining!
Finding Something to Hack On
If you’re looking for something to do, these are great places to start:
Issues labeled “good first issue” — these issues tend to be simple, what needs to be done is well known, and are good for new contributors to tackle. The goal is to learn Credibil’s development workflow and make sure that you can build and test Credibil.
Issues labeled “help wanted” — these are issues that we need a little help with!
If you’re unsure if an issue is a good fit for you or not, feel free to ask in a comment on the issue, or in chat.
Mentoring
We’re happy to mentor people, whether you’re learning Rust, learning about Verifiable Credentials, Credential wallets or anything else that piques your interest.
We categorize issues in the issue tracker using a tag scheme inspired by Rust’s issue tags. For example, the E-easy marks good beginner issues, and E-rust marks issues which likely require some familiarity with Rust, though not necessarily Verifiable Credentials.
Also, we encourage people to just look around and find things they’re interested in. This a good time to get involved, as there aren’t a lot of things set in stone yet.
Building
This section describes everything required to build and run Credibil.
Prerequisites
Before we can actually build Credibil, we’ll need to make sure these things are installed first.
The Rust Toolchain
Install the Rust toolchain here. This
includes rustup
, cargo
, rustc
, etc…
Building credibil-vc
Library
To make an unoptimized, debug build of the credibil-vc
crate, go to the root
of the repository and run this command:
cargo build
The built executable will be located at target/debug/libcredibil_vc.rlib
.
To make an optimized build, run this command in the root of the repository:
cargo build --release
The built executable will be located at target/release/libcredibil_vc.rlib
.
Building Example Crates
You can build any of the example crates by appending -p whatever
to
the cargo build
invocation. For example, to build the example issuer
crate,
execute this command:
cargo build -p issuer
Alternatively, you can cd
into the crate’s directory, and run cargo build
there, without needing to supply the -p
flag:
cd examples/issuer
cargo build
Testing
This section describes how to run Credibil’s tests and add new tests.
Before continuing, make sure you can build Credibil successfully. Can’t run the tests if you can’t build it!
Installing wasm32
Targets
To compile the tests, you’ll need the wasm32-wasip1
and
wasm32-unknown-unknown
targets installed, which, assuming you’re using
rustup.rs to manage your Rust versions, can be done as
follows:
rustup target add wasm32-wasip1
Running All Tests
To run all of Credibil’s tests, execute this command:
cargo test --workspace
You can also exclude a particular crate from testing with --exclude
. For
example, if you want to avoid testing the example verifier
crate:
cargo test --workspace --exclude verifier
Testing a Specific Crate
You can test a particular Credibil crate with cargo test -p whatever
. For example, to test the example issuer
crate, execute
this command:
cargo test -p issuer
Alternatively, you can cd
into the crate’s directory, and run cargo test
there, without needing to supply the -p
flag:
cd examples/issuer
cargo test
Adding New Tests
Adding Rust’s #[test]
-Style Tests
For very “unit-y” tests, we add test
modules in the same .rs
file as the
code that is being tested. These test
modules are configured to only get
compiled during testing with #[cfg(test)]
.
#![allow(unused)] fn main() { // some code... #[cfg(test)] mod tests { use super::*; #[test] fn some_test_for_that_code() { // ... } } }
If you’re writing a unit test and a test
module doesn’t already exist, you can
create one.
For more “integration-y” tests, we create a tests
directory within the crate,
and put the tests inside there. For example, there are various end-to-end flow tests at the root tests
directory. Always feel free to
add a tests
directory to a crate, if you want to add a new test and there
aren’t any existing tests.
Coding guidelines
For the most part, Credibil follows common Rust conventions and pull request (PR) workflows, though we do have a few additional things to be aware of.
rustfmt
All PRs must be formatted according to rustfmt, and this is checked in the continuous integration tests. You can format code locally with:
$ cargo fmt
at the root of the repository. You can find more information about rustfmt online too, such as how to configure your editor.
Minimum Supported rustc
Version
Credibil supports the latest three stable releases of Rust. This means that if the latest version of Rust is 1.85.0 then Credibil supports Rust 1.83.0, 1.84.0, and 1.85.0. CI will test by default with 1.78.0 and there will be one job running the full test suite on Linux x86_64 on 1.76.0.
Some of the CI jobs depend on nightly Rust, for example to run rustdoc with nightly features, however these use pinned versions in CI that are updated periodically and the general repository does not depend on nightly features.
Updating Credibil’s MSRV is done by editing the rust-version
field in the
workspace root’s Cargo.toml
Dependencies of Credibil
Credibil have a higher threshold than default for adding
dependencies to the project. All dependencies are required to be “vetted”
through the cargo vet
tool. This is
checked on CI and will run on all modifications to Cargo.lock
.
A “vet” for Credibil is not a meticulous code review of a dependency for correctness but rather it is a statement that the crate does not contain malicious code and is safe for us to run during development and (optionally) users to run when they run Credibil themselves. Credibil’s vet entries are used by other organizations which means that this isn’t simply for our own personal use. Credibil additionally uses vet entries from other organizations as well which means we don’t have to vet everything ourselves.
New vet entries are required to be made by trusted contributors to Credibil.
This is all configured in the supply-chain
folder of Credibil. These files
generally aren’t hand-edited though and are instead managed through the cargo vet
tool itself. Note that our supply-chain/audits.toml
additionally contains
entries which indicates that authors are trusted as opposed to vets of
individual crates. This lowers the burden of updating version of a crate from a
trusted author.
When put together this means that contributions to Credibil which update existing dependencies or add new dependencies will not be mergeable by default (CI will fail). This is expected from our project’s configuration and this situation will be handled one of a few ways:
Note that this process is not in place to prevent new dependencies or prevent
updates, but rather it ensures that development of Credibil is done with a
trusted set of code that has been reviewed by trusted parties. We welcome
dependency updates and new functionality, so please don’t be too alarmed when
contributing and seeing a failure of cargo vet
on CI!
cargo vet
for Contributors
If you’re a contributor to Credibil and you’ve landed on this documentation, hello and thanks for your contribution! Here’s some guidelines for changing the set of dependencies in Credibil:
-
If a new dependency is being added it might be worth trying to slim down what’s required or avoiding the dependency altogether. Avoiding new dependencies is best when reasonable, but it is not always reasonable to do so. This is left to the judgement of the author and reviewer.
-
When updating dependencies this should be done for a specific purpose relevant to the PR-at-hand. For example if the PR implements a new feature then the dependency update should be required for the new feature. Otherwise it’s best to leave dependency updates to their own PRs. It’s ok to update dependencies “just for the update” but we prefer to have that as separate PRs.
Dependency additions or updates require action on behalf of project maintainers
so we ask that you don’t run cargo vet
yourself or update the supply-chain
folder yourself. Instead a maintainer will review your PR and perform the cargo vet
entries themselves. Reviewers will typically make a separate pull request
to add cargo vet
entries and once that lands yours will be added to the queue.
cargo vet
for Maintainers
Maintainers of Credibil are required to explicitly vet and approve all
dependency updates and modifications to Credibil. This means that when reviewing
a PR you should ensure that contributors are not modifying the supply-chain
directory themselves outside of commits authored by other maintainers. Otherwise
though to add vet entries this is done through one of a few methods:
-
For a PR where maintainers themselves are modifying dependencies the
cargo vet
entries can be included inline with the PR itself by the author. The reviewer knows that the author of the PR is themself a maintainer. -
PRs that “just update dependencies” are ok to have at any time. You can do this in preparation for a future feature or for a future contributor. This more-or-less is the same as the previous categories.
-
For contributors who should not add vet entries themselves maintainers should review the PR and add vet entries either in a separate PR or as part of the contributor’s PR itself. As a separate PR you’ll check out the branch, run
cargo vet
, then rebase away the contributor’s commits and push yourcargo vet
commit alone to merge. For pushing directly to the contributor’s own PR be sure to read the notes below.
Note for the last case it’s important to ensure that if you push directly to a contributor’s PR any future updates pushed by the contributor either contain or don’t overwrite your vet entries. Also verify that if the PR branch is rebased or force-pushed, the details of your previously pushed vetting remain the same: e.g., versions were not bumped and descriptive reasons remain the same. If pushing a vetting commit to a contributor’s PR and also asking for more changes, request that the contributor make the requested fixes in an additional commit rather than force-pushing a rewritten history, so your existing vetting commit remains untouched. These guidelines make it easier to verify no tampering has occurred.
Policy for adding cargo vet
entries
For maintainers this is intended to document the project’s policy on adding
cargo vet
entries. The goal of this policy is to not make dependency updates
so onerous that they never happen while still achieving much of the intended
benefit of cargo vet
in protection against supply-chain style attacks.
-
For dependencies that receive at least 10,000 downloads a day on crates.io it’s ok to add an entry to
exemptions
insupply-chain/config.toml
. This does not require careful review or review at all of these dependencies. The assumption here is that a supply chain attack against a popular crate is statistically likely to be discovered relatively quickly. Changes tomain
in Credibil take at least 2 weeks to be released due to our release process, so the assumption is that popular crates that are victim of a supply chain attack would be discovered during this time. This policy additionally greatly helps when updating dependencies on popular crates that are common to see without increasing the burden too much on maintainers. -
For other dependencies a manual vet is required. The
cargo vet
tool will assist in adding a vet by pointing you towards the source code, as published on crates.io, to be browsed online. Manual review should be done to ensure that “nothing nefarious” is happening. For exampleunsafe
should be inspected as well as use of ambient system capabilities such asstd::fs
,std::net
, orstd::process
, and build scripts. Note that you’re not reviewing for correctness, instead only for whether a supply-chain attack appears to be present.
This policy intends to strike a rough balance between usability and security.
It’s always recommended to add vet entries where possible, but the first bullet
above can be used to update an exemptions
entry or add a new entry. Note that
when the “popular threshold” is used do not add a vet entry because the
crate is, in fact, not vetted. This is required to go through an
[[exemptions]]
entry.
Development Process
We use issues for asking questions (open one here!) and tracking bugs and unimplemented features, and pull requests (PRs) for tracking and reviewing code submissions.
Before submitting a PR
Consider opening an issue to talk about it. PRs without corresponding issues are appropriate for fairly narrow technical matters, not for fixes to user-facing bugs or for feature implementations, especially when those features might have multiple implementation strategies that usefully could be discussed.
Our issue templates might help you through the process.
When submitting PRs
-
Please answer the questions in the pull request template. They are the minimum information we need to know in order to understand your changes.
-
Write clear commit messages that start with a one-line summary of the change (and if it’s difficult to summarize in one line, consider splitting the change into multiple PRs), optionally followed by additional context. Good things to mention include which areas of the code are affected, which features are affected, and anything that reviewers might want to pay special attention to.
-
If there is code which needs explanation, prefer to put the explanation in a comment in the code, or in documentation, rather than in the commit message. Commit messages should explain why the new version is better than the old.
-
Please include new test cases that cover your changes, if you can. If you’re not sure how to do that, we’ll help you during our review process.
-
For pull requests that fix existing issues, use issue keywords. Note that not all pull requests need to have accompanying issues.
-
When updating your pull request, please make sure to re-request review if the request has been cancelled.
Focused commits or squashing
We are not picky about how your git commits are structured. When we merge your PR, we will squash all of your commits into one, so it’s okay if you add fixes in new commits.
We appreciate it if you can organize your work into separate commits which each make one focused change, because then we can more easily understand your changes during review. But we don’t require this.
Once someone has reviewed your PR, it’s easier for us if you don’t rebase it when making further changes. Instead, at that point we prefer that you make new commits on top of the already-reviewed work.
That said, sometimes we may need to ask you to rebase for various technical reasons. If you need help doing that, please ask!
Review and merge
Anyone may submit a pull request, and anyone may comment on or review others’ pull requests. However, one review from somebody in the Core Team is required before the Core Team merges it.
Even Core Team members must create PRs and get review from another Core Team member for every change, including minor work items such as version bumps, removing warnings, etc.
Maintainer Guidelines
This section describes procedures and expectations for Core Team members. It may be of interest if you just want to understand how we work, or if you are joining the Core Team yourself.
Code Review
We only merge changes submitted as GitHub Pull Requests, and only after they’ve been approved by at least one Core Team reviewer who did not author the PR. This section covers expectations for the people performing those reviews. These guidelines are in addition to expectations which apply to everyone in the community, such as following the Code of Conduct.
It is our goal to respond to every contribution in a timely fashion. Although we make no guarantees, we aim to usually provide some kind of response within about one business day.
That’s important because we appreciate all the contributions we receive, made by a diverse collection of people from all over the world. One way to show our appreciation, and our respect for the effort that goes into contributing to this project, is by not keeping contributors waiting. It’s no fun to submit a pull request and then sit around wondering if anyone is ever going to look at it.
That does not mean we will review every PR promptly, let alone merge them. Some contributions have needed weeks of discussion and changes before they were ready to merge. For some other contributions, we’ve had to conclude that we could not merge them, no matter how much we appreciate the effort that went into them.
What this does mean is that we will communicate with each contributor to set expectations around the review process. Some examples of good communication are:
-
“I intend to review this but I can’t yet. Please leave me a message if I haven’t responded by (a specific date in the near future).”
-
“I think (a specific other contributor) should review this.”
-
“I’m having difficulty reviewing this PR because of (a specific reason, if it’s something the contributor might reasonably be able to help with). Are you able to change that? If not, I’ll ask my colleagues for help (or some other concrete resolution).”
If you are able to quickly review the PR, of course, you can just do that.
You can find open Credibil pull requests for which your review has been requested with this search:
https://github.com/credibil/vc/pulls?q=is:open+type:pr+user-review-requested:@me
Auto-assigned reviewers
We automatically assign a reviewer to every newly opened pull request. We do this to avoid the problem of diffusion of responsibility, where everyone thinks somebody else will respond to the PR, so nobody does.
To be in the pool of auto-assigned reviewers, a Core Team member must commit to following the above goals and guidelines around communicating in a timely fashion.
We don’t ask everyone to make this commitment. In particular, we don’t believe it’s fair to expect quick responses from unpaid contributors, although we gratefully accept any review work they do have time to contribute.
If you are in the auto-assignment pool, remember: You are not necessarily expected to review the pull requests which are assigned to you. Your only responsibility is to ensure that contributors know what to expect from us, and to arrange that somebody reviews each PR.
TODO: Review this hidden div content
We have several different teams that reviewers may be auto-assigned from. You
should be in teams where you are likely to know who to re-assign a PR to, if you
can’t review it yourself. The teams are determined by the CODEOWNERS
file at
the root of the Credibil repository. But despite the name, membership in these
teams is not about who is an authority or “owner” in a particular area. So
rather than creating a team for each fine-grained division in the repository
such as individual target architectures or WASI extensions, we use a few
coarse-grained teams:
- core-utils-reviewers: Credibil’s core functionality
- credibil-fuzz-reviewers: Fuzz testing targets
- credibil-default-reviewers: Anything else, including CI and documentation
Ideally, auto-assigned reviewers should be attending the regular Credibil open source meetings, as appropriate for the areas they’re reviewing. This is to help these reviewers stay aware of who is working on what, to more easily hand off PRs to the most relevant reviewer for the work. However, this is only advice, not a hard requirement.
If you are not sure who to hand off a PR review to, you can look at GitHub’s
suggestions for reviewers, or look at git log
for the paths that the PR
affects. You can also just ask other Core Team members for advice.
General advice
This is a collection of general advice for people who are reviewing pull requests. Feel free to take any that you find works for you and ignore the rest. You can also open pull requests to suggest more references for this section.
The Gentle Art of Patch Review suggests a “Three-Phase Contribution Review” process:
- Is the idea behind the contribution sound?
- Is the contribution architected correctly?
- Is the contribution polished?
Phase one should be a quick check for whether the pull request should move forward at all, or needs a significantly different approach. If it needs significant changes or is not going to be accepted, there’s no point reviewing in detail until those issues are addressed.
On the other end, it’s a good idea to defer reviewing for typos or bikeshedding about variable names until phase three. If there need to be significant structural changes, entire paragraphs or functions might disappear, and then any minor errors that were in them won’t matter.
The full essay has much more advice and is recommended reading.
Releasing Updates
Overview
Overall, the Credibil’s release process favours bringing new features to market frequently, while minimizing the risk of releasing a broken version. It draws heavily on the Rust release process, which is well-documented and understood.
Core tenets
The release process starts as soon as a change is identified. That is, the decision to release is not a separate step at the end of development, but rather a natural consequence of bringing a change to market.
TLDR:
main
is the source of truth.- Development is undertaken on
feature
branches and is considered complete when requirements have been met and CI checks pass. - Completed features are merged back into
main
, gated behind a “feature flag”. - A short-lived
release
branch is created for testing and release preparation with any changes merged back intomain
. - Once ready, a release is tagged, published, and the branch deleted.
main
branch
The main
branch is the source of truth. It should always be in a releasable state, and is the basis for all development. While new development is undertaken on feature
branches, changes should be merged as soon as practicable, protected behind a temporary feature flag for the change.
Feature branches
Development is undertaken on feature
branches. Branches should be as short-lived as possible, and are deleted once the change is merged back into main
.
If possible, larger changes should be broken down into smaller, more manageable changes, and developed on separate branches.
Every feature change should be added to the manually curated CHANGELOG.md for the relevant crate.
Publishing a release
The publishing process is initiated by creating a new, short-lived, release
branch from main
. The branch should be named for the release version, e.g. release-v0.2.0
.
Create a new branch from main
and check out:
git checkout -b release-v0.2.0
The new version number can be determined by running a a semver check on the codebase to establish the whether this is a major, minor, or patch release.
cargo make semver
Comprehensive integration testing is undertaken on this branch, with any changes merged back into main
. Once the release is ready, the code is tagged, published, and the branch deleted.
Create release tag and push the branch and tag to the remote repository:
git tag v0.2.0 -m "v0.2.0 release"
# push new branch
git push --set-upstream origin release-v0.2.0
git push --tags
TODO: Add a section on how to create a release on Github.
cargo make release minor
Changelog
All notable changes should be documented in the project’s CHANGELOG.md file. Per Keep a Changelog recommendations, changes are a manually documented, high-level summary of changes in a release.
Dry run
Set the release level using one of release
, major
, minor
, patch
, alpha
, beta
, rc
For example, to release a minor version:
cargo make release minor
Publish
Release to crates.io:
cargo make publish minor
Governance
TODO: Add governance information…
Code of Conduct
Note: this Code of Conduct pertains to individuals’ behavior. Please also see the Organizational Code of Conduct.
Our Pledge
In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.
Our Standards
Examples of behavior that contributes to creating a positive environment include:
- Using welcoming and inclusive language
- Being respectful of differing viewpoints and experiences
- Gracefully accepting constructive criticism
- Focusing on what is best for the community
- Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
- The use of sexualized language or imagery and unwelcome sexual attention or advances
- Trolling, insulting/derogatory comments, and personal or political attacks
- Public or private harassment
- Publishing others’ private information, such as a physical or electronic address, without explicit permission
- Other conduct which could reasonably be considered inappropriate in a professional setting
Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
Scope
This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the Credibil CoC team at report@credibil.io. The CoC team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The CoC team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of Credibil’s leadership.
Attribution
This Code of Conduct is adapted from the Contributor Covenant, version 1.4, available at http://contributor-covenant.org/version/1/4/