Giter Site home page Giter Site logo

sphereon-opensource / ssi-sdk Goto Github PK

View Code? Open in Web Editor NEW
55.0 11.0 12.0 10.9 MB

Self Sovereign Identity SDK

License: Apache License 2.0

TypeScript 99.95% JavaScript 0.05%
ssi sdk did veramo verifiable-credentials siopv2 oidc4vp did-comm oid4vci oid4vp

ssi-sdk's Introduction


Sphereon
SSI SDK

SSI SDK with OID4VC, Presentation Exchange, MS Entra support

This mono repository, contains packages that add support for Presentation Exchange and OpenID4VC (SIOPv2, OID4VCI, OID4VP) and other functionalities to SSI-SDK and Veramo based agents.

We also have additional DID methods and BBS+, RSA key support in our SSI-SDK crypto extensions project, that are compatible with this SSI-SDK and Veramo.

The modules can be integrated in agents running on the issuer, holder and verifier sides, both directly into typescript/javascript projects using NodeJS, mobile projects using React-Native, your browser, or other programming languages using the REST APIs.

OpenID for Verifiable Credentials (OID4VC)

This is a new set of specifications by the OpenID Foundation, that enable peer to peer authentication (SIOPv2), Credential Issuance (OID4VCI) and Credential Presentation/Verification (OID4VP). The SSI-SDK modules offer higher-level and more tight integrations for these specification than our lower level libraries, like OID4VCI, SIOPv2 & OID4VP and Well-known DIDs.

These low-level libraries are typically not too opinionated and require an implementor to do some more work like providing signature/key callback functions. Contrary this SSI-SDK is more opinionated and requires you to use other modules of the SSI-SDK or Veramo to provide certain functionalities, like DID and key management. The benefit however is that it provides a fully working agent solution with a low amount of configuration and/or additional coding in your solution, and a rich ecosystem of plugins.

If you want to test out some of these plugins, we highly recommend using our Open-Source wallet and/or SIOPv2-OID4VP demo deployed at https://ssi.sphereon.com, which are using the plugins below.

Plugin Description
Presentation Exchange Allows to persist and manage v1 and v2 Presentation Definitions, as well as Verify Presentation Definitions, create Verifiable Presentations with Submission Data, select and match Credentials and DIDs all stored in the agent. Can be used in both Relying Party/Verifier contexts as holder contexts
OID4VCI Issuer storage Allows to persist and manage OpenID4VCI Metadata and options in the agent database
OID4VCI Issuer OpenID for Verifiable Credentials Issuer core logic and functions. This is the integration of the agent with the low-level OID4VCI client library
OID4VCI Issuer REST API OpenID for Verifiable Credentials Issuer REST API. Exposes both OID4VCI endpoints, as well as status/management endpoints.
OID4VCI Issuer REST client OpenID for Verifiable Credentials Issuer REST client, allowing for easy integration and communication from a webapp with the REST API of the agent.
SIOPv2 Authenticator with OID4VP support OpenID Provider for a wallet/holder context, that allows the agent to authenticate with SIOPv2 against the Relying Party and optionally use OpenID4VP to transport Verifiable Credentials. It is integrated into Key Management system, DID providers and VC modules. Supports JWT and JSON-LD VCs and has support for the JWT VC Presentation Profile
SIOPv2 Relying Party logic with OID4VP support Plugin for a Relying Party agent context, containing the core logic to create Authorization Requests, verify Authorization Responses, as well as handle/manage Presentation Definitions and verifications (OID4VP). It is integrated into the Key Management system, DID providers and VC modules. Supports JWT and JSON-LD VCs and has support for the JWT VC Presentation Profile
SIOPv2 Relying Party REST API Plugin for a Relying Party agent context, it exposes a REST API which allows to integrate into webapps/websites. Support sessions and multiple presentation definitions. You typically run this as a separate agent to your application, but it could be integrated if you want.
SIOPv2 Relying Party REST client Plugin for a Relying Party webapp, it exposes a REST client, allowing for easy integration and communication from the Webapp with the REST API of the Agent. Support creating QR codes for different Presentation Definitions as well as Session Handling.

Microsoft:registered: Entra Verified ID

The below packages add direct support for Microsoft:registered: Entra Verified ID. These plugins are using Microsoft libraries and REST APIs. Please note that you do not have to use these plugins to be able to support Microsoft:registered: Authenticator, have your agent verify Verifiable Credentials issued by Entra Verified ID, or have your agent communicate with Microsoft Entra Verified ID SIOPv2/OID4VP. The above OID4VC plugins can do these tasks without requiring a direct integration with Microsoft: registered: Entra Verified ID as they conform to the same standards. The biggest exception is issuing VCs using Microsoft:registered: Entra Verified ID. Entra Verified ID will soon have support for OID4VCI, until that time you will have to use their Request API to issue credentials from Entra Verified ID

Plugin Description
Microsoft:registered: Azure :registered: Authenticator Plugin to authenticate using the Microsoft:registered: Authentication Library (MSAL) against Microsoft:registered: Azure :registered:.
Entra Verified ID Request API Plugin to use Microsoft:registered: Entra Verified ID's Request API (REST) to issue/verify Verifiable Credentials

Well-known DIDs

Well-known DIDs allow you to bind domain names to DIDs, by making clever use of Verifiable Credentials signed by the respective DIDs, hosting the result in a well-known location, and then linking to this location from the DIDs itself using service endpoints. We have a low-level library for managing well-known DIDs. The packages in the SSI-SDK provide an integration into DIDs managed by agents using the SDK or Veramo.

Plugin Description
well-known DID issuer Supports managing well-known DIDs and configurations. Allows to store them in the agent
well-known DID verifier Verified DIDs and domains to conform to the well-known DID specification

Contacts and storage

The contact-manager plugin allows you to persist external agent systems like issuers and verifiers. It supports multiple identifiers per contact in the form of correlationIDs, which are URIs as well as assign roles like issuers, holders, verifiers. Typically on a first encounter you would provide a UI to the user asking to provide a name if the protocol cannot already prefill a name. Then the contact gets stored, so simple names can be used instead of DIDs in a UI for instance. It can also be used to manage trust when encountering a certain contact in the future.

Plugin Description
data-store TypeORM based contact store to persist and query entities (contacts, identities)
contact-manager Manage contacts and their related identities

Issuance branding and storage

The issuance-branding plugin allows you to persist branding for issuers and credentials. This allows for these entities to be styled even when there is no active connection possible to the external parties. It supports logo's, background attributes like an image and or color, text color and additional branding information per locale.

Plugin Description
data-store TypeORM based issuance branding store to persist and query branding (issuer, credential)
issuance-branding Manage issuer and credential branding

Generic SSI plugins

Next to the below plugins, we suggest to check out the excelent work done by the Veramo team on the Veramo website. All these SSI-SDK plugins are compatible with Veramo (4.X). Hence you can mix and match the plugins from the SSI-SDK with Veramo plugins. The below plugins add additional functionalities or replace functionalities in Veramo.

Plugin Description
SSI Types Generic interfaces for Verifiable Credentials (JWT and JSON-LD) and DIDs. Also supports creating a uniform representation of Credentials, no matter whether they are in JWT or JSON-LD format
SSI Core Adds generic functions used by other plugins, like signing, encoding/decoding
DID Utils & Key Utils Generic key and DID utils can be found in our SSI SDK Crypto Extensions repo
JSON LD issuer/verified Adds JSON-LD issuance and verification for Verifiable Credentials. Integrates seamlessly with Veramo's W3C VC plugin
QR code generator Create generic, SIOPv2/OID4VP, OID4VCI and WACI PEX QR codes. This package specifically targets React and React-Native

DID resolution


Note: DID resolution is not part of this SDK. We do provide a Universal DID client you can use in Veramo, simply by using the below code when setting up the Agent:

Using the Universal resolver for all DID methods:

export const agent = createAgent<IDIDManager & CredentialIssuerLD & IKeyManager & IDataStore & IDataStoreORM & IResolver>({
  plugins: [
    // Other plugins
    new DIDResolverPlugin({
      resolver: new UniResolver({ resolveURL: 'https://dev.uniresolver.io/1.0/identifiers' })
    })
  ]
})

Using the Universal resolver for specific DID methods and DID-key:

export const agent = createAgent<IDIDManager & CredentialIssuerLD & IKeyManager & IDataStore & IDataStoreORM & IResolver>({
  plugins: [
    // Other plugins
    new DIDResolverPlugin({
      resolver: new Resolver({
        ...getDidKeyResolver(),
        ...getUniResolver('lto', { resolveUrl: 'https://uniresolver.test.sphereon.io/1.0/identifiers' }),
        ...getUniResolver('factom', { resolveUrl: 'https://dev.uniresolver.io/1.0/identifiers' }),
      }),
    }),
  ]
})

Building and testing

Lerna

The SSI-SDK makes use of Lerna for managing multiple packages. Lerna is a tool that optimizes the workflow around managing multi-package repositories with git and npm / yarn.

Build

The below command builds all packages for you using lerna

yarn build

Test

The test command runs:

  • jest
  • coverage

You can also run only a single section of these tests, using for example yarn test:watch.

yarn test

Utility scripts

There are other utility scripts that help with development.

  • yarn prettier - runs prettier to fix code style.

Publish

There are scripts that can publish the following versions:

  • latest
  • next
  • unstable
yarn publish:[version]

ssi-sdk's People

Contributors

brummos avatar btencatesphereon avatar cre8 avatar dependabot[bot] avatar github-actions[bot] avatar hrehman-sphereon avatar jcmelati avatar ksadjad avatar lukasjhan avatar maikel-maas avatar mehmetbicak avatar nklomp avatar sanderpostma avatar sksadjad avatar sphereon-ci avatar timoglastra avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

ssi-sdk's Issues

Enhancement Request: Configurable CredentialOfferState in createVciIssuerBuilder

I was examining the createVciIssuerBuilder function in the functions.ts file at oid4vci-issuer package and noticed that it currently does not allow for a configurable CredentialOfferState. The function uses withInMemoryCredentialOfferState(), withInMemoryCNonceState(), and withInMemoryCredentialOfferURIState() which seems to set the states to an in-memory state.

builder.withInMemoryCredentialOfferState() .withInMemoryCNonceState() .withInMemoryCredentialOfferURIState()

However, in the OID4VCI repository, I noticed that there is a capability to support a custom implementation of the state manager.

Is the current implementation in createVciIssuerBuilder deliberate or is there a plan to allow for a configurable CredentialOfferState in the future?

Allowing a configurable CredentialOfferState would provide more flexibility for different use cases.

Looking forward to your response.

When verifing from the mobile wallet, when the https://dev.uniresolver.io/ is down, the verification fails with gateway timeout

From what I could see, the problem is that the OP has the code to receive a resolver as param and a failover to go to the uniresolver, but the OpSession never sends the resolver, using the requestOpts. The OpSession can be initialized to contain the IOPOptions, which contains the ResolveOpts, which contains the resolver, but when the getAuthorizationRequest() method is executed, resolver is not sent as a param here:

this.verifiedAuthorizationRequest = await op.verifyAuthorizationRequest(this.requestJwtOrUri),

but at the OP.ts side, there is an optional param, which is taken into consideration, which can contain a resolver:
requestOpts?: { correlationId?: string; verification?: InternalVerification | ExternalVerification.

The mobile app also does not set the resolver, but it could to the session.

The result is a strong dependency to the https://dev.uniresolver.io and when it is down (and it is often), then the mobile wallet cannot respond to verifiers.

Dynamic update of presentation definitions

When I started my agent, I want to let the user update the presentation definition. It's possible to write this into the pex-store, but the SIOPv2PR instance is loading the values on start.

I tried to update them like this:

const siop = new SIOPv2RP({
    defaultOpts: {
      didOpts: {
        checkLinkedDomains: CheckLinkedDomain.IF_PRESENT,
        identifierOpts: {
          identifier,
          kid,
        },
      },
    },
    instanceOpts: [],
  });  
  setInterval(() => {
    const rep = agentConfig.datasource.getRepository(KeyValueStoreEntity);
    //TODO: this is not the preferred way since we need to reload the application to get the latest version.
    rep.find({ where: { key: Like('%oid4vp%') } }).then(
      (res) => {
        const instanceOpts = res.map((r) => {
          const content = JSON.parse(r.data).value as IPresentationDefinition;
          return {
            definitionId: content.id,
            definition: content,
          } as IPEXInstanceOptions;
        });
        //@ts-ignore
        siop.opts.instanceOpts = instanceOpts;
      },
      () => {}
    );
  }, 1500);

This works, until the first presentation request for this definition is made. Then the instance is getting saved in the internal map and will always interact with it. So updating the instanceOpts has no effect on this.

Is there another way to update the values without restarting an instance? A restart of the agent could reset an ongoing presentation flow so I want avoid this step.

JWT is created with incorrect alg when making Authentication Response JWT

Trying to use agent.sendSiopAuthenticationResponse() the authentication fails on the RP side due to trying incorrect public key format (trying ES256K when it is supposed to be EdDSA)

Error:
Error: no_suitable_keys: DID document for did:key:z6MkwVRpJ1AHXrb3z1Ao59a87MB6NqvUiseQ9XnVDf7RFE3K does not have public keys for ES256K

The issue seems to be down to the following line

https://github.com/Sphereon-Opensource/ssi-sdk/blob/b28b1f9db47b2c1669858b6cb5b127a8df4a3d31/packages/did-auth-siop-op-authenticator/src/session/OpSession.ts#L239

identifier.controllerKeyId

where did-siop-auth package expects a did-type formatted key:

https://github.com/Sphereon-Opensource/did-auth-siop/blob/a5ca3e90e4a1d978167bdb6b53b236b9cd0fd12f/src/main/functions/DidJWT.ts#L149

isEd25519DidKeyMethod(payload.kid)

However, Veramo provides a public key hex reference instead of a did-url

I'm not sure if this is an issue with this package or Veramo, I'm still struggling a bit to understand all of the keys to be honest...

Including dynamic token for endpoint protection

The authentication option with a static token is good for demo purposes or for initialisation a veramo agent on demand for each call. But in my case I am using auth0 to add an access token that is valid for some minutes, longer than the user is interacting with the agent.

So extending the type like:

export interface IRestClientAuthenticationOpts {
    enabled?: boolean;
    staticBearerToken?: string;
    dynamicBearerToken?: () => Promise<string>;
}

And then update the OID4VCIRestClient like:

  private async createHeaders(existing?: Record<string, any>): Promise<HeadersInit> {
    const headers: HeadersInit = {
      ...existing,
      Accept: 'application/json',
    }
    if (this.authOpts?.enabled === true) {
      if (!this.authOpts.staticBearerToken || !this.authOpts.dynamicBearerToken) {
        throw Error(`Cannot have authentication enabled, whilst not enabling static bearer or a dynamic tokens at this point`)
      }
      if(this.authOpts.staticBearerToken && this.authOpts.dynamicBearerToken !== undefined) {
        throw Error(`Please either pass a static token or a dynamic token, not both.`);
      }
      if(this.authOpts.dynamicBearerToken !== undefined) {
        headers.Authorization = `Bearer ${await this.authOpts.dynamicBearerToken}`
      } else {
        headers.Authorization = `Bearer ${this.authOpts.staticBearerToken}`
      }
    }
    return headers
  }

To get more flexibility I think making createHeaders has to be async and the two calls in the class have to be updated

Should solve the problem. What do you think about the proposal?

Intermittent issue when verifying Authentication Request

I'm getting an intermittent issue when trying to generate the AuthenticationRespons token using the did-auth-siop-op-authenticator package and await agent.verifySiopAuthenticationRequestURI() method

There is a 502 bad gateway thrown which makes little sense to me given that it should need to make any external request.

After some testing, I noticed that it worked if I let some time pass which makes it seem to be down to some async requests not waiting for the response.

Looking into the codebase I wonder if it might be down to the following line:
https://github.com/Sphereon-Opensource/ssi-sdk/blob/386efc71b18195004773fc74eb51b62cd3f5dd76/packages/did-auth-siop-op-authenticator/src/session/OpSession.ts#L133

There is no "await" running verifyAuthenticationRequest() which is async.

oid4vciIssueCredential() fails when verifying did:peer proof

Hello again,

while trying to issue a credential, I noticed, that oid4vciIssueCredential() seems to fail when processing a request, that contains a proof of possession over a did:peer. Interestingly, this does not happen when the proof is over a did:key, even though my construction of the JWT is the same (using Veramo's keyManagerSign() ).

Error: invalid_signature: Signature invalid for JWT
    at verifyEd25519 (/home/void/Desktop/openidcomm/Code/node_modules/@sphereon/ssi-sdk.oid4vci-issuer/node_modules/did-jwt/src/VerifierAlgorithm.ts:179:22)
    at verifyJWSDecoded (/home/void/Desktop/openidcomm/Code/node_modules/@sphereon/ssi-sdk.oid4vci-issuer/node_modules/did-jwt/src/JWT.ts:337:38)
    at /home/void/Desktop/openidcomm/Code/node_modules/@sphereon/ssi-sdk.oid4vci-issuer/node_modules/did-jwt/src/JWT.ts:446:44
    at processTicksAndRejections (node:internal/process/task_queues:95:5)

After some research I came across this issue in the did-jwt library, which seems to describe the exact same issue I am facing. While it was fixed in version 7.4.0, this project seems to still use version 6.11.6 . I have written an example similar to the one in the issue, highlighting the difference between did:key and did:peer. Let me know if it is needed.

Am I doing something wrong or could this maybe really be the issue? Thanks a lot in advance

Get ERR_PACKAGE_PATH_NOT_EXPORTED when using ssi-sdk.oid4vci-issuer-rest-api.

Hi I'm a SSI developer in Japan.
I've been trying to build OID4VCI Issuer using your SDK.
(As an aside, I'm using AFJ for building a OID4VCI wallet.)
Building typescript works, but when I start a node process, I get a following error. Is there a way to fix this?
My package.json is as follows. Thanks,

Error [ERR_PACKAGE_PATH_NOT_EXPORTED]: No "exports" main defined in /Users/takahiro-kanuma/VSCodeProjects/sandbox/ssi-sandbox/sphereon-oid4vci/node_modules/did-jwt-vc/node_modules/uint8arrays/package.json
at new NodeError (node:internal/errors:387:5)
at throwExportsNotFound (node:internal/modules/esm/resolve:365:9)
at packageExportsResolve (node:internal/modules/esm/resolve:589:7)
at resolveExports (node:internal/modules/cjs/loader:554:36)
at Function.Module._findPath (node:internal/modules/cjs/loader:594:31)
at Function.Module._resolveFilename (node:internal/modules/cjs/loader:1012:27)
at Function.Module._load (node:internal/modules/cjs/loader:871:27)
at Module.require (node:internal/modules/cjs/loader:1098:19)
at require (node:internal/modules/cjs/helpers:108:18)
at Object. (/Users/takahiro-kanuma/VSCodeProjects/sandbox/ssi-sandbox/sphereon-oid4vci/node_modules/did-jwt-vc/node_modules/did-jwt/lib/index.cjs:1:19) {
code: 'ERR_PACKAGE_PATH_NOT_EXPORTED'
}

{
  "name": "oid4vci-trial",
  "version": "1.0.0",
  "main": "index.js",
  "type": "module",
  "license": "MIT",
  "scripts": {
    "start": "tsc && node dist/index.js",
    "lint": "eslint ."
  },
  "dependencies": {
    "@sphereon/ssi-sdk-ext.did-provider-jwk": "0.14.0",
    "@sphereon/ssi-sdk-ext.key-manager": "0.14.0",
    "@sphereon/ssi-sdk.data-store": "^0.15.1",
    "@sphereon/ssi-sdk.oid4vci-issuer-rest-api": "^0.15.1",
    "@sphereon/ssi-sdk.vc-handler-ld-local": "^0.15.1",
    "@types/node": "^20.6.0",
    "@veramo/data-store": "4.2.0",
    "@veramo/did-manager": "4.2.0",
    "@veramo/did-resolver": "4.2.0",
    "did-resolver": "4.1.0",
    "log4js": "^6.9.1",
    "typeorm": "^0.3.17"
  },
  "devDependencies": {
    "@tsconfig/node18": "^18.2.1",
    "@types/express": "^4.17.17",
    "@typescript-eslint/eslint-plugin": "^6.7.0",
    "@typescript-eslint/parser": "^6.7.0",
    "eslint": "^8.49.0",
    "eslint-config-prettier": "^9.0.0",
    "typescript": "^5.2.2"
  }
}

Status List bitstring bit ordering

const status = statusList.getStatus(typeof args.statusListIndex === 'number' ? args.statusListIndex : Number.parseInt(args.statusListIndex))

Hi,

I believe that the underlying library interprets the bit ordering wrongly.
Example:

> list = await sl.StatusList.decode({encodedList: 'H4sIAAAAAAAA_-3OMQEAAAgDIL_1b2Q0Y3gMErBTL98BAAAAAAAAAAAAAAAAAAAAOP19LQMAQAAA'})
StatusList {
  bitstring: Bitstring {
    bits: Uint8Array(16384) [
      192, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
        0, 0, 0, 0,
      ... 16284 more items
    ],
    length: 131072
  },
  length: 131072
}
> list.getStatus(1)
false
> list.getStatus(6)
true

The spec is quite clear that the left-most bit has index 0, the second left-most has index 1 etc. In my example, the two left-most bits are set to one, but the library sees as set indices 6 and 7.

feature: make getInstanceOpts from SIOPv2RP fetching from the DB

right now the the instances have to be passed to the constructor and are saved in a read only variable. My current solution is to read the values from the key value store and the pass it to the plugin:

const instanceOpts: IPEXInstanceOptions[] = await rep
    .find({ where: { key: Like('%oid4vp%') } })
    .then((res) =>
      res.map((r) => {
        const content = JSON.parse(r.data).value as IPresentationDefinition;
        return {
          definitionId: content.id,
          definition: content,
        } as IPEXInstanceOptions;
      })
    );

  return [
    new CredentialPlugin(),
    new PresentationExchange({
      stores: new KeyValueStore({
        namespace: 'oid4vp',
        store: new KeyValueTypeORMStoreAdapter({
          dbConnection: agentConfig.datasource,
        }),
      }),
    }),
    new SIOPv2RP({
      defaultOpts: {
        didOpts: {
          checkLinkedDomains: CheckLinkedDomain.IF_PRESENT,
          identifierOpts: {
            identifier,
            kid,
          },
        },
      },
      instanceOpts,
    }),

Would it be possible to implement this call directly into the siopv2RP instance so we don't have to restart it? Or at least make the variable olding the definitions public so we can patch it from outside (maybe via a new version).

Is this already covered on your roadmap? If not I would like to implement it :)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.